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
|
|
|
|
*/
|
|
|
|
|
feat(core): allow users to define timing of ViewChild/ContentChild queries (#28810)
Prior to this commit, the timing of `ViewChild`/`ContentChild` query
resolution depended on the results of each query. If any results
for a particular query were nested inside embedded views (e.g.
*ngIfs), that query would be resolved after change detection ran.
Otherwise, the query would be resolved as soon as nodes were created.
This inconsistency in resolution timing had the potential to cause
confusion because query results would sometimes be available in
ngOnInit, but sometimes wouldn't be available until ngAfterContentInit
or ngAfterViewInit. Code depending on a query result could suddenly
stop working as soon as an *ngIf or an *ngFor was added to the template.
With this commit, users can dictate when they want a particular
`ViewChild` or `ContentChild` query to be resolved with the `static`
flag. For example, one can mark a particular query as `static: false`
to ensure change detection always runs before its results are set:
```ts
@ContentChild('foo', {static: false}) foo !: ElementRef;
```
This means that even if there isn't a query result wrapped in an
*ngIf or an *ngFor now, adding one to the template later won't change
the timing of the query resolution and potentially break your component.
Similarly, if you know that your query needs to be resolved earlier
(e.g. you need results in an ngOnInit hook), you can mark it as
`static: true`.
```ts
@ViewChild(TemplateRef, {static: true}) foo !: TemplateRef;
```
Note: this means that your component will not support *ngIf results.
If you do not supply a `static` option when creating your `ViewChild` or
`ContentChild` query, the default query resolution timing will kick in.
Note: This new option only applies to `ViewChild` and `ContentChild`
queries, not `ViewChildren` or `ContentChildren` queries, as those types
already resolve after CD runs.
PR Close #28810
2019-02-18 14:38:14 -08:00
|
|
|
import {CompileDirectiveMetadata, CompilePipeSummary, CompileQueryMetadata, rendererTypeName, tokenReference, viewClassName} from '../compile_metadata';
|
2017-05-18 13:46:51 -07:00
|
|
|
import {CompileReflector} from '../compile_reflector';
|
2020-04-08 10:14:18 -07:00
|
|
|
import {BindingForm, BuiltinConverter, convertActionBinding, convertPropertyBinding, convertPropertyBindingBuiltins, EventHandlerVars, LocalResolver} 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';
|
2020-04-08 10:14:18 -07:00
|
|
|
import {AttrAst, BoundDirectivePropertyAst, BoundElementPropertyAst, BoundEventAst, BoundTextAst, DirectiveAst, ElementAst, EmbeddedTemplateAst, NgContentAst, PropertyBindingType, ProviderAst, QueryMatch, ReferenceAst, TemplateAst, TemplateAstVisitor, templateVisitAll, TextAst, VariableAst} 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;
|
2016-09-23 16:37:04 -04:00
|
|
|
|
2020-04-08 10:14:18 -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));
|
2020-04-08 10:14:18 -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
|
|
|
|
2020-04-08 10:14:18 -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,
|
2019-09-17 18:33:31 +02:00
|
|
|
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,
|
2020-04-08 10:14:18 -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 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 :
|
2020-04-08 10:14:18 -07:00
|
|
|
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) {
|
|
|
|
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;
|
2019-09-17 18:33:31 +02:00
|
|
|
const flags = NodeFlags.TypeViewQuery | calcStaticDynamicQueryFlags(query);
|
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(
|
2020-04-08 10:14:18 -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[] = [];
|
2020-04-08 10:14:18 -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(
|
|
|
|
[
|
2020-04-08 10:14:18 -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,
|
2020-04-08 10:14:18 -07:00
|
|
|
nodeDef: o.importExpr(Identifiers.ngContentDef)
|
|
|
|
.callFn([o.literal(ast.ngContentIndex), o.literal(ast.index)])
|
2017-03-14 09:16:15 -07:00
|
|
|
}));
|
2017-02-27 23:08:19 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
visitText(ast: TextAst, context: any): any {
|
2017-09-22 14:29:16 -07:00
|
|
|
// Static text nodes have no check function
|
|
|
|
const checkIndex = -1;
|
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-09-22 14:29:16 -07:00
|
|
|
o.literal(checkIndex),
|
|
|
|
o.literal(ast.ngContentIndex),
|
|
|
|
o.literalArr([o.literal(ast.value)]),
|
2017-03-14 09:16:15 -07:00
|
|
|
])
|
|
|
|
}));
|
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
|
2020-04-08 10:14:18 -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
|
|
|
|
2017-09-22 14:29:16 -07:00
|
|
|
// Check index is the same as the node index during compilation
|
|
|
|
// They might only differ at runtime
|
|
|
|
const checkIndex = nodeIndex;
|
|
|
|
|
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-09-22 14:29:16 -07:00
|
|
|
o.literal(checkIndex),
|
|
|
|
o.literal(ast.ngContentIndex),
|
|
|
|
o.literalArr(inter.strings.map(s => o.literal(s))),
|
2017-03-14 09:16:15 -07:00
|
|
|
]),
|
|
|
|
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
|
2020-04-08 10:14:18 -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
|
2020-04-08 10:14:18 -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
|
|
|
}
|
|
|
|
|
2017-09-22 14:29:16 -07:00
|
|
|
// Check index is the same as the node index during compilation
|
|
|
|
// They might only differ at runtime
|
|
|
|
const checkIndex = nodeIndex;
|
|
|
|
|
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-09-22 14:29:16 -07:00
|
|
|
o.literal(checkIndex),
|
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,
|
2020-04-08 10:14:18 -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
|
|
|
|
2019-09-17 18:33:31 +02:00
|
|
|
ast.providers.forEach(providerAst => {
|
2020-04-08 10:14:18 -07:00
|
|
|
let dirAst: DirectiveAst = undefined!;
|
2019-09-17 18:33:31 +02:00
|
|
|
ast.directives.forEach(localDirAst => {
|
2017-02-27 23:08:19 -08:00
|
|
|
if (localDirAst.directive.type.reference === tokenReference(providerAst.token)) {
|
|
|
|
dirAst = localDirAst;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
if (dirAst) {
|
2019-09-17 18:33:31 +02:00
|
|
|
const {hostBindings: dirHostBindings, hostEvents: dirHostEvents} =
|
|
|
|
this._visitDirective(providerAst, dirAst, ast.references, ast.queryMatches, usedEvents);
|
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) => {
|
2020-04-08 10:14:18 -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) => {
|
2020-04-08 10:14:18 -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) => {
|
2020-04-08 10:14:18 -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(
|
2019-09-17 18:33:31 +02:00
|
|
|
providerAst: ProviderAst, dirAst: DirectiveAst, refs: ReferenceAst[],
|
|
|
|
queryMatches: QueryMatch[], usedEvents: Map<string, any>): {
|
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
|
2020-04-08 10:14:18 -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;
|
2019-09-17 18:33:31 +02:00
|
|
|
const flags = NodeFlags.TypeContentQuery | calcStaticDynamicQueryFlags(query);
|
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,
|
2020-04-08 10:14:18 -07:00
|
|
|
eventAst: hostEventAst,
|
|
|
|
dirAst,
|
2017-03-01 12:17:43 -08:00
|
|
|
}));
|
2017-02-27 23:08:19 -08:00
|
|
|
|
2017-09-22 14:29:16 -07:00
|
|
|
// Check index is the same as the node index during compilation
|
|
|
|
// They might only differ at runtime
|
|
|
|
const checkIndex = nodeIndex;
|
2017-02-27 23:08:19 -08:00
|
|
|
|
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-09-22 14:29:16 -07:00
|
|
|
o.literal(checkIndex),
|
|
|
|
o.literal(flags),
|
|
|
|
queryMatchExprs.length ? o.literalArr(queryMatchExprs) : o.NULL_EXPR,
|
|
|
|
o.literal(childCount),
|
|
|
|
providerExpr,
|
|
|
|
depsExpr,
|
2017-03-14 09:16:15 -07:00
|
|
|
inputDefs.length ? new o.LiteralMapExpr(inputDefs) : o.NULL_EXPR,
|
2017-09-22 14:29:16 -07:00
|
|
|
outputDefs.length ? new o.LiteralMapExpr(outputDefs) : o.NULL_EXPR,
|
2017-03-14 09:16:15 -07:00
|
|
|
]),
|
|
|
|
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
|
|
|
|
}) {
|
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;
|
|
|
|
}
|
|
|
|
|
2019-06-06 20:01:51 +02:00
|
|
|
notifyImplicitReceiverUse(): void {
|
|
|
|
// Not needed in View Engine as View Engine walks through the generated
|
|
|
|
// expressions to figure out if the implicit receiver is used and needs
|
|
|
|
// to be generated as part of the pre-update statements.
|
|
|
|
}
|
|
|
|
|
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-09-22 14:29:16 -07:00
|
|
|
const checkIndex = this.nodes.length;
|
|
|
|
|
2017-05-16 16:30:37 -07:00
|
|
|
this.nodes.push(() => ({
|
|
|
|
sourceSpan,
|
|
|
|
nodeFlags: NodeFlags.TypePureArray,
|
2017-09-22 14:29:16 -07:00
|
|
|
nodeDef: o.importExpr(Identifiers.pureArrayDef).callFn([
|
|
|
|
o.literal(checkIndex),
|
|
|
|
o.literal(argCount),
|
|
|
|
])
|
2017-05-16 16:30:37 -07:00
|
|
|
}));
|
2017-02-27 23:08:19 -08:00
|
|
|
|
2017-09-22 14:29:16 -07:00
|
|
|
return (args: o.Expression[]) => callCheckStmt(checkIndex, args);
|
2017-02-27 23:08:19 -08:00
|
|
|
}
|
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
|
|
|
const map = o.literalMap(keys.map((e, i) => ({...e, value: o.literal(i)})));
|
2017-09-22 14:29:16 -07:00
|
|
|
const checkIndex = this.nodes.length;
|
2017-03-14 09:16:15 -07:00
|
|
|
this.nodes.push(() => ({
|
|
|
|
sourceSpan,
|
2017-03-21 08:41:19 -07:00
|
|
|
nodeFlags: NodeFlags.TypePureObject,
|
2017-09-22 14:29:16 -07:00
|
|
|
nodeDef: o.importExpr(Identifiers.pureObjectDef).callFn([
|
|
|
|
o.literal(checkIndex),
|
|
|
|
map,
|
|
|
|
])
|
2017-03-14 09:16:15 -07:00
|
|
|
}));
|
2017-02-27 23:08:19 -08:00
|
|
|
|
2017-09-22 14:29:16 -07:00
|
|
|
return (args: o.Expression[]) => callCheckStmt(checkIndex, args);
|
2017-02-27 23:08:19 -08:00
|
|
|
}
|
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 {
|
2020-04-08 10:14:18 -07:00
|
|
|
const pipe = this.usedPipes.find((pipeSummary) => pipeSummary.name === name)!;
|
2017-02-27 23:08:19 -08:00
|
|
|
if (pipe.pure) {
|
2017-09-22 14:29:16 -07:00
|
|
|
const checkIndex = this.nodes.length;
|
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-09-22 14:29:16 -07:00
|
|
|
nodeDef: o.importExpr(Identifiers.purePipeDef).callFn([
|
|
|
|
o.literal(checkIndex),
|
|
|
|
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,
|
2017-09-22 14:29:16 -07:00
|
|
|
callCheckStmt(checkIndex, [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(
|
|
|
|
{
|
2020-04-08 10:14:18 -07:00
|
|
|
createLiteralArrayConverter: (argCount: number) =>
|
|
|
|
this._createLiteralArrayConverter(expression.sourceSpan, argCount),
|
|
|
|
createLiteralMapConverter: (keys: {key: string, quoted: boolean}[]) =>
|
|
|
|
this._createLiteralMapConverter(expression.sourceSpan, keys),
|
2017-03-14 09:16:15 -07:00
|
|
|
createPipeConverter: (name: string, argCount: number) =>
|
2020-04-08 10:14:18 -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(
|
2020-04-08 10:14:18 -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} =
|
2017-11-29 16:29:05 -08:00
|
|
|
convertPropertyBinding(nameResolver, context, value, bindingId, BindingForm.General);
|
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)];
|
2020-04-08 10:14:18 -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(
|
|
|
|
[
|
2020-04-08 10:14:18 -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 {
|
2018-08-06 14:09:38 -07:00
|
|
|
const inputType = inputAst.type;
|
|
|
|
switch (inputType) {
|
2017-03-01 12:17:43 -08:00
|
|
|
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
|
|
|
]);
|
2018-08-06 14:09:38 -07:00
|
|
|
default:
|
|
|
|
// This default case is not needed by TypeScript compiler, as the switch is exhaustive.
|
|
|
|
// However Closure Compiler does not understand that and reports an error in typed mode.
|
|
|
|
// The `throw new Error` below works around the problem, and the unexpected: never variable
|
|
|
|
// makes sure tsc still checks this code is unreachable.
|
|
|
|
const unexpected: never = inputType;
|
|
|
|
throw new Error(`unexpected ${unexpected}`);
|
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);
|
2020-04-08 10:14:18 -07:00
|
|
|
elementAst.attrs.forEach(attrAst => {
|
|
|
|
mapResult[attrAst.name] = attrAst.value;
|
|
|
|
});
|
2017-02-27 23:08:19 -08:00
|
|
|
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
|
|
|
}
|
|
|
|
|
|
|
|
function elementEventNameAndTarget(
|
2020-04-08 10:14:18 -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
|
|
|
|
2019-09-17 18:33:31 +02:00
|
|
|
function calcStaticDynamicQueryFlags(query: CompileQueryMetadata) {
|
2017-03-23 13:37:45 -07:00
|
|
|
let flags = NodeFlags.None;
|
2019-09-17 18:33:31 +02:00
|
|
|
// Note: We only make queries static that query for a single item and the user specifically
|
|
|
|
// set the to be static. This is because of backwards compatibility with the old view compiler...
|
|
|
|
if (query.first && query.static) {
|
2017-03-23 13:37:45 -07:00
|
|
|
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
|
|
|
|
2020-04-08 10:14:18 -07:00
|
|
|
export function elementEventFullName(target: string|null, name: string): string {
|
2017-08-16 09:00:03 -07:00
|
|
|
return target ? `${target}:${name}` : name;
|
|
|
|
}
|