@angular-eslint/bundled-angular-compiler 17.2.2-alpha.6 → 17.2.2-alpha.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/index.js +2751 -776
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  'use strict';
2
2
 
3
3
  /**
4
- * @license Angular v17.1.0
4
+ * @license Angular v17.3.0
5
5
  * (c) 2010-2022 Google LLC. https://angular.io/
6
6
  * License: MIT
7
7
  */
@@ -2238,6 +2238,13 @@ class ConstantPool {
2238
2238
  this.literals = new Map();
2239
2239
  this.literalFactories = new Map();
2240
2240
  this.sharedConstants = new Map();
2241
+ /**
2242
+ * Constant pool also tracks claimed names from {@link uniqueName}.
2243
+ * This is useful to avoid collisions if variables are intended to be
2244
+ * named a certain way- but may conflict. We wouldn't want to always suffix
2245
+ * them with unique numbers.
2246
+ */
2247
+ this._claimedNames = new Map();
2241
2248
  this.nextNameIndex = 0;
2242
2249
  }
2243
2250
  getConstLiteral(literal, forceShared) {
@@ -2360,14 +2367,17 @@ class ConstantPool {
2360
2367
  return { literalFactory, literalFactoryArguments };
2361
2368
  }
2362
2369
  /**
2363
- * Produce a unique name.
2370
+ * Produce a unique name in the context of this pool.
2364
2371
  *
2365
2372
  * The name might be unique among different prefixes if any of the prefixes end in
2366
2373
  * a digit so the prefix should be a constant string (not based on user input) and
2367
2374
  * must not end in a digit.
2368
2375
  */
2369
- uniqueName(prefix) {
2370
- return `${prefix}${this.nextNameIndex++}`;
2376
+ uniqueName(name, alwaysIncludeSuffix = true) {
2377
+ const count = this._claimedNames.get(name) ?? 0;
2378
+ const result = count === 0 && !alwaysIncludeSuffix ? `${name}` : `${name}${count}`;
2379
+ this._claimedNames.set(name, count + 1);
2380
+ return result;
2371
2381
  }
2372
2382
  freshName() {
2373
2383
  return this.uniqueName(CONSTANT_PREFIX);
@@ -2630,6 +2640,14 @@ class Identifiers {
2630
2640
  static { this.viewQuery = { name: 'ɵɵviewQuery', moduleName: CORE }; }
2631
2641
  static { this.loadQuery = { name: 'ɵɵloadQuery', moduleName: CORE }; }
2632
2642
  static { this.contentQuery = { name: 'ɵɵcontentQuery', moduleName: CORE }; }
2643
+ // Signal queries
2644
+ static { this.viewQuerySignal = { name: 'ɵɵviewQuerySignal', moduleName: CORE }; }
2645
+ static { this.contentQuerySignal = { name: 'ɵɵcontentQuerySignal', moduleName: CORE }; }
2646
+ static { this.queryAdvance = { name: 'ɵɵqueryAdvance', moduleName: CORE }; }
2647
+ // Two-way bindings
2648
+ static { this.twoWayProperty = { name: 'ɵɵtwoWayProperty', moduleName: CORE }; }
2649
+ static { this.twoWayBindingSet = { name: 'ɵɵtwoWayBindingSet', moduleName: CORE }; }
2650
+ static { this.twoWayListener = { name: 'ɵɵtwoWayListener', moduleName: CORE }; }
2633
2651
  static { this.NgOnChangesFeature = { name: 'ɵɵNgOnChangesFeature', moduleName: CORE }; }
2634
2652
  static { this.InheritDefinitionFeature = { name: 'ɵɵInheritDefinitionFeature', moduleName: CORE }; }
2635
2653
  static { this.CopyDefinitionFeature = { name: 'ɵɵCopyDefinitionFeature', moduleName: CORE }; }
@@ -2659,6 +2677,7 @@ class Identifiers {
2659
2677
  // type-checking
2660
2678
  static { this.InputSignalBrandWriteType = { name: 'ɵINPUT_SIGNAL_BRAND_WRITE_TYPE', moduleName: CORE }; }
2661
2679
  static { this.UnwrapDirectiveSignalInputs = { name: 'ɵUnwrapDirectiveSignalInputs', moduleName: CORE }; }
2680
+ static { this.unwrapWritableSignal = { name: 'ɵunwrapWritableSignal', moduleName: CORE }; }
2662
2681
  }
2663
2682
 
2664
2683
  const DASH_CASE_REGEXP = /-+([a-z0-9])/g;
@@ -4948,6 +4967,8 @@ const CHAINABLE_INSTRUCTIONS = new Set([
4948
4967
  Identifiers.textInterpolate8,
4949
4968
  Identifiers.textInterpolateV,
4950
4969
  Identifiers.templateCreate,
4970
+ Identifiers.twoWayProperty,
4971
+ Identifiers.twoWayListener,
4951
4972
  ]);
4952
4973
  /** Generates a call to a single instruction. */
4953
4974
  function invokeInstruction(span, reference, params) {
@@ -4958,11 +4979,11 @@ function invokeInstruction(span, reference, params) {
4958
4979
  *
4959
4980
  * A variable declaration is added to the statements the first time the allocator is invoked.
4960
4981
  */
4961
- function temporaryAllocator(statements, name) {
4982
+ function temporaryAllocator(pushStatement, name) {
4962
4983
  let temp = null;
4963
4984
  return () => {
4964
4985
  if (!temp) {
4965
- statements.push(new DeclareVarStmt(TEMPORARY_NAME, undefined, DYNAMIC_TYPE));
4986
+ pushStatement(new DeclareVarStmt(TEMPORARY_NAME, undefined, DYNAMIC_TYPE));
4966
4987
  temp = variable(name);
4967
4988
  }
4968
4989
  return temp;
@@ -5060,29 +5081,6 @@ function trimTrailingNulls(parameters) {
5060
5081
  }
5061
5082
  return parameters;
5062
5083
  }
5063
- function getQueryPredicate(query, constantPool) {
5064
- if (Array.isArray(query.predicate)) {
5065
- let predicate = [];
5066
- query.predicate.forEach((selector) => {
5067
- // Each item in predicates array may contain strings with comma-separated refs
5068
- // (for ex. 'ref, ref1, ..., refN'), thus we extract individual refs and store them
5069
- // as separate array entities
5070
- const selectors = selector.split(',').map(token => literal(token.trim()));
5071
- predicate.push(...selectors);
5072
- });
5073
- return constantPool.getConstLiteral(literalArr(predicate), true);
5074
- }
5075
- else {
5076
- // The original predicate may have been wrapped in a `forwardRef()` call.
5077
- switch (query.predicate.forwardRef) {
5078
- case 0 /* ForwardRefHandling.None */:
5079
- case 2 /* ForwardRefHandling.Unwrapped */:
5080
- return query.predicate.expression;
5081
- case 1 /* ForwardRefHandling.Wrapped */:
5082
- return importExpr(Identifiers.resolveForwardRef).callFn([query.predicate.expression]);
5083
- }
5084
- }
5085
- }
5086
5084
  /**
5087
5085
  * A representation for an object literal used during codegen of definition objects. The generic
5088
5086
  * type `T` allows to reference a documented type of the generated structure, such that the
@@ -5148,7 +5146,7 @@ function getAttrsForDirectiveMatching(elOrTpl) {
5148
5146
  }
5149
5147
  });
5150
5148
  elOrTpl.inputs.forEach(i => {
5151
- if (i.type === 0 /* BindingType.Property */) {
5149
+ if (i.type === 0 /* BindingType.Property */ || i.type === 5 /* BindingType.TwoWay */) {
5152
5150
  attributesMap[i.name] = '';
5153
5151
  }
5154
5152
  });
@@ -6897,10 +6895,9 @@ exports.ParsedPropertyType = void 0;
6897
6895
  ParsedPropertyType[ParsedPropertyType["DEFAULT"] = 0] = "DEFAULT";
6898
6896
  ParsedPropertyType[ParsedPropertyType["LITERAL_ATTR"] = 1] = "LITERAL_ATTR";
6899
6897
  ParsedPropertyType[ParsedPropertyType["ANIMATION"] = 2] = "ANIMATION";
6898
+ ParsedPropertyType[ParsedPropertyType["TWO_WAY"] = 3] = "TWO_WAY";
6900
6899
  })(exports.ParsedPropertyType || (exports.ParsedPropertyType = {}));
6901
6900
  class ParsedEvent {
6902
- // Regular events have a target
6903
- // Animation events have a phase
6904
6901
  constructor(name, targetOrPhase, type, handler, sourceSpan, handlerSpan, keySpan) {
6905
6902
  this.name = name;
6906
6903
  this.targetOrPhase = targetOrPhase;
@@ -6944,32 +6941,10 @@ class EventHandlerVars {
6944
6941
  * used in an action binding (e.g. an event handler).
6945
6942
  */
6946
6943
  function convertActionBinding(localResolver, implicitReceiver, action, bindingId, baseSourceSpan, implicitReceiverAccesses, globals) {
6947
- if (!localResolver) {
6948
- localResolver = new DefaultLocalResolver(globals);
6949
- }
6950
- const actionWithoutBuiltins = convertPropertyBindingBuiltins({
6951
- createLiteralArrayConverter: (argCount) => {
6952
- // Note: no caching for literal arrays in actions.
6953
- return (args) => literalArr(args);
6954
- },
6955
- createLiteralMapConverter: (keys) => {
6956
- // Note: no caching for literal maps in actions.
6957
- return (values) => {
6958
- const entries = keys.map((k, i) => ({
6959
- key: k.key,
6960
- value: values[i],
6961
- quoted: k.quoted,
6962
- }));
6963
- return literalMap(entries);
6964
- };
6965
- },
6966
- createPipeConverter: (name) => {
6967
- throw new Error(`Illegal State: Actions are not allowed to contain pipes. Pipe: ${name}`);
6968
- }
6969
- }, action);
6944
+ localResolver ??= new DefaultLocalResolver(globals);
6970
6945
  const visitor = new _AstToIrVisitor(localResolver, implicitReceiver, bindingId, /* supportsInterpolation */ false, baseSourceSpan, implicitReceiverAccesses);
6971
6946
  const actionStmts = [];
6972
- flattenStatements(actionWithoutBuiltins.visit(visitor, _Mode.Statement), actionStmts);
6947
+ flattenStatements(convertActionBuiltins(action).visit(visitor, _Mode.Statement), actionStmts);
6973
6948
  prependTemporaryDecls(visitor.temporaryCount, bindingId, actionStmts);
6974
6949
  if (visitor.usesImplicitReceiver) {
6975
6950
  localResolver.notifyImplicitReceiverUse();
@@ -6984,6 +6959,77 @@ function convertActionBinding(localResolver, implicitReceiver, action, bindingId
6984
6959
  }
6985
6960
  return actionStmts;
6986
6961
  }
6962
+ function convertAssignmentActionBinding(localResolver, implicitReceiver, action, bindingId, baseSourceSpan, implicitReceiverAccesses, globals) {
6963
+ localResolver ??= new DefaultLocalResolver(globals);
6964
+ const visitor = new _AstToIrVisitor(localResolver, implicitReceiver, bindingId, /* supportsInterpolation */ false, baseSourceSpan, implicitReceiverAccesses);
6965
+ let convertedAction = convertActionBuiltins(action).visit(visitor, _Mode.Statement);
6966
+ // This should already have been asserted in the parser, but we verify it here just in case.
6967
+ if (!(convertedAction instanceof ExpressionStatement)) {
6968
+ throw new Error(`Illegal state: unsupported expression in two-way action binding.`);
6969
+ }
6970
+ // Converts `[(ngModel)]="name"` to `twoWayBindingSet(ctx.name, $event) || (ctx.name = $event)`.
6971
+ convertedAction = wrapAssignmentAction(convertedAction.expr).toStmt();
6972
+ const actionStmts = [];
6973
+ flattenStatements(convertedAction, actionStmts);
6974
+ prependTemporaryDecls(visitor.temporaryCount, bindingId, actionStmts);
6975
+ // Assignment events always return `$event`.
6976
+ actionStmts.push(new ReturnStatement(EventHandlerVars.event));
6977
+ implicitReceiverAccesses?.add(EventHandlerVars.event.name);
6978
+ if (visitor.usesImplicitReceiver) {
6979
+ localResolver.notifyImplicitReceiverUse();
6980
+ }
6981
+ return actionStmts;
6982
+ }
6983
+ function wrapAssignmentReadExpression(ast) {
6984
+ return new ExternalExpr(Identifiers.twoWayBindingSet)
6985
+ .callFn([ast, EventHandlerVars.event])
6986
+ .or(ast.set(EventHandlerVars.event));
6987
+ }
6988
+ function isReadExpression$1(value) {
6989
+ return value instanceof ReadPropExpr || value instanceof ReadKeyExpr;
6990
+ }
6991
+ function wrapAssignmentAction(ast) {
6992
+ // The only officially supported expressions inside of a two-way binding are read expressions.
6993
+ if (isReadExpression$1(ast)) {
6994
+ return wrapAssignmentReadExpression(ast);
6995
+ }
6996
+ // However, historically the expression parser was handling two-way events by appending `=$event`
6997
+ // to the raw string before attempting to parse it. This has led to bugs over the years (see
6998
+ // #37809) and to unintentionally supporting unassignable events in the two-way binding. The
6999
+ // logic below aims to emulate the old behavior while still supporting the new output format
7000
+ // which uses `twoWayBindingSet`. Note that the generated code doesn't necessarily make sense
7001
+ // based on what the user wrote, for example the event binding for `[(value)]="a ? b : c"`
7002
+ // would produce `ctx.a ? ctx.b : ctx.c = $event`. We aim to reproduce what the parser used
7003
+ // to generate before #54154.
7004
+ if (ast instanceof BinaryOperatorExpr && isReadExpression$1(ast.rhs)) {
7005
+ // `a && b` -> `ctx.a && twoWayBindingSet(ctx.b, $event) || (ctx.b = $event)`
7006
+ return new BinaryOperatorExpr(ast.operator, ast.lhs, wrapAssignmentReadExpression(ast.rhs));
7007
+ }
7008
+ // Note: this also supports nullish coalescing expressions which
7009
+ // would've been downleveled to ternary expressions by this point.
7010
+ if (ast instanceof ConditionalExpr && isReadExpression$1(ast.falseCase)) {
7011
+ // `a ? b : c` -> `ctx.a ? ctx.b : twoWayBindingSet(ctx.c, $event) || (ctx.c = $event)`
7012
+ return new ConditionalExpr(ast.condition, ast.trueCase, wrapAssignmentReadExpression(ast.falseCase));
7013
+ }
7014
+ // `!!a` -> `twoWayBindingSet(ctx.a, $event) || (ctx.a = $event)`
7015
+ // Note: previously we'd actually produce `!!(ctx.a = $event)`, but the wrapping
7016
+ // node doesn't affect the result so we don't need to carry it over.
7017
+ if (ast instanceof NotExpr) {
7018
+ let expr = ast.condition;
7019
+ while (true) {
7020
+ if (expr instanceof NotExpr) {
7021
+ expr = expr.condition;
7022
+ }
7023
+ else {
7024
+ if (isReadExpression$1(expr)) {
7025
+ return wrapAssignmentReadExpression(expr);
7026
+ }
7027
+ break;
7028
+ }
7029
+ }
7030
+ }
7031
+ throw new Error(`Illegal state: unsupported expression in two-way action binding.`);
7032
+ }
6987
7033
  function convertPropertyBindingBuiltins(converterFactory, ast) {
6988
7034
  return convertBuiltins(converterFactory, ast);
6989
7035
  }
@@ -7067,6 +7113,29 @@ function convertBuiltins(converterFactory, ast) {
7067
7113
  const visitor = new _BuiltinAstConverter(converterFactory);
7068
7114
  return ast.visit(visitor);
7069
7115
  }
7116
+ function convertActionBuiltins(action) {
7117
+ const converterFactory = {
7118
+ createLiteralArrayConverter: () => {
7119
+ // Note: no caching for literal arrays in actions.
7120
+ return (args) => literalArr(args);
7121
+ },
7122
+ createLiteralMapConverter: (keys) => {
7123
+ // Note: no caching for literal maps in actions.
7124
+ return (values) => {
7125
+ const entries = keys.map((k, i) => ({
7126
+ key: k.key,
7127
+ value: values[i],
7128
+ quoted: k.quoted,
7129
+ }));
7130
+ return literalMap(entries);
7131
+ };
7132
+ },
7133
+ createPipeConverter: (name) => {
7134
+ throw new Error(`Illegal State: Actions are not allowed to contain pipes. Pipe: ${name}`);
7135
+ }
7136
+ };
7137
+ return convertPropertyBindingBuiltins(converterFactory, action);
7138
+ }
7070
7139
  function temporaryName(bindingId, temporaryNumber) {
7071
7140
  return `tmp_${bindingId}_${temporaryNumber}`;
7072
7141
  }
@@ -7809,6 +7878,10 @@ const animationKeywords = new Set([
7809
7878
  // `steps()` function
7810
7879
  'end', 'jump-both', 'jump-end', 'jump-none', 'jump-start', 'start'
7811
7880
  ]);
7881
+ /**
7882
+ * The following array contains all of the CSS at-rule identifiers which are scoped.
7883
+ */
7884
+ const scopedAtRuleIdentifiers = ['@media', '@supports', '@document', '@layer', '@container', '@scope', '@starting-style'];
7812
7885
  /**
7813
7886
  * The following class has its origin from a port of shadowCSS from webcomponents.js to TypeScript.
7814
7887
  * It has since diverge in many ways to tailor Angular's needs.
@@ -8296,9 +8369,7 @@ class ShadowCss {
8296
8369
  if (rule.selector[0] !== '@') {
8297
8370
  selector = this._scopeSelector(rule.selector, scopeSelector, hostSelector);
8298
8371
  }
8299
- else if (rule.selector.startsWith('@media') || rule.selector.startsWith('@supports') ||
8300
- rule.selector.startsWith('@document') || rule.selector.startsWith('@layer') ||
8301
- rule.selector.startsWith('@container') || rule.selector.startsWith('@scope')) {
8372
+ else if (scopedAtRuleIdentifiers.some(atRule => rule.selector.startsWith(atRule))) {
8302
8373
  content = this._scopeSelectors(rule.content, scopeSelector, hostSelector);
8303
8374
  }
8304
8375
  else if (rule.selector.startsWith('@font-face') || rule.selector.startsWith('@page')) {
@@ -8948,46 +9019,54 @@ var OpKind;
8948
9019
  * An update up for a repeater.
8949
9020
  */
8950
9021
  OpKind[OpKind["Repeater"] = 35] = "Repeater";
9022
+ /**
9023
+ * An operation to bind an expression to the property side of a two-way binding.
9024
+ */
9025
+ OpKind[OpKind["TwoWayProperty"] = 36] = "TwoWayProperty";
9026
+ /**
9027
+ * An operation declaring the event side of a two-way binding.
9028
+ */
9029
+ OpKind[OpKind["TwoWayListener"] = 37] = "TwoWayListener";
8951
9030
  /**
8952
9031
  * The start of an i18n block.
8953
9032
  */
8954
- OpKind[OpKind["I18nStart"] = 36] = "I18nStart";
9033
+ OpKind[OpKind["I18nStart"] = 38] = "I18nStart";
8955
9034
  /**
8956
9035
  * A self-closing i18n on a single element.
8957
9036
  */
8958
- OpKind[OpKind["I18n"] = 37] = "I18n";
9037
+ OpKind[OpKind["I18n"] = 39] = "I18n";
8959
9038
  /**
8960
9039
  * The end of an i18n block.
8961
9040
  */
8962
- OpKind[OpKind["I18nEnd"] = 38] = "I18nEnd";
9041
+ OpKind[OpKind["I18nEnd"] = 40] = "I18nEnd";
8963
9042
  /**
8964
9043
  * An expression in an i18n message.
8965
9044
  */
8966
- OpKind[OpKind["I18nExpression"] = 39] = "I18nExpression";
9045
+ OpKind[OpKind["I18nExpression"] = 41] = "I18nExpression";
8967
9046
  /**
8968
9047
  * An instruction that applies a set of i18n expressions.
8969
9048
  */
8970
- OpKind[OpKind["I18nApply"] = 40] = "I18nApply";
9049
+ OpKind[OpKind["I18nApply"] = 42] = "I18nApply";
8971
9050
  /**
8972
9051
  * An instruction to create an ICU expression.
8973
9052
  */
8974
- OpKind[OpKind["IcuStart"] = 41] = "IcuStart";
9053
+ OpKind[OpKind["IcuStart"] = 43] = "IcuStart";
8975
9054
  /**
8976
9055
  * An instruction to update an ICU expression.
8977
9056
  */
8978
- OpKind[OpKind["IcuEnd"] = 42] = "IcuEnd";
9057
+ OpKind[OpKind["IcuEnd"] = 44] = "IcuEnd";
8979
9058
  /**
8980
9059
  * An instruction representing a placeholder in an ICU expression.
8981
9060
  */
8982
- OpKind[OpKind["IcuPlaceholder"] = 43] = "IcuPlaceholder";
9061
+ OpKind[OpKind["IcuPlaceholder"] = 45] = "IcuPlaceholder";
8983
9062
  /**
8984
9063
  * An i18n context containing information needed to generate an i18n message.
8985
9064
  */
8986
- OpKind[OpKind["I18nContext"] = 44] = "I18nContext";
9065
+ OpKind[OpKind["I18nContext"] = 46] = "I18nContext";
8987
9066
  /**
8988
9067
  * A creation op that corresponds to i18n attributes on an element.
8989
9068
  */
8990
- OpKind[OpKind["I18nAttributes"] = 45] = "I18nAttributes";
9069
+ OpKind[OpKind["I18nAttributes"] = 47] = "I18nAttributes";
8991
9070
  })(OpKind || (OpKind = {}));
8992
9071
  /**
8993
9072
  * Distinguishes different kinds of IR expressions.
@@ -9074,31 +9153,22 @@ var ExpressionKind;
9074
9153
  * A reference to a temporary variable.
9075
9154
  */
9076
9155
  ExpressionKind[ExpressionKind["ReadTemporaryExpr"] = 19] = "ReadTemporaryExpr";
9077
- /**
9078
- * An expression representing a sanitizer function.
9079
- */
9080
- ExpressionKind[ExpressionKind["SanitizerExpr"] = 20] = "SanitizerExpr";
9081
- /**
9082
- * An expression representing a function to create trusted values.
9083
- */
9084
- ExpressionKind[ExpressionKind["TrustedValueFnExpr"] = 21] = "TrustedValueFnExpr";
9085
9156
  /**
9086
9157
  * An expression that will cause a literal slot index to be emitted.
9087
9158
  */
9088
- ExpressionKind[ExpressionKind["SlotLiteralExpr"] = 22] = "SlotLiteralExpr";
9159
+ ExpressionKind[ExpressionKind["SlotLiteralExpr"] = 20] = "SlotLiteralExpr";
9089
9160
  /**
9090
9161
  * A test expression for a conditional op.
9091
9162
  */
9092
- ExpressionKind[ExpressionKind["ConditionalCase"] = 23] = "ConditionalCase";
9163
+ ExpressionKind[ExpressionKind["ConditionalCase"] = 21] = "ConditionalCase";
9093
9164
  /**
9094
- * A variable for use inside a repeater, providing one of the ambiently-available context
9095
- * properties ($even, $first, etc.).
9165
+ * An expression that will be automatically extracted to the component const array.
9096
9166
  */
9097
- ExpressionKind[ExpressionKind["DerivedRepeaterVar"] = 24] = "DerivedRepeaterVar";
9167
+ ExpressionKind[ExpressionKind["ConstCollected"] = 22] = "ConstCollected";
9098
9168
  /**
9099
- * An expression that will be automatically extracted to the component const array.
9169
+ * Operation that sets the value of a two-way binding.
9100
9170
  */
9101
- ExpressionKind[ExpressionKind["ConstCollected"] = 25] = "ConstCollected";
9171
+ ExpressionKind[ExpressionKind["TwoWayBindingSet"] = 23] = "TwoWayBindingSet";
9102
9172
  })(ExpressionKind || (ExpressionKind = {}));
9103
9173
  var VariableFlags;
9104
9174
  (function (VariableFlags) {
@@ -9142,15 +9212,6 @@ var CompatibilityMode;
9142
9212
  CompatibilityMode[CompatibilityMode["Normal"] = 0] = "Normal";
9143
9213
  CompatibilityMode[CompatibilityMode["TemplateDefinitionBuilder"] = 1] = "TemplateDefinitionBuilder";
9144
9214
  })(CompatibilityMode || (CompatibilityMode = {}));
9145
- /**
9146
- * Enumeration of the different kinds of `@defer` secondary blocks.
9147
- */
9148
- var DeferSecondaryKind;
9149
- (function (DeferSecondaryKind) {
9150
- DeferSecondaryKind[DeferSecondaryKind["Loading"] = 0] = "Loading";
9151
- DeferSecondaryKind[DeferSecondaryKind["Placeholder"] = 1] = "Placeholder";
9152
- DeferSecondaryKind[DeferSecondaryKind["Error"] = 2] = "Error";
9153
- })(DeferSecondaryKind || (DeferSecondaryKind = {}));
9154
9215
  /**
9155
9216
  * Enumeration of the types of attributes which can be applied to an element.
9156
9217
  */
@@ -9184,6 +9245,10 @@ var BindingKind;
9184
9245
  * Animation property bindings.
9185
9246
  */
9186
9247
  BindingKind[BindingKind["Animation"] = 6] = "Animation";
9248
+ /**
9249
+ * Property side of a two-way binding.
9250
+ */
9251
+ BindingKind[BindingKind["TwoWayProperty"] = 7] = "TwoWayProperty";
9187
9252
  })(BindingKind || (BindingKind = {}));
9188
9253
  /**
9189
9254
  * Enumeration of possible times i18n params can be resolved.
@@ -9196,7 +9261,7 @@ var I18nParamResolutionTime;
9196
9261
  */
9197
9262
  I18nParamResolutionTime[I18nParamResolutionTime["Creation"] = 0] = "Creation";
9198
9263
  /**
9199
- * Param is resolved during post-processing. This should be used for params who's value comes from
9264
+ * Param is resolved during post-processing. This should be used for params whose value comes from
9200
9265
  * an ICU.
9201
9266
  */
9202
9267
  I18nParamResolutionTime[I18nParamResolutionTime["Postproccessing"] = 1] = "Postproccessing";
@@ -9223,7 +9288,7 @@ var I18nParamValueFlags;
9223
9288
  (function (I18nParamValueFlags) {
9224
9289
  I18nParamValueFlags[I18nParamValueFlags["None"] = 0] = "None";
9225
9290
  /**
9226
- * This value represtents an element tag.
9291
+ * This value represents an element tag.
9227
9292
  */
9228
9293
  I18nParamValueFlags[I18nParamValueFlags["ElementTag"] = 1] = "ElementTag";
9229
9294
  /**
@@ -9374,6 +9439,21 @@ const NEW_OP = {
9374
9439
  prev: null,
9375
9440
  next: null,
9376
9441
  };
9442
+
9443
+ /**
9444
+ * Create an `InterpolationTextOp`.
9445
+ */
9446
+ function createInterpolateTextOp(xref, interpolation, sourceSpan) {
9447
+ return {
9448
+ kind: OpKind.InterpolateText,
9449
+ target: xref,
9450
+ interpolation,
9451
+ sourceSpan,
9452
+ ...TRAIT_DEPENDS_ON_SLOT_CONTEXT,
9453
+ ...TRAIT_CONSUMES_VARS,
9454
+ ...NEW_OP,
9455
+ };
9456
+ }
9377
9457
  class Interpolation {
9378
9458
  constructor(strings, expressions, i18nPlaceholders) {
9379
9459
  this.strings = strings;
@@ -9384,6 +9464,27 @@ class Interpolation {
9384
9464
  }
9385
9465
  }
9386
9466
  }
9467
+ /**
9468
+ * Create a `BindingOp`, not yet transformed into a particular type of binding.
9469
+ */
9470
+ function createBindingOp(target, kind, name, expression, unit, securityContext, isTextAttribute, isStructuralTemplateAttribute, templateKind, i18nMessage, sourceSpan) {
9471
+ return {
9472
+ kind: OpKind.Binding,
9473
+ bindingKind: kind,
9474
+ target,
9475
+ name,
9476
+ expression,
9477
+ unit,
9478
+ securityContext,
9479
+ isTextAttribute,
9480
+ isStructuralTemplateAttribute,
9481
+ templateKind,
9482
+ i18nContext: null,
9483
+ i18nMessage,
9484
+ sourceSpan,
9485
+ ...NEW_OP,
9486
+ };
9487
+ }
9387
9488
  /**
9388
9489
  * Create a `PropertyOp`.
9389
9490
  */
@@ -9406,6 +9507,27 @@ function createPropertyOp(target, name, expression, isAnimationTrigger, security
9406
9507
  ...NEW_OP,
9407
9508
  };
9408
9509
  }
9510
+ /**
9511
+ * Create a `TwoWayPropertyOp`.
9512
+ */
9513
+ function createTwoWayPropertyOp(target, name, expression, securityContext, isStructuralTemplateAttribute, templateKind, i18nContext, i18nMessage, sourceSpan) {
9514
+ return {
9515
+ kind: OpKind.TwoWayProperty,
9516
+ target,
9517
+ name,
9518
+ expression,
9519
+ securityContext,
9520
+ sanitizer: null,
9521
+ isStructuralTemplateAttribute,
9522
+ templateKind,
9523
+ i18nContext,
9524
+ i18nMessage,
9525
+ sourceSpan,
9526
+ ...TRAIT_DEPENDS_ON_SLOT_CONTEXT,
9527
+ ...TRAIT_CONSUMES_VARS,
9528
+ ...NEW_OP,
9529
+ };
9530
+ }
9409
9531
  /** Create a `StylePropOp`. */
9410
9532
  function createStylePropOp(xref, name, expression, unit, sourceSpan) {
9411
9533
  return {
@@ -9495,6 +9617,47 @@ function createAdvanceOp(delta, sourceSpan) {
9495
9617
  ...NEW_OP,
9496
9618
  };
9497
9619
  }
9620
+ /**
9621
+ * Create a conditional op, which will display an embedded view according to a condtion.
9622
+ */
9623
+ function createConditionalOp(target, targetSlot, test, conditions, sourceSpan) {
9624
+ return {
9625
+ kind: OpKind.Conditional,
9626
+ target,
9627
+ targetSlot,
9628
+ test,
9629
+ conditions,
9630
+ processed: null,
9631
+ sourceSpan,
9632
+ contextValue: null,
9633
+ ...NEW_OP,
9634
+ ...TRAIT_DEPENDS_ON_SLOT_CONTEXT,
9635
+ ...TRAIT_CONSUMES_VARS,
9636
+ };
9637
+ }
9638
+ function createRepeaterOp(repeaterCreate, targetSlot, collection, sourceSpan) {
9639
+ return {
9640
+ kind: OpKind.Repeater,
9641
+ target: repeaterCreate,
9642
+ targetSlot,
9643
+ collection,
9644
+ sourceSpan,
9645
+ ...NEW_OP,
9646
+ ...TRAIT_DEPENDS_ON_SLOT_CONTEXT,
9647
+ };
9648
+ }
9649
+ function createDeferWhenOp(target, expr, prefetch, sourceSpan) {
9650
+ return {
9651
+ kind: OpKind.DeferWhen,
9652
+ target,
9653
+ expr,
9654
+ prefetch,
9655
+ sourceSpan,
9656
+ ...NEW_OP,
9657
+ ...TRAIT_DEPENDS_ON_SLOT_CONTEXT,
9658
+ ...TRAIT_CONSUMES_VARS,
9659
+ };
9660
+ }
9498
9661
  /**
9499
9662
  * Create an i18n expression op.
9500
9663
  */
@@ -9742,6 +9905,31 @@ class ResetViewExpr extends ExpressionBase {
9742
9905
  return new ResetViewExpr(this.expr.clone());
9743
9906
  }
9744
9907
  }
9908
+ class TwoWayBindingSetExpr extends ExpressionBase {
9909
+ constructor(target, value) {
9910
+ super();
9911
+ this.target = target;
9912
+ this.value = value;
9913
+ this.kind = ExpressionKind.TwoWayBindingSet;
9914
+ }
9915
+ visitExpression(visitor, context) {
9916
+ this.target.visitExpression(visitor, context);
9917
+ this.value.visitExpression(visitor, context);
9918
+ }
9919
+ isEquivalent(other) {
9920
+ return this.target.isEquivalent(other.target) && this.value.isEquivalent(other.value);
9921
+ }
9922
+ isConstant() {
9923
+ return false;
9924
+ }
9925
+ transformInternalExpressions(transform, flags) {
9926
+ this.target = transformExpressionsInExpression(this.target, transform, flags);
9927
+ this.value = transformExpressionsInExpression(this.value, transform, flags);
9928
+ }
9929
+ clone() {
9930
+ return new TwoWayBindingSetExpr(this.target, this.value);
9931
+ }
9932
+ }
9745
9933
  /**
9746
9934
  * Read of a variable declared as an `ir.VariableOp` and referenced through its `ir.XrefId`.
9747
9935
  */
@@ -10090,6 +10278,40 @@ class SlotLiteralExpr extends ExpressionBase {
10090
10278
  }
10091
10279
  transformInternalExpressions() { }
10092
10280
  }
10281
+ class ConditionalCaseExpr extends ExpressionBase {
10282
+ /**
10283
+ * Create an expression for one branch of a conditional.
10284
+ * @param expr The expression to be tested for this case. Might be null, as in an `else` case.
10285
+ * @param target The Xref of the view to be displayed if this condition is true.
10286
+ */
10287
+ constructor(expr, target, targetSlot, alias = null) {
10288
+ super();
10289
+ this.expr = expr;
10290
+ this.target = target;
10291
+ this.targetSlot = targetSlot;
10292
+ this.alias = alias;
10293
+ this.kind = ExpressionKind.ConditionalCase;
10294
+ }
10295
+ visitExpression(visitor, context) {
10296
+ if (this.expr !== null) {
10297
+ this.expr.visitExpression(visitor, context);
10298
+ }
10299
+ }
10300
+ isEquivalent(e) {
10301
+ return e instanceof ConditionalCaseExpr && e.expr === this.expr;
10302
+ }
10303
+ isConstant() {
10304
+ return true;
10305
+ }
10306
+ clone() {
10307
+ return new ConditionalCaseExpr(this.expr, this.target, this.targetSlot);
10308
+ }
10309
+ transformInternalExpressions(transform, flags) {
10310
+ if (this.expr !== null) {
10311
+ this.expr = transformExpressionsInExpression(this.expr, transform, flags);
10312
+ }
10313
+ }
10314
+ }
10093
10315
  class ConstCollectedExpr extends ExpressionBase {
10094
10316
  constructor(expr) {
10095
10317
  super();
@@ -10167,6 +10389,11 @@ function transformExpressionsInOp(op, transform, flags) {
10167
10389
  op.sanitizer =
10168
10390
  op.sanitizer && transformExpressionsInExpression(op.sanitizer, transform, flags);
10169
10391
  break;
10392
+ case OpKind.TwoWayProperty:
10393
+ op.expression = transformExpressionsInExpression(op.expression, transform, flags);
10394
+ op.sanitizer =
10395
+ op.sanitizer && transformExpressionsInExpression(op.sanitizer, transform, flags);
10396
+ break;
10170
10397
  case OpKind.I18nExpression:
10171
10398
  op.expression = transformExpressionsInExpression(op.expression, transform, flags);
10172
10399
  break;
@@ -10195,6 +10422,7 @@ function transformExpressionsInOp(op, transform, flags) {
10195
10422
  }
10196
10423
  break;
10197
10424
  case OpKind.Listener:
10425
+ case OpKind.TwoWayListener:
10198
10426
  for (const innerOp of op.handlerOps) {
10199
10427
  transformExpressionsInOp(innerOp, transform, flags | VisitorContextFlag.InChildOperation);
10200
10428
  }
@@ -10672,6 +10900,91 @@ const elementContainerOpKinds = new Set([
10672
10900
  function isElementOrContainerOp(op) {
10673
10901
  return elementContainerOpKinds.has(op.kind);
10674
10902
  }
10903
+ /**
10904
+ * Create an `ElementStartOp`.
10905
+ */
10906
+ function createElementStartOp(tag, xref, namespace, i18nPlaceholder, startSourceSpan, wholeSourceSpan) {
10907
+ return {
10908
+ kind: OpKind.ElementStart,
10909
+ xref,
10910
+ tag,
10911
+ handle: new SlotHandle(),
10912
+ attributes: null,
10913
+ localRefs: [],
10914
+ nonBindable: false,
10915
+ namespace,
10916
+ i18nPlaceholder,
10917
+ startSourceSpan,
10918
+ wholeSourceSpan,
10919
+ ...TRAIT_CONSUMES_SLOT,
10920
+ ...NEW_OP,
10921
+ };
10922
+ }
10923
+ /**
10924
+ * Create a `TemplateOp`.
10925
+ */
10926
+ function createTemplateOp(xref, templateKind, tag, functionNameSuffix, namespace, i18nPlaceholder, startSourceSpan, wholeSourceSpan) {
10927
+ return {
10928
+ kind: OpKind.Template,
10929
+ xref,
10930
+ templateKind,
10931
+ attributes: null,
10932
+ tag,
10933
+ handle: new SlotHandle(),
10934
+ functionNameSuffix,
10935
+ decls: null,
10936
+ vars: null,
10937
+ localRefs: [],
10938
+ nonBindable: false,
10939
+ namespace,
10940
+ i18nPlaceholder,
10941
+ startSourceSpan,
10942
+ wholeSourceSpan,
10943
+ ...TRAIT_CONSUMES_SLOT,
10944
+ ...NEW_OP,
10945
+ };
10946
+ }
10947
+ function createRepeaterCreateOp(primaryView, emptyView, tag, track, varNames, emptyTag, i18nPlaceholder, emptyI18nPlaceholder, startSourceSpan, wholeSourceSpan) {
10948
+ return {
10949
+ kind: OpKind.RepeaterCreate,
10950
+ attributes: null,
10951
+ xref: primaryView,
10952
+ handle: new SlotHandle(),
10953
+ emptyView,
10954
+ track,
10955
+ trackByFn: null,
10956
+ tag,
10957
+ emptyTag,
10958
+ emptyAttributes: null,
10959
+ functionNameSuffix: 'For',
10960
+ namespace: Namespace.HTML,
10961
+ nonBindable: false,
10962
+ localRefs: [],
10963
+ decls: null,
10964
+ vars: null,
10965
+ varNames,
10966
+ usesComponentInstance: false,
10967
+ i18nPlaceholder,
10968
+ emptyI18nPlaceholder,
10969
+ startSourceSpan,
10970
+ wholeSourceSpan,
10971
+ ...TRAIT_CONSUMES_SLOT,
10972
+ ...NEW_OP,
10973
+ ...TRAIT_CONSUMES_VARS,
10974
+ numSlotsUsed: emptyView === null ? 2 : 3,
10975
+ };
10976
+ }
10977
+ /**
10978
+ * Create an `ElementEndOp`.
10979
+ */
10980
+ function createElementEndOp(xref, sourceSpan) {
10981
+ return {
10982
+ kind: OpKind.ElementEnd,
10983
+ xref,
10984
+ sourceSpan,
10985
+ ...NEW_OP,
10986
+ };
10987
+ }
10675
10988
  function createDisableBindingsOp(xref) {
10676
10989
  return {
10677
10990
  kind: OpKind.DisableBindings,
@@ -10686,6 +10999,62 @@ function createEnableBindingsOp(xref) {
10686
10999
  ...NEW_OP,
10687
11000
  };
10688
11001
  }
11002
+ /**
11003
+ * Create a `TextOp`.
11004
+ */
11005
+ function createTextOp(xref, initialValue, icuPlaceholder, sourceSpan) {
11006
+ return {
11007
+ kind: OpKind.Text,
11008
+ xref,
11009
+ handle: new SlotHandle(),
11010
+ initialValue,
11011
+ icuPlaceholder,
11012
+ sourceSpan,
11013
+ ...TRAIT_CONSUMES_SLOT,
11014
+ ...NEW_OP,
11015
+ };
11016
+ }
11017
+ /**
11018
+ * Create a `ListenerOp`. Host bindings reuse all the listener logic.
11019
+ */
11020
+ function createListenerOp(target, targetSlot, name, tag, handlerOps, animationPhase, eventTarget, hostListener, sourceSpan) {
11021
+ const handlerList = new OpList();
11022
+ handlerList.push(handlerOps);
11023
+ return {
11024
+ kind: OpKind.Listener,
11025
+ target,
11026
+ targetSlot,
11027
+ tag,
11028
+ hostListener,
11029
+ name,
11030
+ handlerOps: handlerList,
11031
+ handlerFnName: null,
11032
+ consumesDollarEvent: false,
11033
+ isAnimationListener: animationPhase !== null,
11034
+ animationPhase,
11035
+ eventTarget,
11036
+ sourceSpan,
11037
+ ...NEW_OP,
11038
+ };
11039
+ }
11040
+ /**
11041
+ * Create a `TwoWayListenerOp`.
11042
+ */
11043
+ function createTwoWayListenerOp(target, targetSlot, name, tag, handlerOps, sourceSpan) {
11044
+ const handlerList = new OpList();
11045
+ handlerList.push(handlerOps);
11046
+ return {
11047
+ kind: OpKind.TwoWayListener,
11048
+ target,
11049
+ targetSlot,
11050
+ tag,
11051
+ name,
11052
+ handlerOps: handlerList,
11053
+ handlerFnName: null,
11054
+ sourceSpan,
11055
+ ...NEW_OP,
11056
+ };
11057
+ }
10689
11058
  function createPipeOp(xref, slot, name) {
10690
11059
  return {
10691
11060
  kind: OpKind.Pipe,
@@ -10710,6 +11079,21 @@ function createProjectionDefOp(def) {
10710
11079
  ...NEW_OP,
10711
11080
  };
10712
11081
  }
11082
+ function createProjectionOp(xref, selector, i18nPlaceholder, sourceSpan) {
11083
+ return {
11084
+ kind: OpKind.Projection,
11085
+ xref,
11086
+ handle: new SlotHandle(),
11087
+ selector,
11088
+ i18nPlaceholder,
11089
+ projectionSlotIndex: 0,
11090
+ attributes: null,
11091
+ localRefs: [],
11092
+ sourceSpan,
11093
+ ...NEW_OP,
11094
+ ...TRAIT_CONSUMES_SLOT,
11095
+ };
11096
+ }
10713
11097
  /**
10714
11098
  * Create an `ExtractedAttributeOp`.
10715
11099
  */
@@ -10728,6 +11112,42 @@ function createExtractedAttributeOp(target, bindingKind, namespace, name, expres
10728
11112
  ...NEW_OP,
10729
11113
  };
10730
11114
  }
11115
+ function createDeferOp(xref, main, mainSlot, metadata, resolverFn, sourceSpan) {
11116
+ return {
11117
+ kind: OpKind.Defer,
11118
+ xref,
11119
+ handle: new SlotHandle(),
11120
+ mainView: main,
11121
+ mainSlot,
11122
+ loadingView: null,
11123
+ loadingSlot: null,
11124
+ loadingConfig: null,
11125
+ loadingMinimumTime: null,
11126
+ loadingAfterTime: null,
11127
+ placeholderView: null,
11128
+ placeholderSlot: null,
11129
+ placeholderConfig: null,
11130
+ placeholderMinimumTime: null,
11131
+ errorView: null,
11132
+ errorSlot: null,
11133
+ metadata,
11134
+ resolverFn,
11135
+ sourceSpan,
11136
+ ...NEW_OP,
11137
+ ...TRAIT_CONSUMES_SLOT,
11138
+ numSlotsUsed: 2,
11139
+ };
11140
+ }
11141
+ function createDeferOnOp(defer, trigger, prefetch, sourceSpan) {
11142
+ return {
11143
+ kind: OpKind.DeferOn,
11144
+ defer,
11145
+ trigger,
11146
+ prefetch,
11147
+ sourceSpan,
11148
+ ...NEW_OP,
11149
+ };
11150
+ }
10731
11151
  /**
10732
11152
  * Create an `ExtractedMessageOp`.
10733
11153
  */
@@ -10775,6 +11195,30 @@ function createI18nEndOp(xref, sourceSpan) {
10775
11195
  ...NEW_OP,
10776
11196
  };
10777
11197
  }
11198
+ /**
11199
+ * Creates an ICU start op.
11200
+ */
11201
+ function createIcuStartOp(xref, message, messagePlaceholder, sourceSpan) {
11202
+ return {
11203
+ kind: OpKind.IcuStart,
11204
+ xref,
11205
+ message,
11206
+ messagePlaceholder,
11207
+ context: null,
11208
+ sourceSpan,
11209
+ ...NEW_OP,
11210
+ };
11211
+ }
11212
+ /**
11213
+ * Creates an ICU end op.
11214
+ */
11215
+ function createIcuEndOp(xref) {
11216
+ return {
11217
+ kind: OpKind.IcuEnd,
11218
+ xref,
11219
+ ...NEW_OP,
11220
+ };
11221
+ }
10778
11222
  /**
10779
11223
  * Creates an ICU placeholder op.
10780
11224
  */
@@ -10804,6 +11248,17 @@ function createI18nContextOp(contextKind, xref, i18nBlock, message, sourceSpan)
10804
11248
  ...NEW_OP,
10805
11249
  };
10806
11250
  }
11251
+ function createI18nAttributesOp(xref, handle, target) {
11252
+ return {
11253
+ kind: OpKind.I18nAttributes,
11254
+ xref,
11255
+ handle,
11256
+ target,
11257
+ i18nAttributesConfig: null,
11258
+ ...NEW_OP,
11259
+ ...TRAIT_CONSUMES_SLOT,
11260
+ };
11261
+ }
10807
11262
 
10808
11263
  function createHostPropertyOp(name, expression, isAnimationTrigger, i18nContext, securityContext, sourceSpan) {
10809
11264
  return {
@@ -10859,11 +11314,12 @@ class CompilationJob {
10859
11314
  * embedded views or host bindings.
10860
11315
  */
10861
11316
  class ComponentCompilationJob extends CompilationJob {
10862
- constructor(componentName, pool, compatibility, relativeContextFilePath, i18nUseExternalIds, deferBlocksMeta) {
11317
+ constructor(componentName, pool, compatibility, relativeContextFilePath, i18nUseExternalIds, deferBlocksMeta, allDeferrableDepsFn) {
10863
11318
  super(componentName, pool, compatibility);
10864
11319
  this.relativeContextFilePath = relativeContextFilePath;
10865
11320
  this.i18nUseExternalIds = i18nUseExternalIds;
10866
11321
  this.deferBlocksMeta = deferBlocksMeta;
11322
+ this.allDeferrableDepsFn = allDeferrableDepsFn;
10867
11323
  this.kind = CompilationJobKind.Tmpl;
10868
11324
  this.fnSuffix = 'Template';
10869
11325
  this.views = new Map();
@@ -10950,7 +11406,7 @@ class CompilationUnit {
10950
11406
  *ops() {
10951
11407
  for (const op of this.create) {
10952
11408
  yield op;
10953
- if (op.kind === OpKind.Listener) {
11409
+ if (op.kind === OpKind.Listener || op.kind === OpKind.TwoWayListener) {
10954
11410
  for (const listenerOp of op.handlerOps) {
10955
11411
  yield listenerOp;
10956
11412
  }
@@ -11190,12 +11646,17 @@ function extractAttributes(job) {
11190
11646
  bindingKind = BindingKind.Property;
11191
11647
  }
11192
11648
  OpList.insertBefore(
11193
- // Deliberaly null i18nMessage value
11649
+ // Deliberately null i18nMessage value
11194
11650
  createExtractedAttributeOp(op.target, bindingKind, null, op.name, /* expression */ null,
11195
11651
  /* i18nContext */ null,
11196
11652
  /* i18nMessage */ null, op.securityContext), lookupElement$2(elements, op.target));
11197
11653
  }
11198
11654
  break;
11655
+ case OpKind.TwoWayProperty:
11656
+ OpList.insertBefore(createExtractedAttributeOp(op.target, BindingKind.TwoWayProperty, null, op.name, /* expression */ null,
11657
+ /* i18nContext */ null,
11658
+ /* i18nMessage */ null, op.securityContext), lookupElement$2(elements, op.target));
11659
+ break;
11199
11660
  case OpKind.StyleProp:
11200
11661
  case OpKind.ClassProp:
11201
11662
  // TODO: Can style or class bindings be i18n attributes?
@@ -11229,6 +11690,15 @@ function extractAttributes(job) {
11229
11690
  }
11230
11691
  }
11231
11692
  break;
11693
+ case OpKind.TwoWayListener:
11694
+ // Two-way listeners aren't supported in host bindings.
11695
+ if (job.kind !== CompilationJobKind.Host) {
11696
+ const extractedAttributeOp = createExtractedAttributeOp(op.target, BindingKind.Property, null, op.name, /* expression */ null,
11697
+ /* i18nContext */ null,
11698
+ /* i18nMessage */ null, SecurityContext.NONE);
11699
+ OpList.insertBefore(extractedAttributeOp, lookupElement$2(elements, op.target));
11700
+ }
11701
+ break;
11232
11702
  }
11233
11703
  }
11234
11704
  }
@@ -11317,6 +11787,15 @@ function specializeBindings(job) {
11317
11787
  OpList.replace(op, createPropertyOp(op.target, op.name, op.expression, op.bindingKind === BindingKind.Animation, op.securityContext, op.isStructuralTemplateAttribute, op.templateKind, op.i18nContext, op.i18nMessage, op.sourceSpan));
11318
11788
  }
11319
11789
  break;
11790
+ case BindingKind.TwoWayProperty:
11791
+ if (!(op.expression instanceof Expression)) {
11792
+ // We shouldn't be able to hit this code path since interpolations in two-way bindings
11793
+ // result in a parser error. We assert here so that downstream we can assume that
11794
+ // the value is always an expression.
11795
+ throw new Error(`Expected value of two-way property binding "${op.name}" to be an expression`);
11796
+ }
11797
+ OpList.replace(op, createTwoWayPropertyOp(op.target, op.name, op.expression, op.securityContext, op.isStructuralTemplateAttribute, op.templateKind, op.i18nContext, op.i18nMessage, op.sourceSpan));
11798
+ break;
11320
11799
  case BindingKind.I18n:
11321
11800
  case BindingKind.ClassName:
11322
11801
  case BindingKind.StyleProperty:
@@ -11353,6 +11832,8 @@ const CHAINABLE = new Set([
11353
11832
  Identifiers.syntheticHostListener,
11354
11833
  Identifiers.syntheticHostProperty,
11355
11834
  Identifiers.templateCreate,
11835
+ Identifiers.twoWayProperty,
11836
+ Identifiers.twoWayListener,
11356
11837
  ]);
11357
11838
  /**
11358
11839
  * Post-process a reified view compilation and convert sequential calls to chainable instructions
@@ -11491,7 +11972,7 @@ function generateConditionalExpressions(job) {
11491
11972
  }
11492
11973
  }
11493
11974
 
11494
- new Map([
11975
+ const BINARY_OPERATORS = new Map([
11495
11976
  ['&&', exports.BinaryOperator.And],
11496
11977
  ['>', exports.BinaryOperator.Bigger],
11497
11978
  ['>=', exports.BinaryOperator.BiggerEquals],
@@ -11511,6 +11992,28 @@ new Map([
11511
11992
  ['||', exports.BinaryOperator.Or],
11512
11993
  ['+', exports.BinaryOperator.Plus],
11513
11994
  ]);
11995
+ function namespaceForKey(namespacePrefixKey) {
11996
+ const NAMESPACES = new Map([['svg', Namespace.SVG], ['math', Namespace.Math]]);
11997
+ if (namespacePrefixKey === null) {
11998
+ return Namespace.HTML;
11999
+ }
12000
+ return NAMESPACES.get(namespacePrefixKey) ?? Namespace.HTML;
12001
+ }
12002
+ function keyForNamespace(namespace) {
12003
+ const NAMESPACES = new Map([['svg', Namespace.SVG], ['math', Namespace.Math]]);
12004
+ for (const [k, n] of NAMESPACES.entries()) {
12005
+ if (n === namespace) {
12006
+ return k;
12007
+ }
12008
+ }
12009
+ return null; // No namespace prefix for HTML
12010
+ }
12011
+ function prefixWithNamespace(strippedTag, namespace) {
12012
+ if (namespace === Namespace.HTML) {
12013
+ return strippedTag;
12014
+ }
12015
+ return `:${keyForNamespace(namespace)}:${strippedTag}`;
12016
+ }
11514
12017
  function literalOrArrayLiteral(value) {
11515
12018
  if (Array.isArray(value)) {
11516
12019
  return literalArr(value.map(literalOrArrayLiteral));
@@ -11604,7 +12107,7 @@ class ElementAttributes {
11604
12107
  return this.byKind.get(BindingKind.StyleProperty) ?? FLYWEIGHT_ARRAY;
11605
12108
  }
11606
12109
  get bindings() {
11607
- return this.byKind.get(BindingKind.Property) ?? FLYWEIGHT_ARRAY;
12110
+ return this.propertyBindings ?? FLYWEIGHT_ARRAY;
11608
12111
  }
11609
12112
  get template() {
11610
12113
  return this.byKind.get(BindingKind.Template) ?? FLYWEIGHT_ARRAY;
@@ -11616,9 +12119,10 @@ class ElementAttributes {
11616
12119
  this.compatibility = compatibility;
11617
12120
  this.known = new Map();
11618
12121
  this.byKind = new Map;
12122
+ this.propertyBindings = null;
11619
12123
  this.projectAs = null;
11620
12124
  }
11621
- isKnown(kind, name, value) {
12125
+ isKnown(kind, name) {
11622
12126
  const nameToValue = this.known.get(kind) ?? new Set();
11623
12127
  this.known.set(kind, nameToValue);
11624
12128
  if (nameToValue.has(name)) {
@@ -11634,7 +12138,7 @@ class ElementAttributes {
11634
12138
  const allowDuplicates = this.compatibility === CompatibilityMode.TemplateDefinitionBuilder &&
11635
12139
  (kind === BindingKind.Attribute || kind === BindingKind.ClassName ||
11636
12140
  kind === BindingKind.StyleProperty);
11637
- if (!allowDuplicates && this.isKnown(kind, name, value)) {
12141
+ if (!allowDuplicates && this.isKnown(kind, name)) {
11638
12142
  return;
11639
12143
  }
11640
12144
  // TODO: Can this be its own phase
@@ -11665,10 +12169,16 @@ class ElementAttributes {
11665
12169
  }
11666
12170
  }
11667
12171
  arrayFor(kind) {
11668
- if (!this.byKind.has(kind)) {
11669
- this.byKind.set(kind, []);
12172
+ if (kind === BindingKind.Property || kind === BindingKind.TwoWayProperty) {
12173
+ this.propertyBindings ??= [];
12174
+ return this.propertyBindings;
12175
+ }
12176
+ else {
12177
+ if (!this.byKind.has(kind)) {
12178
+ this.byKind.set(kind, []);
12179
+ }
12180
+ return this.byKind.get(kind);
11670
12181
  }
11671
- return this.byKind.get(kind);
11672
12182
  }
11673
12183
  }
11674
12184
  /**
@@ -11764,11 +12274,16 @@ function createDeferDepsFns(job) {
11764
12274
  if (op.metadata.deps.length === 0) {
11765
12275
  continue;
11766
12276
  }
12277
+ if (op.resolverFn !== null) {
12278
+ continue;
12279
+ }
11767
12280
  const dependencies = [];
11768
12281
  for (const dep of op.metadata.deps) {
11769
12282
  if (dep.isDeferrable) {
11770
12283
  // Callback function, e.g. `m () => m.MyCmp;`.
11771
- const innerFn = arrowFn([new FnParam('m', DYNAMIC_TYPE)], variable('m').prop(dep.symbolName));
12284
+ const innerFn = arrowFn(
12285
+ // Default imports are always accessed through the `default` property.
12286
+ [new FnParam('m', DYNAMIC_TYPE)], variable('m').prop(dep.isDefaultImport ? 'default' : dep.symbolName));
11772
12287
  // Dynamic import, e.g. `import('./a').then(...)`.
11773
12288
  const importExpr = (new DynamicImportExpr(dep.importPath)).prop('then').callFn([innerFn]);
11774
12289
  dependencies.push(importExpr);
@@ -11782,7 +12297,8 @@ function createDeferDepsFns(job) {
11782
12297
  if (op.handle.slot === null) {
11783
12298
  throw new Error('AssertionError: slot must be assigned bfore extracting defer deps functions');
11784
12299
  }
11785
- op.resolverFn = job.pool.getSharedFunctionReference(depsFnExpr, `${job.componentName}_Defer_${op.handle.slot}_DepsFn`,
12300
+ const fullPathName = unit.fnName?.replace(`_Template`, ``);
12301
+ op.resolverFn = job.pool.getSharedFunctionReference(depsFnExpr, `${fullPathName}_Defer_${op.handle.slot}_DepsFn`,
11786
12302
  /* Don't use unique names for TDB compatibility */ false);
11787
12303
  }
11788
12304
  }
@@ -11870,7 +12386,7 @@ function createI18nContexts(job) {
11870
12386
  if (op.message.id !== currentI18nOp.message.id) {
11871
12387
  // This ICU is a sub-message inside its parent i18n block message. We need to give it
11872
12388
  // its own context.
11873
- const contextOp = createI18nContextOp(I18nContextKind.Icu, job.allocateXrefId(), currentI18nOp.xref, op.message, null);
12389
+ const contextOp = createI18nContextOp(I18nContextKind.Icu, job.allocateXrefId(), currentI18nOp.root, op.message, null);
11874
12390
  unit.create.push(contextOp);
11875
12391
  op.context = contextOp.xref;
11876
12392
  }
@@ -12374,7 +12890,7 @@ function createI18nMessage(job, context, messagePlaceholder) {
12374
12890
  */
12375
12891
  function formatIcuPlaceholder(op) {
12376
12892
  if (op.strings.length !== op.expressionPlaceholders.length + 1) {
12377
- throw Error(`AsserionError: Invalid ICU placeholder with ${op.strings.length} strings and ${op.expressionPlaceholders.length} expressions`);
12893
+ throw Error(`AssertionError: Invalid ICU placeholder with ${op.strings.length} strings and ${op.expressionPlaceholders.length} expressions`);
12378
12894
  }
12379
12895
  const values = op.expressionPlaceholders.map(formatValue);
12380
12896
  return op.strings.flatMap((str, i) => [str, values[i] || '']).join('');
@@ -12589,6 +13105,7 @@ function recursivelyProcessView(view, parentScope) {
12589
13105
  }
12590
13106
  break;
12591
13107
  case OpKind.Listener:
13108
+ case OpKind.TwoWayListener:
12592
13109
  // Prepend variables to listener handler functions.
12593
13110
  op.handlerOps.prepend(generateVariablesInScopeForView(view, scope));
12594
13111
  break;
@@ -13246,15 +13763,12 @@ class Parser$1 {
13246
13763
  this._lexer = _lexer;
13247
13764
  this.errors = [];
13248
13765
  }
13249
- parseAction(input, isAssignmentEvent, location, absoluteOffset, interpolationConfig = DEFAULT_INTERPOLATION_CONFIG) {
13766
+ parseAction(input, location, absoluteOffset, interpolationConfig = DEFAULT_INTERPOLATION_CONFIG) {
13250
13767
  this._checkNoInterpolation(input, location, interpolationConfig);
13251
13768
  const sourceToLex = this._stripComments(input);
13252
13769
  const tokens = this._lexer.tokenize(sourceToLex);
13253
- let flags = 1 /* ParseFlags.Action */;
13254
- if (isAssignmentEvent) {
13255
- flags |= 2 /* ParseFlags.AssignmentEvent */;
13256
- }
13257
- const ast = new _ParseAST(input, location, absoluteOffset, tokens, flags, this.errors, 0).parseChain();
13770
+ const ast = new _ParseAST(input, location, absoluteOffset, tokens, 1 /* ParseFlags.Action */, this.errors, 0)
13771
+ .parseChain();
13258
13772
  return new ASTWithSource(ast, input, location, absoluteOffset, this.errors);
13259
13773
  }
13260
13774
  parseBinding(input, location, absoluteOffset, interpolationConfig = DEFAULT_INTERPOLATION_CONFIG) {
@@ -13731,7 +14245,7 @@ class _ParseAST {
13731
14245
  let result = this.parseExpression();
13732
14246
  if (this.consumeOptionalOperator('|')) {
13733
14247
  if (this.parseFlags & 1 /* ParseFlags.Action */) {
13734
- this.error('Cannot have a pipe in an action expression');
14248
+ this.error(`Cannot have a pipe in an action expression`);
13735
14249
  }
13736
14250
  do {
13737
14251
  const nameStart = this.inputIndex;
@@ -14073,7 +14587,7 @@ class _ParseAST {
14073
14587
  const nameSpan = this.sourceSpan(nameStart);
14074
14588
  let receiver;
14075
14589
  if (isSafe) {
14076
- if (this.consumeOptionalAssignment()) {
14590
+ if (this.consumeOptionalOperator('=')) {
14077
14591
  this.error('The \'?.\' operator cannot be used in the assignment');
14078
14592
  receiver = new EmptyExpr$1(this.span(start), this.sourceSpan(start));
14079
14593
  }
@@ -14082,7 +14596,7 @@ class _ParseAST {
14082
14596
  }
14083
14597
  }
14084
14598
  else {
14085
- if (this.consumeOptionalAssignment()) {
14599
+ if (this.consumeOptionalOperator('=')) {
14086
14600
  if (!(this.parseFlags & 1 /* ParseFlags.Action */)) {
14087
14601
  this.error('Bindings cannot contain assignments');
14088
14602
  return new EmptyExpr$1(this.span(start), this.sourceSpan(start));
@@ -14109,22 +14623,6 @@ class _ParseAST {
14109
14623
  return isSafe ? new SafeCall(span, sourceSpan, receiver, args, argumentSpan) :
14110
14624
  new Call(span, sourceSpan, receiver, args, argumentSpan);
14111
14625
  }
14112
- consumeOptionalAssignment() {
14113
- // When parsing assignment events (originating from two-way-binding aka banana-in-a-box syntax),
14114
- // it is valid for the primary expression to be terminated by the non-null operator. This
14115
- // primary expression is substituted as LHS of the assignment operator to achieve
14116
- // two-way-binding, such that the LHS could be the non-null operator. The grammar doesn't
14117
- // naturally allow for this syntax, so assignment events are parsed specially.
14118
- if ((this.parseFlags & 2 /* ParseFlags.AssignmentEvent */) && this.next.isOperator('!') &&
14119
- this.peek(1).isOperator('=')) {
14120
- // First skip over the ! operator.
14121
- this.advance();
14122
- // Then skip over the = operator, to fully consume the optional assignment operator.
14123
- this.advance();
14124
- return true;
14125
- }
14126
- return this.consumeOptionalOperator('=');
14127
- }
14128
14626
  parseCallArguments() {
14129
14627
  if (this.next.isCharacter($RPAREN))
14130
14628
  return [];
@@ -14665,7 +15163,7 @@ const SCHEMA = [
14665
15163
  '[Element]|textContent,%ariaAtomic,%ariaAutoComplete,%ariaBusy,%ariaChecked,%ariaColCount,%ariaColIndex,%ariaColSpan,%ariaCurrent,%ariaDescription,%ariaDisabled,%ariaExpanded,%ariaHasPopup,%ariaHidden,%ariaKeyShortcuts,%ariaLabel,%ariaLevel,%ariaLive,%ariaModal,%ariaMultiLine,%ariaMultiSelectable,%ariaOrientation,%ariaPlaceholder,%ariaPosInSet,%ariaPressed,%ariaReadOnly,%ariaRelevant,%ariaRequired,%ariaRoleDescription,%ariaRowCount,%ariaRowIndex,%ariaRowSpan,%ariaSelected,%ariaSetSize,%ariaSort,%ariaValueMax,%ariaValueMin,%ariaValueNow,%ariaValueText,%classList,className,elementTiming,id,innerHTML,*beforecopy,*beforecut,*beforepaste,*fullscreenchange,*fullscreenerror,*search,*webkitfullscreenchange,*webkitfullscreenerror,outerHTML,%part,#scrollLeft,#scrollTop,slot' +
14666
15164
  /* added manually to avoid breaking changes */
14667
15165
  ',*message,*mozfullscreenchange,*mozfullscreenerror,*mozpointerlockchange,*mozpointerlockerror,*webglcontextcreationerror,*webglcontextlost,*webglcontextrestored',
14668
- '[HTMLElement]^[Element]|accessKey,autocapitalize,!autofocus,contentEditable,dir,!draggable,enterKeyHint,!hidden,innerText,inputMode,lang,nonce,*abort,*animationend,*animationiteration,*animationstart,*auxclick,*beforexrselect,*blur,*cancel,*canplay,*canplaythrough,*change,*click,*close,*contextmenu,*copy,*cuechange,*cut,*dblclick,*drag,*dragend,*dragenter,*dragleave,*dragover,*dragstart,*drop,*durationchange,*emptied,*ended,*error,*focus,*formdata,*gotpointercapture,*input,*invalid,*keydown,*keypress,*keyup,*load,*loadeddata,*loadedmetadata,*loadstart,*lostpointercapture,*mousedown,*mouseenter,*mouseleave,*mousemove,*mouseout,*mouseover,*mouseup,*mousewheel,*paste,*pause,*play,*playing,*pointercancel,*pointerdown,*pointerenter,*pointerleave,*pointermove,*pointerout,*pointerover,*pointerrawupdate,*pointerup,*progress,*ratechange,*reset,*resize,*scroll,*securitypolicyviolation,*seeked,*seeking,*select,*selectionchange,*selectstart,*slotchange,*stalled,*submit,*suspend,*timeupdate,*toggle,*transitioncancel,*transitionend,*transitionrun,*transitionstart,*volumechange,*waiting,*webkitanimationend,*webkitanimationiteration,*webkitanimationstart,*webkittransitionend,*wheel,outerText,!spellcheck,%style,#tabIndex,title,!translate,virtualKeyboardPolicy',
15166
+ '[HTMLElement]^[Element]|accessKey,autocapitalize,!autofocus,contentEditable,dir,!draggable,enterKeyHint,!hidden,!inert,innerText,inputMode,lang,nonce,*abort,*animationend,*animationiteration,*animationstart,*auxclick,*beforexrselect,*blur,*cancel,*canplay,*canplaythrough,*change,*click,*close,*contextmenu,*copy,*cuechange,*cut,*dblclick,*drag,*dragend,*dragenter,*dragleave,*dragover,*dragstart,*drop,*durationchange,*emptied,*ended,*error,*focus,*formdata,*gotpointercapture,*input,*invalid,*keydown,*keypress,*keyup,*load,*loadeddata,*loadedmetadata,*loadstart,*lostpointercapture,*mousedown,*mouseenter,*mouseleave,*mousemove,*mouseout,*mouseover,*mouseup,*mousewheel,*paste,*pause,*play,*playing,*pointercancel,*pointerdown,*pointerenter,*pointerleave,*pointermove,*pointerout,*pointerover,*pointerrawupdate,*pointerup,*progress,*ratechange,*reset,*resize,*scroll,*securitypolicyviolation,*seeked,*seeking,*select,*selectionchange,*selectstart,*slotchange,*stalled,*submit,*suspend,*timeupdate,*toggle,*transitioncancel,*transitionend,*transitionrun,*transitionstart,*volumechange,*waiting,*webkitanimationend,*webkitanimationiteration,*webkitanimationstart,*webkittransitionend,*wheel,outerText,!spellcheck,%style,#tabIndex,title,!translate,virtualKeyboardPolicy',
14669
15167
  'abbr,address,article,aside,b,bdi,bdo,cite,content,code,dd,dfn,dt,em,figcaption,figure,footer,header,hgroup,i,kbd,main,mark,nav,noscript,rb,rp,rt,rtc,ruby,s,samp,section,small,strong,sub,sup,u,var,wbr^[HTMLElement]|accessKey,autocapitalize,!autofocus,contentEditable,dir,!draggable,enterKeyHint,!hidden,innerText,inputMode,lang,nonce,*abort,*animationend,*animationiteration,*animationstart,*auxclick,*beforexrselect,*blur,*cancel,*canplay,*canplaythrough,*change,*click,*close,*contextmenu,*copy,*cuechange,*cut,*dblclick,*drag,*dragend,*dragenter,*dragleave,*dragover,*dragstart,*drop,*durationchange,*emptied,*ended,*error,*focus,*formdata,*gotpointercapture,*input,*invalid,*keydown,*keypress,*keyup,*load,*loadeddata,*loadedmetadata,*loadstart,*lostpointercapture,*mousedown,*mouseenter,*mouseleave,*mousemove,*mouseout,*mouseover,*mouseup,*mousewheel,*paste,*pause,*play,*playing,*pointercancel,*pointerdown,*pointerenter,*pointerleave,*pointermove,*pointerout,*pointerover,*pointerrawupdate,*pointerup,*progress,*ratechange,*reset,*resize,*scroll,*securitypolicyviolation,*seeked,*seeking,*select,*selectionchange,*selectstart,*slotchange,*stalled,*submit,*suspend,*timeupdate,*toggle,*transitioncancel,*transitionend,*transitionrun,*transitionstart,*volumechange,*waiting,*webkitanimationend,*webkitanimationiteration,*webkitanimationstart,*webkittransitionend,*wheel,outerText,!spellcheck,%style,#tabIndex,title,!translate,virtualKeyboardPolicy',
14670
15168
  'media^[HTMLElement]|!autoplay,!controls,%controlsList,%crossOrigin,#currentTime,!defaultMuted,#defaultPlaybackRate,!disableRemotePlayback,!loop,!muted,*encrypted,*waitingforkey,#playbackRate,preload,!preservesPitch,src,%srcObject,#volume',
14671
15169
  ':svg:^[HTMLElement]|!autofocus,nonce,*abort,*animationend,*animationiteration,*animationstart,*auxclick,*beforexrselect,*blur,*cancel,*canplay,*canplaythrough,*change,*click,*close,*contextmenu,*copy,*cuechange,*cut,*dblclick,*drag,*dragend,*dragenter,*dragleave,*dragover,*dragstart,*drop,*durationchange,*emptied,*ended,*error,*focus,*formdata,*gotpointercapture,*input,*invalid,*keydown,*keypress,*keyup,*load,*loadeddata,*loadedmetadata,*loadstart,*lostpointercapture,*mousedown,*mouseenter,*mouseleave,*mousemove,*mouseout,*mouseover,*mouseup,*mousewheel,*paste,*pause,*play,*playing,*pointercancel,*pointerdown,*pointerenter,*pointerleave,*pointermove,*pointerout,*pointerover,*pointerrawupdate,*pointerup,*progress,*ratechange,*reset,*resize,*scroll,*securitypolicyviolation,*seeked,*seeking,*select,*selectionchange,*selectstart,*slotchange,*stalled,*submit,*suspend,*timeupdate,*toggle,*transitioncancel,*transitionend,*transitionrun,*transitionstart,*volumechange,*waiting,*webkitanimationend,*webkitanimationiteration,*webkitanimationstart,*webkittransitionend,*wheel,%style,#tabIndex',
@@ -20202,7 +20700,10 @@ function nameFunctionsAndVariables(job) {
20202
20700
  }
20203
20701
  function addNamesToView(unit, baseName, state, compatibility) {
20204
20702
  if (unit.fnName === null) {
20205
- unit.fnName = sanitizeIdentifier(`${baseName}_${unit.job.fnSuffix}`);
20703
+ // Ensure unique names for view units. This is necessary because there might be multiple
20704
+ // components with same names in the context of the same pool. Only add the suffix
20705
+ // if really needed.
20706
+ unit.fnName = unit.job.pool.uniqueName(sanitizeIdentifier(`${baseName}_${unit.job.fnSuffix}`), /* alwaysIncludeSuffix */ false);
20206
20707
  }
20207
20708
  // Keep track of the names we assign to variables in the view. We'll need to propagate these
20208
20709
  // into reads of those variables afterwards.
@@ -20235,6 +20736,15 @@ function addNamesToView(unit, baseName, state, compatibility) {
20235
20736
  }
20236
20737
  op.handlerFnName = sanitizeIdentifier(op.handlerFnName);
20237
20738
  break;
20739
+ case OpKind.TwoWayListener:
20740
+ if (op.handlerFnName !== null) {
20741
+ break;
20742
+ }
20743
+ if (op.targetSlot.slot === null) {
20744
+ throw new Error(`Expected a slot to be assigned`);
20745
+ }
20746
+ op.handlerFnName = sanitizeIdentifier(`${unit.fnName}_${op.tag.replace('-', '_')}_${op.name}_${op.targetSlot.slot}_listener`);
20747
+ break;
20238
20748
  case OpKind.Variable:
20239
20749
  varNames.set(op.xref, getVariableName(unit, op.variable, state));
20240
20750
  break;
@@ -20248,7 +20758,7 @@ function addNamesToView(unit, baseName, state, compatibility) {
20248
20758
  if (op.emptyView !== null) {
20249
20759
  const emptyView = unit.job.views.get(op.emptyView);
20250
20760
  // Repeater empty view function is at slot +2 (metadata is in the first slot).
20251
- addNamesToView(emptyView, `${baseName}_${`${op.functionNameSuffix}Empty`}_${op.handle.slot + 2}`, state, compatibility);
20761
+ addNamesToView(emptyView, `${baseName}_${op.functionNameSuffix}Empty_${op.handle.slot + 2}`, state, compatibility);
20252
20762
  }
20253
20763
  // Repeater primary view function is at slot +1 (metadata is in the first slot).
20254
20764
  addNamesToView(unit.job.views.get(op.xref), `${baseName}_${op.functionNameSuffix}_${op.handle.slot + 1}`, state, compatibility);
@@ -20349,7 +20859,7 @@ function stripImportant(name) {
20349
20859
  function mergeNextContextExpressions(job) {
20350
20860
  for (const unit of job.units) {
20351
20861
  for (const op of unit.create) {
20352
- if (op.kind === OpKind.Listener) {
20862
+ if (op.kind === OpKind.Listener || op.kind === OpKind.TwoWayListener) {
20353
20863
  mergeNextContextsInOps(op.handlerOps);
20354
20864
  }
20355
20865
  }
@@ -20490,6 +21000,14 @@ function kindWithInterpolationTest(kind, interpolation) {
20490
21000
  return op.kind === kind && interpolation === op.expression instanceof Interpolation;
20491
21001
  };
20492
21002
  }
21003
+ function basicListenerKindTest(op) {
21004
+ return (op.kind === OpKind.Listener && !(op.hostListener && op.isAnimationListener)) ||
21005
+ op.kind === OpKind.TwoWayListener;
21006
+ }
21007
+ function nonInterpolationPropertyKindTest(op) {
21008
+ return (op.kind === OpKind.Property || op.kind === OpKind.TwoWayProperty) &&
21009
+ !(op.expression instanceof Interpolation);
21010
+ }
20493
21011
  /**
20494
21012
  * Defines the groups based on `OpKind` that ops will be divided into, for the various create
20495
21013
  * op kinds. Ops will be collected into groups, then optionally transformed, before recombining
@@ -20497,7 +21015,7 @@ function kindWithInterpolationTest(kind, interpolation) {
20497
21015
  */
20498
21016
  const CREATE_ORDERING = [
20499
21017
  { test: op => op.kind === OpKind.Listener && op.hostListener && op.isAnimationListener },
20500
- { test: op => op.kind === OpKind.Listener && !(op.hostListener && op.isAnimationListener) },
21018
+ { test: basicListenerKindTest },
20501
21019
  ];
20502
21020
  /**
20503
21021
  * Defines the groups based on `OpKind` that ops will be divided into, for the various update
@@ -20510,7 +21028,7 @@ const UPDATE_ORDERING = [
20510
21028
  { test: kindTest(OpKind.ClassProp) },
20511
21029
  { test: kindWithInterpolationTest(OpKind.Attribute, true) },
20512
21030
  { test: kindWithInterpolationTest(OpKind.Property, true) },
20513
- { test: kindWithInterpolationTest(OpKind.Property, false) },
21031
+ { test: nonInterpolationPropertyKindTest },
20514
21032
  { test: kindWithInterpolationTest(OpKind.Attribute, false) },
20515
21033
  ];
20516
21034
  /**
@@ -20529,8 +21047,9 @@ const UPDATE_HOST_ORDERING = [
20529
21047
  * The set of all op kinds we handle in the reordering phase.
20530
21048
  */
20531
21049
  const handledOpKinds = new Set([
20532
- OpKind.Listener, OpKind.StyleMap, OpKind.ClassMap, OpKind.StyleProp,
20533
- OpKind.ClassProp, OpKind.Property, OpKind.HostProperty, OpKind.Attribute
21050
+ OpKind.Listener, OpKind.TwoWayListener, OpKind.StyleMap, OpKind.ClassMap,
21051
+ OpKind.StyleProp, OpKind.ClassProp, OpKind.Property, OpKind.TwoWayProperty,
21052
+ OpKind.HostProperty, OpKind.Attribute
20534
21053
  ]);
20535
21054
  /**
20536
21055
  * Many type of operations have ordering constraints that must be respected. For example, a
@@ -20794,8 +21313,9 @@ function propagateI18nBlocksToTemplates(unit, subTemplateIndex) {
20794
21313
  break;
20795
21314
  case OpKind.RepeaterCreate:
20796
21315
  // Propagate i18n blocks to the @for template.
20797
- unit.job.views.get(op.xref);
20798
- subTemplateIndex = propagateI18nBlocksForView(unit.job.views.get(op.xref), i18nBlock, op.i18nPlaceholder, subTemplateIndex);
21316
+ const forView = unit.job.views.get(op.xref);
21317
+ subTemplateIndex =
21318
+ propagateI18nBlocksForView(forView, i18nBlock, op.i18nPlaceholder, subTemplateIndex);
20799
21319
  // Then if there's an @empty template, propagate the i18n blocks for it as well.
20800
21320
  if (op.emptyView !== null) {
20801
21321
  subTemplateIndex = propagateI18nBlocksForView(unit.job.views.get(op.emptyView), i18nBlock, op.emptyI18nPlaceholder, subTemplateIndex);
@@ -20996,6 +21516,12 @@ function listener(name, handlerFn, eventTargetResolver, syntheticHost, sourceSpa
20996
21516
  }
20997
21517
  return call(syntheticHost ? Identifiers.syntheticHostListener : Identifiers.listener, args, sourceSpan);
20998
21518
  }
21519
+ function twoWayBindingSet(target, value) {
21520
+ return importExpr(Identifiers.twoWayBindingSet).callFn([target, value]);
21521
+ }
21522
+ function twoWayListener(name, handlerFn, sourceSpan) {
21523
+ return call(Identifiers.twoWayListener, [literal(name), handlerFn], sourceSpan);
21524
+ }
20999
21525
  function pipe(slot, name) {
21000
21526
  return call(Identifiers.pipe, [
21001
21527
  literal(slot),
@@ -21156,6 +21682,13 @@ function property(name, expression, sanitizer, sourceSpan) {
21156
21682
  }
21157
21683
  return call(Identifiers.property, args, sourceSpan);
21158
21684
  }
21685
+ function twoWayProperty(name, expression, sanitizer, sourceSpan) {
21686
+ const args = [literal(name), expression];
21687
+ if (sanitizer !== null) {
21688
+ args.push(sanitizer);
21689
+ }
21690
+ return call(Identifiers.twoWayProperty, args, sourceSpan);
21691
+ }
21159
21692
  function attribute(name, expression, sanitizer, namespace) {
21160
21693
  const args = [literal(name), expression];
21161
21694
  if (sanitizer !== null || namespace !== null) {
@@ -21573,6 +22106,9 @@ function reifyCreateOperations(unit, ops) {
21573
22106
  }
21574
22107
  OpList.replace(op, listener(op.name, listenerFn, eventTargetResolver, op.hostListener && op.isAnimationListener, op.sourceSpan));
21575
22108
  break;
22109
+ case OpKind.TwoWayListener:
22110
+ OpList.replace(op, twoWayListener(op.name, reifyListenerHandler(unit, op.handlerFnName, op.handlerOps, true), op.sourceSpan));
22111
+ break;
21576
22112
  case OpKind.Variable:
21577
22113
  if (op.variable.name === null) {
21578
22114
  throw new Error(`AssertionError: unnamed variable ${op.xref}`);
@@ -21681,6 +22217,9 @@ function reifyUpdateOperations(_unit, ops) {
21681
22217
  OpList.replace(op, property(op.name, op.expression, op.sanitizer, op.sourceSpan));
21682
22218
  }
21683
22219
  break;
22220
+ case OpKind.TwoWayProperty:
22221
+ OpList.replace(op, twoWayProperty(op.name, op.expression, op.sanitizer, op.sourceSpan));
22222
+ break;
21684
22223
  case OpKind.StyleProp:
21685
22224
  if (op.expression instanceof Interpolation) {
21686
22225
  OpList.replace(op, stylePropInterpolate(op.name, op.expression.strings, op.expression.expressions, op.unit, op.sourceSpan));
@@ -21778,6 +22317,8 @@ function reifyIrExpression(expr) {
21778
22317
  return reference(expr.targetSlot.slot + 1 + expr.offset);
21779
22318
  case ExpressionKind.LexicalRead:
21780
22319
  throw new Error(`AssertionError: unresolved LexicalRead of ${expr.name}`);
22320
+ case ExpressionKind.TwoWayBindingSet:
22321
+ throw new Error(`AssertionError: unresolved TwoWayBindingSet`);
21781
22322
  case ExpressionKind.RestoreView:
21782
22323
  if (typeof expr.view === 'number') {
21783
22324
  throw new Error(`AssertionError: unresolved RestoreView`);
@@ -21845,7 +22386,7 @@ function reifyListenerHandler(unit, name, handlerOps, consumesDollarEvent) {
21845
22386
  }
21846
22387
 
21847
22388
  /**
21848
- * Bidningd with no content can be safely deleted.
22389
+ * Binding with no content can be safely deleted.
21849
22390
  */
21850
22391
  function removeEmptyBindings(job) {
21851
22392
  for (const unit of job.units) {
@@ -21938,6 +22479,7 @@ function processLexicalScope$1(view, ops) {
21938
22479
  }
21939
22480
  break;
21940
22481
  case OpKind.Listener:
22482
+ case OpKind.TwoWayListener:
21941
22483
  processLexicalScope$1(view, op.handlerOps);
21942
22484
  break;
21943
22485
  }
@@ -21967,16 +22509,19 @@ function processLexicalScope$1(view, ops) {
21967
22509
  */
21968
22510
  function resolveDollarEvent(job) {
21969
22511
  for (const unit of job.units) {
21970
- transformDollarEvent(unit, unit.create);
21971
- transformDollarEvent(unit, unit.update);
22512
+ transformDollarEvent(unit.create);
22513
+ transformDollarEvent(unit.update);
21972
22514
  }
21973
22515
  }
21974
- function transformDollarEvent(unit, ops) {
22516
+ function transformDollarEvent(ops) {
21975
22517
  for (const op of ops) {
21976
- if (op.kind === OpKind.Listener) {
22518
+ if (op.kind === OpKind.Listener || op.kind === OpKind.TwoWayListener) {
21977
22519
  transformExpressionsInOp(op, (expr) => {
21978
22520
  if (expr instanceof LexicalReadExpr && expr.name === '$event') {
21979
- op.consumesDollarEvent = true;
22521
+ // Two-way listeners always consume `$event` so they omit this field.
22522
+ if (op.kind === OpKind.Listener) {
22523
+ op.consumesDollarEvent = true;
22524
+ }
21980
22525
  return new ReadVarExpr(expr.name);
21981
22526
  }
21982
22527
  return expr;
@@ -22206,7 +22751,7 @@ function recordTemplateStart(job, view, slot, i18nPlaceholder, i18nContext, i18n
22206
22751
  * Records an i18n param value for the closing of a template.
22207
22752
  */
22208
22753
  function recordTemplateClose(job, view, slot, i18nPlaceholder, i18nContext, i18nBlock, structuralDirective) {
22209
- const { startName, closeName } = i18nPlaceholder;
22754
+ const { closeName } = i18nPlaceholder;
22210
22755
  const flags = I18nParamValueFlags.TemplateTag | I18nParamValueFlags.CloseTag;
22211
22756
  // Self-closing tags don't have a closing tag placeholder, instead the template's closing is
22212
22757
  // recorded via an additional flag on the template start value.
@@ -22248,14 +22793,14 @@ function addParam(params, placeholder, value, subTemplateIndex, flags) {
22248
22793
  */
22249
22794
  function resolveI18nExpressionPlaceholders(job) {
22250
22795
  // Record all of the i18n context ops, and the sub-template index for each i18n op.
22251
- const subTemplateIndicies = new Map();
22796
+ const subTemplateIndices = new Map();
22252
22797
  const i18nContexts = new Map();
22253
22798
  const icuPlaceholders = new Map();
22254
22799
  for (const unit of job.units) {
22255
22800
  for (const op of unit.create) {
22256
22801
  switch (op.kind) {
22257
22802
  case OpKind.I18nStart:
22258
- subTemplateIndicies.set(op.xref, op.subTemplateIndex);
22803
+ subTemplateIndices.set(op.xref, op.subTemplateIndex);
22259
22804
  break;
22260
22805
  case OpKind.I18nContext:
22261
22806
  i18nContexts.set(op.xref, op);
@@ -22277,7 +22822,7 @@ function resolveI18nExpressionPlaceholders(job) {
22277
22822
  for (const op of unit.update) {
22278
22823
  if (op.kind === OpKind.I18nExpression) {
22279
22824
  const index = expressionIndices.get(referenceIndex(op)) || 0;
22280
- const subTemplateIndex = subTemplateIndicies.get(op.i18nOwner) ?? null;
22825
+ const subTemplateIndex = subTemplateIndices.get(op.i18nOwner) ?? null;
22281
22826
  const value = {
22282
22827
  value: index,
22283
22828
  subTemplateIndex: subTemplateIndex,
@@ -22351,6 +22896,7 @@ function processLexicalScope(unit, ops, savedView) {
22351
22896
  }
22352
22897
  break;
22353
22898
  case OpKind.Listener:
22899
+ case OpKind.TwoWayListener:
22354
22900
  // Listener functions have separate variable declarations, so process them as a separate
22355
22901
  // lexical scope.
22356
22902
  processLexicalScope(unit, op.handlerOps, savedView);
@@ -22361,11 +22907,11 @@ function processLexicalScope(unit, ops, savedView) {
22361
22907
  // scope. Also, look for `ir.RestoreViewExpr`s and match them with the snapshotted view context
22362
22908
  // variable.
22363
22909
  for (const op of ops) {
22364
- if (op.kind == OpKind.Listener) {
22910
+ if (op.kind == OpKind.Listener || op.kind === OpKind.TwoWayListener) {
22365
22911
  // Listeners were already processed above with their own scopes.
22366
22912
  continue;
22367
22913
  }
22368
- transformExpressionsInOp(op, (expr, flags) => {
22914
+ transformExpressionsInOp(op, (expr) => {
22369
22915
  if (expr instanceof LexicalReadExpr) {
22370
22916
  // `expr` is a read of a name within the lexical scope of this view.
22371
22917
  // Either that name is defined within the current view, or it represents a property from the
@@ -22510,6 +23056,84 @@ function getOnlySecurityContext(securityContext) {
22510
23056
  return securityContext;
22511
23057
  }
22512
23058
 
23059
+ /**
23060
+ * Transforms a `TwoWayBindingSet` expression into an expression that either
23061
+ * sets a value through the `twoWayBindingSet` instruction or falls back to setting
23062
+ * the value directly. E.g. the expression `TwoWayBindingSet(target, value)` becomes:
23063
+ * `ng.twoWayBindingSet(target, value) || (target = value)`.
23064
+ */
23065
+ function transformTwoWayBindingSet(job) {
23066
+ for (const unit of job.units) {
23067
+ for (const op of unit.create) {
23068
+ if (op.kind === OpKind.TwoWayListener) {
23069
+ transformExpressionsInOp(op, (expr) => {
23070
+ if (expr instanceof TwoWayBindingSetExpr) {
23071
+ return wrapAction(expr.target, expr.value);
23072
+ }
23073
+ return expr;
23074
+ }, VisitorContextFlag.InChildOperation);
23075
+ }
23076
+ }
23077
+ }
23078
+ }
23079
+ function wrapSetOperation(target, value) {
23080
+ // ASSUMPTION: here we're assuming that `ReadVariableExpr` will be a reference
23081
+ // to a local template variable. This appears to be the case at the time of writing.
23082
+ // If the expression is targeting a variable read, we only emit the `twoWayBindingSet` since
23083
+ // the fallback would be attempting to write into a constant. Invalid usages will be flagged
23084
+ // during template type checking.
23085
+ if (target instanceof ReadVariableExpr) {
23086
+ return twoWayBindingSet(target, value);
23087
+ }
23088
+ return twoWayBindingSet(target, value).or(target.set(value));
23089
+ }
23090
+ function isReadExpression(value) {
23091
+ return value instanceof ReadPropExpr || value instanceof ReadKeyExpr ||
23092
+ value instanceof ReadVariableExpr;
23093
+ }
23094
+ function wrapAction(target, value) {
23095
+ // The only officially supported expressions inside of a two-way binding are read expressions.
23096
+ if (isReadExpression(target)) {
23097
+ return wrapSetOperation(target, value);
23098
+ }
23099
+ // However, historically the expression parser was handling two-way events by appending `=$event`
23100
+ // to the raw string before attempting to parse it. This has led to bugs over the years (see
23101
+ // #37809) and to unintentionally supporting unassignable events in the two-way binding. The
23102
+ // logic below aims to emulate the old behavior while still supporting the new output format
23103
+ // which uses `twoWayBindingSet`. Note that the generated code doesn't necessarily make sense
23104
+ // based on what the user wrote, for example the event binding for `[(value)]="a ? b : c"`
23105
+ // would produce `ctx.a ? ctx.b : ctx.c = $event`. We aim to reproduce what the parser used
23106
+ // to generate before #54154.
23107
+ if (target instanceof BinaryOperatorExpr && isReadExpression(target.rhs)) {
23108
+ // `a && b` -> `ctx.a && twoWayBindingSet(ctx.b, $event) || (ctx.b = $event)`
23109
+ return new BinaryOperatorExpr(target.operator, target.lhs, wrapSetOperation(target.rhs, value));
23110
+ }
23111
+ // Note: this also supports nullish coalescing expressions which
23112
+ // would've been downleveled to ternary expressions by this point.
23113
+ if (target instanceof ConditionalExpr && isReadExpression(target.falseCase)) {
23114
+ // `a ? b : c` -> `ctx.a ? ctx.b : twoWayBindingSet(ctx.c, $event) || (ctx.c = $event)`
23115
+ return new ConditionalExpr(target.condition, target.trueCase, wrapSetOperation(target.falseCase, value));
23116
+ }
23117
+ // `!!a` -> `twoWayBindingSet(ctx.a, $event) || (ctx.a = $event)`
23118
+ // Note: previously we'd actually produce `!!(ctx.a = $event)`, but the wrapping
23119
+ // node doesn't affect the result so we don't need to carry it over.
23120
+ if (target instanceof NotExpr) {
23121
+ let expr = target.condition;
23122
+ while (true) {
23123
+ if (expr instanceof NotExpr) {
23124
+ expr = expr.condition;
23125
+ }
23126
+ else {
23127
+ if (isReadExpression(expr)) {
23128
+ return wrapSetOperation(expr, value);
23129
+ }
23130
+ break;
23131
+ }
23132
+ }
23133
+ }
23134
+ throw new Error(`Unsupported expression in two-way action binding.`);
23135
+ }
23136
+
22513
23137
  /**
22514
23138
  * When inside of a listener, we may need access to one or more enclosing views. Therefore, each
22515
23139
  * view should save the current view, and each listener must have the ability to restore the
@@ -22525,7 +23149,7 @@ function saveAndRestoreView(job) {
22525
23149
  }, new GetCurrentViewExpr(), VariableFlags.None),
22526
23150
  ]);
22527
23151
  for (const op of unit.create) {
22528
- if (op.kind !== OpKind.Listener) {
23152
+ if (op.kind !== OpKind.Listener && op.kind !== OpKind.TwoWayListener) {
22529
23153
  continue;
22530
23154
  }
22531
23155
  // Embedded views always need the save/restore view operation.
@@ -22715,7 +23339,7 @@ function generateTemporaries(ops) {
22715
23339
  generatedStatements.push(...Array.from(new Set(defs.values()))
22716
23340
  .map(name => createStatementOp(new DeclareVarStmt(name))));
22717
23341
  opCount++;
22718
- if (op.kind === OpKind.Listener) {
23342
+ if (op.kind === OpKind.Listener || op.kind === OpKind.TwoWayListener) {
22719
23343
  op.handlerOps.prepend(generateTemporaries(op.handlerOps));
22720
23344
  }
22721
23345
  }
@@ -22911,7 +23535,7 @@ function countVariables(job) {
22911
23535
  }
22912
23536
  });
22913
23537
  }
22914
- // Compatiblity mode pass for pure function offsets (as explained above).
23538
+ // Compatibility mode pass for pure function offsets (as explained above).
22915
23539
  if (job.compatibility === CompatibilityMode.TemplateDefinitionBuilder) {
22916
23540
  for (const op of unit.ops()) {
22917
23541
  visitExpressionsInOp(op, expr => {
@@ -22963,6 +23587,9 @@ function varsUsedByOp(op) {
22963
23587
  slots += op.expression.expressions.length;
22964
23588
  }
22965
23589
  return slots;
23590
+ case OpKind.TwoWayProperty:
23591
+ // Two-way properties can only have expressions so they only need one variable slot.
23592
+ return 1;
22966
23593
  case OpKind.StyleProp:
22967
23594
  case OpKind.ClassProp:
22968
23595
  case OpKind.StyleMap:
@@ -23036,14 +23663,14 @@ function optimizeVariables(job) {
23036
23663
  inlineAlwaysInlineVariables(unit.create);
23037
23664
  inlineAlwaysInlineVariables(unit.update);
23038
23665
  for (const op of unit.create) {
23039
- if (op.kind === OpKind.Listener) {
23666
+ if (op.kind === OpKind.Listener || op.kind === OpKind.TwoWayListener) {
23040
23667
  inlineAlwaysInlineVariables(op.handlerOps);
23041
23668
  }
23042
23669
  }
23043
23670
  optimizeVariablesInOpList(unit.create, job.compatibility);
23044
23671
  optimizeVariablesInOpList(unit.update, job.compatibility);
23045
23672
  for (const op of unit.create) {
23046
- if (op.kind === OpKind.Listener) {
23673
+ if (op.kind === OpKind.Listener || op.kind === OpKind.TwoWayListener) {
23047
23674
  optimizeVariablesInOpList(op.handlerOps, job.compatibility);
23048
23675
  }
23049
23676
  }
@@ -23176,7 +23803,6 @@ function optimizeVariablesInOpList(ops, compatibility) {
23176
23803
  const toInline = [];
23177
23804
  for (const [id, count] of varUsages) {
23178
23805
  const decl = varDecls.get(id);
23179
- opMap.get(decl);
23180
23806
  // We can inline variables that:
23181
23807
  // - are used exactly once, and
23182
23808
  // - are not used remotely
@@ -23465,7 +24091,7 @@ function wrapI18nIcus(job) {
23465
24091
  * Use of this source code is governed by an MIT-style license that can be
23466
24092
  * found in the LICENSE file at https://angular.io/license
23467
24093
  */
23468
- [
24094
+ const phases = [
23469
24095
  { kind: CompilationJobKind.Tmpl, fn: removeContentSelectors },
23470
24096
  { kind: CompilationJobKind.Host, fn: parseHostStyleProperties },
23471
24097
  { kind: CompilationJobKind.Tmpl, fn: emitNamespaceChanges },
@@ -23498,6 +24124,7 @@ function wrapI18nIcus(job) {
23498
24124
  { kind: CompilationJobKind.Tmpl, fn: generateTrackVariables },
23499
24125
  { kind: CompilationJobKind.Both, fn: resolveNames },
23500
24126
  { kind: CompilationJobKind.Tmpl, fn: resolveDeferTargetNames },
24127
+ { kind: CompilationJobKind.Tmpl, fn: transformTwoWayBindingSet },
23501
24128
  { kind: CompilationJobKind.Tmpl, fn: optimizeTrackFns },
23502
24129
  { kind: CompilationJobKind.Both, fn: resolveContexts },
23503
24130
  { kind: CompilationJobKind.Both, fn: resolveSanitizers },
@@ -23506,7 +24133,6 @@ function wrapI18nIcus(job) {
23506
24133
  { kind: CompilationJobKind.Both, fn: expandSafeReads },
23507
24134
  { kind: CompilationJobKind.Both, fn: generateTemporaryVariables },
23508
24135
  { kind: CompilationJobKind.Tmpl, fn: allocateSlots },
23509
- { kind: CompilationJobKind.Tmpl, fn: createDeferDepsFns },
23510
24136
  { kind: CompilationJobKind.Tmpl, fn: resolveI18nElementPlaceholders },
23511
24137
  { kind: CompilationJobKind.Tmpl, fn: resolveI18nExpressionPlaceholders },
23512
24138
  { kind: CompilationJobKind.Tmpl, fn: extractI18nMessages },
@@ -23519,6 +24145,7 @@ function wrapI18nIcus(job) {
23519
24145
  { kind: CompilationJobKind.Tmpl, fn: generateAdvance },
23520
24146
  { kind: CompilationJobKind.Both, fn: optimizeVariables },
23521
24147
  { kind: CompilationJobKind.Both, fn: nameFunctionsAndVariables },
24148
+ { kind: CompilationJobKind.Tmpl, fn: createDeferDepsFns },
23522
24149
  { kind: CompilationJobKind.Tmpl, fn: mergeNextContextExpressions },
23523
24150
  { kind: CompilationJobKind.Tmpl, fn: generateNgContainerOps },
23524
24151
  { kind: CompilationJobKind.Tmpl, fn: collapseEmptyInstructions },
@@ -23527,546 +24154,1111 @@ function wrapI18nIcus(job) {
23527
24154
  { kind: CompilationJobKind.Both, fn: reify },
23528
24155
  { kind: CompilationJobKind.Both, fn: chain },
23529
24156
  ];
23530
-
23531
- CompatibilityMode.TemplateDefinitionBuilder;
23532
- // Schema containing DOM elements and their properties.
23533
- new DomElementSchemaRegistry();
23534
- // TODO: Can we populate Template binding kinds in ingest?
23535
- new Map([
23536
- [0 /* e.BindingType.Property */, BindingKind.Property],
23537
- [1 /* e.BindingType.Attribute */, BindingKind.Attribute],
23538
- [2 /* e.BindingType.Class */, BindingKind.ClassName],
23539
- [3 /* e.BindingType.Style */, BindingKind.StyleProperty],
23540
- [4 /* e.BindingType.Animation */, BindingKind.Animation],
23541
- ]);
23542
-
23543
- const IMPORTANT_FLAG = '!important';
23544
24157
  /**
23545
- * Minimum amount of binding slots required in the runtime for style/class bindings.
23546
- *
23547
- * Styling in Angular uses up two slots in the runtime LView/TData data structures to
23548
- * record binding data, property information and metadata.
23549
- *
23550
- * When a binding is registered it will place the following information in the `LView`:
23551
- *
23552
- * slot 1) binding value
23553
- * slot 2) cached value (all other values collected before it in string form)
23554
- *
23555
- * When a binding is registered it will place the following information in the `TData`:
23556
- *
23557
- * slot 1) prop name
23558
- * slot 2) binding index that points to the previous style/class binding (and some extra config
23559
- * values)
23560
- *
23561
- * Let's imagine we have a binding that looks like so:
23562
- *
23563
- * ```
23564
- * <div [style.width]="x" [style.height]="y">
23565
- * ```
23566
- *
23567
- * Our `LView` and `TData` data-structures look like so:
23568
- *
23569
- * ```typescript
23570
- * LView = [
23571
- * // ...
23572
- * x, // value of x
23573
- * "width: x",
23574
- *
23575
- * y, // value of y
23576
- * "width: x; height: y",
23577
- * // ...
23578
- * ];
23579
- *
23580
- * TData = [
23581
- * // ...
23582
- * "width", // binding slot 20
23583
- * 0,
23584
- *
23585
- * "height",
23586
- * 20,
23587
- * // ...
23588
- * ];
23589
- * ```
23590
- *
23591
- * */
23592
- const MIN_STYLING_BINDING_SLOTS_REQUIRED = 2;
24158
+ * Run all transformation phases in the correct order against a compilation job. After this
24159
+ * processing, the compilation should be in a state where it can be emitted.
24160
+ */
24161
+ function transform(job, kind) {
24162
+ for (const phase of phases) {
24163
+ if (phase.kind === kind || phase.kind === CompilationJobKind.Both) {
24164
+ // The type of `Phase` above ensures it is impossible to call a phase that doesn't support the
24165
+ // job kind.
24166
+ phase.fn(job);
24167
+ }
24168
+ }
24169
+ }
23593
24170
  /**
23594
- * Produces creation/update instructions for all styling bindings (class and style)
23595
- *
23596
- * It also produces the creation instruction to register all initial styling values
23597
- * (which are all the static class="..." and style="..." attribute values that exist
23598
- * on an element within a template).
23599
- *
23600
- * The builder class below handles producing instructions for the following cases:
23601
- *
23602
- * - Static style/class attributes (style="..." and class="...")
23603
- * - Dynamic style/class map bindings ([style]="map" and [class]="map|string")
23604
- * - Dynamic style/class property bindings ([style.prop]="exp" and [class.name]="exp")
23605
- *
23606
- * Due to the complex relationship of all of these cases, the instructions generated
23607
- * for these attributes/properties/bindings must be done so in the correct order. The
23608
- * order which these must be generated is as follows:
23609
- *
23610
- * if (createMode) {
23611
- * styling(...)
23612
- * }
23613
- * if (updateMode) {
23614
- * styleMap(...)
23615
- * classMap(...)
23616
- * styleProp(...)
23617
- * classProp(...)
23618
- * }
23619
- *
23620
- * The creation/update methods within the builder class produce these instructions.
24171
+ * Compile all views in the given `ComponentCompilation` into the final template function, which may
24172
+ * reference constants defined in a `ConstantPool`.
23621
24173
  */
23622
- class StylingBuilder {
23623
- constructor(_directiveExpr) {
23624
- this._directiveExpr = _directiveExpr;
23625
- /** Whether or not there are any static styling values present */
23626
- this._hasInitialValues = false;
23627
- /**
23628
- * Whether or not there are any styling bindings present
23629
- * (i.e. `[style]`, `[class]`, `[style.prop]` or `[class.name]`)
23630
- */
23631
- this.hasBindings = false;
23632
- this.hasBindingsWithPipes = false;
23633
- /** the input for [class] (if it exists) */
23634
- this._classMapInput = null;
23635
- /** the input for [style] (if it exists) */
23636
- this._styleMapInput = null;
23637
- /** an array of each [style.prop] input */
23638
- this._singleStyleInputs = null;
23639
- /** an array of each [class.name] input */
23640
- this._singleClassInputs = null;
23641
- this._lastStylingInput = null;
23642
- this._firstStylingInput = null;
23643
- // maps are used instead of hash maps because a Map will
23644
- // retain the ordering of the keys
23645
- /**
23646
- * Represents the location of each style binding in the template
23647
- * (e.g. `<div [style.width]="w" [style.height]="h">` implies
23648
- * that `width=0` and `height=1`)
23649
- */
23650
- this._stylesIndex = new Map();
23651
- /**
23652
- * Represents the location of each class binding in the template
23653
- * (e.g. `<div [class.big]="b" [class.hidden]="h">` implies
23654
- * that `big=0` and `hidden=1`)
23655
- */
23656
- this._classesIndex = new Map();
23657
- this._initialStyleValues = [];
23658
- this._initialClassValues = [];
24174
+ function emitTemplateFn(tpl, pool) {
24175
+ const rootFn = emitView(tpl.root);
24176
+ emitChildViews(tpl.root, pool);
24177
+ return rootFn;
24178
+ }
24179
+ function emitChildViews(parent, pool) {
24180
+ for (const unit of parent.job.units) {
24181
+ if (unit.parent !== parent.xref) {
24182
+ continue;
24183
+ }
24184
+ // Child views are emitted depth-first.
24185
+ emitChildViews(unit, pool);
24186
+ const viewFn = emitView(unit);
24187
+ pool.statements.push(viewFn.toDeclStmt(viewFn.name));
23659
24188
  }
23660
- /**
23661
- * Registers a given input to the styling builder to be later used when producing AOT code.
23662
- *
23663
- * The code below will only accept the input if it is somehow tied to styling (whether it be
23664
- * style/class bindings or static style/class attributes).
23665
- */
23666
- registerBoundInput(input) {
23667
- // [attr.style] or [attr.class] are skipped in the code below,
23668
- // they should not be treated as styling-based bindings since
23669
- // they are intended to be written directly to the attr and
23670
- // will therefore skip all style/class resolution that is present
23671
- // with style="", [style]="" and [style.prop]="", class="",
23672
- // [class.prop]="". [class]="" assignments
23673
- let binding = null;
23674
- let name = input.name;
23675
- switch (input.type) {
23676
- case 0 /* BindingType.Property */:
23677
- binding = this.registerInputBasedOnName(name, input.value, input.sourceSpan);
23678
- break;
23679
- case 3 /* BindingType.Style */:
23680
- binding = this.registerStyleInput(name, false, input.value, input.sourceSpan, input.unit);
23681
- break;
23682
- case 2 /* BindingType.Class */:
23683
- binding = this.registerClassInput(name, false, input.value, input.sourceSpan);
23684
- break;
24189
+ }
24190
+ /**
24191
+ * Emit a template function for an individual `ViewCompilation` (which may be either the root view
24192
+ * or an embedded view).
24193
+ */
24194
+ function emitView(view) {
24195
+ if (view.fnName === null) {
24196
+ throw new Error(`AssertionError: view ${view.xref} is unnamed`);
24197
+ }
24198
+ const createStatements = [];
24199
+ for (const op of view.create) {
24200
+ if (op.kind !== OpKind.Statement) {
24201
+ throw new Error(`AssertionError: expected all create ops to have been compiled, but got ${OpKind[op.kind]}`);
23685
24202
  }
23686
- return binding ? true : false;
24203
+ createStatements.push(op.statement);
23687
24204
  }
23688
- registerInputBasedOnName(name, expression, sourceSpan) {
23689
- let binding = null;
23690
- const prefix = name.substring(0, 6);
23691
- const isStyle = name === 'style' || prefix === 'style.' || prefix === 'style!';
23692
- const isClass = !isStyle && (name === 'class' || prefix === 'class.' || prefix === 'class!');
23693
- if (isStyle || isClass) {
23694
- const isMapBased = name.charAt(5) !== '.'; // style.prop or class.prop makes this a no
23695
- const property = name.slice(isMapBased ? 5 : 6); // the dot explains why there's a +1
23696
- if (isStyle) {
23697
- binding = this.registerStyleInput(property, isMapBased, expression, sourceSpan);
23698
- }
23699
- else {
23700
- binding = this.registerClassInput(property, isMapBased, expression, sourceSpan);
23701
- }
24205
+ const updateStatements = [];
24206
+ for (const op of view.update) {
24207
+ if (op.kind !== OpKind.Statement) {
24208
+ throw new Error(`AssertionError: expected all update ops to have been compiled, but got ${OpKind[op.kind]}`);
23702
24209
  }
23703
- return binding;
24210
+ updateStatements.push(op.statement);
23704
24211
  }
23705
- registerStyleInput(name, isMapBased, value, sourceSpan, suffix) {
23706
- if (isEmptyExpression(value)) {
23707
- return null;
24212
+ const createCond = maybeGenerateRfBlock(1, createStatements);
24213
+ const updateCond = maybeGenerateRfBlock(2, updateStatements);
24214
+ return fn([
24215
+ new FnParam('rf'),
24216
+ new FnParam('ctx'),
24217
+ ], [
24218
+ ...createCond,
24219
+ ...updateCond,
24220
+ ],
24221
+ /* type */ undefined, /* sourceSpan */ undefined, view.fnName);
24222
+ }
24223
+ function maybeGenerateRfBlock(flag, statements) {
24224
+ if (statements.length === 0) {
24225
+ return [];
24226
+ }
24227
+ return [
24228
+ ifStmt(new BinaryOperatorExpr(exports.BinaryOperator.BitwiseAnd, variable('rf'), literal(flag)), statements),
24229
+ ];
24230
+ }
24231
+ function emitHostBindingFunction(job) {
24232
+ if (job.root.fnName === null) {
24233
+ throw new Error(`AssertionError: host binding function is unnamed`);
24234
+ }
24235
+ const createStatements = [];
24236
+ for (const op of job.root.create) {
24237
+ if (op.kind !== OpKind.Statement) {
24238
+ throw new Error(`AssertionError: expected all create ops to have been compiled, but got ${OpKind[op.kind]}`);
23708
24239
  }
23709
- // CSS custom properties are case-sensitive so we shouldn't normalize them.
23710
- // See: https://www.w3.org/TR/css-variables-1/#defining-variables
23711
- if (!isCssCustomProperty(name)) {
23712
- name = hyphenate(name);
24240
+ createStatements.push(op.statement);
24241
+ }
24242
+ const updateStatements = [];
24243
+ for (const op of job.root.update) {
24244
+ if (op.kind !== OpKind.Statement) {
24245
+ throw new Error(`AssertionError: expected all update ops to have been compiled, but got ${OpKind[op.kind]}`);
23713
24246
  }
23714
- const { property, hasOverrideFlag, suffix: bindingSuffix } = parseProperty(name);
23715
- suffix = typeof suffix === 'string' && suffix.length !== 0 ? suffix : bindingSuffix;
23716
- const entry = { name: property, suffix: suffix, value, sourceSpan, hasOverrideFlag };
23717
- if (isMapBased) {
23718
- this._styleMapInput = entry;
24247
+ updateStatements.push(op.statement);
24248
+ }
24249
+ if (createStatements.length === 0 && updateStatements.length === 0) {
24250
+ return null;
24251
+ }
24252
+ const createCond = maybeGenerateRfBlock(1, createStatements);
24253
+ const updateCond = maybeGenerateRfBlock(2, updateStatements);
24254
+ return fn([
24255
+ new FnParam('rf'),
24256
+ new FnParam('ctx'),
24257
+ ], [
24258
+ ...createCond,
24259
+ ...updateCond,
24260
+ ],
24261
+ /* type */ undefined, /* sourceSpan */ undefined, job.root.fnName);
24262
+ }
24263
+
24264
+ const compatibilityMode = CompatibilityMode.TemplateDefinitionBuilder;
24265
+ // Schema containing DOM elements and their properties.
24266
+ const domSchema = new DomElementSchemaRegistry();
24267
+ // Tag name of the `ng-template` element.
24268
+ const NG_TEMPLATE_TAG_NAME$1 = 'ng-template';
24269
+ /**
24270
+ * Process a template AST and convert it into a `ComponentCompilation` in the intermediate
24271
+ * representation.
24272
+ * TODO: Refactor more of the ingestion code into phases.
24273
+ */
24274
+ function ingestComponent(componentName, template, constantPool, relativeContextFilePath, i18nUseExternalIds, deferBlocksMeta, allDeferrableDepsFn) {
24275
+ const job = new ComponentCompilationJob(componentName, constantPool, compatibilityMode, relativeContextFilePath, i18nUseExternalIds, deferBlocksMeta, allDeferrableDepsFn);
24276
+ ingestNodes(job.root, template);
24277
+ return job;
24278
+ }
24279
+ /**
24280
+ * Process a host binding AST and convert it into a `HostBindingCompilationJob` in the intermediate
24281
+ * representation.
24282
+ */
24283
+ function ingestHostBinding(input, bindingParser, constantPool) {
24284
+ const job = new HostBindingCompilationJob(input.componentName, constantPool, compatibilityMode);
24285
+ for (const property of input.properties ?? []) {
24286
+ let bindingKind = BindingKind.Property;
24287
+ // TODO: this should really be handled in the parser.
24288
+ if (property.name.startsWith('attr.')) {
24289
+ property.name = property.name.substring('attr.'.length);
24290
+ bindingKind = BindingKind.Attribute;
23719
24291
  }
23720
- else {
23721
- (this._singleStyleInputs = this._singleStyleInputs || []).push(entry);
23722
- registerIntoMap(this._stylesIndex, property);
24292
+ if (property.isAnimation) {
24293
+ bindingKind = BindingKind.Animation;
23723
24294
  }
23724
- this._lastStylingInput = entry;
23725
- this._firstStylingInput = this._firstStylingInput || entry;
23726
- this._checkForPipes(value);
23727
- this.hasBindings = true;
23728
- return entry;
24295
+ const securityContexts = bindingParser
24296
+ .calcPossibleSecurityContexts(input.componentSelector, property.name, bindingKind === BindingKind.Attribute)
24297
+ .filter(context => context !== SecurityContext.NONE);
24298
+ ingestHostProperty(job, property, bindingKind, securityContexts);
23729
24299
  }
23730
- registerClassInput(name, isMapBased, value, sourceSpan) {
23731
- if (isEmptyExpression(value)) {
23732
- return null;
24300
+ for (const [name, expr] of Object.entries(input.attributes) ?? []) {
24301
+ const securityContexts = bindingParser.calcPossibleSecurityContexts(input.componentSelector, name, true)
24302
+ .filter(context => context !== SecurityContext.NONE);
24303
+ ingestHostAttribute(job, name, expr, securityContexts);
24304
+ }
24305
+ for (const event of input.events ?? []) {
24306
+ ingestHostEvent(job, event);
24307
+ }
24308
+ return job;
24309
+ }
24310
+ // TODO: We should refactor the parser to use the same types and structures for host bindings as
24311
+ // with ordinary components. This would allow us to share a lot more ingestion code.
24312
+ function ingestHostProperty(job, property, bindingKind, securityContexts) {
24313
+ let expression;
24314
+ const ast = property.expression.ast;
24315
+ if (ast instanceof Interpolation$1) {
24316
+ expression = new Interpolation(ast.strings, ast.expressions.map(expr => convertAst(expr, job, property.sourceSpan)), []);
24317
+ }
24318
+ else {
24319
+ expression = convertAst(ast, job, property.sourceSpan);
24320
+ }
24321
+ job.root.update.push(createBindingOp(job.root.xref, bindingKind, property.name, expression, null, securityContexts, false, false, null, /* TODO: How do Host bindings handle i18n attrs? */ null, property.sourceSpan));
24322
+ }
24323
+ function ingestHostAttribute(job, name, value, securityContexts) {
24324
+ const attrBinding = createBindingOp(job.root.xref, BindingKind.Attribute, name, value, null, securityContexts,
24325
+ /* Host attributes should always be extracted to const hostAttrs, even if they are not
24326
+ *strictly* text literals */
24327
+ true, false, null,
24328
+ /* TODO */ null,
24329
+ /** TODO: May be null? */ value.sourceSpan);
24330
+ job.root.update.push(attrBinding);
24331
+ }
24332
+ function ingestHostEvent(job, event) {
24333
+ const [phase, target] = event.type !== 1 /* e.ParsedEventType.Animation */ ? [null, event.targetOrPhase] :
24334
+ [event.targetOrPhase, null];
24335
+ const eventBinding = createListenerOp(job.root.xref, new SlotHandle(), event.name, null, makeListenerHandlerOps(job.root, event.handler, event.handlerSpan), phase, target, true, event.sourceSpan);
24336
+ job.root.create.push(eventBinding);
24337
+ }
24338
+ /**
24339
+ * Ingest the nodes of a template AST into the given `ViewCompilation`.
24340
+ */
24341
+ function ingestNodes(unit, template) {
24342
+ for (const node of template) {
24343
+ if (node instanceof Element$1) {
24344
+ ingestElement(unit, node);
23733
24345
  }
23734
- const { property, hasOverrideFlag } = parseProperty(name);
23735
- const entry = { name: property, value, sourceSpan, hasOverrideFlag, suffix: null };
23736
- if (isMapBased) {
23737
- this._classMapInput = entry;
24346
+ else if (node instanceof Template) {
24347
+ ingestTemplate(unit, node);
23738
24348
  }
23739
- else {
23740
- (this._singleClassInputs = this._singleClassInputs || []).push(entry);
23741
- registerIntoMap(this._classesIndex, property);
24349
+ else if (node instanceof Content) {
24350
+ ingestContent(unit, node);
23742
24351
  }
23743
- this._lastStylingInput = entry;
23744
- this._firstStylingInput = this._firstStylingInput || entry;
23745
- this._checkForPipes(value);
23746
- this.hasBindings = true;
23747
- return entry;
23748
- }
23749
- _checkForPipes(value) {
23750
- if ((value instanceof ASTWithSource) && (value.ast instanceof BindingPipe)) {
23751
- this.hasBindingsWithPipes = true;
24352
+ else if (node instanceof Text$3) {
24353
+ ingestText(unit, node, null);
24354
+ }
24355
+ else if (node instanceof BoundText) {
24356
+ ingestBoundText(unit, node, null);
24357
+ }
24358
+ else if (node instanceof IfBlock) {
24359
+ ingestIfBlock(unit, node);
24360
+ }
24361
+ else if (node instanceof SwitchBlock) {
24362
+ ingestSwitchBlock(unit, node);
24363
+ }
24364
+ else if (node instanceof DeferredBlock) {
24365
+ ingestDeferBlock(unit, node);
24366
+ }
24367
+ else if (node instanceof Icu$1) {
24368
+ ingestIcu(unit, node);
23752
24369
  }
24370
+ else if (node instanceof ForLoopBlock) {
24371
+ ingestForBlock(unit, node);
24372
+ }
24373
+ else {
24374
+ throw new Error(`Unsupported template node: ${node.constructor.name}`);
24375
+ }
24376
+ }
24377
+ }
24378
+ /**
24379
+ * Ingest an element AST from the template into the given `ViewCompilation`.
24380
+ */
24381
+ function ingestElement(unit, element) {
24382
+ if (element.i18n !== undefined &&
24383
+ !(element.i18n instanceof Message || element.i18n instanceof TagPlaceholder)) {
24384
+ throw Error(`Unhandled i18n metadata type for element: ${element.i18n.constructor.name}`);
24385
+ }
24386
+ const id = unit.job.allocateXrefId();
24387
+ const [namespaceKey, elementName] = splitNsName(element.name);
24388
+ const startOp = createElementStartOp(elementName, id, namespaceForKey(namespaceKey), element.i18n instanceof TagPlaceholder ? element.i18n : undefined, element.startSourceSpan, element.sourceSpan);
24389
+ unit.create.push(startOp);
24390
+ ingestElementBindings(unit, startOp, element);
24391
+ ingestReferences(startOp, element);
24392
+ // Start i18n, if needed, goes after the element create and bindings, but before the nodes
24393
+ let i18nBlockId = null;
24394
+ if (element.i18n instanceof Message) {
24395
+ i18nBlockId = unit.job.allocateXrefId();
24396
+ unit.create.push(createI18nStartOp(i18nBlockId, element.i18n, undefined, element.startSourceSpan));
24397
+ }
24398
+ ingestNodes(unit, element.children);
24399
+ // The source span for the end op is typically the element closing tag. However, if no closing tag
24400
+ // exists, such as in `<input>`, we use the start source span instead. Usually the start and end
24401
+ // instructions will be collapsed into one `element` instruction, negating the purpose of this
24402
+ // fallback, but in cases when it is not collapsed (such as an input with a binding), we still
24403
+ // want to map the end instruction to the main element.
24404
+ const endOp = createElementEndOp(id, element.endSourceSpan ?? element.startSourceSpan);
24405
+ unit.create.push(endOp);
24406
+ // If there is an i18n message associated with this element, insert i18n start and end ops.
24407
+ if (i18nBlockId !== null) {
24408
+ OpList.insertBefore(createI18nEndOp(i18nBlockId, element.endSourceSpan ?? element.startSourceSpan), endOp);
24409
+ }
24410
+ }
24411
+ /**
24412
+ * Ingest an `ng-template` node from the AST into the given `ViewCompilation`.
24413
+ */
24414
+ function ingestTemplate(unit, tmpl) {
24415
+ if (tmpl.i18n !== undefined &&
24416
+ !(tmpl.i18n instanceof Message || tmpl.i18n instanceof TagPlaceholder)) {
24417
+ throw Error(`Unhandled i18n metadata type for template: ${tmpl.i18n.constructor.name}`);
24418
+ }
24419
+ const childView = unit.job.allocateView(unit.xref);
24420
+ let tagNameWithoutNamespace = tmpl.tagName;
24421
+ let namespacePrefix = '';
24422
+ if (tmpl.tagName) {
24423
+ [namespacePrefix, tagNameWithoutNamespace] = splitNsName(tmpl.tagName);
24424
+ }
24425
+ const i18nPlaceholder = tmpl.i18n instanceof TagPlaceholder ? tmpl.i18n : undefined;
24426
+ const namespace = namespaceForKey(namespacePrefix);
24427
+ const functionNameSuffix = tagNameWithoutNamespace === null ?
24428
+ '' :
24429
+ prefixWithNamespace(tagNameWithoutNamespace, namespace);
24430
+ const templateKind = isPlainTemplate(tmpl) ? TemplateKind.NgTemplate : TemplateKind.Structural;
24431
+ const templateOp = createTemplateOp(childView.xref, templateKind, tagNameWithoutNamespace, functionNameSuffix, namespace, i18nPlaceholder, tmpl.startSourceSpan, tmpl.sourceSpan);
24432
+ unit.create.push(templateOp);
24433
+ ingestTemplateBindings(unit, templateOp, tmpl, templateKind);
24434
+ ingestReferences(templateOp, tmpl);
24435
+ ingestNodes(childView, tmpl.children);
24436
+ for (const { name, value } of tmpl.variables) {
24437
+ childView.contextVariables.set(name, value !== '' ? value : '$implicit');
24438
+ }
24439
+ // If this is a plain template and there is an i18n message associated with it, insert i18n start
24440
+ // and end ops. For structural directive templates, the i18n ops will be added when ingesting the
24441
+ // element/template the directive is placed on.
24442
+ if (templateKind === TemplateKind.NgTemplate && tmpl.i18n instanceof Message) {
24443
+ const id = unit.job.allocateXrefId();
24444
+ OpList.insertAfter(createI18nStartOp(id, tmpl.i18n, undefined, tmpl.startSourceSpan), childView.create.head);
24445
+ OpList.insertBefore(createI18nEndOp(id, tmpl.endSourceSpan ?? tmpl.startSourceSpan), childView.create.tail);
23753
24446
  }
23754
- /**
23755
- * Registers the element's static style string value to the builder.
23756
- *
23757
- * @param value the style string (e.g. `width:100px; height:200px;`)
23758
- */
23759
- registerStyleAttr(value) {
23760
- this._initialStyleValues = parse(value);
23761
- this._hasInitialValues = true;
24447
+ }
24448
+ /**
24449
+ * Ingest a content node from the AST into the given `ViewCompilation`.
24450
+ */
24451
+ function ingestContent(unit, content) {
24452
+ if (content.i18n !== undefined && !(content.i18n instanceof TagPlaceholder)) {
24453
+ throw Error(`Unhandled i18n metadata type for element: ${content.i18n.constructor.name}`);
23762
24454
  }
23763
- /**
23764
- * Registers the element's static class string value to the builder.
23765
- *
23766
- * @param value the className string (e.g. `disabled gold zoom`)
23767
- */
23768
- registerClassAttr(value) {
23769
- this._initialClassValues = value.trim().split(/\s+/g);
23770
- this._hasInitialValues = true;
24455
+ const op = createProjectionOp(unit.job.allocateXrefId(), content.selector, content.i18n, content.sourceSpan);
24456
+ for (const attr of content.attributes) {
24457
+ const securityContext = domSchema.securityContext(content.name, attr.name, true);
24458
+ unit.update.push(createBindingOp(op.xref, BindingKind.Attribute, attr.name, literal(attr.value), null, securityContext, true, false, null, asMessage(attr.i18n), attr.sourceSpan));
23771
24459
  }
23772
- /**
23773
- * Appends all styling-related expressions to the provided attrs array.
23774
- *
23775
- * @param attrs an existing array where each of the styling expressions
23776
- * will be inserted into.
23777
- */
23778
- populateInitialStylingAttrs(attrs) {
23779
- // [CLASS_MARKER, 'foo', 'bar', 'baz' ...]
23780
- if (this._initialClassValues.length) {
23781
- attrs.push(literal(1 /* AttributeMarker.Classes */));
23782
- for (let i = 0; i < this._initialClassValues.length; i++) {
23783
- attrs.push(literal(this._initialClassValues[i]));
23784
- }
24460
+ unit.create.push(op);
24461
+ }
24462
+ /**
24463
+ * Ingest a literal text node from the AST into the given `ViewCompilation`.
24464
+ */
24465
+ function ingestText(unit, text, icuPlaceholder) {
24466
+ unit.create.push(createTextOp(unit.job.allocateXrefId(), text.value, icuPlaceholder, text.sourceSpan));
24467
+ }
24468
+ /**
24469
+ * Ingest an interpolated text node from the AST into the given `ViewCompilation`.
24470
+ */
24471
+ function ingestBoundText(unit, text, icuPlaceholder) {
24472
+ let value = text.value;
24473
+ if (value instanceof ASTWithSource) {
24474
+ value = value.ast;
24475
+ }
24476
+ if (!(value instanceof Interpolation$1)) {
24477
+ throw new Error(`AssertionError: expected Interpolation for BoundText node, got ${value.constructor.name}`);
24478
+ }
24479
+ if (text.i18n !== undefined && !(text.i18n instanceof Container)) {
24480
+ throw Error(`Unhandled i18n metadata type for text interpolation: ${text.i18n?.constructor.name}`);
24481
+ }
24482
+ const i18nPlaceholders = text.i18n instanceof Container ?
24483
+ text.i18n.children
24484
+ .filter((node) => node instanceof Placeholder)
24485
+ .map(placeholder => placeholder.name) :
24486
+ [];
24487
+ if (i18nPlaceholders.length > 0 && i18nPlaceholders.length !== value.expressions.length) {
24488
+ throw Error(`Unexpected number of i18n placeholders (${value.expressions.length}) for BoundText with ${value.expressions.length} expressions`);
24489
+ }
24490
+ const textXref = unit.job.allocateXrefId();
24491
+ unit.create.push(createTextOp(textXref, '', icuPlaceholder, text.sourceSpan));
24492
+ // TemplateDefinitionBuilder does not generate source maps for sub-expressions inside an
24493
+ // interpolation. We copy that behavior in compatibility mode.
24494
+ // TODO: is it actually correct to generate these extra maps in modern mode?
24495
+ const baseSourceSpan = unit.job.compatibility ? null : text.sourceSpan;
24496
+ unit.update.push(createInterpolateTextOp(textXref, new Interpolation(value.strings, value.expressions.map(expr => convertAst(expr, unit.job, baseSourceSpan)), i18nPlaceholders), text.sourceSpan));
24497
+ }
24498
+ /**
24499
+ * Ingest an `@if` block into the given `ViewCompilation`.
24500
+ */
24501
+ function ingestIfBlock(unit, ifBlock) {
24502
+ let firstXref = null;
24503
+ let firstSlotHandle = null;
24504
+ let conditions = [];
24505
+ for (let i = 0; i < ifBlock.branches.length; i++) {
24506
+ const ifCase = ifBlock.branches[i];
24507
+ const cView = unit.job.allocateView(unit.xref);
24508
+ let tagName = null;
24509
+ // Only the first branch can be used for projection, because the conditional
24510
+ // uses the container of the first branch as the insertion point for all branches.
24511
+ if (i === 0) {
24512
+ tagName = ingestControlFlowInsertionPoint(unit, cView.xref, ifCase);
23785
24513
  }
23786
- // [STYLE_MARKER, 'width', '200px', 'height', '100px', ...]
23787
- if (this._initialStyleValues.length) {
23788
- attrs.push(literal(2 /* AttributeMarker.Styles */));
23789
- for (let i = 0; i < this._initialStyleValues.length; i += 2) {
23790
- attrs.push(literal(this._initialStyleValues[i]), literal(this._initialStyleValues[i + 1]));
24514
+ if (ifCase.expressionAlias !== null) {
24515
+ cView.contextVariables.set(ifCase.expressionAlias.name, CTX_REF);
24516
+ }
24517
+ let ifCaseI18nMeta = undefined;
24518
+ if (ifCase.i18n !== undefined) {
24519
+ if (!(ifCase.i18n instanceof BlockPlaceholder)) {
24520
+ throw Error(`Unhandled i18n metadata type for if block: ${ifCase.i18n?.constructor.name}`);
23791
24521
  }
24522
+ ifCaseI18nMeta = ifCase.i18n;
23792
24523
  }
23793
- }
23794
- /**
23795
- * Builds an instruction with all the expressions and parameters for `elementHostAttrs`.
23796
- *
23797
- * The instruction generation code below is used for producing the AOT statement code which is
23798
- * responsible for registering initial styles (within a directive hostBindings' creation block),
23799
- * as well as any of the provided attribute values, to the directive host element.
23800
- */
23801
- assignHostAttrs(attrs, definitionMap) {
23802
- if (this._directiveExpr && (attrs.length || this._hasInitialValues)) {
23803
- this.populateInitialStylingAttrs(attrs);
23804
- definitionMap.set('hostAttrs', literalArr(attrs));
24524
+ const templateOp = createTemplateOp(cView.xref, TemplateKind.Block, tagName, 'Conditional', Namespace.HTML, ifCaseI18nMeta, ifCase.startSourceSpan, ifCase.sourceSpan);
24525
+ unit.create.push(templateOp);
24526
+ if (firstXref === null) {
24527
+ firstXref = cView.xref;
24528
+ firstSlotHandle = templateOp.handle;
23805
24529
  }
24530
+ const caseExpr = ifCase.expression ? convertAst(ifCase.expression, unit.job, null) : null;
24531
+ const conditionalCaseExpr = new ConditionalCaseExpr(caseExpr, templateOp.xref, templateOp.handle, ifCase.expressionAlias);
24532
+ conditions.push(conditionalCaseExpr);
24533
+ ingestNodes(cView, ifCase.children);
23806
24534
  }
23807
- /**
23808
- * Builds an instruction with all the expressions and parameters for `classMap`.
23809
- *
23810
- * The instruction data will contain all expressions for `classMap` to function
23811
- * which includes the `[class]` expression params.
23812
- */
23813
- buildClassMapInstruction(valueConverter) {
23814
- if (this._classMapInput) {
23815
- return this._buildMapBasedInstruction(valueConverter, true, this._classMapInput);
23816
- }
24535
+ const conditional = createConditionalOp(firstXref, firstSlotHandle, null, conditions, ifBlock.sourceSpan);
24536
+ unit.update.push(conditional);
24537
+ }
24538
+ /**
24539
+ * Ingest an `@switch` block into the given `ViewCompilation`.
24540
+ */
24541
+ function ingestSwitchBlock(unit, switchBlock) {
24542
+ // Don't ingest empty switches since they won't render anything.
24543
+ if (switchBlock.cases.length === 0) {
24544
+ return;
24545
+ }
24546
+ let firstXref = null;
24547
+ let firstSlotHandle = null;
24548
+ let conditions = [];
24549
+ for (const switchCase of switchBlock.cases) {
24550
+ const cView = unit.job.allocateView(unit.xref);
24551
+ let switchCaseI18nMeta = undefined;
24552
+ if (switchCase.i18n !== undefined) {
24553
+ if (!(switchCase.i18n instanceof BlockPlaceholder)) {
24554
+ throw Error(`Unhandled i18n metadata type for switch block: ${switchCase.i18n?.constructor.name}`);
24555
+ }
24556
+ switchCaseI18nMeta = switchCase.i18n;
24557
+ }
24558
+ const templateOp = createTemplateOp(cView.xref, TemplateKind.Block, null, 'Case', Namespace.HTML, switchCaseI18nMeta, switchCase.startSourceSpan, switchCase.sourceSpan);
24559
+ unit.create.push(templateOp);
24560
+ if (firstXref === null) {
24561
+ firstXref = cView.xref;
24562
+ firstSlotHandle = templateOp.handle;
24563
+ }
24564
+ const caseExpr = switchCase.expression ?
24565
+ convertAst(switchCase.expression, unit.job, switchBlock.startSourceSpan) :
24566
+ null;
24567
+ const conditionalCaseExpr = new ConditionalCaseExpr(caseExpr, templateOp.xref, templateOp.handle);
24568
+ conditions.push(conditionalCaseExpr);
24569
+ ingestNodes(cView, switchCase.children);
24570
+ }
24571
+ const conditional = createConditionalOp(firstXref, firstSlotHandle, convertAst(switchBlock.expression, unit.job, null), conditions, switchBlock.sourceSpan);
24572
+ unit.update.push(conditional);
24573
+ }
24574
+ function ingestDeferView(unit, suffix, i18nMeta, children, sourceSpan) {
24575
+ if (i18nMeta !== undefined && !(i18nMeta instanceof BlockPlaceholder)) {
24576
+ throw Error('Unhandled i18n metadata type for defer block');
24577
+ }
24578
+ if (children === undefined) {
23817
24579
  return null;
23818
24580
  }
23819
- /**
23820
- * Builds an instruction with all the expressions and parameters for `styleMap`.
23821
- *
23822
- * The instruction data will contain all expressions for `styleMap` to function
23823
- * which includes the `[style]` expression params.
23824
- */
23825
- buildStyleMapInstruction(valueConverter) {
23826
- if (this._styleMapInput) {
23827
- return this._buildMapBasedInstruction(valueConverter, false, this._styleMapInput);
24581
+ const secondaryView = unit.job.allocateView(unit.xref);
24582
+ ingestNodes(secondaryView, children);
24583
+ const templateOp = createTemplateOp(secondaryView.xref, TemplateKind.Block, null, `Defer${suffix}`, Namespace.HTML, i18nMeta, sourceSpan, sourceSpan);
24584
+ unit.create.push(templateOp);
24585
+ return templateOp;
24586
+ }
24587
+ function ingestDeferBlock(unit, deferBlock) {
24588
+ const blockMeta = unit.job.deferBlocksMeta.get(deferBlock);
24589
+ if (blockMeta === undefined) {
24590
+ throw new Error(`AssertionError: unable to find metadata for deferred block`);
24591
+ }
24592
+ // Generate the defer main view and all secondary views.
24593
+ const main = ingestDeferView(unit, '', deferBlock.i18n, deferBlock.children, deferBlock.sourceSpan);
24594
+ const loading = ingestDeferView(unit, 'Loading', deferBlock.loading?.i18n, deferBlock.loading?.children, deferBlock.loading?.sourceSpan);
24595
+ const placeholder = ingestDeferView(unit, 'Placeholder', deferBlock.placeholder?.i18n, deferBlock.placeholder?.children, deferBlock.placeholder?.sourceSpan);
24596
+ const error = ingestDeferView(unit, 'Error', deferBlock.error?.i18n, deferBlock.error?.children, deferBlock.error?.sourceSpan);
24597
+ // Create the main defer op, and ops for all secondary views.
24598
+ const deferXref = unit.job.allocateXrefId();
24599
+ const deferOp = createDeferOp(deferXref, main.xref, main.handle, blockMeta, unit.job.allDeferrableDepsFn, deferBlock.sourceSpan);
24600
+ deferOp.placeholderView = placeholder?.xref ?? null;
24601
+ deferOp.placeholderSlot = placeholder?.handle ?? null;
24602
+ deferOp.loadingSlot = loading?.handle ?? null;
24603
+ deferOp.errorSlot = error?.handle ?? null;
24604
+ deferOp.placeholderMinimumTime = deferBlock.placeholder?.minimumTime ?? null;
24605
+ deferOp.loadingMinimumTime = deferBlock.loading?.minimumTime ?? null;
24606
+ deferOp.loadingAfterTime = deferBlock.loading?.afterTime ?? null;
24607
+ unit.create.push(deferOp);
24608
+ // Configure all defer `on` conditions.
24609
+ // TODO: refactor prefetch triggers to use a separate op type, with a shared superclass. This will
24610
+ // make it easier to refactor prefetch behavior in the future.
24611
+ let prefetch = false;
24612
+ let deferOnOps = [];
24613
+ let deferWhenOps = [];
24614
+ for (const triggers of [deferBlock.triggers, deferBlock.prefetchTriggers]) {
24615
+ if (triggers.idle !== undefined) {
24616
+ const deferOnOp = createDeferOnOp(deferXref, { kind: DeferTriggerKind.Idle }, prefetch, triggers.idle.sourceSpan);
24617
+ deferOnOps.push(deferOnOp);
24618
+ }
24619
+ if (triggers.immediate !== undefined) {
24620
+ const deferOnOp = createDeferOnOp(deferXref, { kind: DeferTriggerKind.Immediate }, prefetch, triggers.immediate.sourceSpan);
24621
+ deferOnOps.push(deferOnOp);
24622
+ }
24623
+ if (triggers.timer !== undefined) {
24624
+ const deferOnOp = createDeferOnOp(deferXref, { kind: DeferTriggerKind.Timer, delay: triggers.timer.delay }, prefetch, triggers.timer.sourceSpan);
24625
+ deferOnOps.push(deferOnOp);
24626
+ }
24627
+ if (triggers.hover !== undefined) {
24628
+ const deferOnOp = createDeferOnOp(deferXref, {
24629
+ kind: DeferTriggerKind.Hover,
24630
+ targetName: triggers.hover.reference,
24631
+ targetXref: null,
24632
+ targetSlot: null,
24633
+ targetView: null,
24634
+ targetSlotViewSteps: null,
24635
+ }, prefetch, triggers.hover.sourceSpan);
24636
+ deferOnOps.push(deferOnOp);
24637
+ }
24638
+ if (triggers.interaction !== undefined) {
24639
+ const deferOnOp = createDeferOnOp(deferXref, {
24640
+ kind: DeferTriggerKind.Interaction,
24641
+ targetName: triggers.interaction.reference,
24642
+ targetXref: null,
24643
+ targetSlot: null,
24644
+ targetView: null,
24645
+ targetSlotViewSteps: null,
24646
+ }, prefetch, triggers.interaction.sourceSpan);
24647
+ deferOnOps.push(deferOnOp);
24648
+ }
24649
+ if (triggers.viewport !== undefined) {
24650
+ const deferOnOp = createDeferOnOp(deferXref, {
24651
+ kind: DeferTriggerKind.Viewport,
24652
+ targetName: triggers.viewport.reference,
24653
+ targetXref: null,
24654
+ targetSlot: null,
24655
+ targetView: null,
24656
+ targetSlotViewSteps: null,
24657
+ }, prefetch, triggers.viewport.sourceSpan);
24658
+ deferOnOps.push(deferOnOp);
24659
+ }
24660
+ if (triggers.when !== undefined) {
24661
+ if (triggers.when.value instanceof Interpolation$1) {
24662
+ // TemplateDefinitionBuilder supports this case, but it's very strange to me. What would it
24663
+ // even mean?
24664
+ throw new Error(`Unexpected interpolation in defer block when trigger`);
24665
+ }
24666
+ const deferOnOp = createDeferWhenOp(deferXref, convertAst(triggers.when.value, unit.job, triggers.when.sourceSpan), prefetch, triggers.when.sourceSpan);
24667
+ deferWhenOps.push(deferOnOp);
24668
+ }
24669
+ // If no (non-prefetching) defer triggers were provided, default to `idle`.
24670
+ if (deferOnOps.length === 0 && deferWhenOps.length === 0) {
24671
+ deferOnOps.push(createDeferOnOp(deferXref, { kind: DeferTriggerKind.Idle }, false, null));
24672
+ }
24673
+ prefetch = true;
24674
+ }
24675
+ unit.create.push(deferOnOps);
24676
+ unit.update.push(deferWhenOps);
24677
+ }
24678
+ function ingestIcu(unit, icu) {
24679
+ if (icu.i18n instanceof Message && isSingleI18nIcu(icu.i18n)) {
24680
+ const xref = unit.job.allocateXrefId();
24681
+ unit.create.push(createIcuStartOp(xref, icu.i18n, icuFromI18nMessage(icu.i18n).name, null));
24682
+ for (const [placeholder, text] of Object.entries({ ...icu.vars, ...icu.placeholders })) {
24683
+ if (text instanceof BoundText) {
24684
+ ingestBoundText(unit, text, placeholder);
24685
+ }
24686
+ else {
24687
+ ingestText(unit, text, placeholder);
24688
+ }
23828
24689
  }
23829
- return null;
24690
+ unit.create.push(createIcuEndOp(xref));
23830
24691
  }
23831
- _buildMapBasedInstruction(valueConverter, isClassBased, stylingInput) {
23832
- // each styling binding value is stored in the LView
23833
- // map-based bindings allocate two slots: one for the
23834
- // previous binding value and another for the previous
23835
- // className or style attribute value.
23836
- let totalBindingSlotsRequired = MIN_STYLING_BINDING_SLOTS_REQUIRED;
23837
- // these values must be outside of the update block so that they can
23838
- // be evaluated (the AST visit call) during creation time so that any
23839
- // pipes can be picked up in time before the template is built
23840
- const mapValue = stylingInput.value.visit(valueConverter);
23841
- let reference;
23842
- if (mapValue instanceof Interpolation$1) {
23843
- totalBindingSlotsRequired += mapValue.expressions.length;
23844
- reference = isClassBased ? getClassMapInterpolationExpression(mapValue) :
23845
- getStyleMapInterpolationExpression(mapValue);
24692
+ else {
24693
+ throw Error(`Unhandled i18n metadata type for ICU: ${icu.i18n?.constructor.name}`);
24694
+ }
24695
+ }
24696
+ /**
24697
+ * Ingest an `@for` block into the given `ViewCompilation`.
24698
+ */
24699
+ function ingestForBlock(unit, forBlock) {
24700
+ const repeaterView = unit.job.allocateView(unit.xref);
24701
+ // Set all the context variables and aliases available in the repeater.
24702
+ repeaterView.contextVariables.set(forBlock.item.name, forBlock.item.value);
24703
+ repeaterView.contextVariables.set(forBlock.contextVariables.$index.name, forBlock.contextVariables.$index.value);
24704
+ repeaterView.contextVariables.set(forBlock.contextVariables.$count.name, forBlock.contextVariables.$count.value);
24705
+ // We copy TemplateDefinitionBuilder's scheme of creating names for `$count` and `$index` that are
24706
+ // suffixed with special information, to disambiguate which level of nested loop the below aliases
24707
+ // refer to.
24708
+ // TODO: We should refactor Template Pipeline's variable phases to gracefully handle shadowing,
24709
+ // and arbitrarily many levels of variables depending on each other.
24710
+ const indexName = `ɵ${forBlock.contextVariables.$index.name}_${repeaterView.xref}`;
24711
+ const countName = `ɵ${forBlock.contextVariables.$count.name}_${repeaterView.xref}`;
24712
+ repeaterView.contextVariables.set(indexName, forBlock.contextVariables.$index.value);
24713
+ repeaterView.contextVariables.set(countName, forBlock.contextVariables.$count.value);
24714
+ repeaterView.aliases.add({
24715
+ kind: SemanticVariableKind.Alias,
24716
+ name: null,
24717
+ identifier: forBlock.contextVariables.$first.name,
24718
+ expression: new LexicalReadExpr(indexName).identical(literal(0))
24719
+ });
24720
+ repeaterView.aliases.add({
24721
+ kind: SemanticVariableKind.Alias,
24722
+ name: null,
24723
+ identifier: forBlock.contextVariables.$last.name,
24724
+ expression: new LexicalReadExpr(indexName).identical(new LexicalReadExpr(countName).minus(literal(1)))
24725
+ });
24726
+ repeaterView.aliases.add({
24727
+ kind: SemanticVariableKind.Alias,
24728
+ name: null,
24729
+ identifier: forBlock.contextVariables.$even.name,
24730
+ expression: new LexicalReadExpr(indexName).modulo(literal(2)).identical(literal(0))
24731
+ });
24732
+ repeaterView.aliases.add({
24733
+ kind: SemanticVariableKind.Alias,
24734
+ name: null,
24735
+ identifier: forBlock.contextVariables.$odd.name,
24736
+ expression: new LexicalReadExpr(indexName).modulo(literal(2)).notIdentical(literal(0))
24737
+ });
24738
+ const sourceSpan = convertSourceSpan(forBlock.trackBy.span, forBlock.sourceSpan);
24739
+ const track = convertAst(forBlock.trackBy, unit.job, sourceSpan);
24740
+ ingestNodes(repeaterView, forBlock.children);
24741
+ let emptyView = null;
24742
+ let emptyTagName = null;
24743
+ if (forBlock.empty !== null) {
24744
+ emptyView = unit.job.allocateView(unit.xref);
24745
+ ingestNodes(emptyView, forBlock.empty.children);
24746
+ emptyTagName = ingestControlFlowInsertionPoint(unit, emptyView.xref, forBlock.empty);
24747
+ }
24748
+ const varNames = {
24749
+ $index: forBlock.contextVariables.$index.name,
24750
+ $count: forBlock.contextVariables.$count.name,
24751
+ $first: forBlock.contextVariables.$first.name,
24752
+ $last: forBlock.contextVariables.$last.name,
24753
+ $even: forBlock.contextVariables.$even.name,
24754
+ $odd: forBlock.contextVariables.$odd.name,
24755
+ $implicit: forBlock.item.name,
24756
+ };
24757
+ if (forBlock.i18n !== undefined && !(forBlock.i18n instanceof BlockPlaceholder)) {
24758
+ throw Error('AssertionError: Unhandled i18n metadata type or @for');
24759
+ }
24760
+ if (forBlock.empty?.i18n !== undefined &&
24761
+ !(forBlock.empty.i18n instanceof BlockPlaceholder)) {
24762
+ throw Error('AssertionError: Unhandled i18n metadata type or @empty');
24763
+ }
24764
+ const i18nPlaceholder = forBlock.i18n;
24765
+ const emptyI18nPlaceholder = forBlock.empty?.i18n;
24766
+ const tagName = ingestControlFlowInsertionPoint(unit, repeaterView.xref, forBlock);
24767
+ const repeaterCreate = createRepeaterCreateOp(repeaterView.xref, emptyView?.xref ?? null, tagName, track, varNames, emptyTagName, i18nPlaceholder, emptyI18nPlaceholder, forBlock.startSourceSpan, forBlock.sourceSpan);
24768
+ unit.create.push(repeaterCreate);
24769
+ const expression = convertAst(forBlock.expression, unit.job, convertSourceSpan(forBlock.expression.span, forBlock.sourceSpan));
24770
+ const repeater = createRepeaterOp(repeaterCreate.xref, repeaterCreate.handle, expression, forBlock.sourceSpan);
24771
+ unit.update.push(repeater);
24772
+ }
24773
+ /**
24774
+ * Convert a template AST expression into an output AST expression.
24775
+ */
24776
+ function convertAst(ast, job, baseSourceSpan) {
24777
+ if (ast instanceof ASTWithSource) {
24778
+ return convertAst(ast.ast, job, baseSourceSpan);
24779
+ }
24780
+ else if (ast instanceof PropertyRead) {
24781
+ const isThisReceiver = ast.receiver instanceof ThisReceiver;
24782
+ // Whether this is an implicit receiver, *excluding* explicit reads of `this`.
24783
+ const isImplicitReceiver = ast.receiver instanceof ImplicitReceiver && !(ast.receiver instanceof ThisReceiver);
24784
+ // Whether the name of the read is a node that should be never retain its explicit this
24785
+ // receiver.
24786
+ const isSpecialNode = ast.name === '$any' || ast.name === '$event';
24787
+ // TODO: The most sensible condition here would be simply `isImplicitReceiver`, to convert only
24788
+ // actual implicit `this` reads, and not explicit ones. However, TemplateDefinitionBuilder (and
24789
+ // the Typecheck block!) both have the same bug, in which they also consider explicit `this`
24790
+ // reads to be implicit. This causes problems when the explicit `this` read is inside a
24791
+ // template with a context that also provides the variable name being read:
24792
+ // ```
24793
+ // <ng-template let-a>{{this.a}}</ng-template>
24794
+ // ```
24795
+ // The whole point of the explicit `this` was to access the class property, but TDB and the
24796
+ // current TCB treat the read as implicit, and give you the context property instead!
24797
+ //
24798
+ // For now, we emulate this old behvaior by aggressively converting explicit reads to to
24799
+ // implicit reads, except for the special cases that TDB and the current TCB protect. However,
24800
+ // it would be an improvement to fix this.
24801
+ //
24802
+ // See also the corresponding comment for the TCB, in `type_check_block.ts`.
24803
+ if (isImplicitReceiver || (isThisReceiver && !isSpecialNode)) {
24804
+ return new LexicalReadExpr(ast.name);
23846
24805
  }
23847
24806
  else {
23848
- reference = isClassBased ? Identifiers.classMap : Identifiers.styleMap;
24807
+ return new ReadPropExpr(convertAst(ast.receiver, job, baseSourceSpan), ast.name, null, convertSourceSpan(ast.span, baseSourceSpan));
23849
24808
  }
23850
- return {
23851
- reference,
23852
- calls: [{
23853
- supportsInterpolation: true,
23854
- sourceSpan: stylingInput.sourceSpan,
23855
- allocateBindingSlots: totalBindingSlotsRequired,
23856
- params: (convertFn) => {
23857
- const convertResult = convertFn(mapValue);
23858
- const params = Array.isArray(convertResult) ? convertResult : [convertResult];
23859
- return params;
23860
- }
23861
- }]
23862
- };
23863
24809
  }
23864
- _buildSingleInputs(reference, inputs, valueConverter, getInterpolationExpressionFn, isClassBased) {
23865
- const instructions = [];
23866
- inputs.forEach(input => {
23867
- const previousInstruction = instructions[instructions.length - 1];
23868
- const value = input.value.visit(valueConverter);
23869
- let referenceForCall = reference;
23870
- // each styling binding value is stored in the LView
23871
- // but there are two values stored for each binding:
23872
- // 1) the value itself
23873
- // 2) an intermediate value (concatenation of style up to this point).
23874
- // We need to store the intermediate value so that we don't allocate
23875
- // the strings on each CD.
23876
- let totalBindingSlotsRequired = MIN_STYLING_BINDING_SLOTS_REQUIRED;
23877
- if (value instanceof Interpolation$1) {
23878
- totalBindingSlotsRequired += value.expressions.length;
23879
- if (getInterpolationExpressionFn) {
23880
- referenceForCall = getInterpolationExpressionFn(value);
23881
- }
23882
- }
23883
- const call = {
23884
- sourceSpan: input.sourceSpan,
23885
- allocateBindingSlots: totalBindingSlotsRequired,
23886
- supportsInterpolation: !!getInterpolationExpressionFn,
23887
- params: (convertFn) => {
23888
- // params => stylingProp(propName, value, suffix)
23889
- const params = [];
23890
- params.push(literal(input.name));
23891
- const convertResult = convertFn(value);
23892
- if (Array.isArray(convertResult)) {
23893
- params.push(...convertResult);
23894
- }
23895
- else {
23896
- params.push(convertResult);
23897
- }
23898
- // [style.prop] bindings may use suffix values (e.g. px, em, etc...), therefore,
23899
- // if that is detected then we need to pass that in as an optional param.
23900
- if (!isClassBased && input.suffix !== null) {
23901
- params.push(literal(input.suffix));
23902
- }
23903
- return params;
23904
- }
23905
- };
23906
- // If we ended up generating a call to the same instruction as the previous styling property
23907
- // we can chain the calls together safely to save some bytes, otherwise we have to generate
23908
- // a separate instruction call. This is primarily a concern with interpolation instructions
23909
- // where we may start off with one `reference`, but end up using another based on the
23910
- // number of interpolations.
23911
- if (previousInstruction && previousInstruction.reference === referenceForCall) {
23912
- previousInstruction.calls.push(call);
23913
- }
23914
- else {
23915
- instructions.push({ reference: referenceForCall, calls: [call] });
23916
- }
24810
+ else if (ast instanceof PropertyWrite) {
24811
+ if (ast.receiver instanceof ImplicitReceiver) {
24812
+ return new WritePropExpr(
24813
+ // TODO: Is it correct to always use the root context in place of the implicit receiver?
24814
+ new ContextExpr(job.root.xref), ast.name, convertAst(ast.value, job, baseSourceSpan), null, convertSourceSpan(ast.span, baseSourceSpan));
24815
+ }
24816
+ return new WritePropExpr(convertAst(ast.receiver, job, baseSourceSpan), ast.name, convertAst(ast.value, job, baseSourceSpan), undefined, convertSourceSpan(ast.span, baseSourceSpan));
24817
+ }
24818
+ else if (ast instanceof KeyedWrite) {
24819
+ return new WriteKeyExpr(convertAst(ast.receiver, job, baseSourceSpan), convertAst(ast.key, job, baseSourceSpan), convertAst(ast.value, job, baseSourceSpan), undefined, convertSourceSpan(ast.span, baseSourceSpan));
24820
+ }
24821
+ else if (ast instanceof Call) {
24822
+ if (ast.receiver instanceof ImplicitReceiver) {
24823
+ throw new Error(`Unexpected ImplicitReceiver`);
24824
+ }
24825
+ else {
24826
+ return new InvokeFunctionExpr(convertAst(ast.receiver, job, baseSourceSpan), ast.args.map(arg => convertAst(arg, job, baseSourceSpan)), undefined, convertSourceSpan(ast.span, baseSourceSpan));
24827
+ }
24828
+ }
24829
+ else if (ast instanceof LiteralPrimitive) {
24830
+ return literal(ast.value, undefined, convertSourceSpan(ast.span, baseSourceSpan));
24831
+ }
24832
+ else if (ast instanceof Unary) {
24833
+ switch (ast.operator) {
24834
+ case '+':
24835
+ return new UnaryOperatorExpr(exports.UnaryOperator.Plus, convertAst(ast.expr, job, baseSourceSpan), undefined, convertSourceSpan(ast.span, baseSourceSpan));
24836
+ case '-':
24837
+ return new UnaryOperatorExpr(exports.UnaryOperator.Minus, convertAst(ast.expr, job, baseSourceSpan), undefined, convertSourceSpan(ast.span, baseSourceSpan));
24838
+ default:
24839
+ throw new Error(`AssertionError: unknown unary operator ${ast.operator}`);
24840
+ }
24841
+ }
24842
+ else if (ast instanceof Binary) {
24843
+ const operator = BINARY_OPERATORS.get(ast.operation);
24844
+ if (operator === undefined) {
24845
+ throw new Error(`AssertionError: unknown binary operator ${ast.operation}`);
24846
+ }
24847
+ return new BinaryOperatorExpr(operator, convertAst(ast.left, job, baseSourceSpan), convertAst(ast.right, job, baseSourceSpan), undefined, convertSourceSpan(ast.span, baseSourceSpan));
24848
+ }
24849
+ else if (ast instanceof ThisReceiver) {
24850
+ // TODO: should context expressions have source maps?
24851
+ return new ContextExpr(job.root.xref);
24852
+ }
24853
+ else if (ast instanceof KeyedRead) {
24854
+ return new ReadKeyExpr(convertAst(ast.receiver, job, baseSourceSpan), convertAst(ast.key, job, baseSourceSpan), undefined, convertSourceSpan(ast.span, baseSourceSpan));
24855
+ }
24856
+ else if (ast instanceof Chain) {
24857
+ throw new Error(`AssertionError: Chain in unknown context`);
24858
+ }
24859
+ else if (ast instanceof LiteralMap) {
24860
+ const entries = ast.keys.map((key, idx) => {
24861
+ const value = ast.values[idx];
24862
+ // TODO: should literals have source maps, or do we just map the whole surrounding
24863
+ // expression?
24864
+ return new LiteralMapEntry(key.key, convertAst(value, job, baseSourceSpan), key.quoted);
23917
24865
  });
23918
- return instructions;
24866
+ return new LiteralMapExpr(entries, undefined, convertSourceSpan(ast.span, baseSourceSpan));
23919
24867
  }
23920
- _buildClassInputs(valueConverter) {
23921
- if (this._singleClassInputs) {
23922
- return this._buildSingleInputs(Identifiers.classProp, this._singleClassInputs, valueConverter, null, true);
24868
+ else if (ast instanceof LiteralArray) {
24869
+ // TODO: should literals have source maps, or do we just map the whole surrounding expression?
24870
+ return new LiteralArrayExpr(ast.expressions.map(expr => convertAst(expr, job, baseSourceSpan)));
24871
+ }
24872
+ else if (ast instanceof Conditional) {
24873
+ return new ConditionalExpr(convertAst(ast.condition, job, baseSourceSpan), convertAst(ast.trueExp, job, baseSourceSpan), convertAst(ast.falseExp, job, baseSourceSpan), undefined, convertSourceSpan(ast.span, baseSourceSpan));
24874
+ }
24875
+ else if (ast instanceof NonNullAssert) {
24876
+ // A non-null assertion shouldn't impact generated instructions, so we can just drop it.
24877
+ return convertAst(ast.expression, job, baseSourceSpan);
24878
+ }
24879
+ else if (ast instanceof BindingPipe) {
24880
+ // TODO: pipes should probably have source maps; figure out details.
24881
+ return new PipeBindingExpr(job.allocateXrefId(), new SlotHandle(), ast.name, [
24882
+ convertAst(ast.exp, job, baseSourceSpan),
24883
+ ...ast.args.map(arg => convertAst(arg, job, baseSourceSpan)),
24884
+ ]);
24885
+ }
24886
+ else if (ast instanceof SafeKeyedRead) {
24887
+ return new SafeKeyedReadExpr(convertAst(ast.receiver, job, baseSourceSpan), convertAst(ast.key, job, baseSourceSpan), convertSourceSpan(ast.span, baseSourceSpan));
24888
+ }
24889
+ else if (ast instanceof SafePropertyRead) {
24890
+ // TODO: source span
24891
+ return new SafePropertyReadExpr(convertAst(ast.receiver, job, baseSourceSpan), ast.name);
24892
+ }
24893
+ else if (ast instanceof SafeCall) {
24894
+ // TODO: source span
24895
+ return new SafeInvokeFunctionExpr(convertAst(ast.receiver, job, baseSourceSpan), ast.args.map(a => convertAst(a, job, baseSourceSpan)));
24896
+ }
24897
+ else if (ast instanceof EmptyExpr$1) {
24898
+ return new EmptyExpr(convertSourceSpan(ast.span, baseSourceSpan));
24899
+ }
24900
+ else if (ast instanceof PrefixNot) {
24901
+ return not(convertAst(ast.expression, job, baseSourceSpan), convertSourceSpan(ast.span, baseSourceSpan));
24902
+ }
24903
+ else {
24904
+ throw new Error(`Unhandled expression type "${ast.constructor.name}" in file "${baseSourceSpan?.start.file.url}"`);
24905
+ }
24906
+ }
24907
+ function convertAstWithInterpolation(job, value, i18nMeta, sourceSpan) {
24908
+ let expression;
24909
+ if (value instanceof Interpolation$1) {
24910
+ expression = new Interpolation(value.strings, value.expressions.map(e => convertAst(e, job, sourceSpan ?? null)), Object.keys(asMessage(i18nMeta)?.placeholders ?? {}));
24911
+ }
24912
+ else if (value instanceof AST) {
24913
+ expression = convertAst(value, job, sourceSpan ?? null);
24914
+ }
24915
+ else {
24916
+ expression = literal(value);
24917
+ }
24918
+ return expression;
24919
+ }
24920
+ // TODO: Can we populate Template binding kinds in ingest?
24921
+ const BINDING_KINDS = new Map([
24922
+ [0 /* e.BindingType.Property */, BindingKind.Property],
24923
+ [5 /* e.BindingType.TwoWay */, BindingKind.TwoWayProperty],
24924
+ [1 /* e.BindingType.Attribute */, BindingKind.Attribute],
24925
+ [2 /* e.BindingType.Class */, BindingKind.ClassName],
24926
+ [3 /* e.BindingType.Style */, BindingKind.StyleProperty],
24927
+ [4 /* e.BindingType.Animation */, BindingKind.Animation],
24928
+ ]);
24929
+ /**
24930
+ * Checks whether the given template is a plain ng-template (as opposed to another kind of template
24931
+ * such as a structural directive template or control flow template). This is checked based on the
24932
+ * tagName. We can expect that only plain ng-templates will come through with a tagName of
24933
+ * 'ng-template'.
24934
+ *
24935
+ * Here are some of the cases we expect:
24936
+ *
24937
+ * | Angular HTML | Template tagName |
24938
+ * | ---------------------------------- | ------------------ |
24939
+ * | `<ng-template>` | 'ng-template' |
24940
+ * | `<div *ngIf="true">` | 'div' |
24941
+ * | `<svg><ng-template>` | 'svg:ng-template' |
24942
+ * | `@if (true) {` | 'Conditional' |
24943
+ * | `<ng-template *ngIf>` (plain) | 'ng-template' |
24944
+ * | `<ng-template *ngIf>` (structural) | null |
24945
+ */
24946
+ function isPlainTemplate(tmpl) {
24947
+ return splitNsName(tmpl.tagName ?? '')[1] === NG_TEMPLATE_TAG_NAME$1;
24948
+ }
24949
+ /**
24950
+ * Ensures that the i18nMeta, if provided, is an i18n.Message.
24951
+ */
24952
+ function asMessage(i18nMeta) {
24953
+ if (i18nMeta == null) {
24954
+ return null;
24955
+ }
24956
+ if (!(i18nMeta instanceof Message)) {
24957
+ throw Error(`Expected i18n meta to be a Message, but got: ${i18nMeta.constructor.name}`);
24958
+ }
24959
+ return i18nMeta;
24960
+ }
24961
+ /**
24962
+ * Process all of the bindings on an element in the template AST and convert them to their IR
24963
+ * representation.
24964
+ */
24965
+ function ingestElementBindings(unit, op, element) {
24966
+ let bindings = new Array();
24967
+ let i18nAttributeBindingNames = new Set();
24968
+ for (const attr of element.attributes) {
24969
+ // Attribute literal bindings, such as `attr.foo="bar"`.
24970
+ const securityContext = domSchema.securityContext(element.name, attr.name, true);
24971
+ bindings.push(createBindingOp(op.xref, BindingKind.Attribute, attr.name, convertAstWithInterpolation(unit.job, attr.value, attr.i18n), null, securityContext, true, false, null, asMessage(attr.i18n), attr.sourceSpan));
24972
+ if (attr.i18n) {
24973
+ i18nAttributeBindingNames.add(attr.name);
23923
24974
  }
23924
- return [];
23925
24975
  }
23926
- _buildStyleInputs(valueConverter) {
23927
- if (this._singleStyleInputs) {
23928
- return this._buildSingleInputs(Identifiers.styleProp, this._singleStyleInputs, valueConverter, getStylePropInterpolationExpression, false);
24976
+ for (const input of element.inputs) {
24977
+ if (i18nAttributeBindingNames.has(input.name)) {
24978
+ console.error(`On component ${unit.job.componentName}, the binding ${input
24979
+ .name} is both an i18n attribute and a property. You may want to remove the property binding. This will become a compilation error in future versions of Angular.`);
23929
24980
  }
23930
- return [];
24981
+ // All dynamic bindings (both attribute and property bindings).
24982
+ bindings.push(createBindingOp(op.xref, BINDING_KINDS.get(input.type), input.name, convertAstWithInterpolation(unit.job, astOf(input.value), input.i18n), input.unit, input.securityContext, false, false, null, asMessage(input.i18n) ?? null, input.sourceSpan));
23931
24983
  }
23932
- /**
23933
- * Constructs all instructions which contain the expressions that will be placed
23934
- * into the update block of a template function or a directive hostBindings function.
23935
- */
23936
- buildUpdateLevelInstructions(valueConverter) {
23937
- const instructions = [];
23938
- if (this.hasBindings) {
23939
- const styleMapInstruction = this.buildStyleMapInstruction(valueConverter);
23940
- if (styleMapInstruction) {
23941
- instructions.push(styleMapInstruction);
24984
+ unit.create.push(bindings.filter((b) => b?.kind === OpKind.ExtractedAttribute));
24985
+ unit.update.push(bindings.filter((b) => b?.kind === OpKind.Binding));
24986
+ for (const output of element.outputs) {
24987
+ if (output.type === 1 /* e.ParsedEventType.Animation */ && output.phase === null) {
24988
+ throw Error('Animation listener should have a phase');
24989
+ }
24990
+ if (output.type === 2 /* e.ParsedEventType.TwoWay */) {
24991
+ unit.create.push(createTwoWayListenerOp(op.xref, op.handle, output.name, op.tag, makeTwoWayListenerHandlerOps(unit, output.handler, output.handlerSpan), output.sourceSpan));
24992
+ }
24993
+ else {
24994
+ unit.create.push(createListenerOp(op.xref, op.handle, output.name, op.tag, makeListenerHandlerOps(unit, output.handler, output.handlerSpan), output.phase, output.target, false, output.sourceSpan));
24995
+ }
24996
+ }
24997
+ // If any of the bindings on this element have an i18n message, then an i18n attrs configuration
24998
+ // op is also required.
24999
+ if (bindings.some(b => b?.i18nMessage) !== null) {
25000
+ unit.create.push(createI18nAttributesOp(unit.job.allocateXrefId(), new SlotHandle(), op.xref));
25001
+ }
25002
+ }
25003
+ /**
25004
+ * Process all of the bindings on a template in the template AST and convert them to their IR
25005
+ * representation.
25006
+ */
25007
+ function ingestTemplateBindings(unit, op, template, templateKind) {
25008
+ let bindings = new Array();
25009
+ for (const attr of template.templateAttrs) {
25010
+ if (attr instanceof TextAttribute) {
25011
+ const securityContext = domSchema.securityContext(NG_TEMPLATE_TAG_NAME$1, attr.name, true);
25012
+ bindings.push(createTemplateBinding(unit, op.xref, 1 /* e.BindingType.Attribute */, attr.name, attr.value, null, securityContext, true, templateKind, asMessage(attr.i18n), attr.sourceSpan));
25013
+ }
25014
+ else {
25015
+ bindings.push(createTemplateBinding(unit, op.xref, attr.type, attr.name, astOf(attr.value), attr.unit, attr.securityContext, true, templateKind, asMessage(attr.i18n), attr.sourceSpan));
25016
+ }
25017
+ }
25018
+ for (const attr of template.attributes) {
25019
+ // Attribute literal bindings, such as `attr.foo="bar"`.
25020
+ const securityContext = domSchema.securityContext(NG_TEMPLATE_TAG_NAME$1, attr.name, true);
25021
+ bindings.push(createTemplateBinding(unit, op.xref, 1 /* e.BindingType.Attribute */, attr.name, attr.value, null, securityContext, false, templateKind, asMessage(attr.i18n), attr.sourceSpan));
25022
+ }
25023
+ for (const input of template.inputs) {
25024
+ // Dynamic bindings (both attribute and property bindings).
25025
+ bindings.push(createTemplateBinding(unit, op.xref, input.type, input.name, astOf(input.value), input.unit, input.securityContext, false, templateKind, asMessage(input.i18n), input.sourceSpan));
25026
+ }
25027
+ unit.create.push(bindings.filter((b) => b?.kind === OpKind.ExtractedAttribute));
25028
+ unit.update.push(bindings.filter((b) => b?.kind === OpKind.Binding));
25029
+ for (const output of template.outputs) {
25030
+ if (output.type === 1 /* e.ParsedEventType.Animation */ && output.phase === null) {
25031
+ throw Error('Animation listener should have a phase');
25032
+ }
25033
+ if (templateKind === TemplateKind.NgTemplate) {
25034
+ if (output.type === 2 /* e.ParsedEventType.TwoWay */) {
25035
+ unit.create.push(createTwoWayListenerOp(op.xref, op.handle, output.name, op.tag, makeTwoWayListenerHandlerOps(unit, output.handler, output.handlerSpan), output.sourceSpan));
23942
25036
  }
23943
- const classMapInstruction = this.buildClassMapInstruction(valueConverter);
23944
- if (classMapInstruction) {
23945
- instructions.push(classMapInstruction);
25037
+ else {
25038
+ unit.create.push(createListenerOp(op.xref, op.handle, output.name, op.tag, makeListenerHandlerOps(unit, output.handler, output.handlerSpan), output.phase, output.target, false, output.sourceSpan));
23946
25039
  }
23947
- instructions.push(...this._buildStyleInputs(valueConverter));
23948
- instructions.push(...this._buildClassInputs(valueConverter));
23949
25040
  }
23950
- return instructions;
25041
+ if (templateKind === TemplateKind.Structural &&
25042
+ output.type !== 1 /* e.ParsedEventType.Animation */) {
25043
+ // Animation bindings are excluded from the structural template's const array.
25044
+ const securityContext = domSchema.securityContext(NG_TEMPLATE_TAG_NAME$1, output.name, false);
25045
+ unit.create.push(createExtractedAttributeOp(op.xref, BindingKind.Property, null, output.name, null, null, null, securityContext));
25046
+ }
23951
25047
  }
23952
- }
23953
- function registerIntoMap(map, key) {
23954
- if (!map.has(key)) {
23955
- map.set(key, map.size);
25048
+ // TODO: Perhaps we could do this in a phase? (It likely wouldn't change the slot indices.)
25049
+ if (bindings.some(b => b?.i18nMessage) !== null) {
25050
+ unit.create.push(createI18nAttributesOp(unit.job.allocateXrefId(), new SlotHandle(), op.xref));
23956
25051
  }
23957
25052
  }
23958
- function parseProperty(name) {
23959
- let hasOverrideFlag = false;
23960
- const overrideIndex = name.indexOf(IMPORTANT_FLAG);
23961
- if (overrideIndex !== -1) {
23962
- name = overrideIndex > 0 ? name.substring(0, overrideIndex) : '';
23963
- hasOverrideFlag = true;
25053
+ /**
25054
+ * Helper to ingest an individual binding on a template, either an explicit `ng-template`, or an
25055
+ * implicit template created via structural directive.
25056
+ *
25057
+ * Bindings on templates are *extremely* tricky. I have tried to isolate all of the confusing edge
25058
+ * cases into this function, and to comment it well to document the behavior.
25059
+ *
25060
+ * Some of this behavior is intuitively incorrect, and we should consider changing it in the future.
25061
+ *
25062
+ * @param view The compilation unit for the view containing the template.
25063
+ * @param xref The xref of the template op.
25064
+ * @param type The binding type, according to the parser. This is fairly reasonable, e.g. both
25065
+ * dynamic and static attributes have e.BindingType.Attribute.
25066
+ * @param name The binding's name.
25067
+ * @param value The bindings's value, which will either be an input AST expression, or a string
25068
+ * literal. Note that the input AST expression may or may not be const -- it will only be a
25069
+ * string literal if the parser considered it a text binding.
25070
+ * @param unit If the binding has a unit (e.g. `px` for style bindings), then this is the unit.
25071
+ * @param securityContext The security context of the binding.
25072
+ * @param isStructuralTemplateAttribute Whether this binding actually applies to the structural
25073
+ * ng-template. For example, an `ngFor` would actually apply to the structural template. (Most
25074
+ * bindings on structural elements target the inner element, not the template.)
25075
+ * @param templateKind Whether this is an explicit `ng-template` or an implicit template created by
25076
+ * a structural directive. This should never be a block template.
25077
+ * @param i18nMessage The i18n metadata for the binding, if any.
25078
+ * @param sourceSpan The source span of the binding.
25079
+ * @returns An IR binding op, or null if the binding should be skipped.
25080
+ */
25081
+ function createTemplateBinding(view, xref, type, name, value, unit, securityContext, isStructuralTemplateAttribute, templateKind, i18nMessage, sourceSpan) {
25082
+ const isTextBinding = typeof value === 'string';
25083
+ // If this is a structural template, then several kinds of bindings should not result in an
25084
+ // update instruction.
25085
+ if (templateKind === TemplateKind.Structural) {
25086
+ if (!isStructuralTemplateAttribute) {
25087
+ switch (type) {
25088
+ case 0 /* e.BindingType.Property */:
25089
+ case 2 /* e.BindingType.Class */:
25090
+ case 3 /* e.BindingType.Style */:
25091
+ // Because this binding doesn't really target the ng-template, it must be a binding on an
25092
+ // inner node of a structural template. We can't skip it entirely, because we still need
25093
+ // it on the ng-template's consts (e.g. for the purposes of directive matching). However,
25094
+ // we should not generate an update instruction for it.
25095
+ return createExtractedAttributeOp(xref, BindingKind.Property, null, name, null, null, i18nMessage, securityContext);
25096
+ case 5 /* e.BindingType.TwoWay */:
25097
+ return createExtractedAttributeOp(xref, BindingKind.TwoWayProperty, null, name, null, null, i18nMessage, securityContext);
25098
+ }
25099
+ }
25100
+ if (!isTextBinding && (type === 1 /* e.BindingType.Attribute */ || type === 4 /* e.BindingType.Animation */)) {
25101
+ // Again, this binding doesn't really target the ng-template; it actually targets the element
25102
+ // inside the structural template. In the case of non-text attribute or animation bindings,
25103
+ // the binding doesn't even show up on the ng-template const array, so we just skip it
25104
+ // entirely.
25105
+ return null;
25106
+ }
23964
25107
  }
23965
- let suffix = null;
23966
- let property = name;
23967
- const unitIndex = name.lastIndexOf('.');
23968
- if (unitIndex > 0) {
23969
- suffix = name.slice(unitIndex + 1);
23970
- property = name.substring(0, unitIndex);
25108
+ let bindingType = BINDING_KINDS.get(type);
25109
+ if (templateKind === TemplateKind.NgTemplate) {
25110
+ // We know we are dealing with bindings directly on an explicit ng-template.
25111
+ // Static attribute bindings should be collected into the const array as k/v pairs. Property
25112
+ // bindings should result in a `property` instruction, and `AttributeMarker.Bindings` const
25113
+ // entries.
25114
+ //
25115
+ // The difficulty is with dynamic attribute, style, and class bindings. These don't really make
25116
+ // sense on an `ng-template` and should probably be parser errors. However,
25117
+ // TemplateDefinitionBuilder generates `property` instructions for them, and so we do that as
25118
+ // well.
25119
+ //
25120
+ // Note that we do have a slight behavior difference with TemplateDefinitionBuilder: although
25121
+ // TDB emits `property` instructions for dynamic attributes, styles, and classes, only styles
25122
+ // and classes also get const collected into the `AttributeMarker.Bindings` field. Dynamic
25123
+ // attribute bindings are missing from the consts entirely. We choose to emit them into the
25124
+ // consts field anyway, to avoid creating special cases for something so arcane and nonsensical.
25125
+ if (type === 2 /* e.BindingType.Class */ || type === 3 /* e.BindingType.Style */ ||
25126
+ (type === 1 /* e.BindingType.Attribute */ && !isTextBinding)) {
25127
+ // TODO: These cases should be parse errors.
25128
+ bindingType = BindingKind.Property;
25129
+ }
25130
+ }
25131
+ return createBindingOp(xref, bindingType, name, convertAstWithInterpolation(view.job, value, i18nMessage), unit, securityContext, isTextBinding, isStructuralTemplateAttribute, templateKind, i18nMessage, sourceSpan);
25132
+ }
25133
+ function makeListenerHandlerOps(unit, handler, handlerSpan) {
25134
+ handler = astOf(handler);
25135
+ const handlerOps = new Array();
25136
+ let handlerExprs = handler instanceof Chain ? handler.expressions : [handler];
25137
+ if (handlerExprs.length === 0) {
25138
+ throw new Error('Expected listener to have non-empty expression list.');
25139
+ }
25140
+ const expressions = handlerExprs.map(expr => convertAst(expr, unit.job, handlerSpan));
25141
+ const returnExpr = expressions.pop();
25142
+ handlerOps.push(...expressions.map(e => createStatementOp(new ExpressionStatement(e, e.sourceSpan))));
25143
+ handlerOps.push(createStatementOp(new ReturnStatement(returnExpr, returnExpr.sourceSpan)));
25144
+ return handlerOps;
25145
+ }
25146
+ function makeTwoWayListenerHandlerOps(unit, handler, handlerSpan) {
25147
+ handler = astOf(handler);
25148
+ const handlerOps = new Array();
25149
+ if (handler instanceof Chain) {
25150
+ if (handler.expressions.length === 1) {
25151
+ handler = handler.expressions[0];
25152
+ }
25153
+ else {
25154
+ // This is validated during parsing already, but we do it here just in case.
25155
+ throw new Error('Expected two-way listener to have a single expression.');
25156
+ }
23971
25157
  }
23972
- return { property, suffix, hasOverrideFlag };
25158
+ const handlerExpr = convertAst(handler, unit.job, handlerSpan);
25159
+ const eventReference = new LexicalReadExpr('$event');
25160
+ const twoWaySetExpr = new TwoWayBindingSetExpr(handlerExpr, eventReference);
25161
+ handlerOps.push(createStatementOp(new ExpressionStatement(twoWaySetExpr)));
25162
+ handlerOps.push(createStatementOp(new ReturnStatement(eventReference)));
25163
+ return handlerOps;
25164
+ }
25165
+ function astOf(ast) {
25166
+ return ast instanceof ASTWithSource ? ast.ast : ast;
23973
25167
  }
23974
25168
  /**
23975
- * Gets the instruction to generate for an interpolated class map.
23976
- * @param interpolation An Interpolation AST
25169
+ * Process all of the local references on an element-like structure in the template AST and
25170
+ * convert them to their IR representation.
23977
25171
  */
23978
- function getClassMapInterpolationExpression(interpolation) {
23979
- switch (getInterpolationArgsLength(interpolation)) {
23980
- case 1:
23981
- return Identifiers.classMap;
23982
- case 3:
23983
- return Identifiers.classMapInterpolate1;
23984
- case 5:
23985
- return Identifiers.classMapInterpolate2;
23986
- case 7:
23987
- return Identifiers.classMapInterpolate3;
23988
- case 9:
23989
- return Identifiers.classMapInterpolate4;
23990
- case 11:
23991
- return Identifiers.classMapInterpolate5;
23992
- case 13:
23993
- return Identifiers.classMapInterpolate6;
23994
- case 15:
23995
- return Identifiers.classMapInterpolate7;
23996
- case 17:
23997
- return Identifiers.classMapInterpolate8;
23998
- default:
23999
- return Identifiers.classMapInterpolateV;
25172
+ function ingestReferences(op, element) {
25173
+ assertIsArray(op.localRefs);
25174
+ for (const { name, value } of element.references) {
25175
+ op.localRefs.push({
25176
+ name,
25177
+ target: value,
25178
+ });
24000
25179
  }
24001
25180
  }
24002
25181
  /**
24003
- * Gets the instruction to generate for an interpolated style map.
24004
- * @param interpolation An Interpolation AST
25182
+ * Assert that the given value is an array.
24005
25183
  */
24006
- function getStyleMapInterpolationExpression(interpolation) {
24007
- switch (getInterpolationArgsLength(interpolation)) {
24008
- case 1:
24009
- return Identifiers.styleMap;
24010
- case 3:
24011
- return Identifiers.styleMapInterpolate1;
24012
- case 5:
24013
- return Identifiers.styleMapInterpolate2;
24014
- case 7:
24015
- return Identifiers.styleMapInterpolate3;
24016
- case 9:
24017
- return Identifiers.styleMapInterpolate4;
24018
- case 11:
24019
- return Identifiers.styleMapInterpolate5;
24020
- case 13:
24021
- return Identifiers.styleMapInterpolate6;
24022
- case 15:
24023
- return Identifiers.styleMapInterpolate7;
24024
- case 17:
24025
- return Identifiers.styleMapInterpolate8;
24026
- default:
24027
- return Identifiers.styleMapInterpolateV;
25184
+ function assertIsArray(value) {
25185
+ if (!Array.isArray(value)) {
25186
+ throw new Error(`AssertionError: expected an array`);
24028
25187
  }
24029
25188
  }
24030
25189
  /**
24031
- * Gets the instruction to generate for an interpolated style prop.
24032
- * @param interpolation An Interpolation AST
25190
+ * Creates an absolute `ParseSourceSpan` from the relative `ParseSpan`.
25191
+ *
25192
+ * `ParseSpan` objects are relative to the start of the expression.
25193
+ * This method converts these to full `ParseSourceSpan` objects that
25194
+ * show where the span is within the overall source file.
25195
+ *
25196
+ * @param span the relative span to convert.
25197
+ * @param baseSourceSpan a span corresponding to the base of the expression tree.
25198
+ * @returns a `ParseSourceSpan` for the given span or null if no `baseSourceSpan` was provided.
24033
25199
  */
24034
- function getStylePropInterpolationExpression(interpolation) {
24035
- switch (getInterpolationArgsLength(interpolation)) {
24036
- case 1:
24037
- return Identifiers.styleProp;
24038
- case 3:
24039
- return Identifiers.stylePropInterpolate1;
24040
- case 5:
24041
- return Identifiers.stylePropInterpolate2;
24042
- case 7:
24043
- return Identifiers.stylePropInterpolate3;
24044
- case 9:
24045
- return Identifiers.stylePropInterpolate4;
24046
- case 11:
24047
- return Identifiers.stylePropInterpolate5;
24048
- case 13:
24049
- return Identifiers.stylePropInterpolate6;
24050
- case 15:
24051
- return Identifiers.stylePropInterpolate7;
24052
- case 17:
24053
- return Identifiers.stylePropInterpolate8;
24054
- default:
24055
- return Identifiers.stylePropInterpolateV;
25200
+ function convertSourceSpan(span, baseSourceSpan) {
25201
+ if (baseSourceSpan === null) {
25202
+ return null;
24056
25203
  }
25204
+ const start = baseSourceSpan.start.moveBy(span.start);
25205
+ const end = baseSourceSpan.start.moveBy(span.end);
25206
+ const fullStart = baseSourceSpan.fullStart.moveBy(span.start);
25207
+ return new ParseSourceSpan(start, end, fullStart);
24057
25208
  }
24058
25209
  /**
24059
- * Checks whether property name is a custom CSS property.
24060
- * See: https://www.w3.org/TR/css-variables-1
25210
+ * With the directive-based control flow users were able to conditionally project content using
25211
+ * the `*` syntax. E.g. `<div *ngIf="expr" projectMe></div>` will be projected into
25212
+ * `<ng-content select="[projectMe]"/>`, because the attributes and tag name from the `div` are
25213
+ * copied to the template via the template creation instruction. With `@if` and `@for` that is
25214
+ * not the case, because the conditional is placed *around* elements, rather than *on* them.
25215
+ * The result is that content projection won't work in the same way if a user converts from
25216
+ * `*ngIf` to `@if`.
25217
+ *
25218
+ * This function aims to cover the most common case by doing the same copying when a control flow
25219
+ * node has *one and only one* root element or template node.
25220
+ *
25221
+ * This approach comes with some caveats:
25222
+ * 1. As soon as any other node is added to the root, the copying behavior won't work anymore.
25223
+ * A diagnostic will be added to flag cases like this and to explain how to work around it.
25224
+ * 2. If `preserveWhitespaces` is enabled, it's very likely that indentation will break this
25225
+ * workaround, because it'll include an additional text node as the first child. We can work
25226
+ * around it here, but in a discussion it was decided not to, because the user explicitly opted
25227
+ * into preserving the whitespace and we would have to drop it from the generated code.
25228
+ * The diagnostic mentioned point #1 will flag such cases to users.
25229
+ *
25230
+ * @returns Tag name to be used for the control flow template.
24061
25231
  */
24062
- function isCssCustomProperty(name) {
24063
- return name.startsWith('--');
24064
- }
24065
- function isEmptyExpression(ast) {
24066
- if (ast instanceof ASTWithSource) {
24067
- ast = ast.ast;
25232
+ function ingestControlFlowInsertionPoint(unit, xref, node) {
25233
+ let root = null;
25234
+ for (const child of node.children) {
25235
+ // Skip over comment nodes.
25236
+ if (child instanceof Comment$1) {
25237
+ continue;
25238
+ }
25239
+ // We can only infer the tag name/attributes if there's a single root node.
25240
+ if (root !== null) {
25241
+ return null;
25242
+ }
25243
+ // Root nodes can only elements or templates with a tag name (e.g. `<div *foo></div>`).
25244
+ if (child instanceof Element$1 || (child instanceof Template && child.tagName !== null)) {
25245
+ root = child;
25246
+ }
24068
25247
  }
24069
- return ast instanceof EmptyExpr$1;
25248
+ // If we've found a single root node, its tag name and *static* attributes can be copied
25249
+ // to the surrounding template to be used for content projection. Note that it's important
25250
+ // that we don't copy any bound attributes since they don't participate in content projection
25251
+ // and they can be used in directive matching (in the case of `Template.templateAttrs`).
25252
+ if (root !== null) {
25253
+ for (const attr of root.attributes) {
25254
+ const securityContext = domSchema.securityContext(NG_TEMPLATE_TAG_NAME$1, attr.name, true);
25255
+ unit.update.push(createBindingOp(xref, BindingKind.Attribute, attr.name, literal(attr.value), null, securityContext, true, false, null, asMessage(attr.i18n), attr.sourceSpan));
25256
+ }
25257
+ const tagName = root instanceof Element$1 ? root.name : root.tagName;
25258
+ // Don't pass along `ng-template` tag name since it enables directive matching.
25259
+ return tagName === NG_TEMPLATE_TAG_NAME$1 ? null : tagName;
25260
+ }
25261
+ return null;
24070
25262
  }
24071
25263
 
24072
25264
  class HtmlParser extends Parser {
@@ -24194,7 +25386,7 @@ class BindingParser {
24194
25386
  for (const propName of Object.keys(properties)) {
24195
25387
  const expression = properties[propName];
24196
25388
  if (typeof expression === 'string') {
24197
- this.parsePropertyBinding(propName, expression, true, sourceSpan, sourceSpan.start.offset, undefined, [],
25389
+ this.parsePropertyBinding(propName, expression, true, false, sourceSpan, sourceSpan.start.offset, undefined, [],
24198
25390
  // Use the `sourceSpan` for `keySpan`. This isn't really accurate, but neither is the
24199
25391
  // sourceSpan, as it represents the sourceSpan of the host itself rather than the
24200
25392
  // source of the host binding (which doesn't exist in the template). Regardless,
@@ -24290,7 +25482,7 @@ class BindingParser {
24290
25482
  else if (binding.value) {
24291
25483
  const srcSpan = isIvyAst ? bindingSpan : sourceSpan;
24292
25484
  const valueSpan = moveParseSourceSpan(sourceSpan, binding.value.ast.sourceSpan);
24293
- this._parsePropertyAst(key, binding.value, srcSpan, keySpan, valueSpan, targetMatchableAttrs, targetProps);
25485
+ this._parsePropertyAst(key, binding.value, false, srcSpan, keySpan, valueSpan, targetMatchableAttrs, targetProps);
24294
25486
  }
24295
25487
  else {
24296
25488
  targetMatchableAttrs.push([key, '' /* value */]);
@@ -24343,7 +25535,7 @@ class BindingParser {
24343
25535
  targetProps.push(new ParsedProperty(name, this._exprParser.wrapLiteralPrimitive(value, '', absoluteOffset), exports.ParsedPropertyType.LITERAL_ATTR, sourceSpan, keySpan, valueSpan));
24344
25536
  }
24345
25537
  }
24346
- parsePropertyBinding(name, expression, isHost, sourceSpan, absoluteOffset, valueSpan, targetMatchableAttrs, targetProps, keySpan) {
25538
+ parsePropertyBinding(name, expression, isHost, isPartOfAssignmentBinding, sourceSpan, absoluteOffset, valueSpan, targetMatchableAttrs, targetProps, keySpan) {
24347
25539
  if (name.length === 0) {
24348
25540
  this._reportError(`Property name is missing in binding`, sourceSpan);
24349
25541
  }
@@ -24366,20 +25558,20 @@ class BindingParser {
24366
25558
  this._parseAnimation(name, expression, sourceSpan, absoluteOffset, keySpan, valueSpan, targetMatchableAttrs, targetProps);
24367
25559
  }
24368
25560
  else {
24369
- this._parsePropertyAst(name, this.parseBinding(expression, isHost, valueSpan || sourceSpan, absoluteOffset), sourceSpan, keySpan, valueSpan, targetMatchableAttrs, targetProps);
25561
+ this._parsePropertyAst(name, this.parseBinding(expression, isHost, valueSpan || sourceSpan, absoluteOffset), isPartOfAssignmentBinding, sourceSpan, keySpan, valueSpan, targetMatchableAttrs, targetProps);
24370
25562
  }
24371
25563
  }
24372
25564
  parsePropertyInterpolation(name, value, sourceSpan, valueSpan, targetMatchableAttrs, targetProps, keySpan, interpolatedTokens) {
24373
25565
  const expr = this.parseInterpolation(value, valueSpan || sourceSpan, interpolatedTokens);
24374
25566
  if (expr) {
24375
- this._parsePropertyAst(name, expr, sourceSpan, keySpan, valueSpan, targetMatchableAttrs, targetProps);
25567
+ this._parsePropertyAst(name, expr, false, sourceSpan, keySpan, valueSpan, targetMatchableAttrs, targetProps);
24376
25568
  return true;
24377
25569
  }
24378
25570
  return false;
24379
25571
  }
24380
- _parsePropertyAst(name, ast, sourceSpan, keySpan, valueSpan, targetMatchableAttrs, targetProps) {
25572
+ _parsePropertyAst(name, ast, isPartOfAssignmentBinding, sourceSpan, keySpan, valueSpan, targetMatchableAttrs, targetProps) {
24381
25573
  targetMatchableAttrs.push([name, ast.source]);
24382
- targetProps.push(new ParsedProperty(name, ast, exports.ParsedPropertyType.DEFAULT, sourceSpan, keySpan, valueSpan));
25574
+ targetProps.push(new ParsedProperty(name, ast, isPartOfAssignmentBinding ? exports.ParsedPropertyType.TWO_WAY : exports.ParsedPropertyType.DEFAULT, sourceSpan, keySpan, valueSpan));
24383
25575
  }
24384
25576
  _parseAnimation(name, expression, sourceSpan, absoluteOffset, keySpan, valueSpan, targetMatchableAttrs, targetProps) {
24385
25577
  if (name.length === 0) {
@@ -24449,7 +25641,8 @@ class BindingParser {
24449
25641
  const mappedPropName = this._schemaRegistry.getMappedPropName(boundProp.name);
24450
25642
  boundPropertyName = mapPropertyName ? mappedPropName : boundProp.name;
24451
25643
  securityContexts = calcPossibleSecurityContexts(this._schemaRegistry, elementSelector, mappedPropName, false);
24452
- bindingType = 0 /* BindingType.Property */;
25644
+ bindingType =
25645
+ boundProp.type === exports.ParsedPropertyType.TWO_WAY ? 5 /* BindingType.TwoWay */ : 0 /* BindingType.Property */;
24453
25646
  if (!skipValidation) {
24454
25647
  this._validatePropertyOrAttributeName(mappedPropName, boundProp.sourceSpan, false);
24455
25648
  }
@@ -24466,7 +25659,7 @@ class BindingParser {
24466
25659
  if (keySpan !== undefined) {
24467
25660
  keySpan = moveParseSourceSpan(keySpan, new AbsoluteSourceSpan(keySpan.start.offset + 1, keySpan.end.offset));
24468
25661
  }
24469
- this._parseAnimationEvent(name, expression, isAssignmentEvent, sourceSpan, handlerSpan, targetEvents, keySpan);
25662
+ this._parseAnimationEvent(name, expression, sourceSpan, handlerSpan, targetEvents, keySpan);
24470
25663
  }
24471
25664
  else {
24472
25665
  this._parseRegularEvent(name, expression, isAssignmentEvent, sourceSpan, handlerSpan, targetMatchableAttrs, targetEvents, keySpan);
@@ -24476,11 +25669,11 @@ class BindingParser {
24476
25669
  const prop = this._schemaRegistry.getMappedPropName(propName);
24477
25670
  return calcPossibleSecurityContexts(this._schemaRegistry, selector, prop, isAttribute);
24478
25671
  }
24479
- _parseAnimationEvent(name, expression, isAssignmentEvent, sourceSpan, handlerSpan, targetEvents, keySpan) {
25672
+ _parseAnimationEvent(name, expression, sourceSpan, handlerSpan, targetEvents, keySpan) {
24480
25673
  const matches = splitAtPeriod(name, [name, '']);
24481
25674
  const eventName = matches[0];
24482
25675
  const phase = matches[1].toLowerCase();
24483
- const ast = this._parseAction(expression, isAssignmentEvent, handlerSpan);
25676
+ const ast = this._parseAction(expression, handlerSpan);
24484
25677
  targetEvents.push(new ParsedEvent(eventName, phase, 1 /* ParsedEventType.Animation */, ast, sourceSpan, handlerSpan, keySpan));
24485
25678
  if (eventName.length === 0) {
24486
25679
  this._reportError(`Animation event name is missing in binding`, sourceSpan);
@@ -24497,17 +25690,24 @@ class BindingParser {
24497
25690
  _parseRegularEvent(name, expression, isAssignmentEvent, sourceSpan, handlerSpan, targetMatchableAttrs, targetEvents, keySpan) {
24498
25691
  // long format: 'target: eventName'
24499
25692
  const [target, eventName] = splitAtColon(name, [null, name]);
24500
- const ast = this._parseAction(expression, isAssignmentEvent, handlerSpan);
25693
+ const prevErrorCount = this.errors.length;
25694
+ const ast = this._parseAction(expression, handlerSpan);
25695
+ const isValid = this.errors.length === prevErrorCount;
24501
25696
  targetMatchableAttrs.push([name, ast.source]);
24502
- targetEvents.push(new ParsedEvent(eventName, target, 0 /* ParsedEventType.Regular */, ast, sourceSpan, handlerSpan, keySpan));
25697
+ // Don't try to validate assignment events if there were other
25698
+ // parsing errors to avoid adding more noise to the error logs.
25699
+ if (isAssignmentEvent && isValid && !this._isAllowedAssignmentEvent(ast)) {
25700
+ this._reportError('Unsupported expression in a two-way binding', sourceSpan);
25701
+ }
25702
+ targetEvents.push(new ParsedEvent(eventName, target, isAssignmentEvent ? 2 /* ParsedEventType.TwoWay */ : 0 /* ParsedEventType.Regular */, ast, sourceSpan, handlerSpan, keySpan));
24503
25703
  // Don't detect directives for event names for now,
24504
25704
  // so don't add the event name to the matchableAttrs
24505
25705
  }
24506
- _parseAction(value, isAssignmentEvent, sourceSpan) {
25706
+ _parseAction(value, sourceSpan) {
24507
25707
  const sourceInfo = (sourceSpan && sourceSpan.start || '(unknown').toString();
24508
25708
  const absoluteOffset = (sourceSpan && sourceSpan.start) ? sourceSpan.start.offset : 0;
24509
25709
  try {
24510
- const ast = this._exprParser.parseAction(value, isAssignmentEvent, sourceInfo, absoluteOffset, this._interpolationConfig);
25710
+ const ast = this._exprParser.parseAction(value, sourceInfo, absoluteOffset, this._interpolationConfig);
24511
25711
  if (ast) {
24512
25712
  this._reportExpressionParserErrors(ast.errors, sourceSpan);
24513
25713
  }
@@ -24542,6 +25742,26 @@ class BindingParser {
24542
25742
  this._reportError(report.msg, sourceSpan, exports.ParseErrorLevel.ERROR);
24543
25743
  }
24544
25744
  }
25745
+ /**
25746
+ * Returns whether a parsed AST is allowed to be used within the event side of a two-way binding.
25747
+ * @param ast Parsed AST to be checked.
25748
+ */
25749
+ _isAllowedAssignmentEvent(ast) {
25750
+ if (ast instanceof ASTWithSource) {
25751
+ return this._isAllowedAssignmentEvent(ast.ast);
25752
+ }
25753
+ if (ast instanceof NonNullAssert) {
25754
+ return this._isAllowedAssignmentEvent(ast.expression);
25755
+ }
25756
+ if (ast instanceof PropertyRead || ast instanceof KeyedRead) {
25757
+ return true;
25758
+ }
25759
+ if (ast instanceof Binary) {
25760
+ return (ast.operation === '&&' || ast.operation === '||' || ast.operation === '??') &&
25761
+ (ast.right instanceof PropertyRead || ast.right instanceof KeyedRead);
25762
+ }
25763
+ return ast instanceof Conditional || ast instanceof PrefixNot;
25764
+ }
24545
25765
  }
24546
25766
  function isAnimationLabel(name) {
24547
25767
  return name[0] == '@';
@@ -24662,11 +25882,16 @@ const FOR_LOOP_EXPRESSION_PATTERN = /^\s*([0-9A-Za-z_$]*)\s+of\s+([\S\s]*)/;
24662
25882
  /** Pattern for the tracking expression in a for loop block. */
24663
25883
  const FOR_LOOP_TRACK_PATTERN = /^track\s+([\S\s]*)/;
24664
25884
  /** Pattern for the `as` expression in a conditional block. */
24665
- const CONDITIONAL_ALIAS_PATTERN = /^as\s+(.*)/;
25885
+ const CONDITIONAL_ALIAS_PATTERN = /^(as\s)+(.*)/;
24666
25886
  /** Pattern used to identify an `else if` block. */
24667
25887
  const ELSE_IF_PATTERN = /^else[^\S\r\n]+if/;
24668
25888
  /** Pattern used to identify a `let` parameter. */
24669
25889
  const FOR_LOOP_LET_PATTERN = /^let\s+([\S\s]*)/;
25890
+ /**
25891
+ * Pattern to group a string into leading whitespace, non whitespace, and trailing whitespace.
25892
+ * Useful for getting the variable name span when a span can contain leading and trailing space.
25893
+ */
25894
+ const CHARACTERS_IN_SURROUNDING_WHITESPACE_PATTERN = /(\s*)(\S+)(\s*)/;
24670
25895
  /** Names of variables that are allowed to be used in the `let` expression of a `for` loop. */
24671
25896
  const ALLOWED_FOR_LOOP_LET_VARIABLES = new Set(['$index', '$first', '$last', '$even', '$odd', '$count']);
24672
25897
  /**
@@ -24806,8 +26031,13 @@ function parseForLoopParameters(block, errors, bindingParser) {
24806
26031
  return null;
24807
26032
  }
24808
26033
  const [, itemName, rawExpression] = match;
26034
+ // `expressionParam.expression` contains the variable declaration and the expression of the
26035
+ // for...of statement, i.e. 'user of users' The variable of a ForOfStatement is _only_ the "const
26036
+ // user" part and does not include "of x".
26037
+ const variableName = expressionParam.expression.split(' ')[0];
26038
+ const variableSpan = new ParseSourceSpan(expressionParam.sourceSpan.start, expressionParam.sourceSpan.start.moveBy(variableName.length));
24809
26039
  const result = {
24810
- itemName: new Variable(itemName, '$implicit', expressionParam.sourceSpan, expressionParam.sourceSpan),
26040
+ itemName: new Variable(itemName, '$implicit', variableSpan, variableSpan),
24811
26041
  trackBy: null,
24812
26042
  expression: parseBlockParameterToBinding(expressionParam, bindingParser, rawExpression),
24813
26043
  context: {},
@@ -24815,7 +26045,8 @@ function parseForLoopParameters(block, errors, bindingParser) {
24815
26045
  for (const param of secondaryParams) {
24816
26046
  const letMatch = param.expression.match(FOR_LOOP_LET_PATTERN);
24817
26047
  if (letMatch !== null) {
24818
- parseLetParameter(param.sourceSpan, letMatch[1], param.sourceSpan, result.context, errors);
26048
+ const variablesSpan = new ParseSourceSpan(param.sourceSpan.start.moveBy(letMatch[0].length - letMatch[1].length), param.sourceSpan.end);
26049
+ parseLetParameter(param.sourceSpan, letMatch[1], variablesSpan, result.context, errors);
24819
26050
  continue;
24820
26051
  }
24821
26052
  const trackMatch = param.expression.match(FOR_LOOP_TRACK_PATTERN);
@@ -24825,6 +26056,9 @@ function parseForLoopParameters(block, errors, bindingParser) {
24825
26056
  }
24826
26057
  else {
24827
26058
  const expression = parseBlockParameterToBinding(param, bindingParser, trackMatch[1]);
26059
+ if (expression.ast instanceof EmptyExpr$1) {
26060
+ errors.push(new ParseError(param.sourceSpan, '@for loop must have a "track" expression'));
26061
+ }
24828
26062
  const keywordSpan = new ParseSourceSpan(param.sourceSpan.start, param.sourceSpan.start.moveBy('track'.length));
24829
26063
  result.trackBy = { expression, keywordSpan };
24830
26064
  }
@@ -24846,6 +26080,7 @@ function parseForLoopParameters(block, errors, bindingParser) {
24846
26080
  /** Parses the `let` parameter of a `for` loop block. */
24847
26081
  function parseLetParameter(sourceSpan, expression, span, context, errors) {
24848
26082
  const parts = expression.split(',');
26083
+ let startSpan = span.start;
24849
26084
  for (const part of parts) {
24850
26085
  const expressionParts = part.split('=');
24851
26086
  const name = expressionParts.length === 2 ? expressionParts[0].trim() : '';
@@ -24860,8 +26095,26 @@ function parseLetParameter(sourceSpan, expression, span, context, errors) {
24860
26095
  errors.push(new ParseError(sourceSpan, `Duplicate "let" parameter variable "${variableName}"`));
24861
26096
  }
24862
26097
  else {
24863
- context[variableName] = new Variable(name, variableName, span, span);
26098
+ const [, keyLeadingWhitespace, keyName] = expressionParts[0].match(CHARACTERS_IN_SURROUNDING_WHITESPACE_PATTERN) ?? [];
26099
+ const keySpan = keyLeadingWhitespace !== undefined && expressionParts.length === 2 ?
26100
+ new ParseSourceSpan(
26101
+ /* strip leading spaces */
26102
+ startSpan.moveBy(keyLeadingWhitespace.length),
26103
+ /* advance to end of the variable name */
26104
+ startSpan.moveBy(keyLeadingWhitespace.length + keyName.length)) :
26105
+ span;
26106
+ let valueSpan = undefined;
26107
+ if (expressionParts.length === 2) {
26108
+ const [, valueLeadingWhitespace, implicit] = expressionParts[1].match(CHARACTERS_IN_SURROUNDING_WHITESPACE_PATTERN) ?? [];
26109
+ valueSpan = valueLeadingWhitespace !== undefined ?
26110
+ new ParseSourceSpan(startSpan.moveBy(expressionParts[0].length + 1 + valueLeadingWhitespace.length), startSpan.moveBy(expressionParts[0].length + 1 + valueLeadingWhitespace.length +
26111
+ implicit.length)) :
26112
+ undefined;
26113
+ }
26114
+ const sourceSpan = new ParseSourceSpan(keySpan.start, valueSpan?.end ?? keySpan.end);
26115
+ context[variableName] = new Variable(name, variableName, sourceSpan, keySpan, valueSpan);
24864
26116
  }
26117
+ startSpan = startSpan.moveBy(part.length + 1 /* add 1 to move past the comma */);
24865
26118
  }
24866
26119
  }
24867
26120
  /**
@@ -24973,8 +26226,10 @@ function parseConditionalBlockParameters(block, errors, bindingParser) {
24973
26226
  errors.push(new ParseError(param.sourceSpan, 'Conditional can only have one "as" expression'));
24974
26227
  }
24975
26228
  else {
24976
- const name = aliasMatch[1].trim();
24977
- expressionAlias = new Variable(name, name, param.sourceSpan, param.sourceSpan);
26229
+ const name = aliasMatch[2].trim();
26230
+ const variableStart = param.sourceSpan.start.moveBy(aliasMatch[1].length);
26231
+ const variableSpan = new ParseSourceSpan(variableStart, variableStart.moveBy(name.length));
26232
+ expressionAlias = new Variable(name, name, variableSpan, variableSpan);
24978
26233
  }
24979
26234
  }
24980
26235
  return { expression, expressionAlias };
@@ -25835,7 +27090,7 @@ class HtmlAstToIvyAst {
25835
27090
  if (bindParts[KW_BIND_IDX] != null) {
25836
27091
  const identifier = bindParts[IDENT_KW_IDX];
25837
27092
  const keySpan = createKeySpan(srcSpan, bindParts[KW_BIND_IDX], identifier);
25838
- this.bindingParser.parsePropertyBinding(identifier, value, false, srcSpan, absoluteOffset, attribute.valueSpan, matchableAttributes, parsedProperties, keySpan);
27093
+ this.bindingParser.parsePropertyBinding(identifier, value, false, false, srcSpan, absoluteOffset, attribute.valueSpan, matchableAttributes, parsedProperties, keySpan);
25839
27094
  }
25840
27095
  else if (bindParts[KW_LET_IDX]) {
25841
27096
  if (isTemplateElement) {
@@ -25862,7 +27117,7 @@ class HtmlAstToIvyAst {
25862
27117
  else if (bindParts[KW_BINDON_IDX]) {
25863
27118
  const identifier = bindParts[IDENT_KW_IDX];
25864
27119
  const keySpan = createKeySpan(srcSpan, bindParts[KW_BINDON_IDX], identifier);
25865
- this.bindingParser.parsePropertyBinding(identifier, value, false, srcSpan, absoluteOffset, attribute.valueSpan, matchableAttributes, parsedProperties, keySpan);
27120
+ this.bindingParser.parsePropertyBinding(identifier, value, false, true, srcSpan, absoluteOffset, attribute.valueSpan, matchableAttributes, parsedProperties, keySpan);
25866
27121
  this.parseAssignmentEvent(identifier, value, srcSpan, attribute.valueSpan, matchableAttributes, boundEvents, keySpan);
25867
27122
  }
25868
27123
  else if (bindParts[KW_AT_IDX]) {
@@ -25892,11 +27147,11 @@ class HtmlAstToIvyAst {
25892
27147
  const identifier = name.substring(delims.start.length, name.length - delims.end.length);
25893
27148
  const keySpan = createKeySpan(srcSpan, delims.start, identifier);
25894
27149
  if (delims.start === BINDING_DELIMS.BANANA_BOX.start) {
25895
- this.bindingParser.parsePropertyBinding(identifier, value, false, srcSpan, absoluteOffset, attribute.valueSpan, matchableAttributes, parsedProperties, keySpan);
27150
+ this.bindingParser.parsePropertyBinding(identifier, value, false, true, srcSpan, absoluteOffset, attribute.valueSpan, matchableAttributes, parsedProperties, keySpan);
25896
27151
  this.parseAssignmentEvent(identifier, value, srcSpan, attribute.valueSpan, matchableAttributes, boundEvents, keySpan);
25897
27152
  }
25898
27153
  else if (delims.start === BINDING_DELIMS.PROPERTY.start) {
25899
- this.bindingParser.parsePropertyBinding(identifier, value, false, srcSpan, absoluteOffset, attribute.valueSpan, matchableAttributes, parsedProperties, keySpan);
27154
+ this.bindingParser.parsePropertyBinding(identifier, value, false, false, srcSpan, absoluteOffset, attribute.valueSpan, matchableAttributes, parsedProperties, keySpan);
25900
27155
  }
25901
27156
  else {
25902
27157
  const events = [];
@@ -25938,7 +27193,7 @@ class HtmlAstToIvyAst {
25938
27193
  }
25939
27194
  parseAssignmentEvent(name, expression, sourceSpan, valueSpan, targetMatchableAttrs, boundEvents, keySpan) {
25940
27195
  const events = [];
25941
- this.bindingParser.parseEvent(`${name}Change`, `${expression} =$event`, /* isAssignmentEvent */ true, sourceSpan, valueSpan || sourceSpan, targetMatchableAttrs, events, keySpan);
27196
+ this.bindingParser.parseEvent(`${name}Change`, expression, /* isAssignmentEvent */ true, sourceSpan, valueSpan || sourceSpan, targetMatchableAttrs, events, keySpan);
25942
27197
  addEvents(events, boundEvents);
25943
27198
  }
25944
27199
  reportError(message, sourceSpan, level = exports.ParseErrorLevel.ERROR) {
@@ -26217,6 +27472,535 @@ function serializePlaceholderValue(value) {
26217
27472
  }
26218
27473
  }
26219
27474
 
27475
+ const IMPORTANT_FLAG = '!important';
27476
+ /**
27477
+ * Minimum amount of binding slots required in the runtime for style/class bindings.
27478
+ *
27479
+ * Styling in Angular uses up two slots in the runtime LView/TData data structures to
27480
+ * record binding data, property information and metadata.
27481
+ *
27482
+ * When a binding is registered it will place the following information in the `LView`:
27483
+ *
27484
+ * slot 1) binding value
27485
+ * slot 2) cached value (all other values collected before it in string form)
27486
+ *
27487
+ * When a binding is registered it will place the following information in the `TData`:
27488
+ *
27489
+ * slot 1) prop name
27490
+ * slot 2) binding index that points to the previous style/class binding (and some extra config
27491
+ * values)
27492
+ *
27493
+ * Let's imagine we have a binding that looks like so:
27494
+ *
27495
+ * ```
27496
+ * <div [style.width]="x" [style.height]="y">
27497
+ * ```
27498
+ *
27499
+ * Our `LView` and `TData` data-structures look like so:
27500
+ *
27501
+ * ```typescript
27502
+ * LView = [
27503
+ * // ...
27504
+ * x, // value of x
27505
+ * "width: x",
27506
+ *
27507
+ * y, // value of y
27508
+ * "width: x; height: y",
27509
+ * // ...
27510
+ * ];
27511
+ *
27512
+ * TData = [
27513
+ * // ...
27514
+ * "width", // binding slot 20
27515
+ * 0,
27516
+ *
27517
+ * "height",
27518
+ * 20,
27519
+ * // ...
27520
+ * ];
27521
+ * ```
27522
+ *
27523
+ * */
27524
+ const MIN_STYLING_BINDING_SLOTS_REQUIRED = 2;
27525
+ /**
27526
+ * Produces creation/update instructions for all styling bindings (class and style)
27527
+ *
27528
+ * It also produces the creation instruction to register all initial styling values
27529
+ * (which are all the static class="..." and style="..." attribute values that exist
27530
+ * on an element within a template).
27531
+ *
27532
+ * The builder class below handles producing instructions for the following cases:
27533
+ *
27534
+ * - Static style/class attributes (style="..." and class="...")
27535
+ * - Dynamic style/class map bindings ([style]="map" and [class]="map|string")
27536
+ * - Dynamic style/class property bindings ([style.prop]="exp" and [class.name]="exp")
27537
+ *
27538
+ * Due to the complex relationship of all of these cases, the instructions generated
27539
+ * for these attributes/properties/bindings must be done so in the correct order. The
27540
+ * order which these must be generated is as follows:
27541
+ *
27542
+ * if (createMode) {
27543
+ * styling(...)
27544
+ * }
27545
+ * if (updateMode) {
27546
+ * styleMap(...)
27547
+ * classMap(...)
27548
+ * styleProp(...)
27549
+ * classProp(...)
27550
+ * }
27551
+ *
27552
+ * The creation/update methods within the builder class produce these instructions.
27553
+ */
27554
+ class StylingBuilder {
27555
+ constructor(_directiveExpr) {
27556
+ this._directiveExpr = _directiveExpr;
27557
+ /** Whether or not there are any static styling values present */
27558
+ this._hasInitialValues = false;
27559
+ /**
27560
+ * Whether or not there are any styling bindings present
27561
+ * (i.e. `[style]`, `[class]`, `[style.prop]` or `[class.name]`)
27562
+ */
27563
+ this.hasBindings = false;
27564
+ this.hasBindingsWithPipes = false;
27565
+ /** the input for [class] (if it exists) */
27566
+ this._classMapInput = null;
27567
+ /** the input for [style] (if it exists) */
27568
+ this._styleMapInput = null;
27569
+ /** an array of each [style.prop] input */
27570
+ this._singleStyleInputs = null;
27571
+ /** an array of each [class.name] input */
27572
+ this._singleClassInputs = null;
27573
+ this._lastStylingInput = null;
27574
+ this._firstStylingInput = null;
27575
+ // maps are used instead of hash maps because a Map will
27576
+ // retain the ordering of the keys
27577
+ /**
27578
+ * Represents the location of each style binding in the template
27579
+ * (e.g. `<div [style.width]="w" [style.height]="h">` implies
27580
+ * that `width=0` and `height=1`)
27581
+ */
27582
+ this._stylesIndex = new Map();
27583
+ /**
27584
+ * Represents the location of each class binding in the template
27585
+ * (e.g. `<div [class.big]="b" [class.hidden]="h">` implies
27586
+ * that `big=0` and `hidden=1`)
27587
+ */
27588
+ this._classesIndex = new Map();
27589
+ this._initialStyleValues = [];
27590
+ this._initialClassValues = [];
27591
+ }
27592
+ /**
27593
+ * Registers a given input to the styling builder to be later used when producing AOT code.
27594
+ *
27595
+ * The code below will only accept the input if it is somehow tied to styling (whether it be
27596
+ * style/class bindings or static style/class attributes).
27597
+ */
27598
+ registerBoundInput(input) {
27599
+ // [attr.style] or [attr.class] are skipped in the code below,
27600
+ // they should not be treated as styling-based bindings since
27601
+ // they are intended to be written directly to the attr and
27602
+ // will therefore skip all style/class resolution that is present
27603
+ // with style="", [style]="" and [style.prop]="", class="",
27604
+ // [class.prop]="". [class]="" assignments
27605
+ let binding = null;
27606
+ let name = input.name;
27607
+ switch (input.type) {
27608
+ case 0 /* BindingType.Property */:
27609
+ binding = this.registerInputBasedOnName(name, input.value, input.sourceSpan);
27610
+ break;
27611
+ case 3 /* BindingType.Style */:
27612
+ binding = this.registerStyleInput(name, false, input.value, input.sourceSpan, input.unit);
27613
+ break;
27614
+ case 2 /* BindingType.Class */:
27615
+ binding = this.registerClassInput(name, false, input.value, input.sourceSpan);
27616
+ break;
27617
+ }
27618
+ return binding ? true : false;
27619
+ }
27620
+ registerInputBasedOnName(name, expression, sourceSpan) {
27621
+ let binding = null;
27622
+ const prefix = name.substring(0, 6);
27623
+ const isStyle = name === 'style' || prefix === 'style.' || prefix === 'style!';
27624
+ const isClass = !isStyle && (name === 'class' || prefix === 'class.' || prefix === 'class!');
27625
+ if (isStyle || isClass) {
27626
+ const isMapBased = name.charAt(5) !== '.'; // style.prop or class.prop makes this a no
27627
+ const property = name.slice(isMapBased ? 5 : 6); // the dot explains why there's a +1
27628
+ if (isStyle) {
27629
+ binding = this.registerStyleInput(property, isMapBased, expression, sourceSpan);
27630
+ }
27631
+ else {
27632
+ binding = this.registerClassInput(property, isMapBased, expression, sourceSpan);
27633
+ }
27634
+ }
27635
+ return binding;
27636
+ }
27637
+ registerStyleInput(name, isMapBased, value, sourceSpan, suffix) {
27638
+ if (isEmptyExpression(value)) {
27639
+ return null;
27640
+ }
27641
+ // CSS custom properties are case-sensitive so we shouldn't normalize them.
27642
+ // See: https://www.w3.org/TR/css-variables-1/#defining-variables
27643
+ if (!isCssCustomProperty(name)) {
27644
+ name = hyphenate(name);
27645
+ }
27646
+ const { property, hasOverrideFlag, suffix: bindingSuffix } = parseProperty(name);
27647
+ suffix = typeof suffix === 'string' && suffix.length !== 0 ? suffix : bindingSuffix;
27648
+ const entry = { name: property, suffix: suffix, value, sourceSpan, hasOverrideFlag };
27649
+ if (isMapBased) {
27650
+ this._styleMapInput = entry;
27651
+ }
27652
+ else {
27653
+ (this._singleStyleInputs = this._singleStyleInputs || []).push(entry);
27654
+ registerIntoMap(this._stylesIndex, property);
27655
+ }
27656
+ this._lastStylingInput = entry;
27657
+ this._firstStylingInput = this._firstStylingInput || entry;
27658
+ this._checkForPipes(value);
27659
+ this.hasBindings = true;
27660
+ return entry;
27661
+ }
27662
+ registerClassInput(name, isMapBased, value, sourceSpan) {
27663
+ if (isEmptyExpression(value)) {
27664
+ return null;
27665
+ }
27666
+ const { property, hasOverrideFlag } = parseProperty(name);
27667
+ const entry = { name: property, value, sourceSpan, hasOverrideFlag, suffix: null };
27668
+ if (isMapBased) {
27669
+ this._classMapInput = entry;
27670
+ }
27671
+ else {
27672
+ (this._singleClassInputs = this._singleClassInputs || []).push(entry);
27673
+ registerIntoMap(this._classesIndex, property);
27674
+ }
27675
+ this._lastStylingInput = entry;
27676
+ this._firstStylingInput = this._firstStylingInput || entry;
27677
+ this._checkForPipes(value);
27678
+ this.hasBindings = true;
27679
+ return entry;
27680
+ }
27681
+ _checkForPipes(value) {
27682
+ if ((value instanceof ASTWithSource) && (value.ast instanceof BindingPipe)) {
27683
+ this.hasBindingsWithPipes = true;
27684
+ }
27685
+ }
27686
+ /**
27687
+ * Registers the element's static style string value to the builder.
27688
+ *
27689
+ * @param value the style string (e.g. `width:100px; height:200px;`)
27690
+ */
27691
+ registerStyleAttr(value) {
27692
+ this._initialStyleValues = parse(value);
27693
+ this._hasInitialValues = true;
27694
+ }
27695
+ /**
27696
+ * Registers the element's static class string value to the builder.
27697
+ *
27698
+ * @param value the className string (e.g. `disabled gold zoom`)
27699
+ */
27700
+ registerClassAttr(value) {
27701
+ this._initialClassValues = value.trim().split(/\s+/g);
27702
+ this._hasInitialValues = true;
27703
+ }
27704
+ /**
27705
+ * Appends all styling-related expressions to the provided attrs array.
27706
+ *
27707
+ * @param attrs an existing array where each of the styling expressions
27708
+ * will be inserted into.
27709
+ */
27710
+ populateInitialStylingAttrs(attrs) {
27711
+ // [CLASS_MARKER, 'foo', 'bar', 'baz' ...]
27712
+ if (this._initialClassValues.length) {
27713
+ attrs.push(literal(1 /* AttributeMarker.Classes */));
27714
+ for (let i = 0; i < this._initialClassValues.length; i++) {
27715
+ attrs.push(literal(this._initialClassValues[i]));
27716
+ }
27717
+ }
27718
+ // [STYLE_MARKER, 'width', '200px', 'height', '100px', ...]
27719
+ if (this._initialStyleValues.length) {
27720
+ attrs.push(literal(2 /* AttributeMarker.Styles */));
27721
+ for (let i = 0; i < this._initialStyleValues.length; i += 2) {
27722
+ attrs.push(literal(this._initialStyleValues[i]), literal(this._initialStyleValues[i + 1]));
27723
+ }
27724
+ }
27725
+ }
27726
+ /**
27727
+ * Builds an instruction with all the expressions and parameters for `elementHostAttrs`.
27728
+ *
27729
+ * The instruction generation code below is used for producing the AOT statement code which is
27730
+ * responsible for registering initial styles (within a directive hostBindings' creation block),
27731
+ * as well as any of the provided attribute values, to the directive host element.
27732
+ */
27733
+ assignHostAttrs(attrs, definitionMap) {
27734
+ if (this._directiveExpr && (attrs.length || this._hasInitialValues)) {
27735
+ this.populateInitialStylingAttrs(attrs);
27736
+ definitionMap.set('hostAttrs', literalArr(attrs));
27737
+ }
27738
+ }
27739
+ /**
27740
+ * Builds an instruction with all the expressions and parameters for `classMap`.
27741
+ *
27742
+ * The instruction data will contain all expressions for `classMap` to function
27743
+ * which includes the `[class]` expression params.
27744
+ */
27745
+ buildClassMapInstruction(valueConverter) {
27746
+ if (this._classMapInput) {
27747
+ return this._buildMapBasedInstruction(valueConverter, true, this._classMapInput);
27748
+ }
27749
+ return null;
27750
+ }
27751
+ /**
27752
+ * Builds an instruction with all the expressions and parameters for `styleMap`.
27753
+ *
27754
+ * The instruction data will contain all expressions for `styleMap` to function
27755
+ * which includes the `[style]` expression params.
27756
+ */
27757
+ buildStyleMapInstruction(valueConverter) {
27758
+ if (this._styleMapInput) {
27759
+ return this._buildMapBasedInstruction(valueConverter, false, this._styleMapInput);
27760
+ }
27761
+ return null;
27762
+ }
27763
+ _buildMapBasedInstruction(valueConverter, isClassBased, stylingInput) {
27764
+ // each styling binding value is stored in the LView
27765
+ // map-based bindings allocate two slots: one for the
27766
+ // previous binding value and another for the previous
27767
+ // className or style attribute value.
27768
+ let totalBindingSlotsRequired = MIN_STYLING_BINDING_SLOTS_REQUIRED;
27769
+ // these values must be outside of the update block so that they can
27770
+ // be evaluated (the AST visit call) during creation time so that any
27771
+ // pipes can be picked up in time before the template is built
27772
+ const mapValue = stylingInput.value.visit(valueConverter);
27773
+ let reference;
27774
+ if (mapValue instanceof Interpolation$1) {
27775
+ totalBindingSlotsRequired += mapValue.expressions.length;
27776
+ reference = isClassBased ? getClassMapInterpolationExpression(mapValue) :
27777
+ getStyleMapInterpolationExpression(mapValue);
27778
+ }
27779
+ else {
27780
+ reference = isClassBased ? Identifiers.classMap : Identifiers.styleMap;
27781
+ }
27782
+ return {
27783
+ reference,
27784
+ calls: [{
27785
+ supportsInterpolation: true,
27786
+ sourceSpan: stylingInput.sourceSpan,
27787
+ allocateBindingSlots: totalBindingSlotsRequired,
27788
+ params: (convertFn) => {
27789
+ const convertResult = convertFn(mapValue);
27790
+ const params = Array.isArray(convertResult) ? convertResult : [convertResult];
27791
+ return params;
27792
+ }
27793
+ }]
27794
+ };
27795
+ }
27796
+ _buildSingleInputs(reference, inputs, valueConverter, getInterpolationExpressionFn, isClassBased) {
27797
+ const instructions = [];
27798
+ inputs.forEach(input => {
27799
+ const previousInstruction = instructions[instructions.length - 1];
27800
+ const value = input.value.visit(valueConverter);
27801
+ let referenceForCall = reference;
27802
+ // each styling binding value is stored in the LView
27803
+ // but there are two values stored for each binding:
27804
+ // 1) the value itself
27805
+ // 2) an intermediate value (concatenation of style up to this point).
27806
+ // We need to store the intermediate value so that we don't allocate
27807
+ // the strings on each CD.
27808
+ let totalBindingSlotsRequired = MIN_STYLING_BINDING_SLOTS_REQUIRED;
27809
+ if (value instanceof Interpolation$1) {
27810
+ totalBindingSlotsRequired += value.expressions.length;
27811
+ if (getInterpolationExpressionFn) {
27812
+ referenceForCall = getInterpolationExpressionFn(value);
27813
+ }
27814
+ }
27815
+ const call = {
27816
+ sourceSpan: input.sourceSpan,
27817
+ allocateBindingSlots: totalBindingSlotsRequired,
27818
+ supportsInterpolation: !!getInterpolationExpressionFn,
27819
+ params: (convertFn) => {
27820
+ // params => stylingProp(propName, value, suffix)
27821
+ const params = [];
27822
+ params.push(literal(input.name));
27823
+ const convertResult = convertFn(value);
27824
+ if (Array.isArray(convertResult)) {
27825
+ params.push(...convertResult);
27826
+ }
27827
+ else {
27828
+ params.push(convertResult);
27829
+ }
27830
+ // [style.prop] bindings may use suffix values (e.g. px, em, etc...), therefore,
27831
+ // if that is detected then we need to pass that in as an optional param.
27832
+ if (!isClassBased && input.suffix !== null) {
27833
+ params.push(literal(input.suffix));
27834
+ }
27835
+ return params;
27836
+ }
27837
+ };
27838
+ // If we ended up generating a call to the same instruction as the previous styling property
27839
+ // we can chain the calls together safely to save some bytes, otherwise we have to generate
27840
+ // a separate instruction call. This is primarily a concern with interpolation instructions
27841
+ // where we may start off with one `reference`, but end up using another based on the
27842
+ // number of interpolations.
27843
+ if (previousInstruction && previousInstruction.reference === referenceForCall) {
27844
+ previousInstruction.calls.push(call);
27845
+ }
27846
+ else {
27847
+ instructions.push({ reference: referenceForCall, calls: [call] });
27848
+ }
27849
+ });
27850
+ return instructions;
27851
+ }
27852
+ _buildClassInputs(valueConverter) {
27853
+ if (this._singleClassInputs) {
27854
+ return this._buildSingleInputs(Identifiers.classProp, this._singleClassInputs, valueConverter, null, true);
27855
+ }
27856
+ return [];
27857
+ }
27858
+ _buildStyleInputs(valueConverter) {
27859
+ if (this._singleStyleInputs) {
27860
+ return this._buildSingleInputs(Identifiers.styleProp, this._singleStyleInputs, valueConverter, getStylePropInterpolationExpression, false);
27861
+ }
27862
+ return [];
27863
+ }
27864
+ /**
27865
+ * Constructs all instructions which contain the expressions that will be placed
27866
+ * into the update block of a template function or a directive hostBindings function.
27867
+ */
27868
+ buildUpdateLevelInstructions(valueConverter) {
27869
+ const instructions = [];
27870
+ if (this.hasBindings) {
27871
+ const styleMapInstruction = this.buildStyleMapInstruction(valueConverter);
27872
+ if (styleMapInstruction) {
27873
+ instructions.push(styleMapInstruction);
27874
+ }
27875
+ const classMapInstruction = this.buildClassMapInstruction(valueConverter);
27876
+ if (classMapInstruction) {
27877
+ instructions.push(classMapInstruction);
27878
+ }
27879
+ instructions.push(...this._buildStyleInputs(valueConverter));
27880
+ instructions.push(...this._buildClassInputs(valueConverter));
27881
+ }
27882
+ return instructions;
27883
+ }
27884
+ }
27885
+ function registerIntoMap(map, key) {
27886
+ if (!map.has(key)) {
27887
+ map.set(key, map.size);
27888
+ }
27889
+ }
27890
+ function parseProperty(name) {
27891
+ let hasOverrideFlag = false;
27892
+ const overrideIndex = name.indexOf(IMPORTANT_FLAG);
27893
+ if (overrideIndex !== -1) {
27894
+ name = overrideIndex > 0 ? name.substring(0, overrideIndex) : '';
27895
+ hasOverrideFlag = true;
27896
+ }
27897
+ let suffix = null;
27898
+ let property = name;
27899
+ const unitIndex = name.lastIndexOf('.');
27900
+ if (unitIndex > 0) {
27901
+ suffix = name.slice(unitIndex + 1);
27902
+ property = name.substring(0, unitIndex);
27903
+ }
27904
+ return { property, suffix, hasOverrideFlag };
27905
+ }
27906
+ /**
27907
+ * Gets the instruction to generate for an interpolated class map.
27908
+ * @param interpolation An Interpolation AST
27909
+ */
27910
+ function getClassMapInterpolationExpression(interpolation) {
27911
+ switch (getInterpolationArgsLength(interpolation)) {
27912
+ case 1:
27913
+ return Identifiers.classMap;
27914
+ case 3:
27915
+ return Identifiers.classMapInterpolate1;
27916
+ case 5:
27917
+ return Identifiers.classMapInterpolate2;
27918
+ case 7:
27919
+ return Identifiers.classMapInterpolate3;
27920
+ case 9:
27921
+ return Identifiers.classMapInterpolate4;
27922
+ case 11:
27923
+ return Identifiers.classMapInterpolate5;
27924
+ case 13:
27925
+ return Identifiers.classMapInterpolate6;
27926
+ case 15:
27927
+ return Identifiers.classMapInterpolate7;
27928
+ case 17:
27929
+ return Identifiers.classMapInterpolate8;
27930
+ default:
27931
+ return Identifiers.classMapInterpolateV;
27932
+ }
27933
+ }
27934
+ /**
27935
+ * Gets the instruction to generate for an interpolated style map.
27936
+ * @param interpolation An Interpolation AST
27937
+ */
27938
+ function getStyleMapInterpolationExpression(interpolation) {
27939
+ switch (getInterpolationArgsLength(interpolation)) {
27940
+ case 1:
27941
+ return Identifiers.styleMap;
27942
+ case 3:
27943
+ return Identifiers.styleMapInterpolate1;
27944
+ case 5:
27945
+ return Identifiers.styleMapInterpolate2;
27946
+ case 7:
27947
+ return Identifiers.styleMapInterpolate3;
27948
+ case 9:
27949
+ return Identifiers.styleMapInterpolate4;
27950
+ case 11:
27951
+ return Identifiers.styleMapInterpolate5;
27952
+ case 13:
27953
+ return Identifiers.styleMapInterpolate6;
27954
+ case 15:
27955
+ return Identifiers.styleMapInterpolate7;
27956
+ case 17:
27957
+ return Identifiers.styleMapInterpolate8;
27958
+ default:
27959
+ return Identifiers.styleMapInterpolateV;
27960
+ }
27961
+ }
27962
+ /**
27963
+ * Gets the instruction to generate for an interpolated style prop.
27964
+ * @param interpolation An Interpolation AST
27965
+ */
27966
+ function getStylePropInterpolationExpression(interpolation) {
27967
+ switch (getInterpolationArgsLength(interpolation)) {
27968
+ case 1:
27969
+ return Identifiers.styleProp;
27970
+ case 3:
27971
+ return Identifiers.stylePropInterpolate1;
27972
+ case 5:
27973
+ return Identifiers.stylePropInterpolate2;
27974
+ case 7:
27975
+ return Identifiers.stylePropInterpolate3;
27976
+ case 9:
27977
+ return Identifiers.stylePropInterpolate4;
27978
+ case 11:
27979
+ return Identifiers.stylePropInterpolate5;
27980
+ case 13:
27981
+ return Identifiers.stylePropInterpolate6;
27982
+ case 15:
27983
+ return Identifiers.stylePropInterpolate7;
27984
+ case 17:
27985
+ return Identifiers.stylePropInterpolate8;
27986
+ default:
27987
+ return Identifiers.stylePropInterpolateV;
27988
+ }
27989
+ }
27990
+ /**
27991
+ * Checks whether property name is a custom CSS property.
27992
+ * See: https://www.w3.org/TR/css-variables-1
27993
+ */
27994
+ function isCssCustomProperty(name) {
27995
+ return name.startsWith('--');
27996
+ }
27997
+ function isEmptyExpression(ast) {
27998
+ if (ast instanceof ASTWithSource) {
27999
+ ast = ast.ast;
28000
+ }
28001
+ return ast instanceof EmptyExpr$1;
28002
+ }
28003
+
26220
28004
  // Selector attribute name of `<ng-content>`
26221
28005
  const NG_CONTENT_SELECT_ATTR = 'select';
26222
28006
  // Attribute name of `ngProjectAs`.
@@ -26243,7 +28027,9 @@ function prepareEventListenerParameters(eventAst, handlerName = null, scope = nu
26243
28027
  const implicitReceiverExpr = (scope === null || scope.bindingLevel === 0) ?
26244
28028
  variable(CONTEXT_NAME) :
26245
28029
  scope.getOrCreateSharedContextVar(0);
26246
- const bindingStatements = convertActionBinding(scope, implicitReceiverExpr, handler, 'b', eventAst.handlerSpan, implicitReceiverAccesses, EVENT_BINDING_SCOPE_GLOBALS);
28030
+ const bindingStatements = eventAst.type === 2 /* ParsedEventType.TwoWay */ ?
28031
+ convertAssignmentActionBinding(scope, implicitReceiverExpr, handler, 'b', eventAst.handlerSpan, implicitReceiverAccesses, EVENT_BINDING_SCOPE_GLOBALS) :
28032
+ convertActionBinding(scope, implicitReceiverExpr, handler, 'b', eventAst.handlerSpan, implicitReceiverAccesses, EVENT_BINDING_SCOPE_GLOBALS);
26247
28033
  const statements = [];
26248
28034
  const variableDeclarations = scope?.variableDeclarations();
26249
28035
  const restoreViewStatement = scope?.restoreViewStatement();
@@ -26765,7 +28551,8 @@ class TemplateDefinitionBuilder {
26765
28551
  element.inputs.forEach(input => {
26766
28552
  const stylingInputWasSet = stylingBuilder.registerBoundInput(input);
26767
28553
  if (!stylingInputWasSet) {
26768
- if (input.type === 0 /* BindingType.Property */ && input.i18n) {
28554
+ if ((input.type === 0 /* BindingType.Property */ || input.type === 5 /* BindingType.TwoWay */) &&
28555
+ input.i18n) {
26769
28556
  boundI18nAttrs.push(input);
26770
28557
  }
26771
28558
  else {
@@ -26810,7 +28597,7 @@ class TemplateDefinitionBuilder {
26810
28597
  // Generate Listeners (outputs)
26811
28598
  if (element.outputs.length > 0) {
26812
28599
  for (const outputAst of element.outputs) {
26813
- this.creationInstruction(outputAst.sourceSpan, Identifiers.listener, this.prepareListenerParameter(element.name, outputAst, elementIndex));
28600
+ this.creationInstruction(outputAst.sourceSpan, outputAst.type === 2 /* ParsedEventType.TwoWay */ ? Identifiers.twoWayListener : Identifiers.listener, this.prepareListenerParameter(element.name, outputAst, elementIndex));
26814
28601
  }
26815
28602
  }
26816
28603
  // Note: it's important to keep i18n/i18nStart instructions after i18nAttributes and
@@ -26853,6 +28640,7 @@ class TemplateDefinitionBuilder {
26853
28640
  this.allocateBindingSlots(value);
26854
28641
  propertyBindings.push({
26855
28642
  span: input.sourceSpan,
28643
+ reference: Identifiers.property,
26856
28644
  paramsOrFn: getBindingFunctionParams(() => hasValue ? this.convertPropertyBinding(value) : emptyValueBindInstruction, prepareSyntheticPropertyName(input.name))
26857
28645
  });
26858
28646
  }
@@ -26891,7 +28679,9 @@ class TemplateDefinitionBuilder {
26891
28679
  }
26892
28680
  }
26893
28681
  this.allocateBindingSlots(value);
26894
- if (inputType === 0 /* BindingType.Property */) {
28682
+ // Note: we don't separate two-way property bindings and regular ones,
28683
+ // because their assignment order needs to be maintained.
28684
+ if (inputType === 0 /* BindingType.Property */ || inputType === 5 /* BindingType.TwoWay */) {
26895
28685
  if (value instanceof Interpolation$1) {
26896
28686
  // prop="{{value}}" and friends
26897
28687
  this.interpolatedUpdateInstruction(getPropertyInterpolationExpression(value), elementIndex, attrName, input, value, params);
@@ -26901,6 +28691,7 @@ class TemplateDefinitionBuilder {
26901
28691
  // Collect all the properties so that we can chain into a single function at the end.
26902
28692
  propertyBindings.push({
26903
28693
  span: input.sourceSpan,
28694
+ reference: inputType === 5 /* BindingType.TwoWay */ ? Identifiers.twoWayProperty : Identifiers.property,
26904
28695
  paramsOrFn: getBindingFunctionParams(() => this.convertPropertyBinding(value), attrName, params)
26905
28696
  });
26906
28697
  }
@@ -26933,7 +28724,7 @@ class TemplateDefinitionBuilder {
26933
28724
  }
26934
28725
  });
26935
28726
  for (const propertyBinding of propertyBindings) {
26936
- this.updateInstructionWithAdvance(elementIndex, propertyBinding.span, Identifiers.property, propertyBinding.paramsOrFn);
28727
+ this.updateInstructionWithAdvance(elementIndex, propertyBinding.span, propertyBinding.reference, propertyBinding.paramsOrFn);
26937
28728
  }
26938
28729
  for (const attributeBinding of attributeBindings) {
26939
28730
  this.updateInstructionWithAdvance(elementIndex, attributeBinding.span, Identifiers.attribute, attributeBinding.paramsOrFn);
@@ -26966,7 +28757,9 @@ class TemplateDefinitionBuilder {
26966
28757
  }
26967
28758
  }
26968
28759
  const contextName = `${this.contextName}${contextNameSuffix}_${index}`;
26969
- const name = `${contextName}_Template`;
28760
+ // Note: For the unique name, we don't include an unique suffix, unless really needed.
28761
+ // This keeps the generated output more clean as most of the time, we don't expect conflicts.
28762
+ const name = this.constantPool.uniqueName(`${contextName}_Template`, /** alwaysIncludeSuffix */ false);
26970
28763
  // Create the template function
26971
28764
  const visitor = new TemplateDefinitionBuilder(this.constantPool, this._bindingScope, this.level + 1, contextName, this.i18n, index, name, this._namespace, this.fileBasedI18nSuffix, this.i18nUseExternalIds, this.deferBlocks, this.elementLocations, this.allDeferrableDepsFn, this._constants);
26972
28765
  // Nested templates must not be visited until after their parent templates have completed
@@ -27029,7 +28822,7 @@ class TemplateDefinitionBuilder {
27029
28822
  }
27030
28823
  // Generate listeners for directive output
27031
28824
  for (const outputAst of template.outputs) {
27032
- this.creationInstruction(outputAst.sourceSpan, Identifiers.listener, this.prepareListenerParameter('ng_template', outputAst, templateIndex));
28825
+ this.creationInstruction(outputAst.sourceSpan, outputAst.type === 2 /* ParsedEventType.TwoWay */ ? Identifiers.twoWayListener : Identifiers.listener, this.prepareListenerParameter('ng_template', outputAst, templateIndex));
27033
28826
  }
27034
28827
  }
27035
28828
  }
@@ -27283,7 +29076,9 @@ class TemplateDefinitionBuilder {
27283
29076
  for (const deferredDep of metadata.deps) {
27284
29077
  if (deferredDep.isDeferrable) {
27285
29078
  // Callback function, e.g. `m () => m.MyCmp;`.
27286
- const innerFn = arrowFn([new FnParam('m', DYNAMIC_TYPE)], variable('m').prop(deferredDep.symbolName));
29079
+ const innerFn = arrowFn([new FnParam('m', DYNAMIC_TYPE)],
29080
+ // Default imports are always accessed through the `default` property.
29081
+ variable('m').prop(deferredDep.isDefaultImport ? 'default' : deferredDep.symbolName));
27287
29082
  // Dynamic import, e.g. `import('./a').then(...)`.
27288
29083
  const importExpr = (new DynamicImportExpr(deferredDep.importPath)).prop('then').callFn([innerFn]);
27289
29084
  dependencyExp.push(importExpr);
@@ -28525,6 +30320,171 @@ function createClosureModeGuard() {
28525
30320
  .and(variable(NG_I18N_CLOSURE_MODE));
28526
30321
  }
28527
30322
 
30323
+ /**
30324
+ * Translates query flags into `TQueryFlags` type in
30325
+ * packages/core/src/render3/interfaces/query.ts
30326
+ * @param query
30327
+ */
30328
+ function toQueryFlags(query) {
30329
+ return ((query.descendants ? 1 /* QueryFlags.descendants */ : 0 /* QueryFlags.none */) |
30330
+ (query.static ? 2 /* QueryFlags.isStatic */ : 0 /* QueryFlags.none */) |
30331
+ (query.emitDistinctChangesOnly ? 4 /* QueryFlags.emitDistinctChangesOnly */ : 0 /* QueryFlags.none */));
30332
+ }
30333
+ function getQueryPredicate(query, constantPool) {
30334
+ if (Array.isArray(query.predicate)) {
30335
+ let predicate = [];
30336
+ query.predicate.forEach((selector) => {
30337
+ // Each item in predicates array may contain strings with comma-separated refs
30338
+ // (for ex. 'ref, ref1, ..., refN'), thus we extract individual refs and store them
30339
+ // as separate array entities
30340
+ const selectors = selector.split(',').map((token) => literal(token.trim()));
30341
+ predicate.push(...selectors);
30342
+ });
30343
+ return constantPool.getConstLiteral(literalArr(predicate), true);
30344
+ }
30345
+ else {
30346
+ // The original predicate may have been wrapped in a `forwardRef()` call.
30347
+ switch (query.predicate.forwardRef) {
30348
+ case 0 /* ForwardRefHandling.None */:
30349
+ case 2 /* ForwardRefHandling.Unwrapped */:
30350
+ return query.predicate.expression;
30351
+ case 1 /* ForwardRefHandling.Wrapped */:
30352
+ return importExpr(Identifiers.resolveForwardRef).callFn([query.predicate.expression]);
30353
+ }
30354
+ }
30355
+ }
30356
+ function createQueryCreateCall(query, constantPool, queryTypeFns, prependParams) {
30357
+ const parameters = [];
30358
+ if (prependParams !== undefined) {
30359
+ parameters.push(...prependParams);
30360
+ }
30361
+ if (query.isSignal) {
30362
+ parameters.push(new ReadPropExpr(variable(CONTEXT_NAME), query.propertyName));
30363
+ }
30364
+ parameters.push(getQueryPredicate(query, constantPool), literal(toQueryFlags(query)));
30365
+ if (query.read) {
30366
+ parameters.push(query.read);
30367
+ }
30368
+ const queryCreateFn = query.isSignal ? queryTypeFns.signalBased : queryTypeFns.nonSignal;
30369
+ return importExpr(queryCreateFn).callFn(parameters);
30370
+ }
30371
+ const queryAdvancePlaceholder = Symbol('queryAdvancePlaceholder');
30372
+ /**
30373
+ * Collapses query advance placeholders in a list of statements.
30374
+ *
30375
+ * This allows for less generated code because multiple sibling query advance
30376
+ * statements can be collapsed into a single call with the count as argument.
30377
+ *
30378
+ * e.g.
30379
+ *
30380
+ * ```ts
30381
+ * bla();
30382
+ * queryAdvance();
30383
+ * queryAdvance();
30384
+ * bla();
30385
+ * ```
30386
+ *
30387
+ * --> will turn into
30388
+ *
30389
+ * ```
30390
+ * bla();
30391
+ * queryAdvance(2);
30392
+ * bla();
30393
+ * ```
30394
+ */
30395
+ function collapseAdvanceStatements(statements) {
30396
+ const result = [];
30397
+ let advanceCollapseCount = 0;
30398
+ const flushAdvanceCount = () => {
30399
+ if (advanceCollapseCount > 0) {
30400
+ result.unshift(importExpr(Identifiers.queryAdvance)
30401
+ .callFn(advanceCollapseCount === 1 ? [] : [literal(advanceCollapseCount)])
30402
+ .toStmt());
30403
+ advanceCollapseCount = 0;
30404
+ }
30405
+ };
30406
+ // Iterate through statements in reverse and collapse advance placeholders.
30407
+ for (let i = statements.length - 1; i >= 0; i--) {
30408
+ const st = statements[i];
30409
+ if (st === queryAdvancePlaceholder) {
30410
+ advanceCollapseCount++;
30411
+ }
30412
+ else {
30413
+ flushAdvanceCount();
30414
+ result.unshift(st);
30415
+ }
30416
+ }
30417
+ flushAdvanceCount();
30418
+ return result;
30419
+ }
30420
+ // Define and update any view queries
30421
+ function createViewQueriesFunction(viewQueries, constantPool, name) {
30422
+ const createStatements = [];
30423
+ const updateStatements = [];
30424
+ const tempAllocator = temporaryAllocator(st => updateStatements.push(st), TEMPORARY_NAME);
30425
+ viewQueries.forEach((query) => {
30426
+ // creation call, e.g. r3.viewQuery(somePredicate, true) or
30427
+ // r3.viewQuerySignal(ctx.prop, somePredicate, true);
30428
+ const queryDefinitionCall = createQueryCreateCall(query, constantPool, {
30429
+ signalBased: Identifiers.viewQuerySignal,
30430
+ nonSignal: Identifiers.viewQuery,
30431
+ });
30432
+ createStatements.push(queryDefinitionCall.toStmt());
30433
+ // Signal queries update lazily and we just advance the index.
30434
+ if (query.isSignal) {
30435
+ updateStatements.push(queryAdvancePlaceholder);
30436
+ return;
30437
+ }
30438
+ // update, e.g. (r3.queryRefresh(tmp = r3.loadQuery()) && (ctx.someDir = tmp));
30439
+ const temporary = tempAllocator();
30440
+ const getQueryList = importExpr(Identifiers.loadQuery).callFn([]);
30441
+ const refresh = importExpr(Identifiers.queryRefresh).callFn([temporary.set(getQueryList)]);
30442
+ const updateDirective = variable(CONTEXT_NAME)
30443
+ .prop(query.propertyName)
30444
+ .set(query.first ? temporary.prop('first') : temporary);
30445
+ updateStatements.push(refresh.and(updateDirective).toStmt());
30446
+ });
30447
+ const viewQueryFnName = name ? `${name}_Query` : null;
30448
+ return fn([new FnParam(RENDER_FLAGS, NUMBER_TYPE), new FnParam(CONTEXT_NAME, null)], [
30449
+ renderFlagCheckIfStmt(1 /* core.RenderFlags.Create */, createStatements),
30450
+ renderFlagCheckIfStmt(2 /* core.RenderFlags.Update */, collapseAdvanceStatements(updateStatements))
30451
+ ], INFERRED_TYPE, null, viewQueryFnName);
30452
+ }
30453
+ // Define and update any content queries
30454
+ function createContentQueriesFunction(queries, constantPool, name) {
30455
+ const createStatements = [];
30456
+ const updateStatements = [];
30457
+ const tempAllocator = temporaryAllocator(st => updateStatements.push(st), TEMPORARY_NAME);
30458
+ for (const query of queries) {
30459
+ // creation, e.g. r3.contentQuery(dirIndex, somePredicate, true, null) or
30460
+ // r3.contentQuerySignal(dirIndex, propName, somePredicate, <flags>, <read>).
30461
+ createStatements.push(createQueryCreateCall(query, constantPool, { nonSignal: Identifiers.contentQuery, signalBased: Identifiers.contentQuerySignal },
30462
+ /* prependParams */ [variable('dirIndex')])
30463
+ .toStmt());
30464
+ // Signal queries update lazily and we just advance the index.
30465
+ if (query.isSignal) {
30466
+ updateStatements.push(queryAdvancePlaceholder);
30467
+ continue;
30468
+ }
30469
+ // update, e.g. (r3.queryRefresh(tmp = r3.loadQuery()) && (ctx.someDir = tmp));
30470
+ const temporary = tempAllocator();
30471
+ const getQueryList = importExpr(Identifiers.loadQuery).callFn([]);
30472
+ const refresh = importExpr(Identifiers.queryRefresh).callFn([temporary.set(getQueryList)]);
30473
+ const updateDirective = variable(CONTEXT_NAME)
30474
+ .prop(query.propertyName)
30475
+ .set(query.first ? temporary.prop('first') : temporary);
30476
+ updateStatements.push(refresh.and(updateDirective).toStmt());
30477
+ }
30478
+ const contentQueriesFnName = name ? `${name}_ContentQueries` : null;
30479
+ return fn([
30480
+ new FnParam(RENDER_FLAGS, NUMBER_TYPE), new FnParam(CONTEXT_NAME, null),
30481
+ new FnParam('dirIndex', null)
30482
+ ], [
30483
+ renderFlagCheckIfStmt(1 /* core.RenderFlags.Create */, createStatements),
30484
+ renderFlagCheckIfStmt(2 /* core.RenderFlags.Update */, collapseAdvanceStatements(updateStatements)),
30485
+ ], INFERRED_TYPE, null, contentQueriesFnName);
30486
+ }
30487
+
28528
30488
  // This regex matches any binding names that contain the "attr." prefix, e.g. "attr.required"
28529
30489
  // If there is a match, the first matching group will contain the attribute name to bind.
28530
30490
  const ATTR_REGEX = /attr\.([^\]]+)/;
@@ -28625,9 +30585,9 @@ function compileDirectiveFromMetadata(meta, constantPool, bindingParser) {
28625
30585
  function createDeferredDepsFunction(constantPool, name, deps) {
28626
30586
  // This defer block has deps for which we need to generate dynamic imports.
28627
30587
  const dependencyExp = [];
28628
- for (const [symbolName, importPath] of deps) {
30588
+ for (const [symbolName, { importPath, isDefaultImport }] of deps) {
28629
30589
  // Callback function, e.g. `m () => m.MyCmp;`.
28630
- const innerFn = arrowFn([new FnParam('m', DYNAMIC_TYPE)], variable('m').prop(symbolName));
30590
+ const innerFn = arrowFn([new FnParam('m', DYNAMIC_TYPE)], variable('m').prop(isDefaultImport ? 'default' : symbolName));
28631
30591
  // Dynamic import, e.g. `import('./a').then(...)`.
28632
30592
  const importExpr = (new DynamicImportExpr(importPath)).prop('then').callFn([innerFn]);
28633
30593
  dependencyExp.push(importExpr);
@@ -28656,16 +30616,16 @@ function compileComponentFromMetadata(meta, constantPool, bindingParser) {
28656
30616
  // e.g. `template: function MyComponent_Template(_ctx, _cm) {...}`
28657
30617
  const templateTypeName = meta.name;
28658
30618
  const templateName = templateTypeName ? `${templateTypeName}_Template` : null;
30619
+ let allDeferrableDepsFn = null;
30620
+ if (meta.deferBlocks.size > 0 && meta.deferrableTypes.size > 0 &&
30621
+ meta.deferBlockDepsEmitMode === 1 /* DeferBlockDepsEmitMode.PerComponent */) {
30622
+ const fnName = `${templateTypeName}_DeferFn`;
30623
+ allDeferrableDepsFn = createDeferredDepsFunction(constantPool, fnName, meta.deferrableTypes);
30624
+ }
28659
30625
  // Template compilation is currently conditional as we're in the process of rewriting it.
28660
- {
30626
+ if (!meta.useTemplatePipeline) {
28661
30627
  // This is the main path currently used in compilation, which compiles the template with the
28662
30628
  // legacy `TemplateDefinitionBuilder`.
28663
- let allDeferrableDepsFn = null;
28664
- if (meta.deferBlocks.size > 0 && meta.deferrableTypes.size > 0 &&
28665
- meta.deferBlockDepsEmitMode === 1 /* DeferBlockDepsEmitMode.PerComponent */) {
28666
- const fnName = `${templateTypeName}_DeferFn`;
28667
- allDeferrableDepsFn = createDeferredDepsFunction(constantPool, fnName, meta.deferrableTypes);
28668
- }
28669
30629
  const template = meta.template;
28670
30630
  const templateBuilder = new TemplateDefinitionBuilder(constantPool, BindingScope.createRootScope(), 0, templateTypeName, null, null, templateName, Identifiers.namespaceHTML, meta.relativeContextFilePath, meta.i18nUseExternalIds, meta.deferBlocks, new Map(), allDeferrableDepsFn);
28671
30631
  const templateFunctionExpression = templateBuilder.buildTemplateFunction(template.nodes, []);
@@ -28699,6 +30659,31 @@ function compileComponentFromMetadata(meta, constantPool, bindingParser) {
28699
30659
  }
28700
30660
  definitionMap.set('template', templateFunctionExpression);
28701
30661
  }
30662
+ else {
30663
+ // This path compiles the template using the prototype template pipeline. First the template is
30664
+ // ingested into IR:
30665
+ const tpl = ingestComponent(meta.name, meta.template.nodes, constantPool, meta.relativeContextFilePath, meta.i18nUseExternalIds, meta.deferBlocks, allDeferrableDepsFn);
30666
+ // Then the IR is transformed to prepare it for cod egeneration.
30667
+ transform(tpl, CompilationJobKind.Tmpl);
30668
+ // Finally we emit the template function:
30669
+ const templateFn = emitTemplateFn(tpl, constantPool);
30670
+ if (tpl.contentSelectors !== null) {
30671
+ definitionMap.set('ngContentSelectors', tpl.contentSelectors);
30672
+ }
30673
+ definitionMap.set('decls', literal(tpl.root.decls));
30674
+ definitionMap.set('vars', literal(tpl.root.vars));
30675
+ if (tpl.consts.length > 0) {
30676
+ if (tpl.constsInitializers.length > 0) {
30677
+ definitionMap.set('consts', arrowFn([], [
30678
+ ...tpl.constsInitializers, new ReturnStatement(literalArr(tpl.consts))
30679
+ ]));
30680
+ }
30681
+ else {
30682
+ definitionMap.set('consts', literalArr(tpl.consts));
30683
+ }
30684
+ }
30685
+ definitionMap.set('template', templateFn);
30686
+ }
28702
30687
  if (meta.declarationListEmitMode !== 3 /* DeclarationListEmitMode.RuntimeResolved */ &&
28703
30688
  meta.declarations.length > 0) {
28704
30689
  definitionMap.set('dependencies', compileDeclarationList(literalArr(meta.declarations.map(decl => decl.type)), meta.declarationListEmitMode));
@@ -28794,22 +30779,6 @@ function compileDeclarationList(list, mode) {
28794
30779
  throw new Error(`Unsupported with an array of pre-resolved dependencies`);
28795
30780
  }
28796
30781
  }
28797
- function prepareQueryParams(query, constantPool) {
28798
- const parameters = [getQueryPredicate(query, constantPool), literal(toQueryFlags(query))];
28799
- if (query.read) {
28800
- parameters.push(query.read);
28801
- }
28802
- return parameters;
28803
- }
28804
- /**
28805
- * Translates query flags into `TQueryFlags` type in packages/core/src/render3/interfaces/query.ts
28806
- * @param query
28807
- */
28808
- function toQueryFlags(query) {
28809
- return (query.descendants ? 1 /* QueryFlags.descendants */ : 0 /* QueryFlags.none */) |
28810
- (query.static ? 2 /* QueryFlags.isStatic */ : 0 /* QueryFlags.none */) |
28811
- (query.emitDistinctChangesOnly ? 4 /* QueryFlags.emitDistinctChangesOnly */ : 0 /* QueryFlags.none */);
28812
- }
28813
30782
  function convertAttributesToExpressions(attributes) {
28814
30783
  const values = [];
28815
30784
  for (let key of Object.getOwnPropertyNames(attributes)) {
@@ -28818,34 +30787,6 @@ function convertAttributesToExpressions(attributes) {
28818
30787
  }
28819
30788
  return values;
28820
30789
  }
28821
- // Define and update any content queries
28822
- function createContentQueriesFunction(queries, constantPool, name) {
28823
- const createStatements = [];
28824
- const updateStatements = [];
28825
- const tempAllocator = temporaryAllocator(updateStatements, TEMPORARY_NAME);
28826
- for (const query of queries) {
28827
- // creation, e.g. r3.contentQuery(dirIndex, somePredicate, true, null);
28828
- createStatements.push(importExpr(Identifiers.contentQuery)
28829
- .callFn([variable('dirIndex'), ...prepareQueryParams(query, constantPool)])
28830
- .toStmt());
28831
- // update, e.g. (r3.queryRefresh(tmp = r3.loadQuery()) && (ctx.someDir = tmp));
28832
- const temporary = tempAllocator();
28833
- const getQueryList = importExpr(Identifiers.loadQuery).callFn([]);
28834
- const refresh = importExpr(Identifiers.queryRefresh).callFn([temporary.set(getQueryList)]);
28835
- const updateDirective = variable(CONTEXT_NAME)
28836
- .prop(query.propertyName)
28837
- .set(query.first ? temporary.prop('first') : temporary);
28838
- updateStatements.push(refresh.and(updateDirective).toStmt());
28839
- }
28840
- const contentQueriesFnName = name ? `${name}_ContentQueries` : null;
28841
- return fn([
28842
- new FnParam(RENDER_FLAGS, NUMBER_TYPE), new FnParam(CONTEXT_NAME, null),
28843
- new FnParam('dirIndex', null)
28844
- ], [
28845
- renderFlagCheckIfStmt(1 /* core.RenderFlags.Create */, createStatements),
28846
- renderFlagCheckIfStmt(2 /* core.RenderFlags.Update */, updateStatements)
28847
- ], INFERRED_TYPE, null, contentQueriesFnName);
28848
- }
28849
30790
  function stringAsType(str) {
28850
30791
  return expressionType(literal(str));
28851
30792
  }
@@ -28911,35 +30852,39 @@ function createDirectiveType(meta) {
28911
30852
  }
28912
30853
  return expressionType(importExpr(Identifiers.DirectiveDeclaration, typeParams));
28913
30854
  }
28914
- // Define and update any view queries
28915
- function createViewQueriesFunction(viewQueries, constantPool, name) {
28916
- const createStatements = [];
28917
- const updateStatements = [];
28918
- const tempAllocator = temporaryAllocator(updateStatements, TEMPORARY_NAME);
28919
- viewQueries.forEach((query) => {
28920
- // creation, e.g. r3.viewQuery(somePredicate, true);
28921
- const queryDefinition = importExpr(Identifiers.viewQuery).callFn(prepareQueryParams(query, constantPool));
28922
- createStatements.push(queryDefinition.toStmt());
28923
- // update, e.g. (r3.queryRefresh(tmp = r3.loadQuery()) && (ctx.someDir = tmp));
28924
- const temporary = tempAllocator();
28925
- const getQueryList = importExpr(Identifiers.loadQuery).callFn([]);
28926
- const refresh = importExpr(Identifiers.queryRefresh).callFn([temporary.set(getQueryList)]);
28927
- const updateDirective = variable(CONTEXT_NAME)
28928
- .prop(query.propertyName)
28929
- .set(query.first ? temporary.prop('first') : temporary);
28930
- updateStatements.push(refresh.and(updateDirective).toStmt());
28931
- });
28932
- const viewQueryFnName = name ? `${name}_Query` : null;
28933
- return fn([new FnParam(RENDER_FLAGS, NUMBER_TYPE), new FnParam(CONTEXT_NAME, null)], [
28934
- renderFlagCheckIfStmt(1 /* core.RenderFlags.Create */, createStatements),
28935
- renderFlagCheckIfStmt(2 /* core.RenderFlags.Update */, updateStatements)
28936
- ], INFERRED_TYPE, null, viewQueryFnName);
28937
- }
28938
30855
  // Return a host binding function or null if one is not necessary.
28939
30856
  function createHostBindingsFunction(hostBindingsMetadata, typeSourceSpan, bindingParser, constantPool, selector, name, definitionMap) {
28940
30857
  const bindings = bindingParser.createBoundHostProperties(hostBindingsMetadata.properties, typeSourceSpan);
28941
30858
  // Calculate host event bindings
28942
30859
  const eventBindings = bindingParser.createDirectiveHostEventAsts(hostBindingsMetadata.listeners, typeSourceSpan);
30860
+ if (hostBindingsMetadata.useTemplatePipeline) {
30861
+ // The parser for host bindings treats class and style attributes specially -- they are
30862
+ // extracted into these separate fields. This is not the case for templates, so the compiler can
30863
+ // actually already handle these special attributes internally. Therefore, we just drop them
30864
+ // into the attributes map.
30865
+ if (hostBindingsMetadata.specialAttributes.styleAttr) {
30866
+ hostBindingsMetadata.attributes['style'] =
30867
+ literal(hostBindingsMetadata.specialAttributes.styleAttr);
30868
+ }
30869
+ if (hostBindingsMetadata.specialAttributes.classAttr) {
30870
+ hostBindingsMetadata.attributes['class'] =
30871
+ literal(hostBindingsMetadata.specialAttributes.classAttr);
30872
+ }
30873
+ const hostJob = ingestHostBinding({
30874
+ componentName: name,
30875
+ componentSelector: selector,
30876
+ properties: bindings,
30877
+ events: eventBindings,
30878
+ attributes: hostBindingsMetadata.attributes,
30879
+ }, bindingParser, constantPool);
30880
+ transform(hostJob, CompilationJobKind.Host);
30881
+ definitionMap.set('hostAttrs', hostJob.root.attributes);
30882
+ const varCount = hostJob.root.vars;
30883
+ if (varCount !== null && varCount > 0) {
30884
+ definitionMap.set('hostVars', literal(varCount));
30885
+ }
30886
+ return emitHostBindingFunction(hostJob);
30887
+ }
28943
30888
  let bindingId = 0;
28944
30889
  const getNextBindingId = () => `${bindingId++}`;
28945
30890
  const bindingContext = variable(CONTEXT_NAME);
@@ -29345,7 +31290,7 @@ class R3TargetBinder {
29345
31290
  // Finally, run the TemplateBinder to bind references, variables, and other entities within the
29346
31291
  // template. This extracts all the metadata that doesn't depend on directive matching.
29347
31292
  const { expressions, symbols, nestingLevel, usedPipes, eagerPipes, deferBlocks } = TemplateBinder.applyWithScope(target.template, scope);
29348
- return new R3BoundTarget(target, directives, eagerDirectives, bindings, references, expressions, symbols, nestingLevel, scopedNodeEntities, usedPipes, eagerPipes, deferBlocks, scope);
31293
+ return new R3BoundTarget(target, directives, eagerDirectives, bindings, references, expressions, symbols, nestingLevel, scopedNodeEntities, usedPipes, eagerPipes, deferBlocks);
29349
31294
  }
29350
31295
  }
29351
31296
  /**
@@ -29741,7 +31686,7 @@ class TemplateBinder extends RecursiveAstVisitor {
29741
31686
  const usedPipes = new Set();
29742
31687
  const eagerPipes = new Set();
29743
31688
  const template = nodes instanceof Template ? nodes : null;
29744
- const deferBlocks = new Set();
31689
+ const deferBlocks = new Map();
29745
31690
  // The top-level template has nesting level 0.
29746
31691
  const binder = new TemplateBinder(expressions, symbols, usedPipes, eagerPipes, deferBlocks, nestingLevel, scope, template, 0);
29747
31692
  binder.ingest(nodes);
@@ -29770,8 +31715,16 @@ class TemplateBinder extends RecursiveAstVisitor {
29770
31715
  nodeOrNodes.children.forEach(this.visitNode);
29771
31716
  this.nestingLevel.set(nodeOrNodes, this.level);
29772
31717
  }
31718
+ else if (nodeOrNodes instanceof DeferredBlock) {
31719
+ if (this.scope.rootNode !== nodeOrNodes) {
31720
+ throw new Error(`Assertion error: resolved incorrect scope for deferred block ${nodeOrNodes}`);
31721
+ }
31722
+ this.deferBlocks.set(nodeOrNodes, this.scope);
31723
+ nodeOrNodes.children.forEach(node => node.visit(this));
31724
+ this.nestingLevel.set(nodeOrNodes, this.level);
31725
+ }
29773
31726
  else if (nodeOrNodes instanceof SwitchBlockCase || nodeOrNodes instanceof ForLoopBlockEmpty ||
29774
- nodeOrNodes instanceof DeferredBlock || nodeOrNodes instanceof DeferredBlockError ||
31727
+ nodeOrNodes instanceof DeferredBlockError ||
29775
31728
  nodeOrNodes instanceof DeferredBlockPlaceholder ||
29776
31729
  nodeOrNodes instanceof DeferredBlockLoading) {
29777
31730
  nodeOrNodes.children.forEach(node => node.visit(this));
@@ -29828,7 +31781,6 @@ class TemplateBinder extends RecursiveAstVisitor {
29828
31781
  event.handler.visit(this);
29829
31782
  }
29830
31783
  visitDeferredBlock(deferred) {
29831
- this.deferBlocks.add(deferred);
29832
31784
  this.ingestScopedNode(deferred);
29833
31785
  deferred.triggers.when?.value.visit(this);
29834
31786
  deferred.prefetchTriggers.when?.value.visit(this);
@@ -29917,7 +31869,7 @@ class TemplateBinder extends RecursiveAstVisitor {
29917
31869
  * See `BoundTarget` for documentation on the individual methods.
29918
31870
  */
29919
31871
  class R3BoundTarget {
29920
- constructor(target, directives, eagerDirectives, bindings, references, exprTargets, symbols, nestingLevel, scopedNodeEntities, usedPipes, eagerPipes, deferredBlocks, rootScope) {
31872
+ constructor(target, directives, eagerDirectives, bindings, references, exprTargets, symbols, nestingLevel, scopedNodeEntities, usedPipes, eagerPipes, deferBlocks) {
29921
31873
  this.target = target;
29922
31874
  this.directives = directives;
29923
31875
  this.eagerDirectives = eagerDirectives;
@@ -29929,8 +31881,7 @@ class R3BoundTarget {
29929
31881
  this.scopedNodeEntities = scopedNodeEntities;
29930
31882
  this.usedPipes = usedPipes;
29931
31883
  this.eagerPipes = eagerPipes;
29932
- this.deferredBlocks = deferredBlocks;
29933
- this.rootScope = rootScope;
31884
+ this.deferBlocks = deferBlocks;
29934
31885
  }
29935
31886
  getEntitiesInScope(node) {
29936
31887
  return this.scopedNodeEntities.get(node) ?? new Set();
@@ -29969,7 +31920,7 @@ class R3BoundTarget {
29969
31920
  return Array.from(this.eagerPipes);
29970
31921
  }
29971
31922
  getDeferBlocks() {
29972
- return Array.from(this.deferredBlocks);
31923
+ return Array.from(this.deferBlocks.keys());
29973
31924
  }
29974
31925
  getDeferredTriggerTarget(block, trigger) {
29975
31926
  // Only triggers that refer to DOM nodes can be resolved.
@@ -30021,10 +31972,14 @@ class R3BoundTarget {
30021
31972
  return null;
30022
31973
  }
30023
31974
  isDeferred(element) {
30024
- for (const deferBlock of this.deferredBlocks) {
30025
- const scope = this.rootScope.childScopes.get(deferBlock);
30026
- if (scope && scope.elementsInScope.has(element)) {
30027
- return true;
31975
+ for (const deferredScope of this.deferBlocks.values()) {
31976
+ const stack = [deferredScope];
31977
+ while (stack.length > 0) {
31978
+ const current = stack.pop();
31979
+ if (current.elementsInScope.has(element)) {
31980
+ return true;
31981
+ }
31982
+ stack.push(...current.childScopes.values());
30028
31983
  }
30029
31984
  }
30030
31985
  return false;
@@ -30096,6 +32051,7 @@ function extractScopedNodeEntities(rootScope) {
30096
32051
  class ResourceLoader {
30097
32052
  }
30098
32053
 
32054
+ const SHOULD_USE_TEMPLATE_PIPELINE_FOR_JIT = true;
30099
32055
  class CompilerFacadeImpl {
30100
32056
  constructor(jitEvaluator = new JitEvaluator()) {
30101
32057
  this.jitEvaluator = jitEvaluator;
@@ -30229,6 +32185,7 @@ class CompilerFacadeImpl {
30229
32185
  null,
30230
32186
  relativeContextFilePath: '',
30231
32187
  i18nUseExternalIds: true,
32188
+ useTemplatePipeline: SHOULD_USE_TEMPLATE_PIPELINE_FOR_JIT,
30232
32189
  };
30233
32190
  const jitExpressionSourceMap = `ng:///${facade.name}.js`;
30234
32191
  return this.compileComponentFromMeta(angularCoreEnv, jitExpressionSourceMap, meta);
@@ -30292,6 +32249,7 @@ class CompilerFacadeImpl {
30292
32249
  function convertToR3QueryMetadata(facade) {
30293
32250
  return {
30294
32251
  ...facade,
32252
+ isSignal: facade.isSignal,
30295
32253
  predicate: convertQueryPredicate(facade.predicate),
30296
32254
  read: facade.read ? new WrappedNodeExpr(facade.read) : null,
30297
32255
  static: facade.static,
@@ -30307,6 +32265,7 @@ function convertQueryDeclarationToMetadata(declaration) {
30307
32265
  read: declaration.read ? new WrappedNodeExpr(declaration.read) : null,
30308
32266
  static: declaration.static ?? false,
30309
32267
  emitDistinctChangesOnly: declaration.emitDistinctChangesOnly ?? true,
32268
+ isSignal: !!declaration.isSignal,
30310
32269
  };
30311
32270
  }
30312
32271
  function convertQueryPredicate(predicate) {
@@ -30349,7 +32308,10 @@ function convertDirectiveFacadeToMetadata(facade) {
30349
32308
  typeSourceSpan: facade.typeSourceSpan,
30350
32309
  type: wrapReference(facade.type),
30351
32310
  deps: null,
30352
- host: extractHostBindings(facade.propMetadata, facade.typeSourceSpan, facade.host),
32311
+ host: {
32312
+ ...extractHostBindings(facade.propMetadata, facade.typeSourceSpan, facade.host),
32313
+ useTemplatePipeline: SHOULD_USE_TEMPLATE_PIPELINE_FOR_JIT,
32314
+ },
30353
32315
  inputs: { ...inputsFromMetadata, ...inputsFromType },
30354
32316
  outputs: { ...outputsFromMetadata, ...outputsFromType },
30355
32317
  queries: facade.queries.map(convertToR3QueryMetadata),
@@ -30392,6 +32354,7 @@ function convertHostDeclarationToMetadata(host = {}) {
30392
32354
  classAttr: host.classAttribute,
30393
32355
  styleAttr: host.styleAttribute,
30394
32356
  },
32357
+ useTemplatePipeline: SHOULD_USE_TEMPLATE_PIPELINE_FOR_JIT,
30395
32358
  };
30396
32359
  }
30397
32360
  function convertHostDirectivesToMetadata(metadata) {
@@ -30464,6 +32427,7 @@ function convertDeclareComponentFacadeToMetadata(decl, typeSourceSpan, sourceMap
30464
32427
  declarationListEmitMode: 2 /* DeclarationListEmitMode.ClosureResolved */,
30465
32428
  relativeContextFilePath: '',
30466
32429
  i18nUseExternalIds: true,
32430
+ useTemplatePipeline: SHOULD_USE_TEMPLATE_PIPELINE_FOR_JIT,
30467
32431
  };
30468
32432
  }
30469
32433
  function convertDeclarationFacadeToMetadata(declaration) {
@@ -30745,7 +32709,7 @@ function publishFacade(global) {
30745
32709
  * @description
30746
32710
  * Entry point for all public APIs of the compiler package.
30747
32711
  */
30748
- const VERSION = new Version('17.1.0');
32712
+ const VERSION = new Version('17.3.0');
30749
32713
 
30750
32714
  class CompilerConfig {
30751
32715
  constructor({ defaultEncapsulation = exports.ViewEncapsulation.Emulated, preserveWhitespaces, strictInjectionParameters } = {}) {
@@ -32246,9 +34210,11 @@ function compileComponentClassMetadata(metadata, deferrableTypes) {
32246
34210
  }
32247
34211
  const dynamicImports = [];
32248
34212
  const importedSymbols = [];
32249
- for (const [symbolName, importPath] of deferrableTypes) {
34213
+ for (const [symbolName, { importPath, isDefaultImport }] of deferrableTypes) {
32250
34214
  // e.g. `(m) => m.CmpA`
32251
- const innerFn = arrowFn([new FnParam('m', DYNAMIC_TYPE)], variable('m').prop(symbolName));
34215
+ const innerFn =
34216
+ // Default imports are always accessed through the `default` property.
34217
+ arrowFn([new FnParam('m', DYNAMIC_TYPE)], variable('m').prop(isDefaultImport ? 'default' : symbolName));
32252
34218
  // e.g. `import('./cmp-a').then(...)`
32253
34219
  const importExpr = (new DynamicImportExpr(importPath)).prop('then').callFn([innerFn]);
32254
34220
  dynamicImports.push(importExpr);
@@ -32311,7 +34277,7 @@ const MINIMUM_PARTIAL_LINKER_VERSION$5 = '12.0.0';
32311
34277
  function compileDeclareClassMetadata(metadata) {
32312
34278
  const definitionMap = new DefinitionMap();
32313
34279
  definitionMap.set('minVersion', literal(MINIMUM_PARTIAL_LINKER_VERSION$5));
32314
- definitionMap.set('version', literal('17.1.0'));
34280
+ definitionMap.set('version', literal('17.3.0'));
32315
34281
  definitionMap.set('ngImport', importExpr(Identifiers.core));
32316
34282
  definitionMap.set('type', metadata.type);
32317
34283
  definitionMap.set('decorators', metadata.decorators);
@@ -32407,7 +34373,7 @@ function createDirectiveDefinitionMap(meta) {
32407
34373
  const definitionMap = new DefinitionMap();
32408
34374
  const minVersion = getMinimumVersionForPartialOutput(meta);
32409
34375
  definitionMap.set('minVersion', literal(minVersion));
32410
- definitionMap.set('version', literal('17.1.0'));
34376
+ definitionMap.set('version', literal('17.3.0'));
32411
34377
  // e.g. `type: MyDirective`
32412
34378
  definitionMap.set('type', meta.type.value);
32413
34379
  if (meta.isStandalone) {
@@ -32475,6 +34441,11 @@ function getMinimumVersionForPartialOutput(meta) {
32475
34441
  if (needsNewInputPartialOutput(meta)) {
32476
34442
  minVersion = '17.1.0';
32477
34443
  }
34444
+ // If there are signal-based queries, partial output generates an extra field
34445
+ // that should be parsed by linkers. Ensure a proper minimum linker version.
34446
+ if (meta.queries.some(q => q.isSignal) || meta.viewQueries.some(q => q.isSignal)) {
34447
+ minVersion = '17.2.0';
34448
+ }
32478
34449
  return minVersion;
32479
34450
  }
32480
34451
  /**
@@ -32508,6 +34479,9 @@ function compileQuery(query) {
32508
34479
  if (query.static) {
32509
34480
  meta.set('static', literal(true));
32510
34481
  }
34482
+ if (query.isSignal) {
34483
+ meta.set('isSignal', literal(true));
34484
+ }
32511
34485
  return meta.toLiteralMap();
32512
34486
  }
32513
34487
  /**
@@ -32788,7 +34762,7 @@ const MINIMUM_PARTIAL_LINKER_VERSION$4 = '12.0.0';
32788
34762
  function compileDeclareFactoryFunction(meta) {
32789
34763
  const definitionMap = new DefinitionMap();
32790
34764
  definitionMap.set('minVersion', literal(MINIMUM_PARTIAL_LINKER_VERSION$4));
32791
- definitionMap.set('version', literal('17.1.0'));
34765
+ definitionMap.set('version', literal('17.3.0'));
32792
34766
  definitionMap.set('ngImport', importExpr(Identifiers.core));
32793
34767
  definitionMap.set('type', meta.type.value);
32794
34768
  definitionMap.set('deps', compileDependencies(meta.deps));
@@ -32823,7 +34797,7 @@ function compileDeclareInjectableFromMetadata(meta) {
32823
34797
  function createInjectableDefinitionMap(meta) {
32824
34798
  const definitionMap = new DefinitionMap();
32825
34799
  definitionMap.set('minVersion', literal(MINIMUM_PARTIAL_LINKER_VERSION$3));
32826
- definitionMap.set('version', literal('17.1.0'));
34800
+ definitionMap.set('version', literal('17.3.0'));
32827
34801
  definitionMap.set('ngImport', importExpr(Identifiers.core));
32828
34802
  definitionMap.set('type', meta.type.value);
32829
34803
  // Only generate providedIn property if it has a non-null value
@@ -32874,7 +34848,7 @@ function compileDeclareInjectorFromMetadata(meta) {
32874
34848
  function createInjectorDefinitionMap(meta) {
32875
34849
  const definitionMap = new DefinitionMap();
32876
34850
  definitionMap.set('minVersion', literal(MINIMUM_PARTIAL_LINKER_VERSION$2));
32877
- definitionMap.set('version', literal('17.1.0'));
34851
+ definitionMap.set('version', literal('17.3.0'));
32878
34852
  definitionMap.set('ngImport', importExpr(Identifiers.core));
32879
34853
  definitionMap.set('type', meta.type.value);
32880
34854
  definitionMap.set('providers', meta.providers);
@@ -32907,7 +34881,7 @@ function createNgModuleDefinitionMap(meta) {
32907
34881
  throw new Error('Invalid path! Local compilation mode should not get into the partial compilation path');
32908
34882
  }
32909
34883
  definitionMap.set('minVersion', literal(MINIMUM_PARTIAL_LINKER_VERSION$1));
32910
- definitionMap.set('version', literal('17.1.0'));
34884
+ definitionMap.set('version', literal('17.3.0'));
32911
34885
  definitionMap.set('ngImport', importExpr(Identifiers.core));
32912
34886
  definitionMap.set('type', meta.type.value);
32913
34887
  // We only generate the keys in the metadata if the arrays contain values.
@@ -32958,7 +34932,7 @@ function compileDeclarePipeFromMetadata(meta) {
32958
34932
  function createPipeDefinitionMap(meta) {
32959
34933
  const definitionMap = new DefinitionMap();
32960
34934
  definitionMap.set('minVersion', literal(MINIMUM_PARTIAL_LINKER_VERSION));
32961
- definitionMap.set('version', literal('17.1.0'));
34935
+ definitionMap.set('version', literal('17.3.0'));
32962
34936
  definitionMap.set('ngImport', importExpr(Identifiers.core));
32963
34937
  // e.g. `type: MyPipe`
32964
34938
  definitionMap.set('type', meta.type.value);
@@ -33095,6 +35069,7 @@ exports.TemplateLiteral = TemplateLiteral;
33095
35069
  exports.TemplateLiteralElement = TemplateLiteralElement;
33096
35070
  exports.Text = Text;
33097
35071
  exports.ThisReceiver = ThisReceiver;
35072
+ exports.TmplAstBlockNode = BlockNode;
33098
35073
  exports.TmplAstBoundAttribute = BoundAttribute;
33099
35074
  exports.TmplAstBoundDeferredTrigger = BoundDeferredTrigger;
33100
35075
  exports.TmplAstBoundEvent = BoundEvent;
@@ -33145,7 +35120,6 @@ exports.Xliff2 = Xliff2;
33145
35120
  exports.Xmb = Xmb;
33146
35121
  exports.XmlParser = XmlParser;
33147
35122
  exports.Xtb = Xtb;
33148
- exports._ParseAST = _ParseAST;
33149
35123
  exports.compileClassDebugInfo = compileClassDebugInfo;
33150
35124
  exports.compileClassMetadata = compileClassMetadata;
33151
35125
  exports.compileComponentClassMetadata = compileComponentClassMetadata;
@@ -33194,5 +35168,6 @@ exports.publishFacade = publishFacade;
33194
35168
  exports.r3JitTypeSourceSpan = r3JitTypeSourceSpan;
33195
35169
  exports.sanitizeIdentifier = sanitizeIdentifier;
33196
35170
  exports.splitNsName = splitNsName;
35171
+ exports.tmplAstVisitAll = visitAll$1;
33197
35172
  exports.verifyHostBindings = verifyHostBindings;
33198
35173
  exports.visitAll = visitAll;