2016-06-23 09:47:54 -07:00
|
|
|
/**
|
|
|
|
* @license
|
|
|
|
* Copyright Google Inc. All Rights Reserved.
|
|
|
|
*
|
|
|
|
* Use of this source code is governed by an MIT-style license that can be
|
|
|
|
* found in the LICENSE file at https://angular.io/license
|
|
|
|
*/
|
|
|
|
|
2017-02-27 23:08:19 -08:00
|
|
|
import {ChangeDetectionStrategy, ViewEncapsulation, ɵArgumentType as ArgumentType, ɵBindingType as BindingType, ɵDepFlags as DepFlags, ɵLifecycleHooks as LifecycleHooks, ɵNodeFlags as NodeFlags, ɵQueryBindingType as QueryBindingType, ɵQueryValueType as QueryValueType, ɵViewFlags as ViewFlags, ɵelementEventFullName as elementEventFullName} from '@angular/core';
|
|
|
|
|
|
|
|
import {CompileDiDependencyMetadata, CompileDirectiveMetadata, CompileDirectiveSummary, CompilePipeSummary, CompileProviderMetadata, CompileTokenMetadata, CompileTypeMetadata, identifierModuleUrl, identifierName, rendererTypeName, tokenReference, viewClassName} from '../compile_metadata';
|
|
|
|
import {BuiltinConverter, BuiltinConverterFactory, EventHandlerVars, LocalResolver, convertActionBinding, convertPropertyBinding, convertPropertyBindingBuiltins} from '../compiler_util/expression_converter';
|
2016-06-08 16:38:52 -07:00
|
|
|
import {CompilerConfig} from '../config';
|
2017-02-27 23:08:19 -08:00
|
|
|
import {AST, ASTWithSource, Interpolation} from '../expression_parser/ast';
|
|
|
|
import {Identifiers, createIdentifier, createIdentifierToken, resolveIdentifier} from '../identifiers';
|
2016-12-15 09:12:40 -08:00
|
|
|
import {CompilerInjectable} from '../injectable';
|
2016-01-06 14:13:44 -08:00
|
|
|
import * as o from '../output/output_ast';
|
2017-02-27 23:08:19 -08:00
|
|
|
import {convertValueToOutputAst} from '../output/value_util';
|
2017-03-14 09:16:15 -07:00
|
|
|
import {ParseSourceSpan} from '../parse_util';
|
2016-10-24 09:58:52 -07:00
|
|
|
import {ElementSchemaRegistry} from '../schema/element_schema_registry';
|
2017-02-27 23:08:19 -08:00
|
|
|
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';
|
2016-06-22 14:06:23 -07:00
|
|
|
|
2017-02-27 23:08:19 -08:00
|
|
|
const CLASS_ATTR = 'class';
|
|
|
|
const STYLE_ATTR = 'style';
|
|
|
|
const IMPLICIT_TEMPLATE_VAR = '\$implicit';
|
|
|
|
const NG_CONTAINER_TAG = 'ng-container';
|
2016-05-25 12:46:22 -07:00
|
|
|
|
2016-01-06 14:13:44 -08:00
|
|
|
export class ViewCompileResult {
|
2016-06-08 16:38:52 -07:00
|
|
|
constructor(
|
2017-02-27 23:08:19 -08:00
|
|
|
public statements: o.Statement[], public viewClassVar: string,
|
|
|
|
public rendererTypeVar: string) {}
|
2016-01-06 14:13:44 -08:00
|
|
|
}
|
|
|
|
|
2016-12-15 09:12:40 -08:00
|
|
|
@CompilerInjectable()
|
2016-01-06 14:13:44 -08:00
|
|
|
export class ViewCompiler {
|
2017-02-27 23:08:19 -08:00
|
|
|
constructor(
|
2017-03-01 12:17:43 -08:00
|
|
|
private _genConfigNext: CompilerConfig, private _schemaRegistry: ElementSchemaRegistry) {}
|
2016-01-06 14:13:44 -08:00
|
|
|
|
2016-06-08 16:38:52 -07:00
|
|
|
compileComponent(
|
|
|
|
component: CompileDirectiveMetadata, template: TemplateAst[], styles: o.Expression,
|
2017-02-27 23:08:19 -08:00
|
|
|
usedPipes: CompilePipeSummary[]): ViewCompileResult {
|
|
|
|
let embeddedViewCount = 0;
|
|
|
|
const staticQueryIds = findStaticQueryIds(template);
|
2016-09-23 16:37:04 -04:00
|
|
|
|
|
|
|
const statements: o.Statement[] = [];
|
2017-02-27 23:08:19 -08:00
|
|
|
|
|
|
|
const customRenderData: o.LiteralMapEntry[] = [];
|
|
|
|
if (component.template.animations && component.template.animations.length) {
|
|
|
|
customRenderData.push(new o.LiteralMapEntry(
|
|
|
|
'animation', convertValueToOutputAst(component.template.animations), true));
|
|
|
|
}
|
|
|
|
|
|
|
|
const renderComponentVar = o.variable(rendererTypeName(component.type.reference));
|
|
|
|
statements.push(
|
|
|
|
renderComponentVar
|
2017-03-07 16:36:12 -08:00
|
|
|
.set(o.importExpr(createIdentifier(Identifiers.createRendererType2)).callFn([
|
2017-02-27 23:08:19 -08:00
|
|
|
new o.LiteralMapExpr([
|
|
|
|
new o.LiteralMapEntry('encapsulation', o.literal(component.template.encapsulation)),
|
|
|
|
new o.LiteralMapEntry('styles', styles),
|
|
|
|
new o.LiteralMapEntry('data', new o.LiteralMapExpr(customRenderData))
|
|
|
|
])
|
|
|
|
]))
|
|
|
|
.toDeclStmt(
|
2017-03-07 16:36:12 -08:00
|
|
|
o.importType(createIdentifier(Identifiers.RendererType2)), [o.StmtModifier.Final]));
|
2017-02-27 23:08:19 -08:00
|
|
|
|
|
|
|
const viewBuilderFactory = (parent: ViewBuilder): ViewBuilder => {
|
|
|
|
const embeddedViewIndex = embeddedViewCount++;
|
|
|
|
return new ViewBuilder(
|
|
|
|
parent, component, embeddedViewIndex, usedPipes, staticQueryIds, viewBuilderFactory);
|
|
|
|
};
|
|
|
|
|
|
|
|
const visitor = viewBuilderFactory(null);
|
|
|
|
visitor.visitAll([], template);
|
|
|
|
|
|
|
|
statements.push(...visitor.build());
|
|
|
|
|
|
|
|
return new ViewCompileResult(statements, visitor.viewName, renderComponentVar.name);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
interface ViewBuilderFactory {
|
|
|
|
(parent: ViewBuilder): ViewBuilder;
|
|
|
|
}
|
|
|
|
|
|
|
|
interface UpdateExpression {
|
2017-03-14 09:16:15 -07:00
|
|
|
context: o.Expression;
|
|
|
|
sourceSpan: ParseSourceSpan;
|
|
|
|
value: AST;
|
2017-02-27 23:08:19 -08:00
|
|
|
}
|
|
|
|
|
2017-03-14 09:16:15 -07:00
|
|
|
const LOG_VAR = o.variable('log');
|
2017-02-27 23:08:19 -08:00
|
|
|
const VIEW_VAR = o.variable('view');
|
|
|
|
const CHECK_VAR = o.variable('check');
|
|
|
|
const COMP_VAR = o.variable('comp');
|
|
|
|
const NODE_INDEX_VAR = o.variable('nodeIndex');
|
|
|
|
const EVENT_NAME_VAR = o.variable('eventName');
|
|
|
|
const ALLOW_DEFAULT_VAR = o.variable(`allowDefault`);
|
|
|
|
|
2017-03-14 09:16:15 -07:00
|
|
|
class ViewBuilder implements TemplateAstVisitor, LocalResolver {
|
2017-02-27 23:08:19 -08:00
|
|
|
private compType: o.Type;
|
2017-03-14 09:16:15 -07:00
|
|
|
private nodes: (() => {
|
|
|
|
sourceSpan: ParseSourceSpan,
|
|
|
|
nodeDef: o.Expression,
|
|
|
|
updateDirectives?: UpdateExpression[],
|
|
|
|
updateRenderer?: UpdateExpression[]
|
|
|
|
})[] = [];
|
2017-02-27 23:08:19 -08:00
|
|
|
private purePipeNodeIndices: {[pipeName: string]: number} = Object.create(null);
|
|
|
|
// Need Object.create so that we don't have builtin values...
|
|
|
|
private refNodeIndices: {[refName: string]: number} = Object.create(null);
|
|
|
|
private variables: VariableAst[] = [];
|
|
|
|
private children: ViewBuilder[] = [];
|
|
|
|
|
|
|
|
constructor(
|
|
|
|
private parent: ViewBuilder, private component: CompileDirectiveMetadata,
|
|
|
|
private embeddedViewIndex: number, private usedPipes: CompilePipeSummary[],
|
|
|
|
private staticQueryIds: Map<TemplateAst, StaticAndDynamicQueryIds>,
|
|
|
|
private viewBuilderFactory: ViewBuilderFactory) {
|
|
|
|
// TODO(tbosch): The old view compiler used to use an `any` type
|
|
|
|
// for the context in any embedded view. We keep this behaivor for now
|
|
|
|
// to be able to introduce the new view compiler without too many errors.
|
|
|
|
this.compType = this.embeddedViewIndex > 0 ? o.DYNAMIC_TYPE : o.importType(this.component.type);
|
|
|
|
}
|
|
|
|
|
|
|
|
get viewName(): string {
|
|
|
|
return viewClassName(this.component.type.reference, this.embeddedViewIndex);
|
|
|
|
}
|
|
|
|
|
|
|
|
visitAll(variables: VariableAst[], astNodes: TemplateAst[]) {
|
|
|
|
this.variables = variables;
|
|
|
|
// create the pipes for the pure pipes immediately, so that we know their indices.
|
|
|
|
if (!this.parent) {
|
|
|
|
this.usedPipes.forEach((pipe) => {
|
|
|
|
if (pipe.pure) {
|
2017-03-14 09:16:15 -07:00
|
|
|
this.purePipeNodeIndices[pipe.name] = this._createPipe(null, pipe);
|
2017-02-27 23:08:19 -08:00
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!this.parent) {
|
|
|
|
const queryIds = staticViewQueryIds(this.staticQueryIds);
|
|
|
|
this.component.viewQueries.forEach((query, queryIndex) => {
|
|
|
|
// Note: queries start with id 1 so we can use the number in a Bloom filter!
|
|
|
|
const queryId = queryIndex + 1;
|
|
|
|
const bindingType = query.first ? QueryBindingType.First : QueryBindingType.All;
|
|
|
|
let flags = NodeFlags.TypeViewQuery;
|
|
|
|
if (queryIds.staticQueryIds.has(queryId)) {
|
|
|
|
flags |= NodeFlags.StaticQuery;
|
|
|
|
} else {
|
|
|
|
flags |= NodeFlags.DynamicQuery;
|
|
|
|
}
|
2017-03-14 09:16:15 -07:00
|
|
|
this.nodes.push(() => ({
|
|
|
|
sourceSpan: null,
|
|
|
|
nodeDef: o.importExpr(createIdentifier(Identifiers.queryDef)).callFn([
|
|
|
|
o.literal(flags), o.literal(queryId),
|
|
|
|
new o.LiteralMapExpr(
|
|
|
|
[new o.LiteralMapEntry(query.propertyName, o.literal(bindingType))])
|
|
|
|
])
|
|
|
|
}));
|
2017-02-27 23:08:19 -08:00
|
|
|
});
|
|
|
|
}
|
|
|
|
templateVisitAll(this, astNodes);
|
|
|
|
if (astNodes.length === 0 ||
|
|
|
|
(this.parent && needsAdditionalRootNode(astNodes[astNodes.length - 1]))) {
|
|
|
|
// if the view is empty, or an embedded view has a view container as last root nde,
|
|
|
|
// create an additional root node.
|
2017-03-14 09:16:15 -07:00
|
|
|
this.nodes.push(() => ({
|
|
|
|
sourceSpan: null,
|
|
|
|
nodeDef: o.importExpr(createIdentifier(Identifiers.anchorDef)).callFn([
|
|
|
|
o.literal(NodeFlags.None), o.NULL_EXPR, o.NULL_EXPR, o.literal(0)
|
|
|
|
])
|
|
|
|
}));
|
2017-02-27 23:08:19 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
build(targetStatements: o.Statement[] = []): o.Statement[] {
|
|
|
|
this.children.forEach((child) => child.build(targetStatements));
|
|
|
|
|
2017-03-14 09:16:15 -07:00
|
|
|
const {updateRendererStmts, updateDirectivesStmts, nodeDefExprs} =
|
|
|
|
this._createNodeExpressions();
|
|
|
|
|
|
|
|
const updateRendererFn = this._createUpdateFn(updateRendererStmts);
|
|
|
|
const updateDirectivesFn = this._createUpdateFn(updateDirectivesStmts);
|
2017-02-27 23:08:19 -08:00
|
|
|
|
|
|
|
|
|
|
|
let viewFlags = ViewFlags.None;
|
|
|
|
if (!this.parent && this.component.changeDetection === ChangeDetectionStrategy.OnPush) {
|
|
|
|
viewFlags |= ViewFlags.OnPush;
|
|
|
|
}
|
|
|
|
const viewFactory = new o.DeclareFunctionStmt(
|
2017-03-14 09:16:15 -07:00
|
|
|
this.viewName, [new o.FnParam(LOG_VAR.name)],
|
2017-02-27 23:08:19 -08:00
|
|
|
[new o.ReturnStatement(o.importExpr(createIdentifier(Identifiers.viewDef)).callFn([
|
|
|
|
o.literal(viewFlags),
|
2017-03-14 09:16:15 -07:00
|
|
|
o.literalArr(nodeDefExprs),
|
2017-02-27 23:08:19 -08:00
|
|
|
updateDirectivesFn,
|
|
|
|
updateRendererFn,
|
|
|
|
]))],
|
|
|
|
o.importType(createIdentifier(Identifiers.ViewDefinition)));
|
|
|
|
|
|
|
|
targetStatements.push(viewFactory);
|
|
|
|
return targetStatements;
|
|
|
|
}
|
|
|
|
|
2017-03-14 09:16:15 -07:00
|
|
|
private _createUpdateFn(updateStmts: o.Statement[]): o.Expression {
|
2017-02-27 23:08:19 -08:00
|
|
|
let updateFn: o.Expression;
|
|
|
|
if (updateStmts.length > 0) {
|
|
|
|
const preStmts: o.Statement[] = [];
|
|
|
|
if (!this.component.isHost) {
|
|
|
|
preStmts.push(COMP_VAR.set(VIEW_VAR.prop('component')).toDeclStmt(this.compType));
|
|
|
|
}
|
|
|
|
updateFn = o.fn(
|
|
|
|
[
|
|
|
|
new o.FnParam(CHECK_VAR.name, o.INFERRED_TYPE),
|
|
|
|
new o.FnParam(VIEW_VAR.name, o.INFERRED_TYPE)
|
|
|
|
],
|
|
|
|
[...preStmts, ...updateStmts], o.INFERRED_TYPE);
|
|
|
|
} else {
|
|
|
|
updateFn = o.NULL_EXPR;
|
|
|
|
}
|
|
|
|
return updateFn;
|
|
|
|
}
|
|
|
|
|
|
|
|
visitNgContent(ast: NgContentAst, context: any): any {
|
|
|
|
// ngContentDef(ngContentIndex: number, index: number): NodeDef;
|
2017-03-14 09:16:15 -07:00
|
|
|
this.nodes.push(() => ({
|
|
|
|
sourceSpan: ast.sourceSpan,
|
|
|
|
nodeDef: o.importExpr(createIdentifier(Identifiers.ngContentDef)).callFn([
|
|
|
|
o.literal(ast.ngContentIndex), o.literal(ast.index)
|
|
|
|
])
|
|
|
|
}));
|
2017-02-27 23:08:19 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
visitText(ast: TextAst, context: any): any {
|
|
|
|
// textDef(ngContentIndex: number, constants: string[]): NodeDef;
|
2017-03-14 09:16:15 -07:00
|
|
|
this.nodes.push(() => ({
|
|
|
|
sourceSpan: ast.sourceSpan,
|
|
|
|
nodeDef: o.importExpr(createIdentifier(Identifiers.textDef)).callFn([
|
|
|
|
o.literal(ast.ngContentIndex), o.literalArr([o.literal(ast.value)])
|
|
|
|
])
|
|
|
|
}));
|
2017-02-27 23:08:19 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
visitBoundText(ast: BoundTextAst, context: any): any {
|
2017-03-14 09:16:15 -07:00
|
|
|
const nodeIndex = this.nodes.length;
|
2017-02-27 23:08:19 -08:00
|
|
|
// reserve the space in the nodeDefs array
|
2017-03-14 09:16:15 -07:00
|
|
|
this.nodes.push(null);
|
2017-02-27 23:08:19 -08:00
|
|
|
|
|
|
|
const astWithSource = <ASTWithSource>ast.value;
|
|
|
|
const inter = <Interpolation>astWithSource.ast;
|
|
|
|
|
2017-03-14 09:16:15 -07:00
|
|
|
const updateRendererExpressions = inter.expressions.map(
|
|
|
|
(expr) => this._preprocessUpdateExpression(
|
|
|
|
{sourceSpan: ast.sourceSpan, context: COMP_VAR, value: expr}));
|
2017-02-27 23:08:19 -08:00
|
|
|
|
|
|
|
// textDef(ngContentIndex: number, constants: string[]): NodeDef;
|
2017-03-14 09:16:15 -07:00
|
|
|
this.nodes[nodeIndex] = () => ({
|
|
|
|
sourceSpan: ast.sourceSpan,
|
|
|
|
nodeDef: o.importExpr(createIdentifier(Identifiers.textDef)).callFn([
|
|
|
|
o.literal(ast.ngContentIndex), o.literalArr(inter.strings.map(s => o.literal(s)))
|
|
|
|
]),
|
|
|
|
updateRenderer: updateRendererExpressions
|
|
|
|
});
|
2017-02-27 23:08:19 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
visitEmbeddedTemplate(ast: EmbeddedTemplateAst, context: any): any {
|
2017-03-14 09:16:15 -07:00
|
|
|
const nodeIndex = this.nodes.length;
|
2017-02-27 23:08:19 -08:00
|
|
|
// reserve the space in the nodeDefs array
|
2017-03-14 09:16:15 -07:00
|
|
|
this.nodes.push(null);
|
2017-02-27 23:08:19 -08:00
|
|
|
|
|
|
|
const {flags, queryMatchesExpr, hostEvents} = this._visitElementOrTemplate(nodeIndex, ast);
|
|
|
|
|
|
|
|
const childVisitor = this.viewBuilderFactory(this);
|
|
|
|
this.children.push(childVisitor);
|
|
|
|
childVisitor.visitAll(ast.variables, ast.children);
|
|
|
|
|
2017-03-14 09:16:15 -07:00
|
|
|
const childCount = this.nodes.length - nodeIndex - 1;
|
2017-02-27 23:08:19 -08:00
|
|
|
|
|
|
|
// anchorDef(
|
|
|
|
// flags: NodeFlags, matchedQueries: [string, QueryValueType][], ngContentIndex: number,
|
|
|
|
// childCount: number, handleEventFn?: ElementHandleEventFn, templateFactory?:
|
|
|
|
// ViewDefinitionFactory): NodeDef;
|
2017-03-14 09:16:15 -07:00
|
|
|
this.nodes[nodeIndex] = () => ({
|
|
|
|
sourceSpan: ast.sourceSpan,
|
|
|
|
nodeDef: o.importExpr(createIdentifier(Identifiers.anchorDef)).callFn([
|
|
|
|
o.literal(flags),
|
|
|
|
queryMatchesExpr,
|
|
|
|
o.literal(ast.ngContentIndex),
|
|
|
|
o.literal(childCount),
|
|
|
|
this._createElementHandleEventFn(nodeIndex, hostEvents),
|
|
|
|
o.variable(childVisitor.viewName),
|
|
|
|
])
|
|
|
|
});
|
2017-02-27 23:08:19 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
visitElement(ast: ElementAst, context: any): any {
|
2017-03-14 09:16:15 -07:00
|
|
|
const nodeIndex = this.nodes.length;
|
2017-02-27 23:08:19 -08:00
|
|
|
// reserve the space in the nodeDefs array so we can add children
|
2017-03-14 09:16:15 -07:00
|
|
|
this.nodes.push(null);
|
2017-02-27 23:08:19 -08:00
|
|
|
|
|
|
|
let elName = ast.name;
|
|
|
|
if (ast.name === NG_CONTAINER_TAG) {
|
|
|
|
// Using a null element name creates an anchor.
|
|
|
|
elName = null;
|
|
|
|
}
|
|
|
|
|
2017-03-01 12:17:43 -08:00
|
|
|
const {flags, usedEvents, queryMatchesExpr, hostBindings: dirHostBindings, hostEvents} =
|
2017-02-27 23:08:19 -08:00
|
|
|
this._visitElementOrTemplate(nodeIndex, ast);
|
|
|
|
|
|
|
|
let inputDefs: o.Expression[] = [];
|
2017-03-14 09:16:15 -07:00
|
|
|
let updateRendererExpressions: UpdateExpression[] = [];
|
2017-02-27 23:08:19 -08:00
|
|
|
let outputDefs: o.Expression[] = [];
|
|
|
|
if (elName) {
|
2017-03-01 12:17:43 -08:00
|
|
|
const hostBindings = ast.inputs
|
|
|
|
.map((inputAst) => ({
|
|
|
|
context: COMP_VAR as o.Expression,
|
2017-03-14 09:16:15 -07:00
|
|
|
inputAst,
|
|
|
|
dirAst: null,
|
2017-03-01 12:17:43 -08:00
|
|
|
}))
|
|
|
|
.concat(dirHostBindings);
|
2017-02-27 23:08:19 -08:00
|
|
|
if (hostBindings.length) {
|
2017-03-14 09:16:15 -07:00
|
|
|
updateRendererExpressions =
|
|
|
|
hostBindings.map((hostBinding) => this._preprocessUpdateExpression({
|
|
|
|
context: hostBinding.context,
|
|
|
|
sourceSpan: hostBinding.inputAst.sourceSpan,
|
|
|
|
value: hostBinding.inputAst.value
|
|
|
|
}));
|
|
|
|
inputDefs = hostBindings.map(
|
|
|
|
hostBinding => elementBindingDef(hostBinding.inputAst, hostBinding.dirAst));
|
2017-02-27 23:08:19 -08:00
|
|
|
}
|
|
|
|
outputDefs = usedEvents.map(
|
|
|
|
([target, eventName]) => o.literalArr([o.literal(target), o.literal(eventName)]));
|
|
|
|
}
|
|
|
|
|
|
|
|
templateVisitAll(this, ast.children);
|
|
|
|
|
2017-03-14 09:16:15 -07:00
|
|
|
const childCount = this.nodes.length - nodeIndex - 1;
|
2017-02-27 23:08:19 -08:00
|
|
|
|
|
|
|
const compAst = ast.directives.find(dirAst => dirAst.directive.isComponent);
|
|
|
|
let compRendererType = o.NULL_EXPR;
|
|
|
|
let compView = o.NULL_EXPR;
|
|
|
|
if (compAst) {
|
|
|
|
compView = o.importExpr({reference: compAst.directive.componentViewType});
|
|
|
|
compRendererType = o.importExpr({reference: compAst.directive.rendererType});
|
|
|
|
}
|
|
|
|
|
|
|
|
// elementDef(
|
|
|
|
// flags: NodeFlags, matchedQueriesDsl: [string | number, QueryValueType][],
|
|
|
|
// ngContentIndex: number, childCount: number, namespaceAndName: string,
|
|
|
|
// fixedAttrs: [string, string][] = [],
|
|
|
|
// bindings?:
|
|
|
|
// ([BindingType.ElementClass, string] | [BindingType.ElementStyle, string, string] |
|
|
|
|
// [BindingType.ElementAttribute | BindingType.ElementProperty |
|
|
|
|
// BindingType.DirectiveHostProperty, string, SecurityContext])[],
|
|
|
|
// outputs?: ([OutputType.ElementOutput | OutputType.DirectiveHostOutput, string, string])[],
|
|
|
|
// handleEvent?: ElementHandleEventFn,
|
2017-03-07 16:36:12 -08:00
|
|
|
// componentView?: () => ViewDefinition, componentRendererType?: RendererType2): NodeDef;
|
2017-03-14 09:16:15 -07:00
|
|
|
this.nodes[nodeIndex] = () => ({
|
|
|
|
sourceSpan: ast.sourceSpan,
|
|
|
|
nodeDef: o.importExpr(createIdentifier(Identifiers.elementDef)).callFn([
|
|
|
|
o.literal(flags),
|
|
|
|
queryMatchesExpr,
|
|
|
|
o.literal(ast.ngContentIndex),
|
|
|
|
o.literal(childCount),
|
|
|
|
o.literal(elName),
|
|
|
|
elName ? fixedAttrsDef(ast) : o.NULL_EXPR,
|
|
|
|
inputDefs.length ? o.literalArr(inputDefs) : o.NULL_EXPR,
|
|
|
|
outputDefs.length ? o.literalArr(outputDefs) : o.NULL_EXPR,
|
|
|
|
this._createElementHandleEventFn(nodeIndex, hostEvents),
|
|
|
|
compView,
|
|
|
|
compRendererType,
|
|
|
|
]),
|
|
|
|
updateRenderer: updateRendererExpressions
|
|
|
|
});
|
2017-02-27 23:08:19 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
private _visitElementOrTemplate(nodeIndex: number, ast: {
|
|
|
|
hasViewContainer: boolean,
|
|
|
|
outputs: BoundEventAst[],
|
|
|
|
directives: DirectiveAst[],
|
|
|
|
providers: ProviderAst[],
|
|
|
|
references: ReferenceAst[],
|
|
|
|
queryMatches: QueryMatch[]
|
|
|
|
}): {
|
|
|
|
flags: NodeFlags,
|
|
|
|
usedEvents: [string, string][],
|
|
|
|
queryMatchesExpr: o.Expression,
|
2017-03-14 09:16:15 -07:00
|
|
|
hostBindings:
|
|
|
|
{context: o.Expression, inputAst: BoundElementPropertyAst, dirAst: DirectiveAst}[],
|
2017-02-27 23:08:19 -08:00
|
|
|
hostEvents: {context: o.Expression, eventAst: BoundEventAst, dirAst: DirectiveAst}[],
|
|
|
|
} {
|
|
|
|
let flags = NodeFlags.None;
|
|
|
|
if (ast.hasViewContainer) {
|
|
|
|
flags |= NodeFlags.EmbeddedViews;
|
|
|
|
}
|
|
|
|
const usedEvents = new Map<string, [string, string]>();
|
|
|
|
ast.outputs.forEach((event) => {
|
|
|
|
const {name, target} = elementEventNameAndTarget(event, null);
|
|
|
|
usedEvents.set(elementEventFullName(target, name), [target, name]);
|
|
|
|
});
|
|
|
|
ast.directives.forEach((dirAst) => {
|
|
|
|
dirAst.hostEvents.forEach((event) => {
|
|
|
|
const {name, target} = elementEventNameAndTarget(event, dirAst);
|
|
|
|
usedEvents.set(elementEventFullName(target, name), [target, name]);
|
|
|
|
});
|
|
|
|
});
|
2017-03-14 09:16:15 -07:00
|
|
|
const hostBindings:
|
|
|
|
{context: o.Expression, inputAst: BoundElementPropertyAst, dirAst: DirectiveAst}[] = [];
|
2017-02-27 23:08:19 -08:00
|
|
|
const hostEvents: {context: o.Expression, eventAst: BoundEventAst, dirAst: DirectiveAst}[] = [];
|
|
|
|
const componentFactoryResolverProvider = createComponentFactoryResolver(ast.directives);
|
|
|
|
if (componentFactoryResolverProvider) {
|
|
|
|
this._visitProvider(componentFactoryResolverProvider, ast.queryMatches);
|
|
|
|
}
|
|
|
|
|
|
|
|
ast.providers.forEach((providerAst, providerIndex) => {
|
|
|
|
let dirAst: DirectiveAst;
|
|
|
|
let dirIndex: number;
|
|
|
|
ast.directives.forEach((localDirAst, i) => {
|
|
|
|
if (localDirAst.directive.type.reference === tokenReference(providerAst.token)) {
|
|
|
|
dirAst = localDirAst;
|
|
|
|
dirIndex = i;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
if (dirAst) {
|
|
|
|
const {hostBindings: dirHostBindings, hostEvents: dirHostEvents} = this._visitDirective(
|
|
|
|
providerAst, dirAst, dirIndex, nodeIndex, ast.references, ast.queryMatches, usedEvents,
|
|
|
|
this.staticQueryIds.get(<any>ast));
|
|
|
|
hostBindings.push(...dirHostBindings);
|
|
|
|
hostEvents.push(...dirHostEvents);
|
|
|
|
} else {
|
|
|
|
this._visitProvider(providerAst, ast.queryMatches);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
let queryMatchExprs: o.Expression[] = [];
|
|
|
|
ast.queryMatches.forEach((match) => {
|
|
|
|
let valueType: QueryValueType;
|
|
|
|
if (tokenReference(match.value) === resolveIdentifier(Identifiers.ElementRef)) {
|
|
|
|
valueType = QueryValueType.ElementRef;
|
|
|
|
} else if (tokenReference(match.value) === resolveIdentifier(Identifiers.ViewContainerRef)) {
|
|
|
|
valueType = QueryValueType.ViewContainerRef;
|
|
|
|
} else if (tokenReference(match.value) === resolveIdentifier(Identifiers.TemplateRef)) {
|
|
|
|
valueType = QueryValueType.TemplateRef;
|
|
|
|
}
|
|
|
|
if (valueType != null) {
|
|
|
|
queryMatchExprs.push(o.literalArr([o.literal(match.queryId), o.literal(valueType)]));
|
|
|
|
}
|
|
|
|
});
|
|
|
|
ast.references.forEach((ref) => {
|
|
|
|
let valueType: QueryValueType;
|
|
|
|
if (!ref.value) {
|
|
|
|
valueType = QueryValueType.RenderElement;
|
|
|
|
} else if (tokenReference(ref.value) === resolveIdentifier(Identifiers.TemplateRef)) {
|
|
|
|
valueType = QueryValueType.TemplateRef;
|
|
|
|
}
|
|
|
|
if (valueType != null) {
|
|
|
|
this.refNodeIndices[ref.name] = nodeIndex;
|
|
|
|
queryMatchExprs.push(o.literalArr([o.literal(ref.name), o.literal(valueType)]));
|
|
|
|
}
|
|
|
|
});
|
|
|
|
ast.outputs.forEach((outputAst) => {
|
|
|
|
hostEvents.push({context: COMP_VAR, eventAst: outputAst, dirAst: null});
|
|
|
|
});
|
|
|
|
|
|
|
|
return {
|
|
|
|
flags,
|
|
|
|
usedEvents: Array.from(usedEvents.values()),
|
|
|
|
queryMatchesExpr: queryMatchExprs.length ? o.literalArr(queryMatchExprs) : o.NULL_EXPR,
|
|
|
|
hostBindings,
|
|
|
|
hostEvents
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
private _visitDirective(
|
|
|
|
providerAst: ProviderAst, dirAst: DirectiveAst, directiveIndex: number,
|
|
|
|
elementNodeIndex: number, refs: ReferenceAst[], queryMatches: QueryMatch[],
|
|
|
|
usedEvents: Map<string, any>, queryIds: StaticAndDynamicQueryIds): {
|
2017-03-14 09:16:15 -07:00
|
|
|
hostBindings:
|
|
|
|
{context: o.Expression, inputAst: BoundElementPropertyAst, dirAst: DirectiveAst}[],
|
2017-02-27 23:08:19 -08:00
|
|
|
hostEvents: {context: o.Expression, eventAst: BoundEventAst, dirAst: DirectiveAst}[]
|
|
|
|
} {
|
2017-03-14 09:16:15 -07:00
|
|
|
const nodeIndex = this.nodes.length;
|
2017-02-27 23:08:19 -08:00
|
|
|
// reserve the space in the nodeDefs array so we can add children
|
2017-03-14 09:16:15 -07:00
|
|
|
this.nodes.push(null);
|
2017-02-27 23:08:19 -08:00
|
|
|
|
|
|
|
dirAst.directive.queries.forEach((query, queryIndex) => {
|
|
|
|
let flags = NodeFlags.TypeContentQuery;
|
|
|
|
const queryId = dirAst.contentQueryStartId + queryIndex;
|
|
|
|
// Note: We only make queries static that query for a single item.
|
|
|
|
// This is because of backwards compatibility with the old view compiler...
|
|
|
|
if (queryIds.staticQueryIds.has(queryId) && query.first) {
|
|
|
|
flags |= NodeFlags.StaticQuery;
|
|
|
|
} else {
|
|
|
|
flags |= NodeFlags.DynamicQuery;
|
|
|
|
}
|
|
|
|
const bindingType = query.first ? QueryBindingType.First : QueryBindingType.All;
|
2017-03-14 09:16:15 -07:00
|
|
|
this.nodes.push(() => ({
|
|
|
|
sourceSpan: dirAst.sourceSpan,
|
|
|
|
nodeDef: o.importExpr(createIdentifier(Identifiers.queryDef)).callFn([
|
|
|
|
o.literal(flags), o.literal(queryId),
|
|
|
|
new o.LiteralMapExpr(
|
|
|
|
[new o.LiteralMapEntry(query.propertyName, o.literal(bindingType))])
|
|
|
|
]),
|
|
|
|
}));
|
2017-02-27 23:08:19 -08:00
|
|
|
});
|
|
|
|
|
|
|
|
// Note: the operation below might also create new nodeDefs,
|
|
|
|
// but we don't want them to be a child of a directive,
|
|
|
|
// as they might be a provider/pipe on their own.
|
|
|
|
// I.e. we only allow queries as children of directives nodes.
|
2017-03-14 09:16:15 -07:00
|
|
|
const childCount = this.nodes.length - nodeIndex - 1;
|
2017-02-27 23:08:19 -08:00
|
|
|
|
|
|
|
let {flags, queryMatchExprs, providerExpr, depsExpr} =
|
|
|
|
this._visitProviderOrDirective(providerAst, queryMatches);
|
|
|
|
|
|
|
|
refs.forEach((ref) => {
|
|
|
|
if (ref.value && tokenReference(ref.value) === tokenReference(providerAst.token)) {
|
|
|
|
this.refNodeIndices[ref.name] = nodeIndex;
|
|
|
|
queryMatchExprs.push(
|
|
|
|
o.literalArr([o.literal(ref.name), o.literal(QueryValueType.Provider)]));
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
if (dirAst.directive.isComponent) {
|
|
|
|
flags |= NodeFlags.Component;
|
|
|
|
}
|
|
|
|
|
|
|
|
const inputDefs = dirAst.inputs.map((inputAst, inputIndex) => {
|
|
|
|
const mapValue = o.literalArr([o.literal(inputIndex), o.literal(inputAst.directiveName)]);
|
|
|
|
// Note: it's important to not quote the key so that we can capture renames by minifiers!
|
|
|
|
return new o.LiteralMapEntry(inputAst.directiveName, mapValue, false);
|
|
|
|
});
|
|
|
|
|
|
|
|
const outputDefs: o.LiteralMapEntry[] = [];
|
|
|
|
const dirMeta = dirAst.directive;
|
|
|
|
Object.keys(dirMeta.outputs).forEach((propName) => {
|
|
|
|
const eventName = dirMeta.outputs[propName];
|
|
|
|
if (usedEvents.has(eventName)) {
|
|
|
|
// Note: it's important to not quote the key so that we can capture renames by minifiers!
|
|
|
|
outputDefs.push(new o.LiteralMapEntry(propName, o.literal(eventName), false));
|
|
|
|
}
|
|
|
|
});
|
2017-03-14 09:16:15 -07:00
|
|
|
let updateDirectiveExpressions: UpdateExpression[] = [];
|
2017-02-27 23:08:19 -08:00
|
|
|
if (dirAst.inputs.length || (flags & (NodeFlags.DoCheck | NodeFlags.OnInit)) > 0) {
|
2017-03-14 09:16:15 -07:00
|
|
|
updateDirectiveExpressions = dirAst.inputs.map(
|
|
|
|
(input) => this._preprocessUpdateExpression(
|
|
|
|
{sourceSpan: input.sourceSpan, context: COMP_VAR, value: input.value}));
|
2017-02-27 23:08:19 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
const dirContextExpr = o.importExpr(createIdentifier(Identifiers.nodeValue)).callFn([
|
|
|
|
VIEW_VAR, o.literal(nodeIndex)
|
|
|
|
]);
|
2017-03-14 09:16:15 -07:00
|
|
|
const hostBindings = dirAst.hostProperties.map((inputAst) => ({
|
|
|
|
context: dirContextExpr,
|
|
|
|
dirAst,
|
|
|
|
inputAst,
|
|
|
|
}));
|
2017-03-01 12:17:43 -08:00
|
|
|
const hostEvents = dirAst.hostEvents.map((hostEventAst) => ({
|
|
|
|
context: dirContextExpr,
|
|
|
|
eventAst: hostEventAst, dirAst,
|
|
|
|
}));
|
2017-02-27 23:08:19 -08:00
|
|
|
|
|
|
|
|
|
|
|
// directiveDef(
|
|
|
|
// flags: NodeFlags, matchedQueries: [string, QueryValueType][], childCount: number, ctor:
|
|
|
|
// any,
|
|
|
|
// deps: ([DepFlags, any] | any)[], props?: {[name: string]: [number, string]},
|
|
|
|
// outputs?: {[name: string]: string}, component?: () => ViewDefinition): NodeDef;
|
2017-03-14 09:16:15 -07:00
|
|
|
this.nodes[nodeIndex] = () => ({
|
|
|
|
sourceSpan: dirAst.sourceSpan,
|
|
|
|
nodeDef: o.importExpr(createIdentifier(Identifiers.directiveDef)).callFn([
|
|
|
|
o.literal(flags), queryMatchExprs.length ? o.literalArr(queryMatchExprs) : o.NULL_EXPR,
|
|
|
|
o.literal(childCount), providerExpr, depsExpr,
|
|
|
|
inputDefs.length ? new o.LiteralMapExpr(inputDefs) : o.NULL_EXPR,
|
|
|
|
outputDefs.length ? new o.LiteralMapExpr(outputDefs) : o.NULL_EXPR
|
|
|
|
]),
|
|
|
|
updateDirectives: updateDirectiveExpressions,
|
|
|
|
});
|
2017-02-27 23:08:19 -08:00
|
|
|
|
|
|
|
return {hostBindings, hostEvents};
|
|
|
|
}
|
|
|
|
|
|
|
|
private _visitProvider(providerAst: ProviderAst, queryMatches: QueryMatch[]): void {
|
2017-03-14 09:16:15 -07:00
|
|
|
const nodeIndex = this.nodes.length;
|
2017-02-27 23:08:19 -08:00
|
|
|
// reserve the space in the nodeDefs array so we can add children
|
2017-03-14 09:16:15 -07:00
|
|
|
this.nodes.push(null);
|
2017-02-27 23:08:19 -08:00
|
|
|
|
|
|
|
const {flags, queryMatchExprs, providerExpr, depsExpr} =
|
|
|
|
this._visitProviderOrDirective(providerAst, queryMatches);
|
|
|
|
|
|
|
|
// providerDef(
|
|
|
|
// flags: NodeFlags, matchedQueries: [string, QueryValueType][], token:any,
|
|
|
|
// value: any, deps: ([DepFlags, any] | any)[]): NodeDef;
|
2017-03-14 09:16:15 -07:00
|
|
|
this.nodes[nodeIndex] = () => ({
|
|
|
|
sourceSpan: providerAst.sourceSpan,
|
|
|
|
nodeDef: o.importExpr(createIdentifier(Identifiers.providerDef)).callFn([
|
|
|
|
o.literal(flags), queryMatchExprs.length ? o.literalArr(queryMatchExprs) : o.NULL_EXPR,
|
|
|
|
tokenExpr(providerAst.token), providerExpr, depsExpr
|
|
|
|
])
|
|
|
|
});
|
2017-02-27 23:08:19 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
private _visitProviderOrDirective(providerAst: ProviderAst, queryMatches: QueryMatch[]): {
|
|
|
|
flags: NodeFlags,
|
|
|
|
queryMatchExprs: o.Expression[],
|
|
|
|
providerExpr: o.Expression,
|
|
|
|
depsExpr: 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);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
let queryMatchExprs: o.Expression[] = [];
|
|
|
|
|
|
|
|
queryMatches.forEach((match) => {
|
|
|
|
if (tokenReference(match.value) === tokenReference(providerAst.token)) {
|
|
|
|
queryMatchExprs.push(
|
|
|
|
o.literalArr([o.literal(match.queryId), o.literal(QueryValueType.Provider)]));
|
|
|
|
}
|
|
|
|
});
|
|
|
|
const {providerExpr, depsExpr, flags: providerType} = providerDef(providerAst);
|
|
|
|
return {flags: flags | providerType, queryMatchExprs, providerExpr, depsExpr};
|
|
|
|
}
|
|
|
|
|
|
|
|
getLocal(name: string): o.Expression {
|
|
|
|
if (name == EventHandlerVars.event.name) {
|
|
|
|
return EventHandlerVars.event;
|
|
|
|
}
|
|
|
|
let currViewExpr: o.Expression = VIEW_VAR;
|
|
|
|
for (let currBuilder: ViewBuilder = this; currBuilder;
|
|
|
|
currBuilder = currBuilder.parent, currViewExpr = currViewExpr.prop('parent')) {
|
|
|
|
// check references
|
|
|
|
const refNodeIndex = currBuilder.refNodeIndices[name];
|
|
|
|
if (refNodeIndex != null) {
|
|
|
|
return o.importExpr(createIdentifier(Identifiers.nodeValue)).callFn([
|
|
|
|
currViewExpr, o.literal(refNodeIndex)
|
|
|
|
]);
|
|
|
|
}
|
|
|
|
|
|
|
|
// check variables
|
|
|
|
const varAst = currBuilder.variables.find((varAst) => varAst.name === name);
|
|
|
|
if (varAst) {
|
|
|
|
const varValue = varAst.value || IMPLICIT_TEMPLATE_VAR;
|
|
|
|
return currViewExpr.prop('context').prop(varValue);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
2017-03-14 09:16:15 -07:00
|
|
|
createLiteralArrayConverter(sourceSpan: ParseSourceSpan, argCount: number): BuiltinConverter {
|
2017-02-27 23:08:19 -08:00
|
|
|
if (argCount === 0) {
|
|
|
|
const valueExpr = o.importExpr(createIdentifier(Identifiers.EMPTY_ARRAY));
|
|
|
|
return () => valueExpr;
|
|
|
|
}
|
|
|
|
|
2017-03-14 09:16:15 -07:00
|
|
|
const nodeIndex = this.nodes.length;
|
2017-02-27 23:08:19 -08:00
|
|
|
// pureArrayDef(argCount: number): NodeDef;
|
2017-03-14 09:16:15 -07:00
|
|
|
this.nodes.push(
|
|
|
|
() => ({
|
|
|
|
sourceSpan,
|
|
|
|
nodeDef:
|
|
|
|
o.importExpr(createIdentifier(Identifiers.pureArrayDef)).callFn([o.literal(argCount)])
|
|
|
|
}));
|
2017-02-27 23:08:19 -08:00
|
|
|
|
|
|
|
return (args: o.Expression[]) => callCheckStmt(nodeIndex, args);
|
|
|
|
}
|
2017-03-14 09:16:15 -07:00
|
|
|
createLiteralMapConverter(sourceSpan: ParseSourceSpan, keys: string[]): BuiltinConverter {
|
2017-02-27 23:08:19 -08:00
|
|
|
if (keys.length === 0) {
|
|
|
|
const valueExpr = o.importExpr(createIdentifier(Identifiers.EMPTY_MAP));
|
|
|
|
return () => valueExpr;
|
|
|
|
}
|
|
|
|
|
2017-03-14 09:16:15 -07:00
|
|
|
const nodeIndex = this.nodes.length;
|
2017-02-27 23:08:19 -08:00
|
|
|
// function pureObjectDef(propertyNames: string[]): NodeDef
|
2017-03-14 09:16:15 -07:00
|
|
|
this.nodes.push(() => ({
|
|
|
|
sourceSpan,
|
|
|
|
nodeDef: o.importExpr(createIdentifier(Identifiers.pureObjectDef))
|
|
|
|
.callFn([o.literalArr(keys.map(key => o.literal(key)))])
|
|
|
|
}));
|
2017-02-27 23:08:19 -08:00
|
|
|
|
|
|
|
return (args: o.Expression[]) => callCheckStmt(nodeIndex, args);
|
|
|
|
}
|
2017-03-14 09:16:15 -07:00
|
|
|
createPipeConverter(sourceSpan: ParseSourceSpan, name: string, argCount: number):
|
|
|
|
BuiltinConverter {
|
|
|
|
const pipe = this.usedPipes.find((pipeSummary) => pipeSummary.name === name);
|
2017-02-27 23:08:19 -08:00
|
|
|
if (pipe.pure) {
|
2017-03-14 09:16:15 -07:00
|
|
|
const nodeIndex = this.nodes.length;
|
2017-02-27 23:08:19 -08:00
|
|
|
// function purePipeDef(argCount: number): NodeDef;
|
2017-03-14 09:16:15 -07:00
|
|
|
this.nodes.push(() => ({
|
|
|
|
sourceSpan,
|
|
|
|
nodeDef: o.importExpr(createIdentifier(Identifiers.purePipeDef))
|
|
|
|
.callFn([o.literal(argCount)])
|
|
|
|
}));
|
2017-02-27 23:08:19 -08:00
|
|
|
|
|
|
|
// find underlying pipe in the component view
|
|
|
|
let compViewExpr: o.Expression = VIEW_VAR;
|
|
|
|
let compBuilder: ViewBuilder = this;
|
|
|
|
while (compBuilder.parent) {
|
|
|
|
compBuilder = compBuilder.parent;
|
|
|
|
compViewExpr = compViewExpr.prop('parent');
|
|
|
|
}
|
|
|
|
const pipeNodeIndex = compBuilder.purePipeNodeIndices[name];
|
|
|
|
const pipeValueExpr: o.Expression =
|
|
|
|
o.importExpr(createIdentifier(Identifiers.nodeValue)).callFn([
|
|
|
|
compViewExpr, o.literal(pipeNodeIndex)
|
|
|
|
]);
|
|
|
|
|
|
|
|
return (args: o.Expression[]) =>
|
|
|
|
callUnwrapValue(callCheckStmt(nodeIndex, [pipeValueExpr].concat(args)));
|
|
|
|
} else {
|
2017-03-14 09:16:15 -07:00
|
|
|
const nodeIndex = this._createPipe(sourceSpan, pipe);
|
2017-02-27 23:08:19 -08:00
|
|
|
const nodeValueExpr = o.importExpr(createIdentifier(Identifiers.nodeValue)).callFn([
|
|
|
|
VIEW_VAR, o.literal(nodeIndex)
|
|
|
|
]);
|
|
|
|
|
|
|
|
return (args: o.Expression[]) => callUnwrapValue(nodeValueExpr.callMethod('transform', args));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-03-14 09:16:15 -07:00
|
|
|
private _createPipe(sourceSpan: ParseSourceSpan, pipe: CompilePipeSummary): number {
|
|
|
|
const nodeIndex = this.nodes.length;
|
2017-02-27 23:08:19 -08:00
|
|
|
let flags = NodeFlags.None;
|
|
|
|
pipe.type.lifecycleHooks.forEach((lifecycleHook) => {
|
|
|
|
// for pipes, we only support ngOnDestroy
|
|
|
|
if (lifecycleHook === LifecycleHooks.OnDestroy) {
|
|
|
|
flags |= lifecycleHookToNodeFlag(lifecycleHook);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
const depExprs = pipe.type.diDeps.map(depDef);
|
|
|
|
// function pipeDef(
|
|
|
|
// flags: NodeFlags, ctor: any, deps: ([DepFlags, any] | any)[]): NodeDef
|
2017-03-14 09:16:15 -07:00
|
|
|
this.nodes.push(() => ({
|
|
|
|
sourceSpan,
|
|
|
|
nodeDef: o.importExpr(createIdentifier(Identifiers.pipeDef)).callFn([
|
|
|
|
o.literal(flags), o.importExpr(pipe.type), o.literalArr(depExprs)
|
|
|
|
])
|
|
|
|
}));
|
2017-02-27 23:08:19 -08:00
|
|
|
return nodeIndex;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Attention: This might create new nodeDefs (for pipes and literal arrays and literal maps)!
|
2017-03-14 09:16:15 -07:00
|
|
|
private _preprocessUpdateExpression(expression: UpdateExpression): UpdateExpression {
|
|
|
|
return {
|
|
|
|
sourceSpan: expression.sourceSpan,
|
|
|
|
context: expression.context,
|
|
|
|
value: convertPropertyBindingBuiltins(
|
|
|
|
{
|
|
|
|
createLiteralArrayConverter: (argCount: number) => this.createLiteralArrayConverter(
|
|
|
|
expression.sourceSpan, argCount),
|
|
|
|
createLiteralMapConverter:
|
|
|
|
(keys: string[]) => this.createLiteralMapConverter(expression.sourceSpan, keys),
|
|
|
|
createPipeConverter: (name: string, argCount: number) =>
|
|
|
|
this.createPipeConverter(expression.sourceSpan, name, argCount)
|
|
|
|
},
|
|
|
|
expression.value)
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
private _createNodeExpressions(): {
|
|
|
|
updateRendererStmts: o.Statement[],
|
|
|
|
updateDirectivesStmts: o.Statement[],
|
|
|
|
nodeDefExprs: o.Expression[]
|
|
|
|
} {
|
|
|
|
const self = this;
|
|
|
|
let updateBindingCount = 0;
|
|
|
|
const updateRendererStmts: o.Statement[] = [];
|
|
|
|
const updateDirectivesStmts: o.Statement[] = [];
|
|
|
|
const nodeDefExprs = this.nodes.map((factory, nodeIndex) => {
|
|
|
|
const {nodeDef, updateDirectives, updateRenderer, sourceSpan} = factory();
|
|
|
|
if (updateRenderer) {
|
|
|
|
updateRendererStmts.push(...createUpdateStatements(nodeIndex, sourceSpan, updateRenderer));
|
|
|
|
}
|
|
|
|
if (updateDirectives) {
|
|
|
|
updateDirectivesStmts.push(
|
|
|
|
...createUpdateStatements(nodeIndex, sourceSpan, updateDirectives));
|
2017-02-27 23:08:19 -08:00
|
|
|
}
|
2017-03-14 09:16:15 -07:00
|
|
|
// We use a comma expression to call the log function before
|
|
|
|
// the nodeDef function, but still use the result of the nodeDef function
|
|
|
|
// as the value.
|
|
|
|
const logWithNodeDef = new o.CommaExpr([LOG_VAR.callFn([]).callFn([]), nodeDef]);
|
|
|
|
return o.applySourceSpanToExpressionIfNeeded(logWithNodeDef, sourceSpan);
|
2017-02-27 23:08:19 -08:00
|
|
|
});
|
2017-03-14 09:16:15 -07:00
|
|
|
return {updateRendererStmts, updateDirectivesStmts, nodeDefExprs};
|
|
|
|
|
|
|
|
function createUpdateStatements(
|
|
|
|
nodeIndex: number, sourceSpan: ParseSourceSpan,
|
|
|
|
expressions: UpdateExpression[]): o.Statement[] {
|
|
|
|
const updateStmts: o.Statement[] = [];
|
|
|
|
const exprs = expressions.map(({sourceSpan, context, value}) => {
|
|
|
|
const bindingId = `${updateBindingCount++}`;
|
|
|
|
const nameResolver = context === COMP_VAR ? self : null;
|
|
|
|
const {stmts, currValExpr} =
|
|
|
|
convertPropertyBinding(nameResolver, context, value, bindingId);
|
|
|
|
updateStmts.push(
|
|
|
|
...stmts.map(stmt => o.applySourceSpanToStatementIfNeeded(stmt, sourceSpan)));
|
|
|
|
return o.applySourceSpanToExpressionIfNeeded(currValExpr, sourceSpan);
|
|
|
|
});
|
|
|
|
updateStmts.push(o.applySourceSpanToStatementIfNeeded(
|
|
|
|
callCheckStmt(nodeIndex, exprs).toStmt(), sourceSpan));
|
|
|
|
return updateStmts;
|
|
|
|
}
|
2017-02-27 23:08:19 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
private _createElementHandleEventFn(
|
|
|
|
nodeIndex: number,
|
|
|
|
handlers: {context: o.Expression, eventAst: BoundEventAst, dirAst: DirectiveAst}[]) {
|
|
|
|
const handleEventStmts: o.Statement[] = [];
|
|
|
|
let handleEventBindingCount = 0;
|
|
|
|
handlers.forEach(({context, eventAst, dirAst}) => {
|
|
|
|
const bindingId = `${handleEventBindingCount++}`;
|
|
|
|
const nameResolver = context === COMP_VAR ? this : null;
|
|
|
|
const {stmts, allowDefault} =
|
2017-03-14 09:16:15 -07:00
|
|
|
convertActionBinding(nameResolver, context, eventAst.handler, bindingId);
|
2017-02-27 23:08:19 -08:00
|
|
|
const trueStmts = stmts;
|
|
|
|
if (allowDefault) {
|
|
|
|
trueStmts.push(ALLOW_DEFAULT_VAR.set(allowDefault.and(ALLOW_DEFAULT_VAR)).toStmt());
|
|
|
|
}
|
|
|
|
const {target: eventTarget, name: eventName} = elementEventNameAndTarget(eventAst, dirAst);
|
|
|
|
const fullEventName = elementEventFullName(eventTarget, eventName);
|
2017-03-14 09:16:15 -07:00
|
|
|
handleEventStmts.push(o.applySourceSpanToStatementIfNeeded(
|
|
|
|
new o.IfStmt(o.literal(fullEventName).identical(EVENT_NAME_VAR), trueStmts),
|
|
|
|
eventAst.sourceSpan));
|
2017-02-27 23:08:19 -08:00
|
|
|
});
|
|
|
|
let handleEventFn: o.Expression;
|
|
|
|
if (handleEventStmts.length > 0) {
|
|
|
|
const preStmts: o.Statement[] =
|
|
|
|
[ALLOW_DEFAULT_VAR.set(o.literal(true)).toDeclStmt(o.BOOL_TYPE)];
|
|
|
|
if (!this.component.isHost) {
|
|
|
|
preStmts.push(COMP_VAR.set(VIEW_VAR.prop('component')).toDeclStmt(this.compType));
|
|
|
|
}
|
|
|
|
handleEventFn = o.fn(
|
|
|
|
[
|
|
|
|
new o.FnParam(VIEW_VAR.name, o.INFERRED_TYPE),
|
|
|
|
new o.FnParam(EVENT_NAME_VAR.name, o.INFERRED_TYPE),
|
|
|
|
new o.FnParam(EventHandlerVars.event.name, o.INFERRED_TYPE)
|
|
|
|
],
|
|
|
|
[...preStmts, ...handleEventStmts, new o.ReturnStatement(ALLOW_DEFAULT_VAR)],
|
|
|
|
o.INFERRED_TYPE);
|
|
|
|
} else {
|
|
|
|
handleEventFn = o.NULL_EXPR;
|
|
|
|
}
|
|
|
|
return handleEventFn;
|
|
|
|
}
|
|
|
|
|
|
|
|
visitDirective(ast: DirectiveAst, context: {usedEvents: Set<string>}): any {}
|
|
|
|
visitDirectiveProperty(ast: BoundDirectivePropertyAst, context: any): any {}
|
|
|
|
visitReference(ast: ReferenceAst, context: any): any {}
|
|
|
|
visitVariable(ast: VariableAst, context: any): any {}
|
|
|
|
visitEvent(ast: BoundEventAst, context: any): any {}
|
|
|
|
visitElementProperty(ast: BoundElementPropertyAst, 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(ast: TemplateAst): boolean {
|
|
|
|
if (ast instanceof EmbeddedTemplateAst) {
|
|
|
|
return ast.hasViewContainer;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ast instanceof ElementAst) {
|
|
|
|
if (ast.name === NG_CONTAINER_TAG && ast.children.length) {
|
|
|
|
return needsAdditionalRootNode(ast.children[ast.children.length - 1]);
|
|
|
|
}
|
|
|
|
return ast.hasViewContainer;
|
|
|
|
}
|
|
|
|
|
|
|
|
return ast 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;
|
|
|
|
}
|
|
|
|
|
2017-03-01 12:17:43 -08:00
|
|
|
function elementBindingDef(inputAst: BoundElementPropertyAst, dirAst: DirectiveAst): o.Expression {
|
|
|
|
switch (inputAst.type) {
|
|
|
|
case PropertyBindingType.Attribute:
|
|
|
|
return o.literalArr([
|
|
|
|
o.literal(BindingType.ElementAttribute), o.literal(inputAst.name),
|
|
|
|
o.literal(inputAst.securityContext)
|
|
|
|
]);
|
|
|
|
case PropertyBindingType.Property:
|
|
|
|
return o.literalArr([
|
|
|
|
o.literal(BindingType.ElementProperty), o.literal(inputAst.name),
|
|
|
|
o.literal(inputAst.securityContext)
|
|
|
|
]);
|
|
|
|
case PropertyBindingType.Animation:
|
|
|
|
const bindingType = dirAst && dirAst.directive.isComponent ?
|
|
|
|
BindingType.ComponentHostProperty :
|
|
|
|
BindingType.ElementProperty;
|
|
|
|
return o.literalArr([
|
|
|
|
o.literal(bindingType), o.literal('@' + inputAst.name), o.literal(inputAst.securityContext)
|
|
|
|
]);
|
|
|
|
case PropertyBindingType.Class:
|
|
|
|
return o.literalArr([o.literal(BindingType.ElementClass), o.literal(inputAst.name)]);
|
|
|
|
case PropertyBindingType.Style:
|
|
|
|
return o.literalArr([
|
|
|
|
o.literal(BindingType.ElementStyle), o.literal(inputAst.name), o.literal(inputAst.unit)
|
|
|
|
]);
|
|
|
|
}
|
2017-02-27 23:08:19 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function fixedAttrsDef(elementAst: ElementAst): o.Expression {
|
|
|
|
const mapResult: {[key: string]: string} = Object.create(null);
|
|
|
|
elementAst.attrs.forEach(attrAst => { mapResult[attrAst.name] = attrAst.value; });
|
|
|
|
elementAst.directives.forEach(dirAst => {
|
|
|
|
Object.keys(dirAst.directive.hostAttributes).forEach(name => {
|
|
|
|
const value = dirAst.directive.hostAttributes[name];
|
|
|
|
const prevValue = mapResult[name];
|
|
|
|
mapResult[name] = prevValue != null ? mergeAttributeValue(name, prevValue, value) : value;
|
|
|
|
});
|
|
|
|
});
|
|
|
|
const mapEntries: o.LiteralMapEntry[] = [];
|
|
|
|
// Note: We need to sort to get a defined output order
|
|
|
|
// for tests and for caching generated artifacts...
|
|
|
|
return o.literalArr(Object.keys(mapResult).sort().map(
|
|
|
|
(attrName) => o.literalArr([o.literal(attrName), o.literal(mapResult[attrName])])));
|
|
|
|
}
|
|
|
|
|
|
|
|
function mergeAttributeValue(attrName: string, attrValue1: string, attrValue2: string): string {
|
|
|
|
if (attrName == CLASS_ATTR || attrName == STYLE_ATTR) {
|
|
|
|
return `${attrValue1} ${attrValue2}`;
|
|
|
|
} else {
|
|
|
|
return attrValue2;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function callCheckStmt(nodeIndex: number, exprs: o.Expression[]): o.Expression {
|
|
|
|
if (exprs.length > 10) {
|
|
|
|
return CHECK_VAR.callFn(
|
|
|
|
[VIEW_VAR, o.literal(nodeIndex), o.literal(ArgumentType.Dynamic), o.literalArr(exprs)]);
|
|
|
|
} else {
|
|
|
|
return CHECK_VAR.callFn(
|
|
|
|
[VIEW_VAR, o.literal(nodeIndex), o.literal(ArgumentType.Inline), ...exprs]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function callUnwrapValue(expr: o.Expression): o.Expression {
|
|
|
|
return o.importExpr(createIdentifier(Identifiers.unwrapValue)).callFn([expr]);
|
|
|
|
}
|
|
|
|
|
|
|
|
interface StaticAndDynamicQueryIds {
|
|
|
|
staticQueryIds: Set<number>;
|
|
|
|
dynamicQueryIds: Set<number>;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function findStaticQueryIds(
|
|
|
|
nodes: TemplateAst[], result = new Map<TemplateAst, StaticAndDynamicQueryIds>()):
|
|
|
|
Map<TemplateAst, StaticAndDynamicQueryIds> {
|
|
|
|
nodes.forEach((node) => {
|
|
|
|
const staticQueryIds = new Set<number>();
|
|
|
|
const dynamicQueryIds = new Set<number>();
|
|
|
|
let queryMatches: QueryMatch[];
|
|
|
|
if (node instanceof ElementAst) {
|
|
|
|
findStaticQueryIds(node.children, result);
|
|
|
|
node.children.forEach((child) => {
|
|
|
|
const childData = result.get(child);
|
|
|
|
childData.staticQueryIds.forEach(queryId => staticQueryIds.add(queryId));
|
|
|
|
childData.dynamicQueryIds.forEach(queryId => dynamicQueryIds.add(queryId));
|
|
|
|
});
|
|
|
|
queryMatches = node.queryMatches;
|
|
|
|
} else if (node instanceof EmbeddedTemplateAst) {
|
|
|
|
findStaticQueryIds(node.children, result);
|
|
|
|
node.children.forEach((child) => {
|
|
|
|
const childData = result.get(child);
|
|
|
|
childData.staticQueryIds.forEach(queryId => dynamicQueryIds.add(queryId));
|
|
|
|
childData.dynamicQueryIds.forEach(queryId => dynamicQueryIds.add(queryId));
|
|
|
|
});
|
|
|
|
queryMatches = node.queryMatches;
|
|
|
|
}
|
|
|
|
if (queryMatches) {
|
|
|
|
queryMatches.forEach((match) => staticQueryIds.add(match.queryId));
|
|
|
|
}
|
|
|
|
dynamicQueryIds.forEach(queryId => staticQueryIds.delete(queryId));
|
|
|
|
result.set(node, {staticQueryIds, dynamicQueryIds});
|
|
|
|
});
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
function staticViewQueryIds(nodeStaticQueryIds: Map<TemplateAst, StaticAndDynamicQueryIds>):
|
|
|
|
StaticAndDynamicQueryIds {
|
|
|
|
const staticQueryIds = new Set<number>();
|
|
|
|
const dynamicQueryIds = new Set<number>();
|
|
|
|
Array.from(nodeStaticQueryIds.values()).forEach((entry) => {
|
|
|
|
entry.staticQueryIds.forEach(queryId => staticQueryIds.add(queryId));
|
|
|
|
entry.dynamicQueryIds.forEach(queryId => dynamicQueryIds.add(queryId));
|
|
|
|
});
|
|
|
|
dynamicQueryIds.forEach(queryId => staticQueryIds.delete(queryId));
|
|
|
|
return {staticQueryIds, dynamicQueryIds};
|
|
|
|
}
|
|
|
|
|
|
|
|
function createComponentFactoryResolver(directives: DirectiveAst[]): ProviderAst {
|
|
|
|
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}));
|
2017-03-14 16:26:17 -07:00
|
|
|
|
2017-02-27 23:08:19 -08:00
|
|
|
const token = createIdentifierToken(Identifiers.ComponentFactoryResolver);
|
2017-03-14 16:26:17 -07:00
|
|
|
|
2017-02-27 23:08:19 -08:00
|
|
|
const classMeta: CompileTypeMetadata = {
|
|
|
|
diDeps: [
|
|
|
|
{isValue: true, value: o.literalArr(entryComponentFactories)},
|
2017-03-14 16:26:17 -07:00
|
|
|
{token: token, isSkipSelf: true, isOptional: true},
|
|
|
|
{token: createIdentifierToken(Identifiers.NgModuleRef)},
|
2017-02-27 23:08:19 -08:00
|
|
|
],
|
|
|
|
lifecycleHooks: [],
|
|
|
|
reference: resolveIdentifier(Identifiers.CodegenComponentFactoryResolver)
|
|
|
|
};
|
|
|
|
return new ProviderAst(
|
|
|
|
token, false, true, [{token, multi: false, useClass: classMeta}],
|
|
|
|
ProviderAstType.PrivateService, [], componentDirMeta.sourceSpan);
|
|
|
|
}
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
function elementEventNameAndTarget(
|
|
|
|
eventAst: BoundEventAst, dirAst: DirectiveAst): {name: string, target: string} {
|
|
|
|
if (eventAst.isAnimation) {
|
|
|
|
return {
|
|
|
|
name: `@${eventAst.name}.${eventAst.phase}`,
|
|
|
|
target: dirAst && dirAst.directive.isComponent ? 'component' : null
|
|
|
|
};
|
|
|
|
} else {
|
|
|
|
return eventAst;
|
2016-01-06 14:13:44 -08:00
|
|
|
}
|
|
|
|
}
|