From 6a24db2bc6520a02905a403b2c2ccaf2d65f5fd3 Mon Sep 17 00:00:00 2001 From: Pete Bacon Darwin Date: Thu, 27 Sep 2018 21:21:43 +0100 Subject: [PATCH] fix(ivy): ngcc: only consider decorators from `@angular/core` (#26236) PR Close #26236 --- .../src/ngcc/src/host/fesm2015_host.ts | 13 +- .../src/ngcc/test/host/esm5_host_spec.ts | 123 +++++------- .../src/ngcc/test/host/fesm2015_host_spec.ts | 181 +++++++++--------- .../src/ngcc/test/parsing/esm5_parser_spec.ts | 1 + .../test/rendering/esm2015_renderer_spec.ts | 2 +- .../ngcc/test/rendering/esm5_renderer_spec.ts | 2 +- 6 files changed, 154 insertions(+), 168 deletions(-) diff --git a/packages/compiler-cli/src/ngcc/src/host/fesm2015_host.ts b/packages/compiler-cli/src/ngcc/src/host/fesm2015_host.ts index e69b22f5d3..4d46794232 100644 --- a/packages/compiler-cli/src/ngcc/src/host/fesm2015_host.ts +++ b/packages/compiler-cli/src/ngcc/src/host/fesm2015_host.ts @@ -336,7 +336,7 @@ export class Fesm2015ReflectionHost extends TypeScriptReflectionHost implements decoratorsIdentifier.parent.operatorToken.kind === ts.SyntaxKind.EqualsToken) { // AST of the array of decorator values const decoratorsArray = decoratorsIdentifier.parent.right; - return this.reflectDecorators(decoratorsArray); + return this.reflectDecorators(decoratorsArray).filter(isImportedFromCore); } } return null; @@ -404,8 +404,12 @@ export class Fesm2015ReflectionHost extends TypeScriptReflectionHost implements const propDecoratorsMap = getPropertyValueFromSymbol(decoratorsProperty); if (propDecoratorsMap && ts.isObjectLiteralExpression(propDecoratorsMap)) { const propertiesMap = reflectObjectLiteral(propDecoratorsMap); - propertiesMap.forEach( - (value, name) => { memberDecorators.set(name, this.reflectDecorators(value)); }); + propertiesMap.forEach((value, name) => { + const decorators = this.reflectDecorators(value).filter(isImportedFromCore); + if (decorators.length) { + memberDecorators.set(name, decorators); + } + }); } return memberDecorators; } @@ -727,7 +731,8 @@ export class Fesm2015ReflectionHost extends TypeScriptReflectionHost implements .map(paramInfo => { const type = paramInfo && paramInfo.get('type') || null; const decoratorInfo = paramInfo && paramInfo.get('decorators') || null; - const decorators = decoratorInfo && this.reflectDecorators(decoratorInfo); + const decorators = + decoratorInfo && this.reflectDecorators(decoratorInfo).filter(isImportedFromCore); return {type, decorators}; }); } diff --git a/packages/compiler-cli/src/ngcc/test/host/esm5_host_spec.ts b/packages/compiler-cli/src/ngcc/test/host/esm5_host_spec.ts index 25fe54083e..ea3f641d9e 100644 --- a/packages/compiler-cli/src/ngcc/test/host/esm5_host_spec.ts +++ b/packages/compiler-cli/src/ngcc/test/host/esm5_host_spec.ts @@ -79,46 +79,42 @@ const FOO_FUNCTION_FILE = { const INVALID_DECORATORS_FILE = { name: '/invalid_decorators.js', contents: ` - var NotArrayLiteralDecorator = {}; + import { Directive } from '@angular/core'; var NotArrayLiteral = (function() { function NotArrayLiteral() { } NotArrayLiteral.decorators = () => [ - { type: NotArrayLiteralDecorator, args: [{ selector: '[ignored]' },] }, + { type: Directive, args: [{ selector: '[ignored]' },] }, ]; return NotArrayLiteral; }()); - var NotObjectLiteralDecorator = {}; var NotObjectLiteral = (function() { function NotObjectLiteral() { } NotObjectLiteral.decorators = [ "This is not an object literal", - { type: NotObjectLiteralDecorator }, + { type: Directive }, ]; return NotObjectLiteral; }()); - var NoTypePropertyDecorator1 = {}; - var NoTypePropertyDecorator2 = {}; var NoTypeProperty = (function() { function NoTypeProperty() { } NoTypeProperty.decorators = [ - { notType: NoTypePropertyDecorator1 }, - { type: NoTypePropertyDecorator2 }, + { notType: Directive }, + { type: Directive }, ]; return NoTypeProperty; }()); - var NotIdentifierDecorator = {}; var NotIdentifier = (function() { function NotIdentifier() { } NotIdentifier.decorators = [ { type: 'StringsLiteralsAreNotIdentifiers' }, - { type: NotIdentifierDecorator }, + { type: Directive }, ]; return NotIdentifier; }()); @@ -128,33 +124,31 @@ const INVALID_DECORATORS_FILE = { const INVALID_DECORATOR_ARGS_FILE = { name: '/invalid_decorator_args.js', contents: ` - var NoArgsPropertyDecorator = {}; + import { Directive } from '@angular/core'; var NoArgsProperty = (function() { function NoArgsProperty() { } NoArgsProperty.decorators = [ - { type: NoArgsPropertyDecorator }, + { type: Directive }, ]; return NoArgsProperty; }()); - var NoPropertyAssignmentDecorator = {}; var args = [{ selector: '[ignored]' },]; var NoPropertyAssignment = (function() { function NoPropertyAssignment() { } NoPropertyAssignment.decorators = [ - { type: NoPropertyAssignmentDecorator, args }, + { type: Directive, args }, ]; return NoPropertyAssignment; }()); - var NotArrayLiteralDecorator = {}; var NotArrayLiteral = (function() { function NotArrayLiteral() { } NotArrayLiteral.decorators = [ - { type: NotArrayLiteralDecorator, args: () => [{ selector: '[ignored]' },] }, + { type: Directive, args: () => [{ selector: '[ignored]' },] }, ]; return NotArrayLiteral; }()); @@ -164,51 +158,47 @@ const INVALID_DECORATOR_ARGS_FILE = { const INVALID_PROP_DECORATORS_FILE = { name: '/invalid_prop_decorators.js', contents: ` - var NotObjectLiteralDecorator = {}; + import { Input } from '@angular/core'; var NotObjectLiteral = (function() { function NotObjectLiteral() { } NotObjectLiteral.propDecorators = () => ({ - "prop": [{ type: NotObjectLiteralDecorator },] + "prop": [{ type: Input },] }); return NotObjectLiteral; }()); - var NotObjectLiteralPropDecorator = {}; var NotObjectLiteralProp = (function() { function NotObjectLiteralProp() { } NotObjectLiteralProp.propDecorators = { "prop": [ "This is not an object literal", - { type: NotObjectLiteralPropDecorator }, + { type: Input }, ] }; return NotObjectLiteralProp; }()); - var NoTypePropertyDecorator1 = {}; - var NoTypePropertyDecorator2 = {}; var NoTypeProperty = (function() { function NoTypeProperty() { } NoTypeProperty.propDecorators = { "prop": [ - { notType: NoTypePropertyDecorator1 }, - { type: NoTypePropertyDecorator2 }, + { notType: Input }, + { type: Input }, ] }; return NoTypeProperty; }()); - var NotIdentifierDecorator = {}; var NotIdentifier = (function() { function NotIdentifier() { } NotIdentifier.propDecorators = { "prop": [ { type: 'StringsLiteralsAreNotIdentifiers' }, - { type: NotIdentifierDecorator }, + { type: Input }, ] }; return NotIdentifier; @@ -219,33 +209,31 @@ const INVALID_PROP_DECORATORS_FILE = { const INVALID_PROP_DECORATOR_ARGS_FILE = { name: '/invalid_prop_decorator_args.js', contents: ` - var NoArgsPropertyDecorator = {}; + import { Input } from '@angular/core'; var NoArgsProperty = (function() { function NoArgsProperty() { } NoArgsProperty.propDecorators = { - "prop": [{ type: NoArgsPropertyDecorator },] + "prop": [{ type: Input },] }; return NoArgsProperty; }()); - var NoPropertyAssignmentDecorator = {}; var args = [{ selector: '[ignored]' },]; var NoPropertyAssignment = (function() { function NoPropertyAssignment() { } NoPropertyAssignment.propDecorators = { - "prop": [{ type: NoPropertyAssignmentDecorator, args },] + "prop": [{ type: Input, args },] }; return NoPropertyAssignment; }()); - var NotArrayLiteralDecorator = {}; var NotArrayLiteral = (function() { function NotArrayLiteral() { } NotArrayLiteral.propDecorators = { - "prop": [{ type: NotArrayLiteralDecorator, args: () => [{ selector: '[ignored]' },] },], + "prop": [{ type: Input, args: () => [{ selector: '[ignored]' },] },], }; return NotArrayLiteral; }()); @@ -255,23 +243,22 @@ const INVALID_PROP_DECORATOR_ARGS_FILE = { const INVALID_CTOR_DECORATORS_FILE = { name: '/invalid_ctor_decorators.js', contents: ` + import { Inject } from '@angular/core'; var NoParametersDecorator = {}; var NoParameters = (function() { function NoParameters() {} return NoParameters; }()); - var ArrowFunctionDecorator = {}; var ArrowFunction = (function() { function ArrowFunction(arg1) { } ArrowFunction.ctorParameters = () => [ - { type: 'ParamType', decorators: [{ type: ArrowFunctionDecorator },] } + { type: 'ParamType', decorators: [{ type: Inject },] } ]; return ArrowFunction; }()); - var NotArrayLiteralDecorator = {}; var NotArrayLiteral = (function() { function NotArrayLiteral(arg1) { } @@ -279,19 +266,16 @@ const INVALID_CTOR_DECORATORS_FILE = { return NotArrayLiteral; }()); - var NotObjectLiteralDecorator = {}; var NotObjectLiteral = (function() { function NotObjectLiteral(arg1, arg2) { } NotObjectLiteral.ctorParameters = function() { return [ "This is not an object literal", - { type: 'ParamType', decorators: [{ type: NotObjectLiteralDecorator },] }, + { type: 'ParamType', decorators: [{ type: Inject },] }, ]; }; return NotObjectLiteral; }()); - var NoTypePropertyDecorator1 = {}; - var NoTypePropertyDecorator2 = {}; var NoTypeProperty = (function() { function NoTypeProperty(arg1, arg2) { } @@ -299,15 +283,14 @@ const INVALID_CTOR_DECORATORS_FILE = { { type: 'ParamType', decorators: [ - { notType: NoTypePropertyDecorator1 }, - { type: NoTypePropertyDecorator2 }, + { notType: Inject }, + { type: Inject }, ] }, ]; }; return NoTypeProperty; }()); - var NotIdentifierDecorator = {}; var NotIdentifier = (function() { function NotIdentifier(arg1, arg2) { } @@ -316,7 +299,7 @@ const INVALID_CTOR_DECORATORS_FILE = { type: 'ParamType', decorators: [ { type: 'StringsLiteralsAreNotIdentifiers' }, - { type: NotIdentifierDecorator }, + { type: Inject }, ] }, ]; }; @@ -328,33 +311,31 @@ const INVALID_CTOR_DECORATORS_FILE = { const INVALID_CTOR_DECORATOR_ARGS_FILE = { name: '/invalid_ctor_decorator_args.js', contents: ` - var NoArgsPropertyDecorator = {}; + import { Inject } from '@angular/core'; var NoArgsProperty = (function() { function NoArgsProperty(arg1) { } NoArgsProperty.ctorParameters = function() { return [ - { type: 'ParamType', decorators: [{ type: NoArgsPropertyDecorator },] }, + { type: 'ParamType', decorators: [{ type: Inject },] }, ]; }; return NoArgsProperty; }()); - var NoPropertyAssignmentDecorator = {}; var args = [{ selector: '[ignored]' },]; var NoPropertyAssignment = (function() { function NoPropertyAssignment(arg1) { } NoPropertyAssignment.ctorParameters = function() { return [ - { type: 'ParamType', decorators: [{ type: NoPropertyAssignmentDecorator, args },] }, + { type: 'ParamType', decorators: [{ type: Inject, args },] }, ]; }; return NoPropertyAssignment; }()); - var NotArrayLiteralDecorator = {}; var NotArrayLiteral = (function() { function NotArrayLiteral(arg1) { } NotArrayLiteral.ctorParameters = function() { return [ - { type: 'ParamType', decorators: [{ type: NotArrayLiteralDecorator, args: () => [{ selector: '[ignored]' },] },] }, + { type: 'ParamType', decorators: [{ type: Inject, args: () => [{ selector: '[ignored]' },] },] }, ]; }; return NotArrayLiteral; }()); @@ -502,7 +483,7 @@ describe('Esm5ReflectionHost', () => { const decorators = host.getDecoratorsOfDeclaration(classNode) !; expect(decorators.length).toBe(1); - expect(decorators[0]).toEqual(jasmine.objectContaining({name: 'NotObjectLiteralDecorator'})); + expect(decorators[0]).toEqual(jasmine.objectContaining({name: 'Directive'})); }); it('should ignore decorator elements that have no `type` property', () => { @@ -513,7 +494,7 @@ describe('Esm5ReflectionHost', () => { const decorators = host.getDecoratorsOfDeclaration(classNode) !; expect(decorators.length).toBe(1); - expect(decorators[0]).toEqual(jasmine.objectContaining({name: 'NoTypePropertyDecorator2'})); + expect(decorators[0]).toEqual(jasmine.objectContaining({name: 'Directive'})); }); it('should ignore decorator elements whose `type` value is not an identifier', () => { @@ -524,11 +505,11 @@ describe('Esm5ReflectionHost', () => { const decorators = host.getDecoratorsOfDeclaration(classNode) !; expect(decorators.length).toBe(1); - expect(decorators[0]).toEqual(jasmine.objectContaining({name: 'NotIdentifierDecorator'})); + expect(decorators[0]).toEqual(jasmine.objectContaining({name: 'Directive'})); }); it('should use `getImportOfIdentifier()` to retrieve import info', () => { - const mockImportInfo = {} as Import; + const mockImportInfo = { name: 'mock', from: '@angular/core' } as Import; const spy = spyOn(Esm5ReflectionHost.prototype, 'getImportOfIdentifier') .and.returnValue(mockImportInfo); @@ -554,7 +535,7 @@ describe('Esm5ReflectionHost', () => { const decorators = host.getDecoratorsOfDeclaration(classNode) !; expect(decorators.length).toBe(1); - expect(decorators[0].name).toBe('NoArgsPropertyDecorator'); + expect(decorators[0].name).toBe('Directive'); expect(decorators[0].args).toEqual([]); }); @@ -567,7 +548,7 @@ describe('Esm5ReflectionHost', () => { const decorators = host.getDecoratorsOfDeclaration(classNode) !; expect(decorators.length).toBe(1); - expect(decorators[0].name).toBe('NoPropertyAssignmentDecorator'); + expect(decorators[0].name).toBe('Directive'); expect(decorators[0].args).toEqual([]); }); @@ -579,7 +560,7 @@ describe('Esm5ReflectionHost', () => { const decorators = host.getDecoratorsOfDeclaration(classNode) !; expect(decorators.length).toBe(1); - expect(decorators[0].name).toBe('NotArrayLiteralDecorator'); + expect(decorators[0].name).toBe('Directive'); expect(decorators[0].args).toEqual([]); }); }); @@ -688,9 +669,7 @@ describe('Esm5ReflectionHost', () => { const decorators = prop.decorators !; expect(decorators.length).toBe(1); - expect(decorators[0]).toEqual(jasmine.objectContaining({ - name: 'NotObjectLiteralPropDecorator' - })); + expect(decorators[0]).toEqual(jasmine.objectContaining({name: 'Input'})); }); it('should ignore prop decorator elements that have no `type` property', () => { @@ -703,7 +682,7 @@ describe('Esm5ReflectionHost', () => { const decorators = prop.decorators !; expect(decorators.length).toBe(1); - expect(decorators[0]).toEqual(jasmine.objectContaining({name: 'NoTypePropertyDecorator2'})); + expect(decorators[0]).toEqual(jasmine.objectContaining({name: 'Input'})); }); it('should ignore prop decorator elements whose `type` value is not an identifier', () => { @@ -716,14 +695,14 @@ describe('Esm5ReflectionHost', () => { const decorators = prop.decorators !; expect(decorators.length).toBe(1); - expect(decorators[0]).toEqual(jasmine.objectContaining({name: 'NotIdentifierDecorator'})); + expect(decorators[0]).toEqual(jasmine.objectContaining({name: 'Input'})); }); it('should use `getImportOfIdentifier()` to retrieve import info', () => { let callCount = 0; const spy = spyOn(Esm5ReflectionHost.prototype, 'getImportOfIdentifier').and.callFake(() => { callCount++; - return {name: `name${callCount}`, from: `from${callCount}`}; + return {name: `name${callCount}`, from: `@angular/core`}; }); const program = makeProgram(SOME_DIRECTIVE_FILE); @@ -737,7 +716,7 @@ describe('Esm5ReflectionHost', () => { const index = members.findIndex(member => member.name === 'input1'); expect(members[index].decorators !.length).toBe(1); - expect(members[index].decorators ![0].import).toEqual({name: 'name1', from: 'from1'}); + expect(members[index].decorators ![0].import).toEqual({name: 'name1', from: '@angular/core'}); }); describe('(returned prop decorators `args`)', () => { @@ -752,7 +731,7 @@ describe('Esm5ReflectionHost', () => { const decorators = prop.decorators !; expect(decorators.length).toBe(1); - expect(decorators[0].name).toBe('NoArgsPropertyDecorator'); + expect(decorators[0].name).toBe('Input'); expect(decorators[0].args).toEqual([]); }); @@ -767,7 +746,7 @@ describe('Esm5ReflectionHost', () => { const decorators = prop.decorators !; expect(decorators.length).toBe(1); - expect(decorators[0].name).toBe('NoPropertyAssignmentDecorator'); + expect(decorators[0].name).toBe('Input'); expect(decorators[0].args).toEqual([]); }); @@ -782,7 +761,7 @@ describe('Esm5ReflectionHost', () => { const decorators = prop.decorators !; expect(decorators.length).toBe(1); - expect(decorators[0].name).toBe('NotArrayLiteralDecorator'); + expect(decorators[0].name).toBe('Input'); expect(decorators[0].args).toEqual([]); }); }); @@ -887,7 +866,7 @@ describe('Esm5ReflectionHost', () => { const decorators = parameters ![0].decorators !; expect(decorators.length).toBe(1); - expect(decorators[0]).toEqual(jasmine.objectContaining({name: 'NoTypePropertyDecorator2'})); + expect(decorators[0]).toEqual(jasmine.objectContaining({name: 'Inject'})); }); it('should ignore param decorator elements whose `type` value is not an identifier', () => { @@ -899,11 +878,11 @@ describe('Esm5ReflectionHost', () => { const decorators = parameters ![0].decorators !; expect(decorators.length).toBe(1); - expect(decorators[0]).toEqual(jasmine.objectContaining({name: 'NotIdentifierDecorator'})); + expect(decorators[0]).toEqual(jasmine.objectContaining({name: 'Inject'})); }); it('should use `getImportOfIdentifier()` to retrieve import info', () => { - const mockImportInfo = {} as Import; + const mockImportInfo = { name: 'mock', from: '@angulare/core' } as Import; const spy = spyOn(Esm5ReflectionHost.prototype, 'getImportOfIdentifier') .and.returnValue(mockImportInfo); @@ -934,7 +913,7 @@ describe('Esm5ReflectionHost', () => { const decorators = parameters ![0].decorators !; expect(decorators.length).toBe(1); - expect(decorators[0].name).toBe('NoArgsPropertyDecorator'); + expect(decorators[0].name).toBe('Inject'); expect(decorators[0].args).toEqual([]); }); @@ -948,7 +927,7 @@ describe('Esm5ReflectionHost', () => { const decorators = parameters ![0].decorators !; expect(decorators.length).toBe(1); - expect(decorators[0].name).toBe('NoPropertyAssignmentDecorator'); + expect(decorators[0].name).toBe('Inject'); expect(decorators[0].args).toEqual([]); }); @@ -962,7 +941,7 @@ describe('Esm5ReflectionHost', () => { const decorators = parameters ![0].decorators !; expect(decorators.length).toBe(1); - expect(decorators[0].name).toBe('NotArrayLiteralDecorator'); + expect(decorators[0].name).toBe('Inject'); expect(decorators[0].args).toEqual([]); }); }); diff --git a/packages/compiler-cli/src/ngcc/test/host/fesm2015_host_spec.ts b/packages/compiler-cli/src/ngcc/test/host/fesm2015_host_spec.ts index 0d8d6373ad..4f3e55492e 100644 --- a/packages/compiler-cli/src/ngcc/test/host/fesm2015_host_spec.ts +++ b/packages/compiler-cli/src/ngcc/test/host/fesm2015_host_spec.ts @@ -76,36 +76,32 @@ const FOO_FUNCTION_FILE = { const INVALID_DECORATORS_FILE = { name: '/invalid_decorators.js', contents: ` - const NotArrayLiteralDecorator = {}; + import {Directive} from '@angular/core'; class NotArrayLiteral { } NotArrayLiteral.decorators = () => [ - { type: NotArrayLiteralDecorator, args: [{ selector: '[ignored]' },] }, + { type: Directive, args: [{ selector: '[ignored]' },] }, ]; - const NotObjectLiteralDecorator = {}; class NotObjectLiteral { } NotObjectLiteral.decorators = [ "This is not an object literal", - { type: NotObjectLiteralDecorator }, + { type: Directive }, ]; - const NoTypePropertyDecorator1 = {}; - const NoTypePropertyDecorator2 = {}; class NoTypeProperty { } NoTypeProperty.decorators = [ - { notType: NoTypePropertyDecorator1 }, - { type: NoTypePropertyDecorator2 }, + { notType: Directive }, + { type: Directive }, ]; - const NotIdentifierDecorator = {}; class NotIdentifier { } NotIdentifier.decorators = [ { type: 'StringsLiteralsAreNotIdentifiers' }, - { type: NotIdentifierDecorator }, + { type: Directive }, ]; `, }; @@ -113,26 +109,24 @@ const INVALID_DECORATORS_FILE = { const INVALID_DECORATOR_ARGS_FILE = { name: '/invalid_decorator_args.js', contents: ` - const NoArgsPropertyDecorator = {}; + import {Directive} from '@angular/core'; class NoArgsProperty { } NoArgsProperty.decorators = [ - { type: NoArgsPropertyDecorator }, + { type: Directive }, ]; - const NoPropertyAssignmentDecorator = {}; const args = [{ selector: '[ignored]' },]; class NoPropertyAssignment { } NoPropertyAssignment.decorators = [ - { type: NoPropertyAssignmentDecorator, args }, + { type: Directive, args }, ]; - const NotArrayLiteralDecorator = {}; class NotArrayLiteral { } NotArrayLiteral.decorators = [ - { type: NotArrayLiteralDecorator, args: () => [{ selector: '[ignored]' },] }, + { type: Directive, args: () => [{ selector: '[ignored]' },] }, ]; `, }; @@ -140,41 +134,37 @@ const INVALID_DECORATOR_ARGS_FILE = { const INVALID_PROP_DECORATORS_FILE = { name: '/invalid_prop_decorators.js', contents: ` - const NotObjectLiteralDecorator = {}; + import {Input} from '@angular/core'; class NotObjectLiteral { } NotObjectLiteral.propDecorators = () => ({ - "prop": [{ type: NotObjectLiteralDecorator },] + "prop": [{ type: Input },] }); - const NotObjectLiteralPropDecorator = {}; class NotObjectLiteralProp { } NotObjectLiteralProp.propDecorators = { "prop": [ "This is not an object literal", - { type: NotObjectLiteralPropDecorator }, + { type: Input }, ] }; - const NoTypePropertyDecorator1 = {}; - const NoTypePropertyDecorator2 = {}; class NoTypeProperty { } NoTypeProperty.propDecorators = { "prop": [ - { notType: NoTypePropertyDecorator1 }, - { type: NoTypePropertyDecorator2 }, + { notType: Input }, + { type: Input }, ] }; - const NotIdentifierDecorator = {}; class NotIdentifier { } NotIdentifier.propDecorators = { "prop": [ { type: 'StringsLiteralsAreNotIdentifiers' }, - { type: NotIdentifierDecorator }, + { type: Input }, ] }; `, @@ -183,26 +173,24 @@ const INVALID_PROP_DECORATORS_FILE = { const INVALID_PROP_DECORATOR_ARGS_FILE = { name: '/invalid_prop_decorator_args.js', contents: ` - const NoArgsPropertyDecorator = {}; + import {Input} from '@angular/core'; class NoArgsProperty { } NoArgsProperty.propDecorators = { - "prop": [{ type: NoArgsPropertyDecorator },] + "prop": [{ type: Input },] }; - const NoPropertyAssignmentDecorator = {}; const args = [{ selector: '[ignored]' },]; class NoPropertyAssignment { } NoPropertyAssignment.propDecorators = { - "prop": [{ type: NoPropertyAssignmentDecorator, args },] + "prop": [{ type: Input, args },] }; - const NotArrayLiteralDecorator = {}; class NotArrayLiteral { } NotArrayLiteral.propDecorators = { - "prop": [{ type: NotArrayLiteralDecorator, args: () => [{ selector: '[ignored]' },] },], + "prop": [{ type: Input, args: () => [{ selector: '[ignored]' },] },], }; `, }; @@ -210,40 +198,44 @@ const INVALID_PROP_DECORATOR_ARGS_FILE = { const INVALID_CTOR_DECORATORS_FILE = { name: '/invalid_ctor_decorators.js', contents: ` - const NoParametersDecorator = {}; + import {Inject} from '@angular/core'; class NoParameters { constructor() { } } - const NotArrowFunctionDecorator = {}; + const NotFromCoreDecorator = {}; + class NotFromCore { + constructor(arg1) { + } + } + NotFromCore.ctorParameters = () => [ + { type: 'ParamType', decorators: [{ type: NotFromCoreDecorator },] }, + ] + class NotArrowFunction { constructor(arg1) { } } NotArrowFunction.ctorParameters = function() { - return { type: 'ParamType', decorators: [{ type: NotArrowFunctionDecorator },] }; + return { type: 'ParamType', decorators: [{ type: Inject },] }; }; - const NotArrayLiteralDecorator = {}; class NotArrayLiteral { constructor(arg1) { } } NotArrayLiteral.ctorParameters = () => 'StringsAreNotArrayLiterals'; - const NotObjectLiteralDecorator = {}; class NotObjectLiteral { constructor(arg1, arg2) { } } NotObjectLiteral.ctorParameters = () => [ "This is not an object literal", - { type: 'ParamType', decorators: [{ type: NotObjectLiteralDecorator },] }, + { type: 'ParamType', decorators: [{ type: Inject },] }, ]; - const NoTypePropertyDecorator1 = {}; - const NoTypePropertyDecorator2 = {}; class NoTypeProperty { constructor(arg1, arg2) { } @@ -252,13 +244,12 @@ const INVALID_CTOR_DECORATORS_FILE = { { type: 'ParamType', decorators: [ - { notType: NoTypePropertyDecorator1 }, - { type: NoTypePropertyDecorator2 }, + { notType: Inject }, + { type: Inject }, ] }, ]; - const NotIdentifierDecorator = {}; class NotIdentifier { constructor(arg1, arg2) { } @@ -268,7 +259,7 @@ const INVALID_CTOR_DECORATORS_FILE = { type: 'ParamType', decorators: [ { type: 'StringsLiteralsAreNotIdentifiers' }, - { type: NotIdentifierDecorator }, + { type: Inject }, ] }, ]; @@ -278,32 +269,30 @@ const INVALID_CTOR_DECORATORS_FILE = { const INVALID_CTOR_DECORATOR_ARGS_FILE = { name: '/invalid_ctor_decorator_args.js', contents: ` - const NoArgsPropertyDecorator = {}; + import {Inject} from '@angular/core'; class NoArgsProperty { constructor(arg1) { } } NoArgsProperty.ctorParameters = () => [ - { type: 'ParamType', decorators: [{ type: NoArgsPropertyDecorator },] }, + { type: 'ParamType', decorators: [{ type: Inject },] }, ]; - const NoPropertyAssignmentDecorator = {}; const args = [{ selector: '[ignored]' },]; class NoPropertyAssignment { constructor(arg1) { } } NoPropertyAssignment.ctorParameters = () => [ - { type: 'ParamType', decorators: [{ type: NoPropertyAssignmentDecorator, args },] }, + { type: 'ParamType', decorators: [{ type: Inject, args },] }, ]; - const NotArrayLiteralDecorator = {}; class NotArrayLiteral { constructor(arg1) { } } NotArrayLiteral.ctorParameters = () => [ - { type: 'ParamType', decorators: [{ type: NotArrayLiteralDecorator, args: () => [{ selector: '[ignored]' },] },] }, + { type: 'ParamType', decorators: [{ type: Inject, args: () => [{ selector: '[ignored]' },] },] }, ]; `, }; @@ -459,7 +448,7 @@ describe('Fesm2015ReflectionHost', () => { const decorators = host.getDecoratorsOfDeclaration(classNode) !; expect(decorators.length).toBe(1); - expect(decorators[0]).toEqual(jasmine.objectContaining({name: 'NotObjectLiteralDecorator'})); + expect(decorators[0]).toEqual(jasmine.objectContaining({name: 'Directive'})); }); it('should ignore decorator elements that have no `type` property', () => { @@ -470,7 +459,7 @@ describe('Fesm2015ReflectionHost', () => { const decorators = host.getDecoratorsOfDeclaration(classNode) !; expect(decorators.length).toBe(1); - expect(decorators[0]).toEqual(jasmine.objectContaining({name: 'NoTypePropertyDecorator2'})); + expect(decorators[0]).toEqual(jasmine.objectContaining({name: 'Directive'})); }); it('should ignore decorator elements whose `type` value is not an identifier', () => { @@ -481,11 +470,11 @@ describe('Fesm2015ReflectionHost', () => { const decorators = host.getDecoratorsOfDeclaration(classNode) !; expect(decorators.length).toBe(1); - expect(decorators[0]).toEqual(jasmine.objectContaining({name: 'NotIdentifierDecorator'})); + expect(decorators[0]).toEqual(jasmine.objectContaining({name: 'Directive'})); }); it('should use `getImportOfIdentifier()` to retrieve import info', () => { - const mockImportInfo = {} as Import; + const mockImportInfo = { from: '@angular/core' } as Import; const spy = spyOn(Fesm2015ReflectionHost.prototype, 'getImportOfIdentifier') .and.returnValue(mockImportInfo); @@ -511,7 +500,7 @@ describe('Fesm2015ReflectionHost', () => { const decorators = host.getDecoratorsOfDeclaration(classNode) !; expect(decorators.length).toBe(1); - expect(decorators[0].name).toBe('NoArgsPropertyDecorator'); + expect(decorators[0].name).toBe('Directive'); expect(decorators[0].args).toEqual([]); }); @@ -524,7 +513,7 @@ describe('Fesm2015ReflectionHost', () => { const decorators = host.getDecoratorsOfDeclaration(classNode) !; expect(decorators.length).toBe(1); - expect(decorators[0].name).toBe('NoPropertyAssignmentDecorator'); + expect(decorators[0].name).toBe('Directive'); expect(decorators[0].args).toEqual([]); }); @@ -536,7 +525,7 @@ describe('Fesm2015ReflectionHost', () => { const decorators = host.getDecoratorsOfDeclaration(classNode) !; expect(decorators.length).toBe(1); - expect(decorators[0].name).toBe('NotArrayLiteralDecorator'); + expect(decorators[0].name).toBe('Directive'); expect(decorators[0].args).toEqual([]); }); }); @@ -644,9 +633,7 @@ describe('Fesm2015ReflectionHost', () => { const decorators = prop.decorators !; expect(decorators.length).toBe(1); - expect(decorators[0]).toEqual(jasmine.objectContaining({ - name: 'NotObjectLiteralPropDecorator' - })); + expect(decorators[0]).toEqual(jasmine.objectContaining({name: 'Input'})); }); it('should ignore prop decorator elements that have no `type` property', () => { @@ -659,7 +646,7 @@ describe('Fesm2015ReflectionHost', () => { const decorators = prop.decorators !; expect(decorators.length).toBe(1); - expect(decorators[0]).toEqual(jasmine.objectContaining({name: 'NoTypePropertyDecorator2'})); + expect(decorators[0]).toEqual(jasmine.objectContaining({name: 'Input'})); }); it('should ignore prop decorator elements whose `type` value is not an identifier', () => { @@ -672,7 +659,7 @@ describe('Fesm2015ReflectionHost', () => { const decorators = prop.decorators !; expect(decorators.length).toBe(1); - expect(decorators[0]).toEqual(jasmine.objectContaining({name: 'NotIdentifierDecorator'})); + expect(decorators[0]).toEqual(jasmine.objectContaining({name: 'Input'})); }); it('should use `getImportOfIdentifier()` to retrieve import info', () => { @@ -680,7 +667,7 @@ describe('Fesm2015ReflectionHost', () => { const spy = spyOn(Fesm2015ReflectionHost.prototype, 'getImportOfIdentifier').and.callFake(() => { callCount++; - return {name: `name${callCount}`, from: `from${callCount}`}; + return {name: `name${callCount}`, from: '@angular/core'}; }); const program = makeProgram(SOME_DIRECTIVE_FILE); @@ -698,9 +685,9 @@ describe('Fesm2015ReflectionHost', () => { 'HostListener', ]); - const index = members.findIndex(member => member.name === 'input1'); - expect(members[index].decorators !.length).toBe(1); - expect(members[index].decorators ![0].import).toEqual({name: 'name1', from: 'from1'}); + const member = members.find(member => member.name === 'input1') !; + expect(member.decorators !.length).toBe(1); + expect(member.decorators ![0].import).toEqual({name: 'name1', from: '@angular/core'}); }); describe('(returned prop decorators `args`)', () => { @@ -715,7 +702,7 @@ describe('Fesm2015ReflectionHost', () => { const decorators = prop.decorators !; expect(decorators.length).toBe(1); - expect(decorators[0].name).toBe('NoArgsPropertyDecorator'); + expect(decorators[0].name).toBe('Input'); expect(decorators[0].args).toEqual([]); }); @@ -730,7 +717,7 @@ describe('Fesm2015ReflectionHost', () => { const decorators = prop.decorators !; expect(decorators.length).toBe(1); - expect(decorators[0].name).toBe('NoPropertyAssignmentDecorator'); + expect(decorators[0].name).toBe('Input'); expect(decorators[0].args).toEqual([]); }); @@ -745,7 +732,7 @@ describe('Fesm2015ReflectionHost', () => { const decorators = prop.decorators !; expect(decorators.length).toBe(1); - expect(decorators[0].name).toBe('NotArrayLiteralDecorator'); + expect(decorators[0].name).toBe('Input'); expect(decorators[0].args).toEqual([]); }); }); @@ -757,13 +744,13 @@ describe('Fesm2015ReflectionHost', () => { const host = new Fesm2015ReflectionHost(program.getTypeChecker()); const classNode = getDeclaration(program, SOME_DIRECTIVE_FILE.name, 'SomeDirective', ts.isClassDeclaration); - const parameters = host.getConstructorParameters(classNode); + const parameters = host.getConstructorParameters(classNode) !; expect(parameters).toBeDefined(); - expect(parameters !.map(parameter => parameter.name)).toEqual([ + expect(parameters.map(parameter => parameter.name)).toEqual([ '_viewContainer', '_template', 'injected' ]); - expect(parameters !.map(parameter => parameter.type !.getText())).toEqual([ + expect(parameters.map(parameter => parameter.type !.getText())).toEqual([ 'ViewContainerRef', 'TemplateRef', 'undefined' ]); }); @@ -792,12 +779,12 @@ describe('Fesm2015ReflectionHost', () => { const host = new Fesm2015ReflectionHost(program.getTypeChecker()); const classNode = getDeclaration( program, SIMPLE_CLASS_FILE.name, 'NoDecoratorConstructorClass', ts.isClassDeclaration); - const parameters = host.getConstructorParameters(classNode); + const parameters = host.getConstructorParameters(classNode) !; expect(parameters).toEqual(jasmine.any(Array)); - expect(parameters !.length).toEqual(1); - expect(parameters ![0].name).toEqual('foo'); - expect(parameters ![0].decorators).toBe(null); + expect(parameters.length).toEqual(1); + expect(parameters[0].name).toEqual('foo'); + expect(parameters[0].decorators).toBe(null); }); it('should return an empty array if there are no constructor parameters', () => { @@ -810,15 +797,29 @@ describe('Fesm2015ReflectionHost', () => { expect(parameters).toEqual([]); }); + it('should ignore decorators that are not imported from core', () => { + const program = makeProgram(INVALID_CTOR_DECORATORS_FILE); + const host = new Fesm2015ReflectionHost(program.getTypeChecker()); + const classNode = getDeclaration( + program, INVALID_CTOR_DECORATORS_FILE.name, 'NotFromCore', ts.isClassDeclaration); + const parameters = host.getConstructorParameters(classNode) !; + + expect(parameters.length).toBe(1); + expect(parameters[0]).toEqual(jasmine.objectContaining({ + name: 'arg1', + decorators: [], + })); + }); + it('should ignore `ctorParameters` if it is not an arrow function', () => { const program = makeProgram(INVALID_CTOR_DECORATORS_FILE); const host = new Fesm2015ReflectionHost(program.getTypeChecker()); const classNode = getDeclaration( program, INVALID_CTOR_DECORATORS_FILE.name, 'NotArrowFunction', ts.isClassDeclaration); - const parameters = host.getConstructorParameters(classNode); + const parameters = host.getConstructorParameters(classNode) !; - expect(parameters !.length).toBe(1); - expect(parameters ![0]).toEqual(jasmine.objectContaining({ + expect(parameters.length).toBe(1); + expect(parameters[0]).toEqual(jasmine.objectContaining({ name: 'arg1', decorators: null, })); @@ -829,10 +830,10 @@ describe('Fesm2015ReflectionHost', () => { const host = new Fesm2015ReflectionHost(program.getTypeChecker()); const classNode = getDeclaration( program, INVALID_CTOR_DECORATORS_FILE.name, 'NotArrayLiteral', ts.isClassDeclaration); - const parameters = host.getConstructorParameters(classNode); + const parameters = host.getConstructorParameters(classNode) !; - expect(parameters !.length).toBe(1); - expect(parameters ![0]).toEqual(jasmine.objectContaining({ + expect(parameters.length).toBe(1); + expect(parameters[0]).toEqual(jasmine.objectContaining({ name: 'arg1', decorators: null, })); @@ -866,7 +867,7 @@ describe('Fesm2015ReflectionHost', () => { const decorators = parameters ![0].decorators !; expect(decorators.length).toBe(1); - expect(decorators[0]).toEqual(jasmine.objectContaining({name: 'NoTypePropertyDecorator2'})); + expect(decorators[0]).toEqual(jasmine.objectContaining({name: 'Inject'})); }); it('should ignore param decorator elements whose `type` value is not an identifier', () => { @@ -878,11 +879,11 @@ describe('Fesm2015ReflectionHost', () => { const decorators = parameters ![0].decorators !; expect(decorators.length).toBe(1); - expect(decorators[0]).toEqual(jasmine.objectContaining({name: 'NotIdentifierDecorator'})); + expect(decorators[0]).toEqual(jasmine.objectContaining({name: 'Inject'})); }); it('should use `getImportOfIdentifier()` to retrieve import info', () => { - const mockImportInfo = {} as Import; + const mockImportInfo: Import = {name: 'mock', from: '@angular/core'}; const spy = spyOn(Fesm2015ReflectionHost.prototype, 'getImportOfIdentifier') .and.returnValue(mockImportInfo); @@ -890,8 +891,8 @@ describe('Fesm2015ReflectionHost', () => { const host = new Fesm2015ReflectionHost(program.getTypeChecker()); const classNode = getDeclaration( program, SOME_DIRECTIVE_FILE.name, 'SomeDirective', ts.isClassDeclaration); - const parameters = host.getConstructorParameters(classNode); - const decorators = parameters ![2].decorators !; + const parameters = host.getConstructorParameters(classNode) !; + const decorators = parameters[2].decorators !; expect(decorators.length).toEqual(1); expect(decorators[0].import).toBe(mockImportInfo); @@ -913,7 +914,7 @@ describe('Fesm2015ReflectionHost', () => { const decorators = parameters ![0].decorators !; expect(decorators.length).toBe(1); - expect(decorators[0].name).toBe('NoArgsPropertyDecorator'); + expect(decorators[0].name).toBe('Inject'); expect(decorators[0].args).toEqual([]); }); @@ -927,7 +928,7 @@ describe('Fesm2015ReflectionHost', () => { const decorators = parameters ![0].decorators !; expect(decorators.length).toBe(1); - expect(decorators[0].name).toBe('NoPropertyAssignmentDecorator'); + expect(decorators[0].name).toBe('Inject'); expect(decorators[0].args).toEqual([]); }); @@ -941,7 +942,7 @@ describe('Fesm2015ReflectionHost', () => { const decorators = parameters ![0].decorators !; expect(decorators.length).toBe(1); - expect(decorators[0].name).toBe('NotArrayLiteralDecorator'); + expect(decorators[0].name).toBe('Inject'); expect(decorators[0].args).toEqual([]); }); }); diff --git a/packages/compiler-cli/src/ngcc/test/parsing/esm5_parser_spec.ts b/packages/compiler-cli/src/ngcc/test/parsing/esm5_parser_spec.ts index f69ce36a78..1fad910ea3 100644 --- a/packages/compiler-cli/src/ngcc/test/parsing/esm5_parser_spec.ts +++ b/packages/compiler-cli/src/ngcc/test/parsing/esm5_parser_spec.ts @@ -15,6 +15,7 @@ import {makeProgram} from '../helpers/utils'; const BASIC_FILE = { name: '/primary.js', contents: ` + import {Directive} from '@angular/core'; var A = (function() { function A() {} A.decorators = [ diff --git a/packages/compiler-cli/src/ngcc/test/rendering/esm2015_renderer_spec.ts b/packages/compiler-cli/src/ngcc/test/rendering/esm2015_renderer_spec.ts index b66c4eaae3..32c1366de7 100644 --- a/packages/compiler-cli/src/ngcc/test/rendering/esm2015_renderer_spec.ts +++ b/packages/compiler-cli/src/ngcc/test/rendering/esm2015_renderer_spec.ts @@ -200,7 +200,7 @@ A.decorators = [ const analyzedFile = analyze(parser, analyzer, program.getSourceFile(PROGRAM.name) !); const output = new MagicString(PROGRAM.contents); const analyzedClass = analyzedFile.analyzedClasses[1]; - const decorator = analyzedClass.decorators[1]; + const decorator = analyzedClass.decorators[0]; const decoratorsToRemove = new Map(); decoratorsToRemove.set(decorator.node.parent !, [decorator.node]); renderer.removeDecorators(output, decoratorsToRemove); diff --git a/packages/compiler-cli/src/ngcc/test/rendering/esm5_renderer_spec.ts b/packages/compiler-cli/src/ngcc/test/rendering/esm5_renderer_spec.ts index bbc5841149..79097895ce 100644 --- a/packages/compiler-cli/src/ngcc/test/rendering/esm5_renderer_spec.ts +++ b/packages/compiler-cli/src/ngcc/test/rendering/esm5_renderer_spec.ts @@ -223,7 +223,7 @@ SOME DEFINITION TEXT const analyzedFile = analyze(parser, analyzer, program.getSourceFile(PROGRAM.name) !); const output = new MagicString(PROGRAM.contents); const analyzedClass = analyzedFile.analyzedClasses[1]; - const decorator = analyzedClass.decorators[1]; + const decorator = analyzedClass.decorators[0]; const decoratorsToRemove = new Map(); decoratorsToRemove.set(decorator.node.parent !, [decorator.node]); renderer.removeDecorators(output, decoratorsToRemove);