mirror of
https://github.com/angular/angular
synced 2026-05-24 09:28:37 +00:00
refactor(compiler-cli): replace typescript usage in type reference emits
Replaces our usage of TypeScript APIs in several places that emit references to type nodes. Also deletes some unused code.
This commit is contained in:
parent
5d755be01c
commit
2feced366a
9 changed files with 25 additions and 177 deletions
|
|
@ -8,22 +8,11 @@
|
|||
|
||||
import {
|
||||
AbsoluteSourceSpan,
|
||||
AST,
|
||||
BoundTarget,
|
||||
DirectiveMeta,
|
||||
DirectiveOwner,
|
||||
LegacyAnimationTriggerNames,
|
||||
ParseSourceSpan,
|
||||
SchemaMetadata,
|
||||
TemplateEntity,
|
||||
TmplAstBoundAttribute,
|
||||
TmplAstBoundEvent,
|
||||
TmplAstComponent,
|
||||
TmplAstDirective,
|
||||
TmplAstElement,
|
||||
TmplAstReference,
|
||||
TmplAstTemplate,
|
||||
TmplAstTextAttribute,
|
||||
} from '@angular/compiler';
|
||||
import ts from 'typescript';
|
||||
|
||||
|
|
|
|||
|
|
@ -8,14 +8,9 @@
|
|||
|
||||
import ts from 'typescript';
|
||||
|
||||
import {
|
||||
assertSuccessfulReferenceEmit,
|
||||
ImportFlags,
|
||||
Reference,
|
||||
ReferenceEmitter,
|
||||
} from '../../imports';
|
||||
import {ClassDeclaration, ReflectionHost} from '../../reflection';
|
||||
import {ImportManager, translateExpression} from '../../translator';
|
||||
import {ReferenceEmitter} from '../../imports';
|
||||
import {ReflectionHost} from '../../reflection';
|
||||
import {ImportManager} from '../../translator';
|
||||
import {
|
||||
TcbDirectiveMetadata,
|
||||
TcbPipeMetadata,
|
||||
|
|
@ -26,9 +21,8 @@ import {
|
|||
} from '../api';
|
||||
|
||||
import {ReferenceEmitEnvironment} from './reference_emit_environment';
|
||||
import {generateTypeCtorDeclarationFn, requiresInlineTypeCtor} from './type_constructor';
|
||||
import {TypeParameterEmitter} from './type_parameter_emitter';
|
||||
import {declareVariable, TcbExpr, tempPrint} from './ops/codegen';
|
||||
import {generateTypeCtorDeclarationFn} from './type_constructor';
|
||||
import {declareVariable, TcbExpr} from './ops/codegen';
|
||||
|
||||
/**
|
||||
* A context which hosts one or more Type Check Blocks (TCBs).
|
||||
|
|
@ -83,10 +77,7 @@ export class Environment extends ReferenceEmitEnvironment {
|
|||
return new TcbExpr(typeCtorExpr);
|
||||
} else {
|
||||
const fnName = `_ctor${this.nextIds.typeCtor++}`;
|
||||
const nodeTypeRef = this.referenceTcbType(dir.ref);
|
||||
if (!ts.isTypeReferenceNode(nodeTypeRef)) {
|
||||
throw new Error(`Expected TypeReferenceNode from reference to ${dir.ref.name}`);
|
||||
}
|
||||
const nodeTypeRef = this.referenceTcbValue(dir.ref);
|
||||
const meta: TypeCtorMetadata = {
|
||||
fnName,
|
||||
body: true,
|
||||
|
|
@ -98,7 +89,7 @@ export class Environment extends ReferenceEmitEnvironment {
|
|||
};
|
||||
|
||||
const typeParams = dir.typeParameters || undefined;
|
||||
const typeCtor = generateTypeCtorDeclarationFn(this, meta, nodeTypeRef.typeName, typeParams);
|
||||
const typeCtor = generateTypeCtorDeclarationFn(this, meta, nodeTypeRef, typeParams);
|
||||
this.typeCtorStatements.push(typeCtor);
|
||||
this.typeCtors.set(key, fnName);
|
||||
return new TcbExpr(fnName);
|
||||
|
|
@ -114,46 +105,14 @@ export class Environment extends ReferenceEmitEnvironment {
|
|||
return new TcbExpr(this.pipeInsts.get(key)!);
|
||||
}
|
||||
|
||||
const pipeType = this.referenceTcbType(pipe.ref);
|
||||
const pipeType = this.referenceTcbValue(pipe.ref);
|
||||
const pipeInstId = `_pipe${this.nextIds.pipeInst++}`;
|
||||
|
||||
this.pipeInsts.set(key, pipeInstId);
|
||||
this.pipeInstStatements.push(
|
||||
declareVariable(new TcbExpr(pipeInstId), new TcbExpr(tempPrint(pipeType, this.contextFile))),
|
||||
);
|
||||
this.pipeInstStatements.push(declareVariable(new TcbExpr(pipeInstId), pipeType));
|
||||
return new TcbExpr(pipeInstId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a `ts.Expression` that references the given node.
|
||||
*
|
||||
* This may involve importing the node into the file if it's not declared there already.
|
||||
*/
|
||||
reference(ref: Reference<ClassDeclaration<ts.ClassDeclaration>>): TcbExpr {
|
||||
// Disable aliasing for imports generated in a template type-checking context, as there is no
|
||||
// guarantee that any alias re-exports exist in the .d.ts files. It's safe to use direct imports
|
||||
// in these cases as there is no strict dependency checking during the template type-checking
|
||||
// pass.
|
||||
const ngExpr = this.refEmitter.emit(ref, this.contextFile, ImportFlags.NoAliasing);
|
||||
assertSuccessfulReferenceEmit(ngExpr, this.contextFile, 'class');
|
||||
|
||||
// Use `translateExpression` to convert the `Expression` into a `ts.Expression`.
|
||||
const tsExpression = translateExpression(
|
||||
this.contextFile,
|
||||
ngExpr.expression,
|
||||
this.importManager,
|
||||
);
|
||||
|
||||
return new TcbExpr(tempPrint(tsExpression, this.contextFile));
|
||||
}
|
||||
|
||||
private emitTypeParameters(
|
||||
declaration: ClassDeclaration<ts.ClassDeclaration>,
|
||||
): ts.TypeParameterDeclaration[] | undefined {
|
||||
const emitter = new TypeParameterEmitter(declaration.typeParameters, this.reflector);
|
||||
return emitter.emit((ref) => this.referenceType(ref));
|
||||
}
|
||||
|
||||
getPreludeStatements(): TcbExpr[] {
|
||||
return [...this.pipeInstStatements, ...this.typeCtorStatements];
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,11 +7,10 @@
|
|||
*/
|
||||
|
||||
import {DirectiveOwner, ParseSourceSpan, TmplAstHostElement} from '@angular/compiler';
|
||||
import ts from 'typescript';
|
||||
import type {Context} from './context';
|
||||
import type {Scope} from './scope';
|
||||
import {TcbOp} from './base';
|
||||
import {declareVariable, TcbExpr, tempPrint} from './codegen';
|
||||
import {declareVariable, TcbExpr} from './codegen';
|
||||
import {TcbDirectiveMetadata} from '../../api';
|
||||
import {ExpressionIdentifier} from '../comments';
|
||||
|
||||
|
|
@ -37,7 +36,7 @@ export abstract class TcbDirectiveTypeOpBase extends TcbOp {
|
|||
}
|
||||
|
||||
override execute(): TcbExpr {
|
||||
const rawType = this.tcb.env.referenceTcbType(this.dir.ref);
|
||||
const rawType = this.tcb.env.referenceTcbValue(this.dir.ref);
|
||||
|
||||
let type: TcbExpr;
|
||||
let span: ParseSourceSpan;
|
||||
|
|
@ -46,20 +45,12 @@ export abstract class TcbDirectiveTypeOpBase extends TcbOp {
|
|||
this.dir.typeParameters === null ||
|
||||
this.dir.typeParameters.length === 0
|
||||
) {
|
||||
type = new TcbExpr(tempPrint(rawType, this.tcb.env.contextFile));
|
||||
type = rawType;
|
||||
} else {
|
||||
if (!ts.isTypeReferenceNode(rawType)) {
|
||||
throw new Error(
|
||||
`Expected TypeReferenceNode when referencing the type for ${this.dir.ref.name}`,
|
||||
);
|
||||
}
|
||||
const typeName = ts.isIdentifier(rawType.typeName)
|
||||
? rawType.typeName.text
|
||||
: tempPrint(rawType.typeName, this.tcb.env.contextFile);
|
||||
const typeArguments = Array(this.dir.typeParameters?.length ?? 0)
|
||||
.fill('any')
|
||||
.join(', ');
|
||||
type = new TcbExpr(`${typeName}<${typeArguments}>`);
|
||||
type = new TcbExpr(`${rawType.print()}<${typeArguments}>`);
|
||||
}
|
||||
|
||||
if (this.node instanceof TmplAstHostElement) {
|
||||
|
|
|
|||
|
|
@ -15,14 +15,12 @@ import {
|
|||
TmplAstDirective,
|
||||
TmplAstElement,
|
||||
TmplAstTemplate,
|
||||
TransplantedType,
|
||||
} from '@angular/compiler';
|
||||
import ts from 'typescript';
|
||||
import type {Context} from './context';
|
||||
import type {Scope} from './scope';
|
||||
import {TcbDirectiveMetadata} from '../../api';
|
||||
import {TcbOp} from './base';
|
||||
import {declareVariable, quoteAndEscape, TcbExpr, tempPrint} from './codegen';
|
||||
import {declareVariable, quoteAndEscape, TcbExpr} from './codegen';
|
||||
import {BindingPropertyName, ClassPropertyName} from '../../../metadata';
|
||||
import {REGISTRY} from '../dom';
|
||||
import {tcbExpression, unwrapWritableSignal} from './expression';
|
||||
|
|
@ -125,17 +123,8 @@ export class TcbDirectiveInputsOp extends TcbOp {
|
|||
// expression into the input field directly. To achieve this, a variable is declared
|
||||
// with a type of `typeof Directive.ngAcceptInputType_fieldName` which is then used as
|
||||
// target of the assignment.
|
||||
const dirTypeRef: ts.TypeNode = this.tcb.env.referenceTcbType(this.dir.ref);
|
||||
|
||||
if (!ts.isTypeReferenceNode(dirTypeRef)) {
|
||||
throw new Error(`Expected TypeReferenceNode from reference to ${this.dir.ref.name}`);
|
||||
}
|
||||
|
||||
const typeName = ts.isIdentifier(dirTypeRef.typeName)
|
||||
? dirTypeRef.typeName.text
|
||||
: tempPrint(dirTypeRef.typeName, dirTypeRef.typeName.getSourceFile());
|
||||
|
||||
type = new TcbExpr(`typeof ${typeName}.ngAcceptInputType_${fieldName}`);
|
||||
const dirTypeRef = this.tcb.env.referenceTcbValue(this.dir.ref);
|
||||
type = new TcbExpr(`typeof ${dirTypeRef.print()}.ngAcceptInputType_${fieldName}`);
|
||||
}
|
||||
|
||||
const id = new TcbExpr(this.tcb.allocateId());
|
||||
|
|
@ -160,10 +149,6 @@ export class TcbDirectiveInputsOp extends TcbOp {
|
|||
}
|
||||
|
||||
const id = new TcbExpr(this.tcb.allocateId());
|
||||
const dirTypeRef = this.tcb.env.referenceTcbType(this.dir.ref);
|
||||
if (!ts.isTypeReferenceNode(dirTypeRef)) {
|
||||
throw new Error(`Expected TypeReferenceNode from reference to ${this.dir.ref.name}`);
|
||||
}
|
||||
const type = new TcbExpr(`(typeof ${dirId.print()})[${quoteAndEscape(fieldName)}]`);
|
||||
const temp = declareVariable(id, type);
|
||||
this.scope.addStatement(temp);
|
||||
|
|
|
|||
|
|
@ -6,14 +6,11 @@
|
|||
* found in the LICENSE file at https://angular.dev/license
|
||||
*/
|
||||
import {TmplAstBoundAttribute, TmplAstDirective, TmplAstTemplate} from '@angular/compiler';
|
||||
import ts from 'typescript';
|
||||
import {TcbOp} from './base';
|
||||
import {declareVariable, getStatementsBlock, TcbExpr} from './codegen';
|
||||
import type {Context} from './context';
|
||||
import type {Scope} from './scope';
|
||||
import {TcbDirectiveMetadata} from '../../api';
|
||||
import {Reference} from '../../../imports';
|
||||
import {ClassDeclaration} from '../../../reflection';
|
||||
import {tcbExpression} from './expression';
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -6,13 +6,7 @@
|
|||
* found in the LICENSE file at https://angular.dev/license
|
||||
*/
|
||||
|
||||
import {
|
||||
ExpressionType,
|
||||
ExternalExpr,
|
||||
TransplantedType,
|
||||
Type,
|
||||
TypeModifier,
|
||||
} from '@angular/compiler';
|
||||
import {ExpressionType, TransplantedType} from '@angular/compiler';
|
||||
import ts from 'typescript';
|
||||
|
||||
import {ErrorCode, FatalDiagnosticError, makeDiagnosticChain} from '../../diagnostics';
|
||||
|
|
@ -77,33 +71,6 @@ export class ReferenceEmitEnvironment {
|
|||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a `ts.TypeNode` from a `TcbReferenceMetadata` object.
|
||||
* This is used by the TCB operations which do not hold on to the original `ts.Declaration`.
|
||||
*
|
||||
* Note: It's important that we do not try to evaluate the `typeParameters` here and pad them
|
||||
* out with `any` type arguments. If we supply `any` to a generic pipe (e.g. `var _pipe1: MyPipe<any>;`),
|
||||
* it destroys the generic constraints and degrades the `transform` signature. When they are omitted
|
||||
* entirely, TypeScript implicitly flags an error, which the Angular compiler filters out, and
|
||||
* crucially recovers by falling back to constraint inference (e.g. `var _pipe1: MyPipe;` infers
|
||||
* bounds safely).
|
||||
*/
|
||||
referenceTcbType(ref: TcbReferenceMetadata): ts.TypeNode {
|
||||
if (ref.unexportedDiagnostic !== null || ref.isLocal || ref.moduleName === null) {
|
||||
if (ref.unexportedDiagnostic !== null) {
|
||||
throw new FatalDiagnosticError(
|
||||
ErrorCode.IMPORT_GENERATION_FAILURE,
|
||||
this.contextFile, // Using context file as fallback origin for external file since we lack exact node
|
||||
makeDiagnosticChain(`Unable to import symbol ${ref.name}.`, [
|
||||
makeDiagnosticChain(ref.unexportedDiagnostic),
|
||||
]),
|
||||
);
|
||||
}
|
||||
return ts.factory.createTypeReferenceNode(ref.name);
|
||||
}
|
||||
return this.referenceExternalType(ref.moduleName, ref.name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a `TcbExpr` from a `TcbReferenceMetadata` object.
|
||||
*/
|
||||
|
|
@ -139,23 +106,6 @@ export class ReferenceEmitEnvironment {
|
|||
throw new Error('Unexpected value returned by import manager');
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a `ts.TypeNode` that references a given type from the provided module.
|
||||
*
|
||||
* This will involve importing the type into the file, and will also add type parameters if
|
||||
* provided.
|
||||
*/
|
||||
referenceExternalType(moduleName: string, name: string, typeParams?: Type[]): ts.TypeNode {
|
||||
const external = new ExternalExpr({moduleName, name});
|
||||
return translateType(
|
||||
new ExpressionType(external, TypeModifier.None, typeParams),
|
||||
this.contextFile,
|
||||
this.reflector,
|
||||
this.refEmitter,
|
||||
this.importManager,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a `ts.TypeNode` representing a type that is being referenced from a different place
|
||||
* in the program. Any type references inside the transplanted type will be rewritten so that
|
||||
|
|
|
|||
|
|
@ -15,7 +15,6 @@ import {
|
|||
TcbInputMapping,
|
||||
TcbPipeMetadata,
|
||||
TypeCheckableDirectiveMeta,
|
||||
TcbTypeParameter,
|
||||
} from '../api';
|
||||
import {Environment} from './environment';
|
||||
import {ImportFlags, ReferenceEmitKind, Reference} from '../../imports';
|
||||
|
|
@ -24,9 +23,7 @@ import {
|
|||
ExternalExpr,
|
||||
TransplantedType,
|
||||
BoundTarget,
|
||||
DirectiveOwner,
|
||||
ReferenceTarget,
|
||||
TmplAstReference,
|
||||
TmplAstElement,
|
||||
TmplAstTemplate,
|
||||
WrappedNodeExpr,
|
||||
|
|
|
|||
|
|
@ -7,21 +7,14 @@
|
|||
*/
|
||||
|
||||
import ts from 'typescript';
|
||||
|
||||
import {
|
||||
TcbComponentMetadata,
|
||||
TcbTypeCheckBlockMetadata,
|
||||
TcbTypeParameter,
|
||||
TypeCheckId,
|
||||
} from '../api';
|
||||
|
||||
import {TcbComponentMetadata, TcbTypeCheckBlockMetadata, TcbTypeParameter} from '../api';
|
||||
import {DomSchemaChecker} from './dom';
|
||||
import {Environment} from './environment';
|
||||
import {OutOfBandDiagnosticRecorder} from './oob';
|
||||
import {createHostBindingsBlockGuard} from './host_bindings';
|
||||
import {Context, TcbGenericContextBehavior} from './ops/context';
|
||||
import {Scope} from './ops/scope';
|
||||
import {getStatementsBlock, tempPrint} from './ops/codegen';
|
||||
import {getStatementsBlock} from './ops/codegen';
|
||||
|
||||
/**
|
||||
* Given a `ts.ClassDeclaration` for a component, and metadata regarding that component, compose a
|
||||
|
|
@ -67,13 +60,7 @@ export function generateTypeCheckBlock(
|
|||
meta.isStandalone,
|
||||
meta.preserveWhitespaces,
|
||||
);
|
||||
const ctxRawType = env.referenceTcbType(component.ref);
|
||||
if (!ts.isTypeReferenceNode(ctxRawType)) {
|
||||
throw new Error(
|
||||
`Expected TypeReferenceNode when referencing the ctx param for ${component.ref.name}`,
|
||||
);
|
||||
}
|
||||
|
||||
const ctxRawType = env.referenceTcbValue(component.ref);
|
||||
let typeParameters: TcbTypeParameter[] | undefined = undefined;
|
||||
let typeArguments: string[] | undefined = undefined;
|
||||
|
||||
|
|
@ -102,7 +89,6 @@ export function generateTypeCheckBlock(
|
|||
}
|
||||
}
|
||||
|
||||
const sourceFile = env.contextFile;
|
||||
const typeParamsStr =
|
||||
typeParameters === undefined || typeParameters.length === 0
|
||||
? ''
|
||||
|
|
@ -111,11 +97,8 @@ export function generateTypeCheckBlock(
|
|||
typeArguments === undefined || typeArguments.length === 0
|
||||
? ''
|
||||
: `<${typeArguments.join(', ')}>`;
|
||||
const typeRef = ts.isIdentifier(ctxRawType.typeName)
|
||||
? ctxRawType.typeName.text
|
||||
: tempPrint(ctxRawType.typeName, sourceFile);
|
||||
|
||||
const thisParamStr = `this: ${typeRef}${typeArgsStr}`;
|
||||
const thisParamStr = `this: ${ctxRawType.print()}${typeArgsStr}`;
|
||||
const statements: string[] = [];
|
||||
|
||||
// Add the template type checking code.
|
||||
|
|
|
|||
|
|
@ -14,20 +14,17 @@ import {TypeCtorMetadata, TcbTypeParameter} from '../api';
|
|||
|
||||
import {ReferenceEmitEnvironment} from './reference_emit_environment';
|
||||
import {checkIfGenericTypeBoundsCanBeEmitted, generateTcbTypeParameters} from './tcb_util';
|
||||
import {quoteAndEscape, TcbExpr, tempPrint} from './ops/codegen';
|
||||
import {quoteAndEscape, TcbExpr} from './ops/codegen';
|
||||
|
||||
export function generateTypeCtorDeclarationFn(
|
||||
env: ReferenceEmitEnvironment,
|
||||
meta: TypeCtorMetadata,
|
||||
nodeTypeRef: ts.EntityName,
|
||||
nodeTypeRef: TcbExpr,
|
||||
typeParams: TcbTypeParameter[] | undefined,
|
||||
): TcbExpr {
|
||||
const typeArgs = generateGenericArgs(typeParams);
|
||||
const typeRef = ts.isIdentifier(nodeTypeRef)
|
||||
? nodeTypeRef.text
|
||||
: tempPrint(nodeTypeRef, nodeTypeRef.getSourceFile());
|
||||
const typeRefWithGenerics = `${typeRef}${typeArgs}`;
|
||||
const initParam = constructTypeCtorParameter(env, meta, typeRef, typeRefWithGenerics);
|
||||
const typeRefWithGenerics = `${nodeTypeRef.print()}${typeArgs}`;
|
||||
const initParam = constructTypeCtorParameter(env, meta, nodeTypeRef.print(), typeRefWithGenerics);
|
||||
const typeParameters = typeParametersWithDefaultTypes(typeParams);
|
||||
let source: string;
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue