2016-06-28 12:54:42 -04:00
|
|
|
/**
|
|
|
|
* @license
|
|
|
|
* Copyright Google Inc. All Rights Reserved.
|
|
|
|
*
|
|
|
|
* Use of this source code is governed by an MIT-style license that can be
|
|
|
|
* found in the LICENSE file at https://angular.io/license
|
|
|
|
*/
|
|
|
|
|
|
|
|
import {Injectable} from '@angular/core';
|
|
|
|
|
2016-07-18 06:50:31 -04:00
|
|
|
import {CompileDiDependencyMetadata, CompileIdentifierMap, CompileIdentifierMetadata, CompileNgModuleMetadata, CompileProviderMetadata, CompileTokenMetadata} from './compile_metadata';
|
2016-06-28 12:54:42 -04:00
|
|
|
import {isBlank, isPresent} from './facade/lang';
|
|
|
|
import {Identifiers, identifierToken} from './identifiers';
|
|
|
|
import * as o from './output/output_ast';
|
2016-06-30 16:34:15 -04:00
|
|
|
import {convertValueToOutputAst} from './output/value_util';
|
2016-06-28 12:54:42 -04:00
|
|
|
import {ParseLocation, ParseSourceFile, ParseSourceSpan} from './parse_util';
|
2016-07-18 06:50:31 -04:00
|
|
|
import {NgModuleProviderAnalyzer} from './provider_analyzer';
|
2016-06-28 12:54:42 -04:00
|
|
|
import {ProviderAst, ProviderAstType} from './template_ast';
|
|
|
|
import {createDiTokenExpression} from './util';
|
|
|
|
|
|
|
|
export class ComponentFactoryDependency {
|
|
|
|
constructor(
|
|
|
|
public comp: CompileIdentifierMetadata, public placeholder: CompileIdentifierMetadata) {}
|
|
|
|
}
|
|
|
|
|
2016-07-18 06:50:31 -04:00
|
|
|
export class NgModuleCompileResult {
|
2016-06-28 12:54:42 -04:00
|
|
|
constructor(
|
2016-07-18 06:50:31 -04:00
|
|
|
public statements: o.Statement[], public ngModuleFactoryVar: string,
|
2016-06-28 12:54:42 -04:00
|
|
|
public dependencies: ComponentFactoryDependency[]) {}
|
|
|
|
}
|
|
|
|
|
|
|
|
@Injectable()
|
2016-07-18 06:50:31 -04:00
|
|
|
export class NgModuleCompiler {
|
|
|
|
compile(ngModuleMeta: CompileNgModuleMetadata, extraProviders: CompileProviderMetadata[]):
|
|
|
|
NgModuleCompileResult {
|
|
|
|
var sourceFileName = isPresent(ngModuleMeta.type.moduleUrl) ?
|
|
|
|
`in NgModule ${ngModuleMeta.type.name} in ${ngModuleMeta.type.moduleUrl}` :
|
|
|
|
`in NgModule ${ngModuleMeta.type.name}`;
|
2016-06-28 12:54:42 -04:00
|
|
|
var sourceFile = new ParseSourceFile('', sourceFileName);
|
|
|
|
var sourceSpan = new ParseSourceSpan(
|
|
|
|
new ParseLocation(sourceFile, null, null, null),
|
|
|
|
new ParseLocation(sourceFile, null, null, null));
|
|
|
|
var deps: ComponentFactoryDependency[] = [];
|
2016-07-18 06:50:31 -04:00
|
|
|
var precompileComponents = ngModuleMeta.transitiveModule.precompile.map((precompileComp) => {
|
2016-06-28 12:54:42 -04:00
|
|
|
var id = new CompileIdentifierMetadata({name: precompileComp.name});
|
|
|
|
deps.push(new ComponentFactoryDependency(precompileComp, id));
|
|
|
|
return id;
|
|
|
|
});
|
2016-07-18 06:50:31 -04:00
|
|
|
var builder = new _InjectorBuilder(ngModuleMeta, precompileComponents, sourceSpan);
|
2016-06-28 12:54:42 -04:00
|
|
|
|
2016-07-18 06:50:31 -04:00
|
|
|
var providerParser = new NgModuleProviderAnalyzer(ngModuleMeta, extraProviders, sourceSpan);
|
2016-06-28 12:54:42 -04:00
|
|
|
providerParser.parse().forEach((provider) => builder.addProvider(provider));
|
|
|
|
var injectorClass = builder.build();
|
2016-07-18 06:50:31 -04:00
|
|
|
var ngModuleFactoryVar = `${ngModuleMeta.type.name}NgFactory`;
|
|
|
|
var ngModuleFactoryStmt =
|
|
|
|
o.variable(ngModuleFactoryVar)
|
|
|
|
.set(o.importExpr(Identifiers.NgModuleFactory)
|
2016-06-28 12:54:42 -04:00
|
|
|
.instantiate(
|
2016-07-18 06:50:31 -04:00
|
|
|
[o.variable(injectorClass.name), o.importExpr(ngModuleMeta.type)],
|
2016-06-28 12:54:42 -04:00
|
|
|
o.importType(
|
2016-07-18 06:50:31 -04:00
|
|
|
Identifiers.NgModuleFactory, [o.importType(ngModuleMeta.type)],
|
2016-06-28 12:54:42 -04:00
|
|
|
[o.TypeModifier.Const])))
|
|
|
|
.toDeclStmt(null, [o.StmtModifier.Final]);
|
|
|
|
|
2016-07-18 06:50:31 -04:00
|
|
|
return new NgModuleCompileResult(
|
|
|
|
[injectorClass, ngModuleFactoryStmt], ngModuleFactoryVar, deps);
|
2016-06-28 12:54:42 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
class _InjectorBuilder {
|
2016-07-18 06:50:31 -04:00
|
|
|
private _instances = new CompileIdentifierMap<CompileTokenMetadata, o.Expression>();
|
2016-06-28 12:54:42 -04:00
|
|
|
private _fields: o.ClassField[] = [];
|
|
|
|
private _createStmts: o.Statement[] = [];
|
|
|
|
private _getters: o.ClassGetter[] = [];
|
|
|
|
|
|
|
|
constructor(
|
2016-07-18 06:50:31 -04:00
|
|
|
private _ngModuleMeta: CompileNgModuleMetadata,
|
2016-06-28 12:54:42 -04:00
|
|
|
private _precompileComponents: CompileIdentifierMetadata[],
|
|
|
|
private _sourceSpan: ParseSourceSpan) {}
|
|
|
|
|
|
|
|
addProvider(resolvedProvider: ProviderAst) {
|
|
|
|
var providerValueExpressions =
|
|
|
|
resolvedProvider.providers.map((provider) => this._getProviderValue(provider));
|
|
|
|
var propName = `_${resolvedProvider.token.name}_${this._instances.size}`;
|
|
|
|
var instance = this._createProviderProperty(
|
|
|
|
propName, resolvedProvider, providerValueExpressions, resolvedProvider.multiProvider,
|
|
|
|
resolvedProvider.eager);
|
|
|
|
this._instances.add(resolvedProvider.token, instance);
|
|
|
|
}
|
|
|
|
|
|
|
|
build(): o.ClassStmt {
|
|
|
|
let getMethodStmts: o.Statement[] = this._instances.keys().map((token) => {
|
|
|
|
var providerExpr = this._instances.get(token);
|
|
|
|
return new o.IfStmt(
|
|
|
|
InjectMethodVars.token.identical(createDiTokenExpression(token)),
|
|
|
|
[new o.ReturnStatement(providerExpr)]);
|
|
|
|
});
|
|
|
|
var methods = [
|
|
|
|
new o.ClassMethod(
|
|
|
|
'createInternal', [], this._createStmts.concat(
|
2016-07-18 06:50:31 -04:00
|
|
|
new o.ReturnStatement(this._instances.get(identifierToken(this._ngModuleMeta.type)))
|
|
|
|
), o.importType(this._ngModuleMeta.type)
|
2016-06-28 12:54:42 -04:00
|
|
|
),
|
|
|
|
new o.ClassMethod(
|
|
|
|
'getInternal',
|
|
|
|
[
|
|
|
|
new o.FnParam(InjectMethodVars.token.name, o.DYNAMIC_TYPE),
|
|
|
|
new o.FnParam(InjectMethodVars.notFoundResult.name, o.DYNAMIC_TYPE)
|
|
|
|
],
|
|
|
|
getMethodStmts.concat([new o.ReturnStatement(InjectMethodVars.notFoundResult)]),
|
|
|
|
o.DYNAMIC_TYPE)
|
|
|
|
];
|
|
|
|
|
|
|
|
var ctor = new o.ClassMethod(
|
|
|
|
null, [new o.FnParam(InjectorProps.parent.name, o.importType(Identifiers.Injector))],
|
|
|
|
[o.SUPER_EXPR
|
|
|
|
.callFn([
|
|
|
|
o.variable(InjectorProps.parent.name),
|
|
|
|
o.literalArr(this._precompileComponents.map(
|
|
|
|
(precompiledComponent) => o.importExpr(precompiledComponent)))
|
|
|
|
])
|
|
|
|
.toStmt()]);
|
|
|
|
|
2016-07-18 06:50:31 -04:00
|
|
|
var injClassName = `${this._ngModuleMeta.type.name}Injector`;
|
2016-06-28 12:54:42 -04:00
|
|
|
return new o.ClassStmt(
|
|
|
|
injClassName,
|
2016-07-18 06:50:31 -04:00
|
|
|
o.importExpr(Identifiers.NgModuleInjector, [o.importType(this._ngModuleMeta.type)]),
|
2016-06-28 12:54:42 -04:00
|
|
|
this._fields, this._getters, ctor, methods);
|
|
|
|
}
|
|
|
|
|
|
|
|
private _getProviderValue(provider: CompileProviderMetadata): o.Expression {
|
|
|
|
var result: o.Expression;
|
|
|
|
if (isPresent(provider.useExisting)) {
|
|
|
|
result = this._getDependency(new CompileDiDependencyMetadata({token: provider.useExisting}));
|
|
|
|
} else if (isPresent(provider.useFactory)) {
|
|
|
|
var deps = isPresent(provider.deps) ? provider.deps : provider.useFactory.diDeps;
|
|
|
|
var depsExpr = deps.map((dep) => this._getDependency(dep));
|
|
|
|
result = o.importExpr(provider.useFactory).callFn(depsExpr);
|
|
|
|
} else if (isPresent(provider.useClass)) {
|
|
|
|
var deps = isPresent(provider.deps) ? provider.deps : provider.useClass.diDeps;
|
|
|
|
var depsExpr = deps.map((dep) => this._getDependency(dep));
|
|
|
|
result =
|
|
|
|
o.importExpr(provider.useClass).instantiate(depsExpr, o.importType(provider.useClass));
|
|
|
|
} else {
|
2016-06-30 16:34:15 -04:00
|
|
|
result = convertValueToOutputAst(provider.useValue);
|
2016-06-28 12:54:42 -04:00
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private _createProviderProperty(
|
|
|
|
propName: string, provider: ProviderAst, providerValueExpressions: o.Expression[],
|
|
|
|
isMulti: boolean, isEager: boolean): o.Expression {
|
|
|
|
var resolvedProviderValueExpr: o.Expression;
|
|
|
|
var type: o.Type;
|
|
|
|
if (isMulti) {
|
|
|
|
resolvedProviderValueExpr = o.literalArr(providerValueExpressions);
|
|
|
|
type = new o.ArrayType(o.DYNAMIC_TYPE);
|
|
|
|
} else {
|
|
|
|
resolvedProviderValueExpr = providerValueExpressions[0];
|
|
|
|
type = providerValueExpressions[0].type;
|
|
|
|
}
|
|
|
|
if (isBlank(type)) {
|
|
|
|
type = o.DYNAMIC_TYPE;
|
|
|
|
}
|
|
|
|
if (isEager) {
|
|
|
|
this._fields.push(new o.ClassField(propName, type));
|
|
|
|
this._createStmts.push(o.THIS_EXPR.prop(propName).set(resolvedProviderValueExpr).toStmt());
|
|
|
|
} else {
|
|
|
|
var internalField = `_${propName}`;
|
|
|
|
this._fields.push(new o.ClassField(internalField, type));
|
|
|
|
// Note: Equals is important for JS so that it also checks the undefined case!
|
|
|
|
var getterStmts = [
|
|
|
|
new o.IfStmt(
|
|
|
|
o.THIS_EXPR.prop(internalField).isBlank(),
|
|
|
|
[o.THIS_EXPR.prop(internalField).set(resolvedProviderValueExpr).toStmt()]),
|
|
|
|
new o.ReturnStatement(o.THIS_EXPR.prop(internalField))
|
|
|
|
];
|
|
|
|
this._getters.push(new o.ClassGetter(propName, getterStmts, type));
|
|
|
|
}
|
|
|
|
return o.THIS_EXPR.prop(propName);
|
|
|
|
}
|
|
|
|
|
|
|
|
private _getDependency(dep: CompileDiDependencyMetadata): o.Expression {
|
|
|
|
var result: o.Expression = null;
|
|
|
|
if (dep.isValue) {
|
|
|
|
result = o.literal(dep.value);
|
|
|
|
}
|
|
|
|
if (!dep.isSkipSelf) {
|
|
|
|
if (dep.token &&
|
|
|
|
(dep.token.equalsTo(identifierToken(Identifiers.Injector)) ||
|
|
|
|
dep.token.equalsTo(identifierToken(Identifiers.ComponentFactoryResolver)))) {
|
|
|
|
result = o.THIS_EXPR;
|
|
|
|
}
|
|
|
|
if (isBlank(result)) {
|
|
|
|
result = this._instances.get(dep.token);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (isBlank(result)) {
|
|
|
|
var args = [createDiTokenExpression(dep.token)];
|
|
|
|
if (dep.isOptional) {
|
|
|
|
args.push(o.NULL_EXPR);
|
|
|
|
}
|
|
|
|
result = InjectorProps.parent.callMethod('get', args);
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
class InjectorProps {
|
|
|
|
static parent = o.THIS_EXPR.prop('parent');
|
|
|
|
}
|
|
|
|
|
|
|
|
class InjectMethodVars {
|
|
|
|
static token = o.variable('token');
|
|
|
|
static notFoundResult = o.variable('notFoundResult');
|
|
|
|
}
|