@angular/compiler 17.0.3 → 17.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (58) 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/compiler.mjs +6 -4
  32. package/esm2022/src/render3/view/i18n/context.mjs +13 -1
  33. package/esm2022/src/render3/view/i18n/get_msg_utils.mjs +4 -1
  34. package/esm2022/src/render3/view/i18n/icu_serializer.mjs +4 -1
  35. package/esm2022/src/render3/view/i18n/localize_utils.mjs +6 -1
  36. package/esm2022/src/render3/view/i18n/meta.mjs +5 -4
  37. package/esm2022/src/render3/view/template.mjs +79 -33
  38. package/esm2022/src/template/pipeline/ir/src/enums.mjs +9 -1
  39. package/esm2022/src/template/pipeline/ir/src/ops/create.mjs +3 -2
  40. package/esm2022/src/template/pipeline/ir/src/ops/update.mjs +2 -1
  41. package/esm2022/src/template/pipeline/src/emit.mjs +1 -3
  42. package/esm2022/src/template/pipeline/src/instruction.mjs +3 -3
  43. package/esm2022/src/template/pipeline/src/phases/assign_i18n_slot_dependencies.mjs +2 -7
  44. package/esm2022/src/template/pipeline/src/phases/create_i18n_contexts.mjs +20 -6
  45. package/esm2022/src/template/pipeline/src/phases/extract_i18n_messages.mjs +94 -23
  46. package/esm2022/src/template/pipeline/src/phases/propagate_i18n_blocks.mjs +3 -2
  47. package/esm2022/src/template/pipeline/src/phases/reify.mjs +2 -2
  48. package/esm2022/src/template/pipeline/src/phases/resolve_i18n_element_placeholders.mjs +70 -57
  49. package/esm2022/src/template/pipeline/src/phases/resolve_i18n_expression_placeholders.mjs +4 -4
  50. package/esm2022/src/template/pipeline/src/phases/resolve_i18n_icu_placeholders.mjs +16 -25
  51. package/esm2022/src/template_parser/binding_parser.mjs +1 -1
  52. package/esm2022/src/version.mjs +1 -1
  53. package/fesm2022/compiler.mjs +494 -245
  54. package/fesm2022/compiler.mjs.map +1 -1
  55. package/index.d.ts +36 -12
  56. package/package.json +2 -2
  57. package/esm2022/src/ml_parser/interpolation_config.mjs +0 -23
  58. package/esm2022/src/template/pipeline/src/phases/merge_i18n_contexts.mjs +0 -59
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @license Angular v17.0.3
2
+ * @license Angular v17.0.5
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;
@@ -9141,6 +9195,14 @@ var DerivedRepeaterVarIdentity;
9141
9195
  DerivedRepeaterVarIdentity[DerivedRepeaterVarIdentity["Even"] = 2] = "Even";
9142
9196
  DerivedRepeaterVarIdentity[DerivedRepeaterVarIdentity["Odd"] = 3] = "Odd";
9143
9197
  })(DerivedRepeaterVarIdentity || (DerivedRepeaterVarIdentity = {}));
9198
+ /**
9199
+ * Kinds of i18n contexts. They can be created because of root i18n blocks, or ICUs.
9200
+ */
9201
+ var I18nContextKind;
9202
+ (function (I18nContextKind) {
9203
+ I18nContextKind[I18nContextKind["RootI18n"] = 0] = "RootI18n";
9204
+ I18nContextKind[I18nContextKind["Icu"] = 1] = "Icu";
9205
+ })(I18nContextKind || (I18nContextKind = {}));
9144
9206
 
9145
9207
  /**
9146
9208
  * Marker symbol for `ConsumesSlotOpTrait`.
@@ -9414,6 +9476,7 @@ function createRepeaterOp(repeaterCreate, targetSlot, collection, sourceSpan) {
9414
9476
  collection,
9415
9477
  sourceSpan,
9416
9478
  ...NEW_OP,
9479
+ ...TRAIT_DEPENDS_ON_SLOT_CONTEXT,
9417
9480
  };
9418
9481
  }
9419
9482
  function createDeferWhenOp(target, expr, prefetch, sourceSpan) {
@@ -10924,9 +10987,10 @@ function createIcuEndOp(xref) {
10924
10987
  ...NEW_OP,
10925
10988
  };
10926
10989
  }
10927
- function createI18nContextOp(xref, i18nBlock, message, sourceSpan) {
10990
+ function createI18nContextOp(contextKind, xref, i18nBlock, message, sourceSpan) {
10928
10991
  return {
10929
10992
  kind: OpKind.I18nContext,
10993
+ contextKind,
10930
10994
  xref,
10931
10995
  i18nBlock,
10932
10996
  message,
@@ -11212,7 +11276,6 @@ function needsApplication(i18nContexts, op) {
11212
11276
  */
11213
11277
  function assignI18nSlotDependencies(job) {
11214
11278
  const i18nLastSlotConsumers = new Map();
11215
- const i18nContexts = new Map();
11216
11279
  let lastSlotConsumer = null;
11217
11280
  let currentI18nOp = null;
11218
11281
  for (const unit of job.units) {
@@ -11229,16 +11292,12 @@ function assignI18nSlotDependencies(job) {
11229
11292
  i18nLastSlotConsumers.set(currentI18nOp.xref, lastSlotConsumer);
11230
11293
  currentI18nOp = null;
11231
11294
  break;
11232
- case OpKind.I18nContext:
11233
- i18nContexts.set(op.xref, op);
11234
- break;
11235
11295
  }
11236
11296
  }
11237
11297
  // Assign i18n expressions to target the last slot in its owning block.
11238
11298
  for (const op of unit.update) {
11239
11299
  if (op.kind === OpKind.I18nExpression) {
11240
- const i18nContext = i18nContexts.get(op.context);
11241
- op.target = i18nLastSlotConsumers.get(i18nContext.i18nBlock);
11300
+ op.target = i18nLastSlotConsumers.get(op.target);
11242
11301
  }
11243
11302
  }
11244
11303
  }
@@ -11847,17 +11906,22 @@ function createDeferDepsFns(job) {
11847
11906
  * message.)
11848
11907
  */
11849
11908
  function createI18nContexts(job) {
11909
+ const rootContexts = new Map();
11850
11910
  let currentI18nOp = null;
11851
11911
  let xref;
11852
11912
  for (const unit of job.units) {
11853
11913
  for (const op of unit.create) {
11854
11914
  switch (op.kind) {
11855
11915
  case OpKind.I18nStart:
11856
- // Each i18n block gets its own context.
11857
- xref = job.allocateXrefId();
11858
- unit.create.push(createI18nContextOp(xref, op.xref, op.message, null));
11859
- op.context = xref;
11860
11916
  currentI18nOp = op;
11917
+ // Each root i18n block gets its own context, child ones refer to the context for their
11918
+ // root block.
11919
+ if (op.xref === op.root) {
11920
+ xref = job.allocateXrefId();
11921
+ unit.create.push(createI18nContextOp(I18nContextKind.RootI18n, xref, op.xref, op.message, null));
11922
+ op.context = xref;
11923
+ rootContexts.set(op.xref, xref);
11924
+ }
11861
11925
  break;
11862
11926
  case OpKind.I18nEnd:
11863
11927
  currentI18nOp = null;
@@ -11871,7 +11935,7 @@ function createI18nContexts(job) {
11871
11935
  if (op.message.id !== currentI18nOp.message.id) {
11872
11936
  // There was an enclosing i18n block around this ICU somewhere.
11873
11937
  xref = job.allocateXrefId();
11874
- unit.create.push(createI18nContextOp(xref, currentI18nOp.xref, op.message, null));
11938
+ unit.create.push(createI18nContextOp(I18nContextKind.Icu, xref, currentI18nOp.xref, op.message, null));
11875
11939
  op.context = xref;
11876
11940
  }
11877
11941
  else {
@@ -11883,6 +11947,15 @@ function createI18nContexts(job) {
11883
11947
  }
11884
11948
  }
11885
11949
  }
11950
+ // Assign contexts to child i18n blocks, now that all root i18n blocks have their context
11951
+ // assigned.
11952
+ for (const unit of job.units) {
11953
+ for (const op of unit.create) {
11954
+ if (op.kind === OpKind.I18nStart && op.xref !== op.root) {
11955
+ op.context = rootContexts.get(op.root);
11956
+ }
11957
+ }
11958
+ }
11886
11959
  }
11887
11960
 
11888
11961
  /**
@@ -12265,11 +12338,9 @@ const LIST_DELIMITER = '|';
12265
12338
  * used in the final output.
12266
12339
  */
12267
12340
  function extractI18nMessages(job) {
12268
- // Save the i18n context ops for later use.
12341
+ // Save the i18n start and i18n context ops for later use.
12269
12342
  const i18nContexts = new Map();
12270
- // Record which contexts represent i18n blocks (any other contexts are assumed to have been
12271
- // created from ICUs).
12272
- const i18nBlockContexts = new Set();
12343
+ const i18nBlocks = new Map();
12273
12344
  for (const unit of job.units) {
12274
12345
  for (const op of unit.create) {
12275
12346
  switch (op.kind) {
@@ -12277,7 +12348,7 @@ function extractI18nMessages(job) {
12277
12348
  i18nContexts.set(op.xref, op);
12278
12349
  break;
12279
12350
  case OpKind.I18nStart:
12280
- i18nBlockContexts.add(op.context);
12351
+ i18nBlocks.set(op.xref, op);
12281
12352
  break;
12282
12353
  }
12283
12354
  }
@@ -12304,11 +12375,12 @@ function extractI18nMessages(job) {
12304
12375
  if (!op.context) {
12305
12376
  throw Error('ICU op should have its context set.');
12306
12377
  }
12307
- if (!i18nBlockContexts.has(op.context)) {
12308
- const i18nContext = i18nContexts.get(op.context);
12378
+ const i18nContext = i18nContexts.get(op.context);
12379
+ if (i18nContext.contextKind === I18nContextKind.Icu) {
12309
12380
  const subMessage = createI18nMessage(job, i18nContext, op.messagePlaceholder);
12310
12381
  unit.create.push(subMessage);
12311
- const parentMessage = i18nBlockMessages.get(i18nContext.i18nBlock);
12382
+ const rootI18nId = i18nBlocks.get(i18nContext.i18nBlock).root;
12383
+ const parentMessage = i18nBlockMessages.get(rootI18nId);
12312
12384
  parentMessage?.subMessages.push(subMessage.xref);
12313
12385
  }
12314
12386
  OpList.remove(op);
@@ -12324,38 +12396,110 @@ function extractI18nMessages(job) {
12324
12396
  * Create an i18n message op from an i18n context op.
12325
12397
  */
12326
12398
  function createI18nMessage(job, context, messagePlaceholder) {
12327
- let needsPostprocessing = context.postprocessingParams.size > 0;
12328
- for (const values of context.params.values()) {
12329
- if (values.length > 1) {
12330
- needsPostprocessing = true;
12331
- }
12332
- }
12333
- return createI18nMessageOp(job.allocateXrefId(), context.i18nBlock, context.message, messagePlaceholder ?? null, formatParams(context.params), formatParams(context.postprocessingParams), needsPostprocessing);
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);
12334
12403
  }
12335
12404
  /**
12336
12405
  * 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
12337
12408
  */
12338
12409
  function formatParams(params) {
12339
- const result = new Map();
12410
+ const formattedParams = new Map();
12411
+ let needsPostprocessing = false;
12340
12412
  for (const [placeholder, placeholderValues] of params) {
12341
- const serializedValues = formatParamValues(placeholderValues);
12413
+ const [serializedValues, paramNeedsPostprocessing] = formatParamValues(placeholderValues);
12414
+ needsPostprocessing ||= paramNeedsPostprocessing;
12342
12415
  if (serializedValues !== null) {
12343
- result.set(placeholder, literal(formatParamValues(placeholderValues)));
12416
+ formattedParams.set(placeholder, literal(serializedValues));
12344
12417
  }
12345
12418
  }
12346
- return result;
12419
+ return [formattedParams, needsPostprocessing];
12347
12420
  }
12348
12421
  /**
12349
12422
  * 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
12350
12425
  */
12351
12426
  function formatParamValues(values) {
12352
12427
  if (values.length === 0) {
12353
- return null;
12428
+ return [null, false];
12354
12429
  }
12430
+ collapseElementTemplatePairs(values);
12355
12431
  const serializedValues = values.map(value => formatValue(value));
12356
12432
  return serializedValues.length === 1 ?
12357
- serializedValues[0] :
12358
- `${LIST_START_MARKER}${serializedValues.join(LIST_DELIMITER)}${LIST_END_MARKER}`;
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
+ }
12359
12503
  }
12360
12504
  /**
12361
12505
  * Formats a single `I18nParamValue` into a string
@@ -12723,6 +12867,9 @@ class IcuSerializerVisitor {
12723
12867
  visitPlaceholder(ph) {
12724
12868
  return this.formatPh(ph.name);
12725
12869
  }
12870
+ visitBlockPlaceholder(ph) {
12871
+ return `${this.formatPh(ph.startName)}${ph.children.map(child => child.visit(this)).join('')}${this.formatPh(ph.closeName)}`;
12872
+ }
12726
12873
  visitIcuPlaceholder(ph, context) {
12727
12874
  return this.formatPh(ph.name);
12728
12875
  }
@@ -14446,12 +14593,12 @@ class Comment {
14446
14593
  return visitor.visitComment(this, context);
14447
14594
  }
14448
14595
  }
14449
- class Block {
14450
- constructor(name, parameters, children, sourceSpan, nameSpan, startSourceSpan, endSourceSpan = null) {
14596
+ class Block extends NodeWithI18n {
14597
+ constructor(name, parameters, children, sourceSpan, nameSpan, startSourceSpan, endSourceSpan = null, i18n) {
14598
+ super(sourceSpan, i18n);
14451
14599
  this.name = name;
14452
14600
  this.parameters = parameters;
14453
14601
  this.children = children;
14454
- this.sourceSpan = sourceSpan;
14455
14602
  this.nameSpan = nameSpan;
14456
14603
  this.startSourceSpan = startSourceSpan;
14457
14604
  this.endSourceSpan = endSourceSpan;
@@ -15137,6 +15284,24 @@ class PlaceholderRegistry {
15137
15284
  getUniquePlaceholder(name) {
15138
15285
  return this._generateUniqueName(name.toUpperCase());
15139
15286
  }
15287
+ getStartBlockPlaceholderName(name, parameters) {
15288
+ const signature = this._hashBlock(name, parameters);
15289
+ if (this._signatureToName[signature]) {
15290
+ return this._signatureToName[signature];
15291
+ }
15292
+ const placeholder = this._generateUniqueName(`START_BLOCK_${this._toSnakeCase(name)}`);
15293
+ this._signatureToName[signature] = placeholder;
15294
+ return placeholder;
15295
+ }
15296
+ getCloseBlockPlaceholderName(name) {
15297
+ const signature = this._hashClosingBlock(name);
15298
+ if (this._signatureToName[signature]) {
15299
+ return this._signatureToName[signature];
15300
+ }
15301
+ const placeholder = this._generateUniqueName(`CLOSE_BLOCK_${this._toSnakeCase(name)}`);
15302
+ this._signatureToName[signature] = placeholder;
15303
+ return placeholder;
15304
+ }
15140
15305
  // Generate a hash for a tag - does not take attribute order into account
15141
15306
  _hashTag(tag, attrs, isVoid) {
15142
15307
  const start = `<${tag}`;
@@ -15147,6 +15312,16 @@ class PlaceholderRegistry {
15147
15312
  _hashClosingTag(tag) {
15148
15313
  return this._hashTag(`/${tag}`, {}, false);
15149
15314
  }
15315
+ _hashBlock(name, parameters) {
15316
+ const params = parameters.length === 0 ? '' : ` (${parameters.sort().join('; ')})`;
15317
+ return `@${name}${params} {}`;
15318
+ }
15319
+ _hashClosingBlock(name) {
15320
+ return this._hashBlock(`close_${name}`, []);
15321
+ }
15322
+ _toSnakeCase(name) {
15323
+ return name.toUpperCase().replace(/[^A-Z0-9]/g, '_');
15324
+ }
15150
15325
  _generateUniqueName(base) {
15151
15326
  const seen = this._placeHolderNameCounts.hasOwnProperty(base);
15152
15327
  if (!seen) {
@@ -15163,17 +15338,18 @@ const _expParser = new Parser$1(new Lexer());
15163
15338
  /**
15164
15339
  * Returns a function converting html nodes to an i18n Message given an interpolationConfig
15165
15340
  */
15166
- function createI18nMessageFactory(interpolationConfig) {
15167
- const visitor = new _I18nVisitor(_expParser, interpolationConfig);
15341
+ function createI18nMessageFactory(interpolationConfig, containerBlocks) {
15342
+ const visitor = new _I18nVisitor(_expParser, interpolationConfig, containerBlocks);
15168
15343
  return (nodes, meaning, description, customId, visitNodeFn) => visitor.toI18nMessage(nodes, meaning, description, customId, visitNodeFn);
15169
15344
  }
15170
15345
  function noopVisitNodeFn(_html, i18n) {
15171
15346
  return i18n;
15172
15347
  }
15173
15348
  class _I18nVisitor {
15174
- constructor(_expressionParser, _interpolationConfig) {
15349
+ constructor(_expressionParser, _interpolationConfig, _containerBlocks) {
15175
15350
  this._expressionParser = _expressionParser;
15176
15351
  this._interpolationConfig = _interpolationConfig;
15352
+ this._containerBlocks = _containerBlocks;
15177
15353
  }
15178
15354
  toI18nMessage(nodes, meaning = '', description = '', customId = '', visitNodeFn) {
15179
15355
  const context = {
@@ -15260,10 +15436,26 @@ class _I18nVisitor {
15260
15436
  }
15261
15437
  visitBlock(block, context) {
15262
15438
  const children = visitAll(this, block.children, context);
15263
- const node = new Container(children, block.sourceSpan);
15439
+ if (this._containerBlocks.has(block.name)) {
15440
+ return new Container(children, block.sourceSpan);
15441
+ }
15442
+ const parameters = block.parameters.map(param => param.expression);
15443
+ const startPhName = context.placeholderRegistry.getStartBlockPlaceholderName(block.name, parameters);
15444
+ const closePhName = context.placeholderRegistry.getCloseBlockPlaceholderName(block.name);
15445
+ context.placeholderToContent[startPhName] = {
15446
+ text: block.startSourceSpan.toString(),
15447
+ sourceSpan: block.startSourceSpan,
15448
+ };
15449
+ context.placeholderToContent[closePhName] = {
15450
+ text: block.endSourceSpan ? block.endSourceSpan.toString() : '}',
15451
+ sourceSpan: block.endSourceSpan ?? block.sourceSpan,
15452
+ };
15453
+ const node = new BlockPlaceholder(block.name, parameters, startPhName, closePhName, children, block.sourceSpan, block.startSourceSpan, block.endSourceSpan);
15264
15454
  return context.visitNodeFn(block, node);
15265
15455
  }
15266
- visitBlockParameter(_parameter, _context) { }
15456
+ visitBlockParameter(_parameter, _context) {
15457
+ throw new Error('Unreachable code');
15458
+ }
15267
15459
  /**
15268
15460
  * Convert, text and interpolated tokens up into text and placeholder pieces.
15269
15461
  *
@@ -19132,17 +19324,18 @@ const setI18nRefs = (htmlNode, i18nNode) => {
19132
19324
  * stored with other element's and attribute's information.
19133
19325
  */
19134
19326
  class I18nMetaVisitor {
19135
- constructor(interpolationConfig = DEFAULT_INTERPOLATION_CONFIG, keepI18nAttrs = false, enableI18nLegacyMessageIdFormat = false) {
19327
+ constructor(interpolationConfig = DEFAULT_INTERPOLATION_CONFIG, keepI18nAttrs = false, enableI18nLegacyMessageIdFormat = false, containerBlocks = DEFAULT_CONTAINER_BLOCKS) {
19136
19328
  this.interpolationConfig = interpolationConfig;
19137
19329
  this.keepI18nAttrs = keepI18nAttrs;
19138
19330
  this.enableI18nLegacyMessageIdFormat = enableI18nLegacyMessageIdFormat;
19331
+ this.containerBlocks = containerBlocks;
19139
19332
  // whether visited nodes contain i18n information
19140
19333
  this.hasI18nMeta = false;
19141
19334
  this._errors = [];
19142
19335
  }
19143
19336
  _generateI18nMessage(nodes, meta = '', visitNodeFn) {
19144
19337
  const { meaning, description, customId } = this._parseMetadata(meta);
19145
- const createI18nMessage = createI18nMessageFactory(this.interpolationConfig);
19338
+ const createI18nMessage = createI18nMessageFactory(this.interpolationConfig, this.containerBlocks);
19146
19339
  const message = createI18nMessage(nodes, meaning, description, customId, visitNodeFn);
19147
19340
  this._setMessageId(message, meta);
19148
19341
  this._setLegacyIds(message, meta);
@@ -19443,6 +19636,9 @@ class GetMsgSerializerVisitor {
19443
19636
  visitPlaceholder(ph) {
19444
19637
  return this.formatPh(ph.name);
19445
19638
  }
19639
+ visitBlockPlaceholder(ph) {
19640
+ return `${this.formatPh(ph.startName)}${ph.children.map(child => child.visit(this)).join('')}${this.formatPh(ph.closeName)}`;
19641
+ }
19446
19642
  visitIcuPlaceholder(ph, context) {
19447
19643
  return this.formatPh(ph.name);
19448
19644
  }
@@ -19496,6 +19692,11 @@ class LocalizeSerializerVisitor {
19496
19692
  visitPlaceholder(ph) {
19497
19693
  this.pieces.push(this.createPlaceholderPiece(ph.name, ph.sourceSpan));
19498
19694
  }
19695
+ visitBlockPlaceholder(ph) {
19696
+ this.pieces.push(this.createPlaceholderPiece(ph.startName, ph.startSourceSpan ?? ph.sourceSpan));
19697
+ ph.children.forEach(child => child.visit(this));
19698
+ this.pieces.push(this.createPlaceholderPiece(ph.closeName, ph.endSourceSpan ?? ph.sourceSpan));
19699
+ }
19499
19700
  visitIcuPlaceholder(ph) {
19500
19701
  this.pieces.push(this.createPlaceholderPiece(ph.name, ph.sourceSpan, this.placeholderToMessage[ph.name]));
19501
19702
  }
@@ -19826,57 +20027,6 @@ function serializeLocalRefs(refs) {
19826
20027
  return literalArr(constRefs);
19827
20028
  }
19828
20029
 
19829
- /**
19830
- * Merge i18n contexts for child i18n blocks into their ancestor root contexts.
19831
- */
19832
- function mergeI18nContexts(job) {
19833
- // Record all of the i18n and extracted message ops for use later.
19834
- const i18nOps = new Map();
19835
- const i18nContexts = new Map();
19836
- for (const unit of job.units) {
19837
- for (const op of unit.create) {
19838
- switch (op.kind) {
19839
- case OpKind.I18nStart:
19840
- if (!op.context) {
19841
- throw Error('I18n op should have its context set.');
19842
- }
19843
- i18nOps.set(op.xref, op);
19844
- break;
19845
- case OpKind.I18nContext:
19846
- i18nContexts.set(op.xref, op);
19847
- break;
19848
- }
19849
- }
19850
- }
19851
- // For each non-root i18n op, merge its context into the root i18n op's context.
19852
- for (const childI18nOp of i18nOps.values()) {
19853
- if (childI18nOp.xref !== childI18nOp.root) {
19854
- const childContext = i18nContexts.get(childI18nOp.context);
19855
- const rootI18nOp = i18nOps.get(childI18nOp.root);
19856
- const rootContext = i18nContexts.get(rootI18nOp.context);
19857
- mergeParams(rootContext.params, childContext.params);
19858
- mergeParams(rootContext.postprocessingParams, childContext.postprocessingParams);
19859
- }
19860
- }
19861
- }
19862
- /**
19863
- * Merges the params in the `from` map to into the `to` map.
19864
- */
19865
- function mergeParams(to, from) {
19866
- for (const [placeholder, fromValues] of from) {
19867
- const toValues = to.get(placeholder) || [];
19868
- // TODO(mmalerba): Child element close tag params should be prepended to maintain the same order
19869
- // as TemplateDefinitionBuilder. Can be cleaned up when compatibility is no longer required.
19870
- const flags = fromValues[0].flags;
19871
- if ((flags & I18nParamValueFlags.CloseTag) && !(flags & I18nParamValueFlags.OpenTag)) {
19872
- to.set(placeholder, [...fromValues, ...toValues]);
19873
- }
19874
- else {
19875
- to.set(placeholder, [...toValues, ...fromValues]);
19876
- }
19877
- }
19878
- }
19879
-
19880
20030
  /**
19881
20031
  * Change namespaces between HTML, SVG and MathML, depending on the next element.
19882
20032
  */
@@ -20540,9 +20690,10 @@ function propagateI18nBlocksToTemplates(unit, subTemplateIndex) {
20540
20690
  wrapTemplateWithI18n(templateView, i18nBlock);
20541
20691
  }
20542
20692
  // Continue traversing inside the template's view.
20543
- propagateI18nBlocksToTemplates(templateView, subTemplateIndex);
20693
+ subTemplateIndex = propagateI18nBlocksToTemplates(templateView, subTemplateIndex);
20544
20694
  }
20545
20695
  }
20696
+ return subTemplateIndex;
20546
20697
  }
20547
20698
  /**
20548
20699
  * Wraps a template view with i18n start and end ops.
@@ -20850,8 +21001,8 @@ function repeaterCreate(slot, viewFnName, decls, vars, tag, constIndex, trackByF
20850
21001
  }
20851
21002
  return call(Identifiers.repeaterCreate, args, sourceSpan);
20852
21003
  }
20853
- function repeater(metadataSlot, collection, sourceSpan) {
20854
- return call(Identifiers.repeater, [literal(metadataSlot), collection], sourceSpan);
21004
+ function repeater(collection, sourceSpan) {
21005
+ return call(Identifiers.repeater, [collection], sourceSpan);
20855
21006
  }
20856
21007
  function deferWhen(prefetch, expr, sourceSpan) {
20857
21008
  return call(prefetch ? Identifiers.deferPrefetchWhen : Identifiers.deferWhen, [expr], sourceSpan);
@@ -21459,7 +21610,7 @@ function reifyUpdateOperations(_unit, ops) {
21459
21610
  OpList.replace(op, conditional(op.targetSlot.slot, op.processed, op.contextValue, op.sourceSpan));
21460
21611
  break;
21461
21612
  case OpKind.Repeater:
21462
- OpList.replace(op, repeater(op.targetSlot.slot, op.collection, op.sourceSpan));
21613
+ OpList.replace(op, repeater(op.collection, op.sourceSpan));
21463
21614
  break;
21464
21615
  case OpKind.DeferWhen:
21465
21616
  OpList.replace(op, deferWhen(op.prefetch, op.expr, op.sourceSpan));
@@ -21722,66 +21873,79 @@ function resolveI18nElementPlaceholders(job) {
21722
21873
  }
21723
21874
  }
21724
21875
  }
21725
- for (const unit of job.units) {
21726
- // Track the current i18n op and corresponding i18n context op as we step through the creation
21727
- // IR.
21728
- let currentOps = null;
21729
- for (const op of unit.create) {
21730
- switch (op.kind) {
21731
- case OpKind.I18nStart:
21732
- if (!op.context) {
21733
- throw Error('Could not find i18n context for i18n op');
21876
+ resolvePlaceholdersForView(job, job.root, i18nContexts, elements);
21877
+ }
21878
+ function resolvePlaceholdersForView(job, unit, i18nContexts, elements) {
21879
+ // Track the current i18n op and corresponding i18n context op as we step through the creation
21880
+ // IR.
21881
+ let currentOps = null;
21882
+ for (const op of unit.create) {
21883
+ switch (op.kind) {
21884
+ case OpKind.I18nStart:
21885
+ if (!op.context) {
21886
+ throw Error('Could not find i18n context for i18n op');
21887
+ }
21888
+ currentOps = { i18nBlock: op, i18nContext: i18nContexts.get(op.context) };
21889
+ break;
21890
+ case OpKind.I18nEnd:
21891
+ currentOps = null;
21892
+ break;
21893
+ case OpKind.ElementStart:
21894
+ // For elements with i18n placeholders, record its slot value in the params map under the
21895
+ // corresponding tag start placeholder.
21896
+ if (op.i18nPlaceholder !== undefined) {
21897
+ if (currentOps === null) {
21898
+ throw Error('i18n tag placeholder should only occur inside an i18n block');
21734
21899
  }
21735
- currentOps = { i18nBlock: op, i18nContext: i18nContexts.get(op.context) };
21736
- break;
21737
- case OpKind.I18nEnd:
21738
- currentOps = null;
21739
- break;
21740
- case OpKind.ElementStart:
21741
- // For elements with i18n placeholders, record its slot value in the params map under the
21742
- // corresponding tag start placeholder.
21743
- if (op.i18nPlaceholder !== undefined) {
21744
- if (currentOps === null) {
21745
- throw Error('i18n tag placeholder should only occur inside an i18n block');
21746
- }
21747
- const { startName, closeName } = op.i18nPlaceholder;
21748
- let flags = I18nParamValueFlags.ElementTag | I18nParamValueFlags.OpenTag;
21749
- // For self-closing tags, there is no close tag placeholder. Instead, the start tag
21750
- // placeholder accounts for the start and close of the element.
21751
- if (closeName === '') {
21752
- flags |= I18nParamValueFlags.CloseTag;
21753
- }
21754
- addParam(currentOps.i18nContext.params, startName, op.handle.slot, currentOps.i18nBlock.subTemplateIndex, flags);
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;
21755
21906
  }
21756
- break;
21757
- case OpKind.ElementEnd:
21758
- // For elements with i18n placeholders, record its slot value in the params map under the
21759
- // corresponding tag close placeholder.
21760
- const startOp = elements.get(op.xref);
21761
- if (startOp && startOp.i18nPlaceholder !== undefined) {
21762
- if (currentOps === null) {
21763
- throw Error('i18n tag placeholder should only occur inside an i18n block');
21764
- }
21765
- const { closeName } = startOp.i18nPlaceholder;
21766
- // Self-closing tags don't have a closing tag placeholder.
21767
- if (closeName !== '') {
21768
- addParam(currentOps.i18nContext.params, closeName, startOp.handle.slot, currentOps.i18nBlock.subTemplateIndex, I18nParamValueFlags.ElementTag | I18nParamValueFlags.CloseTag);
21769
- }
21907
+ addParam(currentOps.i18nContext.params, startName, op.handle.slot, currentOps.i18nBlock.subTemplateIndex, flags);
21908
+ }
21909
+ break;
21910
+ case OpKind.ElementEnd:
21911
+ // For elements with i18n placeholders, record its slot value in the params map under the
21912
+ // corresponding tag close placeholder.
21913
+ const startOp = elements.get(op.xref);
21914
+ if (startOp && startOp.i18nPlaceholder !== undefined) {
21915
+ if (currentOps === null) {
21916
+ throw Error('i18n tag placeholder should only occur inside an i18n block');
21770
21917
  }
21771
- break;
21772
- case OpKind.Template:
21773
- // For templates with i18n placeholders, record its slot value in the params map under the
21774
- // corresponding template start and close placeholders.
21775
- if (op.i18nPlaceholder !== undefined) {
21776
- if (currentOps === null) {
21777
- throw Error('i18n tag placeholder should only occur inside an i18n block');
21778
- }
21779
- const subTemplateIndex = getSubTemplateIndexForTemplateTag(job, currentOps.i18nBlock, op);
21780
- addParam(currentOps.i18nContext.params, op.i18nPlaceholder.startName, op.handle.slot, subTemplateIndex, I18nParamValueFlags.TemplateTag);
21781
- addParam(currentOps.i18nContext.params, op.i18nPlaceholder.closeName, op.handle.slot, subTemplateIndex, I18nParamValueFlags.TemplateTag | I18nParamValueFlags.CloseTag);
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);
21782
21922
  }
21783
- break;
21784
- }
21923
+ }
21924
+ 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.
21928
+ if (op.i18nPlaceholder !== undefined) {
21929
+ if (currentOps === null) {
21930
+ throw Error('i18n tag placeholder should only occur inside an i18n block');
21931
+ }
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);
21940
+ 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
+ }
21945
+ else {
21946
+ resolvePlaceholdersForView(job, job.views.get(op.xref), i18nContexts, elements);
21947
+ }
21948
+ break;
21785
21949
  }
21786
21950
  }
21787
21951
  }
@@ -21829,8 +21993,8 @@ function resolveI18nExpressionPlaceholders(job) {
21829
21993
  for (const op of unit.update) {
21830
21994
  if (op.kind === OpKind.I18nExpression) {
21831
21995
  const i18nContext = i18nContexts.get(op.context);
21832
- const index = expressionIndices.get(i18nContext.i18nBlock) || 0;
21833
- const subTemplateIndex = subTemplateIndicies.get(i18nContext.i18nBlock);
21996
+ const index = expressionIndices.get(op.target) || 0;
21997
+ const subTemplateIndex = subTemplateIndicies.get(op.target);
21834
21998
  // Add the expression index in the appropriate params map.
21835
21999
  const params = op.resolutionTime === I18nParamResolutionTime.Creation ?
21836
22000
  i18nContext.params :
@@ -21842,7 +22006,7 @@ function resolveI18nExpressionPlaceholders(job) {
21842
22006
  flags: I18nParamValueFlags.ExpressionIndex
21843
22007
  });
21844
22008
  params.set(op.i18nPlaceholder, values);
21845
- expressionIndices.set(i18nContext.i18nBlock, index + 1);
22009
+ expressionIndices.set(op.target, index + 1);
21846
22010
  }
21847
22011
  }
21848
22012
  }
@@ -21852,28 +22016,12 @@ function resolveI18nExpressionPlaceholders(job) {
21852
22016
  * Resolves placeholders for element tags inside of an ICU.
21853
22017
  */
21854
22018
  function resolveI18nIcuPlaceholders(job) {
21855
- const contextOps = new Map();
21856
- for (const unit of job.units) {
21857
- for (const op of unit.create) {
21858
- switch (op.kind) {
21859
- case OpKind.I18nContext:
21860
- contextOps.set(op.xref, op);
21861
- break;
21862
- }
21863
- }
21864
- }
21865
22019
  for (const unit of job.units) {
21866
22020
  for (const op of unit.create) {
21867
- switch (op.kind) {
21868
- case OpKind.IcuStart:
21869
- if (op.context === null) {
21870
- throw Error('Icu should have its i18n context set.');
21871
- }
21872
- const i18nContext = contextOps.get(op.context);
21873
- for (const node of op.message.nodes) {
21874
- node.visit(new ResolveIcuPlaceholdersVisitor(i18nContext.postprocessingParams));
21875
- }
21876
- break;
22021
+ if (op.kind === OpKind.I18nContext && op.contextKind === I18nContextKind.Icu) {
22022
+ for (const node of op.message.nodes) {
22023
+ node.visit(new ResolveIcuPlaceholdersVisitor(op.postprocessingParams));
22024
+ }
21877
22025
  }
21878
22026
  }
21879
22027
  }
@@ -21886,10 +22034,9 @@ class ResolveIcuPlaceholdersVisitor extends RecurseVisitor {
21886
22034
  super();
21887
22035
  this.params = params;
21888
22036
  }
21889
- visitTagPlaceholder(placeholder) {
21890
- super.visitTagPlaceholder(placeholder);
21891
- // Add the start and end source span for tag placeholders. These need to be recorded for
21892
- // elements inside ICUs. The slots for the elements were recorded separately under the i18n
22037
+ visitContainerPlaceholder(placeholder) {
22038
+ // Add the start and end source span for container placeholders. These need to be recorded for
22039
+ // elements inside ICUs. The slots for the nodes were recorded separately under the i18n
21893
22040
  // block's context as part of the `resolveI18nElementPlaceholders` phase.
21894
22041
  if (placeholder.startName && placeholder.startSourceSpan &&
21895
22042
  !this.params.has(placeholder.startName)) {
@@ -21908,6 +22055,14 @@ class ResolveIcuPlaceholdersVisitor extends RecurseVisitor {
21908
22055
  }]);
21909
22056
  }
21910
22057
  }
22058
+ visitTagPlaceholder(placeholder) {
22059
+ super.visitTagPlaceholder(placeholder);
22060
+ this.visitContainerPlaceholder(placeholder);
22061
+ }
22062
+ visitBlockPlaceholder(placeholder) {
22063
+ super.visitBlockPlaceholder(placeholder);
22064
+ this.visitContainerPlaceholder(placeholder);
22065
+ }
21911
22066
  }
21912
22067
 
21913
22068
  /**
@@ -23036,7 +23191,6 @@ const phases = [
23036
23191
  { kind: CompilationJobKind.Tmpl, fn: resolveI18nElementPlaceholders },
23037
23192
  { kind: CompilationJobKind.Tmpl, fn: resolveI18nExpressionPlaceholders },
23038
23193
  { kind: CompilationJobKind.Tmpl, fn: resolveI18nIcuPlaceholders },
23039
- { kind: CompilationJobKind.Tmpl, fn: mergeI18nContexts },
23040
23194
  { kind: CompilationJobKind.Tmpl, fn: extractI18nMessages },
23041
23195
  { kind: CompilationJobKind.Tmpl, fn: generateTrackFns },
23042
23196
  { kind: CompilationJobKind.Tmpl, fn: collectI18nConsts },
@@ -25102,19 +25256,19 @@ function createIfBlock(ast, connectedBlocks, visitor, bindingParser) {
25102
25256
  const branches = [];
25103
25257
  const mainBlockParams = parseConditionalBlockParameters(ast, errors, bindingParser);
25104
25258
  if (mainBlockParams !== null) {
25105
- branches.push(new IfBlockBranch(mainBlockParams.expression, visitAll(visitor, ast.children, ast.children), mainBlockParams.expressionAlias, ast.sourceSpan, ast.startSourceSpan, ast.endSourceSpan, ast.nameSpan));
25259
+ branches.push(new IfBlockBranch(mainBlockParams.expression, visitAll(visitor, ast.children, ast.children), mainBlockParams.expressionAlias, ast.sourceSpan, ast.startSourceSpan, ast.endSourceSpan, ast.nameSpan, ast.i18n));
25106
25260
  }
25107
25261
  for (const block of connectedBlocks) {
25108
25262
  if (ELSE_IF_PATTERN.test(block.name)) {
25109
25263
  const params = parseConditionalBlockParameters(block, errors, bindingParser);
25110
25264
  if (params !== null) {
25111
25265
  const children = visitAll(visitor, block.children, block.children);
25112
- branches.push(new IfBlockBranch(params.expression, children, params.expressionAlias, block.sourceSpan, block.startSourceSpan, block.endSourceSpan, block.nameSpan));
25266
+ branches.push(new IfBlockBranch(params.expression, children, params.expressionAlias, block.sourceSpan, block.startSourceSpan, block.endSourceSpan, block.nameSpan, block.i18n));
25113
25267
  }
25114
25268
  }
25115
25269
  else if (block.name === 'else') {
25116
25270
  const children = visitAll(visitor, block.children, block.children);
25117
- branches.push(new IfBlockBranch(null, children, null, block.sourceSpan, block.startSourceSpan, block.endSourceSpan, block.nameSpan));
25271
+ branches.push(new IfBlockBranch(null, children, null, block.sourceSpan, block.startSourceSpan, block.endSourceSpan, block.nameSpan, block.i18n));
25118
25272
  }
25119
25273
  }
25120
25274
  // The outer IfBlock should have a span that encapsulates all branches.
@@ -25145,7 +25299,7 @@ function createForLoop(ast, connectedBlocks, visitor, bindingParser) {
25145
25299
  errors.push(new ParseError(block.sourceSpan, '@empty block cannot have parameters'));
25146
25300
  }
25147
25301
  else {
25148
- empty = new ForLoopBlockEmpty(visitAll(visitor, block.children, block.children), block.sourceSpan, block.startSourceSpan, block.endSourceSpan, block.nameSpan);
25302
+ empty = new ForLoopBlockEmpty(visitAll(visitor, block.children, block.children), block.sourceSpan, block.startSourceSpan, block.endSourceSpan, block.nameSpan, block.i18n);
25149
25303
  }
25150
25304
  }
25151
25305
  else {
@@ -25163,7 +25317,7 @@ function createForLoop(ast, connectedBlocks, visitor, bindingParser) {
25163
25317
  // main `for` body, use `mainSourceSpan`.
25164
25318
  const endSpan = empty?.endSourceSpan ?? ast.endSourceSpan;
25165
25319
  const sourceSpan = new ParseSourceSpan(ast.sourceSpan.start, endSpan?.end ?? ast.sourceSpan.end);
25166
- 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);
25320
+ 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);
25167
25321
  }
25168
25322
  }
25169
25323
  return { node, errors };
@@ -25189,7 +25343,7 @@ function createSwitchBlock(ast, visitor, bindingParser) {
25189
25343
  const expression = node.name === 'case' ?
25190
25344
  parseBlockParameterToBinding(node.parameters[0], bindingParser) :
25191
25345
  null;
25192
- const ast = new SwitchBlockCase(expression, visitAll(visitor, node.children, node.children), node.sourceSpan, node.startSourceSpan, node.endSourceSpan, node.nameSpan);
25346
+ const ast = new SwitchBlockCase(expression, visitAll(visitor, node.children, node.children), node.sourceSpan, node.startSourceSpan, node.endSourceSpan, node.nameSpan, node.i18n);
25193
25347
  if (expression === null) {
25194
25348
  defaultCase = ast;
25195
25349
  }
@@ -25776,7 +25930,7 @@ function createDeferredBlock(ast, connectedBlocks, visitor, bindingParser) {
25776
25930
  endOfLastSourceSpan = lastConnectedBlock.sourceSpan.end;
25777
25931
  }
25778
25932
  const sourceSpanWithConnectedBlocks = new ParseSourceSpan(ast.sourceSpan.start, endOfLastSourceSpan);
25779
- const node = new DeferredBlock(visitAll(visitor, ast.children, ast.children), triggers, prefetchTriggers, placeholder, loading, error, ast.nameSpan, sourceSpanWithConnectedBlocks, ast.sourceSpan, ast.startSourceSpan, lastEndSourceSpan);
25933
+ 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);
25780
25934
  return { node, errors };
25781
25935
  }
25782
25936
  function parseConnectedBlocks(connectedBlocks, errors, visitor) {
@@ -25839,7 +25993,7 @@ function parsePlaceholderBlock(ast, visitor) {
25839
25993
  throw new Error(`Unrecognized parameter in @placeholder block: "${param.expression}"`);
25840
25994
  }
25841
25995
  }
25842
- return new DeferredBlockPlaceholder(visitAll(visitor, ast.children, ast.children), minimumTime, ast.nameSpan, ast.sourceSpan, ast.startSourceSpan, ast.endSourceSpan);
25996
+ return new DeferredBlockPlaceholder(visitAll(visitor, ast.children, ast.children), minimumTime, ast.nameSpan, ast.sourceSpan, ast.startSourceSpan, ast.endSourceSpan, ast.i18n);
25843
25997
  }
25844
25998
  function parseLoadingBlock(ast, visitor) {
25845
25999
  let afterTime = null;
@@ -25869,13 +26023,13 @@ function parseLoadingBlock(ast, visitor) {
25869
26023
  throw new Error(`Unrecognized parameter in @loading block: "${param.expression}"`);
25870
26024
  }
25871
26025
  }
25872
- return new DeferredBlockLoading(visitAll(visitor, ast.children, ast.children), afterTime, minimumTime, ast.nameSpan, ast.sourceSpan, ast.startSourceSpan, ast.endSourceSpan);
26026
+ return new DeferredBlockLoading(visitAll(visitor, ast.children, ast.children), afterTime, minimumTime, ast.nameSpan, ast.sourceSpan, ast.startSourceSpan, ast.endSourceSpan, ast.i18n);
25873
26027
  }
25874
26028
  function parseErrorBlock(ast, visitor) {
25875
26029
  if (ast.parameters.length > 0) {
25876
26030
  throw new Error(`@error block cannot have parameters`);
25877
26031
  }
25878
- return new DeferredBlockError(visitAll(visitor, ast.children, ast.children), ast.nameSpan, ast.sourceSpan, ast.startSourceSpan, ast.endSourceSpan);
26032
+ return new DeferredBlockError(visitAll(visitor, ast.children, ast.children), ast.nameSpan, ast.sourceSpan, ast.startSourceSpan, ast.endSourceSpan, ast.i18n);
25879
26033
  }
25880
26034
  function parsePrimaryTriggers(params, bindingParser, errors, placeholder) {
25881
26035
  const triggers = {};
@@ -26475,6 +26629,11 @@ class I18nContext {
26475
26629
  const content = { type, index, ctx: this.id, isVoid: node.isVoid, closed };
26476
26630
  updatePlaceholderMap(this.placeholders, ph, content);
26477
26631
  }
26632
+ appendBlockPart(node, index, closed) {
26633
+ const ph = closed ? node.closeName : node.startName;
26634
+ const content = { type: TagType.TEMPLATE, index, ctx: this.id, closed };
26635
+ updatePlaceholderMap(this.placeholders, ph, content);
26636
+ }
26478
26637
  get icus() {
26479
26638
  return this._registry.icus;
26480
26639
  }
@@ -26507,6 +26666,13 @@ class I18nContext {
26507
26666
  this.appendTag(TagType.TEMPLATE, node, index, true);
26508
26667
  this._unresolvedCtxCount++;
26509
26668
  }
26669
+ appendBlock(node, index) {
26670
+ // add open and close tags at the same time,
26671
+ // since we process nested templates separately
26672
+ this.appendBlockPart(node, index, false);
26673
+ this.appendBlockPart(node, index, true);
26674
+ this._unresolvedCtxCount++;
26675
+ }
26510
26676
  appendElement(node, index, closed) {
26511
26677
  this.appendTag(TagType.ELEMENT, node, index, closed);
26512
26678
  }
@@ -26785,13 +26951,19 @@ class TemplateDefinitionBuilder {
26785
26951
  this.creationInstruction(null, Identifiers.pipe, [literal(slot), literal(name)]);
26786
26952
  });
26787
26953
  }
26788
- buildTemplateFunction(nodes, variables, ngContentSelectorsOffset = 0, i18n) {
26954
+ buildTemplateFunction(nodes, variables, ngContentSelectorsOffset = 0, i18n, variableAliases) {
26789
26955
  this._ngContentSelectorsOffset = ngContentSelectorsOffset;
26790
26956
  if (this._namespace !== Identifiers.namespaceHTML) {
26791
26957
  this.creationInstruction(null, this._namespace);
26792
26958
  }
26793
26959
  // Create variable bindings
26794
- variables.forEach(v => this.registerContextVariables(v));
26960
+ variables.forEach(v => {
26961
+ const alias = variableAliases?.[v.name];
26962
+ this.registerContextVariables(v.name, v.value);
26963
+ if (alias) {
26964
+ this.registerContextVariables(alias, v.value);
26965
+ }
26966
+ });
26795
26967
  // Initiate i18n context in case:
26796
26968
  // - this template has parent i18n context
26797
26969
  // - or the template has i18n meta associated with it,
@@ -26885,12 +27057,12 @@ class TemplateDefinitionBuilder {
26885
27057
  this._constants.prepareStatements.push(...statements);
26886
27058
  return _ref;
26887
27059
  }
26888
- registerContextVariables(variable$1) {
27060
+ registerContextVariables(name, value) {
26889
27061
  const scopedName = this._bindingScope.freshReferenceName();
26890
27062
  const retrievalLevel = this.level;
26891
- const isDirect = variable$1.value === DIRECT_CONTEXT_REFERENCE;
26892
- const lhs = variable(variable$1.name + scopedName);
26893
- this._bindingScope.set(retrievalLevel, variable$1.name, scope => {
27063
+ const isDirect = value === DIRECT_CONTEXT_REFERENCE;
27064
+ const lhs = variable(name + scopedName);
27065
+ this._bindingScope.set(retrievalLevel, name, scope => {
26894
27066
  // If we're at the top level and we're referring to the context variable directly, we
26895
27067
  // can do so through the implicit receiver, instead of renaming it. Note that this does
26896
27068
  // not apply to listeners, because they need to restore the context.
@@ -26926,7 +27098,7 @@ class TemplateDefinitionBuilder {
26926
27098
  return [
26927
27099
  // e.g. const $items$ = x(2) for direct context references and
26928
27100
  // const $item$ = x(2).$implicit for indirect ones.
26929
- lhs.set(isDirect ? rhs : rhs.prop(variable$1.value || IMPLICIT_REFERENCE)).toConstDecl()
27101
+ lhs.set(isDirect ? rhs : rhs.prop(value || IMPLICIT_REFERENCE)).toConstDecl()
26930
27102
  ];
26931
27103
  });
26932
27104
  }
@@ -27349,10 +27521,15 @@ class TemplateDefinitionBuilder {
27349
27521
  this.creationInstruction(span, isNgContainer$1 ? Identifiers.elementContainerEnd : Identifiers.elementEnd);
27350
27522
  }
27351
27523
  }
27352
- prepareEmbeddedTemplateFn(children, contextNameSuffix, variables = [], i18n) {
27524
+ prepareEmbeddedTemplateFn(children, contextNameSuffix, variables = [], i18nMeta, variableAliases) {
27353
27525
  const index = this.allocateDataSlot();
27354
- if (this.i18n && i18n) {
27355
- this.i18n.appendTemplate(i18n, index);
27526
+ if (this.i18n && i18nMeta) {
27527
+ if (i18nMeta instanceof BlockPlaceholder) {
27528
+ this.i18n.appendBlock(i18nMeta, index);
27529
+ }
27530
+ else {
27531
+ this.i18n.appendTemplate(i18nMeta, index);
27532
+ }
27356
27533
  }
27357
27534
  const contextName = `${this.contextName}${contextNameSuffix}_${index}`;
27358
27535
  const name = `${contextName}_Template`;
@@ -27363,7 +27540,7 @@ class TemplateDefinitionBuilder {
27363
27540
  // be able to support bindings in nested templates to local refs that occur after the
27364
27541
  // template definition. e.g. <div *ngIf="showing">{{ foo }}</div> <div #foo></div>
27365
27542
  this._nestedTemplateFns.push(() => {
27366
- const templateFunctionExpr = visitor.buildTemplateFunction(children, variables, this._ngContentReservedSlots.length + this._ngContentSelectorsOffset, i18n);
27543
+ const templateFunctionExpr = visitor.buildTemplateFunction(children, variables, this._ngContentReservedSlots.length + this._ngContentSelectorsOffset, i18nMeta, variableAliases);
27367
27544
  this.constantPool.statements.push(templateFunctionExpr.toDeclStmt(name));
27368
27545
  if (visitor._ngContentReservedSlots.length) {
27369
27546
  this._ngContentReservedSlots.push(...visitor._ngContentReservedSlots);
@@ -27520,7 +27697,7 @@ class TemplateDefinitionBuilder {
27520
27697
  }
27521
27698
  // Note: the template needs to be created *before* we process the expression,
27522
27699
  // otherwise pipes injecting some symbols won't work (see #52102).
27523
- const templateIndex = this.createEmbeddedTemplateFn(tagName, children, '_Conditional', sourceSpan, variables, attrsExprs);
27700
+ const templateIndex = this.createEmbeddedTemplateFn(tagName, children, '_Conditional', sourceSpan, variables, attrsExprs, undefined, branch.i18n);
27524
27701
  const processedExpression = expression === null ? null : expression.visit(this._valueConverter);
27525
27702
  return { index: templateIndex, expression: processedExpression, alias: expressionAlias };
27526
27703
  });
@@ -27571,7 +27748,7 @@ class TemplateDefinitionBuilder {
27571
27748
  // We have to process the block in two steps: once here and again in the update instruction
27572
27749
  // callback in order to generate the correct expressions when pipes or pure functions are used.
27573
27750
  const caseData = block.cases.map(currentCase => {
27574
- const index = this.createEmbeddedTemplateFn(null, currentCase.children, '_Case', currentCase.sourceSpan);
27751
+ const index = this.createEmbeddedTemplateFn(null, currentCase.children, '_Case', currentCase.sourceSpan, undefined, undefined, undefined, currentCase.i18n);
27575
27752
  const expression = currentCase.expression === null ?
27576
27753
  null :
27577
27754
  currentCase.expression.visit(this._valueConverter);
@@ -27619,23 +27796,21 @@ class TemplateDefinitionBuilder {
27619
27796
  if (!metadata) {
27620
27797
  throw new Error('Could not resolve `defer` block metadata. Block may need to be analyzed.');
27621
27798
  }
27622
- const primaryTemplateIndex = this.createEmbeddedTemplateFn(null, deferred.children, '_Defer', deferred.sourceSpan);
27623
- const loadingIndex = loading ?
27624
- this.createEmbeddedTemplateFn(null, loading.children, '_DeferLoading', loading.sourceSpan) :
27799
+ const primaryTemplateIndex = this.createEmbeddedTemplateFn(null, deferred.children, '_Defer', deferred.sourceSpan, undefined, undefined, undefined, deferred.i18n);
27800
+ const loadingIndex = loading ? this.createEmbeddedTemplateFn(null, loading.children, '_DeferLoading', loading.sourceSpan, undefined, undefined, undefined, loading.i18n) :
27625
27801
  null;
27626
27802
  const loadingConsts = loading ?
27627
27803
  trimTrailingNulls([literal(loading.minimumTime), literal(loading.afterTime)]) :
27628
27804
  null;
27629
27805
  const placeholderIndex = placeholder ?
27630
- this.createEmbeddedTemplateFn(null, placeholder.children, '_DeferPlaceholder', placeholder.sourceSpan) :
27806
+ this.createEmbeddedTemplateFn(null, placeholder.children, '_DeferPlaceholder', placeholder.sourceSpan, undefined, undefined, undefined, placeholder.i18n) :
27631
27807
  null;
27632
27808
  const placeholderConsts = placeholder && placeholder.minimumTime !== null ?
27633
27809
  // TODO(crisbeto): potentially pass the time directly instead of storing it in the `consts`
27634
27810
  // since the placeholder block can only have one parameter?
27635
27811
  literalArr([literal(placeholder.minimumTime)]) :
27636
27812
  null;
27637
- const errorIndex = error ?
27638
- this.createEmbeddedTemplateFn(null, error.children, '_DeferError', error.sourceSpan) :
27813
+ const errorIndex = error ? this.createEmbeddedTemplateFn(null, error.children, '_DeferError', error.sourceSpan, undefined, undefined, undefined, error.i18n) :
27639
27814
  null;
27640
27815
  // Note: we generate this last so the index matches the instruction order.
27641
27816
  const deferredIndex = this.allocateDataSlot();
@@ -27789,11 +27964,18 @@ class TemplateDefinitionBuilder {
27789
27964
  // are implicitly inferred by the runtime to index + 1 and index + 2.
27790
27965
  const blockIndex = this.allocateDataSlot();
27791
27966
  const { tagName, attrsExprs } = this.inferProjectionDataFromInsertionPoint(block);
27792
- const primaryData = this.prepareEmbeddedTemplateFn(block.children, '_For', [block.item, block.contextVariables.$index, block.contextVariables.$count]);
27967
+ const primaryData = this.prepareEmbeddedTemplateFn(block.children, '_For', [block.item, block.contextVariables.$index, block.contextVariables.$count], block.i18n, {
27968
+ // We need to provide level-specific versions of `$index` and `$count`, because
27969
+ // they're used when deriving the remaining variables (`$odd`, `$even` etc.) while at the
27970
+ // same time being available implicitly. Without these aliases, we wouldn't be able to
27971
+ // access the `$index` of a parent loop from inside of a nested loop.
27972
+ [block.contextVariables.$index.name]: this.getLevelSpecificVariableName('$index', this.level + 1),
27973
+ [block.contextVariables.$count.name]: this.getLevelSpecificVariableName('$count', this.level + 1),
27974
+ });
27793
27975
  const { expression: trackByExpression, usesComponentInstance: trackByUsesComponentInstance } = this.createTrackByFunction(block);
27794
27976
  let emptyData = null;
27795
27977
  if (block.empty !== null) {
27796
- emptyData = this.prepareEmbeddedTemplateFn(block.empty.children, '_ForEmpty');
27978
+ emptyData = this.prepareEmbeddedTemplateFn(block.empty.children, '_ForEmpty', undefined, block.empty.i18n);
27797
27979
  // Allocate an extra slot for the empty block tracking.
27798
27980
  this.allocateBindingSlots(null);
27799
27981
  }
@@ -27823,17 +28005,44 @@ class TemplateDefinitionBuilder {
27823
28005
  // Note: we don't allocate binding slots for this expression,
27824
28006
  // because its value isn't stored in the LView.
27825
28007
  const value = block.expression.visit(this._valueConverter);
27826
- // `repeater(0, iterable)`
27827
- this.updateInstruction(block.sourceSpan, Identifiers.repeater, () => [literal(blockIndex), this.convertPropertyBinding(value)]);
28008
+ // `advance(x); repeater(iterable)`
28009
+ this.updateInstructionWithAdvance(blockIndex, block.sourceSpan, Identifiers.repeater, () => [this.convertPropertyBinding(value)]);
27828
28010
  }
27829
28011
  registerComputedLoopVariables(block, bindingScope) {
27830
- const indexLocalName = block.contextVariables.$index.name;
27831
- const countLocalName = block.contextVariables.$count.name;
27832
28012
  const level = bindingScope.bindingLevel;
27833
- bindingScope.set(level, block.contextVariables.$odd.name, scope => scope.get(indexLocalName).modulo(literal(2)).notIdentical(literal(0)));
27834
- bindingScope.set(level, block.contextVariables.$even.name, scope => scope.get(indexLocalName).modulo(literal(2)).identical(literal(0)));
27835
- bindingScope.set(level, block.contextVariables.$first.name, scope => scope.get(indexLocalName).identical(literal(0)));
27836
- bindingScope.set(level, block.contextVariables.$last.name, scope => scope.get(indexLocalName).identical(scope.get(countLocalName).minus(literal(1))));
28013
+ bindingScope.set(level, block.contextVariables.$odd.name, (scope, retrievalLevel) => {
28014
+ return this.getLevelSpecificForLoopVariable(block, scope, retrievalLevel, '$index')
28015
+ .modulo(literal(2))
28016
+ .notIdentical(literal(0));
28017
+ });
28018
+ bindingScope.set(level, block.contextVariables.$even.name, (scope, retrievalLevel) => {
28019
+ return this.getLevelSpecificForLoopVariable(block, scope, retrievalLevel, '$index')
28020
+ .modulo(literal(2))
28021
+ .identical(literal(0));
28022
+ });
28023
+ bindingScope.set(level, block.contextVariables.$first.name, (scope, retrievalLevel) => {
28024
+ return this.getLevelSpecificForLoopVariable(block, scope, retrievalLevel, '$index')
28025
+ .identical(literal(0));
28026
+ });
28027
+ bindingScope.set(level, block.contextVariables.$last.name, (scope, retrievalLevel) => {
28028
+ const index = this.getLevelSpecificForLoopVariable(block, scope, retrievalLevel, '$index');
28029
+ const count = this.getLevelSpecificForLoopVariable(block, scope, retrievalLevel, '$count');
28030
+ return index.identical(count.minus(literal(1)));
28031
+ });
28032
+ }
28033
+ getLevelSpecificVariableName(name, level) {
28034
+ // We use the `ɵ` here to ensure that there are no name conflicts with user-defined variables.
28035
+ return `ɵ${name}_${level}`;
28036
+ }
28037
+ /**
28038
+ * Gets the name of a for loop variable at a specific binding level. This allows us to look
28039
+ * up implicitly shadowed variables like `$index` and `$count` at a specific level.
28040
+ */
28041
+ getLevelSpecificForLoopVariable(block, scope, retrievalLevel, name) {
28042
+ const scopeName = scope.bindingLevel === retrievalLevel ?
28043
+ block.contextVariables[name].name :
28044
+ this.getLevelSpecificVariableName(name, retrievalLevel);
28045
+ return scope.get(scopeName);
27837
28046
  }
27838
28047
  optimizeTrackByFunction(block) {
27839
28048
  const indexLocalName = block.contextVariables.$index.name;
@@ -28371,7 +28580,7 @@ class BindingScope {
28371
28580
  if (value.declareLocalCallback && !value.declare) {
28372
28581
  value.declare = true;
28373
28582
  }
28374
- return typeof value.lhs === 'function' ? value.lhs(this) : value.lhs;
28583
+ return typeof value.lhs === 'function' ? value.lhs(this, value.retrievalLevel) : value.lhs;
28375
28584
  }
28376
28585
  current = current.parent;
28377
28586
  }
@@ -28479,7 +28688,9 @@ class BindingScope {
28479
28688
  const componentValue = this.map.get(SHARED_CONTEXT_KEY + 0);
28480
28689
  componentValue.declare = true;
28481
28690
  this.maybeRestoreView();
28482
- const lhs = typeof componentValue.lhs === 'function' ? componentValue.lhs(this) : componentValue.lhs;
28691
+ const lhs = typeof componentValue.lhs === 'function' ?
28692
+ componentValue.lhs(this, componentValue.retrievalLevel) :
28693
+ componentValue.lhs;
28483
28694
  return name === DIRECT_CONTEXT_REFERENCE ? lhs : lhs.prop(name);
28484
28695
  }
28485
28696
  maybeRestoreView() {
@@ -28951,6 +29162,11 @@ function addFeatures(definitionMap, meta) {
28951
29162
  break;
28952
29163
  }
28953
29164
  }
29165
+ // Note: host directives feature needs to be inserted before the
29166
+ // inheritance feature to ensure the correct execution order.
29167
+ if (meta.hostDirectives?.length) {
29168
+ features.push(importExpr(Identifiers.HostDirectivesFeature).callFn([createHostDirectivesFeatureArg(meta.hostDirectives)]));
29169
+ }
28954
29170
  if (meta.usesInheritance) {
28955
29171
  features.push(importExpr(Identifiers.InheritDefinitionFeature));
28956
29172
  }
@@ -28964,9 +29180,6 @@ function addFeatures(definitionMap, meta) {
28964
29180
  if (meta.hasOwnProperty('template') && meta.isStandalone) {
28965
29181
  features.push(importExpr(Identifiers.StandaloneFeature));
28966
29182
  }
28967
- if (meta.hostDirectives?.length) {
28968
- features.push(importExpr(Identifiers.HostDirectivesFeature).callFn([createHostDirectivesFeatureArg(meta.hostDirectives)]));
28969
- }
28970
29183
  if (features.length) {
28971
29184
  definitionMap.set('features', literalArr(features));
28972
29185
  }
@@ -31064,7 +31277,7 @@ function publishFacade(global) {
31064
31277
  * @description
31065
31278
  * Entry point for all public APIs of the compiler package.
31066
31279
  */
31067
- const VERSION = new Version('17.0.3');
31280
+ const VERSION = new Version('17.0.5');
31068
31281
 
31069
31282
  class CompilerConfig {
31070
31283
  constructor({ defaultEncapsulation = ViewEncapsulation.Emulated, preserveWhitespaces, strictInjectionParameters } = {}) {
@@ -31297,7 +31510,8 @@ class _Visitor {
31297
31510
  this._errors = [];
31298
31511
  this._messages = [];
31299
31512
  this._inImplicitNode = false;
31300
- this._createI18nMessage = createI18nMessageFactory(interpolationConfig);
31513
+ this._createI18nMessage =
31514
+ createI18nMessageFactory(interpolationConfig, DEFAULT_CONTAINER_BLOCKS);
31301
31515
  }
31302
31516
  // looks for translatable attributes
31303
31517
  _visitAttributesOf(el) {
@@ -31605,6 +31819,12 @@ class _WriteVisitor$1 {
31605
31819
  visitPlaceholder(ph, context) {
31606
31820
  return [new Tag(_PLACEHOLDER_TAG$2, { id: ph.name, 'equiv-text': `{{${ph.value}}}` })];
31607
31821
  }
31822
+ visitBlockPlaceholder(ph, context) {
31823
+ const ctype = `x-${ph.name.toLowerCase().replace(/[^a-z0-9]/g, '-')}`;
31824
+ const startTagPh = new Tag(_PLACEHOLDER_TAG$2, { id: ph.startName, ctype, 'equiv-text': `@${ph.name}` });
31825
+ const closeTagPh = new Tag(_PLACEHOLDER_TAG$2, { id: ph.closeName, ctype, 'equiv-text': `}` });
31826
+ return [startTagPh, ...this.serialize(ph.children), closeTagPh];
31827
+ }
31608
31828
  visitIcuPlaceholder(ph, context) {
31609
31829
  const equivText = `{${ph.value.expression}, ${ph.value.type}, ${Object.keys(ph.value.cases).map((value) => value + ' {...}').join(' ')}}`;
31610
31830
  return [new Tag(_PLACEHOLDER_TAG$2, { id: ph.name, 'equiv-text': equivText })];
@@ -31876,6 +32096,24 @@ class _WriteVisitor {
31876
32096
  disp: `{{${ph.value}}}`,
31877
32097
  })];
31878
32098
  }
32099
+ visitBlockPlaceholder(ph, context) {
32100
+ const tagPc = new Tag(_PLACEHOLDER_SPANNING_TAG, {
32101
+ id: (this._nextPlaceholderId++).toString(),
32102
+ equivStart: ph.startName,
32103
+ equivEnd: ph.closeName,
32104
+ type: 'other',
32105
+ dispStart: `@${ph.name}`,
32106
+ dispEnd: `}`,
32107
+ });
32108
+ const nodes = [].concat(...ph.children.map(node => node.visit(this)));
32109
+ if (nodes.length) {
32110
+ nodes.forEach((node) => tagPc.children.push(node));
32111
+ }
32112
+ else {
32113
+ tagPc.children.push(new Text$1(''));
32114
+ }
32115
+ return [tagPc];
32116
+ }
31879
32117
  visitIcuPlaceholder(ph, context) {
31880
32118
  const cases = Object.keys(ph.value.cases).map((value) => value + ' {...}').join(' ');
31881
32119
  const idStr = (this._nextPlaceholderId++).toString();
@@ -32324,6 +32562,11 @@ class I18nToHtmlVisitor {
32324
32562
  // An ICU placeholder references the source message to be serialized
32325
32563
  return this._convertToText(this._srcMsg.placeholderToMessage[ph.name]);
32326
32564
  }
32565
+ visitBlockPlaceholder(ph, context) {
32566
+ const params = ph.parameters.length === 0 ? '' : ` (${ph.parameters.join('; ')})`;
32567
+ const children = ph.children.map((c) => c.visit(this)).join('');
32568
+ return `@${ph.name}${params} {${children}}`;
32569
+ }
32327
32570
  /**
32328
32571
  * Convert a source message to a translated text string:
32329
32572
  * - text nodes are replaced with their translation,
@@ -32476,6 +32719,12 @@ class MapPlaceholderNames extends CloneVisitor {
32476
32719
  const children = ph.children.map(n => n.visit(this, mapper));
32477
32720
  return new TagPlaceholder(ph.tag, ph.attrs, startName, closeName, children, ph.isVoid, ph.sourceSpan, ph.startSourceSpan, ph.endSourceSpan);
32478
32721
  }
32722
+ visitBlockPlaceholder(ph, mapper) {
32723
+ const startName = mapper.toPublicName(ph.startName);
32724
+ const closeName = ph.closeName ? mapper.toPublicName(ph.closeName) : ph.closeName;
32725
+ const children = ph.children.map(n => n.visit(this, mapper));
32726
+ return new BlockPlaceholder(ph.name, ph.parameters, startName, closeName, children, ph.sourceSpan, ph.startSourceSpan, ph.endSourceSpan);
32727
+ }
32479
32728
  visitPlaceholder(ph, mapper) {
32480
32729
  return new Placeholder(ph.value, mapper.toPublicName(ph.name), ph.sourceSpan);
32481
32730
  }
@@ -32594,7 +32843,7 @@ const MINIMUM_PARTIAL_LINKER_VERSION$6 = '12.0.0';
32594
32843
  function compileDeclareClassMetadata(metadata) {
32595
32844
  const definitionMap = new DefinitionMap();
32596
32845
  definitionMap.set('minVersion', literal(MINIMUM_PARTIAL_LINKER_VERSION$6));
32597
- definitionMap.set('version', literal('17.0.3'));
32846
+ definitionMap.set('version', literal('17.0.5'));
32598
32847
  definitionMap.set('ngImport', importExpr(Identifiers.core));
32599
32848
  definitionMap.set('type', metadata.type);
32600
32849
  definitionMap.set('decorators', metadata.decorators);
@@ -32702,7 +32951,7 @@ function createDirectiveDefinitionMap(meta) {
32702
32951
  // in 16.1 is actually used.
32703
32952
  const minVersion = hasTransformFunctions ? MINIMUM_PARTIAL_LINKER_VERSION$5 : '14.0.0';
32704
32953
  definitionMap.set('minVersion', literal(minVersion));
32705
- definitionMap.set('version', literal('17.0.3'));
32954
+ definitionMap.set('version', literal('17.0.5'));
32706
32955
  // e.g. `type: MyDirective`
32707
32956
  definitionMap.set('type', meta.type.value);
32708
32957
  if (meta.isStandalone) {
@@ -32979,7 +33228,7 @@ const MINIMUM_PARTIAL_LINKER_VERSION$4 = '12.0.0';
32979
33228
  function compileDeclareFactoryFunction(meta) {
32980
33229
  const definitionMap = new DefinitionMap();
32981
33230
  definitionMap.set('minVersion', literal(MINIMUM_PARTIAL_LINKER_VERSION$4));
32982
- definitionMap.set('version', literal('17.0.3'));
33231
+ definitionMap.set('version', literal('17.0.5'));
32983
33232
  definitionMap.set('ngImport', importExpr(Identifiers.core));
32984
33233
  definitionMap.set('type', meta.type.value);
32985
33234
  definitionMap.set('deps', compileDependencies(meta.deps));
@@ -33014,7 +33263,7 @@ function compileDeclareInjectableFromMetadata(meta) {
33014
33263
  function createInjectableDefinitionMap(meta) {
33015
33264
  const definitionMap = new DefinitionMap();
33016
33265
  definitionMap.set('minVersion', literal(MINIMUM_PARTIAL_LINKER_VERSION$3));
33017
- definitionMap.set('version', literal('17.0.3'));
33266
+ definitionMap.set('version', literal('17.0.5'));
33018
33267
  definitionMap.set('ngImport', importExpr(Identifiers.core));
33019
33268
  definitionMap.set('type', meta.type.value);
33020
33269
  // Only generate providedIn property if it has a non-null value
@@ -33065,7 +33314,7 @@ function compileDeclareInjectorFromMetadata(meta) {
33065
33314
  function createInjectorDefinitionMap(meta) {
33066
33315
  const definitionMap = new DefinitionMap();
33067
33316
  definitionMap.set('minVersion', literal(MINIMUM_PARTIAL_LINKER_VERSION$2));
33068
- definitionMap.set('version', literal('17.0.3'));
33317
+ definitionMap.set('version', literal('17.0.5'));
33069
33318
  definitionMap.set('ngImport', importExpr(Identifiers.core));
33070
33319
  definitionMap.set('type', meta.type.value);
33071
33320
  definitionMap.set('providers', meta.providers);
@@ -33098,7 +33347,7 @@ function createNgModuleDefinitionMap(meta) {
33098
33347
  throw new Error('Invalid path! Local compilation mode should not get into the partial compilation path');
33099
33348
  }
33100
33349
  definitionMap.set('minVersion', literal(MINIMUM_PARTIAL_LINKER_VERSION$1));
33101
- definitionMap.set('version', literal('17.0.3'));
33350
+ definitionMap.set('version', literal('17.0.5'));
33102
33351
  definitionMap.set('ngImport', importExpr(Identifiers.core));
33103
33352
  definitionMap.set('type', meta.type.value);
33104
33353
  // We only generate the keys in the metadata if the arrays contain values.
@@ -33149,7 +33398,7 @@ function compileDeclarePipeFromMetadata(meta) {
33149
33398
  function createPipeDefinitionMap(meta) {
33150
33399
  const definitionMap = new DefinitionMap();
33151
33400
  definitionMap.set('minVersion', literal(MINIMUM_PARTIAL_LINKER_VERSION));
33152
- definitionMap.set('version', literal('17.0.3'));
33401
+ definitionMap.set('version', literal('17.0.5'));
33153
33402
  definitionMap.set('ngImport', importExpr(Identifiers.core));
33154
33403
  // e.g. `type: MyPipe`
33155
33404
  definitionMap.set('type', meta.type.value);