@angular/compiler
Version:
Angular - the compiler library
760 lines • 143 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
*/
(function (factory) {
if (typeof module === "object" && typeof module.exports === "object") {
var v = factory(require, exports);
if (v !== undefined) module.exports = v;
}
else if (typeof define === "function" && define.amd) {
define("@angular/compiler/src/template_parser/template_parser", ["require", "exports", "tslib", "@angular/compiler/src/compile_metadata", "@angular/compiler/src/expression_parser/ast", "@angular/compiler/src/identifiers", "@angular/compiler/src/ml_parser/ast", "@angular/compiler/src/ml_parser/html_parser", "@angular/compiler/src/ml_parser/html_whitespaces", "@angular/compiler/src/ml_parser/icu_ast_expander", "@angular/compiler/src/ml_parser/interpolation_config", "@angular/compiler/src/ml_parser/tags", "@angular/compiler/src/parse_util", "@angular/compiler/src/provider_analyzer", "@angular/compiler/src/selector", "@angular/compiler/src/style_url_resolver", "@angular/compiler/src/util", "@angular/compiler/src/template_parser/binding_parser", "@angular/compiler/src/template_parser/template_ast", "@angular/compiler/src/template_parser/template_preparser"], factory);
}
})(function (require, exports) {
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.isEmptyExpression = exports.removeSummaryDuplicates = exports.createElementCssSelector = exports.splitClasses = exports.TemplateParser = exports.TemplateParseResult = exports.TemplateParseError = void 0;
var tslib_1 = require("tslib");
var compile_metadata_1 = require("@angular/compiler/src/compile_metadata");
var ast_1 = require("@angular/compiler/src/expression_parser/ast");
var identifiers_1 = require("@angular/compiler/src/identifiers");
var html = require("@angular/compiler/src/ml_parser/ast");
var html_parser_1 = require("@angular/compiler/src/ml_parser/html_parser");
var html_whitespaces_1 = require("@angular/compiler/src/ml_parser/html_whitespaces");
var icu_ast_expander_1 = require("@angular/compiler/src/ml_parser/icu_ast_expander");
var interpolation_config_1 = require("@angular/compiler/src/ml_parser/interpolation_config");
var tags_1 = require("@angular/compiler/src/ml_parser/tags");
var parse_util_1 = require("@angular/compiler/src/parse_util");
var provider_analyzer_1 = require("@angular/compiler/src/provider_analyzer");
var selector_1 = require("@angular/compiler/src/selector");
var style_url_resolver_1 = require("@angular/compiler/src/style_url_resolver");
var util_1 = require("@angular/compiler/src/util");
var binding_parser_1 = require("@angular/compiler/src/template_parser/binding_parser");
var t = require("@angular/compiler/src/template_parser/template_ast");
var template_preparser_1 = require("@angular/compiler/src/template_parser/template_preparser");
var BIND_NAME_REGEXP = /^(?:(?:(?:(bind-)|(let-)|(ref-|#)|(on-)|(bindon-)|(@))(.*))|\[\(([^\)]+)\)\]|\[([^\]]+)\]|\(([^\)]+)\))$/;
// Group 1 = "bind-"
var KW_BIND_IDX = 1;
// Group 2 = "let-"
var KW_LET_IDX = 2;
// Group 3 = "ref-/#"
var KW_REF_IDX = 3;
// Group 4 = "on-"
var KW_ON_IDX = 4;
// Group 5 = "bindon-"
var KW_BINDON_IDX = 5;
// Group 6 = "@"
var KW_AT_IDX = 6;
// Group 7 = the identifier after "bind-", "let-", "ref-/#", "on-", "bindon-" or "@"
var IDENT_KW_IDX = 7;
// Group 8 = identifier inside [()]
var IDENT_BANANA_BOX_IDX = 8;
// Group 9 = identifier inside []
var IDENT_PROPERTY_IDX = 9;
// Group 10 = identifier inside ()
var IDENT_EVENT_IDX = 10;
var TEMPLATE_ATTR_PREFIX = '*';
var CLASS_ATTR = 'class';
var _TEXT_CSS_SELECTOR;
function TEXT_CSS_SELECTOR() {
if (!_TEXT_CSS_SELECTOR) {
_TEXT_CSS_SELECTOR = selector_1.CssSelector.parse('*')[0];
}
return _TEXT_CSS_SELECTOR;
}
var TemplateParseError = /** @class */ (function (_super) {
tslib_1.__extends(TemplateParseError, _super);
function TemplateParseError(message, span, level) {
return _super.call(this, span, message, level) || this;
}
return TemplateParseError;
}(parse_util_1.ParseError));
exports.TemplateParseError = TemplateParseError;
var TemplateParseResult = /** @class */ (function () {
function TemplateParseResult(templateAst, usedPipes, errors) {
this.templateAst = templateAst;
this.usedPipes = usedPipes;
this.errors = errors;
}
return TemplateParseResult;
}());
exports.TemplateParseResult = TemplateParseResult;
var TemplateParser = /** @class */ (function () {
function TemplateParser(_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;
}
Object.defineProperty(TemplateParser.prototype, "expressionParser", {
get: function () {
return this._exprParser;
},
enumerable: false,
configurable: true
});
TemplateParser.prototype.parse = function (component, template, directives, pipes, schemas, templateUrl, preserveWhitespaces) {
var _a;
var result = this.tryParse(component, template, directives, pipes, schemas, templateUrl, preserveWhitespaces);
var warnings = result.errors.filter(function (error) { return error.level === parse_util_1.ParseErrorLevel.WARNING; });
var errors = result.errors.filter(function (error) { return error.level === parse_util_1.ParseErrorLevel.ERROR; });
if (warnings.length > 0) {
(_a = this._console) === null || _a === void 0 ? void 0 : _a.warn("Template parse warnings:\n" + warnings.join('\n'));
}
if (errors.length > 0) {
var errorString = errors.join('\n');
throw util_1.syntaxError("Template parse errors:\n" + errorString, errors);
}
return { template: result.templateAst, pipes: result.usedPipes };
};
TemplateParser.prototype.tryParse = function (component, template, directives, pipes, schemas, templateUrl, preserveWhitespaces) {
var htmlParseResult = typeof template === 'string' ?
this._htmlParser.parse(template, templateUrl, {
tokenizeExpansionForms: true,
interpolationConfig: this.getInterpolationConfig(component)
}) :
template;
if (!preserveWhitespaces) {
htmlParseResult = html_whitespaces_1.removeWhitespaces(htmlParseResult);
}
return this.tryParseHtml(this.expandHtml(htmlParseResult), component, directives, pipes, schemas);
};
TemplateParser.prototype.tryParseHtml = function (htmlAstWithErrors, component, directives, pipes, schemas) {
var result;
var errors = htmlAstWithErrors.errors;
var usedPipes = [];
if (htmlAstWithErrors.rootNodes.length > 0) {
var uniqDirectives = removeSummaryDuplicates(directives);
var uniqPipes = removeSummaryDuplicates(pipes);
var providerViewContext = new provider_analyzer_1.ProviderViewContext(this._reflector, component);
var interpolationConfig = undefined;
if (component.template && component.template.interpolation) {
interpolationConfig = {
start: component.template.interpolation[0],
end: component.template.interpolation[1]
};
}
var bindingParser = new binding_parser_1.BindingParser(this._exprParser, interpolationConfig, this._schemaRegistry, uniqPipes, errors);
var 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.apply(errors, tslib_1.__spread(providerViewContext.errors));
usedPipes.push.apply(usedPipes, tslib_1.__spread(bindingParser.getUsedPipes()));
}
else {
result = [];
}
this._assertNoReferenceDuplicationOnTemplate(result, errors);
if (errors.length > 0) {
return new TemplateParseResult(result, usedPipes, errors);
}
if (this.transforms) {
this.transforms.forEach(function (transform) {
result = t.templateVisitAll(transform, result);
});
}
return new TemplateParseResult(result, usedPipes, errors);
};
TemplateParser.prototype.expandHtml = function (htmlAstWithErrors, forced) {
if (forced === void 0) { forced = false; }
var errors = htmlAstWithErrors.errors;
if (errors.length == 0 || forced) {
// Transform ICU messages to angular directives
var expandedHtmlAst = icu_ast_expander_1.expandNodes(htmlAstWithErrors.rootNodes);
errors.push.apply(errors, tslib_1.__spread(expandedHtmlAst.errors));
htmlAstWithErrors = new html_parser_1.ParseTreeResult(expandedHtmlAst.nodes, errors);
}
return htmlAstWithErrors;
};
TemplateParser.prototype.getInterpolationConfig = function (component) {
if (component.template) {
return interpolation_config_1.InterpolationConfig.fromArray(component.template.interpolation);
}
return undefined;
};
/** @internal */
TemplateParser.prototype._assertNoReferenceDuplicationOnTemplate = function (result, errors) {
var existingReferences = [];
result.filter(function (element) { return !!element.references; })
.forEach(function (element) { return element.references.forEach(function (reference) {
var name = reference.name;
if (existingReferences.indexOf(name) < 0) {
existingReferences.push(name);
}
else {
var error = new TemplateParseError("Reference \"#" + name + "\" is defined several times", reference.sourceSpan, parse_util_1.ParseErrorLevel.ERROR);
errors.push(error);
}
}); });
};
return TemplateParser;
}());
exports.TemplateParser = TemplateParser;
var TemplateParseVisitor = /** @class */ (function () {
function TemplateParseVisitor(reflector, config, providerViewContext, directives, _bindingParser, _schemaRegistry, _schemas, _targetErrors) {
var _this = this;
this.reflector = reflector;
this.config = config;
this.providerViewContext = providerViewContext;
this._bindingParser = _bindingParser;
this._schemaRegistry = _schemaRegistry;
this._schemas = _schemas;
this._targetErrors = _targetErrors;
this.selectorMatcher = new selector_1.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(function (directive, index) {
var selector = selector_1.CssSelector.parse(directive.selector);
_this.selectorMatcher.addSelectables(selector, directive);
_this.directivesIndex.set(directive, index);
});
}
TemplateParseVisitor.prototype.visitExpansion = function (expansion, context) {
return null;
};
TemplateParseVisitor.prototype.visitExpansionCase = function (expansionCase, context) {
return null;
};
TemplateParseVisitor.prototype.visitText = function (text, parent) {
var ngContentIndex = parent.findNgContentIndex(TEXT_CSS_SELECTOR());
var valueNoNgsp = html_whitespaces_1.replaceNgsp(text.value);
var expr = this._bindingParser.parseInterpolation(valueNoNgsp, text.sourceSpan);
return expr ? new t.BoundTextAst(expr, ngContentIndex, text.sourceSpan) :
new t.TextAst(valueNoNgsp, ngContentIndex, text.sourceSpan);
};
TemplateParseVisitor.prototype.visitAttribute = function (attribute, context) {
return new t.AttrAst(attribute.name, attribute.value, attribute.sourceSpan);
};
TemplateParseVisitor.prototype.visitComment = function (comment, context) {
return null;
};
TemplateParseVisitor.prototype.visitElement = function (element, parent) {
var _this = this;
var queryStartIndex = this.contentQueryStartId;
var elName = element.name;
var preparsedElement = template_preparser_1.preparseElement(element);
if (preparsedElement.type === template_preparser_1.PreparsedElementType.SCRIPT ||
preparsedElement.type === template_preparser_1.PreparsedElementType.STYLE) {
// Skipping <script> for security reasons
// Skipping <style> as we already processed them
// in the StyleCompiler
return null;
}
if (preparsedElement.type === template_preparser_1.PreparsedElementType.STYLESHEET &&
style_url_resolver_1.isStyleUrlResolvable(preparsedElement.hrefAttr)) {
// Skipping stylesheets with either relative urls or package scheme as we already processed
// them in the StyleCompiler
return null;
}
var matchableAttrs = [];
var elementOrDirectiveProps = [];
var elementOrDirectiveRefs = [];
var elementVars = [];
var events = [];
var templateElementOrDirectiveProps = [];
var templateMatchableAttrs = [];
var templateElementVars = [];
var hasInlineTemplates = false;
var attrs = [];
var isTemplateElement = tags_1.isNgTemplate(element.name);
element.attrs.forEach(function (attr) {
var parsedVariables = [];
var hasBinding = _this._parseAttr(isTemplateElement, attr, matchableAttrs, elementOrDirectiveProps, events, elementOrDirectiveRefs, elementVars);
elementVars.push.apply(elementVars, tslib_1.__spread(parsedVariables.map(function (v) { return t.VariableAst.fromParsedVariable(v); })));
var templateValue;
var templateKey;
var normalizedName = _this._normalizeAttributeName(attr.name);
if (normalizedName.startsWith(TEMPLATE_ATTR_PREFIX)) {
templateValue = attr.value;
templateKey = normalizedName.substring(TEMPLATE_ATTR_PREFIX.length);
}
var 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;
var parsedVariables_1 = [];
var absoluteOffset = (attr.valueSpan || attr.sourceSpan).start.offset;
_this._bindingParser.parseInlineTemplateBinding(templateKey, templateValue, attr.sourceSpan, absoluteOffset, templateMatchableAttrs, templateElementOrDirectiveProps, parsedVariables_1, false /* isIvyAst */);
templateElementVars.push.apply(templateElementVars, tslib_1.__spread(parsedVariables_1.map(function (v) { return 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]);
}
});
var elementCssSelector = createElementCssSelector(elName, matchableAttrs);
var _a = this._parseDirectives(this.selectorMatcher, elementCssSelector), directiveMetas = _a.directives, matchElement = _a.matchElement;
var references = [];
var boundDirectivePropNames = new Set();
var directiveAsts = this._createDirectiveAsts(isTemplateElement, element.name, directiveMetas, elementOrDirectiveProps, elementOrDirectiveRefs, element.sourceSpan, references, boundDirectivePropNames);
var elementProps = this._createElementPropertyAsts(element.name, elementOrDirectiveProps, boundDirectivePropNames);
var isViewRoot = parent.isTemplateElement || hasInlineTemplates;
var providerContext = new provider_analyzer_1.ProviderElementContext(this.providerViewContext, parent.providerContext, isViewRoot, directiveAsts, attrs, references, isTemplateElement, queryStartIndex, element.sourceSpan);
var 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
var projectionSelector = preparsedElement.projectAs != '' ?
selector_1.CssSelector.parse(preparsedElement.projectAs)[0] :
elementCssSelector;
var ngContentIndex = parent.findNgContentIndex(projectionSelector);
var parsedElement;
if (preparsedElement.type === template_preparser_1.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);
var ngContentIndex_1 = 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_1, element.sourceSpan, element.endSourceSpan || null);
}
if (hasInlineTemplates) {
// The element as a *-attribute
var templateQueryStartIndex = this.contentQueryStartId;
var templateSelector = createElementCssSelector('ng-template', templateMatchableAttrs);
var directives = this._parseDirectives(this.selectorMatcher, templateSelector).directives;
var templateBoundDirectivePropNames = new Set();
var templateDirectiveAsts = this._createDirectiveAsts(true, elName, directives, templateElementOrDirectiveProps, [], element.sourceSpan, [], templateBoundDirectivePropNames);
var templateElementProps = this._createElementPropertyAsts(elName, templateElementOrDirectiveProps, templateBoundDirectivePropNames);
this._assertNoComponentsNorElementBindingsOnTemplate(templateDirectiveAsts, templateElementProps, element.sourceSpan);
var templateProviderContext = new provider_analyzer_1.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;
};
TemplateParseVisitor.prototype._parseAttr = function (isTemplateElement, attr, targetMatchableAttrs, targetProps, targetEvents, targetRefs, targetVars) {
var name = this._normalizeAttributeName(attr.name);
var value = attr.value;
var srcSpan = attr.sourceSpan;
var absoluteOffset = attr.valueSpan ? attr.valueSpan.start.offset : srcSpan.start.offset;
var boundEvents = [];
var bindParts = name.match(BIND_NAME_REGEXP);
var 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) {
var 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]) {
var 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.apply(targetEvents, tslib_1.__spread(boundEvents.map(function (e) { return t.BoundEventAst.fromParsedEvent(e); })));
return hasBinding;
};
TemplateParseVisitor.prototype._normalizeAttributeName = function (attrName) {
return /^data-/i.test(attrName) ? attrName.substring(5) : attrName;
};
TemplateParseVisitor.prototype._parseVariable = function (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));
};
TemplateParseVisitor.prototype._parseReference = function (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));
};
TemplateParseVisitor.prototype._parseAssignmentEvent = function (name, expression, sourceSpan, valueSpan, targetMatchableAttrs, targetEvents) {
this._bindingParser.parseEvent(name + "Change", expression + "=$event", sourceSpan, valueSpan, targetMatchableAttrs, targetEvents);
};
TemplateParseVisitor.prototype._parseDirectives = function (selectorMatcher, elementCssSelector) {
var _this = this;
// 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!
var directives = util_1.newArray(this.directivesIndex.size);
// Whether any directive selector matches on the element name
var matchElement = false;
selectorMatcher.match(elementCssSelector, function (selector, directive) {
directives[_this.directivesIndex.get(directive)] = directive;
matchElement = matchElement || selector.hasElementSelector();
});
return {
directives: directives.filter(function (dir) { return !!dir; }),
matchElement: matchElement,
};
};
TemplateParseVisitor.prototype._createDirectiveAsts = function (isTemplateElement, elementName, directives, props, elementOrDirectiveRefs, elementSourceSpan, targetReferences, targetBoundDirectivePropNames) {
var _this = this;
var matchedReferences = new Set();
var component = null;
var directiveAsts = directives.map(function (directive) {
var sourceSpan = new parse_util_1.ParseSourceSpan(elementSourceSpan.start, elementSourceSpan.end, elementSourceSpan.fullStart, "Directive " + compile_metadata_1.identifierName(directive.type));
if (directive.isComponent) {
component = directive;
}
var directiveProperties = [];
var boundProperties = _this._bindingParser.createDirectiveHostPropertyAsts(directive, elementName, sourceSpan);
var hostProperties = boundProperties.map(function (prop) { return 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);
var parsedEvents = _this._bindingParser.createDirectiveHostEventAsts(directive, sourceSpan);
_this._createDirectivePropertyAsts(directive.inputs, props, directiveProperties, targetBoundDirectivePropNames);
elementOrDirectiveRefs.forEach(function (elOrDirRef) {
if ((elOrDirRef.value.length === 0 && directive.isComponent) ||
(elOrDirRef.isReferenceToDirective(directive))) {
targetReferences.push(new t.ReferenceAst(elOrDirRef.name, identifiers_1.createTokenForReference(directive.type.reference), elOrDirRef.value, elOrDirRef.sourceSpan));
matchedReferences.add(elOrDirRef.name);
}
});
var hostEvents = parsedEvents.map(function (e) { return t.BoundEventAst.fromParsedEvent(e); });
var contentQueryStartId = _this.contentQueryStartId;
_this.contentQueryStartId += directive.queries.length;
return new t.DirectiveAst(directive, directiveProperties, hostProperties, hostEvents, contentQueryStartId, sourceSpan);
});
elementOrDirectiveRefs.forEach(function (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) {
var refToken = null;
if (isTemplateElement) {
refToken = identifiers_1.createTokenForExternalReference(_this.reflector, identifiers_1.Identifiers.TemplateRef);
}
targetReferences.push(new t.ReferenceAst(elOrDirRef.name, refToken, elOrDirRef.value, elOrDirRef.sourceSpan));
}
});
return directiveAsts;
};
TemplateParseVisitor.prototype._createDirectivePropertyAsts = function (directiveProperties, boundProps, targetBoundDirectiveProps, targetBoundDirectivePropNames) {
if (directiveProperties) {
var boundPropsByName_1 = new Map();
boundProps.forEach(function (boundProp) {
var prevValue = boundPropsByName_1.get(boundProp.name);
if (!prevValue || prevValue.isLiteral) {
// give [a]="b" a higher precedence than a="b" on the same element
boundPropsByName_1.set(boundProp.name, boundProp);
}
});
Object.keys(directiveProperties).forEach(function (dirProp) {
var elProp = directiveProperties[dirProp];
var boundProp = boundPropsByName_1.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));
}
}
});
}
};
TemplateParseVisitor.prototype._createElementPropertyAsts = function (elementName, props, boundDirectivePropNames) {
var _this = this;
var boundElementProps = [];
props.forEach(function (prop) {
if (!prop.isLiteral && !boundDirectivePropNames.has(prop.name)) {
var boundProp = _this._bindingParser.createBoundElementProperty(elementName, prop);
boundElementProps.push(t.BoundElementPropertyAst.fromBoundProperty(boundProp));
}
});
return this._checkPropertiesInSchema(elementName, boundElementProps);
};
TemplateParseVisitor.prototype._findComponentDirectives = function (directives) {
return directives.filter(function (directive) { return directive.directive.isComponent; });
};
TemplateParseVisitor.prototype._findComponentDirectiveNames = function (directives) {
return this._findComponentDirectives(directives)
.map(function (directive) { return compile_metadata_1.identifierName(directive.directive.type); });
};
TemplateParseVisitor.prototype._assertOnlyOneComponent = function (directives, sourceSpan) {
var 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
*/
TemplateParseVisitor.prototype._assertElementExists = function (matchElement, element) {
var elName = element.name.replace(/^:xhtml:/, '');
if (!matchElement && !this._schemaRegistry.hasElement(elName, this._schemas)) {
var 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);
}
};
TemplateParseVisitor.prototype._assertNoComponentsNorElementBindingsOnTemplate = function (directives, elementProps, sourceSpan) {
var _this = this;
var componentTypeNames = this._findComponentDirectiveNames(directives);
if (componentTypeNames.length > 0) {
this._reportError("Components on an embedded template: " + componentTypeNames.join(','), sourceSpan);
}
elementProps.forEach(function (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);
});
};
TemplateParseVisitor.prototype._assertAllEventsPublishedByDirectives = function (directives, events) {
var _this = this;
var allDirectiveEvents = new Set();
directives.forEach(function (directive) {
Object.keys(directive.directive.outputs).forEach(function (k) {
var eventName = directive.directive.outputs[k];
allDirectiveEvents.add(eventName);
});
});
events.forEach(function (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);
}
});
};
TemplateParseVisitor.prototype._checkPropertiesInSchema = function (elementName, boundProps) {
var _this = this;
// Note: We can't filter out empty expressions before this method,
// as we still want to validate them!
return boundProps.filter(function (boundProp) {
if (boundProp.type === 0 /* Property */ &&
!_this._schemaRegistry.hasProperty(elementName, boundProp.name, _this._schemas)) {
var 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);
});
};
TemplateParseVisitor.prototype._reportError = function (message, sourceSpan, level) {
if (level === void 0) { level = parse_util_1.ParseErrorLevel.ERROR; }
this._targetErrors.push(new parse_util_1.ParseError(sourceSpan, message, level));
};
return TemplateParseVisitor;
}());
var NonBindableVisitor = /** @class */ (function () {
function NonBindableVisitor() {
}
NonBindableVisitor.prototype.visitElement = function (ast, parent) {
var preparsedElement = template_preparser_1.preparseElement(ast);
if (preparsedElement.type === template_preparser_1.PreparsedElementType.SCRIPT ||
preparsedElement.type === template_preparser_1.PreparsedElementType.STYLE ||
preparsedElement.type === template_preparser_1.PreparsedElementType.STYLESHEET) {
// Skipping <script> for security reasons
// Skipping <style> and stylesheets as we already processed them
// in the StyleCompiler
return null;
}
var attrNameAndValues = ast.attrs.map(function (attr) { return [attr.name, attr.value]; });
var selector = createElementCssSelector(ast.name, attrNameAndValues);
var ngContentIndex = parent.findNgContentIndex(selector);
var 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);
};
NonBindableVisitor.prototype.visitComment = function (comment, context) {
return null;
};
NonBindableVisitor.prototype.visitAttribute = function (attribute, context) {
return new t.AttrAst(attribute.name, attribute.value, attribute.sourceSpan);
};
NonBindableVisitor.prototype.visitText = function (text, parent) {
var ngContentIndex = parent.findNgContentIndex(TEXT_CSS_SELECTOR());
return new t.TextAst(text.value, ngContentIndex, text.sourceSpan);
};
NonBindableVisitor.prototype.visitExpansion = function (expansion, context) {
return expansion;
};
NonBindableVisitor.prototype.visitExpansionCase = function (expansionCase, context) {
return expansionCase;
};
return NonBindableVisitor;
}());
/**
* 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: ...}
*/
var ElementOrDirectiveRef = /** @class */ (function () {
function ElementOrDirectiveRef(name, value, sourceSpan) {
this.name = name;
this.value = value;
this.sourceSpan = sourceSpan;
}
/** Gets whether this is a reference to the given directive. */
ElementOrDirectiveRef.prototype.isReferenceToDirective = function (directive) {
return splitExportAs(directive.exportAs).indexOf(this.value) !== -1;
};
return ElementOrDirectiveRef;
}());
/** Splits a raw, potentially comma-delimited `exportAs` value into an array of names. */
function splitExportAs(exportAs) {
return exportAs ? exportAs.split(',').map(function (e) { return e.trim(); }) : [];
}
function splitClasses(classAttrValue) {
return classAttrValue.trim().split(/\s+/g);
}
exports.splitClasses = splitClasses;
var ElementContext = /** @class */ (function () {
function ElementContext(isTemplateElement, _ngContentIndexMatcher, _wildcardNgContentIndex, providerContext) {
this.isTemplateElement = isTemplateElement;
this._ngContentIndexMatcher = _ngContentIndexMatcher;
this._wildcardNgContentIndex = _wildcardNgContentIndex;
this.providerContext = providerContext;
}
ElementContext.create = function (isTemplateElement, directives, providerContext) {
var matcher = new selector_1.SelectorMatcher();
var wildcardNgContentIndex = null;
var component = directives.find(function (directive) { return directive.directive.isComponent; });
if (component) {
var ngContentSelectors = component.directive.template.ngContentSelectors;
for (var i = 0; i < ngContentSelectors.length; i++) {
var selector = ngContentSelectors[i];
if (selector === '*') {
wildcardNgContentIndex = i;
}
else {
matcher.addSelectables(selector_1.CssSelector.parse(ngContentSelectors[i]), i);
}
}
}
return new ElementContext(isTemplateElement, matcher, wildcardNgContentIndex, providerContext);
};
ElementContext.prototype.findNgContentIndex = function (selector) {
var ngContentIndices = [];
this._ngContentIndexMatcher.match(selector, function (selector, ngContentIndex) {
ngContentIndices.push(ngContentIndex);
});
ngContentIndices.sort();
if (this._wildcardNgContentIndex != null) {
ngContentIndices.push(this._wildcardNgContentIndex);
}
return ngContentIndices.length > 0 ? ngContentIndices[0] : null;
};
return ElementContext;
}());
function createElementCssSelector(elementName, attributes) {
var cssSelector = new selector_1.CssSelector();
var elNameNoNs = tags_1.splitNsName(elementName)[1];
cssSelector.setElement(elNameNoNs);
for (var i = 0; i < attributes.length; i++) {
var attrName = attributes[i][0];
var attrNameNoNs = tags_1.splitNsName(attrName)[1];
var attrValue = attributes[i][1];
cssSelector.addAttribute(attrNameNoNs, attrValue);
if (attrName.toLowerCase() == CLASS_ATTR) {
var classes = splitClasses(attrValue);
classes.forEach(function (className) { return cssSelector.addClassName(className); });
}
}
return cssSelector;
}
exports.createElementCssSelector = createElementCssSelector;
var EMPTY_ELEMENT_CONTEXT = new ElementContext(true, new selector_1.SelectorMatcher(), null, null);
var NON_BINDABLE_VISITOR = new NonBindableVisitor();
function _isEmptyTextNode(node) {
return node instanceof html.Text && node.value.trim().length == 0;
}
function removeSummaryDuplicates(items) {
var map = new Map();
items.forEach(function (item) {
if (!map.get(item.type.reference)) {
map.set(item.type.reference, item);
}
});
return Array.from(map.values());
}
exports.removeSummaryDuplicates = removeSummaryDuplicates;
function isEmptyExpression(ast) {
if (ast instanceof ast_1.ASTWithSource) {
ast = ast.ast;
}
return ast instanceof ast_1.EmptyExpr;
}
exports.isEmptyExpression = isEmptyExpression;
});
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidGVtcGxhdGVfcGFyc2VyLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vLi4vLi4vLi4vcGFja2FnZXMvY29tcGlsZXIvc3JjL3RlbXBsYXRlX3BhcnNlci90ZW1wbGF0ZV9wYXJzZXIudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7Ozs7OztHQU1HOzs7Ozs7Ozs7Ozs7OztJQUVILDJFQUFxSztJQUlySyxtRUFBb0g7SUFFcEgsaUVBQXFHO0lBQ3JHLDBEQUF5QztJQUN6QywyRUFBcUU7SUFDckUscUZBQTZFO0lBQzdFLHFGQUEwRDtJQUMxRCw2RkFBc0U7SUFDdEUsNkRBQTREO0lBQzVELCtEQUEyRTtJQUMzRSw2RUFBaUY7SUFFakYsMkRBQXlEO0lBQ3pELCtFQUEyRDtJQUMzRCxtREFBdUQ7SUFFdkQsdUZBQStDO0lBQy9DLHNFQUFvQztJQUNwQywrRkFBMkU7SUFFM0UsSUFBTSxnQkFBZ0IsR0FDbEIsMEdBQTBHLENBQUM7SUFFL0csb0JBQW9CO0lBQ3BCLElBQU0sV0FBVyxHQUFHLENBQUMsQ0FBQztJQUN0QixtQkFBbUI7SUFDbkIsSUFBTSxVQUFVLEdBQUcsQ0FBQyxDQUFDO0lBQ3JCLHFCQUFxQjtJQUNyQixJQUFNLFVBQVUsR0FBRyxDQUFDLENBQUM7SUFDckIsa0JBQWtCO0lBQ2xCLElBQU0sU0FBUyxHQUFHLENBQUMsQ0FBQztJQUNwQixzQkFBc0I7SUFDdEIsSUFBTSxhQUFhLEdBQUcsQ0FBQyxDQUFDO0lBQ3hCLGdCQUFnQjtJQUNoQixJQUFNLFNBQVMsR0FBRyxDQUFDLENBQUM7SUFDcEIsb0ZBQW9GO0lBQ3BGLElBQU0sWUFBWSxHQUFHLENBQUMsQ0FBQztJQUN2QixtQ0FBbUM7SUFDbkMsSUFBTSxvQkFBb0IsR0FBRyxDQUFDLENBQUM7SUFDL0IsaUNBQWlDO0lBQ2pDLElBQU0sa0JBQWtCLEdBQUcsQ0FBQyxDQUFDO0lBQzdCLGtDQUFrQztJQUNsQyxJQUFNLGVBQWUsR0FBRyxFQUFFLENBQUM7SUFFM0IsSUFBTSxvQkFBb0IsR0FBRyxHQUFHLENBQUM7SUFDakMsSUFBTSxVQUFVLEdBQUcsT0FBTyxDQUFDO0lBRTNCLElBQUksa0JBQWdDLENBQUM7SUFDckMsU0FBUyxpQkFBaUI7UUFDeEIsSUFBSSxDQUFDLGtCQUFrQixFQUFFO1lBQ3ZCLGtCQUFrQixHQUFHLHNCQUFXLENBQUMsS0FBSyxDQUFDLEdBQUcsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDO1NBQ2hEO1FBQ0QsT0FBTyxrQkFBa0IsQ0FBQztJQUM1QixDQUFDO0lBRUQ7UUFBd0MsOENBQVU7UUFDaEQsNEJBQVksT0FBZSxFQUFFLElBQXFCLEVBQUUsS0FBc0I7bUJBQ3hFLGtCQUFNLElBQUksRUFBRSxPQUFPLEVBQUUsS0FBSyxDQUFDO1FBQzdCLENBQUM7UUFDSCx5QkFBQztJQUFELENBQUMsQUFKRCxDQUF3Qyx1QkFBVSxHQUlqRDtJQUpZLGdEQUFrQjtJQU0vQjtRQUNFLDZCQUNXLFdBQTZCLEVBQVMsU0FBZ0MsRUFDdEUsTUFBcUI7WUFEckIsZ0JBQVcsR0FBWCxXQUFXLENBQWtCO1lBQVMsY0FBUyxHQUFULFNBQVMsQ0FBdUI7WUFDdEUsV0FBTSxHQUFOLE1BQU0sQ0FBZTtRQUFHLENBQUM7UUFDdEMsMEJBQUM7SUFBRCxDQUFDLEFBSkQsSUFJQztJQUpZLGtEQUFtQjtJQU1oQztRQUNFLHdCQUNZLE9BQXVCLEVBQVUsVUFBNEIsRUFDN0QsV0FBbUIsRUFBVSxlQUFzQyxFQUNuRSxXQUF1QixFQUFVLFFBQXNCLEVBQ3hELFVBQWtDO1lBSGpDLFlBQU8sR0FBUCxPQUFPLENBQWdCO1lBQVUsZUFBVSxHQUFWLFVBQVUsQ0FBa0I7WUFDN0QsZ0JBQVcsR0FBWCxXQUFXLENBQVE7WUFBVSxvQkFBZSxHQUFmLGVBQWUsQ0FBdUI7WUFDbkUsZ0JBQVcsR0FBWCxXQUFXLENBQVk7WUFBVSxhQUFRLEdBQVIsUUFBUSxDQUFjO1lBQ3hELGVBQVUsR0FBVixVQUFVLENBQXdCO1FBQUcsQ0FBQztRQUVqRCxzQkFBVyw0Q0FBZ0I7aUJBQTNCO2dCQUNFLE9BQU8sSUFBSSxDQUFDLFdBQVcsQ0FBQztZQUMxQixDQUFDOzs7V0FBQTtRQUVELDhCQUFLLEdBQUwsVUFDSSxTQUFtQyxFQUFFLFFBQWdDLEVBQ3JFLFVBQXFDLEVBQUUsS0FBMkIsRUFBRSxPQUF5QixFQUM3RixXQUFtQixFQUNuQixtQkFBNEI7O1lBQzlCLElBQU0sTUFBTSxHQUFHLElBQUksQ0FBQyxRQUFRLENBQ3hCLFNBQVMsRUFBRSxRQUFRLEVBQUUsVUFBVSxFQUFFLEtBQUssRUFBRSxPQUFPLEVBQUUsV0FBVyxFQUFFLG1CQUFtQixDQUFDLENBQUM7WUFDdkYsSUFBTSxRQUFRLEdBQUcsTUFBTSxDQUFDLE1BQU8sQ0FBQyxNQUFNLENBQUMsVUFBQSxLQUFLLElBQUksT0FBQSxLQUFLLENBQUMsS0FBSyxLQUFLLDRCQUFlLENBQUMsT