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
|
|
|
|
*/
|
|
|
|
|
2017-02-17 15:55:55 -05:00
|
|
|
import {ɵLifecycleHooks} from '@angular/core';
|
|
|
|
|
2016-11-23 12:42:19 -05:00
|
|
|
import {CompileDiDependencyMetadata, CompileIdentifierMetadata, CompileNgModuleMetadata, CompileProviderMetadata, CompileTokenMetadata, identifierModuleUrl, identifierName, tokenName, tokenReference} from './compile_metadata';
|
2017-01-04 16:59:43 -05:00
|
|
|
import {Identifiers, createIdentifier, resolveIdentifier} from './identifiers';
|
2016-12-15 12:12:40 -05:00
|
|
|
import {CompilerInjectable} from './injectable';
|
2016-10-21 16:37:51 -04:00
|
|
|
import {ClassBuilder, createClassStmt} from './output/class_builder';
|
2016-06-28 12:54:42 -04:00
|
|
|
import * as o from './output/output_ast';
|
2016-06-30 16:34:15 -04:00
|
|
|
import {convertValueToOutputAst} from './output/value_util';
|
2017-03-14 12:16:15 -04:00
|
|
|
import {ParseLocation, ParseSourceFile, ParseSourceSpan, typeSourceSpan} from './parse_util';
|
2016-07-18 06:50:31 -04:00
|
|
|
import {NgModuleProviderAnalyzer} from './provider_analyzer';
|
2016-07-21 14:41:25 -04:00
|
|
|
import {ProviderAst} from './template_parser/template_ast';
|
2016-06-28 12:54:42 -04:00
|
|
|
|
2017-02-17 15:55:55 -05:00
|
|
|
|
2016-12-27 12:36:47 -05:00
|
|
|
/**
|
|
|
|
* This is currently not read, but will probably be used in the future.
|
|
|
|
* We keep it as we already pass it through all the rigth places...
|
|
|
|
*/
|
2016-06-28 12:54:42 -04:00
|
|
|
export class ComponentFactoryDependency {
|
2016-12-27 12:36:47 -05:00
|
|
|
constructor(public compType: any) {}
|
2016-06-28 12:54:42 -04:00
|
|
|
}
|
|
|
|
|
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[]) {}
|
|
|
|
}
|
|
|
|
|
2016-12-15 12:12:40 -05:00
|
|
|
@CompilerInjectable()
|
2016-07-18 06:50:31 -04:00
|
|
|
export class NgModuleCompiler {
|
|
|
|
compile(ngModuleMeta: CompileNgModuleMetadata, extraProviders: CompileProviderMetadata[]):
|
|
|
|
NgModuleCompileResult {
|
2017-03-14 12:16:15 -04:00
|
|
|
const sourceSpan = typeSourceSpan('NgModule', ngModuleMeta.type);
|
2016-11-12 08:08:58 -05:00
|
|
|
const deps: ComponentFactoryDependency[] = [];
|
|
|
|
const bootstrapComponentFactories: CompileIdentifierMetadata[] = [];
|
|
|
|
const entryComponentFactories =
|
2016-08-02 09:54:08 -04:00
|
|
|
ngModuleMeta.transitiveModule.entryComponents.map((entryComponent) => {
|
2016-12-02 13:08:46 -05:00
|
|
|
if (ngModuleMeta.bootstrapComponents.some(
|
2016-12-27 12:36:47 -05:00
|
|
|
(id) => id.reference === entryComponent.componentType)) {
|
|
|
|
bootstrapComponentFactories.push({reference: entryComponent.componentFactory});
|
2016-08-02 09:54:08 -04:00
|
|
|
}
|
2016-12-27 12:36:47 -05:00
|
|
|
deps.push(new ComponentFactoryDependency(entryComponent.componentType));
|
|
|
|
return {reference: entryComponent.componentFactory};
|
2016-08-02 09:54:08 -04:00
|
|
|
});
|
2016-11-12 08:08:58 -05:00
|
|
|
const builder = new _InjectorBuilder(
|
2016-08-02 09:54:08 -04:00
|
|
|
ngModuleMeta, entryComponentFactories, bootstrapComponentFactories, sourceSpan);
|
2016-06-28 12:54:42 -04:00
|
|
|
|
2016-11-12 08:08:58 -05:00
|
|
|
const providerParser = new NgModuleProviderAnalyzer(ngModuleMeta, extraProviders, sourceSpan);
|
2016-06-28 12:54:42 -04:00
|
|
|
providerParser.parse().forEach((provider) => builder.addProvider(provider));
|
2016-11-12 08:08:58 -05:00
|
|
|
const injectorClass = builder.build();
|
2016-11-23 12:42:19 -05:00
|
|
|
const ngModuleFactoryVar = `${identifierName(ngModuleMeta.type)}NgFactory`;
|
2016-11-12 08:08:58 -05:00
|
|
|
const ngModuleFactoryStmt =
|
2016-07-18 06:50:31 -04:00
|
|
|
o.variable(ngModuleFactoryVar)
|
2016-11-23 12:42:19 -05:00
|
|
|
.set(o.importExpr(createIdentifier(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-11-23 12:42:19 -05:00
|
|
|
createIdentifier(Identifiers.NgModuleFactory),
|
2017-03-24 12:59:58 -04:00
|
|
|
[o.importType(ngModuleMeta.type) !], [o.TypeModifier.Const])))
|
2016-06-28 12:54:42 -04:00
|
|
|
.toDeclStmt(null, [o.StmtModifier.Final]);
|
|
|
|
|
2016-11-12 08:08:58 -05:00
|
|
|
const stmts: o.Statement[] = [injectorClass, ngModuleFactoryStmt];
|
2016-09-01 16:46:08 -04:00
|
|
|
if (ngModuleMeta.id) {
|
2016-11-12 08:08:58 -05:00
|
|
|
const registerFactoryStmt =
|
2016-11-23 12:42:19 -05:00
|
|
|
o.importExpr(createIdentifier(Identifiers.RegisterModuleFactoryFn))
|
2016-09-01 16:46:08 -04:00
|
|
|
.callFn([o.literal(ngModuleMeta.id), o.variable(ngModuleFactoryVar)])
|
|
|
|
.toStmt();
|
|
|
|
stmts.push(registerFactoryStmt);
|
|
|
|
}
|
|
|
|
|
|
|
|
return new NgModuleCompileResult(stmts, ngModuleFactoryVar, deps);
|
2016-06-28 12:54:42 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-10-21 16:37:51 -04:00
|
|
|
class _InjectorBuilder implements ClassBuilder {
|
|
|
|
fields: o.ClassField[] = [];
|
|
|
|
getters: o.ClassGetter[] = [];
|
|
|
|
methods: o.ClassMethod[] = [];
|
|
|
|
ctorStmts: o.Statement[] = [];
|
2017-03-14 17:32:26 -04:00
|
|
|
private _lazyProps = new Map<string, o.Expression>();
|
2016-10-24 14:11:31 -04:00
|
|
|
private _tokens: CompileTokenMetadata[] = [];
|
|
|
|
private _instances = new Map<any, o.Expression>();
|
|
|
|
private _createStmts: o.Statement[] = [];
|
|
|
|
private _destroyStmts: o.Statement[] = [];
|
2016-06-28 12:54:42 -04:00
|
|
|
|
|
|
|
constructor(
|
2016-07-18 06:50:31 -04:00
|
|
|
private _ngModuleMeta: CompileNgModuleMetadata,
|
2016-08-02 09:54:08 -04:00
|
|
|
private _entryComponentFactories: CompileIdentifierMetadata[],
|
|
|
|
private _bootstrapComponentFactories: CompileIdentifierMetadata[],
|
|
|
|
private _sourceSpan: ParseSourceSpan) {}
|
2016-06-28 12:54:42 -04:00
|
|
|
|
|
|
|
addProvider(resolvedProvider: ProviderAst) {
|
2016-11-12 08:08:58 -05:00
|
|
|
const providerValueExpressions =
|
2016-06-28 12:54:42 -04:00
|
|
|
resolvedProvider.providers.map((provider) => this._getProviderValue(provider));
|
2016-11-23 12:42:19 -05:00
|
|
|
const propName = `_${tokenName(resolvedProvider.token)}_${this._instances.size}`;
|
2016-11-12 08:08:58 -05:00
|
|
|
const instance = this._createProviderProperty(
|
2016-06-28 12:54:42 -04:00
|
|
|
propName, resolvedProvider, providerValueExpressions, resolvedProvider.multiProvider,
|
|
|
|
resolvedProvider.eager);
|
2017-02-17 15:55:55 -05:00
|
|
|
if (resolvedProvider.lifecycleHooks.indexOf(ɵLifecycleHooks.OnDestroy) !== -1) {
|
2017-03-14 17:32:26 -04:00
|
|
|
let callNgOnDestroy: o.Expression = instance.callMethod('ngOnDestroy', []);
|
|
|
|
if (!resolvedProvider.eager) {
|
2017-03-24 12:59:58 -04:00
|
|
|
callNgOnDestroy = this._lazyProps.get(instance.name) !.and(callNgOnDestroy);
|
2017-03-14 17:32:26 -04:00
|
|
|
}
|
|
|
|
this._destroyStmts.push(callNgOnDestroy.toStmt());
|
2016-08-02 05:08:10 -04:00
|
|
|
}
|
2016-08-29 11:52:25 -04:00
|
|
|
this._tokens.push(resolvedProvider.token);
|
2016-11-23 12:42:19 -05:00
|
|
|
this._instances.set(tokenReference(resolvedProvider.token), instance);
|
2016-06-28 12:54:42 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
build(): o.ClassStmt {
|
2016-11-12 08:08:58 -05:00
|
|
|
const getMethodStmts: o.Statement[] = this._tokens.map((token) => {
|
2017-03-24 12:59:58 -04:00
|
|
|
const providerExpr = this._instances.get(tokenReference(token)) !;
|
2016-06-28 12:54:42 -04:00
|
|
|
return new o.IfStmt(
|
|
|
|
InjectMethodVars.token.identical(createDiTokenExpression(token)),
|
|
|
|
[new o.ReturnStatement(providerExpr)]);
|
|
|
|
});
|
2016-11-12 08:08:58 -05:00
|
|
|
const methods = [
|
2016-06-28 12:54:42 -04:00
|
|
|
new o.ClassMethod(
|
2016-11-12 08:08:58 -05:00
|
|
|
'createInternal', [], this._createStmts.concat(new o.ReturnStatement(
|
2017-03-24 12:59:58 -04:00
|
|
|
this._instances.get(this._ngModuleMeta.type.reference) !)),
|
2016-11-12 08:08:58 -05:00
|
|
|
o.importType(this._ngModuleMeta.type)),
|
2016-06-28 12:54:42 -04:00
|
|
|
new o.ClassMethod(
|
|
|
|
'getInternal',
|
|
|
|
[
|
2017-03-24 12:59:58 -04:00
|
|
|
new o.FnParam(InjectMethodVars.token.name !, o.DYNAMIC_TYPE),
|
|
|
|
new o.FnParam(InjectMethodVars.notFoundResult.name !, o.DYNAMIC_TYPE)
|
2016-06-28 12:54:42 -04:00
|
|
|
],
|
|
|
|
getMethodStmts.concat([new o.ReturnStatement(InjectMethodVars.notFoundResult)]),
|
2016-08-02 05:08:10 -04:00
|
|
|
o.DYNAMIC_TYPE),
|
2016-11-12 08:08:58 -05:00
|
|
|
new o.ClassMethod('destroyInternal', [], this._destroyStmts),
|
2016-06-28 12:54:42 -04:00
|
|
|
];
|
|
|
|
|
2016-11-12 08:08:58 -05:00
|
|
|
const parentArgs = [
|
2016-10-21 16:37:51 -04:00
|
|
|
o.variable(InjectorProps.parent.name),
|
|
|
|
o.literalArr(
|
|
|
|
this._entryComponentFactories.map((componentFactory) => o.importExpr(componentFactory))),
|
|
|
|
o.literalArr(this._bootstrapComponentFactories.map(
|
|
|
|
(componentFactory) => o.importExpr(componentFactory)))
|
|
|
|
];
|
2016-11-23 12:42:19 -05:00
|
|
|
const injClassName = `${identifierName(this._ngModuleMeta.type)}Injector`;
|
2016-10-21 16:37:51 -04:00
|
|
|
return createClassStmt({
|
|
|
|
name: injClassName,
|
|
|
|
ctorParams: [new o.FnParam(
|
2016-11-23 12:42:19 -05:00
|
|
|
InjectorProps.parent.name, o.importType(createIdentifier(Identifiers.Injector)))],
|
2016-10-21 16:37:51 -04:00
|
|
|
parent: o.importExpr(
|
2017-03-24 12:59:58 -04:00
|
|
|
createIdentifier(Identifiers.NgModuleInjector),
|
|
|
|
[o.importType(this._ngModuleMeta.type) !]),
|
2016-10-21 16:37:51 -04:00
|
|
|
parentArgs: parentArgs,
|
2016-10-24 14:11:31 -04:00
|
|
|
builders: [{methods}, this]
|
2016-10-21 16:37:51 -04:00
|
|
|
});
|
2016-06-28 12:54:42 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
private _getProviderValue(provider: CompileProviderMetadata): o.Expression {
|
2016-11-12 08:08:58 -05:00
|
|
|
let result: o.Expression;
|
2017-03-02 12:37:01 -05:00
|
|
|
if (provider.useExisting != null) {
|
2016-11-30 13:52:51 -05:00
|
|
|
result = this._getDependency({token: provider.useExisting});
|
2017-03-02 12:37:01 -05:00
|
|
|
} else if (provider.useFactory != null) {
|
2016-11-12 08:08:58 -05:00
|
|
|
const deps = provider.deps || provider.useFactory.diDeps;
|
|
|
|
const depsExpr = deps.map((dep) => this._getDependency(dep));
|
2016-06-28 12:54:42 -04:00
|
|
|
result = o.importExpr(provider.useFactory).callFn(depsExpr);
|
2017-03-02 12:37:01 -05:00
|
|
|
} else if (provider.useClass != null) {
|
2016-11-12 08:08:58 -05:00
|
|
|
const deps = provider.deps || provider.useClass.diDeps;
|
|
|
|
const depsExpr = deps.map((dep) => this._getDependency(dep));
|
2016-06-28 12:54:42 -04:00
|
|
|
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[],
|
2017-03-14 17:32:26 -04:00
|
|
|
isMulti: boolean, isEager: boolean): o.ReadPropExpr {
|
2016-11-12 08:08:58 -05:00
|
|
|
let resolvedProviderValueExpr: o.Expression;
|
|
|
|
let type: o.Type;
|
2016-06-28 12:54:42 -04:00
|
|
|
if (isMulti) {
|
|
|
|
resolvedProviderValueExpr = o.literalArr(providerValueExpressions);
|
|
|
|
type = new o.ArrayType(o.DYNAMIC_TYPE);
|
|
|
|
} else {
|
|
|
|
resolvedProviderValueExpr = providerValueExpressions[0];
|
2017-03-24 12:59:58 -04:00
|
|
|
type = providerValueExpressions[0].type !;
|
2016-06-28 12:54:42 -04:00
|
|
|
}
|
2016-09-30 12:26:53 -04:00
|
|
|
if (!type) {
|
2016-06-28 12:54:42 -04:00
|
|
|
type = o.DYNAMIC_TYPE;
|
|
|
|
}
|
|
|
|
if (isEager) {
|
2016-10-21 16:37:51 -04:00
|
|
|
this.fields.push(new o.ClassField(propName, type));
|
2016-06-28 12:54:42 -04:00
|
|
|
this._createStmts.push(o.THIS_EXPR.prop(propName).set(resolvedProviderValueExpr).toStmt());
|
|
|
|
} else {
|
2017-03-14 17:32:26 -04:00
|
|
|
const internalFieldProp = o.THIS_EXPR.prop(`_${propName}`);
|
|
|
|
this.fields.push(new o.ClassField(internalFieldProp.name, type));
|
2016-06-28 12:54:42 -04:00
|
|
|
// Note: Equals is important for JS so that it also checks the undefined case!
|
2016-11-12 08:08:58 -05:00
|
|
|
const getterStmts = [
|
2016-06-28 12:54:42 -04:00
|
|
|
new o.IfStmt(
|
2017-03-14 17:32:26 -04:00
|
|
|
internalFieldProp.isBlank(),
|
|
|
|
[internalFieldProp.set(resolvedProviderValueExpr).toStmt()]),
|
|
|
|
new o.ReturnStatement(internalFieldProp)
|
2016-06-28 12:54:42 -04:00
|
|
|
];
|
2016-10-21 16:37:51 -04:00
|
|
|
this.getters.push(new o.ClassGetter(propName, getterStmts, type));
|
2017-03-14 17:32:26 -04:00
|
|
|
this._lazyProps.set(propName, internalFieldProp);
|
2016-06-28 12:54:42 -04:00
|
|
|
}
|
|
|
|
return o.THIS_EXPR.prop(propName);
|
|
|
|
}
|
|
|
|
|
|
|
|
private _getDependency(dep: CompileDiDependencyMetadata): o.Expression {
|
2017-03-24 12:59:58 -04:00
|
|
|
let result: o.Expression = null !;
|
2016-06-28 12:54:42 -04:00
|
|
|
if (dep.isValue) {
|
|
|
|
result = o.literal(dep.value);
|
|
|
|
}
|
|
|
|
if (!dep.isSkipSelf) {
|
2017-03-14 19:26:17 -04:00
|
|
|
if (dep.token) {
|
|
|
|
if (tokenReference(dep.token) === resolveIdentifier(Identifiers.Injector)) {
|
|
|
|
result = o.THIS_EXPR;
|
|
|
|
} else if (
|
|
|
|
tokenReference(dep.token) === resolveIdentifier(Identifiers.ComponentFactoryResolver)) {
|
|
|
|
result = o.THIS_EXPR.prop('componentFactoryResolver');
|
|
|
|
}
|
2016-06-28 12:54:42 -04:00
|
|
|
}
|
2017-03-14 19:26:17 -04:00
|
|
|
|
2016-09-30 12:26:53 -04:00
|
|
|
if (!result) {
|
2017-03-24 12:59:58 -04:00
|
|
|
result = this._instances.get(tokenReference(dep.token !)) !;
|
2016-06-28 12:54:42 -04:00
|
|
|
}
|
|
|
|
}
|
2016-09-30 12:26:53 -04:00
|
|
|
if (!result) {
|
2017-03-24 12:59:58 -04:00
|
|
|
const args = [createDiTokenExpression(dep.token !)];
|
2016-06-28 12:54:42 -04:00
|
|
|
if (dep.isOptional) {
|
|
|
|
args.push(o.NULL_EXPR);
|
|
|
|
}
|
|
|
|
result = InjectorProps.parent.callMethod('get', args);
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-02-28 02:08:19 -05:00
|
|
|
function createDiTokenExpression(token: CompileTokenMetadata): o.Expression {
|
2017-03-02 12:37:01 -05:00
|
|
|
if (token.value != null) {
|
2017-02-28 02:08:19 -05:00
|
|
|
return o.literal(token.value);
|
|
|
|
} else {
|
2017-03-24 12:59:58 -04:00
|
|
|
return o.importExpr(token.identifier !);
|
2017-02-28 02:08:19 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-06-28 12:54:42 -04:00
|
|
|
class InjectorProps {
|
|
|
|
static parent = o.THIS_EXPR.prop('parent');
|
|
|
|
}
|
|
|
|
|
|
|
|
class InjectMethodVars {
|
|
|
|
static token = o.variable('token');
|
|
|
|
static notFoundResult = o.variable('notFoundResult');
|
|
|
|
}
|