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-08-16 16:29:39 -07:00
|
|
|
import {CompileDirectiveMetadata, CompilePipeSummary, rendererTypeName, tokenReference, viewClassName} from '../compile_metadata';
|
2017-05-18 13:46:51 -07:00
|
|
|
import {CompileReflector} from '../compile_reflector';
|
2017-03-21 13:36:42 -07:00
|
|
|
import {BuiltinConverter, EventHandlerVars, LocalResolver, convertActionBinding, convertPropertyBinding, convertPropertyBindingBuiltins} from '../compiler_util/expression_converter';
|
2017-08-16 16:29:39 -07:00
|
|
|
import {ArgumentType, BindingFlags, ChangeDetectionStrategy, NodeFlags, QueryBindingType, QueryValueType, ViewFlags} from '../core';
|
2017-02-27 23:08:19 -08:00
|
|
|
import {AST, ASTWithSource, Interpolation} from '../expression_parser/ast';
|
2017-05-18 13:46:51 -07:00
|
|
|
import {Identifiers} from '../identifiers';
|
|
|
|
import {LifecycleHooks} from '../lifecycle_reflector';
|
2017-04-13 10:37:45 -07:00
|
|
|
import {isNgContainer} from '../ml_parser/tags';
|
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';
|
2017-08-16 16:29:39 -07:00
|
|
|
import {AttrAst, BoundDirectivePropertyAst, BoundElementPropertyAst, BoundEventAst, BoundTextAst, DirectiveAst, ElementAst, EmbeddedTemplateAst, NgContentAst, PropertyBindingType, ProviderAst, QueryMatch, ReferenceAst, TemplateAst, TemplateAstVisitor, TextAst, VariableAst, templateVisitAll} from '../template_parser/template_ast';
|
2017-05-16 16:30:37 -07:00
|
|
|
import {OutputContext} from '../util';
|
2016-06-22 14:06:23 -07:00
|
|
|
|
2017-05-11 10:26:02 -07:00
|
|
|
import {componentFactoryResolverProviderDef, depDef, lifecycleHookToNodeFlag, providerDef} from './provider_compiler';
|
|
|
|
|
2017-02-27 23:08:19 -08:00
|
|
|
const CLASS_ATTR = 'class';
|
|
|
|
const STYLE_ATTR = 'style';
|
|
|
|
const IMPLICIT_TEMPLATE_VAR = '\$implicit';
|
2016-05-25 12:46:22 -07:00
|
|
|
|
2016-01-06 14:13:44 -08:00
|
|
|
export class ViewCompileResult {
|
2017-05-16 16:30:37 -07:00
|
|
|
constructor(public viewClassVar: string, public rendererTypeVar: string) {}
|
2016-01-06 14:13:44 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
export class ViewCompiler {
|
2017-08-16 16:29:39 -07:00
|
|
|
constructor(private _reflector: CompileReflector) {}
|
2016-01-06 14:13:44 -08:00
|
|
|
|
2016-06-08 16:38:52 -07:00
|
|
|
compileComponent(
|
2017-05-16 16:30:37 -07:00
|
|
|
outputCtx: OutputContext, component: CompileDirectiveMetadata, template: TemplateAst[],
|
|
|
|
styles: o.Expression, usedPipes: CompilePipeSummary[]): ViewCompileResult {
|
2017-02-27 23:08:19 -08:00
|
|
|
let embeddedViewCount = 0;
|
|
|
|
const staticQueryIds = findStaticQueryIds(template);
|
2016-09-23 16:37:04 -04:00
|
|
|
|
2017-03-24 09:59:58 -07:00
|
|
|
let renderComponentVarName: string = undefined !;
|
2017-03-13 11:32:07 -07:00
|
|
|
if (!component.isHost) {
|
2017-03-24 09:59:58 -07:00
|
|
|
const template = component.template !;
|
2017-03-13 11:32:07 -07:00
|
|
|
const customRenderData: o.LiteralMapEntry[] = [];
|
2017-03-24 09:59:58 -07:00
|
|
|
if (template.animations && template.animations.length) {
|
2017-05-16 16:30:37 -07:00
|
|
|
customRenderData.push(new o.LiteralMapEntry(
|
|
|
|
'animation', convertValueToOutputAst(outputCtx, template.animations), true));
|
2017-03-13 11:32:07 -07:00
|
|
|
}
|
2017-02-27 23:08:19 -08:00
|
|
|
|
2017-03-13 11:32:07 -07:00
|
|
|
const renderComponentVar = o.variable(rendererTypeName(component.type.reference));
|
2017-03-24 09:59:58 -07:00
|
|
|
renderComponentVarName = renderComponentVar.name !;
|
2017-05-16 16:30:37 -07:00
|
|
|
outputCtx.statements.push(
|
2017-03-13 11:32:07 -07:00
|
|
|
renderComponentVar
|
2017-05-16 16:30:37 -07:00
|
|
|
.set(o.importExpr(Identifiers.createRendererType2).callFn([new o.LiteralMapExpr([
|
2017-07-05 14:51:39 -07:00
|
|
|
new o.LiteralMapEntry('encapsulation', o.literal(template.encapsulation), false),
|
|
|
|
new o.LiteralMapEntry('styles', styles, false),
|
|
|
|
new o.LiteralMapEntry('data', new o.LiteralMapExpr(customRenderData), false)
|
2017-05-16 16:30:37 -07:00
|
|
|
])]))
|
2017-03-13 11:32:07 -07:00
|
|
|
.toDeclStmt(
|
2017-05-16 16:30:37 -07:00
|
|
|
o.importType(Identifiers.RendererType2),
|
|
|
|
[o.StmtModifier.Final, o.StmtModifier.Exported]));
|
2017-03-13 11:32:07 -07:00
|
|
|
}
|
2017-02-27 23:08:19 -08:00
|
|
|
|
2017-03-24 09:59:58 -07:00
|
|
|
const viewBuilderFactory = (parent: ViewBuilder | null): ViewBuilder => {
|
2017-02-27 23:08:19 -08:00
|
|
|
const embeddedViewIndex = embeddedViewCount++;
|
|
|
|
return new ViewBuilder(
|
2017-05-18 13:46:51 -07:00
|
|
|
this._reflector, outputCtx, parent, component, embeddedViewIndex, usedPipes,
|
|
|
|
staticQueryIds, viewBuilderFactory);
|
2017-02-27 23:08:19 -08:00
|
|
|
};
|
|
|
|
|
|
|
|
const visitor = viewBuilderFactory(null);
|
|
|
|
visitor.visitAll([], template);
|
|
|
|
|
2017-05-16 16:30:37 -07:00
|
|
|
outputCtx.statements.push(...visitor.build());
|
2017-02-27 23:08:19 -08:00
|
|
|
|
2017-05-16 16:30:37 -07:00
|
|
|
return new ViewCompileResult(visitor.viewName, renderComponentVarName);
|
2017-02-27 23:08:19 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
interface ViewBuilderFactory {
|
|
|
|
(parent: ViewBuilder): ViewBuilder;
|
|
|
|
}
|
|
|
|
|
|
|
|
interface UpdateExpression {
|
2017-03-14 09:16:15 -07:00
|
|
|
context: o.Expression;
|
2017-03-17 13:45:37 -07:00
|
|
|
nodeIndex: number;
|
|
|
|
bindingIndex: number;
|
2017-03-14 09:16:15 -07:00
|
|
|
sourceSpan: ParseSourceSpan;
|
|
|
|
value: AST;
|
2017-02-27 23:08:19 -08:00
|
|
|
}
|
|
|
|
|
2017-05-11 10:26:02 -07:00
|
|
|
const LOG_VAR = o.variable('_l');
|
|
|
|
const VIEW_VAR = o.variable('_v');
|
|
|
|
const CHECK_VAR = o.variable('_ck');
|
|
|
|
const COMP_VAR = o.variable('_co');
|
2017-03-21 08:41:19 -07:00
|
|
|
const EVENT_NAME_VAR = o.variable('en');
|
|
|
|
const ALLOW_DEFAULT_VAR = o.variable(`ad`);
|
2017-02-27 23:08:19 -08:00
|
|
|
|
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: (() => {
|
2017-03-24 09:59:58 -07:00
|
|
|
sourceSpan: ParseSourceSpan | null,
|
2017-03-14 09:16:15 -07:00
|
|
|
nodeDef: o.Expression,
|
2017-03-21 08:41:19 -07:00
|
|
|
nodeFlags: NodeFlags, updateDirectives?: UpdateExpression[], updateRenderer?: UpdateExpression[]
|
2017-03-14 09:16:15 -07:00
|
|
|
})[] = [];
|
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[] = [];
|
|
|
|
|
2017-09-11 15:10:19 -07:00
|
|
|
public readonly viewName: string;
|
|
|
|
|
2017-02-27 23:08:19 -08:00
|
|
|
constructor(
|
2017-05-18 13:46:51 -07:00
|
|
|
private reflector: CompileReflector, private outputCtx: OutputContext,
|
|
|
|
private parent: ViewBuilder|null, private component: CompileDirectiveMetadata,
|
|
|
|
private embeddedViewIndex: number, private usedPipes: CompilePipeSummary[],
|
2017-02-27 23:08:19 -08:00
|
|
|
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.
|
2017-05-16 16:30:37 -07:00
|
|
|
this.compType = this.embeddedViewIndex > 0 ?
|
|
|
|
o.DYNAMIC_TYPE :
|
|
|
|
o.expressionType(outputCtx.importExpr(this.component.type.reference)) !;
|
2017-09-11 15:10:19 -07:00
|
|
|
this.viewName = viewClassName(this.component.type.reference, this.embeddedViewIndex);
|
2017-02-27 23:08:19 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
2017-03-23 13:37:45 -07:00
|
|
|
const flags =
|
|
|
|
NodeFlags.TypeViewQuery | calcStaticDynamicQueryFlags(queryIds, queryId, query.first);
|
2017-03-14 09:16:15 -07:00
|
|
|
this.nodes.push(() => ({
|
|
|
|
sourceSpan: null,
|
2017-03-21 08:41:19 -07:00
|
|
|
nodeFlags: flags,
|
2017-05-16 16:30:37 -07:00
|
|
|
nodeDef: o.importExpr(Identifiers.queryDef).callFn([
|
2017-03-14 09:16:15 -07:00
|
|
|
o.literal(flags), o.literal(queryId),
|
2017-07-05 14:51:39 -07:00
|
|
|
new o.LiteralMapExpr([new o.LiteralMapEntry(
|
|
|
|
query.propertyName, o.literal(bindingType), false)])
|
2017-03-14 09:16:15 -07:00
|
|
|
])
|
|
|
|
}));
|
2017-02-27 23:08:19 -08:00
|
|
|
});
|
|
|
|
}
|
|
|
|
templateVisitAll(this, astNodes);
|
2017-03-17 15:34:37 -07:00
|
|
|
if (this.parent && (astNodes.length === 0 || needsAdditionalRootNode(astNodes))) {
|
|
|
|
// if the view is an embedded view, then we need to add an additional root node in some cases
|
2017-03-14 09:16:15 -07:00
|
|
|
this.nodes.push(() => ({
|
|
|
|
sourceSpan: null,
|
2017-03-21 08:41:19 -07:00
|
|
|
nodeFlags: NodeFlags.TypeElement,
|
2017-05-16 16:30:37 -07:00
|
|
|
nodeDef: o.importExpr(Identifiers.anchorDef).callFn([
|
2017-03-14 09:16:15 -07:00
|
|
|
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-24 09:59:58 -07:00
|
|
|
this.viewName, [new o.FnParam(LOG_VAR.name !)],
|
2017-05-16 16:30:37 -07:00
|
|
|
[new o.ReturnStatement(o.importExpr(Identifiers.viewDef).callFn([
|
2017-02-27 23:08:19 -08:00
|
|
|
o.literal(viewFlags),
|
2017-03-14 09:16:15 -07:00
|
|
|
o.literalArr(nodeDefExprs),
|
2017-02-27 23:08:19 -08:00
|
|
|
updateDirectivesFn,
|
|
|
|
updateRendererFn,
|
|
|
|
]))],
|
2017-05-16 16:30:37 -07:00
|
|
|
o.importType(Identifiers.ViewDefinition),
|
|
|
|
this.embeddedViewIndex === 0 ? [o.StmtModifier.Exported] : []);
|
2017-02-27 23:08:19 -08:00
|
|
|
|
|
|
|
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[] = [];
|
2017-03-24 09:59:58 -07:00
|
|
|
if (!this.component.isHost && o.findReadVarNames(updateStmts).has(COMP_VAR.name !)) {
|
2017-02-27 23:08:19 -08:00
|
|
|
preStmts.push(COMP_VAR.set(VIEW_VAR.prop('component')).toDeclStmt(this.compType));
|
|
|
|
}
|
|
|
|
updateFn = o.fn(
|
|
|
|
[
|
2017-03-24 09:59:58 -07:00
|
|
|
new o.FnParam(CHECK_VAR.name !, o.INFERRED_TYPE),
|
|
|
|
new o.FnParam(VIEW_VAR.name !, o.INFERRED_TYPE)
|
2017-02-27 23:08:19 -08:00
|
|
|
],
|
|
|
|
[...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,
|
2017-03-21 08:41:19 -07:00
|
|
|
nodeFlags: NodeFlags.TypeNgContent,
|
2017-05-16 16:30:37 -07:00
|
|
|
nodeDef: o.importExpr(Identifiers.ngContentDef).callFn([
|
2017-03-14 09:16:15 -07:00
|
|
|
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,
|
2017-03-21 08:41:19 -07:00
|
|
|
nodeFlags: NodeFlags.TypeText,
|
2017-05-16 16:30:37 -07:00
|
|
|
nodeDef: o.importExpr(Identifiers.textDef).callFn([
|
2017-03-14 09:16:15 -07:00
|
|
|
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-24 09:59:58 -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(
|
2017-03-17 13:45:37 -07:00
|
|
|
(expr, bindingIndex) => this._preprocessUpdateExpression(
|
|
|
|
{nodeIndex, bindingIndex, 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,
|
2017-03-21 08:41:19 -07:00
|
|
|
nodeFlags: NodeFlags.TypeText,
|
2017-05-16 16:30:37 -07:00
|
|
|
nodeDef: o.importExpr(Identifiers.textDef).callFn([
|
2017-03-14 09:16:15 -07:00
|
|
|
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-24 09:59:58 -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,
|
2017-03-21 08:41:19 -07:00
|
|
|
nodeFlags: NodeFlags.TypeElement | flags,
|
2017-05-16 16:30:37 -07:00
|
|
|
nodeDef: o.importExpr(Identifiers.anchorDef).callFn([
|
2017-03-14 09:16:15 -07:00
|
|
|
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-24 09:59:58 -07:00
|
|
|
this.nodes.push(null !);
|
2017-02-27 23:08:19 -08:00
|
|
|
|
2017-04-13 10:37:45 -07:00
|
|
|
// Using a null element name creates an anchor.
|
|
|
|
const elName: string|null = isNgContainer(ast.name) ? null : ast.name;
|
2017-02-27 23:08:19 -08:00
|
|
|
|
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-24 09:59:58 -07:00
|
|
|
const hostBindings: any[] = ast.inputs
|
|
|
|
.map((inputAst) => ({
|
|
|
|
context: COMP_VAR as o.Expression,
|
|
|
|
inputAst,
|
|
|
|
dirAst: null as any,
|
|
|
|
}))
|
|
|
|
.concat(dirHostBindings);
|
2017-02-27 23:08:19 -08:00
|
|
|
if (hostBindings.length) {
|
2017-03-14 09:16:15 -07:00
|
|
|
updateRendererExpressions =
|
2017-03-17 13:45:37 -07:00
|
|
|
hostBindings.map((hostBinding, bindingIndex) => this._preprocessUpdateExpression({
|
2017-03-14 09:16:15 -07:00
|
|
|
context: hostBinding.context,
|
2017-03-17 13:45:37 -07:00
|
|
|
nodeIndex,
|
|
|
|
bindingIndex,
|
2017-03-14 09:16:15 -07:00
|
|
|
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);
|
2017-05-16 16:30:37 -07:00
|
|
|
let compRendererType = o.NULL_EXPR as o.Expression;
|
|
|
|
let compView = o.NULL_EXPR as o.Expression;
|
2017-02-27 23:08:19 -08:00
|
|
|
if (compAst) {
|
2017-05-16 16:30:37 -07:00
|
|
|
compView = this.outputCtx.importExpr(compAst.directive.componentViewType);
|
|
|
|
compRendererType = this.outputCtx.importExpr(compAst.directive.rendererType);
|
2017-02-27 23:08:19 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
// elementDef(
|
|
|
|
// flags: NodeFlags, matchedQueriesDsl: [string | number, QueryValueType][],
|
|
|
|
// ngContentIndex: number, childCount: number, namespaceAndName: string,
|
|
|
|
// fixedAttrs: [string, string][] = [],
|
2017-03-17 09:23:28 -07:00
|
|
|
// bindings?: [BindingFlags, string, string | SecurityContext][],
|
2017-02-27 23:08:19 -08:00
|
|
|
// 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,
|
2017-03-21 08:41:19 -07:00
|
|
|
nodeFlags: NodeFlags.TypeElement | flags,
|
2017-05-16 16:30:37 -07:00
|
|
|
nodeDef: o.importExpr(Identifiers.elementDef).callFn([
|
2017-03-14 09:16:15 -07:00
|
|
|
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,
|
2017-03-24 09:59:58 -07:00
|
|
|
usedEvents: [string | null, string][],
|
2017-02-27 23:08:19 -08:00
|
|
|
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;
|
|
|
|
}
|
2017-03-24 09:59:58 -07:00
|
|
|
const usedEvents = new Map<string, [string | null, string]>();
|
2017-02-27 23:08:19 -08:00
|
|
|
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}[] = [];
|
2017-05-11 10:26:02 -07:00
|
|
|
this._visitComponentFactoryResolverProvider(ast.directives);
|
2017-02-27 23:08:19 -08:00
|
|
|
|
|
|
|
ast.providers.forEach((providerAst, providerIndex) => {
|
2017-03-24 09:59:58 -07:00
|
|
|
let dirAst: DirectiveAst = undefined !;
|
|
|
|
let dirIndex: number = undefined !;
|
2017-02-27 23:08:19 -08:00
|
|
|
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,
|
2017-03-24 09:59:58 -07:00
|
|
|
this.staticQueryIds.get(<any>ast) !);
|
2017-02-27 23:08:19 -08:00
|
|
|
hostBindings.push(...dirHostBindings);
|
|
|
|
hostEvents.push(...dirHostEvents);
|
|
|
|
} else {
|
|
|
|
this._visitProvider(providerAst, ast.queryMatches);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
let queryMatchExprs: o.Expression[] = [];
|
|
|
|
ast.queryMatches.forEach((match) => {
|
2017-03-24 09:59:58 -07:00
|
|
|
let valueType: QueryValueType = undefined !;
|
2017-05-18 13:46:51 -07:00
|
|
|
if (tokenReference(match.value) ===
|
|
|
|
this.reflector.resolveExternalReference(Identifiers.ElementRef)) {
|
2017-02-27 23:08:19 -08:00
|
|
|
valueType = QueryValueType.ElementRef;
|
2017-05-18 13:46:51 -07:00
|
|
|
} else if (
|
|
|
|
tokenReference(match.value) ===
|
|
|
|
this.reflector.resolveExternalReference(Identifiers.ViewContainerRef)) {
|
2017-02-27 23:08:19 -08:00
|
|
|
valueType = QueryValueType.ViewContainerRef;
|
2017-05-18 13:46:51 -07:00
|
|
|
} else if (
|
|
|
|
tokenReference(match.value) ===
|
|
|
|
this.reflector.resolveExternalReference(Identifiers.TemplateRef)) {
|
2017-02-27 23:08:19 -08:00
|
|
|
valueType = QueryValueType.TemplateRef;
|
|
|
|
}
|
|
|
|
if (valueType != null) {
|
|
|
|
queryMatchExprs.push(o.literalArr([o.literal(match.queryId), o.literal(valueType)]));
|
|
|
|
}
|
|
|
|
});
|
|
|
|
ast.references.forEach((ref) => {
|
2017-03-24 09:59:58 -07:00
|
|
|
let valueType: QueryValueType = undefined !;
|
2017-02-27 23:08:19 -08:00
|
|
|
if (!ref.value) {
|
|
|
|
valueType = QueryValueType.RenderElement;
|
2017-05-18 13:46:51 -07:00
|
|
|
} else if (
|
|
|
|
tokenReference(ref.value) ===
|
|
|
|
this.reflector.resolveExternalReference(Identifiers.TemplateRef)) {
|
2017-02-27 23:08:19 -08:00
|
|
|
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) => {
|
2017-03-24 09:59:58 -07:00
|
|
|
hostEvents.push({context: COMP_VAR, eventAst: outputAst, dirAst: null !});
|
2017-02-27 23:08:19 -08:00
|
|
|
});
|
|
|
|
|
|
|
|
return {
|
|
|
|
flags,
|
|
|
|
usedEvents: Array.from(usedEvents.values()),
|
|
|
|
queryMatchesExpr: queryMatchExprs.length ? o.literalArr(queryMatchExprs) : o.NULL_EXPR,
|
|
|
|
hostBindings,
|
2017-03-24 09:59:58 -07:00
|
|
|
hostEvents: hostEvents
|
2017-02-27 23:08:19 -08:00
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
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-24 09:59:58 -07:00
|
|
|
this.nodes.push(null !);
|
2017-02-27 23:08:19 -08:00
|
|
|
|
|
|
|
dirAst.directive.queries.forEach((query, queryIndex) => {
|
|
|
|
const queryId = dirAst.contentQueryStartId + queryIndex;
|
2017-03-23 13:37:45 -07:00
|
|
|
const flags =
|
|
|
|
NodeFlags.TypeContentQuery | calcStaticDynamicQueryFlags(queryIds, queryId, query.first);
|
2017-02-27 23:08:19 -08:00
|
|
|
const bindingType = query.first ? QueryBindingType.First : QueryBindingType.All;
|
2017-03-14 09:16:15 -07:00
|
|
|
this.nodes.push(() => ({
|
|
|
|
sourceSpan: dirAst.sourceSpan,
|
2017-03-21 08:41:19 -07:00
|
|
|
nodeFlags: flags,
|
2017-05-16 16:30:37 -07:00
|
|
|
nodeDef: o.importExpr(Identifiers.queryDef).callFn([
|
2017-03-14 09:16:15 -07:00
|
|
|
o.literal(flags), o.literal(queryId),
|
2017-07-05 14:51:39 -07:00
|
|
|
new o.LiteralMapExpr([new o.LiteralMapEntry(
|
|
|
|
query.propertyName, o.literal(bindingType), false)])
|
2017-03-14 09:16:15 -07:00
|
|
|
]),
|
|
|
|
}));
|
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-17 13:45:37 -07:00
|
|
|
updateDirectiveExpressions =
|
|
|
|
dirAst.inputs.map((input, bindingIndex) => this._preprocessUpdateExpression({
|
|
|
|
nodeIndex,
|
|
|
|
bindingIndex,
|
|
|
|
sourceSpan: input.sourceSpan,
|
|
|
|
context: COMP_VAR,
|
|
|
|
value: input.value
|
|
|
|
}));
|
2017-02-27 23:08:19 -08:00
|
|
|
}
|
|
|
|
|
2017-05-16 16:30:37 -07:00
|
|
|
const dirContextExpr =
|
|
|
|
o.importExpr(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,
|
2017-03-21 08:41:19 -07:00
|
|
|
nodeFlags: NodeFlags.TypeDirective | flags,
|
2017-05-16 16:30:37 -07:00
|
|
|
nodeDef: o.importExpr(Identifiers.directiveDef).callFn([
|
2017-03-14 09:16:15 -07:00
|
|
|
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-03-20 15:26:06 -07:00
|
|
|
directive: dirAst.directive.type,
|
2017-03-14 09:16:15 -07:00
|
|
|
});
|
2017-02-27 23:08:19 -08:00
|
|
|
|
|
|
|
return {hostBindings, hostEvents};
|
|
|
|
}
|
|
|
|
|
|
|
|
private _visitProvider(providerAst: ProviderAst, queryMatches: QueryMatch[]): void {
|
2017-05-11 10:26:02 -07:00
|
|
|
this._addProviderNode(this._visitProviderOrDirective(providerAst, queryMatches));
|
|
|
|
}
|
2017-02-27 23:08:19 -08:00
|
|
|
|
2017-05-11 10:26:02 -07:00
|
|
|
private _visitComponentFactoryResolverProvider(directives: DirectiveAst[]) {
|
|
|
|
const componentDirMeta = directives.find(dirAst => dirAst.directive.isComponent);
|
|
|
|
if (componentDirMeta && componentDirMeta.directive.entryComponents.length) {
|
|
|
|
const {providerExpr, depsExpr, flags, tokenExpr} = componentFactoryResolverProviderDef(
|
2017-05-18 13:46:51 -07:00
|
|
|
this.reflector, this.outputCtx, NodeFlags.PrivateProvider,
|
|
|
|
componentDirMeta.directive.entryComponents);
|
2017-05-11 10:26:02 -07:00
|
|
|
this._addProviderNode({
|
|
|
|
providerExpr,
|
|
|
|
depsExpr,
|
|
|
|
flags,
|
|
|
|
tokenExpr,
|
|
|
|
queryMatchExprs: [],
|
|
|
|
sourceSpan: componentDirMeta.sourceSpan
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
2017-02-27 23:08:19 -08:00
|
|
|
|
2017-05-11 10:26:02 -07:00
|
|
|
private _addProviderNode(data: {
|
|
|
|
flags: NodeFlags,
|
|
|
|
queryMatchExprs: o.Expression[],
|
|
|
|
providerExpr: o.Expression,
|
|
|
|
depsExpr: o.Expression,
|
|
|
|
tokenExpr: o.Expression,
|
|
|
|
sourceSpan: ParseSourceSpan
|
|
|
|
}) {
|
|
|
|
const nodeIndex = this.nodes.length;
|
2017-02-27 23:08:19 -08:00
|
|
|
// providerDef(
|
|
|
|
// flags: NodeFlags, matchedQueries: [string, QueryValueType][], token:any,
|
|
|
|
// value: any, deps: ([DepFlags, any] | any)[]): NodeDef;
|
2017-05-11 10:26:02 -07:00
|
|
|
this.nodes.push(
|
|
|
|
() => ({
|
|
|
|
sourceSpan: data.sourceSpan,
|
|
|
|
nodeFlags: data.flags,
|
2017-05-16 16:30:37 -07:00
|
|
|
nodeDef: o.importExpr(Identifiers.providerDef).callFn([
|
2017-05-11 10:26:02 -07:00
|
|
|
o.literal(data.flags),
|
|
|
|
data.queryMatchExprs.length ? o.literalArr(data.queryMatchExprs) : o.NULL_EXPR,
|
|
|
|
data.tokenExpr, data.providerExpr, data.depsExpr
|
|
|
|
])
|
|
|
|
}));
|
2017-02-27 23:08:19 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
private _visitProviderOrDirective(providerAst: ProviderAst, queryMatches: QueryMatch[]): {
|
|
|
|
flags: NodeFlags,
|
2017-05-11 10:26:02 -07:00
|
|
|
tokenExpr: o.Expression,
|
|
|
|
sourceSpan: ParseSourceSpan,
|
2017-02-27 23:08:19 -08:00
|
|
|
queryMatchExprs: o.Expression[],
|
|
|
|
providerExpr: o.Expression,
|
|
|
|
depsExpr: o.Expression
|
|
|
|
} {
|
|
|
|
let flags = NodeFlags.None;
|
|
|
|
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)]));
|
|
|
|
}
|
|
|
|
});
|
2017-05-16 16:30:37 -07:00
|
|
|
const {providerExpr, depsExpr, flags: providerFlags, tokenExpr} =
|
|
|
|
providerDef(this.outputCtx, providerAst);
|
2017-05-11 10:26:02 -07:00
|
|
|
return {
|
|
|
|
flags: flags | providerFlags,
|
|
|
|
queryMatchExprs,
|
|
|
|
providerExpr,
|
|
|
|
depsExpr,
|
|
|
|
tokenExpr,
|
|
|
|
sourceSpan: providerAst.sourceSpan
|
|
|
|
};
|
2017-02-27 23:08:19 -08:00
|
|
|
}
|
|
|
|
|
2017-03-24 09:59:58 -07:00
|
|
|
getLocal(name: string): o.Expression|null {
|
2017-02-27 23:08:19 -08:00
|
|
|
if (name == EventHandlerVars.event.name) {
|
|
|
|
return EventHandlerVars.event;
|
|
|
|
}
|
|
|
|
let currViewExpr: o.Expression = VIEW_VAR;
|
2017-03-24 09:59:58 -07:00
|
|
|
for (let currBuilder: ViewBuilder|null = this; currBuilder; currBuilder = currBuilder.parent,
|
2017-03-29 09:34:45 -07:00
|
|
|
currViewExpr = currViewExpr.prop('parent').cast(o.DYNAMIC_TYPE)) {
|
2017-02-27 23:08:19 -08:00
|
|
|
// check references
|
|
|
|
const refNodeIndex = currBuilder.refNodeIndices[name];
|
|
|
|
if (refNodeIndex != null) {
|
2017-05-16 16:30:37 -07:00
|
|
|
return o.importExpr(Identifiers.nodeValue).callFn([currViewExpr, o.literal(refNodeIndex)]);
|
2017-02-27 23:08:19 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
// 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-08-16 16:29:39 -07:00
|
|
|
private _createLiteralArrayConverter(sourceSpan: ParseSourceSpan, argCount: number):
|
|
|
|
BuiltinConverter {
|
2017-02-27 23:08:19 -08:00
|
|
|
if (argCount === 0) {
|
2017-05-16 16:30:37 -07:00
|
|
|
const valueExpr = o.importExpr(Identifiers.EMPTY_ARRAY);
|
2017-02-27 23:08:19 -08:00
|
|
|
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-05-16 16:30:37 -07:00
|
|
|
this.nodes.push(() => ({
|
|
|
|
sourceSpan,
|
|
|
|
nodeFlags: NodeFlags.TypePureArray,
|
|
|
|
nodeDef: o.importExpr(Identifiers.pureArrayDef).callFn([o.literal(argCount)])
|
|
|
|
}));
|
2017-02-27 23:08:19 -08:00
|
|
|
|
|
|
|
return (args: o.Expression[]) => callCheckStmt(nodeIndex, args);
|
|
|
|
}
|
2017-07-05 14:51:39 -07:00
|
|
|
|
2017-08-16 16:29:39 -07:00
|
|
|
private _createLiteralMapConverter(
|
|
|
|
sourceSpan: ParseSourceSpan, keys: {key: string, quoted: boolean}[]): BuiltinConverter {
|
2017-02-27 23:08:19 -08:00
|
|
|
if (keys.length === 0) {
|
2017-05-16 16:30:37 -07:00
|
|
|
const valueExpr = o.importExpr(Identifiers.EMPTY_MAP);
|
2017-02-27 23:08:19 -08:00
|
|
|
return () => valueExpr;
|
|
|
|
}
|
|
|
|
|
2017-07-05 14:51:39 -07:00
|
|
|
// function pureObjectDef(propToIndex: {[p: string]: number}): NodeDef
|
|
|
|
const map = o.literalMap(keys.map((e, i) => ({...e, value: o.literal(i)})));
|
2017-03-14 09:16:15 -07:00
|
|
|
const nodeIndex = this.nodes.length;
|
|
|
|
this.nodes.push(() => ({
|
|
|
|
sourceSpan,
|
2017-03-21 08:41:19 -07:00
|
|
|
nodeFlags: NodeFlags.TypePureObject,
|
2017-07-05 14:51:39 -07:00
|
|
|
nodeDef: o.importExpr(Identifiers.pureObjectDef).callFn([map])
|
2017-03-14 09:16:15 -07:00
|
|
|
}));
|
2017-02-27 23:08:19 -08:00
|
|
|
|
|
|
|
return (args: o.Expression[]) => callCheckStmt(nodeIndex, args);
|
|
|
|
}
|
2017-07-05 14:51:39 -07:00
|
|
|
|
2017-08-16 16:29:39 -07:00
|
|
|
private _createPipeConverter(expression: UpdateExpression, name: string, argCount: number):
|
2017-03-14 09:16:15 -07:00
|
|
|
BuiltinConverter {
|
2017-03-24 09:59:58 -07:00
|
|
|
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(() => ({
|
2017-03-17 13:45:37 -07:00
|
|
|
sourceSpan: expression.sourceSpan,
|
2017-03-21 08:41:19 -07:00
|
|
|
nodeFlags: NodeFlags.TypePurePipe,
|
2017-05-16 16:30:37 -07:00
|
|
|
nodeDef: o.importExpr(Identifiers.purePipeDef).callFn([o.literal(argCount)])
|
2017-03-14 09:16:15 -07:00
|
|
|
}));
|
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;
|
2017-03-29 09:34:45 -07:00
|
|
|
compViewExpr = compViewExpr.prop('parent').cast(o.DYNAMIC_TYPE);
|
2017-02-27 23:08:19 -08:00
|
|
|
}
|
|
|
|
const pipeNodeIndex = compBuilder.purePipeNodeIndices[name];
|
|
|
|
const pipeValueExpr: o.Expression =
|
2017-05-16 16:30:37 -07:00
|
|
|
o.importExpr(Identifiers.nodeValue).callFn([compViewExpr, o.literal(pipeNodeIndex)]);
|
2017-02-27 23:08:19 -08:00
|
|
|
|
2017-03-17 13:45:37 -07:00
|
|
|
return (args: o.Expression[]) => callUnwrapValue(
|
|
|
|
expression.nodeIndex, expression.bindingIndex,
|
|
|
|
callCheckStmt(nodeIndex, [pipeValueExpr].concat(args)));
|
2017-02-27 23:08:19 -08:00
|
|
|
} else {
|
2017-03-17 13:45:37 -07:00
|
|
|
const nodeIndex = this._createPipe(expression.sourceSpan, pipe);
|
2017-05-16 16:30:37 -07:00
|
|
|
const nodeValueExpr =
|
|
|
|
o.importExpr(Identifiers.nodeValue).callFn([VIEW_VAR, o.literal(nodeIndex)]);
|
2017-02-27 23:08:19 -08:00
|
|
|
|
2017-03-17 13:45:37 -07:00
|
|
|
return (args: o.Expression[]) => callUnwrapValue(
|
|
|
|
expression.nodeIndex, expression.bindingIndex,
|
|
|
|
nodeValueExpr.callMethod('transform', args));
|
2017-02-27 23:08:19 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-03-24 09:59:58 -07:00
|
|
|
private _createPipe(sourceSpan: ParseSourceSpan|null, pipe: CompilePipeSummary): number {
|
2017-03-14 09:16:15 -07:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2017-05-16 16:30:37 -07:00
|
|
|
const depExprs = pipe.type.diDeps.map((diDep) => depDef(this.outputCtx, diDep));
|
2017-02-27 23:08:19 -08:00
|
|
|
// function pipeDef(
|
|
|
|
// flags: NodeFlags, ctor: any, deps: ([DepFlags, any] | any)[]): NodeDef
|
2017-05-16 16:30:37 -07:00
|
|
|
this.nodes.push(
|
|
|
|
() => ({
|
|
|
|
sourceSpan,
|
|
|
|
nodeFlags: NodeFlags.TypePipe,
|
|
|
|
nodeDef: o.importExpr(Identifiers.pipeDef).callFn([
|
|
|
|
o.literal(flags), this.outputCtx.importExpr(pipe.type.reference), o.literalArr(depExprs)
|
|
|
|
])
|
|
|
|
}));
|
2017-02-27 23:08:19 -08:00
|
|
|
return nodeIndex;
|
|
|
|
}
|
|
|
|
|
2017-08-16 16:29:39 -07:00
|
|
|
/**
|
|
|
|
* For the AST in `UpdateExpression.value`:
|
|
|
|
* - create nodes for pipes, literal arrays and, literal maps,
|
|
|
|
* - update the AST to replace pipes, literal arrays and, literal maps with calls to check fn.
|
|
|
|
*
|
|
|
|
* WARNING: 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 {
|
2017-03-17 13:45:37 -07:00
|
|
|
nodeIndex: expression.nodeIndex,
|
|
|
|
bindingIndex: expression.bindingIndex,
|
2017-03-14 09:16:15 -07:00
|
|
|
sourceSpan: expression.sourceSpan,
|
|
|
|
context: expression.context,
|
|
|
|
value: convertPropertyBindingBuiltins(
|
|
|
|
{
|
2017-08-16 16:29:39 -07:00
|
|
|
createLiteralArrayConverter: (argCount: number) => this._createLiteralArrayConverter(
|
2017-03-14 09:16:15 -07:00
|
|
|
expression.sourceSpan, argCount),
|
|
|
|
createLiteralMapConverter:
|
2017-07-05 14:51:39 -07:00
|
|
|
(keys: {key: string, quoted: boolean}[]) =>
|
2017-08-16 16:29:39 -07:00
|
|
|
this._createLiteralMapConverter(expression.sourceSpan, keys),
|
2017-03-14 09:16:15 -07:00
|
|
|
createPipeConverter: (name: string, argCount: number) =>
|
2017-08-16 16:29:39 -07:00
|
|
|
this._createPipeConverter(expression, name, argCount)
|
2017-03-14 09:16:15 -07:00
|
|
|
},
|
|
|
|
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) => {
|
2017-03-21 08:41:19 -07:00
|
|
|
const {nodeDef, nodeFlags, updateDirectives, updateRenderer, sourceSpan} = factory();
|
2017-03-14 09:16:15 -07:00
|
|
|
if (updateRenderer) {
|
2017-03-20 15:26:06 -07:00
|
|
|
updateRendererStmts.push(
|
2017-03-21 08:41:19 -07:00
|
|
|
...createUpdateStatements(nodeIndex, sourceSpan, updateRenderer, false));
|
2017-03-14 09:16:15 -07:00
|
|
|
}
|
|
|
|
if (updateDirectives) {
|
2017-03-21 08:41:19 -07:00
|
|
|
updateDirectivesStmts.push(...createUpdateStatements(
|
|
|
|
nodeIndex, sourceSpan, updateDirectives,
|
|
|
|
(nodeFlags & (NodeFlags.DoCheck | NodeFlags.OnInit)) > 0));
|
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.
|
2017-03-21 08:41:19 -07:00
|
|
|
// Note: We only add the logger to elements / text nodes,
|
|
|
|
// so we don't generate too much code.
|
|
|
|
const logWithNodeDef = nodeFlags & NodeFlags.CatRenderNode ?
|
|
|
|
new o.CommaExpr([LOG_VAR.callFn([]).callFn([]), nodeDef]) :
|
|
|
|
nodeDef;
|
2017-03-14 09:16:15 -07:00
|
|
|
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(
|
2017-03-24 09:59:58 -07:00
|
|
|
nodeIndex: number, sourceSpan: ParseSourceSpan | null, expressions: UpdateExpression[],
|
2017-03-21 08:41:19 -07:00
|
|
|
allowEmptyExprs: boolean): o.Statement[] {
|
2017-03-14 09:16:15 -07:00
|
|
|
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);
|
2017-03-24 09:59:58 -07:00
|
|
|
updateStmts.push(...stmts.map(
|
|
|
|
(stmt: o.Statement) => o.applySourceSpanToStatementIfNeeded(stmt, sourceSpan)));
|
2017-03-14 09:16:15 -07:00
|
|
|
return o.applySourceSpanToExpressionIfNeeded(currValExpr, sourceSpan);
|
|
|
|
});
|
2017-03-21 08:41:19 -07:00
|
|
|
if (expressions.length || allowEmptyExprs) {
|
2017-03-20 15:26:06 -07:00
|
|
|
updateStmts.push(o.applySourceSpanToStatementIfNeeded(
|
|
|
|
callCheckStmt(nodeIndex, exprs).toStmt(), sourceSpan));
|
|
|
|
}
|
2017-03-14 09:16:15 -07:00
|
|
|
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)];
|
2017-03-24 09:59:58 -07:00
|
|
|
if (!this.component.isHost && o.findReadVarNames(handleEventStmts).has(COMP_VAR.name !)) {
|
2017-02-27 23:08:19 -08:00
|
|
|
preStmts.push(COMP_VAR.set(VIEW_VAR.prop('component')).toDeclStmt(this.compType));
|
|
|
|
}
|
|
|
|
handleEventFn = o.fn(
|
|
|
|
[
|
2017-03-24 09:59:58 -07:00
|
|
|
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)
|
2017-02-27 23:08:19 -08:00
|
|
|
],
|
|
|
|
[...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 {}
|
|
|
|
}
|
|
|
|
|
2017-03-17 15:34:37 -07:00
|
|
|
function needsAdditionalRootNode(astNodes: TemplateAst[]): boolean {
|
|
|
|
const lastAstNode = astNodes[astNodes.length - 1];
|
|
|
|
if (lastAstNode instanceof EmbeddedTemplateAst) {
|
|
|
|
return lastAstNode.hasViewContainer;
|
2017-02-27 23:08:19 -08:00
|
|
|
}
|
|
|
|
|
2017-03-17 15:34:37 -07:00
|
|
|
if (lastAstNode instanceof ElementAst) {
|
2017-04-13 10:37:45 -07:00
|
|
|
if (isNgContainer(lastAstNode.name) && lastAstNode.children.length) {
|
2017-03-17 15:34:37 -07:00
|
|
|
return needsAdditionalRootNode(lastAstNode.children);
|
2017-02-27 23:08:19 -08:00
|
|
|
}
|
2017-03-17 15:34:37 -07:00
|
|
|
return lastAstNode.hasViewContainer;
|
2017-02-27 23:08:19 -08:00
|
|
|
}
|
|
|
|
|
2017-03-17 15:34:37 -07:00
|
|
|
return lastAstNode instanceof NgContentAst;
|
2017-02-27 23:08:19 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
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([
|
2017-03-17 09:23:28 -07:00
|
|
|
o.literal(BindingFlags.TypeElementAttribute), o.literal(inputAst.name),
|
2017-03-01 12:17:43 -08:00
|
|
|
o.literal(inputAst.securityContext)
|
|
|
|
]);
|
|
|
|
case PropertyBindingType.Property:
|
|
|
|
return o.literalArr([
|
2017-03-17 09:23:28 -07:00
|
|
|
o.literal(BindingFlags.TypeProperty), o.literal(inputAst.name),
|
2017-03-01 12:17:43 -08:00
|
|
|
o.literal(inputAst.securityContext)
|
|
|
|
]);
|
|
|
|
case PropertyBindingType.Animation:
|
2017-03-17 11:04:30 -07:00
|
|
|
const bindingType = BindingFlags.TypeProperty |
|
|
|
|
(dirAst && dirAst.directive.isComponent ? BindingFlags.SyntheticHostProperty :
|
|
|
|
BindingFlags.SyntheticProperty);
|
2017-03-01 12:17:43 -08:00
|
|
|
return o.literalArr([
|
|
|
|
o.literal(bindingType), o.literal('@' + inputAst.name), o.literal(inputAst.securityContext)
|
|
|
|
]);
|
|
|
|
case PropertyBindingType.Class:
|
2017-03-17 09:23:28 -07:00
|
|
|
return o.literalArr(
|
|
|
|
[o.literal(BindingFlags.TypeElementClass), o.literal(inputAst.name), o.NULL_EXPR]);
|
2017-03-01 12:17:43 -08:00
|
|
|
case PropertyBindingType.Style:
|
|
|
|
return o.literalArr([
|
2017-03-17 09:23:28 -07:00
|
|
|
o.literal(BindingFlags.TypeElementStyle), o.literal(inputAst.name), o.literal(inputAst.unit)
|
2017-03-01 12:17:43 -08:00
|
|
|
]);
|
|
|
|
}
|
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;
|
|
|
|
});
|
|
|
|
});
|
|
|
|
// 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]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-03-17 13:45:37 -07:00
|
|
|
function callUnwrapValue(nodeIndex: number, bindingIdx: number, expr: o.Expression): o.Expression {
|
2017-05-16 16:30:37 -07:00
|
|
|
return o.importExpr(Identifiers.unwrapValue).callFn([
|
2017-03-17 13:45:37 -07:00
|
|
|
VIEW_VAR, o.literal(nodeIndex), o.literal(bindingIdx), expr
|
|
|
|
]);
|
2017-02-27 23:08:19 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
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>();
|
2017-03-24 09:59:58 -07:00
|
|
|
let queryMatches: QueryMatch[] = undefined !;
|
2017-02-27 23:08:19 -08:00
|
|
|
if (node instanceof ElementAst) {
|
|
|
|
findStaticQueryIds(node.children, result);
|
|
|
|
node.children.forEach((child) => {
|
2017-03-24 09:59:58 -07:00
|
|
|
const childData = result.get(child) !;
|
2017-02-27 23:08:19 -08:00
|
|
|
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) => {
|
2017-03-24 09:59:58 -07:00
|
|
|
const childData = result.get(child) !;
|
2017-02-27 23:08:19 -08:00
|
|
|
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 elementEventNameAndTarget(
|
2017-03-24 09:59:58 -07:00
|
|
|
eventAst: BoundEventAst, dirAst: DirectiveAst | null): {name: string, target: string | null} {
|
2017-02-27 23:08:19 -08:00
|
|
|
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
|
|
|
}
|
|
|
|
}
|
2017-03-23 13:37:45 -07:00
|
|
|
|
|
|
|
function calcStaticDynamicQueryFlags(
|
|
|
|
queryIds: StaticAndDynamicQueryIds, queryId: number, isFirst: boolean) {
|
|
|
|
let flags = NodeFlags.None;
|
|
|
|
// Note: We only make queries static that query for a single item.
|
|
|
|
// This is because of backwards compatibility with the old view compiler...
|
|
|
|
if (isFirst && (queryIds.staticQueryIds.has(queryId) || !queryIds.dynamicQueryIds.has(queryId))) {
|
|
|
|
flags |= NodeFlags.StaticQuery;
|
|
|
|
} else {
|
|
|
|
flags |= NodeFlags.DynamicQuery;
|
|
|
|
}
|
|
|
|
return flags;
|
perf: switch angular to use StaticInjector instead of ReflectiveInjector
This change allows ReflectiveInjector to be tree shaken resulting
in not needed Reflect polyfil and smaller bundles.
Code savings for HelloWorld using Closure:
Reflective: bundle.js: 105,864(34,190 gzip)
Static: bundle.js: 154,889(33,555 gzip)
645( 2%)
BREAKING CHANGE:
`platformXXXX()` no longer accepts providers which depend on reflection.
Specifically the method signature when from `Provider[]` to
`StaticProvider[]`.
Example:
Before:
```
[
MyClass,
{provide: ClassA, useClass: SubClassA}
]
```
After:
```
[
{provide: MyClass, deps: [Dep1,...]},
{provide: ClassA, useClass: SubClassA, deps: [Dep1,...]}
]
```
NOTE: This only applies to platform creation and providers for the JIT
compiler. It does not apply to `@Compotent` or `@NgModule` provides
declarations.
Benchpress note: Previously Benchpress also supported reflective
provides, which now require static providers.
DEPRECATION:
- `ReflectiveInjector` is now deprecated as it will be remove. Use
`Injector.create` as a replacement.
closes #18496
2017-08-03 12:33:29 -07:00
|
|
|
}
|
2017-08-16 09:00:03 -07:00
|
|
|
|
|
|
|
export function elementEventFullName(target: string | null, name: string): string {
|
|
|
|
return target ? `${target}:${name}` : name;
|
|
|
|
}
|