refactor: use view engine also for `NgModuleFactory`s (#16658)
* refactor(core): provide error message in stack for reflective DI Fixes #16355 * fix(compiler): make AOT work with `noUnusedParameters` Fixes #15532 * refactor: use view engine also for `NgModuleFactory`s This is a prerequisite for being able to mock providers in AOTed code later on.
This commit is contained in:
parent
b9521b568f
commit
ce1d7c4a6e
|
@ -6,7 +6,7 @@
|
||||||
* found in the LICENSE file at https://angular.io/license
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {ANALYZE_FOR_ENTRY_COMPONENTS, ChangeDetectionStrategy, ChangeDetectorRef, ComponentFactory, ComponentFactoryResolver, ComponentRef, ElementRef, Injector, LOCALE_ID, NgModuleFactory, NgModuleRef, QueryList, Renderer, SecurityContext, TRANSLATIONS_FORMAT, TemplateRef, ViewContainerRef, ViewEncapsulation, ɵCodegenComponentFactoryResolver, ɵEMPTY_ARRAY, ɵEMPTY_MAP, ɵNgModuleInjector, ɵand, ɵccf, ɵcrt, ɵdid, ɵeld, ɵinlineInterpolate, ɵinterpolate, ɵncd, ɵnov, ɵpad, ɵpid, ɵpod, ɵppd, ɵprd, ɵqud, ɵreflector, ɵregisterModuleFactory, ɵted, ɵunv, ɵvid} from '@angular/core';
|
import {ANALYZE_FOR_ENTRY_COMPONENTS, ChangeDetectionStrategy, ChangeDetectorRef, ComponentFactory, ComponentFactoryResolver, ComponentRef, ElementRef, Injector, LOCALE_ID, NgModuleFactory, NgModuleRef, QueryList, Renderer, SecurityContext, TRANSLATIONS_FORMAT, TemplateRef, ViewContainerRef, ViewEncapsulation, ɵCodegenComponentFactoryResolver, ɵEMPTY_ARRAY, ɵEMPTY_MAP, ɵand, ɵccf, ɵcmf, ɵcrt, ɵdid, ɵeld, ɵinlineInterpolate, ɵinterpolate, ɵmod, ɵmpd, ɵncd, ɵnov, ɵpad, ɵpid, ɵpod, ɵppd, ɵprd, ɵqud, ɵreflector, ɵregisterModuleFactory, ɵted, ɵunv, ɵvid} from '@angular/core';
|
||||||
|
|
||||||
import {CompileIdentifierMetadata, CompileTokenMetadata} from './compile_metadata';
|
import {CompileIdentifierMetadata, CompileTokenMetadata} from './compile_metadata';
|
||||||
|
|
||||||
|
@ -48,10 +48,20 @@ export class Identifiers {
|
||||||
IdentifierSpec = {name: 'ComponentRef', moduleUrl: CORE, runtime: ComponentRef};
|
IdentifierSpec = {name: 'ComponentRef', moduleUrl: CORE, runtime: ComponentRef};
|
||||||
static NgModuleFactory:
|
static NgModuleFactory:
|
||||||
IdentifierSpec = {name: 'NgModuleFactory', moduleUrl: CORE, runtime: NgModuleFactory};
|
IdentifierSpec = {name: 'NgModuleFactory', moduleUrl: CORE, runtime: NgModuleFactory};
|
||||||
static NgModuleInjector: IdentifierSpec = {
|
static createModuleFactory: IdentifierSpec = {
|
||||||
name: 'ɵNgModuleInjector',
|
name: 'ɵcmf',
|
||||||
moduleUrl: CORE,
|
moduleUrl: CORE,
|
||||||
runtime: ɵNgModuleInjector,
|
runtime: ɵcmf,
|
||||||
|
};
|
||||||
|
static moduleDef: IdentifierSpec = {
|
||||||
|
name: 'ɵmod',
|
||||||
|
moduleUrl: CORE,
|
||||||
|
runtime: ɵmod,
|
||||||
|
};
|
||||||
|
static moduleProviderDef: IdentifierSpec = {
|
||||||
|
name: 'ɵmpd',
|
||||||
|
moduleUrl: CORE,
|
||||||
|
runtime: ɵmpd,
|
||||||
};
|
};
|
||||||
static RegisterModuleFactoryFn: IdentifierSpec = {
|
static RegisterModuleFactoryFn: IdentifierSpec = {
|
||||||
name: 'ɵregisterModuleFactory',
|
name: 'ɵregisterModuleFactory',
|
||||||
|
|
|
@ -6,67 +6,58 @@
|
||||||
* found in the LICENSE file at https://angular.io/license
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {ɵLifecycleHooks} from '@angular/core';
|
import {ɵNodeFlags as NodeFlags} from '@angular/core';
|
||||||
|
|
||||||
import {CompileDiDependencyMetadata, CompileIdentifierMetadata, CompileNgModuleMetadata, CompileProviderMetadata, CompileTokenMetadata, identifierModuleUrl, identifierName, tokenName, tokenReference} from './compile_metadata';
|
import {CompileNgModuleMetadata, CompileProviderMetadata, identifierName} from './compile_metadata';
|
||||||
import {Identifiers, createIdentifier, resolveIdentifier} from './identifiers';
|
import {Identifiers, createIdentifier} from './identifiers';
|
||||||
import {CompilerInjectable} from './injectable';
|
import {CompilerInjectable} from './injectable';
|
||||||
import {ClassBuilder, createClassStmt} from './output/class_builder';
|
|
||||||
import * as o from './output/output_ast';
|
import * as o from './output/output_ast';
|
||||||
import {convertValueToOutputAst} from './output/value_util';
|
import {typeSourceSpan} from './parse_util';
|
||||||
import {ParseLocation, ParseSourceFile, ParseSourceSpan, typeSourceSpan} from './parse_util';
|
|
||||||
import {NgModuleProviderAnalyzer} from './provider_analyzer';
|
import {NgModuleProviderAnalyzer} from './provider_analyzer';
|
||||||
import {ProviderAst} from './template_parser/template_ast';
|
import {componentFactoryResolverProviderDef, depDef, providerDef} from './view_compiler/provider_compiler';
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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...
|
|
||||||
*/
|
|
||||||
export class ComponentFactoryDependency {
|
|
||||||
constructor(public compType: any) {}
|
|
||||||
}
|
|
||||||
|
|
||||||
export class NgModuleCompileResult {
|
export class NgModuleCompileResult {
|
||||||
constructor(
|
constructor(public statements: o.Statement[], public ngModuleFactoryVar: string) {}
|
||||||
public statements: o.Statement[], public ngModuleFactoryVar: string,
|
|
||||||
public dependencies: ComponentFactoryDependency[]) {}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const LOG_VAR = o.variable('_l');
|
||||||
|
|
||||||
@CompilerInjectable()
|
@CompilerInjectable()
|
||||||
export class NgModuleCompiler {
|
export class NgModuleCompiler {
|
||||||
compile(ngModuleMeta: CompileNgModuleMetadata, extraProviders: CompileProviderMetadata[]):
|
compile(ngModuleMeta: CompileNgModuleMetadata, extraProviders: CompileProviderMetadata[]):
|
||||||
NgModuleCompileResult {
|
NgModuleCompileResult {
|
||||||
const sourceSpan = typeSourceSpan('NgModule', ngModuleMeta.type);
|
const sourceSpan = typeSourceSpan('NgModule', ngModuleMeta.type);
|
||||||
const deps: ComponentFactoryDependency[] = [];
|
const entryComponentFactories = ngModuleMeta.transitiveModule.entryComponents;
|
||||||
const bootstrapComponentFactories: CompileIdentifierMetadata[] = [];
|
const bootstrapComponents = ngModuleMeta.bootstrapComponents;
|
||||||
const entryComponentFactories =
|
|
||||||
ngModuleMeta.transitiveModule.entryComponents.map((entryComponent) => {
|
|
||||||
if (ngModuleMeta.bootstrapComponents.some(
|
|
||||||
(id) => id.reference === entryComponent.componentType)) {
|
|
||||||
bootstrapComponentFactories.push({reference: entryComponent.componentFactory});
|
|
||||||
}
|
|
||||||
deps.push(new ComponentFactoryDependency(entryComponent.componentType));
|
|
||||||
return {reference: entryComponent.componentFactory};
|
|
||||||
});
|
|
||||||
const builder = new _InjectorBuilder(
|
|
||||||
ngModuleMeta, entryComponentFactories, bootstrapComponentFactories, sourceSpan);
|
|
||||||
|
|
||||||
const providerParser = new NgModuleProviderAnalyzer(ngModuleMeta, extraProviders, sourceSpan);
|
const providerParser = new NgModuleProviderAnalyzer(ngModuleMeta, extraProviders, sourceSpan);
|
||||||
providerParser.parse().forEach((provider) => builder.addProvider(provider));
|
const providerDefs =
|
||||||
const injectorClass = builder.build();
|
[componentFactoryResolverProviderDef(NodeFlags.None, entryComponentFactories)]
|
||||||
|
.concat(providerParser.parse().map((provider) => providerDef(provider)))
|
||||||
|
.map(({providerExpr, depsExpr, flags, tokenExpr}) => {
|
||||||
|
return o.importExpr(createIdentifier(Identifiers.moduleProviderDef)).callFn([
|
||||||
|
o.literal(flags), tokenExpr, providerExpr, depsExpr
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
const ngModuleDef =
|
||||||
|
o.importExpr(createIdentifier(Identifiers.moduleDef)).callFn([o.literalArr(providerDefs)]);
|
||||||
|
const ngModuleDefFactory = o.fn(
|
||||||
|
[new o.FnParam(LOG_VAR.name !)], [new o.ReturnStatement(ngModuleDef)], o.INFERRED_TYPE);
|
||||||
|
|
||||||
const ngModuleFactoryVar = `${identifierName(ngModuleMeta.type)}NgFactory`;
|
const ngModuleFactoryVar = `${identifierName(ngModuleMeta.type)}NgFactory`;
|
||||||
const ngModuleFactoryStmt =
|
const ngModuleFactoryStmt =
|
||||||
o.variable(ngModuleFactoryVar)
|
o.variable(ngModuleFactoryVar)
|
||||||
.set(o.importExpr(createIdentifier(Identifiers.NgModuleFactory))
|
.set(o.importExpr(createIdentifier(Identifiers.createModuleFactory)).callFn([
|
||||||
.instantiate(
|
o.importExpr(ngModuleMeta.type),
|
||||||
[o.variable(injectorClass.name), o.importExpr(ngModuleMeta.type)],
|
o.literalArr(bootstrapComponents.map(id => o.importExpr(id))), ngModuleDefFactory
|
||||||
|
]))
|
||||||
|
.toDeclStmt(
|
||||||
o.importType(
|
o.importType(
|
||||||
createIdentifier(Identifiers.NgModuleFactory),
|
createIdentifier(Identifiers.NgModuleFactory),
|
||||||
[o.importType(ngModuleMeta.type) !], [o.TypeModifier.Const])))
|
[o.importType(ngModuleMeta.type) !], [o.TypeModifier.Const]),
|
||||||
.toDeclStmt(null, [o.StmtModifier.Final]);
|
[o.StmtModifier.Final]);
|
||||||
|
|
||||||
const stmts: o.Statement[] = [injectorClass, ngModuleFactoryStmt];
|
const stmts: o.Statement[] = [ngModuleFactoryStmt];
|
||||||
if (ngModuleMeta.id) {
|
if (ngModuleMeta.id) {
|
||||||
const registerFactoryStmt =
|
const registerFactoryStmt =
|
||||||
o.importExpr(createIdentifier(Identifiers.RegisterModuleFactoryFn))
|
o.importExpr(createIdentifier(Identifiers.RegisterModuleFactoryFn))
|
||||||
|
@ -75,185 +66,6 @@ export class NgModuleCompiler {
|
||||||
stmts.push(registerFactoryStmt);
|
stmts.push(registerFactoryStmt);
|
||||||
}
|
}
|
||||||
|
|
||||||
return new NgModuleCompileResult(stmts, ngModuleFactoryVar, deps);
|
return new NgModuleCompileResult(stmts, ngModuleFactoryVar);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class _InjectorBuilder implements ClassBuilder {
|
|
||||||
fields: o.ClassField[] = [];
|
|
||||||
getters: o.ClassGetter[] = [];
|
|
||||||
methods: o.ClassMethod[] = [];
|
|
||||||
ctorStmts: o.Statement[] = [];
|
|
||||||
private _lazyProps = new Map<string, o.Expression>();
|
|
||||||
private _tokens: CompileTokenMetadata[] = [];
|
|
||||||
private _instances = new Map<any, o.Expression>();
|
|
||||||
private _createStmts: o.Statement[] = [];
|
|
||||||
private _destroyStmts: o.Statement[] = [];
|
|
||||||
|
|
||||||
constructor(
|
|
||||||
private _ngModuleMeta: CompileNgModuleMetadata,
|
|
||||||
private _entryComponentFactories: CompileIdentifierMetadata[],
|
|
||||||
private _bootstrapComponentFactories: CompileIdentifierMetadata[],
|
|
||||||
private _sourceSpan: ParseSourceSpan) {}
|
|
||||||
|
|
||||||
addProvider(resolvedProvider: ProviderAst) {
|
|
||||||
const providerValueExpressions =
|
|
||||||
resolvedProvider.providers.map((provider) => this._getProviderValue(provider));
|
|
||||||
const propName = `_${tokenName(resolvedProvider.token)}_${this._instances.size}`;
|
|
||||||
const instance = this._createProviderProperty(
|
|
||||||
propName, resolvedProvider, providerValueExpressions, resolvedProvider.multiProvider,
|
|
||||||
resolvedProvider.eager);
|
|
||||||
if (resolvedProvider.lifecycleHooks.indexOf(ɵLifecycleHooks.OnDestroy) !== -1) {
|
|
||||||
let callNgOnDestroy: o.Expression = instance.callMethod('ngOnDestroy', []);
|
|
||||||
if (!resolvedProvider.eager) {
|
|
||||||
callNgOnDestroy = this._lazyProps.get(instance.name) !.and(callNgOnDestroy);
|
|
||||||
}
|
|
||||||
this._destroyStmts.push(callNgOnDestroy.toStmt());
|
|
||||||
}
|
|
||||||
this._tokens.push(resolvedProvider.token);
|
|
||||||
this._instances.set(tokenReference(resolvedProvider.token), instance);
|
|
||||||
}
|
|
||||||
|
|
||||||
build(): o.ClassStmt {
|
|
||||||
const getMethodStmts: o.Statement[] = this._tokens.map((token) => {
|
|
||||||
const providerExpr = this._instances.get(tokenReference(token)) !;
|
|
||||||
return new o.IfStmt(
|
|
||||||
InjectMethodVars.token.identical(createDiTokenExpression(token)),
|
|
||||||
[new o.ReturnStatement(providerExpr)]);
|
|
||||||
});
|
|
||||||
const methods = [
|
|
||||||
new o.ClassMethod(
|
|
||||||
'createInternal', [], this._createStmts.concat(new o.ReturnStatement(
|
|
||||||
this._instances.get(this._ngModuleMeta.type.reference) !)),
|
|
||||||
o.importType(this._ngModuleMeta.type)),
|
|
||||||
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),
|
|
||||||
new o.ClassMethod('destroyInternal', [], this._destroyStmts),
|
|
||||||
];
|
|
||||||
|
|
||||||
const parentArgs = [
|
|
||||||
o.variable(InjectorProps.parent.name),
|
|
||||||
o.literalArr(
|
|
||||||
this._entryComponentFactories.map((componentFactory) => o.importExpr(componentFactory))),
|
|
||||||
o.literalArr(this._bootstrapComponentFactories.map(
|
|
||||||
(componentFactory) => o.importExpr(componentFactory)))
|
|
||||||
];
|
|
||||||
const injClassName = `${identifierName(this._ngModuleMeta.type)}Injector`;
|
|
||||||
return createClassStmt({
|
|
||||||
name: injClassName,
|
|
||||||
ctorParams: [new o.FnParam(
|
|
||||||
InjectorProps.parent.name, o.importType(createIdentifier(Identifiers.Injector)))],
|
|
||||||
parent: o.importExpr(
|
|
||||||
createIdentifier(Identifiers.NgModuleInjector),
|
|
||||||
[o.importType(this._ngModuleMeta.type) !]),
|
|
||||||
parentArgs: parentArgs,
|
|
||||||
builders: [{methods}, this]
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private _getProviderValue(provider: CompileProviderMetadata): o.Expression {
|
|
||||||
let result: o.Expression;
|
|
||||||
if (provider.useExisting != null) {
|
|
||||||
result = this._getDependency({token: provider.useExisting});
|
|
||||||
} else if (provider.useFactory != null) {
|
|
||||||
const deps = provider.deps || provider.useFactory.diDeps;
|
|
||||||
const depsExpr = deps.map((dep) => this._getDependency(dep));
|
|
||||||
result = o.importExpr(provider.useFactory).callFn(depsExpr);
|
|
||||||
} else if (provider.useClass != null) {
|
|
||||||
const deps = provider.deps || provider.useClass.diDeps;
|
|
||||||
const depsExpr = deps.map((dep) => this._getDependency(dep));
|
|
||||||
result =
|
|
||||||
o.importExpr(provider.useClass).instantiate(depsExpr, o.importType(provider.useClass));
|
|
||||||
} else {
|
|
||||||
result = convertValueToOutputAst(provider.useValue);
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private _createProviderProperty(
|
|
||||||
propName: string, provider: ProviderAst, providerValueExpressions: o.Expression[],
|
|
||||||
isMulti: boolean, isEager: boolean): o.ReadPropExpr {
|
|
||||||
let resolvedProviderValueExpr: o.Expression;
|
|
||||||
let 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 (!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 {
|
|
||||||
const internalFieldProp = o.THIS_EXPR.prop(`_${propName}`);
|
|
||||||
this.fields.push(new o.ClassField(internalFieldProp.name, type));
|
|
||||||
// Note: Equals is important for JS so that it also checks the undefined case!
|
|
||||||
const getterStmts = [
|
|
||||||
new o.IfStmt(
|
|
||||||
internalFieldProp.isBlank(),
|
|
||||||
[internalFieldProp.set(resolvedProviderValueExpr).toStmt()]),
|
|
||||||
new o.ReturnStatement(internalFieldProp)
|
|
||||||
];
|
|
||||||
this.getters.push(new o.ClassGetter(propName, getterStmts, type));
|
|
||||||
this._lazyProps.set(propName, internalFieldProp);
|
|
||||||
}
|
|
||||||
return o.THIS_EXPR.prop(propName);
|
|
||||||
}
|
|
||||||
|
|
||||||
private _getDependency(dep: CompileDiDependencyMetadata): o.Expression {
|
|
||||||
let result: o.Expression = null !;
|
|
||||||
if (dep.isValue) {
|
|
||||||
result = o.literal(dep.value);
|
|
||||||
}
|
|
||||||
if (!dep.isSkipSelf) {
|
|
||||||
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');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!result) {
|
|
||||||
result = this._instances.get(tokenReference(dep.token !)) !;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!result) {
|
|
||||||
const args = [createDiTokenExpression(dep.token !)];
|
|
||||||
if (dep.isOptional) {
|
|
||||||
args.push(o.NULL_EXPR);
|
|
||||||
}
|
|
||||||
result = InjectorProps.parent.callMethod('get', args);
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function createDiTokenExpression(token: CompileTokenMetadata): o.Expression {
|
|
||||||
if (token.value != null) {
|
|
||||||
return o.literal(token.value);
|
|
||||||
} else {
|
|
||||||
return o.importExpr(token.identifier !);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class InjectorProps {
|
|
||||||
static parent = o.THIS_EXPR.prop('parent');
|
|
||||||
}
|
|
||||||
|
|
||||||
class InjectMethodVars {
|
|
||||||
static token = o.variable('token');
|
|
||||||
static notFoundResult = o.variable('notFoundResult');
|
|
||||||
}
|
|
||||||
|
|
|
@ -96,7 +96,17 @@ export class ProviderElementContext {
|
||||||
}
|
}
|
||||||
|
|
||||||
get transformProviders(): ProviderAst[] {
|
get transformProviders(): ProviderAst[] {
|
||||||
return Array.from(this._transformedProviders.values());
|
// Note: Maps keep their insertion order.
|
||||||
|
const lazyProviders: ProviderAst[] = [];
|
||||||
|
const eagerProviders: ProviderAst[] = [];
|
||||||
|
this._transformedProviders.forEach(provider => {
|
||||||
|
if (provider.eager) {
|
||||||
|
eagerProviders.push(provider);
|
||||||
|
} else {
|
||||||
|
lazyProviders.push(provider);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return lazyProviders.concat(eagerProviders);
|
||||||
}
|
}
|
||||||
|
|
||||||
get transformedDirectiveAsts(): DirectiveAst[] {
|
get transformedDirectiveAsts(): DirectiveAst[] {
|
||||||
|
@ -316,7 +326,17 @@ export class NgModuleProviderAnalyzer {
|
||||||
const errorString = this._errors.join('\n');
|
const errorString = this._errors.join('\n');
|
||||||
throw new Error(`Provider parse errors:\n${errorString}`);
|
throw new Error(`Provider parse errors:\n${errorString}`);
|
||||||
}
|
}
|
||||||
return Array.from(this._transformedProviders.values());
|
// Note: Maps keep their insertion order.
|
||||||
|
const lazyProviders: ProviderAst[] = [];
|
||||||
|
const eagerProviders: ProviderAst[] = [];
|
||||||
|
this._transformedProviders.forEach(provider => {
|
||||||
|
if (provider.eager) {
|
||||||
|
eagerProviders.push(provider);
|
||||||
|
} else {
|
||||||
|
lazyProviders.push(provider);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return lazyProviders.concat(eagerProviders);
|
||||||
}
|
}
|
||||||
|
|
||||||
private _getOrCreateLocalProvider(token: CompileTokenMetadata, eager: boolean): ProviderAst|null {
|
private _getOrCreateLocalProvider(token: CompileTokenMetadata, eager: boolean): ProviderAst|null {
|
||||||
|
|
|
@ -0,0 +1,196 @@
|
||||||
|
/**
|
||||||
|
* @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 {ɵDepFlags as DepFlags, ɵLifecycleHooks as LifecycleHooks, ɵNodeFlags as NodeFlags} from '@angular/core';
|
||||||
|
|
||||||
|
import {CompileDiDependencyMetadata, CompileEntryComponentMetadata, CompileProviderMetadata, CompileTokenMetadata} from '../compile_metadata';
|
||||||
|
import {Identifiers, createIdentifier, createIdentifierToken, resolveIdentifier} from '../identifiers';
|
||||||
|
import * as o from '../output/output_ast';
|
||||||
|
import {convertValueToOutputAst} from '../output/value_util';
|
||||||
|
import {ProviderAst, ProviderAstType} from '../template_parser/template_ast';
|
||||||
|
|
||||||
|
export function providerDef(providerAst: ProviderAst): {
|
||||||
|
providerExpr: o.Expression,
|
||||||
|
flags: NodeFlags,
|
||||||
|
depsExpr: o.Expression,
|
||||||
|
tokenExpr: o.Expression
|
||||||
|
} {
|
||||||
|
let flags = NodeFlags.None;
|
||||||
|
if (!providerAst.eager) {
|
||||||
|
flags |= NodeFlags.LazyProvider;
|
||||||
|
}
|
||||||
|
if (providerAst.providerType === ProviderAstType.PrivateService) {
|
||||||
|
flags |= NodeFlags.PrivateProvider;
|
||||||
|
}
|
||||||
|
providerAst.lifecycleHooks.forEach((lifecycleHook) => {
|
||||||
|
// for regular providers, we only support ngOnDestroy
|
||||||
|
if (lifecycleHook === LifecycleHooks.OnDestroy ||
|
||||||
|
providerAst.providerType === ProviderAstType.Directive ||
|
||||||
|
providerAst.providerType === ProviderAstType.Component) {
|
||||||
|
flags |= lifecycleHookToNodeFlag(lifecycleHook);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
const {providerExpr, flags: providerFlags, depsExpr} = providerAst.multiProvider ?
|
||||||
|
multiProviderDef(flags, providerAst.providers) :
|
||||||
|
singleProviderDef(flags, providerAst.providerType, providerAst.providers[0]);
|
||||||
|
return {
|
||||||
|
providerExpr,
|
||||||
|
flags: providerFlags, depsExpr,
|
||||||
|
tokenExpr: tokenExpr(providerAst.token),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function multiProviderDef(flags: NodeFlags, providers: CompileProviderMetadata[]):
|
||||||
|
{providerExpr: o.Expression, flags: NodeFlags, depsExpr: o.Expression} {
|
||||||
|
const allDepDefs: o.Expression[] = [];
|
||||||
|
const allParams: o.FnParam[] = [];
|
||||||
|
const exprs = providers.map((provider, providerIndex) => {
|
||||||
|
let expr: o.Expression;
|
||||||
|
if (provider.useClass) {
|
||||||
|
const depExprs = convertDeps(providerIndex, provider.deps || provider.useClass.diDeps);
|
||||||
|
expr = o.importExpr(provider.useClass).instantiate(depExprs);
|
||||||
|
} else if (provider.useFactory) {
|
||||||
|
const depExprs = convertDeps(providerIndex, provider.deps || provider.useFactory.diDeps);
|
||||||
|
expr = o.importExpr(provider.useFactory).callFn(depExprs);
|
||||||
|
} else if (provider.useExisting) {
|
||||||
|
const depExprs = convertDeps(providerIndex, [{token: provider.useExisting}]);
|
||||||
|
expr = depExprs[0];
|
||||||
|
} else {
|
||||||
|
expr = convertValueToOutputAst(provider.useValue);
|
||||||
|
}
|
||||||
|
return expr;
|
||||||
|
});
|
||||||
|
const providerExpr =
|
||||||
|
o.fn(allParams, [new o.ReturnStatement(o.literalArr(exprs))], o.INFERRED_TYPE);
|
||||||
|
return {
|
||||||
|
providerExpr,
|
||||||
|
flags: flags | NodeFlags.TypeFactoryProvider,
|
||||||
|
depsExpr: o.literalArr(allDepDefs)
|
||||||
|
};
|
||||||
|
|
||||||
|
function convertDeps(providerIndex: number, deps: CompileDiDependencyMetadata[]) {
|
||||||
|
return deps.map((dep, depIndex) => {
|
||||||
|
const paramName = `p${providerIndex}_${depIndex}`;
|
||||||
|
allParams.push(new o.FnParam(paramName, o.DYNAMIC_TYPE));
|
||||||
|
allDepDefs.push(depDef(dep));
|
||||||
|
return o.variable(paramName);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function singleProviderDef(
|
||||||
|
flags: NodeFlags, providerType: ProviderAstType, providerMeta: CompileProviderMetadata):
|
||||||
|
{providerExpr: o.Expression, flags: NodeFlags, depsExpr: o.Expression} {
|
||||||
|
let providerExpr: o.Expression;
|
||||||
|
let deps: CompileDiDependencyMetadata[];
|
||||||
|
if (providerType === ProviderAstType.Directive || providerType === ProviderAstType.Component) {
|
||||||
|
providerExpr = o.importExpr(providerMeta.useClass !);
|
||||||
|
flags |= NodeFlags.TypeDirective;
|
||||||
|
deps = providerMeta.deps || providerMeta.useClass !.diDeps;
|
||||||
|
} else {
|
||||||
|
if (providerMeta.useClass) {
|
||||||
|
providerExpr = o.importExpr(providerMeta.useClass);
|
||||||
|
flags |= NodeFlags.TypeClassProvider;
|
||||||
|
deps = providerMeta.deps || providerMeta.useClass.diDeps;
|
||||||
|
} else if (providerMeta.useFactory) {
|
||||||
|
providerExpr = o.importExpr(providerMeta.useFactory);
|
||||||
|
flags |= NodeFlags.TypeFactoryProvider;
|
||||||
|
deps = providerMeta.deps || providerMeta.useFactory.diDeps;
|
||||||
|
} else if (providerMeta.useExisting) {
|
||||||
|
providerExpr = o.NULL_EXPR;
|
||||||
|
flags |= NodeFlags.TypeUseExistingProvider;
|
||||||
|
deps = [{token: providerMeta.useExisting}];
|
||||||
|
} else {
|
||||||
|
providerExpr = convertValueToOutputAst(providerMeta.useValue);
|
||||||
|
flags |= NodeFlags.TypeValueProvider;
|
||||||
|
deps = [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const depsExpr = o.literalArr(deps.map(dep => depDef(dep)));
|
||||||
|
return {providerExpr, flags, depsExpr};
|
||||||
|
}
|
||||||
|
|
||||||
|
function tokenExpr(tokenMeta: CompileTokenMetadata): o.Expression {
|
||||||
|
return tokenMeta.identifier ? o.importExpr(tokenMeta.identifier) : o.literal(tokenMeta.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function depDef(dep: CompileDiDependencyMetadata): o.Expression {
|
||||||
|
// Note: the following fields have already been normalized out by provider_analyzer:
|
||||||
|
// - isAttribute, isSelf, isHost
|
||||||
|
const expr = dep.isValue ? convertValueToOutputAst(dep.value) : tokenExpr(dep.token !);
|
||||||
|
let flags = DepFlags.None;
|
||||||
|
if (dep.isSkipSelf) {
|
||||||
|
flags |= DepFlags.SkipSelf;
|
||||||
|
}
|
||||||
|
if (dep.isOptional) {
|
||||||
|
flags |= DepFlags.Optional;
|
||||||
|
}
|
||||||
|
if (dep.isValue) {
|
||||||
|
flags |= DepFlags.Value;
|
||||||
|
}
|
||||||
|
return flags === DepFlags.None ? expr : o.literalArr([o.literal(flags), expr]);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function lifecycleHookToNodeFlag(lifecycleHook: LifecycleHooks): NodeFlags {
|
||||||
|
let nodeFlag = NodeFlags.None;
|
||||||
|
switch (lifecycleHook) {
|
||||||
|
case LifecycleHooks.AfterContentChecked:
|
||||||
|
nodeFlag = NodeFlags.AfterContentChecked;
|
||||||
|
break;
|
||||||
|
case LifecycleHooks.AfterContentInit:
|
||||||
|
nodeFlag = NodeFlags.AfterContentInit;
|
||||||
|
break;
|
||||||
|
case LifecycleHooks.AfterViewChecked:
|
||||||
|
nodeFlag = NodeFlags.AfterViewChecked;
|
||||||
|
break;
|
||||||
|
case LifecycleHooks.AfterViewInit:
|
||||||
|
nodeFlag = NodeFlags.AfterViewInit;
|
||||||
|
break;
|
||||||
|
case LifecycleHooks.DoCheck:
|
||||||
|
nodeFlag = NodeFlags.DoCheck;
|
||||||
|
break;
|
||||||
|
case LifecycleHooks.OnChanges:
|
||||||
|
nodeFlag = NodeFlags.OnChanges;
|
||||||
|
break;
|
||||||
|
case LifecycleHooks.OnDestroy:
|
||||||
|
nodeFlag = NodeFlags.OnDestroy;
|
||||||
|
break;
|
||||||
|
case LifecycleHooks.OnInit:
|
||||||
|
nodeFlag = NodeFlags.OnInit;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return nodeFlag;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function componentFactoryResolverProviderDef(
|
||||||
|
flags: NodeFlags, entryComponents: CompileEntryComponentMetadata[]): {
|
||||||
|
providerExpr: o.Expression,
|
||||||
|
flags: NodeFlags,
|
||||||
|
depsExpr: o.Expression,
|
||||||
|
tokenExpr: o.Expression
|
||||||
|
} {
|
||||||
|
const entryComponentFactories = entryComponents.map(
|
||||||
|
(entryComponent) => o.importExpr({reference: entryComponent.componentFactory}));
|
||||||
|
const token = createIdentifierToken(Identifiers.ComponentFactoryResolver);
|
||||||
|
const classMeta = {
|
||||||
|
diDeps: [
|
||||||
|
{isValue: true, value: o.literalArr(entryComponentFactories)},
|
||||||
|
{token: token, isSkipSelf: true, isOptional: true},
|
||||||
|
{token: createIdentifierToken(Identifiers.NgModuleRef)},
|
||||||
|
],
|
||||||
|
lifecycleHooks: [],
|
||||||
|
reference: resolveIdentifier(Identifiers.CodegenComponentFactoryResolver)
|
||||||
|
};
|
||||||
|
const {providerExpr, flags: providerFlags, depsExpr} =
|
||||||
|
singleProviderDef(flags, ProviderAstType.PrivateService, {
|
||||||
|
token,
|
||||||
|
multi: false,
|
||||||
|
useClass: classMeta,
|
||||||
|
});
|
||||||
|
return {providerExpr, flags: providerFlags, depsExpr, tokenExpr: tokenExpr(token)};
|
||||||
|
}
|
|
@ -21,6 +21,8 @@ import {ParseSourceSpan} from '../parse_util';
|
||||||
import {ElementSchemaRegistry} from '../schema/element_schema_registry';
|
import {ElementSchemaRegistry} from '../schema/element_schema_registry';
|
||||||
import {AttrAst, BoundDirectivePropertyAst, BoundElementPropertyAst, BoundEventAst, BoundTextAst, DirectiveAst, ElementAst, EmbeddedTemplateAst, NgContentAst, PropertyBindingType, ProviderAst, ProviderAstType, QueryMatch, ReferenceAst, TemplateAst, TemplateAstVisitor, TextAst, VariableAst, templateVisitAll} from '../template_parser/template_ast';
|
import {AttrAst, BoundDirectivePropertyAst, BoundElementPropertyAst, BoundEventAst, BoundTextAst, DirectiveAst, ElementAst, EmbeddedTemplateAst, NgContentAst, PropertyBindingType, ProviderAst, ProviderAstType, QueryMatch, ReferenceAst, TemplateAst, TemplateAstVisitor, TextAst, VariableAst, templateVisitAll} from '../template_parser/template_ast';
|
||||||
|
|
||||||
|
import {componentFactoryResolverProviderDef, depDef, lifecycleHookToNodeFlag, providerDef} from './provider_compiler';
|
||||||
|
|
||||||
const CLASS_ATTR = 'class';
|
const CLASS_ATTR = 'class';
|
||||||
const STYLE_ATTR = 'style';
|
const STYLE_ATTR = 'style';
|
||||||
const IMPLICIT_TEMPLATE_VAR = '\$implicit';
|
const IMPLICIT_TEMPLATE_VAR = '\$implicit';
|
||||||
|
@ -96,10 +98,10 @@ interface UpdateExpression {
|
||||||
value: AST;
|
value: AST;
|
||||||
}
|
}
|
||||||
|
|
||||||
const LOG_VAR = o.variable('l');
|
const LOG_VAR = o.variable('_l');
|
||||||
const VIEW_VAR = o.variable('v');
|
const VIEW_VAR = o.variable('_v');
|
||||||
const CHECK_VAR = o.variable('ck');
|
const CHECK_VAR = o.variable('_ck');
|
||||||
const COMP_VAR = o.variable('co');
|
const COMP_VAR = o.variable('_co');
|
||||||
const EVENT_NAME_VAR = o.variable('en');
|
const EVENT_NAME_VAR = o.variable('en');
|
||||||
const ALLOW_DEFAULT_VAR = o.variable(`ad`);
|
const ALLOW_DEFAULT_VAR = o.variable(`ad`);
|
||||||
|
|
||||||
|
@ -409,10 +411,7 @@ class ViewBuilder implements TemplateAstVisitor, LocalResolver {
|
||||||
const hostBindings:
|
const hostBindings:
|
||||||
{context: o.Expression, inputAst: BoundElementPropertyAst, dirAst: DirectiveAst}[] = [];
|
{context: o.Expression, inputAst: BoundElementPropertyAst, dirAst: DirectiveAst}[] = [];
|
||||||
const hostEvents: {context: o.Expression, eventAst: BoundEventAst, dirAst: DirectiveAst}[] = [];
|
const hostEvents: {context: o.Expression, eventAst: BoundEventAst, dirAst: DirectiveAst}[] = [];
|
||||||
const componentFactoryResolverProvider = createComponentFactoryResolver(ast.directives);
|
this._visitComponentFactoryResolverProvider(ast.directives);
|
||||||
if (componentFactoryResolverProvider) {
|
|
||||||
this._visitProvider(componentFactoryResolverProvider, ast.queryMatches);
|
|
||||||
}
|
|
||||||
|
|
||||||
ast.providers.forEach((providerAst, providerIndex) => {
|
ast.providers.forEach((providerAst, providerIndex) => {
|
||||||
let dirAst: DirectiveAst = undefined !;
|
let dirAst: DirectiveAst = undefined !;
|
||||||
|
@ -585,47 +584,58 @@ class ViewBuilder implements TemplateAstVisitor, LocalResolver {
|
||||||
}
|
}
|
||||||
|
|
||||||
private _visitProvider(providerAst: ProviderAst, queryMatches: QueryMatch[]): void {
|
private _visitProvider(providerAst: ProviderAst, queryMatches: QueryMatch[]): void {
|
||||||
|
this._addProviderNode(this._visitProviderOrDirective(providerAst, queryMatches));
|
||||||
|
}
|
||||||
|
|
||||||
|
private _visitComponentFactoryResolverProvider(directives: DirectiveAst[]) {
|
||||||
|
const componentDirMeta = directives.find(dirAst => dirAst.directive.isComponent);
|
||||||
|
if (componentDirMeta && componentDirMeta.directive.entryComponents.length) {
|
||||||
|
const {providerExpr, depsExpr, flags, tokenExpr} = componentFactoryResolverProviderDef(
|
||||||
|
NodeFlags.PrivateProvider, componentDirMeta.directive.entryComponents);
|
||||||
|
this._addProviderNode({
|
||||||
|
providerExpr,
|
||||||
|
depsExpr,
|
||||||
|
flags,
|
||||||
|
tokenExpr,
|
||||||
|
queryMatchExprs: [],
|
||||||
|
sourceSpan: componentDirMeta.sourceSpan
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private _addProviderNode(data: {
|
||||||
|
flags: NodeFlags,
|
||||||
|
queryMatchExprs: o.Expression[],
|
||||||
|
providerExpr: o.Expression,
|
||||||
|
depsExpr: o.Expression,
|
||||||
|
tokenExpr: o.Expression,
|
||||||
|
sourceSpan: ParseSourceSpan
|
||||||
|
}) {
|
||||||
const nodeIndex = this.nodes.length;
|
const nodeIndex = this.nodes.length;
|
||||||
// reserve the space in the nodeDefs array so we can add children
|
|
||||||
this.nodes.push(null !);
|
|
||||||
|
|
||||||
const {flags, queryMatchExprs, providerExpr, depsExpr} =
|
|
||||||
this._visitProviderOrDirective(providerAst, queryMatches);
|
|
||||||
|
|
||||||
// providerDef(
|
// providerDef(
|
||||||
// flags: NodeFlags, matchedQueries: [string, QueryValueType][], token:any,
|
// flags: NodeFlags, matchedQueries: [string, QueryValueType][], token:any,
|
||||||
// value: any, deps: ([DepFlags, any] | any)[]): NodeDef;
|
// value: any, deps: ([DepFlags, any] | any)[]): NodeDef;
|
||||||
this.nodes[nodeIndex] = () => ({
|
this.nodes.push(
|
||||||
sourceSpan: providerAst.sourceSpan,
|
() => ({
|
||||||
nodeFlags: flags,
|
sourceSpan: data.sourceSpan,
|
||||||
|
nodeFlags: data.flags,
|
||||||
nodeDef: o.importExpr(createIdentifier(Identifiers.providerDef)).callFn([
|
nodeDef: o.importExpr(createIdentifier(Identifiers.providerDef)).callFn([
|
||||||
o.literal(flags), queryMatchExprs.length ? o.literalArr(queryMatchExprs) : o.NULL_EXPR,
|
o.literal(data.flags),
|
||||||
tokenExpr(providerAst.token), providerExpr, depsExpr
|
data.queryMatchExprs.length ? o.literalArr(data.queryMatchExprs) : o.NULL_EXPR,
|
||||||
|
data.tokenExpr, data.providerExpr, data.depsExpr
|
||||||
])
|
])
|
||||||
});
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
private _visitProviderOrDirective(providerAst: ProviderAst, queryMatches: QueryMatch[]): {
|
private _visitProviderOrDirective(providerAst: ProviderAst, queryMatches: QueryMatch[]): {
|
||||||
flags: NodeFlags,
|
flags: NodeFlags,
|
||||||
|
tokenExpr: o.Expression,
|
||||||
|
sourceSpan: ParseSourceSpan,
|
||||||
queryMatchExprs: o.Expression[],
|
queryMatchExprs: o.Expression[],
|
||||||
providerExpr: o.Expression,
|
providerExpr: o.Expression,
|
||||||
depsExpr: o.Expression
|
depsExpr: o.Expression
|
||||||
} {
|
} {
|
||||||
let flags = NodeFlags.None;
|
let flags = NodeFlags.None;
|
||||||
if (!providerAst.eager) {
|
|
||||||
flags |= NodeFlags.LazyProvider;
|
|
||||||
}
|
|
||||||
if (providerAst.providerType === ProviderAstType.PrivateService) {
|
|
||||||
flags |= NodeFlags.PrivateProvider;
|
|
||||||
}
|
|
||||||
providerAst.lifecycleHooks.forEach((lifecycleHook) => {
|
|
||||||
// for regular providers, we only support ngOnDestroy
|
|
||||||
if (lifecycleHook === LifecycleHooks.OnDestroy ||
|
|
||||||
providerAst.providerType === ProviderAstType.Directive ||
|
|
||||||
providerAst.providerType === ProviderAstType.Component) {
|
|
||||||
flags |= lifecycleHookToNodeFlag(lifecycleHook);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
let queryMatchExprs: o.Expression[] = [];
|
let queryMatchExprs: o.Expression[] = [];
|
||||||
|
|
||||||
queryMatches.forEach((match) => {
|
queryMatches.forEach((match) => {
|
||||||
|
@ -634,8 +644,15 @@ class ViewBuilder implements TemplateAstVisitor, LocalResolver {
|
||||||
o.literalArr([o.literal(match.queryId), o.literal(QueryValueType.Provider)]));
|
o.literalArr([o.literal(match.queryId), o.literal(QueryValueType.Provider)]));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
const {providerExpr, depsExpr, flags: providerType} = providerDef(providerAst);
|
const {providerExpr, depsExpr, flags: providerFlags, tokenExpr} = providerDef(providerAst);
|
||||||
return {flags: flags | providerType, queryMatchExprs, providerExpr, depsExpr};
|
return {
|
||||||
|
flags: flags | providerFlags,
|
||||||
|
queryMatchExprs,
|
||||||
|
providerExpr,
|
||||||
|
depsExpr,
|
||||||
|
tokenExpr,
|
||||||
|
sourceSpan: providerAst.sourceSpan
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
getLocal(name: string): o.Expression|null {
|
getLocal(name: string): o.Expression|null {
|
||||||
|
@ -885,100 +902,6 @@ class ViewBuilder implements TemplateAstVisitor, LocalResolver {
|
||||||
visitAttr(ast: AttrAst, context: any): any {}
|
visitAttr(ast: AttrAst, context: any): any {}
|
||||||
}
|
}
|
||||||
|
|
||||||
function providerDef(providerAst: ProviderAst):
|
|
||||||
{providerExpr: o.Expression, flags: NodeFlags, depsExpr: o.Expression} {
|
|
||||||
return providerAst.multiProvider ?
|
|
||||||
multiProviderDef(providerAst.providers) :
|
|
||||||
singleProviderDef(providerAst.providerType, providerAst.providers[0]);
|
|
||||||
}
|
|
||||||
|
|
||||||
function multiProviderDef(providers: CompileProviderMetadata[]):
|
|
||||||
{providerExpr: o.Expression, flags: NodeFlags, depsExpr: o.Expression} {
|
|
||||||
const allDepDefs: o.Expression[] = [];
|
|
||||||
const allParams: o.FnParam[] = [];
|
|
||||||
const exprs = providers.map((provider, providerIndex) => {
|
|
||||||
let expr: o.Expression;
|
|
||||||
if (provider.useClass) {
|
|
||||||
const depExprs = convertDeps(providerIndex, provider.deps || provider.useClass.diDeps);
|
|
||||||
expr = o.importExpr(provider.useClass).instantiate(depExprs);
|
|
||||||
} else if (provider.useFactory) {
|
|
||||||
const depExprs = convertDeps(providerIndex, provider.deps || provider.useFactory.diDeps);
|
|
||||||
expr = o.importExpr(provider.useFactory).callFn(depExprs);
|
|
||||||
} else if (provider.useExisting) {
|
|
||||||
const depExprs = convertDeps(providerIndex, [{token: provider.useExisting}]);
|
|
||||||
expr = depExprs[0];
|
|
||||||
} else {
|
|
||||||
expr = convertValueToOutputAst(provider.useValue);
|
|
||||||
}
|
|
||||||
return expr;
|
|
||||||
});
|
|
||||||
const providerExpr =
|
|
||||||
o.fn(allParams, [new o.ReturnStatement(o.literalArr(exprs))], o.INFERRED_TYPE);
|
|
||||||
return {providerExpr, flags: NodeFlags.TypeFactoryProvider, depsExpr: o.literalArr(allDepDefs)};
|
|
||||||
|
|
||||||
function convertDeps(providerIndex: number, deps: CompileDiDependencyMetadata[]) {
|
|
||||||
return deps.map((dep, depIndex) => {
|
|
||||||
const paramName = `p${providerIndex}_${depIndex}`;
|
|
||||||
allParams.push(new o.FnParam(paramName, o.DYNAMIC_TYPE));
|
|
||||||
allDepDefs.push(depDef(dep));
|
|
||||||
return o.variable(paramName);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function singleProviderDef(providerType: ProviderAstType, providerMeta: CompileProviderMetadata):
|
|
||||||
{providerExpr: o.Expression, flags: NodeFlags, depsExpr: o.Expression} {
|
|
||||||
let providerExpr: o.Expression;
|
|
||||||
let flags: NodeFlags;
|
|
||||||
let deps: CompileDiDependencyMetadata[];
|
|
||||||
if (providerType === ProviderAstType.Directive || providerType === ProviderAstType.Component) {
|
|
||||||
providerExpr = o.importExpr(providerMeta.useClass !);
|
|
||||||
flags = NodeFlags.TypeDirective;
|
|
||||||
deps = providerMeta.deps || providerMeta.useClass !.diDeps;
|
|
||||||
} else {
|
|
||||||
if (providerMeta.useClass) {
|
|
||||||
providerExpr = o.importExpr(providerMeta.useClass);
|
|
||||||
flags = NodeFlags.TypeClassProvider;
|
|
||||||
deps = providerMeta.deps || providerMeta.useClass.diDeps;
|
|
||||||
} else if (providerMeta.useFactory) {
|
|
||||||
providerExpr = o.importExpr(providerMeta.useFactory);
|
|
||||||
flags = NodeFlags.TypeFactoryProvider;
|
|
||||||
deps = providerMeta.deps || providerMeta.useFactory.diDeps;
|
|
||||||
} else if (providerMeta.useExisting) {
|
|
||||||
providerExpr = o.NULL_EXPR;
|
|
||||||
flags = NodeFlags.TypeUseExistingProvider;
|
|
||||||
deps = [{token: providerMeta.useExisting}];
|
|
||||||
} else {
|
|
||||||
providerExpr = convertValueToOutputAst(providerMeta.useValue);
|
|
||||||
flags = NodeFlags.TypeValueProvider;
|
|
||||||
deps = [];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const depsExpr = o.literalArr(deps.map(dep => depDef(dep)));
|
|
||||||
return {providerExpr, flags, depsExpr};
|
|
||||||
}
|
|
||||||
|
|
||||||
function tokenExpr(tokenMeta: CompileTokenMetadata): o.Expression {
|
|
||||||
return tokenMeta.identifier ? o.importExpr(tokenMeta.identifier) : o.literal(tokenMeta.value);
|
|
||||||
}
|
|
||||||
|
|
||||||
function depDef(dep: CompileDiDependencyMetadata): o.Expression {
|
|
||||||
// Note: the following fields have already been normalized out by provider_analyzer:
|
|
||||||
// - isAttribute, isSelf, isHost
|
|
||||||
const expr = dep.isValue ? convertValueToOutputAst(dep.value) : tokenExpr(dep.token !);
|
|
||||||
let flags = DepFlags.None;
|
|
||||||
if (dep.isSkipSelf) {
|
|
||||||
flags |= DepFlags.SkipSelf;
|
|
||||||
}
|
|
||||||
if (dep.isOptional) {
|
|
||||||
flags |= DepFlags.Optional;
|
|
||||||
}
|
|
||||||
if (dep.isValue) {
|
|
||||||
flags |= DepFlags.Value;
|
|
||||||
}
|
|
||||||
return flags === DepFlags.None ? expr : o.literalArr([o.literal(flags), expr]);
|
|
||||||
}
|
|
||||||
|
|
||||||
function needsAdditionalRootNode(astNodes: TemplateAst[]): boolean {
|
function needsAdditionalRootNode(astNodes: TemplateAst[]): boolean {
|
||||||
const lastAstNode = astNodes[astNodes.length - 1];
|
const lastAstNode = astNodes[astNodes.length - 1];
|
||||||
if (lastAstNode instanceof EmbeddedTemplateAst) {
|
if (lastAstNode instanceof EmbeddedTemplateAst) {
|
||||||
|
@ -995,36 +918,6 @@ function needsAdditionalRootNode(astNodes: TemplateAst[]): boolean {
|
||||||
return lastAstNode instanceof NgContentAst;
|
return lastAstNode instanceof NgContentAst;
|
||||||
}
|
}
|
||||||
|
|
||||||
function lifecycleHookToNodeFlag(lifecycleHook: LifecycleHooks): NodeFlags {
|
|
||||||
let nodeFlag = NodeFlags.None;
|
|
||||||
switch (lifecycleHook) {
|
|
||||||
case LifecycleHooks.AfterContentChecked:
|
|
||||||
nodeFlag = NodeFlags.AfterContentChecked;
|
|
||||||
break;
|
|
||||||
case LifecycleHooks.AfterContentInit:
|
|
||||||
nodeFlag = NodeFlags.AfterContentInit;
|
|
||||||
break;
|
|
||||||
case LifecycleHooks.AfterViewChecked:
|
|
||||||
nodeFlag = NodeFlags.AfterViewChecked;
|
|
||||||
break;
|
|
||||||
case LifecycleHooks.AfterViewInit:
|
|
||||||
nodeFlag = NodeFlags.AfterViewInit;
|
|
||||||
break;
|
|
||||||
case LifecycleHooks.DoCheck:
|
|
||||||
nodeFlag = NodeFlags.DoCheck;
|
|
||||||
break;
|
|
||||||
case LifecycleHooks.OnChanges:
|
|
||||||
nodeFlag = NodeFlags.OnChanges;
|
|
||||||
break;
|
|
||||||
case LifecycleHooks.OnDestroy:
|
|
||||||
nodeFlag = NodeFlags.OnDestroy;
|
|
||||||
break;
|
|
||||||
case LifecycleHooks.OnInit:
|
|
||||||
nodeFlag = NodeFlags.OnInit;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return nodeFlag;
|
|
||||||
}
|
|
||||||
|
|
||||||
function elementBindingDef(inputAst: BoundElementPropertyAst, dirAst: DirectiveAst): o.Expression {
|
function elementBindingDef(inputAst: BoundElementPropertyAst, dirAst: DirectiveAst): o.Expression {
|
||||||
switch (inputAst.type) {
|
switch (inputAst.type) {
|
||||||
|
@ -1147,30 +1040,6 @@ function staticViewQueryIds(nodeStaticQueryIds: Map<TemplateAst, StaticAndDynami
|
||||||
return {staticQueryIds, dynamicQueryIds};
|
return {staticQueryIds, dynamicQueryIds};
|
||||||
}
|
}
|
||||||
|
|
||||||
function createComponentFactoryResolver(directives: DirectiveAst[]): ProviderAst|null {
|
|
||||||
const componentDirMeta = directives.find(dirAst => dirAst.directive.isComponent);
|
|
||||||
if (componentDirMeta && componentDirMeta.directive.entryComponents.length) {
|
|
||||||
const entryComponentFactories = componentDirMeta.directive.entryComponents.map(
|
|
||||||
(entryComponent) => o.importExpr({reference: entryComponent.componentFactory}));
|
|
||||||
|
|
||||||
const token = createIdentifierToken(Identifiers.ComponentFactoryResolver);
|
|
||||||
|
|
||||||
const classMeta: CompileTypeMetadata = {
|
|
||||||
diDeps: [
|
|
||||||
{isValue: true, value: o.literalArr(entryComponentFactories)},
|
|
||||||
{token: token, isSkipSelf: true, isOptional: true},
|
|
||||||
{token: createIdentifierToken(Identifiers.NgModuleRef)},
|
|
||||||
],
|
|
||||||
lifecycleHooks: [],
|
|
||||||
reference: resolveIdentifier(Identifiers.CodegenComponentFactoryResolver)
|
|
||||||
};
|
|
||||||
return new ProviderAst(
|
|
||||||
token, false, true, [{token, multi: false, useClass: classMeta}],
|
|
||||||
ProviderAstType.PrivateService, [], componentDirMeta.sourceSpan);
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
function elementEventNameAndTarget(
|
function elementEventNameAndTarget(
|
||||||
eventAst: BoundEventAst, dirAst: DirectiveAst | null): {name: string, target: string | null} {
|
eventAst: BoundEventAst, dirAst: DirectiveAst | null): {name: string, target: string | null} {
|
||||||
if (eventAst.isAnimation) {
|
if (eventAst.isAnimation) {
|
||||||
|
|
|
@ -0,0 +1,36 @@
|
||||||
|
/**
|
||||||
|
* @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 {async} from '@angular/core/testing';
|
||||||
|
|
||||||
|
import {MockDirectory, compile, expectNoDiagnostics, setup} from './test_util';
|
||||||
|
|
||||||
|
describe('regressions', () => {
|
||||||
|
let angularFiles = setup();
|
||||||
|
|
||||||
|
it('should compile components with empty templates', async(() => {
|
||||||
|
const appDir = {
|
||||||
|
'app.module.ts': `
|
||||||
|
import { Component, NgModule } from '@angular/core';
|
||||||
|
|
||||||
|
@Component({template: ''})
|
||||||
|
export class EmptyComp {}
|
||||||
|
|
||||||
|
@NgModule({declarations: [EmptyComp]})
|
||||||
|
export class MyModule {}
|
||||||
|
`
|
||||||
|
};
|
||||||
|
const rootDir = {'app': appDir};
|
||||||
|
compile([rootDir, angularFiles], {postCompile: expectNoDiagnostics}, {
|
||||||
|
noUnusedLocals: true,
|
||||||
|
noUnusedParameters: true
|
||||||
|
}).then((result) => {
|
||||||
|
expect(result.genFiles.find((f) => f.genFileUrl === '/app/app.module.ngfactory.ts'))
|
||||||
|
.toBeTruthy();
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
});
|
|
@ -600,12 +600,15 @@ function isSource(fileName: string): boolean {
|
||||||
return !/\.d\.ts$/.test(fileName) && /\.ts$/.test(fileName);
|
return !/\.d\.ts$/.test(fileName) && /\.ts$/.test(fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function compile(rootDirs: MockData, options: {
|
export function compile(
|
||||||
|
rootDirs: MockData, options: {
|
||||||
emit?: boolean,
|
emit?: boolean,
|
||||||
useSummaries?: boolean,
|
useSummaries?: boolean,
|
||||||
preCompile?: (program: ts.Program) => void,
|
preCompile?: (program: ts.Program) => void,
|
||||||
postCompile?: (program: ts.Program) => void,
|
postCompile?: (program: ts.Program) => void,
|
||||||
}& AotCompilerOptions = {}): Promise<{genFiles: GeneratedFile[], outDir: MockDirectory}> {
|
}& AotCompilerOptions = {},
|
||||||
|
tsOptions: ts.CompilerOptions = {}):
|
||||||
|
Promise<{genFiles: GeneratedFile[], outDir: MockDirectory}> {
|
||||||
// Make sure we always return errors via the promise...
|
// Make sure we always return errors via the promise...
|
||||||
return Promise.resolve(null).then(() => {
|
return Promise.resolve(null).then(() => {
|
||||||
// when using summaries, always emit so the next step can use the results.
|
// when using summaries, always emit so the next step can use the results.
|
||||||
|
@ -621,8 +624,9 @@ export function compile(rootDirs: MockData, options: {
|
||||||
aotHost.hideMetadata();
|
aotHost.hideMetadata();
|
||||||
aotHost.tsFilesOnly();
|
aotHost.tsFilesOnly();
|
||||||
}
|
}
|
||||||
|
const tsSettings = {...settings, ...tsOptions};
|
||||||
const scripts = host.scriptNames.slice(0);
|
const scripts = host.scriptNames.slice(0);
|
||||||
const program = ts.createProgram(scripts, settings, host);
|
const program = ts.createProgram(scripts, tsSettings, host);
|
||||||
if (preCompile) preCompile(program);
|
if (preCompile) preCompile(program);
|
||||||
const {compiler, reflector} = createAotCompiler(aotHost, options);
|
const {compiler, reflector} = createAotCompiler(aotHost, options);
|
||||||
return compiler.compileAll(program.getSourceFiles().map(sf => sf.fileName)).then(genFiles => {
|
return compiler.compileAll(program.getSourceFiles().map(sf => sf.fileName)).then(genFiles => {
|
||||||
|
@ -630,7 +634,7 @@ export function compile(rootDirs: MockData, options: {
|
||||||
file => isSource(file.genFileUrl) ? host.addScript(file.genFileUrl, file.source) :
|
file => isSource(file.genFileUrl) ? host.addScript(file.genFileUrl, file.source) :
|
||||||
host.override(file.genFileUrl, file.source));
|
host.override(file.genFileUrl, file.source));
|
||||||
const scripts = host.scriptNames.slice(0);
|
const scripts = host.scriptNames.slice(0);
|
||||||
const newProgram = ts.createProgram(scripts, settings, host);
|
const newProgram = ts.createProgram(scripts, tsSettings, host);
|
||||||
if (postCompile) postCompile(newProgram);
|
if (postCompile) postCompile(newProgram);
|
||||||
if (emit) {
|
if (emit) {
|
||||||
newProgram.emit();
|
newProgram.emit();
|
||||||
|
|
|
@ -977,8 +977,8 @@ Binding to attribute 'onEvent' is disallowed for security reasons ("<my-componen
|
||||||
const dirA = createDir('[dirA]', {providers: [provider]});
|
const dirA = createDir('[dirA]', {providers: [provider]});
|
||||||
const elAst: ElementAst = <ElementAst>parse('<div dirA>', [dirA])[0];
|
const elAst: ElementAst = <ElementAst>parse('<div dirA>', [dirA])[0];
|
||||||
expect(elAst.providers.length).toBe(2);
|
expect(elAst.providers.length).toBe(2);
|
||||||
expect(elAst.providers[1].providerType).toBe(ProviderAstType.PublicService);
|
expect(elAst.providers[0].providerType).toBe(ProviderAstType.PublicService);
|
||||||
expect(elAst.providers[1].providers).toEqual([provider]);
|
expect(elAst.providers[0].providers).toEqual([provider]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should use the private providers of a component', () => {
|
it('should use the private providers of a component', () => {
|
||||||
|
@ -986,8 +986,8 @@ Binding to attribute 'onEvent' is disallowed for security reasons ("<my-componen
|
||||||
const comp = createDir('my-comp', {viewProviders: [provider]});
|
const comp = createDir('my-comp', {viewProviders: [provider]});
|
||||||
const elAst: ElementAst = <ElementAst>parse('<my-comp>', [comp])[0];
|
const elAst: ElementAst = <ElementAst>parse('<my-comp>', [comp])[0];
|
||||||
expect(elAst.providers.length).toBe(2);
|
expect(elAst.providers.length).toBe(2);
|
||||||
expect(elAst.providers[1].providerType).toBe(ProviderAstType.PrivateService);
|
expect(elAst.providers[0].providerType).toBe(ProviderAstType.PrivateService);
|
||||||
expect(elAst.providers[1].providers).toEqual([provider]);
|
expect(elAst.providers[0].providers).toEqual([provider]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should support multi providers', () => {
|
it('should support multi providers', () => {
|
||||||
|
@ -998,8 +998,8 @@ Binding to attribute 'onEvent' is disallowed for security reasons ("<my-componen
|
||||||
const dirB = createDir('[dirB]', {providers: [provider2]});
|
const dirB = createDir('[dirB]', {providers: [provider2]});
|
||||||
const elAst: ElementAst = <ElementAst>parse('<div dirA dirB>', [dirA, dirB])[0];
|
const elAst: ElementAst = <ElementAst>parse('<div dirA dirB>', [dirA, dirB])[0];
|
||||||
expect(elAst.providers.length).toBe(4);
|
expect(elAst.providers.length).toBe(4);
|
||||||
expect(elAst.providers[2].providers).toEqual([provider0, provider2]);
|
expect(elAst.providers[0].providers).toEqual([provider0, provider2]);
|
||||||
expect(elAst.providers[3].providers).toEqual([provider1]);
|
expect(elAst.providers[1].providers).toEqual([provider1]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should overwrite non multi providers', () => {
|
it('should overwrite non multi providers', () => {
|
||||||
|
@ -1010,8 +1010,8 @@ Binding to attribute 'onEvent' is disallowed for security reasons ("<my-componen
|
||||||
const dirB = createDir('[dirB]', {providers: [provider3]});
|
const dirB = createDir('[dirB]', {providers: [provider3]});
|
||||||
const elAst: ElementAst = <ElementAst>parse('<div dirA dirB>', [dirA, dirB])[0];
|
const elAst: ElementAst = <ElementAst>parse('<div dirA dirB>', [dirA, dirB])[0];
|
||||||
expect(elAst.providers.length).toBe(4);
|
expect(elAst.providers.length).toBe(4);
|
||||||
expect(elAst.providers[2].providers).toEqual([provider3]);
|
expect(elAst.providers[0].providers).toEqual([provider3]);
|
||||||
expect(elAst.providers[3].providers).toEqual([provider2]);
|
expect(elAst.providers[1].providers).toEqual([provider2]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should overwrite component providers by directive providers', () => {
|
it('should overwrite component providers by directive providers', () => {
|
||||||
|
@ -1021,7 +1021,7 @@ Binding to attribute 'onEvent' is disallowed for security reasons ("<my-componen
|
||||||
const dirA = createDir('[dirA]', {providers: [dirProvider]});
|
const dirA = createDir('[dirA]', {providers: [dirProvider]});
|
||||||
const elAst: ElementAst = <ElementAst>parse('<my-comp dirA>', [dirA, comp])[0];
|
const elAst: ElementAst = <ElementAst>parse('<my-comp dirA>', [dirA, comp])[0];
|
||||||
expect(elAst.providers.length).toBe(3);
|
expect(elAst.providers.length).toBe(3);
|
||||||
expect(elAst.providers[2].providers).toEqual([dirProvider]);
|
expect(elAst.providers[0].providers).toEqual([dirProvider]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should overwrite view providers by directive providers', () => {
|
it('should overwrite view providers by directive providers', () => {
|
||||||
|
@ -1031,7 +1031,7 @@ Binding to attribute 'onEvent' is disallowed for security reasons ("<my-componen
|
||||||
const dirA = createDir('[dirA]', {providers: [dirProvider]});
|
const dirA = createDir('[dirA]', {providers: [dirProvider]});
|
||||||
const elAst: ElementAst = <ElementAst>parse('<my-comp dirA>', [dirA, comp])[0];
|
const elAst: ElementAst = <ElementAst>parse('<my-comp dirA>', [dirA, comp])[0];
|
||||||
expect(elAst.providers.length).toBe(3);
|
expect(elAst.providers.length).toBe(3);
|
||||||
expect(elAst.providers[2].providers).toEqual([dirProvider]);
|
expect(elAst.providers[0].providers).toEqual([dirProvider]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should overwrite directives by providers', () => {
|
it('should overwrite directives by providers', () => {
|
||||||
|
@ -1053,17 +1053,17 @@ Binding to attribute 'onEvent' is disallowed for security reasons ("<my-componen
|
||||||
`Mixing multi and non multi provider is not possible for token service0 ("[ERROR ->]<div dirA dirB>"): TestComp@0:0`);
|
`Mixing multi and non multi provider is not possible for token service0 ("[ERROR ->]<div dirA dirB>"): TestComp@0:0`);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should sort providers by their DI order', () => {
|
it('should sort providers by their DI order, lazy providers first', () => {
|
||||||
const provider0 = createProvider('service0', {deps: ['type:[dir2]']});
|
const provider0 = createProvider('service0', {deps: ['type:[dir2]']});
|
||||||
const provider1 = createProvider('service1');
|
const provider1 = createProvider('service1');
|
||||||
const dir2 = createDir('[dir2]', {deps: ['service1']});
|
const dir2 = createDir('[dir2]', {deps: ['service1']});
|
||||||
const comp = createDir('my-comp', {providers: [provider0, provider1]});
|
const comp = createDir('my-comp', {providers: [provider0, provider1]});
|
||||||
const elAst: ElementAst = <ElementAst>parse('<my-comp dir2>', [comp, dir2])[0];
|
const elAst: ElementAst = <ElementAst>parse('<my-comp dir2>', [comp, dir2])[0];
|
||||||
expect(elAst.providers.length).toBe(4);
|
expect(elAst.providers.length).toBe(4);
|
||||||
expect(elAst.providers[0].providers[0].useClass).toEqual(comp.type);
|
expect(elAst.providers[1].providers[0].useClass).toEqual(comp.type);
|
||||||
expect(elAst.providers[1].providers).toEqual([provider1]);
|
expect(elAst.providers[2].providers).toEqual([provider1]);
|
||||||
expect(elAst.providers[2].providers[0].useClass).toEqual(dir2.type);
|
expect(elAst.providers[3].providers[0].useClass).toEqual(dir2.type);
|
||||||
expect(elAst.providers[3].providers).toEqual([provider0]);
|
expect(elAst.providers[0].providers).toEqual([provider0]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should sort directives by their DI order', () => {
|
it('should sort directives by their DI order', () => {
|
||||||
|
@ -1086,12 +1086,12 @@ Binding to attribute 'onEvent' is disallowed for security reasons ("<my-componen
|
||||||
const dirA = createDir('[dirA]', {providers: [provider0, provider1], deps: ['service0']});
|
const dirA = createDir('[dirA]', {providers: [provider0, provider1], deps: ['service0']});
|
||||||
const elAst: ElementAst = <ElementAst>parse('<div dirA>', [dirA])[0];
|
const elAst: ElementAst = <ElementAst>parse('<div dirA>', [dirA])[0];
|
||||||
expect(elAst.providers.length).toBe(3);
|
expect(elAst.providers.length).toBe(3);
|
||||||
expect(elAst.providers[0].providers).toEqual([provider0]);
|
expect(elAst.providers[1].providers).toEqual([provider0]);
|
||||||
expect(elAst.providers[0].eager).toBe(true);
|
|
||||||
expect(elAst.providers[1].providers[0].useClass).toEqual(dirA.type);
|
|
||||||
expect(elAst.providers[1].eager).toBe(true);
|
expect(elAst.providers[1].eager).toBe(true);
|
||||||
expect(elAst.providers[2].providers).toEqual([provider1]);
|
expect(elAst.providers[2].providers[0].useClass).toEqual(dirA.type);
|
||||||
expect(elAst.providers[2].eager).toBe(false);
|
expect(elAst.providers[2].eager).toBe(true);
|
||||||
|
expect(elAst.providers[0].providers).toEqual([provider1]);
|
||||||
|
expect(elAst.providers[0].eager).toBe(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should mark dependencies on parent elements as eager', () => {
|
it('should mark dependencies on parent elements as eager', () => {
|
||||||
|
@ -1102,12 +1102,12 @@ Binding to attribute 'onEvent' is disallowed for security reasons ("<my-componen
|
||||||
const elAst: ElementAst =
|
const elAst: ElementAst =
|
||||||
<ElementAst>parse('<div dirA><div dirB></div></div>', [dirA, dirB])[0];
|
<ElementAst>parse('<div dirA><div dirB></div></div>', [dirA, dirB])[0];
|
||||||
expect(elAst.providers.length).toBe(3);
|
expect(elAst.providers.length).toBe(3);
|
||||||
expect(elAst.providers[0].providers[0].useClass).toEqual(dirA.type);
|
expect(elAst.providers[1].providers[0].useClass).toEqual(dirA.type);
|
||||||
expect(elAst.providers[0].eager).toBe(true);
|
|
||||||
expect(elAst.providers[1].providers).toEqual([provider0]);
|
|
||||||
expect(elAst.providers[1].eager).toBe(true);
|
expect(elAst.providers[1].eager).toBe(true);
|
||||||
expect(elAst.providers[2].providers).toEqual([provider1]);
|
expect(elAst.providers[2].providers).toEqual([provider0]);
|
||||||
expect(elAst.providers[2].eager).toBe(false);
|
expect(elAst.providers[2].eager).toBe(true);
|
||||||
|
expect(elAst.providers[0].providers).toEqual([provider1]);
|
||||||
|
expect(elAst.providers[0].eager).toBe(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should mark queried providers as eager', () => {
|
it('should mark queried providers as eager', () => {
|
||||||
|
@ -1117,12 +1117,12 @@ Binding to attribute 'onEvent' is disallowed for security reasons ("<my-componen
|
||||||
createDir('[dirA]', {providers: [provider0, provider1], queries: ['service0']});
|
createDir('[dirA]', {providers: [provider0, provider1], queries: ['service0']});
|
||||||
const elAst: ElementAst = <ElementAst>parse('<div dirA></div>', [dirA])[0];
|
const elAst: ElementAst = <ElementAst>parse('<div dirA></div>', [dirA])[0];
|
||||||
expect(elAst.providers.length).toBe(3);
|
expect(elAst.providers.length).toBe(3);
|
||||||
expect(elAst.providers[0].providers[0].useClass).toEqual(dirA.type);
|
expect(elAst.providers[1].providers[0].useClass).toEqual(dirA.type);
|
||||||
expect(elAst.providers[0].eager).toBe(true);
|
|
||||||
expect(elAst.providers[1].providers).toEqual([provider0]);
|
|
||||||
expect(elAst.providers[1].eager).toBe(true);
|
expect(elAst.providers[1].eager).toBe(true);
|
||||||
expect(elAst.providers[2].providers).toEqual([provider1]);
|
expect(elAst.providers[2].providers).toEqual([provider0]);
|
||||||
expect(elAst.providers[2].eager).toBe(false);
|
expect(elAst.providers[2].eager).toBe(true);
|
||||||
|
expect(elAst.providers[0].providers).toEqual([provider1]);
|
||||||
|
expect(elAst.providers[0].eager).toBe(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should not mark dependencies across embedded views as eager', () => {
|
it('should not mark dependencies across embedded views as eager', () => {
|
||||||
|
@ -1132,10 +1132,10 @@ Binding to attribute 'onEvent' is disallowed for security reasons ("<my-componen
|
||||||
const elAst: ElementAst =
|
const elAst: ElementAst =
|
||||||
<ElementAst>parse('<div dirA><div *ngIf dirB></div></div>', [dirA, dirB])[0];
|
<ElementAst>parse('<div dirA><div *ngIf dirB></div></div>', [dirA, dirB])[0];
|
||||||
expect(elAst.providers.length).toBe(2);
|
expect(elAst.providers.length).toBe(2);
|
||||||
expect(elAst.providers[0].providers[0].useClass).toEqual(dirA.type);
|
expect(elAst.providers[1].providers[0].useClass).toEqual(dirA.type);
|
||||||
expect(elAst.providers[0].eager).toBe(true);
|
expect(elAst.providers[1].eager).toBe(true);
|
||||||
expect(elAst.providers[1].providers).toEqual([provider0]);
|
expect(elAst.providers[0].providers).toEqual([provider0]);
|
||||||
expect(elAst.providers[1].eager).toBe(false);
|
expect(elAst.providers[0].eager).toBe(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should report missing @Self() deps as errors', () => {
|
it('should report missing @Self() deps as errors', () => {
|
||||||
|
|
|
@ -23,7 +23,7 @@ import {Injectable, InjectionToken, Injector, Provider, ReflectiveInjector} from
|
||||||
import {CompilerFactory, CompilerOptions} from './linker/compiler';
|
import {CompilerFactory, CompilerOptions} from './linker/compiler';
|
||||||
import {ComponentFactory, ComponentRef} from './linker/component_factory';
|
import {ComponentFactory, ComponentRef} from './linker/component_factory';
|
||||||
import {ComponentFactoryBoundToModule, ComponentFactoryResolver} from './linker/component_factory_resolver';
|
import {ComponentFactoryBoundToModule, ComponentFactoryResolver} from './linker/component_factory_resolver';
|
||||||
import {NgModuleFactory, NgModuleInjector, NgModuleRef} from './linker/ng_module_factory';
|
import {InternalNgModuleRef, NgModuleFactory, NgModuleRef} from './linker/ng_module_factory';
|
||||||
import {InternalViewRef, ViewRef} from './linker/view_ref';
|
import {InternalViewRef, ViewRef} from './linker/view_ref';
|
||||||
import {WtfScopeFn, wtfCreateScope, wtfLeave} from './profile/profile';
|
import {WtfScopeFn, wtfCreateScope, wtfLeave} from './profile/profile';
|
||||||
import {Testability, TestabilityRegistry} from './testability/testability';
|
import {Testability, TestabilityRegistry} from './testability/testability';
|
||||||
|
@ -293,7 +293,7 @@ export class PlatformRef_ extends PlatformRef {
|
||||||
return ngZone.run(() => {
|
return ngZone.run(() => {
|
||||||
const ngZoneInjector =
|
const ngZoneInjector =
|
||||||
ReflectiveInjector.resolveAndCreate([{provide: NgZone, useValue: ngZone}], this.injector);
|
ReflectiveInjector.resolveAndCreate([{provide: NgZone, useValue: ngZone}], this.injector);
|
||||||
const moduleRef = <NgModuleInjector<M>>moduleFactory.create(ngZoneInjector);
|
const moduleRef = <InternalNgModuleRef<M>>moduleFactory.create(ngZoneInjector);
|
||||||
const exceptionHandler: ErrorHandler = moduleRef.injector.get(ErrorHandler, null);
|
const exceptionHandler: ErrorHandler = moduleRef.injector.get(ErrorHandler, null);
|
||||||
if (!exceptionHandler) {
|
if (!exceptionHandler) {
|
||||||
throw new Error('No ErrorHandler. Is platform module (BrowserModule) included?');
|
throw new Error('No ErrorHandler. Is platform module (BrowserModule) included?');
|
||||||
|
@ -326,10 +326,10 @@ export class PlatformRef_ extends PlatformRef {
|
||||||
.then((moduleFactory) => this._bootstrapModuleFactoryWithZone(moduleFactory, ngZone));
|
.then((moduleFactory) => this._bootstrapModuleFactoryWithZone(moduleFactory, ngZone));
|
||||||
}
|
}
|
||||||
|
|
||||||
private _moduleDoBootstrap(moduleRef: NgModuleInjector<any>): void {
|
private _moduleDoBootstrap(moduleRef: InternalNgModuleRef<any>): void {
|
||||||
const appRef = moduleRef.injector.get(ApplicationRef);
|
const appRef = moduleRef.injector.get(ApplicationRef) as ApplicationRef;
|
||||||
if (moduleRef.bootstrapFactories.length > 0) {
|
if (moduleRef._bootstrapComponents.length > 0) {
|
||||||
moduleRef.bootstrapFactories.forEach(f => appRef.bootstrap(f));
|
moduleRef._bootstrapComponents.forEach(f => appRef.bootstrap(f));
|
||||||
} else if (moduleRef.instance.ngDoBootstrap) {
|
} else if (moduleRef.instance.ngDoBootstrap) {
|
||||||
moduleRef.instance.ngDoBootstrap(appRef);
|
moduleRef.instance.ngDoBootstrap(appRef);
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -7,7 +7,6 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export {CodegenComponentFactoryResolver as ɵCodegenComponentFactoryResolver} from './linker/component_factory_resolver';
|
export {CodegenComponentFactoryResolver as ɵCodegenComponentFactoryResolver} from './linker/component_factory_resolver';
|
||||||
export {NgModuleInjector as ɵNgModuleInjector} from './linker/ng_module_factory';
|
|
||||||
export {registerModuleFactory as ɵregisterModuleFactory} from './linker/ng_module_factory_loader';
|
export {registerModuleFactory as ɵregisterModuleFactory} from './linker/ng_module_factory_loader';
|
||||||
export {reflector as ɵreflector} from './reflection/reflection';
|
export {reflector as ɵreflector} from './reflection/reflection';
|
||||||
export {ArgumentType as ɵArgumentType, BindingFlags as ɵBindingFlags, DepFlags as ɵDepFlags, EMPTY_ARRAY as ɵEMPTY_ARRAY, EMPTY_MAP as ɵEMPTY_MAP, NodeFlags as ɵNodeFlags, QueryBindingType as ɵQueryBindingType, QueryValueType as ɵQueryValueType, ViewDefinition as ɵViewDefinition, ViewFlags as ɵViewFlags, anchorDef as ɵand, createComponentFactory as ɵccf, createRendererType2 as ɵcrt, directiveDef as ɵdid, elementDef as ɵeld, elementEventFullName as ɵelementEventFullName, getComponentViewDefinitionFactory as ɵgetComponentViewDefinitionFactory, inlineInterpolate as ɵinlineInterpolate, interpolate as ɵinterpolate, ngContentDef as ɵncd, nodeValue as ɵnov, pipeDef as ɵpid, providerDef as ɵprd, pureArrayDef as ɵpad, pureObjectDef as ɵpod, purePipeDef as ɵppd, queryDef as ɵqud, textDef as ɵted, unwrapValue as ɵunv, viewDef as ɵvid} from './view/index';
|
export {ArgumentType as ɵArgumentType, BindingFlags as ɵBindingFlags, DepFlags as ɵDepFlags, EMPTY_ARRAY as ɵEMPTY_ARRAY, EMPTY_MAP as ɵEMPTY_MAP, NodeFlags as ɵNodeFlags, QueryBindingType as ɵQueryBindingType, QueryValueType as ɵQueryValueType, ViewDefinition as ɵViewDefinition, ViewFlags as ɵViewFlags, anchorDef as ɵand, createComponentFactory as ɵccf, createNgModuleFactory as ɵcmf, createRendererType2 as ɵcrt, directiveDef as ɵdid, elementDef as ɵeld, elementEventFullName as ɵelementEventFullName, getComponentViewDefinitionFactory as ɵgetComponentViewDefinitionFactory, inlineInterpolate as ɵinlineInterpolate, interpolate as ɵinterpolate, moduleDef as ɵmod, moduleProvideDef as ɵmpd, ngContentDef as ɵncd, nodeValue as ɵnov, pipeDef as ɵpid, providerDef as ɵprd, pureArrayDef as ɵpad, pureObjectDef as ɵpod, purePipeDef as ɵppd, queryDef as ɵqud, textDef as ɵted, unwrapValue as ɵunv, viewDef as ɵvid} from './view/index';
|
||||||
|
|
|
@ -39,20 +39,22 @@ function constructResolvingPath(keys: any[]): string {
|
||||||
export interface InjectionError extends Error {
|
export interface InjectionError extends Error {
|
||||||
keys: ReflectiveKey[];
|
keys: ReflectiveKey[];
|
||||||
injectors: ReflectiveInjector[];
|
injectors: ReflectiveInjector[];
|
||||||
constructResolvingMessage: (this: InjectionError) => string;
|
constructResolvingMessage: (keys: ReflectiveKey[]) => string;
|
||||||
addKey(injector: ReflectiveInjector, key: ReflectiveKey): void;
|
addKey(injector: ReflectiveInjector, key: ReflectiveKey): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
function injectionError(
|
function injectionError(
|
||||||
injector: ReflectiveInjector, key: ReflectiveKey,
|
injector: ReflectiveInjector, key: ReflectiveKey,
|
||||||
constructResolvingMessage: (this: InjectionError) => string,
|
constructResolvingMessage: (keys: ReflectiveKey[]) => string,
|
||||||
originalError?: Error): InjectionError {
|
originalError?: Error): InjectionError {
|
||||||
const error = (originalError ? wrappedError('', originalError) : Error()) as InjectionError;
|
const keys = [key];
|
||||||
|
const errMsg = constructResolvingMessage(keys);
|
||||||
|
const error =
|
||||||
|
(originalError ? wrappedError(errMsg, originalError) : Error(errMsg)) as InjectionError;
|
||||||
error.addKey = addKey;
|
error.addKey = addKey;
|
||||||
error.keys = [key];
|
error.keys = keys;
|
||||||
error.injectors = [injector];
|
error.injectors = [injector];
|
||||||
error.constructResolvingMessage = constructResolvingMessage;
|
error.constructResolvingMessage = constructResolvingMessage;
|
||||||
error.message = error.constructResolvingMessage();
|
|
||||||
(error as any)[ERROR_ORIGINAL_ERROR] = originalError;
|
(error as any)[ERROR_ORIGINAL_ERROR] = originalError;
|
||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
|
@ -60,7 +62,8 @@ function injectionError(
|
||||||
function addKey(this: InjectionError, injector: ReflectiveInjector, key: ReflectiveKey): void {
|
function addKey(this: InjectionError, injector: ReflectiveInjector, key: ReflectiveKey): void {
|
||||||
this.injectors.push(injector);
|
this.injectors.push(injector);
|
||||||
this.keys.push(key);
|
this.keys.push(key);
|
||||||
this.message = this.constructResolvingMessage();
|
// Note: This updated message won't be reflected in the `.stack` property
|
||||||
|
this.message = this.constructResolvingMessage(this.keys);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -78,9 +81,9 @@ function addKey(this: InjectionError, injector: ReflectiveInjector, key: Reflect
|
||||||
* ```
|
* ```
|
||||||
*/
|
*/
|
||||||
export function noProviderError(injector: ReflectiveInjector, key: ReflectiveKey): InjectionError {
|
export function noProviderError(injector: ReflectiveInjector, key: ReflectiveKey): InjectionError {
|
||||||
return injectionError(injector, key, function(this: InjectionError) {
|
return injectionError(injector, key, function(keys: ReflectiveKey[]) {
|
||||||
const first = stringify(this.keys[0].token);
|
const first = stringify(keys[0].token);
|
||||||
return `No provider for ${first}!${constructResolvingPath(this.keys)}`;
|
return `No provider for ${first}!${constructResolvingPath(keys)}`;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -102,8 +105,8 @@ export function noProviderError(injector: ReflectiveInjector, key: ReflectiveKey
|
||||||
*/
|
*/
|
||||||
export function cyclicDependencyError(
|
export function cyclicDependencyError(
|
||||||
injector: ReflectiveInjector, key: ReflectiveKey): InjectionError {
|
injector: ReflectiveInjector, key: ReflectiveKey): InjectionError {
|
||||||
return injectionError(injector, key, function(this: InjectionError) {
|
return injectionError(injector, key, function(keys: ReflectiveKey[]) {
|
||||||
return `Cannot instantiate cyclic dependency!${constructResolvingPath(this.keys)}`;
|
return `Cannot instantiate cyclic dependency!${constructResolvingPath(keys)}`;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -136,9 +139,9 @@ export function cyclicDependencyError(
|
||||||
export function instantiationError(
|
export function instantiationError(
|
||||||
injector: ReflectiveInjector, originalException: any, originalStack: any,
|
injector: ReflectiveInjector, originalException: any, originalStack: any,
|
||||||
key: ReflectiveKey): InjectionError {
|
key: ReflectiveKey): InjectionError {
|
||||||
return injectionError(injector, key, function(this: InjectionError) {
|
return injectionError(injector, key, function(keys: ReflectiveKey[]) {
|
||||||
const first = stringify(this.keys[0].token);
|
const first = stringify(keys[0].token);
|
||||||
return `${getOriginalError(this).message}: Error during instantiation of ${first}!${constructResolvingPath(this.keys)}.`;
|
return `${originalException.message}: Error during instantiation of ${first}!${constructResolvingPath(keys)}.`;
|
||||||
}, originalException);
|
}, originalException);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -54,8 +54,13 @@ export class CodegenComponentFactoryResolver implements ComponentFactoryResolver
|
||||||
}
|
}
|
||||||
|
|
||||||
resolveComponentFactory<T>(component: {new (...args: any[]): T}): ComponentFactory<T> {
|
resolveComponentFactory<T>(component: {new (...args: any[]): T}): ComponentFactory<T> {
|
||||||
let factory = this._factories.get(component) || this._parent.resolveComponentFactory(component);
|
let factory = this._factories.get(component);
|
||||||
|
if (!factory && this._parent) {
|
||||||
|
factory = this._parent.resolveComponentFactory(component);
|
||||||
|
}
|
||||||
|
if (!factory) {
|
||||||
|
throw noComponentFactoryError(component);
|
||||||
|
}
|
||||||
return new ComponentFactoryBoundToModule(factory, this._ngModule);
|
return new ComponentFactoryBoundToModule(factory, this._ngModule);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,12 +6,10 @@
|
||||||
* found in the LICENSE file at https://angular.io/license
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {Injector, THROW_IF_NOT_FOUND} from '../di/injector';
|
import {Injector} from '../di/injector';
|
||||||
import {Type} from '../type';
|
import {Type} from '../type';
|
||||||
import {stringify} from '../util';
|
|
||||||
|
|
||||||
import {ComponentFactory} from './component_factory';
|
import {ComponentFactoryResolver} from './component_factory_resolver';
|
||||||
import {CodegenComponentFactoryResolver, ComponentFactoryBoundToModule, ComponentFactoryResolver} from './component_factory_resolver';
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -50,76 +48,16 @@ export abstract class NgModuleRef<T> {
|
||||||
abstract onDestroy(callback: () => void): void;
|
abstract onDestroy(callback: () => void): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface InternalNgModuleRef<T> extends NgModuleRef<T> {
|
||||||
|
// Note: we are using the prefix _ as NgModuleData is an NgModuleRef and therefore directly
|
||||||
|
// exposed to the user.
|
||||||
|
_bootstrapComponents: Type<any>[];
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @experimental
|
* @experimental
|
||||||
*/
|
*/
|
||||||
export class NgModuleFactory<T> {
|
export abstract class NgModuleFactory<T> {
|
||||||
constructor(
|
abstract get moduleType(): Type<T>;
|
||||||
private _injectorClass: {new (parentInjector: Injector): NgModuleInjector<T>},
|
abstract create(parentInjector: Injector|null): NgModuleRef<T>;
|
||||||
private _moduleType: Type<T>) {}
|
|
||||||
|
|
||||||
get moduleType(): Type<T> { return this._moduleType; }
|
|
||||||
|
|
||||||
create(parentInjector: Injector|null): NgModuleRef<T> {
|
|
||||||
const instance = new this._injectorClass(parentInjector || Injector.NULL);
|
|
||||||
instance.create();
|
|
||||||
return instance;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const _UNDEFINED = new Object();
|
|
||||||
|
|
||||||
export abstract class NgModuleInjector<T> implements Injector, NgModuleRef<T> {
|
|
||||||
bootstrapFactories: ComponentFactory<any>[];
|
|
||||||
instance: T;
|
|
||||||
|
|
||||||
private _destroyListeners: (() => void)[] = [];
|
|
||||||
private _destroyed: boolean = false;
|
|
||||||
private _cmpFactoryResolver: CodegenComponentFactoryResolver;
|
|
||||||
|
|
||||||
constructor(
|
|
||||||
public parent: Injector, factories: ComponentFactory<any>[],
|
|
||||||
bootstrapFactories: ComponentFactory<any>[]) {
|
|
||||||
this.bootstrapFactories =
|
|
||||||
bootstrapFactories.map(f => new ComponentFactoryBoundToModule(f, this));
|
|
||||||
this._cmpFactoryResolver = new CodegenComponentFactoryResolver(
|
|
||||||
factories, parent.get(ComponentFactoryResolver, ComponentFactoryResolver.NULL), this);
|
|
||||||
}
|
|
||||||
|
|
||||||
create() { this.instance = this.createInternal(); }
|
|
||||||
|
|
||||||
abstract createInternal(): T;
|
|
||||||
|
|
||||||
get(token: any, notFoundValue: any = THROW_IF_NOT_FOUND): any {
|
|
||||||
if (token === Injector || token === NgModuleRef) {
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (token === ComponentFactoryResolver) {
|
|
||||||
return this._cmpFactoryResolver;
|
|
||||||
}
|
|
||||||
|
|
||||||
const result = this.getInternal(token, _UNDEFINED);
|
|
||||||
return result === _UNDEFINED ? this.parent.get(token, notFoundValue) : result;
|
|
||||||
}
|
|
||||||
|
|
||||||
abstract getInternal(token: any, notFoundValue: any): any;
|
|
||||||
|
|
||||||
get injector(): Injector { return this; }
|
|
||||||
|
|
||||||
get componentFactoryResolver(): ComponentFactoryResolver { return this._cmpFactoryResolver; }
|
|
||||||
|
|
||||||
destroy(): void {
|
|
||||||
if (this._destroyed) {
|
|
||||||
throw new Error(
|
|
||||||
`The ng module ${stringify(this.instance.constructor)} has already been destroyed.`);
|
|
||||||
}
|
|
||||||
this._destroyed = true;
|
|
||||||
this.destroyInternal();
|
|
||||||
this._destroyListeners.forEach((listener) => listener());
|
|
||||||
}
|
|
||||||
|
|
||||||
onDestroy(callback: () => void): void { this._destroyListeners.push(callback); }
|
|
||||||
|
|
||||||
abstract destroyInternal(): void;
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,7 +10,7 @@ import {RendererType2} from '../render/api';
|
||||||
import {SecurityContext} from '../security';
|
import {SecurityContext} from '../security';
|
||||||
|
|
||||||
import {BindingDef, BindingFlags, ElementData, ElementHandleEventFn, NodeDef, NodeFlags, OutputDef, OutputType, QueryValueType, ViewData, ViewDefinitionFactory, asElementData} from './types';
|
import {BindingDef, BindingFlags, ElementData, ElementHandleEventFn, NodeDef, NodeFlags, OutputDef, OutputType, QueryValueType, ViewData, ViewDefinitionFactory, asElementData} from './types';
|
||||||
import {NOOP, calcBindingFlags, checkAndUpdateBinding, dispatchEvent, elementEventFullName, getParentRenderElement, resolveRendererType2, resolveViewDefinition, splitMatchedQueriesDsl, splitNamespace} from './util';
|
import {NOOP, calcBindingFlags, checkAndUpdateBinding, dispatchEvent, elementEventFullName, getParentRenderElement, resolveDefinition, resolveRendererType2, splitMatchedQueriesDsl, splitNamespace} from './util';
|
||||||
|
|
||||||
export function anchorDef(
|
export function anchorDef(
|
||||||
flags: NodeFlags, matchedQueriesDsl: [string | number, QueryValueType][],
|
flags: NodeFlags, matchedQueriesDsl: [string | number, QueryValueType][],
|
||||||
|
@ -18,7 +18,7 @@ export function anchorDef(
|
||||||
templateFactory?: ViewDefinitionFactory): NodeDef {
|
templateFactory?: ViewDefinitionFactory): NodeDef {
|
||||||
flags |= NodeFlags.TypeElement;
|
flags |= NodeFlags.TypeElement;
|
||||||
const {matchedQueries, references, matchedQueryIds} = splitMatchedQueriesDsl(matchedQueriesDsl);
|
const {matchedQueries, references, matchedQueryIds} = splitMatchedQueriesDsl(matchedQueriesDsl);
|
||||||
const template = templateFactory ? resolveViewDefinition(templateFactory) : null;
|
const template = templateFactory ? resolveDefinition(templateFactory) : null;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
// will bet set by the view definition
|
// will bet set by the view definition
|
||||||
|
|
|
@ -8,13 +8,15 @@
|
||||||
|
|
||||||
export {anchorDef, elementDef} from './element';
|
export {anchorDef, elementDef} from './element';
|
||||||
export {ngContentDef} from './ng_content';
|
export {ngContentDef} from './ng_content';
|
||||||
|
export {moduleDef, moduleProvideDef} from './ng_module';
|
||||||
export {directiveDef, pipeDef, providerDef} from './provider';
|
export {directiveDef, pipeDef, providerDef} from './provider';
|
||||||
export {pureArrayDef, pureObjectDef, purePipeDef} from './pure_expression';
|
export {pureArrayDef, pureObjectDef, purePipeDef} from './pure_expression';
|
||||||
export {queryDef} from './query';
|
export {queryDef} from './query';
|
||||||
export {ViewRef_, createComponentFactory, getComponentViewDefinitionFactory, nodeValue} from './refs';
|
export {ViewRef_, createComponentFactory, getComponentViewDefinitionFactory, nodeValue} from './refs';
|
||||||
|
export {createNgModuleFactory} from './refs';
|
||||||
export {initServicesIfNeeded} from './services';
|
export {initServicesIfNeeded} from './services';
|
||||||
export {textDef} from './text';
|
export {textDef} from './text';
|
||||||
export {EMPTY_ARRAY, EMPTY_MAP, createRendererType2, elementEventFullName, inlineInterpolate, interpolate, rootRenderNodes, unwrapValue} from './util';
|
export {EMPTY_ARRAY, EMPTY_MAP, createRendererType2, elementEventFullName, inlineInterpolate, interpolate, rootRenderNodes, tokenKey, unwrapValue} from './util';
|
||||||
export {viewDef} from './view';
|
export {viewDef} from './view';
|
||||||
export {attachEmbeddedView, detachEmbeddedView, moveEmbeddedView} from './view_attach';
|
export {attachEmbeddedView, detachEmbeddedView, moveEmbeddedView} from './view_attach';
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,184 @@
|
||||||
|
/**
|
||||||
|
* @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 {Injector, THROW_IF_NOT_FOUND} from '../di/injector';
|
||||||
|
import {NgModuleRef} from '../linker/ng_module_factory';
|
||||||
|
|
||||||
|
import {DepDef, DepFlags, NgModuleData, NgModuleDefinition, NgModuleDefinitionFactory, NgModuleProviderDef, NodeFlags} from './types';
|
||||||
|
import {tokenKey} from './util';
|
||||||
|
|
||||||
|
const NOT_CREATED = new Object();
|
||||||
|
|
||||||
|
const InjectorRefTokenKey = tokenKey(Injector);
|
||||||
|
const NgModuleRefTokenKey = tokenKey(NgModuleRef);
|
||||||
|
|
||||||
|
export function moduleProvideDef(
|
||||||
|
flags: NodeFlags, token: any, value: any,
|
||||||
|
deps: ([DepFlags, any] | any)[]): NgModuleProviderDef {
|
||||||
|
const depDefs: DepDef[] = deps.map(value => {
|
||||||
|
let token: any;
|
||||||
|
let flags: DepFlags;
|
||||||
|
if (Array.isArray(value)) {
|
||||||
|
[flags, token] = value;
|
||||||
|
} else {
|
||||||
|
flags = DepFlags.None;
|
||||||
|
token = value;
|
||||||
|
}
|
||||||
|
return {flags, token, tokenKey: tokenKey(token)};
|
||||||
|
});
|
||||||
|
return {
|
||||||
|
// will bet set by the module definition
|
||||||
|
index: -1,
|
||||||
|
deps: depDefs, flags, token, value
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function moduleDef(providers: NgModuleProviderDef[]): NgModuleDefinition {
|
||||||
|
const providersByKey: {[key: string]: NgModuleProviderDef} = {};
|
||||||
|
for (let i = 0; i < providers.length; i++) {
|
||||||
|
const provider = providers[i];
|
||||||
|
provider.index = i;
|
||||||
|
providersByKey[tokenKey(provider.token)] = provider;
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
// Will be filled later...
|
||||||
|
factory: null,
|
||||||
|
providersByKey,
|
||||||
|
providers
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function initNgModule(data: NgModuleData) {
|
||||||
|
const def = data._def;
|
||||||
|
const providers = data._providers = new Array(def.providers.length);
|
||||||
|
for (let i = 0; i < def.providers.length; i++) {
|
||||||
|
const provDef = def.providers[i];
|
||||||
|
providers[i] = provDef.flags & NodeFlags.LazyProvider ? NOT_CREATED :
|
||||||
|
_createProviderInstance(data, provDef);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function resolveNgModuleDep(
|
||||||
|
data: NgModuleData, depDef: DepDef, notFoundValue: any = Injector.THROW_IF_NOT_FOUND): any {
|
||||||
|
if (depDef.flags & DepFlags.Value) {
|
||||||
|
return depDef.token;
|
||||||
|
}
|
||||||
|
if (depDef.flags & DepFlags.Optional) {
|
||||||
|
notFoundValue = null;
|
||||||
|
}
|
||||||
|
if (depDef.flags & DepFlags.SkipSelf) {
|
||||||
|
return data._parent.get(depDef.token, notFoundValue);
|
||||||
|
}
|
||||||
|
const tokenKey = depDef.tokenKey;
|
||||||
|
switch (tokenKey) {
|
||||||
|
case InjectorRefTokenKey:
|
||||||
|
case NgModuleRefTokenKey:
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
const providerDef = data._def.providersByKey[tokenKey];
|
||||||
|
if (providerDef) {
|
||||||
|
let providerInstance = data._providers[providerDef.index];
|
||||||
|
if (providerInstance === NOT_CREATED) {
|
||||||
|
providerInstance = data._providers[providerDef.index] =
|
||||||
|
_createProviderInstance(data, providerDef);
|
||||||
|
}
|
||||||
|
return providerInstance;
|
||||||
|
}
|
||||||
|
return data._parent.get(depDef.token, notFoundValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function _createProviderInstance(ngModule: NgModuleData, providerDef: NgModuleProviderDef): any {
|
||||||
|
let injectable: any;
|
||||||
|
switch (providerDef.flags & NodeFlags.Types) {
|
||||||
|
case NodeFlags.TypeClassProvider:
|
||||||
|
injectable = _createClass(ngModule, providerDef !.value, providerDef !.deps);
|
||||||
|
break;
|
||||||
|
case NodeFlags.TypeFactoryProvider:
|
||||||
|
injectable = _callFactory(ngModule, providerDef !.value, providerDef !.deps);
|
||||||
|
break;
|
||||||
|
case NodeFlags.TypeUseExistingProvider:
|
||||||
|
injectable = resolveNgModuleDep(ngModule, providerDef !.deps[0]);
|
||||||
|
break;
|
||||||
|
case NodeFlags.TypeValueProvider:
|
||||||
|
injectable = providerDef !.value;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return injectable;
|
||||||
|
}
|
||||||
|
|
||||||
|
function _createClass(ngModule: NgModuleData, ctor: any, deps: DepDef[]): any {
|
||||||
|
const len = deps.length;
|
||||||
|
let injectable: any;
|
||||||
|
switch (len) {
|
||||||
|
case 0:
|
||||||
|
injectable = new ctor();
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
injectable = new ctor(resolveNgModuleDep(ngModule, deps[0]));
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
injectable =
|
||||||
|
new ctor(resolveNgModuleDep(ngModule, deps[0]), resolveNgModuleDep(ngModule, deps[1]));
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
injectable = new ctor(
|
||||||
|
resolveNgModuleDep(ngModule, deps[0]), resolveNgModuleDep(ngModule, deps[1]),
|
||||||
|
resolveNgModuleDep(ngModule, deps[2]));
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
const depValues = new Array(len);
|
||||||
|
for (let i = 0; i < len; i++) {
|
||||||
|
depValues[i] = resolveNgModuleDep(ngModule, deps[i]);
|
||||||
|
}
|
||||||
|
injectable = new ctor(...depValues);
|
||||||
|
}
|
||||||
|
return injectable;
|
||||||
|
}
|
||||||
|
|
||||||
|
function _callFactory(ngModule: NgModuleData, factory: any, deps: DepDef[]): any {
|
||||||
|
const len = deps.length;
|
||||||
|
let injectable: any;
|
||||||
|
switch (len) {
|
||||||
|
case 0:
|
||||||
|
injectable = factory();
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
injectable = factory(resolveNgModuleDep(ngModule, deps[0]));
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
injectable =
|
||||||
|
factory(resolveNgModuleDep(ngModule, deps[0]), resolveNgModuleDep(ngModule, deps[1]));
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
injectable = factory(
|
||||||
|
resolveNgModuleDep(ngModule, deps[0]), resolveNgModuleDep(ngModule, deps[1]),
|
||||||
|
resolveNgModuleDep(ngModule, deps[2]));
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
const depValues = Array(len);
|
||||||
|
for (let i = 0; i < len; i++) {
|
||||||
|
depValues[i] = resolveNgModuleDep(ngModule, deps[i]);
|
||||||
|
}
|
||||||
|
injectable = factory(...depValues);
|
||||||
|
}
|
||||||
|
return injectable;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function callNgModuleLifecycle(ngModule: NgModuleData, lifecycles: NodeFlags) {
|
||||||
|
const def = ngModule._def;
|
||||||
|
for (let i = 0; i < def.providers.length; i++) {
|
||||||
|
const provDef = def.providers[i];
|
||||||
|
if (provDef.flags & NodeFlags.OnDestroy) {
|
||||||
|
const instance = ngModule._providers[i];
|
||||||
|
if (instance && instance !== NOT_CREATED) {
|
||||||
|
instance.ngOnDestroy();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -105,7 +105,7 @@ export function _def(
|
||||||
ngContentIndex: -1, childCount, bindings,
|
ngContentIndex: -1, childCount, bindings,
|
||||||
bindingFlags: calcBindingFlags(bindings), outputs,
|
bindingFlags: calcBindingFlags(bindings), outputs,
|
||||||
element: null,
|
element: null,
|
||||||
provider: {token, tokenKey: tokenKey(token), value, deps: depDefs},
|
provider: {token, value, deps: depDefs},
|
||||||
text: null,
|
text: null,
|
||||||
query: null,
|
query: null,
|
||||||
ngContent: null
|
ngContent: null
|
||||||
|
|
|
@ -8,20 +8,22 @@
|
||||||
|
|
||||||
import {ApplicationRef} from '../application_ref';
|
import {ApplicationRef} from '../application_ref';
|
||||||
import {ChangeDetectorRef} from '../change_detection/change_detection';
|
import {ChangeDetectorRef} from '../change_detection/change_detection';
|
||||||
import {Injector} from '../di';
|
import {Injector} from '../di/injector';
|
||||||
import {ComponentFactory, ComponentRef} from '../linker/component_factory';
|
import {ComponentFactory, ComponentRef} from '../linker/component_factory';
|
||||||
import {ComponentFactoryBoundToModule} from '../linker/component_factory_resolver';
|
import {ComponentFactoryBoundToModule, ComponentFactoryResolver} from '../linker/component_factory_resolver';
|
||||||
import {ElementRef} from '../linker/element_ref';
|
import {ElementRef} from '../linker/element_ref';
|
||||||
import {NgModuleRef} from '../linker/ng_module_factory';
|
import {InternalNgModuleRef, NgModuleFactory, NgModuleRef} from '../linker/ng_module_factory';
|
||||||
import {TemplateRef} from '../linker/template_ref';
|
import {TemplateRef} from '../linker/template_ref';
|
||||||
import {ViewContainerRef} from '../linker/view_container_ref';
|
import {ViewContainerRef} from '../linker/view_container_ref';
|
||||||
import {EmbeddedViewRef, InternalViewRef, ViewRef} from '../linker/view_ref';
|
import {EmbeddedViewRef, InternalViewRef, ViewRef} from '../linker/view_ref';
|
||||||
import {Renderer as RendererV1, Renderer2} from '../render/api';
|
import {Renderer as RendererV1, Renderer2} from '../render/api';
|
||||||
import {Type} from '../type';
|
import {Type} from '../type';
|
||||||
|
import {stringify} from '../util';
|
||||||
import {VERSION} from '../version';
|
import {VERSION} from '../version';
|
||||||
|
|
||||||
import {DepFlags, ElementData, NodeDef, NodeFlags, Services, TemplateData, ViewContainerData, ViewData, ViewDefinitionFactory, ViewState, asElementData, asProviderData, asTextData} from './types';
|
import {callNgModuleLifecycle, initNgModule, resolveNgModuleDep} from './ng_module';
|
||||||
import {markParentViewsForCheck, resolveViewDefinition, rootRenderNodes, splitNamespace, tokenKey, viewParentEl} from './util';
|
import {DepFlags, ElementData, NgModuleData, NgModuleDefinition, NgModuleDefinitionFactory, NodeDef, NodeFlags, Services, TemplateData, ViewContainerData, ViewData, ViewDefinitionFactory, ViewState, asElementData, asProviderData, asTextData} from './types';
|
||||||
|
import {markParentViewsForCheck, resolveDefinition, rootRenderNodes, splitNamespace, tokenKey, viewParentEl} from './util';
|
||||||
import {attachEmbeddedView, detachEmbeddedView, moveEmbeddedView, renderDetachView} from './view_attach';
|
import {attachEmbeddedView, detachEmbeddedView, moveEmbeddedView, renderDetachView} from './view_attach';
|
||||||
|
|
||||||
const EMPTY_CONTEXT = new Object();
|
const EMPTY_CONTEXT = new Object();
|
||||||
|
@ -41,6 +43,14 @@ export function getComponentViewDefinitionFactory(componentFactory: ComponentFac
|
||||||
return (componentFactory as ComponentFactory_).viewDefFactory;
|
return (componentFactory as ComponentFactory_).viewDefFactory;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Attention: this function is called as top level function.
|
||||||
|
// Putting any logic in here will destroy closure tree shaking!
|
||||||
|
export function createNgModuleFactory(
|
||||||
|
ngModuleType: Type<any>, bootstrapComponents: Type<any>[],
|
||||||
|
defFactory: NgModuleDefinitionFactory): NgModuleFactory<any> {
|
||||||
|
return new NgModuleFactory_(ngModuleType, bootstrapComponents, defFactory);
|
||||||
|
}
|
||||||
|
|
||||||
class ComponentFactory_ extends ComponentFactory<any> {
|
class ComponentFactory_ extends ComponentFactory<any> {
|
||||||
/**
|
/**
|
||||||
* @internal
|
* @internal
|
||||||
|
@ -85,7 +95,7 @@ class ComponentFactory_ extends ComponentFactory<any> {
|
||||||
if (!ngModule) {
|
if (!ngModule) {
|
||||||
throw new Error('ngModule should be provided');
|
throw new Error('ngModule should be provided');
|
||||||
}
|
}
|
||||||
const viewDef = resolveViewDefinition(this.viewDefFactory);
|
const viewDef = resolveDefinition(this.viewDefFactory);
|
||||||
const componentNodeIndex = viewDef.nodes[0].element !.componentProvider !.index;
|
const componentNodeIndex = viewDef.nodes[0].element !.componentProvider !.index;
|
||||||
const view = Services.createRootView(
|
const view = Services.createRootView(
|
||||||
injector, projectableNodes || [], rootSelectorOrNode, viewDef, ngModule, EMPTY_CONTEXT);
|
injector, projectableNodes || [], rootSelectorOrNode, viewDef, ngModule, EMPTY_CONTEXT);
|
||||||
|
@ -452,3 +462,57 @@ class RendererAdapter implements RendererV1 {
|
||||||
|
|
||||||
animate(): any { throw new Error('Renderer.animate is no longer supported!'); }
|
animate(): any { throw new Error('Renderer.animate is no longer supported!'); }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class NgModuleFactory_ extends NgModuleFactory<any> {
|
||||||
|
constructor(
|
||||||
|
private _moduleType: Type<any>, private _bootstrapComponents: Type<any>[],
|
||||||
|
private _ngModuleDefFactory: NgModuleDefinitionFactory, ) {
|
||||||
|
// Attention: this ctor is called as top level function.
|
||||||
|
// Putting any logic in here will destroy closure tree shaking!
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
get moduleType(): Type<any> { return this._moduleType; }
|
||||||
|
|
||||||
|
create(parentInjector: Injector|null): NgModuleRef<any> {
|
||||||
|
const def = resolveDefinition(this._ngModuleDefFactory);
|
||||||
|
return new NgModuleRef_(
|
||||||
|
this._moduleType, parentInjector || Injector.NULL, this._bootstrapComponents, def);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class NgModuleRef_ implements NgModuleData, InternalNgModuleRef<any> {
|
||||||
|
private _destroyListeners: (() => void)[] = [];
|
||||||
|
private _destroyed: boolean = false;
|
||||||
|
public _providers: any[];
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private _moduleType: any, public _parent: Injector, public _bootstrapComponents: Type<any>[],
|
||||||
|
public _def: NgModuleDefinition) {
|
||||||
|
initNgModule(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
get(token: any, notFoundValue: any = Injector.THROW_IF_NOT_FOUND): any {
|
||||||
|
return resolveNgModuleDep(
|
||||||
|
this, {token: token, tokenKey: tokenKey(token), flags: DepFlags.None}, notFoundValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
get instance() { return this.get(this._moduleType); }
|
||||||
|
|
||||||
|
get componentFactoryResolver() { return this.get(ComponentFactoryResolver); }
|
||||||
|
|
||||||
|
get injector(): Injector { return this; }
|
||||||
|
|
||||||
|
destroy(): void {
|
||||||
|
if (this._destroyed) {
|
||||||
|
throw new Error(
|
||||||
|
`The ng module ${stringify(this.instance.constructor)} has already been destroyed.`);
|
||||||
|
}
|
||||||
|
this._destroyed = true;
|
||||||
|
callNgModuleLifecycle(this, NodeFlags.OnDestroy);
|
||||||
|
this._destroyListeners.forEach((listener) => listener());
|
||||||
|
}
|
||||||
|
|
||||||
|
onDestroy(callback: () => void): void { this._destroyListeners.push(callback); }
|
||||||
|
}
|
||||||
|
|
|
@ -19,8 +19,32 @@ import {Sanitizer, SecurityContext} from '../security';
|
||||||
// Defs
|
// Defs
|
||||||
// -------------------------------------
|
// -------------------------------------
|
||||||
|
|
||||||
export interface ViewDefinition {
|
/**
|
||||||
factory: ViewDefinitionFactory|null;
|
* Factory for ViewDefinitions/NgModuleDefinitions.
|
||||||
|
* We use a function so we can reexeute it in case an error happens and use the given logger
|
||||||
|
* function to log the error from the definition of the node, which is shown in all browser
|
||||||
|
* logs.
|
||||||
|
*/
|
||||||
|
export interface DefinitionFactory<D extends Definition<any>> { (logger: NodeLogger): D; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Function to call console.error at the right source location. This is an indirection
|
||||||
|
* via another function as browser will log the location that actually called
|
||||||
|
* `console.error`.
|
||||||
|
*/
|
||||||
|
export interface NodeLogger { (): () => void; }
|
||||||
|
|
||||||
|
export interface Definition<DF extends DefinitionFactory<any>> { factory: DF|null; }
|
||||||
|
|
||||||
|
export interface NgModuleDefinition extends Definition<NgModuleDefinitionFactory> {
|
||||||
|
providers: NgModuleProviderDef[];
|
||||||
|
providersByKey: {[tokenKey: string]: NgModuleProviderDef};
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface NgModuleDefinitionFactory extends DefinitionFactory<NgModuleDefinition> {}
|
||||||
|
;
|
||||||
|
|
||||||
|
export interface ViewDefinition extends Definition<ViewDefinitionFactory> {
|
||||||
flags: ViewFlags;
|
flags: ViewFlags;
|
||||||
updateDirectives: ViewUpdateFn;
|
updateDirectives: ViewUpdateFn;
|
||||||
updateRenderer: ViewUpdateFn;
|
updateRenderer: ViewUpdateFn;
|
||||||
|
@ -44,20 +68,8 @@ export interface ViewDefinition {
|
||||||
nodeMatchedQueries: number;
|
nodeMatchedQueries: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
export interface ViewDefinitionFactory extends DefinitionFactory<ViewDefinition> {}
|
||||||
* Factory for ViewDefinitions.
|
|
||||||
* We use a function so we can reexeute it in case an error happens and use the given logger
|
|
||||||
* function to log the error from the definition of the node, which is shown in all browser
|
|
||||||
* logs.
|
|
||||||
*/
|
|
||||||
export interface ViewDefinitionFactory { (logger: NodeLogger): ViewDefinition; }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Function to call console.error at the right source location. This is an indirection
|
|
||||||
* via another function as browser will log the location that actually called
|
|
||||||
* `console.error`.
|
|
||||||
*/
|
|
||||||
export interface NodeLogger { (): () => void; }
|
|
||||||
|
|
||||||
export interface ViewUpdateFn { (check: NodeCheckFn, view: ViewData): void; }
|
export interface ViewUpdateFn { (check: NodeCheckFn, view: ViewData): void; }
|
||||||
|
|
||||||
|
@ -245,7 +257,14 @@ export interface ElementHandleEventFn { (view: ViewData, eventName: string, even
|
||||||
|
|
||||||
export interface ProviderDef {
|
export interface ProviderDef {
|
||||||
token: any;
|
token: any;
|
||||||
tokenKey: string;
|
value: any;
|
||||||
|
deps: DepDef[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface NgModuleProviderDef {
|
||||||
|
flags: NodeFlags;
|
||||||
|
index: number;
|
||||||
|
token: any;
|
||||||
value: any;
|
value: any;
|
||||||
deps: DepDef[];
|
deps: DepDef[];
|
||||||
}
|
}
|
||||||
|
@ -296,6 +315,14 @@ export interface NgContentDef {
|
||||||
// Data
|
// Data
|
||||||
// -------------------------------------
|
// -------------------------------------
|
||||||
|
|
||||||
|
export interface NgModuleData extends Injector, NgModuleRef<any> {
|
||||||
|
// Note: we are using the prefix _ as NgModuleData is an NgModuleRef and therefore directly
|
||||||
|
// exposed to the user.
|
||||||
|
_def: NgModuleDefinition;
|
||||||
|
_parent: Injector;
|
||||||
|
_providers: any[];
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* View instance data.
|
* View instance data.
|
||||||
* Attention: Adding fields to this is performance sensitive!
|
* Attention: Adding fields to this is performance sensitive!
|
||||||
|
@ -379,13 +406,20 @@ export interface ElementData {
|
||||||
template: TemplateData;
|
template: TemplateData;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ViewContainerData extends ViewContainerRef { _embeddedViews: ViewData[]; }
|
export interface ViewContainerData extends ViewContainerRef {
|
||||||
|
// Note: we are using the prefix _ as ViewContainerData is a ViewContainerRef and therefore
|
||||||
|
// directly
|
||||||
|
// exposed to the user.
|
||||||
|
_embeddedViews: ViewData[];
|
||||||
|
}
|
||||||
|
|
||||||
export interface TemplateData extends TemplateRef<any> {
|
export interface TemplateData extends TemplateRef<any> {
|
||||||
// views that have been created from the template
|
// views that have been created from the template
|
||||||
// of this element,
|
// of this element,
|
||||||
// but inserted into the embeddedViews of another element.
|
// but inserted into the embeddedViews of another element.
|
||||||
// By default, this is undefined.
|
// By default, this is undefined.
|
||||||
|
// Note: we are using the prefix _ as TemplateData is a TemplateRef and therefore directly
|
||||||
|
// exposed to the user.
|
||||||
_projectedViews: ViewData[];
|
_projectedViews: ViewData[];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -12,7 +12,7 @@ import {RendererType2} from '../render/api';
|
||||||
import {looseIdentical, stringify} from '../util';
|
import {looseIdentical, stringify} from '../util';
|
||||||
|
|
||||||
import {expressionChangedAfterItHasBeenCheckedError} from './errors';
|
import {expressionChangedAfterItHasBeenCheckedError} from './errors';
|
||||||
import {BindingDef, BindingFlags, ElementData, NodeDef, NodeFlags, QueryValueType, Services, ViewData, ViewDefinition, ViewDefinitionFactory, ViewFlags, ViewState, asElementData, asTextData} from './types';
|
import {BindingDef, BindingFlags, Definition, DefinitionFactory, ElementData, NodeDef, NodeFlags, QueryValueType, Services, ViewData, ViewDefinition, ViewDefinitionFactory, ViewFlags, ViewState, asElementData, asTextData} from './types';
|
||||||
|
|
||||||
export const NOOP: any = () => {};
|
export const NOOP: any = () => {};
|
||||||
|
|
||||||
|
@ -220,14 +220,14 @@ export function getParentRenderElement(view: ViewData, renderHost: any, def: Nod
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const VIEW_DEFINITION_CACHE = new WeakMap<any, ViewDefinition>();
|
const DEFINITION_CACHE = new WeakMap<any, Definition<any>>();
|
||||||
|
|
||||||
export function resolveViewDefinition(factory: ViewDefinitionFactory): ViewDefinition {
|
export function resolveDefinition<D extends Definition<any>>(factory: DefinitionFactory<D>): D {
|
||||||
let value: ViewDefinition = VIEW_DEFINITION_CACHE.get(factory) !;
|
let value = DEFINITION_CACHE.get(factory) !as D;
|
||||||
if (!value) {
|
if (!value) {
|
||||||
value = factory(() => NOOP);
|
value = factory(() => NOOP);
|
||||||
value.factory = factory;
|
value.factory = factory;
|
||||||
VIEW_DEFINITION_CACHE.set(factory, value);
|
DEFINITION_CACHE.set(factory, value);
|
||||||
}
|
}
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,7 +17,7 @@ import {checkAndUpdateQuery, createQuery} from './query';
|
||||||
import {createTemplateData, createViewContainerData} from './refs';
|
import {createTemplateData, createViewContainerData} from './refs';
|
||||||
import {checkAndUpdateTextDynamic, checkAndUpdateTextInline, createText} from './text';
|
import {checkAndUpdateTextDynamic, checkAndUpdateTextInline, createText} from './text';
|
||||||
import {ArgumentType, CheckType, ElementData, NodeData, NodeDef, NodeFlags, ProviderData, RootData, Services, ViewData, ViewDefinition, ViewFlags, ViewHandleEventFn, ViewState, ViewUpdateFn, asElementData, asQueryList, asTextData} from './types';
|
import {ArgumentType, CheckType, ElementData, NodeData, NodeDef, NodeFlags, ProviderData, RootData, Services, ViewData, ViewDefinition, ViewFlags, ViewHandleEventFn, ViewState, ViewUpdateFn, asElementData, asQueryList, asTextData} from './types';
|
||||||
import {NOOP, checkBindingNoChanges, isComponentView, markParentViewsForCheckProjectedViews, resolveViewDefinition} from './util';
|
import {NOOP, checkBindingNoChanges, isComponentView, markParentViewsForCheckProjectedViews, resolveDefinition, tokenKey} from './util';
|
||||||
import {detachProjectedView} from './view_attach';
|
import {detachProjectedView} from './view_attach';
|
||||||
|
|
||||||
export function viewDef(
|
export function viewDef(
|
||||||
|
@ -102,7 +102,7 @@ export function viewDef(
|
||||||
const isPrivateService = (node.flags & NodeFlags.PrivateProvider) !== 0;
|
const isPrivateService = (node.flags & NodeFlags.PrivateProvider) !== 0;
|
||||||
const isComponent = (node.flags & NodeFlags.Component) !== 0;
|
const isComponent = (node.flags & NodeFlags.Component) !== 0;
|
||||||
if (!isPrivateService || isComponent) {
|
if (!isPrivateService || isComponent) {
|
||||||
currentParent !.element !.publicProviders ![node.provider !.tokenKey] = node;
|
currentParent !.element !.publicProviders ![tokenKey(node.provider !.token)] = node;
|
||||||
} else {
|
} else {
|
||||||
if (!currentElementHasPrivateProviders) {
|
if (!currentElementHasPrivateProviders) {
|
||||||
currentElementHasPrivateProviders = true;
|
currentElementHasPrivateProviders = true;
|
||||||
|
@ -110,7 +110,7 @@ export function viewDef(
|
||||||
currentParent !.element !.allProviders =
|
currentParent !.element !.allProviders =
|
||||||
Object.create(currentParent !.element !.publicProviders);
|
Object.create(currentParent !.element !.publicProviders);
|
||||||
}
|
}
|
||||||
currentParent !.element !.allProviders ![node.provider !.tokenKey] = node;
|
currentParent !.element !.allProviders ![tokenKey(node.provider !.token)] = node;
|
||||||
}
|
}
|
||||||
if (isComponent) {
|
if (isComponent) {
|
||||||
currentParent !.element !.componentProvider = node;
|
currentParent !.element !.componentProvider = node;
|
||||||
|
@ -240,7 +240,7 @@ function createViewNodes(view: ViewData) {
|
||||||
const el = createElement(view, renderHost, nodeDef) as any;
|
const el = createElement(view, renderHost, nodeDef) as any;
|
||||||
let componentView: ViewData = undefined !;
|
let componentView: ViewData = undefined !;
|
||||||
if (nodeDef.flags & NodeFlags.ComponentView) {
|
if (nodeDef.flags & NodeFlags.ComponentView) {
|
||||||
const compViewDef = resolveViewDefinition(nodeDef.element !.componentView !);
|
const compViewDef = resolveDefinition(nodeDef.element !.componentView !);
|
||||||
const rendererType = nodeDef.element !.componentRendererType;
|
const rendererType = nodeDef.element !.componentRendererType;
|
||||||
let compRenderer: Renderer2;
|
let compRenderer: Renderer2;
|
||||||
if (!rendererType) {
|
if (!rendererType) {
|
||||||
|
|
|
@ -11,7 +11,7 @@ import {Console} from '@angular/core/src/console';
|
||||||
import {ComponentFixture, TestBed, inject} from '@angular/core/testing';
|
import {ComponentFixture, TestBed, inject} from '@angular/core/testing';
|
||||||
import {expect} from '@angular/platform-browser/testing/src/matchers';
|
import {expect} from '@angular/platform-browser/testing/src/matchers';
|
||||||
|
|
||||||
import {NgModuleInjector} from '../../src/linker/ng_module_factory';
|
import {InternalNgModuleRef} from '../../src/linker/ng_module_factory';
|
||||||
import {clearModulesForTest} from '../../src/linker/ng_module_factory_loader';
|
import {clearModulesForTest} from '../../src/linker/ng_module_factory_loader';
|
||||||
import {stringify} from '../../src/util';
|
import {stringify} from '../../src/util';
|
||||||
|
|
||||||
|
@ -404,9 +404,9 @@ function declareTests({useJit}: {useJit: boolean}) {
|
||||||
class SomeModule {
|
class SomeModule {
|
||||||
}
|
}
|
||||||
|
|
||||||
const ngModule = <NgModuleInjector<any>>createModule(SomeModule);
|
const ngModule = <InternalNgModuleRef<any>>createModule(SomeModule);
|
||||||
expect(ngModule.bootstrapFactories.length).toBe(1);
|
expect(ngModule._bootstrapComponents.length).toBe(1);
|
||||||
expect(ngModule.bootstrapFactories[0].componentType).toBe(SomeComp);
|
expect(ngModule._bootstrapComponents[0]).toBe(SomeComp);
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
@ -787,6 +787,16 @@ function declareTests({useJit}: {useJit: boolean}) {
|
||||||
expect(child.get(Injector)).toBe(child);
|
expect(child.get(Injector)).toBe(child);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should allow to inject lazy providers via Injector.get from an eager provider that is declared earlier',
|
||||||
|
() => {
|
||||||
|
@NgModule({providers: [{provide: 'a', useFactory: () => 'aValue'}]})
|
||||||
|
class SomeModule {
|
||||||
|
public a: string;
|
||||||
|
constructor(injector: Injector) { this.a = injector.get('a'); }
|
||||||
|
}
|
||||||
|
expect(createModule(SomeModule).instance.a).toBe('aValue');
|
||||||
|
});
|
||||||
|
|
||||||
it('should throw when no provider defined', () => {
|
it('should throw when no provider defined', () => {
|
||||||
const injector = createInjector([]);
|
const injector = createInjector([]);
|
||||||
expect(() => injector.get('NonExisting')).toThrowError('No provider for NonExisting!');
|
expect(() => injector.get('NonExisting')).toThrowError('No provider for NonExisting!');
|
||||||
|
|
|
@ -342,6 +342,19 @@ export function main() {
|
||||||
expect(created).toBe(true);
|
expect(created).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should allow injecting lazy providers via Injector.get from an eager provider that is declared earlier',
|
||||||
|
() => {
|
||||||
|
@Component({providers: [{provide: 'a', useFactory: () => 'aValue'}], template: ''})
|
||||||
|
class SomeComponent {
|
||||||
|
public a: string;
|
||||||
|
constructor(injector: Injector) { this.a = injector.get('a'); }
|
||||||
|
}
|
||||||
|
|
||||||
|
const comp = TestBed.configureTestingModule({declarations: [SomeComponent]})
|
||||||
|
.createComponent(SomeComponent);
|
||||||
|
expect(comp.componentInstance.a).toBe('aValue');
|
||||||
|
});
|
||||||
|
|
||||||
it('should support ngOnDestroy for lazy providers', () => {
|
it('should support ngOnDestroy for lazy providers', () => {
|
||||||
let created = false;
|
let created = false;
|
||||||
let destroyed = false;
|
let destroyed = false;
|
||||||
|
|
|
@ -100,12 +100,12 @@ describe('RouterPreloader', () => {
|
||||||
const loadedConfig: LoadedRouterConfig = c[0]._loadedConfig !;
|
const loadedConfig: LoadedRouterConfig = c[0]._loadedConfig !;
|
||||||
const module: any = loadedConfig.module;
|
const module: any = loadedConfig.module;
|
||||||
expect(loadedConfig.routes[0].path).toEqual('LoadedModule1');
|
expect(loadedConfig.routes[0].path).toEqual('LoadedModule1');
|
||||||
expect(module.parent).toBe(testModule);
|
expect(module._parent).toBe(testModule);
|
||||||
|
|
||||||
const loadedConfig2: LoadedRouterConfig = loadedConfig.routes[0]._loadedConfig !;
|
const loadedConfig2: LoadedRouterConfig = loadedConfig.routes[0]._loadedConfig !;
|
||||||
const module2: any = loadedConfig2.module;
|
const module2: any = loadedConfig2.module;
|
||||||
expect(loadedConfig2.routes[0].path).toEqual('LoadedModule2');
|
expect(loadedConfig2.routes[0].path).toEqual('LoadedModule2');
|
||||||
expect(module2.parent).toBe(module);
|
expect(module2._parent).toBe(module);
|
||||||
|
|
||||||
expect(events.map(e => e.toString())).toEqual([
|
expect(events.map(e => e.toString())).toEqual([
|
||||||
'RouteConfigLoadStart(path: lazy)',
|
'RouteConfigLoadStart(path: lazy)',
|
||||||
|
@ -167,12 +167,12 @@ describe('RouterPreloader', () => {
|
||||||
|
|
||||||
const loadedConfig: LoadedRouterConfig = c[0]._loadedConfig !;
|
const loadedConfig: LoadedRouterConfig = c[0]._loadedConfig !;
|
||||||
const module: any = loadedConfig.module;
|
const module: any = loadedConfig.module;
|
||||||
expect(module.parent).toBe(testModule);
|
expect(module._parent).toBe(testModule);
|
||||||
|
|
||||||
const loadedConfig2: LoadedRouterConfig = loadedConfig.routes[0]._loadedConfig !;
|
const loadedConfig2: LoadedRouterConfig = loadedConfig.routes[0]._loadedConfig !;
|
||||||
const loadedConfig3: LoadedRouterConfig = loadedConfig2.routes[0]._loadedConfig !;
|
const loadedConfig3: LoadedRouterConfig = loadedConfig2.routes[0]._loadedConfig !;
|
||||||
const module3: any = loadedConfig3.module;
|
const module3: any = loadedConfig3.module;
|
||||||
expect(module3.parent).toBe(module2);
|
expect(module3._parent).toBe(module2);
|
||||||
})));
|
})));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -612,12 +612,9 @@ export declare type NgIterable<T> = Array<T> | Iterable<T>;
|
||||||
export declare const NgModule: NgModuleDecorator;
|
export declare const NgModule: NgModuleDecorator;
|
||||||
|
|
||||||
/** @experimental */
|
/** @experimental */
|
||||||
export declare class NgModuleFactory<T> {
|
export declare abstract class NgModuleFactory<T> {
|
||||||
readonly moduleType: Type<T>;
|
readonly abstract moduleType: Type<T>;
|
||||||
constructor(_injectorClass: {
|
abstract create(parentInjector: Injector | null): NgModuleRef<T>;
|
||||||
new (parentInjector: Injector): NgModuleInjector<T>;
|
|
||||||
}, _moduleType: Type<T>);
|
|
||||||
create(parentInjector: Injector | null): NgModuleRef<T>;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @stable */
|
/** @stable */
|
||||||
|
|
Loading…
Reference in New Issue