@angular/compiler
Version:
Angular - the compiler library
959 lines • 157 kB
JavaScript
/**
* @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 * as tslib_1 from "tslib";
import { flatten, sanitizeIdentifier } from '../../compile_metadata';
import { BindingForm, BuiltinFunctionCall, convertActionBinding, convertPropertyBinding } from '../../compiler_util/expression_converter';
import * as core from '../../core';
import { AstMemoryEfficientTransformer, FunctionCall, ImplicitReceiver, Interpolation, LiteralArray, LiteralPrimitive, PropertyRead } from '../../expression_parser/ast';
import { Lexer } from '../../expression_parser/lexer';
import { Parser } from '../../expression_parser/parser';
import * as html from '../../ml_parser/ast';
import { HtmlParser } from '../../ml_parser/html_parser';
import { WhitespaceVisitor } from '../../ml_parser/html_whitespaces';
import { DEFAULT_INTERPOLATION_CONFIG } from '../../ml_parser/interpolation_config';
import { splitNsName } from '../../ml_parser/tags';
import * as o from '../../output/output_ast';
import { DomElementSchemaRegistry } from '../../schema/dom_element_schema_registry';
import { CssSelector } from '../../selector';
import { BindingParser } from '../../template_parser/binding_parser';
import { error } from '../../util';
import * as t from '../r3_ast';
import { Identifiers as R3 } from '../r3_identifiers';
import { htmlAstToRender3Ast } from '../r3_template_transform';
import { parseStyle } from './styling';
import { CONTEXT_NAME, I18N_ATTR, I18N_ATTR_PREFIX, ID_SEPARATOR, IMPLICIT_REFERENCE, MEANING_SEPARATOR, REFERENCE_PREFIX, RENDER_FLAGS, asLiteral, invalid, mapToExpression, noop, trimTrailingNulls, unsupported } from './util';
function mapBindingToInstruction(type) {
switch (type) {
case 0 /* Property */:
return R3.elementProperty;
case 1 /* Attribute */:
return R3.elementAttribute;
case 2 /* Class */:
return R3.elementClassProp;
default:
return undefined;
}
}
// if (rf & flags) { .. }
export function renderFlagCheckIfStmt(flags, statements) {
return o.ifStmt(o.variable(RENDER_FLAGS).bitwiseAnd(o.literal(flags), null, false), statements);
}
var TemplateDefinitionBuilder = /** @class */ (function () {
function TemplateDefinitionBuilder(constantPool, contextParameter, parentBindingScope, level, contextName, templateName, viewQueries, directiveMatcher, directives, pipeTypeByName, pipes, _namespace) {
if (level === void 0) { level = 0; }
var _this = this;
this.constantPool = constantPool;
this.contextParameter = contextParameter;
this.level = level;
this.contextName = contextName;
this.templateName = templateName;
this.viewQueries = viewQueries;
this.directiveMatcher = directiveMatcher;
this.directives = directives;
this.pipeTypeByName = pipeTypeByName;
this.pipes = pipes;
this._namespace = _namespace;
this._dataIndex = 0;
this._bindingContext = 0;
this._prefixCode = [];
this._creationCode = [];
this._variableCode = [];
this._bindingCode = [];
this._postfixCode = [];
this._unsupported = unsupported;
// Whether we are inside a translatable element (`<p i18n>... somewhere here ... </p>)
this._inI18nSection = false;
this._i18nSectionIndex = -1;
// Maps of placeholder to node indexes for each of the i18n section
this._phToNodeIdxes = [{}];
// Number of slots to reserve for pureFunctions
this._pureFunctionSlots = 0;
// These should be handled in the template or element directly.
this.visitReference = invalid;
this.visitVariable = invalid;
this.visitTextAttribute = invalid;
this.visitBoundAttribute = invalid;
this.visitBoundEvent = invalid;
// view queries can take up space in data and allocation happens earlier (in the "viewQuery"
// function)
this._dataIndex = viewQueries.length;
this._bindingScope =
parentBindingScope.nestedScope(function (lhsVar, expression) {
_this._bindingCode.push(lhsVar.set(expression).toDeclStmt(o.INFERRED_TYPE, [o.StmtModifier.Final]));
});
this._valueConverter = new ValueConverter(constantPool, function () { return _this.allocateDataSlot(); }, function (numSlots) { return _this._pureFunctionSlots += numSlots; }, function (name, localName, slot, value) {
var pipeType = pipeTypeByName.get(name);
if (pipeType) {
_this.pipes.add(pipeType);
}
_this._bindingScope.set(localName, value);
_this._creationCode.push(o.importExpr(R3.pipe).callFn([o.literal(slot), o.literal(name)]).toStmt());
});
}
TemplateDefinitionBuilder.prototype.buildTemplateFunction = function (nodes, variables, hasNgContent, ngContentSelectors) {
if (hasNgContent === void 0) { hasNgContent = false; }
if (ngContentSelectors === void 0) { ngContentSelectors = []; }
var e_1, _a, e_2, _b;
if (this._namespace !== R3.namespaceHTML) {
this.instruction(this._creationCode, null, this._namespace);
}
try {
// Create variable bindings
for (var variables_1 = tslib_1.__values(variables), variables_1_1 = variables_1.next(); !variables_1_1.done; variables_1_1 = variables_1.next()) {
var variable = variables_1_1.value;
var variableName = variable.name;
var expression = o.variable(this.contextParameter).prop(variable.value || IMPLICIT_REFERENCE);
var scopedName = this._bindingScope.freshReferenceName();
// Add the reference to the local scope.
this._bindingScope.set(variableName, o.variable(variableName + scopedName), expression);
}
}
catch (e_1_1) { e_1 = { error: e_1_1 }; }
finally {
try {
if (variables_1_1 && !variables_1_1.done && (_a = variables_1.return)) _a.call(variables_1);
}
finally { if (e_1) throw e_1.error; }
}
// Output a `ProjectionDef` instruction when some `<ng-content>` are present
if (hasNgContent) {
var parameters = [];
// Only selectors with a non-default value are generated
if (ngContentSelectors.length > 1) {
var r3Selectors = ngContentSelectors.map(function (s) { return core.parseSelectorToR3Selector(s); });
// `projectionDef` needs both the parsed and raw value of the selectors
var parsed = this.constantPool.getConstLiteral(asLiteral(r3Selectors), true);
var unParsed = this.constantPool.getConstLiteral(asLiteral(ngContentSelectors), true);
parameters.push(parsed, unParsed);
}
this.instruction.apply(this, tslib_1.__spread([this._creationCode, null, R3.projectionDef], parameters));
}
t.visitAll(this, nodes);
if (this._pureFunctionSlots > 0) {
this.instruction(this._creationCode, null, R3.reserveSlots, o.literal(this._pureFunctionSlots));
}
var creationCode = this._creationCode.length > 0 ?
[renderFlagCheckIfStmt(1 /* Create */, this._creationCode)] :
[];
var updateCode = this._bindingCode.length > 0 ?
[renderFlagCheckIfStmt(2 /* Update */, this._bindingCode)] :
[];
try {
// Generate maps of placeholder name to node indexes
// TODO(vicb): This is a WIP, not fully supported yet
for (var _c = tslib_1.__values(this._phToNodeIdxes), _d = _c.next(); !_d.done; _d = _c.next()) {
var phToNodeIdx = _d.value;
if (Object.keys(phToNodeIdx).length > 0) {
var scopedName = this._bindingScope.freshReferenceName();
var phMap = o.variable(scopedName)
.set(mapToExpression(phToNodeIdx, true))
.toDeclStmt(o.INFERRED_TYPE, [o.StmtModifier.Final]);
this._prefixCode.push(phMap);
}
}
}
catch (e_2_1) { e_2 = { error: e_2_1 }; }
finally {
try {
if (_d && !_d.done && (_b = _c.return)) _b.call(_c);
}
finally { if (e_2) throw e_2.error; }
}
return o.fn([new o.FnParam(RENDER_FLAGS, o.NUMBER_TYPE), new o.FnParam(this.contextParameter, null)], tslib_1.__spread(this._prefixCode, creationCode, this._variableCode, updateCode, this._postfixCode), o.INFERRED_TYPE, null, this.templateName);
};
// LocalResolver
TemplateDefinitionBuilder.prototype.getLocal = function (name) { return this._bindingScope.get(name); };
TemplateDefinitionBuilder.prototype.visitContent = function (ngContent) {
var slot = this.allocateDataSlot();
var selectorIndex = ngContent.selectorIndex;
var parameters = [o.literal(slot)];
var attributeAsList = [];
ngContent.attributes.forEach(function (attribute) {
var name = attribute.name;
if (name !== 'select') {
attributeAsList.push(name, attribute.value);
}
});
if (attributeAsList.length > 0) {
parameters.push(o.literal(selectorIndex), asLiteral(attributeAsList));
}
else if (selectorIndex !== 0) {
parameters.push(o.literal(selectorIndex));
}
this.instruction.apply(this, tslib_1.__spread([this._creationCode, ngContent.sourceSpan, R3.projection], parameters));
};
TemplateDefinitionBuilder.prototype.getNamespaceInstruction = function (namespaceKey) {
switch (namespaceKey) {
case 'math':
return R3.namespaceMathML;
case 'svg':
return R3.namespaceSVG;
default:
return R3.namespaceHTML;
}
};
TemplateDefinitionBuilder.prototype.addNamespaceInstruction = function (nsInstruction, element) {
this._namespace = nsInstruction;
this.instruction(this._creationCode, element.sourceSpan, nsInstruction);
};
TemplateDefinitionBuilder.prototype.visitElement = function (element) {
var _this = this;
var e_3, _a, _b, _c;
var elementIndex = this.allocateDataSlot();
var referenceDataSlots = new Map();
var wasInI18nSection = this._inI18nSection;
var outputAttrs = {};
var attrI18nMetas = {};
var i18nMeta = '';
var _d = tslib_1.__read(splitNsName(element.name), 2), namespaceKey = _d[0], elementName = _d[1];
// Elements inside i18n sections are replaced with placeholders
// TODO(vicb): nested elements are a WIP in this phase
if (this._inI18nSection) {
var phName = element.name.toLowerCase();
if (!this._phToNodeIdxes[this._i18nSectionIndex][phName]) {
this._phToNodeIdxes[this._i18nSectionIndex][phName] = [];
}
this._phToNodeIdxes[this._i18nSectionIndex][phName].push(elementIndex);
}
try {
// Handle i18n attributes
for (var _e = tslib_1.__values(element.attributes), _f = _e.next(); !_f.done; _f = _e.next()) {
var attr = _f.value;
var name_1 = attr.name;
var value = attr.value;
if (name_1 === I18N_ATTR) {
if (this._inI18nSection) {
throw new Error("Could not mark an element as translatable inside of a translatable section");
}
this._inI18nSection = true;
this._i18nSectionIndex++;
this._phToNodeIdxes[this._i18nSectionIndex] = {};
i18nMeta = value;
}
else if (name_1.startsWith(I18N_ATTR_PREFIX)) {
attrI18nMetas[name_1.slice(I18N_ATTR_PREFIX.length)] = value;
}
else {
outputAttrs[name_1] = value;
}
}
}
catch (e_3_1) { e_3 = { error: e_3_1 }; }
finally {
try {
if (_f && !_f.done && (_a = _e.return)) _a.call(_e);
}
finally { if (e_3) throw e_3.error; }
}
// Match directives on non i18n attributes
if (this.directiveMatcher) {
var selector = createCssSelector(element.name, outputAttrs);
this.directiveMatcher.match(selector, function (sel, staticType) { _this.directives.add(staticType); });
}
// Element creation mode
var parameters = [
o.literal(elementIndex),
o.literal(elementName),
];
// Add the attributes
var i18nMessages = [];
var attributes = [];
var initialStyleDeclarations = [];
var initialClassDeclarations = [];
var styleInputs = [];
var classInputs = [];
var allOtherInputs = [];
element.inputs.forEach(function (input) {
switch (input.type) {
// [attr.style] or [attr.class] should not be treated as styling-based
// bindings since they are intended to be written directly to the attr
// and therefore will skip all style/class resolution that is present
// with style="", [style]="" and [style.prop]="", class="",
// [class.prop]="". [class]="" assignments
case 0 /* Property */:
if (input.name == 'style') {
// this should always go first in the compilation (for [style])
styleInputs.splice(0, 0, input);
}
else if (isClassBinding(input)) {
// this should always go first in the compilation (for [class])
classInputs.splice(0, 0, input);
}
else {
allOtherInputs.push(input);
}
break;
case 3 /* Style */:
styleInputs.push(input);
break;
case 2 /* Class */:
classInputs.push(input);
break;
default:
allOtherInputs.push(input);
break;
}
});
var currStyleIndex = 0;
var currClassIndex = 0;
var staticStylesMap = null;
var staticClassesMap = null;
var stylesIndexMap = {};
var classesIndexMap = {};
Object.getOwnPropertyNames(outputAttrs).forEach(function (name) {
var value = outputAttrs[name];
if (name == 'style') {
staticStylesMap = parseStyle(value);
Object.keys(staticStylesMap).forEach(function (prop) { stylesIndexMap[prop] = currStyleIndex++; });
}
else if (name == 'class') {
staticClassesMap = {};
value.split(/\s+/g).forEach(function (className) {
classesIndexMap[className] = currClassIndex++;
staticClassesMap[className] = true;
});
}
else {
attributes.push(o.literal(name));
if (attrI18nMetas.hasOwnProperty(name)) {
var meta = parseI18nMeta(attrI18nMetas[name]);
var variable = _this.constantPool.getTranslation(value, meta);
attributes.push(variable);
}
else {
attributes.push(o.literal(value));
}
}
});
var hasMapBasedStyling = false;
for (var i = 0; i < styleInputs.length; i++) {
var input = styleInputs[i];
var isMapBasedStyleBinding = i === 0 && input.name === 'style';
if (isMapBasedStyleBinding) {
hasMapBasedStyling = true;
}
else if (!stylesIndexMap.hasOwnProperty(input.name)) {
stylesIndexMap[input.name] = currStyleIndex++;
}
}
for (var i = 0; i < classInputs.length; i++) {
var input = classInputs[i];
var isMapBasedClassBinding = i === 0 && isClassBinding(input);
if (!isMapBasedClassBinding && !stylesIndexMap.hasOwnProperty(input.name)) {
classesIndexMap[input.name] = currClassIndex++;
}
}
// in the event that a [style] binding is used then sanitization will
// always be imported because it is not possible to know ahead of time
// whether style bindings will use or not use any sanitizable properties
// that isStyleSanitizable() will detect
var useDefaultStyleSanitizer = hasMapBasedStyling;
// this will build the instructions so that they fall into the following syntax
// => [prop1, prop2, prop3, 0, prop1, value1, prop2, value2]
Object.keys(stylesIndexMap).forEach(function (prop) {
useDefaultStyleSanitizer = useDefaultStyleSanitizer || isStyleSanitizable(prop);
initialStyleDeclarations.push(o.literal(prop));
});
if (staticStylesMap) {
initialStyleDeclarations.push(o.literal(1 /* VALUES_MODE */));
Object.keys(staticStylesMap).forEach(function (prop) {
initialStyleDeclarations.push(o.literal(prop));
var value = staticStylesMap[prop];
initialStyleDeclarations.push(o.literal(value));
});
}
Object.keys(classesIndexMap).forEach(function (prop) {
initialClassDeclarations.push(o.literal(prop));
});
if (staticClassesMap) {
initialClassDeclarations.push(o.literal(1 /* VALUES_MODE */));
Object.keys(staticClassesMap).forEach(function (className) {
initialClassDeclarations.push(o.literal(className));
initialClassDeclarations.push(o.literal(true));
});
}
var hasStylingInstructions = initialStyleDeclarations.length || styleInputs.length ||
initialClassDeclarations.length || classInputs.length;
var attrArg = attributes.length > 0 ?
this.constantPool.getConstLiteral(o.literalArr(attributes), true) :
o.TYPED_NULL_EXPR;
parameters.push(attrArg);
if (element.references && element.references.length > 0) {
var references = flatten(element.references.map(function (reference) {
var slot = _this.allocateDataSlot();
referenceDataSlots.set(reference.name, slot);
// Generate the update temporary.
var variableName = _this._bindingScope.freshReferenceName();
_this._variableCode.push(o.variable(variableName, o.INFERRED_TYPE)
.set(o.importExpr(R3.load).callFn([o.literal(slot)]))
.toDeclStmt(o.INFERRED_TYPE, [o.StmtModifier.Final]));
_this._bindingScope.set(reference.name, o.variable(variableName));
return [reference.name, reference.value];
}));
parameters.push(this.constantPool.getConstLiteral(asLiteral(references), true));
}
else {
parameters.push(o.TYPED_NULL_EXPR);
}
// Generate the instruction create element instruction
if (i18nMessages.length > 0) {
(_b = this._creationCode).push.apply(_b, tslib_1.__spread(i18nMessages));
}
var wasInNamespace = this._namespace;
var currentNamespace = this.getNamespaceInstruction(namespaceKey);
// If the namespace is changing now, include an instruction to change it
// during element creation.
if (currentNamespace !== wasInNamespace) {
this.addNamespaceInstruction(currentNamespace, element);
}
var implicit = o.variable(CONTEXT_NAME);
var createSelfClosingInstruction = !hasStylingInstructions && element.children.length === 0 && element.outputs.length === 0;
if (createSelfClosingInstruction) {
this.instruction.apply(this, tslib_1.__spread([this._creationCode, element.sourceSpan, R3.element], trimTrailingNulls(parameters)));
}
else {
// Generate the instruction create element instruction
if (i18nMessages.length > 0) {
(_c = this._creationCode).push.apply(_c, tslib_1.__spread(i18nMessages));
}
this.instruction.apply(this, tslib_1.__spread([this._creationCode, element.sourceSpan, R3.elementStart], trimTrailingNulls(parameters)));
// initial styling for static style="..." attributes
if (hasStylingInstructions) {
var paramsList = [];
if (initialClassDeclarations.length) {
// the template compiler handles initial class styling (e.g. class="foo") values
// in a special command called `elementClass` so that the initial class
// can be processed during runtime. These initial class values are bound to
// a constant because the inital class values do not change (since they're static).
paramsList.push(this.constantPool.getConstLiteral(o.literalArr(initialClassDeclarations), true));
}
else if (initialStyleDeclarations.length || useDefaultStyleSanitizer) {
// no point in having an extra `null` value unless there are follow-up params
paramsList.push(o.NULL_EXPR);
}
if (initialStyleDeclarations.length) {
// the template compiler handles initial style (e.g. style="foo") values
// in a special command called `elementStyle` so that the initial styles
// can be processed during runtime. These initial styles values are bound to
// a constant because the inital style values do not change (since they're static).
paramsList.push(this.constantPool.getConstLiteral(o.literalArr(initialStyleDeclarations), true));
}
else if (useDefaultStyleSanitizer) {
// no point in having an extra `null` value unless there are follow-up params
paramsList.push(o.NULL_EXPR);
}
if (useDefaultStyleSanitizer) {
paramsList.push(o.importExpr(R3.defaultStyleSanitizer));
}
this._creationCode.push(o.importExpr(R3.elementStyling).callFn(paramsList).toStmt());
}
// Generate Listeners (outputs)
element.outputs.forEach(function (outputAst) {
var elName = sanitizeIdentifier(element.name);
var evName = sanitizeIdentifier(outputAst.name);
var functionName = _this.templateName + "_" + elName + "_" + evName + "_listener";
var localVars = [];
var bindingScope = _this._bindingScope.nestedScope(function (lhsVar, rhsExpression) {
localVars.push(lhsVar.set(rhsExpression).toDeclStmt(o.INFERRED_TYPE, [o.StmtModifier.Final]));
});
var bindingExpr = convertActionBinding(bindingScope, implicit, outputAst.handler, 'b', function () { return error('Unexpected interpolation'); });
var handler = o.fn([new o.FnParam('$event', o.DYNAMIC_TYPE)], tslib_1.__spread(localVars, bindingExpr.render3Stmts), o.INFERRED_TYPE, null, functionName);
_this.instruction(_this._creationCode, outputAst.sourceSpan, R3.listener, o.literal(outputAst.name), handler);
});
}
if ((styleInputs.length || classInputs.length) && hasStylingInstructions) {
var indexLiteral = o.literal(elementIndex);
var firstStyle = styleInputs[0];
var mapBasedStyleInput = firstStyle && firstStyle.name == 'style' ? firstStyle : null;
var firstClass = classInputs[0];
var mapBasedClassInput = firstClass && isClassBinding(firstClass) ? firstClass : null;
var stylingInput = mapBasedStyleInput || mapBasedClassInput;
if (stylingInput) {
var params = [];
if (mapBasedClassInput) {
params.push(this.convertPropertyBinding(implicit, mapBasedClassInput.value, true));
}
else if (mapBasedStyleInput) {
params.push(o.NULL_EXPR);
}
if (mapBasedStyleInput) {
params.push(this.convertPropertyBinding(implicit, mapBasedStyleInput.value, true));
}
this.instruction.apply(this, tslib_1.__spread([this._bindingCode, stylingInput.sourceSpan, R3.elementStylingMap, indexLiteral], params));
}
var lastInputCommand = null;
if (styleInputs.length) {
var i = mapBasedStyleInput ? 1 : 0;
for (i; i < styleInputs.length; i++) {
var input = styleInputs[i];
var convertedBinding = this.convertPropertyBinding(implicit, input.value, true);
var params = [convertedBinding];
var sanitizationRef = resolveSanitizationFn(input, input.securityContext);
if (sanitizationRef) {
params.push(sanitizationRef);
}
var key = input.name;
var styleIndex = stylesIndexMap[key];
this.instruction.apply(this, tslib_1.__spread([this._bindingCode, input.sourceSpan, R3.elementStyleProp, indexLiteral,
o.literal(styleIndex)], params));
}
lastInputCommand = styleInputs[styleInputs.length - 1];
}
if (classInputs.length) {
var i = mapBasedClassInput ? 1 : 0;
for (i; i < classInputs.length; i++) {
var input = classInputs[i];
var convertedBinding = this.convertPropertyBinding(implicit, input.value, true);
var params = [convertedBinding];
var sanitizationRef = resolveSanitizationFn(input, input.securityContext);
if (sanitizationRef) {
params.push(sanitizationRef);
}
var key = input.name;
var classIndex = classesIndexMap[key];
this.instruction.apply(this, tslib_1.__spread([this._bindingCode, input.sourceSpan, R3.elementClassProp, indexLiteral,
o.literal(classIndex)], params));
}
lastInputCommand = classInputs[classInputs.length - 1];
}
this.instruction(this._bindingCode, lastInputCommand.sourceSpan, R3.elementStylingApply, indexLiteral);
}
// Generate element input bindings
allOtherInputs.forEach(function (input) {
if (input.type === 4 /* Animation */) {
console.error('warning: animation bindings not yet supported');
return;
}
var convertedBinding = _this.convertPropertyBinding(implicit, input.value);
var instruction = mapBindingToInstruction(input.type);
if (instruction) {
var params = [convertedBinding];
var sanitizationRef = resolveSanitizationFn(input, input.securityContext);
if (sanitizationRef) {
params.push(sanitizationRef);
}
// TODO(chuckj): runtime: security context?
_this.instruction.apply(_this, tslib_1.__spread([_this._bindingCode, input.sourceSpan, instruction, o.literal(elementIndex),
o.literal(input.name)], params));
}
else {
_this._unsupported("binding type " + input.type);
}
});
// Traverse element child nodes
if (this._inI18nSection && element.children.length == 1 &&
element.children[0] instanceof t.Text) {
var text = element.children[0];
this.visitSingleI18nTextChild(text, i18nMeta);
}
else {
t.visitAll(this, element.children);
}
if (!createSelfClosingInstruction) {
// Finish element construction mode.
this.instruction(this._creationCode, element.endSourceSpan || element.sourceSpan, R3.elementEnd);
}
// Restore the state before exiting this node
this._inI18nSection = wasInI18nSection;
};
TemplateDefinitionBuilder.prototype.visitTemplate = function (template) {
var _this = this;
var templateIndex = this.allocateDataSlot();
var elName = '';
if (template.children.length === 1 && template.children[0] instanceof t.Element) {
// When the template as a single child, derive the context name from the tag
elName = sanitizeIdentifier(template.children[0].name);
}
var contextName = elName ? this.contextName + "_" + elName : '';
var templateName = contextName ? contextName + "_Template_" + templateIndex : "Template_" + templateIndex;
var templateContext = "ctx" + this.level;
var parameters = [
o.literal(templateIndex),
o.variable(templateName),
o.TYPED_NULL_EXPR,
];
var attributeNames = [];
var attributeMap = {};
template.attributes.forEach(function (a) {
attributeNames.push(asLiteral(a.name), asLiteral(''));
attributeMap[a.name] = a.value;
});
// Match directives on template attributes
if (this.directiveMatcher) {
var selector = createCssSelector('ng-template', attributeMap);
this.directiveMatcher.match(selector, function (cssSelector, staticType) { _this.directives.add(staticType); });
}
if (attributeNames.length) {
parameters.push(this.constantPool.getConstLiteral(o.literalArr(attributeNames), true));
}
// e.g. C(1, C1Template)
this.instruction.apply(this, tslib_1.__spread([this._creationCode, template.sourceSpan, R3.containerCreate], trimTrailingNulls(parameters)));
// e.g. p(1, 'forOf', ɵb(ctx.items));
var context = o.variable(CONTEXT_NAME);
template.inputs.forEach(function (input) {
var convertedBinding = _this.convertPropertyBinding(context, input.value);
_this.instruction(_this._bindingCode, template.sourceSpan, R3.elementProperty, o.literal(templateIndex), o.literal(input.name), convertedBinding);
});
// Create the template function
var templateVisitor = new TemplateDefinitionBuilder(this.constantPool, templateContext, this._bindingScope, this.level + 1, contextName, templateName, [], this.directiveMatcher, this.directives, this.pipeTypeByName, this.pipes, this._namespace);
var templateFunctionExpr = templateVisitor.buildTemplateFunction(template.children, template.variables);
this._postfixCode.push(templateFunctionExpr.toDeclStmt(templateName, null));
};
TemplateDefinitionBuilder.prototype.visitBoundText = function (text) {
var nodeIndex = this.allocateDataSlot();
this.instruction(this._creationCode, text.sourceSpan, R3.text, o.literal(nodeIndex));
this.instruction(this._bindingCode, text.sourceSpan, R3.textBinding, o.literal(nodeIndex), this.convertPropertyBinding(o.variable(CONTEXT_NAME), text.value));
};
TemplateDefinitionBuilder.prototype.visitText = function (text) {
this.instruction(this._creationCode, text.sourceSpan, R3.text, o.literal(this.allocateDataSlot()), o.literal(text.value));
};
// When the content of the element is a single text node the translation can be inlined:
//
// `<p i18n="desc|mean">some content</p>`
// compiles to
// ```
// /**
// * @desc desc
// * @meaning mean
// */
// const MSG_XYZ = goog.getMsg('some content');
// i0.ɵT(1, MSG_XYZ);
// ```
TemplateDefinitionBuilder.prototype.visitSingleI18nTextChild = function (text, i18nMeta) {
var meta = parseI18nMeta(i18nMeta);
var variable = this.constantPool.getTranslation(text.value, meta);
this.instruction(this._creationCode, text.sourceSpan, R3.text, o.literal(this.allocateDataSlot()), variable);
};
TemplateDefinitionBuilder.prototype.allocateDataSlot = function () { return this._dataIndex++; };
TemplateDefinitionBuilder.prototype.bindingContext = function () { return "" + this._bindingContext++; };
TemplateDefinitionBuilder.prototype.instruction = function (statements, span, reference) {
var params = [];
for (var _i = 3; _i < arguments.length; _i++) {
params[_i - 3] = arguments[_i];
}
statements.push(o.importExpr(reference, null, span).callFn(params, span).toStmt());
};
TemplateDefinitionBuilder.prototype.convertPropertyBinding = function (implicit, value, skipBindFn) {
var _a, _b;
var pipesConvertedValue = value.visit(this._valueConverter);
if (pipesConvertedValue instanceof Interpolation) {
var convertedPropertyBinding = convertPropertyBinding(this, implicit, pipesConvertedValue, this.bindingContext(), BindingForm.TrySimple, interpolate);
(_a = this._bindingCode).push.apply(_a, tslib_1.__spread(convertedPropertyBinding.stmts));
return convertedPropertyBinding.currValExpr;
}
else {
var convertedPropertyBinding = convertPropertyBinding(this, implicit, pipesConvertedValue, this.bindingContext(), BindingForm.TrySimple, function () { return error('Unexpected interpolation'); });
(_b = this._bindingCode).push.apply(_b, tslib_1.__spread(convertedPropertyBinding.stmts));
var valExpr = convertedPropertyBinding.currValExpr;
return skipBindFn ? valExpr : o.importExpr(R3.bind).callFn([valExpr]);
}
};
return TemplateDefinitionBuilder;
}());
export { TemplateDefinitionBuilder };
var ValueConverter = /** @class */ (function (_super) {
tslib_1.__extends(ValueConverter, _super);
function ValueConverter(constantPool, allocateSlot, allocatePureFunctionSlots, definePipe) {
var _this = _super.call(this) || this;
_this.constantPool = constantPool;
_this.allocateSlot = allocateSlot;
_this.allocatePureFunctionSlots = allocatePureFunctionSlots;
_this.definePipe = definePipe;
return _this;
}
// AstMemoryEfficientTransformer
ValueConverter.prototype.visitPipe = function (pipe, context) {
// Allocate a slot to create the pipe
var slot = this.allocateSlot();
var slotPseudoLocal = "PIPE:" + slot;
// Allocate one slot for the result plus one slot per pipe argument
var pureFunctionSlot = this.allocatePureFunctionSlots(2 + pipe.args.length);
var target = new PropertyRead(pipe.span, new ImplicitReceiver(pipe.span), slotPseudoLocal);
var _a = pipeBindingCallInfo(pipe.args), identifier = _a.identifier, isVarLength = _a.isVarLength;
this.definePipe(pipe.name, slotPseudoLocal, slot, o.importExpr(identifier));
var args = tslib_1.__spread([pipe.exp], pipe.args);
var convertedArgs = isVarLength ? this.visitAll([new LiteralArray(pipe.span, args)]) : this.visitAll(args);
return new FunctionCall(pipe.span, target, tslib_1.__spread([
new LiteralPrimitive(pipe.span, slot),
new LiteralPrimitive(pipe.span, pureFunctionSlot)
], convertedArgs));
};
ValueConverter.prototype.visitLiteralArray = function (array, context) {
var _this = this;
return new BuiltinFunctionCall(array.span, this.visitAll(array.expressions), function (values) {
// If the literal has calculated (non-literal) elements transform it into
// calls to literal factories that compose the literal and will cache intermediate
// values. Otherwise, just return an literal array that contains the values.
var literal = o.literalArr(values);
return values.every(function (a) { return a.isConstant(); }) ?
_this.constantPool.getConstLiteral(literal, true) :
getLiteralFactory(_this.constantPool, literal, _this.allocatePureFunctionSlots);
});
};
ValueConverter.prototype.visitLiteralMap = function (map, context) {
var _this = this;
return new BuiltinFunctionCall(map.span, this.visitAll(map.values), function (values) {
// If the literal has calculated (non-literal) elements transform it into
// calls to literal factories that compose the literal and will cache intermediate
// values. Otherwise, just return an literal array that contains the values.
var literal = o.literalMap(values.map(function (value, index) { return ({ key: map.keys[index].key, value: value, quoted: map.keys[index].quoted }); }));
return values.every(function (a) { return a.isConstant(); }) ?
_this.constantPool.getConstLiteral(literal, true) :
getLiteralFactory(_this.constantPool, literal, _this.allocatePureFunctionSlots);
});
};
return ValueConverter;
}(AstMemoryEfficientTransformer));
// Pipes always have at least one parameter, the value they operate on
var pipeBindingIdentifiers = [R3.pipeBind1, R3.pipeBind2, R3.pipeBind3, R3.pipeBind4];
function pipeBindingCallInfo(args) {
var identifier = pipeBindingIdentifiers[args.length];
return {
identifier: identifier || R3.pipeBindV,
isVarLength: !identifier,
};
}
var pureFunctionIdentifiers = [
R3.pureFunction0, R3.pureFunction1, R3.pureFunction2, R3.pureFunction3, R3.pureFunction4,
R3.pureFunction5, R3.pureFunction6, R3.pureFunction7, R3.pureFunction8
];
function pureFunctionCallInfo(args) {
var identifier = pureFunctionIdentifiers[args.length];
return {
identifier: identifier || R3.pureFunctionV,
isVarLength: !identifier,
};
}
function getLiteralFactory(constantPool, literal, allocateSlots) {
var _a = constantPool.getLiteralFactory(literal), literalFactory = _a.literalFactory, literalFactoryArguments = _a.literalFactoryArguments;
// Allocate 1 slot for the result plus 1 per argument
var startSlot = allocateSlots(1 + literalFactoryArguments.length);
literalFactoryArguments.length > 0 || error("Expected arguments to a literal factory function");
var _b = pureFunctionCallInfo(literalFactoryArguments), identifier = _b.identifier, isVarLength = _b.isVarLength;
// Literal factories are pure functions that only need to be re-invoked when the parameters
// change.
var args = [
o.literal(startSlot),
literalFactory,
];
if (isVarLength) {
args.push(o.literalArr(literalFactoryArguments));
}
else {
args.push.apply(args, tslib_1.__spread(literalFactoryArguments));
}
return o.importExpr(identifier).callFn(args);
}
var BindingScope = /** @class */ (function () {
function BindingScope(parent, declareLocalVarCallback) {
if (parent === void 0) { parent = null; }
if (declareLocalVarCallback === void 0) { declareLocalVarCallback = noop; }
this.parent = parent;
this.declareLocalVarCallback = declareLocalVarCallback;
/**
* Keeps a map from local variables to their expressions.
*
* This is used when one refers to variable such as: 'let abc = a.b.c`.
* - key to the map is the string literal `"abc"`.
* - value `lhs` is the left hand side which is an AST representing `abc`.
* - value `rhs` is the right hand side which is an AST representing `a.b.c`.
* - value `declared` is true if the `declareLocalVarCallback` has been called for this scope
* already.
*/
this.map = new Map();
this.referenceNameIndex = 0;
}
Object.defineProperty(BindingScope, "ROOT_SCOPE", {
get: function () {
if (!BindingScope._ROOT_SCOPE) {
BindingScope._ROOT_SCOPE = new BindingScope().set('$event', o.variable('$event'));
}
return BindingScope._ROOT_SCOPE;
},
enumerable: true,
configurable: true
});
BindingScope.prototype.get = function (name) {
var current = this;
while (current) {
var value = current.map.get(name);
if (value != null) {
if (current !== this) {
// make a local copy and reset the `declared` state.
value = { lhs: value.lhs, rhs: value.rhs, declared: false };
// Cache the value locally.
this.map.set(name, value);
}
if (value.rhs && !value.declared) {
// if it is first time we are referencing the variable in the scope
// than invoke the callback to insert variable declaration.
this.declareLocalVarCallback(value.lhs, value.rhs);
value.declared = true;
}
return value.lhs;
}
current = current.parent;
}
return null;
};
/**
* Create a local variable for later reference.
*
* @param name Name of the variable.
* @param lhs AST representing the left hand side of the `let lhs = rhs;`.
* @param rhs AST representing the right hand side of the `let lhs = rhs;`. The `rhs` can be
* `undefined` for variable that are ambient such as `$event` and which don't have `rhs`
* declaration.
*/
BindingScope.prototype.set = function (name, lhs, rhs) {
!this.map.has(name) ||
error("The name " + name + " is already defined in scope to be " + this.map.get(name));
this.map.set(name, { lhs: lhs, rhs: rhs, declared: false });
return this;
};
BindingScope.prototype.getLocal = function (name) { return this.get(name); };
BindingScope.prototype.nestedScope = function (declareCallback) {
return new BindingScope(this, declareCallback);
};
BindingScope.prototype.freshReferenceName = function () {
var current = this;
// Find the top scope as it maintains the global reference count
while (current.parent)
current = current.parent;
var ref = "" + REFERENCE_PREFIX + current.referenceNameIndex++;
return ref;
};
return BindingScope;
}());
export { BindingScope };
/**
* Creates a `CssSelector` given a tag name and a map of attributes
*/
function createCssSelector(tag, attributes) {
var cssSelector = new CssSelector();
cssSelector.setElement(tag);
Object.getOwnPropertyNames(attributes).forEach(function (name) {
var value = attributes[name];
cssSelector.addAttribute(name, value);
if (name.toLowerCase() === 'class') {
var classes = value.trim().split(/\s+/g);
classes.forEach(function (className) { return cssSelector.addClassName(className); });
}
});
return cssSelector;
}
// Parse i18n metas like:
// - "@@id",
// - "description[@@id]",
// - "meaning|description[@@id]"
function parseI18nMeta(i18n) {
var _a, _b;
var meaning;
var description;
var id;
if (i18n) {
// TODO(vicb): figure out how to force a message ID with closure ?
var idIndex = i18n.indexOf(ID_SEPARATOR);
var descIndex = i18n.indexOf(MEANING_SEPARATOR);
var meaningAndDesc = void 0;
_a = tslib_1.__read((idIndex > -1) ? [i18n.slice(0, idIndex), i18n.slice(idIndex + 2)] : [i18n, ''], 2), meaningAndDesc = _a[0], id = _a[1];
_b = tslib_1.__read((descIndex > -1) ?
[meaningAndDesc.slice(0, descIndex), meaningAndDesc.slice(descIndex + 1)] :
['', meaningAndDesc], 2), meaning = _b[0], description = _b[1];
}
return { description: description, id: id, meaning: meaning };
}
function interpolate(args) {
args = args.slice(1); // Ignore the length prefix added for render2
switch (args.length) {
case 3:
return o.importExpr(R3.interpolation1).callFn(args);
case 5:
return o.importExpr(R3.interpolation2).callFn(args);
case 7:
return o.importExpr(R3.interpolation3).callFn(args);
case 9:
return o.importExpr(R3.interpolation4).callFn(args);
case 11:
return o.importExpr(R3.interpolation5).callFn(args);
case 13:
return o.importExpr(R3.interpolation6).callFn(args);
case 15:
return o.importExpr(R3.interpolation7).callFn(args);
case 17:
return o.importExpr(R3.interpolation8).callFn(args);
}
(args.length >= 19 && args.length % 2 == 1) ||
error("Invalid interpolation argument length " + args.length);
return o.importExpr(R3.interpolationV).callFn([o.literalArr(args)]);
}
/**
* Parse a template into render3 `Node`s and additional metadata, with no other dependencies.
*
* @param template text of the template to parse
* @param templateUrl URL to use for source mapping of the parsed template
*/
export function parseTemplate(template, templateUrl, options) {
if (options === void 0) { options = {}; }
var bindingParser = makeBindingParser();
var htmlParser = new HtmlParser();
var parseResult = htmlParser.parse(template, templateUrl);
if (parseResult.errors && parseResult.errors.length > 0) {
return { errors: parseResult.errors, nodes: [], hasNgContent: false, ngContentSelectors: [] };
}
var rootNodes = parseResult.rootNodes;
if (!options.preserveWhitespaces) {
rootNodes = html.visitAll(new WhitespaceVisitor(), rootNodes);
}
var _a = htmlAstToRender3Ast(rootNodes, bindingParser), nodes = _a.nodes, hasNgContent = _a.hasNgContent, ngContentSelectors = _a.ngContentSelectors, errors = _a.errors;
if (errors && errors.length > 0) {
return { errors: errors, nodes: [], hasNgContent: false, ngContentSelectors: [] };
}
return { nodes: nodes, hasNgContent: hasNgContent, ngContentSelectors: ngContentSelectors };
}
/**
* Construct a `BindingParser` with a default configuration.
*/
export function makeBindingParser() {
return new BindingParser(new Parser(new Lexer()), DEFAULT_INTERPOLATION_CONFIG, new DomElementSchemaRegistry(), null, []);
}
function isClassBinding(input) {
return input.name == 'className' || input.name == 'class';
}
function resolveSanitizationFn(input, context) {
switch (context) {
case core.SecurityContext.HTML:
return o.importExpr(R3.sanitizeHtml);
case core.SecurityContext.SCRIPT:
return o.importExpr(R3.sanitizeScript);
case core.SecurityContext.STYLE:
// the compiler does not fill in an instruction for [style.prop?] binding
// values because the style algorithm knows internally what props are subject
// to sanitization (only [attr.style] values are explicitly sanitized)
return input.type === 1 /* Attribute */ ? o.importExpr(R3.sanitizeStyle) : null;
case core.SecurityContext.URL:
return o.importExpr(R3.sanitizeUrl);
case core.SecurityContext.RESOURCE_URL:
return o.importExpr(R3.sanitizeResourceUrl);
default:
return null;
}
}
function isStyleSanitizable(prop) {
switch (prop) {
case 'background-image':
case 'background':
case 'border-image':
case 'filter':
case 'list-style':
case 'list-style-image':
return true;
}
return false;
}
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidGVtcGxhdGUuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi8uLi8uLi8uLi8uLi8uLi8uLi8uLi9wYWNrYWdlcy9jb21waWxlci9zcmMvcmVuZGVyMy92aWV3L3RlbXBsYXRlLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBOzs7Ozs7R0FNRzs7QUFFSCxPQUFPLEVBQUMsT0FBTyxFQUFFLGtCQUFrQixFQUFDLE1BQU0sd0JBQXdCLENBQUM7QUFFbkUsT0FBTyxFQUFDLFdBQVcsRUFBRSxtQkFBbUIsRUFBaUIsb0JBQW9CLEVBQUUsc0JBQXNCLEVBQUMsTUFBTSwwQ0FBMEMsQ0FBQztBQUV2SixPQUFPLEtBQUssSUFBSSxNQUFNLFlBQVksQ0FBQztBQUNuQyxPQUFPLEVBQU0sNkJBQTZCLEVBQTRCLFlBQVksRUFBRSxnQkFBZ0IsRUFBRSxhQUFhLEVBQUUsWUFBWSxFQUFjLGdCQUFnQixFQUFFLFlBQVksRUFBQyxNQUFNLDZCQUE2QixDQUFDO0FBQ2xOLE9BQU8sRUFBQyxLQUFLLEVBQUMsTUFBTSwrQkFBK0IsQ0FBQztBQUNwRCxPQUFPLEVBQUMsTUFBTSxFQUFDLE1BQU0sZ0NBQWdDLENBQUM7QUFDdEQsT0FBTyxLQUFLLElBQUksTUFBTSxxQkFBcUIsQ0FBQztBQUM1QyxPQUFPLEVBQUMsVUFBVSxFQUFDLE1BQU0sNkJBQTZCLENBQUM7QUFDdkQsT0FBTyxFQUFDLGlCQUFpQixFQUFDLE1BQU0sa0NBQWtDLENBQUM7QUFDbkUsT0FBTyxFQUFDLDRCQUE0QixFQUFDLE1BQU0sc0NBQXNDLENBQUM7QUFDbEYsT0FBTyxFQUFDLFdBQVcsRUFBQyxNQUFNLHNCQUFzQixDQUFDO0FBQ2pELE9BQU8sS0FBSyxDQUFDLE1BQU0seUJBQXlCLENBQUM7QUFFN0MsT0FBTyxFQUFDLHdCQUF3QixFQUFDLE1BQU0sMENBQTBDLENBQUM7QUFDbEYsT0FBTyxFQUFDLFdBQVcsRUFBa0IsTUFBTSxnQkFBZ0IsQ0FBQztBQUM1RCxPQUFPLEVBQUMsYUFBYSxFQUFDLE1BQU0sc0NBQXNDLENBQUM7QUFDbkUsT0FBTyxFQUFnQixLQUFLLEVBQUMsTUFBTSxZQUFZLENBQUM7QUFDaEQsT0FBTyxLQUFLLENBQUMsTUFBTSxXQUFXLENBQUM7QUFDL0IsT0FBTyxFQUFDLFdBQVcsSUFBSSxFQUFFLEVBQUMsT