@angular/compiler 17.1.0-next.4 → 17.1.0-rc.0

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 (55) hide show
  1. package/esm2022/src/compiler_facade_interface.mjs +1 -1
  2. package/esm2022/src/constant_pool.mjs +5 -3
  3. package/esm2022/src/core.mjs +8 -1
  4. package/esm2022/src/jit_compiler_facade.mjs +5 -3
  5. package/esm2022/src/ml_parser/tags.mjs +8 -3
  6. package/esm2022/src/output/abstract_emitter.mjs +4 -1
  7. package/esm2022/src/output/output_ast.mjs +11 -7
  8. package/esm2022/src/render3/partial/class_metadata.mjs +1 -1
  9. package/esm2022/src/render3/partial/directive.mjs +4 -4
  10. package/esm2022/src/render3/partial/factory.mjs +1 -1
  11. package/esm2022/src/render3/partial/injectable.mjs +1 -1
  12. package/esm2022/src/render3/partial/injector.mjs +1 -1
  13. package/esm2022/src/render3/partial/ng_module.mjs +1 -1
  14. package/esm2022/src/render3/partial/pipe.mjs +1 -1
  15. package/esm2022/src/render3/r3_identifiers.mjs +5 -1
  16. package/esm2022/src/render3/view/style_parser.mjs +2 -1
  17. package/esm2022/src/render3/view/template.mjs +18 -10
  18. package/esm2022/src/render3/view/util.mjs +35 -7
  19. package/esm2022/src/template/pipeline/ir/src/enums.mjs +7 -13
  20. package/esm2022/src/template/pipeline/ir/src/expression.mjs +15 -21
  21. package/esm2022/src/template/pipeline/ir/src/ops/create.mjs +37 -14
  22. package/esm2022/src/template/pipeline/ir/src/ops/update.mjs +6 -3
  23. package/esm2022/src/template/pipeline/src/conversion.mjs +2 -1
  24. package/esm2022/src/template/pipeline/src/emit.mjs +3 -5
  25. package/esm2022/src/template/pipeline/src/ingest.mjs +106 -55
  26. package/esm2022/src/template/pipeline/src/instruction.mjs +24 -17
  27. package/esm2022/src/template/pipeline/src/phases/attribute_extraction.mjs +15 -19
  28. package/esm2022/src/template/pipeline/src/phases/binding_specialization.mjs +4 -2
  29. package/esm2022/src/template/pipeline/src/phases/const_collection.mjs +54 -22
  30. package/esm2022/src/template/pipeline/src/phases/convert_i18n_bindings.mjs +2 -2
  31. package/esm2022/src/template/pipeline/src/phases/create_defer_deps_fns.mjs +3 -2
  32. package/esm2022/src/template/pipeline/src/phases/create_i18n_contexts.mjs +63 -51
  33. package/esm2022/src/template/pipeline/src/phases/deduplicate_text_bindings.mjs +40 -0
  34. package/esm2022/src/template/pipeline/src/phases/extract_i18n_messages.mjs +52 -49
  35. package/esm2022/src/template/pipeline/src/phases/host_style_property_parsing.mjs +3 -3
  36. package/esm2022/src/template/pipeline/src/phases/i18n_const_collection.mjs +2 -3
  37. package/esm2022/src/template/pipeline/src/phases/i18n_text_extraction.mjs +22 -3
  38. package/esm2022/src/template/pipeline/src/phases/naming.mjs +13 -5
  39. package/esm2022/src/template/pipeline/src/phases/ordering.mjs +17 -5
  40. package/esm2022/src/template/pipeline/src/phases/parse_extracted_styles.mjs +21 -3
  41. package/esm2022/src/template/pipeline/src/phases/phase_remove_content_selectors.mjs +1 -10
  42. package/esm2022/src/template/pipeline/src/phases/propagate_i18n_blocks.mjs +5 -3
  43. package/esm2022/src/template/pipeline/src/phases/reify.mjs +37 -12
  44. package/esm2022/src/template/pipeline/src/phases/resolve_i18n_expression_placeholders.mjs +23 -10
  45. package/esm2022/src/template/pipeline/src/phases/track_fn_generation.mjs +4 -1
  46. package/esm2022/src/template/pipeline/src/phases/var_counting.mjs +8 -1
  47. package/esm2022/src/template/pipeline/src/phases/wrap_icus.mjs +4 -3
  48. package/esm2022/src/template/pipeline/src/util/elements.mjs +8 -1
  49. package/esm2022/src/version.mjs +1 -1
  50. package/fesm2022/compiler.mjs +655 -426
  51. package/fesm2022/compiler.mjs.map +1 -1
  52. package/index.d.ts +21 -9
  53. package/package.json +2 -2
  54. package/esm2022/src/template/pipeline/src/phases/repeater_derived_vars.mjs +0 -45
  55. package/esm2022/src/template/pipeline/src/phases/resolve_i18n_icu_placeholders.mjs +0 -62
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @license Angular v17.1.0-next.4
2
+ * @license Angular v17.1.0-rc.0
3
3
  * (c) 2010-2022 Google LLC. https://angular.io/
4
4
  * License: MIT
5
5
  */
@@ -409,6 +409,13 @@ var ChangeDetectionStrategy;
409
409
  ChangeDetectionStrategy[ChangeDetectionStrategy["OnPush"] = 0] = "OnPush";
410
410
  ChangeDetectionStrategy[ChangeDetectionStrategy["Default"] = 1] = "Default";
411
411
  })(ChangeDetectionStrategy || (ChangeDetectionStrategy = {}));
412
+ /** Flags describing an input for a directive. */
413
+ var InputFlags;
414
+ (function (InputFlags) {
415
+ InputFlags[InputFlags["None"] = 0] = "None";
416
+ InputFlags[InputFlags["SignalBased"] = 1] = "SignalBased";
417
+ InputFlags[InputFlags["HasDecoratorInputTransform"] = 2] = "HasDecoratorInputTransform";
418
+ })(InputFlags || (InputFlags = {}));
412
419
  const CUSTOM_ELEMENTS_SCHEMA = {
413
420
  name: 'custom-elements'
414
421
  };
@@ -472,6 +479,7 @@ var core = /*#__PURE__*/Object.freeze({
472
479
  emitDistinctChangesOnlyDefaultValue: emitDistinctChangesOnlyDefaultValue,
473
480
  get ViewEncapsulation () { return ViewEncapsulation; },
474
481
  get ChangeDetectionStrategy () { return ChangeDetectionStrategy; },
482
+ get InputFlags () { return InputFlags; },
475
483
  CUSTOM_ELEMENTS_SCHEMA: CUSTOM_ELEMENTS_SCHEMA,
476
484
  NO_ERRORS_SCHEMA: NO_ERRORS_SCHEMA,
477
485
  Type: Type$1,
@@ -890,12 +898,13 @@ var BinaryOperator;
890
898
  BinaryOperator[BinaryOperator["Modulo"] = 8] = "Modulo";
891
899
  BinaryOperator[BinaryOperator["And"] = 9] = "And";
892
900
  BinaryOperator[BinaryOperator["Or"] = 10] = "Or";
893
- BinaryOperator[BinaryOperator["BitwiseAnd"] = 11] = "BitwiseAnd";
894
- BinaryOperator[BinaryOperator["Lower"] = 12] = "Lower";
895
- BinaryOperator[BinaryOperator["LowerEquals"] = 13] = "LowerEquals";
896
- BinaryOperator[BinaryOperator["Bigger"] = 14] = "Bigger";
897
- BinaryOperator[BinaryOperator["BiggerEquals"] = 15] = "BiggerEquals";
898
- BinaryOperator[BinaryOperator["NullishCoalesce"] = 16] = "NullishCoalesce";
901
+ BinaryOperator[BinaryOperator["BitwiseOr"] = 11] = "BitwiseOr";
902
+ BinaryOperator[BinaryOperator["BitwiseAnd"] = 12] = "BitwiseAnd";
903
+ BinaryOperator[BinaryOperator["Lower"] = 13] = "Lower";
904
+ BinaryOperator[BinaryOperator["LowerEquals"] = 14] = "LowerEquals";
905
+ BinaryOperator[BinaryOperator["Bigger"] = 15] = "Bigger";
906
+ BinaryOperator[BinaryOperator["BiggerEquals"] = 16] = "BiggerEquals";
907
+ BinaryOperator[BinaryOperator["NullishCoalesce"] = 17] = "NullishCoalesce";
899
908
  })(BinaryOperator || (BinaryOperator = {}));
900
909
  function nullSafeIsEquivalent(base, other) {
901
910
  if (base == null || other == null) {
@@ -968,6 +977,9 @@ class Expression {
968
977
  and(rhs, sourceSpan) {
969
978
  return new BinaryOperatorExpr(BinaryOperator.And, this, rhs, null, sourceSpan);
970
979
  }
980
+ bitwiseOr(rhs, sourceSpan, parens = true) {
981
+ return new BinaryOperatorExpr(BinaryOperator.BitwiseOr, this, rhs, null, sourceSpan, parens);
982
+ }
971
983
  bitwiseAnd(rhs, sourceSpan, parens = true) {
972
984
  return new BinaryOperatorExpr(BinaryOperator.BitwiseAnd, this, rhs, null, sourceSpan, parens);
973
985
  }
@@ -2308,7 +2320,9 @@ class ConstantPool {
2308
2320
  }))));
2309
2321
  }
2310
2322
  }
2311
- getSharedFunctionReference(fn, prefix) {
2323
+ // TODO: useUniqueName(false) is necessary for naming compatibility with
2324
+ // TemplateDefinitionBuilder, but should be removed once Template Pipeline is the default.
2325
+ getSharedFunctionReference(fn, prefix, useUniqueName = true) {
2312
2326
  const isArrow = fn instanceof ArrowFunctionExpr;
2313
2327
  for (const current of this.statements) {
2314
2328
  // Arrow functions are saved as variables so we check if the
@@ -2323,7 +2337,7 @@ class ConstantPool {
2323
2337
  }
2324
2338
  }
2325
2339
  // Otherwise declare the function.
2326
- const name = this.uniqueName(prefix);
2340
+ const name = useUniqueName ? this.uniqueName(prefix) : prefix;
2327
2341
  this.statements.push(fn.toDeclStmt(name, StmtModifier.Final));
2328
2342
  return variable(name);
2329
2343
  }
@@ -2626,6 +2640,10 @@ class Identifiers {
2626
2640
  name: 'ɵɵgetInheritedFactory',
2627
2641
  moduleName: CORE,
2628
2642
  }; }
2643
+ static { this.InputFlags = {
2644
+ name: 'ɵɵInputFlags',
2645
+ moduleName: CORE,
2646
+ }; }
2629
2647
  // sanitization-related functions
2630
2648
  static { this.sanitizeHtml = { name: 'ɵɵsanitizeHtml', moduleName: CORE }; }
2631
2649
  static { this.sanitizeStyle = { name: 'ɵɵsanitizeStyle', moduleName: CORE }; }
@@ -3267,6 +3285,9 @@ class AbstractEmitterVisitor {
3267
3285
  case BinaryOperator.And:
3268
3286
  opStr = '&&';
3269
3287
  break;
3288
+ case BinaryOperator.BitwiseOr:
3289
+ opStr = '|';
3290
+ break;
3270
3291
  case BinaryOperator.BitwiseAnd:
3271
3292
  opStr = '&';
3272
3293
  break;
@@ -3688,13 +3709,18 @@ var TagContentType;
3688
3709
  TagContentType[TagContentType["ESCAPABLE_RAW_TEXT"] = 1] = "ESCAPABLE_RAW_TEXT";
3689
3710
  TagContentType[TagContentType["PARSABLE_DATA"] = 2] = "PARSABLE_DATA";
3690
3711
  })(TagContentType || (TagContentType = {}));
3691
- function splitNsName(elementName) {
3712
+ function splitNsName(elementName, fatal = true) {
3692
3713
  if (elementName[0] != ':') {
3693
3714
  return [null, elementName];
3694
3715
  }
3695
3716
  const colonIndex = elementName.indexOf(':', 1);
3696
3717
  if (colonIndex === -1) {
3697
- throw new Error(`Unsupported format "${elementName}" expecting ":namespace:name"`);
3718
+ if (fatal) {
3719
+ throw new Error(`Unsupported format "${elementName}" expecting ":namespace:name"`);
3720
+ }
3721
+ else {
3722
+ return [null, elementName];
3723
+ }
3698
3724
  }
3699
3725
  return [elementName.slice(1, colonIndex), elementName.slice(colonIndex + 1)];
3700
3726
  }
@@ -4966,7 +4992,7 @@ function asLiteral(value) {
4966
4992
  * This will attempt to generate optimized data structures to minimize memory or
4967
4993
  * file size of fully compiled applications.
4968
4994
  */
4969
- function conditionallyCreateDirectiveBindingLiteral(map, keepDeclared) {
4995
+ function conditionallyCreateDirectiveBindingLiteral(map, forInputs) {
4970
4996
  const keys = Object.getOwnPropertyNames(map);
4971
4997
  if (keys.length === 0) {
4972
4998
  return null;
@@ -4988,12 +5014,28 @@ function conditionallyCreateDirectiveBindingLiteral(map, keepDeclared) {
4988
5014
  minifiedName = key;
4989
5015
  declaredName = value.classPropertyName;
4990
5016
  publicName = value.bindingPropertyName;
4991
- if (keepDeclared && (publicName !== declaredName || value.transformFunction != null)) {
4992
- const expressionKeys = [asLiteral(publicName), asLiteral(declaredName)];
4993
- if (value.transformFunction != null) {
4994
- expressionKeys.push(value.transformFunction);
5017
+ const differentDeclaringName = publicName !== declaredName;
5018
+ const hasDecoratorInputTransform = value.transformFunction !== null;
5019
+ // Build up input flags
5020
+ let flags = null;
5021
+ if (value.isSignal) {
5022
+ flags = bitwiseOrInputFlagsExpr(InputFlags.SignalBased, flags);
5023
+ }
5024
+ if (hasDecoratorInputTransform) {
5025
+ flags = bitwiseOrInputFlagsExpr(InputFlags.HasDecoratorInputTransform, flags);
5026
+ }
5027
+ // Inputs, compared to outputs, will track their declared name (for `ngOnChanges`), support
5028
+ // decorator input transform functions, or store flag information if there is any.
5029
+ if (forInputs && (differentDeclaringName || hasDecoratorInputTransform || flags !== null)) {
5030
+ const flagsExpr = flags ?? importExpr(Identifiers.InputFlags).prop(InputFlags[InputFlags.None]);
5031
+ const result = [flagsExpr, asLiteral(publicName)];
5032
+ if (differentDeclaringName || hasDecoratorInputTransform) {
5033
+ result.push(asLiteral(declaredName));
5034
+ if (hasDecoratorInputTransform) {
5035
+ result.push(value.transformFunction);
5036
+ }
4995
5037
  }
4996
- expressionValue = literalArr(expressionKeys);
5038
+ expressionValue = literalArr(result);
4997
5039
  }
4998
5040
  else {
4999
5041
  expressionValue = asLiteral(publicName);
@@ -5007,6 +5049,17 @@ function conditionallyCreateDirectiveBindingLiteral(map, keepDeclared) {
5007
5049
  };
5008
5050
  }));
5009
5051
  }
5052
+ /** Gets an output AST expression referencing the given flag. */
5053
+ function getInputFlagExpr(flag) {
5054
+ return importExpr(Identifiers.InputFlags).prop(InputFlags[flag]);
5055
+ }
5056
+ /** Combines a given input flag with an existing flag expression, if present. */
5057
+ function bitwiseOrInputFlagsExpr(flag, expr) {
5058
+ if (expr === null) {
5059
+ return getInputFlagExpr(flag);
5060
+ }
5061
+ return getInputFlagExpr(flag).bitwiseOr(expr);
5062
+ }
5010
5063
  /**
5011
5064
  * Remove trailing null nodes as they are implied.
5012
5065
  */
@@ -8975,14 +9028,18 @@ var OpKind;
8975
9028
  * An instruction to update an ICU expression.
8976
9029
  */
8977
9030
  OpKind[OpKind["IcuEnd"] = 42] = "IcuEnd";
9031
+ /**
9032
+ * An instruction representing a placeholder in an ICU expression.
9033
+ */
9034
+ OpKind[OpKind["IcuPlaceholder"] = 43] = "IcuPlaceholder";
8978
9035
  /**
8979
9036
  * An i18n context containing information needed to generate an i18n message.
8980
9037
  */
8981
- OpKind[OpKind["I18nContext"] = 43] = "I18nContext";
9038
+ OpKind[OpKind["I18nContext"] = 44] = "I18nContext";
8982
9039
  /**
8983
9040
  * A creation op that corresponds to i18n attributes on an element.
8984
9041
  */
8985
- OpKind[OpKind["I18nAttributes"] = 44] = "I18nAttributes";
9042
+ OpKind[OpKind["I18nAttributes"] = 45] = "I18nAttributes";
8986
9043
  })(OpKind || (OpKind = {}));
8987
9044
  /**
8988
9045
  * Distinguishes different kinds of IR expressions.
@@ -9259,16 +9316,6 @@ var DeferTriggerKind;
9259
9316
  DeferTriggerKind[DeferTriggerKind["Interaction"] = 4] = "Interaction";
9260
9317
  DeferTriggerKind[DeferTriggerKind["Viewport"] = 5] = "Viewport";
9261
9318
  })(DeferTriggerKind || (DeferTriggerKind = {}));
9262
- /**
9263
- * Repeaters implicitly define these derived variables, and child nodes may read them.
9264
- */
9265
- var DerivedRepeaterVarIdentity;
9266
- (function (DerivedRepeaterVarIdentity) {
9267
- DerivedRepeaterVarIdentity[DerivedRepeaterVarIdentity["First"] = 0] = "First";
9268
- DerivedRepeaterVarIdentity[DerivedRepeaterVarIdentity["Last"] = 1] = "Last";
9269
- DerivedRepeaterVarIdentity[DerivedRepeaterVarIdentity["Even"] = 2] = "Even";
9270
- DerivedRepeaterVarIdentity[DerivedRepeaterVarIdentity["Odd"] = 3] = "Odd";
9271
- })(DerivedRepeaterVarIdentity || (DerivedRepeaterVarIdentity = {}));
9272
9319
  /**
9273
9320
  * Kinds of i18n contexts. They can be created because of root i18n blocks, or ICUs.
9274
9321
  */
@@ -9513,10 +9560,11 @@ function createClassMapOp(xref, expression, sourceSpan) {
9513
9560
  /**
9514
9561
  * Create an `AttributeOp`.
9515
9562
  */
9516
- function createAttributeOp(target, name, expression, securityContext, isTextAttribute, isStructuralTemplateAttribute, templateKind, i18nMessage, sourceSpan) {
9563
+ function createAttributeOp(target, namespace, name, expression, securityContext, isTextAttribute, isStructuralTemplateAttribute, templateKind, i18nMessage, sourceSpan) {
9517
9564
  return {
9518
9565
  kind: OpKind.Attribute,
9519
9566
  target,
9567
+ namespace,
9520
9568
  name,
9521
9569
  expression,
9522
9570
  securityContext,
@@ -9581,12 +9629,13 @@ function createDeferWhenOp(target, expr, prefetch, sourceSpan) {
9581
9629
  sourceSpan,
9582
9630
  ...NEW_OP,
9583
9631
  ...TRAIT_DEPENDS_ON_SLOT_CONTEXT,
9632
+ ...TRAIT_CONSUMES_VARS,
9584
9633
  };
9585
9634
  }
9586
9635
  /**
9587
9636
  * Create an i18n expression op.
9588
9637
  */
9589
- function createI18nExpressionOp(context, target, i18nOwner, handle, expression, i18nPlaceholder, resolutionTime, usage, name, sourceSpan) {
9638
+ function createI18nExpressionOp(context, target, i18nOwner, handle, expression, icuPlaceholder, i18nPlaceholder, resolutionTime, usage, name, sourceSpan) {
9590
9639
  return {
9591
9640
  kind: OpKind.I18nExpression,
9592
9641
  context,
@@ -9594,6 +9643,7 @@ function createI18nExpressionOp(context, target, i18nOwner, handle, expression,
9594
9643
  i18nOwner,
9595
9644
  handle,
9596
9645
  expression,
9646
+ icuPlaceholder,
9597
9647
  i18nPlaceholder,
9598
9648
  resolutionTime,
9599
9649
  usage,
@@ -10211,26 +10261,6 @@ class ConditionalCaseExpr extends ExpressionBase {
10211
10261
  }
10212
10262
  }
10213
10263
  }
10214
- class DerivedRepeaterVarExpr extends ExpressionBase {
10215
- constructor(xref, identity) {
10216
- super();
10217
- this.xref = xref;
10218
- this.identity = identity;
10219
- this.kind = ExpressionKind.DerivedRepeaterVar;
10220
- }
10221
- transformInternalExpressions(transform, flags) { }
10222
- visitExpression(visitor, context) { }
10223
- isEquivalent(e) {
10224
- return e instanceof DerivedRepeaterVarExpr && e.identity === this.identity &&
10225
- e.xref === this.xref;
10226
- }
10227
- isConstant() {
10228
- return false;
10229
- }
10230
- clone() {
10231
- return new DerivedRepeaterVarExpr(this.xref, this.identity);
10232
- }
10233
- }
10234
10264
  class ConstCollectedExpr extends ExpressionBase {
10235
10265
  constructor(expr) {
10236
10266
  super();
@@ -10363,6 +10393,9 @@ function transformExpressionsInOp(op, transform, flags) {
10363
10393
  op.placeholderConfig =
10364
10394
  transformExpressionsInExpression(op.placeholderConfig, transform, flags);
10365
10395
  }
10396
+ if (op.resolverFn !== null) {
10397
+ op.resolverFn = transformExpressionsInExpression(op.resolverFn, transform, flags);
10398
+ }
10366
10399
  break;
10367
10400
  case OpKind.I18nMessage:
10368
10401
  for (const [placeholder, expr] of op.params) {
@@ -10399,6 +10432,7 @@ function transformExpressionsInOp(op, transform, flags) {
10399
10432
  case OpKind.Template:
10400
10433
  case OpKind.Text:
10401
10434
  case OpKind.I18nAttributes:
10435
+ case OpKind.IcuPlaceholder:
10402
10436
  // These operations contain no expressions.
10403
10437
  break;
10404
10438
  default:
@@ -10481,6 +10515,16 @@ function transformExpressionsInExpression(expr, transform, flags) {
10481
10515
  expr.template.expressions =
10482
10516
  expr.template.expressions.map(e => transformExpressionsInExpression(e, transform, flags));
10483
10517
  }
10518
+ else if (expr instanceof ArrowFunctionExpr) {
10519
+ if (Array.isArray(expr.body)) {
10520
+ for (let i = 0; i < expr.body.length; i++) {
10521
+ transformExpressionsInStatement(expr.body[i], transform, flags);
10522
+ }
10523
+ }
10524
+ else {
10525
+ expr.body = transformExpressionsInExpression(expr.body, transform, flags);
10526
+ }
10527
+ }
10484
10528
  else if (expr instanceof WrappedNodeExpr) {
10485
10529
  // TODO: Do we need to transform any TS nodes nested inside of this expression?
10486
10530
  }
@@ -10806,7 +10850,7 @@ function isElementOrContainerOp(op) {
10806
10850
  /**
10807
10851
  * Create an `ElementStartOp`.
10808
10852
  */
10809
- function createElementStartOp(tag, xref, namespace, i18nPlaceholder, sourceSpan) {
10853
+ function createElementStartOp(tag, xref, namespace, i18nPlaceholder, startSourceSpan, wholeSourceSpan) {
10810
10854
  return {
10811
10855
  kind: OpKind.ElementStart,
10812
10856
  xref,
@@ -10817,7 +10861,8 @@ function createElementStartOp(tag, xref, namespace, i18nPlaceholder, sourceSpan)
10817
10861
  nonBindable: false,
10818
10862
  namespace,
10819
10863
  i18nPlaceholder,
10820
- sourceSpan,
10864
+ startSourceSpan,
10865
+ wholeSourceSpan,
10821
10866
  ...TRAIT_CONSUMES_SLOT,
10822
10867
  ...NEW_OP,
10823
10868
  };
@@ -10825,7 +10870,7 @@ function createElementStartOp(tag, xref, namespace, i18nPlaceholder, sourceSpan)
10825
10870
  /**
10826
10871
  * Create a `TemplateOp`.
10827
10872
  */
10828
- function createTemplateOp(xref, templateKind, tag, functionNameSuffix, namespace, i18nPlaceholder, sourceSpan) {
10873
+ function createTemplateOp(xref, templateKind, tag, functionNameSuffix, namespace, i18nPlaceholder, startSourceSpan, wholeSourceSpan) {
10829
10874
  return {
10830
10875
  kind: OpKind.Template,
10831
10876
  xref,
@@ -10840,12 +10885,13 @@ function createTemplateOp(xref, templateKind, tag, functionNameSuffix, namespace
10840
10885
  nonBindable: false,
10841
10886
  namespace,
10842
10887
  i18nPlaceholder,
10843
- sourceSpan,
10888
+ startSourceSpan,
10889
+ wholeSourceSpan,
10844
10890
  ...TRAIT_CONSUMES_SLOT,
10845
10891
  ...NEW_OP,
10846
10892
  };
10847
10893
  }
10848
- function createRepeaterCreateOp(primaryView, emptyView, tag, track, varNames, i18nPlaceholder, emptyI18nPlaceholder, sourceSpan) {
10894
+ function createRepeaterCreateOp(primaryView, emptyView, tag, track, varNames, emptyTag, i18nPlaceholder, emptyI18nPlaceholder, startSourceSpan, wholeSourceSpan) {
10849
10895
  return {
10850
10896
  kind: OpKind.RepeaterCreate,
10851
10897
  attributes: null,
@@ -10855,6 +10901,8 @@ function createRepeaterCreateOp(primaryView, emptyView, tag, track, varNames, i1
10855
10901
  track,
10856
10902
  trackByFn: null,
10857
10903
  tag,
10904
+ emptyTag,
10905
+ emptyAttributes: null,
10858
10906
  functionNameSuffix: 'For',
10859
10907
  namespace: Namespace.HTML,
10860
10908
  nonBindable: false,
@@ -10865,9 +10913,11 @@ function createRepeaterCreateOp(primaryView, emptyView, tag, track, varNames, i1
10865
10913
  usesComponentInstance: false,
10866
10914
  i18nPlaceholder,
10867
10915
  emptyI18nPlaceholder,
10868
- sourceSpan,
10916
+ startSourceSpan,
10917
+ wholeSourceSpan,
10869
10918
  ...TRAIT_CONSUMES_SLOT,
10870
10919
  ...NEW_OP,
10920
+ ...TRAIT_CONSUMES_VARS,
10871
10921
  numSlotsUsed: emptyView === null ? 2 : 3,
10872
10922
  };
10873
10923
  }
@@ -10899,12 +10949,13 @@ function createEnableBindingsOp(xref) {
10899
10949
  /**
10900
10950
  * Create a `TextOp`.
10901
10951
  */
10902
- function createTextOp(xref, initialValue, sourceSpan) {
10952
+ function createTextOp(xref, initialValue, icuPlaceholder, sourceSpan) {
10903
10953
  return {
10904
10954
  kind: OpKind.Text,
10905
10955
  xref,
10906
10956
  handle: new SlotHandle(),
10907
10957
  initialValue,
10958
+ icuPlaceholder,
10908
10959
  sourceSpan,
10909
10960
  ...TRAIT_CONSUMES_SLOT,
10910
10961
  ...NEW_OP,
@@ -10957,7 +11008,7 @@ function createProjectionDefOp(def) {
10957
11008
  ...NEW_OP,
10958
11009
  };
10959
11010
  }
10960
- function createProjectionOp(xref, selector, i18nPlaceholder, attributes, sourceSpan) {
11011
+ function createProjectionOp(xref, selector, i18nPlaceholder, sourceSpan) {
10961
11012
  return {
10962
11013
  kind: OpKind.Projection,
10963
11014
  xref,
@@ -10965,7 +11016,7 @@ function createProjectionOp(xref, selector, i18nPlaceholder, attributes, sourceS
10965
11016
  selector,
10966
11017
  i18nPlaceholder,
10967
11018
  projectionSlotIndex: 0,
10968
- attributes,
11019
+ attributes: null,
10969
11020
  localRefs: [],
10970
11021
  sourceSpan,
10971
11022
  ...NEW_OP,
@@ -10975,11 +11026,12 @@ function createProjectionOp(xref, selector, i18nPlaceholder, attributes, sourceS
10975
11026
  /**
10976
11027
  * Create an `ExtractedAttributeOp`.
10977
11028
  */
10978
- function createExtractedAttributeOp(target, bindingKind, name, expression, i18nContext, i18nMessage, securityContext) {
11029
+ function createExtractedAttributeOp(target, bindingKind, namespace, name, expression, i18nContext, i18nMessage, securityContext) {
10979
11030
  return {
10980
11031
  kind: OpKind.ExtractedAttribute,
10981
11032
  target,
10982
11033
  bindingKind,
11034
+ namespace,
10983
11035
  name,
10984
11036
  expression,
10985
11037
  i18nContext,
@@ -11046,7 +11098,7 @@ function createI18nMessageOp(xref, i18nContext, i18nBlock, message, messagePlace
11046
11098
  /**
11047
11099
  * Create an `I18nStartOp`.
11048
11100
  */
11049
- function createI18nStartOp(xref, message, root) {
11101
+ function createI18nStartOp(xref, message, root, sourceSpan) {
11050
11102
  return {
11051
11103
  kind: OpKind.I18nStart,
11052
11104
  xref,
@@ -11056,6 +11108,7 @@ function createI18nStartOp(xref, message, root) {
11056
11108
  messageIndex: null,
11057
11109
  subTemplateIndex: null,
11058
11110
  context: null,
11111
+ sourceSpan,
11059
11112
  ...NEW_OP,
11060
11113
  ...TRAIT_CONSUMES_SLOT,
11061
11114
  };
@@ -11063,10 +11116,11 @@ function createI18nStartOp(xref, message, root) {
11063
11116
  /**
11064
11117
  * Create an `I18nEndOp`.
11065
11118
  */
11066
- function createI18nEndOp(xref) {
11119
+ function createI18nEndOp(xref, sourceSpan) {
11067
11120
  return {
11068
11121
  kind: OpKind.I18nEnd,
11069
11122
  xref,
11123
+ sourceSpan,
11070
11124
  ...NEW_OP,
11071
11125
  };
11072
11126
  }
@@ -11094,6 +11148,19 @@ function createIcuEndOp(xref) {
11094
11148
  ...NEW_OP,
11095
11149
  };
11096
11150
  }
11151
+ /**
11152
+ * Creates an ICU placeholder op.
11153
+ */
11154
+ function createIcuPlaceholderOp(xref, name, strings) {
11155
+ return {
11156
+ kind: OpKind.IcuPlaceholder,
11157
+ xref,
11158
+ name,
11159
+ strings,
11160
+ expressionPlaceholders: [],
11161
+ ...NEW_OP,
11162
+ };
11163
+ }
11097
11164
  function createI18nContextOp(contextKind, xref, i18nBlock, message, sourceSpan) {
11098
11165
  if (i18nBlock === null && contextKind !== I18nContextKind.Attr) {
11099
11166
  throw new Error('AssertionError: i18nBlock must be provided for non-attribute contexts.');
@@ -11475,6 +11542,13 @@ function createOpXrefMap(unit) {
11475
11542
  continue;
11476
11543
  }
11477
11544
  map.set(op.xref, op);
11545
+ // TODO(dylhunn): `@for` loops with `@empty` blocks need to be special-cased here,
11546
+ // because the slot consumer trait currently only supports one slot per consumer and we
11547
+ // need two. This should be revisited when making the refactors mentioned in:
11548
+ // https://github.com/angular/angular/pull/53620#discussion_r1430918822
11549
+ if (op.kind === OpKind.RepeaterCreate && op.emptyView !== null) {
11550
+ map.set(op.emptyView, op);
11551
+ }
11478
11552
  }
11479
11553
  return map;
11480
11554
  }
@@ -11507,7 +11581,8 @@ function extractAttributes(job) {
11507
11581
  }
11508
11582
  OpList.insertBefore(
11509
11583
  // Deliberaly null i18nMessage value
11510
- createExtractedAttributeOp(op.target, bindingKind, op.name, /* expression */ null, /* i18nContext */ null,
11584
+ createExtractedAttributeOp(op.target, bindingKind, null, op.name, /* expression */ null,
11585
+ /* i18nContext */ null,
11511
11586
  /* i18nMessage */ null, op.securityContext), lookupElement$2(elements, op.target));
11512
11587
  }
11513
11588
  break;
@@ -11519,17 +11594,22 @@ function extractAttributes(job) {
11519
11594
  // mode.
11520
11595
  if (unit.job.compatibility === CompatibilityMode.TemplateDefinitionBuilder &&
11521
11596
  op.expression instanceof EmptyExpr) {
11522
- OpList.insertBefore(createExtractedAttributeOp(op.target, BindingKind.Property, op.name, /* expression */ null,
11597
+ OpList.insertBefore(createExtractedAttributeOp(op.target, BindingKind.Property, null, op.name, /* expression */ null,
11523
11598
  /* i18nContext */ null,
11524
11599
  /* i18nMessage */ null, SecurityContext.STYLE), lookupElement$2(elements, op.target));
11525
11600
  }
11526
11601
  break;
11527
11602
  case OpKind.Listener:
11528
11603
  if (!op.isAnimationListener) {
11529
- const extractedAttributeOp = createExtractedAttributeOp(op.target, BindingKind.Property, op.name, /* expression */ null,
11604
+ const extractedAttributeOp = createExtractedAttributeOp(op.target, BindingKind.Property, null, op.name, /* expression */ null,
11530
11605
  /* i18nContext */ null,
11531
11606
  /* i18nMessage */ null, SecurityContext.NONE);
11532
11607
  if (job.kind === CompilationJobKind.Host) {
11608
+ if (job.compatibility) {
11609
+ // TemplateDefinitionBuilder does not extract listener bindings to the const array
11610
+ // (which is honestly pretty inconsistent).
11611
+ break;
11612
+ }
11533
11613
  // This attribute will apply to the enclosing host binding compilation unit, so order
11534
11614
  // doesn't matter.
11535
11615
  unit.create.push(extractedAttributeOp);
@@ -11560,24 +11640,14 @@ function extractAttributeOp(unit, op, elements) {
11560
11640
  if (op.expression instanceof Interpolation) {
11561
11641
  return;
11562
11642
  }
11563
- let extractable = op.expression.isConstant();
11643
+ let extractable = op.isTextAttribute || op.expression.isConstant();
11564
11644
  if (unit.job.compatibility === CompatibilityMode.TemplateDefinitionBuilder) {
11565
- // TemplateDefinitionBuilder only extracted attributes that were string literals.
11566
- extractable = isStringLiteral(op.expression);
11567
- if (op.name === 'style' || op.name === 'class') {
11568
- // For style and class attributes, TemplateDefinitionBuilder only extracted them if they were
11569
- // text attributes. For example, `[attr.class]="'my-class'"` was not extracted despite being a
11570
- // string literal, because it is not a text attribute.
11571
- extractable &&= op.isTextAttribute;
11572
- }
11573
- if (unit.job.kind === CompilationJobKind.Host) {
11574
- // TemplateDefinitionBuilder also does not seem to extract string literals if they are part of
11575
- // a host attribute.
11576
- extractable &&= op.isTextAttribute;
11577
- }
11645
+ // TemplateDefinitionBuilder only extracts text attributes. It does not extract attriibute
11646
+ // bindings, even if they are constants.
11647
+ extractable &&= op.isTextAttribute;
11578
11648
  }
11579
11649
  if (extractable) {
11580
- const extractedAttributeOp = createExtractedAttributeOp(op.target, op.isStructuralTemplateAttribute ? BindingKind.Template : BindingKind.Attribute, op.name, op.expression, op.i18nContext, op.i18nMessage, op.securityContext);
11650
+ const extractedAttributeOp = createExtractedAttributeOp(op.target, op.isStructuralTemplateAttribute ? BindingKind.Template : BindingKind.Attribute, op.namespace, op.name, op.expression, op.i18nContext, op.i18nMessage, op.securityContext);
11581
11651
  if (unit.job.kind === CompilationJobKind.Host) {
11582
11652
  // This attribute will apply to the enclosing host binding compilation unit, so order doesn't
11583
11653
  // matter.
@@ -11624,7 +11694,8 @@ function specializeBindings(job) {
11624
11694
  target.nonBindable = true;
11625
11695
  }
11626
11696
  else {
11627
- OpList.replace(op, createAttributeOp(op.target, op.name, op.expression, op.securityContext, op.isTextAttribute, op.isStructuralTemplateAttribute, op.templateKind, op.i18nMessage, op.sourceSpan));
11697
+ const [namespace, name] = splitNsName(op.name);
11698
+ OpList.replace(op, createAttributeOp(op.target, namespace, name, op.expression, op.securityContext, op.isTextAttribute, op.isStructuralTemplateAttribute, op.templateKind, op.i18nMessage, op.sourceSpan));
11628
11699
  }
11629
11700
  break;
11630
11701
  case BindingKind.Property:
@@ -11814,6 +11885,7 @@ const BINARY_OPERATORS = new Map([
11814
11885
  ['&&', BinaryOperator.And],
11815
11886
  ['>', BinaryOperator.Bigger],
11816
11887
  ['>=', BinaryOperator.BiggerEquals],
11888
+ ['|', BinaryOperator.BitwiseOr],
11817
11889
  ['&', BinaryOperator.BitwiseAnd],
11818
11890
  ['/', BinaryOperator.Divide],
11819
11891
  ['==', BinaryOperator.Equals],
@@ -11868,9 +11940,9 @@ function collectElementConsts(job) {
11868
11940
  for (const unit of job.units) {
11869
11941
  for (const op of unit.create) {
11870
11942
  if (op.kind === OpKind.ExtractedAttribute) {
11871
- const attributes = allElementAttributes.get(op.target) || new ElementAttributes();
11943
+ const attributes = allElementAttributes.get(op.target) || new ElementAttributes(job.compatibility);
11872
11944
  allElementAttributes.set(op.target, attributes);
11873
- attributes.add(op.bindingKind, op.name, op.expression, op.trustedValueFn);
11945
+ attributes.add(op.bindingKind, op.name, op.expression, op.namespace, op.trustedValueFn);
11874
11946
  OpList.remove(op);
11875
11947
  }
11876
11948
  }
@@ -11879,15 +11951,26 @@ function collectElementConsts(job) {
11879
11951
  if (job instanceof ComponentCompilationJob) {
11880
11952
  for (const unit of job.units) {
11881
11953
  for (const op of unit.create) {
11882
- if (isElementOrContainerOp(op)) {
11954
+ // TODO: Simplify and combine these cases.
11955
+ if (op.kind == OpKind.Projection) {
11883
11956
  const attributes = allElementAttributes.get(op.xref);
11884
11957
  if (attributes !== undefined) {
11885
11958
  const attrArray = serializeAttributes(attributes);
11886
11959
  if (attrArray.entries.length > 0) {
11887
- op.attributes = job.addConst(attrArray);
11960
+ op.attributes = attrArray;
11888
11961
  }
11889
11962
  }
11890
11963
  }
11964
+ else if (isElementOrContainerOp(op)) {
11965
+ op.attributes = getConstIndex(job, allElementAttributes, op.xref);
11966
+ // TODO(dylhunn): `@for` loops with `@empty` blocks need to be special-cased here,
11967
+ // because the slot consumer trait currently only supports one slot per consumer and we
11968
+ // need two. This should be revisited when making the refactors mentioned in:
11969
+ // https://github.com/angular/angular/pull/53620#discussion_r1430918822
11970
+ if (op.kind === OpKind.RepeaterCreate && op.emptyView !== null) {
11971
+ op.emptyAttributes = getConstIndex(job, allElementAttributes, op.emptyView);
11972
+ }
11973
+ }
11891
11974
  }
11892
11975
  }
11893
11976
  }
@@ -11905,6 +11988,16 @@ function collectElementConsts(job) {
11905
11988
  }
11906
11989
  }
11907
11990
  }
11991
+ function getConstIndex(job, allElementAttributes, xref) {
11992
+ const attributes = allElementAttributes.get(xref);
11993
+ if (attributes !== undefined) {
11994
+ const attrArray = serializeAttributes(attributes);
11995
+ if (attrArray.entries.length > 0) {
11996
+ return job.addConst(attrArray);
11997
+ }
11998
+ }
11999
+ return null;
12000
+ }
11908
12001
  /**
11909
12002
  * Shared instance of an empty array to avoid unnecessary array allocations.
11910
12003
  */
@@ -11913,11 +12006,6 @@ const FLYWEIGHT_ARRAY = Object.freeze([]);
11913
12006
  * Container for all of the various kinds of attributes which are applied on an element.
11914
12007
  */
11915
12008
  class ElementAttributes {
11916
- constructor() {
11917
- this.known = new Set();
11918
- this.byKind = new Map;
11919
- this.projectAs = null;
11920
- }
11921
12009
  get attributes() {
11922
12010
  return this.byKind.get(BindingKind.Attribute) ?? FLYWEIGHT_ARRAY;
11923
12011
  }
@@ -11936,11 +12024,31 @@ class ElementAttributes {
11936
12024
  get i18n() {
11937
12025
  return this.byKind.get(BindingKind.I18n) ?? FLYWEIGHT_ARRAY;
11938
12026
  }
11939
- add(kind, name, value, trustedValueFn) {
11940
- if (this.known.has(name)) {
12027
+ constructor(compatibility) {
12028
+ this.compatibility = compatibility;
12029
+ this.known = new Map();
12030
+ this.byKind = new Map;
12031
+ this.projectAs = null;
12032
+ }
12033
+ isKnown(kind, name, value) {
12034
+ const nameToValue = this.known.get(kind) ?? new Set();
12035
+ this.known.set(kind, nameToValue);
12036
+ if (nameToValue.has(name)) {
12037
+ return true;
12038
+ }
12039
+ nameToValue.add(name);
12040
+ return false;
12041
+ }
12042
+ add(kind, name, value, namespace, trustedValueFn) {
12043
+ // TemplateDefinitionBuilder puts duplicate attribute, class, and style values into the consts
12044
+ // array. This seems inefficient, we can probably keep just the first one or the last value
12045
+ // (whichever actually gets applied when multiple values are listed for the same attribute).
12046
+ const allowDuplicates = this.compatibility === CompatibilityMode.TemplateDefinitionBuilder &&
12047
+ (kind === BindingKind.Attribute || kind === BindingKind.ClassName ||
12048
+ kind === BindingKind.StyleProperty);
12049
+ if (!allowDuplicates && this.isKnown(kind, name, value)) {
11941
12050
  return;
11942
12051
  }
11943
- this.known.add(name);
11944
12052
  // TODO: Can this be its own phase
11945
12053
  if (name === 'ngProjectAs') {
11946
12054
  if (value === null || !(value instanceof LiteralExpr) || (value.value == null) ||
@@ -11952,7 +12060,7 @@ class ElementAttributes {
11952
12060
  // attribute. Is this sane?
11953
12061
  }
11954
12062
  const array = this.arrayFor(kind);
11955
- array.push(...getAttributeNameLiterals$1(name));
12063
+ array.push(...getAttributeNameLiterals$1(namespace, name));
11956
12064
  if (kind === BindingKind.Attribute || kind === BindingKind.StyleProperty) {
11957
12065
  if (value === null) {
11958
12066
  throw Error('Attribute, i18n attribute, & style element attributes must have a value');
@@ -11978,13 +12086,10 @@ class ElementAttributes {
11978
12086
  /**
11979
12087
  * Gets an array of literal expressions representing the attribute's namespaced name.
11980
12088
  */
11981
- function getAttributeNameLiterals$1(name) {
11982
- const [attributeNamespace, attributeName] = splitNsName(name);
11983
- const nameLiteral = literal(attributeName);
11984
- if (attributeNamespace) {
11985
- return [
11986
- literal(0 /* core.AttributeMarker.NamespaceURI */), literal(attributeNamespace), nameLiteral
11987
- ];
12089
+ function getAttributeNameLiterals$1(namespace, name) {
12090
+ const nameLiteral = literal(name);
12091
+ if (namespace) {
12092
+ return [literal(0 /* core.AttributeMarker.NamespaceURI */), literal(namespace), nameLiteral];
11988
12093
  }
11989
12094
  return [nameLiteral];
11990
12095
  }
@@ -12052,7 +12157,7 @@ function convertI18nBindings(job) {
12052
12157
  if (op.expression.i18nPlaceholders.length !== op.expression.expressions.length) {
12053
12158
  throw new Error(`AssertionError: An i18n attribute binding instruction requires the same number of expressions and placeholders, but found ${op.expression.i18nPlaceholders.length} placeholders and ${op.expression.expressions.length} expressions`);
12054
12159
  }
12055
- ops.push(createI18nExpressionOp(op.i18nContext, i18nAttributesForElem.target, i18nAttributesForElem.xref, i18nAttributesForElem.handle, expr, op.expression.i18nPlaceholders[i], I18nParamResolutionTime.Creation, I18nExpressionFor.I18nAttribute, op.name, op.sourceSpan));
12160
+ ops.push(createI18nExpressionOp(op.i18nContext, i18nAttributesForElem.target, i18nAttributesForElem.xref, i18nAttributesForElem.handle, expr, null, op.expression.i18nPlaceholders[i], I18nParamResolutionTime.Creation, I18nExpressionFor.I18nAttribute, op.name, op.sourceSpan));
12056
12161
  }
12057
12162
  OpList.replaceWithMany(op, ops);
12058
12163
  break;
@@ -12089,7 +12194,8 @@ function createDeferDepsFns(job) {
12089
12194
  if (op.handle.slot === null) {
12090
12195
  throw new Error('AssertionError: slot must be assigned bfore extracting defer deps functions');
12091
12196
  }
12092
- op.resolverFn = job.pool.getSharedFunctionReference(depsFnExpr, `${job.componentName}_Defer_${op.handle.slot}_DepsFn`);
12197
+ op.resolverFn = job.pool.getSharedFunctionReference(depsFnExpr, `${job.componentName}_Defer_${op.handle.slot}_DepsFn`,
12198
+ /* Don't use unique names for TDB compatibility */ false);
12093
12199
  }
12094
12200
  }
12095
12201
  }
@@ -12106,75 +12212,119 @@ function createDeferDepsFns(job) {
12106
12212
  * message.)
12107
12213
  */
12108
12214
  function createI18nContexts(job) {
12109
- const rootContexts = new Map();
12110
- let currentI18nOp = null;
12111
- let xref;
12112
- // We use the message instead of the message ID, because placeholder values might differ even
12113
- // when IDs are the same.
12114
- const messageToContext = new Map();
12215
+ // Create i18n context ops for i18n attrs.
12216
+ const attrContextByMessage = new Map();
12217
+ for (const unit of job.units) {
12218
+ for (const op of unit.ops()) {
12219
+ switch (op.kind) {
12220
+ case OpKind.Binding:
12221
+ case OpKind.Property:
12222
+ case OpKind.Attribute:
12223
+ case OpKind.ExtractedAttribute:
12224
+ if (op.i18nMessage === null) {
12225
+ continue;
12226
+ }
12227
+ if (!attrContextByMessage.has(op.i18nMessage)) {
12228
+ const i18nContext = createI18nContextOp(I18nContextKind.Attr, job.allocateXrefId(), null, op.i18nMessage, null);
12229
+ unit.create.push(i18nContext);
12230
+ attrContextByMessage.set(op.i18nMessage, i18nContext.xref);
12231
+ }
12232
+ op.i18nContext = attrContextByMessage.get(op.i18nMessage);
12233
+ break;
12234
+ }
12235
+ }
12236
+ }
12237
+ // Create i18n context ops for root i18n blocks.
12238
+ const blockContextByI18nBlock = new Map();
12115
12239
  for (const unit of job.units) {
12116
12240
  for (const op of unit.create) {
12117
12241
  switch (op.kind) {
12118
12242
  case OpKind.I18nStart:
12119
- currentI18nOp = op;
12120
- // Each root i18n block gets its own context, child ones refer to the context for their
12121
- // root block.
12122
12243
  if (op.xref === op.root) {
12123
- xref = job.allocateXrefId();
12124
- unit.create.push(createI18nContextOp(I18nContextKind.RootI18n, xref, op.xref, op.message, null));
12125
- op.context = xref;
12126
- rootContexts.set(op.xref, xref);
12244
+ const contextOp = createI18nContextOp(I18nContextKind.RootI18n, job.allocateXrefId(), op.xref, op.message, null);
12245
+ unit.create.push(contextOp);
12246
+ op.context = contextOp.xref;
12247
+ blockContextByI18nBlock.set(op.xref, contextOp);
12127
12248
  }
12128
12249
  break;
12250
+ }
12251
+ }
12252
+ }
12253
+ // Assign i18n contexts for child i18n blocks. These don't need their own conext, instead they
12254
+ // should inherit from their root i18n block.
12255
+ for (const unit of job.units) {
12256
+ for (const op of unit.create) {
12257
+ if (op.kind === OpKind.I18nStart && op.xref !== op.root) {
12258
+ const rootContext = blockContextByI18nBlock.get(op.root);
12259
+ if (rootContext === undefined) {
12260
+ throw Error('AssertionError: Root i18n block i18n context should have been created.');
12261
+ }
12262
+ op.context = rootContext.xref;
12263
+ blockContextByI18nBlock.set(op.xref, rootContext);
12264
+ }
12265
+ }
12266
+ }
12267
+ // Create or assign i18n contexts for ICUs.
12268
+ let currentI18nOp = null;
12269
+ for (const unit of job.units) {
12270
+ for (const op of unit.create) {
12271
+ switch (op.kind) {
12272
+ case OpKind.I18nStart:
12273
+ currentI18nOp = op;
12274
+ break;
12129
12275
  case OpKind.I18nEnd:
12130
12276
  currentI18nOp = null;
12131
12277
  break;
12132
12278
  case OpKind.IcuStart:
12133
- // If an ICU represents a different message than its containing block, we give it its own
12134
- // i18n context.
12135
12279
  if (currentI18nOp === null) {
12136
- throw Error('Unexpected ICU outside of an i18n block.');
12280
+ throw Error('AssertionError: Unexpected ICU outside of an i18n block.');
12137
12281
  }
12138
12282
  if (op.message.id !== currentI18nOp.message.id) {
12139
- // There was an enclosing i18n block around this ICU somewhere.
12140
- xref = job.allocateXrefId();
12141
- unit.create.push(createI18nContextOp(I18nContextKind.Icu, xref, currentI18nOp.xref, op.message, null));
12142
- op.context = xref;
12283
+ // This ICU is a sub-message inside its parent i18n block message. We need to give it
12284
+ // its own context.
12285
+ const contextOp = createI18nContextOp(I18nContextKind.Icu, job.allocateXrefId(), currentI18nOp.xref, op.message, null);
12286
+ unit.create.push(contextOp);
12287
+ op.context = contextOp.xref;
12143
12288
  }
12144
12289
  else {
12145
- // The i18n block was generated because of this ICU, OR it was explicit, but the ICU is
12146
- // the only localizable content inside of it.
12290
+ // This ICU is the only translatable content in its parent i18n block. We need to
12291
+ // convert the parent's context into an ICU context.
12147
12292
  op.context = currentI18nOp.context;
12293
+ blockContextByI18nBlock.get(currentI18nOp.xref).contextKind = I18nContextKind.Icu;
12148
12294
  }
12149
12295
  break;
12150
12296
  }
12151
12297
  }
12152
- for (const op of unit.ops()) {
12153
- switch (op.kind) {
12154
- case OpKind.Binding:
12155
- case OpKind.Property:
12156
- case OpKind.Attribute:
12157
- case OpKind.ExtractedAttribute:
12158
- if (!op.i18nMessage) {
12159
- continue;
12160
- }
12161
- if (!messageToContext.has(op.i18nMessage)) {
12162
- // create the context
12163
- const i18nContext = job.allocateXrefId();
12164
- unit.create.push(createI18nContextOp(I18nContextKind.Attr, i18nContext, null, op.i18nMessage, null));
12165
- messageToContext.set(op.i18nMessage, i18nContext);
12166
- }
12167
- op.i18nContext = messageToContext.get(op.i18nMessage);
12168
- break;
12169
- }
12170
- }
12171
12298
  }
12172
- // Assign contexts to child i18n blocks, now that all root i18n blocks have their context
12173
- // assigned.
12299
+ }
12300
+
12301
+ /**
12302
+ * Deduplicate text bindings, e.g. <div class="cls1" class="cls2">
12303
+ */
12304
+ function deduplicateTextBindings(job) {
12305
+ const seen = new Map();
12174
12306
  for (const unit of job.units) {
12175
- for (const op of unit.create) {
12176
- if (op.kind === OpKind.I18nStart && op.xref !== op.root) {
12177
- op.context = rootContexts.get(op.root);
12307
+ for (const op of unit.update.reversed()) {
12308
+ if (op.kind === OpKind.Binding && op.isTextAttribute) {
12309
+ const seenForElement = seen.get(op.target) || new Set();
12310
+ if (seenForElement.has(op.name)) {
12311
+ if (job.compatibility === CompatibilityMode.TemplateDefinitionBuilder) {
12312
+ // For most duplicated attributes, TemplateDefinitionBuilder lists all of the values in
12313
+ // the consts array. However, for style and class attributes it only keeps the last one.
12314
+ // We replicate that behavior here since it has actual consequences for apps with
12315
+ // duplicate class or style attrs.
12316
+ if (op.name === 'style' || op.name === 'class') {
12317
+ OpList.remove(op);
12318
+ }
12319
+ }
12320
+ else {
12321
+ // TODO: Determine the correct behavior. It would probably make sense to merge multiple
12322
+ // style and class attributes. Alternatively we could just throw an error, as HTML
12323
+ // doesn't permit duplicate attributes.
12324
+ }
12325
+ }
12326
+ seenForElement.add(op.name);
12327
+ seen.set(op.target, seenForElement);
12178
12328
  }
12179
12329
  }
12180
12330
  }
@@ -12560,13 +12710,18 @@ const LIST_DELIMITER = '|';
12560
12710
  * used in the final output.
12561
12711
  */
12562
12712
  function extractI18nMessages(job) {
12563
- // Save the i18n start and i18n context ops for later use.
12564
- const i18nContexts = new Map();
12713
+ // Create an i18n message for each context.
12714
+ // TODO: Merge the context op with the message op since they're 1:1 anyways.
12715
+ const i18nMessagesByContext = new Map();
12565
12716
  const i18nBlocks = new Map();
12717
+ const i18nContexts = new Map();
12566
12718
  for (const unit of job.units) {
12567
12719
  for (const op of unit.create) {
12568
12720
  switch (op.kind) {
12569
12721
  case OpKind.I18nContext:
12722
+ const i18nMessageOp = createI18nMessage(job, op);
12723
+ unit.create.push(i18nMessageOp);
12724
+ i18nMessagesByContext.set(op.xref, i18nMessageOp);
12570
12725
  i18nContexts.set(op.xref, op);
12571
12726
  break;
12572
12727
  case OpKind.I18nStart:
@@ -12575,54 +12730,47 @@ function extractI18nMessages(job) {
12575
12730
  }
12576
12731
  }
12577
12732
  }
12578
- // TODO: Miles and I think that contexts have a 1-to-1 correspondence with extracted messages, so
12579
- // this phase can probably be simplified.
12580
- // Extract messages from contexts of i18n attributes.
12581
- for (const unit of job.units) {
12582
- for (const op of unit.create) {
12583
- if (op.kind !== OpKind.I18nContext || op.contextKind !== I18nContextKind.Attr) {
12584
- continue;
12585
- }
12586
- const i18nMessageOp = createI18nMessage(job, op);
12587
- unit.create.push(i18nMessageOp);
12588
- }
12589
- }
12590
- // Extract messages from root i18n blocks.
12591
- const i18nBlockMessages = new Map();
12592
- for (const unit of job.units) {
12593
- for (const op of unit.create) {
12594
- if (op.kind === OpKind.I18nStart && op.xref === op.root) {
12595
- if (!op.context) {
12596
- throw Error('I18n start op should have its context set.');
12597
- }
12598
- const i18nMessageOp = createI18nMessage(job, i18nContexts.get(op.context));
12599
- i18nBlockMessages.set(op.xref, i18nMessageOp);
12600
- unit.create.push(i18nMessageOp);
12601
- }
12602
- }
12603
- }
12604
- // Extract messages from ICUs with their own sub-context.
12733
+ // Associate sub-messages for ICUs with their root message. At this point we can also remove the
12734
+ // ICU start/end ops, as they are no longer needed.
12735
+ let currentIcu = null;
12605
12736
  for (const unit of job.units) {
12606
12737
  for (const op of unit.create) {
12607
12738
  switch (op.kind) {
12608
12739
  case OpKind.IcuStart:
12609
- if (!op.context) {
12610
- throw Error('ICU op should have its context set.');
12740
+ currentIcu = op;
12741
+ OpList.remove(op);
12742
+ // Skip any contexts not associated with an ICU.
12743
+ const icuContext = i18nContexts.get(op.context);
12744
+ if (icuContext.contextKind !== I18nContextKind.Icu) {
12745
+ continue;
12611
12746
  }
12612
- const i18nContext = i18nContexts.get(op.context);
12613
- if (i18nContext.contextKind === I18nContextKind.Icu) {
12614
- if (i18nContext.i18nBlock === null) {
12615
- throw Error('ICU context should have its i18n block set.');
12616
- }
12617
- const subMessage = createI18nMessage(job, i18nContext, op.messagePlaceholder);
12618
- unit.create.push(subMessage);
12619
- const rootI18nId = i18nBlocks.get(i18nContext.i18nBlock).root;
12620
- const parentMessage = i18nBlockMessages.get(rootI18nId);
12621
- parentMessage?.subMessages.push(subMessage.xref);
12747
+ // Skip ICUs that share a context with their i18n message. These represent root-level
12748
+ // ICUs, not sub-messages.
12749
+ const i18nBlock = i18nBlocks.get(icuContext.i18nBlock);
12750
+ if (i18nBlock.context === icuContext.xref) {
12751
+ continue;
12622
12752
  }
12623
- OpList.remove(op);
12753
+ // Find the root message and push this ICUs message as a sub-message.
12754
+ const rootI18nBlock = i18nBlocks.get(i18nBlock.root);
12755
+ const rootMessage = i18nMessagesByContext.get(rootI18nBlock.context);
12756
+ if (rootMessage === undefined) {
12757
+ throw Error('AssertionError: ICU sub-message should belong to a root message.');
12758
+ }
12759
+ const subMessage = i18nMessagesByContext.get(icuContext.xref);
12760
+ subMessage.messagePlaceholder = op.messagePlaceholder;
12761
+ rootMessage.subMessages.push(subMessage.xref);
12624
12762
  break;
12625
12763
  case OpKind.IcuEnd:
12764
+ currentIcu = null;
12765
+ OpList.remove(op);
12766
+ break;
12767
+ case OpKind.IcuPlaceholder:
12768
+ // Add ICU placeholders to the message, then remove the ICU placeholder ops.
12769
+ if (currentIcu === null || currentIcu.context == null) {
12770
+ throw Error('AssertionError: Unexpected ICU placeholder outside of i18n context');
12771
+ }
12772
+ const msg = i18nMessagesByContext.get(currentIcu.context);
12773
+ msg.postprocessingParams.set(op.name, literal(formatIcuPlaceholder(op)));
12626
12774
  OpList.remove(op);
12627
12775
  break;
12628
12776
  }
@@ -12635,14 +12783,19 @@ function extractI18nMessages(job) {
12635
12783
  function createI18nMessage(job, context, messagePlaceholder) {
12636
12784
  let formattedParams = formatParams(context.params);
12637
12785
  const formattedPostprocessingParams = formatParams(context.postprocessingParams);
12638
- let needsPostprocessing = formattedPostprocessingParams.size > 0;
12639
- for (const values of context.params.values()) {
12640
- if (values.length > 1) {
12641
- needsPostprocessing = true;
12642
- }
12643
- }
12786
+ let needsPostprocessing = [...context.params.values()].some(v => v.length > 1);
12644
12787
  return createI18nMessageOp(job.allocateXrefId(), context.xref, context.i18nBlock, context.message, messagePlaceholder ?? null, formattedParams, formattedPostprocessingParams, needsPostprocessing);
12645
12788
  }
12789
+ /**
12790
+ * Formats an ICU placeholder into a single string with expression placeholders.
12791
+ */
12792
+ function formatIcuPlaceholder(op) {
12793
+ if (op.strings.length !== op.expressionPlaceholders.length + 1) {
12794
+ throw Error(`AsserionError: Invalid ICU placeholder with ${op.strings.length} strings and ${op.expressionPlaceholders.length} expressions`);
12795
+ }
12796
+ const values = op.expressionPlaceholders.map(formatValue);
12797
+ return op.strings.flatMap((str, i) => [str, values[i] || '']).join('');
12798
+ }
12646
12799
  /**
12647
12800
  * Formats a map of `I18nParamValue[]` values into a map of `Expression` values.
12648
12801
  */
@@ -12981,7 +13134,7 @@ const BANG_IMPORTANT = '!important';
12981
13134
  */
12982
13135
  function parseHostStyleProperties(job) {
12983
13136
  for (const op of job.root.update) {
12984
- if (op.kind !== OpKind.Binding) {
13137
+ if (!(op.kind === OpKind.Binding && op.bindingKind === BindingKind.Property)) {
12985
13138
  continue;
12986
13139
  }
12987
13140
  if (op.name.endsWith(BANG_IMPORTANT)) {
@@ -12991,7 +13144,7 @@ function parseHostStyleProperties(job) {
12991
13144
  if (op.name.startsWith(STYLE_DOT)) {
12992
13145
  op.bindingKind = BindingKind.StyleProperty;
12993
13146
  op.name = op.name.substring(STYLE_DOT.length);
12994
- if (isCssCustomProperty$1(op.name)) {
13147
+ if (!isCssCustomProperty$1(op.name)) {
12995
13148
  op.name = hyphenate$1(op.name);
12996
13149
  }
12997
13150
  const { property, suffix } = parseProperty$1(op.name);
@@ -20139,7 +20292,7 @@ function collectMessage(job, fileBasedI18nSuffix, messages, messageOp) {
20139
20292
  let transformFn = undefined;
20140
20293
  // If nescessary, add a post-processing step and resolve any placeholder params that are
20141
20294
  // set in post-processing.
20142
- if (messageOp.needsPostprocessing) {
20295
+ if (messageOp.needsPostprocessing || messageOp.postprocessingParams.size > 0) {
20143
20296
  // Sort the post-processing params for consistency with TemaplateDefinitionBuilder output.
20144
20297
  const postprocessingParams = Object.fromEntries([...messageOp.postprocessingParams.entries()].sort());
20145
20298
  const formattedPostprocessingParams = formatI18nPlaceholderNamesInMap(postprocessingParams, /* useCamelCase */ false);
@@ -20169,7 +20322,6 @@ function addSubMessageParams(messageOp, subMessagePlaceholders) {
20169
20322
  else {
20170
20323
  messageOp.params.set(placeholder, literal(`${ESCAPE}${I18N_ICU_MAPPING_PREFIX}${placeholder}${ESCAPE}`));
20171
20324
  messageOp.postprocessingParams.set(placeholder, literalArr(subMessages));
20172
- messageOp.needsPostprocessing = true;
20173
20325
  }
20174
20326
  }
20175
20327
  }
@@ -20255,6 +20407,7 @@ function convertI18nText(job) {
20255
20407
  let currentIcu = null;
20256
20408
  const textNodeI18nBlocks = new Map();
20257
20409
  const textNodeIcus = new Map();
20410
+ const icuPlaceholderByText = new Map();
20258
20411
  for (const op of unit.create) {
20259
20412
  switch (op.kind) {
20260
20413
  case OpKind.I18nStart:
@@ -20279,7 +20432,19 @@ function convertI18nText(job) {
20279
20432
  if (currentI18n !== null) {
20280
20433
  textNodeI18nBlocks.set(op.xref, currentI18n);
20281
20434
  textNodeIcus.set(op.xref, currentIcu);
20282
- OpList.remove(op);
20435
+ if (op.icuPlaceholder !== null) {
20436
+ // Create an op to represent the ICU placeholder. Initially set its static text to the
20437
+ // value of the text op, though this may be overwritten later if this text op is a
20438
+ // placeholder for an interpolation.
20439
+ const icuPlaceholderOp = createIcuPlaceholderOp(job.allocateXrefId(), op.icuPlaceholder, [op.initialValue]);
20440
+ OpList.replace(op, icuPlaceholderOp);
20441
+ icuPlaceholderByText.set(op.xref, icuPlaceholderOp);
20442
+ }
20443
+ else {
20444
+ // Otherwise just remove the text op, since its value is already accounted for in the
20445
+ // translated message.
20446
+ OpList.remove(op);
20447
+ }
20283
20448
  }
20284
20449
  break;
20285
20450
  }
@@ -20294,6 +20459,7 @@ function convertI18nText(job) {
20294
20459
  }
20295
20460
  const i18nOp = textNodeI18nBlocks.get(op.target);
20296
20461
  const icuOp = textNodeIcus.get(op.target);
20462
+ const icuPlaceholder = icuPlaceholderByText.get(op.target);
20297
20463
  const contextId = icuOp ? icuOp.context : i18nOp.context;
20298
20464
  const resolutionTime = icuOp ? I18nParamResolutionTime.Postproccessing :
20299
20465
  I18nParamResolutionTime.Creation;
@@ -20302,9 +20468,14 @@ function convertI18nText(job) {
20302
20468
  const expr = op.interpolation.expressions[i];
20303
20469
  // For now, this i18nExpression depends on the slot context of the enclosing i18n block.
20304
20470
  // Later, we will modify this, and advance to a different point.
20305
- ops.push(createI18nExpressionOp(contextId, i18nOp.xref, i18nOp.xref, i18nOp.handle, expr, op.interpolation.i18nPlaceholders[i], resolutionTime, I18nExpressionFor.I18nText, '', expr.sourceSpan ?? op.sourceSpan));
20471
+ ops.push(createI18nExpressionOp(contextId, i18nOp.xref, i18nOp.xref, i18nOp.handle, expr, icuPlaceholder?.xref ?? null, op.interpolation.i18nPlaceholders[i] ?? null, resolutionTime, I18nExpressionFor.I18nText, '', expr.sourceSpan ?? op.sourceSpan));
20306
20472
  }
20307
20473
  OpList.replaceWithMany(op, ops);
20474
+ // If this interpolation is part of an ICU placeholder, add the strings and expressions to
20475
+ // the placeholder.
20476
+ if (icuPlaceholder !== undefined) {
20477
+ icuPlaceholder.strings = op.interpolation.strings;
20478
+ }
20308
20479
  break;
20309
20480
  }
20310
20481
  }
@@ -20413,6 +20584,7 @@ function parse(value) {
20413
20584
  break;
20414
20585
  case 58 /* Char.Colon */:
20415
20586
  if (!currentProp && parenDepth === 0 && quote === 0 /* Char.QuoteNone */) {
20587
+ // TODO: Do not hyphenate CSS custom property names like: `--intentionallyCamelCase`
20416
20588
  currentProp = hyphenate(value.substring(propStart, i - 1).trim());
20417
20589
  valueStart = i;
20418
20590
  }
@@ -20487,7 +20659,7 @@ function addNamesToView(unit, baseName, state, compatibility) {
20487
20659
  op.handlerFnName = sanitizeIdentifier(op.handlerFnName);
20488
20660
  break;
20489
20661
  case OpKind.Variable:
20490
- varNames.set(op.xref, getVariableName(op.variable, state));
20662
+ varNames.set(op.xref, getVariableName(unit, op.variable, state));
20491
20663
  break;
20492
20664
  case OpKind.RepeaterCreate:
20493
20665
  if (!(unit instanceof ViewCompilationUnit)) {
@@ -20542,15 +20714,23 @@ function addNamesToView(unit, baseName, state, compatibility) {
20542
20714
  });
20543
20715
  }
20544
20716
  }
20545
- function getVariableName(variable, state) {
20717
+ function getVariableName(unit, variable, state) {
20546
20718
  if (variable.name === null) {
20547
20719
  switch (variable.kind) {
20548
20720
  case SemanticVariableKind.Context:
20549
20721
  variable.name = `ctx_r${state.index++}`;
20550
20722
  break;
20551
20723
  case SemanticVariableKind.Identifier:
20552
- // TODO: Prefix increment and `_r` for compatiblity only.
20553
- variable.name = `${variable.identifier}_r${++state.index}`;
20724
+ if (unit.job.compatibility === CompatibilityMode.TemplateDefinitionBuilder) {
20725
+ // TODO: Prefix increment and `_r` are for compatiblity with the old naming scheme.
20726
+ // This has the potential to cause collisions when `ctx` is the identifier, so we need a
20727
+ // special check for that as well.
20728
+ const compatPrefix = variable.identifier === 'ctx' ? 'i' : '';
20729
+ variable.name = `${variable.identifier}_${compatPrefix}r${++state.index}`;
20730
+ }
20731
+ else {
20732
+ variable.name = `${variable.identifier}_i${state.index++}`;
20733
+ }
20554
20734
  break;
20555
20735
  default:
20556
20736
  // TODO: Prefix increment for compatibility only.
@@ -20747,17 +20927,27 @@ const CREATE_ORDERING = [
20747
20927
  * op kinds.
20748
20928
  */
20749
20929
  const UPDATE_ORDERING = [
20750
- { test: kindWithInterpolationTest(OpKind.HostProperty, true) },
20751
- { test: kindWithInterpolationTest(OpKind.HostProperty, false) },
20752
20930
  { test: kindTest(OpKind.StyleMap), transform: keepLast },
20753
20931
  { test: kindTest(OpKind.ClassMap), transform: keepLast },
20754
20932
  { test: kindTest(OpKind.StyleProp) },
20755
20933
  { test: kindTest(OpKind.ClassProp) },
20756
- { test: kindWithInterpolationTest(OpKind.Property, true) },
20757
20934
  { test: kindWithInterpolationTest(OpKind.Attribute, true) },
20935
+ { test: kindWithInterpolationTest(OpKind.Property, true) },
20758
20936
  { test: kindWithInterpolationTest(OpKind.Property, false) },
20759
20937
  { test: kindWithInterpolationTest(OpKind.Attribute, false) },
20760
20938
  ];
20939
+ /**
20940
+ * Host bindings have their own update ordering.
20941
+ */
20942
+ const UPDATE_HOST_ORDERING = [
20943
+ { test: kindWithInterpolationTest(OpKind.HostProperty, true) },
20944
+ { test: kindWithInterpolationTest(OpKind.HostProperty, false) },
20945
+ { test: kindTest(OpKind.Attribute) },
20946
+ { test: kindTest(OpKind.StyleMap), transform: keepLast },
20947
+ { test: kindTest(OpKind.ClassMap), transform: keepLast },
20948
+ { test: kindTest(OpKind.StyleProp) },
20949
+ { test: kindTest(OpKind.ClassProp) },
20950
+ ];
20761
20951
  /**
20762
20952
  * The set of all op kinds we handle in the reordering phase.
20763
20953
  */
@@ -20778,7 +20968,8 @@ function orderOps(job) {
20778
20968
  // Create mode:
20779
20969
  orderWithin(unit.create, CREATE_ORDERING);
20780
20970
  // Update mode:
20781
- orderWithin(unit.update, UPDATE_ORDERING);
20971
+ const ordering = unit.job.kind === CompilationJobKind.Host ? UPDATE_HOST_ORDERING : UPDATE_ORDERING;
20972
+ orderWithin(unit.update, ordering);
20782
20973
  }
20783
20974
  }
20784
20975
  /**
@@ -20833,21 +21024,39 @@ function keepLast(ops) {
20833
21024
  * class property.
20834
21025
  */
20835
21026
  function parseExtractedStyles(job) {
21027
+ const elements = new Map();
21028
+ for (const unit of job.units) {
21029
+ for (const op of unit.create) {
21030
+ if (isElementOrContainerOp(op)) {
21031
+ elements.set(op.xref, op);
21032
+ }
21033
+ }
21034
+ }
20836
21035
  for (const unit of job.units) {
20837
21036
  for (const op of unit.create) {
20838
21037
  if (op.kind === OpKind.ExtractedAttribute && op.bindingKind === BindingKind.Attribute &&
20839
21038
  isStringLiteral(op.expression)) {
21039
+ const target = elements.get(op.target);
21040
+ if (target !== undefined && target.kind === OpKind.Template &&
21041
+ target.templateKind === TemplateKind.Structural) {
21042
+ // TemplateDefinitionBuilder will not apply class and style bindings to structural
21043
+ // directives; instead, it will leave them as attributes.
21044
+ // (It's not clear what that would mean, anyway -- classes and styles on a structural
21045
+ // element should probably be a parse error.)
21046
+ // TODO: We may be able to remove this once Template Pipeline is the default.
21047
+ continue;
21048
+ }
20840
21049
  if (op.name === 'style') {
20841
21050
  const parsedStyles = parse(op.expression.value);
20842
21051
  for (let i = 0; i < parsedStyles.length - 1; i += 2) {
20843
- OpList.insertBefore(createExtractedAttributeOp(op.target, BindingKind.StyleProperty, parsedStyles[i], literal(parsedStyles[i + 1]), null, null, SecurityContext.STYLE), op);
21052
+ OpList.insertBefore(createExtractedAttributeOp(op.target, BindingKind.StyleProperty, null, parsedStyles[i], literal(parsedStyles[i + 1]), null, null, SecurityContext.STYLE), op);
20844
21053
  }
20845
21054
  OpList.remove(op);
20846
21055
  }
20847
21056
  else if (op.name === 'class') {
20848
21057
  const parsedClasses = op.expression.value.trim().split(/\s+/g);
20849
21058
  for (const parsedClass of parsedClasses) {
20850
- OpList.insertBefore(createExtractedAttributeOp(op.target, BindingKind.ClassName, parsedClass, null, null, null, SecurityContext.NONE), op);
21059
+ OpList.insertBefore(createExtractedAttributeOp(op.target, BindingKind.ClassName, null, parsedClass, null, null, null, SecurityContext.NONE), op);
20851
21060
  }
20852
21061
  OpList.remove(op);
20853
21062
  }
@@ -20871,15 +21080,6 @@ function removeContentSelectors(job) {
20871
21080
  OpList.remove(op);
20872
21081
  }
20873
21082
  break;
20874
- case OpKind.Projection:
20875
- // op.attributes is an array of [attr1-name, attr1-value, attr2-name, attr2-value, ...],
20876
- // find the "select" attribute and remove its name and corresponding value.
20877
- for (let i = op.attributes.length - 2; i >= 0; i -= 2) {
20878
- if (isSelectAttribute(op.attributes[i])) {
20879
- op.attributes.splice(i, 2);
20880
- }
20881
- }
20882
- break;
20883
21083
  }
20884
21084
  }
20885
21085
  }
@@ -21051,8 +21251,10 @@ function wrapTemplateWithI18n(unit, parentI18n) {
21051
21251
  // Only add i18n ops if they have not already been propagated to this template.
21052
21252
  if (unit.create.head.next?.kind !== OpKind.I18nStart) {
21053
21253
  const id = unit.job.allocateXrefId();
21054
- OpList.insertAfter(createI18nStartOp(id, parentI18n.message, parentI18n.root), unit.create.head);
21055
- OpList.insertBefore(createI18nEndOp(id), unit.create.tail);
21254
+ OpList.insertAfter(
21255
+ // Nested ng-template i18n start/end ops should not recieve source spans.
21256
+ createI18nStartOp(id, parentI18n.message, parentI18n.root, null), unit.create.head);
21257
+ OpList.insertBefore(createI18nEndOp(id, null), unit.create.tail);
21056
21258
  }
21057
21259
  }
21058
21260
 
@@ -21233,9 +21435,7 @@ function namespaceMath() {
21233
21435
  return call(Identifiers.namespaceMathML, [], null);
21234
21436
  }
21235
21437
  function advance(delta, sourceSpan) {
21236
- return call(Identifiers.advance, [
21237
- literal(delta),
21238
- ], sourceSpan);
21438
+ return call(Identifiers.advance, delta > 1 ? [literal(delta)] : [], sourceSpan);
21239
21439
  }
21240
21440
  function reference(slot) {
21241
21441
  return importExpr(Identifiers.reference).callFn([
@@ -21313,22 +21513,22 @@ function projectionDef(def) {
21313
21513
  }
21314
21514
  function projection(slot, projectionSlotIndex, attributes, sourceSpan) {
21315
21515
  const args = [literal(slot)];
21316
- if (projectionSlotIndex !== 0 || attributes.length > 0) {
21516
+ if (projectionSlotIndex !== 0 || attributes !== null) {
21317
21517
  args.push(literal(projectionSlotIndex));
21318
- if (attributes.length > 0) {
21319
- args.push(literalArr(attributes.map(attr => literal(attr))));
21518
+ if (attributes !== null) {
21519
+ args.push(attributes);
21320
21520
  }
21321
21521
  }
21322
21522
  return call(Identifiers.projection, args, sourceSpan);
21323
21523
  }
21324
- function i18nStart(slot, constIndex, subTemplateIndex) {
21524
+ function i18nStart(slot, constIndex, subTemplateIndex, sourceSpan) {
21325
21525
  const args = [literal(slot), literal(constIndex)];
21326
21526
  if (subTemplateIndex !== null) {
21327
21527
  args.push(literal(subTemplateIndex));
21328
21528
  }
21329
- return call(Identifiers.i18nStart, args, null);
21529
+ return call(Identifiers.i18nStart, args, sourceSpan);
21330
21530
  }
21331
- function repeaterCreate(slot, viewFnName, decls, vars, tag, constIndex, trackByFn, trackByUsesComponentInstance, emptyViewFnName, emptyDecls, emptyVars, sourceSpan) {
21531
+ function repeaterCreate(slot, viewFnName, decls, vars, tag, constIndex, trackByFn, trackByUsesComponentInstance, emptyViewFnName, emptyDecls, emptyVars, emptyTag, emptyConstIndex, sourceSpan) {
21332
21532
  const args = [
21333
21533
  literal(slot),
21334
21534
  variable(viewFnName),
@@ -21342,6 +21542,12 @@ function repeaterCreate(slot, viewFnName, decls, vars, tag, constIndex, trackByF
21342
21542
  args.push(literal(trackByUsesComponentInstance));
21343
21543
  if (emptyViewFnName !== null) {
21344
21544
  args.push(variable(emptyViewFnName), literal(emptyDecls), literal(emptyVars));
21545
+ if (emptyTag !== null || emptyConstIndex !== null) {
21546
+ args.push(literal(emptyTag));
21547
+ }
21548
+ if (emptyConstIndex !== null) {
21549
+ args.push(literal(emptyConstIndex));
21550
+ }
21345
21551
  }
21346
21552
  }
21347
21553
  return call(Identifiers.repeaterCreate, args, sourceSpan);
@@ -21352,15 +21558,15 @@ function repeater(collection, sourceSpan) {
21352
21558
  function deferWhen(prefetch, expr, sourceSpan) {
21353
21559
  return call(prefetch ? Identifiers.deferPrefetchWhen : Identifiers.deferWhen, [expr], sourceSpan);
21354
21560
  }
21355
- function i18n(slot, constIndex, subTemplateIndex) {
21561
+ function i18n(slot, constIndex, subTemplateIndex, sourceSpan) {
21356
21562
  const args = [literal(slot), literal(constIndex)];
21357
21563
  if (subTemplateIndex) {
21358
21564
  args.push(literal(subTemplateIndex));
21359
21565
  }
21360
- return call(Identifiers.i18n, args, null);
21566
+ return call(Identifiers.i18n, args, sourceSpan);
21361
21567
  }
21362
- function i18nEnd() {
21363
- return call(Identifiers.i18nEnd, [], null);
21568
+ function i18nEnd(endSourceSpan) {
21569
+ return call(Identifiers.i18nEnd, [], endSourceSpan);
21364
21570
  }
21365
21571
  function i18nAttributes(slot, i18nAttributesConfig) {
21366
21572
  const args = [literal(slot), literal(i18nAttributesConfig)];
@@ -21373,10 +21579,13 @@ function property(name, expression, sanitizer, sourceSpan) {
21373
21579
  }
21374
21580
  return call(Identifiers.property, args, sourceSpan);
21375
21581
  }
21376
- function attribute(name, expression, sanitizer) {
21582
+ function attribute(name, expression, sanitizer, namespace) {
21377
21583
  const args = [literal(name), expression];
21378
- if (sanitizer !== null) {
21379
- args.push(sanitizer);
21584
+ if (sanitizer !== null || namespace !== null) {
21585
+ args.push(sanitizer ?? literal(null));
21586
+ }
21587
+ if (namespace !== null) {
21588
+ args.push(literal(namespace));
21380
21589
  }
21381
21590
  return call(Identifiers.attribute, args, null);
21382
21591
  }
@@ -21720,6 +21929,31 @@ function reify(job) {
21720
21929
  reifyUpdateOperations(unit, unit.update);
21721
21930
  }
21722
21931
  }
21932
+ /**
21933
+ * This function can be used a sanity check -- it walks every expression in the const pool, and
21934
+ * every expression reachable from an op, and makes sure that there are no IR expressions
21935
+ * left. This is nice to use for debugging mysterious failures where an IR expression cannot be
21936
+ * output from the output AST code.
21937
+ */
21938
+ function ensureNoIrForDebug(job) {
21939
+ for (const stmt of job.pool.statements) {
21940
+ transformExpressionsInStatement(stmt, expr => {
21941
+ if (isIrExpression(expr)) {
21942
+ throw new Error(`AssertionError: IR expression found during reify: ${ExpressionKind[expr.kind]}`);
21943
+ }
21944
+ return expr;
21945
+ }, VisitorContextFlag.None);
21946
+ }
21947
+ for (const unit of job.units) {
21948
+ for (const op of unit.ops()) {
21949
+ visitExpressionsInOp(op, expr => {
21950
+ if (isIrExpression(expr)) {
21951
+ throw new Error(`AssertionError: IR expression found during reify: ${ExpressionKind[expr.kind]}`);
21952
+ }
21953
+ });
21954
+ }
21955
+ }
21956
+ }
21723
21957
  function reifyCreateOperations(unit, ops) {
21724
21958
  for (const op of ops) {
21725
21959
  transformExpressionsInOp(op, reifyIrExpression, VisitorContextFlag.None);
@@ -21728,31 +21962,31 @@ function reifyCreateOperations(unit, ops) {
21728
21962
  OpList.replace(op, text(op.handle.slot, op.initialValue, op.sourceSpan));
21729
21963
  break;
21730
21964
  case OpKind.ElementStart:
21731
- OpList.replace(op, elementStart(op.handle.slot, op.tag, op.attributes, op.localRefs, op.sourceSpan));
21965
+ OpList.replace(op, elementStart(op.handle.slot, op.tag, op.attributes, op.localRefs, op.startSourceSpan));
21732
21966
  break;
21733
21967
  case OpKind.Element:
21734
- OpList.replace(op, element(op.handle.slot, op.tag, op.attributes, op.localRefs, op.sourceSpan));
21968
+ OpList.replace(op, element(op.handle.slot, op.tag, op.attributes, op.localRefs, op.wholeSourceSpan));
21735
21969
  break;
21736
21970
  case OpKind.ElementEnd:
21737
21971
  OpList.replace(op, elementEnd(op.sourceSpan));
21738
21972
  break;
21739
21973
  case OpKind.ContainerStart:
21740
- OpList.replace(op, elementContainerStart(op.handle.slot, op.attributes, op.localRefs, op.sourceSpan));
21974
+ OpList.replace(op, elementContainerStart(op.handle.slot, op.attributes, op.localRefs, op.startSourceSpan));
21741
21975
  break;
21742
21976
  case OpKind.Container:
21743
- OpList.replace(op, elementContainer(op.handle.slot, op.attributes, op.localRefs, op.sourceSpan));
21977
+ OpList.replace(op, elementContainer(op.handle.slot, op.attributes, op.localRefs, op.wholeSourceSpan));
21744
21978
  break;
21745
21979
  case OpKind.ContainerEnd:
21746
21980
  OpList.replace(op, elementContainerEnd());
21747
21981
  break;
21748
21982
  case OpKind.I18nStart:
21749
- OpList.replace(op, i18nStart(op.handle.slot, op.messageIndex, op.subTemplateIndex));
21983
+ OpList.replace(op, i18nStart(op.handle.slot, op.messageIndex, op.subTemplateIndex, op.sourceSpan));
21750
21984
  break;
21751
21985
  case OpKind.I18nEnd:
21752
- OpList.replace(op, i18nEnd());
21986
+ OpList.replace(op, i18nEnd(op.sourceSpan));
21753
21987
  break;
21754
21988
  case OpKind.I18n:
21755
- OpList.replace(op, i18n(op.handle.slot, op.messageIndex, op.subTemplateIndex));
21989
+ OpList.replace(op, i18n(op.handle.slot, op.messageIndex, op.subTemplateIndex, op.sourceSpan));
21756
21990
  break;
21757
21991
  case OpKind.I18nAttributes:
21758
21992
  if (op.i18nAttributesConfig === null) {
@@ -21768,7 +22002,7 @@ function reifyCreateOperations(unit, ops) {
21768
22002
  throw new Error(`AssertionError: local refs array should have been extracted into a constant`);
21769
22003
  }
21770
22004
  const childView = unit.job.views.get(op.xref);
21771
- OpList.replace(op, template(op.handle.slot, variable(childView.fnName), childView.decls, childView.vars, op.tag, op.attributes, op.localRefs, op.sourceSpan));
22005
+ OpList.replace(op, template(op.handle.slot, variable(childView.fnName), childView.decls, childView.vars, op.tag, op.attributes, op.localRefs, op.startSourceSpan));
21772
22006
  break;
21773
22007
  case OpKind.DisableBindings:
21774
22008
  OpList.replace(op, disableBindings());
@@ -21783,7 +22017,7 @@ function reifyCreateOperations(unit, ops) {
21783
22017
  const listenerFn = reifyListenerHandler(unit, op.handlerFnName, op.handlerOps, op.consumesDollarEvent);
21784
22018
  const eventTargetResolver = op.eventTarget ? GLOBAL_TARGET_RESOLVERS$1.get(op.eventTarget) : null;
21785
22019
  if (eventTargetResolver === undefined) {
21786
- throw new Error(`AssertionError: unknown event target ${op.eventTarget}`);
22020
+ throw new Error(`Unexpected global target '${op.eventTarget}' defined for '${op.name}' event. Supported list of global targets: window,document,body.`);
21787
22021
  }
21788
22022
  OpList.replace(op, listener(op.name, listenerFn, eventTargetResolver, op.hostListener && op.isAnimationListener, op.sourceSpan));
21789
22023
  break;
@@ -21870,7 +22104,7 @@ function reifyCreateOperations(unit, ops) {
21870
22104
  emptyDecls = emptyView.decls;
21871
22105
  emptyVars = emptyView.vars;
21872
22106
  }
21873
- OpList.replace(op, repeaterCreate(op.handle.slot, repeaterView.fnName, op.decls, op.vars, op.tag, op.attributes, op.trackByFn, op.usesComponentInstance, emptyViewFnName, emptyDecls, emptyVars, op.sourceSpan));
22107
+ OpList.replace(op, repeaterCreate(op.handle.slot, repeaterView.fnName, op.decls, op.vars, op.tag, op.attributes, op.trackByFn, op.usesComponentInstance, emptyViewFnName, emptyDecls, emptyVars, op.emptyTag, op.emptyAttributes, op.wholeSourceSpan));
21874
22108
  break;
21875
22109
  case OpKind.Statement:
21876
22110
  // Pass statement operations directly through.
@@ -21936,7 +22170,7 @@ function reifyUpdateOperations(_unit, ops) {
21936
22170
  OpList.replace(op, attributeInterpolate(op.name, op.expression.strings, op.expression.expressions, op.sanitizer, op.sourceSpan));
21937
22171
  }
21938
22172
  else {
21939
- OpList.replace(op, attribute(op.name, op.expression, op.sanitizer));
22173
+ OpList.replace(op, attribute(op.name, op.expression, op.sanitizer, op.namespace));
21940
22174
  }
21941
22175
  break;
21942
22176
  case OpKind.HostProperty:
@@ -22125,42 +22359,6 @@ function removeUnusedI18nAttributesOps(job) {
22125
22359
  }
22126
22360
  }
22127
22361
 
22128
- /**
22129
- * Inside the body of a repeater, certain context variables (such as `$first`) are ambiently
22130
- * available. This phase finds those variable usages, and replaces them with the appropriate
22131
- * expression.
22132
- */
22133
- function generateRepeaterDerivedVars(job) {
22134
- const repeaters = new Map();
22135
- for (const unit of job.units) {
22136
- for (const op of unit.ops()) {
22137
- if (op.kind === OpKind.RepeaterCreate) {
22138
- repeaters.set(op.xref, op);
22139
- }
22140
- }
22141
- }
22142
- for (const unit of job.units) {
22143
- for (const op of unit.ops()) {
22144
- transformExpressionsInOp(op, expr => {
22145
- if (!(expr instanceof DerivedRepeaterVarExpr)) {
22146
- return expr;
22147
- }
22148
- const repeaterOp = repeaters.get(expr.xref);
22149
- switch (expr.identity) {
22150
- case DerivedRepeaterVarIdentity.First:
22151
- return new BinaryOperatorExpr(BinaryOperator.Identical, new LexicalReadExpr(repeaterOp.varNames.$index), literal(0));
22152
- case DerivedRepeaterVarIdentity.Last:
22153
- return new BinaryOperatorExpr(BinaryOperator.Identical, new LexicalReadExpr(repeaterOp.varNames.$index), new BinaryOperatorExpr(BinaryOperator.Minus, new LexicalReadExpr(repeaterOp.varNames.$count), literal(1)));
22154
- case DerivedRepeaterVarIdentity.Even:
22155
- return new BinaryOperatorExpr(BinaryOperator.Identical, new BinaryOperatorExpr(BinaryOperator.Modulo, new LexicalReadExpr(repeaterOp.varNames.$index), literal(2)), literal(0));
22156
- case DerivedRepeaterVarIdentity.Odd:
22157
- return new BinaryOperatorExpr(BinaryOperator.NotIdentical, new BinaryOperatorExpr(BinaryOperator.Modulo, new LexicalReadExpr(repeaterOp.varNames.$index), literal(2)), literal(0));
22158
- }
22159
- }, VisitorContextFlag.None);
22160
- }
22161
- }
22162
- }
22163
-
22164
22362
  /**
22165
22363
  * Resolves `ir.ContextExpr` expressions (which represent embedded view or component contexts) to
22166
22364
  * either the `ctx` parameter to component functions (for the current view context) or to variables
@@ -22500,6 +22698,7 @@ function resolveI18nExpressionPlaceholders(job) {
22500
22698
  // Record all of the i18n context ops, and the sub-template index for each i18n op.
22501
22699
  const subTemplateIndicies = new Map();
22502
22700
  const i18nContexts = new Map();
22701
+ const icuPlaceholders = new Map();
22503
22702
  for (const unit of job.units) {
22504
22703
  for (const op of unit.create) {
22505
22704
  switch (op.kind) {
@@ -22509,6 +22708,9 @@ function resolveI18nExpressionPlaceholders(job) {
22509
22708
  case OpKind.I18nContext:
22510
22709
  i18nContexts.set(op.xref, op);
22511
22710
  break;
22711
+ case OpKind.IcuPlaceholder:
22712
+ icuPlaceholders.set(op.xref, op);
22713
+ break;
22512
22714
  }
22513
22715
  }
22514
22716
  }
@@ -22522,76 +22724,32 @@ function resolveI18nExpressionPlaceholders(job) {
22522
22724
  for (const unit of job.units) {
22523
22725
  for (const op of unit.update) {
22524
22726
  if (op.kind === OpKind.I18nExpression) {
22525
- const i18nContext = i18nContexts.get(op.context);
22526
22727
  const index = expressionIndices.get(referenceIndex(op)) || 0;
22527
22728
  const subTemplateIndex = subTemplateIndicies.get(op.i18nOwner) ?? null;
22528
- // Add the expression index in the appropriate params map.
22529
- const params = op.resolutionTime === I18nParamResolutionTime.Creation ?
22530
- i18nContext.params :
22531
- i18nContext.postprocessingParams;
22532
- const values = params.get(op.i18nPlaceholder) || [];
22533
- values.push({
22729
+ const value = {
22534
22730
  value: index,
22535
22731
  subTemplateIndex: subTemplateIndex,
22536
22732
  flags: I18nParamValueFlags.ExpressionIndex
22537
- });
22538
- params.set(op.i18nPlaceholder, values);
22733
+ };
22734
+ updatePlaceholder(op, value, i18nContexts, icuPlaceholders);
22539
22735
  expressionIndices.set(referenceIndex(op), index + 1);
22540
22736
  }
22541
22737
  }
22542
22738
  }
22543
22739
  }
22544
-
22545
- /**
22546
- * Resolves placeholders for element tags inside of an ICU.
22547
- */
22548
- function resolveI18nIcuPlaceholders(job) {
22549
- for (const unit of job.units) {
22550
- for (const op of unit.create) {
22551
- if (op.kind === OpKind.I18nContext && op.contextKind === I18nContextKind.Icu) {
22552
- for (const node of op.message.nodes) {
22553
- node.visit(new ResolveIcuPlaceholdersVisitor(op.postprocessingParams));
22554
- }
22555
- }
22556
- }
22740
+ function updatePlaceholder(op, value, i18nContexts, icuPlaceholders) {
22741
+ if (op.i18nPlaceholder !== null) {
22742
+ const i18nContext = i18nContexts.get(op.context);
22743
+ const params = op.resolutionTime === I18nParamResolutionTime.Creation ?
22744
+ i18nContext.params :
22745
+ i18nContext.postprocessingParams;
22746
+ const values = params.get(op.i18nPlaceholder) || [];
22747
+ values.push(value);
22748
+ params.set(op.i18nPlaceholder, values);
22557
22749
  }
22558
- }
22559
- /**
22560
- * Visitor for i18n AST that resolves ICU params into the given map.
22561
- */
22562
- class ResolveIcuPlaceholdersVisitor extends RecurseVisitor {
22563
- constructor(params) {
22564
- super();
22565
- this.params = params;
22566
- }
22567
- visitContainerPlaceholder(placeholder) {
22568
- // Add the start and end source span for container placeholders. These need to be recorded for
22569
- // elements inside ICUs. The slots for the nodes were recorded separately under the i18n
22570
- // block's context as part of the `resolveI18nElementPlaceholders` phase.
22571
- if (placeholder.startName && placeholder.startSourceSpan &&
22572
- !this.params.has(placeholder.startName)) {
22573
- this.params.set(placeholder.startName, [{
22574
- value: placeholder.startSourceSpan?.toString(),
22575
- subTemplateIndex: null,
22576
- flags: I18nParamValueFlags.None
22577
- }]);
22578
- }
22579
- if (placeholder.closeName && placeholder.endSourceSpan &&
22580
- !this.params.has(placeholder.closeName)) {
22581
- this.params.set(placeholder.closeName, [{
22582
- value: placeholder.endSourceSpan?.toString(),
22583
- subTemplateIndex: null,
22584
- flags: I18nParamValueFlags.None
22585
- }]);
22586
- }
22587
- }
22588
- visitTagPlaceholder(placeholder) {
22589
- super.visitTagPlaceholder(placeholder);
22590
- this.visitContainerPlaceholder(placeholder);
22591
- }
22592
- visitBlockPlaceholder(placeholder) {
22593
- super.visitBlockPlaceholder(placeholder);
22594
- this.visitContainerPlaceholder(placeholder);
22750
+ if (op.icuPlaceholder !== null) {
22751
+ const icuPlaceholderOp = icuPlaceholders.get(op.icuPlaceholder);
22752
+ icuPlaceholderOp?.expressionPlaceholders.push(value);
22595
22753
  }
22596
22754
  }
22597
22755
 
@@ -23043,6 +23201,9 @@ function generateTrackFns(job) {
23043
23201
  // Find all component context reads.
23044
23202
  let usesComponentContext = false;
23045
23203
  op.track = transformExpressionsInExpression(op.track, expr => {
23204
+ if (expr instanceof PipeBindingExpr || expr instanceof PipeBindingVariadicExpr) {
23205
+ throw new Error(`Illegal State: Pipes are not allowed in this context`);
23206
+ }
23046
23207
  if (expr instanceof TrackContextExpr) {
23047
23208
  usesComponentContext = true;
23048
23209
  return variable('this');
@@ -23266,7 +23427,14 @@ function varsUsedByOp(op) {
23266
23427
  return op.interpolation.expressions.length;
23267
23428
  case OpKind.I18nExpression:
23268
23429
  case OpKind.Conditional:
23430
+ case OpKind.DeferWhen:
23269
23431
  return 1;
23432
+ case OpKind.RepeaterCreate:
23433
+ // Repeaters may require an extra variable binding slot, if they have an empty view, for the
23434
+ // empty block tracking.
23435
+ // TODO: It's a bit odd to have a create mode instruction consume variable slots. Maybe we can
23436
+ // find a way to use the Repeater update op instead.
23437
+ return op.emptyView ? 1 : 0;
23270
23438
  default:
23271
23439
  throw new Error(`Unhandled op: ${OpKind[op.kind]}`);
23272
23440
  }
@@ -23722,12 +23890,13 @@ function wrapI18nIcus(job) {
23722
23890
  case OpKind.IcuStart:
23723
23891
  if (currentI18nOp === null) {
23724
23892
  addedI18nId = job.allocateXrefId();
23725
- OpList.insertBefore(createI18nStartOp(addedI18nId, op.message), op);
23893
+ // ICU i18n start/end ops should not recieve source spans.
23894
+ OpList.insertBefore(createI18nStartOp(addedI18nId, op.message, undefined, null), op);
23726
23895
  }
23727
23896
  break;
23728
23897
  case OpKind.IcuEnd:
23729
23898
  if (addedI18nId !== null) {
23730
- OpList.insertAfter(createI18nEndOp(addedI18nId), op);
23899
+ OpList.insertAfter(createI18nEndOp(addedI18nId, null), op);
23731
23900
  addedI18nId = null;
23732
23901
  }
23733
23902
  break;
@@ -23750,6 +23919,7 @@ const phases = [
23750
23919
  { kind: CompilationJobKind.Tmpl, fn: emitNamespaceChanges },
23751
23920
  { kind: CompilationJobKind.Tmpl, fn: propagateI18nBlocks },
23752
23921
  { kind: CompilationJobKind.Tmpl, fn: wrapI18nIcus },
23922
+ { kind: CompilationJobKind.Both, fn: deduplicateTextBindings },
23753
23923
  { kind: CompilationJobKind.Both, fn: specializeStyleBindings },
23754
23924
  { kind: CompilationJobKind.Both, fn: specializeBindings },
23755
23925
  { kind: CompilationJobKind.Both, fn: extractAttributes },
@@ -23773,7 +23943,6 @@ const phases = [
23773
23943
  { kind: CompilationJobKind.Tmpl, fn: saveAndRestoreView },
23774
23944
  { kind: CompilationJobKind.Both, fn: deleteAnyCasts },
23775
23945
  { kind: CompilationJobKind.Both, fn: resolveDollarEvent },
23776
- { kind: CompilationJobKind.Tmpl, fn: generateRepeaterDerivedVars },
23777
23946
  { kind: CompilationJobKind.Tmpl, fn: generateTrackVariables },
23778
23947
  { kind: CompilationJobKind.Both, fn: resolveNames },
23779
23948
  { kind: CompilationJobKind.Tmpl, fn: resolveDeferTargetNames },
@@ -23788,7 +23957,6 @@ const phases = [
23788
23957
  { kind: CompilationJobKind.Tmpl, fn: createDeferDepsFns },
23789
23958
  { kind: CompilationJobKind.Tmpl, fn: resolveI18nElementPlaceholders },
23790
23959
  { kind: CompilationJobKind.Tmpl, fn: resolveI18nExpressionPlaceholders },
23791
- { kind: CompilationJobKind.Tmpl, fn: resolveI18nIcuPlaceholders },
23792
23960
  { kind: CompilationJobKind.Tmpl, fn: extractI18nMessages },
23793
23961
  { kind: CompilationJobKind.Tmpl, fn: generateTrackFns },
23794
23962
  { kind: CompilationJobKind.Tmpl, fn: collectI18nConsts },
@@ -23948,7 +24116,7 @@ function ingestHostBinding(input, bindingParser, constantPool) {
23948
24116
  const securityContexts = bindingParser
23949
24117
  .calcPossibleSecurityContexts(input.componentSelector, property.name, bindingKind === BindingKind.Attribute)
23950
24118
  .filter(context => context !== SecurityContext.NONE);
23951
- ingestHostProperty(job, property, bindingKind, false, securityContexts);
24119
+ ingestHostProperty(job, property, bindingKind, securityContexts);
23952
24120
  }
23953
24121
  for (const [name, expr] of Object.entries(input.attributes) ?? []) {
23954
24122
  const securityContexts = bindingParser.calcPossibleSecurityContexts(input.componentSelector, name, true)
@@ -23962,7 +24130,7 @@ function ingestHostBinding(input, bindingParser, constantPool) {
23962
24130
  }
23963
24131
  // TODO: We should refactor the parser to use the same types and structures for host bindings as
23964
24132
  // with ordinary components. This would allow us to share a lot more ingestion code.
23965
- function ingestHostProperty(job, property, bindingKind, isTextAttribute, securityContexts) {
24133
+ function ingestHostProperty(job, property, bindingKind, securityContexts) {
23966
24134
  let expression;
23967
24135
  const ast = property.expression.ast;
23968
24136
  if (ast instanceof Interpolation$1) {
@@ -23971,20 +24139,21 @@ function ingestHostProperty(job, property, bindingKind, isTextAttribute, securit
23971
24139
  else {
23972
24140
  expression = convertAst(ast, job, property.sourceSpan);
23973
24141
  }
23974
- job.root.update.push(createBindingOp(job.root.xref, bindingKind, property.name, expression, null, securityContexts, isTextAttribute, false, null, /* TODO: How do Host bindings handle i18n attrs? */ null, property.sourceSpan));
24142
+ 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));
23975
24143
  }
23976
24144
  function ingestHostAttribute(job, name, value, securityContexts) {
23977
- const attrBinding = createBindingOp(job.root.xref, BindingKind.Attribute, name, value, null, securityContexts, true, false, null,
24145
+ const attrBinding = createBindingOp(job.root.xref, BindingKind.Attribute, name, value, null, securityContexts,
24146
+ /* Host attributes should always be extracted to const hostAttrs, even if they are not
24147
+ *strictly* text literals */
24148
+ true, false, null,
23978
24149
  /* TODO */ null,
23979
- /* TODO: host attribute source spans */ null);
24150
+ /** TODO: May be null? */ value.sourceSpan);
23980
24151
  job.root.update.push(attrBinding);
23981
24152
  }
23982
24153
  function ingestHostEvent(job, event) {
23983
24154
  const [phase, target] = event.type === 0 /* e.ParsedEventType.Regular */ ? [null, event.targetOrPhase] :
23984
24155
  [event.targetOrPhase, null];
23985
- const eventBinding = createListenerOp(job.root.xref, new SlotHandle(), event.name, null, [], phase, target, true, event.sourceSpan);
23986
- // TODO: Can this be a chain?
23987
- eventBinding.handlerOps.push(createStatementOp(new ReturnStatement(convertAst(event.handler.ast, job, event.sourceSpan), event.handlerSpan)));
24156
+ const eventBinding = createListenerOp(job.root.xref, new SlotHandle(), event.name, null, makeListenerHandlerOps(job.root, event.handler, event.handlerSpan), phase, target, true, event.sourceSpan);
23988
24157
  job.root.create.push(eventBinding);
23989
24158
  }
23990
24159
  /**
@@ -24002,10 +24171,10 @@ function ingestNodes(unit, template) {
24002
24171
  ingestContent(unit, node);
24003
24172
  }
24004
24173
  else if (node instanceof Text$3) {
24005
- ingestText(unit, node);
24174
+ ingestText(unit, node, null);
24006
24175
  }
24007
24176
  else if (node instanceof BoundText) {
24008
- ingestBoundText(unit, node);
24177
+ ingestBoundText(unit, node, null);
24009
24178
  }
24010
24179
  else if (node instanceof IfBlock) {
24011
24180
  ingestIfBlock(unit, node);
@@ -24037,7 +24206,7 @@ function ingestElement(unit, element) {
24037
24206
  }
24038
24207
  const id = unit.job.allocateXrefId();
24039
24208
  const [namespaceKey, elementName] = splitNsName(element.name);
24040
- const startOp = createElementStartOp(elementName, id, namespaceForKey(namespaceKey), element.i18n instanceof TagPlaceholder ? element.i18n : undefined, element.startSourceSpan);
24209
+ const startOp = createElementStartOp(elementName, id, namespaceForKey(namespaceKey), element.i18n instanceof TagPlaceholder ? element.i18n : undefined, element.startSourceSpan, element.sourceSpan);
24041
24210
  unit.create.push(startOp);
24042
24211
  ingestElementBindings(unit, startOp, element);
24043
24212
  ingestReferences(startOp, element);
@@ -24045,7 +24214,7 @@ function ingestElement(unit, element) {
24045
24214
  let i18nBlockId = null;
24046
24215
  if (element.i18n instanceof Message) {
24047
24216
  i18nBlockId = unit.job.allocateXrefId();
24048
- unit.create.push(createI18nStartOp(i18nBlockId, element.i18n));
24217
+ unit.create.push(createI18nStartOp(i18nBlockId, element.i18n, undefined, element.startSourceSpan));
24049
24218
  }
24050
24219
  ingestNodes(unit, element.children);
24051
24220
  // The source span for the end op is typically the element closing tag. However, if no closing tag
@@ -24057,7 +24226,7 @@ function ingestElement(unit, element) {
24057
24226
  unit.create.push(endOp);
24058
24227
  // If there is an i18n message associated with this element, insert i18n start and end ops.
24059
24228
  if (i18nBlockId !== null) {
24060
- OpList.insertBefore(createI18nEndOp(i18nBlockId), endOp);
24229
+ OpList.insertBefore(createI18nEndOp(i18nBlockId, element.endSourceSpan ?? element.startSourceSpan), endOp);
24061
24230
  }
24062
24231
  }
24063
24232
  /**
@@ -24080,7 +24249,7 @@ function ingestTemplate(unit, tmpl) {
24080
24249
  '' :
24081
24250
  prefixWithNamespace(tagNameWithoutNamespace, namespace);
24082
24251
  const templateKind = isPlainTemplate(tmpl) ? TemplateKind.NgTemplate : TemplateKind.Structural;
24083
- const templateOp = createTemplateOp(childView.xref, templateKind, tagNameWithoutNamespace, functionNameSuffix, namespace, i18nPlaceholder, tmpl.startSourceSpan);
24252
+ const templateOp = createTemplateOp(childView.xref, templateKind, tagNameWithoutNamespace, functionNameSuffix, namespace, i18nPlaceholder, tmpl.startSourceSpan, tmpl.sourceSpan);
24084
24253
  unit.create.push(templateOp);
24085
24254
  ingestTemplateBindings(unit, templateOp, tmpl, templateKind);
24086
24255
  ingestReferences(templateOp, tmpl);
@@ -24093,8 +24262,8 @@ function ingestTemplate(unit, tmpl) {
24093
24262
  // element/template the directive is placed on.
24094
24263
  if (templateKind === TemplateKind.NgTemplate && tmpl.i18n instanceof Message) {
24095
24264
  const id = unit.job.allocateXrefId();
24096
- OpList.insertAfter(createI18nStartOp(id, tmpl.i18n), childView.create.head);
24097
- OpList.insertBefore(createI18nEndOp(id), childView.create.tail);
24265
+ OpList.insertAfter(createI18nStartOp(id, tmpl.i18n, undefined, tmpl.startSourceSpan), childView.create.head);
24266
+ OpList.insertBefore(createI18nEndOp(id, tmpl.endSourceSpan ?? tmpl.startSourceSpan), childView.create.tail);
24098
24267
  }
24099
24268
  }
24100
24269
  /**
@@ -24104,8 +24273,7 @@ function ingestContent(unit, content) {
24104
24273
  if (content.i18n !== undefined && !(content.i18n instanceof TagPlaceholder)) {
24105
24274
  throw Error(`Unhandled i18n metadata type for element: ${content.i18n.constructor.name}`);
24106
24275
  }
24107
- const attrs = content.attributes.flatMap(a => [a.name, a.value]);
24108
- const op = createProjectionOp(unit.job.allocateXrefId(), content.selector, content.i18n, attrs, content.sourceSpan);
24276
+ const op = createProjectionOp(unit.job.allocateXrefId(), content.selector, content.i18n, content.sourceSpan);
24109
24277
  for (const attr of content.attributes) {
24110
24278
  const securityContext = domSchema.securityContext(content.name, attr.name, true);
24111
24279
  unit.update.push(createBindingOp(op.xref, BindingKind.Attribute, attr.name, literal(attr.value), null, securityContext, true, false, null, asMessage(attr.i18n), attr.sourceSpan));
@@ -24115,13 +24283,13 @@ function ingestContent(unit, content) {
24115
24283
  /**
24116
24284
  * Ingest a literal text node from the AST into the given `ViewCompilation`.
24117
24285
  */
24118
- function ingestText(unit, text) {
24119
- unit.create.push(createTextOp(unit.job.allocateXrefId(), text.value, text.sourceSpan));
24286
+ function ingestText(unit, text, icuPlaceholder) {
24287
+ unit.create.push(createTextOp(unit.job.allocateXrefId(), text.value, icuPlaceholder, text.sourceSpan));
24120
24288
  }
24121
24289
  /**
24122
24290
  * Ingest an interpolated text node from the AST into the given `ViewCompilation`.
24123
24291
  */
24124
- function ingestBoundText(unit, text, i18nPlaceholders) {
24292
+ function ingestBoundText(unit, text, icuPlaceholder) {
24125
24293
  let value = text.value;
24126
24294
  if (value instanceof ASTWithSource) {
24127
24295
  value = value.ast;
@@ -24132,19 +24300,16 @@ function ingestBoundText(unit, text, i18nPlaceholders) {
24132
24300
  if (text.i18n !== undefined && !(text.i18n instanceof Container)) {
24133
24301
  throw Error(`Unhandled i18n metadata type for text interpolation: ${text.i18n?.constructor.name}`);
24134
24302
  }
24135
- if (i18nPlaceholders === undefined) {
24136
- // TODO: We probably can just use the placeholders field, instead of walking the AST.
24137
- i18nPlaceholders = text.i18n instanceof Container ?
24138
- text.i18n.children
24139
- .filter((node) => node instanceof Placeholder)
24140
- .map(placeholder => placeholder.name) :
24141
- [];
24142
- }
24303
+ const i18nPlaceholders = text.i18n instanceof Container ?
24304
+ text.i18n.children
24305
+ .filter((node) => node instanceof Placeholder)
24306
+ .map(placeholder => placeholder.name) :
24307
+ [];
24143
24308
  if (i18nPlaceholders.length > 0 && i18nPlaceholders.length !== value.expressions.length) {
24144
24309
  throw Error(`Unexpected number of i18n placeholders (${value.expressions.length}) for BoundText with ${value.expressions.length} expressions`);
24145
24310
  }
24146
24311
  const textXref = unit.job.allocateXrefId();
24147
- unit.create.push(createTextOp(textXref, '', text.sourceSpan));
24312
+ unit.create.push(createTextOp(textXref, '', icuPlaceholder, text.sourceSpan));
24148
24313
  // TemplateDefinitionBuilder does not generate source maps for sub-expressions inside an
24149
24314
  // interpolation. We copy that behavior in compatibility mode.
24150
24315
  // TODO: is it actually correct to generate these extra maps in modern mode?
@@ -24177,7 +24342,7 @@ function ingestIfBlock(unit, ifBlock) {
24177
24342
  }
24178
24343
  ifCaseI18nMeta = ifCase.i18n;
24179
24344
  }
24180
- const templateOp = createTemplateOp(cView.xref, TemplateKind.Block, tagName, 'Conditional', Namespace.HTML, ifCaseI18nMeta, ifCase.sourceSpan);
24345
+ const templateOp = createTemplateOp(cView.xref, TemplateKind.Block, tagName, 'Conditional', Namespace.HTML, ifCaseI18nMeta, ifCase.startSourceSpan, ifCase.sourceSpan);
24181
24346
  unit.create.push(templateOp);
24182
24347
  if (firstXref === null) {
24183
24348
  firstXref = cView.xref;
@@ -24195,6 +24360,10 @@ function ingestIfBlock(unit, ifBlock) {
24195
24360
  * Ingest an `@switch` block into the given `ViewCompilation`.
24196
24361
  */
24197
24362
  function ingestSwitchBlock(unit, switchBlock) {
24363
+ // Don't ingest empty switches since they won't render anything.
24364
+ if (switchBlock.cases.length === 0) {
24365
+ return;
24366
+ }
24198
24367
  let firstXref = null;
24199
24368
  let firstSlotHandle = null;
24200
24369
  let conditions = [];
@@ -24207,7 +24376,7 @@ function ingestSwitchBlock(unit, switchBlock) {
24207
24376
  }
24208
24377
  switchCaseI18nMeta = switchCase.i18n;
24209
24378
  }
24210
- const templateOp = createTemplateOp(cView.xref, TemplateKind.Block, null, 'Case', Namespace.HTML, switchCaseI18nMeta, switchCase.sourceSpan);
24379
+ const templateOp = createTemplateOp(cView.xref, TemplateKind.Block, null, 'Case', Namespace.HTML, switchCaseI18nMeta, switchCase.startSourceSpan, switchCase.sourceSpan);
24211
24380
  unit.create.push(templateOp);
24212
24381
  if (firstXref === null) {
24213
24382
  firstXref = cView.xref;
@@ -24232,7 +24401,7 @@ function ingestDeferView(unit, suffix, i18nMeta, children, sourceSpan) {
24232
24401
  }
24233
24402
  const secondaryView = unit.job.allocateView(unit.xref);
24234
24403
  ingestNodes(secondaryView, children);
24235
- const templateOp = createTemplateOp(secondaryView.xref, TemplateKind.Block, null, `Defer${suffix}`, Namespace.HTML, i18nMeta, sourceSpan);
24404
+ const templateOp = createTemplateOp(secondaryView.xref, TemplateKind.Block, null, `Defer${suffix}`, Namespace.HTML, i18nMeta, sourceSpan, sourceSpan);
24236
24405
  unit.create.push(templateOp);
24237
24406
  return templateOp;
24238
24407
  }
@@ -24310,6 +24479,11 @@ function ingestDeferBlock(unit, deferBlock) {
24310
24479
  deferOnOps.push(deferOnOp);
24311
24480
  }
24312
24481
  if (triggers.when !== undefined) {
24482
+ if (triggers.when.value instanceof Interpolation$1) {
24483
+ // TemplateDefinitionBuilder supports this case, but it's very strange to me. What would it
24484
+ // even mean?
24485
+ throw new Error(`Unexpected interpolation in defer block when trigger`);
24486
+ }
24313
24487
  const deferOnOp = createDeferWhenOp(deferXref, convertAst(triggers.when.value, unit.job, triggers.when.sourceSpan), prefetch, triggers.when.sourceSpan);
24314
24488
  deferWhenOps.push(deferOnOp);
24315
24489
  }
@@ -24329,10 +24503,10 @@ function ingestIcu(unit, icu) {
24329
24503
  unit.create.push(createIcuStartOp(xref, icu.i18n, icuFromI18nMessage(icu.i18n).name, null));
24330
24504
  for (const [placeholder, text] of Object.entries({ ...icu.vars, ...icu.placeholders })) {
24331
24505
  if (text instanceof BoundText) {
24332
- ingestBoundText(unit, text, [placeholder]);
24506
+ ingestBoundText(unit, text, placeholder);
24333
24507
  }
24334
24508
  else {
24335
- ingestText(unit, text);
24509
+ ingestText(unit, text, placeholder);
24336
24510
  }
24337
24511
  }
24338
24512
  unit.create.push(createIcuEndOp(xref));
@@ -24346,29 +24520,52 @@ function ingestIcu(unit, icu) {
24346
24520
  */
24347
24521
  function ingestForBlock(unit, forBlock) {
24348
24522
  const repeaterView = unit.job.allocateView(unit.xref);
24349
- const createRepeaterAlias = (ident, repeaterVar) => {
24350
- repeaterView.aliases.add({
24351
- kind: SemanticVariableKind.Alias,
24352
- name: null,
24353
- identifier: ident,
24354
- expression: new DerivedRepeaterVarExpr(repeaterView.xref, repeaterVar),
24355
- });
24356
- };
24357
24523
  // Set all the context variables and aliases available in the repeater.
24358
24524
  repeaterView.contextVariables.set(forBlock.item.name, forBlock.item.value);
24359
24525
  repeaterView.contextVariables.set(forBlock.contextVariables.$index.name, forBlock.contextVariables.$index.value);
24360
24526
  repeaterView.contextVariables.set(forBlock.contextVariables.$count.name, forBlock.contextVariables.$count.value);
24361
- createRepeaterAlias(forBlock.contextVariables.$first.name, DerivedRepeaterVarIdentity.First);
24362
- createRepeaterAlias(forBlock.contextVariables.$last.name, DerivedRepeaterVarIdentity.Last);
24363
- createRepeaterAlias(forBlock.contextVariables.$even.name, DerivedRepeaterVarIdentity.Even);
24364
- createRepeaterAlias(forBlock.contextVariables.$odd.name, DerivedRepeaterVarIdentity.Odd);
24527
+ // We copy TemplateDefinitionBuilder's scheme of creating names for `$count` and `$index` that are
24528
+ // suffixed with special information, to disambiguate which level of nested loop the below aliases
24529
+ // refer to.
24530
+ // TODO: We should refactor Template Pipeline's variable phases to gracefully handle shadowing,
24531
+ // and arbitrarily many levels of variables depending on each other.
24532
+ const indexName = `ɵ${forBlock.contextVariables.$index.name}_${repeaterView.xref}`;
24533
+ const countName = `ɵ${forBlock.contextVariables.$count.name}_${repeaterView.xref}`;
24534
+ repeaterView.contextVariables.set(indexName, forBlock.contextVariables.$index.value);
24535
+ repeaterView.contextVariables.set(countName, forBlock.contextVariables.$count.value);
24536
+ repeaterView.aliases.add({
24537
+ kind: SemanticVariableKind.Alias,
24538
+ name: null,
24539
+ identifier: forBlock.contextVariables.$first.name,
24540
+ expression: new LexicalReadExpr(indexName).identical(literal(0))
24541
+ });
24542
+ repeaterView.aliases.add({
24543
+ kind: SemanticVariableKind.Alias,
24544
+ name: null,
24545
+ identifier: forBlock.contextVariables.$last.name,
24546
+ expression: new LexicalReadExpr(indexName).identical(new LexicalReadExpr(countName).minus(literal(1)))
24547
+ });
24548
+ repeaterView.aliases.add({
24549
+ kind: SemanticVariableKind.Alias,
24550
+ name: null,
24551
+ identifier: forBlock.contextVariables.$even.name,
24552
+ expression: new LexicalReadExpr(indexName).modulo(literal(2)).identical(literal(0))
24553
+ });
24554
+ repeaterView.aliases.add({
24555
+ kind: SemanticVariableKind.Alias,
24556
+ name: null,
24557
+ identifier: forBlock.contextVariables.$odd.name,
24558
+ expression: new LexicalReadExpr(indexName).modulo(literal(2)).notIdentical(literal(0))
24559
+ });
24365
24560
  const sourceSpan = convertSourceSpan(forBlock.trackBy.span, forBlock.sourceSpan);
24366
24561
  const track = convertAst(forBlock.trackBy, unit.job, sourceSpan);
24367
24562
  ingestNodes(repeaterView, forBlock.children);
24368
24563
  let emptyView = null;
24564
+ let emptyTagName = null;
24369
24565
  if (forBlock.empty !== null) {
24370
24566
  emptyView = unit.job.allocateView(unit.xref);
24371
24567
  ingestNodes(emptyView, forBlock.empty.children);
24568
+ emptyTagName = ingestControlFlowInsertionPoint(unit, emptyView.xref, forBlock.empty);
24372
24569
  }
24373
24570
  const varNames = {
24374
24571
  $index: forBlock.contextVariables.$index.name,
@@ -24389,7 +24586,7 @@ function ingestForBlock(unit, forBlock) {
24389
24586
  const i18nPlaceholder = forBlock.i18n;
24390
24587
  const emptyI18nPlaceholder = forBlock.empty?.i18n;
24391
24588
  const tagName = ingestControlFlowInsertionPoint(unit, repeaterView.xref, forBlock);
24392
- const repeaterCreate = createRepeaterCreateOp(repeaterView.xref, emptyView?.xref ?? null, tagName, track, varNames, i18nPlaceholder, emptyI18nPlaceholder, forBlock.sourceSpan);
24589
+ const repeaterCreate = createRepeaterCreateOp(repeaterView.xref, emptyView?.xref ?? null, tagName, track, varNames, emptyTagName, i18nPlaceholder, emptyI18nPlaceholder, forBlock.startSourceSpan, forBlock.sourceSpan);
24393
24590
  unit.create.push(repeaterCreate);
24394
24591
  const expression = convertAst(forBlock.expression, unit.job, convertSourceSpan(forBlock.expression.span, forBlock.sourceSpan));
24395
24592
  const repeater = createRepeaterOp(repeaterCreate.xref, repeaterCreate.handle, expression, forBlock.sourceSpan);
@@ -24403,7 +24600,29 @@ function convertAst(ast, job, baseSourceSpan) {
24403
24600
  return convertAst(ast.ast, job, baseSourceSpan);
24404
24601
  }
24405
24602
  else if (ast instanceof PropertyRead) {
24406
- if (ast.receiver instanceof ImplicitReceiver && !(ast.receiver instanceof ThisReceiver)) {
24603
+ const isThisReceiver = ast.receiver instanceof ThisReceiver;
24604
+ // Whether this is an implicit receiver, *excluding* explicit reads of `this`.
24605
+ const isImplicitReceiver = ast.receiver instanceof ImplicitReceiver && !(ast.receiver instanceof ThisReceiver);
24606
+ // Whether the name of the read is a node that should be never retain its explicit this
24607
+ // receiver.
24608
+ const isSpecialNode = ast.name === '$any' || ast.name === '$event';
24609
+ // TODO: The most sensible condition here would be simply `isImplicitReceiver`, to convert only
24610
+ // actual implicit `this` reads, and not explicit ones. However, TemplateDefinitionBuilder (and
24611
+ // the Typecheck block!) both have the same bug, in which they also consider explicit `this`
24612
+ // reads to be implicit. This causes problems when the explicit `this` read is inside a
24613
+ // template with a context that also provides the variable name being read:
24614
+ // ```
24615
+ // <ng-template let-a>{{this.a}}</ng-template>
24616
+ // ```
24617
+ // The whole point of the explicit `this` was to access the class property, but TDB and the
24618
+ // current TCB treat the read as implicit, and give you the context property instead!
24619
+ //
24620
+ // For now, we emulate this old behvaior by aggressively converting explicit reads to to
24621
+ // implicit reads, except for the special cases that TDB and the current TCB protect. However,
24622
+ // it would be an improvement to fix this.
24623
+ //
24624
+ // See also the corresponding comment for the TCB, in `type_check_block.ts`.
24625
+ if (isImplicitReceiver || (isThisReceiver && !isSpecialNode)) {
24407
24626
  return new LexicalReadExpr(ast.name);
24408
24627
  }
24409
24628
  else {
@@ -24507,13 +24726,13 @@ function convertAst(ast, job, baseSourceSpan) {
24507
24726
  throw new Error(`Unhandled expression type "${ast.constructor.name}" in file "${baseSourceSpan?.start.file.url}"`);
24508
24727
  }
24509
24728
  }
24510
- function convertAstWithInterpolation(job, value, i18nMeta) {
24729
+ function convertAstWithInterpolation(job, value, i18nMeta, sourceSpan) {
24511
24730
  let expression;
24512
24731
  if (value instanceof Interpolation$1) {
24513
- expression = new Interpolation(value.strings, value.expressions.map(e => convertAst(e, job, null)), Object.keys(asMessage(i18nMeta)?.placeholders ?? {}));
24732
+ expression = new Interpolation(value.strings, value.expressions.map(e => convertAst(e, job, sourceSpan ?? null)), Object.keys(asMessage(i18nMeta)?.placeholders ?? {}));
24514
24733
  }
24515
24734
  else if (value instanceof AST) {
24516
- expression = convertAst(value, job, null);
24735
+ expression = convertAst(value, job, sourceSpan ?? null);
24517
24736
  }
24518
24737
  else {
24519
24738
  expression = literal(value);
@@ -24626,7 +24845,7 @@ function ingestTemplateBindings(unit, op, template, templateKind) {
24626
24845
  output.type !== 1 /* e.ParsedEventType.Animation */) {
24627
24846
  // Animation bindings are excluded from the structural template's const array.
24628
24847
  const securityContext = domSchema.securityContext(NG_TEMPLATE_TAG_NAME$1, output.name, false);
24629
- unit.create.push(createExtractedAttributeOp(op.xref, BindingKind.Property, output.name, null, null, null, securityContext));
24848
+ unit.create.push(createExtractedAttributeOp(op.xref, BindingKind.Property, null, output.name, null, null, null, securityContext));
24630
24849
  }
24631
24850
  }
24632
24851
  // TODO: Perhaps we could do this in a phase? (It likely wouldn't change the slot indices.)
@@ -24674,7 +24893,7 @@ function createTemplateBinding(view, xref, type, name, value, unit, securityCont
24674
24893
  // inner node of a structural template. We can't skip it entirely, because we still need it on
24675
24894
  // the ng-template's consts (e.g. for the purposes of directive matching). However, we should
24676
24895
  // not generate an update instruction for it.
24677
- return createExtractedAttributeOp(xref, BindingKind.Property, name, null, null, i18nMessage, securityContext);
24896
+ return createExtractedAttributeOp(xref, BindingKind.Property, null, name, null, null, i18nMessage, securityContext);
24678
24897
  }
24679
24898
  if (!isTextBinding && (type === 1 /* e.BindingType.Attribute */ || type === 4 /* e.BindingType.Animation */)) {
24680
24899
  // Again, this binding doesn't really target the ng-template; it actually targets the element
@@ -28476,6 +28695,9 @@ class TemplateDefinitionBuilder {
28476
28695
  this.updateInstructionWithAdvance(containerIndex, block.branches[0].sourceSpan, Identifiers.conditional, paramsCallback);
28477
28696
  }
28478
28697
  visitSwitchBlock(block) {
28698
+ if (block.cases.length === 0) {
28699
+ return;
28700
+ }
28479
28701
  // We have to process the block in two steps: once here and again in the update instruction
28480
28702
  // callback in order to generate the correct expressions when pipes or pure functions are used.
28481
28703
  const caseData = block.cases.map(currentCase => {
@@ -28705,7 +28927,12 @@ class TemplateDefinitionBuilder {
28705
28927
  });
28706
28928
  const { expression: trackByExpression, usesComponentInstance: trackByUsesComponentInstance } = this.createTrackByFunction(block);
28707
28929
  let emptyData = null;
28930
+ let emptyTagName = null;
28931
+ let emptyAttrsExprs;
28708
28932
  if (block.empty !== null) {
28933
+ const emptyInferred = this.inferProjectionDataFromInsertionPoint(block.empty);
28934
+ emptyTagName = emptyInferred.tagName;
28935
+ emptyAttrsExprs = emptyInferred.attrsExprs;
28709
28936
  emptyData = this.prepareEmbeddedTemplateFn(block.empty.children, '_ForEmpty', undefined, block.empty.i18n);
28710
28937
  // Allocate an extra slot for the empty block tracking.
28711
28938
  this.allocateBindingSlots(null);
@@ -28723,13 +28950,13 @@ class TemplateDefinitionBuilder {
28723
28950
  trackByExpression,
28724
28951
  ];
28725
28952
  if (emptyData !== null) {
28726
- params.push(literal(trackByUsesComponentInstance), variable(emptyData.name), literal(emptyData.getConstCount()), literal(emptyData.getVarCount()));
28953
+ params.push(literal(trackByUsesComponentInstance), variable(emptyData.name), literal(emptyData.getConstCount()), literal(emptyData.getVarCount()), literal(emptyTagName), this.addAttrsToConsts(emptyAttrsExprs || null));
28727
28954
  }
28728
28955
  else if (trackByUsesComponentInstance) {
28729
28956
  // If the tracking function doesn't use the component instance, we can omit the flag.
28730
28957
  params.push(literal(trackByUsesComponentInstance));
28731
28958
  }
28732
- return params;
28959
+ return trimTrailingNulls(params);
28733
28960
  });
28734
28961
  // Note: the expression needs to be processed *after* the template,
28735
28962
  // otherwise pipes injecting some symbols won't work (see #52102).
@@ -28934,7 +29161,7 @@ class TemplateDefinitionBuilder {
28934
29161
  if (delta < 1) {
28935
29162
  throw new Error('advance instruction can only go forwards');
28936
29163
  }
28937
- this.instructionFn(this._updateCodeFns, span, Identifiers.advance, [literal(delta)]);
29164
+ this.instructionFn(this._updateCodeFns, span, Identifiers.advance, delta > 1 ? [literal(delta)] : []);
28938
29165
  this._currentIndex = nodeIndex;
28939
29166
  }
28940
29167
  }
@@ -29488,12 +29715,16 @@ class BindingScope {
29488
29715
  }
29489
29716
  /** Binding scope of a `track` function inside a `for` loop block. */
29490
29717
  class TrackByBindingScope extends BindingScope {
29491
- constructor(parentScope, globalAliases) {
29718
+ constructor(parentScope, globalOverrides) {
29492
29719
  super(parentScope.bindingLevel + 1, parentScope);
29493
- this.globalAliases = globalAliases;
29720
+ this.globalOverrides = globalOverrides;
29494
29721
  this.componentAccessCount = 0;
29495
29722
  }
29496
29723
  get(name) {
29724
+ // Intercept any overridden globals.
29725
+ if (this.globalOverrides.hasOwnProperty(name)) {
29726
+ return variable(this.globalOverrides[name]);
29727
+ }
29497
29728
  let current = this.parent;
29498
29729
  // Prevent accesses of template variables outside the `for` loop.
29499
29730
  while (current) {
@@ -29502,10 +29733,6 @@ class TrackByBindingScope extends BindingScope {
29502
29733
  }
29503
29734
  current = current.parent;
29504
29735
  }
29505
- // Intercept any aliased globals.
29506
- if (this.globalAliases[name]) {
29507
- return variable(this.globalAliases[name]);
29508
- }
29509
29736
  // When the component scope is accessed, we redirect it through `this`.
29510
29737
  this.componentAccessCount++;
29511
29738
  return variable('this').prop(name);
@@ -31633,8 +31860,10 @@ function convertDirectiveFacadeToMetadata(facade) {
31633
31860
  bindingPropertyName: ann.alias || field,
31634
31861
  classPropertyName: field,
31635
31862
  required: ann.required || false,
31636
- // TODO(signals): Support JIT signal inputs via decorator transform.
31637
- isSignal: false,
31863
+ // For JIT, decorators are used to declare signal inputs. That is because of
31864
+ // a technical limitation where it's not possible to statically reflect class
31865
+ // members of a directive/component at runtime before instantiating the class.
31866
+ isSignal: !!ann.isSignal,
31638
31867
  transformFunction: ann.transform != null ? new WrappedNodeExpr(ann.transform) : null,
31639
31868
  };
31640
31869
  }
@@ -32046,7 +32275,7 @@ function publishFacade(global) {
32046
32275
  * @description
32047
32276
  * Entry point for all public APIs of the compiler package.
32048
32277
  */
32049
- const VERSION = new Version('17.1.0-next.4');
32278
+ const VERSION = new Version('17.1.0-rc.0');
32050
32279
 
32051
32280
  class CompilerConfig {
32052
32281
  constructor({ defaultEncapsulation = ViewEncapsulation.Emulated, preserveWhitespaces, strictInjectionParameters } = {}) {
@@ -33612,7 +33841,7 @@ const MINIMUM_PARTIAL_LINKER_VERSION$5 = '12.0.0';
33612
33841
  function compileDeclareClassMetadata(metadata) {
33613
33842
  const definitionMap = new DefinitionMap();
33614
33843
  definitionMap.set('minVersion', literal(MINIMUM_PARTIAL_LINKER_VERSION$5));
33615
- definitionMap.set('version', literal('17.1.0-next.4'));
33844
+ definitionMap.set('version', literal('17.1.0-rc.0'));
33616
33845
  definitionMap.set('ngImport', importExpr(Identifiers.core));
33617
33846
  definitionMap.set('type', metadata.type);
33618
33847
  definitionMap.set('decorators', metadata.decorators);
@@ -33708,7 +33937,7 @@ function createDirectiveDefinitionMap(meta) {
33708
33937
  const definitionMap = new DefinitionMap();
33709
33938
  const minVersion = getMinimumVersionForPartialOutput(meta);
33710
33939
  definitionMap.set('minVersion', literal(minVersion));
33711
- definitionMap.set('version', literal('17.1.0-next.4'));
33940
+ definitionMap.set('version', literal('17.1.0-rc.0'));
33712
33941
  // e.g. `type: MyDirective`
33713
33942
  definitionMap.set('type', meta.type.value);
33714
33943
  if (meta.isStandalone) {
@@ -33766,8 +33995,8 @@ function getMinimumVersionForPartialOutput(meta) {
33766
33995
  // Note: in order to allow consuming Angular libraries that have been compiled with 16.1+ in
33767
33996
  // Angular 16.0, we only force a minimum version of 16.1 if input transform feature as introduced
33768
33997
  // in 16.1 is actually used.
33769
- const hasTransformFunctions = Object.values(meta.inputs).some(input => input.transformFunction !== null);
33770
- if (hasTransformFunctions) {
33998
+ const hasDecoratorTransformFunctions = Object.values(meta.inputs).some(input => input.transformFunction !== null);
33999
+ if (hasDecoratorTransformFunctions) {
33771
34000
  minVersion = '16.1.0';
33772
34001
  }
33773
34002
  // If there are input flags and we need the new emit, use the actual minimum version,
@@ -34092,7 +34321,7 @@ const MINIMUM_PARTIAL_LINKER_VERSION$4 = '12.0.0';
34092
34321
  function compileDeclareFactoryFunction(meta) {
34093
34322
  const definitionMap = new DefinitionMap();
34094
34323
  definitionMap.set('minVersion', literal(MINIMUM_PARTIAL_LINKER_VERSION$4));
34095
- definitionMap.set('version', literal('17.1.0-next.4'));
34324
+ definitionMap.set('version', literal('17.1.0-rc.0'));
34096
34325
  definitionMap.set('ngImport', importExpr(Identifiers.core));
34097
34326
  definitionMap.set('type', meta.type.value);
34098
34327
  definitionMap.set('deps', compileDependencies(meta.deps));
@@ -34127,7 +34356,7 @@ function compileDeclareInjectableFromMetadata(meta) {
34127
34356
  function createInjectableDefinitionMap(meta) {
34128
34357
  const definitionMap = new DefinitionMap();
34129
34358
  definitionMap.set('minVersion', literal(MINIMUM_PARTIAL_LINKER_VERSION$3));
34130
- definitionMap.set('version', literal('17.1.0-next.4'));
34359
+ definitionMap.set('version', literal('17.1.0-rc.0'));
34131
34360
  definitionMap.set('ngImport', importExpr(Identifiers.core));
34132
34361
  definitionMap.set('type', meta.type.value);
34133
34362
  // Only generate providedIn property if it has a non-null value
@@ -34178,7 +34407,7 @@ function compileDeclareInjectorFromMetadata(meta) {
34178
34407
  function createInjectorDefinitionMap(meta) {
34179
34408
  const definitionMap = new DefinitionMap();
34180
34409
  definitionMap.set('minVersion', literal(MINIMUM_PARTIAL_LINKER_VERSION$2));
34181
- definitionMap.set('version', literal('17.1.0-next.4'));
34410
+ definitionMap.set('version', literal('17.1.0-rc.0'));
34182
34411
  definitionMap.set('ngImport', importExpr(Identifiers.core));
34183
34412
  definitionMap.set('type', meta.type.value);
34184
34413
  definitionMap.set('providers', meta.providers);
@@ -34211,7 +34440,7 @@ function createNgModuleDefinitionMap(meta) {
34211
34440
  throw new Error('Invalid path! Local compilation mode should not get into the partial compilation path');
34212
34441
  }
34213
34442
  definitionMap.set('minVersion', literal(MINIMUM_PARTIAL_LINKER_VERSION$1));
34214
- definitionMap.set('version', literal('17.1.0-next.4'));
34443
+ definitionMap.set('version', literal('17.1.0-rc.0'));
34215
34444
  definitionMap.set('ngImport', importExpr(Identifiers.core));
34216
34445
  definitionMap.set('type', meta.type.value);
34217
34446
  // We only generate the keys in the metadata if the arrays contain values.
@@ -34262,7 +34491,7 @@ function compileDeclarePipeFromMetadata(meta) {
34262
34491
  function createPipeDefinitionMap(meta) {
34263
34492
  const definitionMap = new DefinitionMap();
34264
34493
  definitionMap.set('minVersion', literal(MINIMUM_PARTIAL_LINKER_VERSION));
34265
- definitionMap.set('version', literal('17.1.0-next.4'));
34494
+ definitionMap.set('version', literal('17.1.0-rc.0'));
34266
34495
  definitionMap.set('ngImport', importExpr(Identifiers.core));
34267
34496
  // e.g. `type: MyPipe`
34268
34497
  definitionMap.set('type', meta.type.value);