diff --git a/packages/compiler-cli/src/ngtsc/shims/src/summary_generator.ts b/packages/compiler-cli/src/ngtsc/shims/src/summary_generator.ts index ef14fae7b7..6e6b6bae1b 100644 --- a/packages/compiler-cli/src/ngtsc/shims/src/summary_generator.ts +++ b/packages/compiler-cli/src/ngtsc/shims/src/summary_generator.ts @@ -34,16 +34,31 @@ export class SummaryGenerator implements ShimGenerator { // to semantically understand which decorators are Angular decorators. It's okay to output an // overly broad set of summary exports as the exports are no-ops anyway, and summaries are a // compatibility layer which will be removed after Ivy is enabled. - const symbolNames = original - .statements - // Pick out top level class declarations... - .filter(ts.isClassDeclaration) - // which are named, exported, and have decorators. - .filter( - decl => isExported(decl) && decl.decorators !== undefined && - decl.name !== undefined) - // Grab the symbol name. - .map(decl => decl.name !.text); + const symbolNames: string[] = []; + for (const stmt of original.statements) { + if (ts.isClassDeclaration(stmt)) { + // If the class isn't exported, or if it's not decorated, then skip it. + if (!isExported(stmt) || stmt.decorators === undefined || stmt.name === undefined) { + continue; + } + symbolNames.push(stmt.name.text); + } else if (ts.isExportDeclaration(stmt)) { + // Look for an export statement of the form "export {...};". If it doesn't match that, then + // skip it. + if (stmt.exportClause === undefined || stmt.moduleSpecifier !== undefined) { + continue; + } + + for (const specifier of stmt.exportClause.elements) { + // At this point, there is no guarantee that specifier here refers to a class declaration, + // but that's okay. + + // Use specifier.name as that's guaranteed to be the exported name, regardless of whether + // specifier.propertyName is set. + symbolNames.push(specifier.name.text); + } + } + } const varLines = symbolNames.map(name => `export const ${name}NgSummary: any = null;`); diff --git a/packages/compiler-cli/test/ngtsc/ngtsc_spec.ts b/packages/compiler-cli/test/ngtsc/ngtsc_spec.ts index 30d1ff7078..d9247ca57c 100644 --- a/packages/compiler-cli/test/ngtsc/ngtsc_spec.ts +++ b/packages/compiler-cli/test/ngtsc/ngtsc_spec.ts @@ -1768,6 +1768,24 @@ describe('ngtsc behavioral tests', () => { expect(summaryContents).toEqual(`export var TestModuleNgSummary = null;\n`); }); + it('should generate a summary stub for classes exported via exports', () => { + env.tsconfig({'allowEmptyCodegenFiles': true}); + + env.write('test.ts', ` + import {Injectable, NgModule} from '@angular/core'; + + @NgModule({}) + class NotDirectlyExported {} + + export {NotDirectlyExported}; + `); + + env.driveMain(); + + const summaryContents = env.getContents('test.ngsummary.js'); + expect(summaryContents).toEqual(`export var NotDirectlyExportedNgSummary = null;\n`); + }); + it('it should generate empty export when there are no other summary symbols, to ensure the output is a valid ES module', () => { env.tsconfig({'allowEmptyCodegenFiles': true});