@angular/compiler 17.0.0-next.6 → 17.0.0-next.7

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 (59) hide show
  1. package/esm2022/src/injectable_compiler_2.mjs +3 -6
  2. package/esm2022/src/jit_compiler_facade.mjs +2 -7
  3. package/esm2022/src/ml_parser/lexer.mjs +2 -2
  4. package/esm2022/src/output/abstract_emitter.mjs +8 -1
  5. package/esm2022/src/render3/partial/class_metadata.mjs +1 -1
  6. package/esm2022/src/render3/partial/component.mjs +45 -1
  7. package/esm2022/src/render3/partial/directive.mjs +1 -1
  8. package/esm2022/src/render3/partial/factory.mjs +1 -1
  9. package/esm2022/src/render3/partial/injectable.mjs +1 -1
  10. package/esm2022/src/render3/partial/injector.mjs +1 -1
  11. package/esm2022/src/render3/partial/ng_module.mjs +1 -1
  12. package/esm2022/src/render3/partial/pipe.mjs +1 -1
  13. package/esm2022/src/render3/r3_factory.mjs +2 -2
  14. package/esm2022/src/render3/r3_template_transform.mjs +18 -25
  15. package/esm2022/src/render3/util.mjs +3 -3
  16. package/esm2022/src/render3/view/block_syntax_switch.mjs +13 -0
  17. package/esm2022/src/render3/view/compiler.mjs +8 -6
  18. package/esm2022/src/render3/view/template.mjs +4 -6
  19. package/esm2022/src/render3/view/util.mjs +8 -2
  20. package/esm2022/src/template/pipeline/ir/src/enums.mjs +42 -9
  21. package/esm2022/src/template/pipeline/ir/src/expression.mjs +61 -16
  22. package/esm2022/src/template/pipeline/ir/src/operations.mjs +2 -2
  23. package/esm2022/src/template/pipeline/ir/src/ops/create.mjs +52 -12
  24. package/esm2022/src/template/pipeline/ir/src/ops/update.mjs +34 -4
  25. package/esm2022/src/template/pipeline/ir/src/traits.mjs +17 -2
  26. package/esm2022/src/template/pipeline/ir/src/variable.mjs +6 -2
  27. package/esm2022/src/template/pipeline/src/conversion.mjs +3 -2
  28. package/esm2022/src/template/pipeline/src/emit.mjs +10 -10
  29. package/esm2022/src/template/pipeline/src/ingest.mjs +157 -18
  30. package/esm2022/src/template/pipeline/src/instruction.mjs +34 -5
  31. package/esm2022/src/template/pipeline/src/phases/apply_i18n_expressions.mjs +37 -0
  32. package/esm2022/src/template/pipeline/src/phases/chaining.mjs +13 -12
  33. package/esm2022/src/template/pipeline/src/phases/conditionals.mjs +22 -9
  34. package/esm2022/src/template/pipeline/src/phases/empty_elements.mjs +14 -3
  35. package/esm2022/src/template/pipeline/src/phases/generate_advance.mjs +19 -1
  36. package/esm2022/src/template/pipeline/src/phases/generate_variables.mjs +6 -2
  37. package/esm2022/src/template/pipeline/src/phases/has_const_trait_collection.mjs +34 -0
  38. package/esm2022/src/template/pipeline/src/phases/host_style_property_parsing.mjs +6 -1
  39. package/esm2022/src/template/pipeline/src/phases/i18n_const_collection.mjs +3 -2
  40. package/esm2022/src/template/pipeline/src/phases/i18n_message_extraction.mjs +8 -8
  41. package/esm2022/src/template/pipeline/src/phases/i18n_text_extraction.mjs +31 -5
  42. package/esm2022/src/template/pipeline/src/phases/naming.mjs +3 -3
  43. package/esm2022/src/template/pipeline/src/phases/ng_container.mjs +6 -1
  44. package/esm2022/src/template/pipeline/src/phases/pure_function_extraction.mjs +4 -4
  45. package/esm2022/src/template/pipeline/src/phases/reify.mjs +24 -9
  46. package/esm2022/src/template/pipeline/src/phases/resolve_i18n_placeholders.mjs +79 -37
  47. package/esm2022/src/template/pipeline/src/phases/slot_allocation.mjs +5 -5
  48. package/esm2022/src/template/pipeline/src/phases/temporary_variables.mjs +53 -38
  49. package/esm2022/src/template/pipeline/src/phases/var_counting.mjs +10 -1
  50. package/esm2022/src/template/pipeline/src/phases/variable_optimization.mjs +9 -1
  51. package/esm2022/src/version.mjs +1 -1
  52. package/fesm2022/compiler.mjs +1033 -467
  53. package/fesm2022/compiler.mjs.map +1 -1
  54. package/fesm2022/testing.mjs +1 -1
  55. package/index.d.ts +5 -7
  56. package/package.json +2 -2
  57. package/testing/index.d.ts +1 -1
  58. package/esm2022/src/template/pipeline/src/phases/generate_i18n_blocks.mjs +0 -38
  59. package/esm2022/src/template/pipeline/src/phases/no_listeners_on_templates.mjs +0 -32
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @license Angular v17.0.0-next.6
2
+ * @license Angular v17.0.0-next.7
3
3
  * (c) 2010-2022 Google LLC. https://angular.io/
4
4
  * License: MIT
5
5
  */
@@ -3139,7 +3139,14 @@ class AbstractEmitterVisitor {
3139
3139
  return null;
3140
3140
  }
3141
3141
  visitInvokeFunctionExpr(expr, ctx) {
3142
+ const shouldParenthesize = expr.fn instanceof ArrowFunctionExpr;
3143
+ if (shouldParenthesize) {
3144
+ ctx.print(expr.fn, '(');
3145
+ }
3142
3146
  expr.fn.visitExpression(this, ctx);
3147
+ if (shouldParenthesize) {
3148
+ ctx.print(expr.fn, ')');
3149
+ }
3143
3150
  ctx.print(expr, `(`);
3144
3151
  this.visitAllExpressions(expr.args, ctx, ',');
3145
3152
  ctx.print(expr, `)`);
@@ -3438,7 +3445,7 @@ function wrapReference(value) {
3438
3445
  }
3439
3446
  function refsToArray(refs, shouldForwardDeclare) {
3440
3447
  const values = literalArr(refs.map(ref => ref.value));
3441
- return shouldForwardDeclare ? fn([], [new ReturnStatement(values)]) : values;
3448
+ return shouldForwardDeclare ? arrowFn([], values) : values;
3442
3449
  }
3443
3450
  function createMayBeForwardRefExpression(expression, forwardRef) {
3444
3451
  return { expression, forwardRef };
@@ -3471,7 +3478,7 @@ function convertFromMaybeForwardRefExpression({ expression, forwardRef }) {
3471
3478
  * ```
3472
3479
  */
3473
3480
  function generateForwardRef(expr) {
3474
- return importExpr(Identifiers.forwardRef).callFn([fn([], [new ReturnStatement(expr)])]);
3481
+ return importExpr(Identifiers.forwardRef).callFn([arrowFn([], expr)]);
3475
3482
  }
3476
3483
 
3477
3484
  var R3FactoryDelegateType;
@@ -3559,7 +3566,7 @@ function compileFactoryFunction(meta) {
3559
3566
  if (baseFactoryVar !== null) {
3560
3567
  // There is a base factory variable so wrap its declaration along with the factory function into
3561
3568
  // an IIFE.
3562
- factoryFn = fn([], [
3569
+ factoryFn = arrowFn([], [
3563
3570
  new DeclareVarStmt(baseFactoryVar.name), new ReturnStatement(factoryFn)
3564
3571
  ]).callFn([], /* sourceSpan */ undefined, /* pure */ true);
3565
3572
  }
@@ -4931,7 +4938,13 @@ class DefinitionMap {
4931
4938
  }
4932
4939
  set(key, value) {
4933
4940
  if (value) {
4934
- this.values.push({ key: key, value, quoted: false });
4941
+ const existing = this.values.find(value => value.key === key);
4942
+ if (existing) {
4943
+ existing.value = value;
4944
+ }
4945
+ else {
4946
+ this.values.push({ key: key, value, quoted: false });
4947
+ }
4935
4948
  }
4936
4949
  }
4937
4950
  toLiteralMap() {
@@ -5074,10 +5087,7 @@ function compileInjectable(meta, resolveForwardRefs) {
5074
5087
  });
5075
5088
  }
5076
5089
  else {
5077
- result = {
5078
- statements: [],
5079
- expression: fn([], [new ReturnStatement(meta.useFactory.callFn([]))])
5080
- };
5090
+ result = { statements: [], expression: arrowFn([], meta.useFactory.callFn([])) };
5081
5091
  }
5082
5092
  }
5083
5093
  else if (meta.useValue !== undefined) {
@@ -5146,7 +5156,7 @@ function delegateToFactory(type, useType, unwrapForwardRefs) {
5146
5156
  return createFactoryFunction(unwrappedType);
5147
5157
  }
5148
5158
  function createFactoryFunction(type) {
5149
- return fn([new FnParam('t', DYNAMIC_TYPE)], [new ReturnStatement(type.prop('ɵfac').callFn([variable('t')]))]);
5159
+ return arrowFn([new FnParam('t', DYNAMIC_TYPE)], type.prop('ɵfac').callFn([variable('t')]));
5150
5160
  }
5151
5161
 
5152
5162
  const UNUSABLE_INTERPOLATION_REGEXPS = [
@@ -8759,38 +8769,58 @@ var OpKind;
8759
8769
  * An attribute that has been extracted for inclusion in the consts array.
8760
8770
  */
8761
8771
  OpKind[OpKind["ExtractedAttribute"] = 25] = "ExtractedAttribute";
8772
+ /**
8773
+ * An operation that configures a `@defer` block.
8774
+ */
8775
+ OpKind[OpKind["Defer"] = 26] = "Defer";
8776
+ /**
8777
+ * An IR operation that provides secondary templates of a `@defer` block.
8778
+ */
8779
+ OpKind[OpKind["DeferSecondaryBlock"] = 27] = "DeferSecondaryBlock";
8780
+ /**
8781
+ * An operation that controls when a `@defer` loads.
8782
+ */
8783
+ OpKind[OpKind["DeferOn"] = 28] = "DeferOn";
8762
8784
  /**
8763
8785
  * An i18n message that has been extracted for inclusion in the consts array.
8764
8786
  */
8765
- OpKind[OpKind["ExtractedMessage"] = 26] = "ExtractedMessage";
8787
+ OpKind[OpKind["ExtractedMessage"] = 29] = "ExtractedMessage";
8766
8788
  /**
8767
8789
  * A host binding property.
8768
8790
  */
8769
- OpKind[OpKind["HostProperty"] = 27] = "HostProperty";
8791
+ OpKind[OpKind["HostProperty"] = 30] = "HostProperty";
8770
8792
  /**
8771
8793
  * A namespace change, which causes the subsequent elements to be processed as either HTML or SVG.
8772
8794
  */
8773
- OpKind[OpKind["Namespace"] = 28] = "Namespace";
8795
+ OpKind[OpKind["Namespace"] = 31] = "Namespace";
8774
8796
  /**
8775
8797
  * Configure a content projeciton definition for the view.
8776
8798
  */
8777
- OpKind[OpKind["ProjectionDef"] = 29] = "ProjectionDef";
8799
+ OpKind[OpKind["ProjectionDef"] = 32] = "ProjectionDef";
8778
8800
  /**
8779
8801
  * Create a content projection slot.
8780
8802
  */
8781
- OpKind[OpKind["Projection"] = 30] = "Projection";
8803
+ OpKind[OpKind["Projection"] = 33] = "Projection";
8782
8804
  /**
8783
8805
  * The start of an i18n block.
8784
8806
  */
8785
- OpKind[OpKind["I18nStart"] = 31] = "I18nStart";
8807
+ OpKind[OpKind["I18nStart"] = 34] = "I18nStart";
8786
8808
  /**
8787
8809
  * A self-closing i18n on a single element.
8788
8810
  */
8789
- OpKind[OpKind["I18n"] = 32] = "I18n";
8811
+ OpKind[OpKind["I18n"] = 35] = "I18n";
8790
8812
  /**
8791
8813
  * The end of an i18n block.
8792
8814
  */
8793
- OpKind[OpKind["I18nEnd"] = 33] = "I18nEnd";
8815
+ OpKind[OpKind["I18nEnd"] = 36] = "I18nEnd";
8816
+ /**
8817
+ * An expression in an i18n message.
8818
+ */
8819
+ OpKind[OpKind["I18nExpression"] = 37] = "I18nExpression";
8820
+ /**
8821
+ * An instruction that applies a set of i18n expressions.
8822
+ */
8823
+ OpKind[OpKind["I18nApply"] = 38] = "I18nApply";
8794
8824
  })(OpKind || (OpKind = {}));
8795
8825
  /**
8796
8826
  * Distinguishes different kinds of IR expressions.
@@ -8881,6 +8911,10 @@ var ExpressionKind;
8881
8911
  * An expression that will cause a literal slot index to be emitted.
8882
8912
  */
8883
8913
  ExpressionKind[ExpressionKind["SlotLiteralExpr"] = 20] = "SlotLiteralExpr";
8914
+ /**
8915
+ * A test expression for a conditional op.
8916
+ */
8917
+ ExpressionKind[ExpressionKind["ConditionalCase"] = 21] = "ConditionalCase";
8884
8918
  })(ExpressionKind || (ExpressionKind = {}));
8885
8919
  /**
8886
8920
  * Distinguishes between different kinds of `SemanticVariable`s.
@@ -8922,6 +8956,15 @@ var SanitizerFn;
8922
8956
  SanitizerFn[SanitizerFn["ResourceUrl"] = 4] = "ResourceUrl";
8923
8957
  SanitizerFn[SanitizerFn["IframeAttribute"] = 5] = "IframeAttribute";
8924
8958
  })(SanitizerFn || (SanitizerFn = {}));
8959
+ /**
8960
+ * Enumeration of the different kinds of `@defer` secondary blocks.
8961
+ */
8962
+ var DeferSecondaryKind;
8963
+ (function (DeferSecondaryKind) {
8964
+ DeferSecondaryKind[DeferSecondaryKind["Loading"] = 0] = "Loading";
8965
+ DeferSecondaryKind[DeferSecondaryKind["Placeholder"] = 1] = "Placeholder";
8966
+ DeferSecondaryKind[DeferSecondaryKind["Error"] = 2] = "Error";
8967
+ })(DeferSecondaryKind || (DeferSecondaryKind = {}));
8925
8968
  /**
8926
8969
  * Enumeration of the types of attributes which can be applied to an element.
8927
8970
  */
@@ -8977,6 +9020,10 @@ const ConsumesVarsTrait = Symbol('ConsumesVars');
8977
9020
  * Marker symbol for `UsesVarOffset` trait.
8978
9021
  */
8979
9022
  const UsesVarOffset = Symbol('UsesVarOffset');
9023
+ /**
9024
+ * Marker symbol for `HasConst` trait.
9025
+ */
9026
+ const HasConst = Symbol('HasConst');
8980
9027
  /**
8981
9028
  * Default values for most `ConsumesSlotOpTrait` fields (used with the spread operator to initialize
8982
9029
  * implementors of the trait).
@@ -8992,7 +9039,7 @@ const TRAIT_CONSUMES_SLOT = {
8992
9039
  */
8993
9040
  const TRAIT_USES_SLOT_INDEX = {
8994
9041
  [UsesSlotIndex]: true,
8995
- slot: null,
9042
+ targetSlot: null,
8996
9043
  };
8997
9044
  /**
8998
9045
  * Default values for most `DependsOnSlotContextOpTrait` fields (used with the spread operator to
@@ -9016,6 +9063,14 @@ const TRAIT_USES_VAR_OFFSET = {
9016
9063
  [UsesVarOffset]: true,
9017
9064
  varOffset: null,
9018
9065
  };
9066
+ /**
9067
+ * Default values for `HasConst` fields (used with the spread operator to initialize
9068
+ * implementors of this trait).
9069
+ */
9070
+ const TRAIT_HAS_CONST = {
9071
+ [HasConst]: true,
9072
+ constIndex: null,
9073
+ };
9019
9074
  /**
9020
9075
  * Test whether an operation implements `ConsumesSlotOpTrait`.
9021
9076
  */
@@ -9040,6 +9095,9 @@ function hasUsesVarOffsetTrait(expr) {
9040
9095
  function hasUsesSlotIndexTrait(value) {
9041
9096
  return value[UsesSlotIndex] === true;
9042
9097
  }
9098
+ function hasConstTrait(value) {
9099
+ return value[HasConst] === true;
9100
+ }
9043
9101
 
9044
9102
  /**
9045
9103
  * Create a `StatementOp`.
@@ -9078,11 +9136,12 @@ const NEW_OP = {
9078
9136
  /**
9079
9137
  * Create an `InterpolationTextOp`.
9080
9138
  */
9081
- function createInterpolateTextOp(xref, interpolation, sourceSpan) {
9139
+ function createInterpolateTextOp(xref, interpolation, i18nPlaceholders, sourceSpan) {
9082
9140
  return {
9083
9141
  kind: OpKind.InterpolateText,
9084
9142
  target: xref,
9085
9143
  interpolation,
9144
+ i18nPlaceholders,
9086
9145
  sourceSpan,
9087
9146
  ...TRAIT_DEPENDS_ON_SLOT_CONTEXT,
9088
9147
  ...TRAIT_CONSUMES_VARS,
@@ -9220,17 +9279,46 @@ function createAdvanceOp(delta, sourceSpan) {
9220
9279
  /**
9221
9280
  * Create a conditional op, which will display an embedded view according to a condtion.
9222
9281
  */
9223
- function createConditionalOp(target, test, sourceSpan) {
9282
+ function createConditionalOp(target, test, conditions, sourceSpan) {
9224
9283
  return {
9225
9284
  kind: OpKind.Conditional,
9226
9285
  target,
9227
9286
  test,
9228
- conditions: [],
9287
+ conditions,
9229
9288
  processed: null,
9230
9289
  sourceSpan,
9290
+ contextValue: null,
9231
9291
  ...NEW_OP,
9232
9292
  ...TRAIT_USES_SLOT_INDEX,
9233
9293
  ...TRAIT_DEPENDS_ON_SLOT_CONTEXT,
9294
+ ...TRAIT_CONSUMES_VARS,
9295
+ };
9296
+ }
9297
+ /**
9298
+ * Create an i18n expression op.
9299
+ */
9300
+ function createI18nExpressionOp(target, expression, i18nPlaceholder, sourceSpan) {
9301
+ return {
9302
+ kind: OpKind.I18nExpression,
9303
+ target,
9304
+ expression,
9305
+ i18nPlaceholder,
9306
+ sourceSpan,
9307
+ ...NEW_OP,
9308
+ ...TRAIT_CONSUMES_VARS,
9309
+ ...TRAIT_DEPENDS_ON_SLOT_CONTEXT,
9310
+ };
9311
+ }
9312
+ /**
9313
+ * Creates an op to apply i18n expression ops.
9314
+ */
9315
+ function createI18nApplyOp(target, sourceSpan) {
9316
+ return {
9317
+ kind: OpKind.I18nApply,
9318
+ target,
9319
+ sourceSpan,
9320
+ ...NEW_OP,
9321
+ ...TRAIT_USES_SLOT_INDEX,
9234
9322
  };
9235
9323
  }
9236
9324
 
@@ -9281,7 +9369,7 @@ class ReferenceExpr extends ExpressionBase {
9281
9369
  this.offset = offset;
9282
9370
  this.kind = ExpressionKind.Reference;
9283
9371
  this[_a] = true;
9284
- this.slot = null;
9372
+ this.targetSlot = null;
9285
9373
  }
9286
9374
  visitExpression() { }
9287
9375
  isEquivalent(e) {
@@ -9293,7 +9381,7 @@ class ReferenceExpr extends ExpressionBase {
9293
9381
  transformInternalExpressions() { }
9294
9382
  clone() {
9295
9383
  const expr = new ReferenceExpr(this.target, this.offset);
9296
- expr.slot = this.slot;
9384
+ expr.targetSlot = this.targetSlot;
9297
9385
  return expr;
9298
9386
  }
9299
9387
  }
@@ -9530,7 +9618,7 @@ class PipeBindingExpr extends ExpressionBase {
9530
9618
  this[_d] = true;
9531
9619
  this[_e] = true;
9532
9620
  this[_f] = true;
9533
- this.slot = null;
9621
+ this.targetSlot = null;
9534
9622
  this.varOffset = null;
9535
9623
  }
9536
9624
  visitExpression(visitor, context) {
@@ -9551,7 +9639,7 @@ class PipeBindingExpr extends ExpressionBase {
9551
9639
  }
9552
9640
  clone() {
9553
9641
  const r = new PipeBindingExpr(this.target, this.name, this.args.map(a => a.clone()));
9554
- r.slot = this.slot;
9642
+ r.targetSlot = this.targetSlot;
9555
9643
  r.varOffset = this.varOffset;
9556
9644
  return r;
9557
9645
  }
@@ -9568,7 +9656,7 @@ class PipeBindingVariadicExpr extends ExpressionBase {
9568
9656
  this[_g] = true;
9569
9657
  this[_h] = true;
9570
9658
  this[_j] = true;
9571
- this.slot = null;
9659
+ this.targetSlot = null;
9572
9660
  this.varOffset = null;
9573
9661
  }
9574
9662
  visitExpression(visitor, context) {
@@ -9585,7 +9673,7 @@ class PipeBindingVariadicExpr extends ExpressionBase {
9585
9673
  }
9586
9674
  clone() {
9587
9675
  const r = new PipeBindingVariadicExpr(this.target, this.name, this.args.clone(), this.numArgs);
9588
- r.slot = this.slot;
9676
+ r.targetSlot = this.targetSlot;
9589
9677
  r.varOffset = this.varOffset;
9590
9678
  return r;
9591
9679
  }
@@ -9785,20 +9873,56 @@ class SlotLiteralExpr extends ExpressionBase {
9785
9873
  this.target = target;
9786
9874
  this.kind = ExpressionKind.SlotLiteralExpr;
9787
9875
  this[_k] = true;
9788
- this.slot = null;
9876
+ this.targetSlot = null;
9789
9877
  }
9790
9878
  visitExpression(visitor, context) { }
9791
9879
  isEquivalent(e) {
9792
- return e instanceof SlotLiteralExpr && e.target === this.target && e.slot === this.slot;
9880
+ return e instanceof SlotLiteralExpr && e.target === this.target &&
9881
+ e.targetSlot === this.targetSlot;
9793
9882
  }
9794
9883
  isConstant() {
9795
9884
  return true;
9796
9885
  }
9797
9886
  clone() {
9798
- return new SlotLiteralExpr(this.target);
9887
+ const copy = new SlotLiteralExpr(this.target);
9888
+ copy.targetSlot = this.targetSlot;
9889
+ return copy;
9799
9890
  }
9800
9891
  transformInternalExpressions() { }
9801
9892
  }
9893
+ class ConditionalCaseExpr extends ExpressionBase {
9894
+ /**
9895
+ * Create an expression for one branch of a conditional.
9896
+ * @param expr The expression to be tested for this case. Might be null, as in an `else` case.
9897
+ * @param target The Xref of the view to be displayed if this condition is true.
9898
+ */
9899
+ constructor(expr, target, alias = null) {
9900
+ super();
9901
+ this.expr = expr;
9902
+ this.target = target;
9903
+ this.alias = alias;
9904
+ this.kind = ExpressionKind.ConditionalCase;
9905
+ }
9906
+ visitExpression(visitor, context) {
9907
+ if (this.expr !== null) {
9908
+ this.expr.visitExpression(visitor, context);
9909
+ }
9910
+ }
9911
+ isEquivalent(e) {
9912
+ return e instanceof ConditionalCaseExpr && e.expr === this.expr;
9913
+ }
9914
+ isConstant() {
9915
+ return true;
9916
+ }
9917
+ clone() {
9918
+ return new ConditionalCaseExpr(this.expr, this.target);
9919
+ }
9920
+ transformInternalExpressions(transform, flags) {
9921
+ if (this.expr !== null) {
9922
+ this.expr = transformExpressionsInExpression(this.expr, transform, flags);
9923
+ }
9924
+ }
9925
+ }
9802
9926
  /**
9803
9927
  * Visits all `Expression`s in the AST of `op` with the `visitor` function.
9804
9928
  */
@@ -9851,6 +9975,9 @@ function transformExpressionsInOp(op, transform, flags) {
9851
9975
  op.sanitizer =
9852
9976
  op.sanitizer && transformExpressionsInExpression(op.sanitizer, transform, flags);
9853
9977
  break;
9978
+ case OpKind.I18nExpression:
9979
+ op.expression = transformExpressionsInExpression(op.expression, transform, flags);
9980
+ break;
9854
9981
  case OpKind.InterpolateText:
9855
9982
  transformExpressionsInInterpolation(op.interpolation, transform, flags);
9856
9983
  break;
@@ -9862,15 +9989,18 @@ function transformExpressionsInOp(op, transform, flags) {
9862
9989
  break;
9863
9990
  case OpKind.Conditional:
9864
9991
  for (const condition of op.conditions) {
9865
- if (condition[1] === null) {
9992
+ if (condition.expr === null) {
9866
9993
  // This is a default case.
9867
9994
  continue;
9868
9995
  }
9869
- condition[1] = transformExpressionsInExpression(condition[1], transform, flags);
9996
+ condition.expr = transformExpressionsInExpression(condition.expr, transform, flags);
9870
9997
  }
9871
9998
  if (op.processed !== null) {
9872
9999
  op.processed = transformExpressionsInExpression(op.processed, transform, flags);
9873
10000
  }
10001
+ if (op.contextValue !== null) {
10002
+ op.contextValue = transformExpressionsInExpression(op.contextValue, transform, flags);
10003
+ }
9874
10004
  break;
9875
10005
  case OpKind.Listener:
9876
10006
  for (const innerOp of op.handlerOps) {
@@ -9887,18 +10017,20 @@ function transformExpressionsInOp(op, transform, flags) {
9887
10017
  transformExpressionsInStatement(statement, transform, flags);
9888
10018
  }
9889
10019
  break;
10020
+ case OpKind.I18n:
9890
10021
  case OpKind.I18nStart:
9891
- for (const placeholder in op.tagNameParams) {
9892
- op.tagNameParams[placeholder] =
9893
- transformExpressionsInExpression(op.tagNameParams[placeholder], transform, flags);
10022
+ for (const [placeholder, expression] of op.params) {
10023
+ op.params.set(placeholder, transformExpressionsInExpression(expression, transform, flags));
9894
10024
  }
9895
10025
  break;
10026
+ case OpKind.Defer:
10027
+ case OpKind.DeferSecondaryBlock:
10028
+ case OpKind.DeferOn:
9896
10029
  case OpKind.Projection:
9897
10030
  case OpKind.ProjectionDef:
9898
10031
  case OpKind.Element:
9899
10032
  case OpKind.ElementStart:
9900
10033
  case OpKind.ElementEnd:
9901
- case OpKind.I18n:
9902
10034
  case OpKind.I18nEnd:
9903
10035
  case OpKind.Container:
9904
10036
  case OpKind.ContainerStart:
@@ -9910,6 +10042,7 @@ function transformExpressionsInOp(op, transform, flags) {
9910
10042
  case OpKind.Pipe:
9911
10043
  case OpKind.Advance:
9912
10044
  case OpKind.Namespace:
10045
+ case OpKind.I18nApply:
9913
10046
  // These operations contain no expressions.
9914
10047
  break;
9915
10048
  default:
@@ -10190,7 +10323,7 @@ class OpList {
10190
10323
  // Replace `oldOp` with the chain `first` -> `last`.
10191
10324
  if (oldPrev !== null) {
10192
10325
  oldPrev.next = first;
10193
- first.prev = oldOp.prev;
10326
+ first.prev = oldPrev;
10194
10327
  }
10195
10328
  if (oldNext !== null) {
10196
10329
  oldNext.prev = last;
@@ -10297,7 +10430,7 @@ function isElementOrContainerOp(op) {
10297
10430
  /**
10298
10431
  * Create an `ElementStartOp`.
10299
10432
  */
10300
- function createElementStartOp(tag, xref, namespace, i18n, sourceSpan) {
10433
+ function createElementStartOp(tag, xref, namespace, i18nPlaceholder, sourceSpan) {
10301
10434
  return {
10302
10435
  kind: OpKind.ElementStart,
10303
10436
  xref,
@@ -10306,7 +10439,7 @@ function createElementStartOp(tag, xref, namespace, i18n, sourceSpan) {
10306
10439
  localRefs: [],
10307
10440
  nonBindable: false,
10308
10441
  namespace,
10309
- i18n,
10442
+ i18nPlaceholder,
10310
10443
  sourceSpan,
10311
10444
  ...TRAIT_CONSUMES_SLOT,
10312
10445
  ...NEW_OP,
@@ -10315,19 +10448,18 @@ function createElementStartOp(tag, xref, namespace, i18n, sourceSpan) {
10315
10448
  /**
10316
10449
  * Create a `TemplateOp`.
10317
10450
  */
10318
- function createTemplateOp(xref, tag, namespace, controlFlow, i18n, sourceSpan) {
10451
+ function createTemplateOp(xref, tag, namespace, generatedInBlock, i18n, sourceSpan) {
10319
10452
  return {
10320
10453
  kind: OpKind.Template,
10321
10454
  xref,
10322
10455
  attributes: null,
10323
10456
  tag,
10324
- controlFlow,
10457
+ block: generatedInBlock,
10325
10458
  decls: null,
10326
10459
  vars: null,
10327
10460
  localRefs: [],
10328
10461
  nonBindable: false,
10329
10462
  namespace,
10330
- i18n,
10331
10463
  sourceSpan,
10332
10464
  ...TRAIT_CONSUMES_SLOT,
10333
10465
  ...NEW_OP,
@@ -10432,11 +10564,9 @@ function createProjectionOp(xref, selector) {
10432
10564
  attributes: null,
10433
10565
  localRefs: [],
10434
10566
  nonBindable: false,
10435
- i18n: undefined,
10436
10567
  sourceSpan: null,
10437
10568
  ...NEW_OP,
10438
10569
  ...TRAIT_CONSUMES_SLOT,
10439
- ...TRAIT_USES_SLOT_INDEX,
10440
10570
  };
10441
10571
  }
10442
10572
  /**
@@ -10452,6 +10582,42 @@ function createExtractedAttributeOp(target, bindingKind, name, expression) {
10452
10582
  ...NEW_OP,
10453
10583
  };
10454
10584
  }
10585
+ function createDeferOp(xref, main, sourceSpan) {
10586
+ return {
10587
+ kind: OpKind.Defer,
10588
+ xref,
10589
+ target: main,
10590
+ loading: null,
10591
+ placeholder: null,
10592
+ error: null,
10593
+ sourceSpan,
10594
+ ...NEW_OP,
10595
+ ...TRAIT_CONSUMES_SLOT,
10596
+ ...TRAIT_USES_SLOT_INDEX,
10597
+ };
10598
+ }
10599
+ function createDeferSecondaryOp(deferOp, secondaryView, secondaryBlockKind) {
10600
+ return {
10601
+ kind: OpKind.DeferSecondaryBlock,
10602
+ deferOp,
10603
+ target: secondaryView,
10604
+ secondaryBlockKind,
10605
+ constValue: null,
10606
+ makeExpression: literalOrArrayLiteral$1,
10607
+ ...NEW_OP,
10608
+ ...TRAIT_USES_SLOT_INDEX,
10609
+ ...TRAIT_HAS_CONST,
10610
+ };
10611
+ }
10612
+ function createDeferOnOp(xref, sourceSpan) {
10613
+ return {
10614
+ kind: OpKind.DeferOn,
10615
+ xref,
10616
+ sourceSpan,
10617
+ ...NEW_OP,
10618
+ ...TRAIT_CONSUMES_SLOT,
10619
+ };
10620
+ }
10455
10621
  /**
10456
10622
  * Create an `ExtractedMessageOp`.
10457
10623
  */
@@ -10467,12 +10633,12 @@ function createExtractedMessageOp(owner, expression, statements) {
10467
10633
  /**
10468
10634
  * Create an `I18nStartOp`.
10469
10635
  */
10470
- function createI18nStartOp(xref, i18n) {
10636
+ function createI18nStartOp(xref, message) {
10471
10637
  return {
10472
10638
  kind: OpKind.I18nStart,
10473
10639
  xref,
10474
- i18n,
10475
- tagNameParams: {},
10640
+ message,
10641
+ params: new Map(),
10476
10642
  messageIndex: null,
10477
10643
  ...NEW_OP,
10478
10644
  ...TRAIT_CONSUMES_SLOT,
@@ -10488,6 +10654,12 @@ function createI18nEndOp(xref) {
10488
10654
  ...NEW_OP,
10489
10655
  };
10490
10656
  }
10657
+ function literalOrArrayLiteral$1(value) {
10658
+ if (Array.isArray(value)) {
10659
+ return literalArr(value.map(literalOrArrayLiteral$1));
10660
+ }
10661
+ return literal(value, INFERRED_TYPE);
10662
+ }
10491
10663
 
10492
10664
  function createHostPropertyOp(name, expression, isAnimationTrigger, sourceSpan) {
10493
10665
  return {
@@ -10501,6 +10673,12 @@ function createHostPropertyOp(name, expression, isAnimationTrigger, sourceSpan)
10501
10673
  };
10502
10674
  }
10503
10675
 
10676
+ /**
10677
+ * When referenced in the template's context parameters, this indicates a reference to the entire
10678
+ * context object, rather than a specific parameter.
10679
+ */
10680
+ const CTX_REF = 'CTX_REF_MARKER';
10681
+
10504
10682
  var CompilationJobKind;
10505
10683
  (function (CompilationJobKind) {
10506
10684
  CompilationJobKind[CompilationJobKind["Tmpl"] = 0] = "Tmpl";
@@ -10688,10 +10866,16 @@ function phaseVarCounting(job) {
10688
10866
  // First, count the vars used in each view, and update the view-level counter.
10689
10867
  for (const unit of job.units) {
10690
10868
  let varCount = 0;
10869
+ // Count variables on top-level ops first. Don't explore nested expressions just yet.
10691
10870
  for (const op of unit.ops()) {
10692
10871
  if (hasConsumesVarsTrait(op)) {
10693
10872
  varCount += varsUsedByOp(op);
10694
10873
  }
10874
+ }
10875
+ // Count variables on expressions inside ops. We do this later because some of these expressions
10876
+ // might be conditional (e.g. `pipeBinding` inside of a ternary), and we don't want to interfere
10877
+ // with indices for top-level binding slots (e.g. `property`).
10878
+ for (const op of unit.ops()) {
10695
10879
  visitExpressionsInOp(op, expr => {
10696
10880
  if (!isIrExpression(expr)) {
10697
10881
  return;
@@ -10752,6 +10936,9 @@ function varsUsedByOp(op) {
10752
10936
  case OpKind.InterpolateText:
10753
10937
  // `ir.InterpolateTextOp`s use a variable slot for each dynamic expression.
10754
10938
  return op.interpolation.expressions.length;
10939
+ case OpKind.I18nExpression:
10940
+ case OpKind.Conditional:
10941
+ return 1;
10755
10942
  default:
10756
10943
  throw new Error(`Unhandled op: ${OpKind[op.kind]}`);
10757
10944
  }
@@ -10823,6 +11010,35 @@ function removeAnys(e) {
10823
11010
  return e;
10824
11011
  }
10825
11012
 
11013
+ /**
11014
+ * Adds apply operations after i18n expressions.
11015
+ */
11016
+ function phaseApplyI18nExpressions(job) {
11017
+ for (const unit of job.units) {
11018
+ for (const op of unit.update) {
11019
+ // Only add apply after expressions that are not followed by more expressions.
11020
+ if (op.kind === OpKind.I18nExpression && needsApplication(op)) {
11021
+ // TODO: what should be the source span for the apply op?
11022
+ OpList.insertAfter(createI18nApplyOp(op.target, null), op);
11023
+ }
11024
+ }
11025
+ }
11026
+ }
11027
+ /**
11028
+ * Checks whether the given expression op needs to be followed with an apply op.
11029
+ */
11030
+ function needsApplication(op) {
11031
+ // If the next op is not another expression, we need to apply.
11032
+ if (op.next?.kind !== OpKind.I18nExpression) {
11033
+ return true;
11034
+ }
11035
+ // If the next op is an expression targeting a different i18n block, we need to apply.
11036
+ if (op.next.target !== op.target) {
11037
+ return true;
11038
+ }
11039
+ return false;
11040
+ }
11041
+
10826
11042
  /**
10827
11043
  * Gets a map of all elements in the given view by their xref id.
10828
11044
  */
@@ -10984,14 +11200,20 @@ function phaseBindingSpecialization(job) {
10984
11200
  }
10985
11201
 
10986
11202
  const CHAINABLE = new Set([
10987
- Identifiers.elementStart,
10988
- Identifiers.elementEnd,
11203
+ Identifiers.attribute,
11204
+ Identifiers.classProp,
10989
11205
  Identifiers.element,
10990
- Identifiers.property,
11206
+ Identifiers.elementContainer,
11207
+ Identifiers.elementContainerEnd,
11208
+ Identifiers.elementContainerStart,
11209
+ Identifiers.elementEnd,
11210
+ Identifiers.elementStart,
10991
11211
  Identifiers.hostProperty,
10992
- Identifiers.syntheticHostProperty,
11212
+ Identifiers.i18nExp,
11213
+ Identifiers.listener,
11214
+ Identifiers.listener,
11215
+ Identifiers.property,
10993
11216
  Identifiers.styleProp,
10994
- Identifiers.attribute,
10995
11217
  Identifiers.stylePropInterpolate1,
10996
11218
  Identifiers.stylePropInterpolate2,
10997
11219
  Identifiers.stylePropInterpolate3,
@@ -11001,13 +11223,8 @@ const CHAINABLE = new Set([
11001
11223
  Identifiers.stylePropInterpolate7,
11002
11224
  Identifiers.stylePropInterpolate8,
11003
11225
  Identifiers.stylePropInterpolateV,
11004
- Identifiers.classProp,
11005
- Identifiers.listener,
11006
- Identifiers.elementContainerStart,
11007
- Identifiers.elementContainerEnd,
11008
- Identifiers.elementContainer,
11009
- Identifiers.listener,
11010
11226
  Identifiers.syntheticHostListener,
11227
+ Identifiers.syntheticHostProperty,
11011
11228
  Identifiers.templateCreate,
11012
11229
  ]);
11013
11230
  /**
@@ -11084,9 +11301,9 @@ function phaseConditionals(job) {
11084
11301
  }
11085
11302
  let test;
11086
11303
  // Any case with a `null` condition is `default`. If one exists, default to it instead.
11087
- const defaultCase = op.conditions.findIndex(([xref, cond]) => cond === null);
11304
+ const defaultCase = op.conditions.findIndex((cond) => cond.expr === null);
11088
11305
  if (defaultCase >= 0) {
11089
- const [xref, cond] = op.conditions.splice(defaultCase, 1)[0];
11306
+ const xref = op.conditions.splice(defaultCase, 1)[0].target;
11090
11307
  test = new SlotLiteralExpr(xref);
11091
11308
  }
11092
11309
  else {
@@ -11094,13 +11311,26 @@ function phaseConditionals(job) {
11094
11311
  test = literal(-1);
11095
11312
  }
11096
11313
  // Switch expressions assign their main test to a temporary, to avoid re-executing it.
11097
- let tmp = new AssignTemporaryExpr(op.test, job.allocateXrefId());
11098
- // For each remaining condition, test whether the temporary satifies the check.
11314
+ let tmp = op.test == null ? null : new AssignTemporaryExpr(op.test, job.allocateXrefId());
11315
+ // For each remaining condition, test whether the temporary satifies the check. (If no temp is
11316
+ // present, just check each expression directly.)
11099
11317
  for (let i = op.conditions.length - 1; i >= 0; i--) {
11100
- const useTmp = i === 0 ? tmp : new ReadTemporaryExpr(tmp.xref);
11101
- const [xref, check] = op.conditions[i];
11102
- const comparison = new BinaryOperatorExpr(BinaryOperator.Identical, useTmp, check);
11103
- test = new ConditionalExpr(comparison, new SlotLiteralExpr(xref), test);
11318
+ let conditionalCase = op.conditions[i];
11319
+ if (conditionalCase.expr === null) {
11320
+ continue;
11321
+ }
11322
+ if (tmp !== null) {
11323
+ const useTmp = i === 0 ? tmp : new ReadTemporaryExpr(tmp.xref);
11324
+ conditionalCase.expr =
11325
+ new BinaryOperatorExpr(BinaryOperator.Identical, useTmp, conditionalCase.expr);
11326
+ }
11327
+ else if (conditionalCase.alias !== null) {
11328
+ const caseExpressionTemporaryXref = job.allocateXrefId();
11329
+ conditionalCase.expr =
11330
+ new AssignTemporaryExpr(conditionalCase.expr, caseExpressionTemporaryXref);
11331
+ op.contextValue = new ReadTemporaryExpr(caseExpressionTemporaryXref);
11332
+ }
11333
+ test = new ConditionalExpr(conditionalCase.expr, new SlotLiteralExpr(conditionalCase.target), test);
11104
11334
  }
11105
11335
  // Save the resulting aggregate Joost-expression.
11106
11336
  op.processed = test;
@@ -11162,14 +11392,15 @@ const BINARY_OPERATORS = new Map([
11162
11392
  ['||', BinaryOperator.Or],
11163
11393
  ['+', BinaryOperator.Plus],
11164
11394
  ]);
11165
- const NAMESPACES = new Map([['svg', Namespace.SVG], ['math', Namespace.Math]]);
11166
11395
  function namespaceForKey(namespacePrefixKey) {
11396
+ const NAMESPACES = new Map([['svg', Namespace.SVG], ['math', Namespace.Math]]);
11167
11397
  if (namespacePrefixKey === null) {
11168
11398
  return Namespace.HTML;
11169
11399
  }
11170
11400
  return NAMESPACES.get(namespacePrefixKey) ?? Namespace.HTML;
11171
11401
  }
11172
11402
  function keyForNamespace(namespace) {
11403
+ const NAMESPACES = new Map([['svg', Namespace.SVG], ['math', Namespace.Math]]);
11173
11404
  for (const [k, n] of NAMESPACES.entries()) {
11174
11405
  if (n === namespace) {
11175
11406
  return k;
@@ -11345,6 +11576,10 @@ const REPLACEMENTS = new Map([
11345
11576
  [OpKind.ContainerEnd, [OpKind.ContainerStart, OpKind.Container]],
11346
11577
  [OpKind.I18nEnd, [OpKind.I18nStart, OpKind.I18n]],
11347
11578
  ]);
11579
+ /**
11580
+ * Op kinds that should not prevent merging of start/end ops.
11581
+ */
11582
+ const IGNORED_OP_KINDS = new Set([OpKind.Pipe]);
11348
11583
  /**
11349
11584
  * Replace sequences of mergable elements (e.g. `ElementStart` and `ElementEnd`) with a consolidated
11350
11585
  * element (e.g. `Element`).
@@ -11352,15 +11587,22 @@ const REPLACEMENTS = new Map([
11352
11587
  function phaseEmptyElements(job) {
11353
11588
  for (const unit of job.units) {
11354
11589
  for (const op of unit.create) {
11590
+ // Find end ops that may be able to be merged.
11355
11591
  const opReplacements = REPLACEMENTS.get(op.kind);
11356
11592
  if (opReplacements === undefined) {
11357
11593
  continue;
11358
11594
  }
11359
11595
  const [startKind, mergedKind] = opReplacements;
11360
- if (op.prev !== null && op.prev.kind === startKind) {
11596
+ // Locate the previous (non-ignored) op.
11597
+ let prevOp = op.prev;
11598
+ while (prevOp !== null && IGNORED_OP_KINDS.has(prevOp.kind)) {
11599
+ prevOp = prevOp.prev;
11600
+ }
11601
+ // If the previous op is the corresponding start op, we can megre.
11602
+ if (prevOp !== null && prevOp.kind === startKind) {
11361
11603
  // Transmute the start instruction to the merged version. This is safe as they're designed
11362
11604
  // to be identical apart from the `kind`.
11363
- op.prev.kind = mergedKind;
11605
+ prevOp.kind = mergedKind;
11364
11606
  // Remove the end instruction.
11365
11607
  OpList.remove(op);
11366
11608
  }
@@ -11551,13 +11793,31 @@ function phaseGenerateAdvance(job) {
11551
11793
  for (const unit of job.units) {
11552
11794
  // First build a map of all of the declarations in the view that have assigned slots.
11553
11795
  const slotMap = new Map();
11796
+ let lastSlotOp = null;
11554
11797
  for (const op of unit.create) {
11798
+ // For i18n blocks, we want to advance to the last element index in the block before invoking
11799
+ // `i18nExp` instructions, to make sure the necessary lifecycle hooks of components/directives
11800
+ // are properly flushed.
11801
+ if (op.kind === OpKind.I18nEnd) {
11802
+ if (lastSlotOp === null) {
11803
+ throw Error('Expected to have encountered an op prior to i18nEnd that consumes a slot');
11804
+ }
11805
+ // TODO(mmalerba): For empty i18n blocks, we move to the next slot to match
11806
+ // TemplateDefinitionBuilder. This seems like just a quirk resulting from the special
11807
+ // handling of i18n blocks that can be removed when compatibility is no longer required.
11808
+ let lastSlot = lastSlotOp.slot;
11809
+ if (lastSlotOp.kind === OpKind.I18nStart && job.compatibility) {
11810
+ lastSlot++;
11811
+ }
11812
+ slotMap.set(op.xref, lastSlot);
11813
+ }
11555
11814
  if (!hasConsumesSlotTrait(op)) {
11556
11815
  continue;
11557
11816
  }
11558
11817
  else if (op.slot === null) {
11559
11818
  throw new Error(`AssertionError: expected slots to have been allocated before generating advance() calls`);
11560
11819
  }
11820
+ lastSlotOp = op;
11561
11821
  slotMap.set(op.xref, op.slot);
11562
11822
  }
11563
11823
  // Next, step through the update operations and generate `ir.AdvanceOp`s as required to ensure
@@ -11592,31 +11852,39 @@ function phaseGenerateAdvance(job) {
11592
11852
  }
11593
11853
 
11594
11854
  /**
11595
- * Generate i18n start and end isntructions to mark i18n blocks.
11855
+ * Locate projection slots, populate the each component's `ngContentSelectors` literal field,
11856
+ * populate `project` arguments, and generate the required `projectionDef` instruction for the job's
11857
+ * root view.
11596
11858
  */
11597
- function phaseGenerateI18nBlocks(job) {
11859
+ function phaseGenerateProjectionDef(job) {
11860
+ // TODO: Why does TemplateDefinitionBuilder force a shared constant?
11861
+ const share = job.compatibility === CompatibilityMode.TemplateDefinitionBuilder;
11862
+ // Collect all selectors from this component, and its nested views. Also, assign each projection a
11863
+ // unique ascending projection slot index.
11864
+ const selectors = [];
11865
+ let projectionSlotIndex = 0;
11598
11866
  for (const unit of job.units) {
11599
- const elements = getElementsByXrefId(unit);
11600
11867
  for (const op of unit.create) {
11601
- switch (op.kind) {
11602
- case OpKind.ElementEnd:
11603
- const start = elements.get(op.xref);
11604
- if (start.i18n instanceof Message) {
11605
- const id = job.allocateXrefId();
11606
- OpList.insertAfter(createI18nStartOp(id, start.i18n), start);
11607
- OpList.insertBefore(createI18nEndOp(id), op);
11608
- }
11609
- break;
11610
- case OpKind.Template:
11611
- if (op.i18n !== undefined) {
11612
- const id = job.allocateXrefId();
11613
- OpList.insertBefore(createI18nStartOp(id, op.i18n), op);
11614
- OpList.insertAfter(createI18nEndOp(id), op);
11615
- }
11616
- break;
11868
+ if (op.kind === OpKind.Projection) {
11869
+ selectors.push(op.selector);
11870
+ op.projectionSlotIndex = projectionSlotIndex++;
11617
11871
  }
11618
11872
  }
11619
11873
  }
11874
+ if (selectors.length > 0) {
11875
+ // Create the projectionDef array. If we only found a single wildcard selector, then we use the
11876
+ // default behavior with no arguments instead.
11877
+ let defExpr = null;
11878
+ if (selectors.length > 1 || selectors[0] !== '*') {
11879
+ const def = selectors.map(s => s === '*' ? s : parseSelectorToR3Selector(s));
11880
+ defExpr = job.pool.getConstLiteral(literalOrArrayLiteral(def), share);
11881
+ }
11882
+ // Create the ngContentSelectors constant.
11883
+ job.contentSelectors = job.pool.getConstLiteral(literalOrArrayLiteral(selectors), share);
11884
+ // The projection def instruction goes at the beginning of the root view, before any
11885
+ // `projection` instructions.
11886
+ job.root.create.prepend([createProjectionDefOp(defExpr)]);
11887
+ }
11620
11888
  }
11621
11889
 
11622
11890
  /**
@@ -11727,7 +11995,11 @@ function generateVariablesInScopeForView(view, scope) {
11727
11995
  }
11728
11996
  // Add variables for all context variables available in this scope's view.
11729
11997
  for (const [name, value] of view.job.views.get(scope.view).contextVariables) {
11730
- newOps.push(createVariableOp(view.job.allocateXrefId(), scope.contextVariables.get(name), new ReadPropExpr(new ContextExpr(scope.view), value)));
11998
+ const context = new ContextExpr(scope.view);
11999
+ // We either read the context, or, if the variable is CTX_REF, use the context directly.
12000
+ const variable = value === CTX_REF ? context : new ReadPropExpr(context, value);
12001
+ // Add the variable declaration.
12002
+ newOps.push(createVariableOp(view.job.allocateXrefId(), scope.contextVariables.get(name), variable));
11731
12003
  }
11732
12004
  // Add variables for all local references declared for elements in this scope.
11733
12005
  for (const ref of scope.references) {
@@ -11740,15 +12012,46 @@ function generateVariablesInScopeForView(view, scope) {
11740
12012
  return newOps;
11741
12013
  }
11742
12014
 
12015
+ /**
12016
+ * Looks for the HasConst trait, indicating that an op or expression has some data which
12017
+ * should be collected into the constant array. Capable of collecting either a single literal value,
12018
+ * or an array literal.
12019
+ */
12020
+ function phaseConstTraitCollection(job) {
12021
+ const collectGlobalConsts = (e) => {
12022
+ if (e instanceof ExpressionBase && hasConstTrait(e)) {
12023
+ // TODO: Figure out how to make this type narrowing work.
12024
+ const ea = e;
12025
+ if (ea.constValue !== null) {
12026
+ ea.constIndex = job.addConst(ea.constValue);
12027
+ }
12028
+ }
12029
+ return e;
12030
+ };
12031
+ for (const unit of job.units) {
12032
+ for (const op of unit.ops()) {
12033
+ if (hasConstTrait(op) && op.constValue !== null) {
12034
+ op.constIndex = job.addConst(op.makeExpression(op.constValue));
12035
+ }
12036
+ transformExpressionsInOp(op, collectGlobalConsts, VisitorContextFlag.None);
12037
+ }
12038
+ }
12039
+ }
12040
+
11743
12041
  const STYLE_DOT = 'style.';
11744
12042
  const CLASS_DOT = 'class.';
11745
12043
  const STYLE_BANG = 'style!';
11746
12044
  const CLASS_BANG = 'class!';
12045
+ const BANG_IMPORTANT = '!important';
11747
12046
  function phaseHostStylePropertyParsing(job) {
11748
12047
  for (const op of job.root.update) {
11749
12048
  if (op.kind !== OpKind.Binding) {
11750
12049
  continue;
11751
12050
  }
12051
+ if (op.name.endsWith(BANG_IMPORTANT)) {
12052
+ // Delete any `!important` suffixes from the binding name.
12053
+ op.name = op.name.substring(0, op.name.length - BANG_IMPORTANT.length);
12054
+ }
11752
12055
  if (op.name.startsWith(STYLE_DOT)) {
11753
12056
  op.bindingKind = BindingKind.StyleProperty;
11754
12057
  op.name = op.name.substring(STYLE_DOT.length);
@@ -11802,6 +12105,32 @@ function parseProperty$1(name) {
11802
12105
  return { property, suffix };
11803
12106
  }
11804
12107
 
12108
+ /**
12109
+ * Lifts i18n properties into the consts array.
12110
+ */
12111
+ function phaseI18nConstCollection(job) {
12112
+ // Serialize the extracted messages into the const array.
12113
+ // TODO: Use `Map` instead of object.
12114
+ const messageConstIndices = {};
12115
+ for (const unit of job.units) {
12116
+ for (const op of unit.create) {
12117
+ if (op.kind === OpKind.ExtractedMessage) {
12118
+ messageConstIndices[op.owner] = job.addConst(op.expression, op.statements);
12119
+ OpList.remove(op);
12120
+ }
12121
+ }
12122
+ }
12123
+ // Assign const index to i18n ops that messages were extracted from.
12124
+ for (const unit of job.units) {
12125
+ for (const op of unit.create) {
12126
+ if ((op.kind === OpKind.I18nStart || op.kind === OpKind.I18n) &&
12127
+ messageConstIndices[op.xref] !== undefined) {
12128
+ op.messageIndex = messageConstIndices[op.xref];
12129
+ }
12130
+ }
12131
+ }
12132
+ }
12133
+
11805
12134
  function mapEntry(key, value) {
11806
12135
  return { key, value, quoted: false };
11807
12136
  }
@@ -16691,7 +17020,7 @@ class _Tokenizer {
16691
17020
  new PlainCharacterCursor(_file, range);
16692
17021
  this._preserveLineEndings = options.preserveLineEndings || false;
16693
17022
  this._i18nNormalizeLineEndingsInICUs = options.i18nNormalizeLineEndingsInICUs || false;
16694
- this._tokenizeBlocks = options.tokenizeBlocks || false;
17023
+ this._tokenizeBlocks = options.tokenizeBlocks ?? true;
16695
17024
  try {
16696
17025
  this._cursor.init();
16697
17026
  }
@@ -18668,16 +18997,16 @@ function phaseI18nMessageExtraction(job) {
18668
18997
  const fileBasedI18nSuffix = job.relativeContextFilePath.replace(/[^A-Za-z0-9]/g, '_').toUpperCase() + '_';
18669
18998
  for (const unit of job.units) {
18670
18999
  for (const op of unit.create) {
18671
- if (op.kind === OpKind.I18nStart && op.i18n instanceof Message) {
19000
+ if ((op.kind === OpKind.I18nStart || op.kind === OpKind.I18n)) {
18672
19001
  // Sort the params map to match the ordering in TemplateDefinitionBuilder.
18673
- const params = Object.fromEntries(Object.entries(op.tagNameParams).sort());
19002
+ const params = new Map([...op.params.entries()].sort());
18674
19003
  const mainVar = variable(job.pool.uniqueName(TRANSLATION_VAR_PREFIX));
18675
19004
  // Closure Compiler requires const names to start with `MSG_` but disallows any other const
18676
19005
  // to start with `MSG_`. We define a variable starting with `MSG_` just for the
18677
19006
  // `goog.getMsg` call
18678
- const closureVar = i18nGenerateClosureVar(job.pool, op.i18n.id, fileBasedI18nSuffix, job.i18nUseExternalIds);
19007
+ const closureVar = i18nGenerateClosureVar(job.pool, op.message.id, fileBasedI18nSuffix, job.i18nUseExternalIds);
18679
19008
  // TODO: figure out transformFn.
18680
- const statements = getTranslationDeclStmts$1(op.i18n, mainVar, closureVar, params, undefined /*transformFn*/);
19009
+ const statements = getTranslationDeclStmts$1(op.message, mainVar, closureVar, params, undefined /*transformFn*/);
18681
19010
  unit.create.push(createExtractedMessageOp(op.xref, mainVar, statements));
18682
19011
  }
18683
19012
  }
@@ -18709,10 +19038,11 @@ function phaseI18nMessageExtraction(job) {
18709
19038
  * post-processing).
18710
19039
  * @returns An array of statements that defined a given translation.
18711
19040
  */
18712
- function getTranslationDeclStmts$1(message, variable, closureVar, params = {}, transformFn) {
19041
+ function getTranslationDeclStmts$1(message, variable, closureVar, params, transformFn) {
19042
+ const paramsObject = Object.fromEntries(params);
18713
19043
  const statements = [
18714
19044
  declareI18nVariable(variable),
18715
- ifStmt(createClosureModeGuard$1(), createGoogleGetMsgStatements(variable, message, closureVar, params), createLocalizeStatements(variable, message, formatI18nPlaceholderNamesInMap(params, /* useCamelCase */ false))),
19045
+ ifStmt(createClosureModeGuard$1(), createGoogleGetMsgStatements(variable, message, closureVar, paramsObject), createLocalizeStatements(variable, message, formatI18nPlaceholderNamesInMap(paramsObject, /* useCamelCase */ false))),
18716
19046
  ];
18717
19047
  if (transformFn) {
18718
19048
  statements.push(new ExpressionStatement(variable.set(transformFn(variable))));
@@ -18755,22 +19085,48 @@ function i18nGenerateClosureVar(pool, messageId, fileBasedI18nSuffix, useExterna
18755
19085
  */
18756
19086
  function phaseI18nTextExtraction(job) {
18757
19087
  for (const unit of job.units) {
18758
- let inI18nBlock = false;
19088
+ // Remove all text nodes within i18n blocks, their content is already captured in the i18n
19089
+ // message.
19090
+ let currentI18nId = null;
19091
+ const textNodes = new Map();
18759
19092
  for (const op of unit.create) {
18760
19093
  switch (op.kind) {
18761
19094
  case OpKind.I18nStart:
18762
- inI18nBlock = true;
19095
+ currentI18nId = op.xref;
18763
19096
  break;
18764
19097
  case OpKind.I18nEnd:
18765
- inI18nBlock = false;
19098
+ currentI18nId = null;
18766
19099
  break;
18767
19100
  case OpKind.Text:
18768
- if (inI18nBlock) {
19101
+ if (currentI18nId !== null) {
19102
+ textNodes.set(op.xref, currentI18nId);
18769
19103
  OpList.remove(op);
18770
19104
  }
18771
19105
  break;
18772
19106
  }
18773
19107
  }
19108
+ // Update any interpolations to the removed text, and instead represent them as a series of i18n
19109
+ // expressions that we then apply.
19110
+ for (const op of unit.update) {
19111
+ switch (op.kind) {
19112
+ case OpKind.InterpolateText:
19113
+ if (!textNodes.has(op.target)) {
19114
+ continue;
19115
+ }
19116
+ const i18nBlockId = textNodes.get(op.target);
19117
+ const ops = [];
19118
+ for (let i = 0; i < op.interpolation.expressions.length; i++) {
19119
+ const expr = op.interpolation.expressions[i];
19120
+ const placeholder = op.i18nPlaceholders[i];
19121
+ ops.push(createI18nExpressionOp(i18nBlockId, expr, placeholder, expr.sourceSpan ?? op.sourceSpan));
19122
+ }
19123
+ if (ops.length > 0) {
19124
+ // ops.push(ir.createI18nApplyOp(i18nBlockId, op.i18nPlaceholders, op.sourceSpan));
19125
+ }
19126
+ OpList.replaceWithMany(op, ops);
19127
+ break;
19128
+ }
19129
+ }
18774
19130
  }
18775
19131
  }
18776
19132
 
@@ -18934,7 +19290,7 @@ function addNamesToView(unit, baseName, state, compatibility) {
18934
19290
  if (op.handlerFnName !== null) {
18935
19291
  break;
18936
19292
  }
18937
- if (!op.hostListener && op.slot === null) {
19293
+ if (!op.hostListener && op.targetSlot === null) {
18938
19294
  throw new Error(`Expected a slot to be assigned`);
18939
19295
  }
18940
19296
  let animation = '';
@@ -18946,7 +19302,7 @@ function addNamesToView(unit, baseName, state, compatibility) {
18946
19302
  op.handlerFnName = `${baseName}_${animation}${op.name}_HostBindingHandler`;
18947
19303
  }
18948
19304
  else {
18949
- op.handlerFnName = `${unit.fnName}_${op.tag.replace('-', '_')}_${animation}${op.name}_${op.slot}_listener`;
19305
+ op.handlerFnName = `${unit.fnName}_${op.tag.replace('-', '_')}_${animation}${op.name}_${op.targetSlot}_listener`;
18950
19306
  }
18951
19307
  op.handlerFnName = sanitizeIdentifier(op.handlerFnName);
18952
19308
  break;
@@ -19095,6 +19451,11 @@ function phaseNgContainer(job) {
19095
19451
  for (const unit of job.units) {
19096
19452
  const updatedElementXrefs = new Set();
19097
19453
  for (const op of unit.create) {
19454
+ if (op.kind === OpKind.Element && op.tag === CONTAINER_TAG) {
19455
+ // Transmute the `Element` instruction to `Container`.
19456
+ op.kind = OpKind.Container;
19457
+ updatedElementXrefs.add(op.xref);
19458
+ }
19098
19459
  if (op.kind === OpKind.ElementStart && op.tag === CONTAINER_TAG) {
19099
19460
  // Transmute the `ElementStart` instruction to `ContainerStart`.
19100
19461
  op.kind = OpKind.ContainerStart;
@@ -19108,30 +19469,6 @@ function phaseNgContainer(job) {
19108
19469
  }
19109
19470
  }
19110
19471
 
19111
- function phaseNoListenersOnTemplates(job) {
19112
- for (const unit of job.units) {
19113
- let inTemplate = false;
19114
- for (const op of unit.create) {
19115
- switch (op.kind) {
19116
- case OpKind.Template:
19117
- inTemplate = true;
19118
- break;
19119
- case OpKind.ElementStart:
19120
- case OpKind.Element:
19121
- case OpKind.ContainerStart:
19122
- case OpKind.Container:
19123
- inTemplate = false;
19124
- break;
19125
- case OpKind.Listener:
19126
- if (inTemplate) {
19127
- OpList.remove(op);
19128
- }
19129
- break;
19130
- }
19131
- }
19132
- }
19133
- }
19134
-
19135
19472
  /**
19136
19473
  * Looks up an element in the given map by xref ID.
19137
19474
  */
@@ -19189,7 +19526,96 @@ function phaseNullishCoalescing(job) {
19189
19526
  }
19190
19527
  }
19191
19528
 
19192
- /**
19529
+ function kindTest(kind) {
19530
+ return (op) => op.kind === kind;
19531
+ }
19532
+ /**
19533
+ * Defines the groups based on `OpKind` that ops will be divided into, for the various create
19534
+ * binding kinds. Ops will be collected into groups, then optionally transformed, before recombining
19535
+ * the groups in the order defined here.
19536
+ */
19537
+ const CREATE_ORDERING = [
19538
+ { test: op => op.kind === OpKind.Listener && op.hostListener && op.isAnimationListener },
19539
+ { test: op => op.kind === OpKind.Listener && !(op.hostListener && op.isAnimationListener) },
19540
+ ];
19541
+ /**
19542
+ * As above, but for update ops.
19543
+ */
19544
+ const UPDATE_ORDERING = [
19545
+ { test: op => op.kind === OpKind.HostProperty && op.expression instanceof Interpolation },
19546
+ { test: op => op.kind === OpKind.HostProperty && !(op.expression instanceof Interpolation) },
19547
+ { test: kindTest(OpKind.StyleMap), transform: keepLast },
19548
+ { test: kindTest(OpKind.ClassMap), transform: keepLast },
19549
+ { test: kindTest(OpKind.StyleProp) },
19550
+ { test: kindTest(OpKind.ClassProp) },
19551
+ { test: op => op.kind === OpKind.Property && op.expression instanceof Interpolation },
19552
+ { test: op => op.kind === OpKind.Property && !(op.expression instanceof Interpolation) },
19553
+ { test: kindTest(OpKind.Attribute) },
19554
+ ];
19555
+ /**
19556
+ * The set of all op kinds we handle in the reordering phase.
19557
+ */
19558
+ const handledOpKinds = new Set([
19559
+ OpKind.Listener, OpKind.StyleMap, OpKind.ClassMap, OpKind.StyleProp,
19560
+ OpKind.ClassProp, OpKind.Property, OpKind.HostProperty, OpKind.Attribute
19561
+ ]);
19562
+ function phaseOrdering(job) {
19563
+ for (const unit of job.units) {
19564
+ // First, we pull out ops that need to be ordered. Then, when we encounter an op that shouldn't
19565
+ // be reordered, put the ones we've pulled so far back in the correct order. Finally, if we
19566
+ // still have ops pulled at the end, put them back in the correct order.
19567
+ // Create mode:
19568
+ let opsToOrder = [];
19569
+ for (const op of unit.create) {
19570
+ if (handledOpKinds.has(op.kind)) {
19571
+ opsToOrder.push(op);
19572
+ OpList.remove(op);
19573
+ }
19574
+ else {
19575
+ OpList.insertBefore(reorder(opsToOrder, CREATE_ORDERING), op);
19576
+ opsToOrder = [];
19577
+ }
19578
+ }
19579
+ unit.create.push(reorder(opsToOrder, CREATE_ORDERING));
19580
+ // Update mode:
19581
+ opsToOrder = [];
19582
+ for (const op of unit.update) {
19583
+ if (handledOpKinds.has(op.kind)) {
19584
+ opsToOrder.push(op);
19585
+ OpList.remove(op);
19586
+ }
19587
+ else {
19588
+ OpList.insertBefore(reorder(opsToOrder, UPDATE_ORDERING), op);
19589
+ opsToOrder = [];
19590
+ }
19591
+ }
19592
+ unit.update.push(reorder(opsToOrder, UPDATE_ORDERING));
19593
+ }
19594
+ }
19595
+ /**
19596
+ * Reorders the given list of ops according to the ordering defined by `ORDERING`.
19597
+ */
19598
+ function reorder(ops, ordering) {
19599
+ // Break the ops list into groups based on OpKind.
19600
+ const groups = Array.from(ordering, () => new Array());
19601
+ for (const op of ops) {
19602
+ const groupIndex = ordering.findIndex(o => o.test(op));
19603
+ groups[groupIndex].push(op);
19604
+ }
19605
+ // Reassemble the groups into a single list, in the correct order.
19606
+ return groups.flatMap((group, i) => {
19607
+ const transform = ordering[i].transform;
19608
+ return transform ? transform(group) : group;
19609
+ });
19610
+ }
19611
+ /**
19612
+ * Keeps only the last op in a list of ops.
19613
+ */
19614
+ function keepLast(ops) {
19615
+ return ops.slice(ops.length - 1);
19616
+ }
19617
+
19618
+ /**
19193
19619
  * Parses extracted style and class attributes into separate ExtractedAttributeOps per style or
19194
19620
  * class property.
19195
19621
  */
@@ -19217,6 +19643,36 @@ function phaseParseExtractedStyles(cpl) {
19217
19643
  }
19218
19644
  }
19219
19645
 
19646
+ /**
19647
+ * Attributes of `ng-content` named 'select' are specifically removed, because they control which
19648
+ * content matches as a property of the `projection`, and are not a plain attribute.
19649
+ */
19650
+ function phaseRemoveContentSelectors(job) {
19651
+ for (const unit of job.units) {
19652
+ const elements = getElementsByXrefId(unit);
19653
+ for (const op of unit.update) {
19654
+ switch (op.kind) {
19655
+ case OpKind.Binding:
19656
+ const target = lookupElement(elements, op.target);
19657
+ if (op.name.toLowerCase() === 'select' && target.kind === OpKind.Projection) {
19658
+ OpList.remove(op);
19659
+ }
19660
+ continue;
19661
+ }
19662
+ }
19663
+ }
19664
+ }
19665
+ /**
19666
+ * Looks up an element in the given map by xref ID.
19667
+ */
19668
+ function lookupElement(elements, xref) {
19669
+ const el = elements.get(xref);
19670
+ if (el === undefined) {
19671
+ throw new Error('All attributes should have an element-like target.');
19672
+ }
19673
+ return el;
19674
+ }
19675
+
19220
19676
  function phasePipeCreation(job) {
19221
19677
  for (const unit of job.units) {
19222
19678
  processPipeBindingsInView(unit);
@@ -19283,95 +19739,6 @@ function phasePipeVariadic(job) {
19283
19739
  }
19284
19740
  }
19285
19741
 
19286
- function kindTest(kind) {
19287
- return (op) => op.kind === kind;
19288
- }
19289
- /**
19290
- * Defines the groups based on `OpKind` that ops will be divided into, for the various create
19291
- * binding kinds. Ops will be collected into groups, then optionally transformed, before recombining
19292
- * the groups in the order defined here.
19293
- */
19294
- const CREATE_ORDERING = [
19295
- { test: op => op.kind === OpKind.Listener && op.hostListener && op.isAnimationListener },
19296
- { test: op => op.kind === OpKind.Listener && !(op.hostListener && op.isAnimationListener) },
19297
- ];
19298
- /**
19299
- * As above, but for update ops.
19300
- */
19301
- const UPDATE_ORDERING = [
19302
- { test: op => op.kind === OpKind.HostProperty && op.expression instanceof Interpolation },
19303
- { test: op => op.kind === OpKind.HostProperty && !(op.expression instanceof Interpolation) },
19304
- { test: kindTest(OpKind.StyleMap), transform: keepLast },
19305
- { test: kindTest(OpKind.ClassMap), transform: keepLast },
19306
- { test: kindTest(OpKind.StyleProp) },
19307
- { test: kindTest(OpKind.ClassProp) },
19308
- { test: op => op.kind === OpKind.Property && op.expression instanceof Interpolation },
19309
- { test: op => op.kind === OpKind.Property && !(op.expression instanceof Interpolation) },
19310
- { test: kindTest(OpKind.Attribute) },
19311
- ];
19312
- /**
19313
- * The set of all op kinds we handle in the reordering phase.
19314
- */
19315
- const handledOpKinds = new Set([
19316
- OpKind.Listener, OpKind.StyleMap, OpKind.ClassMap, OpKind.StyleProp,
19317
- OpKind.ClassProp, OpKind.Property, OpKind.HostProperty, OpKind.Attribute
19318
- ]);
19319
- function phaseOrdering(job) {
19320
- for (const unit of job.units) {
19321
- // First, we pull out ops that need to be ordered. Then, when we encounter an op that shouldn't
19322
- // be reordered, put the ones we've pulled so far back in the correct order. Finally, if we
19323
- // still have ops pulled at the end, put them back in the correct order.
19324
- // Create mode:
19325
- let opsToOrder = [];
19326
- for (const op of unit.create) {
19327
- if (handledOpKinds.has(op.kind)) {
19328
- opsToOrder.push(op);
19329
- OpList.remove(op);
19330
- }
19331
- else {
19332
- OpList.insertBefore(reorder(opsToOrder, CREATE_ORDERING), op);
19333
- opsToOrder = [];
19334
- }
19335
- }
19336
- unit.create.push(reorder(opsToOrder, CREATE_ORDERING));
19337
- // Update mode:
19338
- opsToOrder = [];
19339
- for (const op of unit.update) {
19340
- if (handledOpKinds.has(op.kind)) {
19341
- opsToOrder.push(op);
19342
- OpList.remove(op);
19343
- }
19344
- else {
19345
- OpList.insertBefore(reorder(opsToOrder, UPDATE_ORDERING), op);
19346
- opsToOrder = [];
19347
- }
19348
- }
19349
- unit.update.push(reorder(opsToOrder, UPDATE_ORDERING));
19350
- }
19351
- }
19352
- /**
19353
- * Reorders the given list of ops according to the ordering defined by `ORDERING`.
19354
- */
19355
- function reorder(ops, ordering) {
19356
- // Break the ops list into groups based on OpKind.
19357
- const groups = Array.from(ordering, () => new Array());
19358
- for (const op of ops) {
19359
- const groupIndex = ordering.findIndex(o => o.test(op));
19360
- groups[groupIndex].push(op);
19361
- }
19362
- // Reassemble the groups into a single list, in the correct order.
19363
- return groups.flatMap((group, i) => {
19364
- const transform = ordering[i].transform;
19365
- return transform ? transform(group) : group;
19366
- });
19367
- }
19368
- /**
19369
- * Keeps only the last op in a list of ops.
19370
- */
19371
- function keepLast(ops) {
19372
- return ops.slice(ops.length - 1);
19373
- }
19374
-
19375
19742
  function phasePureFunctionExtraction(job) {
19376
19743
  for (const view of job.units) {
19377
19744
  for (const op of view.ops()) {
@@ -19402,7 +19769,7 @@ class PureFunctionConstant extends GenericKeyFn {
19402
19769
  toSharedConstantDeclaration(declName, keyExpr) {
19403
19770
  const fnParams = [];
19404
19771
  for (let idx = 0; idx < this.numArgs; idx++) {
19405
- fnParams.push(new FnParam('_p' + idx));
19772
+ fnParams.push(new FnParam('a' + idx));
19406
19773
  }
19407
19774
  // We will never visit `ir.PureFunctionParameterExpr`s that don't belong to us, because this
19408
19775
  // transform runs inside another visitor which will visit nested pure functions before this one.
@@ -19410,9 +19777,9 @@ class PureFunctionConstant extends GenericKeyFn {
19410
19777
  if (!(expr instanceof PureFunctionParameterExpr)) {
19411
19778
  return expr;
19412
19779
  }
19413
- return variable('_p' + expr.index);
19780
+ return variable('a' + expr.index);
19414
19781
  }, VisitorContextFlag.None);
19415
- return new DeclareFunctionStmt(declName, fnParams, [new ReturnStatement(returnExpr)]);
19782
+ return new DeclareVarStmt(declName, new ArrowFunctionExpr(fnParams, returnExpr), undefined, StmtModifier.Final);
19416
19783
  }
19417
19784
  }
19418
19785
 
@@ -19576,6 +19943,25 @@ function text(slot, initialValue, sourceSpan) {
19576
19943
  }
19577
19944
  return call(Identifiers.text, args, sourceSpan);
19578
19945
  }
19946
+ function defer(selfSlot, primarySlot, dependencyResolverFn, loadingSlot, placeholderSlot, errorSlot, loadingConfigIndex, placeholderConfigIndex, sourceSpan) {
19947
+ const args = [
19948
+ literal(selfSlot),
19949
+ literal(primarySlot),
19950
+ literal(dependencyResolverFn),
19951
+ literal(loadingSlot),
19952
+ literal(placeholderSlot),
19953
+ literal(errorSlot),
19954
+ literal(loadingConfigIndex),
19955
+ literal(placeholderConfigIndex),
19956
+ ];
19957
+ while (args[args.length - 1].value === null) {
19958
+ args.pop();
19959
+ }
19960
+ return call(Identifiers.defer, args, sourceSpan);
19961
+ }
19962
+ function deferOn(sourceSpan) {
19963
+ return call(Identifiers.deferOnIdle, [], sourceSpan);
19964
+ }
19579
19965
  function projectionDef(def) {
19580
19966
  return call(Identifiers.projectionDef, def ? [def] : [], null);
19581
19967
  }
@@ -19592,8 +19978,8 @@ function projection(slot, projectionSlotIndex, attributes) {
19592
19978
  function i18nStart(slot, constIndex) {
19593
19979
  return call(Identifiers.i18nStart, [literal(slot), literal(constIndex)], null);
19594
19980
  }
19595
- function i18n(slot) {
19596
- return call(Identifiers.i18n, [literal(slot)], null);
19981
+ function i18n(slot, constIndex) {
19982
+ return call(Identifiers.i18n, [literal(slot), literal(constIndex)], null);
19597
19983
  }
19598
19984
  function i18nEnd() {
19599
19985
  return call(Identifiers.i18nEnd, [], null);
@@ -19670,6 +20056,12 @@ function textInterpolate(strings, expressions, sourceSpan) {
19670
20056
  }
19671
20057
  return callVariadicInstruction(TEXT_INTERPOLATE_CONFIG, [], interpolationArgs, [], sourceSpan);
19672
20058
  }
20059
+ function i18nExp(expr, sourceSpan) {
20060
+ return call(Identifiers.i18nExp, [expr], sourceSpan);
20061
+ }
20062
+ function i18nApply(slot, sourceSpan) {
20063
+ return call(Identifiers.i18nApply, [literal(slot)], sourceSpan);
20064
+ }
19673
20065
  function propertyInterpolate(name, strings, expressions, sanitizer, sourceSpan) {
19674
20066
  const interpolationArgs = collateInterpolationArgs(strings, expressions);
19675
20067
  const extraArgs = [];
@@ -19739,8 +20131,12 @@ function call(instruction, args, sourceSpan) {
19739
20131
  const expr = importExpr(instruction).callFn(args, sourceSpan);
19740
20132
  return createStatementOp(new ExpressionStatement(expr, sourceSpan));
19741
20133
  }
19742
- function conditional(slot, condition) {
19743
- return call(Identifiers.conditional, [literal(slot), condition], null);
20134
+ function conditional(slot, condition, contextValue, sourceSpan) {
20135
+ const args = [literal(slot), condition];
20136
+ if (contextValue !== null) {
20137
+ args.push(contextValue);
20138
+ }
20139
+ return call(Identifiers.conditional, args, sourceSpan);
19744
20140
  }
19745
20141
  /**
19746
20142
  * `InterpolationConfig` for the `textInterpolate` instruction.
@@ -19972,14 +20368,14 @@ function reifyCreateOperations(unit, ops) {
19972
20368
  OpList.replace(op, i18nEnd());
19973
20369
  break;
19974
20370
  case OpKind.I18n:
19975
- OpList.replace(op, i18n(op.slot));
20371
+ OpList.replace(op, i18n(op.slot, op.messageIndex));
19976
20372
  break;
19977
20373
  case OpKind.Template:
19978
20374
  if (!(unit instanceof ViewCompilationUnit)) {
19979
20375
  throw new Error(`AssertionError: must be compiling a component`);
19980
20376
  }
19981
20377
  const childView = unit.job.views.get(op.xref);
19982
- OpList.replace(op, template(op.slot, variable(childView.fnName), childView.decls, childView.vars, op.controlFlow ? null : op.tag, op.attributes, op.sourceSpan));
20378
+ OpList.replace(op, template(op.slot, variable(childView.fnName), childView.decls, childView.vars, op.block ? null : op.tag, op.attributes, op.sourceSpan));
19983
20379
  break;
19984
20380
  case OpKind.DisableBindings:
19985
20381
  OpList.replace(op, disableBindings());
@@ -20016,6 +20412,15 @@ function reifyCreateOperations(unit, ops) {
20016
20412
  break;
20017
20413
  }
20018
20414
  break;
20415
+ case OpKind.Defer:
20416
+ OpList.replace(op, defer(op.slot, op.targetSlot, null, op.loading && op.loading.targetSlot, op.placeholder && op.placeholder.targetSlot, op.error && op.error.targetSlot, op.loading?.constIndex ?? null, op.placeholder?.constIndex ?? null, op.sourceSpan));
20417
+ break;
20418
+ case OpKind.DeferSecondaryBlock:
20419
+ OpList.remove(op);
20420
+ break;
20421
+ case OpKind.DeferOn:
20422
+ OpList.replace(op, deferOn(op.sourceSpan));
20423
+ break;
20019
20424
  case OpKind.ProjectionDef:
20020
20425
  OpList.replace(op, projectionDef(op.def));
20021
20426
  break;
@@ -20075,6 +20480,12 @@ function reifyUpdateOperations(_unit, ops) {
20075
20480
  OpList.replace(op, classMap(op.expression, op.sourceSpan));
20076
20481
  }
20077
20482
  break;
20483
+ case OpKind.I18nExpression:
20484
+ OpList.replace(op, i18nExp(op.expression, op.sourceSpan));
20485
+ break;
20486
+ case OpKind.I18nApply:
20487
+ OpList.replace(op, i18nApply(op.targetSlot, op.sourceSpan));
20488
+ break;
20078
20489
  case OpKind.InterpolateText:
20079
20490
  OpList.replace(op, textInterpolate(op.interpolation.strings, op.interpolation.expressions, op.sourceSpan));
20080
20491
  break;
@@ -20109,10 +20520,10 @@ function reifyUpdateOperations(_unit, ops) {
20109
20520
  if (op.processed === null) {
20110
20521
  throw new Error(`Conditional test was not set.`);
20111
20522
  }
20112
- if (op.slot === null) {
20523
+ if (op.targetSlot === null) {
20113
20524
  throw new Error(`Conditional slot was not set.`);
20114
20525
  }
20115
- OpList.replace(op, conditional(op.slot, op.processed));
20526
+ OpList.replace(op, conditional(op.targetSlot, op.processed, op.contextValue, op.sourceSpan));
20116
20527
  break;
20117
20528
  case OpKind.Statement:
20118
20529
  // Pass statement operations directly through.
@@ -20130,7 +20541,7 @@ function reifyIrExpression(expr) {
20130
20541
  case ExpressionKind.NextContext:
20131
20542
  return nextContext(expr.steps);
20132
20543
  case ExpressionKind.Reference:
20133
- return reference(expr.slot + 1 + expr.offset);
20544
+ return reference(expr.targetSlot + 1 + expr.offset);
20134
20545
  case ExpressionKind.LexicalRead:
20135
20546
  throw new Error(`AssertionError: unresolved LexicalRead of ${expr.name}`);
20136
20547
  case ExpressionKind.RestoreView:
@@ -20165,13 +20576,13 @@ function reifyIrExpression(expr) {
20165
20576
  case ExpressionKind.PureFunctionParameterExpr:
20166
20577
  throw new Error(`AssertionError: expected PureFunctionParameterExpr to have been extracted`);
20167
20578
  case ExpressionKind.PipeBinding:
20168
- return pipeBind(expr.slot, expr.varOffset, expr.args);
20579
+ return pipeBind(expr.targetSlot, expr.varOffset, expr.args);
20169
20580
  case ExpressionKind.PipeBindingVariadic:
20170
- return pipeBindV(expr.slot, expr.varOffset, expr.args);
20581
+ return pipeBindV(expr.targetSlot, expr.varOffset, expr.args);
20171
20582
  case ExpressionKind.SanitizerExpr:
20172
20583
  return importExpr(sanitizerIdentifierMap.get(expr.fn));
20173
20584
  case ExpressionKind.SlotLiteralExpr:
20174
- return literal(expr.slot);
20585
+ return literal(expr.targetSlot);
20175
20586
  default:
20176
20587
  throw new Error(`AssertionError: Unsupported reification of ir.Expression kind: ${ExpressionKind[expr.kind]}`);
20177
20588
  }
@@ -20300,49 +20711,92 @@ const ESCAPE = '\uFFFD';
20300
20711
  */
20301
20712
  function phaseResolveI18nPlaceholders(job) {
20302
20713
  for (const unit of job.units) {
20303
- let i18nOp = null;
20304
- let startTags = [];
20305
- let closeTags = [];
20714
+ const i18nOps = new Map();
20715
+ let startTagSlots = new Map();
20716
+ let closeTagSlots = new Map();
20717
+ let currentI18nOp = null;
20718
+ // Record slots for tag name placeholders.
20306
20719
  for (const op of unit.create) {
20307
- if (op.kind === OpKind.I18nStart && op.i18n instanceof Message) {
20308
- i18nOp = op;
20309
- }
20310
- else if (op.kind === OpKind.I18nEnd) {
20311
- i18nOp = null;
20720
+ switch (op.kind) {
20721
+ case OpKind.I18nStart:
20722
+ case OpKind.I18n:
20723
+ // Initialize collected slots for a new i18n block.
20724
+ i18nOps.set(op.xref, op);
20725
+ currentI18nOp = op.kind === OpKind.I18nStart ? op : null;
20726
+ startTagSlots = new Map();
20727
+ closeTagSlots = new Map();
20728
+ break;
20729
+ case OpKind.I18nEnd:
20730
+ // Add values for tag placeholders.
20731
+ if (currentI18nOp === null) {
20732
+ throw Error('Missing corresponding i18n start op for i18n end op');
20733
+ }
20734
+ for (const [placeholder, slots] of startTagSlots) {
20735
+ currentI18nOp.params.set(placeholder, serializeSlots(slots, true));
20736
+ }
20737
+ for (const [placeholder, slots] of closeTagSlots) {
20738
+ currentI18nOp.params.set(placeholder, serializeSlots(slots, false));
20739
+ }
20740
+ currentI18nOp = null;
20741
+ break;
20742
+ case OpKind.Element:
20743
+ case OpKind.ElementStart:
20744
+ // Record slots for tag placeholders.
20745
+ if (op.i18nPlaceholder != undefined) {
20746
+ if (currentI18nOp === null) {
20747
+ throw Error('i18n tag placeholder should only occur inside an i18n block');
20748
+ }
20749
+ if (!op.slot) {
20750
+ throw Error('Slots should be allocated before i18n placeholder resolution');
20751
+ }
20752
+ const { startName, closeName } = op.i18nPlaceholder;
20753
+ addTagSlot(startTagSlots, startName, op.slot);
20754
+ addTagSlot(closeTagSlots, closeName, op.slot);
20755
+ }
20756
+ break;
20312
20757
  }
20313
- else if ((op.kind === OpKind.Element || op.kind === OpKind.ElementStart) &&
20314
- op.i18n instanceof TagPlaceholder) {
20315
- if (i18nOp === null) {
20316
- throw Error('i18n tag placeholder should only occur inside an i18n block');
20758
+ }
20759
+ // Fill in values for each of the expression placeholders applied in i18nApply operations.
20760
+ const i18nBlockPlaceholderIndices = new Map();
20761
+ for (const op of unit.update) {
20762
+ if (op.kind === OpKind.I18nExpression) {
20763
+ const i18nOp = i18nOps.get(op.target);
20764
+ let index = i18nBlockPlaceholderIndices.get(op.target) || 0;
20765
+ if (!i18nOp) {
20766
+ throw Error('Cannot find corresponding i18nStart for i18nExpr');
20317
20767
  }
20318
- // In order to add the keys in the same order as TemplateDefinitionBuilder, we separately
20319
- // track the start and close tag placeholders.
20320
- // TODO: when TemplateDefinitionBuilder compatibility is not required, we can just add both
20321
- // keys directly to the map here.
20322
- startTags.push({
20323
- i18nOp,
20324
- placeholder: op.i18n.startName,
20325
- value: literal(`${ESCAPE}#${op.slot}${ESCAPE}`)
20326
- });
20327
- closeTags.push({
20328
- i18nOp,
20329
- placeholder: op.i18n.closeName,
20330
- value: literal(`${ESCAPE}/#${op.slot}${ESCAPE}`)
20331
- });
20768
+ i18nOp.params.set(op.i18nPlaceholder.name, literal(`${ESCAPE}${index++}${ESCAPE}`));
20769
+ i18nBlockPlaceholderIndices.set(op.target, index);
20332
20770
  }
20333
20771
  }
20334
- // Add the start tags in the order we encountered them, to match TemplateDefinitionBuilder.
20335
- for (const { i18nOp, placeholder, value } of startTags) {
20336
- i18nOp.tagNameParams[placeholder] = value;
20337
- }
20338
- // Add the close tags in reverse order that we encountered the start tags, to match
20339
- // TemplateDefinitionBuilder.
20340
- for (let i = closeTags.length - 1; i >= 0; i--) {
20341
- const { i18nOp, placeholder, value } = closeTags[i];
20342
- i18nOp.tagNameParams[placeholder] = value;
20772
+ // Verify that all placeholders have been resolved.
20773
+ for (const op of i18nOps.values()) {
20774
+ for (const placeholder in op.message.placeholders) {
20775
+ if (!op.params.has(placeholder)) {
20776
+ throw Error(`Failed to resolve i18n placeholder: ${placeholder}`);
20777
+ }
20778
+ }
20343
20779
  }
20344
20780
  }
20345
20781
  }
20782
+ /**
20783
+ * Updates the given slots map with the specified slot.
20784
+ */
20785
+ function addTagSlot(tagSlots, placeholder, slot) {
20786
+ const slots = tagSlots.get(placeholder) || [];
20787
+ slots.push(slot);
20788
+ tagSlots.set(placeholder, slots);
20789
+ }
20790
+ /**
20791
+ * Serializes a list of slots to a string literal expression.
20792
+ */
20793
+ function serializeSlots(slots, start) {
20794
+ const slotStrings = slots.map(slot => `${ESCAPE}${start ? '' : '/'}#${slot}${ESCAPE}`);
20795
+ if (slotStrings.length === 1) {
20796
+ return literal(slotStrings[0]);
20797
+ }
20798
+ return literal(`[${slotStrings.join('|')}]`);
20799
+ }
20346
20800
 
20347
20801
  /**
20348
20802
  * Resolves lexical references in views (`ir.LexicalReadExpr`) to either a target variable or to
@@ -20586,19 +21040,19 @@ function phaseSlotAllocation(job) {
20586
21040
  const childView = job.views.get(op.xref);
20587
21041
  op.decls = childView.decls;
20588
21042
  }
20589
- if (hasUsesSlotIndexTrait(op) && op.slot === null) {
21043
+ if (hasUsesSlotIndexTrait(op) && op.targetSlot === null) {
20590
21044
  if (!slotMap.has(op.target)) {
20591
21045
  // We do expect to find a slot allocated for everything which might be referenced.
20592
21046
  throw new Error(`AssertionError: no slot allocated for ${OpKind[op.kind]} target ${op.target}`);
20593
21047
  }
20594
- op.slot = slotMap.get(op.target);
21048
+ op.targetSlot = slotMap.get(op.target);
20595
21049
  }
20596
21050
  // Process all `ir.Expression`s within this view, and look for `usesSlotIndexExprTrait`.
20597
21051
  visitExpressionsInOp(op, expr => {
20598
21052
  if (!isIrExpression(expr)) {
20599
21053
  return;
20600
21054
  }
20601
- if (!hasUsesSlotIndexTrait(expr) || expr.slot !== null) {
21055
+ if (!hasUsesSlotIndexTrait(expr) || expr.targetSlot !== null) {
20602
21056
  return;
20603
21057
  }
20604
21058
  // The `UsesSlotIndexExprTrait` indicates that this expression references something declared
@@ -20609,7 +21063,7 @@ function phaseSlotAllocation(job) {
20609
21063
  throw new Error(`AssertionError: no slot allocated for ${expr.constructor.name} target ${expr.target}`);
20610
21064
  }
20611
21065
  // Record the allocated slot on the expression.
20612
- expr.slot = slotMap.get(expr.target);
21066
+ expr.targetSlot = slotMap.get(expr.target);
20613
21067
  });
20614
21068
  }
20615
21069
  }
@@ -20660,47 +21114,62 @@ function phaseStyleBindingSpecialization(cpl) {
20660
21114
  */
20661
21115
  function phaseTemporaryVariables(cpl) {
20662
21116
  for (const unit of cpl.units) {
20663
- let opCount = 0;
20664
- let generatedStatements = [];
20665
- for (const op of unit.ops()) {
20666
- // Identify the final time each temp var is read.
20667
- const finalReads = new Map();
20668
- visitExpressionsInOp(op, expr => {
20669
- if (expr instanceof ReadTemporaryExpr) {
20670
- finalReads.set(expr.xref, expr);
20671
- }
20672
- });
20673
- // Name the temp vars, accounting for the fact that a name can be reused after it has been
20674
- // read for the final time.
20675
- let count = 0;
20676
- const assigned = new Set();
20677
- const released = new Set();
20678
- const defs = new Map();
20679
- visitExpressionsInOp(op, expr => {
20680
- if (expr instanceof AssignTemporaryExpr) {
20681
- if (!assigned.has(expr.xref)) {
20682
- assigned.add(expr.xref);
20683
- // TODO: Exactly replicate the naming scheme used by `TemplateDefinitionBuilder`.
20684
- // It seems to rely on an expression index instead of an op index.
20685
- defs.set(expr.xref, `tmp_${opCount}_${count++}`);
20686
- }
20687
- assignName(defs, expr);
21117
+ unit.create.prepend(generateTemporaries(unit.create));
21118
+ unit.update.prepend(generateTemporaries(unit.update));
21119
+ }
21120
+ }
21121
+ function generateTemporaries(ops) {
21122
+ let opCount = 0;
21123
+ let generatedStatements = [];
21124
+ // For each op, search for any variables that are assigned or read. For each variable, generate a
21125
+ // name and produce a `DeclareVarStmt` to the beginning of the block.
21126
+ for (const op of ops) {
21127
+ // Identify the final time each temp var is read.
21128
+ const finalReads = new Map();
21129
+ visitExpressionsInOp(op, (expr, flag) => {
21130
+ if (flag & VisitorContextFlag.InChildOperation) {
21131
+ return;
21132
+ }
21133
+ if (expr instanceof ReadTemporaryExpr) {
21134
+ finalReads.set(expr.xref, expr);
21135
+ }
21136
+ });
21137
+ // Name the temp vars, accounting for the fact that a name can be reused after it has been
21138
+ // read for the final time.
21139
+ let count = 0;
21140
+ const assigned = new Set();
21141
+ const released = new Set();
21142
+ const defs = new Map();
21143
+ visitExpressionsInOp(op, (expr, flag) => {
21144
+ if (flag & VisitorContextFlag.InChildOperation) {
21145
+ return;
21146
+ }
21147
+ if (expr instanceof AssignTemporaryExpr) {
21148
+ if (!assigned.has(expr.xref)) {
21149
+ assigned.add(expr.xref);
21150
+ // TODO: Exactly replicate the naming scheme used by `TemplateDefinitionBuilder`.
21151
+ // It seems to rely on an expression index instead of an op index.
21152
+ defs.set(expr.xref, `tmp_${opCount}_${count++}`);
20688
21153
  }
20689
- else if (expr instanceof ReadTemporaryExpr) {
20690
- if (finalReads.get(expr.xref) === expr) {
20691
- released.add(expr.xref);
20692
- count--;
20693
- }
20694
- assignName(defs, expr);
21154
+ assignName(defs, expr);
21155
+ }
21156
+ else if (expr instanceof ReadTemporaryExpr) {
21157
+ if (finalReads.get(expr.xref) === expr) {
21158
+ released.add(expr.xref);
21159
+ count--;
20695
21160
  }
20696
- });
20697
- // Add declarations for the temp vars.
20698
- generatedStatements.push(...Array.from(new Set(defs.values()))
20699
- .map(name => createStatementOp(new DeclareVarStmt(name))));
20700
- opCount++;
21161
+ assignName(defs, expr);
21162
+ }
21163
+ });
21164
+ // Add declarations for the temp vars.
21165
+ generatedStatements.push(...Array.from(new Set(defs.values()))
21166
+ .map(name => createStatementOp(new DeclareVarStmt(name))));
21167
+ opCount++;
21168
+ if (op.kind === OpKind.Listener) {
21169
+ op.handlerOps.prepend(generateTemporaries(op.handlerOps));
20701
21170
  }
20702
- unit.update.prepend(generatedStatements);
20703
21171
  }
21172
+ return generatedStatements;
20704
21173
  }
20705
21174
  /**
20706
21175
  * Assigns a name to the temporary variable in the given temporary variable expression.
@@ -21068,6 +21537,13 @@ function allowConservativeInlining(decl, target) {
21068
21537
  // that behavior here.
21069
21538
  switch (decl.variable.kind) {
21070
21539
  case SemanticVariableKind.Identifier:
21540
+ if (decl.initializer instanceof ReadVarExpr && decl.initializer.name === 'ctx') {
21541
+ // Although TemplateDefinitionBuilder is cautious about inlining, we still want to do so
21542
+ // when the variable is the context, to imitate its behavior with aliases in control flow
21543
+ // blocks. This quirky behavior will become dead code once compatibility mode is no longer
21544
+ // supported.
21545
+ return true;
21546
+ }
21071
21547
  return false;
21072
21548
  case SemanticVariableKind.Context:
21073
21549
  // Context can only be inlined into other variables.
@@ -21077,101 +21553,8 @@ function allowConservativeInlining(decl, target) {
21077
21553
  }
21078
21554
  }
21079
21555
 
21080
- /**
21081
- * Locate projection slots, populate the each component's `ngContentSelectors` literal field,
21082
- * populate `project` arguments, and generate the required `projectionDef` instruction for the job's
21083
- * root view.
21084
- */
21085
- function phaseGenerateProjectionDef(job) {
21086
- // TODO: Why does TemplateDefinitionBuilder force a shared constant?
21087
- const share = job.compatibility === CompatibilityMode.TemplateDefinitionBuilder;
21088
- // Collect all selectors from this component, and its nested views. Also, assign each projection a
21089
- // unique ascending projection slot index.
21090
- const selectors = [];
21091
- let projectionSlotIndex = 0;
21092
- for (const unit of job.units) {
21093
- for (const op of unit.create) {
21094
- if (op.kind === OpKind.Projection) {
21095
- selectors.push(op.selector);
21096
- op.projectionSlotIndex = projectionSlotIndex++;
21097
- }
21098
- }
21099
- }
21100
- if (selectors.length > 0) {
21101
- // Create the projectionDef array. If we only found a single wildcard selector, then we use the
21102
- // default behavior with no arguments instead.
21103
- let defExpr = null;
21104
- if (selectors.length > 1 || selectors[0] !== '*') {
21105
- const def = selectors.map(s => s === '*' ? s : parseSelectorToR3Selector(s));
21106
- defExpr = job.pool.getConstLiteral(literalOrArrayLiteral(def), share);
21107
- }
21108
- // Create the ngContentSelectors constant.
21109
- job.contentSelectors = job.pool.getConstLiteral(literalOrArrayLiteral(selectors), share);
21110
- // The projection def instruction goes at the beginning of the root view, before any
21111
- // `projection` instructions.
21112
- job.root.create.prepend([createProjectionDefOp(defExpr)]);
21113
- }
21114
- }
21115
-
21116
- /**
21117
- * Lifts i18n properties into the consts array.
21118
- */
21119
- function phaseI18nConstCollection(job) {
21120
- // Serialize the extracted messages into the const array.
21121
- // TODO: Use `Map` instead of object.
21122
- const messageConstIndices = {};
21123
- for (const unit of job.units) {
21124
- for (const op of unit.create) {
21125
- if (op.kind === OpKind.ExtractedMessage) {
21126
- messageConstIndices[op.owner] = job.addConst(op.expression, op.statements);
21127
- OpList.remove(op);
21128
- }
21129
- }
21130
- }
21131
- // Assign const index to i18n ops that messages were extracted from.
21132
- for (const unit of job.units) {
21133
- for (const op of unit.create) {
21134
- if (op.kind === OpKind.I18nStart && messageConstIndices[op.xref] !== undefined) {
21135
- op.messageIndex = messageConstIndices[op.xref];
21136
- }
21137
- }
21138
- }
21139
- }
21140
-
21141
- /**
21142
- * Attributes of `ng-content` named 'select' are specifically removed, because they control which
21143
- * content matches as a property of the `projection`, and are not a plain attribute.
21144
- */
21145
- function phaseRemoveContentSelectors(job) {
21146
- for (const unit of job.units) {
21147
- const elements = getElementsByXrefId(unit);
21148
- for (const op of unit.update) {
21149
- switch (op.kind) {
21150
- case OpKind.Binding:
21151
- const target = lookupElement(elements, op.target);
21152
- if (op.name.toLowerCase() === 'select' && target.kind === OpKind.Projection) {
21153
- OpList.remove(op);
21154
- }
21155
- continue;
21156
- }
21157
- }
21158
- }
21159
- }
21160
- /**
21161
- * Looks up an element in the given map by xref ID.
21162
- */
21163
- function lookupElement(elements, xref) {
21164
- const el = elements.get(xref);
21165
- if (el === undefined) {
21166
- throw new Error('All attributes should have an element-like target.');
21167
- }
21168
- return el;
21169
- }
21170
-
21171
21556
  const phases = [
21172
21557
  { kind: CompilationJobKind.Tmpl, fn: phaseRemoveContentSelectors },
21173
- { kind: CompilationJobKind.Tmpl, fn: phaseGenerateI18nBlocks },
21174
- { kind: CompilationJobKind.Tmpl, fn: phaseI18nTextExtraction },
21175
21558
  { kind: CompilationJobKind.Host, fn: phaseHostStylePropertyParsing },
21176
21559
  { kind: CompilationJobKind.Tmpl, fn: phaseNamespace },
21177
21560
  { kind: CompilationJobKind.Both, fn: phaseStyleBindingSpecialization },
@@ -21180,8 +21563,9 @@ const phases = [
21180
21563
  { kind: CompilationJobKind.Both, fn: phaseParseExtractedStyles },
21181
21564
  { kind: CompilationJobKind.Tmpl, fn: phaseRemoveEmptyBindings },
21182
21565
  { kind: CompilationJobKind.Tmpl, fn: phaseConditionals },
21183
- { kind: CompilationJobKind.Tmpl, fn: phaseNoListenersOnTemplates },
21184
21566
  { kind: CompilationJobKind.Tmpl, fn: phasePipeCreation },
21567
+ { kind: CompilationJobKind.Tmpl, fn: phaseI18nTextExtraction },
21568
+ { kind: CompilationJobKind.Tmpl, fn: phaseApplyI18nExpressions },
21185
21569
  { kind: CompilationJobKind.Tmpl, fn: phasePipeVariadic },
21186
21570
  { kind: CompilationJobKind.Both, fn: phasePureLiteralStructures },
21187
21571
  { kind: CompilationJobKind.Tmpl, fn: phaseGenerateProjectionDef },
@@ -21200,6 +21584,7 @@ const phases = [
21200
21584
  { kind: CompilationJobKind.Tmpl, fn: phaseResolveI18nPlaceholders },
21201
21585
  { kind: CompilationJobKind.Tmpl, fn: phaseI18nMessageExtraction },
21202
21586
  { kind: CompilationJobKind.Tmpl, fn: phaseI18nConstCollection },
21587
+ { kind: CompilationJobKind.Tmpl, fn: phaseConstTraitCollection },
21203
21588
  { kind: CompilationJobKind.Both, fn: phaseConstCollection },
21204
21589
  { kind: CompilationJobKind.Both, fn: phaseVarCounting },
21205
21590
  { kind: CompilationJobKind.Tmpl, fn: phaseGenerateAdvance },
@@ -21404,9 +21789,15 @@ function ingestNodes(unit, template) {
21404
21789
  else if (node instanceof BoundText) {
21405
21790
  ingestBoundText(unit, node);
21406
21791
  }
21792
+ else if (node instanceof IfBlock) {
21793
+ ingestIfBlock(unit, node);
21794
+ }
21407
21795
  else if (node instanceof SwitchBlock) {
21408
21796
  ingestSwitchBlock(unit, node);
21409
21797
  }
21798
+ else if (node instanceof DeferredBlock) {
21799
+ ingestDeferBlock(unit, node);
21800
+ }
21410
21801
  else {
21411
21802
  throw new Error(`Unsupported template node: ${node.constructor.name}`);
21412
21803
  }
@@ -21416,23 +21807,37 @@ function ingestNodes(unit, template) {
21416
21807
  * Ingest an element AST from the template into the given `ViewCompilation`.
21417
21808
  */
21418
21809
  function ingestElement(unit, element) {
21810
+ if (element.i18n !== undefined &&
21811
+ !(element.i18n instanceof Message || element.i18n instanceof TagPlaceholder)) {
21812
+ throw Error(`Unhandled i18n metadata type for element: ${element.i18n.constructor.name}`);
21813
+ }
21419
21814
  const staticAttributes = {};
21420
21815
  for (const attr of element.attributes) {
21421
21816
  staticAttributes[attr.name] = attr.value;
21422
21817
  }
21423
21818
  const id = unit.job.allocateXrefId();
21424
21819
  const [namespaceKey, elementName] = splitNsName(element.name);
21425
- const startOp = createElementStartOp(elementName, id, namespaceForKey(namespaceKey), element.i18n, element.startSourceSpan);
21820
+ const startOp = createElementStartOp(elementName, id, namespaceForKey(namespaceKey), element.i18n instanceof TagPlaceholder ? element.i18n : undefined, element.startSourceSpan);
21426
21821
  unit.create.push(startOp);
21427
21822
  ingestBindings(unit, startOp, element);
21428
21823
  ingestReferences(startOp, element);
21429
21824
  ingestNodes(unit, element.children);
21430
- unit.create.push(createElementEndOp(id, element.endSourceSpan));
21825
+ const endOp = createElementEndOp(id, element.endSourceSpan);
21826
+ unit.create.push(endOp);
21827
+ // If there is an i18n message associated with this element, insert i18n start and end ops.
21828
+ if (element.i18n instanceof Message) {
21829
+ const i18nBlockId = unit.job.allocateXrefId();
21830
+ OpList.insertAfter(createI18nStartOp(i18nBlockId, element.i18n), startOp);
21831
+ OpList.insertBefore(createI18nEndOp(i18nBlockId), endOp);
21832
+ }
21431
21833
  }
21432
21834
  /**
21433
21835
  * Ingest an `ng-template` node from the AST into the given `ViewCompilation`.
21434
21836
  */
21435
21837
  function ingestTemplate(unit, tmpl) {
21838
+ if (tmpl.i18n !== undefined && !(tmpl.i18n instanceof Message)) {
21839
+ throw Error(`Unhandled i18n metadata type for template: ${tmpl.i18n.constructor.name}`);
21840
+ }
21436
21841
  const childView = unit.job.allocateView(unit.xref);
21437
21842
  let tagNameWithoutNamespace = tmpl.tagName;
21438
21843
  let namespacePrefix = '';
@@ -21440,7 +21845,7 @@ function ingestTemplate(unit, tmpl) {
21440
21845
  [namespacePrefix, tagNameWithoutNamespace] = splitNsName(tmpl.tagName);
21441
21846
  }
21442
21847
  // TODO: validate the fallback tag name here.
21443
- const tplOp = createTemplateOp(childView.xref, tagNameWithoutNamespace ?? 'ng-template', namespaceForKey(namespacePrefix), false, tmpl.i18n, tmpl.startSourceSpan);
21848
+ const tplOp = createTemplateOp(childView.xref, tagNameWithoutNamespace ?? 'ng-template', namespaceForKey(namespacePrefix), false, undefined, tmpl.startSourceSpan);
21444
21849
  unit.create.push(tplOp);
21445
21850
  ingestBindings(unit, tplOp, tmpl);
21446
21851
  ingestReferences(tplOp, tmpl);
@@ -21448,6 +21853,12 @@ function ingestTemplate(unit, tmpl) {
21448
21853
  for (const { name, value } of tmpl.variables) {
21449
21854
  childView.contextVariables.set(name, value);
21450
21855
  }
21856
+ // If there is an i18n message associated with this template, insert i18n start and end ops.
21857
+ if (tmpl.i18n instanceof Message) {
21858
+ const id = unit.job.allocateXrefId();
21859
+ OpList.insertAfter(createI18nStartOp(id, tmpl.i18n), childView.create.head);
21860
+ OpList.insertBefore(createI18nEndOp(id), childView.create.tail);
21861
+ }
21451
21862
  }
21452
21863
  /**
21453
21864
  * Ingest a literal text node from the AST into the given `ViewCompilation`.
@@ -21455,7 +21866,7 @@ function ingestTemplate(unit, tmpl) {
21455
21866
  function ingestContent(unit, content) {
21456
21867
  const op = createProjectionOp(unit.job.allocateXrefId(), content.selector);
21457
21868
  for (const attr of content.attributes) {
21458
- ingestBinding(unit, op.xref, attr.name, literal(attr.value), 1 /* e.BindingType.Attribute */, null, SecurityContext.NONE, attr.sourceSpan, true, false);
21869
+ ingestBinding(unit, op.xref, attr.name, literal(attr.value), 1 /* e.BindingType.Attribute */, null, SecurityContext.NONE, attr.sourceSpan, BindingFlags.TextValue);
21459
21870
  }
21460
21871
  unit.create.push(op);
21461
21872
  }
@@ -21476,35 +21887,109 @@ function ingestBoundText(unit, text) {
21476
21887
  if (!(value instanceof Interpolation$1)) {
21477
21888
  throw new Error(`AssertionError: expected Interpolation for BoundText node, got ${value.constructor.name}`);
21478
21889
  }
21890
+ if (text.i18n !== undefined && !(text.i18n instanceof Container)) {
21891
+ throw Error(`Unhandled i18n metadata type for text interpolation: ${text.i18n.constructor.name}`);
21892
+ }
21893
+ const i18nPlaceholders = text.i18n instanceof Container ?
21894
+ text.i18n.children.filter((node) => node instanceof Placeholder) :
21895
+ [];
21479
21896
  const textXref = unit.job.allocateXrefId();
21480
21897
  unit.create.push(createTextOp(textXref, '', text.sourceSpan));
21481
21898
  // TemplateDefinitionBuilder does not generate source maps for sub-expressions inside an
21482
21899
  // interpolation. We copy that behavior in compatibility mode.
21483
21900
  // TODO: is it actually correct to generate these extra maps in modern mode?
21484
21901
  const baseSourceSpan = unit.job.compatibility ? null : text.sourceSpan;
21485
- unit.update.push(createInterpolateTextOp(textXref, new Interpolation(value.strings, value.expressions.map(expr => convertAst(expr, unit.job, baseSourceSpan))), text.sourceSpan));
21902
+ unit.update.push(createInterpolateTextOp(textXref, new Interpolation(value.strings, value.expressions.map(expr => convertAst(expr, unit.job, baseSourceSpan))), i18nPlaceholders, text.sourceSpan));
21486
21903
  }
21487
21904
  /**
21488
- * Ingest a `@switch` block into the given `ViewCompilation`.
21905
+ * Ingest an `@if` block into the given `ViewCompilation`.
21906
+ */
21907
+ function ingestIfBlock(unit, ifBlock) {
21908
+ let firstXref = null;
21909
+ let conditions = [];
21910
+ for (const ifCase of ifBlock.branches) {
21911
+ const cView = unit.job.allocateView(unit.xref);
21912
+ if (ifCase.expressionAlias !== null) {
21913
+ cView.contextVariables.set(ifCase.expressionAlias.name, CTX_REF);
21914
+ }
21915
+ if (firstXref === null) {
21916
+ firstXref = cView.xref;
21917
+ }
21918
+ unit.create.push(createTemplateOp(cView.xref, 'Conditional', Namespace.HTML, true, undefined, ifCase.sourceSpan));
21919
+ const caseExpr = ifCase.expression ? convertAst(ifCase.expression, unit.job, null) : null;
21920
+ const conditionalCaseExpr = new ConditionalCaseExpr(caseExpr, cView.xref, ifCase.expressionAlias);
21921
+ conditions.push(conditionalCaseExpr);
21922
+ ingestNodes(cView, ifCase.children);
21923
+ }
21924
+ const conditional = createConditionalOp(firstXref, null, conditions, ifBlock.sourceSpan);
21925
+ unit.update.push(conditional);
21926
+ }
21927
+ /**
21928
+ * Ingest an `@switch` block into the given `ViewCompilation`.
21489
21929
  */
21490
21930
  function ingestSwitchBlock(unit, switchBlock) {
21491
21931
  let firstXref = null;
21492
21932
  let conditions = [];
21493
21933
  for (const switchCase of switchBlock.cases) {
21494
21934
  const cView = unit.job.allocateView(unit.xref);
21495
- if (!firstXref)
21935
+ if (firstXref === null) {
21496
21936
  firstXref = cView.xref;
21497
- unit.create.push(createTemplateOp(cView.xref, 'Case', Namespace.HTML, true, undefined, null));
21937
+ }
21938
+ unit.create.push(createTemplateOp(cView.xref, 'Case', Namespace.HTML, true, undefined, switchCase.sourceSpan));
21498
21939
  const caseExpr = switchCase.expression ?
21499
21940
  convertAst(switchCase.expression, unit.job, switchBlock.startSourceSpan) :
21500
21941
  null;
21501
- conditions.push([cView.xref, caseExpr]);
21942
+ const conditionalCaseExpr = new ConditionalCaseExpr(caseExpr, cView.xref);
21943
+ conditions.push(conditionalCaseExpr);
21502
21944
  ingestNodes(cView, switchCase.children);
21503
21945
  }
21504
- const conditional = createConditionalOp(firstXref, convertAst(switchBlock.expression, unit.job, switchBlock.startSourceSpan), null);
21505
- conditional.conditions = conditions;
21946
+ const conditional = createConditionalOp(firstXref, convertAst(switchBlock.expression, unit.job, null), conditions, switchBlock.sourceSpan);
21506
21947
  unit.update.push(conditional);
21507
21948
  }
21949
+ function ingestDeferView(unit, suffix, children, sourceSpan) {
21950
+ if (children === undefined) {
21951
+ return null;
21952
+ }
21953
+ const secondaryView = unit.job.allocateView(unit.xref);
21954
+ ingestNodes(secondaryView, children);
21955
+ const templateOp = createTemplateOp(secondaryView.xref, `Defer${suffix}`, Namespace.HTML, true, undefined, sourceSpan);
21956
+ unit.create.push(templateOp);
21957
+ return templateOp;
21958
+ }
21959
+ function ingestDeferBlock(unit, deferBlock) {
21960
+ // Generate the defer main view and all secondary views.
21961
+ const main = ingestDeferView(unit, '', deferBlock.children, deferBlock.sourceSpan);
21962
+ const loading = ingestDeferView(unit, 'Loading', deferBlock.loading?.children, deferBlock.loading?.sourceSpan);
21963
+ const placeholder = ingestDeferView(unit, 'Placeholder', deferBlock.placeholder?.children, deferBlock.placeholder?.sourceSpan);
21964
+ const error = ingestDeferView(unit, 'Error', deferBlock.error?.children, deferBlock.error?.sourceSpan);
21965
+ // Create the main defer op, and ops for all secondary views.
21966
+ const deferOp = createDeferOp(unit.job.allocateXrefId(), main.xref, deferBlock.sourceSpan);
21967
+ unit.create.push(deferOp);
21968
+ if (loading && deferBlock.loading) {
21969
+ deferOp.loading =
21970
+ createDeferSecondaryOp(deferOp.xref, loading.xref, DeferSecondaryKind.Loading);
21971
+ if (deferBlock.loading.afterTime !== null || deferBlock.loading.minimumTime !== null) {
21972
+ deferOp.loading.constValue = [deferBlock.loading.minimumTime, deferBlock.loading.afterTime];
21973
+ }
21974
+ unit.create.push(deferOp.loading);
21975
+ }
21976
+ if (placeholder && deferBlock.placeholder) {
21977
+ deferOp.placeholder = createDeferSecondaryOp(deferOp.xref, placeholder.xref, DeferSecondaryKind.Placeholder);
21978
+ if (deferBlock.placeholder.minimumTime !== null) {
21979
+ deferOp.placeholder.constValue = [deferBlock.placeholder.minimumTime];
21980
+ }
21981
+ unit.create.push(deferOp.placeholder);
21982
+ }
21983
+ if (error && deferBlock.error) {
21984
+ deferOp.error =
21985
+ createDeferSecondaryOp(deferOp.xref, error.xref, DeferSecondaryKind.Error);
21986
+ unit.create.push(deferOp.error);
21987
+ }
21988
+ // Configure all defer conditions.
21989
+ const deferOnOp = createDeferOnOp(unit.job.allocateXrefId(), null);
21990
+ // Add all ops to the view.
21991
+ unit.create.push(deferOnOp);
21992
+ }
21508
21993
  /**
21509
21994
  * Convert a template AST expression into an output AST expression.
21510
21995
  */
@@ -21603,13 +22088,20 @@ function convertAst(ast, job, baseSourceSpan) {
21603
22088
  * to their IR representation.
21604
22089
  */
21605
22090
  function ingestBindings(unit, op, element) {
22091
+ let flags = BindingFlags.None;
22092
+ const isPlainTemplate = element instanceof Template && splitNsName(element.tagName ?? '')[1] === 'ng-template';
21606
22093
  if (element instanceof Template) {
22094
+ flags |= BindingFlags.OnNgTemplateElement;
22095
+ if (isPlainTemplate) {
22096
+ flags |= BindingFlags.BindingTargetsTemplate;
22097
+ }
22098
+ const templateAttrFlags = flags | BindingFlags.BindingTargetsTemplate | BindingFlags.IsStructuralTemplateAttribute;
21607
22099
  for (const attr of element.templateAttrs) {
21608
22100
  if (attr instanceof TextAttribute) {
21609
- ingestBinding(unit, op.xref, attr.name, literal(attr.value), 1 /* e.BindingType.Attribute */, null, SecurityContext.NONE, attr.sourceSpan, true, true);
22101
+ ingestBinding(unit, op.xref, attr.name, literal(attr.value), 1 /* e.BindingType.Attribute */, null, SecurityContext.NONE, attr.sourceSpan, templateAttrFlags | BindingFlags.TextValue);
21610
22102
  }
21611
22103
  else {
21612
- ingestBinding(unit, op.xref, attr.name, attr.value, attr.type, attr.unit, attr.securityContext, attr.sourceSpan, false, true);
22104
+ ingestBinding(unit, op.xref, attr.name, attr.value, attr.type, attr.unit, attr.securityContext, attr.sourceSpan, templateAttrFlags);
21613
22105
  }
21614
22106
  }
21615
22107
  }
@@ -21617,10 +22109,10 @@ function ingestBindings(unit, op, element) {
21617
22109
  // This is only attribute TextLiteral bindings, such as `attr.foo="bar"`. This can never be
21618
22110
  // `[attr.foo]="bar"` or `attr.foo="{{bar}}"`, both of which will be handled as inputs with
21619
22111
  // `BindingType.Attribute`.
21620
- ingestBinding(unit, op.xref, attr.name, literal(attr.value), 1 /* e.BindingType.Attribute */, null, SecurityContext.NONE, attr.sourceSpan, true, false);
22112
+ ingestBinding(unit, op.xref, attr.name, literal(attr.value), 1 /* e.BindingType.Attribute */, null, SecurityContext.NONE, attr.sourceSpan, flags | BindingFlags.TextValue);
21621
22113
  }
21622
22114
  for (const input of element.inputs) {
21623
- ingestBinding(unit, op.xref, input.name, input.value, input.type, input.unit, input.securityContext, input.sourceSpan, false, false);
22115
+ ingestBinding(unit, op.xref, input.name, input.value, input.type, input.unit, input.securityContext, input.sourceSpan, flags);
21624
22116
  }
21625
22117
  for (const output of element.outputs) {
21626
22118
  let listenerOp;
@@ -21629,6 +22121,10 @@ function ingestBindings(unit, op, element) {
21629
22121
  throw Error('Animation listener should have a phase');
21630
22122
  }
21631
22123
  }
22124
+ if (element instanceof Template && !isPlainTemplate) {
22125
+ unit.create.push(createExtractedAttributeOp(op.xref, BindingKind.Property, output.name, null));
22126
+ continue;
22127
+ }
21632
22128
  listenerOp =
21633
22129
  createListenerOp(op.xref, output.name, op.tag, output.phase, false, output.sourceSpan);
21634
22130
  // if output.handler is a chain, then push each statement from the chain separately, and
@@ -21664,10 +22160,37 @@ const BINDING_KINDS = new Map([
21664
22160
  [3 /* e.BindingType.Style */, BindingKind.StyleProperty],
21665
22161
  [4 /* e.BindingType.Animation */, BindingKind.Animation],
21666
22162
  ]);
21667
- function ingestBinding(view, xref, name, value, type, unit, securityContext, sourceSpan, isTextAttribute, isTemplateBinding) {
22163
+ var BindingFlags;
22164
+ (function (BindingFlags) {
22165
+ BindingFlags[BindingFlags["None"] = 0] = "None";
22166
+ /**
22167
+ * The binding is to a static text literal and not to an expression.
22168
+ */
22169
+ BindingFlags[BindingFlags["TextValue"] = 1] = "TextValue";
22170
+ /**
22171
+ * The binding belongs to the `<ng-template>` side of a `t.Template`.
22172
+ */
22173
+ BindingFlags[BindingFlags["BindingTargetsTemplate"] = 2] = "BindingTargetsTemplate";
22174
+ /**
22175
+ * The binding is on a structural directive.
22176
+ */
22177
+ BindingFlags[BindingFlags["IsStructuralTemplateAttribute"] = 4] = "IsStructuralTemplateAttribute";
22178
+ /**
22179
+ * The binding is on a `t.Template`.
22180
+ */
22181
+ BindingFlags[BindingFlags["OnNgTemplateElement"] = 8] = "OnNgTemplateElement";
22182
+ })(BindingFlags || (BindingFlags = {}));
22183
+ function ingestBinding(view, xref, name, value, type, unit, securityContext, sourceSpan, flags) {
21668
22184
  if (value instanceof ASTWithSource) {
21669
22185
  value = value.ast;
21670
22186
  }
22187
+ if (flags & BindingFlags.OnNgTemplateElement && !(flags & BindingFlags.BindingTargetsTemplate) &&
22188
+ type === 0 /* e.BindingType.Property */) {
22189
+ // This binding only exists for later const extraction, and is not an actual binding to be
22190
+ // created.
22191
+ view.create.push(createExtractedAttributeOp(xref, BindingKind.Property, name, null));
22192
+ return;
22193
+ }
21671
22194
  let expression;
21672
22195
  // TODO: We could easily generate source maps for subexpressions in these cases, but
21673
22196
  // TemplateDefinitionBuilder does not. Should we do so?
@@ -21681,7 +22204,7 @@ function ingestBinding(view, xref, name, value, type, unit, securityContext, sou
21681
22204
  expression = value;
21682
22205
  }
21683
22206
  const kind = BINDING_KINDS.get(type);
21684
- view.update.push(createBindingOp(xref, kind, name, expression, unit, securityContext, isTextAttribute, isTemplateBinding, sourceSpan));
22207
+ view.update.push(createBindingOp(xref, kind, name, expression, unit, securityContext, !!(flags & BindingFlags.TextValue), !!(flags & BindingFlags.IsStructuralTemplateAttribute), sourceSpan));
21685
22208
  }
21686
22209
  /**
21687
22210
  * Process all of the local references on an element-like structure in the template AST and
@@ -23891,26 +24414,6 @@ class HtmlAstToIvyAst {
23891
24414
  if (this.processedNodes.has(block)) {
23892
24415
  return null;
23893
24416
  }
23894
- if (!this.options.enabledBlockTypes.has(block.name)) {
23895
- let errorMessage;
23896
- if (isConnectedDeferLoopBlock(block.name)) {
23897
- errorMessage = `@${block.name} block can only be used after an @defer block.`;
23898
- this.processedNodes.add(block);
23899
- }
23900
- else if (isConnectedForLoopBlock(block.name)) {
23901
- errorMessage = `@${block.name} block can only be used after an @for block.`;
23902
- this.processedNodes.add(block);
23903
- }
23904
- else if (isConnectedIfLoopBlock(block.name)) {
23905
- errorMessage = `@${block.name} block can only be used after an @if or @else if block.`;
23906
- this.processedNodes.add(block);
23907
- }
23908
- else {
23909
- errorMessage = `Unrecognized block @${block.name}.`;
23910
- }
23911
- this.reportError(errorMessage, block.sourceSpan);
23912
- return null;
23913
- }
23914
24417
  let result = null;
23915
24418
  switch (block.name) {
23916
24419
  case 'defer':
@@ -23926,10 +24429,23 @@ class HtmlAstToIvyAst {
23926
24429
  result = createIfBlock(block, this.findConnectedBlocks(index, context, isConnectedIfLoopBlock), this, this.bindingParser);
23927
24430
  break;
23928
24431
  default:
23929
- result = {
23930
- node: null,
23931
- errors: [new ParseError(block.sourceSpan, `Unrecognized block @${block.name}.`)]
23932
- };
24432
+ let errorMessage;
24433
+ if (isConnectedDeferLoopBlock(block.name)) {
24434
+ errorMessage = `@${block.name} block can only be used after an @defer block.`;
24435
+ this.processedNodes.add(block);
24436
+ }
24437
+ else if (isConnectedForLoopBlock(block.name)) {
24438
+ errorMessage = `@${block.name} block can only be used after an @for block.`;
24439
+ this.processedNodes.add(block);
24440
+ }
24441
+ else if (isConnectedIfLoopBlock(block.name)) {
24442
+ errorMessage = `@${block.name} block can only be used after an @if or @else if block.`;
24443
+ this.processedNodes.add(block);
24444
+ }
24445
+ else {
24446
+ errorMessage = `Unrecognized block @${block.name}.`;
24447
+ }
24448
+ result = { node: null, errors: [new ParseError(block.sourceSpan, errorMessage)] };
23933
24449
  break;
23934
24450
  }
23935
24451
  this.errors.push(...result.errors);
@@ -24170,6 +24686,19 @@ function textContents(node) {
24170
24686
  }
24171
24687
  }
24172
24688
 
24689
+ /*!
24690
+ * @license
24691
+ * Copyright Google LLC All Rights Reserved.
24692
+ *
24693
+ * Use of this source code is governed by an MIT-style license that can be
24694
+ * found in the LICENSE file at https://angular.io/license
24695
+ */
24696
+ /**
24697
+ * Whether the @ block syntax is enabled by default. This constant exists
24698
+ * so that we can temporarily disable the syntax internally.
24699
+ */
24700
+ const BLOCK_SYNTAX_ENABLED_DEFAULT = true;
24701
+
24173
24702
  var TagType;
24174
24703
  (function (TagType) {
24175
24704
  TagType[TagType["ELEMENT"] = 0] = "ELEMENT";
@@ -26377,7 +26906,7 @@ function parseTemplate(template, templateUrl, options = {}) {
26377
26906
  leadingTriviaChars: LEADING_TRIVIA_CHARS,
26378
26907
  ...options,
26379
26908
  tokenizeExpansionForms: true,
26380
- tokenizeBlocks: options.enabledBlockTypes != null && options.enabledBlockTypes.size > 0,
26909
+ tokenizeBlocks: options.enableBlockSyntax ?? BLOCK_SYNTAX_ENABLED_DEFAULT,
26381
26910
  });
26382
26911
  if (!options.alwaysAttemptHtmlToR3AstConversion && parseResult.errors &&
26383
26912
  parseResult.errors.length > 0) {
@@ -26429,10 +26958,7 @@ function parseTemplate(template, templateUrl, options = {}) {
26429
26958
  rootNodes = visitAll(new I18nMetaVisitor(interpolationConfig, /* keepI18nAttrs */ false), rootNodes);
26430
26959
  }
26431
26960
  }
26432
- const { nodes, errors, styleUrls, styles, ngContentSelectors, commentNodes } = htmlAstToRender3Ast(rootNodes, bindingParser, {
26433
- collectCommentNodes: !!options.collectCommentNodes,
26434
- enabledBlockTypes: options.enabledBlockTypes || new Set(),
26435
- });
26961
+ const { nodes, errors, styleUrls, styles, ngContentSelectors, commentNodes } = htmlAstToRender3Ast(rootNodes, bindingParser, { collectCommentNodes: !!options.collectCommentNodes });
26436
26962
  errors.push(...parseResult.errors, ...i18nMetaResult.errors);
26437
26963
  const parsedTemplate = {
26438
26964
  interpolationConfig,
@@ -26707,14 +27233,14 @@ function compileComponentFromMetadata(meta, constantPool, bindingParser) {
26707
27233
  // - either as an array:
26708
27234
  // `consts: [['one', 'two'], ['three', 'four']]`
26709
27235
  // - or as a factory function in case additional statements are present (to support i18n):
26710
- // `consts: function() { var i18n_0; if (ngI18nClosureMode) {...} else {...} return [i18n_0];
27236
+ // `consts: () => { var i18n_0; if (ngI18nClosureMode) {...} else {...} return [i18n_0];
26711
27237
  // }`
26712
27238
  const { constExpressions, prepareStatements } = templateBuilder.getConsts();
26713
27239
  if (constExpressions.length > 0) {
26714
27240
  let constsExpr = literalArr(constExpressions);
26715
27241
  // Prepare statements are present - turn `consts` into a function.
26716
27242
  if (prepareStatements.length > 0) {
26717
- constsExpr = fn([], [...prepareStatements, new ReturnStatement(constsExpr)]);
27243
+ constsExpr = arrowFn([], [...prepareStatements, new ReturnStatement(constsExpr)]);
26718
27244
  }
26719
27245
  definitionMap.set('consts', constsExpr);
26720
27246
  }
@@ -26735,7 +27261,9 @@ function compileComponentFromMetadata(meta, constantPool, bindingParser) {
26735
27261
  definitionMap.set('vars', literal(tpl.root.vars));
26736
27262
  if (tpl.consts.length > 0) {
26737
27263
  if (tpl.constsInitializers.length > 0) {
26738
- definitionMap.set('consts', fn([], [...tpl.constsInitializers, new ReturnStatement(literalArr(tpl.consts))]));
27264
+ definitionMap.set('consts', arrowFn([], [
27265
+ ...tpl.constsInitializers, new ReturnStatement(literalArr(tpl.consts))
27266
+ ]));
26739
27267
  }
26740
27268
  else {
26741
27269
  definitionMap.set('consts', literalArr(tpl.consts));
@@ -26829,11 +27357,11 @@ function compileDeclarationList(list, mode) {
26829
27357
  return list;
26830
27358
  case 1 /* DeclarationListEmitMode.Closure */:
26831
27359
  // directives: function () { return [MyDir]; }
26832
- return fn([], [new ReturnStatement(list)]);
27360
+ return arrowFn([], list);
26833
27361
  case 2 /* DeclarationListEmitMode.ClosureResolved */:
26834
27362
  // directives: function () { return [MyDir].map(ng.resolveForwardRef); }
26835
27363
  const resolvedList = list.prop('map').callFn([importExpr(Identifiers.resolveForwardRef)]);
26836
- return fn([], [new ReturnStatement(resolvedList)]);
27364
+ return arrowFn([], resolvedList);
26837
27365
  case 3 /* DeclarationListEmitMode.RuntimeResolved */:
26838
27366
  throw new Error(`Unsupported with an array of pre-resolved dependencies`);
26839
27367
  }
@@ -28113,11 +28641,6 @@ function extractScopedNodeEntities(rootScope) {
28113
28641
  class ResourceLoader {
28114
28642
  }
28115
28643
 
28116
- let enabledBlockTypes;
28117
- /** Temporary utility that enables specific block types in JIT compilations. */
28118
- function ɵsetEnabledBlockTypes(types) {
28119
- enabledBlockTypes = types.length > 0 ? new Set(types) : undefined;
28120
- }
28121
28644
  class CompilerFacadeImpl {
28122
28645
  constructor(jitEvaluator = new JitEvaluator()) {
28123
28646
  this.jitEvaluator = jitEvaluator;
@@ -28523,7 +29046,7 @@ function convertPipeDeclarationToMetadata(pipe) {
28523
29046
  function parseJitTemplate(template, typeName, sourceMapUrl, preserveWhitespaces, interpolation) {
28524
29047
  const interpolationConfig = interpolation ? InterpolationConfig.fromArray(interpolation) : DEFAULT_INTERPOLATION_CONFIG;
28525
29048
  // Parse the template and check for errors.
28526
- const parsed = parseTemplate(template, sourceMapUrl, { preserveWhitespaces, interpolationConfig, enabledBlockTypes });
29049
+ const parsed = parseTemplate(template, sourceMapUrl, { preserveWhitespaces, interpolationConfig });
28527
29050
  if (parsed.errors !== null) {
28528
29051
  const errors = parsed.errors.map(err => err.toString()).join(', ');
28529
29052
  throw new Error(`Errors during JIT compilation of template for ${typeName}: ${errors}`);
@@ -28736,7 +29259,7 @@ function publishFacade(global) {
28736
29259
  * @description
28737
29260
  * Entry point for all public APIs of the compiler package.
28738
29261
  */
28739
- const VERSION = new Version('17.0.0-next.6');
29262
+ const VERSION = new Version('17.0.0-next.7');
28740
29263
 
28741
29264
  class CompilerConfig {
28742
29265
  constructor({ defaultEncapsulation = ViewEncapsulation.Emulated, useJit = true, missingTranslation = null, preserveWhitespaces, strictInjectionParameters } = {}) {
@@ -30243,7 +30766,7 @@ const MINIMUM_PARTIAL_LINKER_VERSION$6 = '12.0.0';
30243
30766
  function compileDeclareClassMetadata(metadata) {
30244
30767
  const definitionMap = new DefinitionMap();
30245
30768
  definitionMap.set('minVersion', literal(MINIMUM_PARTIAL_LINKER_VERSION$6));
30246
- definitionMap.set('version', literal('17.0.0-next.6'));
30769
+ definitionMap.set('version', literal('17.0.0-next.7'));
30247
30770
  definitionMap.set('ngImport', importExpr(Identifiers.core));
30248
30771
  definitionMap.set('type', metadata.type);
30249
30772
  definitionMap.set('decorators', metadata.decorators);
@@ -30351,7 +30874,7 @@ function createDirectiveDefinitionMap(meta) {
30351
30874
  // in 16.1 is actually used.
30352
30875
  const minVersion = hasTransformFunctions ? MINIMUM_PARTIAL_LINKER_VERSION$5 : '14.0.0';
30353
30876
  definitionMap.set('minVersion', literal(minVersion));
30354
- definitionMap.set('version', literal('17.0.0-next.6'));
30877
+ definitionMap.set('version', literal('17.0.0-next.7'));
30355
30878
  // e.g. `type: MyDirective`
30356
30879
  definitionMap.set('type', meta.type.value);
30357
30880
  if (meta.isStandalone) {
@@ -30477,10 +31000,17 @@ function compileDeclareComponentFromMetadata(meta, template, additionalTemplateI
30477
31000
  */
30478
31001
  function createComponentDefinitionMap(meta, template, templateInfo) {
30479
31002
  const definitionMap = createDirectiveDefinitionMap(meta);
31003
+ const blockVisitor = new BlockPresenceVisitor();
31004
+ visitAll$1(blockVisitor, template.nodes);
30480
31005
  definitionMap.set('template', getTemplateExpression(template, templateInfo));
30481
31006
  if (templateInfo.isInline) {
30482
31007
  definitionMap.set('isInline', literal(true));
30483
31008
  }
31009
+ // Set the minVersion to 17.0.0 if the component is using at least one block in its template.
31010
+ // We don't do this for templates without blocks, in order to preserve backwards compatibility.
31011
+ if (blockVisitor.hasBlocks) {
31012
+ definitionMap.set('minVersion', literal('17.0.0'));
31013
+ }
30484
31014
  definitionMap.set('styles', toOptionalLiteralArray(meta.styles, literal));
30485
31015
  definitionMap.set('dependencies', compileUsedDependenciesMetadata(meta));
30486
31016
  definitionMap.set('viewProviders', meta.viewProviders);
@@ -30573,6 +31103,42 @@ function compileUsedDependenciesMetadata(meta) {
30573
31103
  }
30574
31104
  });
30575
31105
  }
31106
+ class BlockPresenceVisitor extends RecursiveVisitor$1 {
31107
+ constructor() {
31108
+ super(...arguments);
31109
+ this.hasBlocks = false;
31110
+ }
31111
+ visitDeferredBlock() {
31112
+ this.hasBlocks = true;
31113
+ }
31114
+ visitDeferredBlockPlaceholder() {
31115
+ this.hasBlocks = true;
31116
+ }
31117
+ visitDeferredBlockLoading() {
31118
+ this.hasBlocks = true;
31119
+ }
31120
+ visitDeferredBlockError() {
31121
+ this.hasBlocks = true;
31122
+ }
31123
+ visitIfBlock() {
31124
+ this.hasBlocks = true;
31125
+ }
31126
+ visitIfBlockBranch() {
31127
+ this.hasBlocks = true;
31128
+ }
31129
+ visitForLoopBlock() {
31130
+ this.hasBlocks = true;
31131
+ }
31132
+ visitForLoopBlockEmpty() {
31133
+ this.hasBlocks = true;
31134
+ }
31135
+ visitSwitchBlock() {
31136
+ this.hasBlocks = true;
31137
+ }
31138
+ visitSwitchBlockCase() {
31139
+ this.hasBlocks = true;
31140
+ }
31141
+ }
30576
31142
 
30577
31143
  /**
30578
31144
  * Every time we make a breaking change to the declaration interface or partial-linker behavior, we
@@ -30585,7 +31151,7 @@ const MINIMUM_PARTIAL_LINKER_VERSION$4 = '12.0.0';
30585
31151
  function compileDeclareFactoryFunction(meta) {
30586
31152
  const definitionMap = new DefinitionMap();
30587
31153
  definitionMap.set('minVersion', literal(MINIMUM_PARTIAL_LINKER_VERSION$4));
30588
- definitionMap.set('version', literal('17.0.0-next.6'));
31154
+ definitionMap.set('version', literal('17.0.0-next.7'));
30589
31155
  definitionMap.set('ngImport', importExpr(Identifiers.core));
30590
31156
  definitionMap.set('type', meta.type.value);
30591
31157
  definitionMap.set('deps', compileDependencies(meta.deps));
@@ -30620,7 +31186,7 @@ function compileDeclareInjectableFromMetadata(meta) {
30620
31186
  function createInjectableDefinitionMap(meta) {
30621
31187
  const definitionMap = new DefinitionMap();
30622
31188
  definitionMap.set('minVersion', literal(MINIMUM_PARTIAL_LINKER_VERSION$3));
30623
- definitionMap.set('version', literal('17.0.0-next.6'));
31189
+ definitionMap.set('version', literal('17.0.0-next.7'));
30624
31190
  definitionMap.set('ngImport', importExpr(Identifiers.core));
30625
31191
  definitionMap.set('type', meta.type.value);
30626
31192
  // Only generate providedIn property if it has a non-null value
@@ -30671,7 +31237,7 @@ function compileDeclareInjectorFromMetadata(meta) {
30671
31237
  function createInjectorDefinitionMap(meta) {
30672
31238
  const definitionMap = new DefinitionMap();
30673
31239
  definitionMap.set('minVersion', literal(MINIMUM_PARTIAL_LINKER_VERSION$2));
30674
- definitionMap.set('version', literal('17.0.0-next.6'));
31240
+ definitionMap.set('version', literal('17.0.0-next.7'));
30675
31241
  definitionMap.set('ngImport', importExpr(Identifiers.core));
30676
31242
  definitionMap.set('type', meta.type.value);
30677
31243
  definitionMap.set('providers', meta.providers);
@@ -30704,7 +31270,7 @@ function createNgModuleDefinitionMap(meta) {
30704
31270
  throw new Error('Invalid path! Local compilation mode should not get into the partial compilation path');
30705
31271
  }
30706
31272
  definitionMap.set('minVersion', literal(MINIMUM_PARTIAL_LINKER_VERSION$1));
30707
- definitionMap.set('version', literal('17.0.0-next.6'));
31273
+ definitionMap.set('version', literal('17.0.0-next.7'));
30708
31274
  definitionMap.set('ngImport', importExpr(Identifiers.core));
30709
31275
  definitionMap.set('type', meta.type.value);
30710
31276
  // We only generate the keys in the metadata if the arrays contain values.
@@ -30755,7 +31321,7 @@ function compileDeclarePipeFromMetadata(meta) {
30755
31321
  function createPipeDefinitionMap(meta) {
30756
31322
  const definitionMap = new DefinitionMap();
30757
31323
  definitionMap.set('minVersion', literal(MINIMUM_PARTIAL_LINKER_VERSION));
30758
- definitionMap.set('version', literal('17.0.0-next.6'));
31324
+ definitionMap.set('version', literal('17.0.0-next.7'));
30759
31325
  definitionMap.set('ngImport', importExpr(Identifiers.core));
30760
31326
  // e.g. `type: MyPipe`
30761
31327
  definitionMap.set('type', meta.type.value);