@angular/compiler
Version:
Angular - the compiler library
713 lines • 133 kB
JavaScript
/**
* @license
* Copyright Google LLC 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 { ASTWithSource, EmptyExpr } from '../expression_parser/ast';
import { createTokenForExternalReference, createTokenForReference, Identifiers } from '../identifiers';
import * as html from '../ml_parser/ast';
import { removeWhitespaces, replaceNgsp } from '../ml_parser/html_whitespaces';
import { expandNodes } from '../ml_parser/icu_ast_expander';
import { InterpolationConfig } from '../ml_parser/interpolation_config';
import { ParseTreeResult } from '../ml_parser/parser';
import { isNgTemplate, splitNsName } from '../ml_parser/tags';
import { identifierName, ParseError, ParseErrorLevel, ParseSourceSpan, syntaxError } from '../parse_util';
import { ProviderElementContext, ProviderViewContext } from '../provider_analyzer';
import { CssSelector, SelectorMatcher } from '../selector';
import { isStyleUrlResolvable } from '../style_url_resolver';
import { newArray } 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, {
tokenizeExpansionForms: true,
interpolationConfig: 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 = [];
const absoluteOffset = (attr.valueSpan || attr.sourceSpan).start.offset;
this._bindingParser.parseInlineTemplateBinding(templateKey, templateValue, attr.sourceSpan, absoluteOffset, templateMatchableAttrs, templateElementOrDirectiveProps, parsedVariables, false /* isIvyAst */);
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 absoluteOffset = attr.valueSpan ? attr.valueSpan.start.offset : srcSpan.start.offset;
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, absoluteOffset, attr.valueSpan, 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, attr.valueSpan || srcSpan, targetMatchableAttrs, boundEvents);
}
else if (bindParts[KW_BINDON_IDX]) {
this._bindingParser.parsePropertyBinding(bindParts[IDENT_KW_IDX], value, false, srcSpan, absoluteOffset, attr.valueSpan, targetMatchableAttrs, targetProps);
this._parseAssignmentEvent(bindParts[IDENT_KW_IDX], value, srcSpan, attr.valueSpan || srcSpan, targetMatchableAttrs, boundEvents);
}
else if (bindParts[KW_AT_IDX]) {
this._bindingParser.parseLiteralAttr(name, value, srcSpan, absoluteOffset, attr.valueSpan, targetMatchableAttrs, targetProps);
}
else if (bindParts[IDENT_BANANA_BOX_IDX]) {
this._bindingParser.parsePropertyBinding(bindParts[IDENT_BANANA_BOX_IDX], value, false, srcSpan, absoluteOffset, attr.valueSpan, targetMatchableAttrs, targetProps);
this._parseAssignmentEvent(bindParts[IDENT_BANANA_BOX_IDX], value, srcSpan, attr.valueSpan || srcSpan, targetMatchableAttrs, boundEvents);
}
else if (bindParts[IDENT_PROPERTY_IDX]) {
this._bindingParser.parsePropertyBinding(bindParts[IDENT_PROPERTY_IDX], value, false, srcSpan, absoluteOffset, attr.valueSpan, targetMatchableAttrs, targetProps);
}
else if (bindParts[IDENT_EVENT_IDX]) {
this._bindingParser.parseEvent(bindParts[IDENT_EVENT_IDX], value, srcSpan, attr.valueSpan || srcSpan, targetMatchableAttrs, boundEvents);
}
}
else {
hasBinding = this._bindingParser.parsePropertyInterpolation(name, value, srcSpan, attr.valueSpan, targetMatchableAttrs, targetProps);
}
if (!hasBinding) {
this._bindingParser.parseLiteralAttr(name, value, srcSpan, absoluteOffset, attr.valueSpan, 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);
}
else if (identifier.length === 0) {
this._reportError(`Variable does not have a name`, 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);
}
else if (identifier.length === 0) {
this._reportError(`Reference does not have a name`, sourceSpan);
}
targetRefs.push(new ElementOrDirectiveRef(identifier, value, sourceSpan));
}
_parseAssignmentEvent(name, expression, sourceSpan, valueSpan, targetMatchableAttrs, targetEvents) {
this._bindingParser.parseEvent(`${name}Change`, `${expression}=$event`, sourceSpan, valueSpan, 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 = newArray(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, elementSourceSpan.fullStart, `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());
}
export function isEmptyExpression(ast) {
if (ast instanceof ASTWithSource) {
ast = ast.ast;
}
return ast instanceof EmptyExpr;
}
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidGVtcGxhdGVfcGFyc2VyLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vLi4vLi4vLi4vcGFja2FnZXMvY29tcGlsZXIvc3JjL3RlbXBsYXRlX3BhcnNlci90ZW1wbGF0ZV9wYXJzZXIudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7Ozs7OztHQU1HO0FBTUgsT0FBTyxFQUFNLGFBQWEsRUFBRSxTQUFTLEVBQThDLE1BQU0sMEJBQTBCLENBQUM7QUFFcEgsT0FBTyxFQUFDLCtCQUErQixFQUFFLHVCQUF1QixFQUFFLFdBQVcsRUFBQyxNQUFNLGdCQUFnQixDQUFDO0FBQ3JHLE9BQU8sS0FBSyxJQUFJLE1BQU0sa0JBQWtCLENBQUM7QUFFekMsT0FBTyxFQUFDLGlCQUFpQixFQUFFLFdBQVcsRUFBQyxNQUFNLCtCQUErQixDQUFDO0FBQzdFLE9BQU8sRUFBQyxXQUFXLEVBQUMsTUFBTSwrQkFBK0IsQ0FBQztBQUMxRCxPQUFPLEVBQUMsbUJBQW1CLEVBQUMsTUFBTSxtQ0FBbUMsQ0FBQztBQUN0RSxPQUFPLEVBQUMsZUFBZSxFQUFDLE1BQU0scUJBQXFCLENBQUM7QUFDcEQsT0FBTyxFQUFDLFlBQVksRUFBRSxXQUFXLEVBQUMsTUFBTSxtQkFBbUIsQ0FBQztBQUM1RCxPQUFPLEVBQUMsY0FBYyxFQUFFLFVBQVUsRUFBRSxlQUFlLEVBQUUsZUFBZSxFQUFFLFdBQVcsRUFBQyxNQUFNLGVBQWUsQ0FBQztBQUN4RyxPQUFPLEVBQUMsc0JBQXNCLEVBQUUsbUJBQW1CLEVBQUMsTUFBTSxzQkFBc0IsQ0FBQztBQUVqRixPQUFPLEVBQUMsV0FBVyxFQUFFLGVBQWUsRUFBQyxNQUFNLGFBQWEsQ0FBQztBQUN6RCxPQUFPLEVBQUMsb0JBQW9CLEVBQUMsTUFBTSx1QkFBdUIsQ0FBQztBQUMzRCxPQUFPLEVBQVUsUUFBUSxFQUFDLE1BQU0sU0FBUyxDQUFDO0FBRTFDLE9BQU8sRUFBQyxhQUFhLEVBQUMsTUFBTSxrQkFBa0IsQ0FB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