refactor(compiler-cli): switch jit transforms to use new import manager (#54819)

Switches the JIT transforms to use the new import manager.

PR Close #54819
This commit is contained in:
Paul Gschwendtner 2024-03-11 15:45:51 +00:00 committed by Alex Rickabaugh
parent dc80046132
commit 94bc3afc23
7 changed files with 32 additions and 37 deletions

View file

@ -54,10 +54,11 @@ export const signalInputsTransform: PropertyTransform = (
'transform': factory.createIdentifier('undefined'),
};
const sourceFile = member.getSourceFile();
const newDecorator = factory.createDecorator(
factory.createCallExpression(
createSyntheticAngularCoreDecoratorAccess(
factory, importManager, classDecorator, 'Input'),
factory, importManager, classDecorator, sourceFile, 'Input'),
undefined,
[
// Cast to `any` because `isSignal` will be private, and in case this

View file

@ -6,12 +6,13 @@
* found in the LICENSE file at https://angular.io/license
*/
import {Decorator} from '@angular/compiler-cli/src/ngtsc/reflection';
import ts from 'typescript';
import {isAngularDecorator, tryParseSignalModelMapping} from '../../../ngtsc/annotations';
import {ImportManager} from '../../../ngtsc/translator';
import {ImportManagerV2} from '../../../ngtsc/translator';
import {PropertyTransform} from './transform_api';
import {createSyntheticAngularCoreDecoratorAccess, PropertyTransform} from './transform_api';
/**
* Transform that automatically adds `@Input` and `@Output` to members initialized as `model()`.
@ -23,7 +24,7 @@ export const signalModelTransform: PropertyTransform = (
factory,
importTracker,
importManager,
decorator,
classDecorator,
isCore,
) => {
if (host.getDecoratorsOfDeclaration(member)?.some(d => {
@ -42,10 +43,6 @@ export const signalModelTransform: PropertyTransform = (
return member;
}
const classDecoratorIdentifier = ts.isIdentifier(decorator.identifier) ?
decorator.identifier :
decorator.identifier.expression;
const inputConfig = factory.createObjectLiteralExpression([
factory.createPropertyAssignment(
'isSignal', modelMapping.input.isSignal ? factory.createTrue() : factory.createFalse()),
@ -55,6 +52,7 @@ export const signalModelTransform: PropertyTransform = (
'required', modelMapping.input.required ? factory.createTrue() : factory.createFalse()),
]);
const sourceFile = member.getSourceFile();
const inputDecorator = createDecorator(
'Input',
// Config is cast to `any` because `isSignal` will be private, and in case this
@ -62,11 +60,11 @@ export const signalModelTransform: PropertyTransform = (
// not fail. It is already validated now due to us parsing the input metadata.
factory.createAsExpression(
inputConfig, factory.createKeywordTypeNode(ts.SyntaxKind.AnyKeyword)),
classDecoratorIdentifier, factory, importManager);
classDecorator, factory, sourceFile, importManager);
const outputDecorator = createDecorator(
'Output', factory.createStringLiteral(modelMapping.output.bindingPropertyName),
classDecoratorIdentifier, factory, importManager);
classDecorator, factory, sourceFile, importManager);
return factory.updatePropertyDeclaration(
member,
@ -79,14 +77,10 @@ export const signalModelTransform: PropertyTransform = (
};
function createDecorator(
name: string, config: ts.Expression, classDecoratorIdentifier: ts.Identifier,
factory: ts.NodeFactory, importManager: ImportManager): ts.Decorator {
const callTarget = factory.createPropertyAccessExpression(
importManager.generateNamespaceImport('@angular/core'),
// The synthetic identifier may be checked later by the downlevel decorators
// transform to resolve to an Angular import using `getSymbolAtLocation`. We trick
// the transform to think it's not synthetic and comes from Angular core.
ts.setOriginalNode(factory.createIdentifier(name), classDecoratorIdentifier));
name: string, config: ts.Expression, classDecorator: Decorator, factory: ts.NodeFactory,
sourceFile: ts.SourceFile, importManager: ImportManagerV2): ts.Decorator {
const callTarget = createSyntheticAngularCoreDecoratorAccess(
factory, importManager, classDecorator, sourceFile, name);
return factory.createDecorator(factory.createCallExpression(callTarget, undefined, [config]));
}

View file

@ -43,10 +43,11 @@ export const initializerApiOutputTransform: PropertyTransform = (
return member;
}
const sourceFile = member.getSourceFile();
const newDecorator = factory.createDecorator(
factory.createCallExpression(
createSyntheticAngularCoreDecoratorAccess(
factory, importManager, classDecorator, 'Output'),
factory, importManager, classDecorator, sourceFile, 'Output'),
undefined, [factory.createStringLiteral(output.metadata.bindingPropertyName)]),
);

View file

@ -56,11 +56,12 @@ export const queryFunctionsTransforms: PropertyTransform = (
return member;
}
const sourceFile = member.getSourceFile();
const callArgs = queryDefinition.call.arguments;
const newDecorator = factory.createDecorator(
factory.createCallExpression(
createSyntheticAngularCoreDecoratorAccess(
factory, importManager, classDecorator,
factory, importManager, classDecorator, sourceFile,
queryFunctionToDecorator[queryDefinition.name]),
undefined,
// All positional arguments of the query functions can be mostly re-used as is

View file

@ -11,8 +11,7 @@ import ts from 'typescript';
import {isAngularDecorator} from '../../../ngtsc/annotations';
import {ImportedSymbolsTracker} from '../../../ngtsc/imports';
import {ReflectionHost} from '../../../ngtsc/reflection';
import {addImports} from '../../../ngtsc/transform';
import {ImportManager} from '../../../ngtsc/translator';
import {ImportManagerV2} from '../../../ngtsc/translator';
import {signalInputsTransform} from './input_function';
import {signalModelTransform} from './model_function';
@ -48,7 +47,7 @@ export function getInitializerApiJitTransform(
): ts.TransformerFactory<ts.SourceFile> {
return ctx => {
return sourceFile => {
const importManager = new ImportManager(undefined, undefined, ctx.factory);
const importManager = new ImportManagerV2();
sourceFile = ts.visitNode(
sourceFile,
@ -56,12 +55,7 @@ export function getInitializerApiJitTransform(
ts.isSourceFile,
);
const newImports = importManager.getAllImports(sourceFile.fileName);
if (newImports.length > 0) {
sourceFile = addImports(ctx.factory, importManager, sourceFile);
}
return sourceFile;
return importManager.transformTsFile(ctx, sourceFile);
};
};
}
@ -69,7 +63,7 @@ export function getInitializerApiJitTransform(
function createTransformVisitor(
ctx: ts.TransformationContext,
host: ReflectionHost,
importManager: ImportManager,
importManager: ImportManagerV2,
importTracker: ImportedSymbolsTracker,
isCore: boolean,
): ts.Visitor<ts.Node, ts.Node> {

View file

@ -10,13 +10,13 @@ import ts from 'typescript';
import {ImportedSymbolsTracker} from '../../../ngtsc/imports';
import {Decorator, ReflectionHost} from '../../../ngtsc/reflection';
import {ImportManager} from '../../../ngtsc/translator';
import {ImportManagerV2} from '../../../ngtsc/translator';
/** Function that can be used to transform class properties. */
export type PropertyTransform =
(node: ts.PropertyDeclaration&{name: ts.Identifier | ts.StringLiteralLike},
host: ReflectionHost, factory: ts.NodeFactory, importTracker: ImportedSymbolsTracker,
importManager: ImportManager, classDecorator: Decorator, isCore: boolean) =>
importManager: ImportManagerV2, classDecorator: Decorator, isCore: boolean) =>
ts.PropertyDeclaration;
/**
@ -26,14 +26,18 @@ export type PropertyTransform =
* decorator downlevel transform.
*/
export function createSyntheticAngularCoreDecoratorAccess(
factory: ts.NodeFactory, importManager: ImportManager, ngClassDecorator: Decorator,
decoratorName: string): ts.PropertyAccessExpression {
factory: ts.NodeFactory, importManager: ImportManagerV2, ngClassDecorator: Decorator,
sourceFile: ts.SourceFile, decoratorName: string): ts.PropertyAccessExpression {
const classDecoratorIdentifier = ts.isIdentifier(ngClassDecorator.identifier) ?
ngClassDecorator.identifier :
ngClassDecorator.identifier.expression;
return factory.createPropertyAccessExpression(
importManager.generateNamespaceImport('@angular/core'),
importManager.addImport({
exportModuleSpecifier: '@angular/core',
exportSymbolName: null,
requestedFile: sourceFile,
}),
// The synthetic identifier may be checked later by the downlevel decorators
// transform to resolve to an Angular import using `getSymbolAtLocation`. We trick
// the transform to think it's not synthetic and comes from Angular core.

View file

@ -137,12 +137,12 @@ describe('signal queries metadata transform', () => {
expect(result).toContain(omitLeadingWhitespace(`
__decorate([
i0.ViewChild('el', { ...{ read: SomeToken }, isSignal: true })
bla.ViewChild('el', { ...{ read: SomeToken }, isSignal: true })
], MyDir.prototype, "el", void 0);
`));
expect(result).toContain(omitLeadingWhitespace(`
__decorate([
i0.ViewChild('el', { ...{ read: bla.Component }, isSignal: true })
bla.ViewChild('el', { ...{ read: bla.Component }, isSignal: true })
], MyDir.prototype, "el2", void 0);
`));
});