@angular/compiler 17.0.7 → 17.0.9

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 (40) hide show
  1. package/esm2022/src/constant_pool.mjs +5 -3
  2. package/esm2022/src/ml_parser/tags.mjs +8 -3
  3. package/esm2022/src/render3/partial/class_metadata.mjs +1 -1
  4. package/esm2022/src/render3/partial/directive.mjs +1 -1
  5. package/esm2022/src/render3/partial/factory.mjs +1 -1
  6. package/esm2022/src/render3/partial/injectable.mjs +1 -1
  7. package/esm2022/src/render3/partial/injector.mjs +1 -1
  8. package/esm2022/src/render3/partial/ng_module.mjs +1 -1
  9. package/esm2022/src/render3/partial/pipe.mjs +1 -1
  10. package/esm2022/src/render3/view/style_parser.mjs +2 -1
  11. package/esm2022/src/render3/view/template.mjs +11 -8
  12. package/esm2022/src/template/pipeline/ir/src/enums.mjs +7 -13
  13. package/esm2022/src/template/pipeline/ir/src/expression.mjs +5 -21
  14. package/esm2022/src/template/pipeline/ir/src/ops/create.mjs +29 -10
  15. package/esm2022/src/template/pipeline/ir/src/ops/update.mjs +6 -3
  16. package/esm2022/src/template/pipeline/src/emit.mjs +3 -5
  17. package/esm2022/src/template/pipeline/src/ingest.mjs +76 -48
  18. package/esm2022/src/template/pipeline/src/instruction.mjs +8 -7
  19. package/esm2022/src/template/pipeline/src/phases/attribute_extraction.mjs +10 -19
  20. package/esm2022/src/template/pipeline/src/phases/binding_specialization.mjs +4 -2
  21. package/esm2022/src/template/pipeline/src/phases/const_collection.mjs +31 -20
  22. package/esm2022/src/template/pipeline/src/phases/convert_i18n_bindings.mjs +2 -2
  23. package/esm2022/src/template/pipeline/src/phases/create_defer_deps_fns.mjs +3 -2
  24. package/esm2022/src/template/pipeline/src/phases/create_i18n_contexts.mjs +63 -51
  25. package/esm2022/src/template/pipeline/src/phases/deduplicate_text_bindings.mjs +40 -0
  26. package/esm2022/src/template/pipeline/src/phases/extract_i18n_messages.mjs +52 -49
  27. package/esm2022/src/template/pipeline/src/phases/host_style_property_parsing.mjs +3 -3
  28. package/esm2022/src/template/pipeline/src/phases/i18n_const_collection.mjs +2 -3
  29. package/esm2022/src/template/pipeline/src/phases/i18n_text_extraction.mjs +22 -3
  30. package/esm2022/src/template/pipeline/src/phases/parse_extracted_styles.mjs +21 -3
  31. package/esm2022/src/template/pipeline/src/phases/reify.mjs +9 -9
  32. package/esm2022/src/template/pipeline/src/phases/resolve_i18n_expression_placeholders.mjs +23 -10
  33. package/esm2022/src/template/pipeline/src/phases/var_counting.mjs +8 -1
  34. package/esm2022/src/version.mjs +1 -1
  35. package/fesm2022/compiler.mjs +419 -361
  36. package/fesm2022/compiler.mjs.map +1 -1
  37. package/index.d.ts +3 -3
  38. package/package.json +2 -2
  39. package/esm2022/src/template/pipeline/src/phases/repeater_derived_vars.mjs +0 -45
  40. package/esm2022/src/template/pipeline/src/phases/resolve_i18n_icu_placeholders.mjs +0 -62
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @license Angular v17.0.7
2
+ * @license Angular v17.0.9
3
3
  * (c) 2010-2022 Google LLC. https://angular.io/
4
4
  * License: MIT
5
5
  */
@@ -2308,7 +2308,9 @@ class ConstantPool {
2308
2308
  }))));
2309
2309
  }
2310
2310
  }
2311
- getSharedFunctionReference(fn, prefix) {
2311
+ // TODO: useUniqueName(false) is necessary for naming compatibility with
2312
+ // TemplateDefinitionBuilder, but should be removed once Template Pipeline is the default.
2313
+ getSharedFunctionReference(fn, prefix, useUniqueName = true) {
2312
2314
  const isArrow = fn instanceof ArrowFunctionExpr;
2313
2315
  for (const current of this.statements) {
2314
2316
  // Arrow functions are saved as variables so we check if the
@@ -2323,7 +2325,7 @@ class ConstantPool {
2323
2325
  }
2324
2326
  }
2325
2327
  // Otherwise declare the function.
2326
- const name = this.uniqueName(prefix);
2328
+ const name = useUniqueName ? this.uniqueName(prefix) : prefix;
2327
2329
  this.statements.push(fn.toDeclStmt(name, StmtModifier.Final));
2328
2330
  return variable(name);
2329
2331
  }
@@ -3685,13 +3687,18 @@ var TagContentType;
3685
3687
  TagContentType[TagContentType["ESCAPABLE_RAW_TEXT"] = 1] = "ESCAPABLE_RAW_TEXT";
3686
3688
  TagContentType[TagContentType["PARSABLE_DATA"] = 2] = "PARSABLE_DATA";
3687
3689
  })(TagContentType || (TagContentType = {}));
3688
- function splitNsName(elementName) {
3690
+ function splitNsName(elementName, fatal = true) {
3689
3691
  if (elementName[0] != ':') {
3690
3692
  return [null, elementName];
3691
3693
  }
3692
3694
  const colonIndex = elementName.indexOf(':', 1);
3693
3695
  if (colonIndex === -1) {
3694
- throw new Error(`Unsupported format "${elementName}" expecting ":namespace:name"`);
3696
+ if (fatal) {
3697
+ throw new Error(`Unsupported format "${elementName}" expecting ":namespace:name"`);
3698
+ }
3699
+ else {
3700
+ return [null, elementName];
3701
+ }
3695
3702
  }
3696
3703
  return [elementName.slice(1, colonIndex), elementName.slice(colonIndex + 1)];
3697
3704
  }
@@ -8966,14 +8973,18 @@ var OpKind;
8966
8973
  * An instruction to update an ICU expression.
8967
8974
  */
8968
8975
  OpKind[OpKind["IcuEnd"] = 42] = "IcuEnd";
8976
+ /**
8977
+ * An instruction representing a placeholder in an ICU expression.
8978
+ */
8979
+ OpKind[OpKind["IcuPlaceholder"] = 43] = "IcuPlaceholder";
8969
8980
  /**
8970
8981
  * An i18n context containing information needed to generate an i18n message.
8971
8982
  */
8972
- OpKind[OpKind["I18nContext"] = 43] = "I18nContext";
8983
+ OpKind[OpKind["I18nContext"] = 44] = "I18nContext";
8973
8984
  /**
8974
8985
  * A creation op that corresponds to i18n attributes on an element.
8975
8986
  */
8976
- OpKind[OpKind["I18nAttributes"] = 44] = "I18nAttributes";
8987
+ OpKind[OpKind["I18nAttributes"] = 45] = "I18nAttributes";
8977
8988
  })(OpKind || (OpKind = {}));
8978
8989
  /**
8979
8990
  * Distinguishes different kinds of IR expressions.
@@ -9250,16 +9261,6 @@ var DeferTriggerKind;
9250
9261
  DeferTriggerKind[DeferTriggerKind["Interaction"] = 4] = "Interaction";
9251
9262
  DeferTriggerKind[DeferTriggerKind["Viewport"] = 5] = "Viewport";
9252
9263
  })(DeferTriggerKind || (DeferTriggerKind = {}));
9253
- /**
9254
- * Repeaters implicitly define these derived variables, and child nodes may read them.
9255
- */
9256
- var DerivedRepeaterVarIdentity;
9257
- (function (DerivedRepeaterVarIdentity) {
9258
- DerivedRepeaterVarIdentity[DerivedRepeaterVarIdentity["First"] = 0] = "First";
9259
- DerivedRepeaterVarIdentity[DerivedRepeaterVarIdentity["Last"] = 1] = "Last";
9260
- DerivedRepeaterVarIdentity[DerivedRepeaterVarIdentity["Even"] = 2] = "Even";
9261
- DerivedRepeaterVarIdentity[DerivedRepeaterVarIdentity["Odd"] = 3] = "Odd";
9262
- })(DerivedRepeaterVarIdentity || (DerivedRepeaterVarIdentity = {}));
9263
9264
  /**
9264
9265
  * Kinds of i18n contexts. They can be created because of root i18n blocks, or ICUs.
9265
9266
  */
@@ -9504,10 +9505,11 @@ function createClassMapOp(xref, expression, sourceSpan) {
9504
9505
  /**
9505
9506
  * Create an `AttributeOp`.
9506
9507
  */
9507
- function createAttributeOp(target, name, expression, securityContext, isTextAttribute, isStructuralTemplateAttribute, templateKind, i18nMessage, sourceSpan) {
9508
+ function createAttributeOp(target, namespace, name, expression, securityContext, isTextAttribute, isStructuralTemplateAttribute, templateKind, i18nMessage, sourceSpan) {
9508
9509
  return {
9509
9510
  kind: OpKind.Attribute,
9510
9511
  target,
9512
+ namespace,
9511
9513
  name,
9512
9514
  expression,
9513
9515
  securityContext,
@@ -9572,12 +9574,13 @@ function createDeferWhenOp(target, expr, prefetch, sourceSpan) {
9572
9574
  sourceSpan,
9573
9575
  ...NEW_OP,
9574
9576
  ...TRAIT_DEPENDS_ON_SLOT_CONTEXT,
9577
+ ...TRAIT_CONSUMES_VARS,
9575
9578
  };
9576
9579
  }
9577
9580
  /**
9578
9581
  * Create an i18n expression op.
9579
9582
  */
9580
- function createI18nExpressionOp(context, target, i18nOwner, handle, expression, i18nPlaceholder, resolutionTime, usage, name, sourceSpan) {
9583
+ function createI18nExpressionOp(context, target, i18nOwner, handle, expression, icuPlaceholder, i18nPlaceholder, resolutionTime, usage, name, sourceSpan) {
9581
9584
  return {
9582
9585
  kind: OpKind.I18nExpression,
9583
9586
  context,
@@ -9585,6 +9588,7 @@ function createI18nExpressionOp(context, target, i18nOwner, handle, expression,
9585
9588
  i18nOwner,
9586
9589
  handle,
9587
9590
  expression,
9591
+ icuPlaceholder,
9588
9592
  i18nPlaceholder,
9589
9593
  resolutionTime,
9590
9594
  usage,
@@ -10202,26 +10206,6 @@ class ConditionalCaseExpr extends ExpressionBase {
10202
10206
  }
10203
10207
  }
10204
10208
  }
10205
- class DerivedRepeaterVarExpr extends ExpressionBase {
10206
- constructor(xref, identity) {
10207
- super();
10208
- this.xref = xref;
10209
- this.identity = identity;
10210
- this.kind = ExpressionKind.DerivedRepeaterVar;
10211
- }
10212
- transformInternalExpressions(transform, flags) { }
10213
- visitExpression(visitor, context) { }
10214
- isEquivalent(e) {
10215
- return e instanceof DerivedRepeaterVarExpr && e.identity === this.identity &&
10216
- e.xref === this.xref;
10217
- }
10218
- isConstant() {
10219
- return false;
10220
- }
10221
- clone() {
10222
- return new DerivedRepeaterVarExpr(this.xref, this.identity);
10223
- }
10224
- }
10225
10209
  class ConstCollectedExpr extends ExpressionBase {
10226
10210
  constructor(expr) {
10227
10211
  super();
@@ -10354,6 +10338,9 @@ function transformExpressionsInOp(op, transform, flags) {
10354
10338
  op.placeholderConfig =
10355
10339
  transformExpressionsInExpression(op.placeholderConfig, transform, flags);
10356
10340
  }
10341
+ if (op.resolverFn !== null) {
10342
+ op.resolverFn = transformExpressionsInExpression(op.resolverFn, transform, flags);
10343
+ }
10357
10344
  break;
10358
10345
  case OpKind.I18nMessage:
10359
10346
  for (const [placeholder, expr] of op.params) {
@@ -10390,6 +10377,7 @@ function transformExpressionsInOp(op, transform, flags) {
10390
10377
  case OpKind.Template:
10391
10378
  case OpKind.Text:
10392
10379
  case OpKind.I18nAttributes:
10380
+ case OpKind.IcuPlaceholder:
10393
10381
  // These operations contain no expressions.
10394
10382
  break;
10395
10383
  default:
@@ -10797,7 +10785,7 @@ function isElementOrContainerOp(op) {
10797
10785
  /**
10798
10786
  * Create an `ElementStartOp`.
10799
10787
  */
10800
- function createElementStartOp(tag, xref, namespace, i18nPlaceholder, sourceSpan) {
10788
+ function createElementStartOp(tag, xref, namespace, i18nPlaceholder, startSourceSpan, wholeSourceSpan) {
10801
10789
  return {
10802
10790
  kind: OpKind.ElementStart,
10803
10791
  xref,
@@ -10808,7 +10796,8 @@ function createElementStartOp(tag, xref, namespace, i18nPlaceholder, sourceSpan)
10808
10796
  nonBindable: false,
10809
10797
  namespace,
10810
10798
  i18nPlaceholder,
10811
- sourceSpan,
10799
+ startSourceSpan,
10800
+ wholeSourceSpan,
10812
10801
  ...TRAIT_CONSUMES_SLOT,
10813
10802
  ...NEW_OP,
10814
10803
  };
@@ -10816,7 +10805,7 @@ function createElementStartOp(tag, xref, namespace, i18nPlaceholder, sourceSpan)
10816
10805
  /**
10817
10806
  * Create a `TemplateOp`.
10818
10807
  */
10819
- function createTemplateOp(xref, templateKind, tag, functionNameSuffix, namespace, i18nPlaceholder, sourceSpan) {
10808
+ function createTemplateOp(xref, templateKind, tag, functionNameSuffix, namespace, i18nPlaceholder, startSourceSpan, wholeSourceSpan) {
10820
10809
  return {
10821
10810
  kind: OpKind.Template,
10822
10811
  xref,
@@ -10831,12 +10820,13 @@ function createTemplateOp(xref, templateKind, tag, functionNameSuffix, namespace
10831
10820
  nonBindable: false,
10832
10821
  namespace,
10833
10822
  i18nPlaceholder,
10834
- sourceSpan,
10823
+ startSourceSpan,
10824
+ wholeSourceSpan,
10835
10825
  ...TRAIT_CONSUMES_SLOT,
10836
10826
  ...NEW_OP,
10837
10827
  };
10838
10828
  }
10839
- function createRepeaterCreateOp(primaryView, emptyView, tag, track, varNames, i18nPlaceholder, emptyI18nPlaceholder, sourceSpan) {
10829
+ function createRepeaterCreateOp(primaryView, emptyView, tag, track, varNames, i18nPlaceholder, emptyI18nPlaceholder, startSourceSpan, wholeSourceSpan) {
10840
10830
  return {
10841
10831
  kind: OpKind.RepeaterCreate,
10842
10832
  attributes: null,
@@ -10856,9 +10846,11 @@ function createRepeaterCreateOp(primaryView, emptyView, tag, track, varNames, i1
10856
10846
  usesComponentInstance: false,
10857
10847
  i18nPlaceholder,
10858
10848
  emptyI18nPlaceholder,
10859
- sourceSpan,
10849
+ startSourceSpan,
10850
+ wholeSourceSpan,
10860
10851
  ...TRAIT_CONSUMES_SLOT,
10861
10852
  ...NEW_OP,
10853
+ ...TRAIT_CONSUMES_VARS,
10862
10854
  numSlotsUsed: emptyView === null ? 2 : 3,
10863
10855
  };
10864
10856
  }
@@ -10890,12 +10882,13 @@ function createEnableBindingsOp(xref) {
10890
10882
  /**
10891
10883
  * Create a `TextOp`.
10892
10884
  */
10893
- function createTextOp(xref, initialValue, sourceSpan) {
10885
+ function createTextOp(xref, initialValue, icuPlaceholder, sourceSpan) {
10894
10886
  return {
10895
10887
  kind: OpKind.Text,
10896
10888
  xref,
10897
10889
  handle: new SlotHandle(),
10898
10890
  initialValue,
10891
+ icuPlaceholder,
10899
10892
  sourceSpan,
10900
10893
  ...TRAIT_CONSUMES_SLOT,
10901
10894
  ...NEW_OP,
@@ -10966,11 +10959,12 @@ function createProjectionOp(xref, selector, i18nPlaceholder, attributes, sourceS
10966
10959
  /**
10967
10960
  * Create an `ExtractedAttributeOp`.
10968
10961
  */
10969
- function createExtractedAttributeOp(target, bindingKind, name, expression, i18nContext, i18nMessage, securityContext) {
10962
+ function createExtractedAttributeOp(target, bindingKind, namespace, name, expression, i18nContext, i18nMessage, securityContext) {
10970
10963
  return {
10971
10964
  kind: OpKind.ExtractedAttribute,
10972
10965
  target,
10973
10966
  bindingKind,
10967
+ namespace,
10974
10968
  name,
10975
10969
  expression,
10976
10970
  i18nContext,
@@ -11085,6 +11079,19 @@ function createIcuEndOp(xref) {
11085
11079
  ...NEW_OP,
11086
11080
  };
11087
11081
  }
11082
+ /**
11083
+ * Creates an ICU placeholder op.
11084
+ */
11085
+ function createIcuPlaceholderOp(xref, name, strings) {
11086
+ return {
11087
+ kind: OpKind.IcuPlaceholder,
11088
+ xref,
11089
+ name,
11090
+ strings,
11091
+ expressionPlaceholders: [],
11092
+ ...NEW_OP,
11093
+ };
11094
+ }
11088
11095
  function createI18nContextOp(contextKind, xref, i18nBlock, message, sourceSpan) {
11089
11096
  if (i18nBlock === null && contextKind !== I18nContextKind.Attr) {
11090
11097
  throw new Error('AssertionError: i18nBlock must be provided for non-attribute contexts.');
@@ -11498,7 +11505,8 @@ function extractAttributes(job) {
11498
11505
  }
11499
11506
  OpList.insertBefore(
11500
11507
  // Deliberaly null i18nMessage value
11501
- createExtractedAttributeOp(op.target, bindingKind, op.name, /* expression */ null, /* i18nContext */ null,
11508
+ createExtractedAttributeOp(op.target, bindingKind, null, op.name, /* expression */ null,
11509
+ /* i18nContext */ null,
11502
11510
  /* i18nMessage */ null, op.securityContext), lookupElement$2(elements, op.target));
11503
11511
  }
11504
11512
  break;
@@ -11510,14 +11518,14 @@ function extractAttributes(job) {
11510
11518
  // mode.
11511
11519
  if (unit.job.compatibility === CompatibilityMode.TemplateDefinitionBuilder &&
11512
11520
  op.expression instanceof EmptyExpr) {
11513
- OpList.insertBefore(createExtractedAttributeOp(op.target, BindingKind.Property, op.name, /* expression */ null,
11521
+ OpList.insertBefore(createExtractedAttributeOp(op.target, BindingKind.Property, null, op.name, /* expression */ null,
11514
11522
  /* i18nContext */ null,
11515
11523
  /* i18nMessage */ null, SecurityContext.STYLE), lookupElement$2(elements, op.target));
11516
11524
  }
11517
11525
  break;
11518
11526
  case OpKind.Listener:
11519
11527
  if (!op.isAnimationListener) {
11520
- const extractedAttributeOp = createExtractedAttributeOp(op.target, BindingKind.Property, op.name, /* expression */ null,
11528
+ const extractedAttributeOp = createExtractedAttributeOp(op.target, BindingKind.Property, null, op.name, /* expression */ null,
11521
11529
  /* i18nContext */ null,
11522
11530
  /* i18nMessage */ null, SecurityContext.NONE);
11523
11531
  if (job.kind === CompilationJobKind.Host) {
@@ -11551,24 +11559,14 @@ function extractAttributeOp(unit, op, elements) {
11551
11559
  if (op.expression instanceof Interpolation) {
11552
11560
  return;
11553
11561
  }
11554
- let extractable = op.expression.isConstant();
11562
+ let extractable = op.isTextAttribute || op.expression.isConstant();
11555
11563
  if (unit.job.compatibility === CompatibilityMode.TemplateDefinitionBuilder) {
11556
- // TemplateDefinitionBuilder only extracted attributes that were string literals.
11557
- extractable = isStringLiteral(op.expression);
11558
- if (op.name === 'style' || op.name === 'class') {
11559
- // For style and class attributes, TemplateDefinitionBuilder only extracted them if they were
11560
- // text attributes. For example, `[attr.class]="'my-class'"` was not extracted despite being a
11561
- // string literal, because it is not a text attribute.
11562
- extractable &&= op.isTextAttribute;
11563
- }
11564
- if (unit.job.kind === CompilationJobKind.Host) {
11565
- // TemplateDefinitionBuilder also does not seem to extract string literals if they are part of
11566
- // a host attribute.
11567
- extractable &&= op.isTextAttribute;
11568
- }
11564
+ // TemplateDefinitionBuilder only extracts text attributes. It does not extract attriibute
11565
+ // bindings, even if they are constants.
11566
+ extractable &&= op.isTextAttribute;
11569
11567
  }
11570
11568
  if (extractable) {
11571
- const extractedAttributeOp = createExtractedAttributeOp(op.target, op.isStructuralTemplateAttribute ? BindingKind.Template : BindingKind.Attribute, op.name, op.expression, op.i18nContext, op.i18nMessage, op.securityContext);
11569
+ const extractedAttributeOp = createExtractedAttributeOp(op.target, op.isStructuralTemplateAttribute ? BindingKind.Template : BindingKind.Attribute, op.namespace, op.name, op.expression, op.i18nContext, op.i18nMessage, op.securityContext);
11572
11570
  if (unit.job.kind === CompilationJobKind.Host) {
11573
11571
  // This attribute will apply to the enclosing host binding compilation unit, so order doesn't
11574
11572
  // matter.
@@ -11615,7 +11613,8 @@ function specializeBindings(job) {
11615
11613
  target.nonBindable = true;
11616
11614
  }
11617
11615
  else {
11618
- OpList.replace(op, createAttributeOp(op.target, op.name, op.expression, op.securityContext, op.isTextAttribute, op.isStructuralTemplateAttribute, op.templateKind, op.i18nMessage, op.sourceSpan));
11616
+ const [namespace, name] = splitNsName(op.name);
11617
+ OpList.replace(op, createAttributeOp(op.target, namespace, name, op.expression, op.securityContext, op.isTextAttribute, op.isStructuralTemplateAttribute, op.templateKind, op.i18nMessage, op.sourceSpan));
11619
11618
  }
11620
11619
  break;
11621
11620
  case BindingKind.Property:
@@ -11859,9 +11858,9 @@ function collectElementConsts(job) {
11859
11858
  for (const unit of job.units) {
11860
11859
  for (const op of unit.create) {
11861
11860
  if (op.kind === OpKind.ExtractedAttribute) {
11862
- const attributes = allElementAttributes.get(op.target) || new ElementAttributes();
11861
+ const attributes = allElementAttributes.get(op.target) || new ElementAttributes(job.compatibility);
11863
11862
  allElementAttributes.set(op.target, attributes);
11864
- attributes.add(op.bindingKind, op.name, op.expression, op.trustedValueFn);
11863
+ attributes.add(op.bindingKind, op.name, op.expression, op.namespace, op.trustedValueFn);
11865
11864
  OpList.remove(op);
11866
11865
  }
11867
11866
  }
@@ -11904,11 +11903,6 @@ const FLYWEIGHT_ARRAY = Object.freeze([]);
11904
11903
  * Container for all of the various kinds of attributes which are applied on an element.
11905
11904
  */
11906
11905
  class ElementAttributes {
11907
- constructor() {
11908
- this.known = new Set();
11909
- this.byKind = new Map;
11910
- this.projectAs = null;
11911
- }
11912
11906
  get attributes() {
11913
11907
  return this.byKind.get(BindingKind.Attribute) ?? FLYWEIGHT_ARRAY;
11914
11908
  }
@@ -11927,11 +11921,31 @@ class ElementAttributes {
11927
11921
  get i18n() {
11928
11922
  return this.byKind.get(BindingKind.I18n) ?? FLYWEIGHT_ARRAY;
11929
11923
  }
11930
- add(kind, name, value, trustedValueFn) {
11931
- if (this.known.has(name)) {
11924
+ constructor(compatibility) {
11925
+ this.compatibility = compatibility;
11926
+ this.known = new Map();
11927
+ this.byKind = new Map;
11928
+ this.projectAs = null;
11929
+ }
11930
+ isKnown(kind, name, value) {
11931
+ const nameToValue = this.known.get(kind) ?? new Set();
11932
+ this.known.set(kind, nameToValue);
11933
+ if (nameToValue.has(name)) {
11934
+ return true;
11935
+ }
11936
+ nameToValue.add(name);
11937
+ return false;
11938
+ }
11939
+ add(kind, name, value, namespace, trustedValueFn) {
11940
+ // TemplateDefinitionBuilder puts duplicate attribute, class, and style values into the consts
11941
+ // array. This seems inefficient, we can probably keep just the first one or the last value
11942
+ // (whichever actually gets applied when multiple values are listed for the same attribute).
11943
+ const allowDuplicates = this.compatibility === CompatibilityMode.TemplateDefinitionBuilder &&
11944
+ (kind === BindingKind.Attribute || kind === BindingKind.ClassName ||
11945
+ kind === BindingKind.StyleProperty);
11946
+ if (!allowDuplicates && this.isKnown(kind, name, value)) {
11932
11947
  return;
11933
11948
  }
11934
- this.known.add(name);
11935
11949
  // TODO: Can this be its own phase
11936
11950
  if (name === 'ngProjectAs') {
11937
11951
  if (value === null || !(value instanceof LiteralExpr) || (value.value == null) ||
@@ -11943,7 +11957,7 @@ class ElementAttributes {
11943
11957
  // attribute. Is this sane?
11944
11958
  }
11945
11959
  const array = this.arrayFor(kind);
11946
- array.push(...getAttributeNameLiterals$1(name));
11960
+ array.push(...getAttributeNameLiterals$1(namespace, name));
11947
11961
  if (kind === BindingKind.Attribute || kind === BindingKind.StyleProperty) {
11948
11962
  if (value === null) {
11949
11963
  throw Error('Attribute, i18n attribute, & style element attributes must have a value');
@@ -11969,13 +11983,10 @@ class ElementAttributes {
11969
11983
  /**
11970
11984
  * Gets an array of literal expressions representing the attribute's namespaced name.
11971
11985
  */
11972
- function getAttributeNameLiterals$1(name) {
11973
- const [attributeNamespace, attributeName] = splitNsName(name);
11974
- const nameLiteral = literal(attributeName);
11975
- if (attributeNamespace) {
11976
- return [
11977
- literal(0 /* core.AttributeMarker.NamespaceURI */), literal(attributeNamespace), nameLiteral
11978
- ];
11986
+ function getAttributeNameLiterals$1(namespace, name) {
11987
+ const nameLiteral = literal(name);
11988
+ if (namespace) {
11989
+ return [literal(0 /* core.AttributeMarker.NamespaceURI */), literal(namespace), nameLiteral];
11979
11990
  }
11980
11991
  return [nameLiteral];
11981
11992
  }
@@ -12043,7 +12054,7 @@ function convertI18nBindings(job) {
12043
12054
  if (op.expression.i18nPlaceholders.length !== op.expression.expressions.length) {
12044
12055
  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`);
12045
12056
  }
12046
- ops.push(createI18nExpressionOp(op.i18nContext, i18nAttributesForElem.target, i18nAttributesForElem.xref, i18nAttributesForElem.handle, expr, op.expression.i18nPlaceholders[i], I18nParamResolutionTime.Creation, I18nExpressionFor.I18nAttribute, op.name, op.sourceSpan));
12057
+ 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));
12047
12058
  }
12048
12059
  OpList.replaceWithMany(op, ops);
12049
12060
  break;
@@ -12080,7 +12091,8 @@ function createDeferDepsFns(job) {
12080
12091
  if (op.handle.slot === null) {
12081
12092
  throw new Error('AssertionError: slot must be assigned bfore extracting defer deps functions');
12082
12093
  }
12083
- op.resolverFn = job.pool.getSharedFunctionReference(depsFnExpr, `${job.componentName}_Defer_${op.handle.slot}_DepsFn`);
12094
+ op.resolverFn = job.pool.getSharedFunctionReference(depsFnExpr, `${job.componentName}_Defer_${op.handle.slot}_DepsFn`,
12095
+ /* Don't use unique names for TDB compatibility */ false);
12084
12096
  }
12085
12097
  }
12086
12098
  }
@@ -12097,75 +12109,119 @@ function createDeferDepsFns(job) {
12097
12109
  * message.)
12098
12110
  */
12099
12111
  function createI18nContexts(job) {
12100
- const rootContexts = new Map();
12101
- let currentI18nOp = null;
12102
- let xref;
12103
- // We use the message instead of the message ID, because placeholder values might differ even
12104
- // when IDs are the same.
12105
- const messageToContext = new Map();
12112
+ // Create i18n context ops for i18n attrs.
12113
+ const attrContextByMessage = new Map();
12114
+ for (const unit of job.units) {
12115
+ for (const op of unit.ops()) {
12116
+ switch (op.kind) {
12117
+ case OpKind.Binding:
12118
+ case OpKind.Property:
12119
+ case OpKind.Attribute:
12120
+ case OpKind.ExtractedAttribute:
12121
+ if (op.i18nMessage === null) {
12122
+ continue;
12123
+ }
12124
+ if (!attrContextByMessage.has(op.i18nMessage)) {
12125
+ const i18nContext = createI18nContextOp(I18nContextKind.Attr, job.allocateXrefId(), null, op.i18nMessage, null);
12126
+ unit.create.push(i18nContext);
12127
+ attrContextByMessage.set(op.i18nMessage, i18nContext.xref);
12128
+ }
12129
+ op.i18nContext = attrContextByMessage.get(op.i18nMessage);
12130
+ break;
12131
+ }
12132
+ }
12133
+ }
12134
+ // Create i18n context ops for root i18n blocks.
12135
+ const blockContextByI18nBlock = new Map();
12106
12136
  for (const unit of job.units) {
12107
12137
  for (const op of unit.create) {
12108
12138
  switch (op.kind) {
12109
12139
  case OpKind.I18nStart:
12110
- currentI18nOp = op;
12111
- // Each root i18n block gets its own context, child ones refer to the context for their
12112
- // root block.
12113
12140
  if (op.xref === op.root) {
12114
- xref = job.allocateXrefId();
12115
- unit.create.push(createI18nContextOp(I18nContextKind.RootI18n, xref, op.xref, op.message, null));
12116
- op.context = xref;
12117
- rootContexts.set(op.xref, xref);
12141
+ const contextOp = createI18nContextOp(I18nContextKind.RootI18n, job.allocateXrefId(), op.xref, op.message, null);
12142
+ unit.create.push(contextOp);
12143
+ op.context = contextOp.xref;
12144
+ blockContextByI18nBlock.set(op.xref, contextOp);
12118
12145
  }
12119
12146
  break;
12147
+ }
12148
+ }
12149
+ }
12150
+ // Assign i18n contexts for child i18n blocks. These don't need their own conext, instead they
12151
+ // should inherit from their root i18n block.
12152
+ for (const unit of job.units) {
12153
+ for (const op of unit.create) {
12154
+ if (op.kind === OpKind.I18nStart && op.xref !== op.root) {
12155
+ const rootContext = blockContextByI18nBlock.get(op.root);
12156
+ if (rootContext === undefined) {
12157
+ throw Error('AssertionError: Root i18n block i18n context should have been created.');
12158
+ }
12159
+ op.context = rootContext.xref;
12160
+ blockContextByI18nBlock.set(op.xref, rootContext);
12161
+ }
12162
+ }
12163
+ }
12164
+ // Create or assign i18n contexts for ICUs.
12165
+ let currentI18nOp = null;
12166
+ for (const unit of job.units) {
12167
+ for (const op of unit.create) {
12168
+ switch (op.kind) {
12169
+ case OpKind.I18nStart:
12170
+ currentI18nOp = op;
12171
+ break;
12120
12172
  case OpKind.I18nEnd:
12121
12173
  currentI18nOp = null;
12122
12174
  break;
12123
12175
  case OpKind.IcuStart:
12124
- // If an ICU represents a different message than its containing block, we give it its own
12125
- // i18n context.
12126
12176
  if (currentI18nOp === null) {
12127
- throw Error('Unexpected ICU outside of an i18n block.');
12177
+ throw Error('AssertionError: Unexpected ICU outside of an i18n block.');
12128
12178
  }
12129
12179
  if (op.message.id !== currentI18nOp.message.id) {
12130
- // There was an enclosing i18n block around this ICU somewhere.
12131
- xref = job.allocateXrefId();
12132
- unit.create.push(createI18nContextOp(I18nContextKind.Icu, xref, currentI18nOp.xref, op.message, null));
12133
- op.context = xref;
12180
+ // This ICU is a sub-message inside its parent i18n block message. We need to give it
12181
+ // its own context.
12182
+ const contextOp = createI18nContextOp(I18nContextKind.Icu, job.allocateXrefId(), currentI18nOp.xref, op.message, null);
12183
+ unit.create.push(contextOp);
12184
+ op.context = contextOp.xref;
12134
12185
  }
12135
12186
  else {
12136
- // The i18n block was generated because of this ICU, OR it was explicit, but the ICU is
12137
- // the only localizable content inside of it.
12187
+ // This ICU is the only translatable content in its parent i18n block. We need to
12188
+ // convert the parent's context into an ICU context.
12138
12189
  op.context = currentI18nOp.context;
12190
+ blockContextByI18nBlock.get(currentI18nOp.xref).contextKind = I18nContextKind.Icu;
12139
12191
  }
12140
12192
  break;
12141
12193
  }
12142
12194
  }
12143
- for (const op of unit.ops()) {
12144
- switch (op.kind) {
12145
- case OpKind.Binding:
12146
- case OpKind.Property:
12147
- case OpKind.Attribute:
12148
- case OpKind.ExtractedAttribute:
12149
- if (!op.i18nMessage) {
12150
- continue;
12151
- }
12152
- if (!messageToContext.has(op.i18nMessage)) {
12153
- // create the context
12154
- const i18nContext = job.allocateXrefId();
12155
- unit.create.push(createI18nContextOp(I18nContextKind.Attr, i18nContext, null, op.i18nMessage, null));
12156
- messageToContext.set(op.i18nMessage, i18nContext);
12157
- }
12158
- op.i18nContext = messageToContext.get(op.i18nMessage);
12159
- break;
12160
- }
12161
- }
12162
12195
  }
12163
- // Assign contexts to child i18n blocks, now that all root i18n blocks have their context
12164
- // assigned.
12196
+ }
12197
+
12198
+ /**
12199
+ * Deduplicate text bindings, e.g. <div class="cls1" class="cls2">
12200
+ */
12201
+ function deduplicateTextBindings(job) {
12202
+ const seen = new Map();
12165
12203
  for (const unit of job.units) {
12166
- for (const op of unit.create) {
12167
- if (op.kind === OpKind.I18nStart && op.xref !== op.root) {
12168
- op.context = rootContexts.get(op.root);
12204
+ for (const op of unit.update.reversed()) {
12205
+ if (op.kind === OpKind.Binding && op.isTextAttribute) {
12206
+ const seenForElement = seen.get(op.target) || new Set();
12207
+ if (seenForElement.has(op.name)) {
12208
+ if (job.compatibility === CompatibilityMode.TemplateDefinitionBuilder) {
12209
+ // For most duplicated attributes, TemplateDefinitionBuilder lists all of the values in
12210
+ // the consts array. However, for style and class attributes it only keeps the last one.
12211
+ // We replicate that behavior here since it has actual consequences for apps with
12212
+ // duplicate class or style attrs.
12213
+ if (op.name === 'style' || op.name === 'class') {
12214
+ OpList.remove(op);
12215
+ }
12216
+ }
12217
+ else {
12218
+ // TODO: Determine the correct behavior. It would probably make sense to merge multiple
12219
+ // style and class attributes. Alternatively we could just throw an error, as HTML
12220
+ // doesn't permit duplicate attributes.
12221
+ }
12222
+ }
12223
+ seenForElement.add(op.name);
12224
+ seen.set(op.target, seenForElement);
12169
12225
  }
12170
12226
  }
12171
12227
  }
@@ -12551,13 +12607,18 @@ const LIST_DELIMITER = '|';
12551
12607
  * used in the final output.
12552
12608
  */
12553
12609
  function extractI18nMessages(job) {
12554
- // Save the i18n start and i18n context ops for later use.
12555
- const i18nContexts = new Map();
12610
+ // Create an i18n message for each context.
12611
+ // TODO: Merge the context op with the message op since they're 1:1 anyways.
12612
+ const i18nMessagesByContext = new Map();
12556
12613
  const i18nBlocks = new Map();
12614
+ const i18nContexts = new Map();
12557
12615
  for (const unit of job.units) {
12558
12616
  for (const op of unit.create) {
12559
12617
  switch (op.kind) {
12560
12618
  case OpKind.I18nContext:
12619
+ const i18nMessageOp = createI18nMessage(job, op);
12620
+ unit.create.push(i18nMessageOp);
12621
+ i18nMessagesByContext.set(op.xref, i18nMessageOp);
12561
12622
  i18nContexts.set(op.xref, op);
12562
12623
  break;
12563
12624
  case OpKind.I18nStart:
@@ -12566,54 +12627,47 @@ function extractI18nMessages(job) {
12566
12627
  }
12567
12628
  }
12568
12629
  }
12569
- // TODO: Miles and I think that contexts have a 1-to-1 correspondence with extracted messages, so
12570
- // this phase can probably be simplified.
12571
- // Extract messages from contexts of i18n attributes.
12572
- for (const unit of job.units) {
12573
- for (const op of unit.create) {
12574
- if (op.kind !== OpKind.I18nContext || op.contextKind !== I18nContextKind.Attr) {
12575
- continue;
12576
- }
12577
- const i18nMessageOp = createI18nMessage(job, op);
12578
- unit.create.push(i18nMessageOp);
12579
- }
12580
- }
12581
- // Extract messages from root i18n blocks.
12582
- const i18nBlockMessages = new Map();
12583
- for (const unit of job.units) {
12584
- for (const op of unit.create) {
12585
- if (op.kind === OpKind.I18nStart && op.xref === op.root) {
12586
- if (!op.context) {
12587
- throw Error('I18n start op should have its context set.');
12588
- }
12589
- const i18nMessageOp = createI18nMessage(job, i18nContexts.get(op.context));
12590
- i18nBlockMessages.set(op.xref, i18nMessageOp);
12591
- unit.create.push(i18nMessageOp);
12592
- }
12593
- }
12594
- }
12595
- // Extract messages from ICUs with their own sub-context.
12630
+ // Associate sub-messages for ICUs with their root message. At this point we can also remove the
12631
+ // ICU start/end ops, as they are no longer needed.
12632
+ let currentIcu = null;
12596
12633
  for (const unit of job.units) {
12597
12634
  for (const op of unit.create) {
12598
12635
  switch (op.kind) {
12599
12636
  case OpKind.IcuStart:
12600
- if (!op.context) {
12601
- throw Error('ICU op should have its context set.');
12637
+ currentIcu = op;
12638
+ OpList.remove(op);
12639
+ // Skip any contexts not associated with an ICU.
12640
+ const icuContext = i18nContexts.get(op.context);
12641
+ if (icuContext.contextKind !== I18nContextKind.Icu) {
12642
+ continue;
12602
12643
  }
12603
- const i18nContext = i18nContexts.get(op.context);
12604
- if (i18nContext.contextKind === I18nContextKind.Icu) {
12605
- if (i18nContext.i18nBlock === null) {
12606
- throw Error('ICU context should have its i18n block set.');
12607
- }
12608
- const subMessage = createI18nMessage(job, i18nContext, op.messagePlaceholder);
12609
- unit.create.push(subMessage);
12610
- const rootI18nId = i18nBlocks.get(i18nContext.i18nBlock).root;
12611
- const parentMessage = i18nBlockMessages.get(rootI18nId);
12612
- parentMessage?.subMessages.push(subMessage.xref);
12644
+ // Skip ICUs that share a context with their i18n message. These represent root-level
12645
+ // ICUs, not sub-messages.
12646
+ const i18nBlock = i18nBlocks.get(icuContext.i18nBlock);
12647
+ if (i18nBlock.context === icuContext.xref) {
12648
+ continue;
12613
12649
  }
12614
- OpList.remove(op);
12650
+ // Find the root message and push this ICUs message as a sub-message.
12651
+ const rootI18nBlock = i18nBlocks.get(i18nBlock.root);
12652
+ const rootMessage = i18nMessagesByContext.get(rootI18nBlock.context);
12653
+ if (rootMessage === undefined) {
12654
+ throw Error('AssertionError: ICU sub-message should belong to a root message.');
12655
+ }
12656
+ const subMessage = i18nMessagesByContext.get(icuContext.xref);
12657
+ subMessage.messagePlaceholder = op.messagePlaceholder;
12658
+ rootMessage.subMessages.push(subMessage.xref);
12615
12659
  break;
12616
12660
  case OpKind.IcuEnd:
12661
+ currentIcu = null;
12662
+ OpList.remove(op);
12663
+ break;
12664
+ case OpKind.IcuPlaceholder:
12665
+ // Add ICU placeholders to the message, then remove the ICU placeholder ops.
12666
+ if (currentIcu === null || currentIcu.context == null) {
12667
+ throw Error('AssertionError: Unexpected ICU placeholder outside of i18n context');
12668
+ }
12669
+ const msg = i18nMessagesByContext.get(currentIcu.context);
12670
+ msg.postprocessingParams.set(op.name, literal(formatIcuPlaceholder(op)));
12617
12671
  OpList.remove(op);
12618
12672
  break;
12619
12673
  }
@@ -12626,14 +12680,19 @@ function extractI18nMessages(job) {
12626
12680
  function createI18nMessage(job, context, messagePlaceholder) {
12627
12681
  let formattedParams = formatParams(context.params);
12628
12682
  const formattedPostprocessingParams = formatParams(context.postprocessingParams);
12629
- let needsPostprocessing = formattedPostprocessingParams.size > 0;
12630
- for (const values of context.params.values()) {
12631
- if (values.length > 1) {
12632
- needsPostprocessing = true;
12633
- }
12634
- }
12683
+ let needsPostprocessing = [...context.params.values()].some(v => v.length > 1);
12635
12684
  return createI18nMessageOp(job.allocateXrefId(), context.xref, context.i18nBlock, context.message, messagePlaceholder ?? null, formattedParams, formattedPostprocessingParams, needsPostprocessing);
12636
12685
  }
12686
+ /**
12687
+ * Formats an ICU placeholder into a single string with expression placeholders.
12688
+ */
12689
+ function formatIcuPlaceholder(op) {
12690
+ if (op.strings.length !== op.expressionPlaceholders.length + 1) {
12691
+ throw Error(`AsserionError: Invalid ICU placeholder with ${op.strings.length} strings and ${op.expressionPlaceholders.length} expressions`);
12692
+ }
12693
+ const values = op.expressionPlaceholders.map(formatValue);
12694
+ return op.strings.flatMap((str, i) => [str, values[i] || '']).join('');
12695
+ }
12637
12696
  /**
12638
12697
  * Formats a map of `I18nParamValue[]` values into a map of `Expression` values.
12639
12698
  */
@@ -12972,7 +13031,7 @@ const BANG_IMPORTANT = '!important';
12972
13031
  */
12973
13032
  function parseHostStyleProperties(job) {
12974
13033
  for (const op of job.root.update) {
12975
- if (op.kind !== OpKind.Binding) {
13034
+ if (!(op.kind === OpKind.Binding && op.bindingKind === BindingKind.Property)) {
12976
13035
  continue;
12977
13036
  }
12978
13037
  if (op.name.endsWith(BANG_IMPORTANT)) {
@@ -12982,7 +13041,7 @@ function parseHostStyleProperties(job) {
12982
13041
  if (op.name.startsWith(STYLE_DOT)) {
12983
13042
  op.bindingKind = BindingKind.StyleProperty;
12984
13043
  op.name = op.name.substring(STYLE_DOT.length);
12985
- if (isCssCustomProperty$1(op.name)) {
13044
+ if (!isCssCustomProperty$1(op.name)) {
12986
13045
  op.name = hyphenate$1(op.name);
12987
13046
  }
12988
13047
  const { property, suffix } = parseProperty$1(op.name);
@@ -20130,7 +20189,7 @@ function collectMessage(job, fileBasedI18nSuffix, messages, messageOp) {
20130
20189
  let transformFn = undefined;
20131
20190
  // If nescessary, add a post-processing step and resolve any placeholder params that are
20132
20191
  // set in post-processing.
20133
- if (messageOp.needsPostprocessing) {
20192
+ if (messageOp.needsPostprocessing || messageOp.postprocessingParams.size > 0) {
20134
20193
  // Sort the post-processing params for consistency with TemaplateDefinitionBuilder output.
20135
20194
  const postprocessingParams = Object.fromEntries([...messageOp.postprocessingParams.entries()].sort());
20136
20195
  const formattedPostprocessingParams = formatI18nPlaceholderNamesInMap(postprocessingParams, /* useCamelCase */ false);
@@ -20160,7 +20219,6 @@ function addSubMessageParams(messageOp, subMessagePlaceholders) {
20160
20219
  else {
20161
20220
  messageOp.params.set(placeholder, literal(`${ESCAPE}${I18N_ICU_MAPPING_PREFIX}${placeholder}${ESCAPE}`));
20162
20221
  messageOp.postprocessingParams.set(placeholder, literalArr(subMessages));
20163
- messageOp.needsPostprocessing = true;
20164
20222
  }
20165
20223
  }
20166
20224
  }
@@ -20246,6 +20304,7 @@ function convertI18nText(job) {
20246
20304
  let currentIcu = null;
20247
20305
  const textNodeI18nBlocks = new Map();
20248
20306
  const textNodeIcus = new Map();
20307
+ const icuPlaceholderByText = new Map();
20249
20308
  for (const op of unit.create) {
20250
20309
  switch (op.kind) {
20251
20310
  case OpKind.I18nStart:
@@ -20270,7 +20329,19 @@ function convertI18nText(job) {
20270
20329
  if (currentI18n !== null) {
20271
20330
  textNodeI18nBlocks.set(op.xref, currentI18n);
20272
20331
  textNodeIcus.set(op.xref, currentIcu);
20273
- OpList.remove(op);
20332
+ if (op.icuPlaceholder !== null) {
20333
+ // Create an op to represent the ICU placeholder. Initially set its static text to the
20334
+ // value of the text op, though this may be overwritten later if this text op is a
20335
+ // placeholder for an interpolation.
20336
+ const icuPlaceholderOp = createIcuPlaceholderOp(job.allocateXrefId(), op.icuPlaceholder, [op.initialValue]);
20337
+ OpList.replace(op, icuPlaceholderOp);
20338
+ icuPlaceholderByText.set(op.xref, icuPlaceholderOp);
20339
+ }
20340
+ else {
20341
+ // Otherwise just remove the text op, since its value is already accounted for in the
20342
+ // translated message.
20343
+ OpList.remove(op);
20344
+ }
20274
20345
  }
20275
20346
  break;
20276
20347
  }
@@ -20285,6 +20356,7 @@ function convertI18nText(job) {
20285
20356
  }
20286
20357
  const i18nOp = textNodeI18nBlocks.get(op.target);
20287
20358
  const icuOp = textNodeIcus.get(op.target);
20359
+ const icuPlaceholder = icuPlaceholderByText.get(op.target);
20288
20360
  const contextId = icuOp ? icuOp.context : i18nOp.context;
20289
20361
  const resolutionTime = icuOp ? I18nParamResolutionTime.Postproccessing :
20290
20362
  I18nParamResolutionTime.Creation;
@@ -20293,9 +20365,14 @@ function convertI18nText(job) {
20293
20365
  const expr = op.interpolation.expressions[i];
20294
20366
  // For now, this i18nExpression depends on the slot context of the enclosing i18n block.
20295
20367
  // Later, we will modify this, and advance to a different point.
20296
- ops.push(createI18nExpressionOp(contextId, i18nOp.xref, i18nOp.xref, i18nOp.handle, expr, op.interpolation.i18nPlaceholders[i], resolutionTime, I18nExpressionFor.I18nText, '', expr.sourceSpan ?? op.sourceSpan));
20368
+ 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));
20297
20369
  }
20298
20370
  OpList.replaceWithMany(op, ops);
20371
+ // If this interpolation is part of an ICU placeholder, add the strings and expressions to
20372
+ // the placeholder.
20373
+ if (icuPlaceholder !== undefined) {
20374
+ icuPlaceholder.strings = op.interpolation.strings;
20375
+ }
20299
20376
  break;
20300
20377
  }
20301
20378
  }
@@ -20404,6 +20481,7 @@ function parse(value) {
20404
20481
  break;
20405
20482
  case 58 /* Char.Colon */:
20406
20483
  if (!currentProp && parenDepth === 0 && quote === 0 /* Char.QuoteNone */) {
20484
+ // TODO: Do not hyphenate CSS custom property names like: `--intentionallyCamelCase`
20407
20485
  currentProp = hyphenate(value.substring(propStart, i - 1).trim());
20408
20486
  valueStart = i;
20409
20487
  }
@@ -20824,21 +20902,39 @@ function keepLast(ops) {
20824
20902
  * class property.
20825
20903
  */
20826
20904
  function parseExtractedStyles(job) {
20905
+ const elements = new Map();
20906
+ for (const unit of job.units) {
20907
+ for (const op of unit.create) {
20908
+ if (isElementOrContainerOp(op)) {
20909
+ elements.set(op.xref, op);
20910
+ }
20911
+ }
20912
+ }
20827
20913
  for (const unit of job.units) {
20828
20914
  for (const op of unit.create) {
20829
20915
  if (op.kind === OpKind.ExtractedAttribute && op.bindingKind === BindingKind.Attribute &&
20830
20916
  isStringLiteral(op.expression)) {
20917
+ const target = elements.get(op.target);
20918
+ if (target !== undefined && target.kind === OpKind.Template &&
20919
+ target.templateKind === TemplateKind.Structural) {
20920
+ // TemplateDefinitionBuilder will not apply class and style bindings to structural
20921
+ // directives; instead, it will leave them as attributes.
20922
+ // (It's not clear what that would mean, anyway -- classes and styles on a structural
20923
+ // element should probably be a parse error.)
20924
+ // TODO: We may be able to remove this once Template Pipeline is the default.
20925
+ continue;
20926
+ }
20831
20927
  if (op.name === 'style') {
20832
20928
  const parsedStyles = parse(op.expression.value);
20833
20929
  for (let i = 0; i < parsedStyles.length - 1; i += 2) {
20834
- OpList.insertBefore(createExtractedAttributeOp(op.target, BindingKind.StyleProperty, parsedStyles[i], literal(parsedStyles[i + 1]), null, null, SecurityContext.STYLE), op);
20930
+ OpList.insertBefore(createExtractedAttributeOp(op.target, BindingKind.StyleProperty, null, parsedStyles[i], literal(parsedStyles[i + 1]), null, null, SecurityContext.STYLE), op);
20835
20931
  }
20836
20932
  OpList.remove(op);
20837
20933
  }
20838
20934
  else if (op.name === 'class') {
20839
20935
  const parsedClasses = op.expression.value.trim().split(/\s+/g);
20840
20936
  for (const parsedClass of parsedClasses) {
20841
- OpList.insertBefore(createExtractedAttributeOp(op.target, BindingKind.ClassName, parsedClass, null, null, null, SecurityContext.NONE), op);
20937
+ OpList.insertBefore(createExtractedAttributeOp(op.target, BindingKind.ClassName, null, parsedClass, null, null, null, SecurityContext.NONE), op);
20842
20938
  }
20843
20939
  OpList.remove(op);
20844
20940
  }
@@ -21224,9 +21320,7 @@ function namespaceMath() {
21224
21320
  return call(Identifiers.namespaceMathML, [], null);
21225
21321
  }
21226
21322
  function advance(delta, sourceSpan) {
21227
- return call(Identifiers.advance, [
21228
- literal(delta),
21229
- ], sourceSpan);
21323
+ return call(Identifiers.advance, delta > 1 ? [literal(delta)] : [], sourceSpan);
21230
21324
  }
21231
21325
  function reference(slot) {
21232
21326
  return importExpr(Identifiers.reference).callFn([
@@ -21364,10 +21458,13 @@ function property(name, expression, sanitizer, sourceSpan) {
21364
21458
  }
21365
21459
  return call(Identifiers.property, args, sourceSpan);
21366
21460
  }
21367
- function attribute(name, expression, sanitizer) {
21461
+ function attribute(name, expression, sanitizer, namespace) {
21368
21462
  const args = [literal(name), expression];
21369
- if (sanitizer !== null) {
21370
- args.push(sanitizer);
21463
+ if (sanitizer !== null || namespace !== null) {
21464
+ args.push(sanitizer ?? literal(null));
21465
+ }
21466
+ if (namespace !== null) {
21467
+ args.push(literal(namespace));
21371
21468
  }
21372
21469
  return call(Identifiers.attribute, args, null);
21373
21470
  }
@@ -21719,19 +21816,19 @@ function reifyCreateOperations(unit, ops) {
21719
21816
  OpList.replace(op, text(op.handle.slot, op.initialValue, op.sourceSpan));
21720
21817
  break;
21721
21818
  case OpKind.ElementStart:
21722
- OpList.replace(op, elementStart(op.handle.slot, op.tag, op.attributes, op.localRefs, op.sourceSpan));
21819
+ OpList.replace(op, elementStart(op.handle.slot, op.tag, op.attributes, op.localRefs, op.startSourceSpan));
21723
21820
  break;
21724
21821
  case OpKind.Element:
21725
- OpList.replace(op, element(op.handle.slot, op.tag, op.attributes, op.localRefs, op.sourceSpan));
21822
+ OpList.replace(op, element(op.handle.slot, op.tag, op.attributes, op.localRefs, op.wholeSourceSpan));
21726
21823
  break;
21727
21824
  case OpKind.ElementEnd:
21728
21825
  OpList.replace(op, elementEnd(op.sourceSpan));
21729
21826
  break;
21730
21827
  case OpKind.ContainerStart:
21731
- OpList.replace(op, elementContainerStart(op.handle.slot, op.attributes, op.localRefs, op.sourceSpan));
21828
+ OpList.replace(op, elementContainerStart(op.handle.slot, op.attributes, op.localRefs, op.startSourceSpan));
21732
21829
  break;
21733
21830
  case OpKind.Container:
21734
- OpList.replace(op, elementContainer(op.handle.slot, op.attributes, op.localRefs, op.sourceSpan));
21831
+ OpList.replace(op, elementContainer(op.handle.slot, op.attributes, op.localRefs, op.wholeSourceSpan));
21735
21832
  break;
21736
21833
  case OpKind.ContainerEnd:
21737
21834
  OpList.replace(op, elementContainerEnd());
@@ -21759,7 +21856,7 @@ function reifyCreateOperations(unit, ops) {
21759
21856
  throw new Error(`AssertionError: local refs array should have been extracted into a constant`);
21760
21857
  }
21761
21858
  const childView = unit.job.views.get(op.xref);
21762
- OpList.replace(op, template(op.handle.slot, variable(childView.fnName), childView.decls, childView.vars, op.tag, op.attributes, op.localRefs, op.sourceSpan));
21859
+ OpList.replace(op, template(op.handle.slot, variable(childView.fnName), childView.decls, childView.vars, op.tag, op.attributes, op.localRefs, op.startSourceSpan));
21763
21860
  break;
21764
21861
  case OpKind.DisableBindings:
21765
21862
  OpList.replace(op, disableBindings());
@@ -21774,7 +21871,7 @@ function reifyCreateOperations(unit, ops) {
21774
21871
  const listenerFn = reifyListenerHandler(unit, op.handlerFnName, op.handlerOps, op.consumesDollarEvent);
21775
21872
  const eventTargetResolver = op.eventTarget ? GLOBAL_TARGET_RESOLVERS$1.get(op.eventTarget) : null;
21776
21873
  if (eventTargetResolver === undefined) {
21777
- throw new Error(`AssertionError: unknown event target ${op.eventTarget}`);
21874
+ throw new Error(`Unexpected global target '${op.eventTarget}' defined for '${op.name}' event. Supported list of global targets: window,document,body.`);
21778
21875
  }
21779
21876
  OpList.replace(op, listener(op.name, listenerFn, eventTargetResolver, op.hostListener && op.isAnimationListener, op.sourceSpan));
21780
21877
  break;
@@ -21861,7 +21958,7 @@ function reifyCreateOperations(unit, ops) {
21861
21958
  emptyDecls = emptyView.decls;
21862
21959
  emptyVars = emptyView.vars;
21863
21960
  }
21864
- 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));
21961
+ 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.wholeSourceSpan));
21865
21962
  break;
21866
21963
  case OpKind.Statement:
21867
21964
  // Pass statement operations directly through.
@@ -21927,7 +22024,7 @@ function reifyUpdateOperations(_unit, ops) {
21927
22024
  OpList.replace(op, attributeInterpolate(op.name, op.expression.strings, op.expression.expressions, op.sanitizer, op.sourceSpan));
21928
22025
  }
21929
22026
  else {
21930
- OpList.replace(op, attribute(op.name, op.expression, op.sanitizer));
22027
+ OpList.replace(op, attribute(op.name, op.expression, op.sanitizer, op.namespace));
21931
22028
  }
21932
22029
  break;
21933
22030
  case OpKind.HostProperty:
@@ -22116,42 +22213,6 @@ function removeUnusedI18nAttributesOps(job) {
22116
22213
  }
22117
22214
  }
22118
22215
 
22119
- /**
22120
- * Inside the body of a repeater, certain context variables (such as `$first`) are ambiently
22121
- * available. This phase finds those variable usages, and replaces them with the appropriate
22122
- * expression.
22123
- */
22124
- function generateRepeaterDerivedVars(job) {
22125
- const repeaters = new Map();
22126
- for (const unit of job.units) {
22127
- for (const op of unit.ops()) {
22128
- if (op.kind === OpKind.RepeaterCreate) {
22129
- repeaters.set(op.xref, op);
22130
- }
22131
- }
22132
- }
22133
- for (const unit of job.units) {
22134
- for (const op of unit.ops()) {
22135
- transformExpressionsInOp(op, expr => {
22136
- if (!(expr instanceof DerivedRepeaterVarExpr)) {
22137
- return expr;
22138
- }
22139
- const repeaterOp = repeaters.get(expr.xref);
22140
- switch (expr.identity) {
22141
- case DerivedRepeaterVarIdentity.First:
22142
- return new BinaryOperatorExpr(BinaryOperator.Identical, new LexicalReadExpr(repeaterOp.varNames.$index), literal(0));
22143
- case DerivedRepeaterVarIdentity.Last:
22144
- return new BinaryOperatorExpr(BinaryOperator.Identical, new LexicalReadExpr(repeaterOp.varNames.$index), new BinaryOperatorExpr(BinaryOperator.Minus, new LexicalReadExpr(repeaterOp.varNames.$count), literal(1)));
22145
- case DerivedRepeaterVarIdentity.Even:
22146
- return new BinaryOperatorExpr(BinaryOperator.Identical, new BinaryOperatorExpr(BinaryOperator.Modulo, new LexicalReadExpr(repeaterOp.varNames.$index), literal(2)), literal(0));
22147
- case DerivedRepeaterVarIdentity.Odd:
22148
- return new BinaryOperatorExpr(BinaryOperator.NotIdentical, new BinaryOperatorExpr(BinaryOperator.Modulo, new LexicalReadExpr(repeaterOp.varNames.$index), literal(2)), literal(0));
22149
- }
22150
- }, VisitorContextFlag.None);
22151
- }
22152
- }
22153
- }
22154
-
22155
22216
  /**
22156
22217
  * Resolves `ir.ContextExpr` expressions (which represent embedded view or component contexts) to
22157
22218
  * either the `ctx` parameter to component functions (for the current view context) or to variables
@@ -22491,6 +22552,7 @@ function resolveI18nExpressionPlaceholders(job) {
22491
22552
  // Record all of the i18n context ops, and the sub-template index for each i18n op.
22492
22553
  const subTemplateIndicies = new Map();
22493
22554
  const i18nContexts = new Map();
22555
+ const icuPlaceholders = new Map();
22494
22556
  for (const unit of job.units) {
22495
22557
  for (const op of unit.create) {
22496
22558
  switch (op.kind) {
@@ -22500,6 +22562,9 @@ function resolveI18nExpressionPlaceholders(job) {
22500
22562
  case OpKind.I18nContext:
22501
22563
  i18nContexts.set(op.xref, op);
22502
22564
  break;
22565
+ case OpKind.IcuPlaceholder:
22566
+ icuPlaceholders.set(op.xref, op);
22567
+ break;
22503
22568
  }
22504
22569
  }
22505
22570
  }
@@ -22513,76 +22578,32 @@ function resolveI18nExpressionPlaceholders(job) {
22513
22578
  for (const unit of job.units) {
22514
22579
  for (const op of unit.update) {
22515
22580
  if (op.kind === OpKind.I18nExpression) {
22516
- const i18nContext = i18nContexts.get(op.context);
22517
22581
  const index = expressionIndices.get(referenceIndex(op)) || 0;
22518
22582
  const subTemplateIndex = subTemplateIndicies.get(op.i18nOwner) ?? null;
22519
- // Add the expression index in the appropriate params map.
22520
- const params = op.resolutionTime === I18nParamResolutionTime.Creation ?
22521
- i18nContext.params :
22522
- i18nContext.postprocessingParams;
22523
- const values = params.get(op.i18nPlaceholder) || [];
22524
- values.push({
22583
+ const value = {
22525
22584
  value: index,
22526
22585
  subTemplateIndex: subTemplateIndex,
22527
22586
  flags: I18nParamValueFlags.ExpressionIndex
22528
- });
22529
- params.set(op.i18nPlaceholder, values);
22587
+ };
22588
+ updatePlaceholder(op, value, i18nContexts, icuPlaceholders);
22530
22589
  expressionIndices.set(referenceIndex(op), index + 1);
22531
22590
  }
22532
22591
  }
22533
22592
  }
22534
22593
  }
22535
-
22536
- /**
22537
- * Resolves placeholders for element tags inside of an ICU.
22538
- */
22539
- function resolveI18nIcuPlaceholders(job) {
22540
- for (const unit of job.units) {
22541
- for (const op of unit.create) {
22542
- if (op.kind === OpKind.I18nContext && op.contextKind === I18nContextKind.Icu) {
22543
- for (const node of op.message.nodes) {
22544
- node.visit(new ResolveIcuPlaceholdersVisitor(op.postprocessingParams));
22545
- }
22546
- }
22547
- }
22594
+ function updatePlaceholder(op, value, i18nContexts, icuPlaceholders) {
22595
+ if (op.i18nPlaceholder !== null) {
22596
+ const i18nContext = i18nContexts.get(op.context);
22597
+ const params = op.resolutionTime === I18nParamResolutionTime.Creation ?
22598
+ i18nContext.params :
22599
+ i18nContext.postprocessingParams;
22600
+ const values = params.get(op.i18nPlaceholder) || [];
22601
+ values.push(value);
22602
+ params.set(op.i18nPlaceholder, values);
22548
22603
  }
22549
- }
22550
- /**
22551
- * Visitor for i18n AST that resolves ICU params into the given map.
22552
- */
22553
- class ResolveIcuPlaceholdersVisitor extends RecurseVisitor {
22554
- constructor(params) {
22555
- super();
22556
- this.params = params;
22557
- }
22558
- visitContainerPlaceholder(placeholder) {
22559
- // Add the start and end source span for container placeholders. These need to be recorded for
22560
- // elements inside ICUs. The slots for the nodes were recorded separately under the i18n
22561
- // block's context as part of the `resolveI18nElementPlaceholders` phase.
22562
- if (placeholder.startName && placeholder.startSourceSpan &&
22563
- !this.params.has(placeholder.startName)) {
22564
- this.params.set(placeholder.startName, [{
22565
- value: placeholder.startSourceSpan?.toString(),
22566
- subTemplateIndex: null,
22567
- flags: I18nParamValueFlags.None
22568
- }]);
22569
- }
22570
- if (placeholder.closeName && placeholder.endSourceSpan &&
22571
- !this.params.has(placeholder.closeName)) {
22572
- this.params.set(placeholder.closeName, [{
22573
- value: placeholder.endSourceSpan?.toString(),
22574
- subTemplateIndex: null,
22575
- flags: I18nParamValueFlags.None
22576
- }]);
22577
- }
22578
- }
22579
- visitTagPlaceholder(placeholder) {
22580
- super.visitTagPlaceholder(placeholder);
22581
- this.visitContainerPlaceholder(placeholder);
22582
- }
22583
- visitBlockPlaceholder(placeholder) {
22584
- super.visitBlockPlaceholder(placeholder);
22585
- this.visitContainerPlaceholder(placeholder);
22604
+ if (op.icuPlaceholder !== null) {
22605
+ const icuPlaceholderOp = icuPlaceholders.get(op.icuPlaceholder);
22606
+ icuPlaceholderOp?.expressionPlaceholders.push(value);
22586
22607
  }
22587
22608
  }
22588
22609
 
@@ -23257,7 +23278,14 @@ function varsUsedByOp(op) {
23257
23278
  return op.interpolation.expressions.length;
23258
23279
  case OpKind.I18nExpression:
23259
23280
  case OpKind.Conditional:
23281
+ case OpKind.DeferWhen:
23260
23282
  return 1;
23283
+ case OpKind.RepeaterCreate:
23284
+ // Repeaters may require an extra variable binding slot, if they have an empty view, for the
23285
+ // empty block tracking.
23286
+ // TODO: It's a bit odd to have a create mode instruction consume variable slots. Maybe we can
23287
+ // find a way to use the Repeater update op instead.
23288
+ return op.emptyView ? 1 : 0;
23261
23289
  default:
23262
23290
  throw new Error(`Unhandled op: ${OpKind[op.kind]}`);
23263
23291
  }
@@ -23741,6 +23769,7 @@ const phases = [
23741
23769
  { kind: CompilationJobKind.Tmpl, fn: emitNamespaceChanges },
23742
23770
  { kind: CompilationJobKind.Tmpl, fn: propagateI18nBlocks },
23743
23771
  { kind: CompilationJobKind.Tmpl, fn: wrapI18nIcus },
23772
+ { kind: CompilationJobKind.Both, fn: deduplicateTextBindings },
23744
23773
  { kind: CompilationJobKind.Both, fn: specializeStyleBindings },
23745
23774
  { kind: CompilationJobKind.Both, fn: specializeBindings },
23746
23775
  { kind: CompilationJobKind.Both, fn: extractAttributes },
@@ -23764,7 +23793,6 @@ const phases = [
23764
23793
  { kind: CompilationJobKind.Tmpl, fn: saveAndRestoreView },
23765
23794
  { kind: CompilationJobKind.Both, fn: deleteAnyCasts },
23766
23795
  { kind: CompilationJobKind.Both, fn: resolveDollarEvent },
23767
- { kind: CompilationJobKind.Tmpl, fn: generateRepeaterDerivedVars },
23768
23796
  { kind: CompilationJobKind.Tmpl, fn: generateTrackVariables },
23769
23797
  { kind: CompilationJobKind.Both, fn: resolveNames },
23770
23798
  { kind: CompilationJobKind.Tmpl, fn: resolveDeferTargetNames },
@@ -23779,7 +23807,6 @@ const phases = [
23779
23807
  { kind: CompilationJobKind.Tmpl, fn: createDeferDepsFns },
23780
23808
  { kind: CompilationJobKind.Tmpl, fn: resolveI18nElementPlaceholders },
23781
23809
  { kind: CompilationJobKind.Tmpl, fn: resolveI18nExpressionPlaceholders },
23782
- { kind: CompilationJobKind.Tmpl, fn: resolveI18nIcuPlaceholders },
23783
23810
  { kind: CompilationJobKind.Tmpl, fn: extractI18nMessages },
23784
23811
  { kind: CompilationJobKind.Tmpl, fn: generateTrackFns },
23785
23812
  { kind: CompilationJobKind.Tmpl, fn: collectI18nConsts },
@@ -23939,7 +23966,7 @@ function ingestHostBinding(input, bindingParser, constantPool) {
23939
23966
  const securityContexts = bindingParser
23940
23967
  .calcPossibleSecurityContexts(input.componentSelector, property.name, bindingKind === BindingKind.Attribute)
23941
23968
  .filter(context => context !== SecurityContext.NONE);
23942
- ingestHostProperty(job, property, bindingKind, false, securityContexts);
23969
+ ingestHostProperty(job, property, bindingKind, securityContexts);
23943
23970
  }
23944
23971
  for (const [name, expr] of Object.entries(input.attributes) ?? []) {
23945
23972
  const securityContexts = bindingParser.calcPossibleSecurityContexts(input.componentSelector, name, true)
@@ -23953,7 +23980,7 @@ function ingestHostBinding(input, bindingParser, constantPool) {
23953
23980
  }
23954
23981
  // TODO: We should refactor the parser to use the same types and structures for host bindings as
23955
23982
  // with ordinary components. This would allow us to share a lot more ingestion code.
23956
- function ingestHostProperty(job, property, bindingKind, isTextAttribute, securityContexts) {
23983
+ function ingestHostProperty(job, property, bindingKind, securityContexts) {
23957
23984
  let expression;
23958
23985
  const ast = property.expression.ast;
23959
23986
  if (ast instanceof Interpolation$1) {
@@ -23962,20 +23989,21 @@ function ingestHostProperty(job, property, bindingKind, isTextAttribute, securit
23962
23989
  else {
23963
23990
  expression = convertAst(ast, job, property.sourceSpan);
23964
23991
  }
23965
- 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));
23992
+ 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));
23966
23993
  }
23967
23994
  function ingestHostAttribute(job, name, value, securityContexts) {
23968
- const attrBinding = createBindingOp(job.root.xref, BindingKind.Attribute, name, value, null, securityContexts, true, false, null,
23995
+ const attrBinding = createBindingOp(job.root.xref, BindingKind.Attribute, name, value, null, securityContexts,
23996
+ /* Host attributes should always be extracted to const hostAttrs, even if they are not
23997
+ *strictly* text literals */
23998
+ true, false, null,
23969
23999
  /* TODO */ null,
23970
- /* TODO: host attribute source spans */ null);
24000
+ /** TODO: May be null? */ value.sourceSpan);
23971
24001
  job.root.update.push(attrBinding);
23972
24002
  }
23973
24003
  function ingestHostEvent(job, event) {
23974
24004
  const [phase, target] = event.type === 0 /* e.ParsedEventType.Regular */ ? [null, event.targetOrPhase] :
23975
24005
  [event.targetOrPhase, null];
23976
- const eventBinding = createListenerOp(job.root.xref, new SlotHandle(), event.name, null, [], phase, target, true, event.sourceSpan);
23977
- // TODO: Can this be a chain?
23978
- eventBinding.handlerOps.push(createStatementOp(new ReturnStatement(convertAst(event.handler.ast, job, event.sourceSpan), event.handlerSpan)));
24006
+ const eventBinding = createListenerOp(job.root.xref, new SlotHandle(), event.name, null, makeListenerHandlerOps(job.root, event.handler, event.handlerSpan), phase, target, true, event.sourceSpan);
23979
24007
  job.root.create.push(eventBinding);
23980
24008
  }
23981
24009
  /**
@@ -23993,10 +24021,10 @@ function ingestNodes(unit, template) {
23993
24021
  ingestContent(unit, node);
23994
24022
  }
23995
24023
  else if (node instanceof Text$3) {
23996
- ingestText(unit, node);
24024
+ ingestText(unit, node, null);
23997
24025
  }
23998
24026
  else if (node instanceof BoundText) {
23999
- ingestBoundText(unit, node);
24027
+ ingestBoundText(unit, node, null);
24000
24028
  }
24001
24029
  else if (node instanceof IfBlock) {
24002
24030
  ingestIfBlock(unit, node);
@@ -24028,7 +24056,7 @@ function ingestElement(unit, element) {
24028
24056
  }
24029
24057
  const id = unit.job.allocateXrefId();
24030
24058
  const [namespaceKey, elementName] = splitNsName(element.name);
24031
- const startOp = createElementStartOp(elementName, id, namespaceForKey(namespaceKey), element.i18n instanceof TagPlaceholder ? element.i18n : undefined, element.startSourceSpan);
24059
+ const startOp = createElementStartOp(elementName, id, namespaceForKey(namespaceKey), element.i18n instanceof TagPlaceholder ? element.i18n : undefined, element.startSourceSpan, element.sourceSpan);
24032
24060
  unit.create.push(startOp);
24033
24061
  ingestElementBindings(unit, startOp, element);
24034
24062
  ingestReferences(startOp, element);
@@ -24071,7 +24099,7 @@ function ingestTemplate(unit, tmpl) {
24071
24099
  '' :
24072
24100
  prefixWithNamespace(tagNameWithoutNamespace, namespace);
24073
24101
  const templateKind = isPlainTemplate(tmpl) ? TemplateKind.NgTemplate : TemplateKind.Structural;
24074
- const templateOp = createTemplateOp(childView.xref, templateKind, tagNameWithoutNamespace, functionNameSuffix, namespace, i18nPlaceholder, tmpl.startSourceSpan);
24102
+ const templateOp = createTemplateOp(childView.xref, templateKind, tagNameWithoutNamespace, functionNameSuffix, namespace, i18nPlaceholder, tmpl.startSourceSpan, tmpl.sourceSpan);
24075
24103
  unit.create.push(templateOp);
24076
24104
  ingestTemplateBindings(unit, templateOp, tmpl, templateKind);
24077
24105
  ingestReferences(templateOp, tmpl);
@@ -24106,13 +24134,13 @@ function ingestContent(unit, content) {
24106
24134
  /**
24107
24135
  * Ingest a literal text node from the AST into the given `ViewCompilation`.
24108
24136
  */
24109
- function ingestText(unit, text) {
24110
- unit.create.push(createTextOp(unit.job.allocateXrefId(), text.value, text.sourceSpan));
24137
+ function ingestText(unit, text, icuPlaceholder) {
24138
+ unit.create.push(createTextOp(unit.job.allocateXrefId(), text.value, icuPlaceholder, text.sourceSpan));
24111
24139
  }
24112
24140
  /**
24113
24141
  * Ingest an interpolated text node from the AST into the given `ViewCompilation`.
24114
24142
  */
24115
- function ingestBoundText(unit, text, i18nPlaceholders) {
24143
+ function ingestBoundText(unit, text, icuPlaceholder) {
24116
24144
  let value = text.value;
24117
24145
  if (value instanceof ASTWithSource) {
24118
24146
  value = value.ast;
@@ -24123,19 +24151,16 @@ function ingestBoundText(unit, text, i18nPlaceholders) {
24123
24151
  if (text.i18n !== undefined && !(text.i18n instanceof Container)) {
24124
24152
  throw Error(`Unhandled i18n metadata type for text interpolation: ${text.i18n?.constructor.name}`);
24125
24153
  }
24126
- if (i18nPlaceholders === undefined) {
24127
- // TODO: We probably can just use the placeholders field, instead of walking the AST.
24128
- i18nPlaceholders = text.i18n instanceof Container ?
24129
- text.i18n.children
24130
- .filter((node) => node instanceof Placeholder)
24131
- .map(placeholder => placeholder.name) :
24132
- [];
24133
- }
24154
+ const i18nPlaceholders = text.i18n instanceof Container ?
24155
+ text.i18n.children
24156
+ .filter((node) => node instanceof Placeholder)
24157
+ .map(placeholder => placeholder.name) :
24158
+ [];
24134
24159
  if (i18nPlaceholders.length > 0 && i18nPlaceholders.length !== value.expressions.length) {
24135
24160
  throw Error(`Unexpected number of i18n placeholders (${value.expressions.length}) for BoundText with ${value.expressions.length} expressions`);
24136
24161
  }
24137
24162
  const textXref = unit.job.allocateXrefId();
24138
- unit.create.push(createTextOp(textXref, '', text.sourceSpan));
24163
+ unit.create.push(createTextOp(textXref, '', icuPlaceholder, text.sourceSpan));
24139
24164
  // TemplateDefinitionBuilder does not generate source maps for sub-expressions inside an
24140
24165
  // interpolation. We copy that behavior in compatibility mode.
24141
24166
  // TODO: is it actually correct to generate these extra maps in modern mode?
@@ -24168,7 +24193,7 @@ function ingestIfBlock(unit, ifBlock) {
24168
24193
  }
24169
24194
  ifCaseI18nMeta = ifCase.i18n;
24170
24195
  }
24171
- const templateOp = createTemplateOp(cView.xref, TemplateKind.Block, tagName, 'Conditional', Namespace.HTML, ifCaseI18nMeta, ifCase.sourceSpan);
24196
+ const templateOp = createTemplateOp(cView.xref, TemplateKind.Block, tagName, 'Conditional', Namespace.HTML, ifCaseI18nMeta, ifCase.startSourceSpan, ifCase.sourceSpan);
24172
24197
  unit.create.push(templateOp);
24173
24198
  if (firstXref === null) {
24174
24199
  firstXref = cView.xref;
@@ -24186,6 +24211,10 @@ function ingestIfBlock(unit, ifBlock) {
24186
24211
  * Ingest an `@switch` block into the given `ViewCompilation`.
24187
24212
  */
24188
24213
  function ingestSwitchBlock(unit, switchBlock) {
24214
+ // Don't ingest empty switches since they won't render anything.
24215
+ if (switchBlock.cases.length === 0) {
24216
+ return;
24217
+ }
24189
24218
  let firstXref = null;
24190
24219
  let firstSlotHandle = null;
24191
24220
  let conditions = [];
@@ -24198,7 +24227,7 @@ function ingestSwitchBlock(unit, switchBlock) {
24198
24227
  }
24199
24228
  switchCaseI18nMeta = switchCase.i18n;
24200
24229
  }
24201
- const templateOp = createTemplateOp(cView.xref, TemplateKind.Block, null, 'Case', Namespace.HTML, switchCaseI18nMeta, switchCase.sourceSpan);
24230
+ const templateOp = createTemplateOp(cView.xref, TemplateKind.Block, null, 'Case', Namespace.HTML, switchCaseI18nMeta, switchCase.startSourceSpan, switchCase.sourceSpan);
24202
24231
  unit.create.push(templateOp);
24203
24232
  if (firstXref === null) {
24204
24233
  firstXref = cView.xref;
@@ -24223,7 +24252,7 @@ function ingestDeferView(unit, suffix, i18nMeta, children, sourceSpan) {
24223
24252
  }
24224
24253
  const secondaryView = unit.job.allocateView(unit.xref);
24225
24254
  ingestNodes(secondaryView, children);
24226
- const templateOp = createTemplateOp(secondaryView.xref, TemplateKind.Block, null, `Defer${suffix}`, Namespace.HTML, i18nMeta, sourceSpan);
24255
+ const templateOp = createTemplateOp(secondaryView.xref, TemplateKind.Block, null, `Defer${suffix}`, Namespace.HTML, i18nMeta, sourceSpan, sourceSpan);
24227
24256
  unit.create.push(templateOp);
24228
24257
  return templateOp;
24229
24258
  }
@@ -24301,6 +24330,11 @@ function ingestDeferBlock(unit, deferBlock) {
24301
24330
  deferOnOps.push(deferOnOp);
24302
24331
  }
24303
24332
  if (triggers.when !== undefined) {
24333
+ if (triggers.when.value instanceof Interpolation$1) {
24334
+ // TemplateDefinitionBuilder supports this case, but it's very strange to me. What would it
24335
+ // even mean?
24336
+ throw new Error(`Unexpected interpolation in defer block when trigger`);
24337
+ }
24304
24338
  const deferOnOp = createDeferWhenOp(deferXref, convertAst(triggers.when.value, unit.job, triggers.when.sourceSpan), prefetch, triggers.when.sourceSpan);
24305
24339
  deferWhenOps.push(deferOnOp);
24306
24340
  }
@@ -24320,10 +24354,10 @@ function ingestIcu(unit, icu) {
24320
24354
  unit.create.push(createIcuStartOp(xref, icu.i18n, icuFromI18nMessage(icu.i18n).name, null));
24321
24355
  for (const [placeholder, text] of Object.entries({ ...icu.vars, ...icu.placeholders })) {
24322
24356
  if (text instanceof BoundText) {
24323
- ingestBoundText(unit, text, [placeholder]);
24357
+ ingestBoundText(unit, text, placeholder);
24324
24358
  }
24325
24359
  else {
24326
- ingestText(unit, text);
24360
+ ingestText(unit, text, placeholder);
24327
24361
  }
24328
24362
  }
24329
24363
  unit.create.push(createIcuEndOp(xref));
@@ -24337,22 +24371,43 @@ function ingestIcu(unit, icu) {
24337
24371
  */
24338
24372
  function ingestForBlock(unit, forBlock) {
24339
24373
  const repeaterView = unit.job.allocateView(unit.xref);
24340
- const createRepeaterAlias = (ident, repeaterVar) => {
24341
- repeaterView.aliases.add({
24342
- kind: SemanticVariableKind.Alias,
24343
- name: null,
24344
- identifier: ident,
24345
- expression: new DerivedRepeaterVarExpr(repeaterView.xref, repeaterVar),
24346
- });
24347
- };
24348
24374
  // Set all the context variables and aliases available in the repeater.
24349
24375
  repeaterView.contextVariables.set(forBlock.item.name, forBlock.item.value);
24350
24376
  repeaterView.contextVariables.set(forBlock.contextVariables.$index.name, forBlock.contextVariables.$index.value);
24351
24377
  repeaterView.contextVariables.set(forBlock.contextVariables.$count.name, forBlock.contextVariables.$count.value);
24352
- createRepeaterAlias(forBlock.contextVariables.$first.name, DerivedRepeaterVarIdentity.First);
24353
- createRepeaterAlias(forBlock.contextVariables.$last.name, DerivedRepeaterVarIdentity.Last);
24354
- createRepeaterAlias(forBlock.contextVariables.$even.name, DerivedRepeaterVarIdentity.Even);
24355
- createRepeaterAlias(forBlock.contextVariables.$odd.name, DerivedRepeaterVarIdentity.Odd);
24378
+ // We copy TemplateDefinitionBuilder's scheme of creating names for `$count` and `$index` that are
24379
+ // suffixed with special information, to disambiguate which level of nested loop the below aliases
24380
+ // refer to.
24381
+ // TODO: We should refactor Template Pipeline's variable phases to gracefully handle shadowing,
24382
+ // and arbitrarily many levels of variables depending on each other.
24383
+ const indexName = `ɵ${forBlock.contextVariables.$index.name}_${repeaterView.xref}`;
24384
+ const countName = `ɵ${forBlock.contextVariables.$count.name}_${repeaterView.xref}`;
24385
+ repeaterView.contextVariables.set(indexName, forBlock.contextVariables.$index.value);
24386
+ repeaterView.contextVariables.set(countName, forBlock.contextVariables.$count.value);
24387
+ repeaterView.aliases.add({
24388
+ kind: SemanticVariableKind.Alias,
24389
+ name: null,
24390
+ identifier: forBlock.contextVariables.$first.name,
24391
+ expression: new LexicalReadExpr(indexName).identical(literal(0))
24392
+ });
24393
+ repeaterView.aliases.add({
24394
+ kind: SemanticVariableKind.Alias,
24395
+ name: null,
24396
+ identifier: forBlock.contextVariables.$last.name,
24397
+ expression: new LexicalReadExpr(indexName).identical(new LexicalReadExpr(countName).minus(literal(1)))
24398
+ });
24399
+ repeaterView.aliases.add({
24400
+ kind: SemanticVariableKind.Alias,
24401
+ name: null,
24402
+ identifier: forBlock.contextVariables.$even.name,
24403
+ expression: new LexicalReadExpr(indexName).modulo(literal(2)).identical(literal(0))
24404
+ });
24405
+ repeaterView.aliases.add({
24406
+ kind: SemanticVariableKind.Alias,
24407
+ name: null,
24408
+ identifier: forBlock.contextVariables.$odd.name,
24409
+ expression: new LexicalReadExpr(indexName).modulo(literal(2)).notIdentical(literal(0))
24410
+ });
24356
24411
  const sourceSpan = convertSourceSpan(forBlock.trackBy.span, forBlock.sourceSpan);
24357
24412
  const track = convertAst(forBlock.trackBy, unit.job, sourceSpan);
24358
24413
  ingestNodes(repeaterView, forBlock.children);
@@ -24380,7 +24435,7 @@ function ingestForBlock(unit, forBlock) {
24380
24435
  const i18nPlaceholder = forBlock.i18n;
24381
24436
  const emptyI18nPlaceholder = forBlock.empty?.i18n;
24382
24437
  const tagName = ingestControlFlowInsertionPoint(unit, repeaterView.xref, forBlock);
24383
- const repeaterCreate = createRepeaterCreateOp(repeaterView.xref, emptyView?.xref ?? null, tagName, track, varNames, i18nPlaceholder, emptyI18nPlaceholder, forBlock.sourceSpan);
24438
+ const repeaterCreate = createRepeaterCreateOp(repeaterView.xref, emptyView?.xref ?? null, tagName, track, varNames, i18nPlaceholder, emptyI18nPlaceholder, forBlock.startSourceSpan, forBlock.sourceSpan);
24384
24439
  unit.create.push(repeaterCreate);
24385
24440
  const expression = convertAst(forBlock.expression, unit.job, convertSourceSpan(forBlock.expression.span, forBlock.sourceSpan));
24386
24441
  const repeater = createRepeaterOp(repeaterCreate.xref, repeaterCreate.handle, expression, forBlock.sourceSpan);
@@ -24498,13 +24553,13 @@ function convertAst(ast, job, baseSourceSpan) {
24498
24553
  throw new Error(`Unhandled expression type "${ast.constructor.name}" in file "${baseSourceSpan?.start.file.url}"`);
24499
24554
  }
24500
24555
  }
24501
- function convertAstWithInterpolation(job, value, i18nMeta) {
24556
+ function convertAstWithInterpolation(job, value, i18nMeta, sourceSpan) {
24502
24557
  let expression;
24503
24558
  if (value instanceof Interpolation$1) {
24504
- expression = new Interpolation(value.strings, value.expressions.map(e => convertAst(e, job, null)), Object.keys(asMessage(i18nMeta)?.placeholders ?? {}));
24559
+ expression = new Interpolation(value.strings, value.expressions.map(e => convertAst(e, job, sourceSpan ?? null)), Object.keys(asMessage(i18nMeta)?.placeholders ?? {}));
24505
24560
  }
24506
24561
  else if (value instanceof AST) {
24507
- expression = convertAst(value, job, null);
24562
+ expression = convertAst(value, job, sourceSpan ?? null);
24508
24563
  }
24509
24564
  else {
24510
24565
  expression = literal(value);
@@ -24617,7 +24672,7 @@ function ingestTemplateBindings(unit, op, template, templateKind) {
24617
24672
  output.type !== 1 /* e.ParsedEventType.Animation */) {
24618
24673
  // Animation bindings are excluded from the structural template's const array.
24619
24674
  const securityContext = domSchema.securityContext(NG_TEMPLATE_TAG_NAME$1, output.name, false);
24620
- unit.create.push(createExtractedAttributeOp(op.xref, BindingKind.Property, output.name, null, null, null, securityContext));
24675
+ unit.create.push(createExtractedAttributeOp(op.xref, BindingKind.Property, null, output.name, null, null, null, securityContext));
24621
24676
  }
24622
24677
  }
24623
24678
  // TODO: Perhaps we could do this in a phase? (It likely wouldn't change the slot indices.)
@@ -24665,7 +24720,7 @@ function createTemplateBinding(view, xref, type, name, value, unit, securityCont
24665
24720
  // inner node of a structural template. We can't skip it entirely, because we still need it on
24666
24721
  // the ng-template's consts (e.g. for the purposes of directive matching). However, we should
24667
24722
  // not generate an update instruction for it.
24668
- return createExtractedAttributeOp(xref, BindingKind.Property, name, null, null, i18nMessage, securityContext);
24723
+ return createExtractedAttributeOp(xref, BindingKind.Property, null, name, null, null, i18nMessage, securityContext);
24669
24724
  }
24670
24725
  if (!isTextBinding && (type === 1 /* e.BindingType.Attribute */ || type === 4 /* e.BindingType.Animation */)) {
24671
24726
  // Again, this binding doesn't really target the ng-template; it actually targets the element
@@ -28467,6 +28522,9 @@ class TemplateDefinitionBuilder {
28467
28522
  this.updateInstructionWithAdvance(containerIndex, block.branches[0].sourceSpan, Identifiers.conditional, paramsCallback);
28468
28523
  }
28469
28524
  visitSwitchBlock(block) {
28525
+ if (block.cases.length === 0) {
28526
+ return;
28527
+ }
28470
28528
  // We have to process the block in two steps: once here and again in the update instruction
28471
28529
  // callback in order to generate the correct expressions when pipes or pure functions are used.
28472
28530
  const caseData = block.cases.map(currentCase => {
@@ -28925,7 +28983,7 @@ class TemplateDefinitionBuilder {
28925
28983
  if (delta < 1) {
28926
28984
  throw new Error('advance instruction can only go forwards');
28927
28985
  }
28928
- this.instructionFn(this._updateCodeFns, span, Identifiers.advance, [literal(delta)]);
28986
+ this.instructionFn(this._updateCodeFns, span, Identifiers.advance, delta > 1 ? [literal(delta)] : []);
28929
28987
  this._currentIndex = nodeIndex;
28930
28988
  }
28931
28989
  }
@@ -29479,12 +29537,16 @@ class BindingScope {
29479
29537
  }
29480
29538
  /** Binding scope of a `track` function inside a `for` loop block. */
29481
29539
  class TrackByBindingScope extends BindingScope {
29482
- constructor(parentScope, globalAliases) {
29540
+ constructor(parentScope, globalOverrides) {
29483
29541
  super(parentScope.bindingLevel + 1, parentScope);
29484
- this.globalAliases = globalAliases;
29542
+ this.globalOverrides = globalOverrides;
29485
29543
  this.componentAccessCount = 0;
29486
29544
  }
29487
29545
  get(name) {
29546
+ // Intercept any overridden globals.
29547
+ if (this.globalOverrides.hasOwnProperty(name)) {
29548
+ return variable(this.globalOverrides[name]);
29549
+ }
29488
29550
  let current = this.parent;
29489
29551
  // Prevent accesses of template variables outside the `for` loop.
29490
29552
  while (current) {
@@ -29493,10 +29555,6 @@ class TrackByBindingScope extends BindingScope {
29493
29555
  }
29494
29556
  current = current.parent;
29495
29557
  }
29496
- // Intercept any aliased globals.
29497
- if (this.globalAliases[name]) {
29498
- return variable(this.globalAliases[name]);
29499
- }
29500
29558
  // When the component scope is accessed, we redirect it through `this`.
29501
29559
  this.componentAccessCount++;
29502
29560
  return variable('this').prop(name);
@@ -31994,7 +32052,7 @@ function publishFacade(global) {
31994
32052
  * @description
31995
32053
  * Entry point for all public APIs of the compiler package.
31996
32054
  */
31997
- const VERSION = new Version('17.0.7');
32055
+ const VERSION = new Version('17.0.9');
31998
32056
 
31999
32057
  class CompilerConfig {
32000
32058
  constructor({ defaultEncapsulation = ViewEncapsulation.Emulated, preserveWhitespaces, strictInjectionParameters } = {}) {
@@ -33560,7 +33618,7 @@ const MINIMUM_PARTIAL_LINKER_VERSION$6 = '12.0.0';
33560
33618
  function compileDeclareClassMetadata(metadata) {
33561
33619
  const definitionMap = new DefinitionMap();
33562
33620
  definitionMap.set('minVersion', literal(MINIMUM_PARTIAL_LINKER_VERSION$6));
33563
- definitionMap.set('version', literal('17.0.7'));
33621
+ definitionMap.set('version', literal('17.0.9'));
33564
33622
  definitionMap.set('ngImport', importExpr(Identifiers.core));
33565
33623
  definitionMap.set('type', metadata.type);
33566
33624
  definitionMap.set('decorators', metadata.decorators);
@@ -33668,7 +33726,7 @@ function createDirectiveDefinitionMap(meta) {
33668
33726
  // in 16.1 is actually used.
33669
33727
  const minVersion = hasTransformFunctions ? MINIMUM_PARTIAL_LINKER_VERSION$5 : '14.0.0';
33670
33728
  definitionMap.set('minVersion', literal(minVersion));
33671
- definitionMap.set('version', literal('17.0.7'));
33729
+ definitionMap.set('version', literal('17.0.9'));
33672
33730
  // e.g. `type: MyDirective`
33673
33731
  definitionMap.set('type', meta.type.value);
33674
33732
  if (meta.isStandalone) {
@@ -33945,7 +34003,7 @@ const MINIMUM_PARTIAL_LINKER_VERSION$4 = '12.0.0';
33945
34003
  function compileDeclareFactoryFunction(meta) {
33946
34004
  const definitionMap = new DefinitionMap();
33947
34005
  definitionMap.set('minVersion', literal(MINIMUM_PARTIAL_LINKER_VERSION$4));
33948
- definitionMap.set('version', literal('17.0.7'));
34006
+ definitionMap.set('version', literal('17.0.9'));
33949
34007
  definitionMap.set('ngImport', importExpr(Identifiers.core));
33950
34008
  definitionMap.set('type', meta.type.value);
33951
34009
  definitionMap.set('deps', compileDependencies(meta.deps));
@@ -33980,7 +34038,7 @@ function compileDeclareInjectableFromMetadata(meta) {
33980
34038
  function createInjectableDefinitionMap(meta) {
33981
34039
  const definitionMap = new DefinitionMap();
33982
34040
  definitionMap.set('minVersion', literal(MINIMUM_PARTIAL_LINKER_VERSION$3));
33983
- definitionMap.set('version', literal('17.0.7'));
34041
+ definitionMap.set('version', literal('17.0.9'));
33984
34042
  definitionMap.set('ngImport', importExpr(Identifiers.core));
33985
34043
  definitionMap.set('type', meta.type.value);
33986
34044
  // Only generate providedIn property if it has a non-null value
@@ -34031,7 +34089,7 @@ function compileDeclareInjectorFromMetadata(meta) {
34031
34089
  function createInjectorDefinitionMap(meta) {
34032
34090
  const definitionMap = new DefinitionMap();
34033
34091
  definitionMap.set('minVersion', literal(MINIMUM_PARTIAL_LINKER_VERSION$2));
34034
- definitionMap.set('version', literal('17.0.7'));
34092
+ definitionMap.set('version', literal('17.0.9'));
34035
34093
  definitionMap.set('ngImport', importExpr(Identifiers.core));
34036
34094
  definitionMap.set('type', meta.type.value);
34037
34095
  definitionMap.set('providers', meta.providers);
@@ -34064,7 +34122,7 @@ function createNgModuleDefinitionMap(meta) {
34064
34122
  throw new Error('Invalid path! Local compilation mode should not get into the partial compilation path');
34065
34123
  }
34066
34124
  definitionMap.set('minVersion', literal(MINIMUM_PARTIAL_LINKER_VERSION$1));
34067
- definitionMap.set('version', literal('17.0.7'));
34125
+ definitionMap.set('version', literal('17.0.9'));
34068
34126
  definitionMap.set('ngImport', importExpr(Identifiers.core));
34069
34127
  definitionMap.set('type', meta.type.value);
34070
34128
  // We only generate the keys in the metadata if the arrays contain values.
@@ -34115,7 +34173,7 @@ function compileDeclarePipeFromMetadata(meta) {
34115
34173
  function createPipeDefinitionMap(meta) {
34116
34174
  const definitionMap = new DefinitionMap();
34117
34175
  definitionMap.set('minVersion', literal(MINIMUM_PARTIAL_LINKER_VERSION));
34118
- definitionMap.set('version', literal('17.0.7'));
34176
+ definitionMap.set('version', literal('17.0.9'));
34119
34177
  definitionMap.set('ngImport', importExpr(Identifiers.core));
34120
34178
  // e.g. `type: MyPipe`
34121
34179
  definitionMap.set('type', meta.type.value);