UNPKG

@angular/compiler

Version:

Angular - the compiler library

783 lines 313 kB
/** * @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/render3/view/template", ["require", "exports", "tslib", "@angular/compiler/src/compile_metadata", "@angular/compiler/src/compiler_util/expression_converter", "@angular/compiler/src/core", "@angular/compiler/src/expression_parser/ast", "@angular/compiler/src/expression_parser/lexer", "@angular/compiler/src/expression_parser/parser", "@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/interpolation_config", "@angular/compiler/src/ml_parser/tags", "@angular/compiler/src/output/map_util", "@angular/compiler/src/output/output_ast", "@angular/compiler/src/schema/dom_element_schema_registry", "@angular/compiler/src/selector", "@angular/compiler/src/template_parser/binding_parser", "@angular/compiler/src/util", "@angular/compiler/src/render3/r3_ast", "@angular/compiler/src/render3/r3_identifiers", "@angular/compiler/src/render3/r3_template_transform", "@angular/compiler/src/render3/util", "@angular/compiler/src/render3/view/i18n/context", "@angular/compiler/src/render3/view/i18n/get_msg_utils", "@angular/compiler/src/render3/view/i18n/localize_utils", "@angular/compiler/src/render3/view/i18n/meta", "@angular/compiler/src/render3/view/i18n/util", "@angular/compiler/src/render3/view/styling_builder", "@angular/compiler/src/render3/view/util"], factory); } })(function (require, exports) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.getTranslationDeclStmts = exports.resolveSanitizationFn = exports.makeBindingParser = exports.parseTemplate = exports.createCssSelector = exports.BindingScope = exports.ValueConverter = exports.TemplateDefinitionBuilder = exports.prepareEventListenerParameters = exports.renderFlagCheckIfStmt = exports.LEADING_TRIVIA_CHARS = void 0; var tslib_1 = require("tslib"); var compile_metadata_1 = require("@angular/compiler/src/compile_metadata"); var expression_converter_1 = require("@angular/compiler/src/compiler_util/expression_converter"); var core = require("@angular/compiler/src/core"); var ast_1 = require("@angular/compiler/src/expression_parser/ast"); var lexer_1 = require("@angular/compiler/src/expression_parser/lexer"); var parser_1 = require("@angular/compiler/src/expression_parser/parser"); 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 interpolation_config_1 = require("@angular/compiler/src/ml_parser/interpolation_config"); var tags_1 = require("@angular/compiler/src/ml_parser/tags"); var map_util_1 = require("@angular/compiler/src/output/map_util"); var o = require("@angular/compiler/src/output/output_ast"); var dom_element_schema_registry_1 = require("@angular/compiler/src/schema/dom_element_schema_registry"); var selector_1 = require("@angular/compiler/src/selector"); var binding_parser_1 = require("@angular/compiler/src/template_parser/binding_parser"); var util_1 = require("@angular/compiler/src/util"); var t = require("@angular/compiler/src/render3/r3_ast"); var r3_identifiers_1 = require("@angular/compiler/src/render3/r3_identifiers"); var r3_template_transform_1 = require("@angular/compiler/src/render3/r3_template_transform"); var util_2 = require("@angular/compiler/src/render3/util"); var context_1 = require("@angular/compiler/src/render3/view/i18n/context"); var get_msg_utils_1 = require("@angular/compiler/src/render3/view/i18n/get_msg_utils"); var localize_utils_1 = require("@angular/compiler/src/render3/view/i18n/localize_utils"); var meta_1 = require("@angular/compiler/src/render3/view/i18n/meta"); var util_3 = require("@angular/compiler/src/render3/view/i18n/util"); var styling_builder_1 = require("@angular/compiler/src/render3/view/styling_builder"); var util_4 = require("@angular/compiler/src/render3/view/util"); // Selector attribute name of `<ng-content>` var NG_CONTENT_SELECT_ATTR = 'select'; // Attribute name of `ngProjectAs`. var NG_PROJECT_AS_ATTR_NAME = 'ngProjectAs'; // Global symbols available only inside event bindings. var EVENT_BINDING_SCOPE_GLOBALS = new Set(['$event']); // List of supported global targets for event listeners var GLOBAL_TARGET_RESOLVERS = new Map([['window', r3_identifiers_1.Identifiers.resolveWindow], ['document', r3_identifiers_1.Identifiers.resolveDocument], ['body', r3_identifiers_1.Identifiers.resolveBody]]); exports.LEADING_TRIVIA_CHARS = [' ', '\n', '\r', '\t']; // if (rf & flags) { .. } function renderFlagCheckIfStmt(flags, statements) { return o.ifStmt(o.variable(util_4.RENDER_FLAGS).bitwiseAnd(o.literal(flags), null, false), statements); } exports.renderFlagCheckIfStmt = renderFlagCheckIfStmt; function prepareEventListenerParameters(eventAst, handlerName, scope) { if (handlerName === void 0) { handlerName = null; } if (scope === void 0) { scope = null; } var type = eventAst.type, name = eventAst.name, target = eventAst.target, phase = eventAst.phase, handler = eventAst.handler; if (target && !GLOBAL_TARGET_RESOLVERS.has(target)) { throw new Error("Unexpected global target '" + target + "' defined for '" + name + "' event.\n Supported list of global targets: " + Array.from(GLOBAL_TARGET_RESOLVERS.keys()) + "."); } var eventArgumentName = '$event'; var implicitReceiverAccesses = new Set(); var implicitReceiverExpr = (scope === null || scope.bindingLevel === 0) ? o.variable(util_4.CONTEXT_NAME) : scope.getOrCreateSharedContextVar(0); var bindingExpr = expression_converter_1.convertActionBinding(scope, implicitReceiverExpr, handler, 'b', function () { return util_1.error('Unexpected interpolation'); }, eventAst.handlerSpan, implicitReceiverAccesses, EVENT_BINDING_SCOPE_GLOBALS); var statements = []; if (scope) { statements.push.apply(statements, tslib_1.__spread(scope.restoreViewStatement())); statements.push.apply(statements, tslib_1.__spread(scope.variableDeclarations())); } statements.push.apply(statements, tslib_1.__spread(bindingExpr.render3Stmts)); var eventName = type === 1 /* Animation */ ? util_2.prepareSyntheticListenerName(name, phase) : name; var fnName = handlerName && compile_metadata_1.sanitizeIdentifier(handlerName); var fnArgs = []; if (implicitReceiverAccesses.has(eventArgumentName)) { fnArgs.push(new o.FnParam(eventArgumentName, o.DYNAMIC_TYPE)); } var handlerFn = o.fn(fnArgs, statements, o.INFERRED_TYPE, null, fnName); var params = [o.literal(eventName), handlerFn]; if (target) { params.push(o.literal(false), // `useCapture` flag, defaults to `false` o.importExpr(GLOBAL_TARGET_RESOLVERS.get(target))); } return params; } exports.prepareEventListenerParameters = prepareEventListenerParameters; function createComponentDefConsts() { return { prepareStatements: [], constExpressions: [] }; } var TemplateDefinitionBuilder = /** @class */ (function () { function TemplateDefinitionBuilder(constantPool, parentBindingScope, level, contextName, i18nContext, templateIndex, templateName, directiveMatcher, directives, pipeTypeByName, pipes, _namespace, relativeContextFilePath, i18nUseExternalIds, _constants) { var _this = this; if (level === void 0) { level = 0; } if (_constants === void 0) { _constants = createComponentDefConsts(); } this.constantPool = constantPool; this.level = level; this.contextName = contextName; this.i18nContext = i18nContext; this.templateIndex = templateIndex; this.templateName = templateName; this.directiveMatcher = directiveMatcher; this.directives = directives; this.pipeTypeByName = pipeTypeByName; this.pipes = pipes; this._namespace = _namespace; this.i18nUseExternalIds = i18nUseExternalIds; this._constants = _constants; this._dataIndex = 0; this._bindingContext = 0; this._prefixCode = []; /** * List of callbacks to generate creation mode instructions. We store them here as we process * the template so bindings in listeners are resolved only once all nodes have been visited. * This ensures all local refs and context variables are available for matching. */ this._creationCodeFns = []; /** * List of callbacks to generate update mode instructions. We store them here as we process * the template so bindings are resolved only once all nodes have been visited. This ensures * all local refs and context variables are available for matching. */ this._updateCodeFns = []; /** Index of the currently-selected node. */ this._currentIndex = 0; /** Temporary variable declarations generated from visiting pipes, literals, etc. */ this._tempVariables = []; /** * List of callbacks to build nested templates. Nested templates must not be visited until * after the parent template has finished visiting all of its nodes. This ensures that all * local ref bindings in nested templates are able to find local ref values if the refs * are defined after the template declaration. */ this._nestedTemplateFns = []; this._unsupported = util_4.unsupported; // i18n context local to this template this.i18n = null; // Number of slots to reserve for pureFunctions this._pureFunctionSlots = 0; // Number of binding slots this._bindingSlots = 0; // Projection slots found in the template. Projection slots can distribute projected // nodes based on a selector, or can just use the wildcard selector to match // all nodes which aren't matching any selector. this._ngContentReservedSlots = []; // Number of non-default selectors found in all parent templates of this template. We need to // track it to properly adjust projection slot index in the `projection` instruction. this._ngContentSelectorsOffset = 0; // Expression that should be used as implicit receiver when converting template // expressions to output AST. this._implicitReceiverExpr = null; // These should be handled in the template or element directly. this.visitReference = util_4.invalid; this.visitVariable = util_4.invalid; this.visitTextAttribute = util_4.invalid; this.visitBoundAttribute = util_4.invalid; this.visitBoundEvent = util_4.invalid; this._bindingScope = parentBindingScope.nestedScope(level); // Turn the relative context file path into an identifier by replacing non-alphanumeric // characters with underscores. this.fileBasedI18nSuffix = relativeContextFilePath.replace(/[^A-Za-z0-9]/g, '_') + '_'; this._valueConverter = new ValueConverter(constantPool, function () { return _this.allocateDataSlot(); }, function (numSlots) { return _this.allocatePureFunctionSlots(numSlots); }, function (name, localName, slot, value) { var pipeType = pipeTypeByName.get(name); if (pipeType) { _this.pipes.add(pipeType); } _this._bindingScope.set(_this.level, localName, value); _this.creationInstruction(null, r3_identifiers_1.Identifiers.pipe, [o.literal(slot), o.literal(name)]); }); } TemplateDefinitionBuilder.prototype.buildTemplateFunction = function (nodes, variables, ngContentSelectorsOffset, i18n) { var _this = this; if (ngContentSelectorsOffset === void 0) { ngContentSelectorsOffset = 0; } this._ngContentSelectorsOffset = ngContentSelectorsOffset; if (this._namespace !== r3_identifiers_1.Identifiers.namespaceHTML) { this.creationInstruction(null, this._namespace); } // Create variable bindings variables.forEach(function (v) { return _this.registerContextVariables(v); }); // Initiate i18n context in case: // - this template has parent i18n context // - or the template has i18n meta associated with it, // but it's not initiated by the Element (e.g. <ng-template i18n>) var initI18nContext = this.i18nContext || (util_3.isI18nRootNode(i18n) && !util_3.isSingleI18nIcu(i18n) && !(isSingleElementTemplate(nodes) && nodes[0].i18n === i18n)); var selfClosingI18nInstruction = hasTextChildrenOnly(nodes); if (initI18nContext) { this.i18nStart(null, i18n, selfClosingI18nInstruction); } // This is the initial pass through the nodes of this template. In this pass, we // queue all creation mode and update mode instructions for generation in the second // pass. It's necessary to separate the passes to ensure local refs are defined before // resolving bindings. We also count bindings in this pass as we walk bound expressions. t.visitAll(this, nodes); // Add total binding count to pure function count so pure function instructions are // generated with the correct slot offset when update instructions are processed. this._pureFunctionSlots += this._bindingSlots; // Pipes are walked in the first pass (to enqueue `pipe()` creation instructions and // `pipeBind` update instructions), so we have to update the slot offsets manually // to account for bindings. this._valueConverter.updatePipeSlotOffsets(this._bindingSlots); // Nested templates must be processed before creation instructions so template() // instructions can be generated with the correct internal const count. this._nestedTemplateFns.forEach(function (buildTemplateFn) { return buildTemplateFn(); }); // Output the `projectionDef` instruction when some `<ng-content>` tags are present. // The `projectionDef` instruction is only emitted for the component template and // is skipped for nested templates (<ng-template> tags). if (this.level === 0 && this._ngContentReservedSlots.length) { var parameters = []; // By default the `projectionDef` instructions creates one slot for the wildcard // selector if no parameters are passed. Therefore we only want to allocate a new // array for the projection slots if the default projection slot is not sufficient. if (this._ngContentReservedSlots.length > 1 || this._ngContentReservedSlots[0] !== '*') { var r3ReservedSlots = this._ngContentReservedSlots.map(function (s) { return s !== '*' ? core.parseSelectorToR3Selector(s) : s; }); parameters.push(this.constantPool.getConstLiteral(util_4.asLiteral(r3ReservedSlots), true)); } // Since we accumulate ngContent selectors while processing template elements, // we *prepend* `projectionDef` to creation instructions block, to put it before // any `projection` instructions this.creationInstruction(null, r3_identifiers_1.Identifiers.projectionDef, parameters, /* prepend */ true); } if (initI18nContext) { this.i18nEnd(null, selfClosingI18nInstruction); } // Generate all the creation mode instructions (e.g. resolve bindings in listeners) var creationStatements = this._creationCodeFns.map(function (fn) { return fn(); }); // Generate all the update mode instructions (e.g. resolve property or text bindings) var updateStatements = this._updateCodeFns.map(function (fn) { return fn(); }); // Variable declaration must occur after binding resolution so we can generate context // instructions that build on each other. // e.g. const b = nextContext().$implicit(); const b = nextContext(); var creationVariables = this._bindingScope.viewSnapshotStatements(); var updateVariables = this._bindingScope.variableDeclarations().concat(this._tempVariables); var creationBlock = creationStatements.length > 0 ? [renderFlagCheckIfStmt(1 /* Create */, creationVariables.concat(creationStatements))] : []; var updateBlock = updateStatements.length > 0 ? [renderFlagCheckIfStmt(2 /* Update */, updateVariables.concat(updateStatements))] : []; return o.fn( // i.e. (rf: RenderFlags, ctx: any) [new o.FnParam(util_4.RENDER_FLAGS, o.NUMBER_TYPE), new o.FnParam(util_4.CONTEXT_NAME, null)], tslib_1.__spread(this._prefixCode, creationBlock, updateBlock), o.INFERRED_TYPE, null, this.templateName); }; // LocalResolver TemplateDefinitionBuilder.prototype.getLocal = function (name) { return this._bindingScope.get(name); }; // LocalResolver TemplateDefinitionBuilder.prototype.notifyImplicitReceiverUse = function () { this._bindingScope.notifyImplicitReceiverUse(); }; TemplateDefinitionBuilder.prototype.i18nTranslate = function (message, params, ref, transformFn) { var _a; if (params === void 0) { params = {}; } var _ref = ref || this.i18nGenerateMainBlockVar(); // Closure Compiler requires const names to start with `MSG_` but disallows any other const to // start with `MSG_`. We define a variable starting with `MSG_` just for the `goog.getMsg` call var closureVar = this.i18nGenerateClosureVar(message.id); var statements = getTranslationDeclStmts(message, _ref, closureVar, params, transformFn); (_a = this._constants.prepareStatements).push.apply(_a, tslib_1.__spread(statements)); return _ref; }; TemplateDefinitionBuilder.prototype.registerContextVariables = function (variable) { var scopedName = this._bindingScope.freshReferenceName(); var retrievalLevel = this.level; var lhs = o.variable(variable.name + scopedName); this._bindingScope.set(retrievalLevel, variable.name, lhs, 1 /* CONTEXT */, function (scope, relativeLevel) { var rhs; if (scope.bindingLevel === retrievalLevel) { // e.g. ctx rhs = o.variable(util_4.CONTEXT_NAME); } else { var sharedCtxVar = scope.getSharedContextName(retrievalLevel); // e.g. ctx_r0 OR x(2); rhs = sharedCtxVar ? sharedCtxVar : generateNextContextExpr(relativeLevel); } // e.g. const $item$ = x(2).$implicit; return [lhs.set(rhs.prop(variable.value || util_4.IMPLICIT_REFERENCE)).toConstDecl()]; }); }; TemplateDefinitionBuilder.prototype.i18nAppendBindings = function (expressions) { var _this = this; if (expressions.length > 0) { expressions.forEach(function (expression) { return _this.i18n.appendBinding(expression); }); } }; TemplateDefinitionBuilder.prototype.i18nBindProps = function (props) { var _this = this; var bound = {}; Object.keys(props).forEach(function (key) { var prop = props[key]; if (prop instanceof t.Text) { bound[key] = o.literal(prop.value); } else { var value = prop.value.visit(_this._valueConverter); _this.allocateBindingSlots(value); if (value instanceof ast_1.Interpolation) { var strings = value.strings, expressions = value.expressions; var _a = _this.i18n, id = _a.id, bindings = _a.bindings; var label = util_3.assembleI18nBoundString(strings, bindings.size, id); _this.i18nAppendBindings(expressions); bound[key] = o.literal(label); } } }); return bound; }; // Generates top level vars for i18n blocks (i.e. `i18n_N`). TemplateDefinitionBuilder.prototype.i18nGenerateMainBlockVar = function () { return o.variable(this.constantPool.uniqueName(util_3.TRANSLATION_VAR_PREFIX)); }; // Generates vars with Closure-specific names for i18n blocks (i.e. `MSG_XXX`). TemplateDefinitionBuilder.prototype.i18nGenerateClosureVar = function (messageId) { var name; var suffix = this.fileBasedI18nSuffix.toUpperCase(); if (this.i18nUseExternalIds) { var prefix = util_3.getTranslationConstPrefix("EXTERNAL_"); var uniqueSuffix = this.constantPool.uniqueName(suffix); name = "" + prefix + compile_metadata_1.sanitizeIdentifier(messageId) + "$$" + uniqueSuffix; } else { var prefix = util_3.getTranslationConstPrefix(suffix); name = this.constantPool.uniqueName(prefix); } return o.variable(name); }; TemplateDefinitionBuilder.prototype.i18nUpdateRef = function (context) { var icus = context.icus, meta = context.meta, isRoot = context.isRoot, isResolved = context.isResolved, isEmitted = context.isEmitted; if (isRoot && isResolved && !isEmitted && !util_3.isSingleI18nIcu(meta)) { context.isEmitted = true; var placeholders = context.getSerializedPlaceholders(); var icuMapping_1 = {}; var params_1 = placeholders.size ? util_3.placeholdersToParams(placeholders) : {}; if (icus.size) { icus.forEach(function (refs, key) { if (refs.length === 1) { // if we have one ICU defined for a given // placeholder - just output its reference params_1[key] = refs[0]; } else { // ... otherwise we need to activate post-processing // to replace ICU placeholders with proper values var placeholder = util_3.wrapI18nPlaceholder("" + util_3.I18N_ICU_MAPPING_PREFIX + key); params_1[key] = o.literal(placeholder); icuMapping_1[key] = o.literalArr(refs); } }); } // translation requires post processing in 2 cases: // - if we have placeholders with multiple values (ex. `START_DIV`: [�#1�, �#2�, ...]) // - if we have multiple ICUs that refer to the same placeholder name var needsPostprocessing = Array.from(placeholders.values()).some(function (value) { return value.length > 1; }) || Object.keys(icuMapping_1).length; var transformFn = void 0; if (needsPostprocessing) { transformFn = function (raw) { var args = [raw]; if (Object.keys(icuMapping_1).length) { args.push(map_util_1.mapLiteral(icuMapping_1, true)); } return instruction(null, r3_identifiers_1.Identifiers.i18nPostprocess, args); }; } this.i18nTranslate(meta, params_1, context.ref, transformFn); } }; TemplateDefinitionBuilder.prototype.i18nStart = function (span, meta, selfClosing) { if (span === void 0) { span = null; } var index = this.allocateDataSlot(); this.i18n = this.i18nContext ? this.i18nContext.forkChildContext(index, this.templateIndex, meta) : new context_1.I18nContext(index, this.i18nGenerateMainBlockVar(), 0, this.templateIndex, meta); // generate i18nStart instruction var _a = this.i18n, id = _a.id, ref = _a.ref; var params = [o.literal(index), this.addToConsts(ref)]; if (id > 0) { // do not push 3rd argument (sub-block id) // into i18nStart call for top level i18n context params.push(o.literal(id)); } this.creationInstruction(span, selfClosing ? r3_identifiers_1.Identifiers.i18n : r3_identifiers_1.Identifiers.i18nStart, params); }; TemplateDefinitionBuilder.prototype.i18nEnd = function (span, selfClosing) { var _this = this; if (span === void 0) { span = null; } if (!this.i18n) { throw new Error('i18nEnd is executed with no i18n context present'); } if (this.i18nContext) { this.i18nContext.reconcileChildContext(this.i18n); this.i18nUpdateRef(this.i18nContext); } else { this.i18nUpdateRef(this.i18n); } // setup accumulated bindings var _a = this.i18n, index = _a.index, bindings = _a.bindings; if (bindings.size) { var chainBindings_1 = []; bindings.forEach(function (binding) { chainBindings_1.push({ sourceSpan: span, value: function () { return _this.convertPropertyBinding(binding); } }); }); // for i18n block, advance to the most recent element index (by taking the current number of // elements and subtracting one) before invoking `i18nExp` instructions, to make sure the // necessary lifecycle hooks of components/directives are properly flushed. this.updateInstructionChainWithAdvance(this.getConstCount() - 1, r3_identifiers_1.Identifiers.i18nExp, chainBindings_1); this.updateInstruction(span, r3_identifiers_1.Identifiers.i18nApply, [o.literal(index)]); } if (!selfClosing) { this.creationInstruction(span, r3_identifiers_1.Identifiers.i18nEnd); } this.i18n = null; // reset local i18n context }; TemplateDefinitionBuilder.prototype.i18nAttributesInstruction = function (nodeIndex, attrs, sourceSpan) { var _this = this; var hasBindings = false; var i18nAttrArgs = []; var bindings = []; attrs.forEach(function (attr) { var message = attr.i18n; var converted = attr.value.visit(_this._valueConverter); _this.allocateBindingSlots(converted); if (converted instanceof ast_1.Interpolation) { var placeholders = util_3.assembleBoundTextPlaceholders(message); var params = util_3.placeholdersToParams(placeholders); i18nAttrArgs.push(o.literal(attr.name), _this.i18nTranslate(message, params)); converted.expressions.forEach(function (expression) { hasBindings = true; bindings.push({ sourceSpan: sourceSpan, value: function () { return _this.convertPropertyBinding(expression); }, }); }); } }); if (bindings.length > 0) { this.updateInstructionChainWithAdvance(nodeIndex, r3_identifiers_1.Identifiers.i18nExp, bindings); } if (i18nAttrArgs.length > 0) { var index = o.literal(this.allocateDataSlot()); var constIndex = this.addToConsts(o.literalArr(i18nAttrArgs)); this.creationInstruction(sourceSpan, r3_identifiers_1.Identifiers.i18nAttributes, [index, constIndex]); if (hasBindings) { this.updateInstruction(sourceSpan, r3_identifiers_1.Identifiers.i18nApply, [index]); } } }; TemplateDefinitionBuilder.prototype.getNamespaceInstruction = function (namespaceKey) { switch (namespaceKey) { case 'math': return r3_identifiers_1.Identifiers.namespaceMathML; case 'svg': return r3_identifiers_1.Identifiers.namespaceSVG; default: return r3_identifiers_1.Identifiers.namespaceHTML; } }; TemplateDefinitionBuilder.prototype.addNamespaceInstruction = function (nsInstruction, element) { this._namespace = nsInstruction; this.creationInstruction(element.startSourceSpan, nsInstruction); }; /** * Adds an update instruction for an interpolated property or attribute, such as * `prop="{{value}}"` or `attr.title="{{value}}"` */ TemplateDefinitionBuilder.prototype.interpolatedUpdateInstruction = function (instruction, elementIndex, attrName, input, value, params) { var _this = this; this.updateInstructionWithAdvance(elementIndex, input.sourceSpan, instruction, function () { return tslib_1.__spread([o.literal(attrName)], _this.getUpdateInstructionArguments(value), params); }); }; TemplateDefinitionBuilder.prototype.visitContent = function (ngContent) { var slot = this.allocateDataSlot(); var projectionSlotIdx = this._ngContentSelectorsOffset + this._ngContentReservedSlots.length; var parameters = [o.literal(slot)]; this._ngContentReservedSlots.push(ngContent.selector); var nonContentSelectAttributes = ngContent.attributes.filter(function (attr) { return attr.name.toLowerCase() !== NG_CONTENT_SELECT_ATTR; }); var attributes = this.getAttributeExpressions(ngContent.name, nonContentSelectAttributes, [], []); if (attributes.length > 0) { parameters.push(o.literal(projectionSlotIdx), o.literalArr(attributes)); } else if (projectionSlotIdx !== 0) { parameters.push(o.literal(projectionSlotIdx)); } this.creationInstruction(ngContent.sourceSpan, r3_identifiers_1.Identifiers.projection, parameters); if (this.i18n) { this.i18n.appendProjection(ngContent.i18n, slot); } }; TemplateDefinitionBuilder.prototype.visitElement = function (element) { var e_1, _a; var _this = this; var _b, _c; var elementIndex = this.allocateDataSlot(); var stylingBuilder = new styling_builder_1.StylingBuilder(null); var isNonBindableMode = false; var isI18nRootElement = util_3.isI18nRootNode(element.i18n) && !util_3.isSingleI18nIcu(element.i18n); var outputAttrs = []; var _d = tslib_1.__read(tags_1.splitNsName(element.name), 2), namespaceKey = _d[0], elementName = _d[1]; var isNgContainer = tags_1.isNgContainer(element.name); try { // Handle styling, i18n, ngNonBindable 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, value = attr.value; if (name_1 === util_4.NON_BINDABLE_ATTR) { isNonBindableMode = true; } else if (name_1 === 'style') { stylingBuilder.registerStyleAttr(value); } else if (name_1 === 'class') { stylingBuilder.registerClassAttr(value); } else { outputAttrs.push(attr); } } } catch (e_1_1) { e_1 = { error: e_1_1 }; } finally { try { if (_f && !_f.done && (_a = _e.return)) _a.call(_e); } finally { if (e_1) throw e_1.error; } } // Match directives on non i18n attributes this.matchDirectives(element.name, element); // Regular element or ng-container creation mode var parameters = [o.literal(elementIndex)]; if (!isNgContainer) { parameters.push(o.literal(elementName)); } // Add the attributes var allOtherInputs = []; var boundI18nAttrs = []; element.inputs.forEach(function (input) { var stylingInputWasSet = stylingBuilder.registerBoundInput(input); if (!stylingInputWasSet) { if (input.type === 0 /* Property */ && input.i18n) { boundI18nAttrs.push(input); } else { allOtherInputs.push(input); } } }); // add attributes for directive and projection matching purposes var attributes = this.getAttributeExpressions(element.name, outputAttrs, allOtherInputs, element.outputs, stylingBuilder, [], boundI18nAttrs); parameters.push(this.addAttrsToConsts(attributes)); // local refs (ex.: <div #foo #bar="baz">) var refs = this.prepareRefsArray(element.references); parameters.push(this.addToConsts(refs)); 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); } if (this.i18n) { this.i18n.appendElement(element.i18n, elementIndex); } // Note that we do not append text node instructions and ICUs inside i18n section, // so we exclude them while calculating whether current element has children var hasChildren = (!isI18nRootElement && this.i18n) ? !hasTextChildrenOnly(element.children) : element.children.length > 0; var createSelfClosingInstruction = !stylingBuilder.hasBindingsWithPipes && element.outputs.length === 0 && boundI18nAttrs.length === 0 && !hasChildren; var createSelfClosingI18nInstruction = !createSelfClosingInstruction && hasTextChildrenOnly(element.children); if (createSelfClosingInstruction) { this.creationInstruction(element.sourceSpan, isNgContainer ? r3_identifiers_1.Identifiers.elementContainer : r3_identifiers_1.Identifiers.element, util_4.trimTrailingNulls(parameters)); } else { this.creationInstruction(element.startSourceSpan, isNgContainer ? r3_identifiers_1.Identifiers.elementContainerStart : r3_identifiers_1.Identifiers.elementStart, util_4.trimTrailingNulls(parameters)); if (isNonBindableMode) { this.creationInstruction(element.startSourceSpan, r3_identifiers_1.Identifiers.disableBindings); } if (boundI18nAttrs.length > 0) { this.i18nAttributesInstruction(elementIndex, boundI18nAttrs, (_b = element.startSourceSpan) !== null && _b !== void 0 ? _b : element.sourceSpan); } // Generate Listeners (outputs) if (element.outputs.length > 0) { var listeners = element.outputs.map(function (outputAst) { return ({ sourceSpan: outputAst.sourceSpan, params: _this.prepareListenerParameter(element.name, outputAst, elementIndex) }); }); this.creationInstructionChain(r3_identifiers_1.Identifiers.listener, listeners); } // Note: it's important to keep i18n/i18nStart instructions after i18nAttributes and // listeners, to make sure i18nAttributes instruction targets current element at runtime. if (isI18nRootElement) { this.i18nStart(element.startSourceSpan, element.i18n, createSelfClosingI18nInstruction); } } // the code here will collect all update-level styling instructions and add them to the // update block of the template function AOT code. Instructions like `styleProp`, // `styleMap`, `classMap`, `classProp` // are all generated and assigned in the code below. var stylingInstructions = stylingBuilder.buildUpdateLevelInstructions(this._valueConverter); var limit = stylingInstructions.length - 1; for (var i = 0; i <= limit; i++) { var instruction_1 = stylingInstructions[i]; this._bindingSlots += this.processStylingUpdateInstruction(elementIndex, instruction_1); } // the reason why `undefined` is used is because the renderer understands this as a // special value to symbolize that there is no RHS to this binding // TODO (matsko): revisit this once FW-959 is approached var emptyValueBindInstruction = o.literal(undefined); var propertyBindings = []; var attributeBindings = []; // Generate element input bindings allOtherInputs.forEach(function (input) { var inputType = input.type; if (inputType === 4 /* Animation */) { var value_1 = input.value.visit(_this._valueConverter); // animation bindings can be presented in the following formats: // 1. [@binding]="fooExp" // 2. [@binding]="{value:fooExp, params:{...}}" // 3. [@binding] // 4. @binding // All formats will be valid for when a synthetic binding is created. // The reasoning for this is because the renderer should get each // synthetic binding value in the order of the array that they are // defined in... var hasValue_1 = value_1 instanceof ast_1.LiteralPrimitive ? !!value_1.value : true; _this.allocateBindingSlots(value_1); propertyBindings.push({ name: util_2.prepareSyntheticPropertyName(input.name), sourceSpan: input.sourceSpan, value: function () { return hasValue_1 ? _this.convertPropertyBinding(value_1) : emptyValueBindInstruction; } }); } else { // we must skip attributes with associated i18n context, since these attributes are handled // separately and corresponding `i18nExp` and `i18nApply` instructions will be generated if (input.i18n) return; var value_2 = input.value.visit(_this._valueConverter); if (value_2 !== undefined) { var params_2 = []; var _a = tslib_1.__read(tags_1.splitNsName(input.name), 2), attrNamespace = _a[0], attrName_1 = _a[1]; var isAttributeBinding = inputType === 1 /* Attribute */; var sanitizationRef = resolveSanitizationFn(input.securityContext, isAttributeBinding); if (sanitizationRef) params_2.push(sanitizationRef); if (attrNamespace) { var namespaceLiteral = o.literal(attrNamespace); if (sanitizationRef) { params_2.push(namespaceLiteral); } else { // If there wasn't a sanitization ref, we need to add // an extra param so that we can pass in the namespace. params_2.push(o.literal(null), namespaceLiteral); } } _this.allocateBindingSlots(value_2); if (inputType === 0 /* Property */) { if (value_2 instanceof ast_1.Interpolation) { // prop="{{value}}" and friends _this.interpolatedUpdateInstruction(getPropertyInterpolationExpression(value_2), elementIndex, attrName_1, input, value_2, params_2); } else { // [prop]="value" // Collect all the properties so that we can chain into a single function at the end. propertyBindings.push({ name: attrName_1, sourceSpan: input.sourceSpan, value: function () { return _this.convertPropertyBinding(value_2); }, params: params_2 }); } } else if (inputType === 1 /* Attribute */) { if (value_2 instanceof ast_1.Interpolation && util_4.getInterpolationArgsLength(value_2) > 1) { // attr.name="text{{value}}" and friends _this.interpolatedUpdateInstruction(getAttributeInterpolationExpression(value_2), elementIndex, attrName_1, input, value_2, params_2); } else { var boundValue_1 = value_2 instanceof ast_1.Interpolation ? value_2.expressions[0] : value_2; // [attr.name]="value" or attr.name="{{value}}" // Collect the attribute bindings so that they can be chained at the end. attributeBindings.push({ name: attrName_1, sourceSpan: input.sourceSpan, value: function () { return _this.convertPropertyBinding(boundValue_1); }, params: params_2 }); } } else { // class prop _this.updateInstructionWithAdvance(elementIndex, input.sourceSpan, r3_identifiers_1.Identifiers.classProp, function () { return tslib_1.__spread([ o.literal(elementIndex), o.literal(attrName_1), _this.convertPropertyBinding(value_2) ], params_2); }); } } } }); if (propertyBindings.length > 0) { this.updateInstructionChainWithAdvance(elementIndex, r3_identifiers_1.Identifiers.property, propertyBindings); } if (attributeBindings.length > 0) { this.updateInstructionChainWithAdvance(elementIndex, r3_identifiers_1.Identifiers.attribute, attributeBindings); } // Traverse element child nodes t.visitAll(this, element.children); if (!isI18nRootElement && this.i18n) { this.i18n.appendElement(element.i18n, elementIndex, true); } if (!createSelfClosingInstruction) { // Finish element construction mode. var span = (_c = element.endSourceSpan) !== null && _c !== void 0 ? _c : element.sourceSpan; if (isI18nRootElement) { this.i18nEnd(span, createSelfClosingI18nInstruction); } if (isNonBindableMode) { this.creationInstruction(span, r3_identifiers_1.Identifiers.enableBindings); } this.creationInstruction(span, isNgContainer ? r3_identifiers_1.Identifiers.elementContainerEnd : r3_identifiers_1.Identifiers.elementEnd); } }; TemplateDefinitionBuilder.prototype.visitTemplate = function (template) { var _this = this; var _a; var NG_TEMPLATE_TAG_NAME = 'ng-template'; var templateIndex = this.allocateDataSlot(); if (this.i18n) { this.i18n.appendTemplate(template.i18n, templateIndex); } var tagName = compile_metadata_1.sanitizeIdentifier(template.tagName || ''); var contextName = "" + this.contextName + (tagName ? '_' + tagName : '') + "_" + templateIndex; var templateName = contextName + "_Template"; var parameters = [ o.literal(templateIndex), o.variable(templateName), // We don't care about the tag's namespace here, because we infer // it based on the parent nodes inside the template instruction. o.literal(template.tagName ? tags_1.splitNsName(template.tagName)[1] : template.tagName), ]; // find directives matching on a given <ng-template> node this.matchDirectives(NG_TEMPLATE_TAG_NAME, template); // prepare attributes parameter (including attributes used for directive matching) var attrsExprs = this.getAttributeExpressions(NG_TEMPLATE_TAG_NAME, template.attributes, template.inputs, template.outputs, undefined /* styles */, template.templateAttrs); parameters.push(this.addAttrsToConsts(attrsExprs)); // local refs (ex.: <ng-template #foo>) if (template.references && template.references.length) { var refs = this.prepareRefsArray(template.references); parameters.push(this.addToConsts(refs)); parameters.push(o.importExpr(r3_identifiers_1.Identifiers.templateRefExtractor)); } // Create the template function var templateVisitor = new TemplateDefinitionBuilder(this.constantPool, this._bindingScope, this.level + 1, contextName, this.i18n, templateIndex, templateName, this.directiveMatcher, this.directives, this.pipeTypeByName, this.pipes, this._namespace, this.fileBasedI18nSuffix, this.i18nUseExternalIds, this._constants); // Nested templates must not be visited until after their parent templates have completed // processing, so they are queued here until after the initial pass. Otherwise, we wouldn't // be able to support bindings in nested templates to local refs that occur after the // template definition. e.g. <div *ngIf="showing">{{ foo }}</div> <div #foo></div> this._nestedTemplateFns.push(function () { var _a; var templateFunctionExpr = templateVisitor.buildTemplateFunction(template.children, template.variables, _this._ngContentReservedSlots.length + _this._ngContentSelectorsOffset, template.i18n); _this.constantPool.statements.push(templateFunctionExpr.toDeclStmt(templateName)); if (templateVisitor._ngContentReservedSlots.length) { (_a = _this._ngContentReservedSlots).push.apply(_a, tslib_1.__spread(templateVisitor._ngContentReservedSlots)); } }); // e.g. template(1, MyComp_Template_1) this.creationInstruction(template.sourceSpan, r3_identifiers_1.Identifiers.templateCreate, function () { parameters.splice(2, 0, o.literal(templateVisitor.getConstCount()), o.literal(templateVisitor.getVarCount())); return util_4.trimTrailingNulls(parameters)