Skip to content

Commit afb05ff

Browse files
devversionthePunderWoman
authored andcommitted
fix(compiler-cli): support JIT transforms before other transforms modifying classes (#57262)
Similar to a previous fix that intended to make the JIT transforms compatible with pre-transforms like e.g. Tsickle, we need to solve an additional issue where the class properties are synthetic and result in an `getSourceFile() => undefined` invocation that breaks the import insertion, causing errors like: ``` TypeError: Cannot read properties of undefined (reading 'fileName') ``` PR Close #57262
1 parent 183715a commit afb05ff

File tree

7 files changed

+53
-4
lines changed

7 files changed

+53
-4
lines changed

packages/compiler-cli/src/transformers/jit_transforms/initializer_api_transforms/input_function.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ import {
3030
*/
3131
export const signalInputsTransform: PropertyTransform = (
3232
member,
33+
sourceFile,
3334
host,
3435
factory,
3536
importTracker,
@@ -61,7 +62,6 @@ export const signalInputsTransform: PropertyTransform = (
6162
'transform': factory.createIdentifier('undefined'),
6263
};
6364

64-
const sourceFile = member.node.getSourceFile();
6565
const newDecorator = factory.createDecorator(
6666
factory.createCallExpression(
6767
createSyntheticAngularCoreDecoratorAccess(

packages/compiler-cli/src/transformers/jit_transforms/initializer_api_transforms/model_function.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import {createSyntheticAngularCoreDecoratorAccess, PropertyTransform} from './tr
2020
*/
2121
export const signalModelTransform: PropertyTransform = (
2222
member,
23+
sourceFile,
2324
host,
2425
factory,
2526
importTracker,
@@ -56,7 +57,6 @@ export const signalModelTransform: PropertyTransform = (
5657
),
5758
]);
5859

59-
const sourceFile = member.node.getSourceFile();
6060
const inputDecorator = createDecorator(
6161
'Input',
6262
// Config is cast to `any` because `isSignal` will be private, and in case this

packages/compiler-cli/src/transformers/jit_transforms/initializer_api_transforms/output_function.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import {createSyntheticAngularCoreDecoratorAccess, PropertyTransform} from './tr
2222
*/
2323
export const initializerApiOutputTransform: PropertyTransform = (
2424
member,
25+
sourceFile,
2526
host,
2627
factory,
2728
importTracker,
@@ -43,7 +44,6 @@ export const initializerApiOutputTransform: PropertyTransform = (
4344
return member.node;
4445
}
4546

46-
const sourceFile = member.node.getSourceFile();
4747
const newDecorator = factory.createDecorator(
4848
factory.createCallExpression(
4949
createSyntheticAngularCoreDecoratorAccess(

packages/compiler-cli/src/transformers/jit_transforms/initializer_api_transforms/query_functions.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ const queryFunctionToDecorator: Record<QueryFunctionName, string> = {
4040
*/
4141
export const queryFunctionsTransforms: PropertyTransform = (
4242
member,
43+
sourceFile,
4344
host,
4445
factory,
4546
importTracker,
@@ -61,7 +62,6 @@ export const queryFunctionsTransforms: PropertyTransform = (
6162
return member.node;
6263
}
6364

64-
const sourceFile = member.node.getSourceFile();
6565
const callArgs = queryDefinition.call.arguments;
6666
const newDecorator = factory.createDecorator(
6767
factory.createCallExpression(

packages/compiler-cli/src/transformers/jit_transforms/initializer_api_transforms/transform.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ function createTransformVisitor(
8181
if (angularDecorator !== undefined) {
8282
let hasChanged = false;
8383

84+
const sourceFile = originalNode.getSourceFile();
8485
const members = node.members.map((memberNode) => {
8586
if (!ts.isPropertyDeclaration(memberNode)) {
8687
return memberNode;
@@ -94,6 +95,7 @@ function createTransformVisitor(
9495
for (const transform of propertyTransforms) {
9596
const newNode = transform(
9697
{...member, node: memberNode},
98+
sourceFile,
9799
host,
98100
ctx.factory,
99101
importTracker,

packages/compiler-cli/src/transformers/jit_transforms/initializer_api_transforms/transform_api.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import {ImportManager} from '../../../ngtsc/translator';
1515
/** Function that can be used to transform class properties. */
1616
export type PropertyTransform = (
1717
member: Pick<ClassMember, 'name' | 'accessLevel' | 'value'> & {node: ts.PropertyDeclaration},
18+
sourceFile: ts.SourceFile,
1819
host: ReflectionHost,
1920
factory: ts.NodeFactory,
2021
importTracker: ImportedSymbolsTracker,

packages/compiler-cli/test/initializer_api_transforms_spec.ts

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -318,6 +318,52 @@ describe('initializer API metadata transform', () => {
318318
`),
319319
);
320320
});
321+
322+
// Tsickle may transform the class via the downlevel decorator transform in advance in G3.
323+
// In addition, the property declaration may be transformed too, to add `@type` JSDocs in G3.
324+
it('should migrate if the class member and class is transformed in advance', () => {
325+
const fakeDownlevelPreTransform = (ctx: ts.TransformationContext) => {
326+
return (sf: ts.SourceFile) => {
327+
const visitor = (node: ts.Node) => {
328+
// Updating the `{transform: () => {}} arrow function triggers both transforms.
329+
if (ts.isArrowFunction(node)) {
330+
return ctx.factory.updateArrowFunction(
331+
node,
332+
node.modifiers,
333+
node.typeParameters,
334+
node.parameters,
335+
node.type,
336+
node.equalsGreaterThanToken,
337+
ctx.factory.createBlock([]),
338+
);
339+
}
340+
return ts.visitEachChild(node, visitor, ctx);
341+
};
342+
return ts.visitEachChild(sf, visitor, ctx);
343+
};
344+
};
345+
346+
const result = transform(
347+
`
348+
import {input, Directive} from '@angular/core';
349+
350+
@Directive({})
351+
class MyDir {
352+
someInput = input({transform: () => {}});
353+
}
354+
`,
355+
false,
356+
fakeDownlevelPreTransform,
357+
);
358+
359+
expect(result).toContain(
360+
omitLeadingWhitespace(`
361+
__decorate([
362+
i0.Input({ isSignal: true, alias: "someInput", required: false, transform: undefined })
363+
], MyDir.prototype, "someInput", void 0);
364+
`),
365+
);
366+
});
321367
});
322368

323369
describe('model()', () => {

0 commit comments

Comments
 (0)