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
|
||||
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
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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]\' }',
|
||||
|
|
|
@ -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]\' }',
|
||||
|
|
|
@ -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]\' }',
|
||||
|
|
|
@ -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