diff --git a/integration/ngcc/test.sh b/integration/ngcc/test.sh index 79e78d3ce6..3f31155677 100755 --- a/integration/ngcc/test.sh +++ b/integration/ngcc/test.sh @@ -67,8 +67,15 @@ if [[ $? != 0 ]]; then exit 1; fi grep "_MatMenuBase.ngBaseDef = ɵngcc0.ɵɵdefineBase({ inputs: {" node_modules/@angular/material/esm5/menu.es5.js if [[ $? != 0 ]]; then exit 1; fi -# Did it handle namespace imported decorators in UMD? +# Did it handle namespace imported decorators in UMD using `__decorate` syntax? +grep "type: core.Injectable" node_modules/@angular/common/bundles/common.umd.js +# (and ensure the @angular/common package is indeed using `__decorate` syntax) +grep "JsonPipe = __decorate(" node_modules/@angular/common/bundles/common.umd.js.__ivy_ngcc_bak + +# Did it handle namespace imported decorators in UMD using static properties? grep "type: core.Injectable," node_modules/@angular/cdk/bundles/cdk-a11y.umd.js +# (and ensure the @angular/cdk/a11y package is indeed using static properties) +grep "FocusMonitor.decorators =" node_modules/@angular/cdk/bundles/cdk-a11y.umd.js.__ivy_ngcc_bak # Can it be safely run again (as a noop)? # And check that it logged skipping compilation as expected diff --git a/packages/compiler-cli/ngcc/src/host/esm2015_host.ts b/packages/compiler-cli/ngcc/src/host/esm2015_host.ts index 24de49a39c..8a26ddb23d 100644 --- a/packages/compiler-cli/ngcc/src/host/esm2015_host.ts +++ b/packages/compiler-cli/ngcc/src/host/esm2015_host.ts @@ -957,7 +957,7 @@ export class Esm2015ReflectionHost extends TypeScriptReflectionHost implements N return { name: decoratorIdentifier.text, - identifier: decoratorIdentifier, + identifier: decoratorExpression, import: this.getImportOfIdentifier(decoratorIdentifier), node: call, args: Array.from(call.arguments), diff --git a/packages/compiler-cli/ngcc/test/host/commonjs_host_import_helper_spec.ts b/packages/compiler-cli/ngcc/test/host/commonjs_host_import_helper_spec.ts index fe98c5f576..8163a75311 100644 --- a/packages/compiler-cli/ngcc/test/host/commonjs_host_import_helper_spec.ts +++ b/packages/compiler-cli/ngcc/test/host/commonjs_host_import_helper_spec.ts @@ -86,6 +86,7 @@ exports.OtherDirective = OtherDirective; const decorator = decorators[0]; expect(decorator.name).toEqual('Directive'); + expect(decorator.identifier.getText()).toEqual('core.Directive'); expect(decorator.import).toEqual({name: 'Directive', from: '@angular/core'}); expect(decorator.args !.map(arg => arg.getText())).toEqual([ '{ selector: \'[someDirective]\' }', diff --git a/packages/compiler-cli/ngcc/test/host/esm2015_host_import_helper_spec.ts b/packages/compiler-cli/ngcc/test/host/esm2015_host_import_helper_spec.ts index ab46d1b66c..230445bb2c 100644 --- a/packages/compiler-cli/ngcc/test/host/esm2015_host_import_helper_spec.ts +++ b/packages/compiler-cli/ngcc/test/host/esm2015_host_import_helper_spec.ts @@ -145,6 +145,7 @@ runInEachFileSystem(() => { const decorator = decorators[0]; expect(decorator.name).toEqual('Directive'); + expect(decorator.identifier.getText()).toEqual('Directive'); expect(decorator.import).toEqual({name: 'Directive', from: '@angular/core'}); expect(decorator.args !.map(arg => arg.getText())).toEqual([ '{ selector: \'[someDirective]\' }', @@ -166,6 +167,7 @@ runInEachFileSystem(() => { const decorator = decorators[0]; expect(decorator.name).toEqual('Directive'); + expect(decorator.identifier.getText()).toEqual('Directive'); expect(decorator.import).toEqual({name: 'Directive', from: './directives'}); expect(decorator.args !.map(arg => arg.getText())).toEqual([ '{ selector: \'[someDirective]\' }', diff --git a/packages/compiler-cli/ngcc/test/host/esm5_host_import_helper_spec.ts b/packages/compiler-cli/ngcc/test/host/esm5_host_import_helper_spec.ts index 10a16f88c3..900a46c5cb 100644 --- a/packages/compiler-cli/ngcc/test/host/esm5_host_import_helper_spec.ts +++ b/packages/compiler-cli/ngcc/test/host/esm5_host_import_helper_spec.ts @@ -165,6 +165,7 @@ export { SomeDirective }; const decorator = decorators[0]; expect(decorator.name).toEqual('Directive'); + expect(decorator.identifier.getText()).toEqual('Directive'); expect(decorator.import).toEqual({name: 'Directive', from: '@angular/core'}); expect(decorator.args !.map(arg => arg.getText())).toEqual([ '{ selector: \'[someDirective]\' }', @@ -185,6 +186,7 @@ export { SomeDirective }; const decorator = decorators[0]; expect(decorator.name).toEqual('Directive'); + expect(decorator.identifier.getText()).toEqual('Directive'); expect(decorator.import).toEqual({name: 'Directive', from: './directives'}); expect(decorator.args !.map(arg => arg.getText())).toEqual([ '{ selector: \'[someDirective]\' }', diff --git a/packages/compiler-cli/ngcc/test/host/umd_host_import_helper_spec.ts b/packages/compiler-cli/ngcc/test/host/umd_host_import_helper_spec.ts new file mode 100644 index 0000000000..0896665b0c --- /dev/null +++ b/packages/compiler-cli/ngcc/test/host/umd_host_import_helper_spec.ts @@ -0,0 +1,132 @@ +/** + * @license + * Copyright Google Inc. All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + +import {absoluteFrom} from '../../../src/ngtsc/file_system'; +import {TestFile, runInEachFileSystem} from '../../../src/ngtsc/file_system/testing'; +import {ClassMemberKind, isNamedVariableDeclaration} from '../../../src/ngtsc/reflection'; +import {getDeclaration} from '../../../src/ngtsc/testing'; +import {loadTestFiles} from '../../../test/helpers'; +import {UmdReflectionHost} from '../../src/host/umd_host'; +import {MockLogger} from '../helpers/mock_logger'; +import {makeTestBundleProgram} from '../helpers/utils'; + +import {expectTypeValueReferencesForParameters} from './util'; + +runInEachFileSystem(() => { + describe('UmdReflectionHost [import helper style]', () => { + let _: typeof absoluteFrom; + + let SOME_DIRECTIVE_FILE: TestFile; + + beforeEach(() => { + _ = absoluteFrom; + + SOME_DIRECTIVE_FILE = { + name: _('/some_directive.umd.js'), + contents: ` +(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('@angular/core')) : + typeof define === 'function' && define.amd ? define('some_directive', ['exports', '@angular/core'], factory) : + (factory(global.some_directive,global.ng.core)); +}(this, (function (exports,core) { 'use strict'; + + var __decorate = null; + var __metadata = null; + var __param = null; + + var INJECTED_TOKEN = new InjectionToken('injected'); + var ViewContainerRef = {}; + var TemplateRef = {}; + + var SomeDirective = (function() { + function SomeDirective(_viewContainer, _template, injected) {} + __decorate([ + core.Input(), + __metadata("design:type", String) + ], SomeDirective.prototype, "input1", void 0); + __decorate([ + core.Input(), + __metadata("design:type", Number) + ], SomeDirective.prototype, "input2", void 0); + SomeDirective = __decorate([ + core.Directive({ selector: '[someDirective]' }), + __param(2, core.Inject(INJECTED_TOKEN)), + __metadata("design:paramtypes", [ViewContainerRef, TemplateRef, String]) + ], SomeDirective); + return SomeDirective; + }()); + exports.SomeDirective = SomeDirective; +})));`, + }; + }); + + describe('getDecoratorsOfDeclaration()', () => { + it('should find the decorators on a class', () => { + loadTestFiles([SOME_DIRECTIVE_FILE]); + const {program, host: compilerHost} = makeTestBundleProgram(SOME_DIRECTIVE_FILE.name); + const host = new UmdReflectionHost(new MockLogger(), false, program, compilerHost); + const classNode = getDeclaration( + program, SOME_DIRECTIVE_FILE.name, 'SomeDirective', 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.identifier.getText()).toEqual('core.Directive'); + expect(decorator.import).toEqual({name: 'Directive', from: '@angular/core'}); + expect(decorator.args !.map(arg => arg.getText())).toEqual([ + '{ selector: \'[someDirective]\' }', + ]); + }); + }); + + describe('getMembersOfClass()', () => { + it('should find decorated members on a class', () => { + loadTestFiles([SOME_DIRECTIVE_FILE]); + const {program, host: compilerHost} = makeTestBundleProgram(SOME_DIRECTIVE_FILE.name); + const host = new UmdReflectionHost(new MockLogger(), false, program, compilerHost); + const classNode = getDeclaration( + program, SOME_DIRECTIVE_FILE.name, 'SomeDirective', isNamedVariableDeclaration); + const members = host.getMembersOfClass(classNode); + + const input1 = members.find(member => member.name === 'input1') !; + expect(input1.kind).toEqual(ClassMemberKind.Property); + expect(input1.isStatic).toEqual(false); + expect(input1.decorators !.map(d => d.name)).toEqual(['Input']); + + const input2 = members.find(member => member.name === 'input2') !; + expect(input2.kind).toEqual(ClassMemberKind.Property); + expect(input2.isStatic).toEqual(false); + expect(input1.decorators !.map(d => d.name)).toEqual(['Input']); + }); + + describe('getConstructorParameters', () => { + it('should find the decorated constructor parameters', () => { + loadTestFiles([SOME_DIRECTIVE_FILE]); + const {program, host: compilerHost} = makeTestBundleProgram(SOME_DIRECTIVE_FILE.name); + const host = new UmdReflectionHost(new MockLogger(), false, program, compilerHost); + const classNode = getDeclaration( + program, SOME_DIRECTIVE_FILE.name, 'SomeDirective', isNamedVariableDeclaration); + const parameters = host.getConstructorParameters(classNode); + + expect(parameters).toBeDefined(); + expect(parameters !.map(parameter => parameter.name)).toEqual([ + '_viewContainer', '_template', 'injected' + ]); + expectTypeValueReferencesForParameters(parameters !, [ + 'ViewContainerRef', + 'TemplateRef', + null, + ]); + }); + }); + }); + }); +});