fix(ivy): ngcc - render decorators in UMD and CommonJS bundles correctly (#31614)
In #31426 a fix was implemented to render namespaced decorator imports correctly, however it turns out that the fix only worked when decorator information was extracted from static properties, not when using `__decorate` calls. This commit fixes the issue by creating the decorator metadata with the full decorator expression, instead of only its name. Closes #31394 PR Close #31614
This commit is contained in:
parent
80f290e301
commit
fc6f48185c
|
@ -67,8 +67,15 @@ if [[ $? != 0 ]]; then exit 1; fi
|
||||||
grep "_MatMenuBase.ngBaseDef = ɵngcc0.ɵɵdefineBase({ inputs: {" node_modules/@angular/material/esm5/menu.es5.js
|
grep "_MatMenuBase.ngBaseDef = ɵngcc0.ɵɵdefineBase({ inputs: {" node_modules/@angular/material/esm5/menu.es5.js
|
||||||
if [[ $? != 0 ]]; then exit 1; fi
|
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
|
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)?
|
# Can it be safely run again (as a noop)?
|
||||||
# And check that it logged skipping compilation as expected
|
# And check that it logged skipping compilation as expected
|
||||||
|
|
|
@ -957,7 +957,7 @@ export class Esm2015ReflectionHost extends TypeScriptReflectionHost implements N
|
||||||
|
|
||||||
return {
|
return {
|
||||||
name: decoratorIdentifier.text,
|
name: decoratorIdentifier.text,
|
||||||
identifier: decoratorIdentifier,
|
identifier: decoratorExpression,
|
||||||
import: this.getImportOfIdentifier(decoratorIdentifier),
|
import: this.getImportOfIdentifier(decoratorIdentifier),
|
||||||
node: call,
|
node: call,
|
||||||
args: Array.from(call.arguments),
|
args: Array.from(call.arguments),
|
||||||
|
|
|
@ -86,6 +86,7 @@ exports.OtherDirective = OtherDirective;
|
||||||
|
|
||||||
const decorator = decorators[0];
|
const decorator = decorators[0];
|
||||||
expect(decorator.name).toEqual('Directive');
|
expect(decorator.name).toEqual('Directive');
|
||||||
|
expect(decorator.identifier.getText()).toEqual('core.Directive');
|
||||||
expect(decorator.import).toEqual({name: 'Directive', from: '@angular/core'});
|
expect(decorator.import).toEqual({name: 'Directive', from: '@angular/core'});
|
||||||
expect(decorator.args !.map(arg => arg.getText())).toEqual([
|
expect(decorator.args !.map(arg => arg.getText())).toEqual([
|
||||||
'{ selector: \'[someDirective]\' }',
|
'{ selector: \'[someDirective]\' }',
|
||||||
|
|
|
@ -145,6 +145,7 @@ runInEachFileSystem(() => {
|
||||||
|
|
||||||
const decorator = decorators[0];
|
const decorator = decorators[0];
|
||||||
expect(decorator.name).toEqual('Directive');
|
expect(decorator.name).toEqual('Directive');
|
||||||
|
expect(decorator.identifier.getText()).toEqual('Directive');
|
||||||
expect(decorator.import).toEqual({name: 'Directive', from: '@angular/core'});
|
expect(decorator.import).toEqual({name: 'Directive', from: '@angular/core'});
|
||||||
expect(decorator.args !.map(arg => arg.getText())).toEqual([
|
expect(decorator.args !.map(arg => arg.getText())).toEqual([
|
||||||
'{ selector: \'[someDirective]\' }',
|
'{ selector: \'[someDirective]\' }',
|
||||||
|
@ -166,6 +167,7 @@ runInEachFileSystem(() => {
|
||||||
|
|
||||||
const decorator = decorators[0];
|
const decorator = decorators[0];
|
||||||
expect(decorator.name).toEqual('Directive');
|
expect(decorator.name).toEqual('Directive');
|
||||||
|
expect(decorator.identifier.getText()).toEqual('Directive');
|
||||||
expect(decorator.import).toEqual({name: 'Directive', from: './directives'});
|
expect(decorator.import).toEqual({name: 'Directive', from: './directives'});
|
||||||
expect(decorator.args !.map(arg => arg.getText())).toEqual([
|
expect(decorator.args !.map(arg => arg.getText())).toEqual([
|
||||||
'{ selector: \'[someDirective]\' }',
|
'{ selector: \'[someDirective]\' }',
|
||||||
|
|
|
@ -165,6 +165,7 @@ export { SomeDirective };
|
||||||
|
|
||||||
const decorator = decorators[0];
|
const decorator = decorators[0];
|
||||||
expect(decorator.name).toEqual('Directive');
|
expect(decorator.name).toEqual('Directive');
|
||||||
|
expect(decorator.identifier.getText()).toEqual('Directive');
|
||||||
expect(decorator.import).toEqual({name: 'Directive', from: '@angular/core'});
|
expect(decorator.import).toEqual({name: 'Directive', from: '@angular/core'});
|
||||||
expect(decorator.args !.map(arg => arg.getText())).toEqual([
|
expect(decorator.args !.map(arg => arg.getText())).toEqual([
|
||||||
'{ selector: \'[someDirective]\' }',
|
'{ selector: \'[someDirective]\' }',
|
||||||
|
@ -185,6 +186,7 @@ export { SomeDirective };
|
||||||
|
|
||||||
const decorator = decorators[0];
|
const decorator = decorators[0];
|
||||||
expect(decorator.name).toEqual('Directive');
|
expect(decorator.name).toEqual('Directive');
|
||||||
|
expect(decorator.identifier.getText()).toEqual('Directive');
|
||||||
expect(decorator.import).toEqual({name: 'Directive', from: './directives'});
|
expect(decorator.import).toEqual({name: 'Directive', from: './directives'});
|
||||||
expect(decorator.args !.map(arg => arg.getText())).toEqual([
|
expect(decorator.args !.map(arg => arg.getText())).toEqual([
|
||||||
'{ selector: \'[someDirective]\' }',
|
'{ selector: \'[someDirective]\' }',
|
||||||
|
|
|
@ -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,
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
Loading…
Reference in New Issue