Skip to content

Commit cb1bc61

Browse files
authored
Move legacy decorators into separate transform (#48669)
1 parent 3ccbe80 commit cb1bc61

File tree

12 files changed

+1663
-1043
lines changed

12 files changed

+1663
-1043
lines changed

src/compiler/factory/nodeFactory.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1252,7 +1252,8 @@ namespace ts {
12521252
node.transformFlags |=
12531253
propagateChildFlags(node.expression) |
12541254
TransformFlags.ContainsTypeScript |
1255-
TransformFlags.ContainsTypeScriptClassSyntax;
1255+
TransformFlags.ContainsTypeScriptClassSyntax |
1256+
TransformFlags.ContainsDecorators;
12561257
return node;
12571258
}
12581259

src/compiler/factory/utilities.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1215,4 +1215,16 @@ namespace ts {
12151215
return resultHolder.value;
12161216
}
12171217
}
1218+
1219+
/**
1220+
* If `nodes` is not undefined, creates an empty `NodeArray` that preserves the `pos` and `end` of `nodes`.
1221+
* @internal
1222+
*/
1223+
export function elideNodes<T extends Node>(factory: NodeFactory, nodes: NodeArray<T>): NodeArray<T>;
1224+
export function elideNodes<T extends Node>(factory: NodeFactory, nodes: NodeArray<T> | undefined): NodeArray<T> | undefined;
1225+
export function elideNodes<T extends Node>(factory: NodeFactory, nodes: NodeArray<T> | undefined): NodeArray<T> | undefined {
1226+
if (nodes === undefined) return undefined;
1227+
if (nodes.length === 0) return nodes;
1228+
return setTextRange(factory.createNodeArray([], nodes.hasTrailingComma), nodes);
1229+
}
12181230
}

src/compiler/transformer.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ namespace ts {
4848
addRange(transformers, customTransformers && map(customTransformers.before, wrapScriptTransformerFactory));
4949

5050
transformers.push(transformTypeScript);
51+
transformers.push(transformLegacyDecorators);
5152
transformers.push(transformClassFields);
5253

5354
if (getJSXTransformEnabled(compilerOptions)) {

src/compiler/transformers/legacyDecorators.ts

Lines changed: 675 additions & 0 deletions
Large diffs are not rendered by default.

src/compiler/transformers/ts.ts

Lines changed: 225 additions & 1015 deletions
Large diffs are not rendered by default.

src/compiler/transformers/typeSerializer.ts

Lines changed: 569 additions & 0 deletions
Large diffs are not rendered by default.

src/compiler/transformers/utilities.ts

Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -384,4 +384,140 @@ namespace ts {
384384
export function isNonStaticMethodOrAccessorWithPrivateName(member: ClassElement): member is PrivateIdentifierMethodDeclaration | PrivateIdentifierAccessorDeclaration {
385385
return !isStatic(member) && isMethodOrAccessor(member) && isPrivateIdentifier(member.name);
386386
}
387+
388+
/**
389+
* Gets an array of arrays of decorators for the parameters of a function-like node.
390+
* The offset into the result array should correspond to the offset of the parameter.
391+
*
392+
* @param node The function-like node.
393+
*/
394+
function getDecoratorsOfParameters(node: FunctionLikeDeclaration | undefined) {
395+
let decorators: (readonly Decorator[] | undefined)[] | undefined;
396+
if (node) {
397+
const parameters = node.parameters;
398+
const firstParameterIsThis = parameters.length > 0 && parameterIsThisKeyword(parameters[0]);
399+
const firstParameterOffset = firstParameterIsThis ? 1 : 0;
400+
const numParameters = firstParameterIsThis ? parameters.length - 1 : parameters.length;
401+
for (let i = 0; i < numParameters; i++) {
402+
const parameter = parameters[i + firstParameterOffset];
403+
if (decorators || parameter.decorators) {
404+
if (!decorators) {
405+
decorators = new Array(numParameters);
406+
}
407+
408+
decorators[i] = parameter.decorators;
409+
}
410+
}
411+
}
412+
413+
return decorators;
414+
}
415+
416+
/**
417+
* Gets an AllDecorators object containing the decorators for the class and the decorators for the
418+
* parameters of the constructor of the class.
419+
*
420+
* @param node The class node.
421+
*/
422+
export function getAllDecoratorsOfClass(node: ClassLikeDeclaration): AllDecorators | undefined {
423+
const decorators = node.decorators;
424+
const parameters = getDecoratorsOfParameters(getFirstConstructorWithBody(node));
425+
if (!decorators && !parameters) {
426+
return undefined;
427+
}
428+
429+
return {
430+
decorators,
431+
parameters
432+
};
433+
}
434+
435+
/**
436+
* Gets an AllDecorators object containing the decorators for the member and its parameters.
437+
*
438+
* @param parent The class node that contains the member.
439+
* @param member The class member.
440+
*/
441+
export function getAllDecoratorsOfClassElement(member: ClassElement, parent: ClassLikeDeclaration): AllDecorators | undefined {
442+
switch (member.kind) {
443+
case SyntaxKind.GetAccessor:
444+
case SyntaxKind.SetAccessor:
445+
return getAllDecoratorsOfAccessors(member as AccessorDeclaration, parent);
446+
447+
case SyntaxKind.MethodDeclaration:
448+
return getAllDecoratorsOfMethod(member as MethodDeclaration);
449+
450+
case SyntaxKind.PropertyDeclaration:
451+
return getAllDecoratorsOfProperty(member as PropertyDeclaration);
452+
453+
default:
454+
return undefined;
455+
}
456+
}
457+
458+
/**
459+
* Gets an AllDecorators object containing the decorators for the accessor and its parameters.
460+
*
461+
* @param parent The class node that contains the accessor.
462+
* @param accessor The class accessor member.
463+
*/
464+
function getAllDecoratorsOfAccessors(accessor: AccessorDeclaration, parent: ClassExpression | ClassDeclaration): AllDecorators | undefined {
465+
if (!accessor.body) {
466+
return undefined;
467+
}
468+
469+
const { firstAccessor, secondAccessor, getAccessor, setAccessor } = getAllAccessorDeclarations(parent.members, accessor);
470+
const firstAccessorWithDecorators = firstAccessor.decorators ? firstAccessor : secondAccessor && secondAccessor.decorators ? secondAccessor : undefined;
471+
if (!firstAccessorWithDecorators || accessor !== firstAccessorWithDecorators) {
472+
return undefined;
473+
}
474+
475+
const decorators = firstAccessorWithDecorators.decorators;
476+
const parameters = getDecoratorsOfParameters(setAccessor);
477+
if (!decorators && !parameters) {
478+
return undefined;
479+
}
480+
481+
return {
482+
decorators,
483+
parameters,
484+
getDecorators: getAccessor?.decorators,
485+
setDecorators: setAccessor?.decorators
486+
};
487+
}
488+
489+
/**
490+
* Gets an AllDecorators object containing the decorators for the method and its parameters.
491+
*
492+
* @param method The class method member.
493+
*/
494+
function getAllDecoratorsOfMethod(method: MethodDeclaration): AllDecorators | undefined {
495+
if (!method.body) {
496+
return undefined;
497+
}
498+
499+
const decorators = method.decorators;
500+
const parameters = getDecoratorsOfParameters(method);
501+
if (!decorators && !parameters) {
502+
return undefined;
503+
}
504+
505+
return { decorators, parameters };
506+
}
507+
508+
/**
509+
* Gets an AllDecorators object containing the decorators for the property.
510+
*
511+
* @param property The class property member.
512+
*/
513+
function getAllDecoratorsOfProperty(property: PropertyDeclaration): AllDecorators | undefined {
514+
const decorators = property.decorators;
515+
if (!decorators) {
516+
return undefined;
517+
518+
}
519+
520+
return { decorators };
521+
}
522+
387523
}

src/compiler/tsconfig.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,8 @@
4949
"transformers/taggedTemplate.ts",
5050
"transformers/ts.ts",
5151
"transformers/classFields.ts",
52+
"transformers/typeSerializer.ts",
53+
"transformers/legacyDecorators.ts",
5254
"transformers/es2017.ts",
5355
"transformers/es2018.ts",
5456
"transformers/es2019.ts",

src/compiler/types.ts

Lines changed: 24 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -4751,6 +4751,14 @@ namespace ts {
47514751
setAccessor: SetAccessorDeclaration | undefined;
47524752
}
47534753

4754+
/* @internal */
4755+
export interface AllDecorators {
4756+
decorators: NodeArray<Decorator> | undefined;
4757+
parameters?: readonly (readonly Decorator[] | undefined)[];
4758+
getDecorators?: NodeArray<Decorator> | undefined;
4759+
setDecorators?: NodeArray<Decorator> | undefined;
4760+
}
4761+
47544762
/** Indicates how to serialize the name for a TypeReferenceNode when emitting decorator metadata */
47554763
/* @internal */
47564764
export enum TypeReferenceSerializationKind {
@@ -6841,21 +6849,22 @@ namespace ts {
68416849

68426850
// Markers
68436851
// - Flags used to indicate that a subtree contains a specific transformation.
6844-
ContainsTypeScriptClassSyntax = 1 << 12, // Decorators, Property Initializers, Parameter Property Initializers
6845-
ContainsLexicalThis = 1 << 13,
6846-
ContainsRestOrSpread = 1 << 14,
6847-
ContainsObjectRestOrSpread = 1 << 15,
6848-
ContainsComputedPropertyName = 1 << 16,
6849-
ContainsBlockScopedBinding = 1 << 17,
6850-
ContainsBindingPattern = 1 << 18,
6851-
ContainsYield = 1 << 19,
6852-
ContainsAwait = 1 << 20,
6853-
ContainsHoistedDeclarationOrCompletion = 1 << 21,
6854-
ContainsDynamicImport = 1 << 22,
6855-
ContainsClassFields = 1 << 23,
6856-
ContainsPossibleTopLevelAwait = 1 << 24,
6857-
ContainsLexicalSuper = 1 << 25,
6858-
ContainsUpdateExpressionForIdentifier = 1 << 26,
6852+
ContainsTypeScriptClassSyntax = 1 << 13, // Property Initializers, Parameter Property Initializers
6853+
ContainsLexicalThis = 1 << 14,
6854+
ContainsRestOrSpread = 1 << 15,
6855+
ContainsObjectRestOrSpread = 1 << 16,
6856+
ContainsComputedPropertyName = 1 << 17,
6857+
ContainsBlockScopedBinding = 1 << 18,
6858+
ContainsBindingPattern = 1 << 19,
6859+
ContainsYield = 1 << 20,
6860+
ContainsAwait = 1 << 21,
6861+
ContainsHoistedDeclarationOrCompletion = 1 << 22,
6862+
ContainsDynamicImport = 1 << 23,
6863+
ContainsClassFields = 1 << 24,
6864+
ContainsDecorators = 1 << 25,
6865+
ContainsPossibleTopLevelAwait = 1 << 26,
6866+
ContainsLexicalSuper = 1 << 27,
6867+
ContainsUpdateExpressionForIdentifier = 1 << 28,
68596868
// Please leave this as 1 << 29.
68606869
// It is the maximum bit we can set before we outgrow the size of a v8 small integer (SMI) on an x86 system.
68616870
// It is a good reminder of how much room we have left

src/compiler/utilities.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3011,6 +3011,11 @@ namespace ts {
30113011
return [child, node];
30123012
}
30133013

3014+
export function skipTypeParentheses(node: TypeNode): TypeNode {
3015+
while (isParenthesizedTypeNode(node)) node = node.type;
3016+
return node;
3017+
}
3018+
30143019
export function skipParentheses(node: Expression, excludeJSDocTypeAssertions?: boolean): Expression;
30153020
export function skipParentheses(node: Node, excludeJSDocTypeAssertions?: boolean): Node;
30163021
export function skipParentheses(node: Node, excludeJSDocTypeAssertions?: boolean): Node {
@@ -5417,7 +5422,7 @@ namespace ts {
54175422
* Moves the start position of a range past any decorators.
54185423
*/
54195424
export function moveRangePastDecorators(node: Node): TextRange {
5420-
return node.decorators && node.decorators.length > 0
5425+
return node.decorators && !positionIsSynthesized(node.decorators.end)
54215426
? moveRangePos(node, node.decorators.end)
54225427
: node;
54235428
}
@@ -5426,7 +5431,7 @@ namespace ts {
54265431
* Moves the start position of a range past any decorators or modifiers.
54275432
*/
54285433
export function moveRangePastModifiers(node: Node): TextRange {
5429-
return node.modifiers && node.modifiers.length > 0
5434+
return node.modifiers && !positionIsSynthesized(node.modifiers.end)
54305435
? moveRangePos(node, node.modifiers.end)
54315436
: moveRangePastDecorators(node);
54325437
}

0 commit comments

Comments
 (0)