diff --git a/aio/tools/examples/example-boilerplate.js b/aio/tools/examples/example-boilerplate.js index 6a7c1715a1..6304e05950 100644 --- a/aio/tools/examples/example-boilerplate.js +++ b/aio/tools/examples/example-boilerplate.js @@ -68,11 +68,8 @@ class ExampleBoilerPlate { if (ivy) { // We only need the "fesm5" bundles as the CLI webpack build does not need - // any other formats for building and serving. Ngcc currently only updates - // the module typings if we specified an "es2015" format. This means that - // we also need to build with "fesm2015" in order to get updated typings - // which are needed for compilation. - shelljs.exec(`yarn --cwd ${SHARED_PATH} ivy-ngcc --properties module es2015`); + // any other formats for building and serving. + shelljs.exec(`yarn --cwd ${SHARED_PATH} ivy-ngcc --properties module`); } exampleFolders.forEach(exampleFolder => { diff --git a/packages/compiler-cli/ngcc/src/host/esm5_host.ts b/packages/compiler-cli/ngcc/src/host/esm5_host.ts index 628b6be1c2..846bc45dac 100644 --- a/packages/compiler-cli/ngcc/src/host/esm5_host.ts +++ b/packages/compiler-cli/ngcc/src/host/esm5_host.ts @@ -14,6 +14,7 @@ import {getNameText, hasNameIdentifier} from '../utils'; import {Esm2015ReflectionHost, ParamInfo, getPropertyValueFromSymbol, isAssignmentStatement} from './esm2015_host'; + /** * ESM5 packages contain ECMAScript IIFE functions that act like classes. For example: * @@ -117,8 +118,30 @@ export class Esm5ReflectionHost extends Esm2015ReflectionHost { id = outerClassNode.name; } - // Resolve the identifier to a Symbol, and return the declaration of that. - return super.getDeclarationOfIdentifier(id); + const declaration = super.getDeclarationOfIdentifier(id); + + if (!declaration || !ts.isVariableDeclaration(declaration.node) || + declaration.node.initializer !== undefined || + // VariableDeclaration => VariableDeclarationList => VariableStatement => IIFE Block + !ts.isBlock(declaration.node.parent.parent.parent)) { + return declaration; + } + + // We might have an alias to another variable declaration. + // Search the containing iife body for it. + const block = declaration.node.parent.parent.parent; + const aliasSymbol = this.checker.getSymbolAtLocation(declaration.node.name); + for (let i = 0; i < block.statements.length; i++) { + const statement = block.statements[i]; + // Looking for statement that looks like: `AliasedVariable = OriginalVariable;` + if (isAssignmentStatement(statement) && ts.isIdentifier(statement.expression.left) && + ts.isIdentifier(statement.expression.right) && + this.checker.getSymbolAtLocation(statement.expression.left) === aliasSymbol) { + return this.getDeclarationOfIdentifier(statement.expression.right); + } + } + + return declaration; } /** diff --git a/packages/compiler-cli/ngcc/test/host/esm5_host_import_helper_spec.ts b/packages/compiler-cli/ngcc/test/host/esm5_host_import_helper_spec.ts index fb4b7d8752..d439f82745 100644 --- a/packages/compiler-cli/ngcc/test/host/esm5_host_import_helper_spec.ts +++ b/packages/compiler-cli/ngcc/test/host/esm5_host_import_helper_spec.ts @@ -371,6 +371,18 @@ describe('Esm5ReflectionHost [import helper style]', () => { expect(actualDeclaration !.node).toBe(expectedDeclarationNode); expect(actualDeclaration !.viaModule).toBe('@angular/core'); }); + + it('should find the "actual" declaration of an aliased variable identifier', () => { + const program = makeTestProgram(fileSystem.files[2]); + const host = new Esm5ReflectionHost(false, program.getTypeChecker()); + const ngModuleRef = findIdentifier( + program.getSourceFile(fileSystem.files[2].name) !, 'HttpClientXsrfModule_1', + isNgModulePropertyAssignment); + + const declaration = host.getDeclarationOfIdentifier(ngModuleRef !); + expect(declaration).not.toBe(null); + expect(declaration !.node.getText()).toContain('function HttpClientXsrfModule()'); + }); }); }); @@ -422,3 +434,20 @@ describe('Esm5ReflectionHost [import helper style]', () => { return node.forEachChild(node => findVariableDeclaration(node, variableName)); } }); + +function findIdentifier( + node: ts.Node | undefined, identifierName: string, + requireFn: (node: ts.Identifier) => boolean): ts.Identifier|undefined { + if (!node) { + return undefined; + } + if (ts.isIdentifier(node) && node.text === identifierName && requireFn(node)) { + return node; + } + return node.forEachChild(node => findIdentifier(node, identifierName, requireFn)); +} + +function isNgModulePropertyAssignment(identifier: ts.Identifier): boolean { + return ts.isPropertyAssignment(identifier.parent) && + identifier.parent.name.getText() === 'ngModule'; +} \ No newline at end of file