@angular/compiler 17.0.2 → 17.0.4

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 (61) hide show
  1. package/esm2022/src/compiler.mjs +2 -3
  2. package/esm2022/src/expression_parser/parser.mjs +2 -2
  3. package/esm2022/src/i18n/digest.mjs +4 -1
  4. package/esm2022/src/i18n/extractor_merger.mjs +4 -2
  5. package/esm2022/src/i18n/i18n_ast.mjs +27 -1
  6. package/esm2022/src/i18n/i18n_html_parser.mjs +2 -2
  7. package/esm2022/src/i18n/i18n_parser.mjs +23 -6
  8. package/esm2022/src/i18n/message_bundle.mjs +7 -1
  9. package/esm2022/src/i18n/serializers/placeholder.mjs +29 -1
  10. package/esm2022/src/i18n/serializers/serializer.mjs +6 -1
  11. package/esm2022/src/i18n/serializers/xliff.mjs +7 -1
  12. package/esm2022/src/i18n/serializers/xliff2.mjs +19 -1
  13. package/esm2022/src/i18n/serializers/xmb.mjs +12 -1
  14. package/esm2022/src/i18n/translation_bundle.mjs +6 -1
  15. package/esm2022/src/jit_compiler_facade.mjs +2 -2
  16. package/esm2022/src/ml_parser/ast.mjs +4 -4
  17. package/esm2022/src/ml_parser/defaults.mjs +24 -0
  18. package/esm2022/src/ml_parser/lexer.mjs +2 -2
  19. package/esm2022/src/render3/partial/class_metadata.mjs +1 -1
  20. package/esm2022/src/render3/partial/component.mjs +2 -2
  21. package/esm2022/src/render3/partial/directive.mjs +1 -1
  22. package/esm2022/src/render3/partial/factory.mjs +1 -1
  23. package/esm2022/src/render3/partial/injectable.mjs +1 -1
  24. package/esm2022/src/render3/partial/injector.mjs +1 -1
  25. package/esm2022/src/render3/partial/ng_module.mjs +1 -1
  26. package/esm2022/src/render3/partial/pipe.mjs +1 -1
  27. package/esm2022/src/render3/r3_ast.mjs +17 -9
  28. package/esm2022/src/render3/r3_control_flow.mjs +7 -7
  29. package/esm2022/src/render3/r3_deferred_blocks.mjs +5 -5
  30. package/esm2022/src/render3/view/api.mjs +1 -1
  31. package/esm2022/src/render3/view/i18n/context.mjs +13 -1
  32. package/esm2022/src/render3/view/i18n/get_msg_utils.mjs +4 -1
  33. package/esm2022/src/render3/view/i18n/icu_serializer.mjs +4 -1
  34. package/esm2022/src/render3/view/i18n/localize_utils.mjs +6 -1
  35. package/esm2022/src/render3/view/i18n/meta.mjs +5 -4
  36. package/esm2022/src/render3/view/template.mjs +82 -34
  37. package/esm2022/src/template/pipeline/ir/src/enums.mjs +7 -3
  38. package/esm2022/src/template/pipeline/ir/src/expression.mjs +3 -3
  39. package/esm2022/src/template/pipeline/ir/src/ops/create.mjs +14 -5
  40. package/esm2022/src/template/pipeline/ir/src/ops/update.mjs +2 -12
  41. package/esm2022/src/template/pipeline/src/emit.mjs +3 -3
  42. package/esm2022/src/template/pipeline/src/ingest.mjs +28 -7
  43. package/esm2022/src/template/pipeline/src/instruction.mjs +3 -3
  44. package/esm2022/src/template/pipeline/src/phases/apply_i18n_expressions.mjs +15 -5
  45. package/esm2022/src/template/pipeline/src/phases/create_i18n_contexts.mjs +2 -2
  46. package/esm2022/src/template/pipeline/src/phases/extract_i18n_messages.mjs +23 -14
  47. package/esm2022/src/template/pipeline/src/phases/i18n_const_collection.mjs +9 -3
  48. package/esm2022/src/template/pipeline/src/phases/i18n_text_extraction.mjs +18 -3
  49. package/esm2022/src/template/pipeline/src/phases/reify.mjs +2 -2
  50. package/esm2022/src/template/pipeline/src/phases/resolve_i18n_element_placeholders.mjs +2 -2
  51. package/esm2022/src/template/pipeline/src/phases/resolve_i18n_expression_placeholders.mjs +9 -5
  52. package/esm2022/src/template/pipeline/src/phases/resolve_i18n_icu_placeholders.mjs +78 -0
  53. package/esm2022/src/template/pipeline/src/phases/wrap_icus.mjs +11 -5
  54. package/esm2022/src/template_parser/binding_parser.mjs +1 -1
  55. package/esm2022/src/version.mjs +1 -1
  56. package/fesm2022/compiler.mjs +479 -174
  57. package/fesm2022/compiler.mjs.map +1 -1
  58. package/index.d.ts +36 -12
  59. package/package.json +2 -2
  60. package/esm2022/src/ml_parser/interpolation_config.mjs +0 -23
  61. package/esm2022/src/template/pipeline/src/phases/create_i18n_icu_expressions.mjs +0 -52
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @license Angular v17.0.2
2
+ * @license Angular v17.0.4
3
3
  * (c) 2010-2022 Google LLC. https://angular.io/
4
4
  * License: MIT
5
5
  */
@@ -539,6 +539,9 @@ class _SerializerVisitor {
539
539
  visitIcuPlaceholder(ph, context) {
540
540
  return `<ph icu name="${ph.name}">${ph.value.visit(this)}</ph>`;
541
541
  }
542
+ visitBlockPlaceholder(ph, context) {
543
+ return `<ph block name="${ph.startName}">${ph.children.map(child => child.visit(this)).join(', ')}</ph name="${ph.closeName}">`;
544
+ }
542
545
  }
543
546
  const serializerVisitor$1 = new _SerializerVisitor();
544
547
  function serializeNodes(nodes) {
@@ -3847,43 +3850,47 @@ class BlockNode {
3847
3850
  }
3848
3851
  }
3849
3852
  class DeferredBlockPlaceholder extends BlockNode {
3850
- constructor(children, minimumTime, nameSpan, sourceSpan, startSourceSpan, endSourceSpan) {
3853
+ constructor(children, minimumTime, nameSpan, sourceSpan, startSourceSpan, endSourceSpan, i18n) {
3851
3854
  super(nameSpan, sourceSpan, startSourceSpan, endSourceSpan);
3852
3855
  this.children = children;
3853
3856
  this.minimumTime = minimumTime;
3857
+ this.i18n = i18n;
3854
3858
  }
3855
3859
  visit(visitor) {
3856
3860
  return visitor.visitDeferredBlockPlaceholder(this);
3857
3861
  }
3858
3862
  }
3859
3863
  class DeferredBlockLoading extends BlockNode {
3860
- constructor(children, afterTime, minimumTime, nameSpan, sourceSpan, startSourceSpan, endSourceSpan) {
3864
+ constructor(children, afterTime, minimumTime, nameSpan, sourceSpan, startSourceSpan, endSourceSpan, i18n) {
3861
3865
  super(nameSpan, sourceSpan, startSourceSpan, endSourceSpan);
3862
3866
  this.children = children;
3863
3867
  this.afterTime = afterTime;
3864
3868
  this.minimumTime = minimumTime;
3869
+ this.i18n = i18n;
3865
3870
  }
3866
3871
  visit(visitor) {
3867
3872
  return visitor.visitDeferredBlockLoading(this);
3868
3873
  }
3869
3874
  }
3870
3875
  class DeferredBlockError extends BlockNode {
3871
- constructor(children, nameSpan, sourceSpan, startSourceSpan, endSourceSpan) {
3876
+ constructor(children, nameSpan, sourceSpan, startSourceSpan, endSourceSpan, i18n) {
3872
3877
  super(nameSpan, sourceSpan, startSourceSpan, endSourceSpan);
3873
3878
  this.children = children;
3879
+ this.i18n = i18n;
3874
3880
  }
3875
3881
  visit(visitor) {
3876
3882
  return visitor.visitDeferredBlockError(this);
3877
3883
  }
3878
3884
  }
3879
3885
  class DeferredBlock extends BlockNode {
3880
- constructor(children, triggers, prefetchTriggers, placeholder, loading, error, nameSpan, sourceSpan, mainBlockSpan, startSourceSpan, endSourceSpan) {
3886
+ constructor(children, triggers, prefetchTriggers, placeholder, loading, error, nameSpan, sourceSpan, mainBlockSpan, startSourceSpan, endSourceSpan, i18n) {
3881
3887
  super(nameSpan, sourceSpan, startSourceSpan, endSourceSpan);
3882
3888
  this.children = children;
3883
3889
  this.placeholder = placeholder;
3884
3890
  this.loading = loading;
3885
3891
  this.error = error;
3886
3892
  this.mainBlockSpan = mainBlockSpan;
3893
+ this.i18n = i18n;
3887
3894
  this.triggers = triggers;
3888
3895
  this.prefetchTriggers = prefetchTriggers;
3889
3896
  // We cache the keys since we know that they won't change and we
@@ -3922,17 +3929,18 @@ class SwitchBlock extends BlockNode {
3922
3929
  }
3923
3930
  }
3924
3931
  class SwitchBlockCase extends BlockNode {
3925
- constructor(expression, children, sourceSpan, startSourceSpan, endSourceSpan, nameSpan) {
3932
+ constructor(expression, children, sourceSpan, startSourceSpan, endSourceSpan, nameSpan, i18n) {
3926
3933
  super(nameSpan, sourceSpan, startSourceSpan, endSourceSpan);
3927
3934
  this.expression = expression;
3928
3935
  this.children = children;
3936
+ this.i18n = i18n;
3929
3937
  }
3930
3938
  visit(visitor) {
3931
3939
  return visitor.visitSwitchBlockCase(this);
3932
3940
  }
3933
3941
  }
3934
3942
  class ForLoopBlock extends BlockNode {
3935
- constructor(item, expression, trackBy, trackKeywordSpan, contextVariables, children, empty, sourceSpan, mainBlockSpan, startSourceSpan, endSourceSpan, nameSpan) {
3943
+ constructor(item, expression, trackBy, trackKeywordSpan, contextVariables, children, empty, sourceSpan, mainBlockSpan, startSourceSpan, endSourceSpan, nameSpan, i18n) {
3936
3944
  super(nameSpan, sourceSpan, startSourceSpan, endSourceSpan);
3937
3945
  this.item = item;
3938
3946
  this.expression = expression;
@@ -3942,15 +3950,17 @@ class ForLoopBlock extends BlockNode {
3942
3950
  this.children = children;
3943
3951
  this.empty = empty;
3944
3952
  this.mainBlockSpan = mainBlockSpan;
3953
+ this.i18n = i18n;
3945
3954
  }
3946
3955
  visit(visitor) {
3947
3956
  return visitor.visitForLoopBlock(this);
3948
3957
  }
3949
3958
  }
3950
3959
  class ForLoopBlockEmpty extends BlockNode {
3951
- constructor(children, sourceSpan, startSourceSpan, endSourceSpan, nameSpan) {
3960
+ constructor(children, sourceSpan, startSourceSpan, endSourceSpan, nameSpan, i18n) {
3952
3961
  super(nameSpan, sourceSpan, startSourceSpan, endSourceSpan);
3953
3962
  this.children = children;
3963
+ this.i18n = i18n;
3954
3964
  }
3955
3965
  visit(visitor) {
3956
3966
  return visitor.visitForLoopBlockEmpty(this);
@@ -3966,11 +3976,12 @@ class IfBlock extends BlockNode {
3966
3976
  }
3967
3977
  }
3968
3978
  class IfBlockBranch extends BlockNode {
3969
- constructor(expression, children, expressionAlias, sourceSpan, startSourceSpan, endSourceSpan, nameSpan) {
3979
+ constructor(expression, children, expressionAlias, sourceSpan, startSourceSpan, endSourceSpan, nameSpan, i18n) {
3970
3980
  super(nameSpan, sourceSpan, startSourceSpan, endSourceSpan);
3971
3981
  this.expression = expression;
3972
3982
  this.children = children;
3973
3983
  this.expressionAlias = expressionAlias;
3984
+ this.i18n = i18n;
3974
3985
  }
3975
3986
  visit(visitor) {
3976
3987
  return visitor.visitIfBlockBranch(this);
@@ -4240,6 +4251,21 @@ class IcuPlaceholder {
4240
4251
  return visitor.visitIcuPlaceholder(this, context);
4241
4252
  }
4242
4253
  }
4254
+ class BlockPlaceholder {
4255
+ constructor(name, parameters, startName, closeName, children, sourceSpan, startSourceSpan, endSourceSpan) {
4256
+ this.name = name;
4257
+ this.parameters = parameters;
4258
+ this.startName = startName;
4259
+ this.closeName = closeName;
4260
+ this.children = children;
4261
+ this.sourceSpan = sourceSpan;
4262
+ this.startSourceSpan = startSourceSpan;
4263
+ this.endSourceSpan = endSourceSpan;
4264
+ }
4265
+ visit(visitor, context) {
4266
+ return visitor.visitBlockPlaceholder(this, context);
4267
+ }
4268
+ }
4243
4269
  // Clone the AST
4244
4270
  class CloneVisitor {
4245
4271
  visitText(text, context) {
@@ -4265,6 +4291,10 @@ class CloneVisitor {
4265
4291
  visitIcuPlaceholder(ph, context) {
4266
4292
  return new IcuPlaceholder(ph.value, ph.name, ph.sourceSpan);
4267
4293
  }
4294
+ visitBlockPlaceholder(ph, context) {
4295
+ const children = ph.children.map(n => n.visit(this, context));
4296
+ return new BlockPlaceholder(ph.name, ph.parameters, ph.startName, ph.closeName, children, ph.sourceSpan, ph.startSourceSpan, ph.endSourceSpan);
4297
+ }
4268
4298
  }
4269
4299
  // Visit all the nodes recursively
4270
4300
  class RecurseVisitor {
@@ -4282,6 +4312,9 @@ class RecurseVisitor {
4282
4312
  }
4283
4313
  visitPlaceholder(ph, context) { }
4284
4314
  visitIcuPlaceholder(ph, context) { }
4315
+ visitBlockPlaceholder(ph, context) {
4316
+ ph.children.forEach(child => child.visit(this));
4317
+ }
4285
4318
  }
4286
4319
  /**
4287
4320
  * Serialize the message to the Localize backtick string format that would appear in compiled code.
@@ -4312,6 +4345,10 @@ class LocalizeMessageStringVisitor {
4312
4345
  visitIcuPlaceholder(ph) {
4313
4346
  return `{$${ph.name}}`;
4314
4347
  }
4348
+ visitBlockPlaceholder(ph) {
4349
+ const children = ph.children.map(child => child.visit(this)).join('');
4350
+ return `{$${ph.startName}}${children}{$${ph.closeName}}`;
4351
+ }
4315
4352
  }
4316
4353
 
4317
4354
  class Serializer {
@@ -4354,6 +4391,11 @@ class SimplePlaceholderMapper extends RecurseVisitor {
4354
4391
  visitPlaceholder(ph, context) {
4355
4392
  this.visitPlaceholderName(ph.name);
4356
4393
  }
4394
+ visitBlockPlaceholder(ph, context) {
4395
+ this.visitPlaceholderName(ph.startName);
4396
+ super.visitBlockPlaceholder(ph, context);
4397
+ this.visitPlaceholderName(ph.closeName);
4398
+ }
4357
4399
  visitIcuPlaceholder(ph, context) {
4358
4400
  this.visitPlaceholderName(ph.name);
4359
4401
  }
@@ -4566,6 +4608,17 @@ class _Visitor$1 {
4566
4608
  new Tag(_PLACEHOLDER_TAG$3, { name: ph.name }, [exTag, interpolationAsText])
4567
4609
  ];
4568
4610
  }
4611
+ visitBlockPlaceholder(ph, context) {
4612
+ const startAsText = new Text$1(`@${ph.name}`);
4613
+ const startEx = new Tag(_EXAMPLE_TAG, {}, [startAsText]);
4614
+ // TC requires PH to have a non empty EX, and uses the text node to show the "original" value.
4615
+ const startTagPh = new Tag(_PLACEHOLDER_TAG$3, { name: ph.startName }, [startEx, startAsText]);
4616
+ const closeAsText = new Text$1(`}`);
4617
+ const closeEx = new Tag(_EXAMPLE_TAG, {}, [closeAsText]);
4618
+ // TC requires PH to have a non empty EX, and uses the text node to show the "original" value.
4619
+ const closeTagPh = new Tag(_PLACEHOLDER_TAG$3, { name: ph.closeName }, [closeEx, closeAsText]);
4620
+ return [startTagPh, ...this.serialize(ph.children), closeTagPh];
4621
+ }
4569
4622
  visitIcuPlaceholder(ph, context) {
4570
4623
  const icuExpression = ph.value.expression;
4571
4624
  const icuType = ph.value.type;
@@ -5211,6 +5264,7 @@ class InterpolationConfig {
5211
5264
  }
5212
5265
  }
5213
5266
  const DEFAULT_INTERPOLATION_CONFIG = new InterpolationConfig('{{', '}}');
5267
+ const DEFAULT_CONTAINER_BLOCKS = new Set(['switch']);
5214
5268
 
5215
5269
  const $EOF = 0;
5216
5270
  const $BSPACE = 8;
@@ -8852,11 +8906,11 @@ var OpKind;
8852
8906
  /**
8853
8907
  * An instruction to create an ICU expression.
8854
8908
  */
8855
- OpKind[OpKind["Icu"] = 41] = "Icu";
8909
+ OpKind[OpKind["IcuStart"] = 41] = "IcuStart";
8856
8910
  /**
8857
8911
  * An instruction to update an ICU expression.
8858
8912
  */
8859
- OpKind[OpKind["IcuUpdate"] = 42] = "IcuUpdate";
8913
+ OpKind[OpKind["IcuEnd"] = 42] = "IcuEnd";
8860
8914
  /**
8861
8915
  * An i18n context containing information needed to generate an i18n message.
8862
8916
  */
@@ -9105,6 +9159,10 @@ var I18nParamValueFlags;
9105
9159
  * This value represents the closing of a tag.
9106
9160
  */
9107
9161
  I18nParamValueFlags[I18nParamValueFlags["CloseTag"] = 8] = "CloseTag";
9162
+ /**
9163
+ * This value represents an i18n expression index.
9164
+ */
9165
+ I18nParamValueFlags[I18nParamValueFlags["ExpressionIndex"] = 16] = "ExpressionIndex";
9108
9166
  })(I18nParamValueFlags || (I18nParamValueFlags = {}));
9109
9167
  /**
9110
9168
  * Whether the active namespace is HTML, MathML, or SVG mode.
@@ -9410,6 +9468,7 @@ function createRepeaterOp(repeaterCreate, targetSlot, collection, sourceSpan) {
9410
9468
  collection,
9411
9469
  sourceSpan,
9412
9470
  ...NEW_OP,
9471
+ ...TRAIT_DEPENDS_ON_SLOT_CONTEXT,
9413
9472
  };
9414
9473
  }
9415
9474
  function createDeferWhenOp(target, expr, prefetch, sourceSpan) {
@@ -9453,17 +9512,6 @@ function createI18nApplyOp(target, handle, sourceSpan) {
9453
9512
  ...NEW_OP,
9454
9513
  };
9455
9514
  }
9456
- /**
9457
- * Creates an op to update an ICU expression.
9458
- */
9459
- function createIcuUpdateOp(xref, sourceSpan) {
9460
- return {
9461
- kind: OpKind.IcuUpdate,
9462
- xref,
9463
- sourceSpan,
9464
- ...NEW_OP,
9465
- };
9466
- }
9467
9515
 
9468
9516
  var _a, _b, _c, _d, _e, _f;
9469
9517
  /**
@@ -10254,8 +10302,8 @@ function transformExpressionsInOp(op, transform, flags) {
10254
10302
  case OpKind.I18nContext:
10255
10303
  case OpKind.I18nEnd:
10256
10304
  case OpKind.I18nStart:
10257
- case OpKind.Icu:
10258
- case OpKind.IcuUpdate:
10305
+ case OpKind.IcuEnd:
10306
+ case OpKind.IcuStart:
10259
10307
  case OpKind.Namespace:
10260
10308
  case OpKind.Pipe:
10261
10309
  case OpKind.Projection:
@@ -10908,20 +10956,29 @@ function createI18nEndOp(xref) {
10908
10956
  };
10909
10957
  }
10910
10958
  /**
10911
- * Creates an op to create an ICU expression.
10959
+ * Creates an ICU start op.
10912
10960
  */
10913
- function createIcuOp(xref, message, icu, messagePlaceholder, sourceSpan) {
10961
+ function createIcuStartOp(xref, message, messagePlaceholder, sourceSpan) {
10914
10962
  return {
10915
- kind: OpKind.Icu,
10963
+ kind: OpKind.IcuStart,
10916
10964
  xref,
10917
10965
  message,
10918
- icu,
10919
10966
  messagePlaceholder,
10920
10967
  context: null,
10921
10968
  sourceSpan,
10922
10969
  ...NEW_OP,
10923
10970
  };
10924
10971
  }
10972
+ /**
10973
+ * Creates an ICU end op.
10974
+ */
10975
+ function createIcuEndOp(xref) {
10976
+ return {
10977
+ kind: OpKind.IcuEnd,
10978
+ xref,
10979
+ ...NEW_OP,
10980
+ };
10981
+ }
10925
10982
  function createI18nContextOp(xref, i18nBlock, message, sourceSpan) {
10926
10983
  return {
10927
10984
  kind: OpKind.I18nContext,
@@ -11170,10 +11227,18 @@ function removeAnys(e) {
11170
11227
  * Adds apply operations after i18n expressions.
11171
11228
  */
11172
11229
  function applyI18nExpressions(job) {
11230
+ const i18nContexts = new Map();
11231
+ for (const unit of job.units) {
11232
+ for (const op of unit.create) {
11233
+ if (op.kind === OpKind.I18nContext) {
11234
+ i18nContexts.set(op.xref, op);
11235
+ }
11236
+ }
11237
+ }
11173
11238
  for (const unit of job.units) {
11174
11239
  for (const op of unit.update) {
11175
11240
  // Only add apply after expressions that are not followed by more expressions.
11176
- if (op.kind === OpKind.I18nExpression && needsApplication(op)) {
11241
+ if (op.kind === OpKind.I18nExpression && needsApplication(i18nContexts, op)) {
11177
11242
  // TODO: what should be the source span for the apply op?
11178
11243
  OpList.insertAfter(createI18nApplyOp(op.target, op.handle, null), op);
11179
11244
  }
@@ -11183,13 +11248,15 @@ function applyI18nExpressions(job) {
11183
11248
  /**
11184
11249
  * Checks whether the given expression op needs to be followed with an apply op.
11185
11250
  */
11186
- function needsApplication(op) {
11251
+ function needsApplication(i18nContexts, op) {
11187
11252
  // If the next op is not another expression, we need to apply.
11188
11253
  if (op.next?.kind !== OpKind.I18nExpression) {
11189
11254
  return true;
11190
11255
  }
11191
- // If the next op is an expression targeting a different i18n context, we need to apply.
11192
- if (op.next.context !== op.context) {
11256
+ // If the next op is an expression targeting a different i18n block, we need to apply.
11257
+ const context = i18nContexts.get(op.context);
11258
+ const nextContext = i18nContexts.get(op.next.context);
11259
+ if (context.i18nBlock !== nextContext.i18nBlock) {
11193
11260
  return true;
11194
11261
  }
11195
11262
  return false;
@@ -11850,7 +11917,7 @@ function createI18nContexts(job) {
11850
11917
  case OpKind.I18nEnd:
11851
11918
  currentI18nOp = null;
11852
11919
  break;
11853
- case OpKind.Icu:
11920
+ case OpKind.IcuStart:
11854
11921
  // If an ICU represents a different message than its containing block, we give it its own
11855
11922
  // i18n context.
11856
11923
  if (currentI18nOp === null) {
@@ -11873,50 +11940,6 @@ function createI18nContexts(job) {
11873
11940
  }
11874
11941
  }
11875
11942
 
11876
- /**
11877
- * Replace the ICU update ops with i18n expression ops.
11878
- */
11879
- function createI18nIcuExpressions(job) {
11880
- const icus = new Map();
11881
- const i18nContexts = new Map();
11882
- const i18nBlocks = new Map();
11883
- // Collect maps of ops that need to be referenced to create the I18nExpressionOps.
11884
- for (const unit of job.units) {
11885
- for (const op of unit.create) {
11886
- switch (op.kind) {
11887
- case OpKind.Icu:
11888
- icus.set(op.xref, op);
11889
- break;
11890
- case OpKind.I18nContext:
11891
- i18nContexts.set(op.xref, op);
11892
- break;
11893
- case OpKind.I18nStart:
11894
- i18nBlocks.set(op.xref, op);
11895
- break;
11896
- }
11897
- }
11898
- // Replace each IcuUpdateOp with an I18nExpressionOp.
11899
- for (const op of unit.update) {
11900
- switch (op.kind) {
11901
- case OpKind.IcuUpdate:
11902
- const icuOp = icus.get(op.xref);
11903
- if (icuOp?.icu.expressionPlaceholder === undefined) {
11904
- throw Error('ICU should have an i18n placeholder');
11905
- }
11906
- if (icuOp.context === null) {
11907
- throw Error('ICU should have its i18n context set');
11908
- }
11909
- const i18nContext = i18nContexts.get(icuOp.context);
11910
- const i18nBlock = i18nBlocks.get(i18nContext.i18nBlock);
11911
- OpList.replace(op, createI18nExpressionOp(i18nContext.xref, i18nBlock.xref, i18nBlock.handle, new LexicalReadExpr(icuOp.icu.expression), icuOp.icu.expressionPlaceholder,
11912
- // ICU-based i18n Expressions are resolved during post-processing.
11913
- I18nParamResolutionTime.Postproccessing, null));
11914
- break;
11915
- }
11916
- }
11917
- }
11918
- }
11919
-
11920
11943
  /**
11921
11944
  * Defer instructions take a configuration array, which should be collected into the component
11922
11945
  * consts. This phase finds the config options, and creates the corresponding const array.
@@ -12331,18 +12354,23 @@ function extractI18nMessages(job) {
12331
12354
  // Extract messages from ICUs with their own sub-context.
12332
12355
  for (const unit of job.units) {
12333
12356
  for (const op of unit.create) {
12334
- if (op.kind === OpKind.Icu) {
12335
- if (!op.context) {
12336
- throw Error('ICU op should have its context set.');
12337
- }
12338
- if (!i18nBlockContexts.has(op.context)) {
12339
- const i18nContext = i18nContexts.get(op.context);
12340
- const subMessage = createI18nMessage(job, i18nContext, op.messagePlaceholder);
12341
- unit.create.push(subMessage);
12342
- const parentMessage = i18nBlockMessages.get(i18nContext.i18nBlock);
12343
- parentMessage?.subMessages.push(subMessage.xref);
12344
- }
12345
- OpList.remove(op);
12357
+ switch (op.kind) {
12358
+ case OpKind.IcuStart:
12359
+ if (!op.context) {
12360
+ throw Error('ICU op should have its context set.');
12361
+ }
12362
+ if (!i18nBlockContexts.has(op.context)) {
12363
+ const i18nContext = i18nContexts.get(op.context);
12364
+ const subMessage = createI18nMessage(job, i18nContext, op.messagePlaceholder);
12365
+ unit.create.push(subMessage);
12366
+ const parentMessage = i18nBlockMessages.get(i18nContext.i18nBlock);
12367
+ parentMessage?.subMessages.push(subMessage.xref);
12368
+ }
12369
+ OpList.remove(op);
12370
+ break;
12371
+ case OpKind.IcuEnd:
12372
+ OpList.remove(op);
12373
+ break;
12346
12374
  }
12347
12375
  }
12348
12376
  }
@@ -12364,7 +12392,7 @@ function createI18nMessage(job, context, messagePlaceholder) {
12364
12392
  */
12365
12393
  function formatParams(params) {
12366
12394
  const result = new Map();
12367
- for (const [placeholder, placeholderValues] of [...params].sort()) {
12395
+ for (const [placeholder, placeholderValues] of params) {
12368
12396
  const serializedValues = formatParamValues(placeholderValues);
12369
12397
  if (serializedValues !== null) {
12370
12398
  result.set(placeholder, literal(formatParamValues(placeholderValues)));
@@ -12388,6 +12416,10 @@ function formatParamValues(values) {
12388
12416
  * Formats a single `I18nParamValue` into a string
12389
12417
  */
12390
12418
  function formatValue(value) {
12419
+ // If there are no special flags, just return the raw value.
12420
+ if (value.flags === I18nParamValueFlags.None) {
12421
+ return `${value.value}`;
12422
+ }
12391
12423
  let tagMarker = '';
12392
12424
  let closeMarker = '';
12393
12425
  if (value.flags & I18nParamValueFlags.ElementTag) {
@@ -12746,6 +12778,9 @@ class IcuSerializerVisitor {
12746
12778
  visitPlaceholder(ph) {
12747
12779
  return this.formatPh(ph.name);
12748
12780
  }
12781
+ visitBlockPlaceholder(ph) {
12782
+ return `${this.formatPh(ph.startName)}${ph.children.map(child => child.visit(this)).join('')}${this.formatPh(ph.closeName)}`;
12783
+ }
12749
12784
  visitIcuPlaceholder(ph, context) {
12750
12785
  return this.formatPh(ph.name);
12751
12786
  }
@@ -14469,12 +14504,12 @@ class Comment {
14469
14504
  return visitor.visitComment(this, context);
14470
14505
  }
14471
14506
  }
14472
- class Block {
14473
- constructor(name, parameters, children, sourceSpan, nameSpan, startSourceSpan, endSourceSpan = null) {
14507
+ class Block extends NodeWithI18n {
14508
+ constructor(name, parameters, children, sourceSpan, nameSpan, startSourceSpan, endSourceSpan = null, i18n) {
14509
+ super(sourceSpan, i18n);
14474
14510
  this.name = name;
14475
14511
  this.parameters = parameters;
14476
14512
  this.children = children;
14477
- this.sourceSpan = sourceSpan;
14478
14513
  this.nameSpan = nameSpan;
14479
14514
  this.startSourceSpan = startSourceSpan;
14480
14515
  this.endSourceSpan = endSourceSpan;
@@ -15160,6 +15195,24 @@ class PlaceholderRegistry {
15160
15195
  getUniquePlaceholder(name) {
15161
15196
  return this._generateUniqueName(name.toUpperCase());
15162
15197
  }
15198
+ getStartBlockPlaceholderName(name, parameters) {
15199
+ const signature = this._hashBlock(name, parameters);
15200
+ if (this._signatureToName[signature]) {
15201
+ return this._signatureToName[signature];
15202
+ }
15203
+ const placeholder = this._generateUniqueName(`START_BLOCK_${this._toSnakeCase(name)}`);
15204
+ this._signatureToName[signature] = placeholder;
15205
+ return placeholder;
15206
+ }
15207
+ getCloseBlockPlaceholderName(name) {
15208
+ const signature = this._hashClosingBlock(name);
15209
+ if (this._signatureToName[signature]) {
15210
+ return this._signatureToName[signature];
15211
+ }
15212
+ const placeholder = this._generateUniqueName(`CLOSE_BLOCK_${this._toSnakeCase(name)}`);
15213
+ this._signatureToName[signature] = placeholder;
15214
+ return placeholder;
15215
+ }
15163
15216
  // Generate a hash for a tag - does not take attribute order into account
15164
15217
  _hashTag(tag, attrs, isVoid) {
15165
15218
  const start = `<${tag}`;
@@ -15170,6 +15223,16 @@ class PlaceholderRegistry {
15170
15223
  _hashClosingTag(tag) {
15171
15224
  return this._hashTag(`/${tag}`, {}, false);
15172
15225
  }
15226
+ _hashBlock(name, parameters) {
15227
+ const params = parameters.length === 0 ? '' : ` (${parameters.sort().join('; ')})`;
15228
+ return `@${name}${params} {}`;
15229
+ }
15230
+ _hashClosingBlock(name) {
15231
+ return this._hashBlock(`close_${name}`, []);
15232
+ }
15233
+ _toSnakeCase(name) {
15234
+ return name.toUpperCase().replace(/[^A-Z0-9]/g, '_');
15235
+ }
15173
15236
  _generateUniqueName(base) {
15174
15237
  const seen = this._placeHolderNameCounts.hasOwnProperty(base);
15175
15238
  if (!seen) {
@@ -15186,17 +15249,18 @@ const _expParser = new Parser$1(new Lexer());
15186
15249
  /**
15187
15250
  * Returns a function converting html nodes to an i18n Message given an interpolationConfig
15188
15251
  */
15189
- function createI18nMessageFactory(interpolationConfig) {
15190
- const visitor = new _I18nVisitor(_expParser, interpolationConfig);
15252
+ function createI18nMessageFactory(interpolationConfig, containerBlocks) {
15253
+ const visitor = new _I18nVisitor(_expParser, interpolationConfig, containerBlocks);
15191
15254
  return (nodes, meaning, description, customId, visitNodeFn) => visitor.toI18nMessage(nodes, meaning, description, customId, visitNodeFn);
15192
15255
  }
15193
15256
  function noopVisitNodeFn(_html, i18n) {
15194
15257
  return i18n;
15195
15258
  }
15196
15259
  class _I18nVisitor {
15197
- constructor(_expressionParser, _interpolationConfig) {
15260
+ constructor(_expressionParser, _interpolationConfig, _containerBlocks) {
15198
15261
  this._expressionParser = _expressionParser;
15199
15262
  this._interpolationConfig = _interpolationConfig;
15263
+ this._containerBlocks = _containerBlocks;
15200
15264
  }
15201
15265
  toI18nMessage(nodes, meaning = '', description = '', customId = '', visitNodeFn) {
15202
15266
  const context = {
@@ -15283,10 +15347,26 @@ class _I18nVisitor {
15283
15347
  }
15284
15348
  visitBlock(block, context) {
15285
15349
  const children = visitAll(this, block.children, context);
15286
- const node = new Container(children, block.sourceSpan);
15350
+ if (this._containerBlocks.has(block.name)) {
15351
+ return new Container(children, block.sourceSpan);
15352
+ }
15353
+ const parameters = block.parameters.map(param => param.expression);
15354
+ const startPhName = context.placeholderRegistry.getStartBlockPlaceholderName(block.name, parameters);
15355
+ const closePhName = context.placeholderRegistry.getCloseBlockPlaceholderName(block.name);
15356
+ context.placeholderToContent[startPhName] = {
15357
+ text: block.startSourceSpan.toString(),
15358
+ sourceSpan: block.startSourceSpan,
15359
+ };
15360
+ context.placeholderToContent[closePhName] = {
15361
+ text: block.endSourceSpan ? block.endSourceSpan.toString() : '}',
15362
+ sourceSpan: block.endSourceSpan ?? block.sourceSpan,
15363
+ };
15364
+ const node = new BlockPlaceholder(block.name, parameters, startPhName, closePhName, children, block.sourceSpan, block.startSourceSpan, block.endSourceSpan);
15287
15365
  return context.visitNodeFn(block, node);
15288
15366
  }
15289
- visitBlockParameter(_parameter, _context) { }
15367
+ visitBlockParameter(_parameter, _context) {
15368
+ throw new Error('Unreachable code');
15369
+ }
15290
15370
  /**
15291
15371
  * Convert, text and interpolated tokens up into text and placeholder pieces.
15292
15372
  *
@@ -19155,17 +19235,18 @@ const setI18nRefs = (htmlNode, i18nNode) => {
19155
19235
  * stored with other element's and attribute's information.
19156
19236
  */
19157
19237
  class I18nMetaVisitor {
19158
- constructor(interpolationConfig = DEFAULT_INTERPOLATION_CONFIG, keepI18nAttrs = false, enableI18nLegacyMessageIdFormat = false) {
19238
+ constructor(interpolationConfig = DEFAULT_INTERPOLATION_CONFIG, keepI18nAttrs = false, enableI18nLegacyMessageIdFormat = false, containerBlocks = DEFAULT_CONTAINER_BLOCKS) {
19159
19239
  this.interpolationConfig = interpolationConfig;
19160
19240
  this.keepI18nAttrs = keepI18nAttrs;
19161
19241
  this.enableI18nLegacyMessageIdFormat = enableI18nLegacyMessageIdFormat;
19242
+ this.containerBlocks = containerBlocks;
19162
19243
  // whether visited nodes contain i18n information
19163
19244
  this.hasI18nMeta = false;
19164
19245
  this._errors = [];
19165
19246
  }
19166
19247
  _generateI18nMessage(nodes, meta = '', visitNodeFn) {
19167
19248
  const { meaning, description, customId } = this._parseMetadata(meta);
19168
- const createI18nMessage = createI18nMessageFactory(this.interpolationConfig);
19249
+ const createI18nMessage = createI18nMessageFactory(this.interpolationConfig, this.containerBlocks);
19169
19250
  const message = createI18nMessage(nodes, meaning, description, customId, visitNodeFn);
19170
19251
  this._setMessageId(message, meta);
19171
19252
  this._setLegacyIds(message, meta);
@@ -19466,6 +19547,9 @@ class GetMsgSerializerVisitor {
19466
19547
  visitPlaceholder(ph) {
19467
19548
  return this.formatPh(ph.name);
19468
19549
  }
19550
+ visitBlockPlaceholder(ph) {
19551
+ return `${this.formatPh(ph.startName)}${ph.children.map(child => child.visit(this)).join('')}${this.formatPh(ph.closeName)}`;
19552
+ }
19469
19553
  visitIcuPlaceholder(ph, context) {
19470
19554
  return this.formatPh(ph.name);
19471
19555
  }
@@ -19519,6 +19603,11 @@ class LocalizeSerializerVisitor {
19519
19603
  visitPlaceholder(ph) {
19520
19604
  this.pieces.push(this.createPlaceholderPiece(ph.name, ph.sourceSpan));
19521
19605
  }
19606
+ visitBlockPlaceholder(ph) {
19607
+ this.pieces.push(this.createPlaceholderPiece(ph.startName, ph.startSourceSpan ?? ph.sourceSpan));
19608
+ ph.children.forEach(child => child.visit(this));
19609
+ this.pieces.push(this.createPlaceholderPiece(ph.closeName, ph.endSourceSpan ?? ph.sourceSpan));
19610
+ }
19522
19611
  visitIcuPlaceholder(ph) {
19523
19612
  this.pieces.push(this.createPlaceholderPiece(ph.name, ph.sourceSpan, this.placeholderToMessage[ph.name]));
19524
19613
  }
@@ -19638,6 +19727,8 @@ function collectMessage(job, fileBasedI18nSuffix, messages, messageOp) {
19638
19727
  statements.push(...subMessageStatements);
19639
19728
  messageOp.params.set(subMessage.messagePlaceholder, subMessageVar);
19640
19729
  }
19730
+ // Sort the params for consistency with TemaplateDefinitionBuilder output.
19731
+ messageOp.params = new Map([...messageOp.params.entries()].sort());
19641
19732
  // Check that the message has all of its parameters filled out.
19642
19733
  assertAllParamsResolved(messageOp);
19643
19734
  const mainVar = variable(job.pool.uniqueName(TRANSLATION_VAR_PREFIX));
@@ -19649,6 +19740,8 @@ function collectMessage(job, fileBasedI18nSuffix, messages, messageOp) {
19649
19740
  // If nescessary, add a post-processing step and resolve any placeholder params that are
19650
19741
  // set in post-processing.
19651
19742
  if (messageOp.needsPostprocessing) {
19743
+ // Sort the post-processing params for consistency with TemaplateDefinitionBuilder output.
19744
+ messageOp.postprocessingParams = new Map([...messageOp.postprocessingParams.entries()].sort());
19652
19745
  const extraTransformFnParams = [];
19653
19746
  if (messageOp.postprocessingParams.size > 0) {
19654
19747
  extraTransformFnParams.push(literalMap([...messageOp.postprocessingParams].map(([key, value]) => ({ key, value, quoted: true }))));
@@ -19730,12 +19823,14 @@ function i18nGenerateClosureVar(pool, messageId, fileBasedI18nSuffix, useExterna
19730
19823
  * Asserts that all of the message's placeholders have values.
19731
19824
  */
19732
19825
  function assertAllParamsResolved(op) {
19733
- for (const placeholder in op.message.placeholders) {
19826
+ for (let placeholder in op.message.placeholders) {
19827
+ placeholder = placeholder.trimEnd();
19734
19828
  if (!op.params.has(placeholder) && !op.postprocessingParams.has(placeholder)) {
19735
19829
  throw Error(`Failed to resolve i18n placeholder: ${placeholder}`);
19736
19830
  }
19737
19831
  }
19738
- for (const placeholder in op.message.placeholderToMessage) {
19832
+ for (let placeholder in op.message.placeholderToMessage) {
19833
+ placeholder = placeholder.trimEnd();
19739
19834
  if (!op.params.has(placeholder) && !op.postprocessingParams.has(placeholder)) {
19740
19835
  throw Error(`Failed to resolve i18n message placeholder: ${placeholder}`);
19741
19836
  }
@@ -19750,7 +19845,9 @@ function extractI18nText(job) {
19750
19845
  // Remove all text nodes within i18n blocks, their content is already captured in the i18n
19751
19846
  // message.
19752
19847
  let currentI18n = null;
19848
+ let currentIcu = null;
19753
19849
  const textNodeI18nBlocks = new Map();
19850
+ const textNodeIcus = new Map();
19754
19851
  for (const op of unit.create) {
19755
19852
  switch (op.kind) {
19756
19853
  case OpKind.I18nStart:
@@ -19762,9 +19859,19 @@ function extractI18nText(job) {
19762
19859
  case OpKind.I18nEnd:
19763
19860
  currentI18n = null;
19764
19861
  break;
19862
+ case OpKind.IcuStart:
19863
+ if (op.context === null) {
19864
+ throw Error('Icu op should have its context set.');
19865
+ }
19866
+ currentIcu = op;
19867
+ break;
19868
+ case OpKind.IcuEnd:
19869
+ currentIcu = null;
19870
+ break;
19765
19871
  case OpKind.Text:
19766
19872
  if (currentI18n !== null) {
19767
19873
  textNodeI18nBlocks.set(op.xref, currentI18n);
19874
+ textNodeIcus.set(op.xref, currentIcu);
19768
19875
  OpList.remove(op);
19769
19876
  }
19770
19877
  break;
@@ -19779,13 +19886,16 @@ function extractI18nText(job) {
19779
19886
  continue;
19780
19887
  }
19781
19888
  const i18nOp = textNodeI18nBlocks.get(op.target);
19889
+ const icuOp = textNodeIcus.get(op.target);
19890
+ const contextId = icuOp ? icuOp.context : i18nOp.context;
19891
+ const resolutionTime = icuOp ? I18nParamResolutionTime.Postproccessing :
19892
+ I18nParamResolutionTime.Creation;
19782
19893
  const ops = [];
19783
19894
  for (let i = 0; i < op.interpolation.expressions.length; i++) {
19784
19895
  const expr = op.interpolation.expressions[i];
19785
- const placeholder = op.i18nPlaceholders[i];
19786
19896
  // For now, this i18nExpression depends on the slot context of the enclosing i18n block.
19787
19897
  // Later, we will modify this, and advance to a different point.
19788
- ops.push(createI18nExpressionOp(i18nOp.context, i18nOp.xref, i18nOp.handle, expr, placeholder.name, I18nParamResolutionTime.Creation, expr.sourceSpan ?? op.sourceSpan));
19898
+ ops.push(createI18nExpressionOp(contextId, i18nOp.xref, i18nOp.handle, expr, op.i18nPlaceholders[i], resolutionTime, expr.sourceSpan ?? op.sourceSpan));
19789
19899
  }
19790
19900
  OpList.replaceWithMany(op, ops);
19791
19901
  break;
@@ -20852,8 +20962,8 @@ function repeaterCreate(slot, viewFnName, decls, vars, tag, constIndex, trackByF
20852
20962
  }
20853
20963
  return call(Identifiers.repeaterCreate, args, sourceSpan);
20854
20964
  }
20855
- function repeater(metadataSlot, collection, sourceSpan) {
20856
- return call(Identifiers.repeater, [literal(metadataSlot), collection], sourceSpan);
20965
+ function repeater(collection, sourceSpan) {
20966
+ return call(Identifiers.repeater, [collection], sourceSpan);
20857
20967
  }
20858
20968
  function deferWhen(prefetch, expr, sourceSpan) {
20859
20969
  return call(prefetch ? Identifiers.deferPrefetchWhen : Identifiers.deferWhen, [expr], sourceSpan);
@@ -21461,7 +21571,7 @@ function reifyUpdateOperations(_unit, ops) {
21461
21571
  OpList.replace(op, conditional(op.targetSlot.slot, op.processed, op.contextValue, op.sourceSpan));
21462
21572
  break;
21463
21573
  case OpKind.Repeater:
21464
- OpList.replace(op, repeater(op.targetSlot.slot, op.collection, op.sourceSpan));
21574
+ OpList.replace(op, repeater(op.collection, op.sourceSpan));
21465
21575
  break;
21466
21576
  case OpKind.DeferWhen:
21467
21577
  OpList.replace(op, deferWhen(op.prefetch, op.expr, op.sourceSpan));
@@ -21800,7 +21910,7 @@ function getSubTemplateIndexForTemplateTag(job, i18nOp, op) {
21800
21910
  return i18nOp.subTemplateIndex;
21801
21911
  }
21802
21912
  /** Add a param value to the given params map. */
21803
- function addParam(params, placeholder, value, subTemplateIndex, flags = I18nParamValueFlags.None) {
21913
+ function addParam(params, placeholder, value, subTemplateIndex, flags) {
21804
21914
  const values = params.get(placeholder) ?? [];
21805
21915
  values.push({ value, subTemplateIndex, flags });
21806
21916
  params.set(placeholder, values);
@@ -21825,27 +21935,100 @@ function resolveI18nExpressionPlaceholders(job) {
21825
21935
  }
21826
21936
  }
21827
21937
  }
21828
- // Keep track of the next available expression index per i18n context.
21938
+ // Keep track of the next available expression index per i18n block.
21829
21939
  const expressionIndices = new Map();
21830
21940
  for (const unit of job.units) {
21831
21941
  for (const op of unit.update) {
21832
21942
  if (op.kind === OpKind.I18nExpression) {
21833
- const index = expressionIndices.get(op.context) || 0;
21834
21943
  const i18nContext = i18nContexts.get(op.context);
21944
+ const index = expressionIndices.get(i18nContext.i18nBlock) || 0;
21835
21945
  const subTemplateIndex = subTemplateIndicies.get(i18nContext.i18nBlock);
21836
21946
  // Add the expression index in the appropriate params map.
21837
21947
  const params = op.resolutionTime === I18nParamResolutionTime.Creation ?
21838
21948
  i18nContext.params :
21839
21949
  i18nContext.postprocessingParams;
21840
21950
  const values = params.get(op.i18nPlaceholder) || [];
21841
- values.push({ value: index, subTemplateIndex: subTemplateIndex, flags: I18nParamValueFlags.None });
21951
+ values.push({
21952
+ value: index,
21953
+ subTemplateIndex: subTemplateIndex,
21954
+ flags: I18nParamValueFlags.ExpressionIndex
21955
+ });
21842
21956
  params.set(op.i18nPlaceholder, values);
21843
- expressionIndices.set(op.context, index + 1);
21957
+ expressionIndices.set(i18nContext.i18nBlock, index + 1);
21844
21958
  }
21845
21959
  }
21846
21960
  }
21847
21961
  }
21848
21962
 
21963
+ /**
21964
+ * Resolves placeholders for element tags inside of an ICU.
21965
+ */
21966
+ function resolveI18nIcuPlaceholders(job) {
21967
+ const contextOps = new Map();
21968
+ for (const unit of job.units) {
21969
+ for (const op of unit.create) {
21970
+ switch (op.kind) {
21971
+ case OpKind.I18nContext:
21972
+ contextOps.set(op.xref, op);
21973
+ break;
21974
+ }
21975
+ }
21976
+ }
21977
+ for (const unit of job.units) {
21978
+ for (const op of unit.create) {
21979
+ switch (op.kind) {
21980
+ case OpKind.IcuStart:
21981
+ if (op.context === null) {
21982
+ throw Error('Icu should have its i18n context set.');
21983
+ }
21984
+ const i18nContext = contextOps.get(op.context);
21985
+ for (const node of op.message.nodes) {
21986
+ node.visit(new ResolveIcuPlaceholdersVisitor(i18nContext.postprocessingParams));
21987
+ }
21988
+ break;
21989
+ }
21990
+ }
21991
+ }
21992
+ }
21993
+ /**
21994
+ * Visitor for i18n AST that resolves ICU params into the given map.
21995
+ */
21996
+ class ResolveIcuPlaceholdersVisitor extends RecurseVisitor {
21997
+ constructor(params) {
21998
+ super();
21999
+ this.params = params;
22000
+ }
22001
+ visitContainerPlaceholder(placeholder) {
22002
+ // Add the start and end source span for container placeholders. These need to be recorded for
22003
+ // elements inside ICUs. The slots for the nodes were recorded separately under the i18n
22004
+ // block's context as part of the `resolveI18nElementPlaceholders` phase.
22005
+ if (placeholder.startName && placeholder.startSourceSpan &&
22006
+ !this.params.has(placeholder.startName)) {
22007
+ this.params.set(placeholder.startName, [{
22008
+ value: placeholder.startSourceSpan?.toString(),
22009
+ subTemplateIndex: null,
22010
+ flags: I18nParamValueFlags.None
22011
+ }]);
22012
+ }
22013
+ if (placeholder.closeName && placeholder.endSourceSpan &&
22014
+ !this.params.has(placeholder.closeName)) {
22015
+ this.params.set(placeholder.closeName, [{
22016
+ value: placeholder.endSourceSpan?.toString(),
22017
+ subTemplateIndex: null,
22018
+ flags: I18nParamValueFlags.None
22019
+ }]);
22020
+ }
22021
+ }
22022
+ visitTagPlaceholder(placeholder) {
22023
+ super.visitTagPlaceholder(placeholder);
22024
+ this.visitContainerPlaceholder(placeholder);
22025
+ }
22026
+ visitBlockPlaceholder(placeholder) {
22027
+ super.visitBlockPlaceholder(placeholder);
22028
+ this.visitContainerPlaceholder(placeholder);
22029
+ }
22030
+ }
22031
+
21849
22032
  /**
21850
22033
  * Resolves lexical references in views (`ir.LexicalReadExpr`) to either a target variable or to
21851
22034
  * property reads on the top-level component context.
@@ -22896,6 +23079,7 @@ function allowConservativeInlining(decl, target) {
22896
23079
  function wrapI18nIcus(job) {
22897
23080
  for (const unit of job.units) {
22898
23081
  let currentI18nOp = null;
23082
+ let addedI18nId = null;
22899
23083
  for (const op of unit.create) {
22900
23084
  switch (op.kind) {
22901
23085
  case OpKind.I18nStart:
@@ -22904,11 +23088,16 @@ function wrapI18nIcus(job) {
22904
23088
  case OpKind.I18nEnd:
22905
23089
  currentI18nOp = null;
22906
23090
  break;
22907
- case OpKind.Icu:
23091
+ case OpKind.IcuStart:
22908
23092
  if (currentI18nOp === null) {
22909
- const id = job.allocateXrefId();
22910
- OpList.insertBefore(createI18nStartOp(id, op.message), op);
22911
- OpList.insertAfter(createI18nEndOp(id), op);
23093
+ addedI18nId = job.allocateXrefId();
23094
+ OpList.insertBefore(createI18nStartOp(addedI18nId, op.message), op);
23095
+ }
23096
+ break;
23097
+ case OpKind.IcuEnd:
23098
+ if (addedI18nId !== null) {
23099
+ OpList.insertAfter(createI18nEndOp(addedI18nId), op);
23100
+ addedI18nId = null;
22912
23101
  }
22913
23102
  break;
22914
23103
  }
@@ -22942,7 +23131,6 @@ const phases = [
22942
23131
  { kind: CompilationJobKind.Tmpl, fn: createPipes },
22943
23132
  { kind: CompilationJobKind.Tmpl, fn: configureDeferInstructions },
22944
23133
  { kind: CompilationJobKind.Tmpl, fn: extractI18nText },
22945
- { kind: CompilationJobKind.Tmpl, fn: createI18nIcuExpressions },
22946
23134
  { kind: CompilationJobKind.Tmpl, fn: applyI18nExpressions },
22947
23135
  { kind: CompilationJobKind.Tmpl, fn: createVariadicPipes },
22948
23136
  { kind: CompilationJobKind.Both, fn: generatePureLiteralStructures },
@@ -22966,6 +23154,7 @@ const phases = [
22966
23154
  { kind: CompilationJobKind.Tmpl, fn: createDeferDepsFns },
22967
23155
  { kind: CompilationJobKind.Tmpl, fn: resolveI18nElementPlaceholders },
22968
23156
  { kind: CompilationJobKind.Tmpl, fn: resolveI18nExpressionPlaceholders },
23157
+ { kind: CompilationJobKind.Tmpl, fn: resolveI18nIcuPlaceholders },
22969
23158
  { kind: CompilationJobKind.Tmpl, fn: mergeI18nContexts },
22970
23159
  { kind: CompilationJobKind.Tmpl, fn: extractI18nMessages },
22971
23160
  { kind: CompilationJobKind.Tmpl, fn: generateTrackFns },
@@ -23279,7 +23468,7 @@ function ingestText(unit, text) {
23279
23468
  /**
23280
23469
  * Ingest an interpolated text node from the AST into the given `ViewCompilation`.
23281
23470
  */
23282
- function ingestBoundText(unit, text) {
23471
+ function ingestBoundText(unit, text, i18nPlaceholders) {
23283
23472
  let value = text.value;
23284
23473
  if (value instanceof ASTWithSource) {
23285
23474
  value = value.ast;
@@ -23290,9 +23479,16 @@ function ingestBoundText(unit, text) {
23290
23479
  if (text.i18n !== undefined && !(text.i18n instanceof Container)) {
23291
23480
  throw Error(`Unhandled i18n metadata type for text interpolation: ${text.i18n?.constructor.name}`);
23292
23481
  }
23293
- const i18nPlaceholders = text.i18n instanceof Container ?
23294
- text.i18n.children.filter((node) => node instanceof Placeholder) :
23295
- [];
23482
+ if (i18nPlaceholders === undefined) {
23483
+ i18nPlaceholders = text.i18n instanceof Container ?
23484
+ text.i18n.children
23485
+ .filter((node) => node instanceof Placeholder)
23486
+ .map(placeholder => placeholder.name) :
23487
+ [];
23488
+ }
23489
+ if (i18nPlaceholders.length > 0 && i18nPlaceholders.length !== value.expressions.length) {
23490
+ throw Error(`Unexpected number of i18n placeholders (${value.expressions.length}) for BoundText with ${value.expressions.length} expressions`);
23491
+ }
23296
23492
  const textXref = unit.job.allocateXrefId();
23297
23493
  unit.create.push(createTextOp(textXref, '', text.sourceSpan));
23298
23494
  // TemplateDefinitionBuilder does not generate source maps for sub-expressions inside an
@@ -23458,8 +23654,22 @@ function ingestDeferBlock(unit, deferBlock) {
23458
23654
  function ingestIcu(unit, icu) {
23459
23655
  if (icu.i18n instanceof Message && isSingleI18nIcu(icu.i18n)) {
23460
23656
  const xref = unit.job.allocateXrefId();
23461
- unit.create.push(createIcuOp(xref, icu.i18n, icu.i18n.nodes[0], icuFromI18nMessage(icu.i18n).name, null));
23462
- unit.update.push(createIcuUpdateOp(xref, null));
23657
+ const icuNode = icu.i18n.nodes[0];
23658
+ unit.create.push(createIcuStartOp(xref, icu.i18n, icuFromI18nMessage(icu.i18n).name, null));
23659
+ const expressionPlaceholder = icuNode.expressionPlaceholder?.trimEnd();
23660
+ if (expressionPlaceholder === undefined || icu.vars[expressionPlaceholder] === undefined) {
23661
+ throw Error('ICU should have a text binding');
23662
+ }
23663
+ ingestBoundText(unit, icu.vars[expressionPlaceholder], [expressionPlaceholder]);
23664
+ for (const [placeholder, text] of Object.entries(icu.placeholders)) {
23665
+ if (text instanceof BoundText) {
23666
+ ingestBoundText(unit, text, [placeholder]);
23667
+ }
23668
+ else {
23669
+ ingestText(unit, text);
23670
+ }
23671
+ }
23672
+ unit.create.push(createIcuEndOp(xref));
23463
23673
  }
23464
23674
  else {
23465
23675
  throw Error(`Unhandled i18n metadata type for ICU: ${icu.i18n?.constructor.name}`);
@@ -25011,19 +25221,19 @@ function createIfBlock(ast, connectedBlocks, visitor, bindingParser) {
25011
25221
  const branches = [];
25012
25222
  const mainBlockParams = parseConditionalBlockParameters(ast, errors, bindingParser);
25013
25223
  if (mainBlockParams !== null) {
25014
- branches.push(new IfBlockBranch(mainBlockParams.expression, visitAll(visitor, ast.children, ast.children), mainBlockParams.expressionAlias, ast.sourceSpan, ast.startSourceSpan, ast.endSourceSpan, ast.nameSpan));
25224
+ branches.push(new IfBlockBranch(mainBlockParams.expression, visitAll(visitor, ast.children, ast.children), mainBlockParams.expressionAlias, ast.sourceSpan, ast.startSourceSpan, ast.endSourceSpan, ast.nameSpan, ast.i18n));
25015
25225
  }
25016
25226
  for (const block of connectedBlocks) {
25017
25227
  if (ELSE_IF_PATTERN.test(block.name)) {
25018
25228
  const params = parseConditionalBlockParameters(block, errors, bindingParser);
25019
25229
  if (params !== null) {
25020
25230
  const children = visitAll(visitor, block.children, block.children);
25021
- branches.push(new IfBlockBranch(params.expression, children, params.expressionAlias, block.sourceSpan, block.startSourceSpan, block.endSourceSpan, block.nameSpan));
25231
+ branches.push(new IfBlockBranch(params.expression, children, params.expressionAlias, block.sourceSpan, block.startSourceSpan, block.endSourceSpan, block.nameSpan, block.i18n));
25022
25232
  }
25023
25233
  }
25024
25234
  else if (block.name === 'else') {
25025
25235
  const children = visitAll(visitor, block.children, block.children);
25026
- branches.push(new IfBlockBranch(null, children, null, block.sourceSpan, block.startSourceSpan, block.endSourceSpan, block.nameSpan));
25236
+ branches.push(new IfBlockBranch(null, children, null, block.sourceSpan, block.startSourceSpan, block.endSourceSpan, block.nameSpan, block.i18n));
25027
25237
  }
25028
25238
  }
25029
25239
  // The outer IfBlock should have a span that encapsulates all branches.
@@ -25054,7 +25264,7 @@ function createForLoop(ast, connectedBlocks, visitor, bindingParser) {
25054
25264
  errors.push(new ParseError(block.sourceSpan, '@empty block cannot have parameters'));
25055
25265
  }
25056
25266
  else {
25057
- empty = new ForLoopBlockEmpty(visitAll(visitor, block.children, block.children), block.sourceSpan, block.startSourceSpan, block.endSourceSpan, block.nameSpan);
25267
+ empty = new ForLoopBlockEmpty(visitAll(visitor, block.children, block.children), block.sourceSpan, block.startSourceSpan, block.endSourceSpan, block.nameSpan, block.i18n);
25058
25268
  }
25059
25269
  }
25060
25270
  else {
@@ -25072,7 +25282,7 @@ function createForLoop(ast, connectedBlocks, visitor, bindingParser) {
25072
25282
  // main `for` body, use `mainSourceSpan`.
25073
25283
  const endSpan = empty?.endSourceSpan ?? ast.endSourceSpan;
25074
25284
  const sourceSpan = new ParseSourceSpan(ast.sourceSpan.start, endSpan?.end ?? ast.sourceSpan.end);
25075
- node = new ForLoopBlock(params.itemName, params.expression, params.trackBy.expression, params.trackBy.keywordSpan, params.context, visitAll(visitor, ast.children, ast.children), empty, sourceSpan, ast.sourceSpan, ast.startSourceSpan, endSpan, ast.nameSpan);
25285
+ node = new ForLoopBlock(params.itemName, params.expression, params.trackBy.expression, params.trackBy.keywordSpan, params.context, visitAll(visitor, ast.children, ast.children), empty, sourceSpan, ast.sourceSpan, ast.startSourceSpan, endSpan, ast.nameSpan, ast.i18n);
25076
25286
  }
25077
25287
  }
25078
25288
  return { node, errors };
@@ -25098,7 +25308,7 @@ function createSwitchBlock(ast, visitor, bindingParser) {
25098
25308
  const expression = node.name === 'case' ?
25099
25309
  parseBlockParameterToBinding(node.parameters[0], bindingParser) :
25100
25310
  null;
25101
- const ast = new SwitchBlockCase(expression, visitAll(visitor, node.children, node.children), node.sourceSpan, node.startSourceSpan, node.endSourceSpan, node.nameSpan);
25311
+ const ast = new SwitchBlockCase(expression, visitAll(visitor, node.children, node.children), node.sourceSpan, node.startSourceSpan, node.endSourceSpan, node.nameSpan, node.i18n);
25102
25312
  if (expression === null) {
25103
25313
  defaultCase = ast;
25104
25314
  }
@@ -25685,7 +25895,7 @@ function createDeferredBlock(ast, connectedBlocks, visitor, bindingParser) {
25685
25895
  endOfLastSourceSpan = lastConnectedBlock.sourceSpan.end;
25686
25896
  }
25687
25897
  const sourceSpanWithConnectedBlocks = new ParseSourceSpan(ast.sourceSpan.start, endOfLastSourceSpan);
25688
- const node = new DeferredBlock(visitAll(visitor, ast.children, ast.children), triggers, prefetchTriggers, placeholder, loading, error, ast.nameSpan, sourceSpanWithConnectedBlocks, ast.sourceSpan, ast.startSourceSpan, lastEndSourceSpan);
25898
+ const node = new DeferredBlock(visitAll(visitor, ast.children, ast.children), triggers, prefetchTriggers, placeholder, loading, error, ast.nameSpan, sourceSpanWithConnectedBlocks, ast.sourceSpan, ast.startSourceSpan, lastEndSourceSpan, ast.i18n);
25689
25899
  return { node, errors };
25690
25900
  }
25691
25901
  function parseConnectedBlocks(connectedBlocks, errors, visitor) {
@@ -25748,7 +25958,7 @@ function parsePlaceholderBlock(ast, visitor) {
25748
25958
  throw new Error(`Unrecognized parameter in @placeholder block: "${param.expression}"`);
25749
25959
  }
25750
25960
  }
25751
- return new DeferredBlockPlaceholder(visitAll(visitor, ast.children, ast.children), minimumTime, ast.nameSpan, ast.sourceSpan, ast.startSourceSpan, ast.endSourceSpan);
25961
+ return new DeferredBlockPlaceholder(visitAll(visitor, ast.children, ast.children), minimumTime, ast.nameSpan, ast.sourceSpan, ast.startSourceSpan, ast.endSourceSpan, ast.i18n);
25752
25962
  }
25753
25963
  function parseLoadingBlock(ast, visitor) {
25754
25964
  let afterTime = null;
@@ -25778,13 +25988,13 @@ function parseLoadingBlock(ast, visitor) {
25778
25988
  throw new Error(`Unrecognized parameter in @loading block: "${param.expression}"`);
25779
25989
  }
25780
25990
  }
25781
- return new DeferredBlockLoading(visitAll(visitor, ast.children, ast.children), afterTime, minimumTime, ast.nameSpan, ast.sourceSpan, ast.startSourceSpan, ast.endSourceSpan);
25991
+ return new DeferredBlockLoading(visitAll(visitor, ast.children, ast.children), afterTime, minimumTime, ast.nameSpan, ast.sourceSpan, ast.startSourceSpan, ast.endSourceSpan, ast.i18n);
25782
25992
  }
25783
25993
  function parseErrorBlock(ast, visitor) {
25784
25994
  if (ast.parameters.length > 0) {
25785
25995
  throw new Error(`@error block cannot have parameters`);
25786
25996
  }
25787
- return new DeferredBlockError(visitAll(visitor, ast.children, ast.children), ast.nameSpan, ast.sourceSpan, ast.startSourceSpan, ast.endSourceSpan);
25997
+ return new DeferredBlockError(visitAll(visitor, ast.children, ast.children), ast.nameSpan, ast.sourceSpan, ast.startSourceSpan, ast.endSourceSpan, ast.i18n);
25788
25998
  }
25789
25999
  function parsePrimaryTriggers(params, bindingParser, errors, placeholder) {
25790
26000
  const triggers = {};
@@ -26384,6 +26594,11 @@ class I18nContext {
26384
26594
  const content = { type, index, ctx: this.id, isVoid: node.isVoid, closed };
26385
26595
  updatePlaceholderMap(this.placeholders, ph, content);
26386
26596
  }
26597
+ appendBlockPart(node, index, closed) {
26598
+ const ph = closed ? node.closeName : node.startName;
26599
+ const content = { type: TagType.TEMPLATE, index, ctx: this.id, closed };
26600
+ updatePlaceholderMap(this.placeholders, ph, content);
26601
+ }
26387
26602
  get icus() {
26388
26603
  return this._registry.icus;
26389
26604
  }
@@ -26416,6 +26631,13 @@ class I18nContext {
26416
26631
  this.appendTag(TagType.TEMPLATE, node, index, true);
26417
26632
  this._unresolvedCtxCount++;
26418
26633
  }
26634
+ appendBlock(node, index) {
26635
+ // add open and close tags at the same time,
26636
+ // since we process nested templates separately
26637
+ this.appendBlockPart(node, index, false);
26638
+ this.appendBlockPart(node, index, true);
26639
+ this._unresolvedCtxCount++;
26640
+ }
26419
26641
  appendElement(node, index, closed) {
26420
26642
  this.appendTag(TagType.ELEMENT, node, index, closed);
26421
26643
  }
@@ -26694,13 +26916,19 @@ class TemplateDefinitionBuilder {
26694
26916
  this.creationInstruction(null, Identifiers.pipe, [literal(slot), literal(name)]);
26695
26917
  });
26696
26918
  }
26697
- buildTemplateFunction(nodes, variables, ngContentSelectorsOffset = 0, i18n) {
26919
+ buildTemplateFunction(nodes, variables, ngContentSelectorsOffset = 0, i18n, variableAliases) {
26698
26920
  this._ngContentSelectorsOffset = ngContentSelectorsOffset;
26699
26921
  if (this._namespace !== Identifiers.namespaceHTML) {
26700
26922
  this.creationInstruction(null, this._namespace);
26701
26923
  }
26702
26924
  // Create variable bindings
26703
- variables.forEach(v => this.registerContextVariables(v));
26925
+ variables.forEach(v => {
26926
+ const alias = variableAliases?.[v.name];
26927
+ this.registerContextVariables(v.name, v.value);
26928
+ if (alias) {
26929
+ this.registerContextVariables(alias, v.value);
26930
+ }
26931
+ });
26704
26932
  // Initiate i18n context in case:
26705
26933
  // - this template has parent i18n context
26706
26934
  // - or the template has i18n meta associated with it,
@@ -26794,12 +27022,12 @@ class TemplateDefinitionBuilder {
26794
27022
  this._constants.prepareStatements.push(...statements);
26795
27023
  return _ref;
26796
27024
  }
26797
- registerContextVariables(variable$1) {
27025
+ registerContextVariables(name, value) {
26798
27026
  const scopedName = this._bindingScope.freshReferenceName();
26799
27027
  const retrievalLevel = this.level;
26800
- const isDirect = variable$1.value === DIRECT_CONTEXT_REFERENCE;
26801
- const lhs = variable(variable$1.name + scopedName);
26802
- this._bindingScope.set(retrievalLevel, variable$1.name, scope => {
27028
+ const isDirect = value === DIRECT_CONTEXT_REFERENCE;
27029
+ const lhs = variable(name + scopedName);
27030
+ this._bindingScope.set(retrievalLevel, name, scope => {
26803
27031
  // If we're at the top level and we're referring to the context variable directly, we
26804
27032
  // can do so through the implicit receiver, instead of renaming it. Note that this does
26805
27033
  // not apply to listeners, because they need to restore the context.
@@ -26835,7 +27063,7 @@ class TemplateDefinitionBuilder {
26835
27063
  return [
26836
27064
  // e.g. const $items$ = x(2) for direct context references and
26837
27065
  // const $item$ = x(2).$implicit for indirect ones.
26838
- lhs.set(isDirect ? rhs : rhs.prop(variable$1.value || IMPLICIT_REFERENCE)).toConstDecl()
27066
+ lhs.set(isDirect ? rhs : rhs.prop(value || IMPLICIT_REFERENCE)).toConstDecl()
26839
27067
  ];
26840
27068
  });
26841
27069
  }
@@ -27258,10 +27486,15 @@ class TemplateDefinitionBuilder {
27258
27486
  this.creationInstruction(span, isNgContainer$1 ? Identifiers.elementContainerEnd : Identifiers.elementEnd);
27259
27487
  }
27260
27488
  }
27261
- prepareEmbeddedTemplateFn(children, contextNameSuffix, variables = [], i18n) {
27489
+ prepareEmbeddedTemplateFn(children, contextNameSuffix, variables = [], i18nMeta, variableAliases) {
27262
27490
  const index = this.allocateDataSlot();
27263
- if (this.i18n && i18n) {
27264
- this.i18n.appendTemplate(i18n, index);
27491
+ if (this.i18n && i18nMeta) {
27492
+ if (i18nMeta instanceof BlockPlaceholder) {
27493
+ this.i18n.appendBlock(i18nMeta, index);
27494
+ }
27495
+ else {
27496
+ this.i18n.appendTemplate(i18nMeta, index);
27497
+ }
27265
27498
  }
27266
27499
  const contextName = `${this.contextName}${contextNameSuffix}_${index}`;
27267
27500
  const name = `${contextName}_Template`;
@@ -27272,7 +27505,7 @@ class TemplateDefinitionBuilder {
27272
27505
  // be able to support bindings in nested templates to local refs that occur after the
27273
27506
  // template definition. e.g. <div *ngIf="showing">{{ foo }}</div> <div #foo></div>
27274
27507
  this._nestedTemplateFns.push(() => {
27275
- const templateFunctionExpr = visitor.buildTemplateFunction(children, variables, this._ngContentReservedSlots.length + this._ngContentSelectorsOffset, i18n);
27508
+ const templateFunctionExpr = visitor.buildTemplateFunction(children, variables, this._ngContentReservedSlots.length + this._ngContentSelectorsOffset, i18nMeta, variableAliases);
27276
27509
  this.constantPool.statements.push(templateFunctionExpr.toDeclStmt(name));
27277
27510
  if (visitor._ngContentReservedSlots.length) {
27278
27511
  this._ngContentReservedSlots.push(...visitor._ngContentReservedSlots);
@@ -27380,7 +27613,9 @@ class TemplateDefinitionBuilder {
27380
27613
  // inside ICUs)
27381
27614
  // - all ICU vars (such as `VAR_SELECT` or `VAR_PLURAL`) are replaced with correct values
27382
27615
  const transformFn = (raw) => {
27383
- const params = { ...vars, ...placeholders };
27616
+ // Sort the map entries in the compiled output. This makes it easy to acheive identical output
27617
+ // in the template pipeline compiler.
27618
+ const params = Object.fromEntries(Object.entries({ ...vars, ...placeholders }).sort());
27384
27619
  const formatted = formatI18nPlaceholderNamesInMap(params, /* useCamelCase */ false);
27385
27620
  return invokeInstruction(null, Identifiers.i18nPostprocess, [raw, mapLiteral(formatted, true)]);
27386
27621
  };
@@ -27427,7 +27662,7 @@ class TemplateDefinitionBuilder {
27427
27662
  }
27428
27663
  // Note: the template needs to be created *before* we process the expression,
27429
27664
  // otherwise pipes injecting some symbols won't work (see #52102).
27430
- const templateIndex = this.createEmbeddedTemplateFn(tagName, children, '_Conditional', sourceSpan, variables, attrsExprs);
27665
+ const templateIndex = this.createEmbeddedTemplateFn(tagName, children, '_Conditional', sourceSpan, variables, attrsExprs, undefined, branch.i18n);
27431
27666
  const processedExpression = expression === null ? null : expression.visit(this._valueConverter);
27432
27667
  return { index: templateIndex, expression: processedExpression, alias: expressionAlias };
27433
27668
  });
@@ -27478,7 +27713,7 @@ class TemplateDefinitionBuilder {
27478
27713
  // We have to process the block in two steps: once here and again in the update instruction
27479
27714
  // callback in order to generate the correct expressions when pipes or pure functions are used.
27480
27715
  const caseData = block.cases.map(currentCase => {
27481
- const index = this.createEmbeddedTemplateFn(null, currentCase.children, '_Case', currentCase.sourceSpan);
27716
+ const index = this.createEmbeddedTemplateFn(null, currentCase.children, '_Case', currentCase.sourceSpan, undefined, undefined, undefined, currentCase.i18n);
27482
27717
  const expression = currentCase.expression === null ?
27483
27718
  null :
27484
27719
  currentCase.expression.visit(this._valueConverter);
@@ -27526,23 +27761,21 @@ class TemplateDefinitionBuilder {
27526
27761
  if (!metadata) {
27527
27762
  throw new Error('Could not resolve `defer` block metadata. Block may need to be analyzed.');
27528
27763
  }
27529
- const primaryTemplateIndex = this.createEmbeddedTemplateFn(null, deferred.children, '_Defer', deferred.sourceSpan);
27530
- const loadingIndex = loading ?
27531
- this.createEmbeddedTemplateFn(null, loading.children, '_DeferLoading', loading.sourceSpan) :
27764
+ const primaryTemplateIndex = this.createEmbeddedTemplateFn(null, deferred.children, '_Defer', deferred.sourceSpan, undefined, undefined, undefined, deferred.i18n);
27765
+ const loadingIndex = loading ? this.createEmbeddedTemplateFn(null, loading.children, '_DeferLoading', loading.sourceSpan, undefined, undefined, undefined, loading.i18n) :
27532
27766
  null;
27533
27767
  const loadingConsts = loading ?
27534
27768
  trimTrailingNulls([literal(loading.minimumTime), literal(loading.afterTime)]) :
27535
27769
  null;
27536
27770
  const placeholderIndex = placeholder ?
27537
- this.createEmbeddedTemplateFn(null, placeholder.children, '_DeferPlaceholder', placeholder.sourceSpan) :
27771
+ this.createEmbeddedTemplateFn(null, placeholder.children, '_DeferPlaceholder', placeholder.sourceSpan, undefined, undefined, undefined, placeholder.i18n) :
27538
27772
  null;
27539
27773
  const placeholderConsts = placeholder && placeholder.minimumTime !== null ?
27540
27774
  // TODO(crisbeto): potentially pass the time directly instead of storing it in the `consts`
27541
27775
  // since the placeholder block can only have one parameter?
27542
27776
  literalArr([literal(placeholder.minimumTime)]) :
27543
27777
  null;
27544
- const errorIndex = error ?
27545
- this.createEmbeddedTemplateFn(null, error.children, '_DeferError', error.sourceSpan) :
27778
+ const errorIndex = error ? this.createEmbeddedTemplateFn(null, error.children, '_DeferError', error.sourceSpan, undefined, undefined, undefined, error.i18n) :
27546
27779
  null;
27547
27780
  // Note: we generate this last so the index matches the instruction order.
27548
27781
  const deferredIndex = this.allocateDataSlot();
@@ -27696,11 +27929,18 @@ class TemplateDefinitionBuilder {
27696
27929
  // are implicitly inferred by the runtime to index + 1 and index + 2.
27697
27930
  const blockIndex = this.allocateDataSlot();
27698
27931
  const { tagName, attrsExprs } = this.inferProjectionDataFromInsertionPoint(block);
27699
- const primaryData = this.prepareEmbeddedTemplateFn(block.children, '_For', [block.item, block.contextVariables.$index, block.contextVariables.$count]);
27932
+ const primaryData = this.prepareEmbeddedTemplateFn(block.children, '_For', [block.item, block.contextVariables.$index, block.contextVariables.$count], block.i18n, {
27933
+ // We need to provide level-specific versions of `$index` and `$count`, because
27934
+ // they're used when deriving the remaining variables (`$odd`, `$even` etc.) while at the
27935
+ // same time being available implicitly. Without these aliases, we wouldn't be able to
27936
+ // access the `$index` of a parent loop from inside of a nested loop.
27937
+ [block.contextVariables.$index.name]: this.getLevelSpecificVariableName('$index', this.level + 1),
27938
+ [block.contextVariables.$count.name]: this.getLevelSpecificVariableName('$count', this.level + 1),
27939
+ });
27700
27940
  const { expression: trackByExpression, usesComponentInstance: trackByUsesComponentInstance } = this.createTrackByFunction(block);
27701
27941
  let emptyData = null;
27702
27942
  if (block.empty !== null) {
27703
- emptyData = this.prepareEmbeddedTemplateFn(block.empty.children, '_ForEmpty');
27943
+ emptyData = this.prepareEmbeddedTemplateFn(block.empty.children, '_ForEmpty', undefined, block.empty.i18n);
27704
27944
  // Allocate an extra slot for the empty block tracking.
27705
27945
  this.allocateBindingSlots(null);
27706
27946
  }
@@ -27730,17 +27970,44 @@ class TemplateDefinitionBuilder {
27730
27970
  // Note: we don't allocate binding slots for this expression,
27731
27971
  // because its value isn't stored in the LView.
27732
27972
  const value = block.expression.visit(this._valueConverter);
27733
- // `repeater(0, iterable)`
27734
- this.updateInstruction(block.sourceSpan, Identifiers.repeater, () => [literal(blockIndex), this.convertPropertyBinding(value)]);
27973
+ // `advance(x); repeater(iterable)`
27974
+ this.updateInstructionWithAdvance(blockIndex, block.sourceSpan, Identifiers.repeater, () => [this.convertPropertyBinding(value)]);
27735
27975
  }
27736
27976
  registerComputedLoopVariables(block, bindingScope) {
27737
- const indexLocalName = block.contextVariables.$index.name;
27738
- const countLocalName = block.contextVariables.$count.name;
27739
27977
  const level = bindingScope.bindingLevel;
27740
- bindingScope.set(level, block.contextVariables.$odd.name, scope => scope.get(indexLocalName).modulo(literal(2)).notIdentical(literal(0)));
27741
- bindingScope.set(level, block.contextVariables.$even.name, scope => scope.get(indexLocalName).modulo(literal(2)).identical(literal(0)));
27742
- bindingScope.set(level, block.contextVariables.$first.name, scope => scope.get(indexLocalName).identical(literal(0)));
27743
- bindingScope.set(level, block.contextVariables.$last.name, scope => scope.get(indexLocalName).identical(scope.get(countLocalName).minus(literal(1))));
27978
+ bindingScope.set(level, block.contextVariables.$odd.name, (scope, retrievalLevel) => {
27979
+ return this.getLevelSpecificForLoopVariable(block, scope, retrievalLevel, '$index')
27980
+ .modulo(literal(2))
27981
+ .notIdentical(literal(0));
27982
+ });
27983
+ bindingScope.set(level, block.contextVariables.$even.name, (scope, retrievalLevel) => {
27984
+ return this.getLevelSpecificForLoopVariable(block, scope, retrievalLevel, '$index')
27985
+ .modulo(literal(2))
27986
+ .identical(literal(0));
27987
+ });
27988
+ bindingScope.set(level, block.contextVariables.$first.name, (scope, retrievalLevel) => {
27989
+ return this.getLevelSpecificForLoopVariable(block, scope, retrievalLevel, '$index')
27990
+ .identical(literal(0));
27991
+ });
27992
+ bindingScope.set(level, block.contextVariables.$last.name, (scope, retrievalLevel) => {
27993
+ const index = this.getLevelSpecificForLoopVariable(block, scope, retrievalLevel, '$index');
27994
+ const count = this.getLevelSpecificForLoopVariable(block, scope, retrievalLevel, '$count');
27995
+ return index.identical(count.minus(literal(1)));
27996
+ });
27997
+ }
27998
+ getLevelSpecificVariableName(name, level) {
27999
+ // We use the `ɵ` here to ensure that there are no name conflicts with user-defined variables.
28000
+ return `ɵ${name}_${level}`;
28001
+ }
28002
+ /**
28003
+ * Gets the name of a for loop variable at a specific binding level. This allows us to look
28004
+ * up implicitly shadowed variables like `$index` and `$count` at a specific level.
28005
+ */
28006
+ getLevelSpecificForLoopVariable(block, scope, retrievalLevel, name) {
28007
+ const scopeName = scope.bindingLevel === retrievalLevel ?
28008
+ block.contextVariables[name].name :
28009
+ this.getLevelSpecificVariableName(name, retrievalLevel);
28010
+ return scope.get(scopeName);
27744
28011
  }
27745
28012
  optimizeTrackByFunction(block) {
27746
28013
  const indexLocalName = block.contextVariables.$index.name;
@@ -28278,7 +28545,7 @@ class BindingScope {
28278
28545
  if (value.declareLocalCallback && !value.declare) {
28279
28546
  value.declare = true;
28280
28547
  }
28281
- return typeof value.lhs === 'function' ? value.lhs(this) : value.lhs;
28548
+ return typeof value.lhs === 'function' ? value.lhs(this, value.retrievalLevel) : value.lhs;
28282
28549
  }
28283
28550
  current = current.parent;
28284
28551
  }
@@ -28386,7 +28653,9 @@ class BindingScope {
28386
28653
  const componentValue = this.map.get(SHARED_CONTEXT_KEY + 0);
28387
28654
  componentValue.declare = true;
28388
28655
  this.maybeRestoreView();
28389
- const lhs = typeof componentValue.lhs === 'function' ? componentValue.lhs(this) : componentValue.lhs;
28656
+ const lhs = typeof componentValue.lhs === 'function' ?
28657
+ componentValue.lhs(this, componentValue.retrievalLevel) :
28658
+ componentValue.lhs;
28390
28659
  return name === DIRECT_CONTEXT_REFERENCE ? lhs : lhs.prop(name);
28391
28660
  }
28392
28661
  maybeRestoreView() {
@@ -30971,7 +31240,7 @@ function publishFacade(global) {
30971
31240
  * @description
30972
31241
  * Entry point for all public APIs of the compiler package.
30973
31242
  */
30974
- const VERSION = new Version('17.0.2');
31243
+ const VERSION = new Version('17.0.4');
30975
31244
 
30976
31245
  class CompilerConfig {
30977
31246
  constructor({ defaultEncapsulation = ViewEncapsulation.Emulated, preserveWhitespaces, strictInjectionParameters } = {}) {
@@ -31204,7 +31473,8 @@ class _Visitor {
31204
31473
  this._errors = [];
31205
31474
  this._messages = [];
31206
31475
  this._inImplicitNode = false;
31207
- this._createI18nMessage = createI18nMessageFactory(interpolationConfig);
31476
+ this._createI18nMessage =
31477
+ createI18nMessageFactory(interpolationConfig, DEFAULT_CONTAINER_BLOCKS);
31208
31478
  }
31209
31479
  // looks for translatable attributes
31210
31480
  _visitAttributesOf(el) {
@@ -31512,6 +31782,12 @@ class _WriteVisitor$1 {
31512
31782
  visitPlaceholder(ph, context) {
31513
31783
  return [new Tag(_PLACEHOLDER_TAG$2, { id: ph.name, 'equiv-text': `{{${ph.value}}}` })];
31514
31784
  }
31785
+ visitBlockPlaceholder(ph, context) {
31786
+ const ctype = `x-${ph.name.toLowerCase().replace(/[^a-z0-9]/g, '-')}`;
31787
+ const startTagPh = new Tag(_PLACEHOLDER_TAG$2, { id: ph.startName, ctype, 'equiv-text': `@${ph.name}` });
31788
+ const closeTagPh = new Tag(_PLACEHOLDER_TAG$2, { id: ph.closeName, ctype, 'equiv-text': `}` });
31789
+ return [startTagPh, ...this.serialize(ph.children), closeTagPh];
31790
+ }
31515
31791
  visitIcuPlaceholder(ph, context) {
31516
31792
  const equivText = `{${ph.value.expression}, ${ph.value.type}, ${Object.keys(ph.value.cases).map((value) => value + ' {...}').join(' ')}}`;
31517
31793
  return [new Tag(_PLACEHOLDER_TAG$2, { id: ph.name, 'equiv-text': equivText })];
@@ -31783,6 +32059,24 @@ class _WriteVisitor {
31783
32059
  disp: `{{${ph.value}}}`,
31784
32060
  })];
31785
32061
  }
32062
+ visitBlockPlaceholder(ph, context) {
32063
+ const tagPc = new Tag(_PLACEHOLDER_SPANNING_TAG, {
32064
+ id: (this._nextPlaceholderId++).toString(),
32065
+ equivStart: ph.startName,
32066
+ equivEnd: ph.closeName,
32067
+ type: 'other',
32068
+ dispStart: `@${ph.name}`,
32069
+ dispEnd: `}`,
32070
+ });
32071
+ const nodes = [].concat(...ph.children.map(node => node.visit(this)));
32072
+ if (nodes.length) {
32073
+ nodes.forEach((node) => tagPc.children.push(node));
32074
+ }
32075
+ else {
32076
+ tagPc.children.push(new Text$1(''));
32077
+ }
32078
+ return [tagPc];
32079
+ }
31786
32080
  visitIcuPlaceholder(ph, context) {
31787
32081
  const cases = Object.keys(ph.value.cases).map((value) => value + ' {...}').join(' ');
31788
32082
  const idStr = (this._nextPlaceholderId++).toString();
@@ -32231,6 +32525,11 @@ class I18nToHtmlVisitor {
32231
32525
  // An ICU placeholder references the source message to be serialized
32232
32526
  return this._convertToText(this._srcMsg.placeholderToMessage[ph.name]);
32233
32527
  }
32528
+ visitBlockPlaceholder(ph, context) {
32529
+ const params = ph.parameters.length === 0 ? '' : ` (${ph.parameters.join('; ')})`;
32530
+ const children = ph.children.map((c) => c.visit(this)).join('');
32531
+ return `@${ph.name}${params} {${children}}`;
32532
+ }
32234
32533
  /**
32235
32534
  * Convert a source message to a translated text string:
32236
32535
  * - text nodes are replaced with their translation,
@@ -32383,6 +32682,12 @@ class MapPlaceholderNames extends CloneVisitor {
32383
32682
  const children = ph.children.map(n => n.visit(this, mapper));
32384
32683
  return new TagPlaceholder(ph.tag, ph.attrs, startName, closeName, children, ph.isVoid, ph.sourceSpan, ph.startSourceSpan, ph.endSourceSpan);
32385
32684
  }
32685
+ visitBlockPlaceholder(ph, mapper) {
32686
+ const startName = mapper.toPublicName(ph.startName);
32687
+ const closeName = ph.closeName ? mapper.toPublicName(ph.closeName) : ph.closeName;
32688
+ const children = ph.children.map(n => n.visit(this, mapper));
32689
+ return new BlockPlaceholder(ph.name, ph.parameters, startName, closeName, children, ph.sourceSpan, ph.startSourceSpan, ph.endSourceSpan);
32690
+ }
32386
32691
  visitPlaceholder(ph, mapper) {
32387
32692
  return new Placeholder(ph.value, mapper.toPublicName(ph.name), ph.sourceSpan);
32388
32693
  }
@@ -32501,7 +32806,7 @@ const MINIMUM_PARTIAL_LINKER_VERSION$6 = '12.0.0';
32501
32806
  function compileDeclareClassMetadata(metadata) {
32502
32807
  const definitionMap = new DefinitionMap();
32503
32808
  definitionMap.set('minVersion', literal(MINIMUM_PARTIAL_LINKER_VERSION$6));
32504
- definitionMap.set('version', literal('17.0.2'));
32809
+ definitionMap.set('version', literal('17.0.4'));
32505
32810
  definitionMap.set('ngImport', importExpr(Identifiers.core));
32506
32811
  definitionMap.set('type', metadata.type);
32507
32812
  definitionMap.set('decorators', metadata.decorators);
@@ -32609,7 +32914,7 @@ function createDirectiveDefinitionMap(meta) {
32609
32914
  // in 16.1 is actually used.
32610
32915
  const minVersion = hasTransformFunctions ? MINIMUM_PARTIAL_LINKER_VERSION$5 : '14.0.0';
32611
32916
  definitionMap.set('minVersion', literal(minVersion));
32612
- definitionMap.set('version', literal('17.0.2'));
32917
+ definitionMap.set('version', literal('17.0.4'));
32613
32918
  // e.g. `type: MyDirective`
32614
32919
  definitionMap.set('type', meta.type.value);
32615
32920
  if (meta.isStandalone) {
@@ -32886,7 +33191,7 @@ const MINIMUM_PARTIAL_LINKER_VERSION$4 = '12.0.0';
32886
33191
  function compileDeclareFactoryFunction(meta) {
32887
33192
  const definitionMap = new DefinitionMap();
32888
33193
  definitionMap.set('minVersion', literal(MINIMUM_PARTIAL_LINKER_VERSION$4));
32889
- definitionMap.set('version', literal('17.0.2'));
33194
+ definitionMap.set('version', literal('17.0.4'));
32890
33195
  definitionMap.set('ngImport', importExpr(Identifiers.core));
32891
33196
  definitionMap.set('type', meta.type.value);
32892
33197
  definitionMap.set('deps', compileDependencies(meta.deps));
@@ -32921,7 +33226,7 @@ function compileDeclareInjectableFromMetadata(meta) {
32921
33226
  function createInjectableDefinitionMap(meta) {
32922
33227
  const definitionMap = new DefinitionMap();
32923
33228
  definitionMap.set('minVersion', literal(MINIMUM_PARTIAL_LINKER_VERSION$3));
32924
- definitionMap.set('version', literal('17.0.2'));
33229
+ definitionMap.set('version', literal('17.0.4'));
32925
33230
  definitionMap.set('ngImport', importExpr(Identifiers.core));
32926
33231
  definitionMap.set('type', meta.type.value);
32927
33232
  // Only generate providedIn property if it has a non-null value
@@ -32972,7 +33277,7 @@ function compileDeclareInjectorFromMetadata(meta) {
32972
33277
  function createInjectorDefinitionMap(meta) {
32973
33278
  const definitionMap = new DefinitionMap();
32974
33279
  definitionMap.set('minVersion', literal(MINIMUM_PARTIAL_LINKER_VERSION$2));
32975
- definitionMap.set('version', literal('17.0.2'));
33280
+ definitionMap.set('version', literal('17.0.4'));
32976
33281
  definitionMap.set('ngImport', importExpr(Identifiers.core));
32977
33282
  definitionMap.set('type', meta.type.value);
32978
33283
  definitionMap.set('providers', meta.providers);
@@ -33005,7 +33310,7 @@ function createNgModuleDefinitionMap(meta) {
33005
33310
  throw new Error('Invalid path! Local compilation mode should not get into the partial compilation path');
33006
33311
  }
33007
33312
  definitionMap.set('minVersion', literal(MINIMUM_PARTIAL_LINKER_VERSION$1));
33008
- definitionMap.set('version', literal('17.0.2'));
33313
+ definitionMap.set('version', literal('17.0.4'));
33009
33314
  definitionMap.set('ngImport', importExpr(Identifiers.core));
33010
33315
  definitionMap.set('type', meta.type.value);
33011
33316
  // We only generate the keys in the metadata if the arrays contain values.
@@ -33056,7 +33361,7 @@ function compileDeclarePipeFromMetadata(meta) {
33056
33361
  function createPipeDefinitionMap(meta) {
33057
33362
  const definitionMap = new DefinitionMap();
33058
33363
  definitionMap.set('minVersion', literal(MINIMUM_PARTIAL_LINKER_VERSION));
33059
- definitionMap.set('version', literal('17.0.2'));
33364
+ definitionMap.set('version', literal('17.0.4'));
33060
33365
  definitionMap.set('ngImport', importExpr(Identifiers.core));
33061
33366
  // e.g. `type: MyPipe`
33062
33367
  definitionMap.set('type', meta.type.value);