fix(compiler-cli): do not drop non-Angular decorators when downleveling (#39577)
There is a compiler transform that downlevels Angular class decorators to static properties so that metadata is available for JIT compilation. The transform was supposed to ignore non-Angular decorators but it was actually completely dropping decorators that did not conform to a very specific syntactic shape (i.e. the decorator was a simple identifier, or a namespaced identifier). This commit ensures that all non-Angular decorators are kepts as-is even if they are built using a syntax that the Angular compiler does not understand. Fixes #39574 PR Close #39577
This commit is contained in:
parent
3a1d36cce3
commit
b9b9178458
@ -538,12 +538,17 @@ export function getDownlevelDecoratorsTransform(
|
|||||||
}
|
}
|
||||||
newMembers.push(ts.visitEachChild(member, decoratorDownlevelVisitor, context));
|
newMembers.push(ts.visitEachChild(member, decoratorDownlevelVisitor, context));
|
||||||
}
|
}
|
||||||
const decorators = host.getDecoratorsOfDeclaration(classDecl) || [];
|
|
||||||
|
// The `ReflectionHost.getDecoratorsOfDeclaration()` method will not return certain kinds of
|
||||||
|
// decorators that will never be Angular decorators. So we cannot rely on it to capture all
|
||||||
|
// the decorators that should be kept. Instead we start off with a set of the raw decorators
|
||||||
|
// on the class, and only remove the ones that have been identified for downleveling.
|
||||||
|
const decoratorsToKeep = new Set<ts.Decorator>(classDecl.decorators);
|
||||||
|
const possibleAngularDecorators = host.getDecoratorsOfDeclaration(classDecl) || [];
|
||||||
|
|
||||||
let hasAngularDecorator = false;
|
let hasAngularDecorator = false;
|
||||||
const decoratorsToLower = [];
|
const decoratorsToLower = [];
|
||||||
const decoratorsToKeep: ts.Decorator[] = [];
|
for (const decorator of possibleAngularDecorators) {
|
||||||
for (const decorator of decorators) {
|
|
||||||
// We only deal with concrete nodes in TypeScript sources, so we don't
|
// We only deal with concrete nodes in TypeScript sources, so we don't
|
||||||
// need to handle synthetically created decorators.
|
// need to handle synthetically created decorators.
|
||||||
const decoratorNode = decorator.node! as ts.Decorator;
|
const decoratorNode = decorator.node! as ts.Decorator;
|
||||||
@ -557,8 +562,7 @@ export function getDownlevelDecoratorsTransform(
|
|||||||
|
|
||||||
if (isNgDecorator && !skipClassDecorators) {
|
if (isNgDecorator && !skipClassDecorators) {
|
||||||
decoratorsToLower.push(extractMetadataFromSingleDecorator(decoratorNode, diagnostics));
|
decoratorsToLower.push(extractMetadataFromSingleDecorator(decoratorNode, diagnostics));
|
||||||
} else {
|
decoratorsToKeep.delete(decoratorNode);
|
||||||
decoratorsToKeep.push(decoratorNode);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -581,8 +585,9 @@ export function getDownlevelDecoratorsTransform(
|
|||||||
ts.createNodeArray(newMembers, classDecl.members.hasTrailingComma), classDecl.members);
|
ts.createNodeArray(newMembers, classDecl.members.hasTrailingComma), classDecl.members);
|
||||||
|
|
||||||
return ts.updateClassDeclaration(
|
return ts.updateClassDeclaration(
|
||||||
classDecl, decoratorsToKeep.length ? decoratorsToKeep : undefined, classDecl.modifiers,
|
classDecl, decoratorsToKeep.size ? Array.from(decoratorsToKeep) : undefined,
|
||||||
classDecl.name, classDecl.typeParameters, classDecl.heritageClauses, members);
|
classDecl.modifiers, classDecl.name, classDecl.typeParameters, classDecl.heritageClauses,
|
||||||
|
members);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -189,6 +189,21 @@ describe('downlevel decorator transform', () => {
|
|||||||
expect(output).not.toContain('MyClass.decorators');
|
expect(output).not.toContain('MyClass.decorators');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should not downlevel non-Angular class decorators generated by a builder', () => {
|
||||||
|
const {output} = transform(`
|
||||||
|
@DecoratorBuilder().customClassDecorator
|
||||||
|
export class MyClass {}
|
||||||
|
`);
|
||||||
|
|
||||||
|
expect(diagnostics.length).toBe(0);
|
||||||
|
expect(output).toContain(dedent`
|
||||||
|
MyClass = tslib_1.__decorate([
|
||||||
|
DecoratorBuilder().customClassDecorator
|
||||||
|
], MyClass);
|
||||||
|
`);
|
||||||
|
expect(output).not.toContain('MyClass.decorators');
|
||||||
|
});
|
||||||
|
|
||||||
it('should downlevel Angular-decorated class member', () => {
|
it('should downlevel Angular-decorated class member', () => {
|
||||||
const {output} = transform(`
|
const {output} = transform(`
|
||||||
import {Input} from '@angular/core';
|
import {Input} from '@angular/core';
|
||||||
|
Loading…
x
Reference in New Issue
Block a user