In Ivy, a pure call to `setClassMetadata` is inserted to retain the information that would otherwise be lost while eliding the Angular decorators. In the past, the Angular constructor decorators were wrapped inside of an anonymous function which was only evaluated once `ReflectionCapabilities` was requested for such metadata. This approach prevents forward references from inside the constructor parameter decorators from being evaluated before they are available. In the `setClassMetadata` call, the constructor parameters were not wrapped within an anonymous function, such that forward references were evaluated too early, causing runtime errors. This commit changes the `setClassMetadata` call to pass the constructor parameter decorators inside of an anonymous function again, such that forward references are not resolved until requested by `ReflectionCapabilities`, therefore avoiding the early reads of forward refs. PR Close #27561
67 lines
2.2 KiB
TypeScript
67 lines
2.2 KiB
TypeScript
/**
|
|
* @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 {setClassMetadata} from '../../src/render3/metadata';
|
|
import {Type} from '../../src/type';
|
|
|
|
interface Decorator {
|
|
type: any;
|
|
args?: any[];
|
|
}
|
|
|
|
interface HasMetadata extends Type<any> {
|
|
decorators?: Decorator[];
|
|
ctorParameters: () => CtorParameter[];
|
|
propDecorators: {[field: string]: Decorator[]};
|
|
}
|
|
|
|
interface CtorParameter {
|
|
type: any;
|
|
decorators?: Decorator[];
|
|
}
|
|
|
|
function metadataOf(value: Type<any>): HasMetadata {
|
|
return value as HasMetadata;
|
|
}
|
|
|
|
describe('render3 setClassMetadata()', () => {
|
|
it('should set decorator metadata on a type', () => {
|
|
const Foo = metadataOf(class Foo{});
|
|
setClassMetadata(Foo, [{type: 'test', args: ['arg']}], null, null);
|
|
expect(Foo.decorators).toEqual([{type: 'test', args: ['arg']}]);
|
|
});
|
|
|
|
it('should merge decorator metadata on a type', () => {
|
|
const Foo = metadataOf(class Foo{});
|
|
Foo.decorators = [{type: 'first'}];
|
|
setClassMetadata(Foo, [{type: 'test', args: ['arg']}], null, null);
|
|
expect(Foo.decorators).toEqual([{type: 'first'}, {type: 'test', args: ['arg']}]);
|
|
});
|
|
|
|
it('should set ctor parameter metadata on a type', () => {
|
|
const Foo = metadataOf(class Foo{});
|
|
Foo.ctorParameters = () => [{type: 'initial'}];
|
|
setClassMetadata(Foo, null, () => [{type: 'test'}], null);
|
|
expect(Foo.ctorParameters()).toEqual([{type: 'test'}]);
|
|
});
|
|
|
|
it('should set parameter decorator metadata on a type', () => {
|
|
const Foo = metadataOf(class Foo{});
|
|
setClassMetadata(Foo, null, null, {field: [{type: 'test', args: ['arg']}]});
|
|
expect(Foo.propDecorators).toEqual({field: [{type: 'test', args: ['arg']}]});
|
|
});
|
|
|
|
it('should merge parameter decorator metadata on a type', () => {
|
|
const Foo = metadataOf(class Foo{});
|
|
Foo.propDecorators = {initial: [{type: 'first'}]};
|
|
setClassMetadata(Foo, null, null, {field: [{type: 'test', args: ['arg']}]});
|
|
expect(Foo.propDecorators)
|
|
.toEqual({field: [{type: 'test', args: ['arg']}], initial: [{type: 'first'}]});
|
|
});
|
|
});
|