@angular/compiler 17.1.0-next.1 → 17.1.0-next.3

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 (47) hide show
  1. package/esm2022/src/compiler.mjs +2 -1
  2. package/esm2022/src/render3/partial/class_metadata.mjs +1 -1
  3. package/esm2022/src/render3/partial/directive.mjs +1 -1
  4. package/esm2022/src/render3/partial/factory.mjs +1 -1
  5. package/esm2022/src/render3/partial/injectable.mjs +1 -1
  6. package/esm2022/src/render3/partial/injector.mjs +1 -1
  7. package/esm2022/src/render3/partial/ng_module.mjs +1 -1
  8. package/esm2022/src/render3/partial/pipe.mjs +1 -1
  9. package/esm2022/src/render3/view/api.mjs +1 -1
  10. package/esm2022/src/render3/view/compiler.mjs +25 -11
  11. package/esm2022/src/render3/view/t2_api.mjs +1 -1
  12. package/esm2022/src/render3/view/t2_binder.mjs +6 -7
  13. package/esm2022/src/render3/view/template.mjs +1 -20
  14. package/esm2022/src/render3/view/util.mjs +24 -2
  15. package/esm2022/src/template/pipeline/ir/src/enums.mjs +34 -1
  16. package/esm2022/src/template/pipeline/ir/src/expression.mjs +8 -1
  17. package/esm2022/src/template/pipeline/ir/src/ops/create.mjs +27 -8
  18. package/esm2022/src/template/pipeline/ir/src/ops/host.mjs +3 -2
  19. package/esm2022/src/template/pipeline/ir/src/ops/update.mjs +23 -14
  20. package/esm2022/src/template/pipeline/src/emit.mjs +10 -8
  21. package/esm2022/src/template/pipeline/src/ingest.mjs +86 -36
  22. package/esm2022/src/template/pipeline/src/instruction.mjs +5 -1
  23. package/esm2022/src/template/pipeline/src/phases/apply_i18n_expressions.mjs +20 -4
  24. package/esm2022/src/template/pipeline/src/phases/assign_i18n_slot_dependencies.mjs +43 -10
  25. package/esm2022/src/template/pipeline/src/phases/attribute_extraction.mjs +19 -5
  26. package/esm2022/src/template/pipeline/src/phases/binding_specialization.mjs +4 -4
  27. package/esm2022/src/template/pipeline/src/phases/const_collection.mjs +3 -3
  28. package/esm2022/src/template/pipeline/src/phases/convert_i18n_bindings.mjs +52 -0
  29. package/esm2022/src/template/pipeline/src/phases/create_i18n_contexts.mjs +20 -6
  30. package/esm2022/src/template/pipeline/src/phases/extract_i18n_messages.mjs +67 -19
  31. package/esm2022/src/template/pipeline/src/phases/generate_advance.mjs +2 -2
  32. package/esm2022/src/template/pipeline/src/phases/i18n_const_collection.mjs +143 -35
  33. package/esm2022/src/template/pipeline/src/phases/i18n_text_extraction.mjs +5 -3
  34. package/esm2022/src/template/pipeline/src/phases/parse_extracted_styles.mjs +3 -3
  35. package/esm2022/src/template/pipeline/src/phases/phase_remove_content_selectors.mjs +15 -3
  36. package/esm2022/src/template/pipeline/src/phases/propagate_i18n_blocks.mjs +7 -2
  37. package/esm2022/src/template/pipeline/src/phases/reify.mjs +7 -1
  38. package/esm2022/src/template/pipeline/src/phases/remove_unused_i18n_attrs.mjs +33 -0
  39. package/esm2022/src/template/pipeline/src/phases/resolve_i18n_element_placeholders.mjs +169 -58
  40. package/esm2022/src/template/pipeline/src/phases/resolve_i18n_expression_placeholders.mjs +10 -5
  41. package/esm2022/src/template/pipeline/src/phases/resolve_i18n_icu_placeholders.mjs +5 -21
  42. package/esm2022/src/version.mjs +1 -1
  43. package/fesm2022/compiler.mjs +873 -358
  44. package/fesm2022/compiler.mjs.map +1 -1
  45. package/index.d.ts +21 -1
  46. package/package.json +2 -2
  47. package/esm2022/src/template/pipeline/src/phases/merge_i18n_contexts.mjs +0 -59
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @license Angular v17.1.0-next.1
2
+ * @license Angular v17.1.0-next.3
3
3
  * (c) 2010-2022 Google LLC. https://angular.io/
4
4
  * License: MIT
5
5
  */
@@ -3679,6 +3679,41 @@ function getInjectFn(target) {
3679
3679
  }
3680
3680
  }
3681
3681
 
3682
+ var TagContentType;
3683
+ (function (TagContentType) {
3684
+ TagContentType[TagContentType["RAW_TEXT"] = 0] = "RAW_TEXT";
3685
+ TagContentType[TagContentType["ESCAPABLE_RAW_TEXT"] = 1] = "ESCAPABLE_RAW_TEXT";
3686
+ TagContentType[TagContentType["PARSABLE_DATA"] = 2] = "PARSABLE_DATA";
3687
+ })(TagContentType || (TagContentType = {}));
3688
+ function splitNsName(elementName) {
3689
+ if (elementName[0] != ':') {
3690
+ return [null, elementName];
3691
+ }
3692
+ const colonIndex = elementName.indexOf(':', 1);
3693
+ if (colonIndex === -1) {
3694
+ throw new Error(`Unsupported format "${elementName}" expecting ":namespace:name"`);
3695
+ }
3696
+ return [elementName.slice(1, colonIndex), elementName.slice(colonIndex + 1)];
3697
+ }
3698
+ // `<ng-container>` tags work the same regardless the namespace
3699
+ function isNgContainer(tagName) {
3700
+ return splitNsName(tagName)[1] === 'ng-container';
3701
+ }
3702
+ // `<ng-content>` tags work the same regardless the namespace
3703
+ function isNgContent(tagName) {
3704
+ return splitNsName(tagName)[1] === 'ng-content';
3705
+ }
3706
+ // `<ng-template>` tags work the same regardless the namespace
3707
+ function isNgTemplate(tagName) {
3708
+ return splitNsName(tagName)[1] === 'ng-template';
3709
+ }
3710
+ function getNsPrefix(fullName) {
3711
+ return fullName === null ? null : splitNsName(fullName)[0];
3712
+ }
3713
+ function mergeNsAndName(prefix, localName) {
3714
+ return prefix ? `:${prefix}:${localName}` : localName;
3715
+ }
3716
+
3682
3717
  /**
3683
3718
  * This is an R3 `Node`-like wrapper for a raw `html.Comment` node. We do not currently
3684
3719
  * require the implementation of a visitor for Comments as they are only collected at
@@ -4677,7 +4712,7 @@ const I18N_ATTR_PREFIX = 'i18n-';
4677
4712
  /** Prefix of var expressions used in ICUs */
4678
4713
  const I18N_ICU_VAR_PREFIX = 'VAR_';
4679
4714
  /** Prefix of ICU expressions for post processing */
4680
- const I18N_ICU_MAPPING_PREFIX = 'I18N_EXP_';
4715
+ const I18N_ICU_MAPPING_PREFIX$1 = 'I18N_EXP_';
4681
4716
  /** Placeholder wrapper for i18n expressions **/
4682
4717
  const I18N_PLACEHOLDER_SYMBOL = '�';
4683
4718
  function isI18nAttribute(name) {
@@ -5019,6 +5054,26 @@ class DefinitionMap {
5019
5054
  return literalMap(this.values);
5020
5055
  }
5021
5056
  }
5057
+ /**
5058
+ * Creates a `CssSelector` from an AST node.
5059
+ */
5060
+ function createCssSelectorFromNode(node) {
5061
+ const elementName = node instanceof Element$1 ? node.name : 'ng-template';
5062
+ const attributes = getAttrsForDirectiveMatching(node);
5063
+ const cssSelector = new CssSelector();
5064
+ const elementNameNoNs = splitNsName(elementName)[1];
5065
+ cssSelector.setElement(elementNameNoNs);
5066
+ Object.getOwnPropertyNames(attributes).forEach((name) => {
5067
+ const nameNoNs = splitNsName(name)[1];
5068
+ const value = attributes[name];
5069
+ cssSelector.addAttribute(nameNoNs, value);
5070
+ if (name.toLowerCase() === 'class') {
5071
+ const classes = value.trim().split(/\s+/);
5072
+ classes.forEach(className => cssSelector.addClassName(className));
5073
+ }
5074
+ });
5075
+ return cssSelector;
5076
+ }
5022
5077
  /**
5023
5078
  * Extract a map of properties to values for a given element or template node, which can be used
5024
5079
  * by the directive matching machinery.
@@ -8915,6 +8970,10 @@ var OpKind;
8915
8970
  * An i18n context containing information needed to generate an i18n message.
8916
8971
  */
8917
8972
  OpKind[OpKind["I18nContext"] = 43] = "I18nContext";
8973
+ /**
8974
+ * A creation op that corresponds to i18n attributes on an element.
8975
+ */
8976
+ OpKind[OpKind["I18nAttributes"] = 44] = "I18nAttributes";
8918
8977
  })(OpKind || (OpKind = {}));
8919
8978
  /**
8920
8979
  * Distinguishes different kinds of IR expressions.
@@ -9136,6 +9195,20 @@ var I18nParamResolutionTime;
9136
9195
  */
9137
9196
  I18nParamResolutionTime[I18nParamResolutionTime["Postproccessing"] = 1] = "Postproccessing";
9138
9197
  })(I18nParamResolutionTime || (I18nParamResolutionTime = {}));
9198
+ /**
9199
+ * The contexts in which an i18n expression can be used.
9200
+ */
9201
+ var I18nExpressionFor;
9202
+ (function (I18nExpressionFor) {
9203
+ /**
9204
+ * This expression is used as a value (i.e. inside an i18n block).
9205
+ */
9206
+ I18nExpressionFor[I18nExpressionFor["I18nText"] = 0] = "I18nText";
9207
+ /**
9208
+ * This expression is used in a binding.
9209
+ */
9210
+ I18nExpressionFor[I18nExpressionFor["I18nAttribute"] = 1] = "I18nAttribute";
9211
+ })(I18nExpressionFor || (I18nExpressionFor = {}));
9139
9212
  /**
9140
9213
  * Flags that describe what an i18n param value. These determine how the value is serialized into
9141
9214
  * the final map.
@@ -9195,6 +9268,21 @@ var DerivedRepeaterVarIdentity;
9195
9268
  DerivedRepeaterVarIdentity[DerivedRepeaterVarIdentity["Even"] = 2] = "Even";
9196
9269
  DerivedRepeaterVarIdentity[DerivedRepeaterVarIdentity["Odd"] = 3] = "Odd";
9197
9270
  })(DerivedRepeaterVarIdentity || (DerivedRepeaterVarIdentity = {}));
9271
+ /**
9272
+ * Kinds of i18n contexts. They can be created because of root i18n blocks, or ICUs.
9273
+ */
9274
+ var I18nContextKind;
9275
+ (function (I18nContextKind) {
9276
+ I18nContextKind[I18nContextKind["RootI18n"] = 0] = "RootI18n";
9277
+ I18nContextKind[I18nContextKind["Icu"] = 1] = "Icu";
9278
+ I18nContextKind[I18nContextKind["Attr"] = 2] = "Attr";
9279
+ })(I18nContextKind || (I18nContextKind = {}));
9280
+ var TemplateKind;
9281
+ (function (TemplateKind) {
9282
+ TemplateKind[TemplateKind["NgTemplate"] = 0] = "NgTemplate";
9283
+ TemplateKind[TemplateKind["Structural"] = 1] = "Structural";
9284
+ TemplateKind[TemplateKind["Block"] = 2] = "Block";
9285
+ })(TemplateKind || (TemplateKind = {}));
9198
9286
 
9199
9287
  /**
9200
9288
  * Marker symbol for `ConsumesSlotOpTrait`.
@@ -9302,12 +9390,11 @@ const NEW_OP = {
9302
9390
  /**
9303
9391
  * Create an `InterpolationTextOp`.
9304
9392
  */
9305
- function createInterpolateTextOp(xref, interpolation, i18nPlaceholders, sourceSpan) {
9393
+ function createInterpolateTextOp(xref, interpolation, sourceSpan) {
9306
9394
  return {
9307
9395
  kind: OpKind.InterpolateText,
9308
9396
  target: xref,
9309
9397
  interpolation,
9310
- i18nPlaceholders,
9311
9398
  sourceSpan,
9312
9399
  ...TRAIT_DEPENDS_ON_SLOT_CONTEXT,
9313
9400
  ...TRAIT_CONSUMES_VARS,
@@ -9315,15 +9402,19 @@ function createInterpolateTextOp(xref, interpolation, i18nPlaceholders, sourceSp
9315
9402
  };
9316
9403
  }
9317
9404
  class Interpolation {
9318
- constructor(strings, expressions) {
9405
+ constructor(strings, expressions, i18nPlaceholders) {
9319
9406
  this.strings = strings;
9320
9407
  this.expressions = expressions;
9408
+ this.i18nPlaceholders = i18nPlaceholders;
9409
+ if (i18nPlaceholders.length !== 0 && i18nPlaceholders.length !== expressions.length) {
9410
+ throw new Error(`Expected ${expressions.length} placeholders to match interpolation expression count, but got ${i18nPlaceholders.length}`);
9411
+ }
9321
9412
  }
9322
9413
  }
9323
9414
  /**
9324
9415
  * Create a `BindingOp`, not yet transformed into a particular type of binding.
9325
9416
  */
9326
- function createBindingOp(target, kind, name, expression, unit, securityContext, isTextAttribute, isTemplate, sourceSpan) {
9417
+ function createBindingOp(target, kind, name, expression, unit, securityContext, isTextAttribute, isStructuralTemplate, i18nContext, sourceSpan) {
9327
9418
  return {
9328
9419
  kind: OpKind.Binding,
9329
9420
  bindingKind: kind,
@@ -9333,7 +9424,8 @@ function createBindingOp(target, kind, name, expression, unit, securityContext,
9333
9424
  unit,
9334
9425
  securityContext,
9335
9426
  isTextAttribute,
9336
- isTemplate,
9427
+ isStructuralTemplate: isStructuralTemplate,
9428
+ i18nContext,
9337
9429
  sourceSpan,
9338
9430
  ...NEW_OP,
9339
9431
  };
@@ -9341,7 +9433,7 @@ function createBindingOp(target, kind, name, expression, unit, securityContext,
9341
9433
  /**
9342
9434
  * Create a `PropertyOp`.
9343
9435
  */
9344
- function createPropertyOp(target, name, expression, isAnimationTrigger, securityContext, isTemplate, sourceSpan) {
9436
+ function createPropertyOp(target, name, expression, isAnimationTrigger, securityContext, isStructuralTemplate, i18nContext, sourceSpan) {
9345
9437
  return {
9346
9438
  kind: OpKind.Property,
9347
9439
  target,
@@ -9350,7 +9442,8 @@ function createPropertyOp(target, name, expression, isAnimationTrigger, security
9350
9442
  isAnimationTrigger,
9351
9443
  securityContext,
9352
9444
  sanitizer: null,
9353
- isTemplate,
9445
+ isStructuralTemplate,
9446
+ i18nContext,
9354
9447
  sourceSpan,
9355
9448
  ...TRAIT_DEPENDS_ON_SLOT_CONTEXT,
9356
9449
  ...TRAIT_CONSUMES_VARS,
@@ -9415,7 +9508,7 @@ function createClassMapOp(xref, expression, sourceSpan) {
9415
9508
  /**
9416
9509
  * Create an `AttributeOp`.
9417
9510
  */
9418
- function createAttributeOp(target, name, expression, securityContext, isTextAttribute, isTemplate, sourceSpan) {
9511
+ function createAttributeOp(target, name, expression, securityContext, isTextAttribute, isStructuralTemplate, i18nContext, sourceSpan) {
9419
9512
  return {
9420
9513
  kind: OpKind.Attribute,
9421
9514
  target,
@@ -9424,7 +9517,8 @@ function createAttributeOp(target, name, expression, securityContext, isTextAttr
9424
9517
  securityContext,
9425
9518
  sanitizer: null,
9426
9519
  isTextAttribute,
9427
- isTemplate,
9520
+ isStructuralTemplate,
9521
+ i18nContext,
9428
9522
  sourceSpan,
9429
9523
  ...TRAIT_DEPENDS_ON_SLOT_CONTEXT,
9430
9524
  ...TRAIT_CONSUMES_VARS,
@@ -9485,15 +9579,18 @@ function createDeferWhenOp(target, expr, prefetch, sourceSpan) {
9485
9579
  /**
9486
9580
  * Create an i18n expression op.
9487
9581
  */
9488
- function createI18nExpressionOp(context, target, handle, expression, i18nPlaceholder, resolutionTime, sourceSpan) {
9582
+ function createI18nExpressionOp(context, target, i18nOwner, handle, expression, i18nPlaceholder, resolutionTime, usage, name, sourceSpan) {
9489
9583
  return {
9490
9584
  kind: OpKind.I18nExpression,
9491
9585
  context,
9492
9586
  target,
9587
+ i18nOwner,
9493
9588
  handle,
9494
9589
  expression,
9495
9590
  i18nPlaceholder,
9496
9591
  resolutionTime,
9592
+ usage,
9593
+ name,
9497
9594
  sourceSpan,
9498
9595
  ...NEW_OP,
9499
9596
  ...TRAIT_CONSUMES_VARS,
@@ -9501,12 +9598,12 @@ function createI18nExpressionOp(context, target, handle, expression, i18nPlaceho
9501
9598
  };
9502
9599
  }
9503
9600
  /**
9504
- *Creates an op to apply i18n expression ops
9601
+ * Creates an op to apply i18n expression ops.
9505
9602
  */
9506
- function createI18nApplyOp(target, handle, sourceSpan) {
9603
+ function createI18nApplyOp(owner, handle, sourceSpan) {
9507
9604
  return {
9508
9605
  kind: OpKind.I18nApply,
9509
- target,
9606
+ owner,
9510
9607
  handle,
9511
9608
  sourceSpan,
9512
9609
  ...NEW_OP,
@@ -10310,6 +10407,7 @@ function transformExpressionsInOp(op, transform, flags) {
10310
10407
  case OpKind.ProjectionDef:
10311
10408
  case OpKind.Template:
10312
10409
  case OpKind.Text:
10410
+ case OpKind.I18nAttributes:
10313
10411
  // These operations contain no expressions.
10314
10412
  break;
10315
10413
  default:
@@ -10330,6 +10428,9 @@ function transformExpressionsInExpression(expr, transform, flags) {
10330
10428
  expr.lhs = transformExpressionsInExpression(expr.lhs, transform, flags);
10331
10429
  expr.rhs = transformExpressionsInExpression(expr.rhs, transform, flags);
10332
10430
  }
10431
+ else if (expr instanceof UnaryOperatorExpr) {
10432
+ expr.expr = transformExpressionsInExpression(expr.expr, transform, flags);
10433
+ }
10333
10434
  else if (expr instanceof ReadPropExpr) {
10334
10435
  expr.receiver = transformExpressionsInExpression(expr.receiver, transform, flags);
10335
10436
  }
@@ -10381,6 +10482,9 @@ function transformExpressionsInExpression(expr, transform, flags) {
10381
10482
  expr.expressions[i] = transformExpressionsInExpression(expr.expressions[i], transform, flags);
10382
10483
  }
10383
10484
  }
10485
+ else if (expr instanceof NotExpr) {
10486
+ expr.condition = transformExpressionsInExpression(expr.condition, transform, flags);
10487
+ }
10384
10488
  else if (expr instanceof ReadVarExpr || expr instanceof ExternalExpr ||
10385
10489
  expr instanceof LiteralExpr) {
10386
10490
  // No action for these types.
@@ -10722,10 +10826,11 @@ function createElementStartOp(tag, xref, namespace, i18nPlaceholder, sourceSpan)
10722
10826
  /**
10723
10827
  * Create a `TemplateOp`.
10724
10828
  */
10725
- function createTemplateOp(xref, tag, functionNameSuffix, namespace, i18nPlaceholder, sourceSpan) {
10829
+ function createTemplateOp(xref, templateKind, tag, functionNameSuffix, namespace, i18nPlaceholder, sourceSpan) {
10726
10830
  return {
10727
10831
  kind: OpKind.Template,
10728
10832
  xref,
10833
+ templateKind,
10729
10834
  attributes: null,
10730
10835
  tag,
10731
10836
  handle: new SlotHandle(),
@@ -10848,14 +10953,15 @@ function createProjectionDefOp(def) {
10848
10953
  ...NEW_OP,
10849
10954
  };
10850
10955
  }
10851
- function createProjectionOp(xref, selector, sourceSpan) {
10956
+ function createProjectionOp(xref, selector, i18nPlaceholder, attributes, sourceSpan) {
10852
10957
  return {
10853
10958
  kind: OpKind.Projection,
10854
10959
  xref,
10855
10960
  handle: new SlotHandle(),
10856
10961
  selector,
10962
+ i18nPlaceholder,
10857
10963
  projectionSlotIndex: 0,
10858
- attributes: [],
10964
+ attributes,
10859
10965
  localRefs: [],
10860
10966
  sourceSpan,
10861
10967
  ...NEW_OP,
@@ -10865,13 +10971,14 @@ function createProjectionOp(xref, selector, sourceSpan) {
10865
10971
  /**
10866
10972
  * Create an `ExtractedAttributeOp`.
10867
10973
  */
10868
- function createExtractedAttributeOp(target, bindingKind, name, expression) {
10974
+ function createExtractedAttributeOp(target, bindingKind, name, expression, i18nContext) {
10869
10975
  return {
10870
10976
  kind: OpKind.ExtractedAttribute,
10871
10977
  target,
10872
10978
  bindingKind,
10873
10979
  name,
10874
10980
  expression,
10981
+ i18nContext,
10875
10982
  ...NEW_OP,
10876
10983
  };
10877
10984
  }
@@ -10914,10 +11021,11 @@ function createDeferOnOp(defer, trigger, prefetch, sourceSpan) {
10914
11021
  /**
10915
11022
  * Create an `ExtractedMessageOp`.
10916
11023
  */
10917
- function createI18nMessageOp(xref, i18nBlock, message, messagePlaceholder, params, postprocessingParams, needsPostprocessing) {
11024
+ function createI18nMessageOp(xref, i18nContext, i18nBlock, message, messagePlaceholder, params, postprocessingParams, needsPostprocessing) {
10918
11025
  return {
10919
11026
  kind: OpKind.I18nMessage,
10920
11027
  xref,
11028
+ i18nContext,
10921
11029
  i18nBlock,
10922
11030
  message,
10923
11031
  messagePlaceholder,
@@ -10979,9 +11087,13 @@ function createIcuEndOp(xref) {
10979
11087
  ...NEW_OP,
10980
11088
  };
10981
11089
  }
10982
- function createI18nContextOp(xref, i18nBlock, message, sourceSpan) {
11090
+ function createI18nContextOp(contextKind, xref, i18nBlock, message, sourceSpan) {
11091
+ if (i18nBlock === null && contextKind !== I18nContextKind.Attr) {
11092
+ throw new Error('AssertionError: i18nBlock must be provided for non-attribute contexts.');
11093
+ }
10983
11094
  return {
10984
11095
  kind: OpKind.I18nContext,
11096
+ contextKind,
10985
11097
  xref,
10986
11098
  i18nBlock,
10987
11099
  message,
@@ -10991,6 +11103,17 @@ function createI18nContextOp(xref, i18nBlock, message, sourceSpan) {
10991
11103
  ...NEW_OP,
10992
11104
  };
10993
11105
  }
11106
+ function createI18nAttributesOp(xref, handle, target) {
11107
+ return {
11108
+ kind: OpKind.I18nAttributes,
11109
+ xref,
11110
+ handle,
11111
+ target,
11112
+ i18nAttributesConfig: null,
11113
+ ...NEW_OP,
11114
+ ...TRAIT_CONSUMES_SLOT,
11115
+ };
11116
+ }
10994
11117
  function literalOrArrayLiteral$1(value) {
10995
11118
  if (Array.isArray(value)) {
10996
11119
  return literalArr(value.map(literalOrArrayLiteral$1));
@@ -10998,12 +11121,13 @@ function literalOrArrayLiteral$1(value) {
10998
11121
  return literal(value, INFERRED_TYPE);
10999
11122
  }
11000
11123
 
11001
- function createHostPropertyOp(name, expression, isAnimationTrigger, sourceSpan) {
11124
+ function createHostPropertyOp(name, expression, isAnimationTrigger, i18nContext, sourceSpan) {
11002
11125
  return {
11003
11126
  kind: OpKind.HostProperty,
11004
11127
  name,
11005
11128
  expression,
11006
11129
  isAnimationTrigger,
11130
+ i18nContext,
11007
11131
  sourceSpan,
11008
11132
  ...TRAIT_CONSUMES_VARS,
11009
11133
  ...NEW_OP,
@@ -11240,7 +11364,7 @@ function applyI18nExpressions(job) {
11240
11364
  // Only add apply after expressions that are not followed by more expressions.
11241
11365
  if (op.kind === OpKind.I18nExpression && needsApplication(i18nContexts, op)) {
11242
11366
  // TODO: what should be the source span for the apply op?
11243
- OpList.insertAfter(createI18nApplyOp(op.target, op.handle, null), op);
11367
+ OpList.insertAfter(createI18nApplyOp(op.i18nOwner, op.handle, null), op);
11244
11368
  }
11245
11369
  }
11246
11370
  }
@@ -11253,21 +11377,37 @@ function needsApplication(i18nContexts, op) {
11253
11377
  if (op.next?.kind !== OpKind.I18nExpression) {
11254
11378
  return true;
11255
11379
  }
11256
- // If the next op is an expression targeting a different i18n block, we need to apply.
11257
11380
  const context = i18nContexts.get(op.context);
11258
11381
  const nextContext = i18nContexts.get(op.next.context);
11259
- if (context.i18nBlock !== nextContext.i18nBlock) {
11382
+ if (context === undefined) {
11383
+ throw new Error('AssertionError: expected an I18nContextOp to exist for the I18nExpressionOp\'s context');
11384
+ }
11385
+ if (nextContext === undefined) {
11386
+ throw new Error('AssertionError: expected an I18nContextOp to exist for the next I18nExpressionOp\'s context');
11387
+ }
11388
+ // If the next op is an expression targeting a different i18n block (or different element, in the
11389
+ // case of i18n attributes), we need to apply.
11390
+ // First, handle the case of i18n blocks.
11391
+ if (context.i18nBlock !== null) {
11392
+ // This is a block context. Compare the blocks.
11393
+ if (context.i18nBlock !== nextContext.i18nBlock) {
11394
+ return true;
11395
+ }
11396
+ return false;
11397
+ }
11398
+ // Second, handle the case of i18n attributes.
11399
+ if (op.i18nOwner !== op.next.i18nOwner) {
11260
11400
  return true;
11261
11401
  }
11262
11402
  return false;
11263
11403
  }
11264
11404
 
11265
11405
  /**
11266
- * Updates i18n expression ops to depend on the last slot in their owning i18n block.
11406
+ * Updates i18n expression ops to target the last slot in their owning i18n block, and moves them
11407
+ * after the last update instruction that depends on that slot.
11267
11408
  */
11268
11409
  function assignI18nSlotDependencies(job) {
11269
11410
  const i18nLastSlotConsumers = new Map();
11270
- const i18nContexts = new Map();
11271
11411
  let lastSlotConsumer = null;
11272
11412
  let currentI18nOp = null;
11273
11413
  for (const unit of job.units) {
@@ -11281,21 +11421,54 @@ function assignI18nSlotDependencies(job) {
11281
11421
  currentI18nOp = op;
11282
11422
  break;
11283
11423
  case OpKind.I18nEnd:
11424
+ if (currentI18nOp === null) {
11425
+ throw new Error('AssertionError: Expected an active I18n block while calculating last slot consumers');
11426
+ }
11427
+ if (lastSlotConsumer === null) {
11428
+ throw new Error('AssertionError: Expected a last slot consumer while calculating last slot consumers');
11429
+ }
11284
11430
  i18nLastSlotConsumers.set(currentI18nOp.xref, lastSlotConsumer);
11285
11431
  currentI18nOp = null;
11286
11432
  break;
11287
- case OpKind.I18nContext:
11288
- i18nContexts.set(op.xref, op);
11289
- break;
11290
11433
  }
11291
11434
  }
11292
- // Assign i18n expressions to target the last slot in its owning block.
11435
+ // Expresions that are currently being moved.
11436
+ let opsToMove = [];
11437
+ // Previously we found the last slot-consuming create mode op in the i18n block. That op will be
11438
+ // the new target for any moved i18n expresion inside the i18n block, and that op's slot is
11439
+ // stored here.
11440
+ let moveAfterTarget = null;
11441
+ // This is the last target in the create IR that we saw during iteration. Eventally, it will be
11442
+ // equal to moveAfterTarget. But wait! We need to find the *last* such op whose target is equal
11443
+ // to `moveAfterTarget`.
11444
+ let previousTarget = null;
11293
11445
  for (const op of unit.update) {
11294
- if (op.kind === OpKind.I18nExpression) {
11295
- const i18nContext = i18nContexts.get(op.context);
11296
- op.target = i18nLastSlotConsumers.get(i18nContext.i18nBlock);
11446
+ if (hasDependsOnSlotContextTrait(op)) {
11447
+ // We've found an op that depends on another slot other than the one that we want to move
11448
+ // the expressions to, after previously having seen the one we want to move to.
11449
+ if (moveAfterTarget !== null && previousTarget === moveAfterTarget &&
11450
+ op.target !== previousTarget) {
11451
+ OpList.insertBefore(opsToMove, op);
11452
+ moveAfterTarget = null;
11453
+ opsToMove = [];
11454
+ }
11455
+ previousTarget = op.target;
11456
+ }
11457
+ if (op.kind === OpKind.I18nExpression && op.usage === I18nExpressionFor.I18nText) {
11458
+ // This is an I18nExpressionOps that is used for text (not attributes).
11459
+ OpList.remove(op);
11460
+ opsToMove.push(op);
11461
+ const target = i18nLastSlotConsumers.get(op.i18nOwner);
11462
+ if (target === undefined) {
11463
+ throw new Error('AssertionError: Expected to find a last slot consumer for an I18nExpressionOp');
11464
+ }
11465
+ op.target = target;
11466
+ moveAfterTarget = op.target;
11297
11467
  }
11298
11468
  }
11469
+ if (moveAfterTarget !== null) {
11470
+ unit.update.push(opsToMove);
11471
+ }
11299
11472
  }
11300
11473
  }
11301
11474
 
@@ -11327,22 +11500,36 @@ function extractAttributes(job) {
11327
11500
  break;
11328
11501
  case OpKind.Property:
11329
11502
  if (!op.isAnimationTrigger) {
11330
- OpList.insertBefore(createExtractedAttributeOp(op.target, op.isTemplate ? BindingKind.Template : BindingKind.Property, op.name, null), lookupElement$2(elements, op.target));
11503
+ let bindingKind;
11504
+ if (op.i18nContext !== null) {
11505
+ // If the binding has an i18n context, it is an i18n attribute, and should have that
11506
+ // kind in the consts array.
11507
+ bindingKind = BindingKind.I18n;
11508
+ }
11509
+ else if (op.isStructuralTemplate) {
11510
+ // TODO: How do i18n attributes on templates work?!
11511
+ bindingKind = BindingKind.Template;
11512
+ }
11513
+ else {
11514
+ bindingKind = BindingKind.Property;
11515
+ }
11516
+ OpList.insertBefore(createExtractedAttributeOp(op.target, bindingKind, op.name, null, null), lookupElement$2(elements, op.target));
11331
11517
  }
11332
11518
  break;
11333
11519
  case OpKind.StyleProp:
11334
11520
  case OpKind.ClassProp:
11521
+ // TODO: Can style or class bindings be i18n attributes?
11335
11522
  // The old compiler treated empty style bindings as regular bindings for the purpose of
11336
11523
  // directive matching. That behavior is incorrect, but we emulate it in compatibility
11337
11524
  // mode.
11338
11525
  if (unit.job.compatibility === CompatibilityMode.TemplateDefinitionBuilder &&
11339
11526
  op.expression instanceof EmptyExpr) {
11340
- OpList.insertBefore(createExtractedAttributeOp(op.target, BindingKind.Property, op.name, null), lookupElement$2(elements, op.target));
11527
+ OpList.insertBefore(createExtractedAttributeOp(op.target, BindingKind.Property, op.name, null, null), lookupElement$2(elements, op.target));
11341
11528
  }
11342
11529
  break;
11343
11530
  case OpKind.Listener:
11344
11531
  if (!op.isAnimationListener) {
11345
- const extractedAttributeOp = createExtractedAttributeOp(op.target, BindingKind.Property, op.name, null);
11532
+ const extractedAttributeOp = createExtractedAttributeOp(op.target, BindingKind.Property, op.name, null, null);
11346
11533
  if (job.kind === CompilationJobKind.Host) {
11347
11534
  // This attribute will apply to the enclosing host binding compilation unit, so order
11348
11535
  // doesn't matter.
@@ -11391,7 +11578,7 @@ function extractAttributeOp(unit, op, elements) {
11391
11578
  }
11392
11579
  }
11393
11580
  if (extractable) {
11394
- const extractedAttributeOp = createExtractedAttributeOp(op.target, op.isTemplate ? BindingKind.Template : BindingKind.Attribute, op.name, op.expression);
11581
+ const extractedAttributeOp = createExtractedAttributeOp(op.target, op.isStructuralTemplate ? BindingKind.Template : BindingKind.Attribute, op.name, op.expression, op.i18nContext);
11395
11582
  if (unit.job.kind === CompilationJobKind.Host) {
11396
11583
  // This attribute will apply to the enclosing host binding compilation unit, so order doesn't
11397
11584
  // matter.
@@ -11438,16 +11625,16 @@ function specializeBindings(job) {
11438
11625
  target.nonBindable = true;
11439
11626
  }
11440
11627
  else {
11441
- OpList.replace(op, createAttributeOp(op.target, op.name, op.expression, op.securityContext, op.isTextAttribute, op.isTemplate, op.sourceSpan));
11628
+ OpList.replace(op, createAttributeOp(op.target, op.name, op.expression, op.securityContext, op.isTextAttribute, op.isStructuralTemplate, op.i18nContext, op.sourceSpan));
11442
11629
  }
11443
11630
  break;
11444
11631
  case BindingKind.Property:
11445
11632
  case BindingKind.Animation:
11446
11633
  if (job.kind === CompilationJobKind.Host) {
11447
- OpList.replace(op, createHostPropertyOp(op.name, op.expression, op.bindingKind === BindingKind.Animation, op.sourceSpan));
11634
+ OpList.replace(op, createHostPropertyOp(op.name, op.expression, op.bindingKind === BindingKind.Animation, op.i18nContext, op.sourceSpan));
11448
11635
  }
11449
11636
  else {
11450
- OpList.replace(op, createPropertyOp(op.target, op.name, op.expression, op.bindingKind === BindingKind.Animation, op.securityContext, op.isTemplate, op.sourceSpan));
11637
+ OpList.replace(op, createPropertyOp(op.target, op.name, op.expression, op.bindingKind === BindingKind.Animation, op.securityContext, op.isStructuralTemplate, op.i18nContext, op.sourceSpan));
11451
11638
  }
11452
11639
  break;
11453
11640
  case BindingKind.I18n:
@@ -11624,41 +11811,6 @@ function generateConditionalExpressions(job) {
11624
11811
  }
11625
11812
  }
11626
11813
 
11627
- var TagContentType;
11628
- (function (TagContentType) {
11629
- TagContentType[TagContentType["RAW_TEXT"] = 0] = "RAW_TEXT";
11630
- TagContentType[TagContentType["ESCAPABLE_RAW_TEXT"] = 1] = "ESCAPABLE_RAW_TEXT";
11631
- TagContentType[TagContentType["PARSABLE_DATA"] = 2] = "PARSABLE_DATA";
11632
- })(TagContentType || (TagContentType = {}));
11633
- function splitNsName(elementName) {
11634
- if (elementName[0] != ':') {
11635
- return [null, elementName];
11636
- }
11637
- const colonIndex = elementName.indexOf(':', 1);
11638
- if (colonIndex === -1) {
11639
- throw new Error(`Unsupported format "${elementName}" expecting ":namespace:name"`);
11640
- }
11641
- return [elementName.slice(1, colonIndex), elementName.slice(colonIndex + 1)];
11642
- }
11643
- // `<ng-container>` tags work the same regardless the namespace
11644
- function isNgContainer(tagName) {
11645
- return splitNsName(tagName)[1] === 'ng-container';
11646
- }
11647
- // `<ng-content>` tags work the same regardless the namespace
11648
- function isNgContent(tagName) {
11649
- return splitNsName(tagName)[1] === 'ng-content';
11650
- }
11651
- // `<ng-template>` tags work the same regardless the namespace
11652
- function isNgTemplate(tagName) {
11653
- return splitNsName(tagName)[1] === 'ng-template';
11654
- }
11655
- function getNsPrefix(fullName) {
11656
- return fullName === null ? null : splitNsName(fullName)[0];
11657
- }
11658
- function mergeNsAndName(prefix, localName) {
11659
- return prefix ? `:${prefix}:${localName}` : localName;
11660
- }
11661
-
11662
11814
  const BINARY_OPERATORS = new Map([
11663
11815
  ['&&', BinaryOperator.And],
11664
11816
  ['>', BinaryOperator.Bigger],
@@ -11803,7 +11955,7 @@ class ElementAttributes {
11803
11955
  array.push(...getAttributeNameLiterals$1(name));
11804
11956
  if (kind === BindingKind.Attribute || kind === BindingKind.StyleProperty) {
11805
11957
  if (value === null) {
11806
- throw Error('Attribute & style element attributes must have a value');
11958
+ throw Error('Attribute, i18n attribute, & style element attributes must have a value');
11807
11959
  }
11808
11960
  array.push(value);
11809
11961
  }
@@ -11857,6 +12009,50 @@ function serializeAttributes({ attributes, bindings, classes, i18n, projectAs, s
11857
12009
  return literalArr(attrArray);
11858
12010
  }
11859
12011
 
12012
+ /**
12013
+ * Some binding instructions in the update block may actually correspond to i18n bindings. In that
12014
+ * case, they should be replaced with i18nExp instructions for the dynamic portions.
12015
+ */
12016
+ function convertI18nBindings(job) {
12017
+ const i18nAttributesByElem = new Map();
12018
+ for (const unit of job.units) {
12019
+ for (const op of unit.create) {
12020
+ if (op.kind === OpKind.I18nAttributes) {
12021
+ i18nAttributesByElem.set(op.target, op);
12022
+ }
12023
+ }
12024
+ for (const op of unit.update) {
12025
+ switch (op.kind) {
12026
+ case OpKind.Property:
12027
+ case OpKind.Attribute:
12028
+ if (op.i18nContext === null) {
12029
+ continue;
12030
+ }
12031
+ if (!(op.expression instanceof Interpolation)) {
12032
+ continue;
12033
+ }
12034
+ const i18nAttributesForElem = i18nAttributesByElem.get(op.target);
12035
+ if (i18nAttributesForElem === undefined) {
12036
+ throw new Error('AssertionError: An i18n attribute binding instruction requires the owning element to have an I18nAttributes create instruction');
12037
+ }
12038
+ if (i18nAttributesForElem.target !== op.target) {
12039
+ throw new Error('AssertionError: Expected i18nAttributes target element to match binding target element');
12040
+ }
12041
+ const ops = [];
12042
+ for (let i = 0; i < op.expression.expressions.length; i++) {
12043
+ const expr = op.expression.expressions[i];
12044
+ if (op.expression.i18nPlaceholders.length !== op.expression.expressions.length) {
12045
+ 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`);
12046
+ }
12047
+ ops.push(createI18nExpressionOp(op.i18nContext, i18nAttributesForElem.target, i18nAttributesForElem.xref, i18nAttributesForElem.handle, expr, op.expression.i18nPlaceholders[i], I18nParamResolutionTime.Creation, I18nExpressionFor.I18nAttribute, op.name, op.sourceSpan));
12048
+ }
12049
+ OpList.replaceWithMany(op, ops);
12050
+ break;
12051
+ }
12052
+ }
12053
+ }
12054
+ }
12055
+
11860
12056
  /**
11861
12057
  * Create extracted deps functions for defer ops.
11862
12058
  */
@@ -11902,17 +12098,22 @@ function createDeferDepsFns(job) {
11902
12098
  * message.)
11903
12099
  */
11904
12100
  function createI18nContexts(job) {
12101
+ const rootContexts = new Map();
11905
12102
  let currentI18nOp = null;
11906
12103
  let xref;
11907
12104
  for (const unit of job.units) {
11908
12105
  for (const op of unit.create) {
11909
12106
  switch (op.kind) {
11910
12107
  case OpKind.I18nStart:
11911
- // Each i18n block gets its own context.
11912
- xref = job.allocateXrefId();
11913
- unit.create.push(createI18nContextOp(xref, op.xref, op.message, null));
11914
- op.context = xref;
11915
12108
  currentI18nOp = op;
12109
+ // Each root i18n block gets its own context, child ones refer to the context for their
12110
+ // root block.
12111
+ if (op.xref === op.root) {
12112
+ xref = job.allocateXrefId();
12113
+ unit.create.push(createI18nContextOp(I18nContextKind.RootI18n, xref, op.xref, op.message, null));
12114
+ op.context = xref;
12115
+ rootContexts.set(op.xref, xref);
12116
+ }
11916
12117
  break;
11917
12118
  case OpKind.I18nEnd:
11918
12119
  currentI18nOp = null;
@@ -11926,7 +12127,7 @@ function createI18nContexts(job) {
11926
12127
  if (op.message.id !== currentI18nOp.message.id) {
11927
12128
  // There was an enclosing i18n block around this ICU somewhere.
11928
12129
  xref = job.allocateXrefId();
11929
- unit.create.push(createI18nContextOp(xref, currentI18nOp.xref, op.message, null));
12130
+ unit.create.push(createI18nContextOp(I18nContextKind.Icu, xref, currentI18nOp.xref, op.message, null));
11930
12131
  op.context = xref;
11931
12132
  }
11932
12133
  else {
@@ -11938,6 +12139,15 @@ function createI18nContexts(job) {
11938
12139
  }
11939
12140
  }
11940
12141
  }
12142
+ // Assign contexts to child i18n blocks, now that all root i18n blocks have their context
12143
+ // assigned.
12144
+ for (const unit of job.units) {
12145
+ for (const op of unit.create) {
12146
+ if (op.kind === OpKind.I18nStart && op.xref !== op.root) {
12147
+ op.context = rootContexts.get(op.root);
12148
+ }
12149
+ }
12150
+ }
11941
12151
  }
11942
12152
 
11943
12153
  /**
@@ -12286,7 +12496,7 @@ function ternaryTransform(e) {
12286
12496
  /**
12287
12497
  * The escape sequence used indicate message param values.
12288
12498
  */
12289
- const ESCAPE = '\uFFFD';
12499
+ const ESCAPE$1 = '\uFFFD';
12290
12500
  /**
12291
12501
  * Marker used to indicate an element tag.
12292
12502
  */
@@ -12320,11 +12530,9 @@ const LIST_DELIMITER = '|';
12320
12530
  * used in the final output.
12321
12531
  */
12322
12532
  function extractI18nMessages(job) {
12323
- // Save the i18n context ops for later use.
12533
+ // Save the i18n start and i18n context ops for later use.
12324
12534
  const i18nContexts = new Map();
12325
- // Record which contexts represent i18n blocks (any other contexts are assumed to have been
12326
- // created from ICUs).
12327
- const i18nBlockContexts = new Set();
12535
+ const i18nBlocks = new Map();
12328
12536
  for (const unit of job.units) {
12329
12537
  for (const op of unit.create) {
12330
12538
  switch (op.kind) {
@@ -12332,11 +12540,23 @@ function extractI18nMessages(job) {
12332
12540
  i18nContexts.set(op.xref, op);
12333
12541
  break;
12334
12542
  case OpKind.I18nStart:
12335
- i18nBlockContexts.add(op.context);
12543
+ i18nBlocks.set(op.xref, op);
12336
12544
  break;
12337
12545
  }
12338
12546
  }
12339
12547
  }
12548
+ // TODO: Miles and I think that contexts have a 1-to-1 correspondence with extracted messages, so
12549
+ // this phase can probably be simplified.
12550
+ // Extract messages from contexts of i18n attributes.
12551
+ for (const unit of job.units) {
12552
+ for (const op of unit.create) {
12553
+ if (op.kind !== OpKind.I18nContext || op.contextKind !== I18nContextKind.Attr) {
12554
+ continue;
12555
+ }
12556
+ const i18nMessageOp = createI18nMessage(job, op);
12557
+ unit.create.push(i18nMessageOp);
12558
+ }
12559
+ }
12340
12560
  // Extract messages from root i18n blocks.
12341
12561
  const i18nBlockMessages = new Map();
12342
12562
  for (const unit of job.units) {
@@ -12359,11 +12579,15 @@ function extractI18nMessages(job) {
12359
12579
  if (!op.context) {
12360
12580
  throw Error('ICU op should have its context set.');
12361
12581
  }
12362
- if (!i18nBlockContexts.has(op.context)) {
12363
- const i18nContext = i18nContexts.get(op.context);
12582
+ const i18nContext = i18nContexts.get(op.context);
12583
+ if (i18nContext.contextKind === I18nContextKind.Icu) {
12584
+ if (i18nContext.i18nBlock === null) {
12585
+ throw Error('ICU context should have its i18n block set.');
12586
+ }
12364
12587
  const subMessage = createI18nMessage(job, i18nContext, op.messagePlaceholder);
12365
12588
  unit.create.push(subMessage);
12366
- const parentMessage = i18nBlockMessages.get(i18nContext.i18nBlock);
12589
+ const rootI18nId = i18nBlocks.get(i18nContext.i18nBlock).root;
12590
+ const parentMessage = i18nBlockMessages.get(rootI18nId);
12367
12591
  parentMessage?.subMessages.push(subMessage.xref);
12368
12592
  }
12369
12593
  OpList.remove(op);
@@ -12379,26 +12603,28 @@ function extractI18nMessages(job) {
12379
12603
  * Create an i18n message op from an i18n context op.
12380
12604
  */
12381
12605
  function createI18nMessage(job, context, messagePlaceholder) {
12382
- let needsPostprocessing = context.postprocessingParams.size > 0;
12606
+ let formattedParams = formatParams(context.params);
12607
+ const formattedPostprocessingParams = formatParams(context.postprocessingParams);
12608
+ let needsPostprocessing = formattedPostprocessingParams.size > 0;
12383
12609
  for (const values of context.params.values()) {
12384
12610
  if (values.length > 1) {
12385
12611
  needsPostprocessing = true;
12386
12612
  }
12387
12613
  }
12388
- return createI18nMessageOp(job.allocateXrefId(), context.i18nBlock, context.message, messagePlaceholder ?? null, formatParams(context.params), formatParams(context.postprocessingParams), needsPostprocessing);
12614
+ return createI18nMessageOp(job.allocateXrefId(), context.xref, context.i18nBlock, context.message, messagePlaceholder ?? null, formattedParams, formattedPostprocessingParams, needsPostprocessing);
12389
12615
  }
12390
12616
  /**
12391
12617
  * Formats a map of `I18nParamValue[]` values into a map of `Expression` values.
12392
12618
  */
12393
12619
  function formatParams(params) {
12394
- const result = new Map();
12620
+ const formattedParams = new Map();
12395
12621
  for (const [placeholder, placeholderValues] of params) {
12396
12622
  const serializedValues = formatParamValues(placeholderValues);
12397
12623
  if (serializedValues !== null) {
12398
- result.set(placeholder, literal(formatParamValues(placeholderValues)));
12624
+ formattedParams.set(placeholder, literal(serializedValues));
12399
12625
  }
12400
12626
  }
12401
- return result;
12627
+ return formattedParams;
12402
12628
  }
12403
12629
  /**
12404
12630
  * Formats an `I18nParamValue[]` into a string (or null for empty array).
@@ -12416,10 +12642,47 @@ function formatParamValues(values) {
12416
12642
  * Formats a single `I18nParamValue` into a string
12417
12643
  */
12418
12644
  function formatValue(value) {
12645
+ // Element tags with a structural directive use a special form that concatenates the element and
12646
+ // template values.
12647
+ if ((value.flags & I18nParamValueFlags.ElementTag) &&
12648
+ (value.flags & I18nParamValueFlags.TemplateTag)) {
12649
+ if (typeof value.value !== 'object') {
12650
+ throw Error('AssertionError: Expected i18n param value to have an element and template slot');
12651
+ }
12652
+ const elementValue = formatValue({
12653
+ ...value,
12654
+ value: value.value.element,
12655
+ flags: value.flags & ~I18nParamValueFlags.TemplateTag
12656
+ });
12657
+ const templateValue = formatValue({
12658
+ ...value,
12659
+ value: value.value.template,
12660
+ flags: value.flags & ~I18nParamValueFlags.ElementTag
12661
+ });
12662
+ // TODO(mmalerba): This is likely a bug in TemplateDefinitionBuilder, we should not need to
12663
+ // record the template value twice. For now I'm re-implementing the behavior here to keep the
12664
+ // output consistent with TemplateDefinitionBuilder.
12665
+ if ((value.flags & I18nParamValueFlags.OpenTag) &&
12666
+ (value.flags & I18nParamValueFlags.CloseTag)) {
12667
+ return `${templateValue}${elementValue}${templateValue}`;
12668
+ }
12669
+ // To match the TemplateDefinitionBuilder output, flip the order depending on whether the
12670
+ // values represent a closing or opening tag (or both).
12671
+ // TODO(mmalerba): Figure out if this makes a difference in terms of either functionality,
12672
+ // or the resulting message ID. If not, we can remove the special-casing in the future.
12673
+ return value.flags & I18nParamValueFlags.CloseTag ? `${elementValue}${templateValue}` :
12674
+ `${templateValue}${elementValue}`;
12675
+ }
12676
+ // Self-closing tags use a special form that concatenates the start and close tag values.
12677
+ if ((value.flags & I18nParamValueFlags.OpenTag) &&
12678
+ (value.flags & I18nParamValueFlags.CloseTag)) {
12679
+ return `${formatValue({ ...value, flags: value.flags & ~I18nParamValueFlags.CloseTag })}${formatValue({ ...value, flags: value.flags & ~I18nParamValueFlags.OpenTag })}`;
12680
+ }
12419
12681
  // If there are no special flags, just return the raw value.
12420
12682
  if (value.flags === I18nParamValueFlags.None) {
12421
12683
  return `${value.value}`;
12422
12684
  }
12685
+ // Encode the remaining flags as part of the value.
12423
12686
  let tagMarker = '';
12424
12687
  let closeMarker = '';
12425
12688
  if (value.flags & I18nParamValueFlags.ElementTag) {
@@ -12432,12 +12695,7 @@ function formatValue(value) {
12432
12695
  closeMarker = value.flags & I18nParamValueFlags.CloseTag ? TAG_CLOSE_MARKER : '';
12433
12696
  }
12434
12697
  const context = value.subTemplateIndex === null ? '' : `${CONTEXT_MARKER}${value.subTemplateIndex}`;
12435
- // Self-closing tags use a special form that concatenates the start and close tag values.
12436
- if ((value.flags & I18nParamValueFlags.OpenTag) &&
12437
- (value.flags & I18nParamValueFlags.CloseTag)) {
12438
- return `${ESCAPE}${tagMarker}${value.value}${context}${ESCAPE}${ESCAPE}${closeMarker}${tagMarker}${value.value}${context}${ESCAPE}`;
12439
- }
12440
- return `${ESCAPE}${closeMarker}${tagMarker}${value.value}${context}${ESCAPE}`;
12698
+ return `${ESCAPE$1}${closeMarker}${tagMarker}${value.value}${context}${ESCAPE$1}`;
12441
12699
  }
12442
12700
 
12443
12701
  /**
@@ -12471,7 +12729,7 @@ function generateAdvance(job) {
12471
12729
  else if (!slotMap.has(op.target)) {
12472
12730
  // We expect ops that _do_ depend on the slot counter to point at declarations that exist in
12473
12731
  // the `slotMap`.
12474
- throw new Error(`AssertionError: reference to unknown slot for var ${op.target}`);
12732
+ throw new Error(`AssertionError: reference to unknown slot for target ${op.target}`);
12475
12733
  }
12476
12734
  const slot = slotMap.get(op.target);
12477
12735
  // Does the slot counter need to be adjusted?
@@ -19681,35 +19939,132 @@ const NG_I18N_CLOSURE_MODE$1 = 'ngI18nClosureMode';
19681
19939
  * considers variables like `I18N_0` as constants and throws an error when their value changes.
19682
19940
  */
19683
19941
  const TRANSLATION_VAR_PREFIX = 'i18n_';
19942
+ /** Prefix of ICU expressions for post processing */
19943
+ const I18N_ICU_MAPPING_PREFIX = 'I18N_EXP_';
19944
+ /**
19945
+ * The escape sequence used for message param values.
19946
+ */
19947
+ const ESCAPE = '\uFFFD';
19684
19948
  /**
19685
19949
  * Lifts i18n properties into the consts array.
19686
19950
  * TODO: Can we use `ConstCollectedExpr`?
19951
+ * TODO: The way the various attributes are linked together is very complex. Perhaps we could
19952
+ * simplify the process, maybe by combining the context and message ops?
19687
19953
  */
19688
19954
  function collectI18nConsts(job) {
19689
19955
  const fileBasedI18nSuffix = job.relativeContextFilePath.replace(/[^A-Za-z0-9]/g, '_').toUpperCase() + '_';
19690
- const messageConstIndices = new Map();
19691
- // Remove all of the i18n message ops into a map.
19956
+ // Step One: Build up various lookup maps we need to collect all the consts.
19957
+ // Context Xref -> Extracted Attribute Op
19958
+ const extractedAttributesByI18nContext = new Map();
19959
+ // Element/ElementStart Xref -> I18n Attributes config op
19960
+ const i18nAttributesByElement = new Map();
19961
+ // Element/ElementStart Xref -> All I18n Expression ops for attrs on that target
19962
+ const i18nExpressionsByElement = new Map();
19963
+ // I18n Message Xref -> I18n Message Op (TODO: use a central op map)
19692
19964
  const messages = new Map();
19965
+ for (const unit of job.units) {
19966
+ for (const op of unit.ops()) {
19967
+ if (op.kind === OpKind.ExtractedAttribute && op.i18nContext !== null) {
19968
+ extractedAttributesByI18nContext.set(op.i18nContext, op);
19969
+ }
19970
+ else if (op.kind === OpKind.I18nAttributes) {
19971
+ i18nAttributesByElement.set(op.target, op);
19972
+ }
19973
+ else if (op.kind === OpKind.I18nExpression && op.usage === I18nExpressionFor.I18nAttribute) {
19974
+ const expressions = i18nExpressionsByElement.get(op.target) ?? [];
19975
+ expressions.push(op);
19976
+ i18nExpressionsByElement.set(op.target, expressions);
19977
+ }
19978
+ else if (op.kind === OpKind.I18nMessage) {
19979
+ messages.set(op.xref, op);
19980
+ }
19981
+ }
19982
+ }
19983
+ // Step Two: Serialize the extracted i18n messages for root i18n blocks and i18n attributes into
19984
+ // the const array.
19985
+ //
19986
+ // Also, each i18n message will have a variable expression that can refer to its
19987
+ // value. Store these expressions in the appropriate place:
19988
+ // 1. For normal i18n content, it also goes in the const array. We save the const index to use
19989
+ // later.
19990
+ // 2. For extracted attributes, it becomes the value of the extracted attribute instruction.
19991
+ // 3. For i18n bindings, it will go in a separate const array instruction below; for now, we just
19992
+ // save it.
19993
+ const i18nValuesByContext = new Map();
19994
+ const messageConstIndices = new Map();
19693
19995
  for (const unit of job.units) {
19694
19996
  for (const op of unit.create) {
19695
19997
  if (op.kind === OpKind.I18nMessage) {
19696
- messages.set(op.xref, op);
19998
+ if (op.messagePlaceholder === null) {
19999
+ const { mainVar, statements } = collectMessage(job, fileBasedI18nSuffix, messages, op);
20000
+ if (op.i18nBlock !== null) {
20001
+ // This is a regular i18n message with a corresponding i18n block. Collect it into the
20002
+ // const array.
20003
+ const i18nConst = job.addConst(mainVar, statements);
20004
+ messageConstIndices.set(op.i18nBlock, i18nConst);
20005
+ }
20006
+ else {
20007
+ // This is an i18n attribute. Extract the initializers into the const pool.
20008
+ job.constsInitializers.push(...statements);
20009
+ // Save the i18n variable value for later.
20010
+ i18nValuesByContext.set(op.i18nContext, mainVar);
20011
+ // This i18n message may correspond to an individual extracted attribute. If so, The
20012
+ // value of that attribute is updated to read the extracted i18n variable.
20013
+ const attributeForMessage = extractedAttributesByI18nContext.get(op.i18nContext);
20014
+ if (attributeForMessage !== undefined) {
20015
+ attributeForMessage.expression = mainVar;
20016
+ }
20017
+ }
20018
+ }
19697
20019
  OpList.remove(op);
19698
20020
  }
19699
20021
  }
19700
20022
  }
19701
- // Serialize the extracted messages for root i18n blocks into the const array.
19702
- for (const op of messages.values()) {
19703
- if (op.kind === OpKind.I18nMessage && op.messagePlaceholder === null) {
19704
- const { mainVar, statements } = collectMessage(job, fileBasedI18nSuffix, messages, op);
19705
- messageConstIndices.set(op.i18nBlock, job.addConst(mainVar, statements));
20023
+ // Step Three: Serialize I18nAttributes configurations into the const array. Each I18nAttributes
20024
+ // instruction has a config array, which contains k-v pairs describing each binding name, and the
20025
+ // i18n variable that provides the value.
20026
+ for (const unit of job.units) {
20027
+ for (const elem of unit.create) {
20028
+ if (isElementOrContainerOp(elem)) {
20029
+ const i18nAttributes = i18nAttributesByElement.get(elem.xref);
20030
+ if (i18nAttributes === undefined) {
20031
+ // This element is not associated with an i18n attributes configuration instruction.
20032
+ continue;
20033
+ }
20034
+ let i18nExpressions = i18nExpressionsByElement.get(elem.xref);
20035
+ if (i18nExpressions === undefined) {
20036
+ // Unused i18nAttributes should have already been removed.
20037
+ // TODO: Should the removal of those dead instructions be merged with this phase?
20038
+ throw new Error('AssertionError: Could not find any i18n expressions associated with an I18nAttributes instruction');
20039
+ }
20040
+ // Find expressions for all the unique property names, removing duplicates.
20041
+ const seenPropertyNames = new Set();
20042
+ i18nExpressions = i18nExpressions.filter(i18nExpr => {
20043
+ const seen = (seenPropertyNames.has(i18nExpr.name));
20044
+ seenPropertyNames.add(i18nExpr.name);
20045
+ return !seen;
20046
+ });
20047
+ const i18nAttributeConfig = i18nExpressions.flatMap(i18nExpr => {
20048
+ const i18nExprValue = i18nValuesByContext.get(i18nExpr.context);
20049
+ if (i18nExprValue === undefined) {
20050
+ throw new Error('AssertionError: Could not find i18n expression\'s value');
20051
+ }
20052
+ return [literal(i18nExpr.name), i18nExprValue];
20053
+ });
20054
+ i18nAttributes.i18nAttributesConfig =
20055
+ job.addConst(new LiteralArrayExpr(i18nAttributeConfig));
20056
+ }
19706
20057
  }
19707
20058
  }
19708
- // Assign const index to i18n ops that messages were extracted from.
20059
+ // Step Four: Propagate the extracted const index into i18n ops that messages were extracted from.
19709
20060
  for (const unit of job.units) {
19710
20061
  for (const op of unit.create) {
19711
20062
  if (op.kind === OpKind.I18nStart) {
19712
- op.messageIndex = messageConstIndices.get(op.root);
20063
+ const msgIndex = messageConstIndices.get(op.root);
20064
+ if (msgIndex === undefined) {
20065
+ throw new Error('AssertionError: Could not find corresponding i18n block index for an i18n message op; was an i18n message incorrectly assumed to correspond to an attribute?');
20066
+ }
20067
+ op.messageIndex = msgIndex;
19713
20068
  }
19714
20069
  }
19715
20070
  }
@@ -19719,18 +20074,23 @@ function collectI18nConsts(job) {
19719
20074
  * This will recursively collect any sub-messages referenced from the parent message as well.
19720
20075
  */
19721
20076
  function collectMessage(job, fileBasedI18nSuffix, messages, messageOp) {
19722
- // Recursively collect any sub-messages, and fill in their placeholders in this message.
20077
+ // Recursively collect any sub-messages, record each sub-message's main variable under its
20078
+ // placeholder so that we can add them to the params for the parent message. It is possible
20079
+ // that multiple sub-messages will share the same placeholder, so we need to track an array of
20080
+ // variables for each placeholder.
19723
20081
  const statements = [];
20082
+ const subMessagePlaceholders = new Map();
19724
20083
  for (const subMessageId of messageOp.subMessages) {
19725
20084
  const subMessage = messages.get(subMessageId);
19726
20085
  const { mainVar: subMessageVar, statements: subMessageStatements } = collectMessage(job, fileBasedI18nSuffix, messages, subMessage);
19727
20086
  statements.push(...subMessageStatements);
19728
- messageOp.params.set(subMessage.messagePlaceholder, subMessageVar);
20087
+ const subMessages = subMessagePlaceholders.get(subMessage.messagePlaceholder) ?? [];
20088
+ subMessages.push(subMessageVar);
20089
+ subMessagePlaceholders.set(subMessage.messagePlaceholder, subMessages);
19729
20090
  }
20091
+ addSubMessageParams(messageOp, subMessagePlaceholders);
19730
20092
  // Sort the params for consistency with TemaplateDefinitionBuilder output.
19731
20093
  messageOp.params = new Map([...messageOp.params.entries()].sort());
19732
- // Check that the message has all of its parameters filled out.
19733
- assertAllParamsResolved(messageOp);
19734
20094
  const mainVar = variable(job.pool.uniqueName(TRANSLATION_VAR_PREFIX));
19735
20095
  // Closure Compiler requires const names to start with `MSG_` but disallows any other
19736
20096
  // const to start with `MSG_`. We define a variable starting with `MSG_` just for the
@@ -19741,10 +20101,11 @@ function collectMessage(job, fileBasedI18nSuffix, messages, messageOp) {
19741
20101
  // set in post-processing.
19742
20102
  if (messageOp.needsPostprocessing) {
19743
20103
  // Sort the post-processing params for consistency with TemaplateDefinitionBuilder output.
19744
- messageOp.postprocessingParams = new Map([...messageOp.postprocessingParams.entries()].sort());
20104
+ const postprocessingParams = Object.fromEntries([...messageOp.postprocessingParams.entries()].sort());
20105
+ const formattedPostprocessingParams = formatI18nPlaceholderNamesInMap(postprocessingParams, /* useCamelCase */ false);
19745
20106
  const extraTransformFnParams = [];
19746
20107
  if (messageOp.postprocessingParams.size > 0) {
19747
- extraTransformFnParams.push(literalMap([...messageOp.postprocessingParams].map(([key, value]) => ({ key, value, quoted: true }))));
20108
+ extraTransformFnParams.push(mapLiteral(formattedPostprocessingParams, /* quoted */ true));
19748
20109
  }
19749
20110
  transformFn = (expr) => importExpr(Identifiers.i18nPostprocess).callFn([expr, ...extraTransformFnParams]);
19750
20111
  }
@@ -19752,6 +20113,26 @@ function collectMessage(job, fileBasedI18nSuffix, messages, messageOp) {
19752
20113
  statements.push(...getTranslationDeclStmts$1(messageOp.message, mainVar, closureVar, messageOp.params, transformFn));
19753
20114
  return { mainVar, statements };
19754
20115
  }
20116
+ /**
20117
+ * Adds the given subMessage placeholders to the given message op.
20118
+ *
20119
+ * If a placeholder only corresponds to a single sub-message variable, we just set that variable
20120
+ * as the param value. However, if the placeholder corresponds to multiple sub-message
20121
+ * variables, we need to add a special placeholder value that is handled by the post-processing
20122
+ * step. We then add the array of variables as a post-processing param.
20123
+ */
20124
+ function addSubMessageParams(messageOp, subMessagePlaceholders) {
20125
+ for (const [placeholder, subMessages] of subMessagePlaceholders) {
20126
+ if (subMessages.length === 1) {
20127
+ messageOp.params.set(placeholder, subMessages[0]);
20128
+ }
20129
+ else {
20130
+ messageOp.params.set(placeholder, literal(`${ESCAPE}${I18N_ICU_MAPPING_PREFIX}${placeholder}${ESCAPE}`));
20131
+ messageOp.postprocessingParams.set(placeholder, literalArr(subMessages));
20132
+ messageOp.needsPostprocessing = true;
20133
+ }
20134
+ }
20135
+ }
19755
20136
  /**
19756
20137
  * Generate statements that define a given translation message.
19757
20138
  *
@@ -19774,7 +20155,8 @@ function collectMessage(job, fileBasedI18nSuffix, messages, messageOp) {
19774
20155
  * @param closureVar The variable for Closure `goog.getMsg` calls, e.g. `MSG_EXTERNAL_XXX`.
19775
20156
  * @param params Object mapping placeholder names to their values (e.g.
19776
20157
  * `{ "interpolation": "\uFFFD0\uFFFD" }`).
19777
- * @param transformFn Optional transformation function that will be applied to the translation (e.g.
20158
+ * @param transformFn Optional transformation function that will be applied to the translation
20159
+ * (e.g.
19778
20160
  * post-processing).
19779
20161
  * @returns An array of statements that defined a given translation.
19780
20162
  */
@@ -19819,28 +20201,13 @@ function i18nGenerateClosureVar(pool, messageId, fileBasedI18nSuffix, useExterna
19819
20201
  }
19820
20202
  return variable(name);
19821
20203
  }
19822
- /**
19823
- * Asserts that all of the message's placeholders have values.
19824
- */
19825
- function assertAllParamsResolved(op) {
19826
- for (let placeholder in op.message.placeholders) {
19827
- placeholder = placeholder.trimEnd();
19828
- if (!op.params.has(placeholder) && !op.postprocessingParams.has(placeholder)) {
19829
- throw Error(`Failed to resolve i18n placeholder: ${placeholder}`);
19830
- }
19831
- }
19832
- for (let placeholder in op.message.placeholderToMessage) {
19833
- placeholder = placeholder.trimEnd();
19834
- if (!op.params.has(placeholder) && !op.postprocessingParams.has(placeholder)) {
19835
- throw Error(`Failed to resolve i18n message placeholder: ${placeholder}`);
19836
- }
19837
- }
19838
- }
19839
20204
 
19840
20205
  /**
19841
20206
  * Removes text nodes within i18n blocks since they are already hardcoded into the i18n message.
20207
+ * Also, replaces interpolations on these text nodes with i18n expressions of the non-text portions,
20208
+ * which will be applied later.
19842
20209
  */
19843
- function extractI18nText(job) {
20210
+ function convertI18nText(job) {
19844
20211
  for (const unit of job.units) {
19845
20212
  // Remove all text nodes within i18n blocks, their content is already captured in the i18n
19846
20213
  // message.
@@ -19895,7 +20262,7 @@ function extractI18nText(job) {
19895
20262
  const expr = op.interpolation.expressions[i];
19896
20263
  // For now, this i18nExpression depends on the slot context of the enclosing i18n block.
19897
20264
  // Later, we will modify this, and advance to a different point.
19898
- ops.push(createI18nExpressionOp(contextId, i18nOp.xref, i18nOp.handle, expr, op.i18nPlaceholders[i], resolutionTime, expr.sourceSpan ?? op.sourceSpan));
20265
+ ops.push(createI18nExpressionOp(contextId, i18nOp.xref, i18nOp.xref, i18nOp.handle, expr, op.interpolation.i18nPlaceholders[i], resolutionTime, I18nExpressionFor.I18nText, '', expr.sourceSpan ?? op.sourceSpan));
19899
20266
  }
19900
20267
  OpList.replaceWithMany(op, ops);
19901
20268
  break;
@@ -19938,57 +20305,6 @@ function serializeLocalRefs(refs) {
19938
20305
  return literalArr(constRefs);
19939
20306
  }
19940
20307
 
19941
- /**
19942
- * Merge i18n contexts for child i18n blocks into their ancestor root contexts.
19943
- */
19944
- function mergeI18nContexts(job) {
19945
- // Record all of the i18n and extracted message ops for use later.
19946
- const i18nOps = new Map();
19947
- const i18nContexts = new Map();
19948
- for (const unit of job.units) {
19949
- for (const op of unit.create) {
19950
- switch (op.kind) {
19951
- case OpKind.I18nStart:
19952
- if (!op.context) {
19953
- throw Error('I18n op should have its context set.');
19954
- }
19955
- i18nOps.set(op.xref, op);
19956
- break;
19957
- case OpKind.I18nContext:
19958
- i18nContexts.set(op.xref, op);
19959
- break;
19960
- }
19961
- }
19962
- }
19963
- // For each non-root i18n op, merge its context into the root i18n op's context.
19964
- for (const childI18nOp of i18nOps.values()) {
19965
- if (childI18nOp.xref !== childI18nOp.root) {
19966
- const childContext = i18nContexts.get(childI18nOp.context);
19967
- const rootI18nOp = i18nOps.get(childI18nOp.root);
19968
- const rootContext = i18nContexts.get(rootI18nOp.context);
19969
- mergeParams(rootContext.params, childContext.params);
19970
- mergeParams(rootContext.postprocessingParams, childContext.postprocessingParams);
19971
- }
19972
- }
19973
- }
19974
- /**
19975
- * Merges the params in the `from` map to into the `to` map.
19976
- */
19977
- function mergeParams(to, from) {
19978
- for (const [placeholder, fromValues] of from) {
19979
- const toValues = to.get(placeholder) || [];
19980
- // TODO(mmalerba): Child element close tag params should be prepended to maintain the same order
19981
- // as TemplateDefinitionBuilder. Can be cleaned up when compatibility is no longer required.
19982
- const flags = fromValues[0].flags;
19983
- if ((flags & I18nParamValueFlags.CloseTag) && !(flags & I18nParamValueFlags.OpenTag)) {
19984
- to.set(placeholder, [...fromValues, ...toValues]);
19985
- }
19986
- else {
19987
- to.set(placeholder, [...toValues, ...fromValues]);
19988
- }
19989
- }
19990
- }
19991
-
19992
20308
  /**
19993
20309
  * Change namespaces between HTML, SVG and MathML, depending on the next element.
19994
20310
  */
@@ -20484,14 +20800,14 @@ function parseExtractedStyles(job) {
20484
20800
  if (op.name === 'style') {
20485
20801
  const parsedStyles = parse(op.expression.value);
20486
20802
  for (let i = 0; i < parsedStyles.length - 1; i += 2) {
20487
- OpList.insertBefore(createExtractedAttributeOp(op.target, BindingKind.StyleProperty, parsedStyles[i], literal(parsedStyles[i + 1])), op);
20803
+ OpList.insertBefore(createExtractedAttributeOp(op.target, BindingKind.StyleProperty, parsedStyles[i], literal(parsedStyles[i + 1]), null), op);
20488
20804
  }
20489
20805
  OpList.remove(op);
20490
20806
  }
20491
20807
  else if (op.name === 'class') {
20492
20808
  const parsedClasses = op.expression.value.trim().split(/\s+/g);
20493
20809
  for (const parsedClass of parsedClasses) {
20494
- OpList.insertBefore(createExtractedAttributeOp(op.target, BindingKind.ClassName, parsedClass, null), op);
20810
+ OpList.insertBefore(createExtractedAttributeOp(op.target, BindingKind.ClassName, parsedClass, null, null), op);
20495
20811
  }
20496
20812
  OpList.remove(op);
20497
20813
  }
@@ -20507,18 +20823,30 @@ function parseExtractedStyles(job) {
20507
20823
  function removeContentSelectors(job) {
20508
20824
  for (const unit of job.units) {
20509
20825
  const elements = createOpXrefMap(unit);
20510
- for (const op of unit.update) {
20826
+ for (const op of unit.ops()) {
20511
20827
  switch (op.kind) {
20512
20828
  case OpKind.Binding:
20513
20829
  const target = lookupInXrefMap(elements, op.target);
20514
- if (op.name.toLowerCase() === 'select' && target.kind === OpKind.Projection) {
20830
+ if (isSelectAttribute(op.name) && target.kind === OpKind.Projection) {
20515
20831
  OpList.remove(op);
20516
20832
  }
20517
20833
  break;
20834
+ case OpKind.Projection:
20835
+ // op.attributes is an array of [attr1-name, attr1-value, attr2-name, attr2-value, ...],
20836
+ // find the "select" attribute and remove its name and corresponding value.
20837
+ for (let i = op.attributes.length - 2; i >= 0; i -= 2) {
20838
+ if (isSelectAttribute(op.attributes[i])) {
20839
+ op.attributes.splice(i, 2);
20840
+ }
20841
+ }
20842
+ break;
20518
20843
  }
20519
20844
  }
20520
20845
  }
20521
20846
  }
20847
+ function isSelectAttribute(name) {
20848
+ return name.toLowerCase() === 'select';
20849
+ }
20522
20850
  /**
20523
20851
  * Looks up an element in the given map by xref ID.
20524
20852
  */
@@ -20638,6 +20966,10 @@ function propagateI18nBlocksToTemplates(unit, subTemplateIndex) {
20638
20966
  i18nBlock = op;
20639
20967
  break;
20640
20968
  case OpKind.I18nEnd:
20969
+ // When we exit a root-level i18n block, reset the sub-template index counter.
20970
+ if (i18nBlock.subTemplateIndex === null) {
20971
+ subTemplateIndex = 0;
20972
+ }
20641
20973
  i18nBlock = null;
20642
20974
  break;
20643
20975
  case OpKind.Template:
@@ -20652,9 +20984,10 @@ function propagateI18nBlocksToTemplates(unit, subTemplateIndex) {
20652
20984
  wrapTemplateWithI18n(templateView, i18nBlock);
20653
20985
  }
20654
20986
  // Continue traversing inside the template's view.
20655
- propagateI18nBlocksToTemplates(templateView, subTemplateIndex);
20987
+ subTemplateIndex = propagateI18nBlocksToTemplates(templateView, subTemplateIndex);
20656
20988
  }
20657
20989
  }
20990
+ return subTemplateIndex;
20658
20991
  }
20659
20992
  /**
20660
20993
  * Wraps a template view with i18n start and end ops.
@@ -20978,6 +21311,10 @@ function i18n(slot, constIndex, subTemplateIndex) {
20978
21311
  function i18nEnd() {
20979
21312
  return call(Identifiers.i18nEnd, [], null);
20980
21313
  }
21314
+ function i18nAttributes(slot, i18nAttributesConfig) {
21315
+ const args = [literal(slot), literal(i18nAttributesConfig)];
21316
+ return call(Identifiers.i18nAttributes, args, null);
21317
+ }
20981
21318
  function property(name, expression, sanitizer, sourceSpan) {
20982
21319
  const args = [literal(name), expression];
20983
21320
  if (sanitizer !== null) {
@@ -21364,6 +21701,12 @@ function reifyCreateOperations(unit, ops) {
21364
21701
  case OpKind.I18n:
21365
21702
  OpList.replace(op, i18n(op.handle.slot, op.messageIndex, op.subTemplateIndex));
21366
21703
  break;
21704
+ case OpKind.I18nAttributes:
21705
+ if (op.i18nAttributesConfig === null) {
21706
+ throw new Error(`AssertionError: i18nAttributesConfig was not set`);
21707
+ }
21708
+ OpList.replace(op, i18nAttributes(op.handle.slot, op.i18nAttributesConfig));
21709
+ break;
21367
21710
  case OpKind.Template:
21368
21711
  if (!(unit instanceof ViewCompilationUnit)) {
21369
21712
  throw new Error(`AssertionError: must be compiling a component`);
@@ -21705,6 +22048,31 @@ function removeI18nContexts(job) {
21705
22048
  }
21706
22049
  }
21707
22050
 
22051
+ /**
22052
+ * i18nAttributes ops will be generated for each i18n attribute. However, not all i18n attribues
22053
+ * will contain dynamic content, and so some of these i18nAttributes ops may be unnecessary.
22054
+ */
22055
+ function removeUnusedI18nAttributesOps(job) {
22056
+ for (const unit of job.units) {
22057
+ const ownersWithI18nExpressions = new Set();
22058
+ for (const op of unit.update) {
22059
+ switch (op.kind) {
22060
+ case OpKind.I18nExpression:
22061
+ ownersWithI18nExpressions.add(op.i18nOwner);
22062
+ }
22063
+ }
22064
+ for (const op of unit.create) {
22065
+ switch (op.kind) {
22066
+ case OpKind.I18nAttributes:
22067
+ if (ownersWithI18nExpressions.has(op.xref)) {
22068
+ continue;
22069
+ }
22070
+ OpList.remove(op);
22071
+ }
22072
+ }
22073
+ }
22074
+ }
22075
+
21708
22076
  /**
21709
22077
  * Inside the body of a repeater, certain context variables (such as `$first`) are ambiently
21710
22078
  * available. This phase finds those variable usages, and replaces them with the appropriate
@@ -21834,66 +22202,175 @@ function resolveI18nElementPlaceholders(job) {
21834
22202
  }
21835
22203
  }
21836
22204
  }
21837
- for (const unit of job.units) {
21838
- // Track the current i18n op and corresponding i18n context op as we step through the creation
21839
- // IR.
21840
- let currentOps = null;
21841
- for (const op of unit.create) {
21842
- switch (op.kind) {
21843
- case OpKind.I18nStart:
21844
- if (!op.context) {
21845
- throw Error('Could not find i18n context for i18n op');
22205
+ resolvePlaceholdersForView(job, job.root, i18nContexts, elements);
22206
+ }
22207
+ /**
22208
+ * Recursively resolves element and template tag placeholders in the given view.
22209
+ */
22210
+ function resolvePlaceholdersForView(job, unit, i18nContexts, elements, pendingStructuralDirective) {
22211
+ // Track the current i18n op and corresponding i18n context op as we step through the creation
22212
+ // IR.
22213
+ let currentOps = null;
22214
+ let pendingStructuralDirectiveCloses = new Map();
22215
+ for (const op of unit.create) {
22216
+ switch (op.kind) {
22217
+ case OpKind.I18nStart:
22218
+ if (!op.context) {
22219
+ throw Error('Could not find i18n context for i18n op');
22220
+ }
22221
+ currentOps = { i18nBlock: op, i18nContext: i18nContexts.get(op.context) };
22222
+ break;
22223
+ case OpKind.I18nEnd:
22224
+ currentOps = null;
22225
+ break;
22226
+ case OpKind.ElementStart:
22227
+ // For elements with i18n placeholders, record its slot value in the params map under the
22228
+ // corresponding tag start placeholder.
22229
+ if (op.i18nPlaceholder !== undefined) {
22230
+ if (currentOps === null) {
22231
+ throw Error('i18n tag placeholder should only occur inside an i18n block');
21846
22232
  }
21847
- currentOps = { i18nBlock: op, i18nContext: i18nContexts.get(op.context) };
21848
- break;
21849
- case OpKind.I18nEnd:
21850
- currentOps = null;
21851
- break;
21852
- case OpKind.ElementStart:
21853
- // For elements with i18n placeholders, record its slot value in the params map under the
21854
- // corresponding tag start placeholder.
21855
- if (op.i18nPlaceholder !== undefined) {
21856
- if (currentOps === null) {
21857
- throw Error('i18n tag placeholder should only occur inside an i18n block');
21858
- }
21859
- const { startName, closeName } = op.i18nPlaceholder;
21860
- let flags = I18nParamValueFlags.ElementTag | I18nParamValueFlags.OpenTag;
21861
- // For self-closing tags, there is no close tag placeholder. Instead, the start tag
21862
- // placeholder accounts for the start and close of the element.
21863
- if (closeName === '') {
21864
- flags |= I18nParamValueFlags.CloseTag;
21865
- }
21866
- addParam(currentOps.i18nContext.params, startName, op.handle.slot, currentOps.i18nBlock.subTemplateIndex, flags);
22233
+ recordElementStart(op, currentOps.i18nContext, currentOps.i18nBlock, pendingStructuralDirective);
22234
+ // If there is a separate close tag placeholder for this element, save the pending
22235
+ // structural directive so we can pass it to the closing tag as well.
22236
+ if (pendingStructuralDirective && op.i18nPlaceholder.closeName) {
22237
+ pendingStructuralDirectiveCloses.set(op.xref, pendingStructuralDirective);
21867
22238
  }
21868
- break;
21869
- case OpKind.ElementEnd:
21870
- // For elements with i18n placeholders, record its slot value in the params map under the
21871
- // corresponding tag close placeholder.
21872
- const startOp = elements.get(op.xref);
21873
- if (startOp && startOp.i18nPlaceholder !== undefined) {
21874
- if (currentOps === null) {
21875
- throw Error('i18n tag placeholder should only occur inside an i18n block');
21876
- }
21877
- const { closeName } = startOp.i18nPlaceholder;
21878
- // Self-closing tags don't have a closing tag placeholder.
21879
- if (closeName !== '') {
21880
- addParam(currentOps.i18nContext.params, closeName, startOp.handle.slot, currentOps.i18nBlock.subTemplateIndex, I18nParamValueFlags.ElementTag | I18nParamValueFlags.CloseTag);
21881
- }
22239
+ // Clear out the pending structural directive now that its been accounted for.
22240
+ pendingStructuralDirective = undefined;
22241
+ }
22242
+ break;
22243
+ case OpKind.ElementEnd:
22244
+ // For elements with i18n placeholders, record its slot value in the params map under the
22245
+ // corresponding tag close placeholder.
22246
+ const startOp = elements.get(op.xref);
22247
+ if (startOp && startOp.i18nPlaceholder !== undefined) {
22248
+ if (currentOps === null) {
22249
+ throw Error('AssertionError: i18n tag placeholder should only occur inside an i18n block');
21882
22250
  }
21883
- break;
21884
- case OpKind.Template:
21885
- // For templates with i18n placeholders, record its slot value in the params map under the
21886
- // corresponding template start and close placeholders.
21887
- if (op.i18nPlaceholder !== undefined) {
21888
- if (currentOps === null) {
21889
- throw Error('i18n tag placeholder should only occur inside an i18n block');
21890
- }
21891
- const subTemplateIndex = getSubTemplateIndexForTemplateTag(job, currentOps.i18nBlock, op);
21892
- addParam(currentOps.i18nContext.params, op.i18nPlaceholder.startName, op.handle.slot, subTemplateIndex, I18nParamValueFlags.TemplateTag);
21893
- addParam(currentOps.i18nContext.params, op.i18nPlaceholder.closeName, op.handle.slot, subTemplateIndex, I18nParamValueFlags.TemplateTag | I18nParamValueFlags.CloseTag);
22251
+ recordElementClose(startOp, currentOps.i18nContext, currentOps.i18nBlock, pendingStructuralDirectiveCloses.get(op.xref));
22252
+ // Clear out the pending structural directive close that was accounted for.
22253
+ pendingStructuralDirectiveCloses.delete(op.xref);
22254
+ }
22255
+ break;
22256
+ case OpKind.Projection:
22257
+ // For content projections with i18n placeholders, record its slot value in the params map
22258
+ // under the corresponding tag start and close placeholders.
22259
+ if (op.i18nPlaceholder !== undefined) {
22260
+ if (currentOps === null) {
22261
+ throw Error('i18n tag placeholder should only occur inside an i18n block');
21894
22262
  }
21895
- break;
21896
- }
22263
+ recordElementStart(op, currentOps.i18nContext, currentOps.i18nBlock, pendingStructuralDirective);
22264
+ recordElementClose(op, currentOps.i18nContext, currentOps.i18nBlock, pendingStructuralDirective);
22265
+ // Clear out the pending structural directive now that its been accounted for.
22266
+ pendingStructuralDirective = undefined;
22267
+ }
22268
+ break;
22269
+ case OpKind.Template:
22270
+ if (op.i18nPlaceholder === undefined) {
22271
+ // If there is no i18n placeholder, just recurse into the view in case it contains i18n
22272
+ // blocks.
22273
+ resolvePlaceholdersForView(job, job.views.get(op.xref), i18nContexts, elements);
22274
+ }
22275
+ else {
22276
+ if (currentOps === null) {
22277
+ throw Error('i18n tag placeholder should only occur inside an i18n block');
22278
+ }
22279
+ if (op.templateKind === TemplateKind.Structural) {
22280
+ // If this is a structural directive template, don't record anything yet. Instead pass
22281
+ // the current template as a pending structural directive to be recorded when we find
22282
+ // the element, content, or template it belongs to. This allows us to create combined
22283
+ // values that represent, e.g. the start of a template and element at the same time.
22284
+ resolvePlaceholdersForView(job, job.views.get(op.xref), i18nContexts, elements, op);
22285
+ }
22286
+ else {
22287
+ // If this is some other kind of template, we can record its start, recurse into its
22288
+ // view, and then record its end.
22289
+ recordTemplateStart(job, op, currentOps.i18nContext, currentOps.i18nBlock, pendingStructuralDirective);
22290
+ resolvePlaceholdersForView(job, job.views.get(op.xref), i18nContexts, elements);
22291
+ recordTemplateClose(job, op, currentOps.i18nContext, currentOps.i18nBlock, pendingStructuralDirective);
22292
+ pendingStructuralDirective = undefined;
22293
+ }
22294
+ }
22295
+ break;
22296
+ }
22297
+ }
22298
+ }
22299
+ /**
22300
+ * Records an i18n param value for the start of an element.
22301
+ */
22302
+ function recordElementStart(op, i18nContext, i18nBlock, structuralDirective) {
22303
+ const { startName, closeName } = op.i18nPlaceholder;
22304
+ let flags = I18nParamValueFlags.ElementTag | I18nParamValueFlags.OpenTag;
22305
+ let value = op.handle.slot;
22306
+ // If the element is associated with a structural directive, start it as well.
22307
+ if (structuralDirective !== undefined) {
22308
+ flags |= I18nParamValueFlags.TemplateTag;
22309
+ value = { element: value, template: structuralDirective.handle.slot };
22310
+ }
22311
+ // For self-closing tags, there is no close tag placeholder. Instead, the start tag
22312
+ // placeholder accounts for the start and close of the element.
22313
+ if (!closeName) {
22314
+ flags |= I18nParamValueFlags.CloseTag;
22315
+ }
22316
+ addParam(i18nContext.params, startName, value, i18nBlock.subTemplateIndex, flags);
22317
+ }
22318
+ /**
22319
+ * Records an i18n param value for the closing of an element.
22320
+ */
22321
+ function recordElementClose(op, i18nContext, i18nBlock, structuralDirective) {
22322
+ const { closeName } = op.i18nPlaceholder;
22323
+ // Self-closing tags don't have a closing tag placeholder, instead the element closing is
22324
+ // recorded via an additional flag on the element start value.
22325
+ if (closeName) {
22326
+ let flags = I18nParamValueFlags.ElementTag | I18nParamValueFlags.CloseTag;
22327
+ let value = op.handle.slot;
22328
+ // If the element is associated with a structural directive, close it as well.
22329
+ if (structuralDirective !== undefined) {
22330
+ flags |= I18nParamValueFlags.TemplateTag;
22331
+ value = { element: value, template: structuralDirective.handle.slot };
22332
+ }
22333
+ addParam(i18nContext.params, closeName, value, i18nBlock.subTemplateIndex, flags);
22334
+ }
22335
+ }
22336
+ /**
22337
+ * Records an i18n param value for the start of a template.
22338
+ */
22339
+ function recordTemplateStart(job, op, i18nContext, i18nBlock, structuralDirective) {
22340
+ let { startName, closeName } = op.i18nPlaceholder;
22341
+ let flags = I18nParamValueFlags.TemplateTag | I18nParamValueFlags.OpenTag;
22342
+ // For self-closing tags, there is no close tag placeholder. Instead, the start tag
22343
+ // placeholder accounts for the start and close of the element.
22344
+ if (!closeName) {
22345
+ flags |= I18nParamValueFlags.CloseTag;
22346
+ }
22347
+ // If the template is associated with a structural directive, record the structural directive's
22348
+ // start first. Since this template must be in the structural directive's view, we can just
22349
+ // directly use the current i18n block's sub-template index.
22350
+ if (structuralDirective !== undefined) {
22351
+ addParam(i18nContext.params, startName, structuralDirective.handle.slot, i18nBlock.subTemplateIndex, flags);
22352
+ }
22353
+ // Record the start of the template. For the sub-template index, pass the index for the template's
22354
+ // view, rather than the current i18n block's index.
22355
+ addParam(i18nContext.params, startName, op.handle.slot, getSubTemplateIndexForTemplateTag(job, i18nBlock, op), flags);
22356
+ }
22357
+ /**
22358
+ * Records an i18n param value for the closing of a template.
22359
+ */
22360
+ function recordTemplateClose(job, op, i18nContext, i18nBlock, structuralDirective) {
22361
+ const { startName, closeName } = op.i18nPlaceholder;
22362
+ const flags = I18nParamValueFlags.TemplateTag | I18nParamValueFlags.CloseTag;
22363
+ // Self-closing tags don't have a closing tag placeholder, instead the template's closing is
22364
+ // recorded via an additional flag on the template start value.
22365
+ if (closeName) {
22366
+ // Record the closing of the template. For the sub-template index, pass the index for the
22367
+ // template's view, rather than the current i18n block's index.
22368
+ addParam(i18nContext.params, closeName, op.handle.slot, getSubTemplateIndexForTemplateTag(job, i18nBlock, op), flags);
22369
+ // If the template is associated with a structural directive, record the structural directive's
22370
+ // closing after. Since this template must be in the structural directive's view, we can just
22371
+ // directly use the current i18n block's sub-template index.
22372
+ if (structuralDirective !== undefined) {
22373
+ addParam(i18nContext.params, closeName, structuralDirective.handle.slot, i18nBlock.subTemplateIndex, flags);
21897
22374
  }
21898
22375
  }
21899
22376
  }
@@ -21909,7 +22386,9 @@ function getSubTemplateIndexForTemplateTag(job, i18nOp, op) {
21909
22386
  }
21910
22387
  return i18nOp.subTemplateIndex;
21911
22388
  }
21912
- /** Add a param value to the given params map. */
22389
+ /**
22390
+ * Add a param value to the given params map.
22391
+ */
21913
22392
  function addParam(params, placeholder, value, subTemplateIndex, flags) {
21914
22393
  const values = params.get(placeholder) ?? [];
21915
22394
  values.push({ value, subTemplateIndex, flags });
@@ -21935,14 +22414,19 @@ function resolveI18nExpressionPlaceholders(job) {
21935
22414
  }
21936
22415
  }
21937
22416
  }
21938
- // Keep track of the next available expression index per i18n block.
22417
+ // Keep track of the next available expression index for each i18n message.
21939
22418
  const expressionIndices = new Map();
22419
+ // Keep track of a reference index for each expression.
22420
+ // We use different references for normal i18n expressio and attribute i18n expressions. This is
22421
+ // because child i18n blocks in templates don't get their own context, since they're rolled into
22422
+ // the translated message of the parent, but they may target a different slot.
22423
+ const referenceIndex = (op) => op.usage === I18nExpressionFor.I18nText ? op.i18nOwner : op.context;
21940
22424
  for (const unit of job.units) {
21941
22425
  for (const op of unit.update) {
21942
22426
  if (op.kind === OpKind.I18nExpression) {
21943
22427
  const i18nContext = i18nContexts.get(op.context);
21944
- const index = expressionIndices.get(i18nContext.i18nBlock) || 0;
21945
- const subTemplateIndex = subTemplateIndicies.get(i18nContext.i18nBlock);
22428
+ const index = expressionIndices.get(referenceIndex(op)) || 0;
22429
+ const subTemplateIndex = subTemplateIndicies.get(op.i18nOwner) ?? null;
21946
22430
  // Add the expression index in the appropriate params map.
21947
22431
  const params = op.resolutionTime === I18nParamResolutionTime.Creation ?
21948
22432
  i18nContext.params :
@@ -21954,7 +22438,7 @@ function resolveI18nExpressionPlaceholders(job) {
21954
22438
  flags: I18nParamValueFlags.ExpressionIndex
21955
22439
  });
21956
22440
  params.set(op.i18nPlaceholder, values);
21957
- expressionIndices.set(i18nContext.i18nBlock, index + 1);
22441
+ expressionIndices.set(referenceIndex(op), index + 1);
21958
22442
  }
21959
22443
  }
21960
22444
  }
@@ -21964,28 +22448,12 @@ function resolveI18nExpressionPlaceholders(job) {
21964
22448
  * Resolves placeholders for element tags inside of an ICU.
21965
22449
  */
21966
22450
  function resolveI18nIcuPlaceholders(job) {
21967
- const contextOps = new Map();
21968
22451
  for (const unit of job.units) {
21969
22452
  for (const op of unit.create) {
21970
- switch (op.kind) {
21971
- case OpKind.I18nContext:
21972
- contextOps.set(op.xref, op);
21973
- break;
21974
- }
21975
- }
21976
- }
21977
- for (const unit of job.units) {
21978
- for (const op of unit.create) {
21979
- switch (op.kind) {
21980
- case OpKind.IcuStart:
21981
- if (op.context === null) {
21982
- throw Error('Icu should have its i18n context set.');
21983
- }
21984
- const i18nContext = contextOps.get(op.context);
21985
- for (const node of op.message.nodes) {
21986
- node.visit(new ResolveIcuPlaceholdersVisitor(i18nContext.postprocessingParams));
21987
- }
21988
- break;
22453
+ if (op.kind === OpKind.I18nContext && op.contextKind === I18nContextKind.Icu) {
22454
+ for (const node of op.message.nodes) {
22455
+ node.visit(new ResolveIcuPlaceholdersVisitor(op.postprocessingParams));
22456
+ }
21989
22457
  }
21990
22458
  }
21991
22459
  }
@@ -23117,11 +23585,11 @@ const phases = [
23117
23585
  { kind: CompilationJobKind.Tmpl, fn: removeContentSelectors },
23118
23586
  { kind: CompilationJobKind.Host, fn: parseHostStyleProperties },
23119
23587
  { kind: CompilationJobKind.Tmpl, fn: emitNamespaceChanges },
23120
- { kind: CompilationJobKind.Both, fn: specializeStyleBindings },
23121
- { kind: CompilationJobKind.Both, fn: specializeBindings },
23122
23588
  { kind: CompilationJobKind.Tmpl, fn: propagateI18nBlocks },
23123
23589
  { kind: CompilationJobKind.Tmpl, fn: wrapI18nIcus },
23124
23590
  { kind: CompilationJobKind.Tmpl, fn: createI18nContexts },
23591
+ { kind: CompilationJobKind.Both, fn: specializeStyleBindings },
23592
+ { kind: CompilationJobKind.Both, fn: specializeBindings },
23125
23593
  { kind: CompilationJobKind.Both, fn: extractAttributes },
23126
23594
  { kind: CompilationJobKind.Both, fn: parseExtractedStyles },
23127
23595
  { kind: CompilationJobKind.Tmpl, fn: removeEmptyBindings },
@@ -23130,7 +23598,10 @@ const phases = [
23130
23598
  { kind: CompilationJobKind.Tmpl, fn: generateConditionalExpressions },
23131
23599
  { kind: CompilationJobKind.Tmpl, fn: createPipes },
23132
23600
  { kind: CompilationJobKind.Tmpl, fn: configureDeferInstructions },
23133
- { kind: CompilationJobKind.Tmpl, fn: extractI18nText },
23601
+ { kind: CompilationJobKind.Tmpl, fn: convertI18nText },
23602
+ { kind: CompilationJobKind.Tmpl, fn: convertI18nBindings },
23603
+ { kind: CompilationJobKind.Tmpl, fn: removeUnusedI18nAttributesOps },
23604
+ { kind: CompilationJobKind.Tmpl, fn: assignI18nSlotDependencies },
23134
23605
  { kind: CompilationJobKind.Tmpl, fn: applyI18nExpressions },
23135
23606
  { kind: CompilationJobKind.Tmpl, fn: createVariadicPipes },
23136
23607
  { kind: CompilationJobKind.Both, fn: generatePureLiteralStructures },
@@ -23155,13 +23626,11 @@ const phases = [
23155
23626
  { kind: CompilationJobKind.Tmpl, fn: resolveI18nElementPlaceholders },
23156
23627
  { kind: CompilationJobKind.Tmpl, fn: resolveI18nExpressionPlaceholders },
23157
23628
  { kind: CompilationJobKind.Tmpl, fn: resolveI18nIcuPlaceholders },
23158
- { kind: CompilationJobKind.Tmpl, fn: mergeI18nContexts },
23159
23629
  { kind: CompilationJobKind.Tmpl, fn: extractI18nMessages },
23160
23630
  { kind: CompilationJobKind.Tmpl, fn: generateTrackFns },
23161
23631
  { kind: CompilationJobKind.Tmpl, fn: collectI18nConsts },
23162
23632
  { kind: CompilationJobKind.Tmpl, fn: collectConstExpressions },
23163
23633
  { kind: CompilationJobKind.Both, fn: collectElementConsts },
23164
- { kind: CompilationJobKind.Tmpl, fn: assignI18nSlotDependencies },
23165
23634
  { kind: CompilationJobKind.Tmpl, fn: removeI18nContexts },
23166
23635
  { kind: CompilationJobKind.Both, fn: countVariables },
23167
23636
  { kind: CompilationJobKind.Tmpl, fn: generateAdvance },
@@ -23316,7 +23785,7 @@ function ingestHostProperty(job, property, isTextAttribute) {
23316
23785
  let expression;
23317
23786
  const ast = property.expression.ast;
23318
23787
  if (ast instanceof Interpolation$1) {
23319
- expression = new Interpolation(ast.strings, ast.expressions.map(expr => convertAst(expr, job, property.sourceSpan)));
23788
+ expression = new Interpolation(ast.strings, ast.expressions.map(expr => convertAst(expr, job, property.sourceSpan)), []);
23320
23789
  }
23321
23790
  else {
23322
23791
  expression = convertAst(ast, job, property.sourceSpan);
@@ -23331,10 +23800,11 @@ function ingestHostProperty(job, property, isTextAttribute) {
23331
23800
  bindingKind = BindingKind.Animation;
23332
23801
  }
23333
23802
  job.root.update.push(createBindingOp(job.root.xref, bindingKind, property.name, expression, null, SecurityContext
23334
- .NONE /* TODO: what should we pass as security context? Passing NONE for now. */, isTextAttribute, false, property.sourceSpan));
23803
+ .NONE /* TODO: what should we pass as security context? Passing NONE for now. */, isTextAttribute, false, /* TODO: How do Host bindings handle i18n attrs? */ null, property.sourceSpan));
23335
23804
  }
23336
23805
  function ingestHostAttribute(job, name, value) {
23337
23806
  const attrBinding = createBindingOp(job.root.xref, BindingKind.Attribute, name, value, null, SecurityContext.NONE, true, false,
23807
+ /* TODO */ null,
23338
23808
  /* TODO: host attribute source spans */ null);
23339
23809
  job.root.update.push(attrBinding);
23340
23810
  }
@@ -23432,10 +23902,11 @@ function ingestTemplate(unit, tmpl) {
23432
23902
  const functionNameSuffix = tagNameWithoutNamespace === null ?
23433
23903
  '' :
23434
23904
  prefixWithNamespace(tagNameWithoutNamespace, namespace);
23435
- const tplOp = createTemplateOp(childView.xref, tagNameWithoutNamespace, functionNameSuffix, namespace, i18nPlaceholder, tmpl.startSourceSpan);
23436
- unit.create.push(tplOp);
23437
- ingestBindings(unit, tplOp, tmpl);
23438
- ingestReferences(tplOp, tmpl);
23905
+ const templateKind = isPlainTemplate(tmpl) ? TemplateKind.NgTemplate : TemplateKind.Structural;
23906
+ const templateOp = createTemplateOp(childView.xref, templateKind, tagNameWithoutNamespace, functionNameSuffix, namespace, i18nPlaceholder, tmpl.startSourceSpan);
23907
+ unit.create.push(templateOp);
23908
+ ingestBindings(unit, templateOp, tmpl);
23909
+ ingestReferences(templateOp, tmpl);
23439
23910
  ingestNodes(childView, tmpl.children);
23440
23911
  for (const { name, value } of tmpl.variables) {
23441
23912
  childView.contextVariables.set(name, value !== '' ? value : '$implicit');
@@ -23443,7 +23914,7 @@ function ingestTemplate(unit, tmpl) {
23443
23914
  // If this is a plain template and there is an i18n message associated with it, insert i18n start
23444
23915
  // and end ops. For structural directive templates, the i18n ops will be added when ingesting the
23445
23916
  // element/template the directive is placed on.
23446
- if (isPlainTemplate(tmpl) && tmpl.i18n instanceof Message) {
23917
+ if (templateKind === TemplateKind.NgTemplate && tmpl.i18n instanceof Message) {
23447
23918
  const id = unit.job.allocateXrefId();
23448
23919
  OpList.insertAfter(createI18nStartOp(id, tmpl.i18n), childView.create.head);
23449
23920
  OpList.insertBefore(createI18nEndOp(id), childView.create.tail);
@@ -23453,9 +23924,13 @@ function ingestTemplate(unit, tmpl) {
23453
23924
  * Ingest a literal text node from the AST into the given `ViewCompilation`.
23454
23925
  */
23455
23926
  function ingestContent(unit, content) {
23456
- const op = createProjectionOp(unit.job.allocateXrefId(), content.selector, content.sourceSpan);
23927
+ if (content.i18n !== undefined && !(content.i18n instanceof TagPlaceholder)) {
23928
+ throw Error(`Unhandled i18n metadata type for element: ${content.i18n.constructor.name}`);
23929
+ }
23930
+ const attrs = content.attributes.flatMap(a => [a.name, a.value]);
23931
+ const op = createProjectionOp(unit.job.allocateXrefId(), content.selector, content.i18n, attrs, content.sourceSpan);
23457
23932
  for (const attr of content.attributes) {
23458
- ingestBinding(unit, op.xref, attr.name, literal(attr.value), 1 /* e.BindingType.Attribute */, null, SecurityContext.NONE, attr.sourceSpan, BindingFlags.TextValue);
23933
+ ingestBinding(unit, op.xref, attr.name, literal(attr.value), 1 /* e.BindingType.Attribute */, null, SecurityContext.NONE, attr.sourceSpan, BindingFlags.TextValue, attr.i18n);
23459
23934
  }
23460
23935
  unit.create.push(op);
23461
23936
  }
@@ -23480,6 +23955,7 @@ function ingestBoundText(unit, text, i18nPlaceholders) {
23480
23955
  throw Error(`Unhandled i18n metadata type for text interpolation: ${text.i18n?.constructor.name}`);
23481
23956
  }
23482
23957
  if (i18nPlaceholders === undefined) {
23958
+ // TODO: We probably can just use the placeholders field, instead of walking the AST.
23483
23959
  i18nPlaceholders = text.i18n instanceof Container ?
23484
23960
  text.i18n.children
23485
23961
  .filter((node) => node instanceof Placeholder)
@@ -23495,7 +23971,7 @@ function ingestBoundText(unit, text, i18nPlaceholders) {
23495
23971
  // interpolation. We copy that behavior in compatibility mode.
23496
23972
  // TODO: is it actually correct to generate these extra maps in modern mode?
23497
23973
  const baseSourceSpan = unit.job.compatibility ? null : text.sourceSpan;
23498
- unit.update.push(createInterpolateTextOp(textXref, new Interpolation(value.strings, value.expressions.map(expr => convertAst(expr, unit.job, baseSourceSpan))), i18nPlaceholders, text.sourceSpan));
23974
+ unit.update.push(createInterpolateTextOp(textXref, new Interpolation(value.strings, value.expressions.map(expr => convertAst(expr, unit.job, baseSourceSpan)), i18nPlaceholders), text.sourceSpan));
23499
23975
  }
23500
23976
  /**
23501
23977
  * Ingest an `@if` block into the given `ViewCompilation`.
@@ -23516,14 +23992,21 @@ function ingestIfBlock(unit, ifBlock) {
23516
23992
  if (ifCase.expressionAlias !== null) {
23517
23993
  cView.contextVariables.set(ifCase.expressionAlias.name, CTX_REF);
23518
23994
  }
23519
- const tmplOp = createTemplateOp(cView.xref, tagName, 'Conditional', Namespace.HTML, undefined /* TODO: figure out how i18n works with new control flow */, ifCase.sourceSpan);
23520
- unit.create.push(tmplOp);
23995
+ let ifCaseI18nMeta = undefined;
23996
+ if (ifCase.i18n !== undefined) {
23997
+ if (!(ifCase.i18n instanceof BlockPlaceholder)) {
23998
+ throw Error(`Unhandled i18n metadata type for if block: ${ifCase.i18n?.constructor.name}`);
23999
+ }
24000
+ ifCaseI18nMeta = ifCase.i18n;
24001
+ }
24002
+ const templateOp = createTemplateOp(cView.xref, TemplateKind.Block, tagName, 'Conditional', Namespace.HTML, ifCaseI18nMeta, ifCase.sourceSpan);
24003
+ unit.create.push(templateOp);
23521
24004
  if (firstXref === null) {
23522
24005
  firstXref = cView.xref;
23523
- firstSlotHandle = tmplOp.handle;
24006
+ firstSlotHandle = templateOp.handle;
23524
24007
  }
23525
24008
  const caseExpr = ifCase.expression ? convertAst(ifCase.expression, unit.job, null) : null;
23526
- const conditionalCaseExpr = new ConditionalCaseExpr(caseExpr, tmplOp.xref, tmplOp.handle, ifCase.expressionAlias);
24009
+ const conditionalCaseExpr = new ConditionalCaseExpr(caseExpr, templateOp.xref, templateOp.handle, ifCase.expressionAlias);
23527
24010
  conditions.push(conditionalCaseExpr);
23528
24011
  ingestNodes(cView, ifCase.children);
23529
24012
  }
@@ -23539,16 +24022,23 @@ function ingestSwitchBlock(unit, switchBlock) {
23539
24022
  let conditions = [];
23540
24023
  for (const switchCase of switchBlock.cases) {
23541
24024
  const cView = unit.job.allocateView(unit.xref);
23542
- const tmplOp = createTemplateOp(cView.xref, null, 'Case', Namespace.HTML, undefined /* TODO: figure out how i18n works with new control flow */, switchCase.sourceSpan);
23543
- unit.create.push(tmplOp);
24025
+ let switchCaseI18nMeta = undefined;
24026
+ if (switchCase.i18n !== undefined) {
24027
+ if (!(switchCase.i18n instanceof BlockPlaceholder)) {
24028
+ throw Error(`Unhandled i18n metadata type for switch block: ${switchCase.i18n?.constructor.name}`);
24029
+ }
24030
+ switchCaseI18nMeta = switchCase.i18n;
24031
+ }
24032
+ const templateOp = createTemplateOp(cView.xref, TemplateKind.Block, null, 'Case', Namespace.HTML, switchCaseI18nMeta, switchCase.sourceSpan);
24033
+ unit.create.push(templateOp);
23544
24034
  if (firstXref === null) {
23545
24035
  firstXref = cView.xref;
23546
- firstSlotHandle = tmplOp.handle;
24036
+ firstSlotHandle = templateOp.handle;
23547
24037
  }
23548
24038
  const caseExpr = switchCase.expression ?
23549
24039
  convertAst(switchCase.expression, unit.job, switchBlock.startSourceSpan) :
23550
24040
  null;
23551
- const conditionalCaseExpr = new ConditionalCaseExpr(caseExpr, tmplOp.xref, tmplOp.handle);
24041
+ const conditionalCaseExpr = new ConditionalCaseExpr(caseExpr, templateOp.xref, templateOp.handle);
23552
24042
  conditions.push(conditionalCaseExpr);
23553
24043
  ingestNodes(cView, switchCase.children);
23554
24044
  }
@@ -23561,7 +24051,7 @@ function ingestDeferView(unit, suffix, children, sourceSpan) {
23561
24051
  }
23562
24052
  const secondaryView = unit.job.allocateView(unit.xref);
23563
24053
  ingestNodes(secondaryView, children);
23564
- const templateOp = createTemplateOp(secondaryView.xref, null, `Defer${suffix}`, Namespace.HTML, undefined, sourceSpan);
24054
+ const templateOp = createTemplateOp(secondaryView.xref, TemplateKind.Block, null, `Defer${suffix}`, Namespace.HTML, undefined, sourceSpan);
23565
24055
  unit.create.push(templateOp);
23566
24056
  return templateOp;
23567
24057
  }
@@ -23656,12 +24146,7 @@ function ingestIcu(unit, icu) {
23656
24146
  const xref = unit.job.allocateXrefId();
23657
24147
  const icuNode = icu.i18n.nodes[0];
23658
24148
  unit.create.push(createIcuStartOp(xref, icu.i18n, icuFromI18nMessage(icu.i18n).name, null));
23659
- const expressionPlaceholder = icuNode.expressionPlaceholder?.trimEnd();
23660
- if (expressionPlaceholder === undefined || icu.vars[expressionPlaceholder] === undefined) {
23661
- throw Error('ICU should have a text binding');
23662
- }
23663
- ingestBoundText(unit, icu.vars[expressionPlaceholder], [expressionPlaceholder]);
23664
- for (const [placeholder, text] of Object.entries(icu.placeholders)) {
24149
+ for (const [placeholder, text] of Object.entries({ ...icu.vars, ...icu.placeholders })) {
23665
24150
  if (text instanceof BoundText) {
23666
24151
  ingestBoundText(unit, text, [placeholder]);
23667
24152
  }
@@ -23757,6 +24242,16 @@ function convertAst(ast, job, baseSourceSpan) {
23757
24242
  else if (ast instanceof LiteralPrimitive) {
23758
24243
  return literal(ast.value, undefined, convertSourceSpan(ast.span, baseSourceSpan));
23759
24244
  }
24245
+ else if (ast instanceof Unary) {
24246
+ switch (ast.operator) {
24247
+ case '+':
24248
+ return new UnaryOperatorExpr(UnaryOperator.Plus, convertAst(ast.expr, job, baseSourceSpan), undefined, convertSourceSpan(ast.span, baseSourceSpan));
24249
+ case '-':
24250
+ return new UnaryOperatorExpr(UnaryOperator.Minus, convertAst(ast.expr, job, baseSourceSpan), undefined, convertSourceSpan(ast.span, baseSourceSpan));
24251
+ default:
24252
+ throw new Error(`AssertionError: unknown unary operator ${ast.operator}`);
24253
+ }
24254
+ }
23760
24255
  else if (ast instanceof Binary) {
23761
24256
  const operator = BINARY_OPERATORS.get(ast.operation);
23762
24257
  if (operator === undefined) {
@@ -23815,6 +24310,9 @@ function convertAst(ast, job, baseSourceSpan) {
23815
24310
  else if (ast instanceof EmptyExpr$1) {
23816
24311
  return new EmptyExpr(convertSourceSpan(ast.span, baseSourceSpan));
23817
24312
  }
24313
+ else if (ast instanceof PrefixNot) {
24314
+ return not(convertAst(ast.expression, job, baseSourceSpan), convertSourceSpan(ast.span, baseSourceSpan));
24315
+ }
23818
24316
  else {
23819
24317
  throw new Error(`Unhandled expression type "${ast.constructor.name}" in file "${baseSourceSpan?.start.file.url}"`);
23820
24318
  }
@@ -23845,6 +24343,7 @@ function isPlainTemplate(tmpl) {
23845
24343
  */
23846
24344
  function ingestBindings(unit, op, element) {
23847
24345
  let flags = BindingFlags.None;
24346
+ let hasI18nAttributes = false;
23848
24347
  if (element instanceof Template) {
23849
24348
  flags |= BindingFlags.OnNgTemplateElement;
23850
24349
  if (element instanceof Template && isPlainTemplate(element)) {
@@ -23853,10 +24352,12 @@ function ingestBindings(unit, op, element) {
23853
24352
  const templateAttrFlags = flags | BindingFlags.BindingTargetsTemplate | BindingFlags.IsStructuralTemplateAttribute;
23854
24353
  for (const attr of element.templateAttrs) {
23855
24354
  if (attr instanceof TextAttribute) {
23856
- ingestBinding(unit, op.xref, attr.name, literal(attr.value), 1 /* e.BindingType.Attribute */, null, SecurityContext.NONE, attr.sourceSpan, templateAttrFlags | BindingFlags.TextValue);
24355
+ ingestBinding(unit, op.xref, attr.name, literal(attr.value), 1 /* e.BindingType.Attribute */, null, SecurityContext.NONE, attr.sourceSpan, templateAttrFlags | BindingFlags.TextValue, attr.i18n);
24356
+ hasI18nAttributes ||= attr.i18n !== undefined;
23857
24357
  }
23858
24358
  else {
23859
- ingestBinding(unit, op.xref, attr.name, attr.value, attr.type, attr.unit, attr.securityContext, attr.sourceSpan, templateAttrFlags);
24359
+ ingestBinding(unit, op.xref, attr.name, attr.value, attr.type, attr.unit, attr.securityContext, attr.sourceSpan, templateAttrFlags, attr.i18n);
24360
+ hasI18nAttributes ||= attr.i18n !== undefined;
23860
24361
  }
23861
24362
  }
23862
24363
  }
@@ -23864,10 +24365,12 @@ function ingestBindings(unit, op, element) {
23864
24365
  // This is only attribute TextLiteral bindings, such as `attr.foo="bar"`. This can never be
23865
24366
  // `[attr.foo]="bar"` or `attr.foo="{{bar}}"`, both of which will be handled as inputs with
23866
24367
  // `BindingType.Attribute`.
23867
- ingestBinding(unit, op.xref, attr.name, literal(attr.value), 1 /* e.BindingType.Attribute */, null, SecurityContext.NONE, attr.sourceSpan, flags | BindingFlags.TextValue);
24368
+ ingestBinding(unit, op.xref, attr.name, literal(attr.value), 1 /* e.BindingType.Attribute */, null, SecurityContext.NONE, attr.sourceSpan, flags | BindingFlags.TextValue, attr.i18n);
24369
+ hasI18nAttributes ||= attr.i18n !== undefined;
23868
24370
  }
23869
24371
  for (const input of element.inputs) {
23870
- ingestBinding(unit, op.xref, input.name, input.value, input.type, input.unit, input.securityContext, input.sourceSpan, flags);
24372
+ ingestBinding(unit, op.xref, input.name, input.value, input.type, input.unit, input.securityContext, input.sourceSpan, flags, input.i18n);
24373
+ hasI18nAttributes ||= input.i18n !== undefined;
23871
24374
  }
23872
24375
  for (const output of element.outputs) {
23873
24376
  let listenerOp;
@@ -23877,7 +24380,7 @@ function ingestBindings(unit, op, element) {
23877
24380
  }
23878
24381
  }
23879
24382
  if (element instanceof Template && !isPlainTemplate(element)) {
23880
- unit.create.push(createExtractedAttributeOp(op.xref, BindingKind.Property, output.name, null));
24383
+ unit.create.push(createExtractedAttributeOp(op.xref, BindingKind.Property, output.name, null, null));
23881
24384
  continue;
23882
24385
  }
23883
24386
  listenerOp = createListenerOp(op.xref, op.handle, output.name, op.tag, output.phase, false, output.sourceSpan);
@@ -23906,6 +24409,10 @@ function ingestBindings(unit, op, element) {
23906
24409
  listenerOp.handlerOps.push(createStatementOp(new ReturnStatement(returnExpr, returnExpr.sourceSpan)));
23907
24410
  unit.create.push(listenerOp);
23908
24411
  }
24412
+ // TODO: Perhaps we could do this in a phase? (It likely wouldn't change the slot indices.)
24413
+ if (hasI18nAttributes) {
24414
+ unit.create.push(createI18nAttributesOp(unit.job.allocateXrefId(), new SlotHandle(), op.xref));
24415
+ }
23909
24416
  }
23910
24417
  const BINDING_KINDS = new Map([
23911
24418
  [0 /* e.BindingType.Property */, BindingKind.Property],
@@ -23934,22 +24441,34 @@ var BindingFlags;
23934
24441
  */
23935
24442
  BindingFlags[BindingFlags["OnNgTemplateElement"] = 8] = "OnNgTemplateElement";
23936
24443
  })(BindingFlags || (BindingFlags = {}));
23937
- function ingestBinding(view, xref, name, value, type, unit, securityContext, sourceSpan, flags) {
24444
+ function ingestBinding(view, xref, name, value, type, unit, securityContext, sourceSpan, flags, i18nMeta) {
23938
24445
  if (value instanceof ASTWithSource) {
23939
24446
  value = value.ast;
23940
24447
  }
24448
+ let i18nContext = null;
24449
+ if (i18nMeta !== undefined) {
24450
+ if (!(i18nMeta instanceof Message)) {
24451
+ throw Error(`Unhandled i18n metadata type for binding: ${i18nMeta.constructor.name}`);
24452
+ }
24453
+ i18nContext = view.job.allocateXrefId();
24454
+ view.create.push(createI18nContextOp(I18nContextKind.Attr, i18nContext, null, i18nMeta, null));
24455
+ }
23941
24456
  if (flags & BindingFlags.OnNgTemplateElement && !(flags & BindingFlags.BindingTargetsTemplate) &&
23942
24457
  type === 0 /* e.BindingType.Property */) {
23943
24458
  // This binding only exists for later const extraction, and is not an actual binding to be
23944
24459
  // created.
23945
- view.create.push(createExtractedAttributeOp(xref, BindingKind.Property, name, null));
24460
+ view.create.push(createExtractedAttributeOp(xref, BindingKind.Property, name, null, i18nContext));
23946
24461
  return;
23947
24462
  }
23948
24463
  let expression;
23949
24464
  // TODO: We could easily generate source maps for subexpressions in these cases, but
23950
24465
  // TemplateDefinitionBuilder does not. Should we do so?
23951
24466
  if (value instanceof Interpolation$1) {
23952
- expression = new Interpolation(value.strings, value.expressions.map(expr => convertAst(expr, view.job, null)));
24467
+ let i18nPlaceholders = [];
24468
+ if (i18nMeta !== undefined) {
24469
+ i18nPlaceholders = Object.keys(i18nMeta.placeholders);
24470
+ }
24471
+ expression = new Interpolation(value.strings, value.expressions.map(expr => convertAst(expr, view.job, null)), i18nPlaceholders);
23953
24472
  }
23954
24473
  else if (value instanceof AST) {
23955
24474
  expression = convertAst(value, view.job, null);
@@ -23958,7 +24477,7 @@ function ingestBinding(view, xref, name, value, type, unit, securityContext, sou
23958
24477
  expression = value;
23959
24478
  }
23960
24479
  const kind = BINDING_KINDS.get(type);
23961
- view.update.push(createBindingOp(xref, kind, name, expression, unit, securityContext, !!(flags & BindingFlags.TextValue), !!(flags & BindingFlags.IsStructuralTemplateAttribute), sourceSpan));
24480
+ view.update.push(createBindingOp(xref, kind, name, expression, unit, securityContext, !!(flags & BindingFlags.TextValue), !!(flags & BindingFlags.IsStructuralTemplateAttribute), i18nContext, sourceSpan));
23962
24481
  }
23963
24482
  /**
23964
24483
  * Process all of the local references on an element-like structure in the template AST and
@@ -24046,7 +24565,7 @@ function ingestControlFlowInsertionPoint(unit, xref, node) {
24046
24565
  // and they can be used in directive matching (in the case of `Template.templateAttrs`).
24047
24566
  if (root !== null) {
24048
24567
  for (const attr of root.attributes) {
24049
- ingestBinding(unit, xref, attr.name, literal(attr.value), 1 /* e.BindingType.Attribute */, null, SecurityContext.NONE, attr.sourceSpan, BindingFlags.TextValue);
24568
+ ingestBinding(unit, xref, attr.name, literal(attr.value), 1 /* e.BindingType.Attribute */, null, SecurityContext.NONE, attr.sourceSpan, BindingFlags.TextValue, attr.i18n);
24050
24569
  }
24051
24570
  const tagName = root instanceof Element$1 ? root.name : root.tagName;
24052
24571
  // Don't pass along `ng-template` tag name since it enables directive matching.
@@ -27129,7 +27648,7 @@ class TemplateDefinitionBuilder {
27129
27648
  else {
27130
27649
  // ... otherwise we need to activate post-processing
27131
27650
  // to replace ICU placeholders with proper values
27132
- const placeholder = wrapI18nPlaceholder(`${I18N_ICU_MAPPING_PREFIX}${key}`);
27651
+ const placeholder = wrapI18nPlaceholder(`${I18N_ICU_MAPPING_PREFIX$1}${key}`);
27133
27652
  params[key] = literal(placeholder);
27134
27653
  icuMapping[key] = literalArr(refs);
27135
27654
  }
@@ -28749,24 +29268,6 @@ class TrackByBindingScope extends BindingScope {
28749
29268
  return this.componentAccessCount;
28750
29269
  }
28751
29270
  }
28752
- /**
28753
- * Creates a `CssSelector` given a tag name and a map of attributes
28754
- */
28755
- function createCssSelector(elementName, attributes) {
28756
- const cssSelector = new CssSelector();
28757
- const elementNameNoNs = splitNsName(elementName)[1];
28758
- cssSelector.setElement(elementNameNoNs);
28759
- Object.getOwnPropertyNames(attributes).forEach((name) => {
28760
- const nameNoNs = splitNsName(name)[1];
28761
- const value = attributes[name];
28762
- cssSelector.addAttribute(nameNoNs, value);
28763
- if (name.toLowerCase() === 'class') {
28764
- const classes = value.trim().split(/\s+/);
28765
- classes.forEach(className => cssSelector.addClassName(className));
28766
- }
28767
- });
28768
- return cssSelector;
28769
- }
28770
29271
  /**
28771
29272
  * Creates an array of expressions out of an `ngProjectAs` attributes
28772
29273
  * which can be added to the instruction parameters.
@@ -29127,6 +29628,11 @@ function addFeatures(definitionMap, meta) {
29127
29628
  break;
29128
29629
  }
29129
29630
  }
29631
+ // Note: host directives feature needs to be inserted before the
29632
+ // inheritance feature to ensure the correct execution order.
29633
+ if (meta.hostDirectives?.length) {
29634
+ features.push(importExpr(Identifiers.HostDirectivesFeature).callFn([createHostDirectivesFeatureArg(meta.hostDirectives)]));
29635
+ }
29130
29636
  if (meta.usesInheritance) {
29131
29637
  features.push(importExpr(Identifiers.InheritDefinitionFeature));
29132
29638
  }
@@ -29140,9 +29646,6 @@ function addFeatures(definitionMap, meta) {
29140
29646
  if (meta.hasOwnProperty('template') && meta.isStandalone) {
29141
29647
  features.push(importExpr(Identifiers.StandaloneFeature));
29142
29648
  }
29143
- if (meta.hostDirectives?.length) {
29144
- features.push(importExpr(Identifiers.HostDirectivesFeature).callFn([createHostDirectivesFeatureArg(meta.hostDirectives)]));
29145
- }
29146
29649
  if (features.length) {
29147
29650
  definitionMap.set('features', literalArr(features));
29148
29651
  }
@@ -29505,6 +30008,8 @@ function createHostBindingsFunction(hostBindingsMetadata, typeSourceSpan, bindin
29505
30008
  }
29506
30009
  return emitHostBindingFunction(hostJob);
29507
30010
  }
30011
+ let bindingId = 0;
30012
+ const getNextBindingId = () => `${bindingId++}`;
29508
30013
  const bindingContext = variable(CONTEXT_NAME);
29509
30014
  const styleBuilder = new StylingBuilder(bindingContext);
29510
30015
  const { styleAttr, classAttr } = hostBindingsMetadata.specialAttributes;
@@ -29557,7 +30062,7 @@ function createHostBindingsFunction(hostBindingsMetadata, typeSourceSpan, bindin
29557
30062
  for (const binding of allOtherBindings) {
29558
30063
  // resolve literal arrays and literal objects
29559
30064
  const value = binding.expression.visit(getValueConverter());
29560
- const bindingExpr = bindingFn(bindingContext, value);
30065
+ const bindingExpr = bindingFn(bindingContext, value, getNextBindingId);
29561
30066
  const { bindingName, instruction, isAttribute } = getBindingNameAndInstruction(binding);
29562
30067
  const securityContexts = bindingParser.calcPossibleSecurityContexts(selector, bindingName, isAttribute)
29563
30068
  .filter(context => context !== SecurityContext.NONE);
@@ -29636,10 +30141,12 @@ function createHostBindingsFunction(hostBindingsMetadata, typeSourceSpan, bindin
29636
30141
  // at the top of this method when all the input bindings were counted.
29637
30142
  totalHostVarsCount +=
29638
30143
  Math.max(call.allocateBindingSlots - MIN_STYLING_BINDING_SLOTS_REQUIRED, 0);
30144
+ const { params, stmts } = convertStylingCall(call, bindingContext, bindingFn, getNextBindingId);
30145
+ updateVariables.push(...stmts);
29639
30146
  updateInstructions.push({
29640
30147
  reference: instruction.reference,
29641
- paramsOrFn: convertStylingCall(call, bindingContext, bindingFn),
29642
- span: null
30148
+ paramsOrFn: params,
30149
+ span: null,
29643
30150
  });
29644
30151
  }
29645
30152
  });
@@ -29660,11 +30167,19 @@ function createHostBindingsFunction(hostBindingsMetadata, typeSourceSpan, bindin
29660
30167
  }
29661
30168
  return null;
29662
30169
  }
29663
- function bindingFn(implicit, value) {
29664
- return convertPropertyBinding(null, implicit, value, 'b');
30170
+ function bindingFn(implicit, value, getNextBindingIdFn) {
30171
+ return convertPropertyBinding(null, implicit, value, getNextBindingIdFn());
29665
30172
  }
29666
- function convertStylingCall(call, bindingContext, bindingFn) {
29667
- return call.params(value => bindingFn(bindingContext, value).currValExpr);
30173
+ function convertStylingCall(call, bindingContext, bindingFn, getNextBindingIdFn) {
30174
+ const stmts = [];
30175
+ const params = call.params(value => {
30176
+ const result = bindingFn(bindingContext, value, getNextBindingIdFn);
30177
+ if (Array.isArray(result.stmts) && result.stmts.length > 0) {
30178
+ stmts.push(...result.stmts);
30179
+ }
30180
+ return result.currValExpr;
30181
+ });
30182
+ return { params, stmts };
29668
30183
  }
29669
30184
  function getBindingNameAndInstruction(binding) {
29670
30185
  let bindingName = binding.name;
@@ -30103,15 +30618,15 @@ class DirectiveBinder {
30103
30618
  template.forEach(node => node.visit(this));
30104
30619
  }
30105
30620
  visitElement(element) {
30106
- this.visitElementOrTemplate(element.name, element);
30621
+ this.visitElementOrTemplate(element);
30107
30622
  }
30108
30623
  visitTemplate(template) {
30109
- this.visitElementOrTemplate('ng-template', template);
30624
+ this.visitElementOrTemplate(template);
30110
30625
  }
30111
- visitElementOrTemplate(elementName, node) {
30626
+ visitElementOrTemplate(node) {
30112
30627
  // First, determine the HTML shape of the node for the purpose of directive matching.
30113
30628
  // Do this by building up a `CssSelector` for the node.
30114
- const cssSelector = createCssSelector(elementName, getAttrsForDirectiveMatching(node));
30629
+ const cssSelector = createCssSelectorFromNode(node);
30115
30630
  // Next, use the `SelectorMatcher` to get the list of directives on the node.
30116
30631
  const directives = [];
30117
30632
  this.matcher.match(cssSelector, (_selector, results) => directives.push(...results));
@@ -31240,7 +31755,7 @@ function publishFacade(global) {
31240
31755
  * @description
31241
31756
  * Entry point for all public APIs of the compiler package.
31242
31757
  */
31243
- const VERSION = new Version('17.1.0-next.1');
31758
+ const VERSION = new Version('17.1.0-next.3');
31244
31759
 
31245
31760
  class CompilerConfig {
31246
31761
  constructor({ defaultEncapsulation = ViewEncapsulation.Emulated, preserveWhitespaces, strictInjectionParameters } = {}) {
@@ -32806,7 +33321,7 @@ const MINIMUM_PARTIAL_LINKER_VERSION$6 = '12.0.0';
32806
33321
  function compileDeclareClassMetadata(metadata) {
32807
33322
  const definitionMap = new DefinitionMap();
32808
33323
  definitionMap.set('minVersion', literal(MINIMUM_PARTIAL_LINKER_VERSION$6));
32809
- definitionMap.set('version', literal('17.1.0-next.1'));
33324
+ definitionMap.set('version', literal('17.1.0-next.3'));
32810
33325
  definitionMap.set('ngImport', importExpr(Identifiers.core));
32811
33326
  definitionMap.set('type', metadata.type);
32812
33327
  definitionMap.set('decorators', metadata.decorators);
@@ -32914,7 +33429,7 @@ function createDirectiveDefinitionMap(meta) {
32914
33429
  // in 16.1 is actually used.
32915
33430
  const minVersion = hasTransformFunctions ? MINIMUM_PARTIAL_LINKER_VERSION$5 : '14.0.0';
32916
33431
  definitionMap.set('minVersion', literal(minVersion));
32917
- definitionMap.set('version', literal('17.1.0-next.1'));
33432
+ definitionMap.set('version', literal('17.1.0-next.3'));
32918
33433
  // e.g. `type: MyDirective`
32919
33434
  definitionMap.set('type', meta.type.value);
32920
33435
  if (meta.isStandalone) {
@@ -33191,7 +33706,7 @@ const MINIMUM_PARTIAL_LINKER_VERSION$4 = '12.0.0';
33191
33706
  function compileDeclareFactoryFunction(meta) {
33192
33707
  const definitionMap = new DefinitionMap();
33193
33708
  definitionMap.set('minVersion', literal(MINIMUM_PARTIAL_LINKER_VERSION$4));
33194
- definitionMap.set('version', literal('17.1.0-next.1'));
33709
+ definitionMap.set('version', literal('17.1.0-next.3'));
33195
33710
  definitionMap.set('ngImport', importExpr(Identifiers.core));
33196
33711
  definitionMap.set('type', meta.type.value);
33197
33712
  definitionMap.set('deps', compileDependencies(meta.deps));
@@ -33226,7 +33741,7 @@ function compileDeclareInjectableFromMetadata(meta) {
33226
33741
  function createInjectableDefinitionMap(meta) {
33227
33742
  const definitionMap = new DefinitionMap();
33228
33743
  definitionMap.set('minVersion', literal(MINIMUM_PARTIAL_LINKER_VERSION$3));
33229
- definitionMap.set('version', literal('17.1.0-next.1'));
33744
+ definitionMap.set('version', literal('17.1.0-next.3'));
33230
33745
  definitionMap.set('ngImport', importExpr(Identifiers.core));
33231
33746
  definitionMap.set('type', meta.type.value);
33232
33747
  // Only generate providedIn property if it has a non-null value
@@ -33277,7 +33792,7 @@ function compileDeclareInjectorFromMetadata(meta) {
33277
33792
  function createInjectorDefinitionMap(meta) {
33278
33793
  const definitionMap = new DefinitionMap();
33279
33794
  definitionMap.set('minVersion', literal(MINIMUM_PARTIAL_LINKER_VERSION$2));
33280
- definitionMap.set('version', literal('17.1.0-next.1'));
33795
+ definitionMap.set('version', literal('17.1.0-next.3'));
33281
33796
  definitionMap.set('ngImport', importExpr(Identifiers.core));
33282
33797
  definitionMap.set('type', meta.type.value);
33283
33798
  definitionMap.set('providers', meta.providers);
@@ -33310,7 +33825,7 @@ function createNgModuleDefinitionMap(meta) {
33310
33825
  throw new Error('Invalid path! Local compilation mode should not get into the partial compilation path');
33311
33826
  }
33312
33827
  definitionMap.set('minVersion', literal(MINIMUM_PARTIAL_LINKER_VERSION$1));
33313
- definitionMap.set('version', literal('17.1.0-next.1'));
33828
+ definitionMap.set('version', literal('17.1.0-next.3'));
33314
33829
  definitionMap.set('ngImport', importExpr(Identifiers.core));
33315
33830
  definitionMap.set('type', meta.type.value);
33316
33831
  // We only generate the keys in the metadata if the arrays contain values.
@@ -33361,7 +33876,7 @@ function compileDeclarePipeFromMetadata(meta) {
33361
33876
  function createPipeDefinitionMap(meta) {
33362
33877
  const definitionMap = new DefinitionMap();
33363
33878
  definitionMap.set('minVersion', literal(MINIMUM_PARTIAL_LINKER_VERSION));
33364
- definitionMap.set('version', literal('17.1.0-next.1'));
33879
+ definitionMap.set('version', literal('17.1.0-next.3'));
33365
33880
  definitionMap.set('ngImport', importExpr(Identifiers.core));
33366
33881
  // e.g. `type: MyPipe`
33367
33882
  definitionMap.set('type', meta.type.value);
@@ -33394,5 +33909,5 @@ publishFacade(_global);
33394
33909
 
33395
33910
  // This file is not used to build this module. It is only used during editing
33396
33911
 
33397
- export { AST, ASTWithName, ASTWithSource, AbsoluteSourceSpan, ArrayType, ArrowFunctionExpr, AstMemoryEfficientTransformer, AstTransformer, Attribute, Binary, BinaryOperator, BinaryOperatorExpr, BindingPipe, Block, BlockParameter, BoundElementProperty, BuiltinType, BuiltinTypeName, CUSTOM_ELEMENTS_SCHEMA, Call, Chain, ChangeDetectionStrategy, CommaExpr, Comment, CompilerConfig, Conditional, ConditionalExpr, ConstantPool, CssSelector, DEFAULT_INTERPOLATION_CONFIG, DYNAMIC_TYPE, DeclareFunctionStmt, DeclareVarStmt, DomElementSchemaRegistry, DynamicImportExpr, EOF, Element, ElementSchemaRegistry, EmitterVisitorContext, EmptyExpr$1 as EmptyExpr, Expansion, ExpansionCase, Expression, ExpressionBinding, ExpressionStatement, ExpressionType, ExternalExpr, ExternalReference, FactoryTarget$1 as FactoryTarget, FunctionExpr, HtmlParser, HtmlTagDefinition, I18NHtmlParser, IfStmt, ImplicitReceiver, InstantiateExpr, Interpolation$1 as Interpolation, InterpolationConfig, InvokeFunctionExpr, JSDocComment, JitEvaluator, KeyedRead, KeyedWrite, LeadingComment, Lexer, LiteralArray, LiteralArrayExpr, LiteralExpr, LiteralMap, LiteralMapExpr, LiteralPrimitive, LocalizedString, MapType, MessageBundle, NONE_TYPE, NO_ERRORS_SCHEMA, NodeWithI18n, NonNullAssert, NotExpr, ParseError, ParseErrorLevel, ParseLocation, ParseSourceFile, ParseSourceSpan, ParseSpan, ParseTreeResult, ParsedEvent, ParsedProperty, ParsedPropertyType, ParsedVariable, Parser$1 as Parser, ParserError, PrefixNot, PropertyRead, PropertyWrite, R3BoundTarget, Identifiers as R3Identifiers, R3NgModuleMetadataKind, R3SelectorScopeMode, R3TargetBinder, R3TemplateDependencyKind, ReadKeyExpr, ReadPropExpr, ReadVarExpr, RecursiveAstVisitor, RecursiveVisitor, ResourceLoader, ReturnStatement, STRING_TYPE, SafeCall, SafeKeyedRead, SafePropertyRead, SelectorContext, SelectorListContext, SelectorMatcher, Serializer, SplitInterpolation, Statement, StmtModifier, TagContentType, TaggedTemplateExpr, TemplateBindingParseResult, TemplateLiteral, TemplateLiteralElement, Text, ThisReceiver, BoundAttribute as TmplAstBoundAttribute, BoundDeferredTrigger as TmplAstBoundDeferredTrigger, BoundEvent as TmplAstBoundEvent, BoundText as TmplAstBoundText, Content as TmplAstContent, DeferredBlock as TmplAstDeferredBlock, DeferredBlockError as TmplAstDeferredBlockError, DeferredBlockLoading as TmplAstDeferredBlockLoading, DeferredBlockPlaceholder as TmplAstDeferredBlockPlaceholder, DeferredTrigger as TmplAstDeferredTrigger, Element$1 as TmplAstElement, ForLoopBlock as TmplAstForLoopBlock, ForLoopBlockEmpty as TmplAstForLoopBlockEmpty, HoverDeferredTrigger as TmplAstHoverDeferredTrigger, Icu$1 as TmplAstIcu, IdleDeferredTrigger as TmplAstIdleDeferredTrigger, IfBlock as TmplAstIfBlock, IfBlockBranch as TmplAstIfBlockBranch, ImmediateDeferredTrigger as TmplAstImmediateDeferredTrigger, InteractionDeferredTrigger as TmplAstInteractionDeferredTrigger, RecursiveVisitor$1 as TmplAstRecursiveVisitor, Reference as TmplAstReference, SwitchBlock as TmplAstSwitchBlock, SwitchBlockCase as TmplAstSwitchBlockCase, Template as TmplAstTemplate, Text$3 as TmplAstText, TextAttribute as TmplAstTextAttribute, TimerDeferredTrigger as TmplAstTimerDeferredTrigger, UnknownBlock as TmplAstUnknownBlock, Variable as TmplAstVariable, ViewportDeferredTrigger as TmplAstViewportDeferredTrigger, Token, TokenType, TransplantedType, TreeError, Type, TypeModifier, TypeofExpr, Unary, UnaryOperator, UnaryOperatorExpr, VERSION, VariableBinding, Version, ViewEncapsulation, WrappedNodeExpr, WriteKeyExpr, WritePropExpr, WriteVarExpr, Xliff, Xliff2, Xmb, XmlParser, Xtb, _ParseAST, compileClassDebugInfo, compileClassMetadata, compileComponentClassMetadata, compileComponentFromMetadata, compileDeclareClassMetadata, compileDeclareComponentFromMetadata, compileDeclareDirectiveFromMetadata, compileDeclareFactoryFunction, compileDeclareInjectableFromMetadata, compileDeclareInjectorFromMetadata, compileDeclareNgModuleFromMetadata, compileDeclarePipeFromMetadata, compileDirectiveFromMetadata, compileFactoryFunction, compileInjectable, compileInjector, compileNgModule, compilePipeFromMetadata, computeMsgId, core, createInjectableType, createMayBeForwardRefExpression, devOnlyGuardedExpression, emitDistinctChangesOnlyDefaultValue, getHtmlTagDefinition, getNsPrefix, getSafePropertyAccessString, identifierName, isIdentifier, isNgContainer, isNgContent, isNgTemplate, jsDocComment, leadingComment, literal, literalMap, makeBindingParser, mergeNsAndName, output_ast as outputAst, parseHostBindings, parseTemplate, preserveWhitespacesDefault, publishFacade, r3JitTypeSourceSpan, sanitizeIdentifier, splitNsName, verifyHostBindings, visitAll };
33912
+ export { AST, ASTWithName, ASTWithSource, AbsoluteSourceSpan, ArrayType, ArrowFunctionExpr, AstMemoryEfficientTransformer, AstTransformer, Attribute, Binary, BinaryOperator, BinaryOperatorExpr, BindingPipe, Block, BlockParameter, BoundElementProperty, BuiltinType, BuiltinTypeName, CUSTOM_ELEMENTS_SCHEMA, Call, Chain, ChangeDetectionStrategy, CommaExpr, Comment, CompilerConfig, Conditional, ConditionalExpr, ConstantPool, CssSelector, DEFAULT_INTERPOLATION_CONFIG, DYNAMIC_TYPE, DeclareFunctionStmt, DeclareVarStmt, DomElementSchemaRegistry, DynamicImportExpr, EOF, Element, ElementSchemaRegistry, EmitterVisitorContext, EmptyExpr$1 as EmptyExpr, Expansion, ExpansionCase, Expression, ExpressionBinding, ExpressionStatement, ExpressionType, ExternalExpr, ExternalReference, FactoryTarget$1 as FactoryTarget, FunctionExpr, HtmlParser, HtmlTagDefinition, I18NHtmlParser, IfStmt, ImplicitReceiver, InstantiateExpr, Interpolation$1 as Interpolation, InterpolationConfig, InvokeFunctionExpr, JSDocComment, JitEvaluator, KeyedRead, KeyedWrite, LeadingComment, Lexer, LiteralArray, LiteralArrayExpr, LiteralExpr, LiteralMap, LiteralMapExpr, LiteralPrimitive, LocalizedString, MapType, MessageBundle, NONE_TYPE, NO_ERRORS_SCHEMA, NodeWithI18n, NonNullAssert, NotExpr, ParseError, ParseErrorLevel, ParseLocation, ParseSourceFile, ParseSourceSpan, ParseSpan, ParseTreeResult, ParsedEvent, ParsedProperty, ParsedPropertyType, ParsedVariable, Parser$1 as Parser, ParserError, PrefixNot, PropertyRead, PropertyWrite, R3BoundTarget, Identifiers as R3Identifiers, R3NgModuleMetadataKind, R3SelectorScopeMode, R3TargetBinder, R3TemplateDependencyKind, ReadKeyExpr, ReadPropExpr, ReadVarExpr, RecursiveAstVisitor, RecursiveVisitor, ResourceLoader, ReturnStatement, STRING_TYPE, SafeCall, SafeKeyedRead, SafePropertyRead, SelectorContext, SelectorListContext, SelectorMatcher, Serializer, SplitInterpolation, Statement, StmtModifier, TagContentType, TaggedTemplateExpr, TemplateBindingParseResult, TemplateLiteral, TemplateLiteralElement, Text, ThisReceiver, BoundAttribute as TmplAstBoundAttribute, BoundDeferredTrigger as TmplAstBoundDeferredTrigger, BoundEvent as TmplAstBoundEvent, BoundText as TmplAstBoundText, Content as TmplAstContent, DeferredBlock as TmplAstDeferredBlock, DeferredBlockError as TmplAstDeferredBlockError, DeferredBlockLoading as TmplAstDeferredBlockLoading, DeferredBlockPlaceholder as TmplAstDeferredBlockPlaceholder, DeferredTrigger as TmplAstDeferredTrigger, Element$1 as TmplAstElement, ForLoopBlock as TmplAstForLoopBlock, ForLoopBlockEmpty as TmplAstForLoopBlockEmpty, HoverDeferredTrigger as TmplAstHoverDeferredTrigger, Icu$1 as TmplAstIcu, IdleDeferredTrigger as TmplAstIdleDeferredTrigger, IfBlock as TmplAstIfBlock, IfBlockBranch as TmplAstIfBlockBranch, ImmediateDeferredTrigger as TmplAstImmediateDeferredTrigger, InteractionDeferredTrigger as TmplAstInteractionDeferredTrigger, RecursiveVisitor$1 as TmplAstRecursiveVisitor, Reference as TmplAstReference, SwitchBlock as TmplAstSwitchBlock, SwitchBlockCase as TmplAstSwitchBlockCase, Template as TmplAstTemplate, Text$3 as TmplAstText, TextAttribute as TmplAstTextAttribute, TimerDeferredTrigger as TmplAstTimerDeferredTrigger, UnknownBlock as TmplAstUnknownBlock, Variable as TmplAstVariable, ViewportDeferredTrigger as TmplAstViewportDeferredTrigger, Token, TokenType, TransplantedType, TreeError, Type, TypeModifier, TypeofExpr, Unary, UnaryOperator, UnaryOperatorExpr, VERSION, VariableBinding, Version, ViewEncapsulation, WrappedNodeExpr, WriteKeyExpr, WritePropExpr, WriteVarExpr, Xliff, Xliff2, Xmb, XmlParser, Xtb, _ParseAST, compileClassDebugInfo, compileClassMetadata, compileComponentClassMetadata, compileComponentFromMetadata, compileDeclareClassMetadata, compileDeclareComponentFromMetadata, compileDeclareDirectiveFromMetadata, compileDeclareFactoryFunction, compileDeclareInjectableFromMetadata, compileDeclareInjectorFromMetadata, compileDeclareNgModuleFromMetadata, compileDeclarePipeFromMetadata, compileDirectiveFromMetadata, compileFactoryFunction, compileInjectable, compileInjector, compileNgModule, compilePipeFromMetadata, computeMsgId, core, createCssSelectorFromNode, createInjectableType, createMayBeForwardRefExpression, devOnlyGuardedExpression, emitDistinctChangesOnlyDefaultValue, getHtmlTagDefinition, getNsPrefix, getSafePropertyAccessString, identifierName, isIdentifier, isNgContainer, isNgContent, isNgTemplate, jsDocComment, leadingComment, literal, literalMap, makeBindingParser, mergeNsAndName, output_ast as outputAst, parseHostBindings, parseTemplate, preserveWhitespacesDefault, publishFacade, r3JitTypeSourceSpan, sanitizeIdentifier, splitNsName, verifyHostBindings, visitAll };
33398
33913
  //# sourceMappingURL=compiler.mjs.map