refactor(TemplateParser): cleanup

This commit is contained in:
Victor Berchet 2016-07-08 10:05:27 -07:00
parent 30a332ee36
commit 9a1babb30c

View File

@ -15,7 +15,7 @@ import {RegExpWrapper, isPresent, StringWrapper, isBlank, isArray} from '../src/
import {BaseException} from '../src/facade/exceptions'; import {BaseException} from '../src/facade/exceptions';
import {AST, Interpolation, ASTWithSource, TemplateBinding, RecursiveAstVisitor, BindingPipe, ParserError} from './expression_parser/ast'; import {AST, Interpolation, ASTWithSource, TemplateBinding, RecursiveAstVisitor, BindingPipe, ParserError} from './expression_parser/ast';
import {Parser} from './expression_parser/parser'; import {Parser} from './expression_parser/parser';
import {CompileDirectiveMetadata, CompilePipeMetadata, CompileMetadataWithType,} from './compile_metadata'; import {CompileDirectiveMetadata, CompilePipeMetadata, CompileMetadataWithType, CompileTokenMetadata,} from './compile_metadata';
import {HtmlParser} from './html_parser'; import {HtmlParser} from './html_parser';
import {splitNsName, mergeNsAndName} from './html_tags'; import {splitNsName, mergeNsAndName} from './html_tags';
import {ParseSourceSpan, ParseError, ParseErrorLevel} from './parse_util'; import {ParseSourceSpan, ParseError, ParseErrorLevel} from './parse_util';
@ -47,7 +47,7 @@ import {ProviderElementContext, ProviderViewContext} from './provider_parser';
// Group 9 = identifier inside [()] // Group 9 = identifier inside [()]
// Group 10 = identifier inside [] // Group 10 = identifier inside []
// Group 11 = identifier inside () // Group 11 = identifier inside ()
var BIND_NAME_REGEXP = const BIND_NAME_REGEXP =
/^(?:(?:(?:(bind-)|(var-)|(let-)|(ref-|#)|(on-)|(bindon-)|(animate-|@))(.+))|\[\(([^\)]+)\)\]|\[([^\]]+)\]|\(([^\)]+)\))$/g; /^(?:(?:(?:(bind-)|(var-)|(let-)|(ref-|#)|(on-)|(bindon-)|(animate-|@))(.+))|\[\(([^\)]+)\)\]|\[([^\]]+)\]|\(([^\)]+)\))$/g;
const TEMPLATE_ELEMENT = 'template'; const TEMPLATE_ELEMENT = 'template';
@ -55,12 +55,12 @@ const TEMPLATE_ATTR = 'template';
const TEMPLATE_ATTR_PREFIX = '*'; const TEMPLATE_ATTR_PREFIX = '*';
const CLASS_ATTR = 'class'; const CLASS_ATTR = 'class';
var PROPERTY_PARTS_SEPARATOR = '.'; const PROPERTY_PARTS_SEPARATOR = '.';
const ATTRIBUTE_PREFIX = 'attr'; const ATTRIBUTE_PREFIX = 'attr';
const CLASS_PREFIX = 'class'; const CLASS_PREFIX = 'class';
const STYLE_PREFIX = 'style'; const STYLE_PREFIX = 'style';
var TEXT_CSS_SELECTOR = CssSelector.parse('*')[0]; const TEXT_CSS_SELECTOR = CssSelector.parse('*')[0];
/** /**
* Provides an array of {@link TemplateAstVisitor}s which will be used to transform * Provides an array of {@link TemplateAstVisitor}s which will be used to transform
@ -91,14 +91,14 @@ export class TemplateParser {
parse( parse(
component: CompileDirectiveMetadata, template: string, directives: CompileDirectiveMetadata[], component: CompileDirectiveMetadata, template: string, directives: CompileDirectiveMetadata[],
pipes: CompilePipeMetadata[], templateUrl: string): TemplateAst[] { pipes: CompilePipeMetadata[], templateUrl: string): TemplateAst[] {
var result = this.tryParse(component, template, directives, pipes, templateUrl); const result = this.tryParse(component, template, directives, pipes, templateUrl);
var warnings = result.errors.filter(error => error.level === ParseErrorLevel.WARNING); const warnings = result.errors.filter(error => error.level === ParseErrorLevel.WARNING);
var errors = result.errors.filter(error => error.level === ParseErrorLevel.FATAL); const errors = result.errors.filter(error => error.level === ParseErrorLevel.FATAL);
if (warnings.length > 0) { if (warnings.length > 0) {
this._console.warn(`Template parse warnings:\n${warnings.join('\n')}`); this._console.warn(`Template parse warnings:\n${warnings.join('\n')}`);
} }
if (errors.length > 0) { if (errors.length > 0) {
var errorString = errors.join('\n'); const errorString = errors.join('\n');
throw new BaseException(`Template parse errors:\n${errorString}`); throw new BaseException(`Template parse errors:\n${errorString}`);
} }
return result.templateAst; return result.templateAst;
@ -107,19 +107,19 @@ export class TemplateParser {
tryParse( tryParse(
component: CompileDirectiveMetadata, template: string, directives: CompileDirectiveMetadata[], component: CompileDirectiveMetadata, template: string, directives: CompileDirectiveMetadata[],
pipes: CompilePipeMetadata[], templateUrl: string): TemplateParseResult { pipes: CompilePipeMetadata[], templateUrl: string): TemplateParseResult {
var htmlAstWithErrors = this._htmlParser.parse(template, templateUrl); const htmlAstWithErrors = this._htmlParser.parse(template, templateUrl);
var errors: ParseError[] = htmlAstWithErrors.errors; const errors: ParseError[] = htmlAstWithErrors.errors;
var result: any /** TODO #???? */; let result: any[];
if (htmlAstWithErrors.rootNodes.length > 0) { if (htmlAstWithErrors.rootNodes.length > 0) {
var uniqDirectives = <CompileDirectiveMetadata[]>removeDuplicates(directives); const uniqDirectives = <CompileDirectiveMetadata[]>removeDuplicates(directives);
var uniqPipes = <CompilePipeMetadata[]>removeDuplicates(pipes); const uniqPipes = <CompilePipeMetadata[]>removeDuplicates(pipes);
var providerViewContext = const providerViewContext =
new ProviderViewContext(component, htmlAstWithErrors.rootNodes[0].sourceSpan); new ProviderViewContext(component, htmlAstWithErrors.rootNodes[0].sourceSpan);
var parseVisitor = new TemplateParseVisitor( const parseVisitor = new TemplateParseVisitor(
providerViewContext, uniqDirectives, uniqPipes, this._exprParser, this._schemaRegistry); providerViewContext, uniqDirectives, uniqPipes, this._exprParser, this._schemaRegistry);
result = htmlVisitAll(parseVisitor, htmlAstWithErrors.rootNodes, EMPTY_ELEMENT_CONTEXT); result = htmlVisitAll(parseVisitor, htmlAstWithErrors.rootNodes, EMPTY_ELEMENT_CONTEXT);
errors = errors.concat(parseVisitor.errors).concat(providerViewContext.errors); errors.push(...parseVisitor.errors, ...providerViewContext.errors);
} else { } else {
result = []; result = [];
} }
@ -176,7 +176,7 @@ class TemplateParseVisitor implements HtmlAstVisitor {
} }
ListWrapper.forEachWithIndex( ListWrapper.forEachWithIndex(
directives, (directive: CompileDirectiveMetadata, index: number) => { directives, (directive: CompileDirectiveMetadata, index: number) => {
var selector = CssSelector.parse(directive.selector); const selector = CssSelector.parse(directive.selector);
this.selectorMatcher.addSelectables(selector, directive); this.selectorMatcher.addSelectables(selector, directive);
this.directivesIndex.set(directive, index); this.directivesIndex.set(directive, index);
}); });
@ -197,9 +197,9 @@ class TemplateParseVisitor implements HtmlAstVisitor {
} }
private _parseInterpolation(value: string, sourceSpan: ParseSourceSpan): ASTWithSource { private _parseInterpolation(value: string, sourceSpan: ParseSourceSpan): ASTWithSource {
var sourceInfo = sourceSpan.start.toString(); const sourceInfo = sourceSpan.start.toString();
try { try {
var ast = this._exprParser.parseInterpolation(value, sourceInfo, this._interpolationConfig); const ast = this._exprParser.parseInterpolation(value, sourceInfo, this._interpolationConfig);
if (ast) this._reportParserErors(ast.errors, sourceSpan); if (ast) this._reportParserErors(ast.errors, sourceSpan);
this._checkPipes(ast, sourceSpan); this._checkPipes(ast, sourceSpan);
if (isPresent(ast) && if (isPresent(ast) &&
@ -215,9 +215,9 @@ class TemplateParseVisitor implements HtmlAstVisitor {
} }
private _parseAction(value: string, sourceSpan: ParseSourceSpan): ASTWithSource { private _parseAction(value: string, sourceSpan: ParseSourceSpan): ASTWithSource {
var sourceInfo = sourceSpan.start.toString(); const sourceInfo = sourceSpan.start.toString();
try { try {
var ast = this._exprParser.parseAction(value, sourceInfo, this._interpolationConfig); const ast = this._exprParser.parseAction(value, sourceInfo, this._interpolationConfig);
if (ast) this._reportParserErors(ast.errors, sourceSpan); if (ast) this._reportParserErors(ast.errors, sourceSpan);
this._checkPipes(ast, sourceSpan); this._checkPipes(ast, sourceSpan);
return ast; return ast;
@ -228,9 +228,9 @@ class TemplateParseVisitor implements HtmlAstVisitor {
} }
private _parseBinding(value: string, sourceSpan: ParseSourceSpan): ASTWithSource { private _parseBinding(value: string, sourceSpan: ParseSourceSpan): ASTWithSource {
var sourceInfo = sourceSpan.start.toString(); const sourceInfo = sourceSpan.start.toString();
try { try {
var ast = this._exprParser.parseBinding(value, sourceInfo, this._interpolationConfig); const ast = this._exprParser.parseBinding(value, sourceInfo, this._interpolationConfig);
if (ast) this._reportParserErors(ast.errors, sourceSpan); if (ast) this._reportParserErors(ast.errors, sourceSpan);
this._checkPipes(ast, sourceSpan); this._checkPipes(ast, sourceSpan);
return ast; return ast;
@ -241,9 +241,9 @@ class TemplateParseVisitor implements HtmlAstVisitor {
} }
private _parseTemplateBindings(value: string, sourceSpan: ParseSourceSpan): TemplateBinding[] { private _parseTemplateBindings(value: string, sourceSpan: ParseSourceSpan): TemplateBinding[] {
var sourceInfo = sourceSpan.start.toString(); const sourceInfo = sourceSpan.start.toString();
try { try {
var bindingsResult = this._exprParser.parseTemplateBindings(value, sourceInfo); const bindingsResult = this._exprParser.parseTemplateBindings(value, sourceInfo);
this._reportParserErors(bindingsResult.errors, sourceSpan); this._reportParserErors(bindingsResult.errors, sourceSpan);
bindingsResult.templateBindings.forEach((binding) => { bindingsResult.templateBindings.forEach((binding) => {
if (isPresent(binding.expression)) { if (isPresent(binding.expression)) {
@ -261,7 +261,7 @@ class TemplateParseVisitor implements HtmlAstVisitor {
private _checkPipes(ast: ASTWithSource, sourceSpan: ParseSourceSpan) { private _checkPipes(ast: ASTWithSource, sourceSpan: ParseSourceSpan) {
if (isPresent(ast)) { if (isPresent(ast)) {
var collector = new PipeCollector(); const collector = new PipeCollector();
ast.visit(collector); ast.visit(collector);
collector.pipes.forEach((pipeName) => { collector.pipes.forEach((pipeName) => {
if (!this.pipesByName.has(pipeName)) { if (!this.pipesByName.has(pipeName)) {
@ -276,8 +276,8 @@ class TemplateParseVisitor implements HtmlAstVisitor {
visitExpansionCase(ast: HtmlExpansionCaseAst, context: any): any { return null; } visitExpansionCase(ast: HtmlExpansionCaseAst, context: any): any { return null; }
visitText(ast: HtmlTextAst, parent: ElementContext): any { visitText(ast: HtmlTextAst, parent: ElementContext): any {
var ngContentIndex = parent.findNgContentIndex(TEXT_CSS_SELECTOR); const ngContentIndex = parent.findNgContentIndex(TEXT_CSS_SELECTOR);
var expr = this._parseInterpolation(ast.value, ast.sourceSpan); const expr = this._parseInterpolation(ast.value, ast.sourceSpan);
if (isPresent(expr)) { if (isPresent(expr)) {
return new BoundTextAst(expr, ngContentIndex, ast.sourceSpan); return new BoundTextAst(expr, ngContentIndex, ast.sourceSpan);
} else { } else {
@ -292,8 +292,8 @@ class TemplateParseVisitor implements HtmlAstVisitor {
visitComment(ast: HtmlCommentAst, context: any): any { return null; } visitComment(ast: HtmlCommentAst, context: any): any { return null; }
visitElement(element: HtmlElementAst, parent: ElementContext): any { visitElement(element: HtmlElementAst, parent: ElementContext): any {
var nodeName = element.name; const nodeName = element.name;
var preparsedElement = preparseElement(element); const preparsedElement = preparseElement(element);
if (preparsedElement.type === PreparsedElementType.SCRIPT || if (preparsedElement.type === PreparsedElementType.SCRIPT ||
preparsedElement.type === PreparsedElementType.STYLE) { preparsedElement.type === PreparsedElementType.STYLE) {
// Skipping <script> for security reasons // Skipping <script> for security reasons
@ -308,27 +308,27 @@ class TemplateParseVisitor implements HtmlAstVisitor {
return null; return null;
} }
var matchableAttrs: string[][] = []; const matchableAttrs: string[][] = [];
var elementOrDirectiveProps: BoundElementOrDirectiveProperty[] = []; const elementOrDirectiveProps: BoundElementOrDirectiveProperty[] = [];
var elementOrDirectiveRefs: ElementOrDirectiveRef[] = []; const elementOrDirectiveRefs: ElementOrDirectiveRef[] = [];
var elementVars: VariableAst[] = []; const elementVars: VariableAst[] = [];
var animationProps: BoundElementPropertyAst[] = []; const animationProps: BoundElementPropertyAst[] = [];
var events: BoundEventAst[] = []; const events: BoundEventAst[] = [];
var templateElementOrDirectiveProps: BoundElementOrDirectiveProperty[] = []; const templateElementOrDirectiveProps: BoundElementOrDirectiveProperty[] = [];
var templateMatchableAttrs: string[][] = []; const templateMatchableAttrs: string[][] = [];
var templateElementVars: VariableAst[] = []; const templateElementVars: VariableAst[] = [];
var hasInlineTemplates = false; let hasInlineTemplates = false;
var attrs: any[] /** TODO #???? */ = []; const attrs: AttrAst[] = [];
var lcElName = splitNsName(nodeName.toLowerCase())[1]; const lcElName = splitNsName(nodeName.toLowerCase())[1];
var isTemplateElement = lcElName == TEMPLATE_ELEMENT; const isTemplateElement = lcElName == TEMPLATE_ELEMENT;
element.attrs.forEach(attr => { element.attrs.forEach(attr => {
var hasBinding = this._parseAttr( const hasBinding = this._parseAttr(
isTemplateElement, attr, matchableAttrs, elementOrDirectiveProps, animationProps, events, isTemplateElement, attr, matchableAttrs, elementOrDirectiveProps, animationProps, events,
elementOrDirectiveRefs, elementVars); elementOrDirectiveRefs, elementVars);
var hasTemplateBinding = this._parseInlineTemplateBinding( const hasTemplateBinding = this._parseInlineTemplateBinding(
attr, templateMatchableAttrs, templateElementOrDirectiveProps, templateElementVars); attr, templateMatchableAttrs, templateElementOrDirectiveProps, templateElementVars);
if (hasTemplateBinding && hasInlineTemplates) { if (hasTemplateBinding && hasInlineTemplates) {
@ -347,31 +347,31 @@ class TemplateParseVisitor implements HtmlAstVisitor {
} }
}); });
var elementCssSelector = createElementCssSelector(nodeName, matchableAttrs); const elementCssSelector = createElementCssSelector(nodeName, matchableAttrs);
var directiveMetas = this._parseDirectives(this.selectorMatcher, elementCssSelector); const directiveMetas = this._parseDirectives(this.selectorMatcher, elementCssSelector);
var references: ReferenceAst[] = []; const references: ReferenceAst[] = [];
var directiveAsts = this._createDirectiveAsts( const directiveAsts = this._createDirectiveAsts(
isTemplateElement, element.name, directiveMetas, elementOrDirectiveProps, isTemplateElement, element.name, directiveMetas, elementOrDirectiveProps,
elementOrDirectiveRefs, element.sourceSpan, references); elementOrDirectiveRefs, element.sourceSpan, references);
var elementProps: BoundElementPropertyAst[] = const elementProps: BoundElementPropertyAst[] =
this._createElementPropertyAsts(element.name, elementOrDirectiveProps, directiveAsts) this._createElementPropertyAsts(element.name, elementOrDirectiveProps, directiveAsts)
.concat(animationProps); .concat(animationProps);
var isViewRoot = parent.isTemplateElement || hasInlineTemplates; const isViewRoot = parent.isTemplateElement || hasInlineTemplates;
var providerContext = new ProviderElementContext( const providerContext = new ProviderElementContext(
this.providerViewContext, parent.providerContext, isViewRoot, directiveAsts, attrs, this.providerViewContext, parent.providerContext, isViewRoot, directiveAsts, attrs,
references, element.sourceSpan); references, element.sourceSpan);
var children = htmlVisitAll( const children = htmlVisitAll(
preparsedElement.nonBindable ? NON_BINDABLE_VISITOR : this, element.children, preparsedElement.nonBindable ? NON_BINDABLE_VISITOR : this, element.children,
ElementContext.create( ElementContext.create(
isTemplateElement, directiveAsts, isTemplateElement, directiveAsts,
isTemplateElement ? parent.providerContext : providerContext)); isTemplateElement ? parent.providerContext : providerContext));
providerContext.afterElement(); providerContext.afterElement();
// Override the actual selector when the `ngProjectAs` attribute is provided // Override the actual selector when the `ngProjectAs` attribute is provided
var projectionSelector = isPresent(preparsedElement.projectAs) ? const projectionSelector = isPresent(preparsedElement.projectAs) ?
CssSelector.parse(preparsedElement.projectAs)[0] : CssSelector.parse(preparsedElement.projectAs)[0] :
elementCssSelector; elementCssSelector;
var ngContentIndex = parent.findNgContentIndex(projectionSelector); const ngContentIndex = parent.findNgContentIndex(projectionSelector);
var parsedElement: any /** TODO #???? */; let parsedElement: TemplateAst;
if (preparsedElement.type === PreparsedElementType.NG_CONTENT) { if (preparsedElement.type === PreparsedElementType.NG_CONTENT) {
if (isPresent(element.children) && element.children.length > 0) { if (isPresent(element.children) && element.children.length > 0) {
@ -393,7 +393,7 @@ class TemplateParseVisitor implements HtmlAstVisitor {
hasInlineTemplates ? null : ngContentIndex, element.sourceSpan); hasInlineTemplates ? null : ngContentIndex, element.sourceSpan);
} else { } else {
this._assertOnlyOneComponent(directiveAsts, element.sourceSpan); this._assertOnlyOneComponent(directiveAsts, element.sourceSpan);
let ngContentIndex = const ngContentIndex =
hasInlineTemplates ? null : parent.findNgContentIndex(projectionSelector); hasInlineTemplates ? null : parent.findNgContentIndex(projectionSelector);
parsedElement = new ElementAst( parsedElement = new ElementAst(
nodeName, attrs, elementProps, events, references, nodeName, attrs, elementProps, events, references,
@ -402,16 +402,18 @@ class TemplateParseVisitor implements HtmlAstVisitor {
hasInlineTemplates ? null : ngContentIndex, element.sourceSpan); hasInlineTemplates ? null : ngContentIndex, element.sourceSpan);
} }
if (hasInlineTemplates) { if (hasInlineTemplates) {
var templateCssSelector = createElementCssSelector(TEMPLATE_ELEMENT, templateMatchableAttrs); const templateCssSelector =
var templateDirectiveMetas = this._parseDirectives(this.selectorMatcher, templateCssSelector); createElementCssSelector(TEMPLATE_ELEMENT, templateMatchableAttrs);
var templateDirectiveAsts = this._createDirectiveAsts( const templateDirectiveMetas =
this._parseDirectives(this.selectorMatcher, templateCssSelector);
const templateDirectiveAsts = this._createDirectiveAsts(
true, element.name, templateDirectiveMetas, templateElementOrDirectiveProps, [], true, element.name, templateDirectiveMetas, templateElementOrDirectiveProps, [],
element.sourceSpan, []); element.sourceSpan, []);
var templateElementProps: BoundElementPropertyAst[] = this._createElementPropertyAsts( const templateElementProps: BoundElementPropertyAst[] = this._createElementPropertyAsts(
element.name, templateElementOrDirectiveProps, templateDirectiveAsts); element.name, templateElementOrDirectiveProps, templateDirectiveAsts);
this._assertNoComponentsNorElementBindingsOnTemplate( this._assertNoComponentsNorElementBindingsOnTemplate(
templateDirectiveAsts, templateElementProps, element.sourceSpan); templateDirectiveAsts, templateElementProps, element.sourceSpan);
var templateProviderContext = new ProviderElementContext( const templateProviderContext = new ProviderElementContext(
this.providerViewContext, parent.providerContext, parent.isTemplateElement, this.providerViewContext, parent.providerContext, parent.isTemplateElement,
templateDirectiveAsts, [], [], element.sourceSpan); templateDirectiveAsts, [], [], element.sourceSpan);
templateProviderContext.afterElement(); templateProviderContext.afterElement();
@ -428,17 +430,17 @@ class TemplateParseVisitor implements HtmlAstVisitor {
private _parseInlineTemplateBinding( private _parseInlineTemplateBinding(
attr: HtmlAttrAst, targetMatchableAttrs: string[][], attr: HtmlAttrAst, targetMatchableAttrs: string[][],
targetProps: BoundElementOrDirectiveProperty[], targetVars: VariableAst[]): boolean { targetProps: BoundElementOrDirectiveProperty[], targetVars: VariableAst[]): boolean {
var templateBindingsSource: string = null; let templateBindingsSource: string = null;
if (attr.name == TEMPLATE_ATTR) { if (attr.name == TEMPLATE_ATTR) {
templateBindingsSource = attr.value; templateBindingsSource = attr.value;
} else if (attr.name.startsWith(TEMPLATE_ATTR_PREFIX)) { } else if (attr.name.startsWith(TEMPLATE_ATTR_PREFIX)) {
var key = attr.name.substring(TEMPLATE_ATTR_PREFIX.length); // remove the star const key = attr.name.substring(TEMPLATE_ATTR_PREFIX.length); // remove the star
templateBindingsSource = (attr.value.length == 0) ? key : key + ' ' + attr.value; templateBindingsSource = (attr.value.length == 0) ? key : key + ' ' + attr.value;
} }
if (isPresent(templateBindingsSource)) { if (isPresent(templateBindingsSource)) {
var bindings = this._parseTemplateBindings(templateBindingsSource, attr.sourceSpan); const bindings = this._parseTemplateBindings(templateBindingsSource, attr.sourceSpan);
for (var i = 0; i < bindings.length; i++) { for (let i = 0; i < bindings.length; i++) {
var binding = bindings[i]; const binding = bindings[i];
if (binding.keyIsVar) { if (binding.keyIsVar) {
targetVars.push(new VariableAst(binding.key, binding.name, attr.sourceSpan)); targetVars.push(new VariableAst(binding.key, binding.name, attr.sourceSpan));
} else if (isPresent(binding.expression)) { } else if (isPresent(binding.expression)) {
@ -459,10 +461,10 @@ class TemplateParseVisitor implements HtmlAstVisitor {
targetProps: BoundElementOrDirectiveProperty[], targetProps: BoundElementOrDirectiveProperty[],
targetAnimationProps: BoundElementPropertyAst[], targetEvents: BoundEventAst[], targetAnimationProps: BoundElementPropertyAst[], targetEvents: BoundEventAst[],
targetRefs: ElementOrDirectiveRef[], targetVars: VariableAst[]): boolean { targetRefs: ElementOrDirectiveRef[], targetVars: VariableAst[]): boolean {
var attrName = this._normalizeAttributeName(attr.name); const attrName = this._normalizeAttributeName(attr.name);
var attrValue = attr.value; const attrValue = attr.value;
var bindParts = RegExpWrapper.firstMatch(BIND_NAME_REGEXP, attrName); const bindParts = RegExpWrapper.firstMatch(BIND_NAME_REGEXP, attrName);
var hasBinding = false; let hasBinding = false;
if (isPresent(bindParts)) { if (isPresent(bindParts)) {
hasBinding = true; hasBinding = true;
if (isPresent(bindParts[1])) { // match: bind-prop if (isPresent(bindParts[1])) { // match: bind-prop
@ -471,7 +473,7 @@ class TemplateParseVisitor implements HtmlAstVisitor {
targetAnimationProps); targetAnimationProps);
} else if (isPresent(bindParts[2])) { // match: var-name / var-name="iden" } else if (isPresent(bindParts[2])) { // match: var-name / var-name="iden"
var identifier = bindParts[8]; const identifier = bindParts[8];
if (isTemplateElement) { if (isTemplateElement) {
this._reportError( this._reportError(
`"var-" on <template> elements is deprecated. Use "let-" instead!`, attr.sourceSpan, `"var-" on <template> elements is deprecated. Use "let-" instead!`, attr.sourceSpan,
@ -486,14 +488,14 @@ class TemplateParseVisitor implements HtmlAstVisitor {
} else if (isPresent(bindParts[3])) { // match: let-name } else if (isPresent(bindParts[3])) { // match: let-name
if (isTemplateElement) { if (isTemplateElement) {
var identifier = bindParts[8]; const identifier = bindParts[8];
this._parseVariable(identifier, attrValue, attr.sourceSpan, targetVars); this._parseVariable(identifier, attrValue, attr.sourceSpan, targetVars);
} else { } else {
this._reportError(`"let-" is only supported on template elements.`, attr.sourceSpan); this._reportError(`"let-" is only supported on template elements.`, attr.sourceSpan);
} }
} else if (isPresent(bindParts[4])) { // match: ref- / #iden } else if (isPresent(bindParts[4])) { // match: ref- / #iden
var identifier = bindParts[8]; const identifier = bindParts[8];
this._parseReference(identifier, attrValue, attr.sourceSpan, targetRefs); this._parseReference(identifier, attrValue, attr.sourceSpan, targetRefs);
} else if (isPresent(bindParts[5])) { // match: on-event } else if (isPresent(bindParts[5])) { // match: on-event
@ -587,7 +589,7 @@ class TemplateParseVisitor implements HtmlAstVisitor {
if (!isPresent(expression) || expression.length == 0) { if (!isPresent(expression) || expression.length == 0) {
expression = 'null'; expression = 'null';
} }
var ast = this._parseBinding(expression, sourceSpan); const ast = this._parseBinding(expression, sourceSpan);
targetMatchableAttrs.push([name, ast.source]); targetMatchableAttrs.push([name, ast.source]);
targetAnimationProps.push(new BoundElementPropertyAst( targetAnimationProps.push(new BoundElementPropertyAst(
name, PropertyBindingType.Animation, SecurityContext.NONE, ast, null, sourceSpan)); name, PropertyBindingType.Animation, SecurityContext.NONE, ast, null, sourceSpan));
@ -596,7 +598,7 @@ class TemplateParseVisitor implements HtmlAstVisitor {
private _parsePropertyInterpolation( private _parsePropertyInterpolation(
name: string, value: string, sourceSpan: ParseSourceSpan, targetMatchableAttrs: string[][], name: string, value: string, sourceSpan: ParseSourceSpan, targetMatchableAttrs: string[][],
targetProps: BoundElementOrDirectiveProperty[]): boolean { targetProps: BoundElementOrDirectiveProperty[]): boolean {
var expr = this._parseInterpolation(value, sourceSpan); const expr = this._parseInterpolation(value, sourceSpan);
if (isPresent(expr)) { if (isPresent(expr)) {
this._parsePropertyAst(name, expr, sourceSpan, targetMatchableAttrs, targetProps); this._parsePropertyAst(name, expr, sourceSpan, targetMatchableAttrs, targetProps);
return true; return true;
@ -622,10 +624,10 @@ class TemplateParseVisitor implements HtmlAstVisitor {
name: string, expression: string, sourceSpan: ParseSourceSpan, name: string, expression: string, sourceSpan: ParseSourceSpan,
targetMatchableAttrs: string[][], targetEvents: BoundEventAst[]) { targetMatchableAttrs: string[][], targetEvents: BoundEventAst[]) {
// long format: 'target: eventName' // long format: 'target: eventName'
var parts = splitAtColon(name, [null, name]); const parts = splitAtColon(name, [null, name]);
var target = parts[0]; const target = parts[0];
var eventName = parts[1]; const eventName = parts[1];
var ast = this._parseAction(expression, sourceSpan); const ast = this._parseAction(expression, sourceSpan);
targetMatchableAttrs.push([name, ast.source]); targetMatchableAttrs.push([name, ast.source]);
targetEvents.push(new BoundEventAst(eventName, target, ast, sourceSpan)); targetEvents.push(new BoundEventAst(eventName, target, ast, sourceSpan));
// Don't detect directives for event names for now, // Don't detect directives for event names for now,
@ -644,7 +646,7 @@ class TemplateParseVisitor implements HtmlAstVisitor {
// Need to sort the directives so that we get consistent results throughout, // Need to sort the directives so that we get consistent results throughout,
// as selectorMatcher uses Maps inside. // as selectorMatcher uses Maps inside.
// Also dedupe directives as they might match more than one time! // Also dedupe directives as they might match more than one time!
var directives = ListWrapper.createFixedSize(this.directivesIndex.size); const directives = ListWrapper.createFixedSize(this.directivesIndex.size);
selectorMatcher.match(elementCssSelector, (selector, directive) => { selectorMatcher.match(elementCssSelector, (selector, directive) => {
directives[this.directivesIndex.get(directive)] = directive; directives[this.directivesIndex.get(directive)] = directive;
}); });
@ -655,15 +657,15 @@ class TemplateParseVisitor implements HtmlAstVisitor {
isTemplateElement: boolean, elementName: string, directives: CompileDirectiveMetadata[], isTemplateElement: boolean, elementName: string, directives: CompileDirectiveMetadata[],
props: BoundElementOrDirectiveProperty[], elementOrDirectiveRefs: ElementOrDirectiveRef[], props: BoundElementOrDirectiveProperty[], elementOrDirectiveRefs: ElementOrDirectiveRef[],
sourceSpan: ParseSourceSpan, targetReferences: ReferenceAst[]): DirectiveAst[] { sourceSpan: ParseSourceSpan, targetReferences: ReferenceAst[]): DirectiveAst[] {
var matchedReferences = new Set<string>(); const matchedReferences = new Set<string>();
var component: CompileDirectiveMetadata = null; let component: CompileDirectiveMetadata = null;
var directiveAsts = directives.map((directive: CompileDirectiveMetadata) => { const directiveAsts = directives.map((directive: CompileDirectiveMetadata) => {
if (directive.isComponent) { if (directive.isComponent) {
component = directive; component = directive;
} }
var hostProperties: BoundElementPropertyAst[] = []; const hostProperties: BoundElementPropertyAst[] = [];
var hostEvents: BoundEventAst[] = []; const hostEvents: BoundEventAst[] = [];
var directiveProperties: BoundDirectivePropertyAst[] = []; const directiveProperties: BoundDirectivePropertyAst[] = [];
this._createDirectiveHostPropertyAsts( this._createDirectiveHostPropertyAsts(
elementName, directive.hostProperties, sourceSpan, hostProperties); elementName, directive.hostProperties, sourceSpan, hostProperties);
this._createDirectiveHostEventAsts(directive.hostListeners, sourceSpan, hostEvents); this._createDirectiveHostEventAsts(directive.hostListeners, sourceSpan, hostEvents);
@ -685,9 +687,9 @@ class TemplateParseVisitor implements HtmlAstVisitor {
this._reportError( this._reportError(
`There is no directive with "exportAs" set to "${elOrDirRef.value}"`, `There is no directive with "exportAs" set to "${elOrDirRef.value}"`,
elOrDirRef.sourceSpan); elOrDirRef.sourceSpan);
}; }
} else if (isBlank(component)) { } else if (isBlank(component)) {
var refToken: any /** TODO #???? */ = null; let refToken: CompileTokenMetadata = null;
if (isTemplateElement) { if (isTemplateElement) {
refToken = identifierToken(Identifiers.TemplateRef); refToken = identifierToken(Identifiers.TemplateRef);
} }
@ -702,7 +704,7 @@ class TemplateParseVisitor implements HtmlAstVisitor {
targetPropertyAsts: BoundElementPropertyAst[]) { targetPropertyAsts: BoundElementPropertyAst[]) {
if (isPresent(hostProps)) { if (isPresent(hostProps)) {
StringMapWrapper.forEach(hostProps, (expression: string, propName: string) => { StringMapWrapper.forEach(hostProps, (expression: string, propName: string) => {
var exprAst = this._parseBinding(expression, sourceSpan); const exprAst = this._parseBinding(expression, sourceSpan);
targetPropertyAsts.push( targetPropertyAsts.push(
this._createElementPropertyAst(elementName, propName, exprAst, sourceSpan)); this._createElementPropertyAst(elementName, propName, exprAst, sourceSpan));
}); });
@ -723,9 +725,9 @@ class TemplateParseVisitor implements HtmlAstVisitor {
directiveProperties: {[key: string]: string}, boundProps: BoundElementOrDirectiveProperty[], directiveProperties: {[key: string]: string}, boundProps: BoundElementOrDirectiveProperty[],
targetBoundDirectiveProps: BoundDirectivePropertyAst[]) { targetBoundDirectiveProps: BoundDirectivePropertyAst[]) {
if (isPresent(directiveProperties)) { if (isPresent(directiveProperties)) {
var boundPropsByName = new Map<string, BoundElementOrDirectiveProperty>(); const boundPropsByName = new Map<string, BoundElementOrDirectiveProperty>();
boundProps.forEach(boundProp => { boundProps.forEach(boundProp => {
var prevValue = boundPropsByName.get(boundProp.name); const prevValue = boundPropsByName.get(boundProp.name);
if (isBlank(prevValue) || prevValue.isLiteral) { if (isBlank(prevValue) || prevValue.isLiteral) {
// give [a]="b" a higher precedence than a="b" on the same element // give [a]="b" a higher precedence than a="b" on the same element
boundPropsByName.set(boundProp.name, boundProp); boundPropsByName.set(boundProp.name, boundProp);
@ -733,7 +735,7 @@ class TemplateParseVisitor implements HtmlAstVisitor {
}); });
StringMapWrapper.forEach(directiveProperties, (elProp: string, dirProp: string) => { StringMapWrapper.forEach(directiveProperties, (elProp: string, dirProp: string) => {
var boundProp = boundPropsByName.get(elProp); const boundProp = boundPropsByName.get(elProp);
// Bindings are optional, so this binding only needs to be set up if an expression is given. // Bindings are optional, so this binding only needs to be set up if an expression is given.
if (isPresent(boundProp)) { if (isPresent(boundProp)) {
@ -747,8 +749,8 @@ class TemplateParseVisitor implements HtmlAstVisitor {
private _createElementPropertyAsts( private _createElementPropertyAsts(
elementName: string, props: BoundElementOrDirectiveProperty[], elementName: string, props: BoundElementOrDirectiveProperty[],
directives: DirectiveAst[]): BoundElementPropertyAst[] { directives: DirectiveAst[]): BoundElementPropertyAst[] {
var boundElementProps: BoundElementPropertyAst[] = []; const boundElementProps: BoundElementPropertyAst[] = [];
var boundDirectivePropsIndex = new Map<string, BoundDirectivePropertyAst>(); const boundDirectivePropsIndex = new Map<string, BoundDirectivePropertyAst>();
directives.forEach((directive: DirectiveAst) => { directives.forEach((directive: DirectiveAst) => {
directive.inputs.forEach((prop: BoundDirectivePropertyAst) => { directive.inputs.forEach((prop: BoundDirectivePropertyAst) => {
boundDirectivePropsIndex.set(prop.templateName, prop); boundDirectivePropsIndex.set(prop.templateName, prop);
@ -766,10 +768,10 @@ class TemplateParseVisitor implements HtmlAstVisitor {
private _createElementPropertyAst( private _createElementPropertyAst(
elementName: string, name: string, ast: AST, elementName: string, name: string, ast: AST,
sourceSpan: ParseSourceSpan): BoundElementPropertyAst { sourceSpan: ParseSourceSpan): BoundElementPropertyAst {
var unit: any /** TODO #???? */ = null; let unit: string = null;
var bindingType: any /** TODO #???? */; let bindingType: PropertyBindingType;
var boundPropertyName: string; let boundPropertyName: string;
var parts = name.split(PROPERTY_PARTS_SEPARATOR); const parts = name.split(PROPERTY_PARTS_SEPARATOR);
let securityContext: SecurityContext; let securityContext: SecurityContext;
if (parts.length === 1) { if (parts.length === 1) {
boundPropertyName = this._schemaRegistry.getMappedPropName(parts[0]); boundPropertyName = this._schemaRegistry.getMappedPropName(parts[0]);
@ -822,9 +824,9 @@ class TemplateParseVisitor implements HtmlAstVisitor {
private _findComponentDirectiveNames(directives: DirectiveAst[]): string[] { private _findComponentDirectiveNames(directives: DirectiveAst[]): string[] {
var componentTypeNames: string[] = []; const componentTypeNames: string[] = [];
directives.forEach(directive => { directives.forEach(directive => {
var typeName = directive.directive.type.name; const typeName = directive.directive.type.name;
if (directive.directive.isComponent) { if (directive.directive.isComponent) {
componentTypeNames.push(typeName); componentTypeNames.push(typeName);
} }
@ -833,7 +835,7 @@ class TemplateParseVisitor implements HtmlAstVisitor {
} }
private _assertOnlyOneComponent(directives: DirectiveAst[], sourceSpan: ParseSourceSpan) { private _assertOnlyOneComponent(directives: DirectiveAst[], sourceSpan: ParseSourceSpan) {
var componentTypeNames = this._findComponentDirectiveNames(directives); const componentTypeNames = this._findComponentDirectiveNames(directives);
if (componentTypeNames.length > 1) { if (componentTypeNames.length > 1) {
this._reportError(`More than one component: ${componentTypeNames.join(',')}`, sourceSpan); this._reportError(`More than one component: ${componentTypeNames.join(',')}`, sourceSpan);
} }
@ -842,7 +844,7 @@ class TemplateParseVisitor implements HtmlAstVisitor {
private _assertNoComponentsNorElementBindingsOnTemplate( private _assertNoComponentsNorElementBindingsOnTemplate(
directives: DirectiveAst[], elementProps: BoundElementPropertyAst[], directives: DirectiveAst[], elementProps: BoundElementPropertyAst[],
sourceSpan: ParseSourceSpan) { sourceSpan: ParseSourceSpan) {
var componentTypeNames: string[] = this._findComponentDirectiveNames(directives); const componentTypeNames: string[] = this._findComponentDirectiveNames(directives);
if (componentTypeNames.length > 0) { if (componentTypeNames.length > 0) {
this._reportError( this._reportError(
`Components on an embedded template: ${componentTypeNames.join(',')}`, sourceSpan); `Components on an embedded template: ${componentTypeNames.join(',')}`, sourceSpan);
@ -856,7 +858,7 @@ class TemplateParseVisitor implements HtmlAstVisitor {
private _assertAllEventsPublishedByDirectives( private _assertAllEventsPublishedByDirectives(
directives: DirectiveAst[], events: BoundEventAst[]) { directives: DirectiveAst[], events: BoundEventAst[]) {
var allDirectiveEvents = new Set<string>(); const allDirectiveEvents = new Set<string>();
directives.forEach(directive => { directives.forEach(directive => {
StringMapWrapper.forEach(directive.directive.outputs, (eventName: string) => { StringMapWrapper.forEach(directive.directive.outputs, (eventName: string) => {
allDirectiveEvents.add(eventName); allDirectiveEvents.add(eventName);
@ -874,7 +876,7 @@ class TemplateParseVisitor implements HtmlAstVisitor {
class NonBindableVisitor implements HtmlAstVisitor { class NonBindableVisitor implements HtmlAstVisitor {
visitElement(ast: HtmlElementAst, parent: ElementContext): ElementAst { visitElement(ast: HtmlElementAst, parent: ElementContext): ElementAst {
var preparsedElement = preparseElement(ast); const preparsedElement = preparseElement(ast);
if (preparsedElement.type === PreparsedElementType.SCRIPT || if (preparsedElement.type === PreparsedElementType.SCRIPT ||
preparsedElement.type === PreparsedElementType.STYLE || preparsedElement.type === PreparsedElementType.STYLE ||
preparsedElement.type === PreparsedElementType.STYLESHEET) { preparsedElement.type === PreparsedElementType.STYLESHEET) {
@ -884,10 +886,10 @@ class NonBindableVisitor implements HtmlAstVisitor {
return null; return null;
} }
var attrNameAndValues = ast.attrs.map(attrAst => [attrAst.name, attrAst.value]); const attrNameAndValues = ast.attrs.map(attrAst => [attrAst.name, attrAst.value]);
var selector = createElementCssSelector(ast.name, attrNameAndValues); const selector = createElementCssSelector(ast.name, attrNameAndValues);
var ngContentIndex = parent.findNgContentIndex(selector); const ngContentIndex = parent.findNgContentIndex(selector);
var children = htmlVisitAll(this, ast.children, EMPTY_ELEMENT_CONTEXT); const children = htmlVisitAll(this, ast.children, EMPTY_ELEMENT_CONTEXT);
return new ElementAst( return new ElementAst(
ast.name, htmlVisitAll(this, ast.attrs), [], [], [], [], [], false, children, ast.name, htmlVisitAll(this, ast.attrs), [], [], [], [], [], false, children,
ngContentIndex, ast.sourceSpan); ngContentIndex, ast.sourceSpan);
@ -897,7 +899,7 @@ class NonBindableVisitor implements HtmlAstVisitor {
return new AttrAst(ast.name, ast.value, ast.sourceSpan); return new AttrAst(ast.name, ast.value, ast.sourceSpan);
} }
visitText(ast: HtmlTextAst, parent: ElementContext): TextAst { visitText(ast: HtmlTextAst, parent: ElementContext): TextAst {
var ngContentIndex = parent.findNgContentIndex(TEXT_CSS_SELECTOR); const ngContentIndex = parent.findNgContentIndex(TEXT_CSS_SELECTOR);
return new TextAst(ast.value, ngContentIndex, ast.sourceSpan); return new TextAst(ast.value, ngContentIndex, ast.sourceSpan);
} }
visitExpansion(ast: HtmlExpansionAst, context: any): any { return ast; } visitExpansion(ast: HtmlExpansionAst, context: any): any { return ast; }
@ -922,13 +924,13 @@ class ElementContext {
static create( static create(
isTemplateElement: boolean, directives: DirectiveAst[], isTemplateElement: boolean, directives: DirectiveAst[],
providerContext: ProviderElementContext): ElementContext { providerContext: ProviderElementContext): ElementContext {
var matcher = new SelectorMatcher(); const matcher = new SelectorMatcher();
var wildcardNgContentIndex: number = null; let wildcardNgContentIndex: number = null;
var component = directives.find(directive => directive.directive.isComponent); const component = directives.find(directive => directive.directive.isComponent);
if (isPresent(component)) { if (isPresent(component)) {
var ngContentSelectors = component.directive.template.ngContentSelectors; const ngContentSelectors = component.directive.template.ngContentSelectors;
for (var i = 0; i < ngContentSelectors.length; i++) { for (let i = 0; i < ngContentSelectors.length; i++) {
var selector = ngContentSelectors[i]; const selector = ngContentSelectors[i];
if (StringWrapper.equals(selector, '*')) { if (StringWrapper.equals(selector, '*')) {
wildcardNgContentIndex = i; wildcardNgContentIndex = i;
} else { } else {
@ -943,7 +945,7 @@ class ElementContext {
private _wildcardNgContentIndex: number, public providerContext: ProviderElementContext) {} private _wildcardNgContentIndex: number, public providerContext: ProviderElementContext) {}
findNgContentIndex(selector: CssSelector): number { findNgContentIndex(selector: CssSelector): number {
var ngContentIndices: any[] /** TODO #???? */ = []; const ngContentIndices: number[] = [];
this._ngContentIndexMatcher.match( this._ngContentIndexMatcher.match(
selector, (selector, ngContentIndex) => { ngContentIndices.push(ngContentIndex); }); selector, (selector, ngContentIndex) => { ngContentIndices.push(ngContentIndex); });
ListWrapper.sort(ngContentIndices); ListWrapper.sort(ngContentIndices);
@ -955,27 +957,27 @@ class ElementContext {
} }
function createElementCssSelector(elementName: string, matchableAttrs: string[][]): CssSelector { function createElementCssSelector(elementName: string, matchableAttrs: string[][]): CssSelector {
var cssSelector = new CssSelector(); const cssSelector = new CssSelector();
let elNameNoNs = splitNsName(elementName)[1]; let elNameNoNs = splitNsName(elementName)[1];
cssSelector.setElement(elNameNoNs); cssSelector.setElement(elNameNoNs);
for (var i = 0; i < matchableAttrs.length; i++) { for (let i = 0; i < matchableAttrs.length; i++) {
let attrName = matchableAttrs[i][0]; let attrName = matchableAttrs[i][0];
let attrNameNoNs = splitNsName(attrName)[1]; let attrNameNoNs = splitNsName(attrName)[1];
let attrValue = matchableAttrs[i][1]; let attrValue = matchableAttrs[i][1];
cssSelector.addAttribute(attrNameNoNs, attrValue); cssSelector.addAttribute(attrNameNoNs, attrValue);
if (attrName.toLowerCase() == CLASS_ATTR) { if (attrName.toLowerCase() == CLASS_ATTR) {
var classes = splitClasses(attrValue); const classes = splitClasses(attrValue);
classes.forEach(className => cssSelector.addClassName(className)); classes.forEach(className => cssSelector.addClassName(className));
} }
} }
return cssSelector; return cssSelector;
} }
var EMPTY_ELEMENT_CONTEXT = new ElementContext(true, new SelectorMatcher(), null, null); const EMPTY_ELEMENT_CONTEXT = new ElementContext(true, new SelectorMatcher(), null, null);
var NON_BINDABLE_VISITOR = new NonBindableVisitor(); const NON_BINDABLE_VISITOR = new NonBindableVisitor();
export class PipeCollector extends RecursiveAstVisitor { export class PipeCollector extends RecursiveAstVisitor {