feat(ivy): generate ngInjectorDef for @NgModule in AOT mode (#24632)
This change generates ngInjectorDef as well as ngModuleDef for @NgModule annotated types, reflecting the dual nature of @NgModules as both compilation scopes and as DI configuration containers. This required implementing ngInjectorDef compilation in @angular/compiler as well as allowing for multiple generated definitions for a single decorator in the core of ngtsc. PR Close #24632
This commit is contained in:

committed by
Jason Aden

parent
166d90d2a9
commit
ae9418c7de
@ -37,7 +37,7 @@ export interface DecoratorHandler<A> {
|
||||
* Generate a description of the field which should be added to the class, including any
|
||||
* initialization code to be generated.
|
||||
*/
|
||||
compile(node: ts.Declaration, analysis: A): CompileResult;
|
||||
compile(node: ts.Declaration, analysis: A): CompileResult|CompileResult[];
|
||||
}
|
||||
|
||||
/**
|
||||
@ -55,7 +55,7 @@ export interface AnalysisOutput<A> {
|
||||
* and a type for the .d.ts file.
|
||||
*/
|
||||
export interface CompileResult {
|
||||
field: string;
|
||||
name: string;
|
||||
initializer: Expression;
|
||||
statements: Statement[];
|
||||
type: Type;
|
||||
|
@ -107,7 +107,7 @@ export class IvyCompilation {
|
||||
* Perform a compilation operation on the given class declaration and return instructions to an
|
||||
* AST transformer if any are available.
|
||||
*/
|
||||
compileIvyFieldFor(node: ts.Declaration): CompileResult|undefined {
|
||||
compileIvyFieldFor(node: ts.Declaration): CompileResult[]|undefined {
|
||||
// Look to see whether the original node was analyzed. If not, there's nothing to do.
|
||||
const original = ts.getOriginalNode(node) as ts.Declaration;
|
||||
if (!this.analysis.has(original)) {
|
||||
@ -116,7 +116,10 @@ export class IvyCompilation {
|
||||
const op = this.analysis.get(original) !;
|
||||
|
||||
// Run the actual compilation, which generates an Expression for the Ivy field.
|
||||
const res = op.adapter.compile(node, op.analysis);
|
||||
let res: CompileResult|CompileResult[] = op.adapter.compile(node, op.analysis);
|
||||
if (!Array.isArray(res)) {
|
||||
res = [res];
|
||||
}
|
||||
|
||||
// Look up the .d.ts transformer for the input file and record that a field was generated,
|
||||
// which will allow the .d.ts to be transformed later.
|
||||
|
@ -17,13 +17,13 @@ import {ImportManager, translateType} from './translator';
|
||||
* Processes .d.ts file text and adds static field declarations, with types.
|
||||
*/
|
||||
export class DtsFileTransformer {
|
||||
private ivyFields = new Map<string, CompileResult>();
|
||||
private ivyFields = new Map<string, CompileResult[]>();
|
||||
private imports = new ImportManager();
|
||||
|
||||
/**
|
||||
* Track that a static field was added to the code for a class.
|
||||
*/
|
||||
recordStaticField(name: string, decl: CompileResult): void { this.ivyFields.set(name, decl); }
|
||||
recordStaticField(name: string, decls: CompileResult[]): void { this.ivyFields.set(name, decls); }
|
||||
|
||||
/**
|
||||
* Process the .d.ts text for a file and add any declarations which were recorded.
|
||||
@ -36,11 +36,18 @@ export class DtsFileTransformer {
|
||||
const stmt = dtsFile.statements[i];
|
||||
if (ts.isClassDeclaration(stmt) && stmt.name !== undefined &&
|
||||
this.ivyFields.has(stmt.name.text)) {
|
||||
const desc = this.ivyFields.get(stmt.name.text) !;
|
||||
const decls = this.ivyFields.get(stmt.name.text) !;
|
||||
const before = dts.substring(0, stmt.end - 1);
|
||||
const after = dts.substring(stmt.end - 1);
|
||||
const type = translateType(desc.type, this.imports);
|
||||
dts = before + ` static ${desc.field}: ${type};\n` + after;
|
||||
|
||||
dts = before +
|
||||
decls
|
||||
.map(decl => {
|
||||
const type = translateType(decl.type, this.imports);
|
||||
return ` static ${decl.name}: ${type};\n`;
|
||||
})
|
||||
.join('') +
|
||||
after;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -11,6 +11,7 @@ import * as ts from 'typescript';
|
||||
|
||||
import {VisitListEntryResult, Visitor, visit} from '../../util/src/visitor';
|
||||
|
||||
import {CompileResult} from './api';
|
||||
import {IvyCompilation} from './compilation';
|
||||
import {ImportManager, translateExpression, translateStatement} from './translator';
|
||||
|
||||
@ -33,14 +34,26 @@ class IvyVisitor extends Visitor {
|
||||
// Determine if this class has an Ivy field that needs to be added, and compile the field
|
||||
// to an expression if so.
|
||||
const res = this.compilation.compileIvyFieldFor(node);
|
||||
if (res !== undefined) {
|
||||
// There is a field to add. Translate the initializer for the field into TS nodes.
|
||||
const exprNode = translateExpression(res.initializer, this.importManager);
|
||||
|
||||
// Create a static property declaration for the new field.
|
||||
const property = ts.createProperty(
|
||||
undefined, [ts.createToken(ts.SyntaxKind.StaticKeyword)], res.field, undefined, undefined,
|
||||
exprNode);
|
||||
if (res !== undefined) {
|
||||
// There is at least one field to add.
|
||||
const statements: ts.Statement[] = [];
|
||||
const members = [...node.members];
|
||||
|
||||
res.forEach(field => {
|
||||
// Translate the initializer for the field into TS nodes.
|
||||
const exprNode = translateExpression(field.initializer, this.importManager);
|
||||
|
||||
// Create a static property declaration for the new field.
|
||||
const property = ts.createProperty(
|
||||
undefined, [ts.createToken(ts.SyntaxKind.StaticKeyword)], field.name, undefined,
|
||||
undefined, exprNode);
|
||||
|
||||
field.statements.map(stmt => translateStatement(stmt, this.importManager))
|
||||
.forEach(stmt => statements.push(stmt));
|
||||
|
||||
members.push(property);
|
||||
});
|
||||
|
||||
// Replace the class declaration with an updated version.
|
||||
node = ts.updateClassDeclaration(
|
||||
@ -48,9 +61,7 @@ class IvyVisitor extends Visitor {
|
||||
// Remove the decorator which triggered this compilation, leaving the others alone.
|
||||
maybeFilterDecorator(
|
||||
node.decorators, this.compilation.ivyDecoratorFor(node) !.node as ts.Decorator),
|
||||
node.modifiers, node.name, node.typeParameters, node.heritageClauses || [],
|
||||
[...node.members, property]);
|
||||
const statements = res.statements.map(stmt => translateStatement(stmt, this.importManager));
|
||||
node.modifiers, node.name, node.typeParameters, node.heritageClauses || [], members);
|
||||
return {node, before: statements};
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user