Skip to content

Commit c4e039a

Browse files
JoostKkara
authored andcommitted
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
1 parent 5a830c4 commit c4e039a

2 files changed

Lines changed: 24 additions & 1 deletion

File tree

‎packages/compiler-cli/ngcc/src/host/esm2015_host.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -638,7 +638,7 @@ export class Esm2015ReflectionHost extends TypeScriptReflectionHost implements N
638638
*/
639639
protected getStaticProperty(symbol: NgccClassSymbol, propertyName: ts.__String): ts.Symbol
640640
|undefined {
641-
return symbol.implementation.exports && symbol.implementation.exports.get(propertyName);
641+
return symbol.declaration.exports && symbol.declaration.exports.get(propertyName);
642642
}
643643

644644
/**

‎packages/compiler-cli/ngcc/test/host/esm2015_host_spec.ts‎

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,9 +132,13 @@ runInEachFileSystem(() => {
132132
CLASS_EXPRESSION_FILE = {
133133
name: _('/class_expression.js'),
134134
contents: `
135+
import {Directive} from '@angular/core';
135136
var AliasedClass_1;
136137
let EmptyClass = class EmptyClass {};
137138
let AliasedClass = AliasedClass_1 = class AliasedClass {}
139+
AliasedClass.decorators = [
140+
{ type: Directive, args: [{ selector: '[someDirective]' },] }
141+
];
138142
let usageOfAliasedClass = AliasedClass_1;
139143
`,
140144
};
@@ -708,6 +712,25 @@ runInEachFileSystem(() => {
708712
]);
709713
});
710714

715+
it('should find the decorators on an aliased class', () => {
716+
loadTestFiles([CLASS_EXPRESSION_FILE]);
717+
const {program} = makeTestBundleProgram(CLASS_EXPRESSION_FILE.name);
718+
const host = new Esm2015ReflectionHost(new MockLogger(), false, program.getTypeChecker());
719+
const classNode = getDeclaration(
720+
program, CLASS_EXPRESSION_FILE.name, 'AliasedClass', isNamedVariableDeclaration);
721+
const decorators = host.getDecoratorsOfDeclaration(classNode) !;
722+
723+
expect(decorators).toBeDefined();
724+
expect(decorators.length).toEqual(1);
725+
726+
const decorator = decorators[0];
727+
expect(decorator.name).toEqual('Directive');
728+
expect(decorator.import).toEqual({name: 'Directive', from: '@angular/core'});
729+
expect(decorator.args !.map(arg => arg.getText())).toEqual([
730+
'{ selector: \'[someDirective]\' }',
731+
]);
732+
});
733+
711734
it('should return null if the symbol is not a class', () => {
712735
loadTestFiles([FOO_FUNCTION_FILE]);
713736
const {program} = makeTestBundleProgram(FOO_FUNCTION_FILE.name);

0 commit comments

Comments
 (0)