@angular/compiler 17.0.5 → 17.0.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. package/esm2022/src/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 +20 -8
  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 +26 -1
  16. package/esm2022/src/template/pipeline/ir/src/expression.mjs +8 -1
  17. package/esm2022/src/template/pipeline/ir/src/ops/create.mjs +25 -7
  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 -6
  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 -5
  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/extract_i18n_messages.mjs +65 -88
  30. package/esm2022/src/template/pipeline/src/phases/generate_advance.mjs +2 -2
  31. package/esm2022/src/template/pipeline/src/phases/i18n_const_collection.mjs +143 -35
  32. package/esm2022/src/template/pipeline/src/phases/i18n_text_extraction.mjs +5 -3
  33. package/esm2022/src/template/pipeline/src/phases/parse_extracted_styles.mjs +3 -3
  34. package/esm2022/src/template/pipeline/src/phases/phase_remove_content_selectors.mjs +15 -3
  35. package/esm2022/src/template/pipeline/src/phases/propagate_i18n_blocks.mjs +5 -1
  36. package/esm2022/src/template/pipeline/src/phases/reify.mjs +7 -1
  37. package/esm2022/src/template/pipeline/src/phases/remove_unused_i18n_attrs.mjs +33 -0
  38. package/esm2022/src/template/pipeline/src/phases/resolve_i18n_element_placeholders.mjs +129 -31
  39. package/esm2022/src/template/pipeline/src/phases/resolve_i18n_expression_placeholders.mjs +10 -5
  40. package/esm2022/src/version.mjs +1 -1
  41. package/fesm2022/compiler.mjs +793 -315
  42. package/fesm2022/compiler.mjs.map +1 -1
  43. package/index.d.ts +21 -1
  44. package/package.json +2 -2
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @license Angular v17.0.5
2
+ * @license Angular v17.0.6
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.
@@ -9202,7 +9275,14 @@ var I18nContextKind;
9202
9275
  (function (I18nContextKind) {
9203
9276
  I18nContextKind[I18nContextKind["RootI18n"] = 0] = "RootI18n";
9204
9277
  I18nContextKind[I18nContextKind["Icu"] = 1] = "Icu";
9278
+ I18nContextKind[I18nContextKind["Attr"] = 2] = "Attr";
9205
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 = {}));
9206
9286
 
9207
9287
  /**
9208
9288
  * Marker symbol for `ConsumesSlotOpTrait`.
@@ -9310,12 +9390,11 @@ const NEW_OP = {
9310
9390
  /**
9311
9391
  * Create an `InterpolationTextOp`.
9312
9392
  */
9313
- function createInterpolateTextOp(xref, interpolation, i18nPlaceholders, sourceSpan) {
9393
+ function createInterpolateTextOp(xref, interpolation, sourceSpan) {
9314
9394
  return {
9315
9395
  kind: OpKind.InterpolateText,
9316
9396
  target: xref,
9317
9397
  interpolation,
9318
- i18nPlaceholders,
9319
9398
  sourceSpan,
9320
9399
  ...TRAIT_DEPENDS_ON_SLOT_CONTEXT,
9321
9400
  ...TRAIT_CONSUMES_VARS,
@@ -9323,15 +9402,19 @@ function createInterpolateTextOp(xref, interpolation, i18nPlaceholders, sourceSp
9323
9402
  };
9324
9403
  }
9325
9404
  class Interpolation {
9326
- constructor(strings, expressions) {
9405
+ constructor(strings, expressions, i18nPlaceholders) {
9327
9406
  this.strings = strings;
9328
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
+ }
9329
9412
  }
9330
9413
  }
9331
9414
  /**
9332
9415
  * Create a `BindingOp`, not yet transformed into a particular type of binding.
9333
9416
  */
9334
- function createBindingOp(target, kind, name, expression, unit, securityContext, isTextAttribute, isTemplate, sourceSpan) {
9417
+ function createBindingOp(target, kind, name, expression, unit, securityContext, isTextAttribute, isStructuralTemplate, i18nContext, sourceSpan) {
9335
9418
  return {
9336
9419
  kind: OpKind.Binding,
9337
9420
  bindingKind: kind,
@@ -9341,7 +9424,8 @@ function createBindingOp(target, kind, name, expression, unit, securityContext,
9341
9424
  unit,
9342
9425
  securityContext,
9343
9426
  isTextAttribute,
9344
- isTemplate,
9427
+ isStructuralTemplate: isStructuralTemplate,
9428
+ i18nContext,
9345
9429
  sourceSpan,
9346
9430
  ...NEW_OP,
9347
9431
  };
@@ -9349,7 +9433,7 @@ function createBindingOp(target, kind, name, expression, unit, securityContext,
9349
9433
  /**
9350
9434
  * Create a `PropertyOp`.
9351
9435
  */
9352
- function createPropertyOp(target, name, expression, isAnimationTrigger, securityContext, isTemplate, sourceSpan) {
9436
+ function createPropertyOp(target, name, expression, isAnimationTrigger, securityContext, isStructuralTemplate, i18nContext, sourceSpan) {
9353
9437
  return {
9354
9438
  kind: OpKind.Property,
9355
9439
  target,
@@ -9358,7 +9442,8 @@ function createPropertyOp(target, name, expression, isAnimationTrigger, security
9358
9442
  isAnimationTrigger,
9359
9443
  securityContext,
9360
9444
  sanitizer: null,
9361
- isTemplate,
9445
+ isStructuralTemplate,
9446
+ i18nContext,
9362
9447
  sourceSpan,
9363
9448
  ...TRAIT_DEPENDS_ON_SLOT_CONTEXT,
9364
9449
  ...TRAIT_CONSUMES_VARS,
@@ -9423,7 +9508,7 @@ function createClassMapOp(xref, expression, sourceSpan) {
9423
9508
  /**
9424
9509
  * Create an `AttributeOp`.
9425
9510
  */
9426
- function createAttributeOp(target, name, expression, securityContext, isTextAttribute, isTemplate, sourceSpan) {
9511
+ function createAttributeOp(target, name, expression, securityContext, isTextAttribute, isStructuralTemplate, i18nContext, sourceSpan) {
9427
9512
  return {
9428
9513
  kind: OpKind.Attribute,
9429
9514
  target,
@@ -9432,7 +9517,8 @@ function createAttributeOp(target, name, expression, securityContext, isTextAttr
9432
9517
  securityContext,
9433
9518
  sanitizer: null,
9434
9519
  isTextAttribute,
9435
- isTemplate,
9520
+ isStructuralTemplate,
9521
+ i18nContext,
9436
9522
  sourceSpan,
9437
9523
  ...TRAIT_DEPENDS_ON_SLOT_CONTEXT,
9438
9524
  ...TRAIT_CONSUMES_VARS,
@@ -9493,15 +9579,18 @@ function createDeferWhenOp(target, expr, prefetch, sourceSpan) {
9493
9579
  /**
9494
9580
  * Create an i18n expression op.
9495
9581
  */
9496
- function createI18nExpressionOp(context, target, handle, expression, i18nPlaceholder, resolutionTime, sourceSpan) {
9582
+ function createI18nExpressionOp(context, target, i18nOwner, handle, expression, i18nPlaceholder, resolutionTime, usage, name, sourceSpan) {
9497
9583
  return {
9498
9584
  kind: OpKind.I18nExpression,
9499
9585
  context,
9500
9586
  target,
9587
+ i18nOwner,
9501
9588
  handle,
9502
9589
  expression,
9503
9590
  i18nPlaceholder,
9504
9591
  resolutionTime,
9592
+ usage,
9593
+ name,
9505
9594
  sourceSpan,
9506
9595
  ...NEW_OP,
9507
9596
  ...TRAIT_CONSUMES_VARS,
@@ -9509,12 +9598,12 @@ function createI18nExpressionOp(context, target, handle, expression, i18nPlaceho
9509
9598
  };
9510
9599
  }
9511
9600
  /**
9512
- *Creates an op to apply i18n expression ops
9601
+ * Creates an op to apply i18n expression ops.
9513
9602
  */
9514
- function createI18nApplyOp(target, handle, sourceSpan) {
9603
+ function createI18nApplyOp(owner, handle, sourceSpan) {
9515
9604
  return {
9516
9605
  kind: OpKind.I18nApply,
9517
- target,
9606
+ owner,
9518
9607
  handle,
9519
9608
  sourceSpan,
9520
9609
  ...NEW_OP,
@@ -10318,6 +10407,7 @@ function transformExpressionsInOp(op, transform, flags) {
10318
10407
  case OpKind.ProjectionDef:
10319
10408
  case OpKind.Template:
10320
10409
  case OpKind.Text:
10410
+ case OpKind.I18nAttributes:
10321
10411
  // These operations contain no expressions.
10322
10412
  break;
10323
10413
  default:
@@ -10338,6 +10428,9 @@ function transformExpressionsInExpression(expr, transform, flags) {
10338
10428
  expr.lhs = transformExpressionsInExpression(expr.lhs, transform, flags);
10339
10429
  expr.rhs = transformExpressionsInExpression(expr.rhs, transform, flags);
10340
10430
  }
10431
+ else if (expr instanceof UnaryOperatorExpr) {
10432
+ expr.expr = transformExpressionsInExpression(expr.expr, transform, flags);
10433
+ }
10341
10434
  else if (expr instanceof ReadPropExpr) {
10342
10435
  expr.receiver = transformExpressionsInExpression(expr.receiver, transform, flags);
10343
10436
  }
@@ -10389,6 +10482,9 @@ function transformExpressionsInExpression(expr, transform, flags) {
10389
10482
  expr.expressions[i] = transformExpressionsInExpression(expr.expressions[i], transform, flags);
10390
10483
  }
10391
10484
  }
10485
+ else if (expr instanceof NotExpr) {
10486
+ expr.condition = transformExpressionsInExpression(expr.condition, transform, flags);
10487
+ }
10392
10488
  else if (expr instanceof ReadVarExpr || expr instanceof ExternalExpr ||
10393
10489
  expr instanceof LiteralExpr) {
10394
10490
  // No action for these types.
@@ -10730,10 +10826,11 @@ function createElementStartOp(tag, xref, namespace, i18nPlaceholder, sourceSpan)
10730
10826
  /**
10731
10827
  * Create a `TemplateOp`.
10732
10828
  */
10733
- function createTemplateOp(xref, tag, functionNameSuffix, namespace, i18nPlaceholder, sourceSpan) {
10829
+ function createTemplateOp(xref, templateKind, tag, functionNameSuffix, namespace, i18nPlaceholder, sourceSpan) {
10734
10830
  return {
10735
10831
  kind: OpKind.Template,
10736
10832
  xref,
10833
+ templateKind,
10737
10834
  attributes: null,
10738
10835
  tag,
10739
10836
  handle: new SlotHandle(),
@@ -10856,14 +10953,15 @@ function createProjectionDefOp(def) {
10856
10953
  ...NEW_OP,
10857
10954
  };
10858
10955
  }
10859
- function createProjectionOp(xref, selector, sourceSpan) {
10956
+ function createProjectionOp(xref, selector, i18nPlaceholder, attributes, sourceSpan) {
10860
10957
  return {
10861
10958
  kind: OpKind.Projection,
10862
10959
  xref,
10863
10960
  handle: new SlotHandle(),
10864
10961
  selector,
10962
+ i18nPlaceholder,
10865
10963
  projectionSlotIndex: 0,
10866
- attributes: [],
10964
+ attributes,
10867
10965
  localRefs: [],
10868
10966
  sourceSpan,
10869
10967
  ...NEW_OP,
@@ -10873,13 +10971,14 @@ function createProjectionOp(xref, selector, sourceSpan) {
10873
10971
  /**
10874
10972
  * Create an `ExtractedAttributeOp`.
10875
10973
  */
10876
- function createExtractedAttributeOp(target, bindingKind, name, expression) {
10974
+ function createExtractedAttributeOp(target, bindingKind, name, expression, i18nContext) {
10877
10975
  return {
10878
10976
  kind: OpKind.ExtractedAttribute,
10879
10977
  target,
10880
10978
  bindingKind,
10881
10979
  name,
10882
10980
  expression,
10981
+ i18nContext,
10883
10982
  ...NEW_OP,
10884
10983
  };
10885
10984
  }
@@ -10922,10 +11021,11 @@ function createDeferOnOp(defer, trigger, prefetch, sourceSpan) {
10922
11021
  /**
10923
11022
  * Create an `ExtractedMessageOp`.
10924
11023
  */
10925
- function createI18nMessageOp(xref, i18nBlock, message, messagePlaceholder, params, postprocessingParams, needsPostprocessing) {
11024
+ function createI18nMessageOp(xref, i18nContext, i18nBlock, message, messagePlaceholder, params, postprocessingParams, needsPostprocessing) {
10926
11025
  return {
10927
11026
  kind: OpKind.I18nMessage,
10928
11027
  xref,
11028
+ i18nContext,
10929
11029
  i18nBlock,
10930
11030
  message,
10931
11031
  messagePlaceholder,
@@ -10988,6 +11088,9 @@ function createIcuEndOp(xref) {
10988
11088
  };
10989
11089
  }
10990
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
+ }
10991
11094
  return {
10992
11095
  kind: OpKind.I18nContext,
10993
11096
  contextKind,
@@ -11000,6 +11103,17 @@ function createI18nContextOp(contextKind, xref, i18nBlock, message, sourceSpan)
11000
11103
  ...NEW_OP,
11001
11104
  };
11002
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
+ }
11003
11117
  function literalOrArrayLiteral$1(value) {
11004
11118
  if (Array.isArray(value)) {
11005
11119
  return literalArr(value.map(literalOrArrayLiteral$1));
@@ -11007,12 +11121,13 @@ function literalOrArrayLiteral$1(value) {
11007
11121
  return literal(value, INFERRED_TYPE);
11008
11122
  }
11009
11123
 
11010
- function createHostPropertyOp(name, expression, isAnimationTrigger, sourceSpan) {
11124
+ function createHostPropertyOp(name, expression, isAnimationTrigger, i18nContext, sourceSpan) {
11011
11125
  return {
11012
11126
  kind: OpKind.HostProperty,
11013
11127
  name,
11014
11128
  expression,
11015
11129
  isAnimationTrigger,
11130
+ i18nContext,
11016
11131
  sourceSpan,
11017
11132
  ...TRAIT_CONSUMES_VARS,
11018
11133
  ...NEW_OP,
@@ -11249,7 +11364,7 @@ function applyI18nExpressions(job) {
11249
11364
  // Only add apply after expressions that are not followed by more expressions.
11250
11365
  if (op.kind === OpKind.I18nExpression && needsApplication(i18nContexts, op)) {
11251
11366
  // TODO: what should be the source span for the apply op?
11252
- OpList.insertAfter(createI18nApplyOp(op.target, op.handle, null), op);
11367
+ OpList.insertAfter(createI18nApplyOp(op.i18nOwner, op.handle, null), op);
11253
11368
  }
11254
11369
  }
11255
11370
  }
@@ -11262,17 +11377,34 @@ function needsApplication(i18nContexts, op) {
11262
11377
  if (op.next?.kind !== OpKind.I18nExpression) {
11263
11378
  return true;
11264
11379
  }
11265
- // If the next op is an expression targeting a different i18n block, we need to apply.
11266
11380
  const context = i18nContexts.get(op.context);
11267
11381
  const nextContext = i18nContexts.get(op.next.context);
11268
- 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) {
11269
11400
  return true;
11270
11401
  }
11271
11402
  return false;
11272
11403
  }
11273
11404
 
11274
11405
  /**
11275
- * 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.
11276
11408
  */
11277
11409
  function assignI18nSlotDependencies(job) {
11278
11410
  const i18nLastSlotConsumers = new Map();
@@ -11289,17 +11421,54 @@ function assignI18nSlotDependencies(job) {
11289
11421
  currentI18nOp = op;
11290
11422
  break;
11291
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
+ }
11292
11430
  i18nLastSlotConsumers.set(currentI18nOp.xref, lastSlotConsumer);
11293
11431
  currentI18nOp = null;
11294
11432
  break;
11295
11433
  }
11296
11434
  }
11297
- // 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;
11298
11445
  for (const op of unit.update) {
11299
- if (op.kind === OpKind.I18nExpression) {
11300
- op.target = i18nLastSlotConsumers.get(op.target);
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;
11301
11467
  }
11302
11468
  }
11469
+ if (moveAfterTarget !== null) {
11470
+ unit.update.push(opsToMove);
11471
+ }
11303
11472
  }
11304
11473
  }
11305
11474
 
@@ -11331,22 +11500,36 @@ function extractAttributes(job) {
11331
11500
  break;
11332
11501
  case OpKind.Property:
11333
11502
  if (!op.isAnimationTrigger) {
11334
- 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));
11335
11517
  }
11336
11518
  break;
11337
11519
  case OpKind.StyleProp:
11338
11520
  case OpKind.ClassProp:
11521
+ // TODO: Can style or class bindings be i18n attributes?
11339
11522
  // The old compiler treated empty style bindings as regular bindings for the purpose of
11340
11523
  // directive matching. That behavior is incorrect, but we emulate it in compatibility
11341
11524
  // mode.
11342
11525
  if (unit.job.compatibility === CompatibilityMode.TemplateDefinitionBuilder &&
11343
11526
  op.expression instanceof EmptyExpr) {
11344
- 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));
11345
11528
  }
11346
11529
  break;
11347
11530
  case OpKind.Listener:
11348
11531
  if (!op.isAnimationListener) {
11349
- const extractedAttributeOp = createExtractedAttributeOp(op.target, BindingKind.Property, op.name, null);
11532
+ const extractedAttributeOp = createExtractedAttributeOp(op.target, BindingKind.Property, op.name, null, null);
11350
11533
  if (job.kind === CompilationJobKind.Host) {
11351
11534
  // This attribute will apply to the enclosing host binding compilation unit, so order
11352
11535
  // doesn't matter.
@@ -11395,7 +11578,7 @@ function extractAttributeOp(unit, op, elements) {
11395
11578
  }
11396
11579
  }
11397
11580
  if (extractable) {
11398
- 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);
11399
11582
  if (unit.job.kind === CompilationJobKind.Host) {
11400
11583
  // This attribute will apply to the enclosing host binding compilation unit, so order doesn't
11401
11584
  // matter.
@@ -11442,16 +11625,16 @@ function specializeBindings(job) {
11442
11625
  target.nonBindable = true;
11443
11626
  }
11444
11627
  else {
11445
- 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));
11446
11629
  }
11447
11630
  break;
11448
11631
  case BindingKind.Property:
11449
11632
  case BindingKind.Animation:
11450
11633
  if (job.kind === CompilationJobKind.Host) {
11451
- 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));
11452
11635
  }
11453
11636
  else {
11454
- 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));
11455
11638
  }
11456
11639
  break;
11457
11640
  case BindingKind.I18n:
@@ -11628,41 +11811,6 @@ function generateConditionalExpressions(job) {
11628
11811
  }
11629
11812
  }
11630
11813
 
11631
- var TagContentType;
11632
- (function (TagContentType) {
11633
- TagContentType[TagContentType["RAW_TEXT"] = 0] = "RAW_TEXT";
11634
- TagContentType[TagContentType["ESCAPABLE_RAW_TEXT"] = 1] = "ESCAPABLE_RAW_TEXT";
11635
- TagContentType[TagContentType["PARSABLE_DATA"] = 2] = "PARSABLE_DATA";
11636
- })(TagContentType || (TagContentType = {}));
11637
- function splitNsName(elementName) {
11638
- if (elementName[0] != ':') {
11639
- return [null, elementName];
11640
- }
11641
- const colonIndex = elementName.indexOf(':', 1);
11642
- if (colonIndex === -1) {
11643
- throw new Error(`Unsupported format "${elementName}" expecting ":namespace:name"`);
11644
- }
11645
- return [elementName.slice(1, colonIndex), elementName.slice(colonIndex + 1)];
11646
- }
11647
- // `<ng-container>` tags work the same regardless the namespace
11648
- function isNgContainer(tagName) {
11649
- return splitNsName(tagName)[1] === 'ng-container';
11650
- }
11651
- // `<ng-content>` tags work the same regardless the namespace
11652
- function isNgContent(tagName) {
11653
- return splitNsName(tagName)[1] === 'ng-content';
11654
- }
11655
- // `<ng-template>` tags work the same regardless the namespace
11656
- function isNgTemplate(tagName) {
11657
- return splitNsName(tagName)[1] === 'ng-template';
11658
- }
11659
- function getNsPrefix(fullName) {
11660
- return fullName === null ? null : splitNsName(fullName)[0];
11661
- }
11662
- function mergeNsAndName(prefix, localName) {
11663
- return prefix ? `:${prefix}:${localName}` : localName;
11664
- }
11665
-
11666
11814
  const BINARY_OPERATORS = new Map([
11667
11815
  ['&&', BinaryOperator.And],
11668
11816
  ['>', BinaryOperator.Bigger],
@@ -11807,7 +11955,7 @@ class ElementAttributes {
11807
11955
  array.push(...getAttributeNameLiterals$1(name));
11808
11956
  if (kind === BindingKind.Attribute || kind === BindingKind.StyleProperty) {
11809
11957
  if (value === null) {
11810
- throw Error('Attribute & style element attributes must have a value');
11958
+ throw Error('Attribute, i18n attribute, & style element attributes must have a value');
11811
11959
  }
11812
11960
  array.push(value);
11813
11961
  }
@@ -11861,6 +12009,50 @@ function serializeAttributes({ attributes, bindings, classes, i18n, projectAs, s
11861
12009
  return literalArr(attrArray);
11862
12010
  }
11863
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
+
11864
12056
  /**
11865
12057
  * Create extracted deps functions for defer ops.
11866
12058
  */
@@ -12304,7 +12496,7 @@ function ternaryTransform(e) {
12304
12496
  /**
12305
12497
  * The escape sequence used indicate message param values.
12306
12498
  */
12307
- const ESCAPE = '\uFFFD';
12499
+ const ESCAPE$1 = '\uFFFD';
12308
12500
  /**
12309
12501
  * Marker used to indicate an element tag.
12310
12502
  */
@@ -12353,6 +12545,18 @@ function extractI18nMessages(job) {
12353
12545
  }
12354
12546
  }
12355
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
+ }
12356
12560
  // Extract messages from root i18n blocks.
12357
12561
  const i18nBlockMessages = new Map();
12358
12562
  for (const unit of job.units) {
@@ -12377,6 +12581,9 @@ function extractI18nMessages(job) {
12377
12581
  }
12378
12582
  const i18nContext = i18nContexts.get(op.context);
12379
12583
  if (i18nContext.contextKind === I18nContextKind.Icu) {
12584
+ if (i18nContext.i18nBlock === null) {
12585
+ throw Error('ICU context should have its i18n block set.');
12586
+ }
12380
12587
  const subMessage = createI18nMessage(job, i18nContext, op.messagePlaceholder);
12381
12588
  unit.create.push(subMessage);
12382
12589
  const rootI18nId = i18nBlocks.get(i18nContext.i18nBlock).root;
@@ -12396,119 +12603,86 @@ function extractI18nMessages(job) {
12396
12603
  * Create an i18n message op from an i18n context op.
12397
12604
  */
12398
12605
  function createI18nMessage(job, context, messagePlaceholder) {
12399
- let [formattedParams, needsPostprocessing] = formatParams(context.params);
12400
- const [formattedPostprocessingParams] = formatParams(context.postprocessingParams);
12401
- needsPostprocessing ||= formattedPostprocessingParams.size > 0;
12402
- return createI18nMessageOp(job.allocateXrefId(), context.i18nBlock, context.message, messagePlaceholder ?? null, formattedParams, formattedPostprocessingParams, needsPostprocessing);
12606
+ let formattedParams = formatParams(context.params);
12607
+ const formattedPostprocessingParams = formatParams(context.postprocessingParams);
12608
+ let needsPostprocessing = formattedPostprocessingParams.size > 0;
12609
+ for (const values of context.params.values()) {
12610
+ if (values.length > 1) {
12611
+ needsPostprocessing = true;
12612
+ }
12613
+ }
12614
+ return createI18nMessageOp(job.allocateXrefId(), context.xref, context.i18nBlock, context.message, messagePlaceholder ?? null, formattedParams, formattedPostprocessingParams, needsPostprocessing);
12403
12615
  }
12404
12616
  /**
12405
12617
  * Formats a map of `I18nParamValue[]` values into a map of `Expression` values.
12406
- * @return A tuple of the formatted params and a boolean indicating whether postprocessing is needed
12407
- * for any of the params
12408
12618
  */
12409
12619
  function formatParams(params) {
12410
12620
  const formattedParams = new Map();
12411
- let needsPostprocessing = false;
12412
12621
  for (const [placeholder, placeholderValues] of params) {
12413
- const [serializedValues, paramNeedsPostprocessing] = formatParamValues(placeholderValues);
12414
- needsPostprocessing ||= paramNeedsPostprocessing;
12622
+ const serializedValues = formatParamValues(placeholderValues);
12415
12623
  if (serializedValues !== null) {
12416
12624
  formattedParams.set(placeholder, literal(serializedValues));
12417
12625
  }
12418
12626
  }
12419
- return [formattedParams, needsPostprocessing];
12627
+ return formattedParams;
12420
12628
  }
12421
12629
  /**
12422
12630
  * Formats an `I18nParamValue[]` into a string (or null for empty array).
12423
- * @return A tuple of the formatted value and a boolean indicating whether postprocessing is needed
12424
- * for the value
12425
12631
  */
12426
12632
  function formatParamValues(values) {
12427
12633
  if (values.length === 0) {
12428
- return [null, false];
12634
+ return null;
12429
12635
  }
12430
- collapseElementTemplatePairs(values);
12431
12636
  const serializedValues = values.map(value => formatValue(value));
12432
12637
  return serializedValues.length === 1 ?
12433
- [serializedValues[0], false] :
12434
- [`${LIST_START_MARKER}${serializedValues.join(LIST_DELIMITER)}${LIST_END_MARKER}`, true];
12435
- }
12436
- /**
12437
- * Collapses element/template pairs that refer to the same subTemplateIndex, i.e. elements and
12438
- * templates that refer to the same element instance.
12439
- *
12440
- * This accounts for the case of a structural directive inside an i18n block, e.g.:
12441
- * ```
12442
- * <div i18n>
12443
- * <div *ngIf="condition">
12444
- * </div>
12445
- * ```
12446
- *
12447
- * In this case, both the element start and template start placeholders are the same,
12448
- * and we collapse them down into a single compound placeholder value. Rather than produce
12449
- * `[\uFFFD#1:1\uFFFD|\uFFFD*2:1\uFFFD]`, we want to produce `\uFFFD#1:1\uFFFD\uFFFD*2:1\uFFFD`,
12450
- * likewise for the closing of the element/template.
12451
- */
12452
- function collapseElementTemplatePairs(values) {
12453
- // Record the indicies of element and template values in the values array by subTemplateIndex.
12454
- const valueIndiciesBySubTemplateIndex = new Map();
12455
- for (let i = 0; i < values.length; i++) {
12456
- const value = values[i];
12457
- if (value.subTemplateIndex !== null &&
12458
- (value.flags & (I18nParamValueFlags.ElementTag | I18nParamValueFlags.TemplateTag))) {
12459
- const valueIndicies = valueIndiciesBySubTemplateIndex.get(value.subTemplateIndex) ?? [];
12460
- valueIndicies.push(i);
12461
- valueIndiciesBySubTemplateIndex.set(value.subTemplateIndex, valueIndicies);
12462
- }
12463
- }
12464
- // For each subTemplateIndex, check if any values can be collapsed.
12465
- for (const [subTemplateIndex, valueIndicies] of valueIndiciesBySubTemplateIndex) {
12466
- if (valueIndicies.length > 1) {
12467
- const elementIndex = valueIndicies.find(index => values[index].flags & I18nParamValueFlags.ElementTag);
12468
- const templateIndex = valueIndicies.find(index => values[index].flags & I18nParamValueFlags.TemplateTag);
12469
- // If the values list contains both an element and template value, we can collapse.
12470
- if (elementIndex !== undefined && templateIndex !== undefined) {
12471
- const elementValue = values[elementIndex];
12472
- const templateValue = values[templateIndex];
12473
- // To match the TemplateDefinitionBuilder output, flip the order depending on whether the
12474
- // values represent a closing or opening tag (or both).
12475
- // TODO(mmalerba): Figure out if this makes a difference in terms of either functionality,
12476
- // or the resulting message ID. If not, we can remove the special-casing in the future.
12477
- let compundValue;
12478
- if ((elementValue.flags & I18nParamValueFlags.OpenTag) &&
12479
- (elementValue.flags & I18nParamValueFlags.CloseTag)) {
12480
- // TODO(mmalerba): Is this a TDB bug? I don't understand why it would put the template
12481
- // value twice.
12482
- compundValue = `${formatValue(templateValue)}${formatValue(elementValue)}${formatValue(templateValue)}`;
12483
- }
12484
- else if (elementValue.flags & I18nParamValueFlags.OpenTag) {
12485
- compundValue = `${formatValue(templateValue)}${formatValue(elementValue)}`;
12486
- }
12487
- else {
12488
- compundValue = `${formatValue(elementValue)}${formatValue(templateValue)}`;
12489
- }
12490
- // Replace the element value with the combined value.
12491
- values.splice(elementIndex, 1, { value: compundValue, subTemplateIndex, flags: I18nParamValueFlags.None });
12492
- // Replace the template value with null to preserve the indicies we calculated earlier.
12493
- values.splice(templateIndex, 1, null);
12494
- }
12495
- }
12496
- }
12497
- // Strip out any nulled out values we introduced above.
12498
- for (let i = values.length - 1; i >= 0; i--) {
12499
- if (values[i] === null) {
12500
- values.splice(i, 1);
12501
- }
12502
- }
12638
+ serializedValues[0] :
12639
+ `${LIST_START_MARKER}${serializedValues.join(LIST_DELIMITER)}${LIST_END_MARKER}`;
12503
12640
  }
12504
12641
  /**
12505
12642
  * Formats a single `I18nParamValue` into a string
12506
12643
  */
12507
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
+ }
12508
12681
  // If there are no special flags, just return the raw value.
12509
12682
  if (value.flags === I18nParamValueFlags.None) {
12510
12683
  return `${value.value}`;
12511
12684
  }
12685
+ // Encode the remaining flags as part of the value.
12512
12686
  let tagMarker = '';
12513
12687
  let closeMarker = '';
12514
12688
  if (value.flags & I18nParamValueFlags.ElementTag) {
@@ -12521,12 +12695,7 @@ function formatValue(value) {
12521
12695
  closeMarker = value.flags & I18nParamValueFlags.CloseTag ? TAG_CLOSE_MARKER : '';
12522
12696
  }
12523
12697
  const context = value.subTemplateIndex === null ? '' : `${CONTEXT_MARKER}${value.subTemplateIndex}`;
12524
- // Self-closing tags use a special form that concatenates the start and close tag values.
12525
- if ((value.flags & I18nParamValueFlags.OpenTag) &&
12526
- (value.flags & I18nParamValueFlags.CloseTag)) {
12527
- return `${ESCAPE}${tagMarker}${value.value}${context}${ESCAPE}${ESCAPE}${closeMarker}${tagMarker}${value.value}${context}${ESCAPE}`;
12528
- }
12529
- return `${ESCAPE}${closeMarker}${tagMarker}${value.value}${context}${ESCAPE}`;
12698
+ return `${ESCAPE$1}${closeMarker}${tagMarker}${value.value}${context}${ESCAPE$1}`;
12530
12699
  }
12531
12700
 
12532
12701
  /**
@@ -12560,7 +12729,7 @@ function generateAdvance(job) {
12560
12729
  else if (!slotMap.has(op.target)) {
12561
12730
  // We expect ops that _do_ depend on the slot counter to point at declarations that exist in
12562
12731
  // the `slotMap`.
12563
- 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}`);
12564
12733
  }
12565
12734
  const slot = slotMap.get(op.target);
12566
12735
  // Does the slot counter need to be adjusted?
@@ -19770,35 +19939,132 @@ const NG_I18N_CLOSURE_MODE$1 = 'ngI18nClosureMode';
19770
19939
  * considers variables like `I18N_0` as constants and throws an error when their value changes.
19771
19940
  */
19772
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';
19773
19948
  /**
19774
19949
  * Lifts i18n properties into the consts array.
19775
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?
19776
19953
  */
19777
19954
  function collectI18nConsts(job) {
19778
19955
  const fileBasedI18nSuffix = job.relativeContextFilePath.replace(/[^A-Za-z0-9]/g, '_').toUpperCase() + '_';
19779
- const messageConstIndices = new Map();
19780
- // 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)
19781
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();
19782
19995
  for (const unit of job.units) {
19783
19996
  for (const op of unit.create) {
19784
19997
  if (op.kind === OpKind.I18nMessage) {
19785
- 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
+ }
19786
20019
  OpList.remove(op);
19787
20020
  }
19788
20021
  }
19789
20022
  }
19790
- // Serialize the extracted messages for root i18n blocks into the const array.
19791
- for (const op of messages.values()) {
19792
- if (op.kind === OpKind.I18nMessage && op.messagePlaceholder === null) {
19793
- const { mainVar, statements } = collectMessage(job, fileBasedI18nSuffix, messages, op);
19794
- 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
+ }
19795
20057
  }
19796
20058
  }
19797
- // 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.
19798
20060
  for (const unit of job.units) {
19799
20061
  for (const op of unit.create) {
19800
20062
  if (op.kind === OpKind.I18nStart) {
19801
- 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;
19802
20068
  }
19803
20069
  }
19804
20070
  }
@@ -19808,18 +20074,23 @@ function collectI18nConsts(job) {
19808
20074
  * This will recursively collect any sub-messages referenced from the parent message as well.
19809
20075
  */
19810
20076
  function collectMessage(job, fileBasedI18nSuffix, messages, messageOp) {
19811
- // 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.
19812
20081
  const statements = [];
20082
+ const subMessagePlaceholders = new Map();
19813
20083
  for (const subMessageId of messageOp.subMessages) {
19814
20084
  const subMessage = messages.get(subMessageId);
19815
20085
  const { mainVar: subMessageVar, statements: subMessageStatements } = collectMessage(job, fileBasedI18nSuffix, messages, subMessage);
19816
20086
  statements.push(...subMessageStatements);
19817
- messageOp.params.set(subMessage.messagePlaceholder, subMessageVar);
20087
+ const subMessages = subMessagePlaceholders.get(subMessage.messagePlaceholder) ?? [];
20088
+ subMessages.push(subMessageVar);
20089
+ subMessagePlaceholders.set(subMessage.messagePlaceholder, subMessages);
19818
20090
  }
20091
+ addSubMessageParams(messageOp, subMessagePlaceholders);
19819
20092
  // Sort the params for consistency with TemaplateDefinitionBuilder output.
19820
20093
  messageOp.params = new Map([...messageOp.params.entries()].sort());
19821
- // Check that the message has all of its parameters filled out.
19822
- assertAllParamsResolved(messageOp);
19823
20094
  const mainVar = variable(job.pool.uniqueName(TRANSLATION_VAR_PREFIX));
19824
20095
  // Closure Compiler requires const names to start with `MSG_` but disallows any other
19825
20096
  // const to start with `MSG_`. We define a variable starting with `MSG_` just for the
@@ -19830,10 +20101,11 @@ function collectMessage(job, fileBasedI18nSuffix, messages, messageOp) {
19830
20101
  // set in post-processing.
19831
20102
  if (messageOp.needsPostprocessing) {
19832
20103
  // Sort the post-processing params for consistency with TemaplateDefinitionBuilder output.
19833
- messageOp.postprocessingParams = new Map([...messageOp.postprocessingParams.entries()].sort());
20104
+ const postprocessingParams = Object.fromEntries([...messageOp.postprocessingParams.entries()].sort());
20105
+ const formattedPostprocessingParams = formatI18nPlaceholderNamesInMap(postprocessingParams, /* useCamelCase */ false);
19834
20106
  const extraTransformFnParams = [];
19835
20107
  if (messageOp.postprocessingParams.size > 0) {
19836
- extraTransformFnParams.push(literalMap([...messageOp.postprocessingParams].map(([key, value]) => ({ key, value, quoted: true }))));
20108
+ extraTransformFnParams.push(mapLiteral(formattedPostprocessingParams, /* quoted */ true));
19837
20109
  }
19838
20110
  transformFn = (expr) => importExpr(Identifiers.i18nPostprocess).callFn([expr, ...extraTransformFnParams]);
19839
20111
  }
@@ -19841,6 +20113,26 @@ function collectMessage(job, fileBasedI18nSuffix, messages, messageOp) {
19841
20113
  statements.push(...getTranslationDeclStmts$1(messageOp.message, mainVar, closureVar, messageOp.params, transformFn));
19842
20114
  return { mainVar, statements };
19843
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
+ }
19844
20136
  /**
19845
20137
  * Generate statements that define a given translation message.
19846
20138
  *
@@ -19863,7 +20155,8 @@ function collectMessage(job, fileBasedI18nSuffix, messages, messageOp) {
19863
20155
  * @param closureVar The variable for Closure `goog.getMsg` calls, e.g. `MSG_EXTERNAL_XXX`.
19864
20156
  * @param params Object mapping placeholder names to their values (e.g.
19865
20157
  * `{ "interpolation": "\uFFFD0\uFFFD" }`).
19866
- * @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.
19867
20160
  * post-processing).
19868
20161
  * @returns An array of statements that defined a given translation.
19869
20162
  */
@@ -19908,28 +20201,13 @@ function i18nGenerateClosureVar(pool, messageId, fileBasedI18nSuffix, useExterna
19908
20201
  }
19909
20202
  return variable(name);
19910
20203
  }
19911
- /**
19912
- * Asserts that all of the message's placeholders have values.
19913
- */
19914
- function assertAllParamsResolved(op) {
19915
- for (let placeholder in op.message.placeholders) {
19916
- placeholder = placeholder.trimEnd();
19917
- if (!op.params.has(placeholder) && !op.postprocessingParams.has(placeholder)) {
19918
- throw Error(`Failed to resolve i18n placeholder: ${placeholder}`);
19919
- }
19920
- }
19921
- for (let placeholder in op.message.placeholderToMessage) {
19922
- placeholder = placeholder.trimEnd();
19923
- if (!op.params.has(placeholder) && !op.postprocessingParams.has(placeholder)) {
19924
- throw Error(`Failed to resolve i18n message placeholder: ${placeholder}`);
19925
- }
19926
- }
19927
- }
19928
20204
 
19929
20205
  /**
19930
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.
19931
20209
  */
19932
- function extractI18nText(job) {
20210
+ function convertI18nText(job) {
19933
20211
  for (const unit of job.units) {
19934
20212
  // Remove all text nodes within i18n blocks, their content is already captured in the i18n
19935
20213
  // message.
@@ -19984,7 +20262,7 @@ function extractI18nText(job) {
19984
20262
  const expr = op.interpolation.expressions[i];
19985
20263
  // For now, this i18nExpression depends on the slot context of the enclosing i18n block.
19986
20264
  // Later, we will modify this, and advance to a different point.
19987
- 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));
19988
20266
  }
19989
20267
  OpList.replaceWithMany(op, ops);
19990
20268
  break;
@@ -20522,14 +20800,14 @@ function parseExtractedStyles(job) {
20522
20800
  if (op.name === 'style') {
20523
20801
  const parsedStyles = parse(op.expression.value);
20524
20802
  for (let i = 0; i < parsedStyles.length - 1; i += 2) {
20525
- 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);
20526
20804
  }
20527
20805
  OpList.remove(op);
20528
20806
  }
20529
20807
  else if (op.name === 'class') {
20530
20808
  const parsedClasses = op.expression.value.trim().split(/\s+/g);
20531
20809
  for (const parsedClass of parsedClasses) {
20532
- OpList.insertBefore(createExtractedAttributeOp(op.target, BindingKind.ClassName, parsedClass, null), op);
20810
+ OpList.insertBefore(createExtractedAttributeOp(op.target, BindingKind.ClassName, parsedClass, null, null), op);
20533
20811
  }
20534
20812
  OpList.remove(op);
20535
20813
  }
@@ -20545,18 +20823,30 @@ function parseExtractedStyles(job) {
20545
20823
  function removeContentSelectors(job) {
20546
20824
  for (const unit of job.units) {
20547
20825
  const elements = createOpXrefMap(unit);
20548
- for (const op of unit.update) {
20826
+ for (const op of unit.ops()) {
20549
20827
  switch (op.kind) {
20550
20828
  case OpKind.Binding:
20551
20829
  const target = lookupInXrefMap(elements, op.target);
20552
- if (op.name.toLowerCase() === 'select' && target.kind === OpKind.Projection) {
20830
+ if (isSelectAttribute(op.name) && target.kind === OpKind.Projection) {
20553
20831
  OpList.remove(op);
20554
20832
  }
20555
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;
20556
20843
  }
20557
20844
  }
20558
20845
  }
20559
20846
  }
20847
+ function isSelectAttribute(name) {
20848
+ return name.toLowerCase() === 'select';
20849
+ }
20560
20850
  /**
20561
20851
  * Looks up an element in the given map by xref ID.
20562
20852
  */
@@ -20676,6 +20966,10 @@ function propagateI18nBlocksToTemplates(unit, subTemplateIndex) {
20676
20966
  i18nBlock = op;
20677
20967
  break;
20678
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
+ }
20679
20973
  i18nBlock = null;
20680
20974
  break;
20681
20975
  case OpKind.Template:
@@ -21017,6 +21311,10 @@ function i18n(slot, constIndex, subTemplateIndex) {
21017
21311
  function i18nEnd() {
21018
21312
  return call(Identifiers.i18nEnd, [], null);
21019
21313
  }
21314
+ function i18nAttributes(slot, i18nAttributesConfig) {
21315
+ const args = [literal(slot), literal(i18nAttributesConfig)];
21316
+ return call(Identifiers.i18nAttributes, args, null);
21317
+ }
21020
21318
  function property(name, expression, sanitizer, sourceSpan) {
21021
21319
  const args = [literal(name), expression];
21022
21320
  if (sanitizer !== null) {
@@ -21403,6 +21701,12 @@ function reifyCreateOperations(unit, ops) {
21403
21701
  case OpKind.I18n:
21404
21702
  OpList.replace(op, i18n(op.handle.slot, op.messageIndex, op.subTemplateIndex));
21405
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;
21406
21710
  case OpKind.Template:
21407
21711
  if (!(unit instanceof ViewCompilationUnit)) {
21408
21712
  throw new Error(`AssertionError: must be compiling a component`);
@@ -21744,6 +22048,31 @@ function removeI18nContexts(job) {
21744
22048
  }
21745
22049
  }
21746
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
+
21747
22076
  /**
21748
22077
  * Inside the body of a repeater, certain context variables (such as `$first`) are ambiently
21749
22078
  * available. This phase finds those variable usages, and replaces them with the appropriate
@@ -21875,10 +22204,14 @@ function resolveI18nElementPlaceholders(job) {
21875
22204
  }
21876
22205
  resolvePlaceholdersForView(job, job.root, i18nContexts, elements);
21877
22206
  }
21878
- function resolvePlaceholdersForView(job, unit, i18nContexts, elements) {
22207
+ /**
22208
+ * Recursively resolves element and template tag placeholders in the given view.
22209
+ */
22210
+ function resolvePlaceholdersForView(job, unit, i18nContexts, elements, pendingStructuralDirective) {
21879
22211
  // Track the current i18n op and corresponding i18n context op as we step through the creation
21880
22212
  // IR.
21881
22213
  let currentOps = null;
22214
+ let pendingStructuralDirectiveCloses = new Map();
21882
22215
  for (const op of unit.create) {
21883
22216
  switch (op.kind) {
21884
22217
  case OpKind.I18nStart:
@@ -21897,14 +22230,14 @@ function resolvePlaceholdersForView(job, unit, i18nContexts, elements) {
21897
22230
  if (currentOps === null) {
21898
22231
  throw Error('i18n tag placeholder should only occur inside an i18n block');
21899
22232
  }
21900
- const { startName, closeName } = op.i18nPlaceholder;
21901
- let flags = I18nParamValueFlags.ElementTag | I18nParamValueFlags.OpenTag;
21902
- // For self-closing tags, there is no close tag placeholder. Instead, the start tag
21903
- // placeholder accounts for the start and close of the element.
21904
- if (closeName === '') {
21905
- flags |= I18nParamValueFlags.CloseTag;
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);
21906
22238
  }
21907
- addParam(currentOps.i18nContext.params, startName, op.handle.slot, currentOps.i18nBlock.subTemplateIndex, flags);
22239
+ // Clear out the pending structural directive now that its been accounted for.
22240
+ pendingStructuralDirective = undefined;
21908
22241
  }
21909
22242
  break;
21910
22243
  case OpKind.ElementEnd:
@@ -21913,42 +22246,134 @@ function resolvePlaceholdersForView(job, unit, i18nContexts, elements) {
21913
22246
  const startOp = elements.get(op.xref);
21914
22247
  if (startOp && startOp.i18nPlaceholder !== undefined) {
21915
22248
  if (currentOps === null) {
21916
- throw Error('i18n tag placeholder should only occur inside an i18n block');
21917
- }
21918
- const { closeName } = startOp.i18nPlaceholder;
21919
- // Self-closing tags don't have a closing tag placeholder.
21920
- if (closeName !== '') {
21921
- addParam(currentOps.i18nContext.params, closeName, startOp.handle.slot, currentOps.i18nBlock.subTemplateIndex, I18nParamValueFlags.ElementTag | I18nParamValueFlags.CloseTag);
22249
+ throw Error('AssertionError: i18n tag placeholder should only occur inside an i18n block');
21922
22250
  }
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);
21923
22254
  }
21924
22255
  break;
21925
- case OpKind.Template:
21926
- // For templates with i18n placeholders, record its slot value in the params map under the
21927
- // corresponding template start and close placeholders.
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.
21928
22259
  if (op.i18nPlaceholder !== undefined) {
21929
22260
  if (currentOps === null) {
21930
22261
  throw Error('i18n tag placeholder should only occur inside an i18n block');
21931
22262
  }
21932
- let startFlags = I18nParamValueFlags.TemplateTag | I18nParamValueFlags.OpenTag;
21933
- const subTemplateIndex = getSubTemplateIndexForTemplateTag(job, currentOps.i18nBlock, op);
21934
- const { startName, closeName } = op.i18nPlaceholder;
21935
- const isSelfClosing = closeName === '';
21936
- if (isSelfClosing) {
21937
- startFlags |= I18nParamValueFlags.CloseTag;
21938
- }
21939
- addParam(currentOps.i18nContext.params, startName, op.handle.slot, subTemplateIndex, startFlags);
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.
21940
22273
  resolvePlaceholdersForView(job, job.views.get(op.xref), i18nContexts, elements);
21941
- if (!isSelfClosing) {
21942
- addParam(currentOps.i18nContext.params, closeName, op.handle.slot, subTemplateIndex, I18nParamValueFlags.TemplateTag | I18nParamValueFlags.CloseTag);
21943
- }
21944
22274
  }
21945
22275
  else {
21946
- resolvePlaceholdersForView(job, job.views.get(op.xref), i18nContexts, elements);
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
+ }
21947
22294
  }
21948
22295
  break;
21949
22296
  }
21950
22297
  }
21951
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);
22374
+ }
22375
+ }
22376
+ }
21952
22377
  /**
21953
22378
  * Get the subTemplateIndex for the given template op. For template ops, use the subTemplateIndex of
21954
22379
  * the child i18n block inside the template.
@@ -21961,7 +22386,9 @@ function getSubTemplateIndexForTemplateTag(job, i18nOp, op) {
21961
22386
  }
21962
22387
  return i18nOp.subTemplateIndex;
21963
22388
  }
21964
- /** Add a param value to the given params map. */
22389
+ /**
22390
+ * Add a param value to the given params map.
22391
+ */
21965
22392
  function addParam(params, placeholder, value, subTemplateIndex, flags) {
21966
22393
  const values = params.get(placeholder) ?? [];
21967
22394
  values.push({ value, subTemplateIndex, flags });
@@ -21987,14 +22414,19 @@ function resolveI18nExpressionPlaceholders(job) {
21987
22414
  }
21988
22415
  }
21989
22416
  }
21990
- // Keep track of the next available expression index per i18n block.
22417
+ // Keep track of the next available expression index for each i18n message.
21991
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;
21992
22424
  for (const unit of job.units) {
21993
22425
  for (const op of unit.update) {
21994
22426
  if (op.kind === OpKind.I18nExpression) {
21995
22427
  const i18nContext = i18nContexts.get(op.context);
21996
- const index = expressionIndices.get(op.target) || 0;
21997
- const subTemplateIndex = subTemplateIndicies.get(op.target);
22428
+ const index = expressionIndices.get(referenceIndex(op)) || 0;
22429
+ const subTemplateIndex = subTemplateIndicies.get(op.i18nOwner) ?? null;
21998
22430
  // Add the expression index in the appropriate params map.
21999
22431
  const params = op.resolutionTime === I18nParamResolutionTime.Creation ?
22000
22432
  i18nContext.params :
@@ -22006,7 +22438,7 @@ function resolveI18nExpressionPlaceholders(job) {
22006
22438
  flags: I18nParamValueFlags.ExpressionIndex
22007
22439
  });
22008
22440
  params.set(op.i18nPlaceholder, values);
22009
- expressionIndices.set(op.target, index + 1);
22441
+ expressionIndices.set(referenceIndex(op), index + 1);
22010
22442
  }
22011
22443
  }
22012
22444
  }
@@ -23153,11 +23585,11 @@ const phases = [
23153
23585
  { kind: CompilationJobKind.Tmpl, fn: removeContentSelectors },
23154
23586
  { kind: CompilationJobKind.Host, fn: parseHostStyleProperties },
23155
23587
  { kind: CompilationJobKind.Tmpl, fn: emitNamespaceChanges },
23156
- { kind: CompilationJobKind.Both, fn: specializeStyleBindings },
23157
- { kind: CompilationJobKind.Both, fn: specializeBindings },
23158
23588
  { kind: CompilationJobKind.Tmpl, fn: propagateI18nBlocks },
23159
23589
  { kind: CompilationJobKind.Tmpl, fn: wrapI18nIcus },
23160
23590
  { kind: CompilationJobKind.Tmpl, fn: createI18nContexts },
23591
+ { kind: CompilationJobKind.Both, fn: specializeStyleBindings },
23592
+ { kind: CompilationJobKind.Both, fn: specializeBindings },
23161
23593
  { kind: CompilationJobKind.Both, fn: extractAttributes },
23162
23594
  { kind: CompilationJobKind.Both, fn: parseExtractedStyles },
23163
23595
  { kind: CompilationJobKind.Tmpl, fn: removeEmptyBindings },
@@ -23166,7 +23598,10 @@ const phases = [
23166
23598
  { kind: CompilationJobKind.Tmpl, fn: generateConditionalExpressions },
23167
23599
  { kind: CompilationJobKind.Tmpl, fn: createPipes },
23168
23600
  { kind: CompilationJobKind.Tmpl, fn: configureDeferInstructions },
23169
- { 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 },
23170
23605
  { kind: CompilationJobKind.Tmpl, fn: applyI18nExpressions },
23171
23606
  { kind: CompilationJobKind.Tmpl, fn: createVariadicPipes },
23172
23607
  { kind: CompilationJobKind.Both, fn: generatePureLiteralStructures },
@@ -23196,7 +23631,6 @@ const phases = [
23196
23631
  { kind: CompilationJobKind.Tmpl, fn: collectI18nConsts },
23197
23632
  { kind: CompilationJobKind.Tmpl, fn: collectConstExpressions },
23198
23633
  { kind: CompilationJobKind.Both, fn: collectElementConsts },
23199
- { kind: CompilationJobKind.Tmpl, fn: assignI18nSlotDependencies },
23200
23634
  { kind: CompilationJobKind.Tmpl, fn: removeI18nContexts },
23201
23635
  { kind: CompilationJobKind.Both, fn: countVariables },
23202
23636
  { kind: CompilationJobKind.Tmpl, fn: generateAdvance },
@@ -23351,7 +23785,7 @@ function ingestHostProperty(job, property, isTextAttribute) {
23351
23785
  let expression;
23352
23786
  const ast = property.expression.ast;
23353
23787
  if (ast instanceof Interpolation$1) {
23354
- 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)), []);
23355
23789
  }
23356
23790
  else {
23357
23791
  expression = convertAst(ast, job, property.sourceSpan);
@@ -23366,10 +23800,11 @@ function ingestHostProperty(job, property, isTextAttribute) {
23366
23800
  bindingKind = BindingKind.Animation;
23367
23801
  }
23368
23802
  job.root.update.push(createBindingOp(job.root.xref, bindingKind, property.name, expression, null, SecurityContext
23369
- .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));
23370
23804
  }
23371
23805
  function ingestHostAttribute(job, name, value) {
23372
23806
  const attrBinding = createBindingOp(job.root.xref, BindingKind.Attribute, name, value, null, SecurityContext.NONE, true, false,
23807
+ /* TODO */ null,
23373
23808
  /* TODO: host attribute source spans */ null);
23374
23809
  job.root.update.push(attrBinding);
23375
23810
  }
@@ -23467,10 +23902,11 @@ function ingestTemplate(unit, tmpl) {
23467
23902
  const functionNameSuffix = tagNameWithoutNamespace === null ?
23468
23903
  '' :
23469
23904
  prefixWithNamespace(tagNameWithoutNamespace, namespace);
23470
- const tplOp = createTemplateOp(childView.xref, tagNameWithoutNamespace, functionNameSuffix, namespace, i18nPlaceholder, tmpl.startSourceSpan);
23471
- unit.create.push(tplOp);
23472
- ingestBindings(unit, tplOp, tmpl);
23473
- 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);
23474
23910
  ingestNodes(childView, tmpl.children);
23475
23911
  for (const { name, value } of tmpl.variables) {
23476
23912
  childView.contextVariables.set(name, value !== '' ? value : '$implicit');
@@ -23478,7 +23914,7 @@ function ingestTemplate(unit, tmpl) {
23478
23914
  // If this is a plain template and there is an i18n message associated with it, insert i18n start
23479
23915
  // and end ops. For structural directive templates, the i18n ops will be added when ingesting the
23480
23916
  // element/template the directive is placed on.
23481
- if (isPlainTemplate(tmpl) && tmpl.i18n instanceof Message) {
23917
+ if (templateKind === TemplateKind.NgTemplate && tmpl.i18n instanceof Message) {
23482
23918
  const id = unit.job.allocateXrefId();
23483
23919
  OpList.insertAfter(createI18nStartOp(id, tmpl.i18n), childView.create.head);
23484
23920
  OpList.insertBefore(createI18nEndOp(id), childView.create.tail);
@@ -23488,9 +23924,13 @@ function ingestTemplate(unit, tmpl) {
23488
23924
  * Ingest a literal text node from the AST into the given `ViewCompilation`.
23489
23925
  */
23490
23926
  function ingestContent(unit, content) {
23491
- 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);
23492
23932
  for (const attr of content.attributes) {
23493
- 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);
23494
23934
  }
23495
23935
  unit.create.push(op);
23496
23936
  }
@@ -23515,6 +23955,7 @@ function ingestBoundText(unit, text, i18nPlaceholders) {
23515
23955
  throw Error(`Unhandled i18n metadata type for text interpolation: ${text.i18n?.constructor.name}`);
23516
23956
  }
23517
23957
  if (i18nPlaceholders === undefined) {
23958
+ // TODO: We probably can just use the placeholders field, instead of walking the AST.
23518
23959
  i18nPlaceholders = text.i18n instanceof Container ?
23519
23960
  text.i18n.children
23520
23961
  .filter((node) => node instanceof Placeholder)
@@ -23530,7 +23971,7 @@ function ingestBoundText(unit, text, i18nPlaceholders) {
23530
23971
  // interpolation. We copy that behavior in compatibility mode.
23531
23972
  // TODO: is it actually correct to generate these extra maps in modern mode?
23532
23973
  const baseSourceSpan = unit.job.compatibility ? null : text.sourceSpan;
23533
- 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));
23534
23975
  }
23535
23976
  /**
23536
23977
  * Ingest an `@if` block into the given `ViewCompilation`.
@@ -23551,14 +23992,21 @@ function ingestIfBlock(unit, ifBlock) {
23551
23992
  if (ifCase.expressionAlias !== null) {
23552
23993
  cView.contextVariables.set(ifCase.expressionAlias.name, CTX_REF);
23553
23994
  }
23554
- const tmplOp = createTemplateOp(cView.xref, tagName, 'Conditional', Namespace.HTML, undefined /* TODO: figure out how i18n works with new control flow */, ifCase.sourceSpan);
23555
- 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);
23556
24004
  if (firstXref === null) {
23557
24005
  firstXref = cView.xref;
23558
- firstSlotHandle = tmplOp.handle;
24006
+ firstSlotHandle = templateOp.handle;
23559
24007
  }
23560
24008
  const caseExpr = ifCase.expression ? convertAst(ifCase.expression, unit.job, null) : null;
23561
- const conditionalCaseExpr = new ConditionalCaseExpr(caseExpr, tmplOp.xref, tmplOp.handle, ifCase.expressionAlias);
24009
+ const conditionalCaseExpr = new ConditionalCaseExpr(caseExpr, templateOp.xref, templateOp.handle, ifCase.expressionAlias);
23562
24010
  conditions.push(conditionalCaseExpr);
23563
24011
  ingestNodes(cView, ifCase.children);
23564
24012
  }
@@ -23574,16 +24022,23 @@ function ingestSwitchBlock(unit, switchBlock) {
23574
24022
  let conditions = [];
23575
24023
  for (const switchCase of switchBlock.cases) {
23576
24024
  const cView = unit.job.allocateView(unit.xref);
23577
- const tmplOp = createTemplateOp(cView.xref, null, 'Case', Namespace.HTML, undefined /* TODO: figure out how i18n works with new control flow */, switchCase.sourceSpan);
23578
- 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);
23579
24034
  if (firstXref === null) {
23580
24035
  firstXref = cView.xref;
23581
- firstSlotHandle = tmplOp.handle;
24036
+ firstSlotHandle = templateOp.handle;
23582
24037
  }
23583
24038
  const caseExpr = switchCase.expression ?
23584
24039
  convertAst(switchCase.expression, unit.job, switchBlock.startSourceSpan) :
23585
24040
  null;
23586
- const conditionalCaseExpr = new ConditionalCaseExpr(caseExpr, tmplOp.xref, tmplOp.handle);
24041
+ const conditionalCaseExpr = new ConditionalCaseExpr(caseExpr, templateOp.xref, templateOp.handle);
23587
24042
  conditions.push(conditionalCaseExpr);
23588
24043
  ingestNodes(cView, switchCase.children);
23589
24044
  }
@@ -23596,7 +24051,7 @@ function ingestDeferView(unit, suffix, children, sourceSpan) {
23596
24051
  }
23597
24052
  const secondaryView = unit.job.allocateView(unit.xref);
23598
24053
  ingestNodes(secondaryView, children);
23599
- 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);
23600
24055
  unit.create.push(templateOp);
23601
24056
  return templateOp;
23602
24057
  }
@@ -23691,12 +24146,7 @@ function ingestIcu(unit, icu) {
23691
24146
  const xref = unit.job.allocateXrefId();
23692
24147
  const icuNode = icu.i18n.nodes[0];
23693
24148
  unit.create.push(createIcuStartOp(xref, icu.i18n, icuFromI18nMessage(icu.i18n).name, null));
23694
- const expressionPlaceholder = icuNode.expressionPlaceholder?.trimEnd();
23695
- if (expressionPlaceholder === undefined || icu.vars[expressionPlaceholder] === undefined) {
23696
- throw Error('ICU should have a text binding');
23697
- }
23698
- ingestBoundText(unit, icu.vars[expressionPlaceholder], [expressionPlaceholder]);
23699
- for (const [placeholder, text] of Object.entries(icu.placeholders)) {
24149
+ for (const [placeholder, text] of Object.entries({ ...icu.vars, ...icu.placeholders })) {
23700
24150
  if (text instanceof BoundText) {
23701
24151
  ingestBoundText(unit, text, [placeholder]);
23702
24152
  }
@@ -23792,6 +24242,16 @@ function convertAst(ast, job, baseSourceSpan) {
23792
24242
  else if (ast instanceof LiteralPrimitive) {
23793
24243
  return literal(ast.value, undefined, convertSourceSpan(ast.span, baseSourceSpan));
23794
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
+ }
23795
24255
  else if (ast instanceof Binary) {
23796
24256
  const operator = BINARY_OPERATORS.get(ast.operation);
23797
24257
  if (operator === undefined) {
@@ -23850,6 +24310,9 @@ function convertAst(ast, job, baseSourceSpan) {
23850
24310
  else if (ast instanceof EmptyExpr$1) {
23851
24311
  return new EmptyExpr(convertSourceSpan(ast.span, baseSourceSpan));
23852
24312
  }
24313
+ else if (ast instanceof PrefixNot) {
24314
+ return not(convertAst(ast.expression, job, baseSourceSpan), convertSourceSpan(ast.span, baseSourceSpan));
24315
+ }
23853
24316
  else {
23854
24317
  throw new Error(`Unhandled expression type "${ast.constructor.name}" in file "${baseSourceSpan?.start.file.url}"`);
23855
24318
  }
@@ -23880,6 +24343,7 @@ function isPlainTemplate(tmpl) {
23880
24343
  */
23881
24344
  function ingestBindings(unit, op, element) {
23882
24345
  let flags = BindingFlags.None;
24346
+ let hasI18nAttributes = false;
23883
24347
  if (element instanceof Template) {
23884
24348
  flags |= BindingFlags.OnNgTemplateElement;
23885
24349
  if (element instanceof Template && isPlainTemplate(element)) {
@@ -23888,10 +24352,12 @@ function ingestBindings(unit, op, element) {
23888
24352
  const templateAttrFlags = flags | BindingFlags.BindingTargetsTemplate | BindingFlags.IsStructuralTemplateAttribute;
23889
24353
  for (const attr of element.templateAttrs) {
23890
24354
  if (attr instanceof TextAttribute) {
23891
- 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;
23892
24357
  }
23893
24358
  else {
23894
- 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;
23895
24361
  }
23896
24362
  }
23897
24363
  }
@@ -23899,10 +24365,12 @@ function ingestBindings(unit, op, element) {
23899
24365
  // This is only attribute TextLiteral bindings, such as `attr.foo="bar"`. This can never be
23900
24366
  // `[attr.foo]="bar"` or `attr.foo="{{bar}}"`, both of which will be handled as inputs with
23901
24367
  // `BindingType.Attribute`.
23902
- 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;
23903
24370
  }
23904
24371
  for (const input of element.inputs) {
23905
- 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;
23906
24374
  }
23907
24375
  for (const output of element.outputs) {
23908
24376
  let listenerOp;
@@ -23912,7 +24380,7 @@ function ingestBindings(unit, op, element) {
23912
24380
  }
23913
24381
  }
23914
24382
  if (element instanceof Template && !isPlainTemplate(element)) {
23915
- unit.create.push(createExtractedAttributeOp(op.xref, BindingKind.Property, output.name, null));
24383
+ unit.create.push(createExtractedAttributeOp(op.xref, BindingKind.Property, output.name, null, null));
23916
24384
  continue;
23917
24385
  }
23918
24386
  listenerOp = createListenerOp(op.xref, op.handle, output.name, op.tag, output.phase, false, output.sourceSpan);
@@ -23941,6 +24409,10 @@ function ingestBindings(unit, op, element) {
23941
24409
  listenerOp.handlerOps.push(createStatementOp(new ReturnStatement(returnExpr, returnExpr.sourceSpan)));
23942
24410
  unit.create.push(listenerOp);
23943
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
+ }
23944
24416
  }
23945
24417
  const BINDING_KINDS = new Map([
23946
24418
  [0 /* e.BindingType.Property */, BindingKind.Property],
@@ -23969,22 +24441,34 @@ var BindingFlags;
23969
24441
  */
23970
24442
  BindingFlags[BindingFlags["OnNgTemplateElement"] = 8] = "OnNgTemplateElement";
23971
24443
  })(BindingFlags || (BindingFlags = {}));
23972
- function ingestBinding(view, xref, name, value, type, unit, securityContext, sourceSpan, flags) {
24444
+ function ingestBinding(view, xref, name, value, type, unit, securityContext, sourceSpan, flags, i18nMeta) {
23973
24445
  if (value instanceof ASTWithSource) {
23974
24446
  value = value.ast;
23975
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
+ }
23976
24456
  if (flags & BindingFlags.OnNgTemplateElement && !(flags & BindingFlags.BindingTargetsTemplate) &&
23977
24457
  type === 0 /* e.BindingType.Property */) {
23978
24458
  // This binding only exists for later const extraction, and is not an actual binding to be
23979
24459
  // created.
23980
- view.create.push(createExtractedAttributeOp(xref, BindingKind.Property, name, null));
24460
+ view.create.push(createExtractedAttributeOp(xref, BindingKind.Property, name, null, i18nContext));
23981
24461
  return;
23982
24462
  }
23983
24463
  let expression;
23984
24464
  // TODO: We could easily generate source maps for subexpressions in these cases, but
23985
24465
  // TemplateDefinitionBuilder does not. Should we do so?
23986
24466
  if (value instanceof Interpolation$1) {
23987
- 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);
23988
24472
  }
23989
24473
  else if (value instanceof AST) {
23990
24474
  expression = convertAst(value, view.job, null);
@@ -23993,7 +24477,7 @@ function ingestBinding(view, xref, name, value, type, unit, securityContext, sou
23993
24477
  expression = value;
23994
24478
  }
23995
24479
  const kind = BINDING_KINDS.get(type);
23996
- 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));
23997
24481
  }
23998
24482
  /**
23999
24483
  * Process all of the local references on an element-like structure in the template AST and
@@ -24081,7 +24565,7 @@ function ingestControlFlowInsertionPoint(unit, xref, node) {
24081
24565
  // and they can be used in directive matching (in the case of `Template.templateAttrs`).
24082
24566
  if (root !== null) {
24083
24567
  for (const attr of root.attributes) {
24084
- 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);
24085
24569
  }
24086
24570
  const tagName = root instanceof Element$1 ? root.name : root.tagName;
24087
24571
  // Don't pass along `ng-template` tag name since it enables directive matching.
@@ -27164,7 +27648,7 @@ class TemplateDefinitionBuilder {
27164
27648
  else {
27165
27649
  // ... otherwise we need to activate post-processing
27166
27650
  // to replace ICU placeholders with proper values
27167
- const placeholder = wrapI18nPlaceholder(`${I18N_ICU_MAPPING_PREFIX}${key}`);
27651
+ const placeholder = wrapI18nPlaceholder(`${I18N_ICU_MAPPING_PREFIX$1}${key}`);
27168
27652
  params[key] = literal(placeholder);
27169
27653
  icuMapping[key] = literalArr(refs);
27170
27654
  }
@@ -28784,24 +29268,6 @@ class TrackByBindingScope extends BindingScope {
28784
29268
  return this.componentAccessCount;
28785
29269
  }
28786
29270
  }
28787
- /**
28788
- * Creates a `CssSelector` given a tag name and a map of attributes
28789
- */
28790
- function createCssSelector(elementName, attributes) {
28791
- const cssSelector = new CssSelector();
28792
- const elementNameNoNs = splitNsName(elementName)[1];
28793
- cssSelector.setElement(elementNameNoNs);
28794
- Object.getOwnPropertyNames(attributes).forEach((name) => {
28795
- const nameNoNs = splitNsName(name)[1];
28796
- const value = attributes[name];
28797
- cssSelector.addAttribute(nameNoNs, value);
28798
- if (name.toLowerCase() === 'class') {
28799
- const classes = value.trim().split(/\s+/);
28800
- classes.forEach(className => cssSelector.addClassName(className));
28801
- }
28802
- });
28803
- return cssSelector;
28804
- }
28805
29271
  /**
28806
29272
  * Creates an array of expressions out of an `ngProjectAs` attributes
28807
29273
  * which can be added to the instruction parameters.
@@ -29542,6 +30008,8 @@ function createHostBindingsFunction(hostBindingsMetadata, typeSourceSpan, bindin
29542
30008
  }
29543
30009
  return emitHostBindingFunction(hostJob);
29544
30010
  }
30011
+ let bindingId = 0;
30012
+ const getNextBindingId = () => `${bindingId++}`;
29545
30013
  const bindingContext = variable(CONTEXT_NAME);
29546
30014
  const styleBuilder = new StylingBuilder(bindingContext);
29547
30015
  const { styleAttr, classAttr } = hostBindingsMetadata.specialAttributes;
@@ -29594,7 +30062,7 @@ function createHostBindingsFunction(hostBindingsMetadata, typeSourceSpan, bindin
29594
30062
  for (const binding of allOtherBindings) {
29595
30063
  // resolve literal arrays and literal objects
29596
30064
  const value = binding.expression.visit(getValueConverter());
29597
- const bindingExpr = bindingFn(bindingContext, value);
30065
+ const bindingExpr = bindingFn(bindingContext, value, getNextBindingId);
29598
30066
  const { bindingName, instruction, isAttribute } = getBindingNameAndInstruction(binding);
29599
30067
  const securityContexts = bindingParser.calcPossibleSecurityContexts(selector, bindingName, isAttribute)
29600
30068
  .filter(context => context !== SecurityContext.NONE);
@@ -29673,10 +30141,12 @@ function createHostBindingsFunction(hostBindingsMetadata, typeSourceSpan, bindin
29673
30141
  // at the top of this method when all the input bindings were counted.
29674
30142
  totalHostVarsCount +=
29675
30143
  Math.max(call.allocateBindingSlots - MIN_STYLING_BINDING_SLOTS_REQUIRED, 0);
30144
+ const { params, stmts } = convertStylingCall(call, bindingContext, bindingFn, getNextBindingId);
30145
+ updateVariables.push(...stmts);
29676
30146
  updateInstructions.push({
29677
30147
  reference: instruction.reference,
29678
- paramsOrFn: convertStylingCall(call, bindingContext, bindingFn),
29679
- span: null
30148
+ paramsOrFn: params,
30149
+ span: null,
29680
30150
  });
29681
30151
  }
29682
30152
  });
@@ -29697,11 +30167,19 @@ function createHostBindingsFunction(hostBindingsMetadata, typeSourceSpan, bindin
29697
30167
  }
29698
30168
  return null;
29699
30169
  }
29700
- function bindingFn(implicit, value) {
29701
- return convertPropertyBinding(null, implicit, value, 'b');
30170
+ function bindingFn(implicit, value, getNextBindingIdFn) {
30171
+ return convertPropertyBinding(null, implicit, value, getNextBindingIdFn());
29702
30172
  }
29703
- function convertStylingCall(call, bindingContext, bindingFn) {
29704
- 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 };
29705
30183
  }
29706
30184
  function getBindingNameAndInstruction(binding) {
29707
30185
  let bindingName = binding.name;
@@ -30140,15 +30618,15 @@ class DirectiveBinder {
30140
30618
  template.forEach(node => node.visit(this));
30141
30619
  }
30142
30620
  visitElement(element) {
30143
- this.visitElementOrTemplate(element.name, element);
30621
+ this.visitElementOrTemplate(element);
30144
30622
  }
30145
30623
  visitTemplate(template) {
30146
- this.visitElementOrTemplate('ng-template', template);
30624
+ this.visitElementOrTemplate(template);
30147
30625
  }
30148
- visitElementOrTemplate(elementName, node) {
30626
+ visitElementOrTemplate(node) {
30149
30627
  // First, determine the HTML shape of the node for the purpose of directive matching.
30150
30628
  // Do this by building up a `CssSelector` for the node.
30151
- const cssSelector = createCssSelector(elementName, getAttrsForDirectiveMatching(node));
30629
+ const cssSelector = createCssSelectorFromNode(node);
30152
30630
  // Next, use the `SelectorMatcher` to get the list of directives on the node.
30153
30631
  const directives = [];
30154
30632
  this.matcher.match(cssSelector, (_selector, results) => directives.push(...results));
@@ -31277,7 +31755,7 @@ function publishFacade(global) {
31277
31755
  * @description
31278
31756
  * Entry point for all public APIs of the compiler package.
31279
31757
  */
31280
- const VERSION = new Version('17.0.5');
31758
+ const VERSION = new Version('17.0.6');
31281
31759
 
31282
31760
  class CompilerConfig {
31283
31761
  constructor({ defaultEncapsulation = ViewEncapsulation.Emulated, preserveWhitespaces, strictInjectionParameters } = {}) {
@@ -32843,7 +33321,7 @@ const MINIMUM_PARTIAL_LINKER_VERSION$6 = '12.0.0';
32843
33321
  function compileDeclareClassMetadata(metadata) {
32844
33322
  const definitionMap = new DefinitionMap();
32845
33323
  definitionMap.set('minVersion', literal(MINIMUM_PARTIAL_LINKER_VERSION$6));
32846
- definitionMap.set('version', literal('17.0.5'));
33324
+ definitionMap.set('version', literal('17.0.6'));
32847
33325
  definitionMap.set('ngImport', importExpr(Identifiers.core));
32848
33326
  definitionMap.set('type', metadata.type);
32849
33327
  definitionMap.set('decorators', metadata.decorators);
@@ -32951,7 +33429,7 @@ function createDirectiveDefinitionMap(meta) {
32951
33429
  // in 16.1 is actually used.
32952
33430
  const minVersion = hasTransformFunctions ? MINIMUM_PARTIAL_LINKER_VERSION$5 : '14.0.0';
32953
33431
  definitionMap.set('minVersion', literal(minVersion));
32954
- definitionMap.set('version', literal('17.0.5'));
33432
+ definitionMap.set('version', literal('17.0.6'));
32955
33433
  // e.g. `type: MyDirective`
32956
33434
  definitionMap.set('type', meta.type.value);
32957
33435
  if (meta.isStandalone) {
@@ -33228,7 +33706,7 @@ const MINIMUM_PARTIAL_LINKER_VERSION$4 = '12.0.0';
33228
33706
  function compileDeclareFactoryFunction(meta) {
33229
33707
  const definitionMap = new DefinitionMap();
33230
33708
  definitionMap.set('minVersion', literal(MINIMUM_PARTIAL_LINKER_VERSION$4));
33231
- definitionMap.set('version', literal('17.0.5'));
33709
+ definitionMap.set('version', literal('17.0.6'));
33232
33710
  definitionMap.set('ngImport', importExpr(Identifiers.core));
33233
33711
  definitionMap.set('type', meta.type.value);
33234
33712
  definitionMap.set('deps', compileDependencies(meta.deps));
@@ -33263,7 +33741,7 @@ function compileDeclareInjectableFromMetadata(meta) {
33263
33741
  function createInjectableDefinitionMap(meta) {
33264
33742
  const definitionMap = new DefinitionMap();
33265
33743
  definitionMap.set('minVersion', literal(MINIMUM_PARTIAL_LINKER_VERSION$3));
33266
- definitionMap.set('version', literal('17.0.5'));
33744
+ definitionMap.set('version', literal('17.0.6'));
33267
33745
  definitionMap.set('ngImport', importExpr(Identifiers.core));
33268
33746
  definitionMap.set('type', meta.type.value);
33269
33747
  // Only generate providedIn property if it has a non-null value
@@ -33314,7 +33792,7 @@ function compileDeclareInjectorFromMetadata(meta) {
33314
33792
  function createInjectorDefinitionMap(meta) {
33315
33793
  const definitionMap = new DefinitionMap();
33316
33794
  definitionMap.set('minVersion', literal(MINIMUM_PARTIAL_LINKER_VERSION$2));
33317
- definitionMap.set('version', literal('17.0.5'));
33795
+ definitionMap.set('version', literal('17.0.6'));
33318
33796
  definitionMap.set('ngImport', importExpr(Identifiers.core));
33319
33797
  definitionMap.set('type', meta.type.value);
33320
33798
  definitionMap.set('providers', meta.providers);
@@ -33347,7 +33825,7 @@ function createNgModuleDefinitionMap(meta) {
33347
33825
  throw new Error('Invalid path! Local compilation mode should not get into the partial compilation path');
33348
33826
  }
33349
33827
  definitionMap.set('minVersion', literal(MINIMUM_PARTIAL_LINKER_VERSION$1));
33350
- definitionMap.set('version', literal('17.0.5'));
33828
+ definitionMap.set('version', literal('17.0.6'));
33351
33829
  definitionMap.set('ngImport', importExpr(Identifiers.core));
33352
33830
  definitionMap.set('type', meta.type.value);
33353
33831
  // We only generate the keys in the metadata if the arrays contain values.
@@ -33398,7 +33876,7 @@ function compileDeclarePipeFromMetadata(meta) {
33398
33876
  function createPipeDefinitionMap(meta) {
33399
33877
  const definitionMap = new DefinitionMap();
33400
33878
  definitionMap.set('minVersion', literal(MINIMUM_PARTIAL_LINKER_VERSION));
33401
- definitionMap.set('version', literal('17.0.5'));
33879
+ definitionMap.set('version', literal('17.0.6'));
33402
33880
  definitionMap.set('ngImport', importExpr(Identifiers.core));
33403
33881
  // e.g. `type: MyPipe`
33404
33882
  definitionMap.set('type', meta.type.value);
@@ -33431,5 +33909,5 @@ publishFacade(_global);
33431
33909
 
33432
33910
  // This file is not used to build this module. It is only used during editing
33433
33911
 
33434
- 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 };
33435
33913
  //# sourceMappingURL=compiler.mjs.map