feat(compiler): reuse the TypeScript typecheck for template typechecking. (#19152)

This speeds up the compilation process significantly.

Also introduces a new option `fullTemplateTypeCheck` to do more checks in templates:
- check expressions inside of templatized content (e.g. inside of `<div *ngIf>`).
- check the arguments of calls to the `transform` function of pipes
- check references to directives that were exposed as variables via `exportAs`
PR Close #19152
This commit is contained in:
Tobias Bosch
2017-09-11 15:18:19 -07:00
committed by Matias Niemelä
parent 554fe65690
commit 996c7c2dde
22 changed files with 712 additions and 401 deletions

View File

@ -6,7 +6,7 @@
* found in the LICENSE file at https://angular.io/license
*/
import {CompileDirectiveMetadata, CompileDirectiveSummary, CompileIdentifierMetadata, CompileNgModuleMetadata, CompileNgModuleSummary, CompilePipeMetadata, CompileProviderMetadata, CompileStylesheetMetadata, CompileSummaryKind, CompileTypeMetadata, CompileTypeSummary, componentFactoryName, createHostComponentMeta, flatten, identifierName, sourceUrl, templateSourceUrl} from '../compile_metadata';
import {CompileDirectiveMetadata, CompileDirectiveSummary, CompileIdentifierMetadata, CompileNgModuleMetadata, CompileNgModuleSummary, CompilePipeMetadata, CompilePipeSummary, CompileProviderMetadata, CompileStylesheetMetadata, CompileSummaryKind, CompileTypeMetadata, CompileTypeSummary, componentFactoryName, createHostComponentMeta, flatten, identifierName, sourceUrl, templateSourceUrl} from '../compile_metadata';
import {CompilerConfig} from '../config';
import {MessageBundle} from '../i18n/message_bundle';
import {Identifiers, createTokenForExternalReference} from '../identifiers';
@ -19,8 +19,10 @@ import * as o from '../output/output_ast';
import {ParseError} from '../parse_util';
import {CompiledStylesheet, StyleCompiler} from '../style_compiler';
import {SummaryResolver} from '../summary_resolver';
import {TemplateAst} from '../template_parser/template_ast';
import {TemplateParser} from '../template_parser/template_parser';
import {OutputContext, syntaxError} from '../util';
import {TypeCheckCompiler} from '../view_compiler/type_check_compiler';
import {ViewCompileResult, ViewCompiler} from '../view_compiler/view_compiler';
import {AotCompilerHost} from './compiler_host';
@ -32,11 +34,15 @@ import {createForJitStub, serializeSummaries} from './summary_serializer';
import {ngfactoryFilePath, splitTypescriptSuffix, summaryFileName, summaryForJitFileName, summaryForJitName} from './util';
export class AotCompiler {
private _templateAstCache =
new Map<StaticSymbol, {template: TemplateAst[], pipes: CompilePipeSummary[]}>();
constructor(
private _config: CompilerConfig, private _host: AotCompilerHost,
private _reflector: StaticReflector, private _metadataResolver: CompileMetadataResolver,
private _templateParser: TemplateParser, private _styleCompiler: StyleCompiler,
private _viewCompiler: ViewCompiler, private _ngModuleCompiler: NgModuleCompiler,
private _htmlParser: HtmlParser, private _templateParser: TemplateParser,
private _styleCompiler: StyleCompiler, private _viewCompiler: ViewCompiler,
private _typeCheckCompiler: TypeCheckCompiler, private _ngModuleCompiler: NgModuleCompiler,
private _outputEmitter: OutputEmitter,
private _summaryResolver: SummaryResolver<StaticSymbol>, private _localeId: string|null,
private _translationFormat: string|null, private _enableSummariesForJit: boolean|null,
@ -66,9 +72,10 @@ export class AotCompiler {
}
emitAllStubs(analyzeResult: NgAnalyzedModules): GeneratedFile[] {
const {files} = analyzeResult;
const {files, ngModuleByPipeOrDirective} = analyzeResult;
const sourceModules = files.map(
file => this._compileStubFile(file.srcUrl, file.directives, file.pipes, file.ngModules));
file => this._compileStubFile(
file.srcUrl, ngModuleByPipeOrDirective, file.directives, file.pipes, file.ngModules));
return flatten(sourceModules);
}
@ -112,7 +119,8 @@ export class AotCompiler {
}
private _compileStubFile(
srcFileUrl: string, directives: StaticSymbol[], pipes: StaticSymbol[],
srcFileUrl: string, ngModuleByPipeOrDirective: Map<StaticSymbol, CompileNgModuleMetadata>,
directives: StaticSymbol[], pipes: StaticSymbol[],
ngModules: StaticSymbol[]): GeneratedFile[] {
const fileSuffix = splitTypescriptSuffix(srcFileUrl, true)[1];
const generatedFiles: GeneratedFile[] = [];
@ -130,10 +138,17 @@ export class AotCompiler {
// the generated code)
directives.forEach((dirType) => {
const compMeta = this._metadataResolver.getDirectiveMetadata(<any>dirType);
if (!compMeta.isComponent) {
return;
}
const ngModule = ngModuleByPipeOrDirective.get(dirType);
if (!ngModule) {
throw new Error(
`Internal Error: cannot determine the module for component ${identifierName(compMeta.type)}!`);
}
this._compileComponentTypeCheckBlock(
ngFactoryOutputCtx, compMeta, ngModule, ngModule.transitiveModule.directives);
// Note: compMeta is a component and therefore template is non null.
compMeta.template !.externalStylesheets.forEach((stylesheetMeta) => {
const styleContext = this._createOutputContext(_stylesModuleUrl(
@ -285,7 +300,8 @@ export class AotCompiler {
ngModule: CompileNgModuleMetadata, fileSuffix: string): void {
const hostType = this._metadataResolver.getHostComponentType(compMeta.type.reference);
const hostMeta = createHostComponentMeta(
hostType, compMeta, this._metadataResolver.getHostComponentViewClass(hostType));
hostType, compMeta, this._metadataResolver.getHostComponentViewClass(hostType),
this._htmlParser);
const hostViewFactoryVar =
this._compileComponent(outputCtx, hostMeta, ngModule, [compMeta.type], null, fileSuffix)
.viewClassVar;
@ -320,19 +336,32 @@ export class AotCompiler {
[o.StmtModifier.Final, o.StmtModifier.Exported]));
}
private _compileComponent(
outputCtx: OutputContext, compMeta: CompileDirectiveMetadata,
ngModule: CompileNgModuleMetadata, directiveIdentifiers: CompileIdentifierMetadata[],
componentStyles: CompiledStylesheet|null, fileSuffix: string): ViewCompileResult {
private _parseTemplate(
compMeta: CompileDirectiveMetadata, ngModule: CompileNgModuleMetadata,
directiveIdentifiers: CompileIdentifierMetadata[]):
{template: TemplateAst[], pipes: CompilePipeSummary[]} {
let result = this._templateAstCache.get(compMeta.type.reference);
if (result) {
return result;
}
const preserveWhitespaces = compMeta !.template !.preserveWhitespaces;
const directives =
directiveIdentifiers.map(dir => this._metadataResolver.getDirectiveSummary(dir.reference));
const pipes = ngModule.transitiveModule.pipes.map(
pipe => this._metadataResolver.getPipeSummary(pipe.reference));
const preserveWhitespaces = compMeta !.template !.preserveWhitespaces;
const {template: parsedTemplate, pipes: usedPipes} = this._templateParser.parse(
compMeta, compMeta.template !.template !, directives, pipes, ngModule.schemas,
result = this._templateParser.parse(
compMeta, compMeta.template !.htmlAst !, directives, pipes, ngModule.schemas,
templateSourceUrl(ngModule.type, compMeta, compMeta.template !), preserveWhitespaces);
this._templateAstCache.set(compMeta.type.reference, result);
return result;
}
private _compileComponent(
outputCtx: OutputContext, compMeta: CompileDirectiveMetadata,
ngModule: CompileNgModuleMetadata, directiveIdentifiers: CompileIdentifierMetadata[],
componentStyles: CompiledStylesheet|null, fileSuffix: string): ViewCompileResult {
const {template: parsedTemplate, pipes: usedPipes} =
this._parseTemplate(compMeta, ngModule, directiveIdentifiers);
const stylesExpr = componentStyles ? o.variable(componentStyles.stylesVar) : o.literalArr([]);
const viewResult = this._viewCompiler.compileComponent(
outputCtx, compMeta, parsedTemplate, stylesExpr, usedPipes);
@ -344,6 +373,14 @@ export class AotCompiler {
return viewResult;
}
private _compileComponentTypeCheckBlock(
outputCtx: OutputContext, compMeta: CompileDirectiveMetadata,
ngModule: CompileNgModuleMetadata, directiveIdentifiers: CompileIdentifierMetadata[]) {
const {template: parsedTemplate, pipes: usedPipes} =
this._parseTemplate(compMeta, ngModule, directiveIdentifiers);
this._typeCheckCompiler.compileComponent(outputCtx, compMeta, parsedTemplate, usedPipes);
}
private _createOutputContext(genFilePath: string): OutputContext {
const importExpr = (symbol: StaticSymbol, typeParams: o.Type[] | null = null) => {
if (!(symbol instanceof StaticSymbol)) {

View File

@ -24,6 +24,7 @@ import {StyleCompiler} from '../style_compiler';
import {TemplateParser} from '../template_parser/template_parser';
import {UrlResolver} from '../url_resolver';
import {syntaxError} from '../util';
import {TypeCheckCompiler} from '../view_compiler/type_check_compiler';
import {ViewCompiler} from '../view_compiler/view_compiler';
import {AotCompiler} from './compiler';
@ -81,9 +82,11 @@ export function createAotCompiler(compilerHost: AotCompilerHost, options: AotCom
console, symbolCache, staticReflector);
// TODO(vicb): do not pass options.i18nFormat here
const viewCompiler = new ViewCompiler(config, staticReflector, elementSchemaRegistry);
const typeCheckCompiler = new TypeCheckCompiler(options, staticReflector);
const compiler = new AotCompiler(
config, compilerHost, staticReflector, resolver, tmplParser, new StyleCompiler(urlResolver),
viewCompiler, new NgModuleCompiler(staticReflector), new TypeScriptEmitter(), summaryResolver,
config, compilerHost, staticReflector, resolver, htmlParser, tmplParser,
new StyleCompiler(urlResolver), viewCompiler, typeCheckCompiler,
new NgModuleCompiler(staticReflector), new TypeScriptEmitter(), summaryResolver,
options.locale || null, options.i18nFormat || null, options.enableSummariesForJit || null,
symbolResolver);
return {compiler, reflector: staticReflector};

View File

@ -16,4 +16,5 @@ export interface AotCompilerOptions {
enableLegacyTemplate?: boolean;
enableSummariesForJit?: boolean;
preserveWhitespaces?: boolean;
fullTemplateTypeCheck?: boolean;
}

View File

@ -9,6 +9,9 @@
import {StaticSymbol} from './aot/static_symbol';
import {ChangeDetectionStrategy, SchemaMetadata, Type, ViewEncapsulation} from './core';
import {LifecycleHooks} from './lifecycle_reflector';
import * as html from './ml_parser/ast';
import {HtmlParser} from './ml_parser/html_parser';
import {ParseTreeResult as HtmlParseTreeResult} from './ml_parser/parser';
import {CssSelector} from './selector';
import {splitAtColon, stringify} from './util';
@ -244,6 +247,7 @@ export class CompileTemplateMetadata {
encapsulation: ViewEncapsulation|null;
template: string|null;
templateUrl: string|null;
htmlAst: HtmlParseTreeResult|null;
isInline: boolean;
styles: string[];
styleUrls: string[];
@ -252,11 +256,13 @@ export class CompileTemplateMetadata {
ngContentSelectors: string[];
interpolation: [string, string]|null;
preserveWhitespaces: boolean;
constructor({encapsulation, template, templateUrl, styles, styleUrls, externalStylesheets,
animations, ngContentSelectors, interpolation, isInline, preserveWhitespaces}: {
constructor({encapsulation, template, templateUrl, htmlAst, styles, styleUrls,
externalStylesheets, animations, ngContentSelectors, interpolation, isInline,
preserveWhitespaces}: {
encapsulation: ViewEncapsulation | null,
template: string|null,
templateUrl: string|null,
htmlAst: HtmlParseTreeResult|null,
styles: string[],
styleUrls: string[],
externalStylesheets: CompileStylesheetMetadata[],
@ -269,6 +275,7 @@ export class CompileTemplateMetadata {
this.encapsulation = encapsulation;
this.template = template;
this.templateUrl = templateUrl;
this.htmlAst = htmlAst;
this.styles = _normalizeArray(styles);
this.styleUrls = _normalizeArray(styleUrls);
this.externalStylesheets = _normalizeArray(externalStylesheets);
@ -503,15 +510,18 @@ export class CompileDirectiveMetadata {
*/
export function createHostComponentMeta(
hostTypeReference: any, compMeta: CompileDirectiveMetadata,
hostViewType: StaticSymbol | ProxyClass): CompileDirectiveMetadata {
hostViewType: StaticSymbol | ProxyClass, htmlParser: HtmlParser): CompileDirectiveMetadata {
const template = CssSelector.parse(compMeta.selector !)[0].getMatchingElementTemplate();
const templateUrl = '';
const htmlAst = htmlParser.parse(template, templateUrl);
return CompileDirectiveMetadata.create({
isHost: true,
type: {reference: hostTypeReference, diDeps: [], lifecycleHooks: []},
template: new CompileTemplateMetadata({
encapsulation: ViewEncapsulation.None,
template: template,
templateUrl: '',
template,
templateUrl,
htmlAst,
styles: [],
styleUrls: [],
ngContentSelectors: [],

View File

@ -30,7 +30,8 @@ export class CompilerConfig {
jitDevMode?: boolean,
missingTranslation?: MissingTranslationStrategy,
enableLegacyTemplate?: boolean,
preserveWhitespaces?: boolean
preserveWhitespaces?: boolean,
fullTemplateTypeCheck?: boolean
} = {}) {
this.defaultEncapsulation = defaultEncapsulation;
this.useJit = !!useJit;

View File

@ -150,7 +150,8 @@ export class DirectiveNormalizer {
return new CompileTemplateMetadata({
encapsulation,
template,
templateUrl: templateAbsUrl, styles, styleUrls,
templateUrl: templateAbsUrl,
htmlAst: rootNodesAndErrors, styles, styleUrls,
ngContentSelectors: visitor.ngContentSelectors,
animations: prenormData.animations,
interpolation: prenormData.interpolation, isInline,
@ -168,6 +169,7 @@ export class DirectiveNormalizer {
encapsulation: templateMeta.encapsulation,
template: templateMeta.template,
templateUrl: templateMeta.templateUrl,
htmlAst: templateMeta.htmlAst,
styles: templateMeta.styles,
styleUrls: templateMeta.styleUrls,
externalStylesheets: externalStylesheets,

View File

@ -11,6 +11,7 @@ import {CompileReflector} from '../compile_reflector';
import {CompilerConfig} from '../config';
import {Type} from '../core';
import {CompileMetadataResolver} from '../metadata_resolver';
import {HtmlParser} from '../ml_parser/html_parser';
import {NgModuleCompiler} from '../ng_module_compiler';
import * as ir from '../output/output_ast';
import {interpretStatements} from '../output/output_interpreter';
@ -43,11 +44,11 @@ export class JitCompiler {
private _sharedStylesheetCount = 0;
constructor(
private _metadataResolver: CompileMetadataResolver, private _templateParser: TemplateParser,
private _styleCompiler: StyleCompiler, private _viewCompiler: ViewCompiler,
private _ngModuleCompiler: NgModuleCompiler, private _summaryResolver: SummaryResolver<Type>,
private _reflector: CompileReflector, private _compilerConfig: CompilerConfig,
private _console: Console,
private _metadataResolver: CompileMetadataResolver, private _htmlParser: HtmlParser,
private _templateParser: TemplateParser, private _styleCompiler: StyleCompiler,
private _viewCompiler: ViewCompiler, private _ngModuleCompiler: NgModuleCompiler,
private _summaryResolver: SummaryResolver<Type>, private _reflector: CompileReflector,
private _compilerConfig: CompilerConfig, private _console: Console,
private getExtraNgModuleProviders: (ngModule: any) => CompileProviderMetadata[]) {}
compileModuleSync(moduleType: Type): object {
@ -228,7 +229,7 @@ export class JitCompiler {
const hostClass = this._metadataResolver.getHostComponentType(compType);
const hostMeta = createHostComponentMeta(
hostClass, compMeta, (compMeta.componentFactory as any).viewDefFactory);
hostClass, compMeta, (compMeta.componentFactory as any).viewDefFactory, this._htmlParser);
compiledTemplate =
new CompiledTemplate(true, compMeta.type, hostMeta, ngModule, [compMeta.type]);
this._compiledHostTemplateCache.set(compType, compiledTemplate);

View File

@ -261,6 +261,7 @@ export class CompileMetadataResolver {
encapsulation: noUndefined(compMeta.encapsulation),
template: noUndefined(compMeta.template),
templateUrl: noUndefined(compMeta.templateUrl),
htmlAst: null,
styles: compMeta.styles || [],
styleUrls: compMeta.styleUrls || [],
animations: animations || [],

View File

@ -1141,8 +1141,8 @@ export function importType(
}
export function expressionType(
expr: Expression, typeModifiers: TypeModifier[] | null = null): ExpressionType|null {
return expr != null ? new ExpressionType(expr, typeModifiers) ! : null;
expr: Expression, typeModifiers: TypeModifier[] | null = null): ExpressionType {
return new ExpressionType(expr, typeModifiers);
}
export function literalArr(

View File

@ -101,8 +101,9 @@ export class TemplateParser {
public transforms: TemplateAstVisitor[]) {}
parse(
component: CompileDirectiveMetadata, template: string, directives: CompileDirectiveSummary[],
pipes: CompilePipeSummary[], schemas: SchemaMetadata[], templateUrl: string,
component: CompileDirectiveMetadata, template: string|ParseTreeResult,
directives: CompileDirectiveSummary[], pipes: CompilePipeSummary[], schemas: SchemaMetadata[],
templateUrl: string,
preserveWhitespaces: boolean): {template: TemplateAst[], pipes: CompilePipeSummary[]} {
const result = this.tryParse(
component, template, directives, pipes, schemas, templateUrl, preserveWhitespaces);
@ -126,11 +127,13 @@ export class TemplateParser {
}
tryParse(
component: CompileDirectiveMetadata, template: string, directives: CompileDirectiveSummary[],
pipes: CompilePipeSummary[], schemas: SchemaMetadata[], templateUrl: string,
preserveWhitespaces: boolean): TemplateParseResult {
let htmlParseResult = this._htmlParser !.parse(
template, templateUrl, true, this.getInterpolationConfig(component));
component: CompileDirectiveMetadata, template: string|ParseTreeResult,
directives: CompileDirectiveSummary[], pipes: CompilePipeSummary[], schemas: SchemaMetadata[],
templateUrl: string, preserveWhitespaces: boolean): TemplateParseResult {
let htmlParseResult = typeof template === 'string' ?
this._htmlParser !.parse(
template, templateUrl, true, this.getInterpolationConfig(component)) :
template;
if (!preserveWhitespaces) {
htmlParseResult = removeWhitespaces(htmlParseResult);

View File

@ -0,0 +1,283 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {AotCompilerOptions} from '../aot/compiler_options';
import {StaticReflector} from '../aot/static_reflector';
import {StaticSymbol} from '../aot/static_symbol';
import {CompileDiDependencyMetadata, CompileDirectiveMetadata, CompilePipeSummary, viewClassName} from '../compile_metadata';
import {BuiltinConverter, EventHandlerVars, LocalResolver, convertActionBinding, convertPropertyBinding, convertPropertyBindingBuiltins} from '../compiler_util/expression_converter';
import {AST, ASTWithSource, Interpolation} from '../expression_parser/ast';
import {Identifiers} from '../identifiers';
import * as o from '../output/output_ast';
import {convertValueToOutputAst} from '../output/value_util';
import {ParseSourceSpan} from '../parse_util';
import {AttrAst, BoundDirectivePropertyAst, BoundElementPropertyAst, BoundEventAst, BoundTextAst, DirectiveAst, ElementAst, EmbeddedTemplateAst, NgContentAst, PropertyBindingType, ProviderAst, ProviderAstType, QueryMatch, ReferenceAst, TemplateAst, TemplateAstVisitor, TextAst, VariableAst, templateVisitAll} from '../template_parser/template_ast';
import {OutputContext} from '../util';
/**
* Generates code that is used to type check templates.
*/
export class TypeCheckCompiler {
constructor(private options: AotCompilerOptions, private reflector: StaticReflector) {}
compileComponent(
outputCtx: OutputContext, component: CompileDirectiveMetadata, template: TemplateAst[],
usedPipes: CompilePipeSummary[]): void {
const pipes = new Map<string, StaticSymbol>();
usedPipes.forEach(p => pipes.set(p.name, p.type.reference));
let embeddedViewCount = 0;
const viewBuilderFactory = (parent: ViewBuilder | null): ViewBuilder => {
const embeddedViewIndex = embeddedViewCount++;
return new ViewBuilder(
this.options, this.reflector, outputCtx, parent, component.type.reference,
embeddedViewIndex, pipes, viewBuilderFactory);
};
const visitor = viewBuilderFactory(null);
visitor.visitAll([], template);
outputCtx.statements.push(...visitor.build());
}
}
interface ViewBuilderFactory {
(parent: ViewBuilder): ViewBuilder;
}
// Note: This is used as key in Map and should therefore be
// unique per value.
type OutputVarType = o.BuiltinTypeName | StaticSymbol;
interface Expression {
context: OutputVarType;
sourceSpan: ParseSourceSpan;
value: AST;
}
class ViewBuilder implements TemplateAstVisitor, LocalResolver {
private outputVarTypes = new Map<string, OutputVarType>();
private outputVarNames = new Map<OutputVarType, string>();
private refOutputVars = new Map<string, OutputVarType>();
private variables: VariableAst[] = [];
private children: ViewBuilder[] = [];
private updates: Expression[] = [];
private actions: Expression[] = [];
constructor(
private options: AotCompilerOptions, private reflector: StaticReflector,
private outputCtx: OutputContext, private parent: ViewBuilder|null,
private component: StaticSymbol, private embeddedViewIndex: number,
private pipes: Map<string, StaticSymbol>, private viewBuilderFactory: ViewBuilderFactory) {}
private getOrAddOutputVar(type: o.BuiltinTypeName|StaticSymbol): string {
let varName = this.outputVarNames.get(type);
if (!varName) {
varName = `_v${this.outputVarNames.size}`;
this.outputVarNames.set(type, varName);
this.outputVarTypes.set(varName, type);
}
return varName;
}
visitAll(variables: VariableAst[], astNodes: TemplateAst[]) {
this.variables = variables;
templateVisitAll(this, astNodes);
}
build(targetStatements: o.Statement[] = []): o.Statement[] {
this.children.forEach((child) => child.build(targetStatements));
const viewStmts: o.Statement[] = [];
let bindingCount = 0;
this.updates.forEach((expression) => {
const {sourceSpan, context, value} = this.preprocessUpdateExpression(expression);
const bindingId = `${bindingCount++}`;
const nameResolver = context === this.component ? this : null;
const {stmts, currValExpr} = convertPropertyBinding(
nameResolver, o.variable(this.getOrAddOutputVar(context)), value, bindingId);
stmts.push(new o.ExpressionStatement(currValExpr));
viewStmts.push(...stmts.map(
(stmt: o.Statement) => o.applySourceSpanToStatementIfNeeded(stmt, sourceSpan)));
});
this.actions.forEach(({sourceSpan, context, value}) => {
const bindingId = `${bindingCount++}`;
const nameResolver = context === this.component ? this : null;
const {stmts} = convertActionBinding(
nameResolver, o.variable(this.getOrAddOutputVar(context)), value, bindingId);
viewStmts.push(...stmts.map(
(stmt: o.Statement) => o.applySourceSpanToStatementIfNeeded(stmt, sourceSpan)));
});
const viewName = `_View_${this.component.name}_${this.embeddedViewIndex}`;
const params: o.FnParam[] = [];
this.outputVarNames.forEach((varName, varType) => {
const outputType = varType instanceof StaticSymbol ?
o.expressionType(this.outputCtx.importExpr(varType)) :
new o.BuiltinType(varType);
params.push(new o.FnParam(varName, outputType));
});
const viewFactory = new o.DeclareFunctionStmt(viewName, params, viewStmts);
targetStatements.push(viewFactory);
return targetStatements;
}
visitBoundText(ast: BoundTextAst, context: any): any {
const astWithSource = <ASTWithSource>ast.value;
const inter = <Interpolation>astWithSource.ast;
inter.expressions.forEach(
(expr) =>
this.updates.push({context: this.component, value: expr, sourceSpan: ast.sourceSpan}));
}
visitEmbeddedTemplate(ast: EmbeddedTemplateAst, context: any): any {
this.visitElementOrTemplate(ast);
// Note: The old view compiler used to use an `any` type
// for the context in any embedded view.
// We keep this behaivor behind a flag for now.
if (this.options.fullTemplateTypeCheck) {
const childVisitor = this.viewBuilderFactory(this);
this.children.push(childVisitor);
childVisitor.visitAll(ast.variables, ast.children);
}
}
visitElement(ast: ElementAst, context: any): any {
this.visitElementOrTemplate(ast);
let inputDefs: o.Expression[] = [];
let updateRendererExpressions: Expression[] = [];
let outputDefs: o.Expression[] = [];
ast.inputs.forEach((inputAst) => {
this.updates.push(
{context: this.component, value: inputAst.value, sourceSpan: inputAst.sourceSpan});
});
templateVisitAll(this, ast.children);
}
private visitElementOrTemplate(ast: {
outputs: BoundEventAst[],
directives: DirectiveAst[],
references: ReferenceAst[],
}) {
ast.directives.forEach((dirAst) => { this.visitDirective(dirAst); });
ast.references.forEach((ref) => {
let outputVarType: OutputVarType = null !;
// Note: The old view compiler used to use an `any` type
// for directives exposed via `exportAs`.
// We keep this behaivor behind a flag for now.
if (ref.value && ref.value.identifier && this.options.fullTemplateTypeCheck) {
outputVarType = ref.value.identifier.reference;
} else {
outputVarType = o.BuiltinTypeName.Dynamic;
}
this.refOutputVars.set(ref.name, outputVarType);
});
ast.outputs.forEach((outputAst) => {
this.actions.push(
{context: this.component, value: outputAst.handler, sourceSpan: outputAst.sourceSpan});
});
}
visitDirective(dirAst: DirectiveAst) {
const dirType = dirAst.directive.type.reference;
dirAst.inputs.forEach(
(input) => this.updates.push(
{context: this.component, value: input.value, sourceSpan: input.sourceSpan}));
// Note: The old view compiler used to use an `any` type
// for expressions in host properties / events.
// We keep this behaivor behind a flag for now.
if (this.options.fullTemplateTypeCheck) {
dirAst.hostProperties.forEach(
(inputAst) => this.updates.push(
{context: dirType, value: inputAst.value, sourceSpan: inputAst.sourceSpan}));
dirAst.hostEvents.forEach((hostEventAst) => this.actions.push({
context: dirType,
value: hostEventAst.handler,
sourceSpan: hostEventAst.sourceSpan
}));
}
}
getLocal(name: string): o.Expression|null {
if (name == EventHandlerVars.event.name) {
return o.variable(this.getOrAddOutputVar(o.BuiltinTypeName.Dynamic));
}
for (let currBuilder: ViewBuilder|null = this; currBuilder; currBuilder = currBuilder.parent) {
let outputVarType: OutputVarType|undefined;
// check references
outputVarType = currBuilder.refOutputVars.get(name);
if (outputVarType == null) {
// check variables
const varAst = currBuilder.variables.find((varAst) => varAst.name === name);
if (varAst) {
outputVarType = o.BuiltinTypeName.Dynamic;
}
}
if (outputVarType != null) {
return o.variable(this.getOrAddOutputVar(outputVarType));
}
}
return null;
}
private pipeOutputVar(name: string): string {
const pipe = this.pipes.get(name);
if (!pipe) {
throw new Error(
`Illegal State: Could not find pipe ${name} in template of ${this.component}`);
}
return this.getOrAddOutputVar(pipe);
}
private preprocessUpdateExpression(expression: Expression): Expression {
return {
sourceSpan: expression.sourceSpan,
context: expression.context,
value: convertPropertyBindingBuiltins(
{
createLiteralArrayConverter: (argCount: number) => (args: o.Expression[]) =>
o.literalArr(args),
createLiteralMapConverter: (keys: {key: string, quoted: boolean}[]) =>
(values: o.Expression[]) => {
const entries = keys.map((k, i) => ({
key: k.key,
value: values[i],
quoted: k.quoted,
}));
return o.literalMap(entries);
},
createPipeConverter: (name: string, argCount: number) => (args: o.Expression[]) => {
// Note: The old view compiler used to use an `any` type
// for pipe calls.
// We keep this behaivor behind a flag for now.
if (this.options.fullTemplateTypeCheck) {
return o.variable(this.pipeOutputVar(name)).callMethod('transform', args);
} else {
return o.variable(this.getOrAddOutputVar(o.BuiltinTypeName.Dynamic));
}
},
},
expression.value)
};
}
visitNgContent(ast: NgContentAst, context: any): any {}
visitText(ast: TextAst, context: any): 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 {}
}