fix(ngcc): ensure that "inline exports" can be interpreted correctly (#39267)

Previously, inline exports of the form `exports.foo = <implementation>;` were
being interpreted (by the ngtsc `PartialInterpeter`) as `Reference` objects.
This is not what is desired since it prevents the value of the export
from being unpacked, such as when analyzing `NgModule` declarations:

```
exports.directives = [Directive1, Directive2];

@NgImport({declarations: [exports.directives]})
class AppModule {}
```

In this example the interpreter would think that `exports.directives`
was a reference rather than an array that needs to be unpacked.

This bug was picked up by the ngcc-validation repository. See
https://github.com/angular/ngcc-validation/pull/1990 and
https://circleci.com/gh/angular/ngcc-validation/17130

PR Close #39267
This commit is contained in:
Pete Bacon Darwin
2020-10-14 16:30:09 +01:00
committed by atscott
parent ac0016cd82
commit 822b838fbc
5 changed files with 57 additions and 43 deletions

View File

@ -2422,9 +2422,10 @@ exports.MissingClass2 = MissingClass2;
const file = getSourceFileOrError(bundle.program, _('/inline_export.js'));
const exportDeclarations = host.getExportsOfModule(file);
expect(exportDeclarations).not.toBeNull();
const decl = exportDeclarations!.get('directives')!;
const decl = exportDeclarations!.get('directives') as InlineDeclaration;
expect(decl).toBeDefined();
expect(decl.node).toBeDefined();
expect(decl.node.getText()).toEqual('exports.directives');
expect(decl.implementation!.getText()).toEqual('[foo]');
expect(decl.kind).toEqual(DeclarationKind.Inline);
});

View File

@ -2750,13 +2750,13 @@ runInEachFileSystem(() => {
const exportDeclarations = host.getExportsOfModule(file);
expect(exportDeclarations).not.toBe(null);
expect(exportDeclarations!.size).toEqual(1);
const classDecl = exportDeclarations!.get('DecoratedClass')!;
const classDecl = exportDeclarations!.get('DecoratedClass') as InlineDeclaration;
expect(classDecl).toBeDefined();
expect(classDecl.kind).toEqual(DeclarationKind.Inline);
expect(classDecl.known).toBe(null);
expect(classDecl.viaModule).toBe(null);
expect(classDecl.node.getText()).toEqual('exports.DecoratedClass');
expect(classDecl.node.parent.parent.getText()).toContain('function DecoratedClass() {');
expect(classDecl.implementation!.getText()).toContain('function DecoratedClass() {');
});
it('should handle wildcard re-exports of other modules (with emitted helpers)', () => {
@ -2824,9 +2824,10 @@ runInEachFileSystem(() => {
const file = getSourceFileOrError(bundle.program, INLINE_EXPORT_FILE.name);
const exportDeclarations = host.getExportsOfModule(file);
expect(exportDeclarations).not.toBe(null);
const decl = exportDeclarations!.get('directives')!;
const decl = exportDeclarations!.get('directives') as InlineDeclaration;
expect(decl).toBeDefined();
expect(decl.node).toBeDefined();
expect(decl.node.getText()).toEqual('exports.directives');
expect(decl.implementation!.getText()).toEqual('[foo]');
expect(decl.kind).toEqual(DeclarationKind.Inline);
});