fix(ivy): process separate declarations and exports for summaries (#29193)
ngsummary files were generated with an export for each class declaration. However, some Angular code declares classes (class Foo) and exports them (export {Foo}) separately, which was causing incomplete summary files. This commit expands the set of symbol names for which summary exports will be generated, fixing this issue. PR Close #29193
This commit is contained in:
parent
3a6ba00286
commit
49dccf4bfc
|
@ -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;`);
|
||||
|
||||
|
|
|
@ -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});
|
||||
|
|
Loading…
Reference in New Issue