From 96d6b79ada29d28bed7d139115957b9812ce5961 Mon Sep 17 00:00:00 2001 From: Alex Rickabaugh Date: Wed, 29 Aug 2018 12:50:00 -0700 Subject: [PATCH] feat(ivy): resolve references to vars in .d.ts files (#25775) Previously, if ngtsc encountered a VariableDeclaration without an initializer, it would assume that the variable was undefined, and return that result. However, for symbols exported from external modules that resolve to .d.ts files, variable declarations are of the form: export declare let varName: Type; This form also lacks an initializer, but indicates the presence of an importable symbol which can be referenced. This commit changes the static resolver to understand variable declarations with the 'declare' keyword and to generate references when it encounters them. PR Close #25775 --- .../src/ngtsc/metadata/src/resolver.ts | 28 ++++++++++++++++--- .../src/ngtsc/metadata/test/resolver_spec.ts | 13 +++++++++ 2 files changed, 37 insertions(+), 4 deletions(-) diff --git a/packages/compiler-cli/src/ngtsc/metadata/src/resolver.ts b/packages/compiler-cli/src/ngtsc/metadata/src/resolver.ts index cb89986276..fdb48b9eb9 100644 --- a/packages/compiler-cli/src/ngtsc/metadata/src/resolver.ts +++ b/packages/compiler-cli/src/ngtsc/metadata/src/resolver.ts @@ -439,10 +439,7 @@ class StaticInterpreter { if (this.host.isClass(node)) { return this.getReference(node, context); } else if (ts.isVariableDeclaration(node)) { - if (!node.initializer) { - return undefined; - } - return this.visitExpression(node.initializer, context); + return this.visitVariableDeclaration(node, context); } else if (ts.isParameter(node) && context.scope.has(node)) { return context.scope.get(node) !; } else if (ts.isExportAssignment(node)) { @@ -456,6 +453,16 @@ class StaticInterpreter { } } + private visitVariableDeclaration(node: ts.VariableDeclaration, context: Context): ResolvedValue { + if (node.initializer !== undefined) { + return this.visitExpression(node.initializer, context); + } else if (isVariableDeclarationDeclared(node)) { + return this.getReference(node, context); + } else { + return undefined; + } + } + private visitEnumDeclaration(node: ts.EnumDeclaration, context: Context): ResolvedValue { const enumRef = this.getReference(node, context) as Reference; const map = new Map(); @@ -728,3 +735,16 @@ function identifierOfDeclaration(decl: ts.Declaration): ts.Identifier|undefined function isPossibleClassDeclaration(node: ts.Node): node is ts.Declaration { return ts.isClassDeclaration(node) || ts.isVariableDeclaration(node); } + +function isVariableDeclarationDeclared(node: ts.VariableDeclaration): boolean { + if (node.parent === undefined || !ts.isVariableDeclarationList(node.parent)) { + return false; + } + const declList = node.parent; + if (declList.parent === undefined || !ts.isVariableStatement(declList.parent)) { + return false; + } + const varStmt = declList.parent; + return varStmt.modifiers !== undefined && + varStmt.modifiers.some(mod => mod.kind === ts.SyntaxKind.DeclareKeyword); +} diff --git a/packages/compiler-cli/src/ngtsc/metadata/test/resolver_spec.ts b/packages/compiler-cli/src/ngtsc/metadata/test/resolver_spec.ts index 7c4863a6dd..f25f517fc7 100644 --- a/packages/compiler-cli/src/ngtsc/metadata/test/resolver_spec.ts +++ b/packages/compiler-cli/src/ngtsc/metadata/test/resolver_spec.ts @@ -284,4 +284,17 @@ describe('ngtsc metadata', () => { expect(result.enumRef.node.name.text).toBe('Foo'); expect(result.name).toBe('B'); }); + + it('variable declaration resolution works', () => { + const {program} = makeProgram([ + {name: 'decl.d.ts', contents: 'export declare let value: number;'}, + {name: 'entry.ts', contents: `import {value} from './decl'; const target$ = value;`}, + ]); + const checker = program.getTypeChecker(); + const host = new TypeScriptReflectionHost(checker); + const result = getDeclaration(program, 'entry.ts', 'target$', ts.isVariableDeclaration); + const res = staticallyResolve(result.initializer !, host, checker); + console.error(res); + expect(res instanceof Reference).toBe(true); + }); });