Skip to content

Commit

Permalink
feat: #561 - Add methods Node#getLocals, #getLocalByName, `#getLo…
Browse files Browse the repository at this point in the history
…calByNameOrThrow`

These are using internal api in the compiler api, so they should be used with that in mind.
  • Loading branch information
dsherret committed May 18, 2019
1 parent 92331e1 commit c343de3
Show file tree
Hide file tree
Showing 3 changed files with 108 additions and 0 deletions.
20 changes: 20 additions & 0 deletions lib/ts-morph.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4236,6 +4236,26 @@ export declare class Node<NodeType extends ts.Node = ts.Node> implements TextRan
* Gets the compiler symbol or undefined if it doesn't exist.
*/
getSymbol(): Symbol | undefined;
/**
* Gets the specified local symbol by name or throws if it doesn't exist.
*
* WARNING: The symbol table of locals is not exposed publicly by the compiler. Use this at your own risk knowing it may break.
* @param name - Name of the local symbol.
*/
getLocalOrThrow(name: string): Symbol;
/**
* Gets the specified local symbol by name or returns undefined if it doesn't exist.
*
* WARNING: The symbol table of locals is not exposed publicly by the compiler. Use this at your own risk knowing it may break.
* @param name - Name of the local symbol.
*/
getLocal(name: string): Symbol | undefined;
/**
* Gets the symbols within the current scope.
*
* WARNING: The symbol table of locals is not exposed publicly by the compiler. Use this at your own risk knowing it may break.
*/
getLocals(): Symbol[];
/**
* Gets the type of the node.
*/
Expand Down
43 changes: 43 additions & 0 deletions src/compiler/ast/common/Node.ts
Original file line number Diff line number Diff line change
Expand Up @@ -249,6 +249,49 @@ export class Node<NodeType extends ts.Node = ts.Node> implements TextRange {
return undefined;
}

/**
* Gets the specified local symbol by name or throws if it doesn't exist.
*
* WARNING: The symbol table of locals is not exposed publicly by the compiler. Use this at your own risk knowing it may break.
* @param name - Name of the local symbol.
*/
getLocalOrThrow(name: string): Symbol {
return errors.throwIfNullOrUndefined(this.getLocal(name), `Expected to find local symbol with name: ${name}`);
}

/**
* Gets the specified local symbol by name or returns undefined if it doesn't exist.
*
* WARNING: The symbol table of locals is not exposed publicly by the compiler. Use this at your own risk knowing it may break.
* @param name - Name of the local symbol.
*/
getLocal(name: string): Symbol | undefined {
const locals = this._getCompilerLocals();
if (locals == null)
return undefined;

const tsSymbol = locals.get(name as ts.__String);
return tsSymbol == null ? undefined : this._context.compilerFactory.getSymbol(tsSymbol);
}

/**
* Gets the symbols within the current scope.
*
* WARNING: The symbol table of locals is not exposed publicly by the compiler. Use this at your own risk knowing it may break.
*/
getLocals(): Symbol[] {
const locals = this._getCompilerLocals();
if (locals == null)
return [];
return ArrayUtils.from(locals.values()).map(symbol => this._context.compilerFactory.getSymbol(symbol));
}

/** @internal */
private _getCompilerLocals() {
this._ensureBound();
return (this.compilerNode as any).locals as ts.SymbolTable | undefined;
}

/**
* Gets the type of the node.
*/
Expand Down
45 changes: 45 additions & 0 deletions src/tests/compiler/ast/common/nodeTests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1943,4 +1943,49 @@ class MyClass {
}, `/** Testing */\nclass A {\n}`);
});
});

describe(nameof<Node>(n => n.getLocals), () => {
function doTest(text: string, expectedLocalNames: string[]) {
const { sourceFile } = getInfoFromText(text);
expect(sourceFile.getLocals().map(l => l.getName())).to.deep.equal(expectedLocalNames);
}

it("should get all the locals in the source file scope", () => {
doTest("const t = 5;\nclass U {}\ninterface I", ["t", "U", "I"]);
});
});

describe(nameof<Node>(n => n.getLocal), () => {
function doTest(text: string, name: string, expectedName: string | undefined) {
const { sourceFile } = getInfoFromText(text);
const result = sourceFile.getLocal(name);
expect(result && result.getName() || undefined).to.equal(expectedName);
}

it("should get the local by name", () => {
doTest("const t = 5;\nclass U {}\ninterface I", "t", "t");
});

it("should return undefined when it doesn't exist", () => {
doTest("const t = 5;\nclass U {}\ninterface I", "m", undefined);
});
});

describe(nameof<Node>(n => n.getLocalOrThrow), () => {
function doTest(text: string, name: string, expectedName: string | undefined) {
const { sourceFile } = getInfoFromText(text);
if (expectedName == null)
expect(() => sourceFile.getLocalOrThrow(name)).to.throw();
else
expect(sourceFile.getLocalOrThrow(name).getName()).to.equal(expectedName);
}

it("should get the local by name", () => {
doTest("const t = 5;\nclass U {}\ninterface I", "t", "t");
});

it("should throw when it doesn't exist", () => {
doTest("const t = 5;\nclass U {}\ninterface I", "m", undefined);
});
});
});

0 comments on commit c343de3

Please sign in to comment.