@angular/compiler 17.1.0-next.4 → 17.1.0-next.5

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 (44) 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/template.mjs +14 -9
  11. package/esm2022/src/template/pipeline/ir/src/enums.mjs +7 -3
  12. package/esm2022/src/template/pipeline/ir/src/expression.mjs +15 -1
  13. package/esm2022/src/template/pipeline/ir/src/ops/create.mjs +35 -13
  14. package/esm2022/src/template/pipeline/ir/src/ops/update.mjs +4 -2
  15. package/esm2022/src/template/pipeline/src/emit.mjs +3 -3
  16. package/esm2022/src/template/pipeline/src/ingest.mjs +67 -41
  17. package/esm2022/src/template/pipeline/src/instruction.mjs +17 -11
  18. package/esm2022/src/template/pipeline/src/phases/attribute_extraction.mjs +10 -15
  19. package/esm2022/src/template/pipeline/src/phases/const_collection.mjs +48 -12
  20. package/esm2022/src/template/pipeline/src/phases/convert_i18n_bindings.mjs +2 -2
  21. package/esm2022/src/template/pipeline/src/phases/create_defer_deps_fns.mjs +3 -2
  22. package/esm2022/src/template/pipeline/src/phases/create_i18n_contexts.mjs +63 -51
  23. package/esm2022/src/template/pipeline/src/phases/deduplicate_text_bindings.mjs +40 -0
  24. package/esm2022/src/template/pipeline/src/phases/extract_i18n_messages.mjs +52 -49
  25. package/esm2022/src/template/pipeline/src/phases/host_style_property_parsing.mjs +2 -2
  26. package/esm2022/src/template/pipeline/src/phases/i18n_const_collection.mjs +2 -3
  27. package/esm2022/src/template/pipeline/src/phases/i18n_text_extraction.mjs +22 -3
  28. package/esm2022/src/template/pipeline/src/phases/naming.mjs +13 -5
  29. package/esm2022/src/template/pipeline/src/phases/ordering.mjs +17 -5
  30. package/esm2022/src/template/pipeline/src/phases/parse_extracted_styles.mjs +19 -1
  31. package/esm2022/src/template/pipeline/src/phases/phase_remove_content_selectors.mjs +1 -10
  32. package/esm2022/src/template/pipeline/src/phases/propagate_i18n_blocks.mjs +5 -3
  33. package/esm2022/src/template/pipeline/src/phases/reify.mjs +36 -11
  34. package/esm2022/src/template/pipeline/src/phases/resolve_i18n_expression_placeholders.mjs +23 -10
  35. package/esm2022/src/template/pipeline/src/phases/track_fn_generation.mjs +4 -1
  36. package/esm2022/src/template/pipeline/src/phases/var_counting.mjs +8 -1
  37. package/esm2022/src/template/pipeline/src/phases/wrap_icus.mjs +4 -3
  38. package/esm2022/src/template/pipeline/src/util/elements.mjs +8 -1
  39. package/esm2022/src/version.mjs +1 -1
  40. package/fesm2022/compiler.mjs +518 -302
  41. package/fesm2022/compiler.mjs.map +1 -1
  42. package/index.d.ts +3 -3
  43. package/package.json +2 -2
  44. 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-next.5
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
  }
@@ -3688,13 +3690,18 @@ var TagContentType;
3688
3690
  TagContentType[TagContentType["ESCAPABLE_RAW_TEXT"] = 1] = "ESCAPABLE_RAW_TEXT";
3689
3691
  TagContentType[TagContentType["PARSABLE_DATA"] = 2] = "PARSABLE_DATA";
3690
3692
  })(TagContentType || (TagContentType = {}));
3691
- function splitNsName(elementName) {
3693
+ function splitNsName(elementName, fatal = true) {
3692
3694
  if (elementName[0] != ':') {
3693
3695
  return [null, elementName];
3694
3696
  }
3695
3697
  const colonIndex = elementName.indexOf(':', 1);
3696
3698
  if (colonIndex === -1) {
3697
- throw new Error(`Unsupported format "${elementName}" expecting ":namespace:name"`);
3699
+ if (fatal) {
3700
+ throw new Error(`Unsupported format "${elementName}" expecting ":namespace:name"`);
3701
+ }
3702
+ else {
3703
+ return [null, elementName];
3704
+ }
3698
3705
  }
3699
3706
  return [elementName.slice(1, colonIndex), elementName.slice(colonIndex + 1)];
3700
3707
  }
@@ -8975,14 +8982,18 @@ var OpKind;
8975
8982
  * An instruction to update an ICU expression.
8976
8983
  */
8977
8984
  OpKind[OpKind["IcuEnd"] = 42] = "IcuEnd";
8985
+ /**
8986
+ * An instruction representing a placeholder in an ICU expression.
8987
+ */
8988
+ OpKind[OpKind["IcuPlaceholder"] = 43] = "IcuPlaceholder";
8978
8989
  /**
8979
8990
  * An i18n context containing information needed to generate an i18n message.
8980
8991
  */
8981
- OpKind[OpKind["I18nContext"] = 43] = "I18nContext";
8992
+ OpKind[OpKind["I18nContext"] = 44] = "I18nContext";
8982
8993
  /**
8983
8994
  * A creation op that corresponds to i18n attributes on an element.
8984
8995
  */
8985
- OpKind[OpKind["I18nAttributes"] = 44] = "I18nAttributes";
8996
+ OpKind[OpKind["I18nAttributes"] = 45] = "I18nAttributes";
8986
8997
  })(OpKind || (OpKind = {}));
8987
8998
  /**
8988
8999
  * Distinguishes different kinds of IR expressions.
@@ -9581,12 +9592,13 @@ function createDeferWhenOp(target, expr, prefetch, sourceSpan) {
9581
9592
  sourceSpan,
9582
9593
  ...NEW_OP,
9583
9594
  ...TRAIT_DEPENDS_ON_SLOT_CONTEXT,
9595
+ ...TRAIT_CONSUMES_VARS,
9584
9596
  };
9585
9597
  }
9586
9598
  /**
9587
9599
  * Create an i18n expression op.
9588
9600
  */
9589
- function createI18nExpressionOp(context, target, i18nOwner, handle, expression, i18nPlaceholder, resolutionTime, usage, name, sourceSpan) {
9601
+ function createI18nExpressionOp(context, target, i18nOwner, handle, expression, icuPlaceholder, i18nPlaceholder, resolutionTime, usage, name, sourceSpan) {
9590
9602
  return {
9591
9603
  kind: OpKind.I18nExpression,
9592
9604
  context,
@@ -9594,6 +9606,7 @@ function createI18nExpressionOp(context, target, i18nOwner, handle, expression,
9594
9606
  i18nOwner,
9595
9607
  handle,
9596
9608
  expression,
9609
+ icuPlaceholder,
9597
9610
  i18nPlaceholder,
9598
9611
  resolutionTime,
9599
9612
  usage,
@@ -10363,6 +10376,9 @@ function transformExpressionsInOp(op, transform, flags) {
10363
10376
  op.placeholderConfig =
10364
10377
  transformExpressionsInExpression(op.placeholderConfig, transform, flags);
10365
10378
  }
10379
+ if (op.resolverFn !== null) {
10380
+ op.resolverFn = transformExpressionsInExpression(op.resolverFn, transform, flags);
10381
+ }
10366
10382
  break;
10367
10383
  case OpKind.I18nMessage:
10368
10384
  for (const [placeholder, expr] of op.params) {
@@ -10399,6 +10415,7 @@ function transformExpressionsInOp(op, transform, flags) {
10399
10415
  case OpKind.Template:
10400
10416
  case OpKind.Text:
10401
10417
  case OpKind.I18nAttributes:
10418
+ case OpKind.IcuPlaceholder:
10402
10419
  // These operations contain no expressions.
10403
10420
  break;
10404
10421
  default:
@@ -10481,6 +10498,16 @@ function transformExpressionsInExpression(expr, transform, flags) {
10481
10498
  expr.template.expressions =
10482
10499
  expr.template.expressions.map(e => transformExpressionsInExpression(e, transform, flags));
10483
10500
  }
10501
+ else if (expr instanceof ArrowFunctionExpr) {
10502
+ if (Array.isArray(expr.body)) {
10503
+ for (let i = 0; i < expr.body.length; i++) {
10504
+ transformExpressionsInStatement(expr.body[i], transform, flags);
10505
+ }
10506
+ }
10507
+ else {
10508
+ expr.body = transformExpressionsInExpression(expr.body, transform, flags);
10509
+ }
10510
+ }
10484
10511
  else if (expr instanceof WrappedNodeExpr) {
10485
10512
  // TODO: Do we need to transform any TS nodes nested inside of this expression?
10486
10513
  }
@@ -10806,7 +10833,7 @@ function isElementOrContainerOp(op) {
10806
10833
  /**
10807
10834
  * Create an `ElementStartOp`.
10808
10835
  */
10809
- function createElementStartOp(tag, xref, namespace, i18nPlaceholder, sourceSpan) {
10836
+ function createElementStartOp(tag, xref, namespace, i18nPlaceholder, startSourceSpan, wholeSourceSpan) {
10810
10837
  return {
10811
10838
  kind: OpKind.ElementStart,
10812
10839
  xref,
@@ -10817,7 +10844,8 @@ function createElementStartOp(tag, xref, namespace, i18nPlaceholder, sourceSpan)
10817
10844
  nonBindable: false,
10818
10845
  namespace,
10819
10846
  i18nPlaceholder,
10820
- sourceSpan,
10847
+ startSourceSpan,
10848
+ wholeSourceSpan,
10821
10849
  ...TRAIT_CONSUMES_SLOT,
10822
10850
  ...NEW_OP,
10823
10851
  };
@@ -10825,7 +10853,7 @@ function createElementStartOp(tag, xref, namespace, i18nPlaceholder, sourceSpan)
10825
10853
  /**
10826
10854
  * Create a `TemplateOp`.
10827
10855
  */
10828
- function createTemplateOp(xref, templateKind, tag, functionNameSuffix, namespace, i18nPlaceholder, sourceSpan) {
10856
+ function createTemplateOp(xref, templateKind, tag, functionNameSuffix, namespace, i18nPlaceholder, startSourceSpan, wholeSourceSpan) {
10829
10857
  return {
10830
10858
  kind: OpKind.Template,
10831
10859
  xref,
@@ -10840,12 +10868,13 @@ function createTemplateOp(xref, templateKind, tag, functionNameSuffix, namespace
10840
10868
  nonBindable: false,
10841
10869
  namespace,
10842
10870
  i18nPlaceholder,
10843
- sourceSpan,
10871
+ startSourceSpan,
10872
+ wholeSourceSpan,
10844
10873
  ...TRAIT_CONSUMES_SLOT,
10845
10874
  ...NEW_OP,
10846
10875
  };
10847
10876
  }
10848
- function createRepeaterCreateOp(primaryView, emptyView, tag, track, varNames, i18nPlaceholder, emptyI18nPlaceholder, sourceSpan) {
10877
+ function createRepeaterCreateOp(primaryView, emptyView, tag, track, varNames, emptyTag, i18nPlaceholder, emptyI18nPlaceholder, startSourceSpan, wholeSourceSpan) {
10849
10878
  return {
10850
10879
  kind: OpKind.RepeaterCreate,
10851
10880
  attributes: null,
@@ -10855,6 +10884,8 @@ function createRepeaterCreateOp(primaryView, emptyView, tag, track, varNames, i1
10855
10884
  track,
10856
10885
  trackByFn: null,
10857
10886
  tag,
10887
+ emptyTag,
10888
+ emptyAttributes: null,
10858
10889
  functionNameSuffix: 'For',
10859
10890
  namespace: Namespace.HTML,
10860
10891
  nonBindable: false,
@@ -10865,9 +10896,11 @@ function createRepeaterCreateOp(primaryView, emptyView, tag, track, varNames, i1
10865
10896
  usesComponentInstance: false,
10866
10897
  i18nPlaceholder,
10867
10898
  emptyI18nPlaceholder,
10868
- sourceSpan,
10899
+ startSourceSpan,
10900
+ wholeSourceSpan,
10869
10901
  ...TRAIT_CONSUMES_SLOT,
10870
10902
  ...NEW_OP,
10903
+ ...TRAIT_CONSUMES_VARS,
10871
10904
  numSlotsUsed: emptyView === null ? 2 : 3,
10872
10905
  };
10873
10906
  }
@@ -10899,12 +10932,13 @@ function createEnableBindingsOp(xref) {
10899
10932
  /**
10900
10933
  * Create a `TextOp`.
10901
10934
  */
10902
- function createTextOp(xref, initialValue, sourceSpan) {
10935
+ function createTextOp(xref, initialValue, icuPlaceholder, sourceSpan) {
10903
10936
  return {
10904
10937
  kind: OpKind.Text,
10905
10938
  xref,
10906
10939
  handle: new SlotHandle(),
10907
10940
  initialValue,
10941
+ icuPlaceholder,
10908
10942
  sourceSpan,
10909
10943
  ...TRAIT_CONSUMES_SLOT,
10910
10944
  ...NEW_OP,
@@ -10957,7 +10991,7 @@ function createProjectionDefOp(def) {
10957
10991
  ...NEW_OP,
10958
10992
  };
10959
10993
  }
10960
- function createProjectionOp(xref, selector, i18nPlaceholder, attributes, sourceSpan) {
10994
+ function createProjectionOp(xref, selector, i18nPlaceholder, sourceSpan) {
10961
10995
  return {
10962
10996
  kind: OpKind.Projection,
10963
10997
  xref,
@@ -10965,7 +10999,7 @@ function createProjectionOp(xref, selector, i18nPlaceholder, attributes, sourceS
10965
10999
  selector,
10966
11000
  i18nPlaceholder,
10967
11001
  projectionSlotIndex: 0,
10968
- attributes,
11002
+ attributes: null,
10969
11003
  localRefs: [],
10970
11004
  sourceSpan,
10971
11005
  ...NEW_OP,
@@ -11046,7 +11080,7 @@ function createI18nMessageOp(xref, i18nContext, i18nBlock, message, messagePlace
11046
11080
  /**
11047
11081
  * Create an `I18nStartOp`.
11048
11082
  */
11049
- function createI18nStartOp(xref, message, root) {
11083
+ function createI18nStartOp(xref, message, root, sourceSpan) {
11050
11084
  return {
11051
11085
  kind: OpKind.I18nStart,
11052
11086
  xref,
@@ -11056,6 +11090,7 @@ function createI18nStartOp(xref, message, root) {
11056
11090
  messageIndex: null,
11057
11091
  subTemplateIndex: null,
11058
11092
  context: null,
11093
+ sourceSpan,
11059
11094
  ...NEW_OP,
11060
11095
  ...TRAIT_CONSUMES_SLOT,
11061
11096
  };
@@ -11063,10 +11098,11 @@ function createI18nStartOp(xref, message, root) {
11063
11098
  /**
11064
11099
  * Create an `I18nEndOp`.
11065
11100
  */
11066
- function createI18nEndOp(xref) {
11101
+ function createI18nEndOp(xref, sourceSpan) {
11067
11102
  return {
11068
11103
  kind: OpKind.I18nEnd,
11069
11104
  xref,
11105
+ sourceSpan,
11070
11106
  ...NEW_OP,
11071
11107
  };
11072
11108
  }
@@ -11094,6 +11130,19 @@ function createIcuEndOp(xref) {
11094
11130
  ...NEW_OP,
11095
11131
  };
11096
11132
  }
11133
+ /**
11134
+ * Creates an ICU placeholder op.
11135
+ */
11136
+ function createIcuPlaceholderOp(xref, name, strings) {
11137
+ return {
11138
+ kind: OpKind.IcuPlaceholder,
11139
+ xref,
11140
+ name,
11141
+ strings,
11142
+ expressionPlaceholders: [],
11143
+ ...NEW_OP,
11144
+ };
11145
+ }
11097
11146
  function createI18nContextOp(contextKind, xref, i18nBlock, message, sourceSpan) {
11098
11147
  if (i18nBlock === null && contextKind !== I18nContextKind.Attr) {
11099
11148
  throw new Error('AssertionError: i18nBlock must be provided for non-attribute contexts.');
@@ -11475,6 +11524,13 @@ function createOpXrefMap(unit) {
11475
11524
  continue;
11476
11525
  }
11477
11526
  map.set(op.xref, op);
11527
+ // TODO(dylhunn): `@for` loops with `@empty` blocks need to be special-cased here,
11528
+ // because the slot consumer trait currently only supports one slot per consumer and we
11529
+ // need two. This should be revisited when making the refactors mentioned in:
11530
+ // https://github.com/angular/angular/pull/53620#discussion_r1430918822
11531
+ if (op.kind === OpKind.RepeaterCreate && op.emptyView !== null) {
11532
+ map.set(op.emptyView, op);
11533
+ }
11478
11534
  }
11479
11535
  return map;
11480
11536
  }
@@ -11530,6 +11586,11 @@ function extractAttributes(job) {
11530
11586
  /* i18nContext */ null,
11531
11587
  /* i18nMessage */ null, SecurityContext.NONE);
11532
11588
  if (job.kind === CompilationJobKind.Host) {
11589
+ if (job.compatibility) {
11590
+ // TemplateDefinitionBuilder does not extract listener bindings to the const array
11591
+ // (which is honestly pretty inconsistent).
11592
+ break;
11593
+ }
11533
11594
  // This attribute will apply to the enclosing host binding compilation unit, so order
11534
11595
  // doesn't matter.
11535
11596
  unit.create.push(extractedAttributeOp);
@@ -11560,21 +11621,11 @@ function extractAttributeOp(unit, op, elements) {
11560
11621
  if (op.expression instanceof Interpolation) {
11561
11622
  return;
11562
11623
  }
11563
- let extractable = op.expression.isConstant();
11624
+ let extractable = op.isTextAttribute || op.expression.isConstant();
11564
11625
  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
- }
11626
+ // TemplateDefinitionBuilder only extracts text attributes. It does not extract attriibute
11627
+ // bindings, even if they are constants.
11628
+ extractable &&= op.isTextAttribute;
11578
11629
  }
11579
11630
  if (extractable) {
11580
11631
  const extractedAttributeOp = createExtractedAttributeOp(op.target, op.isStructuralTemplateAttribute ? BindingKind.Template : BindingKind.Attribute, op.name, op.expression, op.i18nContext, op.i18nMessage, op.securityContext);
@@ -11868,7 +11919,7 @@ function collectElementConsts(job) {
11868
11919
  for (const unit of job.units) {
11869
11920
  for (const op of unit.create) {
11870
11921
  if (op.kind === OpKind.ExtractedAttribute) {
11871
- const attributes = allElementAttributes.get(op.target) || new ElementAttributes();
11922
+ const attributes = allElementAttributes.get(op.target) || new ElementAttributes(job.compatibility);
11872
11923
  allElementAttributes.set(op.target, attributes);
11873
11924
  attributes.add(op.bindingKind, op.name, op.expression, op.trustedValueFn);
11874
11925
  OpList.remove(op);
@@ -11879,15 +11930,26 @@ function collectElementConsts(job) {
11879
11930
  if (job instanceof ComponentCompilationJob) {
11880
11931
  for (const unit of job.units) {
11881
11932
  for (const op of unit.create) {
11882
- if (isElementOrContainerOp(op)) {
11933
+ // TODO: Simplify and combine these cases.
11934
+ if (op.kind == OpKind.Projection) {
11883
11935
  const attributes = allElementAttributes.get(op.xref);
11884
11936
  if (attributes !== undefined) {
11885
11937
  const attrArray = serializeAttributes(attributes);
11886
11938
  if (attrArray.entries.length > 0) {
11887
- op.attributes = job.addConst(attrArray);
11939
+ op.attributes = attrArray;
11888
11940
  }
11889
11941
  }
11890
11942
  }
11943
+ else if (isElementOrContainerOp(op)) {
11944
+ op.attributes = getConstIndex(job, allElementAttributes, op.xref);
11945
+ // TODO(dylhunn): `@for` loops with `@empty` blocks need to be special-cased here,
11946
+ // because the slot consumer trait currently only supports one slot per consumer and we
11947
+ // need two. This should be revisited when making the refactors mentioned in:
11948
+ // https://github.com/angular/angular/pull/53620#discussion_r1430918822
11949
+ if (op.kind === OpKind.RepeaterCreate && op.emptyView !== null) {
11950
+ op.emptyAttributes = getConstIndex(job, allElementAttributes, op.emptyView);
11951
+ }
11952
+ }
11891
11953
  }
11892
11954
  }
11893
11955
  }
@@ -11905,6 +11967,16 @@ function collectElementConsts(job) {
11905
11967
  }
11906
11968
  }
11907
11969
  }
11970
+ function getConstIndex(job, allElementAttributes, xref) {
11971
+ const attributes = allElementAttributes.get(xref);
11972
+ if (attributes !== undefined) {
11973
+ const attrArray = serializeAttributes(attributes);
11974
+ if (attrArray.entries.length > 0) {
11975
+ return job.addConst(attrArray);
11976
+ }
11977
+ }
11978
+ return null;
11979
+ }
11908
11980
  /**
11909
11981
  * Shared instance of an empty array to avoid unnecessary array allocations.
11910
11982
  */
@@ -11913,11 +11985,6 @@ const FLYWEIGHT_ARRAY = Object.freeze([]);
11913
11985
  * Container for all of the various kinds of attributes which are applied on an element.
11914
11986
  */
11915
11987
  class ElementAttributes {
11916
- constructor() {
11917
- this.known = new Set();
11918
- this.byKind = new Map;
11919
- this.projectAs = null;
11920
- }
11921
11988
  get attributes() {
11922
11989
  return this.byKind.get(BindingKind.Attribute) ?? FLYWEIGHT_ARRAY;
11923
11990
  }
@@ -11936,11 +12003,31 @@ class ElementAttributes {
11936
12003
  get i18n() {
11937
12004
  return this.byKind.get(BindingKind.I18n) ?? FLYWEIGHT_ARRAY;
11938
12005
  }
12006
+ constructor(compatibility) {
12007
+ this.compatibility = compatibility;
12008
+ this.known = new Map();
12009
+ this.byKind = new Map;
12010
+ this.projectAs = null;
12011
+ }
12012
+ isKnown(kind, name, value) {
12013
+ const nameToValue = this.known.get(kind) ?? new Set();
12014
+ this.known.set(kind, nameToValue);
12015
+ if (nameToValue.has(name)) {
12016
+ return true;
12017
+ }
12018
+ nameToValue.add(name);
12019
+ return false;
12020
+ }
11939
12021
  add(kind, name, value, trustedValueFn) {
11940
- if (this.known.has(name)) {
12022
+ // TemplateDefinitionBuilder puts duplicate attribute, class, and style values into the consts
12023
+ // array. This seems inefficient, we can probably keep just the first one or the last value
12024
+ // (whichever actually gets applied when multiple values are listed for the same attribute).
12025
+ const allowDuplicates = this.compatibility === CompatibilityMode.TemplateDefinitionBuilder &&
12026
+ (kind === BindingKind.Attribute || kind === BindingKind.ClassName ||
12027
+ kind === BindingKind.StyleProperty);
12028
+ if (!allowDuplicates && this.isKnown(kind, name, value)) {
11941
12029
  return;
11942
12030
  }
11943
- this.known.add(name);
11944
12031
  // TODO: Can this be its own phase
11945
12032
  if (name === 'ngProjectAs') {
11946
12033
  if (value === null || !(value instanceof LiteralExpr) || (value.value == null) ||
@@ -11979,7 +12066,7 @@ class ElementAttributes {
11979
12066
  * Gets an array of literal expressions representing the attribute's namespaced name.
11980
12067
  */
11981
12068
  function getAttributeNameLiterals$1(name) {
11982
- const [attributeNamespace, attributeName] = splitNsName(name);
12069
+ const [attributeNamespace, attributeName] = splitNsName(name, false);
11983
12070
  const nameLiteral = literal(attributeName);
11984
12071
  if (attributeNamespace) {
11985
12072
  return [
@@ -12052,7 +12139,7 @@ function convertI18nBindings(job) {
12052
12139
  if (op.expression.i18nPlaceholders.length !== op.expression.expressions.length) {
12053
12140
  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
12141
  }
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));
12142
+ 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
12143
  }
12057
12144
  OpList.replaceWithMany(op, ops);
12058
12145
  break;
@@ -12089,7 +12176,8 @@ function createDeferDepsFns(job) {
12089
12176
  if (op.handle.slot === null) {
12090
12177
  throw new Error('AssertionError: slot must be assigned bfore extracting defer deps functions');
12091
12178
  }
12092
- op.resolverFn = job.pool.getSharedFunctionReference(depsFnExpr, `${job.componentName}_Defer_${op.handle.slot}_DepsFn`);
12179
+ op.resolverFn = job.pool.getSharedFunctionReference(depsFnExpr, `${job.componentName}_Defer_${op.handle.slot}_DepsFn`,
12180
+ /* Don't use unique names for TDB compatibility */ false);
12093
12181
  }
12094
12182
  }
12095
12183
  }
@@ -12106,75 +12194,119 @@ function createDeferDepsFns(job) {
12106
12194
  * message.)
12107
12195
  */
12108
12196
  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();
12197
+ // Create i18n context ops for i18n attrs.
12198
+ const attrContextByMessage = new Map();
12199
+ for (const unit of job.units) {
12200
+ for (const op of unit.ops()) {
12201
+ switch (op.kind) {
12202
+ case OpKind.Binding:
12203
+ case OpKind.Property:
12204
+ case OpKind.Attribute:
12205
+ case OpKind.ExtractedAttribute:
12206
+ if (op.i18nMessage === null) {
12207
+ continue;
12208
+ }
12209
+ if (!attrContextByMessage.has(op.i18nMessage)) {
12210
+ const i18nContext = createI18nContextOp(I18nContextKind.Attr, job.allocateXrefId(), null, op.i18nMessage, null);
12211
+ unit.create.push(i18nContext);
12212
+ attrContextByMessage.set(op.i18nMessage, i18nContext.xref);
12213
+ }
12214
+ op.i18nContext = attrContextByMessage.get(op.i18nMessage);
12215
+ break;
12216
+ }
12217
+ }
12218
+ }
12219
+ // Create i18n context ops for root i18n blocks.
12220
+ const blockContextByI18nBlock = new Map();
12115
12221
  for (const unit of job.units) {
12116
12222
  for (const op of unit.create) {
12117
12223
  switch (op.kind) {
12118
12224
  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
12225
  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);
12226
+ const contextOp = createI18nContextOp(I18nContextKind.RootI18n, job.allocateXrefId(), op.xref, op.message, null);
12227
+ unit.create.push(contextOp);
12228
+ op.context = contextOp.xref;
12229
+ blockContextByI18nBlock.set(op.xref, contextOp);
12127
12230
  }
12128
12231
  break;
12232
+ }
12233
+ }
12234
+ }
12235
+ // Assign i18n contexts for child i18n blocks. These don't need their own conext, instead they
12236
+ // should inherit from their root i18n block.
12237
+ for (const unit of job.units) {
12238
+ for (const op of unit.create) {
12239
+ if (op.kind === OpKind.I18nStart && op.xref !== op.root) {
12240
+ const rootContext = blockContextByI18nBlock.get(op.root);
12241
+ if (rootContext === undefined) {
12242
+ throw Error('AssertionError: Root i18n block i18n context should have been created.');
12243
+ }
12244
+ op.context = rootContext.xref;
12245
+ blockContextByI18nBlock.set(op.xref, rootContext);
12246
+ }
12247
+ }
12248
+ }
12249
+ // Create or assign i18n contexts for ICUs.
12250
+ let currentI18nOp = null;
12251
+ for (const unit of job.units) {
12252
+ for (const op of unit.create) {
12253
+ switch (op.kind) {
12254
+ case OpKind.I18nStart:
12255
+ currentI18nOp = op;
12256
+ break;
12129
12257
  case OpKind.I18nEnd:
12130
12258
  currentI18nOp = null;
12131
12259
  break;
12132
12260
  case OpKind.IcuStart:
12133
- // If an ICU represents a different message than its containing block, we give it its own
12134
- // i18n context.
12135
12261
  if (currentI18nOp === null) {
12136
- throw Error('Unexpected ICU outside of an i18n block.');
12262
+ throw Error('AssertionError: Unexpected ICU outside of an i18n block.');
12137
12263
  }
12138
12264
  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;
12265
+ // This ICU is a sub-message inside its parent i18n block message. We need to give it
12266
+ // its own context.
12267
+ const contextOp = createI18nContextOp(I18nContextKind.Icu, job.allocateXrefId(), currentI18nOp.xref, op.message, null);
12268
+ unit.create.push(contextOp);
12269
+ op.context = contextOp.xref;
12143
12270
  }
12144
12271
  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.
12272
+ // This ICU is the only translatable content in its parent i18n block. We need to
12273
+ // convert the parent's context into an ICU context.
12147
12274
  op.context = currentI18nOp.context;
12275
+ blockContextByI18nBlock.get(currentI18nOp.xref).contextKind = I18nContextKind.Icu;
12148
12276
  }
12149
12277
  break;
12150
12278
  }
12151
12279
  }
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
12280
  }
12172
- // Assign contexts to child i18n blocks, now that all root i18n blocks have their context
12173
- // assigned.
12281
+ }
12282
+
12283
+ /**
12284
+ * Deduplicate text bindings, e.g. <div class="cls1" class="cls2">
12285
+ */
12286
+ function deduplicateTextBindings(job) {
12287
+ const seen = new Map();
12174
12288
  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);
12289
+ for (const op of unit.update.reversed()) {
12290
+ if (op.kind === OpKind.Binding && op.isTextAttribute) {
12291
+ const seenForElement = seen.get(op.target) || new Set();
12292
+ if (seenForElement.has(op.name)) {
12293
+ if (job.compatibility === CompatibilityMode.TemplateDefinitionBuilder) {
12294
+ // For most duplicated attributes, TemplateDefinitionBuilder lists all of the values in
12295
+ // the consts array. However, for style and class attributes it only keeps the last one.
12296
+ // We replicate that behavior here since it has actual consequences for apps with
12297
+ // duplicate class or style attrs.
12298
+ if (op.name === 'style' || op.name === 'class') {
12299
+ OpList.remove(op);
12300
+ }
12301
+ }
12302
+ else {
12303
+ // TODO: Determine the correct behavior. It would probably make sense to merge multiple
12304
+ // style and class attributes. Alternatively we could just throw an error, as HTML
12305
+ // doesn't permit duplicate attributes.
12306
+ }
12307
+ }
12308
+ seenForElement.add(op.name);
12309
+ seen.set(op.target, seenForElement);
12178
12310
  }
12179
12311
  }
12180
12312
  }
@@ -12560,13 +12692,18 @@ const LIST_DELIMITER = '|';
12560
12692
  * used in the final output.
12561
12693
  */
12562
12694
  function extractI18nMessages(job) {
12563
- // Save the i18n start and i18n context ops for later use.
12564
- const i18nContexts = new Map();
12695
+ // Create an i18n message for each context.
12696
+ // TODO: Merge the context op with the message op since they're 1:1 anyways.
12697
+ const i18nMessagesByContext = new Map();
12565
12698
  const i18nBlocks = new Map();
12699
+ const i18nContexts = new Map();
12566
12700
  for (const unit of job.units) {
12567
12701
  for (const op of unit.create) {
12568
12702
  switch (op.kind) {
12569
12703
  case OpKind.I18nContext:
12704
+ const i18nMessageOp = createI18nMessage(job, op);
12705
+ unit.create.push(i18nMessageOp);
12706
+ i18nMessagesByContext.set(op.xref, i18nMessageOp);
12570
12707
  i18nContexts.set(op.xref, op);
12571
12708
  break;
12572
12709
  case OpKind.I18nStart:
@@ -12575,54 +12712,47 @@ function extractI18nMessages(job) {
12575
12712
  }
12576
12713
  }
12577
12714
  }
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.
12715
+ // Associate sub-messages for ICUs with their root message. At this point we can also remove the
12716
+ // ICU start/end ops, as they are no longer needed.
12717
+ let currentIcu = null;
12605
12718
  for (const unit of job.units) {
12606
12719
  for (const op of unit.create) {
12607
12720
  switch (op.kind) {
12608
12721
  case OpKind.IcuStart:
12609
- if (!op.context) {
12610
- throw Error('ICU op should have its context set.');
12722
+ currentIcu = op;
12723
+ OpList.remove(op);
12724
+ // Skip any contexts not associated with an ICU.
12725
+ const icuContext = i18nContexts.get(op.context);
12726
+ if (icuContext.contextKind !== I18nContextKind.Icu) {
12727
+ continue;
12611
12728
  }
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);
12729
+ // Skip ICUs that share a context with their i18n message. These represent root-level
12730
+ // ICUs, not sub-messages.
12731
+ const i18nBlock = i18nBlocks.get(icuContext.i18nBlock);
12732
+ if (i18nBlock.context === icuContext.xref) {
12733
+ continue;
12622
12734
  }
12623
- OpList.remove(op);
12735
+ // Find the root message and push this ICUs message as a sub-message.
12736
+ const rootI18nBlock = i18nBlocks.get(i18nBlock.root);
12737
+ const rootMessage = i18nMessagesByContext.get(rootI18nBlock.context);
12738
+ if (rootMessage === undefined) {
12739
+ throw Error('AssertionError: ICU sub-message should belong to a root message.');
12740
+ }
12741
+ const subMessage = i18nMessagesByContext.get(icuContext.xref);
12742
+ subMessage.messagePlaceholder = op.messagePlaceholder;
12743
+ rootMessage.subMessages.push(subMessage.xref);
12624
12744
  break;
12625
12745
  case OpKind.IcuEnd:
12746
+ currentIcu = null;
12747
+ OpList.remove(op);
12748
+ break;
12749
+ case OpKind.IcuPlaceholder:
12750
+ // Add ICU placeholders to the message, then remove the ICU placeholder ops.
12751
+ if (currentIcu === null || currentIcu.context == null) {
12752
+ throw Error('AssertionError: Unexpected ICU placeholder outside of i18n context');
12753
+ }
12754
+ const msg = i18nMessagesByContext.get(currentIcu.context);
12755
+ msg.postprocessingParams.set(op.name, literal(formatIcuPlaceholder(op)));
12626
12756
  OpList.remove(op);
12627
12757
  break;
12628
12758
  }
@@ -12635,14 +12765,19 @@ function extractI18nMessages(job) {
12635
12765
  function createI18nMessage(job, context, messagePlaceholder) {
12636
12766
  let formattedParams = formatParams(context.params);
12637
12767
  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
- }
12768
+ let needsPostprocessing = [...context.params.values()].some(v => v.length > 1);
12644
12769
  return createI18nMessageOp(job.allocateXrefId(), context.xref, context.i18nBlock, context.message, messagePlaceholder ?? null, formattedParams, formattedPostprocessingParams, needsPostprocessing);
12645
12770
  }
12771
+ /**
12772
+ * Formats an ICU placeholder into a single string with expression placeholders.
12773
+ */
12774
+ function formatIcuPlaceholder(op) {
12775
+ if (op.strings.length !== op.expressionPlaceholders.length + 1) {
12776
+ throw Error(`AsserionError: Invalid ICU placeholder with ${op.strings.length} strings and ${op.expressionPlaceholders.length} expressions`);
12777
+ }
12778
+ const values = op.expressionPlaceholders.map(formatValue);
12779
+ return op.strings.flatMap((str, i) => [str, values[i] || '']).join('');
12780
+ }
12646
12781
  /**
12647
12782
  * Formats a map of `I18nParamValue[]` values into a map of `Expression` values.
12648
12783
  */
@@ -12981,7 +13116,7 @@ const BANG_IMPORTANT = '!important';
12981
13116
  */
12982
13117
  function parseHostStyleProperties(job) {
12983
13118
  for (const op of job.root.update) {
12984
- if (op.kind !== OpKind.Binding) {
13119
+ if (!(op.kind === OpKind.Binding && op.bindingKind === BindingKind.Property)) {
12985
13120
  continue;
12986
13121
  }
12987
13122
  if (op.name.endsWith(BANG_IMPORTANT)) {
@@ -20139,7 +20274,7 @@ function collectMessage(job, fileBasedI18nSuffix, messages, messageOp) {
20139
20274
  let transformFn = undefined;
20140
20275
  // If nescessary, add a post-processing step and resolve any placeholder params that are
20141
20276
  // set in post-processing.
20142
- if (messageOp.needsPostprocessing) {
20277
+ if (messageOp.needsPostprocessing || messageOp.postprocessingParams.size > 0) {
20143
20278
  // Sort the post-processing params for consistency with TemaplateDefinitionBuilder output.
20144
20279
  const postprocessingParams = Object.fromEntries([...messageOp.postprocessingParams.entries()].sort());
20145
20280
  const formattedPostprocessingParams = formatI18nPlaceholderNamesInMap(postprocessingParams, /* useCamelCase */ false);
@@ -20169,7 +20304,6 @@ function addSubMessageParams(messageOp, subMessagePlaceholders) {
20169
20304
  else {
20170
20305
  messageOp.params.set(placeholder, literal(`${ESCAPE}${I18N_ICU_MAPPING_PREFIX}${placeholder}${ESCAPE}`));
20171
20306
  messageOp.postprocessingParams.set(placeholder, literalArr(subMessages));
20172
- messageOp.needsPostprocessing = true;
20173
20307
  }
20174
20308
  }
20175
20309
  }
@@ -20255,6 +20389,7 @@ function convertI18nText(job) {
20255
20389
  let currentIcu = null;
20256
20390
  const textNodeI18nBlocks = new Map();
20257
20391
  const textNodeIcus = new Map();
20392
+ const icuPlaceholderByText = new Map();
20258
20393
  for (const op of unit.create) {
20259
20394
  switch (op.kind) {
20260
20395
  case OpKind.I18nStart:
@@ -20279,7 +20414,19 @@ function convertI18nText(job) {
20279
20414
  if (currentI18n !== null) {
20280
20415
  textNodeI18nBlocks.set(op.xref, currentI18n);
20281
20416
  textNodeIcus.set(op.xref, currentIcu);
20282
- OpList.remove(op);
20417
+ if (op.icuPlaceholder !== null) {
20418
+ // Create an op to represent the ICU placeholder. Initially set its static text to the
20419
+ // value of the text op, though this may be overwritten later if this text op is a
20420
+ // placeholder for an interpolation.
20421
+ const icuPlaceholderOp = createIcuPlaceholderOp(job.allocateXrefId(), op.icuPlaceholder, [op.initialValue]);
20422
+ OpList.replace(op, icuPlaceholderOp);
20423
+ icuPlaceholderByText.set(op.xref, icuPlaceholderOp);
20424
+ }
20425
+ else {
20426
+ // Otherwise just remove the text op, since its value is already accounted for in the
20427
+ // translated message.
20428
+ OpList.remove(op);
20429
+ }
20283
20430
  }
20284
20431
  break;
20285
20432
  }
@@ -20294,6 +20441,7 @@ function convertI18nText(job) {
20294
20441
  }
20295
20442
  const i18nOp = textNodeI18nBlocks.get(op.target);
20296
20443
  const icuOp = textNodeIcus.get(op.target);
20444
+ const icuPlaceholder = icuPlaceholderByText.get(op.target);
20297
20445
  const contextId = icuOp ? icuOp.context : i18nOp.context;
20298
20446
  const resolutionTime = icuOp ? I18nParamResolutionTime.Postproccessing :
20299
20447
  I18nParamResolutionTime.Creation;
@@ -20302,9 +20450,14 @@ function convertI18nText(job) {
20302
20450
  const expr = op.interpolation.expressions[i];
20303
20451
  // For now, this i18nExpression depends on the slot context of the enclosing i18n block.
20304
20452
  // 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));
20453
+ 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
20454
  }
20307
20455
  OpList.replaceWithMany(op, ops);
20456
+ // If this interpolation is part of an ICU placeholder, add the strings and expressions to
20457
+ // the placeholder.
20458
+ if (icuPlaceholder !== undefined) {
20459
+ icuPlaceholder.strings = op.interpolation.strings;
20460
+ }
20308
20461
  break;
20309
20462
  }
20310
20463
  }
@@ -20487,7 +20640,7 @@ function addNamesToView(unit, baseName, state, compatibility) {
20487
20640
  op.handlerFnName = sanitizeIdentifier(op.handlerFnName);
20488
20641
  break;
20489
20642
  case OpKind.Variable:
20490
- varNames.set(op.xref, getVariableName(op.variable, state));
20643
+ varNames.set(op.xref, getVariableName(unit, op.variable, state));
20491
20644
  break;
20492
20645
  case OpKind.RepeaterCreate:
20493
20646
  if (!(unit instanceof ViewCompilationUnit)) {
@@ -20542,15 +20695,23 @@ function addNamesToView(unit, baseName, state, compatibility) {
20542
20695
  });
20543
20696
  }
20544
20697
  }
20545
- function getVariableName(variable, state) {
20698
+ function getVariableName(unit, variable, state) {
20546
20699
  if (variable.name === null) {
20547
20700
  switch (variable.kind) {
20548
20701
  case SemanticVariableKind.Context:
20549
20702
  variable.name = `ctx_r${state.index++}`;
20550
20703
  break;
20551
20704
  case SemanticVariableKind.Identifier:
20552
- // TODO: Prefix increment and `_r` for compatiblity only.
20553
- variable.name = `${variable.identifier}_r${++state.index}`;
20705
+ if (unit.job.compatibility === CompatibilityMode.TemplateDefinitionBuilder) {
20706
+ // TODO: Prefix increment and `_r` are for compatiblity with the old naming scheme.
20707
+ // This has the potential to cause collisions when `ctx` is the identifier, so we need a
20708
+ // special check for that as well.
20709
+ const compatPrefix = variable.identifier === 'ctx' ? 'i' : '';
20710
+ variable.name = `${variable.identifier}_${compatPrefix}r${++state.index}`;
20711
+ }
20712
+ else {
20713
+ variable.name = `${variable.identifier}_i${state.index++}`;
20714
+ }
20554
20715
  break;
20555
20716
  default:
20556
20717
  // TODO: Prefix increment for compatibility only.
@@ -20747,17 +20908,27 @@ const CREATE_ORDERING = [
20747
20908
  * op kinds.
20748
20909
  */
20749
20910
  const UPDATE_ORDERING = [
20750
- { test: kindWithInterpolationTest(OpKind.HostProperty, true) },
20751
- { test: kindWithInterpolationTest(OpKind.HostProperty, false) },
20752
20911
  { test: kindTest(OpKind.StyleMap), transform: keepLast },
20753
20912
  { test: kindTest(OpKind.ClassMap), transform: keepLast },
20754
20913
  { test: kindTest(OpKind.StyleProp) },
20755
20914
  { test: kindTest(OpKind.ClassProp) },
20756
- { test: kindWithInterpolationTest(OpKind.Property, true) },
20757
20915
  { test: kindWithInterpolationTest(OpKind.Attribute, true) },
20916
+ { test: kindWithInterpolationTest(OpKind.Property, true) },
20758
20917
  { test: kindWithInterpolationTest(OpKind.Property, false) },
20759
20918
  { test: kindWithInterpolationTest(OpKind.Attribute, false) },
20760
20919
  ];
20920
+ /**
20921
+ * Host bindings have their own update ordering.
20922
+ */
20923
+ const UPDATE_HOST_ORDERING = [
20924
+ { test: kindWithInterpolationTest(OpKind.HostProperty, true) },
20925
+ { test: kindWithInterpolationTest(OpKind.HostProperty, false) },
20926
+ { test: kindTest(OpKind.Attribute) },
20927
+ { test: kindTest(OpKind.StyleMap), transform: keepLast },
20928
+ { test: kindTest(OpKind.ClassMap), transform: keepLast },
20929
+ { test: kindTest(OpKind.StyleProp) },
20930
+ { test: kindTest(OpKind.ClassProp) },
20931
+ ];
20761
20932
  /**
20762
20933
  * The set of all op kinds we handle in the reordering phase.
20763
20934
  */
@@ -20778,7 +20949,8 @@ function orderOps(job) {
20778
20949
  // Create mode:
20779
20950
  orderWithin(unit.create, CREATE_ORDERING);
20780
20951
  // Update mode:
20781
- orderWithin(unit.update, UPDATE_ORDERING);
20952
+ const ordering = unit.job.kind === CompilationJobKind.Host ? UPDATE_HOST_ORDERING : UPDATE_ORDERING;
20953
+ orderWithin(unit.update, ordering);
20782
20954
  }
20783
20955
  }
20784
20956
  /**
@@ -20833,10 +21005,28 @@ function keepLast(ops) {
20833
21005
  * class property.
20834
21006
  */
20835
21007
  function parseExtractedStyles(job) {
21008
+ const elements = new Map();
21009
+ for (const unit of job.units) {
21010
+ for (const op of unit.create) {
21011
+ if (isElementOrContainerOp(op)) {
21012
+ elements.set(op.xref, op);
21013
+ }
21014
+ }
21015
+ }
20836
21016
  for (const unit of job.units) {
20837
21017
  for (const op of unit.create) {
20838
21018
  if (op.kind === OpKind.ExtractedAttribute && op.bindingKind === BindingKind.Attribute &&
20839
21019
  isStringLiteral(op.expression)) {
21020
+ const target = elements.get(op.target);
21021
+ if (target !== undefined && target.kind === OpKind.Template &&
21022
+ target.templateKind === TemplateKind.Structural) {
21023
+ // TemplateDefinitionBuilder will not apply class and style bindings to structural
21024
+ // directives; instead, it will leave them as attributes.
21025
+ // (It's not clear what that would mean, anyway -- classes and styles on a structural
21026
+ // element should probably be a parse error.)
21027
+ // TODO: We may be able to remove this once Template Pipeline is the default.
21028
+ continue;
21029
+ }
20840
21030
  if (op.name === 'style') {
20841
21031
  const parsedStyles = parse(op.expression.value);
20842
21032
  for (let i = 0; i < parsedStyles.length - 1; i += 2) {
@@ -20871,15 +21061,6 @@ function removeContentSelectors(job) {
20871
21061
  OpList.remove(op);
20872
21062
  }
20873
21063
  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
21064
  }
20884
21065
  }
20885
21066
  }
@@ -21051,8 +21232,10 @@ function wrapTemplateWithI18n(unit, parentI18n) {
21051
21232
  // Only add i18n ops if they have not already been propagated to this template.
21052
21233
  if (unit.create.head.next?.kind !== OpKind.I18nStart) {
21053
21234
  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);
21235
+ OpList.insertAfter(
21236
+ // Nested ng-template i18n start/end ops should not recieve source spans.
21237
+ createI18nStartOp(id, parentI18n.message, parentI18n.root, null), unit.create.head);
21238
+ OpList.insertBefore(createI18nEndOp(id, null), unit.create.tail);
21056
21239
  }
21057
21240
  }
21058
21241
 
@@ -21313,22 +21496,22 @@ function projectionDef(def) {
21313
21496
  }
21314
21497
  function projection(slot, projectionSlotIndex, attributes, sourceSpan) {
21315
21498
  const args = [literal(slot)];
21316
- if (projectionSlotIndex !== 0 || attributes.length > 0) {
21499
+ if (projectionSlotIndex !== 0 || attributes !== null) {
21317
21500
  args.push(literal(projectionSlotIndex));
21318
- if (attributes.length > 0) {
21319
- args.push(literalArr(attributes.map(attr => literal(attr))));
21501
+ if (attributes !== null) {
21502
+ args.push(attributes);
21320
21503
  }
21321
21504
  }
21322
21505
  return call(Identifiers.projection, args, sourceSpan);
21323
21506
  }
21324
- function i18nStart(slot, constIndex, subTemplateIndex) {
21507
+ function i18nStart(slot, constIndex, subTemplateIndex, sourceSpan) {
21325
21508
  const args = [literal(slot), literal(constIndex)];
21326
21509
  if (subTemplateIndex !== null) {
21327
21510
  args.push(literal(subTemplateIndex));
21328
21511
  }
21329
- return call(Identifiers.i18nStart, args, null);
21512
+ return call(Identifiers.i18nStart, args, sourceSpan);
21330
21513
  }
21331
- function repeaterCreate(slot, viewFnName, decls, vars, tag, constIndex, trackByFn, trackByUsesComponentInstance, emptyViewFnName, emptyDecls, emptyVars, sourceSpan) {
21514
+ function repeaterCreate(slot, viewFnName, decls, vars, tag, constIndex, trackByFn, trackByUsesComponentInstance, emptyViewFnName, emptyDecls, emptyVars, emptyTag, emptyConstIndex, sourceSpan) {
21332
21515
  const args = [
21333
21516
  literal(slot),
21334
21517
  variable(viewFnName),
@@ -21342,6 +21525,12 @@ function repeaterCreate(slot, viewFnName, decls, vars, tag, constIndex, trackByF
21342
21525
  args.push(literal(trackByUsesComponentInstance));
21343
21526
  if (emptyViewFnName !== null) {
21344
21527
  args.push(variable(emptyViewFnName), literal(emptyDecls), literal(emptyVars));
21528
+ if (emptyTag !== null || emptyConstIndex !== null) {
21529
+ args.push(literal(emptyTag));
21530
+ }
21531
+ if (emptyConstIndex !== null) {
21532
+ args.push(literal(emptyConstIndex));
21533
+ }
21345
21534
  }
21346
21535
  }
21347
21536
  return call(Identifiers.repeaterCreate, args, sourceSpan);
@@ -21352,15 +21541,15 @@ function repeater(collection, sourceSpan) {
21352
21541
  function deferWhen(prefetch, expr, sourceSpan) {
21353
21542
  return call(prefetch ? Identifiers.deferPrefetchWhen : Identifiers.deferWhen, [expr], sourceSpan);
21354
21543
  }
21355
- function i18n(slot, constIndex, subTemplateIndex) {
21544
+ function i18n(slot, constIndex, subTemplateIndex, sourceSpan) {
21356
21545
  const args = [literal(slot), literal(constIndex)];
21357
21546
  if (subTemplateIndex) {
21358
21547
  args.push(literal(subTemplateIndex));
21359
21548
  }
21360
- return call(Identifiers.i18n, args, null);
21549
+ return call(Identifiers.i18n, args, sourceSpan);
21361
21550
  }
21362
- function i18nEnd() {
21363
- return call(Identifiers.i18nEnd, [], null);
21551
+ function i18nEnd(endSourceSpan) {
21552
+ return call(Identifiers.i18nEnd, [], endSourceSpan);
21364
21553
  }
21365
21554
  function i18nAttributes(slot, i18nAttributesConfig) {
21366
21555
  const args = [literal(slot), literal(i18nAttributesConfig)];
@@ -21720,6 +21909,31 @@ function reify(job) {
21720
21909
  reifyUpdateOperations(unit, unit.update);
21721
21910
  }
21722
21911
  }
21912
+ /**
21913
+ * This function can be used a sanity check -- it walks every expression in the const pool, and
21914
+ * every expression reachable from an op, and makes sure that there are no IR expressions
21915
+ * left. This is nice to use for debugging mysterious failures where an IR expression cannot be
21916
+ * output from the output AST code.
21917
+ */
21918
+ function ensureNoIrForDebug(job) {
21919
+ for (const stmt of job.pool.statements) {
21920
+ transformExpressionsInStatement(stmt, expr => {
21921
+ if (isIrExpression(expr)) {
21922
+ throw new Error(`AssertionError: IR expression found during reify: ${ExpressionKind[expr.kind]}`);
21923
+ }
21924
+ return expr;
21925
+ }, VisitorContextFlag.None);
21926
+ }
21927
+ for (const unit of job.units) {
21928
+ for (const op of unit.ops()) {
21929
+ visitExpressionsInOp(op, expr => {
21930
+ if (isIrExpression(expr)) {
21931
+ throw new Error(`AssertionError: IR expression found during reify: ${ExpressionKind[expr.kind]}`);
21932
+ }
21933
+ });
21934
+ }
21935
+ }
21936
+ }
21723
21937
  function reifyCreateOperations(unit, ops) {
21724
21938
  for (const op of ops) {
21725
21939
  transformExpressionsInOp(op, reifyIrExpression, VisitorContextFlag.None);
@@ -21728,31 +21942,31 @@ function reifyCreateOperations(unit, ops) {
21728
21942
  OpList.replace(op, text(op.handle.slot, op.initialValue, op.sourceSpan));
21729
21943
  break;
21730
21944
  case OpKind.ElementStart:
21731
- OpList.replace(op, elementStart(op.handle.slot, op.tag, op.attributes, op.localRefs, op.sourceSpan));
21945
+ OpList.replace(op, elementStart(op.handle.slot, op.tag, op.attributes, op.localRefs, op.startSourceSpan));
21732
21946
  break;
21733
21947
  case OpKind.Element:
21734
- OpList.replace(op, element(op.handle.slot, op.tag, op.attributes, op.localRefs, op.sourceSpan));
21948
+ OpList.replace(op, element(op.handle.slot, op.tag, op.attributes, op.localRefs, op.wholeSourceSpan));
21735
21949
  break;
21736
21950
  case OpKind.ElementEnd:
21737
21951
  OpList.replace(op, elementEnd(op.sourceSpan));
21738
21952
  break;
21739
21953
  case OpKind.ContainerStart:
21740
- OpList.replace(op, elementContainerStart(op.handle.slot, op.attributes, op.localRefs, op.sourceSpan));
21954
+ OpList.replace(op, elementContainerStart(op.handle.slot, op.attributes, op.localRefs, op.startSourceSpan));
21741
21955
  break;
21742
21956
  case OpKind.Container:
21743
- OpList.replace(op, elementContainer(op.handle.slot, op.attributes, op.localRefs, op.sourceSpan));
21957
+ OpList.replace(op, elementContainer(op.handle.slot, op.attributes, op.localRefs, op.wholeSourceSpan));
21744
21958
  break;
21745
21959
  case OpKind.ContainerEnd:
21746
21960
  OpList.replace(op, elementContainerEnd());
21747
21961
  break;
21748
21962
  case OpKind.I18nStart:
21749
- OpList.replace(op, i18nStart(op.handle.slot, op.messageIndex, op.subTemplateIndex));
21963
+ OpList.replace(op, i18nStart(op.handle.slot, op.messageIndex, op.subTemplateIndex, op.sourceSpan));
21750
21964
  break;
21751
21965
  case OpKind.I18nEnd:
21752
- OpList.replace(op, i18nEnd());
21966
+ OpList.replace(op, i18nEnd(op.sourceSpan));
21753
21967
  break;
21754
21968
  case OpKind.I18n:
21755
- OpList.replace(op, i18n(op.handle.slot, op.messageIndex, op.subTemplateIndex));
21969
+ OpList.replace(op, i18n(op.handle.slot, op.messageIndex, op.subTemplateIndex, op.sourceSpan));
21756
21970
  break;
21757
21971
  case OpKind.I18nAttributes:
21758
21972
  if (op.i18nAttributesConfig === null) {
@@ -21768,7 +21982,7 @@ function reifyCreateOperations(unit, ops) {
21768
21982
  throw new Error(`AssertionError: local refs array should have been extracted into a constant`);
21769
21983
  }
21770
21984
  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));
21985
+ OpList.replace(op, template(op.handle.slot, variable(childView.fnName), childView.decls, childView.vars, op.tag, op.attributes, op.localRefs, op.startSourceSpan));
21772
21986
  break;
21773
21987
  case OpKind.DisableBindings:
21774
21988
  OpList.replace(op, disableBindings());
@@ -21783,7 +21997,7 @@ function reifyCreateOperations(unit, ops) {
21783
21997
  const listenerFn = reifyListenerHandler(unit, op.handlerFnName, op.handlerOps, op.consumesDollarEvent);
21784
21998
  const eventTargetResolver = op.eventTarget ? GLOBAL_TARGET_RESOLVERS$1.get(op.eventTarget) : null;
21785
21999
  if (eventTargetResolver === undefined) {
21786
- throw new Error(`AssertionError: unknown event target ${op.eventTarget}`);
22000
+ throw new Error(`Unexpected global target '${op.eventTarget}' defined for '${op.name}' event. Supported list of global targets: window,document,body.`);
21787
22001
  }
21788
22002
  OpList.replace(op, listener(op.name, listenerFn, eventTargetResolver, op.hostListener && op.isAnimationListener, op.sourceSpan));
21789
22003
  break;
@@ -21870,7 +22084,7 @@ function reifyCreateOperations(unit, ops) {
21870
22084
  emptyDecls = emptyView.decls;
21871
22085
  emptyVars = emptyView.vars;
21872
22086
  }
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));
22087
+ 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
22088
  break;
21875
22089
  case OpKind.Statement:
21876
22090
  // Pass statement operations directly through.
@@ -22500,6 +22714,7 @@ function resolveI18nExpressionPlaceholders(job) {
22500
22714
  // Record all of the i18n context ops, and the sub-template index for each i18n op.
22501
22715
  const subTemplateIndicies = new Map();
22502
22716
  const i18nContexts = new Map();
22717
+ const icuPlaceholders = new Map();
22503
22718
  for (const unit of job.units) {
22504
22719
  for (const op of unit.create) {
22505
22720
  switch (op.kind) {
@@ -22509,6 +22724,9 @@ function resolveI18nExpressionPlaceholders(job) {
22509
22724
  case OpKind.I18nContext:
22510
22725
  i18nContexts.set(op.xref, op);
22511
22726
  break;
22727
+ case OpKind.IcuPlaceholder:
22728
+ icuPlaceholders.set(op.xref, op);
22729
+ break;
22512
22730
  }
22513
22731
  }
22514
22732
  }
@@ -22522,76 +22740,32 @@ function resolveI18nExpressionPlaceholders(job) {
22522
22740
  for (const unit of job.units) {
22523
22741
  for (const op of unit.update) {
22524
22742
  if (op.kind === OpKind.I18nExpression) {
22525
- const i18nContext = i18nContexts.get(op.context);
22526
22743
  const index = expressionIndices.get(referenceIndex(op)) || 0;
22527
22744
  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({
22745
+ const value = {
22534
22746
  value: index,
22535
22747
  subTemplateIndex: subTemplateIndex,
22536
22748
  flags: I18nParamValueFlags.ExpressionIndex
22537
- });
22538
- params.set(op.i18nPlaceholder, values);
22749
+ };
22750
+ updatePlaceholder(op, value, i18nContexts, icuPlaceholders);
22539
22751
  expressionIndices.set(referenceIndex(op), index + 1);
22540
22752
  }
22541
22753
  }
22542
22754
  }
22543
22755
  }
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
- }
22557
- }
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;
22756
+ function updatePlaceholder(op, value, i18nContexts, icuPlaceholders) {
22757
+ if (op.i18nPlaceholder !== null) {
22758
+ const i18nContext = i18nContexts.get(op.context);
22759
+ const params = op.resolutionTime === I18nParamResolutionTime.Creation ?
22760
+ i18nContext.params :
22761
+ i18nContext.postprocessingParams;
22762
+ const values = params.get(op.i18nPlaceholder) || [];
22763
+ values.push(value);
22764
+ params.set(op.i18nPlaceholder, values);
22566
22765
  }
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);
22766
+ if (op.icuPlaceholder !== null) {
22767
+ const icuPlaceholderOp = icuPlaceholders.get(op.icuPlaceholder);
22768
+ icuPlaceholderOp?.expressionPlaceholders.push(value);
22595
22769
  }
22596
22770
  }
22597
22771
 
@@ -23043,6 +23217,9 @@ function generateTrackFns(job) {
23043
23217
  // Find all component context reads.
23044
23218
  let usesComponentContext = false;
23045
23219
  op.track = transformExpressionsInExpression(op.track, expr => {
23220
+ if (expr instanceof PipeBindingExpr || expr instanceof PipeBindingVariadicExpr) {
23221
+ throw new Error(`Illegal State: Pipes are not allowed in this context`);
23222
+ }
23046
23223
  if (expr instanceof TrackContextExpr) {
23047
23224
  usesComponentContext = true;
23048
23225
  return variable('this');
@@ -23266,7 +23443,14 @@ function varsUsedByOp(op) {
23266
23443
  return op.interpolation.expressions.length;
23267
23444
  case OpKind.I18nExpression:
23268
23445
  case OpKind.Conditional:
23446
+ case OpKind.DeferWhen:
23269
23447
  return 1;
23448
+ case OpKind.RepeaterCreate:
23449
+ // Repeaters may require an extra variable binding slot, if they have an empty view, for the
23450
+ // empty block tracking.
23451
+ // TODO: It's a bit odd to have a create mode instruction consume variable slots. Maybe we can
23452
+ // find a way to use the Repeater update op instead.
23453
+ return op.emptyView ? 1 : 0;
23270
23454
  default:
23271
23455
  throw new Error(`Unhandled op: ${OpKind[op.kind]}`);
23272
23456
  }
@@ -23722,12 +23906,13 @@ function wrapI18nIcus(job) {
23722
23906
  case OpKind.IcuStart:
23723
23907
  if (currentI18nOp === null) {
23724
23908
  addedI18nId = job.allocateXrefId();
23725
- OpList.insertBefore(createI18nStartOp(addedI18nId, op.message), op);
23909
+ // ICU i18n start/end ops should not recieve source spans.
23910
+ OpList.insertBefore(createI18nStartOp(addedI18nId, op.message, undefined, null), op);
23726
23911
  }
23727
23912
  break;
23728
23913
  case OpKind.IcuEnd:
23729
23914
  if (addedI18nId !== null) {
23730
- OpList.insertAfter(createI18nEndOp(addedI18nId), op);
23915
+ OpList.insertAfter(createI18nEndOp(addedI18nId, null), op);
23731
23916
  addedI18nId = null;
23732
23917
  }
23733
23918
  break;
@@ -23750,6 +23935,7 @@ const phases = [
23750
23935
  { kind: CompilationJobKind.Tmpl, fn: emitNamespaceChanges },
23751
23936
  { kind: CompilationJobKind.Tmpl, fn: propagateI18nBlocks },
23752
23937
  { kind: CompilationJobKind.Tmpl, fn: wrapI18nIcus },
23938
+ { kind: CompilationJobKind.Both, fn: deduplicateTextBindings },
23753
23939
  { kind: CompilationJobKind.Both, fn: specializeStyleBindings },
23754
23940
  { kind: CompilationJobKind.Both, fn: specializeBindings },
23755
23941
  { kind: CompilationJobKind.Both, fn: extractAttributes },
@@ -23788,7 +23974,6 @@ const phases = [
23788
23974
  { kind: CompilationJobKind.Tmpl, fn: createDeferDepsFns },
23789
23975
  { kind: CompilationJobKind.Tmpl, fn: resolveI18nElementPlaceholders },
23790
23976
  { kind: CompilationJobKind.Tmpl, fn: resolveI18nExpressionPlaceholders },
23791
- { kind: CompilationJobKind.Tmpl, fn: resolveI18nIcuPlaceholders },
23792
23977
  { kind: CompilationJobKind.Tmpl, fn: extractI18nMessages },
23793
23978
  { kind: CompilationJobKind.Tmpl, fn: generateTrackFns },
23794
23979
  { kind: CompilationJobKind.Tmpl, fn: collectI18nConsts },
@@ -23948,7 +24133,7 @@ function ingestHostBinding(input, bindingParser, constantPool) {
23948
24133
  const securityContexts = bindingParser
23949
24134
  .calcPossibleSecurityContexts(input.componentSelector, property.name, bindingKind === BindingKind.Attribute)
23950
24135
  .filter(context => context !== SecurityContext.NONE);
23951
- ingestHostProperty(job, property, bindingKind, false, securityContexts);
24136
+ ingestHostProperty(job, property, bindingKind, securityContexts);
23952
24137
  }
23953
24138
  for (const [name, expr] of Object.entries(input.attributes) ?? []) {
23954
24139
  const securityContexts = bindingParser.calcPossibleSecurityContexts(input.componentSelector, name, true)
@@ -23962,7 +24147,7 @@ function ingestHostBinding(input, bindingParser, constantPool) {
23962
24147
  }
23963
24148
  // TODO: We should refactor the parser to use the same types and structures for host bindings as
23964
24149
  // with ordinary components. This would allow us to share a lot more ingestion code.
23965
- function ingestHostProperty(job, property, bindingKind, isTextAttribute, securityContexts) {
24150
+ function ingestHostProperty(job, property, bindingKind, securityContexts) {
23966
24151
  let expression;
23967
24152
  const ast = property.expression.ast;
23968
24153
  if (ast instanceof Interpolation$1) {
@@ -23971,20 +24156,21 @@ function ingestHostProperty(job, property, bindingKind, isTextAttribute, securit
23971
24156
  else {
23972
24157
  expression = convertAst(ast, job, property.sourceSpan);
23973
24158
  }
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));
24159
+ 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
24160
  }
23976
24161
  function ingestHostAttribute(job, name, value, securityContexts) {
23977
- const attrBinding = createBindingOp(job.root.xref, BindingKind.Attribute, name, value, null, securityContexts, true, false, null,
24162
+ const attrBinding = createBindingOp(job.root.xref, BindingKind.Attribute, name, value, null, securityContexts,
24163
+ /* Host attributes should always be extracted to const hostAttrs, even if they are not
24164
+ *strictly* text literals */
24165
+ true, false, null,
23978
24166
  /* TODO */ null,
23979
- /* TODO: host attribute source spans */ null);
24167
+ /** TODO: May be null? */ value.sourceSpan);
23980
24168
  job.root.update.push(attrBinding);
23981
24169
  }
23982
24170
  function ingestHostEvent(job, event) {
23983
24171
  const [phase, target] = event.type === 0 /* e.ParsedEventType.Regular */ ? [null, event.targetOrPhase] :
23984
24172
  [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)));
24173
+ const eventBinding = createListenerOp(job.root.xref, new SlotHandle(), event.name, null, makeListenerHandlerOps(job.root, event.handler, event.handlerSpan), phase, target, true, event.sourceSpan);
23988
24174
  job.root.create.push(eventBinding);
23989
24175
  }
23990
24176
  /**
@@ -24002,10 +24188,10 @@ function ingestNodes(unit, template) {
24002
24188
  ingestContent(unit, node);
24003
24189
  }
24004
24190
  else if (node instanceof Text$3) {
24005
- ingestText(unit, node);
24191
+ ingestText(unit, node, null);
24006
24192
  }
24007
24193
  else if (node instanceof BoundText) {
24008
- ingestBoundText(unit, node);
24194
+ ingestBoundText(unit, node, null);
24009
24195
  }
24010
24196
  else if (node instanceof IfBlock) {
24011
24197
  ingestIfBlock(unit, node);
@@ -24037,7 +24223,7 @@ function ingestElement(unit, element) {
24037
24223
  }
24038
24224
  const id = unit.job.allocateXrefId();
24039
24225
  const [namespaceKey, elementName] = splitNsName(element.name);
24040
- const startOp = createElementStartOp(elementName, id, namespaceForKey(namespaceKey), element.i18n instanceof TagPlaceholder ? element.i18n : undefined, element.startSourceSpan);
24226
+ const startOp = createElementStartOp(elementName, id, namespaceForKey(namespaceKey), element.i18n instanceof TagPlaceholder ? element.i18n : undefined, element.startSourceSpan, element.sourceSpan);
24041
24227
  unit.create.push(startOp);
24042
24228
  ingestElementBindings(unit, startOp, element);
24043
24229
  ingestReferences(startOp, element);
@@ -24045,7 +24231,7 @@ function ingestElement(unit, element) {
24045
24231
  let i18nBlockId = null;
24046
24232
  if (element.i18n instanceof Message) {
24047
24233
  i18nBlockId = unit.job.allocateXrefId();
24048
- unit.create.push(createI18nStartOp(i18nBlockId, element.i18n));
24234
+ unit.create.push(createI18nStartOp(i18nBlockId, element.i18n, undefined, element.startSourceSpan));
24049
24235
  }
24050
24236
  ingestNodes(unit, element.children);
24051
24237
  // The source span for the end op is typically the element closing tag. However, if no closing tag
@@ -24057,7 +24243,7 @@ function ingestElement(unit, element) {
24057
24243
  unit.create.push(endOp);
24058
24244
  // If there is an i18n message associated with this element, insert i18n start and end ops.
24059
24245
  if (i18nBlockId !== null) {
24060
- OpList.insertBefore(createI18nEndOp(i18nBlockId), endOp);
24246
+ OpList.insertBefore(createI18nEndOp(i18nBlockId, element.endSourceSpan ?? element.startSourceSpan), endOp);
24061
24247
  }
24062
24248
  }
24063
24249
  /**
@@ -24080,7 +24266,7 @@ function ingestTemplate(unit, tmpl) {
24080
24266
  '' :
24081
24267
  prefixWithNamespace(tagNameWithoutNamespace, namespace);
24082
24268
  const templateKind = isPlainTemplate(tmpl) ? TemplateKind.NgTemplate : TemplateKind.Structural;
24083
- const templateOp = createTemplateOp(childView.xref, templateKind, tagNameWithoutNamespace, functionNameSuffix, namespace, i18nPlaceholder, tmpl.startSourceSpan);
24269
+ const templateOp = createTemplateOp(childView.xref, templateKind, tagNameWithoutNamespace, functionNameSuffix, namespace, i18nPlaceholder, tmpl.startSourceSpan, tmpl.sourceSpan);
24084
24270
  unit.create.push(templateOp);
24085
24271
  ingestTemplateBindings(unit, templateOp, tmpl, templateKind);
24086
24272
  ingestReferences(templateOp, tmpl);
@@ -24093,8 +24279,8 @@ function ingestTemplate(unit, tmpl) {
24093
24279
  // element/template the directive is placed on.
24094
24280
  if (templateKind === TemplateKind.NgTemplate && tmpl.i18n instanceof Message) {
24095
24281
  const id = unit.job.allocateXrefId();
24096
- OpList.insertAfter(createI18nStartOp(id, tmpl.i18n), childView.create.head);
24097
- OpList.insertBefore(createI18nEndOp(id), childView.create.tail);
24282
+ OpList.insertAfter(createI18nStartOp(id, tmpl.i18n, undefined, tmpl.startSourceSpan), childView.create.head);
24283
+ OpList.insertBefore(createI18nEndOp(id, tmpl.endSourceSpan ?? tmpl.startSourceSpan), childView.create.tail);
24098
24284
  }
24099
24285
  }
24100
24286
  /**
@@ -24104,8 +24290,7 @@ function ingestContent(unit, content) {
24104
24290
  if (content.i18n !== undefined && !(content.i18n instanceof TagPlaceholder)) {
24105
24291
  throw Error(`Unhandled i18n metadata type for element: ${content.i18n.constructor.name}`);
24106
24292
  }
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);
24293
+ const op = createProjectionOp(unit.job.allocateXrefId(), content.selector, content.i18n, content.sourceSpan);
24109
24294
  for (const attr of content.attributes) {
24110
24295
  const securityContext = domSchema.securityContext(content.name, attr.name, true);
24111
24296
  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 +24300,13 @@ function ingestContent(unit, content) {
24115
24300
  /**
24116
24301
  * Ingest a literal text node from the AST into the given `ViewCompilation`.
24117
24302
  */
24118
- function ingestText(unit, text) {
24119
- unit.create.push(createTextOp(unit.job.allocateXrefId(), text.value, text.sourceSpan));
24303
+ function ingestText(unit, text, icuPlaceholder) {
24304
+ unit.create.push(createTextOp(unit.job.allocateXrefId(), text.value, icuPlaceholder, text.sourceSpan));
24120
24305
  }
24121
24306
  /**
24122
24307
  * Ingest an interpolated text node from the AST into the given `ViewCompilation`.
24123
24308
  */
24124
- function ingestBoundText(unit, text, i18nPlaceholders) {
24309
+ function ingestBoundText(unit, text, icuPlaceholder) {
24125
24310
  let value = text.value;
24126
24311
  if (value instanceof ASTWithSource) {
24127
24312
  value = value.ast;
@@ -24132,19 +24317,16 @@ function ingestBoundText(unit, text, i18nPlaceholders) {
24132
24317
  if (text.i18n !== undefined && !(text.i18n instanceof Container)) {
24133
24318
  throw Error(`Unhandled i18n metadata type for text interpolation: ${text.i18n?.constructor.name}`);
24134
24319
  }
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
- }
24320
+ const i18nPlaceholders = text.i18n instanceof Container ?
24321
+ text.i18n.children
24322
+ .filter((node) => node instanceof Placeholder)
24323
+ .map(placeholder => placeholder.name) :
24324
+ [];
24143
24325
  if (i18nPlaceholders.length > 0 && i18nPlaceholders.length !== value.expressions.length) {
24144
24326
  throw Error(`Unexpected number of i18n placeholders (${value.expressions.length}) for BoundText with ${value.expressions.length} expressions`);
24145
24327
  }
24146
24328
  const textXref = unit.job.allocateXrefId();
24147
- unit.create.push(createTextOp(textXref, '', text.sourceSpan));
24329
+ unit.create.push(createTextOp(textXref, '', icuPlaceholder, text.sourceSpan));
24148
24330
  // TemplateDefinitionBuilder does not generate source maps for sub-expressions inside an
24149
24331
  // interpolation. We copy that behavior in compatibility mode.
24150
24332
  // TODO: is it actually correct to generate these extra maps in modern mode?
@@ -24177,7 +24359,7 @@ function ingestIfBlock(unit, ifBlock) {
24177
24359
  }
24178
24360
  ifCaseI18nMeta = ifCase.i18n;
24179
24361
  }
24180
- const templateOp = createTemplateOp(cView.xref, TemplateKind.Block, tagName, 'Conditional', Namespace.HTML, ifCaseI18nMeta, ifCase.sourceSpan);
24362
+ const templateOp = createTemplateOp(cView.xref, TemplateKind.Block, tagName, 'Conditional', Namespace.HTML, ifCaseI18nMeta, ifCase.startSourceSpan, ifCase.sourceSpan);
24181
24363
  unit.create.push(templateOp);
24182
24364
  if (firstXref === null) {
24183
24365
  firstXref = cView.xref;
@@ -24207,7 +24389,7 @@ function ingestSwitchBlock(unit, switchBlock) {
24207
24389
  }
24208
24390
  switchCaseI18nMeta = switchCase.i18n;
24209
24391
  }
24210
- const templateOp = createTemplateOp(cView.xref, TemplateKind.Block, null, 'Case', Namespace.HTML, switchCaseI18nMeta, switchCase.sourceSpan);
24392
+ const templateOp = createTemplateOp(cView.xref, TemplateKind.Block, null, 'Case', Namespace.HTML, switchCaseI18nMeta, switchCase.startSourceSpan, switchCase.sourceSpan);
24211
24393
  unit.create.push(templateOp);
24212
24394
  if (firstXref === null) {
24213
24395
  firstXref = cView.xref;
@@ -24232,7 +24414,7 @@ function ingestDeferView(unit, suffix, i18nMeta, children, sourceSpan) {
24232
24414
  }
24233
24415
  const secondaryView = unit.job.allocateView(unit.xref);
24234
24416
  ingestNodes(secondaryView, children);
24235
- const templateOp = createTemplateOp(secondaryView.xref, TemplateKind.Block, null, `Defer${suffix}`, Namespace.HTML, i18nMeta, sourceSpan);
24417
+ const templateOp = createTemplateOp(secondaryView.xref, TemplateKind.Block, null, `Defer${suffix}`, Namespace.HTML, i18nMeta, sourceSpan, sourceSpan);
24236
24418
  unit.create.push(templateOp);
24237
24419
  return templateOp;
24238
24420
  }
@@ -24310,6 +24492,11 @@ function ingestDeferBlock(unit, deferBlock) {
24310
24492
  deferOnOps.push(deferOnOp);
24311
24493
  }
24312
24494
  if (triggers.when !== undefined) {
24495
+ if (triggers.when.value instanceof Interpolation$1) {
24496
+ // TemplateDefinitionBuilder supports this case, but it's very strange to me. What would it
24497
+ // even mean?
24498
+ throw new Error(`Unexpected interpolation in defer block when trigger`);
24499
+ }
24313
24500
  const deferOnOp = createDeferWhenOp(deferXref, convertAst(triggers.when.value, unit.job, triggers.when.sourceSpan), prefetch, triggers.when.sourceSpan);
24314
24501
  deferWhenOps.push(deferOnOp);
24315
24502
  }
@@ -24329,10 +24516,10 @@ function ingestIcu(unit, icu) {
24329
24516
  unit.create.push(createIcuStartOp(xref, icu.i18n, icuFromI18nMessage(icu.i18n).name, null));
24330
24517
  for (const [placeholder, text] of Object.entries({ ...icu.vars, ...icu.placeholders })) {
24331
24518
  if (text instanceof BoundText) {
24332
- ingestBoundText(unit, text, [placeholder]);
24519
+ ingestBoundText(unit, text, placeholder);
24333
24520
  }
24334
24521
  else {
24335
- ingestText(unit, text);
24522
+ ingestText(unit, text, placeholder);
24336
24523
  }
24337
24524
  }
24338
24525
  unit.create.push(createIcuEndOp(xref));
@@ -24366,9 +24553,11 @@ function ingestForBlock(unit, forBlock) {
24366
24553
  const track = convertAst(forBlock.trackBy, unit.job, sourceSpan);
24367
24554
  ingestNodes(repeaterView, forBlock.children);
24368
24555
  let emptyView = null;
24556
+ let emptyTagName = null;
24369
24557
  if (forBlock.empty !== null) {
24370
24558
  emptyView = unit.job.allocateView(unit.xref);
24371
24559
  ingestNodes(emptyView, forBlock.empty.children);
24560
+ emptyTagName = ingestControlFlowInsertionPoint(unit, emptyView.xref, forBlock.empty);
24372
24561
  }
24373
24562
  const varNames = {
24374
24563
  $index: forBlock.contextVariables.$index.name,
@@ -24389,7 +24578,7 @@ function ingestForBlock(unit, forBlock) {
24389
24578
  const i18nPlaceholder = forBlock.i18n;
24390
24579
  const emptyI18nPlaceholder = forBlock.empty?.i18n;
24391
24580
  const tagName = ingestControlFlowInsertionPoint(unit, repeaterView.xref, forBlock);
24392
- const repeaterCreate = createRepeaterCreateOp(repeaterView.xref, emptyView?.xref ?? null, tagName, track, varNames, i18nPlaceholder, emptyI18nPlaceholder, forBlock.sourceSpan);
24581
+ const repeaterCreate = createRepeaterCreateOp(repeaterView.xref, emptyView?.xref ?? null, tagName, track, varNames, emptyTagName, i18nPlaceholder, emptyI18nPlaceholder, forBlock.startSourceSpan, forBlock.sourceSpan);
24393
24582
  unit.create.push(repeaterCreate);
24394
24583
  const expression = convertAst(forBlock.expression, unit.job, convertSourceSpan(forBlock.expression.span, forBlock.sourceSpan));
24395
24584
  const repeater = createRepeaterOp(repeaterCreate.xref, repeaterCreate.handle, expression, forBlock.sourceSpan);
@@ -24403,7 +24592,29 @@ function convertAst(ast, job, baseSourceSpan) {
24403
24592
  return convertAst(ast.ast, job, baseSourceSpan);
24404
24593
  }
24405
24594
  else if (ast instanceof PropertyRead) {
24406
- if (ast.receiver instanceof ImplicitReceiver && !(ast.receiver instanceof ThisReceiver)) {
24595
+ const isThisReceiver = ast.receiver instanceof ThisReceiver;
24596
+ // Whether this is an implicit receiver, *excluding* explicit reads of `this`.
24597
+ const isImplicitReceiver = ast.receiver instanceof ImplicitReceiver && !(ast.receiver instanceof ThisReceiver);
24598
+ // Whether the name of the read is a node that should be never retain its explicit this
24599
+ // receiver.
24600
+ const isSpecialNode = ast.name === '$any' || ast.name === '$event';
24601
+ // TODO: The most sensible condition here would be simply `isImplicitReceiver`, to convert only
24602
+ // actual implicit `this` reads, and not explicit ones. However, TemplateDefinitionBuilder (and
24603
+ // the Typecheck block!) both have the same bug, in which they also consider explicit `this`
24604
+ // reads to be implicit. This causes problems when the explicit `this` read is inside a
24605
+ // template with a context that also provides the variable name being read:
24606
+ // ```
24607
+ // <ng-template let-a>{{this.a}}</ng-template>
24608
+ // ```
24609
+ // The whole point of the explicit `this` was to access the class property, but TDB and the
24610
+ // current TCB treat the read as implicit, and give you the context property instead!
24611
+ //
24612
+ // For now, we emulate this old behvaior by aggressively converting explicit reads to to
24613
+ // implicit reads, except for the special cases that TDB and the current TCB protect. However,
24614
+ // it would be an improvement to fix this.
24615
+ //
24616
+ // See also the corresponding comment for the TCB, in `type_check_block.ts`.
24617
+ if (isImplicitReceiver || (isThisReceiver && !isSpecialNode)) {
24407
24618
  return new LexicalReadExpr(ast.name);
24408
24619
  }
24409
24620
  else {
@@ -24507,13 +24718,13 @@ function convertAst(ast, job, baseSourceSpan) {
24507
24718
  throw new Error(`Unhandled expression type "${ast.constructor.name}" in file "${baseSourceSpan?.start.file.url}"`);
24508
24719
  }
24509
24720
  }
24510
- function convertAstWithInterpolation(job, value, i18nMeta) {
24721
+ function convertAstWithInterpolation(job, value, i18nMeta, sourceSpan) {
24511
24722
  let expression;
24512
24723
  if (value instanceof Interpolation$1) {
24513
- expression = new Interpolation(value.strings, value.expressions.map(e => convertAst(e, job, null)), Object.keys(asMessage(i18nMeta)?.placeholders ?? {}));
24724
+ expression = new Interpolation(value.strings, value.expressions.map(e => convertAst(e, job, sourceSpan ?? null)), Object.keys(asMessage(i18nMeta)?.placeholders ?? {}));
24514
24725
  }
24515
24726
  else if (value instanceof AST) {
24516
- expression = convertAst(value, job, null);
24727
+ expression = convertAst(value, job, sourceSpan ?? null);
24517
24728
  }
24518
24729
  else {
24519
24730
  expression = literal(value);
@@ -28705,7 +28916,12 @@ class TemplateDefinitionBuilder {
28705
28916
  });
28706
28917
  const { expression: trackByExpression, usesComponentInstance: trackByUsesComponentInstance } = this.createTrackByFunction(block);
28707
28918
  let emptyData = null;
28919
+ let emptyTagName = null;
28920
+ let emptyAttrsExprs;
28708
28921
  if (block.empty !== null) {
28922
+ const emptyInferred = this.inferProjectionDataFromInsertionPoint(block.empty);
28923
+ emptyTagName = emptyInferred.tagName;
28924
+ emptyAttrsExprs = emptyInferred.attrsExprs;
28709
28925
  emptyData = this.prepareEmbeddedTemplateFn(block.empty.children, '_ForEmpty', undefined, block.empty.i18n);
28710
28926
  // Allocate an extra slot for the empty block tracking.
28711
28927
  this.allocateBindingSlots(null);
@@ -28723,13 +28939,13 @@ class TemplateDefinitionBuilder {
28723
28939
  trackByExpression,
28724
28940
  ];
28725
28941
  if (emptyData !== null) {
28726
- params.push(literal(trackByUsesComponentInstance), variable(emptyData.name), literal(emptyData.getConstCount()), literal(emptyData.getVarCount()));
28942
+ params.push(literal(trackByUsesComponentInstance), variable(emptyData.name), literal(emptyData.getConstCount()), literal(emptyData.getVarCount()), literal(emptyTagName), this.addAttrsToConsts(emptyAttrsExprs || null));
28727
28943
  }
28728
28944
  else if (trackByUsesComponentInstance) {
28729
28945
  // If the tracking function doesn't use the component instance, we can omit the flag.
28730
28946
  params.push(literal(trackByUsesComponentInstance));
28731
28947
  }
28732
- return params;
28948
+ return trimTrailingNulls(params);
28733
28949
  });
28734
28950
  // Note: the expression needs to be processed *after* the template,
28735
28951
  // otherwise pipes injecting some symbols won't work (see #52102).
@@ -29488,12 +29704,16 @@ class BindingScope {
29488
29704
  }
29489
29705
  /** Binding scope of a `track` function inside a `for` loop block. */
29490
29706
  class TrackByBindingScope extends BindingScope {
29491
- constructor(parentScope, globalAliases) {
29707
+ constructor(parentScope, globalOverrides) {
29492
29708
  super(parentScope.bindingLevel + 1, parentScope);
29493
- this.globalAliases = globalAliases;
29709
+ this.globalOverrides = globalOverrides;
29494
29710
  this.componentAccessCount = 0;
29495
29711
  }
29496
29712
  get(name) {
29713
+ // Intercept any overridden globals.
29714
+ if (this.globalOverrides.hasOwnProperty(name)) {
29715
+ return variable(this.globalOverrides[name]);
29716
+ }
29497
29717
  let current = this.parent;
29498
29718
  // Prevent accesses of template variables outside the `for` loop.
29499
29719
  while (current) {
@@ -29502,10 +29722,6 @@ class TrackByBindingScope extends BindingScope {
29502
29722
  }
29503
29723
  current = current.parent;
29504
29724
  }
29505
- // Intercept any aliased globals.
29506
- if (this.globalAliases[name]) {
29507
- return variable(this.globalAliases[name]);
29508
- }
29509
29725
  // When the component scope is accessed, we redirect it through `this`.
29510
29726
  this.componentAccessCount++;
29511
29727
  return variable('this').prop(name);
@@ -32046,7 +32262,7 @@ function publishFacade(global) {
32046
32262
  * @description
32047
32263
  * Entry point for all public APIs of the compiler package.
32048
32264
  */
32049
- const VERSION = new Version('17.1.0-next.4');
32265
+ const VERSION = new Version('17.1.0-next.5');
32050
32266
 
32051
32267
  class CompilerConfig {
32052
32268
  constructor({ defaultEncapsulation = ViewEncapsulation.Emulated, preserveWhitespaces, strictInjectionParameters } = {}) {
@@ -33612,7 +33828,7 @@ const MINIMUM_PARTIAL_LINKER_VERSION$5 = '12.0.0';
33612
33828
  function compileDeclareClassMetadata(metadata) {
33613
33829
  const definitionMap = new DefinitionMap();
33614
33830
  definitionMap.set('minVersion', literal(MINIMUM_PARTIAL_LINKER_VERSION$5));
33615
- definitionMap.set('version', literal('17.1.0-next.4'));
33831
+ definitionMap.set('version', literal('17.1.0-next.5'));
33616
33832
  definitionMap.set('ngImport', importExpr(Identifiers.core));
33617
33833
  definitionMap.set('type', metadata.type);
33618
33834
  definitionMap.set('decorators', metadata.decorators);
@@ -33708,7 +33924,7 @@ function createDirectiveDefinitionMap(meta) {
33708
33924
  const definitionMap = new DefinitionMap();
33709
33925
  const minVersion = getMinimumVersionForPartialOutput(meta);
33710
33926
  definitionMap.set('minVersion', literal(minVersion));
33711
- definitionMap.set('version', literal('17.1.0-next.4'));
33927
+ definitionMap.set('version', literal('17.1.0-next.5'));
33712
33928
  // e.g. `type: MyDirective`
33713
33929
  definitionMap.set('type', meta.type.value);
33714
33930
  if (meta.isStandalone) {
@@ -34092,7 +34308,7 @@ const MINIMUM_PARTIAL_LINKER_VERSION$4 = '12.0.0';
34092
34308
  function compileDeclareFactoryFunction(meta) {
34093
34309
  const definitionMap = new DefinitionMap();
34094
34310
  definitionMap.set('minVersion', literal(MINIMUM_PARTIAL_LINKER_VERSION$4));
34095
- definitionMap.set('version', literal('17.1.0-next.4'));
34311
+ definitionMap.set('version', literal('17.1.0-next.5'));
34096
34312
  definitionMap.set('ngImport', importExpr(Identifiers.core));
34097
34313
  definitionMap.set('type', meta.type.value);
34098
34314
  definitionMap.set('deps', compileDependencies(meta.deps));
@@ -34127,7 +34343,7 @@ function compileDeclareInjectableFromMetadata(meta) {
34127
34343
  function createInjectableDefinitionMap(meta) {
34128
34344
  const definitionMap = new DefinitionMap();
34129
34345
  definitionMap.set('minVersion', literal(MINIMUM_PARTIAL_LINKER_VERSION$3));
34130
- definitionMap.set('version', literal('17.1.0-next.4'));
34346
+ definitionMap.set('version', literal('17.1.0-next.5'));
34131
34347
  definitionMap.set('ngImport', importExpr(Identifiers.core));
34132
34348
  definitionMap.set('type', meta.type.value);
34133
34349
  // Only generate providedIn property if it has a non-null value
@@ -34178,7 +34394,7 @@ function compileDeclareInjectorFromMetadata(meta) {
34178
34394
  function createInjectorDefinitionMap(meta) {
34179
34395
  const definitionMap = new DefinitionMap();
34180
34396
  definitionMap.set('minVersion', literal(MINIMUM_PARTIAL_LINKER_VERSION$2));
34181
- definitionMap.set('version', literal('17.1.0-next.4'));
34397
+ definitionMap.set('version', literal('17.1.0-next.5'));
34182
34398
  definitionMap.set('ngImport', importExpr(Identifiers.core));
34183
34399
  definitionMap.set('type', meta.type.value);
34184
34400
  definitionMap.set('providers', meta.providers);
@@ -34211,7 +34427,7 @@ function createNgModuleDefinitionMap(meta) {
34211
34427
  throw new Error('Invalid path! Local compilation mode should not get into the partial compilation path');
34212
34428
  }
34213
34429
  definitionMap.set('minVersion', literal(MINIMUM_PARTIAL_LINKER_VERSION$1));
34214
- definitionMap.set('version', literal('17.1.0-next.4'));
34430
+ definitionMap.set('version', literal('17.1.0-next.5'));
34215
34431
  definitionMap.set('ngImport', importExpr(Identifiers.core));
34216
34432
  definitionMap.set('type', meta.type.value);
34217
34433
  // We only generate the keys in the metadata if the arrays contain values.
@@ -34262,7 +34478,7 @@ function compileDeclarePipeFromMetadata(meta) {
34262
34478
  function createPipeDefinitionMap(meta) {
34263
34479
  const definitionMap = new DefinitionMap();
34264
34480
  definitionMap.set('minVersion', literal(MINIMUM_PARTIAL_LINKER_VERSION));
34265
- definitionMap.set('version', literal('17.1.0-next.4'));
34481
+ definitionMap.set('version', literal('17.1.0-next.5'));
34266
34482
  definitionMap.set('ngImport', importExpr(Identifiers.core));
34267
34483
  // e.g. `type: MyPipe`
34268
34484
  definitionMap.set('type', meta.type.value);