From c4e039a43a9a25a4f065c86d95ae077eae4321a4 Mon Sep 17 00:00:00 2001 From: JoostK Date: Wed, 11 Sep 2019 22:08:53 +0200 Subject: [PATCH] fix(ngcc): correctly read static properties for aliased classes (#32619) In ESM2015 bundles, a class with decorators may be emitted as follows: ```javascript var MyClass_1; let MyClass = MyClass_1 = class MyClass {}; MyClass.decorators = [/* here be decorators */]; ``` Such a class has two declarations: the publicly visible `let MyClass` and the implementation `class MyClass {}` node. In #32539 a refactoring took place to handle such classes more consistently, however the logic to find static properties was mistakenly kept identical to its broken state before the refactor, by looking for static properties on the implementation symbol (the one for `class MyClass {}`) whereas the static properties need to be obtained from the symbol corresponding with the `let MyClass` declaration, as that is where the `decorators` property is assigned to in the example above. This commit fixes the behavior by looking for static properties on the public declaration symbol. This fixes an issue where decorators were not found for classes that do in fact have decorators, therefore preventing the classes from being compiled for Ivy. Fixes #31791 PR Close #32619 --- .../ngcc/src/host/esm2015_host.ts | 2 +- .../ngcc/test/host/esm2015_host_spec.ts | 23 +++++++++++++++++++ 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/packages/compiler-cli/ngcc/src/host/esm2015_host.ts b/packages/compiler-cli/ngcc/src/host/esm2015_host.ts index 150f9b1716..a2a6f0b9ae 100644 --- a/packages/compiler-cli/ngcc/src/host/esm2015_host.ts +++ b/packages/compiler-cli/ngcc/src/host/esm2015_host.ts @@ -638,7 +638,7 @@ export class Esm2015ReflectionHost extends TypeScriptReflectionHost implements N */ protected getStaticProperty(symbol: NgccClassSymbol, propertyName: ts.__String): ts.Symbol |undefined { - return symbol.implementation.exports && symbol.implementation.exports.get(propertyName); + return symbol.declaration.exports && symbol.declaration.exports.get(propertyName); } /** diff --git a/packages/compiler-cli/ngcc/test/host/esm2015_host_spec.ts b/packages/compiler-cli/ngcc/test/host/esm2015_host_spec.ts index 5db8a3fa05..b43c32d839 100644 --- a/packages/compiler-cli/ngcc/test/host/esm2015_host_spec.ts +++ b/packages/compiler-cli/ngcc/test/host/esm2015_host_spec.ts @@ -132,9 +132,13 @@ runInEachFileSystem(() => { CLASS_EXPRESSION_FILE = { name: _('/class_expression.js'), contents: ` + import {Directive} from '@angular/core'; var AliasedClass_1; let EmptyClass = class EmptyClass {}; let AliasedClass = AliasedClass_1 = class AliasedClass {} + AliasedClass.decorators = [ + { type: Directive, args: [{ selector: '[someDirective]' },] } + ]; let usageOfAliasedClass = AliasedClass_1; `, }; @@ -708,6 +712,25 @@ runInEachFileSystem(() => { ]); }); + it('should find the decorators on an aliased class', () => { + loadTestFiles([CLASS_EXPRESSION_FILE]); + const {program} = makeTestBundleProgram(CLASS_EXPRESSION_FILE.name); + const host = new Esm2015ReflectionHost(new MockLogger(), false, program.getTypeChecker()); + const classNode = getDeclaration( + program, CLASS_EXPRESSION_FILE.name, 'AliasedClass', isNamedVariableDeclaration); + const decorators = host.getDecoratorsOfDeclaration(classNode) !; + + expect(decorators).toBeDefined(); + expect(decorators.length).toEqual(1); + + const decorator = decorators[0]; + expect(decorator.name).toEqual('Directive'); + expect(decorator.import).toEqual({name: 'Directive', from: '@angular/core'}); + expect(decorator.args !.map(arg => arg.getText())).toEqual([ + '{ selector: \'[someDirective]\' }', + ]); + }); + it('should return null if the symbol is not a class', () => { loadTestFiles([FOO_FUNCTION_FILE]); const {program} = makeTestBundleProgram(FOO_FUNCTION_FILE.name);