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:
parent
166d90d2a9
commit
ae9418c7de
|
@ -116,7 +116,7 @@ export class ComponentDecoratorHandler implements DecoratorHandler<R3ComponentMe
|
||||||
|
|
||||||
const res = compileComponentFromMetadata(analysis, pool, makeBindingParser());
|
const res = compileComponentFromMetadata(analysis, pool, makeBindingParser());
|
||||||
return {
|
return {
|
||||||
field: 'ngComponentDef',
|
name: 'ngComponentDef',
|
||||||
initializer: res.expression,
|
initializer: res.expression,
|
||||||
statements: pool.statements,
|
statements: pool.statements,
|
||||||
type: res.type,
|
type: res.type,
|
||||||
|
|
|
@ -44,7 +44,7 @@ export class DirectiveDecoratorHandler implements DecoratorHandler<R3DirectiveMe
|
||||||
const pool = new ConstantPool();
|
const pool = new ConstantPool();
|
||||||
const res = compileDirectiveFromMetadata(analysis, pool, makeBindingParser());
|
const res = compileDirectiveFromMetadata(analysis, pool, makeBindingParser());
|
||||||
return {
|
return {
|
||||||
field: 'ngDirectiveDef',
|
name: 'ngDirectiveDef',
|
||||||
initializer: res.expression,
|
initializer: res.expression,
|
||||||
statements: pool.statements,
|
statements: pool.statements,
|
||||||
type: res.type,
|
type: res.type,
|
||||||
|
|
|
@ -35,7 +35,7 @@ export class InjectableDecoratorHandler implements DecoratorHandler<R3Injectable
|
||||||
compile(node: ts.ClassDeclaration, analysis: R3InjectableMetadata): CompileResult {
|
compile(node: ts.ClassDeclaration, analysis: R3InjectableMetadata): CompileResult {
|
||||||
const res = compileIvyInjectable(analysis);
|
const res = compileIvyInjectable(analysis);
|
||||||
return {
|
return {
|
||||||
field: 'ngInjectableDef',
|
name: 'ngInjectableDef',
|
||||||
initializer: res.expression,
|
initializer: res.expression,
|
||||||
statements: [],
|
statements: [],
|
||||||
type: res.type,
|
type: res.type,
|
||||||
|
|
|
@ -6,29 +6,36 @@
|
||||||
* found in the LICENSE file at https://angular.io/license
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {ConstantPool, Expression, R3DirectiveMetadata, R3NgModuleMetadata, WrappedNodeExpr, compileNgModule, makeBindingParser, parseTemplate} from '@angular/compiler';
|
import {ConstantPool, Expression, LiteralArrayExpr, R3DirectiveMetadata, R3InjectorMetadata, R3NgModuleMetadata, WrappedNodeExpr, compileInjector, compileNgModule, makeBindingParser, parseTemplate} from '@angular/compiler';
|
||||||
import * as ts from 'typescript';
|
import * as ts from 'typescript';
|
||||||
|
|
||||||
import {Decorator} from '../../host';
|
import {Decorator, ReflectionHost} from '../../host';
|
||||||
import {Reference, ResolvedValue, reflectObjectLiteral, staticallyResolve} from '../../metadata';
|
import {Reference, ResolvedValue, reflectObjectLiteral, staticallyResolve} from '../../metadata';
|
||||||
import {AnalysisOutput, CompileResult, DecoratorHandler} from '../../transform';
|
import {AnalysisOutput, CompileResult, DecoratorHandler} from '../../transform';
|
||||||
|
|
||||||
import {SelectorScopeRegistry} from './selector_scope';
|
import {SelectorScopeRegistry} from './selector_scope';
|
||||||
import {isAngularCore, referenceToExpression} from './util';
|
import {getConstructorDependencies, isAngularCore, referenceToExpression} from './util';
|
||||||
|
|
||||||
|
export interface NgModuleAnalysis {
|
||||||
|
ngModuleDef: R3NgModuleMetadata;
|
||||||
|
ngInjectorDef: R3InjectorMetadata;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Compiles @NgModule annotations to ngModuleDef fields.
|
* Compiles @NgModule annotations to ngModuleDef fields.
|
||||||
*
|
*
|
||||||
* TODO(alxhub): handle injector side of things as well.
|
* TODO(alxhub): handle injector side of things as well.
|
||||||
*/
|
*/
|
||||||
export class NgModuleDecoratorHandler implements DecoratorHandler<R3NgModuleMetadata> {
|
export class NgModuleDecoratorHandler implements DecoratorHandler<NgModuleAnalysis> {
|
||||||
constructor(private checker: ts.TypeChecker, private scopeRegistry: SelectorScopeRegistry) {}
|
constructor(
|
||||||
|
private checker: ts.TypeChecker, private reflector: ReflectionHost,
|
||||||
|
private scopeRegistry: SelectorScopeRegistry) {}
|
||||||
|
|
||||||
detect(decorators: Decorator[]): Decorator|undefined {
|
detect(decorators: Decorator[]): Decorator|undefined {
|
||||||
return decorators.find(decorator => decorator.name === 'NgModule' && isAngularCore(decorator));
|
return decorators.find(decorator => decorator.name === 'NgModule' && isAngularCore(decorator));
|
||||||
}
|
}
|
||||||
|
|
||||||
analyze(node: ts.ClassDeclaration, decorator: Decorator): AnalysisOutput<R3NgModuleMetadata> {
|
analyze(node: ts.ClassDeclaration, decorator: Decorator): AnalysisOutput<NgModuleAnalysis> {
|
||||||
if (decorator.args === null || decorator.args.length !== 1) {
|
if (decorator.args === null || decorator.args.length !== 1) {
|
||||||
throw new Error(`Incorrect number of arguments to @NgModule decorator`);
|
throw new Error(`Incorrect number of arguments to @NgModule decorator`);
|
||||||
}
|
}
|
||||||
|
@ -66,26 +73,51 @@ export class NgModuleDecoratorHandler implements DecoratorHandler<R3NgModuleMeta
|
||||||
|
|
||||||
const context = node.getSourceFile();
|
const context = node.getSourceFile();
|
||||||
|
|
||||||
|
const ngModuleDef: R3NgModuleMetadata = {
|
||||||
|
type: new WrappedNodeExpr(node.name !),
|
||||||
|
bootstrap: [],
|
||||||
|
declarations: declarations.map(decl => referenceToExpression(decl, context)),
|
||||||
|
exports: exports.map(exp => referenceToExpression(exp, context)),
|
||||||
|
imports: imports.map(imp => referenceToExpression(imp, context)),
|
||||||
|
emitInline: false,
|
||||||
|
};
|
||||||
|
|
||||||
|
const providers: Expression = ngModule.has('providers') ?
|
||||||
|
new WrappedNodeExpr(ngModule.get('providers') !) :
|
||||||
|
new LiteralArrayExpr([]);
|
||||||
|
|
||||||
|
const ngInjectorDef: R3InjectorMetadata = {
|
||||||
|
name: node.name !.text,
|
||||||
|
type: new WrappedNodeExpr(node.name !),
|
||||||
|
deps: getConstructorDependencies(node, this.reflector), providers,
|
||||||
|
imports: new LiteralArrayExpr(
|
||||||
|
[...imports, ...exports].map(imp => referenceToExpression(imp, context))),
|
||||||
|
};
|
||||||
|
|
||||||
return {
|
return {
|
||||||
analysis: {
|
analysis: {
|
||||||
type: new WrappedNodeExpr(node.name !),
|
ngModuleDef, ngInjectorDef,
|
||||||
bootstrap: [],
|
|
||||||
declarations: declarations.map(decl => referenceToExpression(decl, context)),
|
|
||||||
exports: exports.map(exp => referenceToExpression(exp, context)),
|
|
||||||
imports: imports.map(imp => referenceToExpression(imp, context)),
|
|
||||||
emitInline: false,
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
compile(node: ts.ClassDeclaration, analysis: R3NgModuleMetadata): CompileResult {
|
compile(node: ts.ClassDeclaration, analysis: NgModuleAnalysis): CompileResult[] {
|
||||||
const res = compileNgModule(analysis);
|
const ngInjectorDef = compileInjector(analysis.ngInjectorDef);
|
||||||
return {
|
const ngModuleDef = compileNgModule(analysis.ngModuleDef);
|
||||||
field: 'ngModuleDef',
|
return [
|
||||||
initializer: res.expression,
|
{
|
||||||
statements: [],
|
name: 'ngModuleDef',
|
||||||
type: res.type,
|
initializer: ngModuleDef.expression,
|
||||||
};
|
statements: [],
|
||||||
|
type: ngModuleDef.type,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'ngInjectorDef',
|
||||||
|
initializer: ngInjectorDef.expression,
|
||||||
|
statements: [],
|
||||||
|
type: ngInjectorDef.type,
|
||||||
|
},
|
||||||
|
];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -99,7 +99,7 @@ export class NgtscProgram implements api.Program {
|
||||||
new ComponentDecoratorHandler(checker, reflector, scopeRegistry),
|
new ComponentDecoratorHandler(checker, reflector, scopeRegistry),
|
||||||
new DirectiveDecoratorHandler(checker, reflector, scopeRegistry),
|
new DirectiveDecoratorHandler(checker, reflector, scopeRegistry),
|
||||||
new InjectableDecoratorHandler(reflector),
|
new InjectableDecoratorHandler(reflector),
|
||||||
new NgModuleDecoratorHandler(checker, scopeRegistry),
|
new NgModuleDecoratorHandler(checker, reflector, scopeRegistry),
|
||||||
];
|
];
|
||||||
const compilation = new IvyCompilation(handlers, checker, reflector);
|
const compilation = new IvyCompilation(handlers, checker, reflector);
|
||||||
|
|
||||||
|
|
|
@ -37,7 +37,7 @@ export interface DecoratorHandler<A> {
|
||||||
* Generate a description of the field which should be added to the class, including any
|
* Generate a description of the field which should be added to the class, including any
|
||||||
* initialization code to be generated.
|
* 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.
|
* and a type for the .d.ts file.
|
||||||
*/
|
*/
|
||||||
export interface CompileResult {
|
export interface CompileResult {
|
||||||
field: string;
|
name: string;
|
||||||
initializer: Expression;
|
initializer: Expression;
|
||||||
statements: Statement[];
|
statements: Statement[];
|
||||||
type: Type;
|
type: Type;
|
||||||
|
|
|
@ -107,7 +107,7 @@ export class IvyCompilation {
|
||||||
* Perform a compilation operation on the given class declaration and return instructions to an
|
* Perform a compilation operation on the given class declaration and return instructions to an
|
||||||
* AST transformer if any are available.
|
* 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.
|
// Look to see whether the original node was analyzed. If not, there's nothing to do.
|
||||||
const original = ts.getOriginalNode(node) as ts.Declaration;
|
const original = ts.getOriginalNode(node) as ts.Declaration;
|
||||||
if (!this.analysis.has(original)) {
|
if (!this.analysis.has(original)) {
|
||||||
|
@ -116,7 +116,10 @@ export class IvyCompilation {
|
||||||
const op = this.analysis.get(original) !;
|
const op = this.analysis.get(original) !;
|
||||||
|
|
||||||
// Run the actual compilation, which generates an Expression for the Ivy field.
|
// 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,
|
// 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.
|
// 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.
|
* Processes .d.ts file text and adds static field declarations, with types.
|
||||||
*/
|
*/
|
||||||
export class DtsFileTransformer {
|
export class DtsFileTransformer {
|
||||||
private ivyFields = new Map<string, CompileResult>();
|
private ivyFields = new Map<string, CompileResult[]>();
|
||||||
private imports = new ImportManager();
|
private imports = new ImportManager();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Track that a static field was added to the code for a class.
|
* 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.
|
* 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];
|
const stmt = dtsFile.statements[i];
|
||||||
if (ts.isClassDeclaration(stmt) && stmt.name !== undefined &&
|
if (ts.isClassDeclaration(stmt) && stmt.name !== undefined &&
|
||||||
this.ivyFields.has(stmt.name.text)) {
|
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 before = dts.substring(0, stmt.end - 1);
|
||||||
const after = dts.substring(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 {VisitListEntryResult, Visitor, visit} from '../../util/src/visitor';
|
||||||
|
|
||||||
|
import {CompileResult} from './api';
|
||||||
import {IvyCompilation} from './compilation';
|
import {IvyCompilation} from './compilation';
|
||||||
import {ImportManager, translateExpression, translateStatement} from './translator';
|
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
|
// Determine if this class has an Ivy field that needs to be added, and compile the field
|
||||||
// to an expression if so.
|
// to an expression if so.
|
||||||
const res = this.compilation.compileIvyFieldFor(node);
|
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.
|
if (res !== undefined) {
|
||||||
const property = ts.createProperty(
|
// There is at least one field to add.
|
||||||
undefined, [ts.createToken(ts.SyntaxKind.StaticKeyword)], res.field, undefined, undefined,
|
const statements: ts.Statement[] = [];
|
||||||
exprNode);
|
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.
|
// Replace the class declaration with an updated version.
|
||||||
node = ts.updateClassDeclaration(
|
node = ts.updateClassDeclaration(
|
||||||
|
@ -48,9 +61,7 @@ class IvyVisitor extends Visitor {
|
||||||
// Remove the decorator which triggered this compilation, leaving the others alone.
|
// Remove the decorator which triggered this compilation, leaving the others alone.
|
||||||
maybeFilterDecorator(
|
maybeFilterDecorator(
|
||||||
node.decorators, this.compilation.ivyDecoratorFor(node) !.node as ts.Decorator),
|
node.decorators, this.compilation.ivyDecoratorFor(node) !.node as ts.Decorator),
|
||||||
node.modifiers, node.name, node.typeParameters, node.heritageClauses || [],
|
node.modifiers, node.name, node.typeParameters, node.heritageClauses || [], members);
|
||||||
[...node.members, property]);
|
|
||||||
const statements = res.statements.map(stmt => translateStatement(stmt, this.importManager));
|
|
||||||
return {node, before: statements};
|
return {node, before: statements};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -180,4 +180,46 @@ describe('ngtsc behavioral tests', () => {
|
||||||
.toContain('static ngModuleDef: i0.NgModuleDef<TestModule, [TestCmp], [], []>');
|
.toContain('static ngModuleDef: i0.NgModuleDef<TestModule, [TestCmp], [], []>');
|
||||||
expect(dtsContents).not.toContain('__decorate');
|
expect(dtsContents).not.toContain('__decorate');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should compile NgModules with services without errors', () => {
|
||||||
|
writeConfig();
|
||||||
|
write('test.ts', `
|
||||||
|
import {Component, NgModule} from '@angular/core';
|
||||||
|
|
||||||
|
export class Token {}
|
||||||
|
|
||||||
|
@NgModule({})
|
||||||
|
export class OtherModule {}
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'test-cmp',
|
||||||
|
template: 'this is a test',
|
||||||
|
})
|
||||||
|
export class TestCmp {}
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
declarations: [TestCmp],
|
||||||
|
providers: [{provide: Token, useValue: 'test'}],
|
||||||
|
imports: [OtherModule],
|
||||||
|
})
|
||||||
|
export class TestModule {}
|
||||||
|
`);
|
||||||
|
|
||||||
|
const exitCode = main(['-p', basePath], errorSpy);
|
||||||
|
expect(errorSpy).not.toHaveBeenCalled();
|
||||||
|
expect(exitCode).toBe(0);
|
||||||
|
|
||||||
|
const jsContents = getContents('test.js');
|
||||||
|
expect(jsContents).toContain('i0.ɵdefineNgModule({ type: TestModule,');
|
||||||
|
expect(jsContents)
|
||||||
|
.toContain(
|
||||||
|
`TestModule.ngInjectorDef = i0.defineInjector({ factory: ` +
|
||||||
|
`function TestModule_Factory() { return new TestModule(); }, providers: [{ provide: ` +
|
||||||
|
`Token, useValue: 'test' }], imports: [OtherModule] });`);
|
||||||
|
|
||||||
|
const dtsContents = getContents('test.d.ts');
|
||||||
|
expect(dtsContents)
|
||||||
|
.toContain('static ngModuleDef: i0.NgModuleDef<TestModule, [TestCmp], [OtherModule], []>');
|
||||||
|
expect(dtsContents).toContain('static ngInjectorDef: i0.InjectorDef');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -83,7 +83,7 @@ export * from './injectable_compiler_2';
|
||||||
export * from './render3/view/api';
|
export * from './render3/view/api';
|
||||||
export {jitExpression} from './render3/r3_jit';
|
export {jitExpression} from './render3/r3_jit';
|
||||||
export {R3DependencyMetadata, R3FactoryMetadata, R3ResolvedDependencyType} from './render3/r3_factory';
|
export {R3DependencyMetadata, R3FactoryMetadata, R3ResolvedDependencyType} from './render3/r3_factory';
|
||||||
export {compileNgModule, R3NgModuleMetadata} from './render3/r3_module_compiler';
|
export {compileInjector, compileNgModule, R3InjectorMetadata, R3NgModuleMetadata} from './render3/r3_module_compiler';
|
||||||
export {makeBindingParser, parseTemplate} from './render3/view/template';
|
export {makeBindingParser, parseTemplate} from './render3/view/template';
|
||||||
export {compileComponentFromMetadata, compileDirectiveFromMetadata, parseHostBindings} from './render3/view/compiler';
|
export {compileComponentFromMetadata, compileDirectiveFromMetadata, parseHostBindings} from './render3/view/compiler';
|
||||||
// This file only reexports content of the `src` folder. Keep it that way.
|
// This file only reexports content of the `src` folder. Keep it that way.
|
|
@ -114,6 +114,11 @@ export class Identifiers {
|
||||||
moduleName: CORE,
|
moduleName: CORE,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static InjectorDef: o.ExternalReference = {
|
||||||
|
name: 'InjectorDef',
|
||||||
|
moduleName: CORE,
|
||||||
|
};
|
||||||
|
|
||||||
static defineInjector: o.ExternalReference = {
|
static defineInjector: o.ExternalReference = {
|
||||||
name: 'defineInjector',
|
name: 'defineInjector',
|
||||||
moduleName: CORE,
|
moduleName: CORE,
|
||||||
|
|
|
@ -13,6 +13,7 @@ import {mapLiteral} from '../output/map_util';
|
||||||
import * as o from '../output/output_ast';
|
import * as o from '../output/output_ast';
|
||||||
import {OutputContext} from '../util';
|
import {OutputContext} from '../util';
|
||||||
|
|
||||||
|
import {R3DependencyMetadata, compileFactoryFunction} from './r3_factory';
|
||||||
import {Identifiers as R3} from './r3_identifiers';
|
import {Identifiers as R3} from './r3_identifiers';
|
||||||
import {convertMetaToOutput, mapToMapExpression} from './util';
|
import {convertMetaToOutput, mapToMapExpression} from './util';
|
||||||
|
|
||||||
|
@ -81,6 +82,35 @@ export function compileNgModule(meta: R3NgModuleMetadata): R3NgModuleDef {
|
||||||
return {expression, type, additionalStatements};
|
return {expression, type, additionalStatements};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface R3InjectorDef {
|
||||||
|
expression: o.Expression;
|
||||||
|
type: o.Type;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface R3InjectorMetadata {
|
||||||
|
name: string;
|
||||||
|
type: o.Expression;
|
||||||
|
deps: R3DependencyMetadata[];
|
||||||
|
providers: o.Expression;
|
||||||
|
imports: o.Expression;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function compileInjector(meta: R3InjectorMetadata): R3InjectorDef {
|
||||||
|
const expression = o.importExpr(R3.defineInjector).callFn([mapToMapExpression({
|
||||||
|
factory: compileFactoryFunction({
|
||||||
|
name: meta.name,
|
||||||
|
fnOrClass: meta.type,
|
||||||
|
deps: meta.deps,
|
||||||
|
useNew: true,
|
||||||
|
injectFn: R3.inject,
|
||||||
|
}),
|
||||||
|
providers: meta.providers,
|
||||||
|
imports: meta.imports,
|
||||||
|
})]);
|
||||||
|
const type = new o.ExpressionType(o.importExpr(R3.InjectorDef));
|
||||||
|
return {expression, type};
|
||||||
|
}
|
||||||
|
|
||||||
// TODO(alxhub): integrate this with `compileNgModule`. Currently the two are separate operations.
|
// TODO(alxhub): integrate this with `compileNgModule`. Currently the two are separate operations.
|
||||||
export function compileNgModuleFromRender2(
|
export function compileNgModuleFromRender2(
|
||||||
ctx: OutputContext, ngModule: CompileShallowModuleMetadata,
|
ctx: OutputContext, ngModule: CompileShallowModuleMetadata,
|
||||||
|
|
Loading…
Reference in New Issue