UNPKG

@angular/compiler

Version:

Angular - the compiler library

685 lines • 129 kB
/** * @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 { identifierName } from '../compile_metadata'; import { ASTWithSource, EmptyExpr } from '../expression_parser/ast'; import { Identifiers, createTokenForExternalReference, createTokenForReference } from '../identifiers'; import * as html from '../ml_parser/ast'; import { ParseTreeResult } from '../ml_parser/html_parser'; import { removeWhitespaces, replaceNgsp } from '../ml_parser/html_whitespaces'; import { expandNodes } from '../ml_parser/icu_ast_expander'; import { InterpolationConfig } from '../ml_parser/interpolation_config'; import { isNgTemplate, splitNsName } from '../ml_parser/tags'; import { ParseError, ParseErrorLevel, ParseSourceSpan } from '../parse_util'; import { ProviderElementContext, ProviderViewContext } from '../provider_analyzer'; import { CssSelector, SelectorMatcher } from '../selector'; import { isStyleUrlResolvable } from '../style_url_resolver'; import { syntaxError } from '../util'; import { BindingParser } from './binding_parser'; import * as t from './template_ast'; import { PreparsedElementType, preparseElement } from './template_preparser'; const BIND_NAME_REGEXP = /^(?:(?:(?:(bind-)|(let-)|(ref-|#)|(on-)|(bindon-)|(@))(.+))|\[\(([^\)]+)\)\]|\[([^\]]+)\]|\(([^\)]+)\))$/; // Group 1 = "bind-" const KW_BIND_IDX = 1; // Group 2 = "let-" const KW_LET_IDX = 2; // Group 3 = "ref-/#" const KW_REF_IDX = 3; // Group 4 = "on-" const KW_ON_IDX = 4; // Group 5 = "bindon-" const KW_BINDON_IDX = 5; // Group 6 = "@" const KW_AT_IDX = 6; // Group 7 = the identifier after "bind-", "let-", "ref-/#", "on-", "bindon-" or "@" const IDENT_KW_IDX = 7; // Group 8 = identifier inside [()] const IDENT_BANANA_BOX_IDX = 8; // Group 9 = identifier inside [] const IDENT_PROPERTY_IDX = 9; // Group 10 = identifier inside () const IDENT_EVENT_IDX = 10; const TEMPLATE_ATTR_PREFIX = '*'; const CLASS_ATTR = 'class'; let _TEXT_CSS_SELECTOR; function TEXT_CSS_SELECTOR() { if (!_TEXT_CSS_SELECTOR) { _TEXT_CSS_SELECTOR = CssSelector.parse('*')[0]; } return _TEXT_CSS_SELECTOR; } export class TemplateParseError extends ParseError { constructor(message, span, level) { super(span, message, level); } } export class TemplateParseResult { constructor(templateAst, usedPipes, errors) { this.templateAst = templateAst; this.usedPipes = usedPipes; this.errors = errors; } } export class TemplateParser { constructor(_config, _reflector, _exprParser, _schemaRegistry, _htmlParser, _console, transforms) { this._config = _config; this._reflector = _reflector; this._exprParser = _exprParser; this._schemaRegistry = _schemaRegistry; this._htmlParser = _htmlParser; this._console = _console; this.transforms = transforms; } get expressionParser() { return this._exprParser; } parse(component, template, directives, pipes, schemas, templateUrl, preserveWhitespaces) { const result = this.tryParse(component, template, directives, pipes, schemas, templateUrl, preserveWhitespaces); const warnings = result.errors.filter(error => error.level === ParseErrorLevel.WARNING); const errors = result.errors.filter(error => error.level === ParseErrorLevel.ERROR); if (warnings.length > 0) { this._console.warn(`Template parse warnings:\n${warnings.join('\n')}`); } if (errors.length > 0) { const errorString = errors.join('\n'); throw syntaxError(`Template parse errors:\n${errorString}`, errors); } return { template: result.templateAst, pipes: result.usedPipes }; } tryParse(component, template, directives, pipes, schemas, templateUrl, preserveWhitespaces) { let htmlParseResult = typeof template === 'string' ? this._htmlParser.parse(template, templateUrl, true, this.getInterpolationConfig(component)) : template; if (!preserveWhitespaces) { htmlParseResult = removeWhitespaces(htmlParseResult); } return this.tryParseHtml(this.expandHtml(htmlParseResult), component, directives, pipes, schemas); } tryParseHtml(htmlAstWithErrors, component, directives, pipes, schemas) { let result; const errors = htmlAstWithErrors.errors; const usedPipes = []; if (htmlAstWithErrors.rootNodes.length > 0) { const uniqDirectives = removeSummaryDuplicates(directives); const uniqPipes = removeSummaryDuplicates(pipes); const providerViewContext = new ProviderViewContext(this._reflector, component); let interpolationConfig = undefined; if (component.template && component.template.interpolation) { interpolationConfig = { start: component.template.interpolation[0], end: component.template.interpolation[1] }; } const bindingParser = new BindingParser(this._exprParser, interpolationConfig, this._schemaRegistry, uniqPipes, errors); const parseVisitor = new TemplateParseVisitor(this._reflector, this._config, providerViewContext, uniqDirectives, bindingParser, this._schemaRegistry, schemas, errors); result = html.visitAll(parseVisitor, htmlAstWithErrors.rootNodes, EMPTY_ELEMENT_CONTEXT); errors.push(...providerViewContext.errors); usedPipes.push(...bindingParser.getUsedPipes()); } else { result = []; } this._assertNoReferenceDuplicationOnTemplate(result, errors); if (errors.length > 0) { return new TemplateParseResult(result, usedPipes, errors); } if (this.transforms) { this.transforms.forEach((transform) => { result = t.templateVisitAll(transform, result); }); } return new TemplateParseResult(result, usedPipes, errors); } expandHtml(htmlAstWithErrors, forced = false) { const errors = htmlAstWithErrors.errors; if (errors.length == 0 || forced) { // Transform ICU messages to angular directives const expandedHtmlAst = expandNodes(htmlAstWithErrors.rootNodes); errors.push(...expandedHtmlAst.errors); htmlAstWithErrors = new ParseTreeResult(expandedHtmlAst.nodes, errors); } return htmlAstWithErrors; } getInterpolationConfig(component) { if (component.template) { return InterpolationConfig.fromArray(component.template.interpolation); } return undefined; } /** @internal */ _assertNoReferenceDuplicationOnTemplate(result, errors) { const existingReferences = []; result.filter(element => !!element.references) .forEach(element => element.references.forEach((reference) => { const name = reference.name; if (existingReferences.indexOf(name) < 0) { existingReferences.push(name); } else { const error = new TemplateParseError(`Reference "#${name}" is defined several times`, reference.sourceSpan, ParseErrorLevel.ERROR); errors.push(error); } })); } } class TemplateParseVisitor { constructor(reflector, config, providerViewContext, directives, _bindingParser, _schemaRegistry, _schemas, _targetErrors) { this.reflector = reflector; this.config = config; this.providerViewContext = providerViewContext; this._bindingParser = _bindingParser; this._schemaRegistry = _schemaRegistry; this._schemas = _schemas; this._targetErrors = _targetErrors; this.selectorMatcher = new SelectorMatcher(); this.directivesIndex = new Map(); this.ngContentCount = 0; // Note: queries start with id 1 so we can use the number in a Bloom filter! this.contentQueryStartId = providerViewContext.component.viewQueries.length + 1; directives.forEach((directive, index) => { const selector = CssSelector.parse(directive.selector); this.selectorMatcher.addSelectables(selector, directive); this.directivesIndex.set(directive, index); }); } visitExpansion(expansion, context) { return null; } visitExpansionCase(expansionCase, context) { return null; } visitText(text, parent) { const ngContentIndex = parent.findNgContentIndex(TEXT_CSS_SELECTOR()); const valueNoNgsp = replaceNgsp(text.value); const expr = this._bindingParser.parseInterpolation(valueNoNgsp, text.sourceSpan); return expr ? new t.BoundTextAst(expr, ngContentIndex, text.sourceSpan) : new t.TextAst(valueNoNgsp, ngContentIndex, text.sourceSpan); } visitAttribute(attribute, context) { return new t.AttrAst(attribute.name, attribute.value, attribute.sourceSpan); } visitComment(comment, context) { return null; } visitElement(element, parent) { const queryStartIndex = this.contentQueryStartId; const elName = element.name; const preparsedElement = preparseElement(element); if (preparsedElement.type === PreparsedElementType.SCRIPT || preparsedElement.type === PreparsedElementType.STYLE) { // Skipping <script> for security reasons // Skipping <style> as we already processed them // in the StyleCompiler return null; } if (preparsedElement.type === PreparsedElementType.STYLESHEET && isStyleUrlResolvable(preparsedElement.hrefAttr)) { // Skipping stylesheets with either relative urls or package scheme as we already processed // them in the StyleCompiler return null; } const matchableAttrs = []; const elementOrDirectiveProps = []; const elementOrDirectiveRefs = []; const elementVars = []; const events = []; const templateElementOrDirectiveProps = []; const templateMatchableAttrs = []; const templateElementVars = []; let hasInlineTemplates = false; const attrs = []; const isTemplateElement = isNgTemplate(element.name); element.attrs.forEach(attr => { const parsedVariables = []; const hasBinding = this._parseAttr(isTemplateElement, attr, matchableAttrs, elementOrDirectiveProps, events, elementOrDirectiveRefs, elementVars); elementVars.push(...parsedVariables.map(v => t.VariableAst.fromParsedVariable(v))); let templateValue; let templateKey; const normalizedName = this._normalizeAttributeName(attr.name); if (normalizedName.startsWith(TEMPLATE_ATTR_PREFIX)) { templateValue = attr.value; templateKey = normalizedName.substring(TEMPLATE_ATTR_PREFIX.length); } const hasTemplateBinding = templateValue != null; if (hasTemplateBinding) { if (hasInlineTemplates) { this._reportError(`Can't have multiple template bindings on one element. Use only one attribute prefixed with *`, attr.sourceSpan); } hasInlineTemplates = true; const parsedVariables = []; this._bindingParser.parseInlineTemplateBinding(templateKey, templateValue, attr.sourceSpan, templateMatchableAttrs, templateElementOrDirectiveProps, parsedVariables); templateElementVars.push(...parsedVariables.map(v => t.VariableAst.fromParsedVariable(v))); } if (!hasBinding && !hasTemplateBinding) { // don't include the bindings as attributes as well in the AST attrs.push(this.visitAttribute(attr, null)); matchableAttrs.push([attr.name, attr.value]); } }); const elementCssSelector = createElementCssSelector(elName, matchableAttrs); const { directives: directiveMetas, matchElement } = this._parseDirectives(this.selectorMatcher, elementCssSelector); const references = []; const boundDirectivePropNames = new Set(); const directiveAsts = this._createDirectiveAsts(isTemplateElement, element.name, directiveMetas, elementOrDirectiveProps, elementOrDirectiveRefs, element.sourceSpan, references, boundDirectivePropNames); const elementProps = this._createElementPropertyAsts(element.name, elementOrDirectiveProps, boundDirectivePropNames); const isViewRoot = parent.isTemplateElement || hasInlineTemplates; const providerContext = new ProviderElementContext(this.providerViewContext, parent.providerContext, isViewRoot, directiveAsts, attrs, references, isTemplateElement, queryStartIndex, element.sourceSpan); const children = html.visitAll(preparsedElement.nonBindable ? NON_BINDABLE_VISITOR : this, element.children, ElementContext.create(isTemplateElement, directiveAsts, isTemplateElement ? parent.providerContext : providerContext)); providerContext.afterElement(); // Override the actual selector when the `ngProjectAs` attribute is provided const projectionSelector = preparsedElement.projectAs != '' ? CssSelector.parse(preparsedElement.projectAs)[0] : elementCssSelector; const ngContentIndex = parent.findNgContentIndex(projectionSelector); let parsedElement; if (preparsedElement.type === PreparsedElementType.NG_CONTENT) { // `<ng-content>` element if (element.children && !element.children.every(_isEmptyTextNode)) { this._reportError(`<ng-content> element cannot have content.`, element.sourceSpan); } parsedElement = new t.NgContentAst(this.ngContentCount++, hasInlineTemplates ? null : ngContentIndex, element.sourceSpan); } else if (isTemplateElement) { // `<ng-template>` element this._assertAllEventsPublishedByDirectives(directiveAsts, events); this._assertNoComponentsNorElementBindingsOnTemplate(directiveAsts, elementProps, element.sourceSpan); parsedElement = new t.EmbeddedTemplateAst(attrs, events, references, elementVars, providerContext.transformedDirectiveAsts, providerContext.transformProviders, providerContext.transformedHasViewContainer, providerContext.queryMatches, children, hasInlineTemplates ? null : ngContentIndex, element.sourceSpan); } else { // element other than `<ng-content>` and `<ng-template>` this._assertElementExists(matchElement, element); this._assertOnlyOneComponent(directiveAsts, element.sourceSpan); const ngContentIndex = hasInlineTemplates ? null : parent.findNgContentIndex(projectionSelector); parsedElement = new t.ElementAst(elName, attrs, elementProps, events, references, providerContext.transformedDirectiveAsts, providerContext.transformProviders, providerContext.transformedHasViewContainer, providerContext.queryMatches, children, hasInlineTemplates ? null : ngContentIndex, element.sourceSpan, element.endSourceSpan || null); } if (hasInlineTemplates) { // The element as a *-attribute const templateQueryStartIndex = this.contentQueryStartId; const templateSelector = createElementCssSelector('ng-template', templateMatchableAttrs); const { directives } = this._parseDirectives(this.selectorMatcher, templateSelector); const templateBoundDirectivePropNames = new Set(); const templateDirectiveAsts = this._createDirectiveAsts(true, elName, directives, templateElementOrDirectiveProps, [], element.sourceSpan, [], templateBoundDirectivePropNames); const templateElementProps = this._createElementPropertyAsts(elName, templateElementOrDirectiveProps, templateBoundDirectivePropNames); this._assertNoComponentsNorElementBindingsOnTemplate(templateDirectiveAsts, templateElementProps, element.sourceSpan); const templateProviderContext = new ProviderElementContext(this.providerViewContext, parent.providerContext, parent.isTemplateElement, templateDirectiveAsts, [], [], true, templateQueryStartIndex, element.sourceSpan); templateProviderContext.afterElement(); parsedElement = new t.EmbeddedTemplateAst([], [], [], templateElementVars, templateProviderContext.transformedDirectiveAsts, templateProviderContext.transformProviders, templateProviderContext.transformedHasViewContainer, templateProviderContext.queryMatches, [parsedElement], ngContentIndex, element.sourceSpan); } return parsedElement; } _parseAttr(isTemplateElement, attr, targetMatchableAttrs, targetProps, targetEvents, targetRefs, targetVars) { const name = this._normalizeAttributeName(attr.name); const value = attr.value; const srcSpan = attr.sourceSpan; const boundEvents = []; const bindParts = name.match(BIND_NAME_REGEXP); let hasBinding = false; if (bindParts !== null) { hasBinding = true; if (bindParts[KW_BIND_IDX] != null) { this._bindingParser.parsePropertyBinding(bindParts[IDENT_KW_IDX], value, false, srcSpan, targetMatchableAttrs, targetProps); } else if (bindParts[KW_LET_IDX]) { if (isTemplateElement) { const identifier = bindParts[IDENT_KW_IDX]; this._parseVariable(identifier, value, srcSpan, targetVars); } else { this._reportError(`"let-" is only supported on ng-template elements.`, srcSpan); } } else if (bindParts[KW_REF_IDX]) { const identifier = bindParts[IDENT_KW_IDX]; this._parseReference(identifier, value, srcSpan, targetRefs); } else if (bindParts[KW_ON_IDX]) { this._bindingParser.parseEvent(bindParts[IDENT_KW_IDX], value, srcSpan, targetMatchableAttrs, boundEvents); } else if (bindParts[KW_BINDON_IDX]) { this._bindingParser.parsePropertyBinding(bindParts[IDENT_KW_IDX], value, false, srcSpan, targetMatchableAttrs, targetProps); this._parseAssignmentEvent(bindParts[IDENT_KW_IDX], value, srcSpan, targetMatchableAttrs, boundEvents); } else if (bindParts[KW_AT_IDX]) { this._bindingParser.parseLiteralAttr(name, value, srcSpan, targetMatchableAttrs, targetProps); } else if (bindParts[IDENT_BANANA_BOX_IDX]) { this._bindingParser.parsePropertyBinding(bindParts[IDENT_BANANA_BOX_IDX], value, false, srcSpan, targetMatchableAttrs, targetProps); this._parseAssignmentEvent(bindParts[IDENT_BANANA_BOX_IDX], value, srcSpan, targetMatchableAttrs, boundEvents); } else if (bindParts[IDENT_PROPERTY_IDX]) { this._bindingParser.parsePropertyBinding(bindParts[IDENT_PROPERTY_IDX], value, false, srcSpan, targetMatchableAttrs, targetProps); } else if (bindParts[IDENT_EVENT_IDX]) { this._bindingParser.parseEvent(bindParts[IDENT_EVENT_IDX], value, srcSpan, targetMatchableAttrs, boundEvents); } } else { hasBinding = this._bindingParser.parsePropertyInterpolation(name, value, srcSpan, targetMatchableAttrs, targetProps); } if (!hasBinding) { this._bindingParser.parseLiteralAttr(name, value, srcSpan, targetMatchableAttrs, targetProps); } targetEvents.push(...boundEvents.map(e => t.BoundEventAst.fromParsedEvent(e))); return hasBinding; } _normalizeAttributeName(attrName) { return /^data-/i.test(attrName) ? attrName.substring(5) : attrName; } _parseVariable(identifier, value, sourceSpan, targetVars) { if (identifier.indexOf('-') > -1) { this._reportError(`"-" is not allowed in variable names`, sourceSpan); } targetVars.push(new t.VariableAst(identifier, value, sourceSpan)); } _parseReference(identifier, value, sourceSpan, targetRefs) { if (identifier.indexOf('-') > -1) { this._reportError(`"-" is not allowed in reference names`, sourceSpan); } targetRefs.push(new ElementOrDirectiveRef(identifier, value, sourceSpan)); } _parseAssignmentEvent(name, expression, sourceSpan, targetMatchableAttrs, targetEvents) { this._bindingParser.parseEvent(`${name}Change`, `${expression}=$event`, sourceSpan, targetMatchableAttrs, targetEvents); } _parseDirectives(selectorMatcher, elementCssSelector) { // Need to sort the directives so that we get consistent results throughout, // as selectorMatcher uses Maps inside. // Also deduplicate directives as they might match more than one time! const directives = new Array(this.directivesIndex.size); // Whether any directive selector matches on the element name let matchElement = false; selectorMatcher.match(elementCssSelector, (selector, directive) => { directives[this.directivesIndex.get(directive)] = directive; matchElement = matchElement || selector.hasElementSelector(); }); return { directives: directives.filter(dir => !!dir), matchElement, }; } _createDirectiveAsts(isTemplateElement, elementName, directives, props, elementOrDirectiveRefs, elementSourceSpan, targetReferences, targetBoundDirectivePropNames) { const matchedReferences = new Set(); let component = null; const directiveAsts = directives.map((directive) => { const sourceSpan = new ParseSourceSpan(elementSourceSpan.start, elementSourceSpan.end, `Directive ${identifierName(directive.type)}`); if (directive.isComponent) { component = directive; } const directiveProperties = []; const boundProperties = this._bindingParser.createDirectiveHostPropertyAsts(directive, elementName, sourceSpan); let hostProperties = boundProperties.map(prop => t.BoundElementPropertyAst.fromBoundProperty(prop)); // Note: We need to check the host properties here as well, // as we don't know the element name in the DirectiveWrapperCompiler yet. hostProperties = this._checkPropertiesInSchema(elementName, hostProperties); const parsedEvents = this._bindingParser.createDirectiveHostEventAsts(directive, sourceSpan); this._createDirectivePropertyAsts(directive.inputs, props, directiveProperties, targetBoundDirectivePropNames); elementOrDirectiveRefs.forEach((elOrDirRef) => { if ((elOrDirRef.value.length === 0 && directive.isComponent) || (elOrDirRef.isReferenceToDirective(directive))) { targetReferences.push(new t.ReferenceAst(elOrDirRef.name, createTokenForReference(directive.type.reference), elOrDirRef.value, elOrDirRef.sourceSpan)); matchedReferences.add(elOrDirRef.name); } }); const hostEvents = parsedEvents.map(e => t.BoundEventAst.fromParsedEvent(e)); const contentQueryStartId = this.contentQueryStartId; this.contentQueryStartId += directive.queries.length; return new t.DirectiveAst(directive, directiveProperties, hostProperties, hostEvents, contentQueryStartId, sourceSpan); }); elementOrDirectiveRefs.forEach((elOrDirRef) => { if (elOrDirRef.value.length > 0) { if (!matchedReferences.has(elOrDirRef.name)) { this._reportError(`There is no directive with "exportAs" set to "${elOrDirRef.value}"`, elOrDirRef.sourceSpan); } } else if (!component) { let refToken = null; if (isTemplateElement) { refToken = createTokenForExternalReference(this.reflector, Identifiers.TemplateRef); } targetReferences.push(new t.ReferenceAst(elOrDirRef.name, refToken, elOrDirRef.value, elOrDirRef.sourceSpan)); } }); return directiveAsts; } _createDirectivePropertyAsts(directiveProperties, boundProps, targetBoundDirectiveProps, targetBoundDirectivePropNames) { if (directiveProperties) { const boundPropsByName = new Map(); boundProps.forEach(boundProp => { const prevValue = boundPropsByName.get(boundProp.name); if (!prevValue || prevValue.isLiteral) { // give [a]="b" a higher precedence than a="b" on the same element boundPropsByName.set(boundProp.name, boundProp); } }); Object.keys(directiveProperties).forEach(dirProp => { const elProp = directiveProperties[dirProp]; const boundProp = boundPropsByName.get(elProp); // Bindings are optional, so this binding only needs to be set up if an expression is given. if (boundProp) { targetBoundDirectivePropNames.add(boundProp.name); if (!isEmptyExpression(boundProp.expression)) { targetBoundDirectiveProps.push(new t.BoundDirectivePropertyAst(dirProp, boundProp.name, boundProp.expression, boundProp.sourceSpan)); } } }); } } _createElementPropertyAsts(elementName, props, boundDirectivePropNames) { const boundElementProps = []; props.forEach((prop) => { if (!prop.isLiteral && !boundDirectivePropNames.has(prop.name)) { const boundProp = this._bindingParser.createBoundElementProperty(elementName, prop); boundElementProps.push(t.BoundElementPropertyAst.fromBoundProperty(boundProp)); } }); return this._checkPropertiesInSchema(elementName, boundElementProps); } _findComponentDirectives(directives) { return directives.filter(directive => directive.directive.isComponent); } _findComponentDirectiveNames(directives) { return this._findComponentDirectives(directives) .map(directive => identifierName(directive.directive.type)); } _assertOnlyOneComponent(directives, sourceSpan) { const componentTypeNames = this._findComponentDirectiveNames(directives); if (componentTypeNames.length > 1) { this._reportError(`More than one component matched on this element.\n` + `Make sure that only one component's selector can match a given element.\n` + `Conflicting components: ${componentTypeNames.join(',')}`, sourceSpan); } } /** * Make sure that non-angular tags conform to the schemas. * * Note: An element is considered an angular tag when at least one directive selector matches the * tag name. * * @param matchElement Whether any directive has matched on the tag name * @param element the html element */ _assertElementExists(matchElement, element) { const elName = element.name.replace(/^:xhtml:/, ''); if (!matchElement && !this._schemaRegistry.hasElement(elName, this._schemas)) { let errorMsg = `'${elName}' is not a known element:\n`; errorMsg += `1. If '${elName}' is an Angular component, then verify that it is part of this module.\n`; if (elName.indexOf('-') > -1) { errorMsg += `2. If '${elName}' is a Web Component then add 'CUSTOM_ELEMENTS_SCHEMA' to the '@NgModule.schemas' of this component to suppress this message.`; } else { errorMsg += `2. To allow any element add 'NO_ERRORS_SCHEMA' to the '@NgModule.schemas' of this component.`; } this._reportError(errorMsg, element.sourceSpan); } } _assertNoComponentsNorElementBindingsOnTemplate(directives, elementProps, sourceSpan) { const componentTypeNames = this._findComponentDirectiveNames(directives); if (componentTypeNames.length > 0) { this._reportError(`Components on an embedded template: ${componentTypeNames.join(',')}`, sourceSpan); } elementProps.forEach(prop => { this._reportError(`Property binding ${prop.name} not used by any directive on an embedded template. Make sure that the property name is spelled correctly and all directives are listed in the "@NgModule.declarations".`, sourceSpan); }); } _assertAllEventsPublishedByDirectives(directives, events) { const allDirectiveEvents = new Set(); directives.forEach(directive => { Object.keys(directive.directive.outputs).forEach(k => { const eventName = directive.directive.outputs[k]; allDirectiveEvents.add(eventName); }); }); events.forEach(event => { if (event.target != null || !allDirectiveEvents.has(event.name)) { this._reportError(`Event binding ${event.fullName} not emitted by any directive on an embedded template. Make sure that the event name is spelled correctly and all directives are listed in the "@NgModule.declarations".`, event.sourceSpan); } }); } _checkPropertiesInSchema(elementName, boundProps) { // Note: We can't filter out empty expressions before this method, // as we still want to validate them! return boundProps.filter((boundProp) => { if (boundProp.type === 0 /* Property */ && !this._schemaRegistry.hasProperty(elementName, boundProp.name, this._schemas)) { let errorMsg = `Can't bind to '${boundProp.name}' since it isn't a known property of '${elementName}'.`; if (elementName.startsWith('ng-')) { errorMsg += `\n1. If '${boundProp.name}' is an Angular directive, then add 'CommonModule' to the '@NgModule.imports' of this component.` + `\n2. To allow any property add 'NO_ERRORS_SCHEMA' to the '@NgModule.schemas' of this component.`; } else if (elementName.indexOf('-') > -1) { errorMsg += `\n1. If '${elementName}' is an Angular component and it has '${boundProp.name}' input, then verify that it is part of this module.` + `\n2. If '${elementName}' is a Web Component then add 'CUSTOM_ELEMENTS_SCHEMA' to the '@NgModule.schemas' of this component to suppress this message.` + `\n3. To allow any property add 'NO_ERRORS_SCHEMA' to the '@NgModule.schemas' of this component.`; } this._reportError(errorMsg, boundProp.sourceSpan); } return !isEmptyExpression(boundProp.value); }); } _reportError(message, sourceSpan, level = ParseErrorLevel.ERROR) { this._targetErrors.push(new ParseError(sourceSpan, message, level)); } } class NonBindableVisitor { visitElement(ast, parent) { const preparsedElement = preparseElement(ast); if (preparsedElement.type === PreparsedElementType.SCRIPT || preparsedElement.type === PreparsedElementType.STYLE || preparsedElement.type === PreparsedElementType.STYLESHEET) { // Skipping <script> for security reasons // Skipping <style> and stylesheets as we already processed them // in the StyleCompiler return null; } const attrNameAndValues = ast.attrs.map((attr) => [attr.name, attr.value]); const selector = createElementCssSelector(ast.name, attrNameAndValues); const ngContentIndex = parent.findNgContentIndex(selector); const children = html.visitAll(this, ast.children, EMPTY_ELEMENT_CONTEXT); return new t.ElementAst(ast.name, html.visitAll(this, ast.attrs), [], [], [], [], [], false, [], children, ngContentIndex, ast.sourceSpan, ast.endSourceSpan); } visitComment(comment, context) { return null; } visitAttribute(attribute, context) { return new t.AttrAst(attribute.name, attribute.value, attribute.sourceSpan); } visitText(text, parent) { const ngContentIndex = parent.findNgContentIndex(TEXT_CSS_SELECTOR()); return new t.TextAst(text.value, ngContentIndex, text.sourceSpan); } visitExpansion(expansion, context) { return expansion; } visitExpansionCase(expansionCase, context) { return expansionCase; } } /** * A reference to an element or directive in a template. E.g., the reference in this template: * * <div #myMenu="coolMenu"> * * would be {name: 'myMenu', value: 'coolMenu', sourceSpan: ...} */ class ElementOrDirectiveRef { constructor(name, value, sourceSpan) { this.name = name; this.value = value; this.sourceSpan = sourceSpan; } /** Gets whether this is a reference to the given directive. */ isReferenceToDirective(directive) { return splitExportAs(directive.exportAs).indexOf(this.value) !== -1; } } /** Splits a raw, potentially comma-delimited `exportAs` value into an array of names. */ function splitExportAs(exportAs) { return exportAs ? exportAs.split(',').map(e => e.trim()) : []; } export function splitClasses(classAttrValue) { return classAttrValue.trim().split(/\s+/g); } class ElementContext { constructor(isTemplateElement, _ngContentIndexMatcher, _wildcardNgContentIndex, providerContext) { this.isTemplateElement = isTemplateElement; this._ngContentIndexMatcher = _ngContentIndexMatcher; this._wildcardNgContentIndex = _wildcardNgContentIndex; this.providerContext = providerContext; } static create(isTemplateElement, directives, providerContext) { const matcher = new SelectorMatcher(); let wildcardNgContentIndex = null; const component = directives.find(directive => directive.directive.isComponent); if (component) { const ngContentSelectors = component.directive.template.ngContentSelectors; for (let i = 0; i < ngContentSelectors.length; i++) { const selector = ngContentSelectors[i]; if (selector === '*') { wildcardNgContentIndex = i; } else { matcher.addSelectables(CssSelector.parse(ngContentSelectors[i]), i); } } } return new ElementContext(isTemplateElement, matcher, wildcardNgContentIndex, providerContext); } findNgContentIndex(selector) { const ngContentIndices = []; this._ngContentIndexMatcher.match(selector, (selector, ngContentIndex) => { ngContentIndices.push(ngContentIndex); }); ngContentIndices.sort(); if (this._wildcardNgContentIndex != null) { ngContentIndices.push(this._wildcardNgContentIndex); } return ngContentIndices.length > 0 ? ngContentIndices[0] : null; } } export function createElementCssSelector(elementName, attributes) { const cssSelector = new CssSelector(); const elNameNoNs = splitNsName(elementName)[1]; cssSelector.setElement(elNameNoNs); for (let i = 0; i < attributes.length; i++) { const attrName = attributes[i][0]; const attrNameNoNs = splitNsName(attrName)[1]; const attrValue = attributes[i][1]; cssSelector.addAttribute(attrNameNoNs, attrValue); if (attrName.toLowerCase() == CLASS_ATTR) { const classes = splitClasses(attrValue); classes.forEach(className => cssSelector.addClassName(className)); } } return cssSelector; } const EMPTY_ELEMENT_CONTEXT = new ElementContext(true, new SelectorMatcher(), null, null); const NON_BINDABLE_VISITOR = new NonBindableVisitor(); function _isEmptyTextNode(node) { return node instanceof html.Text && node.value.trim().length == 0; } export function removeSummaryDuplicates(items) { const map = new Map(); items.forEach((item) => { if (!map.get(item.type.reference)) { map.set(item.type.reference, item); } }); return Array.from(map.values()); } function isEmptyExpression(ast) { if (ast instanceof ASTWithSource) { ast = ast.ast; } return ast instanceof EmptyExpr; } //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidGVtcGxhdGVfcGFyc2VyLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vLi4vLi4vLi4vcGFja2FnZXMvY29tcGlsZXIvc3JjL3RlbXBsYXRlX3BhcnNlci90ZW1wbGF0ZV9wYXJzZXIudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7Ozs7OztHQU1HO0FBRUgsT0FBTyxFQUFtSCxjQUFjLEVBQUMsTUFBTSxxQkFBcUIsQ0FBQztBQUlySyxPQUFPLEVBQU0sYUFBYSxFQUFFLFNBQVMsRUFBOEMsTUFBTSwwQkFBMEIsQ0FBQztBQUVwSCxPQUFPLEVBQUMsV0FBVyxFQUFFLCtCQUErQixFQUFFLHVCQUF1QixFQUFDLE1BQU0sZ0JBQWdCLENBQUM7QUFDckcsT0FBTyxLQUFLLElBQUksTUFBTSxrQkFBa0IsQ0FBQztBQUN6QyxPQUFPLEVBQWEsZUFBZSxFQUFDLE1BQU0sMEJBQTBCLENBQUM7QUFDckUsT0FBTyxFQUFDLGlCQUFpQixFQUFFLFdBQVcsRUFBQyxNQUFNLCtCQUErQixDQUFDO0FBQzdFLE9BQU8sRUFBQyxXQUFXLEVBQUMsTUFBTSwrQkFBK0IsQ0FBQztBQUMxRCxPQUFPLEVBQUMsbUJBQW1CLEVBQUMsTUFBTSxtQ0FBbUMsQ0FBQztBQUN0RSxPQUFPLEVBQUMsWUFBWSxFQUFFLFdBQVcsRUFBQyxNQUFNLG1CQUFtQixDQUFDO0FBQzVELE9BQU8sRUFBQyxVQUFVLEVBQUUsZUFBZSxFQUFFLGVBQWUsRUFBQyxNQUFNLGVBQWUsQ0FBQztBQUMzRSxPQUFPLEVBQUMsc0JBQXNCLEVBQUUsbUJBQW1CLEVBQUMsTUFBTSxzQkFBc0IsQ0FBQztBQUVqRixPQUFPLEVBQUMsV0FBVyxFQUFFLGVBQWUsRUFBQyxNQUFNLGFBQWEsQ0FBQztBQUN6RCxPQUFPLEVBQUMsb0JBQW9CLEVBQUMsTUFBTSx1QkFBdUIsQ0FBQztBQUMzRCxPQUFPLEVBQVUsV0FBVyxFQUFDLE1BQU0sU0FBUyxDQUFDO0FBRTdDLE9BQU8sRUFBQyxh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