@angular/compiler 17.0.0-rc.1 → 17.0.0-rc.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (97) hide show
  1. package/esm2022/src/render3/partial/class_metadata.mjs +1 -1
  2. package/esm2022/src/render3/partial/directive.mjs +1 -1
  3. package/esm2022/src/render3/partial/factory.mjs +1 -1
  4. package/esm2022/src/render3/partial/injectable.mjs +1 -1
  5. package/esm2022/src/render3/partial/injector.mjs +1 -1
  6. package/esm2022/src/render3/partial/ng_module.mjs +1 -1
  7. package/esm2022/src/render3/partial/pipe.mjs +1 -1
  8. package/esm2022/src/render3/r3_ast.mjs +55 -61
  9. package/esm2022/src/render3/r3_control_flow.mjs +17 -13
  10. package/esm2022/src/render3/r3_deferred_blocks.mjs +7 -8
  11. package/esm2022/src/render3/r3_deferred_triggers.mjs +46 -27
  12. package/esm2022/src/render3/view/compiler.mjs +2 -2
  13. package/esm2022/src/render3/view/i18n/util.mjs +1 -1
  14. package/esm2022/src/render3/view/t2_api.mjs +1 -1
  15. package/esm2022/src/render3/view/t2_binder.mjs +22 -10
  16. package/esm2022/src/render3/view/template.mjs +56 -5
  17. package/esm2022/src/template/pipeline/ir/index.mjs +2 -1
  18. package/esm2022/src/template/pipeline/ir/src/enums.mjs +69 -6
  19. package/esm2022/src/template/pipeline/ir/src/expression.mjs +85 -71
  20. package/esm2022/src/template/pipeline/ir/src/handle.mjs +13 -0
  21. package/esm2022/src/template/pipeline/ir/src/ops/create.mjs +68 -50
  22. package/esm2022/src/template/pipeline/ir/src/ops/update.mjs +23 -11
  23. package/esm2022/src/template/pipeline/ir/src/traits.mjs +1 -32
  24. package/esm2022/src/template/pipeline/src/compilation.mjs +3 -2
  25. package/esm2022/src/template/pipeline/src/conversion.mjs +2 -2
  26. package/esm2022/src/template/pipeline/src/emit.mjs +121 -107
  27. package/esm2022/src/template/pipeline/src/ingest.mjs +183 -54
  28. package/esm2022/src/template/pipeline/src/instruction.mjs +55 -20
  29. package/esm2022/src/template/pipeline/src/phases/any_cast.mjs +4 -3
  30. package/esm2022/src/template/pipeline/src/phases/apply_i18n_expressions.mjs +5 -5
  31. package/esm2022/src/template/pipeline/src/phases/assign_i18n_slot_dependencies.mjs +18 -6
  32. package/esm2022/src/template/pipeline/src/phases/attribute_extraction.mjs +2 -2
  33. package/esm2022/src/template/pipeline/src/phases/binding_specialization.mjs +2 -2
  34. package/esm2022/src/template/pipeline/src/phases/chaining.mjs +2 -2
  35. package/esm2022/src/template/pipeline/src/phases/collapse_singleton_interpolations.mjs +2 -2
  36. package/esm2022/src/template/pipeline/src/phases/conditionals.mjs +6 -6
  37. package/esm2022/src/template/pipeline/src/phases/const_collection.mjs +2 -2
  38. package/esm2022/src/template/pipeline/src/phases/create_defer_deps_fns.mjs +43 -0
  39. package/esm2022/src/template/pipeline/src/phases/create_i18n_contexts.mjs +57 -0
  40. package/esm2022/src/template/pipeline/src/phases/create_i18n_icu_expressions.mjs +52 -0
  41. package/esm2022/src/template/pipeline/src/phases/defer_configs.mjs +30 -0
  42. package/esm2022/src/template/pipeline/src/phases/defer_resolve_targets.mjs +113 -0
  43. package/esm2022/src/template/pipeline/src/phases/empty_elements.mjs +4 -4
  44. package/esm2022/src/template/pipeline/src/phases/expand_safe_reads.mjs +7 -4
  45. package/esm2022/src/template/pipeline/src/phases/extract_i18n_messages.mjs +157 -0
  46. package/esm2022/src/template/pipeline/src/phases/generate_advance.mjs +4 -4
  47. package/esm2022/src/template/pipeline/src/phases/generate_projection_def.mjs +2 -2
  48. package/esm2022/src/template/pipeline/src/phases/generate_variables.mjs +4 -3
  49. package/esm2022/src/template/pipeline/src/phases/has_const_expression_collection.mjs +28 -0
  50. package/esm2022/src/template/pipeline/src/phases/host_style_property_parsing.mjs +8 -2
  51. package/esm2022/src/template/pipeline/src/phases/i18n_const_collection.mjs +147 -8
  52. package/esm2022/src/template/pipeline/src/phases/i18n_text_extraction.mjs +16 -14
  53. package/esm2022/src/template/pipeline/src/phases/local_refs.mjs +2 -2
  54. package/esm2022/src/template/pipeline/src/phases/merge_i18n_contexts.mjs +59 -0
  55. package/esm2022/src/template/pipeline/src/phases/namespace.mjs +2 -2
  56. package/esm2022/src/template/pipeline/src/phases/naming.mjs +11 -13
  57. package/esm2022/src/template/pipeline/src/phases/next_context_merging.mjs +2 -2
  58. package/esm2022/src/template/pipeline/src/phases/ng_container.mjs +2 -2
  59. package/esm2022/src/template/pipeline/src/phases/nonbindable.mjs +2 -2
  60. package/esm2022/src/template/pipeline/src/phases/nullish_coalescing.mjs +8 -2
  61. package/esm2022/src/template/pipeline/src/phases/ordering.mjs +7 -2
  62. package/esm2022/src/template/pipeline/src/phases/parse_extracted_styles.mjs +3 -3
  63. package/esm2022/src/template/pipeline/src/phases/phase_remove_content_selectors.mjs +2 -2
  64. package/esm2022/src/template/pipeline/src/phases/pipe_creation.mjs +16 -11
  65. package/esm2022/src/template/pipeline/src/phases/pipe_variadic.mjs +7 -3
  66. package/esm2022/src/template/pipeline/src/phases/propagate_i18n_blocks.mjs +5 -3
  67. package/esm2022/src/template/pipeline/src/phases/pure_function_extraction.mjs +2 -2
  68. package/esm2022/src/template/pipeline/src/phases/pure_literal_structures.mjs +4 -4
  69. package/esm2022/src/template/pipeline/src/phases/reify.mjs +55 -29
  70. package/esm2022/src/template/pipeline/src/phases/remove_empty_bindings.mjs +5 -2
  71. package/esm2022/src/template/pipeline/src/phases/remove_i18n_contexts.mjs +27 -0
  72. package/esm2022/src/template/pipeline/src/phases/repeater_derived_vars.mjs +7 -2
  73. package/esm2022/src/template/pipeline/src/phases/resolve_contexts.mjs +3 -3
  74. package/esm2022/src/template/pipeline/src/phases/resolve_dollar_event.mjs +5 -5
  75. package/esm2022/src/template/pipeline/src/phases/resolve_i18n_element_placeholders.mjs +109 -0
  76. package/esm2022/src/template/pipeline/src/phases/resolve_i18n_expression_placeholders.mjs +48 -0
  77. package/esm2022/src/template/pipeline/src/phases/resolve_names.mjs +3 -3
  78. package/esm2022/src/template/pipeline/src/phases/resolve_sanitizers.mjs +2 -2
  79. package/esm2022/src/template/pipeline/src/phases/save_restore_view.mjs +14 -9
  80. package/esm2022/src/template/pipeline/src/phases/slot_allocation.mjs +4 -29
  81. package/esm2022/src/template/pipeline/src/phases/style_binding_specialization.mjs +3 -3
  82. package/esm2022/src/template/pipeline/src/phases/temporary_variables.mjs +3 -3
  83. package/esm2022/src/template/pipeline/src/phases/track_fn_generation.mjs +2 -2
  84. package/esm2022/src/template/pipeline/src/phases/track_fn_optimization.mjs +8 -2
  85. package/esm2022/src/template/pipeline/src/phases/track_variables.mjs +7 -2
  86. package/esm2022/src/template/pipeline/src/phases/var_counting.mjs +2 -2
  87. package/esm2022/src/template/pipeline/src/phases/variable_optimization.mjs +5 -5
  88. package/esm2022/src/template/pipeline/src/phases/wrap_icus.mjs +2 -2
  89. package/esm2022/src/version.mjs +1 -1
  90. package/fesm2022/compiler.mjs +1806 -1097
  91. package/fesm2022/compiler.mjs.map +1 -1
  92. package/index.d.ts +41 -58
  93. package/package.json +3 -3
  94. package/esm2022/src/template/pipeline/src/phases/has_const_trait_collection.mjs +0 -34
  95. package/esm2022/src/template/pipeline/src/phases/i18n_message_extraction.mjs +0 -122
  96. package/esm2022/src/template/pipeline/src/phases/icu_extraction.mjs +0 -53
  97. package/esm2022/src/template/pipeline/src/phases/resolve_i18n_placeholders.mjs +0 -288
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @license Angular v17.0.0-rc.1
2
+ * @license Angular v17.0.0-rc.3
3
3
  * (c) 2010-2022 Google LLC. https://angular.io/
4
4
  * License: MIT
5
5
  */
@@ -3792,16 +3792,21 @@ class Element$1 {
3792
3792
  }
3793
3793
  }
3794
3794
  class DeferredTrigger {
3795
- constructor(sourceSpan) {
3795
+ constructor(nameSpan, sourceSpan, prefetchSpan, whenOrOnSourceSpan) {
3796
+ this.nameSpan = nameSpan;
3796
3797
  this.sourceSpan = sourceSpan;
3798
+ this.prefetchSpan = prefetchSpan;
3799
+ this.whenOrOnSourceSpan = whenOrOnSourceSpan;
3797
3800
  }
3798
3801
  visit(visitor) {
3799
3802
  return visitor.visitDeferredTrigger(this);
3800
3803
  }
3801
3804
  }
3802
3805
  class BoundDeferredTrigger extends DeferredTrigger {
3803
- constructor(value, sourceSpan) {
3804
- super(sourceSpan);
3806
+ constructor(value, sourceSpan, prefetchSpan, whenSourceSpan) {
3807
+ // BoundDeferredTrigger is for 'when' triggers. These aren't really "triggers" and don't have a
3808
+ // nameSpan. Trigger names are the built in event triggers like hover, interaction, etc.
3809
+ super(/** nameSpan */ null, sourceSpan, prefetchSpan, whenSourceSpan);
3805
3810
  this.value = value;
3806
3811
  }
3807
3812
  }
@@ -3810,75 +3815,75 @@ class IdleDeferredTrigger extends DeferredTrigger {
3810
3815
  class ImmediateDeferredTrigger extends DeferredTrigger {
3811
3816
  }
3812
3817
  class HoverDeferredTrigger extends DeferredTrigger {
3813
- constructor(reference, sourceSpan) {
3814
- super(sourceSpan);
3818
+ constructor(reference, nameSpan, sourceSpan, prefetchSpan, onSourceSpan) {
3819
+ super(nameSpan, sourceSpan, prefetchSpan, onSourceSpan);
3815
3820
  this.reference = reference;
3816
3821
  }
3817
3822
  }
3818
3823
  class TimerDeferredTrigger extends DeferredTrigger {
3819
- constructor(delay, sourceSpan) {
3820
- super(sourceSpan);
3824
+ constructor(delay, nameSpan, sourceSpan, prefetchSpan, onSourceSpan) {
3825
+ super(nameSpan, sourceSpan, prefetchSpan, onSourceSpan);
3821
3826
  this.delay = delay;
3822
3827
  }
3823
3828
  }
3824
3829
  class InteractionDeferredTrigger extends DeferredTrigger {
3825
- constructor(reference, sourceSpan) {
3826
- super(sourceSpan);
3830
+ constructor(reference, nameSpan, sourceSpan, prefetchSpan, onSourceSpan) {
3831
+ super(nameSpan, sourceSpan, prefetchSpan, onSourceSpan);
3827
3832
  this.reference = reference;
3828
3833
  }
3829
3834
  }
3830
3835
  class ViewportDeferredTrigger extends DeferredTrigger {
3831
- constructor(reference, sourceSpan) {
3832
- super(sourceSpan);
3836
+ constructor(reference, nameSpan, sourceSpan, prefetchSpan, onSourceSpan) {
3837
+ super(nameSpan, sourceSpan, prefetchSpan, onSourceSpan);
3833
3838
  this.reference = reference;
3834
3839
  }
3835
3840
  }
3836
- class DeferredBlockPlaceholder {
3837
- constructor(children, minimumTime, sourceSpan, startSourceSpan, endSourceSpan) {
3838
- this.children = children;
3839
- this.minimumTime = minimumTime;
3841
+ class BlockNode {
3842
+ constructor(nameSpan, sourceSpan, startSourceSpan, endSourceSpan) {
3843
+ this.nameSpan = nameSpan;
3840
3844
  this.sourceSpan = sourceSpan;
3841
3845
  this.startSourceSpan = startSourceSpan;
3842
3846
  this.endSourceSpan = endSourceSpan;
3843
3847
  }
3848
+ }
3849
+ class DeferredBlockPlaceholder extends BlockNode {
3850
+ constructor(children, minimumTime, nameSpan, sourceSpan, startSourceSpan, endSourceSpan) {
3851
+ super(nameSpan, sourceSpan, startSourceSpan, endSourceSpan);
3852
+ this.children = children;
3853
+ this.minimumTime = minimumTime;
3854
+ }
3844
3855
  visit(visitor) {
3845
3856
  return visitor.visitDeferredBlockPlaceholder(this);
3846
3857
  }
3847
3858
  }
3848
- class DeferredBlockLoading {
3849
- constructor(children, afterTime, minimumTime, sourceSpan, startSourceSpan, endSourceSpan) {
3859
+ class DeferredBlockLoading extends BlockNode {
3860
+ constructor(children, afterTime, minimumTime, nameSpan, sourceSpan, startSourceSpan, endSourceSpan) {
3861
+ super(nameSpan, sourceSpan, startSourceSpan, endSourceSpan);
3850
3862
  this.children = children;
3851
3863
  this.afterTime = afterTime;
3852
3864
  this.minimumTime = minimumTime;
3853
- this.sourceSpan = sourceSpan;
3854
- this.startSourceSpan = startSourceSpan;
3855
- this.endSourceSpan = endSourceSpan;
3856
3865
  }
3857
3866
  visit(visitor) {
3858
3867
  return visitor.visitDeferredBlockLoading(this);
3859
3868
  }
3860
3869
  }
3861
- class DeferredBlockError {
3862
- constructor(children, sourceSpan, startSourceSpan, endSourceSpan) {
3870
+ class DeferredBlockError extends BlockNode {
3871
+ constructor(children, nameSpan, sourceSpan, startSourceSpan, endSourceSpan) {
3872
+ super(nameSpan, sourceSpan, startSourceSpan, endSourceSpan);
3863
3873
  this.children = children;
3864
- this.sourceSpan = sourceSpan;
3865
- this.startSourceSpan = startSourceSpan;
3866
- this.endSourceSpan = endSourceSpan;
3867
3874
  }
3868
3875
  visit(visitor) {
3869
3876
  return visitor.visitDeferredBlockError(this);
3870
3877
  }
3871
3878
  }
3872
- class DeferredBlock {
3873
- constructor(children, triggers, prefetchTriggers, placeholder, loading, error, sourceSpan, mainBlockSpan, startSourceSpan, endSourceSpan) {
3879
+ class DeferredBlock extends BlockNode {
3880
+ constructor(children, triggers, prefetchTriggers, placeholder, loading, error, nameSpan, sourceSpan, mainBlockSpan, startSourceSpan, endSourceSpan) {
3881
+ super(nameSpan, sourceSpan, startSourceSpan, endSourceSpan);
3874
3882
  this.children = children;
3875
3883
  this.placeholder = placeholder;
3876
3884
  this.loading = loading;
3877
3885
  this.error = error;
3878
- this.sourceSpan = sourceSpan;
3879
3886
  this.mainBlockSpan = mainBlockSpan;
3880
- this.startSourceSpan = startSourceSpan;
3881
- this.endSourceSpan = endSourceSpan;
3882
3887
  this.triggers = triggers;
3883
3888
  this.prefetchTriggers = prefetchTriggers;
3884
3889
  // We cache the keys since we know that they won't change and we
@@ -3900,83 +3905,72 @@ class DeferredBlock {
3900
3905
  visitAll$1(visitor, keys.map(k => triggers[k]));
3901
3906
  }
3902
3907
  }
3903
- class SwitchBlock {
3908
+ class SwitchBlock extends BlockNode {
3904
3909
  constructor(expression, cases,
3905
3910
  /**
3906
3911
  * These blocks are only captured to allow for autocompletion in the language service. They
3907
3912
  * aren't meant to be processed in any other way.
3908
3913
  */
3909
- unknownBlocks, sourceSpan, startSourceSpan, endSourceSpan) {
3914
+ unknownBlocks, sourceSpan, startSourceSpan, endSourceSpan, nameSpan) {
3915
+ super(nameSpan, sourceSpan, startSourceSpan, endSourceSpan);
3910
3916
  this.expression = expression;
3911
3917
  this.cases = cases;
3912
3918
  this.unknownBlocks = unknownBlocks;
3913
- this.sourceSpan = sourceSpan;
3914
- this.startSourceSpan = startSourceSpan;
3915
- this.endSourceSpan = endSourceSpan;
3916
3919
  }
3917
3920
  visit(visitor) {
3918
3921
  return visitor.visitSwitchBlock(this);
3919
3922
  }
3920
3923
  }
3921
- class SwitchBlockCase {
3922
- constructor(expression, children, sourceSpan, startSourceSpan, endSourceSpan) {
3924
+ class SwitchBlockCase extends BlockNode {
3925
+ constructor(expression, children, sourceSpan, startSourceSpan, endSourceSpan, nameSpan) {
3926
+ super(nameSpan, sourceSpan, startSourceSpan, endSourceSpan);
3923
3927
  this.expression = expression;
3924
3928
  this.children = children;
3925
- this.sourceSpan = sourceSpan;
3926
- this.startSourceSpan = startSourceSpan;
3927
- this.endSourceSpan = endSourceSpan;
3928
3929
  }
3929
3930
  visit(visitor) {
3930
3931
  return visitor.visitSwitchBlockCase(this);
3931
3932
  }
3932
3933
  }
3933
- class ForLoopBlock {
3934
- constructor(item, expression, trackBy, contextVariables, children, empty, sourceSpan, mainBlockSpan, startSourceSpan, endSourceSpan) {
3934
+ class ForLoopBlock extends BlockNode {
3935
+ constructor(item, expression, trackBy, trackKeywordSpan, contextVariables, children, empty, sourceSpan, mainBlockSpan, startSourceSpan, endSourceSpan, nameSpan) {
3936
+ super(nameSpan, sourceSpan, startSourceSpan, endSourceSpan);
3935
3937
  this.item = item;
3936
3938
  this.expression = expression;
3937
3939
  this.trackBy = trackBy;
3940
+ this.trackKeywordSpan = trackKeywordSpan;
3938
3941
  this.contextVariables = contextVariables;
3939
3942
  this.children = children;
3940
3943
  this.empty = empty;
3941
- this.sourceSpan = sourceSpan;
3942
3944
  this.mainBlockSpan = mainBlockSpan;
3943
- this.startSourceSpan = startSourceSpan;
3944
- this.endSourceSpan = endSourceSpan;
3945
3945
  }
3946
3946
  visit(visitor) {
3947
3947
  return visitor.visitForLoopBlock(this);
3948
3948
  }
3949
3949
  }
3950
- class ForLoopBlockEmpty {
3951
- constructor(children, sourceSpan, startSourceSpan, endSourceSpan) {
3950
+ class ForLoopBlockEmpty extends BlockNode {
3951
+ constructor(children, sourceSpan, startSourceSpan, endSourceSpan, nameSpan) {
3952
+ super(nameSpan, sourceSpan, startSourceSpan, endSourceSpan);
3952
3953
  this.children = children;
3953
- this.sourceSpan = sourceSpan;
3954
- this.startSourceSpan = startSourceSpan;
3955
- this.endSourceSpan = endSourceSpan;
3956
3954
  }
3957
3955
  visit(visitor) {
3958
3956
  return visitor.visitForLoopBlockEmpty(this);
3959
3957
  }
3960
3958
  }
3961
- class IfBlock {
3962
- constructor(branches, sourceSpan, startSourceSpan, endSourceSpan) {
3959
+ class IfBlock extends BlockNode {
3960
+ constructor(branches, sourceSpan, startSourceSpan, endSourceSpan, nameSpan) {
3961
+ super(nameSpan, sourceSpan, startSourceSpan, endSourceSpan);
3963
3962
  this.branches = branches;
3964
- this.sourceSpan = sourceSpan;
3965
- this.startSourceSpan = startSourceSpan;
3966
- this.endSourceSpan = endSourceSpan;
3967
3963
  }
3968
3964
  visit(visitor) {
3969
3965
  return visitor.visitIfBlock(this);
3970
3966
  }
3971
3967
  }
3972
- class IfBlockBranch {
3973
- constructor(expression, children, expressionAlias, sourceSpan, startSourceSpan, endSourceSpan) {
3968
+ class IfBlockBranch extends BlockNode {
3969
+ constructor(expression, children, expressionAlias, sourceSpan, startSourceSpan, endSourceSpan, nameSpan) {
3970
+ super(nameSpan, sourceSpan, startSourceSpan, endSourceSpan);
3974
3971
  this.expression = expression;
3975
3972
  this.children = children;
3976
3973
  this.expressionAlias = expressionAlias;
3977
- this.sourceSpan = sourceSpan;
3978
- this.startSourceSpan = startSourceSpan;
3979
- this.endSourceSpan = endSourceSpan;
3980
3974
  }
3981
3975
  visit(visitor) {
3982
3976
  return visitor.visitIfBlockBranch(this);
@@ -8800,17 +8794,17 @@ var OpKind;
8800
8794
  */
8801
8795
  OpKind[OpKind["Defer"] = 26] = "Defer";
8802
8796
  /**
8803
- * An IR operation that provides secondary templates of a `@defer` block.
8797
+ * An operation that controls when a `@defer` loads.
8804
8798
  */
8805
- OpKind[OpKind["DeferSecondaryBlock"] = 27] = "DeferSecondaryBlock";
8799
+ OpKind[OpKind["DeferOn"] = 27] = "DeferOn";
8806
8800
  /**
8807
- * An operation that controls when a `@defer` loads.
8801
+ * An operation that controls when a `@defer` loads, using a custom expression as the condition.
8808
8802
  */
8809
- OpKind[OpKind["DeferOn"] = 28] = "DeferOn";
8803
+ OpKind[OpKind["DeferWhen"] = 28] = "DeferWhen";
8810
8804
  /**
8811
8805
  * An i18n message that has been extracted for inclusion in the consts array.
8812
8806
  */
8813
- OpKind[OpKind["ExtractedMessage"] = 29] = "ExtractedMessage";
8807
+ OpKind[OpKind["I18nMessage"] = 29] = "I18nMessage";
8814
8808
  /**
8815
8809
  * A host binding property.
8816
8810
  */
@@ -8863,6 +8857,10 @@ var OpKind;
8863
8857
  * An instruction to update an ICU expression.
8864
8858
  */
8865
8859
  OpKind[OpKind["IcuUpdate"] = 42] = "IcuUpdate";
8860
+ /**
8861
+ * An i18n context containing information needed to generate an i18n message.
8862
+ */
8863
+ OpKind[OpKind["I18nContext"] = 43] = "I18nContext";
8866
8864
  })(OpKind || (OpKind = {}));
8867
8865
  /**
8868
8866
  * Distinguishes different kinds of IR expressions.
@@ -8966,6 +8964,10 @@ var ExpressionKind;
8966
8964
  * properties ($even, $first, etc.).
8967
8965
  */
8968
8966
  ExpressionKind[ExpressionKind["DerivedRepeaterVar"] = 23] = "DerivedRepeaterVar";
8967
+ /**
8968
+ * An expression that will be automatically extracted to the component const array.
8969
+ */
8970
+ ExpressionKind[ExpressionKind["ConstCollected"] = 24] = "ConstCollected";
8969
8971
  })(ExpressionKind || (ExpressionKind = {}));
8970
8972
  var VariableFlags;
8971
8973
  (function (VariableFlags) {
@@ -9080,6 +9082,61 @@ var I18nParamResolutionTime;
9080
9082
  */
9081
9083
  I18nParamResolutionTime[I18nParamResolutionTime["Postproccessing"] = 1] = "Postproccessing";
9082
9084
  })(I18nParamResolutionTime || (I18nParamResolutionTime = {}));
9085
+ /**
9086
+ * Flags that describe what an i18n param value. These determine how the value is serialized into
9087
+ * the final map.
9088
+ */
9089
+ var I18nParamValueFlags;
9090
+ (function (I18nParamValueFlags) {
9091
+ I18nParamValueFlags[I18nParamValueFlags["None"] = 0] = "None";
9092
+ /**
9093
+ * This value represtents an element tag.
9094
+ */
9095
+ I18nParamValueFlags[I18nParamValueFlags["ElementTag"] = 1] = "ElementTag";
9096
+ /**
9097
+ * This value represents a template tag.
9098
+ */
9099
+ I18nParamValueFlags[I18nParamValueFlags["TemplateTag"] = 2] = "TemplateTag";
9100
+ /**
9101
+ * This value represents the opening of a tag.
9102
+ */
9103
+ I18nParamValueFlags[I18nParamValueFlags["OpenTag"] = 4] = "OpenTag";
9104
+ /**
9105
+ * This value represents the closing of a tag.
9106
+ */
9107
+ I18nParamValueFlags[I18nParamValueFlags["CloseTag"] = 8] = "CloseTag";
9108
+ })(I18nParamValueFlags || (I18nParamValueFlags = {}));
9109
+ /**
9110
+ * Whether the active namespace is HTML, MathML, or SVG mode.
9111
+ */
9112
+ var Namespace;
9113
+ (function (Namespace) {
9114
+ Namespace[Namespace["HTML"] = 0] = "HTML";
9115
+ Namespace[Namespace["SVG"] = 1] = "SVG";
9116
+ Namespace[Namespace["Math"] = 2] = "Math";
9117
+ })(Namespace || (Namespace = {}));
9118
+ /**
9119
+ * The type of a `@defer` trigger, for use in the ir.
9120
+ */
9121
+ var DeferTriggerKind;
9122
+ (function (DeferTriggerKind) {
9123
+ DeferTriggerKind[DeferTriggerKind["Idle"] = 0] = "Idle";
9124
+ DeferTriggerKind[DeferTriggerKind["Immediate"] = 1] = "Immediate";
9125
+ DeferTriggerKind[DeferTriggerKind["Timer"] = 2] = "Timer";
9126
+ DeferTriggerKind[DeferTriggerKind["Hover"] = 3] = "Hover";
9127
+ DeferTriggerKind[DeferTriggerKind["Interaction"] = 4] = "Interaction";
9128
+ DeferTriggerKind[DeferTriggerKind["Viewport"] = 5] = "Viewport";
9129
+ })(DeferTriggerKind || (DeferTriggerKind = {}));
9130
+ /**
9131
+ * Repeaters implicitly define these derived variables, and child nodes may read them.
9132
+ */
9133
+ var DerivedRepeaterVarIdentity;
9134
+ (function (DerivedRepeaterVarIdentity) {
9135
+ DerivedRepeaterVarIdentity[DerivedRepeaterVarIdentity["First"] = 0] = "First";
9136
+ DerivedRepeaterVarIdentity[DerivedRepeaterVarIdentity["Last"] = 1] = "Last";
9137
+ DerivedRepeaterVarIdentity[DerivedRepeaterVarIdentity["Even"] = 2] = "Even";
9138
+ DerivedRepeaterVarIdentity[DerivedRepeaterVarIdentity["Odd"] = 3] = "Odd";
9139
+ })(DerivedRepeaterVarIdentity || (DerivedRepeaterVarIdentity = {}));
9083
9140
 
9084
9141
  /**
9085
9142
  * Marker symbol for `ConsumesSlotOpTrait`.
@@ -9089,10 +9146,6 @@ const ConsumesSlot = Symbol('ConsumesSlot');
9089
9146
  * Marker symbol for `DependsOnSlotContextOpTrait`.
9090
9147
  */
9091
9148
  const DependsOnSlotContext = Symbol('DependsOnSlotContext');
9092
- /**
9093
- * Marker symbol for `UsesSlotIndex` trait.
9094
- */
9095
- const UsesSlotIndex = Symbol('UsesSlotIndex');
9096
9149
  /**
9097
9150
  * Marker symbol for `ConsumesVars` trait.
9098
9151
  */
@@ -9101,27 +9154,14 @@ const ConsumesVarsTrait = Symbol('ConsumesVars');
9101
9154
  * Marker symbol for `UsesVarOffset` trait.
9102
9155
  */
9103
9156
  const UsesVarOffset = Symbol('UsesVarOffset');
9104
- /**
9105
- * Marker symbol for `HasConst` trait.
9106
- */
9107
- const HasConst = Symbol('HasConst');
9108
9157
  /**
9109
9158
  * Default values for most `ConsumesSlotOpTrait` fields (used with the spread operator to initialize
9110
9159
  * implementors of the trait).
9111
9160
  */
9112
9161
  const TRAIT_CONSUMES_SLOT = {
9113
9162
  [ConsumesSlot]: true,
9114
- slot: null,
9115
9163
  numSlotsUsed: 1,
9116
9164
  };
9117
- /**
9118
- * Default values for most `UsesSlotIndexTrait` fields (used with the spread operator to initialize
9119
- * implementors of the trait).
9120
- */
9121
- const TRAIT_USES_SLOT_INDEX = {
9122
- [UsesSlotIndex]: true,
9123
- targetSlot: null,
9124
- };
9125
9165
  /**
9126
9166
  * Default values for most `DependsOnSlotContextOpTrait` fields (used with the spread operator to
9127
9167
  * initialize implementors of the trait).
@@ -9144,14 +9184,6 @@ const TRAIT_USES_VAR_OFFSET = {
9144
9184
  [UsesVarOffset]: true,
9145
9185
  varOffset: null,
9146
9186
  };
9147
- /**
9148
- * Default values for `HasConst` fields (used with the spread operator to initialize
9149
- * implementors of this trait).
9150
- */
9151
- const TRAIT_HAS_CONST = {
9152
- [HasConst]: true,
9153
- constIndex: null,
9154
- };
9155
9187
  /**
9156
9188
  * Test whether an operation implements `ConsumesSlotOpTrait`.
9157
9189
  */
@@ -9173,12 +9205,6 @@ function hasConsumesVarsTrait(value) {
9173
9205
  function hasUsesVarOffsetTrait(expr) {
9174
9206
  return expr[UsesVarOffset] === true;
9175
9207
  }
9176
- function hasUsesSlotIndexTrait(value) {
9177
- return value[UsesSlotIndex] === true;
9178
- }
9179
- function hasConstTrait(value) {
9180
- return value[HasConst] === true;
9181
- }
9182
9208
 
9183
9209
  /**
9184
9210
  * Create a `StatementOp`.
@@ -9361,39 +9387,51 @@ function createAdvanceOp(delta, sourceSpan) {
9361
9387
  /**
9362
9388
  * Create a conditional op, which will display an embedded view according to a condtion.
9363
9389
  */
9364
- function createConditionalOp(target, test, conditions, sourceSpan) {
9390
+ function createConditionalOp(target, targetSlot, test, conditions, sourceSpan) {
9365
9391
  return {
9366
9392
  kind: OpKind.Conditional,
9367
9393
  target,
9394
+ targetSlot,
9368
9395
  test,
9369
9396
  conditions,
9370
9397
  processed: null,
9371
9398
  sourceSpan,
9372
9399
  contextValue: null,
9373
9400
  ...NEW_OP,
9374
- ...TRAIT_USES_SLOT_INDEX,
9375
9401
  ...TRAIT_DEPENDS_ON_SLOT_CONTEXT,
9376
9402
  ...TRAIT_CONSUMES_VARS,
9377
9403
  };
9378
9404
  }
9379
- function createRepeaterOp(repeaterCreate, collection, sourceSpan) {
9405
+ function createRepeaterOp(repeaterCreate, targetSlot, collection, sourceSpan) {
9380
9406
  return {
9381
9407
  kind: OpKind.Repeater,
9382
9408
  target: repeaterCreate,
9409
+ targetSlot,
9383
9410
  collection,
9384
9411
  sourceSpan,
9385
9412
  ...NEW_OP,
9386
- ...TRAIT_USES_SLOT_INDEX,
9413
+ };
9414
+ }
9415
+ function createDeferWhenOp(target, expr, prefetch, sourceSpan) {
9416
+ return {
9417
+ kind: OpKind.DeferWhen,
9418
+ target,
9419
+ expr,
9420
+ prefetch,
9421
+ sourceSpan,
9422
+ ...NEW_OP,
9423
+ ...TRAIT_DEPENDS_ON_SLOT_CONTEXT,
9387
9424
  };
9388
9425
  }
9389
9426
  /**
9390
9427
  * Create an i18n expression op.
9391
9428
  */
9392
- function createI18nExpressionOp(owner, expression, i18nPlaceholder, resolutionTime, sourceSpan) {
9429
+ function createI18nExpressionOp(context, target, handle, expression, i18nPlaceholder, resolutionTime, sourceSpan) {
9393
9430
  return {
9394
9431
  kind: OpKind.I18nExpression,
9395
- owner,
9396
- target: owner,
9432
+ context,
9433
+ target,
9434
+ handle,
9397
9435
  expression,
9398
9436
  i18nPlaceholder,
9399
9437
  resolutionTime,
@@ -9406,13 +9444,13 @@ function createI18nExpressionOp(owner, expression, i18nPlaceholder, resolutionTi
9406
9444
  /**
9407
9445
  *Creates an op to apply i18n expression ops
9408
9446
  */
9409
- function createI18nApplyOp(target, sourceSpan) {
9447
+ function createI18nApplyOp(target, handle, sourceSpan) {
9410
9448
  return {
9411
9449
  kind: OpKind.I18nApply,
9412
9450
  target,
9451
+ handle,
9413
9452
  sourceSpan,
9414
9453
  ...NEW_OP,
9415
- ...TRAIT_USES_SLOT_INDEX,
9416
9454
  };
9417
9455
  }
9418
9456
  /**
@@ -9427,7 +9465,7 @@ function createIcuUpdateOp(xref, sourceSpan) {
9427
9465
  };
9428
9466
  }
9429
9467
 
9430
- var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k;
9468
+ var _a, _b, _c, _d, _e, _f;
9431
9469
  /**
9432
9470
  * Check whether a given `o.Expression` is a logical IR expression type.
9433
9471
  */
@@ -9470,14 +9508,12 @@ class LexicalReadExpr extends ExpressionBase {
9470
9508
  * Runtime operation to retrieve the value of a local reference.
9471
9509
  */
9472
9510
  class ReferenceExpr extends ExpressionBase {
9473
- static { _a = UsesSlotIndex; }
9474
- constructor(target, offset) {
9511
+ constructor(target, targetSlot, offset) {
9475
9512
  super();
9476
9513
  this.target = target;
9514
+ this.targetSlot = targetSlot;
9477
9515
  this.offset = offset;
9478
9516
  this.kind = ExpressionKind.Reference;
9479
- this[_a] = true;
9480
- this.targetSlot = null;
9481
9517
  }
9482
9518
  visitExpression() { }
9483
9519
  isEquivalent(e) {
@@ -9488,9 +9524,7 @@ class ReferenceExpr extends ExpressionBase {
9488
9524
  }
9489
9525
  transformInternalExpressions() { }
9490
9526
  clone() {
9491
- const expr = new ReferenceExpr(this.target, this.offset);
9492
- expr.targetSlot = this.targetSlot;
9493
- return expr;
9527
+ return new ReferenceExpr(this.target, this.targetSlot, this.offset);
9494
9528
  }
9495
9529
  }
9496
9530
  /**
@@ -9668,12 +9702,12 @@ class ReadVariableExpr extends ExpressionBase {
9668
9702
  }
9669
9703
  }
9670
9704
  class PureFunctionExpr extends ExpressionBase {
9671
- static { _b = ConsumesVarsTrait, _c = UsesVarOffset; }
9705
+ static { _a = ConsumesVarsTrait, _b = UsesVarOffset; }
9672
9706
  constructor(expression, args) {
9673
9707
  super();
9674
9708
  this.kind = ExpressionKind.PureFunctionExpr;
9709
+ this[_a] = true;
9675
9710
  this[_b] = true;
9676
- this[_c] = true;
9677
9711
  this.varOffset = null;
9678
9712
  /**
9679
9713
  * Once extracted to the `ConstantPool`, a reference to the function which defines the computation
@@ -9737,17 +9771,16 @@ class PureFunctionParameterExpr extends ExpressionBase {
9737
9771
  }
9738
9772
  }
9739
9773
  class PipeBindingExpr extends ExpressionBase {
9740
- static { _d = UsesSlotIndex, _e = ConsumesVarsTrait, _f = UsesVarOffset; }
9741
- constructor(target, name, args) {
9774
+ static { _c = ConsumesVarsTrait, _d = UsesVarOffset; }
9775
+ constructor(target, targetSlot, name, args) {
9742
9776
  super();
9743
9777
  this.target = target;
9778
+ this.targetSlot = targetSlot;
9744
9779
  this.name = name;
9745
9780
  this.args = args;
9746
9781
  this.kind = ExpressionKind.PipeBinding;
9782
+ this[_c] = true;
9747
9783
  this[_d] = true;
9748
- this[_e] = true;
9749
- this[_f] = true;
9750
- this.targetSlot = null;
9751
9784
  this.varOffset = null;
9752
9785
  }
9753
9786
  visitExpression(visitor, context) {
@@ -9767,25 +9800,23 @@ class PipeBindingExpr extends ExpressionBase {
9767
9800
  }
9768
9801
  }
9769
9802
  clone() {
9770
- const r = new PipeBindingExpr(this.target, this.name, this.args.map(a => a.clone()));
9771
- r.targetSlot = this.targetSlot;
9803
+ const r = new PipeBindingExpr(this.target, this.targetSlot, this.name, this.args.map(a => a.clone()));
9772
9804
  r.varOffset = this.varOffset;
9773
9805
  return r;
9774
9806
  }
9775
9807
  }
9776
9808
  class PipeBindingVariadicExpr extends ExpressionBase {
9777
- static { _g = UsesSlotIndex, _h = ConsumesVarsTrait, _j = UsesVarOffset; }
9778
- constructor(target, name, args, numArgs) {
9809
+ static { _e = ConsumesVarsTrait, _f = UsesVarOffset; }
9810
+ constructor(target, targetSlot, name, args, numArgs) {
9779
9811
  super();
9780
9812
  this.target = target;
9813
+ this.targetSlot = targetSlot;
9781
9814
  this.name = name;
9782
9815
  this.args = args;
9783
9816
  this.numArgs = numArgs;
9784
9817
  this.kind = ExpressionKind.PipeBindingVariadic;
9785
- this[_g] = true;
9786
- this[_h] = true;
9787
- this[_j] = true;
9788
- this.targetSlot = null;
9818
+ this[_e] = true;
9819
+ this[_f] = true;
9789
9820
  this.varOffset = null;
9790
9821
  }
9791
9822
  visitExpression(visitor, context) {
@@ -9801,8 +9832,7 @@ class PipeBindingVariadicExpr extends ExpressionBase {
9801
9832
  this.args = transformExpressionsInExpression(this.args, transform, flags);
9802
9833
  }
9803
9834
  clone() {
9804
- const r = new PipeBindingVariadicExpr(this.target, this.name, this.args.clone(), this.numArgs);
9805
- r.targetSlot = this.targetSlot;
9835
+ const r = new PipeBindingVariadicExpr(this.target, this.targetSlot, this.name, this.args.clone(), this.numArgs);
9806
9836
  r.varOffset = this.varOffset;
9807
9837
  return r;
9808
9838
  }
@@ -9996,26 +10026,20 @@ class SanitizerExpr extends ExpressionBase {
9996
10026
  transformInternalExpressions() { }
9997
10027
  }
9998
10028
  class SlotLiteralExpr extends ExpressionBase {
9999
- static { _k = UsesSlotIndex; }
10000
- constructor(target) {
10029
+ constructor(slot) {
10001
10030
  super();
10002
- this.target = target;
10031
+ this.slot = slot;
10003
10032
  this.kind = ExpressionKind.SlotLiteralExpr;
10004
- this[_k] = true;
10005
- this.targetSlot = null;
10006
10033
  }
10007
10034
  visitExpression(visitor, context) { }
10008
10035
  isEquivalent(e) {
10009
- return e instanceof SlotLiteralExpr && e.target === this.target &&
10010
- e.targetSlot === this.targetSlot;
10036
+ return e instanceof SlotLiteralExpr && e.slot === this.slot;
10011
10037
  }
10012
10038
  isConstant() {
10013
10039
  return true;
10014
10040
  }
10015
10041
  clone() {
10016
- const copy = new SlotLiteralExpr(this.target);
10017
- copy.targetSlot = this.targetSlot;
10018
- return copy;
10042
+ return new SlotLiteralExpr(this.slot);
10019
10043
  }
10020
10044
  transformInternalExpressions() { }
10021
10045
  }
@@ -10025,10 +10049,11 @@ class ConditionalCaseExpr extends ExpressionBase {
10025
10049
  * @param expr The expression to be tested for this case. Might be null, as in an `else` case.
10026
10050
  * @param target The Xref of the view to be displayed if this condition is true.
10027
10051
  */
10028
- constructor(expr, target, alias = null) {
10052
+ constructor(expr, target, targetSlot, alias = null) {
10029
10053
  super();
10030
10054
  this.expr = expr;
10031
10055
  this.target = target;
10056
+ this.targetSlot = targetSlot;
10032
10057
  this.alias = alias;
10033
10058
  this.kind = ExpressionKind.ConditionalCase;
10034
10059
  }
@@ -10044,7 +10069,7 @@ class ConditionalCaseExpr extends ExpressionBase {
10044
10069
  return true;
10045
10070
  }
10046
10071
  clone() {
10047
- return new ConditionalCaseExpr(this.expr, this.target);
10072
+ return new ConditionalCaseExpr(this.expr, this.target, this.targetSlot);
10048
10073
  }
10049
10074
  transformInternalExpressions(transform, flags) {
10050
10075
  if (this.expr !== null) {
@@ -10052,13 +10077,6 @@ class ConditionalCaseExpr extends ExpressionBase {
10052
10077
  }
10053
10078
  }
10054
10079
  }
10055
- var DerivedRepeaterVarIdentity;
10056
- (function (DerivedRepeaterVarIdentity) {
10057
- DerivedRepeaterVarIdentity[DerivedRepeaterVarIdentity["First"] = 0] = "First";
10058
- DerivedRepeaterVarIdentity[DerivedRepeaterVarIdentity["Last"] = 1] = "Last";
10059
- DerivedRepeaterVarIdentity[DerivedRepeaterVarIdentity["Even"] = 2] = "Even";
10060
- DerivedRepeaterVarIdentity[DerivedRepeaterVarIdentity["Odd"] = 3] = "Odd";
10061
- })(DerivedRepeaterVarIdentity || (DerivedRepeaterVarIdentity = {}));
10062
10080
  class DerivedRepeaterVarExpr extends ExpressionBase {
10063
10081
  constructor(xref, identity) {
10064
10082
  super();
@@ -10079,6 +10097,31 @@ class DerivedRepeaterVarExpr extends ExpressionBase {
10079
10097
  return new DerivedRepeaterVarExpr(this.xref, this.identity);
10080
10098
  }
10081
10099
  }
10100
+ class ConstCollectedExpr extends ExpressionBase {
10101
+ constructor(expr) {
10102
+ super();
10103
+ this.expr = expr;
10104
+ this.kind = ExpressionKind.ConstCollected;
10105
+ }
10106
+ transformInternalExpressions(transform, flags) {
10107
+ this.expr = transform(this.expr, flags);
10108
+ }
10109
+ visitExpression(visitor, context) {
10110
+ this.expr.visitExpression(visitor, context);
10111
+ }
10112
+ isEquivalent(e) {
10113
+ if (!(e instanceof ConstCollectedExpr)) {
10114
+ return false;
10115
+ }
10116
+ return this.expr.isEquivalent(e.expr);
10117
+ }
10118
+ isConstant() {
10119
+ return this.expr.isConstant();
10120
+ }
10121
+ clone() {
10122
+ return new ConstCollectedExpr(this.expr);
10123
+ }
10124
+ }
10082
10125
  /**
10083
10126
  * Visits all `Expression`s in the AST of `op` with the `visitor` function.
10084
10127
  */
@@ -10167,12 +10210,6 @@ function transformExpressionsInOp(op, transform, flags) {
10167
10210
  op.expression =
10168
10211
  op.expression && transformExpressionsInExpression(op.expression, transform, flags);
10169
10212
  break;
10170
- case OpKind.ExtractedMessage:
10171
- op.expression = transformExpressionsInExpression(op.expression, transform, flags);
10172
- for (const statement of op.statements) {
10173
- transformExpressionsInStatement(statement, transform, flags);
10174
- }
10175
- break;
10176
10213
  case OpKind.RepeaterCreate:
10177
10214
  op.track = transformExpressionsInExpression(op.track, transform, flags);
10178
10215
  if (op.trackByFn !== null) {
@@ -10182,34 +10219,49 @@ function transformExpressionsInOp(op, transform, flags) {
10182
10219
  case OpKind.Repeater:
10183
10220
  op.collection = transformExpressionsInExpression(op.collection, transform, flags);
10184
10221
  break;
10185
- case OpKind.I18n:
10186
- case OpKind.I18nStart:
10187
- for (const [placeholder, expression] of op.params) {
10188
- op.params.set(placeholder, transformExpressionsInExpression(expression, transform, flags));
10222
+ case OpKind.Defer:
10223
+ if (op.loadingConfig !== null) {
10224
+ op.loadingConfig = transformExpressionsInExpression(op.loadingConfig, transform, flags);
10225
+ }
10226
+ if (op.placeholderConfig !== null) {
10227
+ op.placeholderConfig =
10228
+ transformExpressionsInExpression(op.placeholderConfig, transform, flags);
10189
10229
  }
10190
10230
  break;
10191
- case OpKind.Defer:
10192
- case OpKind.DeferSecondaryBlock:
10193
- case OpKind.DeferOn:
10194
- case OpKind.Projection:
10195
- case OpKind.ProjectionDef:
10196
- case OpKind.Element:
10197
- case OpKind.ElementStart:
10198
- case OpKind.ElementEnd:
10199
- case OpKind.I18nEnd:
10231
+ case OpKind.I18nMessage:
10232
+ for (const [placeholder, expr] of op.params) {
10233
+ op.params.set(placeholder, transformExpressionsInExpression(expr, transform, flags));
10234
+ }
10235
+ for (const [placeholder, expr] of op.postprocessingParams) {
10236
+ op.postprocessingParams.set(placeholder, transformExpressionsInExpression(expr, transform, flags));
10237
+ }
10238
+ break;
10239
+ case OpKind.DeferWhen:
10240
+ op.expr = transformExpressionsInExpression(op.expr, transform, flags);
10241
+ break;
10242
+ case OpKind.Advance:
10200
10243
  case OpKind.Container:
10201
- case OpKind.ContainerStart:
10202
10244
  case OpKind.ContainerEnd:
10203
- case OpKind.Template:
10245
+ case OpKind.ContainerStart:
10246
+ case OpKind.DeferOn:
10204
10247
  case OpKind.DisableBindings:
10248
+ case OpKind.Element:
10249
+ case OpKind.ElementEnd:
10250
+ case OpKind.ElementStart:
10205
10251
  case OpKind.EnableBindings:
10206
- case OpKind.Text:
10207
- case OpKind.Pipe:
10208
- case OpKind.Advance:
10209
- case OpKind.Namespace:
10252
+ case OpKind.I18n:
10210
10253
  case OpKind.I18nApply:
10254
+ case OpKind.I18nContext:
10255
+ case OpKind.I18nEnd:
10256
+ case OpKind.I18nStart:
10211
10257
  case OpKind.Icu:
10212
10258
  case OpKind.IcuUpdate:
10259
+ case OpKind.Namespace:
10260
+ case OpKind.Pipe:
10261
+ case OpKind.Projection:
10262
+ case OpKind.ProjectionDef:
10263
+ case OpKind.Template:
10264
+ case OpKind.Text:
10213
10265
  // These operations contain no expressions.
10214
10266
  break;
10215
10267
  default:
@@ -10581,6 +10633,12 @@ class OpList {
10581
10633
  }
10582
10634
  }
10583
10635
 
10636
+ class SlotHandle {
10637
+ constructor() {
10638
+ this.slot = null;
10639
+ }
10640
+ }
10641
+
10584
10642
  /**
10585
10643
  * The set of OpKinds that represent the creation of an element or container
10586
10644
  */
@@ -10602,6 +10660,7 @@ function createElementStartOp(tag, xref, namespace, i18nPlaceholder, sourceSpan)
10602
10660
  kind: OpKind.ElementStart,
10603
10661
  xref,
10604
10662
  tag,
10663
+ handle: new SlotHandle(),
10605
10664
  attributes: null,
10606
10665
  localRefs: [],
10607
10666
  nonBindable: false,
@@ -10615,13 +10674,14 @@ function createElementStartOp(tag, xref, namespace, i18nPlaceholder, sourceSpan)
10615
10674
  /**
10616
10675
  * Create a `TemplateOp`.
10617
10676
  */
10618
- function createTemplateOp(xref, tag, namespace, generatedInBlock, i18nPlaceholder, sourceSpan) {
10677
+ function createTemplateOp(xref, tag, functionNameSuffix, namespace, i18nPlaceholder, sourceSpan) {
10619
10678
  return {
10620
10679
  kind: OpKind.Template,
10621
10680
  xref,
10622
10681
  attributes: null,
10623
10682
  tag,
10624
- block: generatedInBlock,
10683
+ handle: new SlotHandle(),
10684
+ functionNameSuffix,
10625
10685
  decls: null,
10626
10686
  vars: null,
10627
10687
  localRefs: [],
@@ -10633,15 +10693,17 @@ function createTemplateOp(xref, tag, namespace, generatedInBlock, i18nPlaceholde
10633
10693
  ...NEW_OP,
10634
10694
  };
10635
10695
  }
10636
- function createRepeaterCreateOp(primaryView, emptyView, track, varNames, sourceSpan) {
10696
+ function createRepeaterCreateOp(primaryView, emptyView, tag, track, varNames, sourceSpan) {
10637
10697
  return {
10638
10698
  kind: OpKind.RepeaterCreate,
10639
10699
  attributes: null,
10640
10700
  xref: primaryView,
10701
+ handle: new SlotHandle(),
10641
10702
  emptyView,
10642
10703
  track,
10643
10704
  trackByFn: null,
10644
- tag: 'For',
10705
+ tag,
10706
+ functionNameSuffix: 'For',
10645
10707
  namespace: Namespace.HTML,
10646
10708
  nonBindable: false,
10647
10709
  localRefs: [],
@@ -10687,6 +10749,7 @@ function createTextOp(xref, initialValue, sourceSpan) {
10687
10749
  return {
10688
10750
  kind: OpKind.Text,
10689
10751
  xref,
10752
+ handle: new SlotHandle(),
10690
10753
  initialValue,
10691
10754
  sourceSpan,
10692
10755
  ...TRAIT_CONSUMES_SLOT,
@@ -10696,10 +10759,11 @@ function createTextOp(xref, initialValue, sourceSpan) {
10696
10759
  /**
10697
10760
  * Create a `ListenerOp`. Host bindings reuse all the listener logic.
10698
10761
  */
10699
- function createListenerOp(target, name, tag, animationPhase, hostListener, sourceSpan) {
10762
+ function createListenerOp(target, targetSlot, name, tag, animationPhase, hostListener, sourceSpan) {
10700
10763
  return {
10701
10764
  kind: OpKind.Listener,
10702
10765
  target,
10766
+ targetSlot,
10703
10767
  tag,
10704
10768
  hostListener,
10705
10769
  name,
@@ -10710,27 +10774,18 @@ function createListenerOp(target, name, tag, animationPhase, hostListener, sourc
10710
10774
  animationPhase: animationPhase,
10711
10775
  sourceSpan,
10712
10776
  ...NEW_OP,
10713
- ...TRAIT_USES_SLOT_INDEX,
10714
10777
  };
10715
10778
  }
10716
- function createPipeOp(xref, name) {
10779
+ function createPipeOp(xref, slot, name) {
10717
10780
  return {
10718
10781
  kind: OpKind.Pipe,
10719
10782
  xref,
10783
+ handle: slot,
10720
10784
  name,
10721
10785
  ...NEW_OP,
10722
10786
  ...TRAIT_CONSUMES_SLOT,
10723
10787
  };
10724
10788
  }
10725
- /**
10726
- * Whether the active namespace is HTML, MathML, or SVG mode.
10727
- */
10728
- var Namespace;
10729
- (function (Namespace) {
10730
- Namespace[Namespace["HTML"] = 0] = "HTML";
10731
- Namespace[Namespace["SVG"] = 1] = "SVG";
10732
- Namespace[Namespace["Math"] = 2] = "Math";
10733
- })(Namespace || (Namespace = {}));
10734
10789
  function createNamespaceOp(namespace) {
10735
10790
  return {
10736
10791
  kind: OpKind.Namespace,
@@ -10749,6 +10804,7 @@ function createProjectionOp(xref, selector, sourceSpan) {
10749
10804
  return {
10750
10805
  kind: OpKind.Projection,
10751
10806
  xref,
10807
+ handle: new SlotHandle(),
10752
10808
  selector,
10753
10809
  projectionSlotIndex: 0,
10754
10810
  attributes: [],
@@ -10771,51 +10827,56 @@ function createExtractedAttributeOp(target, bindingKind, name, expression) {
10771
10827
  ...NEW_OP,
10772
10828
  };
10773
10829
  }
10774
- function createDeferOp(xref, main, sourceSpan) {
10830
+ function createDeferOp(xref, main, mainSlot, metadata, sourceSpan) {
10775
10831
  return {
10776
10832
  kind: OpKind.Defer,
10777
10833
  xref,
10778
- target: main,
10779
- loading: null,
10780
- placeholder: null,
10781
- error: null,
10834
+ handle: new SlotHandle(),
10835
+ mainView: main,
10836
+ mainSlot,
10837
+ loadingView: null,
10838
+ loadingSlot: null,
10839
+ loadingConfig: null,
10840
+ loadingMinimumTime: null,
10841
+ loadingAfterTime: null,
10842
+ placeholderView: null,
10843
+ placeholderSlot: null,
10844
+ placeholderConfig: null,
10845
+ placeholderMinimumTime: null,
10846
+ errorView: null,
10847
+ errorSlot: null,
10848
+ metadata,
10849
+ resolverFn: null,
10782
10850
  sourceSpan,
10783
10851
  ...NEW_OP,
10784
10852
  ...TRAIT_CONSUMES_SLOT,
10785
- ...TRAIT_USES_SLOT_INDEX,
10786
- };
10787
- }
10788
- function createDeferSecondaryOp(deferOp, secondaryView, secondaryBlockKind) {
10789
- return {
10790
- kind: OpKind.DeferSecondaryBlock,
10791
- deferOp,
10792
- target: secondaryView,
10793
- secondaryBlockKind,
10794
- constValue: null,
10795
- makeExpression: literalOrArrayLiteral$1,
10796
- ...NEW_OP,
10797
- ...TRAIT_USES_SLOT_INDEX,
10798
- ...TRAIT_HAS_CONST,
10853
+ numSlotsUsed: 2,
10799
10854
  };
10800
10855
  }
10801
- function createDeferOnOp(xref, sourceSpan) {
10856
+ function createDeferOnOp(defer, trigger, prefetch, sourceSpan) {
10802
10857
  return {
10803
10858
  kind: OpKind.DeferOn,
10804
- xref,
10859
+ defer,
10860
+ trigger,
10861
+ prefetch,
10805
10862
  sourceSpan,
10806
10863
  ...NEW_OP,
10807
- ...TRAIT_CONSUMES_SLOT,
10808
10864
  };
10809
10865
  }
10810
10866
  /**
10811
10867
  * Create an `ExtractedMessageOp`.
10812
10868
  */
10813
- function createExtractedMessageOp(owner, expression, statements) {
10869
+ function createI18nMessageOp(xref, i18nBlock, message, messagePlaceholder, params, postprocessingParams, needsPostprocessing) {
10814
10870
  return {
10815
- kind: OpKind.ExtractedMessage,
10816
- owner,
10817
- expression,
10818
- statements,
10871
+ kind: OpKind.I18nMessage,
10872
+ xref,
10873
+ i18nBlock,
10874
+ message,
10875
+ messagePlaceholder,
10876
+ params,
10877
+ postprocessingParams,
10878
+ needsPostprocessing,
10879
+ subMessages: [],
10819
10880
  ...NEW_OP,
10820
10881
  };
10821
10882
  }
@@ -10826,13 +10887,12 @@ function createI18nStartOp(xref, message, root) {
10826
10887
  return {
10827
10888
  kind: OpKind.I18nStart,
10828
10889
  xref,
10890
+ handle: new SlotHandle(),
10829
10891
  root: root ?? xref,
10830
10892
  message,
10831
- params: new Map(),
10832
- postprocessingParams: new Map(),
10833
10893
  messageIndex: null,
10834
10894
  subTemplateIndex: null,
10835
- needsPostprocessing: false,
10895
+ context: null,
10836
10896
  ...NEW_OP,
10837
10897
  ...TRAIT_CONSUMES_SLOT,
10838
10898
  };
@@ -10850,12 +10910,27 @@ function createI18nEndOp(xref) {
10850
10910
  /**
10851
10911
  * Creates an op to create an ICU expression.
10852
10912
  */
10853
- function createIcuOp(xref, message, sourceSpan) {
10913
+ function createIcuOp(xref, message, icu, messagePlaceholder, sourceSpan) {
10854
10914
  return {
10855
10915
  kind: OpKind.Icu,
10856
10916
  xref,
10857
10917
  message,
10918
+ icu,
10919
+ messagePlaceholder,
10920
+ context: null,
10921
+ sourceSpan,
10922
+ ...NEW_OP,
10923
+ };
10924
+ }
10925
+ function createI18nContextOp(xref, i18nBlock, message, sourceSpan) {
10926
+ return {
10927
+ kind: OpKind.I18nContext,
10928
+ xref,
10929
+ i18nBlock,
10930
+ message,
10858
10931
  sourceSpan,
10932
+ params: new Map(),
10933
+ postprocessingParams: new Map(),
10859
10934
  ...NEW_OP,
10860
10935
  };
10861
10936
  }
@@ -10917,10 +10992,11 @@ class CompilationJob {
10917
10992
  * embedded views or host bindings.
10918
10993
  */
10919
10994
  class ComponentCompilationJob extends CompilationJob {
10920
- constructor(componentName, pool, compatibility, relativeContextFilePath, i18nUseExternalIds) {
10995
+ constructor(componentName, pool, compatibility, relativeContextFilePath, i18nUseExternalIds, deferBlocksMeta) {
10921
10996
  super(componentName, pool, compatibility);
10922
10997
  this.relativeContextFilePath = relativeContextFilePath;
10923
10998
  this.i18nUseExternalIds = i18nUseExternalIds;
10999
+ this.deferBlocksMeta = deferBlocksMeta;
10924
11000
  this.kind = CompilationJobKind.Tmpl;
10925
11001
  this.fnSuffix = 'Template';
10926
11002
  this.views = new Map();
@@ -11069,9 +11145,10 @@ class HostBindingCompilationUnit extends CompilationUnit {
11069
11145
  }
11070
11146
 
11071
11147
  /**
11072
- * Find any function calls to `$any`, excluding `this.$any`, and delete them.
11148
+ * Find any function calls to `$any`, excluding `this.$any`, and delete them, since they have no
11149
+ * runtime effects.
11073
11150
  */
11074
- function phaseFindAnyCasts(job) {
11151
+ function deleteAnyCasts(job) {
11075
11152
  for (const unit of job.units) {
11076
11153
  for (const op of unit.ops()) {
11077
11154
  transformExpressionsInOp(op, removeAnys, VisitorContextFlag.None);
@@ -11092,13 +11169,13 @@ function removeAnys(e) {
11092
11169
  /**
11093
11170
  * Adds apply operations after i18n expressions.
11094
11171
  */
11095
- function phaseApplyI18nExpressions(job) {
11172
+ function applyI18nExpressions(job) {
11096
11173
  for (const unit of job.units) {
11097
11174
  for (const op of unit.update) {
11098
11175
  // Only add apply after expressions that are not followed by more expressions.
11099
11176
  if (op.kind === OpKind.I18nExpression && needsApplication(op)) {
11100
11177
  // TODO: what should be the source span for the apply op?
11101
- OpList.insertAfter(createI18nApplyOp(op.owner, null), op);
11178
+ OpList.insertAfter(createI18nApplyOp(op.target, op.handle, null), op);
11102
11179
  }
11103
11180
  }
11104
11181
  }
@@ -11111,8 +11188,8 @@ function needsApplication(op) {
11111
11188
  if (op.next?.kind !== OpKind.I18nExpression) {
11112
11189
  return true;
11113
11190
  }
11114
- // If the next op is an expression targeting a different i18n block, we need to apply.
11115
- if (op.next.owner !== op.owner) {
11191
+ // If the next op is an expression targeting a different i18n context, we need to apply.
11192
+ if (op.next.context !== op.context) {
11116
11193
  return true;
11117
11194
  }
11118
11195
  return false;
@@ -11121,46 +11198,35 @@ function needsApplication(op) {
11121
11198
  /**
11122
11199
  * Updates i18n expression ops to depend on the last slot in their owning i18n block.
11123
11200
  */
11124
- function phaseAssignI18nSlotDependencies(job) {
11201
+ function assignI18nSlotDependencies(job) {
11125
11202
  const i18nLastSlotConsumers = new Map();
11203
+ const i18nContexts = new Map();
11126
11204
  let lastSlotConsumer = null;
11205
+ let currentI18nOp = null;
11127
11206
  for (const unit of job.units) {
11128
11207
  // Record the last consumed slot before each i18n end instruction.
11129
11208
  for (const op of unit.create) {
11130
- if (op.kind === OpKind.I18nEnd) {
11131
- i18nLastSlotConsumers.set(op.xref, lastSlotConsumer);
11132
- }
11133
11209
  if (hasConsumesSlotTrait(op)) {
11134
11210
  lastSlotConsumer = op.xref;
11135
11211
  }
11212
+ switch (op.kind) {
11213
+ case OpKind.I18nStart:
11214
+ currentI18nOp = op;
11215
+ break;
11216
+ case OpKind.I18nEnd:
11217
+ i18nLastSlotConsumers.set(currentI18nOp.xref, lastSlotConsumer);
11218
+ currentI18nOp = null;
11219
+ break;
11220
+ case OpKind.I18nContext:
11221
+ i18nContexts.set(op.xref, op);
11222
+ break;
11223
+ }
11136
11224
  }
11137
11225
  // Assign i18n expressions to target the last slot in its owning block.
11138
11226
  for (const op of unit.update) {
11139
11227
  if (op.kind === OpKind.I18nExpression) {
11140
- op.target = i18nLastSlotConsumers.get(op.owner);
11141
- }
11142
- }
11143
- }
11144
- }
11145
-
11146
- /**
11147
- * Attribute interpolations of the form `[attr.foo]="{{foo}}""` should be "collapsed" into a plain
11148
- * attribute instruction, instead of an `attributeInterpolate` instruction.
11149
- *
11150
- * (We cannot do this for singleton property interpolations, because `propertyInterpolate`
11151
- * stringifies its expression.)
11152
- *
11153
- * The reification step is also capable of performing this transformation, but doing it early in the
11154
- * pipeline allows other phases to accurately know what instruction will be emitted.
11155
- */
11156
- function phaseCollapseSingletonInterpolations(job) {
11157
- for (const unit of job.units) {
11158
- for (const op of unit.update) {
11159
- const eligibleOpKind = op.kind === OpKind.Attribute;
11160
- if (eligibleOpKind && op.expression instanceof Interpolation &&
11161
- op.expression.strings.length === 2 &&
11162
- op.expression.strings.every((s) => s === '')) {
11163
- op.expression = op.expression.expressions[0];
11228
+ const i18nContext = i18nContexts.get(op.context);
11229
+ op.target = i18nLastSlotConsumers.get(i18nContext.i18nBlock);
11164
11230
  }
11165
11231
  }
11166
11232
  }
@@ -11184,7 +11250,7 @@ function createOpXrefMap(unit) {
11184
11250
  * Find all extractable attribute and binding ops, and create ExtractedAttributeOps for them.
11185
11251
  * In cases where no instruction needs to be generated for the attribute or binding, it is removed.
11186
11252
  */
11187
- function phaseAttributeExtraction(job) {
11253
+ function extractAttributes(job) {
11188
11254
  for (const unit of job.units) {
11189
11255
  const elements = createOpXrefMap(unit);
11190
11256
  for (const op of unit.ops()) {
@@ -11282,7 +11348,7 @@ function lookupElement$1(elements, xref) {
11282
11348
  }
11283
11349
  return el;
11284
11350
  }
11285
- function phaseBindingSpecialization(job) {
11351
+ function specializeBindings(job) {
11286
11352
  const elements = new Map();
11287
11353
  for (const unit of job.units) {
11288
11354
  for (const op of unit.create) {
@@ -11371,7 +11437,7 @@ const CHAINABLE = new Set([
11371
11437
  * elementStart(0, 'div')(1, 'span');
11372
11438
  * ```
11373
11439
  */
11374
- function phaseChaining(job) {
11440
+ function chain(job) {
11375
11441
  for (const unit of job.units) {
11376
11442
  chainOperationsInList(unit.create);
11377
11443
  chainOperationsInList(unit.update);
@@ -11418,9 +11484,32 @@ function chainOperationsInList(opList) {
11418
11484
  }
11419
11485
 
11420
11486
  /**
11421
- * Collapse the various conditions of conditional ops into a single test expression.
11422
- */
11423
- function phaseConditionals(job) {
11487
+ * Attribute interpolations of the form `[attr.foo]="{{foo}}""` should be "collapsed" into a plain
11488
+ * attribute instruction, instead of an `attributeInterpolate` instruction.
11489
+ *
11490
+ * (We cannot do this for singleton property interpolations, because `propertyInterpolate`
11491
+ * stringifies its expression.)
11492
+ *
11493
+ * The reification step is also capable of performing this transformation, but doing it early in the
11494
+ * pipeline allows other phases to accurately know what instruction will be emitted.
11495
+ */
11496
+ function collapseSingletonInterpolations(job) {
11497
+ for (const unit of job.units) {
11498
+ for (const op of unit.update) {
11499
+ const eligibleOpKind = op.kind === OpKind.Attribute;
11500
+ if (eligibleOpKind && op.expression instanceof Interpolation &&
11501
+ op.expression.strings.length === 2 &&
11502
+ op.expression.strings.every((s) => s === '')) {
11503
+ op.expression = op.expression.expressions[0];
11504
+ }
11505
+ }
11506
+ }
11507
+ }
11508
+
11509
+ /**
11510
+ * Collapse the various conditions of conditional ops (if, switch) into a single test expression.
11511
+ */
11512
+ function generateConditionalExpressions(job) {
11424
11513
  for (const unit of job.units) {
11425
11514
  for (const op of unit.ops()) {
11426
11515
  if (op.kind !== OpKind.Conditional) {
@@ -11430,8 +11519,8 @@ function phaseConditionals(job) {
11430
11519
  // Any case with a `null` condition is `default`. If one exists, default to it instead.
11431
11520
  const defaultCase = op.conditions.findIndex((cond) => cond.expr === null);
11432
11521
  if (defaultCase >= 0) {
11433
- const xref = op.conditions.splice(defaultCase, 1)[0].target;
11434
- test = new SlotLiteralExpr(xref);
11522
+ const slot = op.conditions.splice(defaultCase, 1)[0].targetSlot;
11523
+ test = new SlotLiteralExpr(slot);
11435
11524
  }
11436
11525
  else {
11437
11526
  // By default, a switch evaluates to `-1`, causing no template to be displayed.
@@ -11457,7 +11546,7 @@ function phaseConditionals(job) {
11457
11546
  new AssignTemporaryExpr(conditionalCase.expr, caseExpressionTemporaryXref);
11458
11547
  op.contextValue = new ReadTemporaryExpr(caseExpressionTemporaryXref);
11459
11548
  }
11460
- test = new ConditionalExpr(conditionalCase.expr, new SlotLiteralExpr(conditionalCase.target), test);
11549
+ test = new ConditionalExpr(conditionalCase.expr, new SlotLiteralExpr(conditionalCase.targetSlot), test);
11461
11550
  }
11462
11551
  // Save the resulting aggregate Joost-expression.
11463
11552
  op.processed = test;
@@ -11548,14 +11637,14 @@ function literalOrArrayLiteral(value) {
11548
11637
  if (Array.isArray(value)) {
11549
11638
  return literalArr(value.map(literalOrArrayLiteral));
11550
11639
  }
11551
- return literal(value, INFERRED_TYPE);
11640
+ return literal(value);
11552
11641
  }
11553
11642
 
11554
11643
  /**
11555
11644
  * Converts the semantic attributes of element-like operations (elements, templates) into constant
11556
11645
  * array expressions, and lifts them into the overall component `consts`.
11557
11646
  */
11558
- function phaseConstCollection(job) {
11647
+ function collectElementConsts(job) {
11559
11648
  // Collect all extracted attributes.
11560
11649
  const allElementAttributes = new Map();
11561
11650
  for (const unit of job.units) {
@@ -11701,6 +11790,259 @@ function serializeAttributes({ attributes, bindings, classes, i18n, projectAs, s
11701
11790
  return literalArr(attrArray);
11702
11791
  }
11703
11792
 
11793
+ /**
11794
+ * Create extracted deps functions for defer ops.
11795
+ */
11796
+ function createDeferDepsFns(job) {
11797
+ for (const unit of job.units) {
11798
+ for (const op of unit.create) {
11799
+ if (op.kind === OpKind.Defer) {
11800
+ if (op.metadata.deps.length === 0) {
11801
+ continue;
11802
+ }
11803
+ const dependencies = [];
11804
+ for (const dep of op.metadata.deps) {
11805
+ if (dep.isDeferrable) {
11806
+ // Callback function, e.g. `m () => m.MyCmp;`.
11807
+ const innerFn = arrowFn([new FnParam('m', DYNAMIC_TYPE)], variable('m').prop(dep.symbolName));
11808
+ // Dynamic import, e.g. `import('./a').then(...)`.
11809
+ const importExpr = (new DynamicImportExpr(dep.importPath)).prop('then').callFn([innerFn]);
11810
+ dependencies.push(importExpr);
11811
+ }
11812
+ else {
11813
+ // Non-deferrable symbol, just use a reference to the type.
11814
+ dependencies.push(dep.type);
11815
+ }
11816
+ }
11817
+ const depsFnExpr = arrowFn([], literalArr(dependencies));
11818
+ if (op.handle.slot === null) {
11819
+ throw new Error('AssertionError: slot must be assigned bfore extracting defer deps functions');
11820
+ }
11821
+ op.resolverFn = job.pool.getSharedFunctionReference(depsFnExpr, `${job.componentName}_Defer_${op.handle.slot}_DepsFn`);
11822
+ }
11823
+ }
11824
+ }
11825
+ }
11826
+
11827
+ /**
11828
+ * Create one helper context op per i18n block (including generate descending blocks).
11829
+ *
11830
+ * Also, if an ICU exists inside an i18n block that also contains other localizable content (such as
11831
+ * string), create an additional helper context op for the ICU.
11832
+ *
11833
+ * These context ops are later used for generating i18n messages. (Although we generate at least one
11834
+ * context op per nested view, we will collect them up the tree later, to generate a top-level
11835
+ * message.)
11836
+ */
11837
+ function createI18nContexts(job) {
11838
+ let currentI18nOp = null;
11839
+ let xref;
11840
+ for (const unit of job.units) {
11841
+ for (const op of unit.create) {
11842
+ switch (op.kind) {
11843
+ case OpKind.I18nStart:
11844
+ // Each i18n block gets its own context.
11845
+ xref = job.allocateXrefId();
11846
+ unit.create.push(createI18nContextOp(xref, op.xref, op.message, null));
11847
+ op.context = xref;
11848
+ currentI18nOp = op;
11849
+ break;
11850
+ case OpKind.I18nEnd:
11851
+ currentI18nOp = null;
11852
+ break;
11853
+ case OpKind.Icu:
11854
+ // If an ICU represents a different message than its containing block, we give it its own
11855
+ // i18n context.
11856
+ if (currentI18nOp === null) {
11857
+ throw Error('Unexpected ICU outside of an i18n block.');
11858
+ }
11859
+ if (op.message.id !== currentI18nOp.message.id) {
11860
+ // There was an enclosing i18n block around this ICU somewhere.
11861
+ xref = job.allocateXrefId();
11862
+ unit.create.push(createI18nContextOp(xref, currentI18nOp.xref, op.message, null));
11863
+ op.context = xref;
11864
+ }
11865
+ else {
11866
+ // The i18n block was generated because of this ICU, OR it was explicit, but the ICU is
11867
+ // the only localizable content inside of it.
11868
+ op.context = currentI18nOp.context;
11869
+ }
11870
+ break;
11871
+ }
11872
+ }
11873
+ }
11874
+ }
11875
+
11876
+ /**
11877
+ * Replace the ICU update ops with i18n expression ops.
11878
+ */
11879
+ function createI18nIcuExpressions(job) {
11880
+ const icus = new Map();
11881
+ const i18nContexts = new Map();
11882
+ const i18nBlocks = new Map();
11883
+ // Collect maps of ops that need to be referenced to create the I18nExpressionOps.
11884
+ for (const unit of job.units) {
11885
+ for (const op of unit.create) {
11886
+ switch (op.kind) {
11887
+ case OpKind.Icu:
11888
+ icus.set(op.xref, op);
11889
+ break;
11890
+ case OpKind.I18nContext:
11891
+ i18nContexts.set(op.xref, op);
11892
+ break;
11893
+ case OpKind.I18nStart:
11894
+ i18nBlocks.set(op.xref, op);
11895
+ break;
11896
+ }
11897
+ }
11898
+ // Replace each IcuUpdateOp with an I18nExpressionOp.
11899
+ for (const op of unit.update) {
11900
+ switch (op.kind) {
11901
+ case OpKind.IcuUpdate:
11902
+ const icuOp = icus.get(op.xref);
11903
+ if (icuOp?.icu.expressionPlaceholder === undefined) {
11904
+ throw Error('ICU should have an i18n placeholder');
11905
+ }
11906
+ if (icuOp.context === null) {
11907
+ throw Error('ICU should have its i18n context set');
11908
+ }
11909
+ const i18nContext = i18nContexts.get(icuOp.context);
11910
+ const i18nBlock = i18nBlocks.get(i18nContext.i18nBlock);
11911
+ OpList.replace(op, createI18nExpressionOp(i18nContext.xref, i18nBlock.xref, i18nBlock.handle, new LexicalReadExpr(icuOp.icu.expression), icuOp.icu.expressionPlaceholder,
11912
+ // ICU-based i18n Expressions are resolved during post-processing.
11913
+ I18nParamResolutionTime.Postproccessing, null));
11914
+ break;
11915
+ }
11916
+ }
11917
+ }
11918
+ }
11919
+
11920
+ /**
11921
+ * Defer instructions take a configuration array, which should be collected into the component
11922
+ * consts. This phase finds the config options, and creates the corresponding const array.
11923
+ */
11924
+ function configureDeferInstructions(job) {
11925
+ for (const unit of job.units) {
11926
+ for (const op of unit.create) {
11927
+ if (op.kind !== OpKind.Defer) {
11928
+ continue;
11929
+ }
11930
+ if (op.placeholderMinimumTime !== null) {
11931
+ op.placeholderConfig =
11932
+ new ConstCollectedExpr(literalOrArrayLiteral([op.placeholderMinimumTime]));
11933
+ }
11934
+ if (op.loadingMinimumTime !== null || op.loadingAfterTime !== null) {
11935
+ op.loadingConfig = new ConstCollectedExpr(literalOrArrayLiteral([op.loadingMinimumTime, op.loadingAfterTime]));
11936
+ }
11937
+ }
11938
+ }
11939
+ }
11940
+
11941
+ /**
11942
+ * Some `defer` conditions can reference other elements in the template, using their local reference
11943
+ * names. However, the semantics are quite different from the normal local reference system: in
11944
+ * particular, we need to look at local reference names in enclosing views. This phase resolves
11945
+ * all such references to actual xrefs.
11946
+ */
11947
+ function resolveDeferTargetNames(job) {
11948
+ const scopes = new Map();
11949
+ function getScopeForView(view) {
11950
+ if (scopes.has(view.xref)) {
11951
+ return scopes.get(view.xref);
11952
+ }
11953
+ const scope = new Scope$1();
11954
+ for (const op of view.create) {
11955
+ // add everything that can be referenced.
11956
+ if (!isElementOrContainerOp(op) || op.localRefs === null) {
11957
+ continue;
11958
+ }
11959
+ if (!Array.isArray(op.localRefs)) {
11960
+ throw new Error('LocalRefs were already processed, but were needed to resolve defer targets.');
11961
+ }
11962
+ for (const ref of op.localRefs) {
11963
+ if (ref.target !== '') {
11964
+ continue;
11965
+ }
11966
+ scope.targets.set(ref.name, { xref: op.xref, slot: op.handle });
11967
+ }
11968
+ }
11969
+ scopes.set(view.xref, scope);
11970
+ return scope;
11971
+ }
11972
+ function resolveTrigger(deferOwnerView, op, placeholderView) {
11973
+ switch (op.trigger.kind) {
11974
+ case DeferTriggerKind.Idle:
11975
+ case DeferTriggerKind.Immediate:
11976
+ case DeferTriggerKind.Timer:
11977
+ return;
11978
+ case DeferTriggerKind.Hover:
11979
+ case DeferTriggerKind.Interaction:
11980
+ case DeferTriggerKind.Viewport:
11981
+ if (op.trigger.targetName === null) {
11982
+ // A `null` target name indicates we should default to the first element in the
11983
+ // placeholder block.
11984
+ if (placeholderView === null) {
11985
+ throw new Error('defer on trigger with no target name must have a placeholder block');
11986
+ }
11987
+ const placeholder = job.views.get(placeholderView);
11988
+ if (placeholder == undefined) {
11989
+ throw new Error('AssertionError: could not find placeholder view for defer on trigger');
11990
+ }
11991
+ for (const placeholderOp of placeholder.create) {
11992
+ if (hasConsumesSlotTrait(placeholderOp) &&
11993
+ (isElementOrContainerOp(placeholderOp) ||
11994
+ placeholderOp.kind === OpKind.Projection)) {
11995
+ op.trigger.targetXref = placeholderOp.xref;
11996
+ op.trigger.targetView = placeholderView;
11997
+ op.trigger.targetSlotViewSteps = -1;
11998
+ op.trigger.targetSlot = placeholderOp.handle;
11999
+ return;
12000
+ }
12001
+ }
12002
+ return;
12003
+ }
12004
+ let view = placeholderView !== null ? job.views.get(placeholderView) : deferOwnerView;
12005
+ let step = placeholderView !== null ? -1 : 0;
12006
+ while (view !== null) {
12007
+ const scope = getScopeForView(view);
12008
+ if (scope.targets.has(op.trigger.targetName)) {
12009
+ const { xref, slot } = scope.targets.get(op.trigger.targetName);
12010
+ op.trigger.targetXref = xref;
12011
+ op.trigger.targetView = view.xref;
12012
+ op.trigger.targetSlotViewSteps = step;
12013
+ op.trigger.targetSlot = slot;
12014
+ return;
12015
+ }
12016
+ view = view.parent !== null ? job.views.get(view.parent) : null;
12017
+ step++;
12018
+ }
12019
+ break;
12020
+ default:
12021
+ throw new Error(`Trigger kind ${op.trigger.kind} not handled`);
12022
+ }
12023
+ }
12024
+ // Find the defer ops, and assign the data about their targets.
12025
+ for (const unit of job.units) {
12026
+ const defers = new Map();
12027
+ for (const op of unit.create) {
12028
+ switch (op.kind) {
12029
+ case OpKind.Defer:
12030
+ defers.set(op.xref, op);
12031
+ break;
12032
+ case OpKind.DeferOn:
12033
+ const deferOp = defers.get(op.defer);
12034
+ resolveTrigger(unit, op, deferOp.placeholderView);
12035
+ break;
12036
+ }
12037
+ }
12038
+ }
12039
+ }
12040
+ class Scope$1 {
12041
+ constructor() {
12042
+ this.targets = new Map();
12043
+ }
12044
+ }
12045
+
11704
12046
  const REPLACEMENTS = new Map([
11705
12047
  [OpKind.ElementEnd, [OpKind.ElementStart, OpKind.Element]],
11706
12048
  [OpKind.ContainerEnd, [OpKind.ContainerStart, OpKind.Container]],
@@ -11711,10 +12053,10 @@ const REPLACEMENTS = new Map([
11711
12053
  */
11712
12054
  const IGNORED_OP_KINDS = new Set([OpKind.Pipe]);
11713
12055
  /**
11714
- * Replace sequences of mergable elements (e.g. `ElementStart` and `ElementEnd`) with a consolidated
11715
- * element (e.g. `Element`).
12056
+ * Replace sequences of mergable instructions (e.g. `ElementStart` and `ElementEnd`) with a
12057
+ * consolidated instruction (e.g. `Element`).
11716
12058
  */
11717
- function phaseEmptyElements(job) {
12059
+ function collapseEmptyInstructions(job) {
11718
12060
  for (const unit of job.units) {
11719
12061
  for (const op of unit.create) {
11720
12062
  // Find end ops that may be able to be merged.
@@ -11741,10 +12083,13 @@ function phaseEmptyElements(job) {
11741
12083
  }
11742
12084
 
11743
12085
  /**
11744
- * Finds all unresolved safe read expressions, and converts them into the appropriate output AST
11745
- * reads, guarded by null checks.
12086
+ * Safe read expressions such as `a?.b` have different semantics in Angular templates as
12087
+ * compared to JavaScript. In particular, they default to `null` instead of `undefined`. This phase
12088
+ * finds all unresolved safe read expressions, and converts them into the appropriate output AST
12089
+ * reads, guarded by null checks. We generate temporaries as needed, to avoid re-evaluating the same
12090
+ * sub-expression multiple times.
11746
12091
  */
11747
- function phaseExpandSafeReads(job) {
12092
+ function expandSafeReads(job) {
11748
12093
  for (const unit of job.units) {
11749
12094
  for (const op of unit.ops()) {
11750
12095
  transformExpressionsInOp(op, e => safeTransform(e, { job }), VisitorContextFlag.None);
@@ -11915,42 +12260,159 @@ function ternaryTransform(e) {
11915
12260
  return new ConditionalExpr(new BinaryOperatorExpr(BinaryOperator.Equals, e.guard, NULL_EXPR), NULL_EXPR, e.expr);
11916
12261
  }
11917
12262
 
11918
- function phaseRepeaterDerivedVars(job) {
11919
- const repeaters = new Map();
12263
+ /**
12264
+ * The escape sequence used indicate message param values.
12265
+ */
12266
+ const ESCAPE = '\uFFFD';
12267
+ /**
12268
+ * Marker used to indicate an element tag.
12269
+ */
12270
+ const ELEMENT_MARKER = '#';
12271
+ /**
12272
+ * Marker used to indicate a template tag.
12273
+ */
12274
+ const TEMPLATE_MARKER = '*';
12275
+ /**
12276
+ * Marker used to indicate closing of an element or template tag.
12277
+ */
12278
+ const TAG_CLOSE_MARKER = '/';
12279
+ /**
12280
+ * Marker used to indicate the sub-template context.
12281
+ */
12282
+ const CONTEXT_MARKER = ':';
12283
+ /**
12284
+ * Marker used to indicate the start of a list of values.
12285
+ */
12286
+ const LIST_START_MARKER = '[';
12287
+ /**
12288
+ * Marker used to indicate the end of a list of values.
12289
+ */
12290
+ const LIST_END_MARKER = ']';
12291
+ /**
12292
+ * Delimiter used to separate multiple values in a list.
12293
+ */
12294
+ const LIST_DELIMITER = '|';
12295
+ /**
12296
+ * Formats the param maps on extracted message ops into a maps of `Expression` objects that can be
12297
+ * used in the final output.
12298
+ */
12299
+ function extractI18nMessages(job) {
12300
+ // Save the i18n context ops for later use.
12301
+ const i18nContexts = new Map();
12302
+ // Record which contexts represent i18n blocks (any other contexts are assumed to have been
12303
+ // created from ICUs).
12304
+ const i18nBlockContexts = new Set();
11920
12305
  for (const unit of job.units) {
11921
- for (const op of unit.ops()) {
11922
- if (op.kind === OpKind.RepeaterCreate) {
11923
- repeaters.set(op.xref, op);
12306
+ for (const op of unit.create) {
12307
+ switch (op.kind) {
12308
+ case OpKind.I18nContext:
12309
+ i18nContexts.set(op.xref, op);
12310
+ break;
12311
+ case OpKind.I18nStart:
12312
+ i18nBlockContexts.add(op.context);
12313
+ break;
11924
12314
  }
11925
12315
  }
11926
12316
  }
12317
+ // Extract messages from root i18n blocks.
12318
+ const i18nBlockMessages = new Map();
11927
12319
  for (const unit of job.units) {
11928
- for (const op of unit.ops()) {
11929
- transformExpressionsInOp(op, expr => {
11930
- if (!(expr instanceof DerivedRepeaterVarExpr)) {
11931
- return expr;
12320
+ for (const op of unit.create) {
12321
+ if (op.kind === OpKind.I18nStart && op.xref === op.root) {
12322
+ if (!op.context) {
12323
+ throw Error('I18n start op should have its context set.');
11932
12324
  }
11933
- const repeaterOp = repeaters.get(expr.xref);
11934
- switch (expr.identity) {
11935
- case DerivedRepeaterVarIdentity.First:
11936
- return new BinaryOperatorExpr(BinaryOperator.Identical, new LexicalReadExpr(repeaterOp.varNames.$index), literal(0));
11937
- case DerivedRepeaterVarIdentity.Last:
11938
- return new BinaryOperatorExpr(BinaryOperator.Identical, new LexicalReadExpr(repeaterOp.varNames.$index), new BinaryOperatorExpr(BinaryOperator.Minus, new LexicalReadExpr(repeaterOp.varNames.$count), literal(1)));
11939
- case DerivedRepeaterVarIdentity.Even:
11940
- return new BinaryOperatorExpr(BinaryOperator.Identical, new BinaryOperatorExpr(BinaryOperator.Modulo, new LexicalReadExpr(repeaterOp.varNames.$index), literal(2)), literal(0));
11941
- case DerivedRepeaterVarIdentity.Odd:
11942
- return new BinaryOperatorExpr(BinaryOperator.NotIdentical, new BinaryOperatorExpr(BinaryOperator.Modulo, new LexicalReadExpr(repeaterOp.varNames.$index), literal(2)), literal(0));
12325
+ const i18nMessageOp = createI18nMessage(job, i18nContexts.get(op.context));
12326
+ i18nBlockMessages.set(op.xref, i18nMessageOp);
12327
+ unit.create.push(i18nMessageOp);
12328
+ }
12329
+ }
12330
+ }
12331
+ // Extract messages from ICUs with their own sub-context.
12332
+ for (const unit of job.units) {
12333
+ for (const op of unit.create) {
12334
+ if (op.kind === OpKind.Icu) {
12335
+ if (!op.context) {
12336
+ throw Error('ICU op should have its context set.');
11943
12337
  }
11944
- }, VisitorContextFlag.None);
12338
+ if (!i18nBlockContexts.has(op.context)) {
12339
+ const i18nContext = i18nContexts.get(op.context);
12340
+ const subMessage = createI18nMessage(job, i18nContext, op.messagePlaceholder);
12341
+ unit.create.push(subMessage);
12342
+ const parentMessage = i18nBlockMessages.get(i18nContext.i18nBlock);
12343
+ parentMessage?.subMessages.push(subMessage.xref);
12344
+ }
12345
+ OpList.remove(op);
12346
+ }
11945
12347
  }
11946
12348
  }
11947
12349
  }
12350
+ /**
12351
+ * Create an i18n message op from an i18n context op.
12352
+ */
12353
+ function createI18nMessage(job, context, messagePlaceholder) {
12354
+ let needsPostprocessing = context.postprocessingParams.size > 0;
12355
+ for (const values of context.params.values()) {
12356
+ if (values.length > 1) {
12357
+ needsPostprocessing = true;
12358
+ }
12359
+ }
12360
+ return createI18nMessageOp(job.allocateXrefId(), context.i18nBlock, context.message, messagePlaceholder ?? null, formatParams(context.params), formatParams(context.postprocessingParams), needsPostprocessing);
12361
+ }
12362
+ /**
12363
+ * Formats a map of `I18nParamValue[]` values into a map of `Expression` values.
12364
+ */
12365
+ function formatParams(params) {
12366
+ const result = new Map();
12367
+ for (const [placeholder, placeholderValues] of [...params].sort()) {
12368
+ const serializedValues = formatParamValues(placeholderValues);
12369
+ if (serializedValues !== null) {
12370
+ result.set(placeholder, literal(formatParamValues(placeholderValues)));
12371
+ }
12372
+ }
12373
+ return result;
12374
+ }
12375
+ /**
12376
+ * Formats an `I18nParamValue[]` into a string (or null for empty array).
12377
+ */
12378
+ function formatParamValues(values) {
12379
+ if (values.length === 0) {
12380
+ return null;
12381
+ }
12382
+ const serializedValues = values.map(value => formatValue(value));
12383
+ return serializedValues.length === 1 ?
12384
+ serializedValues[0] :
12385
+ `${LIST_START_MARKER}${serializedValues.join(LIST_DELIMITER)}${LIST_END_MARKER}`;
12386
+ }
12387
+ /**
12388
+ * Formats a single `I18nParamValue` into a string
12389
+ */
12390
+ function formatValue(value) {
12391
+ let tagMarker = '';
12392
+ let closeMarker = '';
12393
+ if (value.flags & I18nParamValueFlags.ElementTag) {
12394
+ tagMarker = ELEMENT_MARKER;
12395
+ }
12396
+ else if (value.flags & I18nParamValueFlags.TemplateTag) {
12397
+ tagMarker = TEMPLATE_MARKER;
12398
+ }
12399
+ if (tagMarker !== '') {
12400
+ closeMarker = value.flags & I18nParamValueFlags.CloseTag ? TAG_CLOSE_MARKER : '';
12401
+ }
12402
+ const context = value.subTemplateIndex === null ? '' : `${CONTEXT_MARKER}${value.subTemplateIndex}`;
12403
+ // Self-closing tags use a special form that concatenates the start and close tag values.
12404
+ if ((value.flags & I18nParamValueFlags.OpenTag) &&
12405
+ (value.flags & I18nParamValueFlags.CloseTag)) {
12406
+ return `${ESCAPE}${tagMarker}${value.value}${context}${ESCAPE}${ESCAPE}${closeMarker}${tagMarker}${value.value}${context}${ESCAPE}`;
12407
+ }
12408
+ return `${ESCAPE}${closeMarker}${tagMarker}${value.value}${context}${ESCAPE}`;
12409
+ }
11948
12410
 
11949
12411
  /**
11950
12412
  * Generate `ir.AdvanceOp`s in between `ir.UpdateOp`s that ensure the runtime's implicit slot
11951
12413
  * context will be advanced correctly.
11952
12414
  */
11953
- function phaseGenerateAdvance(job) {
12415
+ function generateAdvance(job) {
11954
12416
  for (const unit of job.units) {
11955
12417
  // First build a map of all of the declarations in the view that have assigned slots.
11956
12418
  const slotMap = new Map();
@@ -11958,10 +12420,10 @@ function phaseGenerateAdvance(job) {
11958
12420
  if (!hasConsumesSlotTrait(op)) {
11959
12421
  continue;
11960
12422
  }
11961
- else if (op.slot === null) {
12423
+ else if (op.handle.slot === null) {
11962
12424
  throw new Error(`AssertionError: expected slots to have been allocated before generating advance() calls`);
11963
12425
  }
11964
- slotMap.set(op.xref, op.slot);
12426
+ slotMap.set(op.xref, op.handle.slot);
11965
12427
  }
11966
12428
  // Next, step through the update operations and generate `ir.AdvanceOp`s as required to ensure
11967
12429
  // the runtime's implicit slot counter will be set to the correct slot before executing each
@@ -11999,7 +12461,7 @@ function phaseGenerateAdvance(job) {
11999
12461
  * populate `project` arguments, and generate the required `projectionDef` instruction for the job's
12000
12462
  * root view.
12001
12463
  */
12002
- function phaseGenerateProjectionDef(job) {
12464
+ function generateProjectionDefs(job) {
12003
12465
  // TODO: Why does TemplateDefinitionBuilder force a shared constant?
12004
12466
  const share = job.compatibility === CompatibilityMode.TemplateDefinitionBuilder;
12005
12467
  // Collect all selectors from this component, and its nested views. Also, assign each projection a
@@ -12044,7 +12506,7 @@ function phaseGenerateProjectionDef(job) {
12044
12506
  * Variables are generated here unconditionally, and may optimized away in future operations if it
12045
12507
  * turns out their values (and any side effects) are unused.
12046
12508
  */
12047
- function phaseGenerateVariables(job) {
12509
+ function generateVariables(job) {
12048
12510
  recursivelyProcessView(job.root, /* there is no parent scope for the root view */ null);
12049
12511
  }
12050
12512
  /**
@@ -12110,6 +12572,7 @@ function getScopeForView(view, parent) {
12110
12572
  scope.references.push({
12111
12573
  name: op.localRefs[offset].name,
12112
12574
  targetId: op.xref,
12575
+ targetSlot: op.handle,
12113
12576
  offset,
12114
12577
  variable: {
12115
12578
  kind: SemanticVariableKind.Identifier,
@@ -12151,7 +12614,7 @@ function generateVariablesInScopeForView(view, scope) {
12151
12614
  }
12152
12615
  // Add variables for all local references declared for elements in this scope.
12153
12616
  for (const ref of scope.references) {
12154
- newOps.push(createVariableOp(view.job.allocateXrefId(), ref.variable, new ReferenceExpr(ref.targetId, ref.offset), VariableFlags.None));
12617
+ newOps.push(createVariableOp(view.job.allocateXrefId(), ref.variable, new ReferenceExpr(ref.targetId, ref.targetSlot, ref.offset), VariableFlags.None));
12155
12618
  }
12156
12619
  if (scope.parent !== null) {
12157
12620
  // Recursively add variables from the parent scope.
@@ -12161,27 +12624,20 @@ function generateVariablesInScopeForView(view, scope) {
12161
12624
  }
12162
12625
 
12163
12626
  /**
12164
- * Looks for the HasConst trait, indicating that an op or expression has some data which
12165
- * should be collected into the constant array. Capable of collecting either a single literal value,
12166
- * or an array literal.
12627
+ * `ir.ConstCollectedExpr` may be present in any IR expression. This means that expression needs to
12628
+ * be lifted into the component const array, and replaced with a reference to the const array at its
12629
+ *
12630
+ * usage site. This phase walks the IR and performs this transformation.
12167
12631
  */
12168
- function phaseConstTraitCollection(job) {
12169
- const collectGlobalConsts = (e) => {
12170
- if (e instanceof ExpressionBase && hasConstTrait(e)) {
12171
- // TODO: Figure out how to make this type narrowing work.
12172
- const ea = e;
12173
- if (ea.constValue !== null) {
12174
- ea.constIndex = job.addConst(ea.constValue);
12175
- }
12176
- }
12177
- return e;
12178
- };
12632
+ function collectConstExpressions(job) {
12179
12633
  for (const unit of job.units) {
12180
12634
  for (const op of unit.ops()) {
12181
- if (hasConstTrait(op) && op.constValue !== null) {
12182
- op.constIndex = job.addConst(op.makeExpression(op.constValue));
12183
- }
12184
- transformExpressionsInOp(op, collectGlobalConsts, VisitorContextFlag.None);
12635
+ transformExpressionsInOp(op, expr => {
12636
+ if (!(expr instanceof ConstCollectedExpr)) {
12637
+ return expr;
12638
+ }
12639
+ return literal(job.addConst(expr.expr));
12640
+ }, VisitorContextFlag.None);
12185
12641
  }
12186
12642
  }
12187
12643
  }
@@ -12191,7 +12647,13 @@ const CLASS_DOT = 'class.';
12191
12647
  const STYLE_BANG = 'style!';
12192
12648
  const CLASS_BANG = 'class!';
12193
12649
  const BANG_IMPORTANT = '!important';
12194
- function phaseHostStylePropertyParsing(job) {
12650
+ /**
12651
+ * Host bindings are compiled using a different parser entrypoint, and are parsed quite differently
12652
+ * as a result. Therefore, we need to do some extra parsing for host style properties, as compared
12653
+ * to non-host style properties.
12654
+ * TODO: Unify host bindings and non-host bindings in the parser.
12655
+ */
12656
+ function parseHostStyleProperties(job) {
12195
12657
  for (const op of job.root.update) {
12196
12658
  if (op.kind !== OpKind.Binding) {
12197
12659
  continue;
@@ -12253,31 +12715,6 @@ function parseProperty$1(name) {
12253
12715
  return { property, suffix };
12254
12716
  }
12255
12717
 
12256
- /**
12257
- * Lifts i18n properties into the consts array.
12258
- */
12259
- function phaseI18nConstCollection(job) {
12260
- // Serialize the extracted messages into the const array.
12261
- // TODO: Use `Map` instead of object.
12262
- const messageConstIndices = {};
12263
- for (const unit of job.units) {
12264
- for (const op of unit.create) {
12265
- if (op.kind === OpKind.ExtractedMessage) {
12266
- messageConstIndices[op.owner] = job.addConst(op.expression, op.statements);
12267
- OpList.remove(op);
12268
- }
12269
- }
12270
- }
12271
- // Assign const index to i18n ops that messages were extracted from.
12272
- for (const unit of job.units) {
12273
- for (const op of unit.create) {
12274
- if (op.kind === OpKind.I18nStart) {
12275
- op.messageIndex = messageConstIndices[op.root];
12276
- }
12277
- }
12278
- }
12279
- }
12280
-
12281
12718
  function mapEntry(key, value) {
12282
12719
  return { key, value, quoted: false };
12283
12720
  }
@@ -19155,38 +19592,73 @@ const NG_I18N_CLOSURE_MODE$1 = 'ngI18nClosureMode';
19155
19592
  * considers variables like `I18N_0` as constants and throws an error when their value changes.
19156
19593
  */
19157
19594
  const TRANSLATION_VAR_PREFIX = 'i18n_';
19158
- /** Extracts i18n messages into the consts array. */
19159
- function phaseI18nMessageExtraction(job) {
19595
+ /**
19596
+ * Lifts i18n properties into the consts array.
19597
+ * TODO: Can we use `ConstCollectedExpr`?
19598
+ */
19599
+ function collectI18nConsts(job) {
19160
19600
  const fileBasedI18nSuffix = job.relativeContextFilePath.replace(/[^A-Za-z0-9]/g, '_').toUpperCase() + '_';
19601
+ const messageConstIndices = new Map();
19602
+ // Remove all of the i18n message ops into a map.
19603
+ const messages = new Map();
19604
+ for (const unit of job.units) {
19605
+ for (const op of unit.create) {
19606
+ if (op.kind === OpKind.I18nMessage) {
19607
+ messages.set(op.xref, op);
19608
+ OpList.remove(op);
19609
+ }
19610
+ }
19611
+ }
19612
+ // Serialize the extracted messages for root i18n blocks into the const array.
19613
+ for (const op of messages.values()) {
19614
+ if (op.kind === OpKind.I18nMessage && op.messagePlaceholder === null) {
19615
+ const { mainVar, statements } = collectMessage(job, fileBasedI18nSuffix, messages, op);
19616
+ messageConstIndices.set(op.i18nBlock, job.addConst(mainVar, statements));
19617
+ }
19618
+ }
19619
+ // Assign const index to i18n ops that messages were extracted from.
19161
19620
  for (const unit of job.units) {
19162
19621
  for (const op of unit.create) {
19163
19622
  if (op.kind === OpKind.I18nStart) {
19164
- // Only extract messages from root i18n ops, not sub-template ones.
19165
- if (op.xref === op.root) {
19166
- // Sort the params map to match the ordering in TemplateDefinitionBuilder.
19167
- const params = new Map([...op.params.entries()].sort());
19168
- const mainVar = variable(job.pool.uniqueName(TRANSLATION_VAR_PREFIX));
19169
- // Closure Compiler requires const names to start with `MSG_` but disallows any other
19170
- // const to start with `MSG_`. We define a variable starting with `MSG_` just for the
19171
- // `goog.getMsg` call
19172
- const closureVar = i18nGenerateClosureVar(job.pool, op.message.id, fileBasedI18nSuffix, job.i18nUseExternalIds);
19173
- let transformFn = undefined;
19174
- // If nescessary, add a post-processing step and resolve any placeholder params that are
19175
- // set in post-processing.
19176
- if (op.needsPostprocessing) {
19177
- const extraTransformFnParams = [];
19178
- if (op.postprocessingParams.size > 0) {
19179
- extraTransformFnParams.push(literalMap([...op.postprocessingParams.entries()].map(([key, value]) => ({ key, value, quoted: true }))));
19180
- }
19181
- transformFn = (expr) => importExpr(Identifiers.i18nPostprocess).callFn([expr, ...extraTransformFnParams]);
19182
- }
19183
- const statements = getTranslationDeclStmts$1(op.message, mainVar, closureVar, params, transformFn);
19184
- unit.create.push(createExtractedMessageOp(op.xref, mainVar, statements));
19185
- }
19623
+ op.messageIndex = messageConstIndices.get(op.root);
19186
19624
  }
19187
19625
  }
19188
19626
  }
19189
19627
  }
19628
+ /**
19629
+ * Collects the given message into a set of statements that can be added to the const array.
19630
+ * This will recursively collect any sub-messages referenced from the parent message as well.
19631
+ */
19632
+ function collectMessage(job, fileBasedI18nSuffix, messages, messageOp) {
19633
+ // Recursively collect any sub-messages, and fill in their placeholders in this message.
19634
+ const statements = [];
19635
+ for (const subMessageId of messageOp.subMessages) {
19636
+ const subMessage = messages.get(subMessageId);
19637
+ const { mainVar: subMessageVar, statements: subMessageStatements } = collectMessage(job, fileBasedI18nSuffix, messages, subMessage);
19638
+ statements.push(...subMessageStatements);
19639
+ messageOp.params.set(subMessage.messagePlaceholder, subMessageVar);
19640
+ }
19641
+ // Check that the message has all of its parameters filled out.
19642
+ assertAllParamsResolved(messageOp);
19643
+ const mainVar = variable(job.pool.uniqueName(TRANSLATION_VAR_PREFIX));
19644
+ // Closure Compiler requires const names to start with `MSG_` but disallows any other
19645
+ // const to start with `MSG_`. We define a variable starting with `MSG_` just for the
19646
+ // `goog.getMsg` call
19647
+ const closureVar = i18nGenerateClosureVar(job.pool, messageOp.message.id, fileBasedI18nSuffix, job.i18nUseExternalIds);
19648
+ let transformFn = undefined;
19649
+ // If nescessary, add a post-processing step and resolve any placeholder params that are
19650
+ // set in post-processing.
19651
+ if (messageOp.needsPostprocessing) {
19652
+ const extraTransformFnParams = [];
19653
+ if (messageOp.postprocessingParams.size > 0) {
19654
+ extraTransformFnParams.push(literalMap([...messageOp.postprocessingParams].map(([key, value]) => ({ key, value, quoted: true }))));
19655
+ }
19656
+ transformFn = (expr) => importExpr(Identifiers.i18nPostprocess).callFn([expr, ...extraTransformFnParams]);
19657
+ }
19658
+ // Add the message's statements
19659
+ statements.push(...getTranslationDeclStmts$1(messageOp.message, mainVar, closureVar, messageOp.params, transformFn));
19660
+ return { mainVar, statements };
19661
+ }
19190
19662
  /**
19191
19663
  * Generate statements that define a given translation message.
19192
19664
  *
@@ -19254,27 +19726,45 @@ function i18nGenerateClosureVar(pool, messageId, fileBasedI18nSuffix, useExterna
19254
19726
  }
19255
19727
  return variable(name);
19256
19728
  }
19729
+ /**
19730
+ * Asserts that all of the message's placeholders have values.
19731
+ */
19732
+ function assertAllParamsResolved(op) {
19733
+ for (const placeholder in op.message.placeholders) {
19734
+ if (!op.params.has(placeholder) && !op.postprocessingParams.has(placeholder)) {
19735
+ throw Error(`Failed to resolve i18n placeholder: ${placeholder}`);
19736
+ }
19737
+ }
19738
+ for (const placeholder in op.message.placeholderToMessage) {
19739
+ if (!op.params.has(placeholder) && !op.postprocessingParams.has(placeholder)) {
19740
+ throw Error(`Failed to resolve i18n message placeholder: ${placeholder}`);
19741
+ }
19742
+ }
19743
+ }
19257
19744
 
19258
19745
  /**
19259
19746
  * Removes text nodes within i18n blocks since they are already hardcoded into the i18n message.
19260
19747
  */
19261
- function phaseI18nTextExtraction(job) {
19748
+ function extractI18nText(job) {
19262
19749
  for (const unit of job.units) {
19263
19750
  // Remove all text nodes within i18n blocks, their content is already captured in the i18n
19264
19751
  // message.
19265
- let currentI18nId = null;
19266
- const textNodes = new Map();
19752
+ let currentI18n = null;
19753
+ const textNodeI18nBlocks = new Map();
19267
19754
  for (const op of unit.create) {
19268
19755
  switch (op.kind) {
19269
19756
  case OpKind.I18nStart:
19270
- currentI18nId = op.xref;
19757
+ if (op.context === null) {
19758
+ throw Error('I18n op should have its context set.');
19759
+ }
19760
+ currentI18n = op;
19271
19761
  break;
19272
19762
  case OpKind.I18nEnd:
19273
- currentI18nId = null;
19763
+ currentI18n = null;
19274
19764
  break;
19275
19765
  case OpKind.Text:
19276
- if (currentI18nId !== null) {
19277
- textNodes.set(op.xref, currentI18nId);
19766
+ if (currentI18n !== null) {
19767
+ textNodeI18nBlocks.set(op.xref, currentI18n);
19278
19768
  OpList.remove(op);
19279
19769
  }
19280
19770
  break;
@@ -19285,18 +19775,17 @@ function phaseI18nTextExtraction(job) {
19285
19775
  for (const op of unit.update) {
19286
19776
  switch (op.kind) {
19287
19777
  case OpKind.InterpolateText:
19288
- if (!textNodes.has(op.target)) {
19778
+ if (!textNodeI18nBlocks.has(op.target)) {
19289
19779
  continue;
19290
19780
  }
19291
- const i18nBlockId = textNodes.get(op.target);
19781
+ const i18nOp = textNodeI18nBlocks.get(op.target);
19292
19782
  const ops = [];
19293
19783
  for (let i = 0; i < op.interpolation.expressions.length; i++) {
19294
19784
  const expr = op.interpolation.expressions[i];
19295
19785
  const placeholder = op.i18nPlaceholders[i];
19296
- ops.push(createI18nExpressionOp(i18nBlockId, expr, placeholder.name, I18nParamResolutionTime.Creation, expr.sourceSpan ?? op.sourceSpan));
19297
- }
19298
- if (ops.length > 0) {
19299
- // ops.push(ir.createI18nApplyOp(i18nBlockId, op.i18nPlaceholders, op.sourceSpan));
19786
+ // For now, this i18nExpression depends on the slot context of the enclosing i18n block.
19787
+ // Later, we will modify this, and advance to a different point.
19788
+ ops.push(createI18nExpressionOp(i18nOp.context, i18nOp.xref, i18nOp.handle, expr, placeholder.name, I18nParamResolutionTime.Creation, expr.sourceSpan ?? op.sourceSpan));
19300
19789
  }
19301
19790
  OpList.replaceWithMany(op, ops);
19302
19791
  break;
@@ -19305,55 +19794,11 @@ function phaseI18nTextExtraction(job) {
19305
19794
  }
19306
19795
  }
19307
19796
 
19308
- /**
19309
- * Extracts ICUs into i18n expressions.
19310
- */
19311
- function phaseIcuExtraction(job) {
19312
- for (const unit of job.units) {
19313
- // Build a map of ICU to the i18n block they belong to, then remove the `Icu` ops.
19314
- const icus = new Map();
19315
- let currentI18nId = null;
19316
- for (const op of unit.create) {
19317
- switch (op.kind) {
19318
- case OpKind.I18nStart:
19319
- currentI18nId = op.xref;
19320
- break;
19321
- case OpKind.I18nEnd:
19322
- currentI18nId = null;
19323
- break;
19324
- case OpKind.Icu:
19325
- if (currentI18nId === null) {
19326
- throw Error('Unexpected ICU outside of an i18n block.');
19327
- }
19328
- icus.set(op.xref, { message: op.message, i18nBlockId: currentI18nId });
19329
- OpList.remove(op);
19330
- break;
19331
- }
19332
- }
19333
- // Replace the `IcuUpdate` ops with `i18nExpr` ops.
19334
- for (const op of unit.update) {
19335
- switch (op.kind) {
19336
- case OpKind.IcuUpdate:
19337
- const { message, i18nBlockId } = icus.get(op.xref);
19338
- const icuNode = message.nodes.find((n) => n instanceof Icu);
19339
- if (icuNode === undefined) {
19340
- throw Error('Could not find ICU in i18n AST');
19341
- }
19342
- if (icuNode.expressionPlaceholder === undefined) {
19343
- throw Error('ICU is missing an i18n placeholder');
19344
- }
19345
- OpList.replace(op, createI18nExpressionOp(i18nBlockId, new LexicalReadExpr(icuNode.expression), icuNode.expressionPlaceholder, I18nParamResolutionTime.Postproccessing, null));
19346
- break;
19347
- }
19348
- }
19349
- }
19350
- }
19351
-
19352
19797
  /**
19353
19798
  * Lifts local reference declarations on element-like structures within each view into an entry in
19354
19799
  * the `consts` array for the whole component.
19355
19800
  */
19356
- function phaseLocalRefs(job) {
19801
+ function liftLocalRefs(job) {
19357
19802
  for (const unit of job.units) {
19358
19803
  for (const op of unit.create) {
19359
19804
  switch (op.kind) {
@@ -19383,10 +19828,61 @@ function serializeLocalRefs(refs) {
19383
19828
  return literalArr(constRefs);
19384
19829
  }
19385
19830
 
19831
+ /**
19832
+ * Merge i18n contexts for child i18n blocks into their ancestor root contexts.
19833
+ */
19834
+ function mergeI18nContexts(job) {
19835
+ // Record all of the i18n and extracted message ops for use later.
19836
+ const i18nOps = new Map();
19837
+ const i18nContexts = new Map();
19838
+ for (const unit of job.units) {
19839
+ for (const op of unit.create) {
19840
+ switch (op.kind) {
19841
+ case OpKind.I18nStart:
19842
+ if (!op.context) {
19843
+ throw Error('I18n op should have its context set.');
19844
+ }
19845
+ i18nOps.set(op.xref, op);
19846
+ break;
19847
+ case OpKind.I18nContext:
19848
+ i18nContexts.set(op.xref, op);
19849
+ break;
19850
+ }
19851
+ }
19852
+ }
19853
+ // For each non-root i18n op, merge its context into the root i18n op's context.
19854
+ for (const childI18nOp of i18nOps.values()) {
19855
+ if (childI18nOp.xref !== childI18nOp.root) {
19856
+ const childContext = i18nContexts.get(childI18nOp.context);
19857
+ const rootI18nOp = i18nOps.get(childI18nOp.root);
19858
+ const rootContext = i18nContexts.get(rootI18nOp.context);
19859
+ mergeParams(rootContext.params, childContext.params);
19860
+ mergeParams(rootContext.postprocessingParams, childContext.postprocessingParams);
19861
+ }
19862
+ }
19863
+ }
19864
+ /**
19865
+ * Merges the params in the `from` map to into the `to` map.
19866
+ */
19867
+ function mergeParams(to, from) {
19868
+ for (const [placeholder, fromValues] of from) {
19869
+ const toValues = to.get(placeholder) || [];
19870
+ // TODO(mmalerba): Child element close tag params should be prepended to maintain the same order
19871
+ // as TemplateDefinitionBuilder. Can be cleaned up when compatibility is no longer required.
19872
+ const flags = fromValues[0].flags;
19873
+ if ((flags & I18nParamValueFlags.CloseTag) && !(flags & I18nParamValueFlags.OpenTag)) {
19874
+ to.set(placeholder, [...fromValues, ...toValues]);
19875
+ }
19876
+ else {
19877
+ to.set(placeholder, [...toValues, ...fromValues]);
19878
+ }
19879
+ }
19880
+ }
19881
+
19386
19882
  /**
19387
19883
  * Change namespaces between HTML, SVG and MathML, depending on the next element.
19388
19884
  */
19389
- function phaseNamespace(job) {
19885
+ function emitNamespaceChanges(job) {
19390
19886
  for (const unit of job.units) {
19391
19887
  let activeNamespace = Namespace.HTML;
19392
19888
  for (const op of unit.create) {
@@ -19486,8 +19982,8 @@ function hyphenate(value) {
19486
19982
  * This includes propagating those names into any `ir.ReadVariableExpr`s of those variables, so that
19487
19983
  * the reads can be emitted correctly.
19488
19984
  */
19489
- function phaseNaming(cpl) {
19490
- addNamesToView(cpl.root, cpl.componentName, { index: 0 }, cpl.compatibility === CompatibilityMode.TemplateDefinitionBuilder);
19985
+ function nameFunctionsAndVariables(job) {
19986
+ addNamesToView(job.root, job.componentName, { index: 0 }, job.compatibility === CompatibilityMode.TemplateDefinitionBuilder);
19491
19987
  }
19492
19988
  function addNamesToView(unit, baseName, state, compatibility) {
19493
19989
  if (unit.fnName === null) {
@@ -19508,7 +20004,7 @@ function addNamesToView(unit, baseName, state, compatibility) {
19508
20004
  if (op.handlerFnName !== null) {
19509
20005
  break;
19510
20006
  }
19511
- if (!op.hostListener && op.targetSlot === null) {
20007
+ if (!op.hostListener && op.targetSlot.slot === null) {
19512
20008
  throw new Error(`Expected a slot to be assigned`);
19513
20009
  }
19514
20010
  let animation = '';
@@ -19520,7 +20016,7 @@ function addNamesToView(unit, baseName, state, compatibility) {
19520
20016
  op.handlerFnName = `${baseName}_${animation}${op.name}_HostBindingHandler`;
19521
20017
  }
19522
20018
  else {
19523
- op.handlerFnName = `${unit.fnName}_${op.tag.replace('-', '_')}_${animation}${op.name}_${op.targetSlot}_listener`;
20019
+ op.handlerFnName = `${unit.fnName}_${op.tag.replace('-', '_')}_${animation}${op.name}_${op.targetSlot.slot}_listener`;
19524
20020
  }
19525
20021
  op.handlerFnName = sanitizeIdentifier(op.handlerFnName);
19526
20022
  break;
@@ -19531,28 +20027,27 @@ function addNamesToView(unit, baseName, state, compatibility) {
19531
20027
  if (!(unit instanceof ViewCompilationUnit)) {
19532
20028
  throw new Error(`AssertionError: must be compiling a component`);
19533
20029
  }
19534
- if (op.slot === null) {
20030
+ if (op.handle.slot === null) {
19535
20031
  throw new Error(`Expected slot to be assigned`);
19536
20032
  }
19537
20033
  if (op.emptyView !== null) {
19538
20034
  const emptyView = unit.job.views.get(op.emptyView);
19539
20035
  // Repeater empty view function is at slot +2 (metadata is in the first slot).
19540
- addNamesToView(emptyView, `${baseName}_${prefixWithNamespace(`${op.tag}Empty`, op.namespace)}_${op.slot + 2}`, state, compatibility);
20036
+ addNamesToView(emptyView, `${baseName}_${`${op.functionNameSuffix}Empty`}_${op.handle.slot + 2}`, state, compatibility);
19541
20037
  }
19542
- const repeaterToken = op.tag === null ? '' : '_' + prefixWithNamespace(op.tag, op.namespace);
19543
20038
  // Repeater primary view function is at slot +1 (metadata is in the first slot).
19544
- addNamesToView(unit.job.views.get(op.xref), `${baseName}${repeaterToken}_${op.slot + 1}`, state, compatibility);
20039
+ addNamesToView(unit.job.views.get(op.xref), `${baseName}_${op.functionNameSuffix}_${op.handle.slot + 1}`, state, compatibility);
19545
20040
  break;
19546
20041
  case OpKind.Template:
19547
20042
  if (!(unit instanceof ViewCompilationUnit)) {
19548
20043
  throw new Error(`AssertionError: must be compiling a component`);
19549
20044
  }
19550
20045
  const childView = unit.job.views.get(op.xref);
19551
- if (op.slot === null) {
20046
+ if (op.handle.slot === null) {
19552
20047
  throw new Error(`Expected slot to be assigned`);
19553
20048
  }
19554
- const tagToken = op.tag === null ? '' : '_' + prefixWithNamespace(op.tag, op.namespace);
19555
- addNamesToView(childView, `${baseName}${tagToken}_${op.slot}`, state, compatibility);
20049
+ const suffix = op.functionNameSuffix.length === 0 ? '' : `_${op.functionNameSuffix}`;
20050
+ addNamesToView(childView, `${baseName}${suffix}_${op.handle.slot}`, state, compatibility);
19556
20051
  break;
19557
20052
  case OpKind.StyleProp:
19558
20053
  op.name = normalizeStylePropName(op.name);
@@ -19628,7 +20123,7 @@ function stripImportant(name) {
19628
20123
  * is, the call is purely side-effectful).
19629
20124
  * * No operations in between them uses the implicit context.
19630
20125
  */
19631
- function phaseMergeNextContext(job) {
20126
+ function mergeNextContextExpressions(job) {
19632
20127
  for (const unit of job.units) {
19633
20128
  for (const op of unit.create) {
19634
20129
  if (op.kind === OpKind.Listener) {
@@ -19684,7 +20179,7 @@ const CONTAINER_TAG = 'ng-container';
19684
20179
  /**
19685
20180
  * Replace an `Element` or `ElementStart` whose tag is `ng-container` with a specific op.
19686
20181
  */
19687
- function phaseNgContainer(job) {
20182
+ function generateNgContainerOps(job) {
19688
20183
  for (const unit of job.units) {
19689
20184
  const updatedElementXrefs = new Set();
19690
20185
  for (const op of unit.create) {
@@ -19716,7 +20211,7 @@ function lookupElement(elements, xref) {
19716
20211
  * all descendants of that container. Therefore, we must emit `disableBindings` and `enableBindings`
19717
20212
  * instructions for every such container.
19718
20213
  */
19719
- function phaseNonbindable(job) {
20214
+ function disableBindings$1(job) {
19720
20215
  const elements = new Map();
19721
20216
  for (const view of job.units) {
19722
20217
  for (const op of view.create) {
@@ -19740,7 +20235,13 @@ function phaseNonbindable(job) {
19740
20235
  }
19741
20236
  }
19742
20237
 
19743
- function phaseNullishCoalescing(job) {
20238
+ /**
20239
+ * Nullish coalescing expressions such as `a ?? b` have different semantics in Angular templates as
20240
+ * compared to JavaScript. In particular, they default to `null` instead of `undefined`. Therefore,
20241
+ * we replace them with ternary expressions, assigning temporaries as needed to avoid re-evaluating
20242
+ * the same sub-expression multiple times.
20243
+ */
20244
+ function generateNullishCoalesceExpressions(job) {
19744
20245
  for (const unit of job.units) {
19745
20246
  for (const op of unit.ops()) {
19746
20247
  transformExpressionsInOp(op, expr => {
@@ -19798,7 +20299,12 @@ const handledOpKinds = new Set([
19798
20299
  OpKind.Listener, OpKind.StyleMap, OpKind.ClassMap, OpKind.StyleProp,
19799
20300
  OpKind.ClassProp, OpKind.Property, OpKind.HostProperty, OpKind.Attribute
19800
20301
  ]);
19801
- function phaseOrdering(job) {
20302
+ /**
20303
+ * Many type of operations have ordering constraints that must be respected. For example, a
20304
+ * `ClassMap` instruction must be ordered after a `StyleMap` instruction, in order to have
20305
+ * predictable semantics that match TemplateDefinitionBuilder and don't break applications.
20306
+ */
20307
+ function orderOps(job) {
19802
20308
  for (const unit of job.units) {
19803
20309
  // First, we pull out ops that need to be ordered. Then, when we encounter an op that shouldn't
19804
20310
  // be reordered, put the ones we've pulled so far back in the correct order. Finally, if we
@@ -19860,8 +20366,8 @@ function keepLast(ops) {
19860
20366
  * Parses extracted style and class attributes into separate ExtractedAttributeOps per style or
19861
20367
  * class property.
19862
20368
  */
19863
- function phaseParseExtractedStyles(cpl) {
19864
- for (const unit of cpl.units) {
20369
+ function parseExtractedStyles(job) {
20370
+ for (const unit of job.units) {
19865
20371
  for (const op of unit.create) {
19866
20372
  if (op.kind === OpKind.ExtractedAttribute && op.bindingKind === BindingKind.Attribute &&
19867
20373
  isStringLiteral(op.expression)) {
@@ -19888,7 +20394,7 @@ function phaseParseExtractedStyles(cpl) {
19888
20394
  * Attributes of `ng-content` named 'select' are specifically removed, because they control which
19889
20395
  * content matches as a property of the `projection`, and are not a plain attribute.
19890
20396
  */
19891
- function phaseRemoveContentSelectors(job) {
20397
+ function removeContentSelectors(job) {
19892
20398
  for (const unit of job.units) {
19893
20399
  const elements = createOpXrefMap(unit);
19894
20400
  for (const op of unit.update) {
@@ -19914,7 +20420,14 @@ function lookupInXrefMap(map, xref) {
19914
20420
  return el;
19915
20421
  }
19916
20422
 
19917
- function phasePipeCreation(job) {
20423
+ /**
20424
+ * This phase generates pipe creation instructions. We do this based on the pipe bindings found in
20425
+ * the update block, in the order we see them.
20426
+ *
20427
+ * When not in compatibility mode, we can simply group all these creation instructions together, to
20428
+ * maximize chaining opportunities.
20429
+ */
20430
+ function createPipes(job) {
19918
20431
  for (const unit of job.units) {
19919
20432
  processPipeBindingsInView(unit);
19920
20433
  }
@@ -19931,21 +20444,19 @@ function processPipeBindingsInView(unit) {
19931
20444
  if (flags & VisitorContextFlag.InChildOperation) {
19932
20445
  throw new Error(`AssertionError: pipe bindings should not appear in child expressions`);
19933
20446
  }
19934
- // This update op must be associated with a create op that consumes a slot (either by
19935
- // depending on the ambient context of `target`, or merely referencing that create op via
19936
- // `target`).
19937
- if (!hasDependsOnSlotContextTrait(updateOp) &&
19938
- !hasUsesSlotIndexTrait(updateOp)) {
19939
- throw new Error(`AssertionError: pipe binding associated with non-slot operation ${OpKind[updateOp.kind]}`);
19940
- }
19941
20447
  if (unit.job.compatibility) {
20448
+ // TODO: We can delete this cast and check once compatibility mode is removed.
20449
+ const slotHandle = updateOp.target;
20450
+ if (slotHandle == undefined) {
20451
+ throw new Error(`AssertionError: expected slot handle to be assigned for pipe creation`);
20452
+ }
19942
20453
  addPipeToCreationBlock(unit, updateOp.target, expr);
19943
20454
  }
19944
20455
  else {
19945
20456
  // When not in compatibility mode, we just add the pipe to the end of the create block. This
19946
20457
  // is not only simpler and faster, but allows more chaining opportunities for other
19947
20458
  // instructions.
19948
- unit.create.push(createPipeOp(expr.target, expr.name));
20459
+ unit.create.push(createPipeOp(expr.target, expr.targetSlot, expr.name));
19949
20460
  }
19950
20461
  });
19951
20462
  }
@@ -19966,7 +20477,7 @@ function addPipeToCreationBlock(unit, afterTargetXref, binding) {
19966
20477
  while (op.next.kind === OpKind.Pipe) {
19967
20478
  op = op.next;
19968
20479
  }
19969
- const pipe = createPipeOp(binding.target, binding.name);
20480
+ const pipe = createPipeOp(binding.target, binding.targetSlot, binding.name);
19970
20481
  OpList.insertBefore(pipe, op.next);
19971
20482
  // This completes adding the pipe to the creation block.
19972
20483
  return;
@@ -19975,7 +20486,11 @@ function addPipeToCreationBlock(unit, afterTargetXref, binding) {
19975
20486
  throw new Error(`AssertionError: unable to find insertion point for pipe ${binding.name}`);
19976
20487
  }
19977
20488
 
19978
- function phasePipeVariadic(job) {
20489
+ /**
20490
+ * Pipes that accept more than 4 arguments are variadic, and are handled with a different runtime
20491
+ * instruction.
20492
+ */
20493
+ function createVariadicPipes(job) {
19979
20494
  for (const unit of job.units) {
19980
20495
  for (const op of unit.update) {
19981
20496
  transformExpressionsInOp(op, expr => {
@@ -19986,7 +20501,7 @@ function phasePipeVariadic(job) {
19986
20501
  if (expr.args.length <= 4) {
19987
20502
  return expr;
19988
20503
  }
19989
- return new PipeBindingVariadicExpr(expr.target, expr.name, literalArr(expr.args), expr.args.length);
20504
+ return new PipeBindingVariadicExpr(expr.target, expr.targetSlot, expr.name, literalArr(expr.args), expr.args.length);
19990
20505
  }, VisitorContextFlag.None);
19991
20506
  }
19992
20507
  }
@@ -19994,9 +20509,11 @@ function phasePipeVariadic(job) {
19994
20509
 
19995
20510
  /**
19996
20511
  * Propagate i18n blocks down through child templates that act as placeholders in the root i18n
19997
- * message.
20512
+ * message. Specifically, perform an in-order traversal of all the views, and add i18nStart/i18nEnd
20513
+ * op pairs into descending views. Also, assign an increasing sub-template index to each
20514
+ * descending view.
19998
20515
  */
19999
- function phasePropagateI18nBlocks(job) {
20516
+ function propagateI18nBlocks(job) {
20000
20517
  propagateI18nBlocksToTemplates(job.root, 0);
20001
20518
  }
20002
20519
  /**
@@ -20041,7 +20558,7 @@ function wrapTemplateWithI18n(unit, parentI18n) {
20041
20558
  }
20042
20559
  }
20043
20560
 
20044
- function phasePureFunctionExtraction(job) {
20561
+ function extractPureFunctions(job) {
20045
20562
  for (const view of job.units) {
20046
20563
  for (const op of view.ops()) {
20047
20564
  visitExpressionsInOp(op, expr => {
@@ -20086,9 +20603,9 @@ class PureFunctionConstant extends GenericKeyFn {
20086
20603
  }
20087
20604
  }
20088
20605
 
20089
- function phasePureLiteralStructures(job) {
20090
- for (const view of job.units) {
20091
- for (const op of view.update) {
20606
+ function generatePureLiteralStructures(job) {
20607
+ for (const unit of job.units) {
20608
+ for (const op of unit.update) {
20092
20609
  transformExpressionsInOp(op, (expr, flags) => {
20093
20610
  if (flags & VisitorContextFlag.InChildOperation) {
20094
20611
  return expr;
@@ -20170,13 +20687,21 @@ function elementContainer(slot, constIndex, localRefIndex, sourceSpan) {
20170
20687
  function elementContainerEnd() {
20171
20688
  return call(Identifiers.elementContainerEnd, [], null);
20172
20689
  }
20173
- function template(slot, templateFnRef, decls, vars, tag, constIndex, sourceSpan) {
20174
- const args = [literal(slot), templateFnRef, literal(decls), literal(vars)];
20175
- if (tag !== null || constIndex !== null) {
20176
- args.push(literal(tag));
20177
- if (constIndex !== null) {
20178
- args.push(literal(constIndex));
20179
- }
20690
+ function template(slot, templateFnRef, decls, vars, tag, constIndex, localRefs, sourceSpan) {
20691
+ const args = [
20692
+ literal(slot),
20693
+ templateFnRef,
20694
+ literal(decls),
20695
+ literal(vars),
20696
+ literal(tag),
20697
+ literal(constIndex),
20698
+ ];
20699
+ if (localRefs !== null) {
20700
+ args.push(literal(localRefs));
20701
+ args.push(importExpr(Identifiers.templateRefExtractor));
20702
+ }
20703
+ while (args[args.length - 1].isEquivalent(NULL_EXPR)) {
20704
+ args.pop();
20180
20705
  }
20181
20706
  return call(Identifiers.templateCreate, args, sourceSpan);
20182
20707
  }
@@ -20246,24 +20771,48 @@ function text(slot, initialValue, sourceSpan) {
20246
20771
  }
20247
20772
  return call(Identifiers.text, args, sourceSpan);
20248
20773
  }
20249
- function defer(selfSlot, primarySlot, dependencyResolverFn, loadingSlot, placeholderSlot, errorSlot, loadingConfigIndex, placeholderConfigIndex, sourceSpan) {
20774
+ function defer(selfSlot, primarySlot, dependencyResolverFn, loadingSlot, placeholderSlot, errorSlot, loadingConfig, placeholderConfig, enableTimerScheduling, sourceSpan) {
20250
20775
  const args = [
20251
20776
  literal(selfSlot),
20252
20777
  literal(primarySlot),
20253
- literal(dependencyResolverFn),
20778
+ dependencyResolverFn ?? literal(null),
20254
20779
  literal(loadingSlot),
20255
20780
  literal(placeholderSlot),
20256
20781
  literal(errorSlot),
20257
- literal(loadingConfigIndex),
20258
- literal(placeholderConfigIndex),
20782
+ loadingConfig ?? literal(null),
20783
+ placeholderConfig ?? literal(null),
20784
+ enableTimerScheduling ? importExpr(Identifiers.deferEnableTimerScheduling) : literal(null),
20259
20785
  ];
20260
- while (args[args.length - 1].value === null) {
20786
+ let expr;
20787
+ while ((expr = args[args.length - 1]) !== null && expr instanceof LiteralExpr &&
20788
+ expr.value === null) {
20261
20789
  args.pop();
20262
20790
  }
20263
20791
  return call(Identifiers.defer, args, sourceSpan);
20264
20792
  }
20265
- function deferOn(sourceSpan) {
20266
- return call(Identifiers.deferOnIdle, [], sourceSpan);
20793
+ const deferTriggerToR3TriggerInstructionsMap = new Map([
20794
+ [DeferTriggerKind.Idle, [Identifiers.deferOnIdle, Identifiers.deferPrefetchOnIdle]],
20795
+ [
20796
+ DeferTriggerKind.Immediate,
20797
+ [Identifiers.deferOnImmediate, Identifiers.deferPrefetchOnImmediate]
20798
+ ],
20799
+ [DeferTriggerKind.Timer, [Identifiers.deferOnTimer, Identifiers.deferPrefetchOnTimer]],
20800
+ [DeferTriggerKind.Hover, [Identifiers.deferOnHover, Identifiers.deferPrefetchOnHover]],
20801
+ [
20802
+ DeferTriggerKind.Interaction,
20803
+ [Identifiers.deferOnInteraction, Identifiers.deferPrefetchOnInteraction]
20804
+ ],
20805
+ [
20806
+ DeferTriggerKind.Viewport, [Identifiers.deferOnViewport, Identifiers.deferPrefetchOnViewport]
20807
+ ],
20808
+ ]);
20809
+ function deferOn(trigger, args, prefetch, sourceSpan) {
20810
+ const instructions = deferTriggerToR3TriggerInstructionsMap.get(trigger);
20811
+ if (instructions === undefined) {
20812
+ throw new Error(`Unable to determine instruction for trigger ${trigger}`);
20813
+ }
20814
+ const instructionToCall = prefetch ? instructions[1] : instructions[0];
20815
+ return call(instructionToCall, args.map(a => literal(a)), sourceSpan);
20267
20816
  }
20268
20817
  function projectionDef(def) {
20269
20818
  return call(Identifiers.projectionDef, def ? [def] : [], null);
@@ -20285,20 +20834,20 @@ function i18nStart(slot, constIndex, subTemplateIndex) {
20285
20834
  }
20286
20835
  return call(Identifiers.i18nStart, args, null);
20287
20836
  }
20288
- function repeaterCreate(slot, viewFnName, decls, vars, trackByFn, trackByUsesComponentInstance, emptyViewFnName, emptyDecls, emptyVars, sourceSpan) {
20289
- let args = [
20837
+ function repeaterCreate(slot, viewFnName, decls, vars, tag, constIndex, trackByFn, trackByUsesComponentInstance, emptyViewFnName, emptyDecls, emptyVars, sourceSpan) {
20838
+ const args = [
20290
20839
  literal(slot),
20291
20840
  variable(viewFnName),
20292
20841
  literal(decls),
20293
20842
  literal(vars),
20843
+ literal(tag),
20844
+ literal(constIndex),
20294
20845
  trackByFn,
20295
20846
  ];
20296
20847
  if (trackByUsesComponentInstance || emptyViewFnName !== null) {
20297
20848
  args.push(literal(trackByUsesComponentInstance));
20298
20849
  if (emptyViewFnName !== null) {
20299
- args.push(variable(emptyViewFnName));
20300
- args.push(literal(emptyDecls));
20301
- args.push(literal(emptyVars));
20850
+ args.push(variable(emptyViewFnName), literal(emptyDecls), literal(emptyVars));
20302
20851
  }
20303
20852
  }
20304
20853
  return call(Identifiers.repeaterCreate, args, sourceSpan);
@@ -20306,6 +20855,9 @@ function repeaterCreate(slot, viewFnName, decls, vars, trackByFn, trackByUsesCom
20306
20855
  function repeater(metadataSlot, collection, sourceSpan) {
20307
20856
  return call(Identifiers.repeater, [literal(metadataSlot), collection], sourceSpan);
20308
20857
  }
20858
+ function deferWhen(prefetch, expr, sourceSpan) {
20859
+ return call(prefetch ? Identifiers.deferPrefetchWhen : Identifiers.deferWhen, [expr], sourceSpan);
20860
+ }
20309
20861
  function i18n(slot, constIndex, subTemplateIndex) {
20310
20862
  const args = [literal(slot), literal(constIndex)];
20311
20863
  if (subTemplateIndex) {
@@ -20662,8 +21214,8 @@ const sanitizerIdentifierMap = new Map([
20662
21214
  * structures. After reification, the create/update operation lists of all views should only contain
20663
21215
  * `ir.StatementOp`s (which wrap generated `o.Statement`s).
20664
21216
  */
20665
- function phaseReify(cpl) {
20666
- for (const unit of cpl.units) {
21217
+ function reify(job) {
21218
+ for (const unit of job.units) {
20667
21219
  reifyCreateOperations(unit, unit.create);
20668
21220
  reifyUpdateOperations(unit, unit.update);
20669
21221
  }
@@ -20673,41 +21225,44 @@ function reifyCreateOperations(unit, ops) {
20673
21225
  transformExpressionsInOp(op, reifyIrExpression, VisitorContextFlag.None);
20674
21226
  switch (op.kind) {
20675
21227
  case OpKind.Text:
20676
- OpList.replace(op, text(op.slot, op.initialValue, op.sourceSpan));
21228
+ OpList.replace(op, text(op.handle.slot, op.initialValue, op.sourceSpan));
20677
21229
  break;
20678
21230
  case OpKind.ElementStart:
20679
- OpList.replace(op, elementStart(op.slot, op.tag, op.attributes, op.localRefs, op.sourceSpan));
21231
+ OpList.replace(op, elementStart(op.handle.slot, op.tag, op.attributes, op.localRefs, op.sourceSpan));
20680
21232
  break;
20681
21233
  case OpKind.Element:
20682
- OpList.replace(op, element(op.slot, op.tag, op.attributes, op.localRefs, op.sourceSpan));
21234
+ OpList.replace(op, element(op.handle.slot, op.tag, op.attributes, op.localRefs, op.sourceSpan));
20683
21235
  break;
20684
21236
  case OpKind.ElementEnd:
20685
21237
  OpList.replace(op, elementEnd(op.sourceSpan));
20686
21238
  break;
20687
21239
  case OpKind.ContainerStart:
20688
- OpList.replace(op, elementContainerStart(op.slot, op.attributes, op.localRefs, op.sourceSpan));
21240
+ OpList.replace(op, elementContainerStart(op.handle.slot, op.attributes, op.localRefs, op.sourceSpan));
20689
21241
  break;
20690
21242
  case OpKind.Container:
20691
- OpList.replace(op, elementContainer(op.slot, op.attributes, op.localRefs, op.sourceSpan));
21243
+ OpList.replace(op, elementContainer(op.handle.slot, op.attributes, op.localRefs, op.sourceSpan));
20692
21244
  break;
20693
21245
  case OpKind.ContainerEnd:
20694
21246
  OpList.replace(op, elementContainerEnd());
20695
21247
  break;
20696
21248
  case OpKind.I18nStart:
20697
- OpList.replace(op, i18nStart(op.slot, op.messageIndex, op.subTemplateIndex));
21249
+ OpList.replace(op, i18nStart(op.handle.slot, op.messageIndex, op.subTemplateIndex));
20698
21250
  break;
20699
21251
  case OpKind.I18nEnd:
20700
21252
  OpList.replace(op, i18nEnd());
20701
21253
  break;
20702
21254
  case OpKind.I18n:
20703
- OpList.replace(op, i18n(op.slot, op.messageIndex, op.subTemplateIndex));
21255
+ OpList.replace(op, i18n(op.handle.slot, op.messageIndex, op.subTemplateIndex));
20704
21256
  break;
20705
21257
  case OpKind.Template:
20706
21258
  if (!(unit instanceof ViewCompilationUnit)) {
20707
21259
  throw new Error(`AssertionError: must be compiling a component`);
20708
21260
  }
21261
+ if (Array.isArray(op.localRefs)) {
21262
+ throw new Error(`AssertionError: local refs array should have been extracted into a constant`);
21263
+ }
20709
21264
  const childView = unit.job.views.get(op.xref);
20710
- OpList.replace(op, template(op.slot, variable(childView.fnName), childView.decls, childView.vars, op.block ? null : op.tag, op.attributes, op.sourceSpan));
21265
+ OpList.replace(op, template(op.handle.slot, variable(childView.fnName), childView.decls, childView.vars, op.tag, op.attributes, op.localRefs, op.sourceSpan));
20711
21266
  break;
20712
21267
  case OpKind.DisableBindings:
20713
21268
  OpList.replace(op, disableBindings());
@@ -20716,7 +21271,7 @@ function reifyCreateOperations(unit, ops) {
20716
21271
  OpList.replace(op, enableBindings());
20717
21272
  break;
20718
21273
  case OpKind.Pipe:
20719
- OpList.replace(op, pipe(op.slot, op.name));
21274
+ OpList.replace(op, pipe(op.handle.slot, op.name));
20720
21275
  break;
20721
21276
  case OpKind.Listener:
20722
21277
  const listenerFn = reifyListenerHandler(unit, op.handlerFnName, op.handlerOps, op.consumesDollarEvent);
@@ -20745,25 +21300,45 @@ function reifyCreateOperations(unit, ops) {
20745
21300
  }
20746
21301
  break;
20747
21302
  case OpKind.Defer:
20748
- 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));
20749
- break;
20750
- case OpKind.DeferSecondaryBlock:
20751
- OpList.remove(op);
21303
+ const timerScheduling = !!op.loadingMinimumTime || !!op.loadingAfterTime || !!op.placeholderMinimumTime;
21304
+ OpList.replace(op, defer(op.handle.slot, op.mainSlot.slot, op.resolverFn, op.loadingSlot?.slot ?? null, op.placeholderSlot?.slot ?? null, op.errorSlot?.slot ?? null, op.loadingConfig, op.placeholderConfig, timerScheduling, op.sourceSpan));
20752
21305
  break;
20753
21306
  case OpKind.DeferOn:
20754
- OpList.replace(op, deferOn(op.sourceSpan));
21307
+ let args = [];
21308
+ switch (op.trigger.kind) {
21309
+ case DeferTriggerKind.Idle:
21310
+ case DeferTriggerKind.Immediate:
21311
+ break;
21312
+ case DeferTriggerKind.Timer:
21313
+ args = [op.trigger.delay];
21314
+ break;
21315
+ case DeferTriggerKind.Interaction:
21316
+ case DeferTriggerKind.Hover:
21317
+ case DeferTriggerKind.Viewport:
21318
+ if (op.trigger.targetSlot?.slot == null || op.trigger.targetSlotViewSteps === null) {
21319
+ throw new Error(`Slot or view steps not set in trigger reification for trigger kind ${op.trigger.kind}`);
21320
+ }
21321
+ args = [op.trigger.targetSlot.slot];
21322
+ if (op.trigger.targetSlotViewSteps !== 0) {
21323
+ args.push(op.trigger.targetSlotViewSteps);
21324
+ }
21325
+ break;
21326
+ default:
21327
+ throw new Error(`AssertionError: Unsupported reification of defer trigger kind ${op.trigger.kind}`);
21328
+ }
21329
+ OpList.replace(op, deferOn(op.trigger.kind, args, op.prefetch, op.sourceSpan));
20755
21330
  break;
20756
21331
  case OpKind.ProjectionDef:
20757
21332
  OpList.replace(op, projectionDef(op.def));
20758
21333
  break;
20759
21334
  case OpKind.Projection:
20760
- if (op.slot === null) {
21335
+ if (op.handle.slot === null) {
20761
21336
  throw new Error('No slot was assigned for project instruction');
20762
21337
  }
20763
- OpList.replace(op, projection(op.slot, op.projectionSlotIndex, op.attributes, op.sourceSpan));
21338
+ OpList.replace(op, projection(op.handle.slot, op.projectionSlotIndex, op.attributes, op.sourceSpan));
20764
21339
  break;
20765
21340
  case OpKind.RepeaterCreate:
20766
- if (op.slot === null) {
21341
+ if (op.handle.slot === null) {
20767
21342
  throw new Error('No slot was assigned for repeater instruction');
20768
21343
  }
20769
21344
  if (!(unit instanceof ViewCompilationUnit)) {
@@ -20788,7 +21363,7 @@ function reifyCreateOperations(unit, ops) {
20788
21363
  emptyDecls = emptyView.decls;
20789
21364
  emptyVars = emptyView.vars;
20790
21365
  }
20791
- OpList.replace(op, repeaterCreate(op.slot, repeaterView.fnName, op.decls, op.vars, op.trackByFn, op.usesComponentInstance, emptyViewFnName, emptyDecls, emptyVars, op.sourceSpan));
21366
+ OpList.replace(op, repeaterCreate(op.handle.slot, repeaterView.fnName, op.decls, op.vars, op.tag, op.attributes, op.trackByFn, op.usesComponentInstance, emptyViewFnName, emptyDecls, emptyVars, op.sourceSpan));
20792
21367
  break;
20793
21368
  case OpKind.Statement:
20794
21369
  // Pass statement operations directly through.
@@ -20844,7 +21419,7 @@ function reifyUpdateOperations(_unit, ops) {
20844
21419
  OpList.replace(op, i18nExp(op.expression, op.sourceSpan));
20845
21420
  break;
20846
21421
  case OpKind.I18nApply:
20847
- OpList.replace(op, i18nApply(op.targetSlot, op.sourceSpan));
21422
+ OpList.replace(op, i18nApply(op.handle.slot, op.sourceSpan));
20848
21423
  break;
20849
21424
  case OpKind.InterpolateText:
20850
21425
  OpList.replace(op, textInterpolate(op.interpolation.strings, op.interpolation.expressions, op.sourceSpan));
@@ -20880,13 +21455,16 @@ function reifyUpdateOperations(_unit, ops) {
20880
21455
  if (op.processed === null) {
20881
21456
  throw new Error(`Conditional test was not set.`);
20882
21457
  }
20883
- if (op.targetSlot === null) {
21458
+ if (op.targetSlot.slot === null) {
20884
21459
  throw new Error(`Conditional slot was not set.`);
20885
21460
  }
20886
- OpList.replace(op, conditional(op.targetSlot, op.processed, op.contextValue, op.sourceSpan));
21461
+ OpList.replace(op, conditional(op.targetSlot.slot, op.processed, op.contextValue, op.sourceSpan));
20887
21462
  break;
20888
21463
  case OpKind.Repeater:
20889
- OpList.replace(op, repeater(op.targetSlot, op.collection, op.sourceSpan));
21464
+ OpList.replace(op, repeater(op.targetSlot.slot, op.collection, op.sourceSpan));
21465
+ break;
21466
+ case OpKind.DeferWhen:
21467
+ OpList.replace(op, deferWhen(op.prefetch, op.expr, op.sourceSpan));
20890
21468
  break;
20891
21469
  case OpKind.Statement:
20892
21470
  // Pass statement operations directly through.
@@ -20904,7 +21482,7 @@ function reifyIrExpression(expr) {
20904
21482
  case ExpressionKind.NextContext:
20905
21483
  return nextContext(expr.steps);
20906
21484
  case ExpressionKind.Reference:
20907
- return reference(expr.targetSlot + 1 + expr.offset);
21485
+ return reference(expr.targetSlot.slot + 1 + expr.offset);
20908
21486
  case ExpressionKind.LexicalRead:
20909
21487
  throw new Error(`AssertionError: unresolved LexicalRead of ${expr.name}`);
20910
21488
  case ExpressionKind.RestoreView:
@@ -20939,13 +21517,13 @@ function reifyIrExpression(expr) {
20939
21517
  case ExpressionKind.PureFunctionParameterExpr:
20940
21518
  throw new Error(`AssertionError: expected PureFunctionParameterExpr to have been extracted`);
20941
21519
  case ExpressionKind.PipeBinding:
20942
- return pipeBind(expr.targetSlot, expr.varOffset, expr.args);
21520
+ return pipeBind(expr.targetSlot.slot, expr.varOffset, expr.args);
20943
21521
  case ExpressionKind.PipeBindingVariadic:
20944
- return pipeBindV(expr.targetSlot, expr.varOffset, expr.args);
21522
+ return pipeBindV(expr.targetSlot.slot, expr.varOffset, expr.args);
20945
21523
  case ExpressionKind.SanitizerExpr:
20946
21524
  return importExpr(sanitizerIdentifierMap.get(expr.fn));
20947
21525
  case ExpressionKind.SlotLiteralExpr:
20948
- return literal(expr.targetSlot);
21526
+ return literal(expr.slot.slot);
20949
21527
  default:
20950
21528
  throw new Error(`AssertionError: Unsupported reification of ir.Expression kind: ${ExpressionKind[expr.kind]}`);
20951
21529
  }
@@ -20975,7 +21553,10 @@ function reifyListenerHandler(unit, name, handlerOps, consumesDollarEvent) {
20975
21553
  return fn(params, handlerStmts, undefined, undefined, name);
20976
21554
  }
20977
21555
 
20978
- function phaseRemoveEmptyBindings(job) {
21556
+ /**
21557
+ * Bidningd with no content can be safely deleted.
21558
+ */
21559
+ function removeEmptyBindings(job) {
20979
21560
  for (const unit of job.units) {
20980
21561
  for (const op of unit.update) {
20981
21562
  switch (op.kind) {
@@ -20995,13 +21576,68 @@ function phaseRemoveEmptyBindings(job) {
20995
21576
  }
20996
21577
  }
20997
21578
 
21579
+ /**
21580
+ * Remove the i18n context ops after they are no longer needed, and null out references to them to
21581
+ * be safe.
21582
+ */
21583
+ function removeI18nContexts(job) {
21584
+ for (const unit of job.units) {
21585
+ for (const op of unit.create) {
21586
+ switch (op.kind) {
21587
+ case OpKind.I18nContext:
21588
+ OpList.remove(op);
21589
+ break;
21590
+ case OpKind.I18nStart:
21591
+ op.context = null;
21592
+ break;
21593
+ }
21594
+ }
21595
+ }
21596
+ }
21597
+
21598
+ /**
21599
+ * Inside the body of a repeater, certain context variables (such as `$first`) are ambiently
21600
+ * available. This phase finds those variable usages, and replaces them with the appropriate
21601
+ * expression.
21602
+ */
21603
+ function generateRepeaterDerivedVars(job) {
21604
+ const repeaters = new Map();
21605
+ for (const unit of job.units) {
21606
+ for (const op of unit.ops()) {
21607
+ if (op.kind === OpKind.RepeaterCreate) {
21608
+ repeaters.set(op.xref, op);
21609
+ }
21610
+ }
21611
+ }
21612
+ for (const unit of job.units) {
21613
+ for (const op of unit.ops()) {
21614
+ transformExpressionsInOp(op, expr => {
21615
+ if (!(expr instanceof DerivedRepeaterVarExpr)) {
21616
+ return expr;
21617
+ }
21618
+ const repeaterOp = repeaters.get(expr.xref);
21619
+ switch (expr.identity) {
21620
+ case DerivedRepeaterVarIdentity.First:
21621
+ return new BinaryOperatorExpr(BinaryOperator.Identical, new LexicalReadExpr(repeaterOp.varNames.$index), literal(0));
21622
+ case DerivedRepeaterVarIdentity.Last:
21623
+ return new BinaryOperatorExpr(BinaryOperator.Identical, new LexicalReadExpr(repeaterOp.varNames.$index), new BinaryOperatorExpr(BinaryOperator.Minus, new LexicalReadExpr(repeaterOp.varNames.$count), literal(1)));
21624
+ case DerivedRepeaterVarIdentity.Even:
21625
+ return new BinaryOperatorExpr(BinaryOperator.Identical, new BinaryOperatorExpr(BinaryOperator.Modulo, new LexicalReadExpr(repeaterOp.varNames.$index), literal(2)), literal(0));
21626
+ case DerivedRepeaterVarIdentity.Odd:
21627
+ return new BinaryOperatorExpr(BinaryOperator.NotIdentical, new BinaryOperatorExpr(BinaryOperator.Modulo, new LexicalReadExpr(repeaterOp.varNames.$index), literal(2)), literal(0));
21628
+ }
21629
+ }, VisitorContextFlag.None);
21630
+ }
21631
+ }
21632
+ }
21633
+
20998
21634
  /**
20999
21635
  * Resolves `ir.ContextExpr` expressions (which represent embedded view or component contexts) to
21000
21636
  * either the `ctx` parameter to component functions (for the current view context) or to variables
21001
21637
  * that store those contexts (for contexts accessed via the `nextContext()` instruction).
21002
21638
  */
21003
- function phaseResolveContexts(cpl) {
21004
- for (const unit of cpl.units) {
21639
+ function resolveContexts(job) {
21640
+ for (const unit of job.units) {
21005
21641
  processLexicalScope$1(unit, unit.create);
21006
21642
  processLexicalScope$1(unit, unit.update);
21007
21643
  }
@@ -21049,13 +21685,13 @@ function processLexicalScope$1(view, ops) {
21049
21685
  * Any variable inside a listener with the name `$event` will be transformed into a output lexical
21050
21686
  * read immediately, and does not participate in any of the normal logic for handling variables.
21051
21687
  */
21052
- function phaseResolveDollarEvent(job) {
21688
+ function resolveDollarEvent(job) {
21053
21689
  for (const unit of job.units) {
21054
- resolveDollarEvent(unit, unit.create);
21055
- resolveDollarEvent(unit, unit.update);
21690
+ transformDollarEvent(unit, unit.create);
21691
+ transformDollarEvent(unit, unit.update);
21056
21692
  }
21057
21693
  }
21058
- function resolveDollarEvent(unit, ops) {
21694
+ function transformDollarEvent(unit, ops) {
21059
21695
  for (const op of ops) {
21060
21696
  if (op.kind === OpKind.Listener) {
21061
21697
  transformExpressionsInOp(op, (expr) => {
@@ -21070,201 +21706,46 @@ function resolveDollarEvent(unit, ops) {
21070
21706
  }
21071
21707
 
21072
21708
  /**
21073
- * The escape sequence used indicate message param values.
21709
+ * Resolve the element placeholders in i18n messages.
21074
21710
  */
21075
- const ESCAPE = '\uFFFD';
21076
- /**
21077
- * Marker used to indicate an element tag.
21078
- */
21079
- const ELEMENT_MARKER = '#';
21080
- /**
21081
- * Marker used to indicate a template tag.
21082
- */
21083
- const TEMPLATE_MARKER = '*';
21084
- /**
21085
- * Marker used to indicate closing of an element or template tag.
21086
- */
21087
- const TAG_CLOSE_MARKER = '/';
21088
- /**
21089
- * Marker used to indicate the sub-template context.
21090
- */
21091
- const CONTEXT_MARKER = ':';
21092
- /**
21093
- * Marker used to indicate the start of a list of values.
21094
- */
21095
- const LIST_START_MARKER = '[';
21096
- /**
21097
- * Marker used to indicate the end of a list of values.
21098
- */
21099
- const LIST_END_MARKER = ']';
21100
- /**
21101
- * Delimiter used to separate multiple values in a list.
21102
- */
21103
- const LIST_DELIMITER = '|';
21104
- /**
21105
- * Flags that describe what an i18n param value. These determine how the value is serialized into
21106
- * the final map.
21107
- */
21108
- var I18nParamValueFlags;
21109
- (function (I18nParamValueFlags) {
21110
- I18nParamValueFlags[I18nParamValueFlags["None"] = 0] = "None";
21111
- /**
21112
- * This value represtents an element tag.
21113
- */
21114
- I18nParamValueFlags[I18nParamValueFlags["ElementTag"] = 1] = "ElementTag";
21115
- /**
21116
- * This value represents a template tag.
21117
- */
21118
- I18nParamValueFlags[I18nParamValueFlags["TemplateTag"] = 2] = "TemplateTag";
21119
- /**
21120
- * This value represents the opening of a tag.
21121
- */
21122
- I18nParamValueFlags[I18nParamValueFlags["OpenTag"] = 4] = "OpenTag";
21123
- /**
21124
- * This value represents the closing of a tag.
21125
- */
21126
- I18nParamValueFlags[I18nParamValueFlags["CloseTag"] = 8] = "CloseTag";
21127
- })(I18nParamValueFlags || (I18nParamValueFlags = {}));
21128
- /**
21129
- * Represents the complete i18n params map for an i18n op.
21130
- */
21131
- class I18nPlaceholderParams {
21132
- constructor() {
21133
- this.values = new Map();
21134
- }
21135
- /**
21136
- * Adds a new value to the params map.
21137
- */
21138
- addValue(placeholder, value, subTemplateIndex, resolutionTime, flags) {
21139
- const placeholderValues = this.values.get(placeholder) ?? [];
21140
- placeholderValues.push({ value, subTemplateIndex, resolutionTime, flags });
21141
- this.values.set(placeholder, placeholderValues);
21142
- }
21143
- /**
21144
- * Saves the params map, in serialized form, into the given i18n op.
21145
- */
21146
- saveToOp(op) {
21147
- for (const [placeholder, placeholderValues] of this.values) {
21148
- // We need to run post-processing for any 1i8n ops that contain parameters with more than
21149
- // one value, even if there are no parameters resolved at post-processing time.
21150
- const creationValues = placeholderValues.filter(({ resolutionTime }) => resolutionTime === I18nParamResolutionTime.Creation);
21151
- if (creationValues.length > 1) {
21152
- op.needsPostprocessing = true;
21153
- }
21154
- // Save creation time params to op.
21155
- const serializedCreationValues = this.serializeValues(creationValues);
21156
- if (serializedCreationValues !== null) {
21157
- op.params.set(placeholder, literal(serializedCreationValues));
21158
- }
21159
- // Save post-processing time params to op.
21160
- const serializedPostprocessingValues = this.serializeValues(placeholderValues.filter(({ resolutionTime }) => resolutionTime === I18nParamResolutionTime.Postproccessing));
21161
- if (serializedPostprocessingValues !== null) {
21162
- op.needsPostprocessing = true;
21163
- op.postprocessingParams.set(placeholder, literal(serializedPostprocessingValues));
21164
- }
21165
- }
21166
- }
21167
- /**
21168
- * Merges another param map into this one.
21169
- */
21170
- merge(other) {
21171
- for (const [placeholder, otherValues] of other.values) {
21172
- const currentValues = this.values.get(placeholder) || [];
21173
- // Child element close tag params should be prepended to maintain the same order as
21174
- // TemplateDefinitionBuilder.
21175
- const flags = otherValues[0].flags;
21176
- if ((flags & I18nParamValueFlags.CloseTag) && !(flags & I18nParamValueFlags.OpenTag)) {
21177
- this.values.set(placeholder, [...otherValues, ...currentValues]);
21178
- }
21179
- else {
21180
- this.values.set(placeholder, [...currentValues, ...otherValues]);
21181
- }
21182
- }
21183
- }
21184
- /**
21185
- * Serializes a list of i18n placeholder values.
21186
- */
21187
- serializeValues(values) {
21188
- if (values.length === 0) {
21189
- return null;
21190
- }
21191
- const serializedValues = values.map(value => this.serializeValue(value));
21192
- return serializedValues.length === 1 ?
21193
- serializedValues[0] :
21194
- `${LIST_START_MARKER}${serializedValues.join(LIST_DELIMITER)}${LIST_END_MARKER}`;
21195
- }
21196
- /**
21197
- * Serializes a single i18n placeholder value.
21198
- */
21199
- serializeValue(value) {
21200
- let tagMarker = '';
21201
- let closeMarker = '';
21202
- if (value.flags & I18nParamValueFlags.ElementTag) {
21203
- tagMarker = ELEMENT_MARKER;
21204
- }
21205
- else if (value.flags & I18nParamValueFlags.TemplateTag) {
21206
- tagMarker = TEMPLATE_MARKER;
21207
- }
21208
- if (tagMarker !== '') {
21209
- closeMarker = value.flags & I18nParamValueFlags.CloseTag ? TAG_CLOSE_MARKER : '';
21210
- }
21211
- const context = value.subTemplateIndex === null ? '' : `${CONTEXT_MARKER}${value.subTemplateIndex}`;
21212
- // Self-closing tags use a special form that concatenates the start and close tag values.
21213
- if ((value.flags & I18nParamValueFlags.OpenTag) &&
21214
- (value.flags & I18nParamValueFlags.CloseTag)) {
21215
- return `${ESCAPE}${tagMarker}${value.value}${context}${ESCAPE}${ESCAPE}${closeMarker}${tagMarker}${value.value}${context}${ESCAPE}`;
21216
- }
21217
- return `${ESCAPE}${closeMarker}${tagMarker}${value.value}${context}${ESCAPE}`;
21218
- }
21219
- }
21220
- /**
21221
- * Resolve the placeholders in i18n messages.
21222
- */
21223
- function phaseResolveI18nPlaceholders(job) {
21224
- const params = new Map();
21225
- const i18nOps = new Map();
21226
- resolvePlaceholders(job, params, i18nOps);
21227
- propagatePlaceholders(params, i18nOps);
21228
- // After colleccting all params, save them to the i18n ops.
21229
- for (const [xref, i18nOpParams] of params) {
21230
- i18nOpParams.saveToOp(i18nOps.get(xref));
21231
- }
21232
- // Validate the root i18n ops have all placeholders filled in.
21233
- for (const op of i18nOps.values()) {
21234
- if (op.xref === op.root) {
21235
- for (const placeholder in op.message.placeholders) {
21236
- if (!op.params.has(placeholder) && !op.postprocessingParams.has(placeholder)) {
21237
- throw Error(`Failed to resolve i18n placeholder: ${placeholder}`);
21238
- }
21711
+ function resolveI18nElementPlaceholders(job) {
21712
+ // Record all of the element and i18n context ops for use later.
21713
+ const i18nContexts = new Map();
21714
+ const elements = new Map();
21715
+ for (const unit of job.units) {
21716
+ for (const op of unit.create) {
21717
+ switch (op.kind) {
21718
+ case OpKind.I18nContext:
21719
+ i18nContexts.set(op.xref, op);
21720
+ break;
21721
+ case OpKind.ElementStart:
21722
+ elements.set(op.xref, op);
21723
+ break;
21239
21724
  }
21240
21725
  }
21241
21726
  }
21242
- }
21243
- /**
21244
- * Resolve placeholders for each i18n op.
21245
- */
21246
- function resolvePlaceholders(job, params, i18nOps) {
21247
21727
  for (const unit of job.units) {
21248
- const elements = new Map();
21249
- let currentI18nOp = null;
21250
- // Record slots for tag name placeholders.
21728
+ // Track the current i18n op and corresponding i18n context op as we step through the creation
21729
+ // IR.
21730
+ let currentOps = null;
21251
21731
  for (const op of unit.create) {
21252
21732
  switch (op.kind) {
21253
21733
  case OpKind.I18nStart:
21254
- i18nOps.set(op.xref, op);
21255
- currentI18nOp = op.kind === OpKind.I18nStart ? op : null;
21734
+ if (!op.context) {
21735
+ throw Error('Could not find i18n context for i18n op');
21736
+ }
21737
+ currentOps = { i18nBlock: op, i18nContext: i18nContexts.get(op.context) };
21256
21738
  break;
21257
21739
  case OpKind.I18nEnd:
21258
- currentI18nOp = null;
21740
+ currentOps = null;
21259
21741
  break;
21260
21742
  case OpKind.ElementStart:
21261
21743
  // For elements with i18n placeholders, record its slot value in the params map under the
21262
21744
  // corresponding tag start placeholder.
21263
21745
  if (op.i18nPlaceholder !== undefined) {
21264
- if (currentI18nOp === null) {
21746
+ if (currentOps === null) {
21265
21747
  throw Error('i18n tag placeholder should only occur inside an i18n block');
21266
21748
  }
21267
- elements.set(op.xref, op);
21268
21749
  const { startName, closeName } = op.i18nPlaceholder;
21269
21750
  let flags = I18nParamValueFlags.ElementTag | I18nParamValueFlags.OpenTag;
21270
21751
  // For self-closing tags, there is no close tag placeholder. Instead, the start tag
@@ -21272,57 +21753,40 @@ function resolvePlaceholders(job, params, i18nOps) {
21272
21753
  if (closeName === '') {
21273
21754
  flags |= I18nParamValueFlags.CloseTag;
21274
21755
  }
21275
- addParam(params, currentI18nOp, startName, op.slot, currentI18nOp.subTemplateIndex, I18nParamResolutionTime.Creation, flags);
21756
+ addParam(currentOps.i18nContext.params, startName, op.handle.slot, currentOps.i18nBlock.subTemplateIndex, flags);
21276
21757
  }
21277
21758
  break;
21278
21759
  case OpKind.ElementEnd:
21760
+ // For elements with i18n placeholders, record its slot value in the params map under the
21761
+ // corresponding tag close placeholder.
21279
21762
  const startOp = elements.get(op.xref);
21280
21763
  if (startOp && startOp.i18nPlaceholder !== undefined) {
21281
- if (currentI18nOp === null) {
21764
+ if (currentOps === null) {
21282
21765
  throw Error('i18n tag placeholder should only occur inside an i18n block');
21283
21766
  }
21284
21767
  const { closeName } = startOp.i18nPlaceholder;
21285
21768
  // Self-closing tags don't have a closing tag placeholder.
21286
21769
  if (closeName !== '') {
21287
- addParam(params, currentI18nOp, closeName, startOp.slot, currentI18nOp.subTemplateIndex, I18nParamResolutionTime.Creation, I18nParamValueFlags.ElementTag | I18nParamValueFlags.CloseTag);
21770
+ addParam(currentOps.i18nContext.params, closeName, startOp.handle.slot, currentOps.i18nBlock.subTemplateIndex, I18nParamValueFlags.ElementTag | I18nParamValueFlags.CloseTag);
21288
21771
  }
21289
21772
  }
21290
21773
  break;
21291
21774
  case OpKind.Template:
21775
+ // For templates with i18n placeholders, record its slot value in the params map under the
21776
+ // corresponding template start and close placeholders.
21292
21777
  if (op.i18nPlaceholder !== undefined) {
21293
- if (currentI18nOp === null) {
21778
+ if (currentOps === null) {
21294
21779
  throw Error('i18n tag placeholder should only occur inside an i18n block');
21295
21780
  }
21296
- const subTemplateIndex = getSubTemplateIndexForTemplateTag(job, currentI18nOp, op);
21297
- addParam(params, currentI18nOp, op.i18nPlaceholder.startName, op.slot, subTemplateIndex, I18nParamResolutionTime.Creation, I18nParamValueFlags.TemplateTag);
21298
- addParam(params, currentI18nOp, op.i18nPlaceholder.closeName, op.slot, subTemplateIndex, I18nParamResolutionTime.Creation, I18nParamValueFlags.TemplateTag | I18nParamValueFlags.CloseTag);
21781
+ const subTemplateIndex = getSubTemplateIndexForTemplateTag(job, currentOps.i18nBlock, op);
21782
+ addParam(currentOps.i18nContext.params, op.i18nPlaceholder.startName, op.handle.slot, subTemplateIndex, I18nParamValueFlags.TemplateTag);
21783
+ addParam(currentOps.i18nContext.params, op.i18nPlaceholder.closeName, op.handle.slot, subTemplateIndex, I18nParamValueFlags.TemplateTag | I18nParamValueFlags.CloseTag);
21299
21784
  }
21300
21785
  break;
21301
21786
  }
21302
21787
  }
21303
- // Fill in values for each of the i18n expression placeholders.
21304
- const i18nBlockPlaceholderIndices = new Map();
21305
- for (const op of unit.update) {
21306
- if (op.kind === OpKind.I18nExpression) {
21307
- const i18nOp = i18nOps.get(op.owner);
21308
- let index = i18nBlockPlaceholderIndices.get(op.owner) || 0;
21309
- if (!i18nOp) {
21310
- throw Error('Cannot find corresponding i18nStart for i18nExpr');
21311
- }
21312
- addParam(params, i18nOp, op.i18nPlaceholder, index++, i18nOp.subTemplateIndex, op.resolutionTime);
21313
- i18nBlockPlaceholderIndices.set(op.owner, index);
21314
- }
21315
- }
21316
21788
  }
21317
21789
  }
21318
- /**
21319
- * Add a param to the params map for the given i18n op.
21320
- */
21321
- function addParam(params, i18nOp, placeholder, value, subTemplateIndex, resolutionTime, flags = I18nParamValueFlags.None) {
21322
- const i18nOpParams = params.get(i18nOp.xref) || new I18nPlaceholderParams();
21323
- i18nOpParams.addValue(placeholder, value, subTemplateIndex, resolutionTime, flags);
21324
- params.set(i18nOp.xref, i18nOpParams);
21325
- }
21326
21790
  /**
21327
21791
  * Get the subTemplateIndex for the given template op. For template ops, use the subTemplateIndex of
21328
21792
  * the child i18n block inside the template.
@@ -21335,15 +21799,49 @@ function getSubTemplateIndexForTemplateTag(job, i18nOp, op) {
21335
21799
  }
21336
21800
  return i18nOp.subTemplateIndex;
21337
21801
  }
21802
+ /** Add a param value to the given params map. */
21803
+ function addParam(params, placeholder, value, subTemplateIndex, flags = I18nParamValueFlags.None) {
21804
+ const values = params.get(placeholder) ?? [];
21805
+ values.push({ value, subTemplateIndex, flags });
21806
+ params.set(placeholder, values);
21807
+ }
21808
+
21338
21809
  /**
21339
- * Propagate placeholders up to their root i18n op.
21810
+ * Resolve the i18n expression placeholders in i18n messages.
21340
21811
  */
21341
- function propagatePlaceholders(params, i18nOps) {
21342
- for (const [xref, opParams] of params) {
21343
- const op = i18nOps.get(xref);
21344
- if (op.xref !== op.root) {
21345
- const rootParams = params.get(op.root) || new I18nPlaceholderParams();
21346
- rootParams.merge(opParams);
21812
+ function resolveI18nExpressionPlaceholders(job) {
21813
+ // Record all of the i18n context ops, and the sub-template index for each i18n op.
21814
+ const subTemplateIndicies = new Map();
21815
+ const i18nContexts = new Map();
21816
+ for (const unit of job.units) {
21817
+ for (const op of unit.create) {
21818
+ switch (op.kind) {
21819
+ case OpKind.I18nStart:
21820
+ subTemplateIndicies.set(op.xref, op.subTemplateIndex);
21821
+ break;
21822
+ case OpKind.I18nContext:
21823
+ i18nContexts.set(op.xref, op);
21824
+ break;
21825
+ }
21826
+ }
21827
+ }
21828
+ // Keep track of the next available expression index per i18n context.
21829
+ const expressionIndices = new Map();
21830
+ for (const unit of job.units) {
21831
+ for (const op of unit.update) {
21832
+ if (op.kind === OpKind.I18nExpression) {
21833
+ const index = expressionIndices.get(op.context) || 0;
21834
+ const i18nContext = i18nContexts.get(op.context);
21835
+ const subTemplateIndex = subTemplateIndicies.get(i18nContext.i18nBlock);
21836
+ // Add the expression index in the appropriate params map.
21837
+ const params = op.resolutionTime === I18nParamResolutionTime.Creation ?
21838
+ i18nContext.params :
21839
+ i18nContext.postprocessingParams;
21840
+ const values = params.get(op.i18nPlaceholder) || [];
21841
+ values.push({ value: index, subTemplateIndex: subTemplateIndex, flags: I18nParamValueFlags.None });
21842
+ params.set(op.i18nPlaceholder, values);
21843
+ expressionIndices.set(op.context, index + 1);
21844
+ }
21347
21845
  }
21348
21846
  }
21349
21847
  }
@@ -21355,8 +21853,8 @@ function propagatePlaceholders(params, i18nOps) {
21355
21853
  * Also matches `ir.RestoreViewExpr` expressions with the variables of their corresponding saved
21356
21854
  * views.
21357
21855
  */
21358
- function phaseResolveNames(cpl) {
21359
- for (const unit of cpl.units) {
21856
+ function resolveNames(job) {
21857
+ for (const unit of job.units) {
21360
21858
  processLexicalScope(unit, unit.create, null);
21361
21859
  processLexicalScope(unit, unit.update, null);
21362
21860
  }
@@ -21457,7 +21955,7 @@ const sanitizers = new Map([
21457
21955
  /**
21458
21956
  * Resolves sanitization functions for ops that need them.
21459
21957
  */
21460
- function phaseResolveSanitizers(job) {
21958
+ function resolveSanitizers(job) {
21461
21959
  for (const unit of job.units) {
21462
21960
  const elements = createOpXrefMap(unit);
21463
21961
  let sanitizerFn;
@@ -21492,21 +21990,26 @@ function isIframeElement$1(op) {
21492
21990
  return op.kind === OpKind.ElementStart && op.tag?.toLowerCase() === 'iframe';
21493
21991
  }
21494
21992
 
21495
- function phaseSaveRestoreView(job) {
21496
- for (const view of job.views.values()) {
21497
- view.create.prepend([
21498
- createVariableOp(view.job.allocateXrefId(), {
21993
+ /**
21994
+ * When inside of a listener, we may need access to one or more enclosing views. Therefore, each
21995
+ * view should save the current view, and each listener must have the ability to restore the
21996
+ * appropriate view. We eagerly generate all save view variables; they will be optimized away later.
21997
+ */
21998
+ function saveAndRestoreView(job) {
21999
+ for (const unit of job.units) {
22000
+ unit.create.prepend([
22001
+ createVariableOp(unit.job.allocateXrefId(), {
21499
22002
  kind: SemanticVariableKind.SavedView,
21500
22003
  name: null,
21501
- view: view.xref,
22004
+ view: unit.xref,
21502
22005
  }, new GetCurrentViewExpr(), VariableFlags.None),
21503
22006
  ]);
21504
- for (const op of view.create) {
22007
+ for (const op of unit.create) {
21505
22008
  if (op.kind !== OpKind.Listener) {
21506
22009
  continue;
21507
22010
  }
21508
22011
  // Embedded views always need the save/restore view operation.
21509
- let needsRestoreView = view !== job.root;
22012
+ let needsRestoreView = unit !== job.root;
21510
22013
  if (!needsRestoreView) {
21511
22014
  for (const handlerOp of op.handlerOps) {
21512
22015
  visitExpressionsInOp(handlerOp, expr => {
@@ -21518,7 +22021,7 @@ function phaseSaveRestoreView(job) {
21518
22021
  }
21519
22022
  }
21520
22023
  if (needsRestoreView) {
21521
- addSaveRestoreViewOperationToListener(view, op);
22024
+ addSaveRestoreViewOperationToListener(unit, op);
21522
22025
  }
21523
22026
  }
21524
22027
  }
@@ -21550,7 +22053,7 @@ function addSaveRestoreViewOperationToListener(unit, op) {
21550
22053
  * This phase is also responsible for counting the number of slots used for each view (its `decls`)
21551
22054
  * and propagating that number into the `Template` operations which declare embedded views.
21552
22055
  */
21553
- function phaseSlotAllocation(job) {
22056
+ function allocateSlots(job) {
21554
22057
  // Map of all declarations in all views within the component which require an assigned slot index.
21555
22058
  // This map needs to be global (across all views within the component) since it's possible to
21556
22059
  // reference a slot from one view from an expression within another (e.g. local references work
@@ -21566,9 +22069,9 @@ function phaseSlotAllocation(job) {
21566
22069
  continue;
21567
22070
  }
21568
22071
  // Assign slots to this declaration starting at the current `slotCount`.
21569
- op.slot = slotCount;
22072
+ op.handle.slot = slotCount;
21570
22073
  // And track its assigned slot in the `slotMap`.
21571
- slotMap.set(op.xref, op.slot);
22074
+ slotMap.set(op.xref, op.handle.slot);
21572
22075
  // Each declaration may use more than 1 slot, so increment `slotCount` to reserve the number
21573
22076
  // of slots required.
21574
22077
  slotCount += op.numSlotsUsed;
@@ -21590,31 +22093,6 @@ function phaseSlotAllocation(job) {
21590
22093
  const childView = job.views.get(op.xref);
21591
22094
  op.decls = childView.decls;
21592
22095
  }
21593
- if (hasUsesSlotIndexTrait(op) && op.target !== null && op.targetSlot === null) {
21594
- if (!slotMap.has(op.target)) {
21595
- // We do expect to find a slot allocated for everything which might be referenced.
21596
- throw new Error(`AssertionError: no slot allocated for ${OpKind[op.kind]} target ${op.target}`);
21597
- }
21598
- op.targetSlot = slotMap.get(op.target);
21599
- }
21600
- // Process all `ir.Expression`s within this view, and look for `usesSlotIndexExprTrait`.
21601
- visitExpressionsInOp(op, expr => {
21602
- if (!isIrExpression(expr)) {
21603
- return;
21604
- }
21605
- if (!hasUsesSlotIndexTrait(expr) || expr.targetSlot !== null) {
21606
- return;
21607
- }
21608
- // The `UsesSlotIndexExprTrait` indicates that this expression references something declared
21609
- // in this component template by its slot index. Use the `target` `ir.XrefId` to find the
21610
- // allocated slot for that declaration in `slotMap`.
21611
- if (!slotMap.has(expr.target)) {
21612
- // We do expect to find a slot allocated for everything which might be referenced.
21613
- throw new Error(`AssertionError: no slot allocated for ${expr.constructor.name} target ${expr.target}`);
21614
- }
21615
- // Record the allocated slot on the expression.
21616
- expr.targetSlot = slotMap.get(expr.target);
21617
- });
21618
22096
  }
21619
22097
  }
21620
22098
  }
@@ -21623,8 +22101,8 @@ function phaseSlotAllocation(job) {
21623
22101
  * Transforms special-case bindings with 'style' or 'class' in their names. Must run before the
21624
22102
  * main binding specialization pass.
21625
22103
  */
21626
- function phaseStyleBindingSpecialization(cpl) {
21627
- for (const unit of cpl.units) {
22104
+ function specializeStyleBindings(job) {
22105
+ for (const unit of job.units) {
21628
22106
  for (const op of unit.update) {
21629
22107
  if (op.kind !== OpKind.Binding) {
21630
22108
  continue;
@@ -21662,8 +22140,8 @@ function phaseStyleBindingSpecialization(cpl) {
21662
22140
  * in the double keyed read `a?.[f()]?.[f()]`, the two function calls have non-overlapping scopes.
21663
22141
  * Implement an algorithm for reuse.
21664
22142
  */
21665
- function phaseTemporaryVariables(cpl) {
21666
- for (const unit of cpl.units) {
22143
+ function generateTemporaryVariables(job) {
22144
+ for (const unit of job.units) {
21667
22145
  unit.create.prepend(generateTemporaries(unit.create));
21668
22146
  unit.update.prepend(generateTemporaries(unit.update));
21669
22147
  }
@@ -21703,40 +22181,178 @@ function generateTemporaries(ops) {
21703
22181
  }
21704
22182
  assignName(defs, expr);
21705
22183
  }
21706
- else if (expr instanceof ReadTemporaryExpr) {
21707
- if (finalReads.get(expr.xref) === expr) {
21708
- released.add(expr.xref);
21709
- count--;
22184
+ else if (expr instanceof ReadTemporaryExpr) {
22185
+ if (finalReads.get(expr.xref) === expr) {
22186
+ released.add(expr.xref);
22187
+ count--;
22188
+ }
22189
+ assignName(defs, expr);
22190
+ }
22191
+ });
22192
+ // Add declarations for the temp vars.
22193
+ generatedStatements.push(...Array.from(new Set(defs.values()))
22194
+ .map(name => createStatementOp(new DeclareVarStmt(name))));
22195
+ opCount++;
22196
+ if (op.kind === OpKind.Listener) {
22197
+ op.handlerOps.prepend(generateTemporaries(op.handlerOps));
22198
+ }
22199
+ }
22200
+ return generatedStatements;
22201
+ }
22202
+ /**
22203
+ * Assigns a name to the temporary variable in the given temporary variable expression.
22204
+ */
22205
+ function assignName(names, expr) {
22206
+ const name = names.get(expr.xref);
22207
+ if (name === undefined) {
22208
+ throw new Error(`Found xref with unassigned name: ${expr.xref}`);
22209
+ }
22210
+ expr.name = name;
22211
+ }
22212
+
22213
+ /**
22214
+ * Generate track functions that need to be extracted to the constant pool. This entails wrapping
22215
+ * them in an arrow (or traditional) function, replacing context reads with `this.`, and storing
22216
+ * them in the constant pool.
22217
+ *
22218
+ * Note that, if a track function was previously optimized, it will not need to be extracted, and
22219
+ * this phase is a no-op.
22220
+ */
22221
+ function generateTrackFns(job) {
22222
+ for (const unit of job.units) {
22223
+ for (const op of unit.create) {
22224
+ if (op.kind !== OpKind.RepeaterCreate) {
22225
+ continue;
22226
+ }
22227
+ if (op.trackByFn !== null) {
22228
+ // The final track function was already set, probably because it was optimized.
22229
+ continue;
22230
+ }
22231
+ // Find all component context reads.
22232
+ let usesComponentContext = false;
22233
+ op.track = transformExpressionsInExpression(op.track, expr => {
22234
+ if (expr instanceof TrackContextExpr) {
22235
+ usesComponentContext = true;
22236
+ return variable('this');
22237
+ }
22238
+ return expr;
22239
+ }, VisitorContextFlag.None);
22240
+ let fn;
22241
+ const fnParams = [new FnParam('$index'), new FnParam('$item')];
22242
+ if (usesComponentContext) {
22243
+ fn = new FunctionExpr(fnParams, [new ReturnStatement(op.track)]);
22244
+ }
22245
+ else {
22246
+ fn = arrowFn(fnParams, op.track);
22247
+ }
22248
+ op.trackByFn = job.pool.getSharedFunctionReference(fn, '_forTrack');
22249
+ }
22250
+ }
22251
+ }
22252
+
22253
+ /**
22254
+ * `track` functions in `for` repeaters can sometimes be "optimized," i.e. transformed into inline
22255
+ * expressions, in lieu of an external function call. For example, tracking by `$index` can be be
22256
+ * optimized into an inline `trackByIndex` reference. This phase checks track expressions for
22257
+ * optimizable cases.
22258
+ */
22259
+ function optimizeTrackFns(job) {
22260
+ for (const unit of job.units) {
22261
+ for (const op of unit.create) {
22262
+ if (op.kind !== OpKind.RepeaterCreate) {
22263
+ continue;
22264
+ }
22265
+ if (op.track instanceof ReadVarExpr && op.track.name === '$index') {
22266
+ // Top-level access of `$index` uses the built in `repeaterTrackByIndex`.
22267
+ op.trackByFn = importExpr(Identifiers.repeaterTrackByIndex);
22268
+ }
22269
+ else if (op.track instanceof ReadVarExpr && op.track.name === '$item') {
22270
+ // Top-level access of the item uses the built in `repeaterTrackByIdentity`.
22271
+ op.trackByFn = importExpr(Identifiers.repeaterTrackByIdentity);
22272
+ }
22273
+ else if (isTrackByFunctionCall(job.root.xref, op.track)) {
22274
+ // Top-level method calls in the form of `fn($index, item)` can be passed in directly.
22275
+ if (op.track.receiver.receiver.view === unit.xref) {
22276
+ // TODO: this may be wrong
22277
+ op.trackByFn = op.track.receiver;
22278
+ }
22279
+ else {
22280
+ // This is a plain method call, but not in the component's root view.
22281
+ // We need to get the component instance, and then call the method on it.
22282
+ op.trackByFn =
22283
+ importExpr(Identifiers.componentInstance).callFn([]).prop(op.track.receiver.name);
22284
+ // Because the context is not avaiable (without a special function), we don't want to
22285
+ // try to resolve it later. Let's get rid of it by overwriting the original track
22286
+ // expression (which won't be used anyway).
22287
+ op.track = op.trackByFn;
21710
22288
  }
21711
- assignName(defs, expr);
21712
22289
  }
21713
- });
21714
- // Add declarations for the temp vars.
21715
- generatedStatements.push(...Array.from(new Set(defs.values()))
21716
- .map(name => createStatementOp(new DeclareVarStmt(name))));
21717
- opCount++;
21718
- if (op.kind === OpKind.Listener) {
21719
- op.handlerOps.prepend(generateTemporaries(op.handlerOps));
22290
+ else {
22291
+ // The track function could not be optimized.
22292
+ // Replace context reads with a special IR expression, since context reads in a track
22293
+ // function are emitted specially.
22294
+ op.track = transformExpressionsInExpression(op.track, expr => {
22295
+ if (expr instanceof ContextExpr) {
22296
+ op.usesComponentInstance = true;
22297
+ return new TrackContextExpr(expr.view);
22298
+ }
22299
+ return expr;
22300
+ }, VisitorContextFlag.None);
22301
+ }
21720
22302
  }
21721
22303
  }
21722
- return generatedStatements;
21723
22304
  }
22305
+ function isTrackByFunctionCall(rootView, expr) {
22306
+ if (!(expr instanceof InvokeFunctionExpr) || expr.args.length !== 2) {
22307
+ return false;
22308
+ }
22309
+ if (!(expr.receiver instanceof ReadPropExpr &&
22310
+ expr.receiver.receiver instanceof ContextExpr) ||
22311
+ expr.receiver.receiver.view !== rootView) {
22312
+ return false;
22313
+ }
22314
+ const [arg0, arg1] = expr.args;
22315
+ if (!(arg0 instanceof ReadVarExpr) || arg0.name !== '$index') {
22316
+ return false;
22317
+ }
22318
+ if (!(arg1 instanceof ReadVarExpr) || arg1.name !== '$item') {
22319
+ return false;
22320
+ }
22321
+ return true;
22322
+ }
22323
+
21724
22324
  /**
21725
- * Assigns a name to the temporary variable in the given temporary variable expression.
22325
+ * Inside the `track` expression on a `for` repeater, the `$index` and `$item` variables are
22326
+ * ambiently available. In this phase, we find those variable usages, and replace them with the
22327
+ * appropriate output read.
21726
22328
  */
21727
- function assignName(names, expr) {
21728
- const name = names.get(expr.xref);
21729
- if (name === undefined) {
21730
- throw new Error(`Found xref with unassigned name: ${expr.xref}`);
22329
+ function generateTrackVariables(job) {
22330
+ for (const unit of job.units) {
22331
+ for (const op of unit.create) {
22332
+ if (op.kind !== OpKind.RepeaterCreate) {
22333
+ continue;
22334
+ }
22335
+ op.track = transformExpressionsInExpression(op.track, expr => {
22336
+ if (expr instanceof LexicalReadExpr) {
22337
+ if (expr.name === op.varNames.$index) {
22338
+ return variable('$index');
22339
+ }
22340
+ else if (expr.name === op.varNames.$implicit) {
22341
+ return variable('$item');
22342
+ }
22343
+ // TODO: handle prohibited context variables (emit as globals?)
22344
+ }
22345
+ return expr;
22346
+ }, VisitorContextFlag.None);
22347
+ }
21731
22348
  }
21732
- expr.name = name;
21733
22349
  }
21734
22350
 
21735
22351
  /**
21736
22352
  * Counts the number of variable slots used within each view, and stores that on the view itself, as
21737
22353
  * well as propagates it to the `ir.TemplateOp` for embedded views.
21738
22354
  */
21739
- function phaseVarCounting(job) {
22355
+ function countVariables(job) {
21740
22356
  // First, count the vars used in each view, and update the view-level counter.
21741
22357
  for (const unit of job.units) {
21742
22358
  let varCount = 0;
@@ -21881,7 +22497,7 @@ function isSingletonInterpolation(expr) {
21881
22497
  * To guarantee correctness, analysis of "fences" in the instruction lists is used to determine
21882
22498
  * which optimizations are safe to perform.
21883
22499
  */
21884
- function phaseVariableOptimization(job) {
22500
+ function optimizeVariables(job) {
21885
22501
  for (const unit of job.units) {
21886
22502
  inlineAlwaysInlineVariables(unit.create);
21887
22503
  inlineAlwaysInlineVariables(unit.update);
@@ -21923,7 +22539,7 @@ var Fence;
21923
22539
  * Note that all `ContextWrite` fences are implicitly `ContextRead` fences as operations which
21924
22540
  * change the view context do so based on the current one.
21925
22541
  */
21926
- Fence[Fence["ViewContextWrite"] = 3] = "ViewContextWrite";
22542
+ Fence[Fence["ViewContextWrite"] = 2] = "ViewContextWrite";
21927
22543
  /**
21928
22544
  * Indicates that a call is required for its side-effects, even if nothing reads its result.
21929
22545
  *
@@ -22103,9 +22719,9 @@ function optimizeVariablesInOpList(ops, compatibility) {
22103
22719
  function fencesForIrExpression(expr) {
22104
22720
  switch (expr.kind) {
22105
22721
  case ExpressionKind.NextContext:
22106
- return Fence.ViewContextWrite;
22722
+ return Fence.ViewContextRead | Fence.ViewContextWrite;
22107
22723
  case ExpressionKind.RestoreView:
22108
- return Fence.ViewContextWrite | Fence.SideEffectful;
22724
+ return Fence.ViewContextRead | Fence.ViewContextWrite | Fence.SideEffectful;
22109
22725
  case ExpressionKind.Reference:
22110
22726
  return Fence.ViewContextRead;
22111
22727
  default:
@@ -22277,7 +22893,7 @@ function allowConservativeInlining(decl, target) {
22277
22893
  /**
22278
22894
  * Wraps ICUs that do not already belong to an i18n block in a new i18n block.
22279
22895
  */
22280
- function phaseWrapIcus(job) {
22896
+ function wrapI18nIcus(job) {
22281
22897
  for (const unit of job.units) {
22282
22898
  let currentI18nOp = null;
22283
22899
  for (const op of unit.create) {
@@ -22300,133 +22916,6 @@ function phaseWrapIcus(job) {
22300
22916
  }
22301
22917
  }
22302
22918
 
22303
- function phaseTrackVariables(job) {
22304
- for (const unit of job.units) {
22305
- for (const op of unit.create) {
22306
- if (op.kind !== OpKind.RepeaterCreate) {
22307
- continue;
22308
- }
22309
- op.track = transformExpressionsInExpression(op.track, expr => {
22310
- if (expr instanceof LexicalReadExpr) {
22311
- if (expr.name === op.varNames.$index) {
22312
- return variable('$index');
22313
- }
22314
- else if (expr.name === op.varNames.$implicit) {
22315
- return variable('$item');
22316
- }
22317
- // TODO: handle prohibited context variables (emit as globals?)
22318
- }
22319
- return expr;
22320
- }, VisitorContextFlag.None);
22321
- }
22322
- }
22323
- }
22324
-
22325
- /**
22326
- * Generate track functions that need to be extracted to the constant pool. This entails wrapping
22327
- * them in an arrow (or traditional) function, replacing context reads with `this.`, and storing
22328
- * them in the constant pool.
22329
- *
22330
- * Note that, if a track function was previously optimized, it will not need to be extracted, and
22331
- * this phase is a no-op.
22332
- */
22333
- function phaseTrackFnGeneration(job) {
22334
- for (const unit of job.units) {
22335
- for (const op of unit.create) {
22336
- if (op.kind !== OpKind.RepeaterCreate) {
22337
- continue;
22338
- }
22339
- if (op.trackByFn !== null) {
22340
- // The final track function was already set, probably because it was optimized.
22341
- continue;
22342
- }
22343
- // Find all component context reads.
22344
- let usesComponentContext = false;
22345
- op.track = transformExpressionsInExpression(op.track, expr => {
22346
- if (expr instanceof TrackContextExpr) {
22347
- usesComponentContext = true;
22348
- return variable('this');
22349
- }
22350
- return expr;
22351
- }, VisitorContextFlag.None);
22352
- let fn;
22353
- const fnParams = [new FnParam('$index'), new FnParam('$item')];
22354
- if (usesComponentContext) {
22355
- fn = new FunctionExpr(fnParams, [new ReturnStatement(op.track)]);
22356
- }
22357
- else {
22358
- fn = arrowFn(fnParams, op.track);
22359
- }
22360
- op.trackByFn = job.pool.getSharedFunctionReference(fn, '_forTrack');
22361
- }
22362
- }
22363
- }
22364
-
22365
- function phaseTrackFnOptimization(job) {
22366
- for (const unit of job.units) {
22367
- for (const op of unit.create) {
22368
- if (op.kind !== OpKind.RepeaterCreate) {
22369
- continue;
22370
- }
22371
- if (op.track instanceof ReadVarExpr && op.track.name === '$index') {
22372
- // Top-level access of `$index` uses the built in `repeaterTrackByIndex`.
22373
- op.trackByFn = importExpr(Identifiers.repeaterTrackByIndex);
22374
- }
22375
- else if (op.track instanceof ReadVarExpr && op.track.name === '$item') {
22376
- // Top-level access of the item uses the built in `repeaterTrackByIdentity`.
22377
- op.trackByFn = importExpr(Identifiers.repeaterTrackByIdentity);
22378
- }
22379
- else if (isTrackByFunctionCall(job.root.xref, op.track)) {
22380
- // Top-level method calls in the form of `fn($index, item)` can be passed in directly.
22381
- if (op.track.receiver.receiver.view === unit.xref) {
22382
- // TODO: this may be wrong
22383
- op.trackByFn = op.track.receiver;
22384
- }
22385
- else {
22386
- // This is a plain method call, but not in the component's root view.
22387
- // We need to get the component instance, and then call the method on it.
22388
- op.trackByFn =
22389
- importExpr(Identifiers.componentInstance).callFn([]).prop(op.track.receiver.name);
22390
- // Because the context is not avaiable (without a special function), we don't want to
22391
- // try to resolve it later. Let's get rid of it by overwriting the original track
22392
- // expression (which won't be used anyway).
22393
- op.track = op.trackByFn;
22394
- }
22395
- }
22396
- else {
22397
- // The track function could not be optimized.
22398
- // Replace context reads with a special IR expression, since context reads in a track
22399
- // function are emitted specially.
22400
- op.track = transformExpressionsInExpression(op.track, expr => {
22401
- if (expr instanceof ContextExpr) {
22402
- op.usesComponentInstance = true;
22403
- return new TrackContextExpr(expr.view);
22404
- }
22405
- return expr;
22406
- }, VisitorContextFlag.None);
22407
- }
22408
- }
22409
- }
22410
- }
22411
- function isTrackByFunctionCall(rootView, expr) {
22412
- if (!(expr instanceof InvokeFunctionExpr) || expr.args.length !== 2) {
22413
- return false;
22414
- }
22415
- if (!(expr.receiver instanceof ReadPropExpr &&
22416
- expr.receiver.receiver instanceof ContextExpr) ||
22417
- expr.receiver.receiver.view !== rootView) {
22418
- return false;
22419
- }
22420
- const [arg0, arg1] = expr.args;
22421
- if (!(arg0 instanceof ReadVarExpr) || arg0.name !== '$index') {
22422
- return false;
22423
- }
22424
- if (!(arg1 instanceof ReadVarExpr) || arg1.name !== '$item') {
22425
- return false;
22426
- }
22427
- return true;
22428
- }
22429
-
22430
22919
  /**
22431
22920
  *
22432
22921
  * @license
@@ -22436,59 +22925,66 @@ function isTrackByFunctionCall(rootView, expr) {
22436
22925
  * found in the LICENSE file at https://angular.io/license
22437
22926
  */
22438
22927
  const phases = [
22439
- { kind: CompilationJobKind.Tmpl, fn: phaseRemoveContentSelectors },
22440
- { kind: CompilationJobKind.Host, fn: phaseHostStylePropertyParsing },
22441
- { kind: CompilationJobKind.Tmpl, fn: phaseNamespace },
22442
- { kind: CompilationJobKind.Both, fn: phaseStyleBindingSpecialization },
22443
- { kind: CompilationJobKind.Both, fn: phaseBindingSpecialization },
22444
- { kind: CompilationJobKind.Tmpl, fn: phasePropagateI18nBlocks },
22445
- { kind: CompilationJobKind.Tmpl, fn: phaseWrapIcus },
22446
- { kind: CompilationJobKind.Both, fn: phaseAttributeExtraction },
22447
- { kind: CompilationJobKind.Both, fn: phaseParseExtractedStyles },
22448
- { kind: CompilationJobKind.Tmpl, fn: phaseRemoveEmptyBindings },
22449
- { kind: CompilationJobKind.Both, fn: phaseCollapseSingletonInterpolations },
22450
- { kind: CompilationJobKind.Both, fn: phaseOrdering },
22451
- { kind: CompilationJobKind.Tmpl, fn: phaseConditionals },
22452
- { kind: CompilationJobKind.Tmpl, fn: phasePipeCreation },
22453
- { kind: CompilationJobKind.Tmpl, fn: phaseI18nTextExtraction },
22454
- { kind: CompilationJobKind.Tmpl, fn: phaseIcuExtraction },
22455
- { kind: CompilationJobKind.Tmpl, fn: phaseApplyI18nExpressions },
22456
- { kind: CompilationJobKind.Tmpl, fn: phasePipeVariadic },
22457
- { kind: CompilationJobKind.Both, fn: phasePureLiteralStructures },
22458
- { kind: CompilationJobKind.Tmpl, fn: phaseGenerateProjectionDef },
22459
- { kind: CompilationJobKind.Tmpl, fn: phaseGenerateVariables },
22460
- { kind: CompilationJobKind.Tmpl, fn: phaseSaveRestoreView },
22461
- { kind: CompilationJobKind.Tmpl, fn: phaseFindAnyCasts },
22462
- { kind: CompilationJobKind.Both, fn: phaseResolveDollarEvent },
22463
- { kind: CompilationJobKind.Tmpl, fn: phaseRepeaterDerivedVars },
22464
- { kind: CompilationJobKind.Tmpl, fn: phaseTrackVariables },
22465
- { kind: CompilationJobKind.Both, fn: phaseResolveNames },
22466
- { kind: CompilationJobKind.Tmpl, fn: phaseTrackFnOptimization },
22467
- { kind: CompilationJobKind.Both, fn: phaseResolveContexts },
22468
- { kind: CompilationJobKind.Tmpl, fn: phaseResolveSanitizers },
22469
- { kind: CompilationJobKind.Tmpl, fn: phaseLocalRefs },
22470
- { kind: CompilationJobKind.Both, fn: phaseNullishCoalescing },
22471
- { kind: CompilationJobKind.Both, fn: phaseExpandSafeReads },
22472
- { kind: CompilationJobKind.Both, fn: phaseTemporaryVariables },
22473
- { kind: CompilationJobKind.Tmpl, fn: phaseSlotAllocation },
22474
- { kind: CompilationJobKind.Tmpl, fn: phaseResolveI18nPlaceholders },
22475
- { kind: CompilationJobKind.Tmpl, fn: phaseTrackFnGeneration },
22476
- { kind: CompilationJobKind.Tmpl, fn: phaseI18nMessageExtraction },
22477
- { kind: CompilationJobKind.Tmpl, fn: phaseI18nConstCollection },
22478
- { kind: CompilationJobKind.Tmpl, fn: phaseConstTraitCollection },
22479
- { kind: CompilationJobKind.Both, fn: phaseConstCollection },
22480
- { kind: CompilationJobKind.Tmpl, fn: phaseAssignI18nSlotDependencies },
22481
- { kind: CompilationJobKind.Both, fn: phaseVarCounting },
22482
- { kind: CompilationJobKind.Tmpl, fn: phaseGenerateAdvance },
22483
- { kind: CompilationJobKind.Both, fn: phaseVariableOptimization },
22484
- { kind: CompilationJobKind.Both, fn: phaseNaming },
22485
- { kind: CompilationJobKind.Tmpl, fn: phaseMergeNextContext },
22486
- { kind: CompilationJobKind.Tmpl, fn: phaseNgContainer },
22487
- { kind: CompilationJobKind.Tmpl, fn: phaseEmptyElements },
22488
- { kind: CompilationJobKind.Tmpl, fn: phaseNonbindable },
22489
- { kind: CompilationJobKind.Both, fn: phasePureFunctionExtraction },
22490
- { kind: CompilationJobKind.Both, fn: phaseReify },
22491
- { kind: CompilationJobKind.Both, fn: phaseChaining },
22928
+ { kind: CompilationJobKind.Tmpl, fn: removeContentSelectors },
22929
+ { kind: CompilationJobKind.Host, fn: parseHostStyleProperties },
22930
+ { kind: CompilationJobKind.Tmpl, fn: emitNamespaceChanges },
22931
+ { kind: CompilationJobKind.Both, fn: specializeStyleBindings },
22932
+ { kind: CompilationJobKind.Both, fn: specializeBindings },
22933
+ { kind: CompilationJobKind.Tmpl, fn: propagateI18nBlocks },
22934
+ { kind: CompilationJobKind.Tmpl, fn: wrapI18nIcus },
22935
+ { kind: CompilationJobKind.Tmpl, fn: createI18nContexts },
22936
+ { kind: CompilationJobKind.Both, fn: extractAttributes },
22937
+ { kind: CompilationJobKind.Both, fn: parseExtractedStyles },
22938
+ { kind: CompilationJobKind.Tmpl, fn: removeEmptyBindings },
22939
+ { kind: CompilationJobKind.Both, fn: collapseSingletonInterpolations },
22940
+ { kind: CompilationJobKind.Both, fn: orderOps },
22941
+ { kind: CompilationJobKind.Tmpl, fn: generateConditionalExpressions },
22942
+ { kind: CompilationJobKind.Tmpl, fn: createPipes },
22943
+ { kind: CompilationJobKind.Tmpl, fn: configureDeferInstructions },
22944
+ { kind: CompilationJobKind.Tmpl, fn: extractI18nText },
22945
+ { kind: CompilationJobKind.Tmpl, fn: createI18nIcuExpressions },
22946
+ { kind: CompilationJobKind.Tmpl, fn: applyI18nExpressions },
22947
+ { kind: CompilationJobKind.Tmpl, fn: createVariadicPipes },
22948
+ { kind: CompilationJobKind.Both, fn: generatePureLiteralStructures },
22949
+ { kind: CompilationJobKind.Tmpl, fn: generateProjectionDefs },
22950
+ { kind: CompilationJobKind.Tmpl, fn: generateVariables },
22951
+ { kind: CompilationJobKind.Tmpl, fn: saveAndRestoreView },
22952
+ { kind: CompilationJobKind.Tmpl, fn: deleteAnyCasts },
22953
+ { kind: CompilationJobKind.Both, fn: resolveDollarEvent },
22954
+ { kind: CompilationJobKind.Tmpl, fn: generateRepeaterDerivedVars },
22955
+ { kind: CompilationJobKind.Tmpl, fn: generateTrackVariables },
22956
+ { kind: CompilationJobKind.Both, fn: resolveNames },
22957
+ { kind: CompilationJobKind.Tmpl, fn: resolveDeferTargetNames },
22958
+ { kind: CompilationJobKind.Tmpl, fn: optimizeTrackFns },
22959
+ { kind: CompilationJobKind.Both, fn: resolveContexts },
22960
+ { kind: CompilationJobKind.Tmpl, fn: resolveSanitizers },
22961
+ { kind: CompilationJobKind.Tmpl, fn: liftLocalRefs },
22962
+ { kind: CompilationJobKind.Both, fn: generateNullishCoalesceExpressions },
22963
+ { kind: CompilationJobKind.Both, fn: expandSafeReads },
22964
+ { kind: CompilationJobKind.Both, fn: generateTemporaryVariables },
22965
+ { kind: CompilationJobKind.Tmpl, fn: allocateSlots },
22966
+ { kind: CompilationJobKind.Tmpl, fn: createDeferDepsFns },
22967
+ { kind: CompilationJobKind.Tmpl, fn: resolveI18nElementPlaceholders },
22968
+ { kind: CompilationJobKind.Tmpl, fn: resolveI18nExpressionPlaceholders },
22969
+ { kind: CompilationJobKind.Tmpl, fn: mergeI18nContexts },
22970
+ { kind: CompilationJobKind.Tmpl, fn: extractI18nMessages },
22971
+ { kind: CompilationJobKind.Tmpl, fn: generateTrackFns },
22972
+ { kind: CompilationJobKind.Tmpl, fn: collectI18nConsts },
22973
+ { kind: CompilationJobKind.Tmpl, fn: collectConstExpressions },
22974
+ { kind: CompilationJobKind.Both, fn: collectElementConsts },
22975
+ { kind: CompilationJobKind.Tmpl, fn: assignI18nSlotDependencies },
22976
+ { kind: CompilationJobKind.Tmpl, fn: removeI18nContexts },
22977
+ { kind: CompilationJobKind.Both, fn: countVariables },
22978
+ { kind: CompilationJobKind.Tmpl, fn: generateAdvance },
22979
+ { kind: CompilationJobKind.Both, fn: optimizeVariables },
22980
+ { kind: CompilationJobKind.Both, fn: nameFunctionsAndVariables },
22981
+ { kind: CompilationJobKind.Tmpl, fn: mergeNextContextExpressions },
22982
+ { kind: CompilationJobKind.Tmpl, fn: generateNgContainerOps },
22983
+ { kind: CompilationJobKind.Tmpl, fn: collapseEmptyInstructions },
22984
+ { kind: CompilationJobKind.Tmpl, fn: disableBindings$1 },
22985
+ { kind: CompilationJobKind.Both, fn: extractPureFunctions },
22986
+ { kind: CompilationJobKind.Both, fn: reify },
22987
+ { kind: CompilationJobKind.Both, fn: chain },
22492
22988
  ];
22493
22989
  /**
22494
22990
  * Run all transformation phases in the correct order against a compilation job. After this
@@ -22603,10 +23099,10 @@ const compatibilityMode = CompatibilityMode.TemplateDefinitionBuilder;
22603
23099
  * representation.
22604
23100
  * TODO: Refactor more of the ingestion code into phases.
22605
23101
  */
22606
- function ingestComponent(componentName, template, constantPool, relativeContextFilePath, i18nUseExternalIds) {
22607
- const cpl = new ComponentCompilationJob(componentName, constantPool, compatibilityMode, relativeContextFilePath, i18nUseExternalIds);
22608
- ingestNodes(cpl.root, template);
22609
- return cpl;
23102
+ function ingestComponent(componentName, template, constantPool, relativeContextFilePath, i18nUseExternalIds, deferBlocksMeta) {
23103
+ const job = new ComponentCompilationJob(componentName, constantPool, compatibilityMode, relativeContextFilePath, i18nUseExternalIds, deferBlocksMeta);
23104
+ ingestNodes(job.root, template);
23105
+ return job;
22610
23106
  }
22611
23107
  /**
22612
23108
  * Process a host binding AST and convert it into a `HostBindingCompilationJob` in the intermediate
@@ -22654,7 +23150,7 @@ function ingestHostAttribute(job, name, value) {
22654
23150
  job.root.update.push(attrBinding);
22655
23151
  }
22656
23152
  function ingestHostEvent(job, event) {
22657
- const eventBinding = createListenerOp(job.root.xref, event.name, null, event.targetOrPhase, true, event.sourceSpan);
23153
+ const eventBinding = createListenerOp(job.root.xref, new SlotHandle(), event.name, null, event.targetOrPhase, true, event.sourceSpan);
22658
23154
  // TODO: Can this be a chain?
22659
23155
  eventBinding.handlerOps.push(createStatementOp(new ReturnStatement(convertAst(event.handler.ast, job, event.sourceSpan), event.handlerSpan)));
22660
23156
  job.root.create.push(eventBinding);
@@ -22707,10 +23203,6 @@ function ingestElement(unit, element) {
22707
23203
  !(element.i18n instanceof Message || element.i18n instanceof TagPlaceholder)) {
22708
23204
  throw Error(`Unhandled i18n metadata type for element: ${element.i18n.constructor.name}`);
22709
23205
  }
22710
- const staticAttributes = {};
22711
- for (const attr of element.attributes) {
22712
- staticAttributes[attr.name] = attr.value;
22713
- }
22714
23206
  const id = unit.job.allocateXrefId();
22715
23207
  const [namespaceKey, elementName] = splitNsName(element.name);
22716
23208
  const startOp = createElementStartOp(elementName, id, namespaceForKey(namespaceKey), element.i18n instanceof TagPlaceholder ? element.i18n : undefined, element.startSourceSpan);
@@ -22718,7 +23210,12 @@ function ingestElement(unit, element) {
22718
23210
  ingestBindings(unit, startOp, element);
22719
23211
  ingestReferences(startOp, element);
22720
23212
  ingestNodes(unit, element.children);
22721
- const endOp = createElementEndOp(id, element.endSourceSpan);
23213
+ // The source span for the end op is typically the element closing tag. However, if no closing tag
23214
+ // exists, such as in `<input>`, we use the start source span instead. Usually the start and end
23215
+ // instructions will be collapsed into one `element` instruction, negating the purpose of this
23216
+ // fallback, but in cases when it is not collapsed (such as an input with a binding), we still
23217
+ // want to map the end instruction to the main element.
23218
+ const endOp = createElementEndOp(id, element.endSourceSpan ?? element.startSourceSpan);
22722
23219
  unit.create.push(endOp);
22723
23220
  // If there is an i18n message associated with this element, insert i18n start and end ops.
22724
23221
  if (element.i18n instanceof Message) {
@@ -22742,7 +23239,11 @@ function ingestTemplate(unit, tmpl) {
22742
23239
  [namespacePrefix, tagNameWithoutNamespace] = splitNsName(tmpl.tagName);
22743
23240
  }
22744
23241
  const i18nPlaceholder = tmpl.i18n instanceof TagPlaceholder ? tmpl.i18n : undefined;
22745
- const tplOp = createTemplateOp(childView.xref, tagNameWithoutNamespace, namespaceForKey(namespacePrefix), false, i18nPlaceholder, tmpl.startSourceSpan);
23242
+ const namespace = namespaceForKey(namespacePrefix);
23243
+ const functionNameSuffix = tagNameWithoutNamespace === null ?
23244
+ '' :
23245
+ prefixWithNamespace(tagNameWithoutNamespace, namespace);
23246
+ const tplOp = createTemplateOp(childView.xref, tagNameWithoutNamespace, functionNameSuffix, namespace, i18nPlaceholder, tmpl.startSourceSpan);
22746
23247
  unit.create.push(tplOp);
22747
23248
  ingestBindings(unit, tplOp, tmpl);
22748
23249
  ingestReferences(tplOp, tmpl);
@@ -22805,22 +23306,32 @@ function ingestBoundText(unit, text) {
22805
23306
  */
22806
23307
  function ingestIfBlock(unit, ifBlock) {
22807
23308
  let firstXref = null;
23309
+ let firstSlotHandle = null;
22808
23310
  let conditions = [];
22809
- for (const ifCase of ifBlock.branches) {
23311
+ for (let i = 0; i < ifBlock.branches.length; i++) {
23312
+ const ifCase = ifBlock.branches[i];
22810
23313
  const cView = unit.job.allocateView(unit.xref);
23314
+ let tagName = null;
23315
+ // Only the first branch can be used for projection, because the conditional
23316
+ // uses the container of the first branch as the insertion point for all branches.
23317
+ if (i === 0) {
23318
+ tagName = ingestControlFlowInsertionPoint(unit, cView.xref, ifCase);
23319
+ }
22811
23320
  if (ifCase.expressionAlias !== null) {
22812
23321
  cView.contextVariables.set(ifCase.expressionAlias.name, CTX_REF);
22813
23322
  }
23323
+ const tmplOp = createTemplateOp(cView.xref, tagName, 'Conditional', Namespace.HTML, undefined /* TODO: figure out how i18n works with new control flow */, ifCase.sourceSpan);
23324
+ unit.create.push(tmplOp);
22814
23325
  if (firstXref === null) {
22815
23326
  firstXref = cView.xref;
23327
+ firstSlotHandle = tmplOp.handle;
22816
23328
  }
22817
- unit.create.push(createTemplateOp(cView.xref, 'Conditional', Namespace.HTML, true, undefined /* TODO: figure out how i18n works with new control flow */, ifCase.sourceSpan));
22818
23329
  const caseExpr = ifCase.expression ? convertAst(ifCase.expression, unit.job, null) : null;
22819
- const conditionalCaseExpr = new ConditionalCaseExpr(caseExpr, cView.xref, ifCase.expressionAlias);
23330
+ const conditionalCaseExpr = new ConditionalCaseExpr(caseExpr, tmplOp.xref, tmplOp.handle, ifCase.expressionAlias);
22820
23331
  conditions.push(conditionalCaseExpr);
22821
23332
  ingestNodes(cView, ifCase.children);
22822
23333
  }
22823
- const conditional = createConditionalOp(firstXref, null, conditions, ifBlock.sourceSpan);
23334
+ const conditional = createConditionalOp(firstXref, firstSlotHandle, null, conditions, ifBlock.sourceSpan);
22824
23335
  unit.update.push(conditional);
22825
23336
  }
22826
23337
  /**
@@ -22828,21 +23339,24 @@ function ingestIfBlock(unit, ifBlock) {
22828
23339
  */
22829
23340
  function ingestSwitchBlock(unit, switchBlock) {
22830
23341
  let firstXref = null;
23342
+ let firstSlotHandle = null;
22831
23343
  let conditions = [];
22832
23344
  for (const switchCase of switchBlock.cases) {
22833
23345
  const cView = unit.job.allocateView(unit.xref);
23346
+ const tmplOp = createTemplateOp(cView.xref, null, 'Case', Namespace.HTML, undefined /* TODO: figure out how i18n works with new control flow */, switchCase.sourceSpan);
23347
+ unit.create.push(tmplOp);
22834
23348
  if (firstXref === null) {
22835
23349
  firstXref = cView.xref;
23350
+ firstSlotHandle = tmplOp.handle;
22836
23351
  }
22837
- unit.create.push(createTemplateOp(cView.xref, 'Case', Namespace.HTML, true, undefined /* TODO: figure out how i18n works with new control flow */, switchCase.sourceSpan));
22838
23352
  const caseExpr = switchCase.expression ?
22839
23353
  convertAst(switchCase.expression, unit.job, switchBlock.startSourceSpan) :
22840
23354
  null;
22841
- const conditionalCaseExpr = new ConditionalCaseExpr(caseExpr, cView.xref);
23355
+ const conditionalCaseExpr = new ConditionalCaseExpr(caseExpr, tmplOp.xref, tmplOp.handle);
22842
23356
  conditions.push(conditionalCaseExpr);
22843
23357
  ingestNodes(cView, switchCase.children);
22844
23358
  }
22845
- const conditional = createConditionalOp(firstXref, convertAst(switchBlock.expression, unit.job, null), conditions, switchBlock.sourceSpan);
23359
+ const conditional = createConditionalOp(firstXref, firstSlotHandle, convertAst(switchBlock.expression, unit.job, null), conditions, switchBlock.sourceSpan);
22846
23360
  unit.update.push(conditional);
22847
23361
  }
22848
23362
  function ingestDeferView(unit, suffix, children, sourceSpan) {
@@ -22851,48 +23365,100 @@ function ingestDeferView(unit, suffix, children, sourceSpan) {
22851
23365
  }
22852
23366
  const secondaryView = unit.job.allocateView(unit.xref);
22853
23367
  ingestNodes(secondaryView, children);
22854
- const templateOp = createTemplateOp(secondaryView.xref, `Defer${suffix}`, Namespace.HTML, true, undefined, sourceSpan);
23368
+ const templateOp = createTemplateOp(secondaryView.xref, null, `Defer${suffix}`, Namespace.HTML, undefined, sourceSpan);
22855
23369
  unit.create.push(templateOp);
22856
23370
  return templateOp;
22857
23371
  }
22858
23372
  function ingestDeferBlock(unit, deferBlock) {
23373
+ const blockMeta = unit.job.deferBlocksMeta.get(deferBlock);
23374
+ if (blockMeta === undefined) {
23375
+ throw new Error(`AssertionError: unable to find metadata for deferred block`);
23376
+ }
22859
23377
  // Generate the defer main view and all secondary views.
22860
23378
  const main = ingestDeferView(unit, '', deferBlock.children, deferBlock.sourceSpan);
22861
23379
  const loading = ingestDeferView(unit, 'Loading', deferBlock.loading?.children, deferBlock.loading?.sourceSpan);
22862
23380
  const placeholder = ingestDeferView(unit, 'Placeholder', deferBlock.placeholder?.children, deferBlock.placeholder?.sourceSpan);
22863
23381
  const error = ingestDeferView(unit, 'Error', deferBlock.error?.children, deferBlock.error?.sourceSpan);
22864
23382
  // Create the main defer op, and ops for all secondary views.
22865
- const deferOp = createDeferOp(unit.job.allocateXrefId(), main.xref, deferBlock.sourceSpan);
23383
+ const deferXref = unit.job.allocateXrefId();
23384
+ const deferOp = createDeferOp(deferXref, main.xref, main.handle, blockMeta, deferBlock.sourceSpan);
23385
+ deferOp.placeholderView = placeholder?.xref ?? null;
23386
+ deferOp.placeholderSlot = placeholder?.handle ?? null;
23387
+ deferOp.loadingSlot = loading?.handle ?? null;
23388
+ deferOp.errorSlot = error?.handle ?? null;
23389
+ deferOp.placeholderMinimumTime = deferBlock.placeholder?.minimumTime ?? null;
23390
+ deferOp.loadingMinimumTime = deferBlock.loading?.minimumTime ?? null;
23391
+ deferOp.loadingAfterTime = deferBlock.loading?.afterTime ?? null;
22866
23392
  unit.create.push(deferOp);
22867
- if (loading && deferBlock.loading) {
22868
- deferOp.loading =
22869
- createDeferSecondaryOp(deferOp.xref, loading.xref, DeferSecondaryKind.Loading);
22870
- if (deferBlock.loading.afterTime !== null || deferBlock.loading.minimumTime !== null) {
22871
- deferOp.loading.constValue = [deferBlock.loading.minimumTime, deferBlock.loading.afterTime];
22872
- }
22873
- unit.create.push(deferOp.loading);
22874
- }
22875
- if (placeholder && deferBlock.placeholder) {
22876
- deferOp.placeholder = createDeferSecondaryOp(deferOp.xref, placeholder.xref, DeferSecondaryKind.Placeholder);
22877
- if (deferBlock.placeholder.minimumTime !== null) {
22878
- deferOp.placeholder.constValue = [deferBlock.placeholder.minimumTime];
22879
- }
22880
- unit.create.push(deferOp.placeholder);
22881
- }
22882
- if (error && deferBlock.error) {
22883
- deferOp.error =
22884
- createDeferSecondaryOp(deferOp.xref, error.xref, DeferSecondaryKind.Error);
22885
- unit.create.push(deferOp.error);
22886
- }
22887
- // Configure all defer conditions.
22888
- const deferOnOp = createDeferOnOp(unit.job.allocateXrefId(), null);
22889
- // Add all ops to the view.
22890
- unit.create.push(deferOnOp);
23393
+ // Configure all defer `on` conditions.
23394
+ // TODO: refactor prefetch triggers to use a separate op type, with a shared superclass. This will
23395
+ // make it easier to refactor prefetch behavior in the future.
23396
+ let prefetch = false;
23397
+ let deferOnOps = [];
23398
+ let deferWhenOps = [];
23399
+ for (const triggers of [deferBlock.triggers, deferBlock.prefetchTriggers]) {
23400
+ if (triggers.idle !== undefined) {
23401
+ const deferOnOp = createDeferOnOp(deferXref, { kind: DeferTriggerKind.Idle }, prefetch, triggers.idle.sourceSpan);
23402
+ deferOnOps.push(deferOnOp);
23403
+ }
23404
+ if (triggers.immediate !== undefined) {
23405
+ const deferOnOp = createDeferOnOp(deferXref, { kind: DeferTriggerKind.Immediate }, prefetch, triggers.immediate.sourceSpan);
23406
+ deferOnOps.push(deferOnOp);
23407
+ }
23408
+ if (triggers.timer !== undefined) {
23409
+ const deferOnOp = createDeferOnOp(deferXref, { kind: DeferTriggerKind.Timer, delay: triggers.timer.delay }, prefetch, triggers.timer.sourceSpan);
23410
+ deferOnOps.push(deferOnOp);
23411
+ }
23412
+ if (triggers.hover !== undefined) {
23413
+ const deferOnOp = createDeferOnOp(deferXref, {
23414
+ kind: DeferTriggerKind.Hover,
23415
+ targetName: triggers.hover.reference,
23416
+ targetXref: null,
23417
+ targetSlot: null,
23418
+ targetView: null,
23419
+ targetSlotViewSteps: null,
23420
+ }, prefetch, triggers.hover.sourceSpan);
23421
+ deferOnOps.push(deferOnOp);
23422
+ }
23423
+ if (triggers.interaction !== undefined) {
23424
+ const deferOnOp = createDeferOnOp(deferXref, {
23425
+ kind: DeferTriggerKind.Interaction,
23426
+ targetName: triggers.interaction.reference,
23427
+ targetXref: null,
23428
+ targetSlot: null,
23429
+ targetView: null,
23430
+ targetSlotViewSteps: null,
23431
+ }, prefetch, triggers.interaction.sourceSpan);
23432
+ deferOnOps.push(deferOnOp);
23433
+ }
23434
+ if (triggers.viewport !== undefined) {
23435
+ const deferOnOp = createDeferOnOp(deferXref, {
23436
+ kind: DeferTriggerKind.Viewport,
23437
+ targetName: triggers.viewport.reference,
23438
+ targetXref: null,
23439
+ targetSlot: null,
23440
+ targetView: null,
23441
+ targetSlotViewSteps: null,
23442
+ }, prefetch, triggers.viewport.sourceSpan);
23443
+ deferOnOps.push(deferOnOp);
23444
+ }
23445
+ if (triggers.when !== undefined) {
23446
+ const deferOnOp = createDeferWhenOp(deferXref, convertAst(triggers.when.value, unit.job, triggers.when.sourceSpan), prefetch, triggers.when.sourceSpan);
23447
+ deferWhenOps.push(deferOnOp);
23448
+ }
23449
+ // If no (non-prefetching) defer triggers were provided, default to `idle`.
23450
+ if (deferOnOps.length === 0 && deferWhenOps.length === 0) {
23451
+ deferOnOps.push(createDeferOnOp(deferXref, { kind: DeferTriggerKind.Idle }, false, null));
23452
+ }
23453
+ prefetch = true;
23454
+ }
23455
+ unit.create.push(deferOnOps);
23456
+ unit.update.push(deferWhenOps);
22891
23457
  }
22892
23458
  function ingestIcu(unit, icu) {
22893
- if (icu.i18n instanceof Message) {
23459
+ if (icu.i18n instanceof Message && isSingleI18nIcu(icu.i18n)) {
22894
23460
  const xref = unit.job.allocateXrefId();
22895
- unit.create.push(createIcuOp(xref, icu.i18n, null));
23461
+ unit.create.push(createIcuOp(xref, icu.i18n, icu.i18n.nodes[0], icuFromI18nMessage(icu.i18n).name, null));
22896
23462
  unit.update.push(createIcuUpdateOp(xref, null));
22897
23463
  }
22898
23464
  else {
@@ -22937,10 +23503,11 @@ function ingestForBlock(unit, forBlock) {
22937
23503
  $odd: forBlock.contextVariables.$odd.name,
22938
23504
  $implicit: forBlock.item.name,
22939
23505
  };
22940
- const repeaterCreate = createRepeaterCreateOp(repeaterView.xref, emptyView?.xref ?? null, track, varNames, forBlock.sourceSpan);
23506
+ const tagName = ingestControlFlowInsertionPoint(unit, repeaterView.xref, forBlock);
23507
+ const repeaterCreate = createRepeaterCreateOp(repeaterView.xref, emptyView?.xref ?? null, tagName, track, varNames, forBlock.sourceSpan);
22941
23508
  unit.create.push(repeaterCreate);
22942
23509
  const expression = convertAst(forBlock.expression, unit.job, convertSourceSpan(forBlock.expression.span, forBlock.sourceSpan));
22943
- const repeater = createRepeaterOp(repeaterCreate.xref, expression, forBlock.sourceSpan);
23510
+ const repeater = createRepeaterOp(repeaterCreate.xref, repeaterCreate.handle, expression, forBlock.sourceSpan);
22944
23511
  unit.update.push(repeater);
22945
23512
  }
22946
23513
  /**
@@ -22959,6 +23526,11 @@ function convertAst(ast, job, baseSourceSpan) {
22959
23526
  }
22960
23527
  }
22961
23528
  else if (ast instanceof PropertyWrite) {
23529
+ if (ast.receiver instanceof ImplicitReceiver) {
23530
+ return new WritePropExpr(
23531
+ // TODO: Is it correct to always use the root context in place of the implicit receiver?
23532
+ new ContextExpr(job.root.xref), ast.name, convertAst(ast.value, job, baseSourceSpan), null, convertSourceSpan(ast.span, baseSourceSpan));
23533
+ }
22962
23534
  return new WritePropExpr(convertAst(ast.receiver, job, baseSourceSpan), ast.name, convertAst(ast.value, job, baseSourceSpan), undefined, convertSourceSpan(ast.span, baseSourceSpan));
22963
23535
  }
22964
23536
  else if (ast instanceof KeyedWrite) {
@@ -23014,7 +23586,7 @@ function convertAst(ast, job, baseSourceSpan) {
23014
23586
  }
23015
23587
  else if (ast instanceof BindingPipe) {
23016
23588
  // TODO: pipes should probably have source maps; figure out details.
23017
- return new PipeBindingExpr(job.allocateXrefId(), ast.name, [
23589
+ return new PipeBindingExpr(job.allocateXrefId(), new SlotHandle(), ast.name, [
23018
23590
  convertAst(ast.exp, job, baseSourceSpan),
23019
23591
  ...ast.args.map(arg => convertAst(arg, job, baseSourceSpan)),
23020
23592
  ]);
@@ -23034,7 +23606,7 @@ function convertAst(ast, job, baseSourceSpan) {
23034
23606
  return new EmptyExpr(convertSourceSpan(ast.span, baseSourceSpan));
23035
23607
  }
23036
23608
  else {
23037
- throw new Error(`Unhandled expression type: ${ast.constructor.name}`);
23609
+ throw new Error(`Unhandled expression type "${ast.constructor.name}" in file "${baseSourceSpan?.start.file.url}"`);
23038
23610
  }
23039
23611
  }
23040
23612
  /**
@@ -23098,8 +23670,7 @@ function ingestBindings(unit, op, element) {
23098
23670
  unit.create.push(createExtractedAttributeOp(op.xref, BindingKind.Property, output.name, null));
23099
23671
  continue;
23100
23672
  }
23101
- listenerOp =
23102
- createListenerOp(op.xref, output.name, op.tag, output.phase, false, output.sourceSpan);
23673
+ listenerOp = createListenerOp(op.xref, op.handle, output.name, op.tag, output.phase, false, output.sourceSpan);
23103
23674
  // if output.handler is a chain, then push each statement from the chain separately, and
23104
23675
  // return the last one?
23105
23676
  let handlerExprs;
@@ -23220,6 +23791,59 @@ function convertSourceSpan(span, baseSourceSpan) {
23220
23791
  const fullStart = baseSourceSpan.fullStart.moveBy(span.start);
23221
23792
  return new ParseSourceSpan(start, end, fullStart);
23222
23793
  }
23794
+ /**
23795
+ * With the directive-based control flow users were able to conditionally project content using
23796
+ * the `*` syntax. E.g. `<div *ngIf="expr" projectMe></div>` will be projected into
23797
+ * `<ng-content select="[projectMe]"/>`, because the attributes and tag name from the `div` are
23798
+ * copied to the template via the template creation instruction. With `@if` and `@for` that is
23799
+ * not the case, because the conditional is placed *around* elements, rather than *on* them.
23800
+ * The result is that content projection won't work in the same way if a user converts from
23801
+ * `*ngIf` to `@if`.
23802
+ *
23803
+ * This function aims to cover the most common case by doing the same copying when a control flow
23804
+ * node has *one and only one* root element or template node.
23805
+ *
23806
+ * This approach comes with some caveats:
23807
+ * 1. As soon as any other node is added to the root, the copying behavior won't work anymore.
23808
+ * A diagnostic will be added to flag cases like this and to explain how to work around it.
23809
+ * 2. If `preserveWhitespaces` is enabled, it's very likely that indentation will break this
23810
+ * workaround, because it'll include an additional text node as the first child. We can work
23811
+ * around it here, but in a discussion it was decided not to, because the user explicitly opted
23812
+ * into preserving the whitespace and we would have to drop it from the generated code.
23813
+ * The diagnostic mentioned point #1 will flag such cases to users.
23814
+ *
23815
+ * @returns Tag name to be used for the control flow template.
23816
+ */
23817
+ function ingestControlFlowInsertionPoint(unit, xref, node) {
23818
+ let root = null;
23819
+ for (const child of node.children) {
23820
+ // Skip over comment nodes.
23821
+ if (child instanceof Comment$1) {
23822
+ continue;
23823
+ }
23824
+ // We can only infer the tag name/attributes if there's a single root node.
23825
+ if (root !== null) {
23826
+ return null;
23827
+ }
23828
+ // Root nodes can only elements or templates with a tag name (e.g. `<div *foo></div>`).
23829
+ if (child instanceof Element$1 || (child instanceof Template && child.tagName !== null)) {
23830
+ root = child;
23831
+ }
23832
+ }
23833
+ // If we've found a single root node, its tag name and *static* attributes can be copied
23834
+ // to the surrounding template to be used for content projection. Note that it's important
23835
+ // that we don't copy any bound attributes since they don't participate in content projection
23836
+ // and they can be used in directive matching (in the case of `Template.templateAttrs`).
23837
+ if (root !== null) {
23838
+ for (const attr of root.attributes) {
23839
+ ingestBinding(unit, xref, attr.name, literal(attr.value), 1 /* e.BindingType.Attribute */, null, SecurityContext.NONE, attr.sourceSpan, BindingFlags.TextValue);
23840
+ }
23841
+ const tagName = root instanceof Element$1 ? root.name : root.tagName;
23842
+ // Don't pass along `ng-template` tag name since it enables directive matching.
23843
+ return tagName === 'ng-template' ? null : tagName;
23844
+ }
23845
+ return null;
23846
+ }
23223
23847
 
23224
23848
  const USE_TEMPLATE_PIPELINE = false;
23225
23849
 
@@ -24356,7 +24980,7 @@ function normalizeNgContentSelect(selectAttr) {
24356
24980
  }
24357
24981
 
24358
24982
  /** Pattern for the expression in a for loop block. */
24359
- const FOR_LOOP_EXPRESSION_PATTERN = /^\s*([0-9A-Za-z_$]*)\s+of\s+(.*)/;
24983
+ const FOR_LOOP_EXPRESSION_PATTERN = /^\s*([0-9A-Za-z_$]*)\s+of\s+([\S\s]*)/;
24360
24984
  /** Pattern for the tracking expression in a for loop block. */
24361
24985
  const FOR_LOOP_TRACK_PATTERN = /^track\s+([\S\s]*)/;
24362
24986
  /** Pattern for the `as` expression in a conditional block. */
@@ -24387,19 +25011,19 @@ function createIfBlock(ast, connectedBlocks, visitor, bindingParser) {
24387
25011
  const branches = [];
24388
25012
  const mainBlockParams = parseConditionalBlockParameters(ast, errors, bindingParser);
24389
25013
  if (mainBlockParams !== null) {
24390
- branches.push(new IfBlockBranch(mainBlockParams.expression, visitAll(visitor, ast.children, ast.children), mainBlockParams.expressionAlias, ast.sourceSpan, ast.startSourceSpan, ast.endSourceSpan));
25014
+ branches.push(new IfBlockBranch(mainBlockParams.expression, visitAll(visitor, ast.children, ast.children), mainBlockParams.expressionAlias, ast.sourceSpan, ast.startSourceSpan, ast.endSourceSpan, ast.nameSpan));
24391
25015
  }
24392
25016
  for (const block of connectedBlocks) {
24393
25017
  if (ELSE_IF_PATTERN.test(block.name)) {
24394
25018
  const params = parseConditionalBlockParameters(block, errors, bindingParser);
24395
25019
  if (params !== null) {
24396
25020
  const children = visitAll(visitor, block.children, block.children);
24397
- branches.push(new IfBlockBranch(params.expression, children, params.expressionAlias, block.sourceSpan, block.startSourceSpan, block.endSourceSpan));
25021
+ branches.push(new IfBlockBranch(params.expression, children, params.expressionAlias, block.sourceSpan, block.startSourceSpan, block.endSourceSpan, block.nameSpan));
24398
25022
  }
24399
25023
  }
24400
25024
  else if (block.name === 'else') {
24401
25025
  const children = visitAll(visitor, block.children, block.children);
24402
- branches.push(new IfBlockBranch(null, children, null, block.sourceSpan, block.startSourceSpan, block.endSourceSpan));
25026
+ branches.push(new IfBlockBranch(null, children, null, block.sourceSpan, block.startSourceSpan, block.endSourceSpan, block.nameSpan));
24403
25027
  }
24404
25028
  }
24405
25029
  // The outer IfBlock should have a span that encapsulates all branches.
@@ -24411,7 +25035,7 @@ function createIfBlock(ast, connectedBlocks, visitor, bindingParser) {
24411
25035
  wholeSourceSpan = new ParseSourceSpan(ifBlockStartSourceSpan.start, lastBranch.sourceSpan.end);
24412
25036
  }
24413
25037
  return {
24414
- node: new IfBlock(branches, wholeSourceSpan, ast.startSourceSpan, ifBlockEndSourceSpan),
25038
+ node: new IfBlock(branches, wholeSourceSpan, ast.startSourceSpan, ifBlockEndSourceSpan, ast.nameSpan),
24415
25039
  errors,
24416
25040
  };
24417
25041
  }
@@ -24430,7 +25054,7 @@ function createForLoop(ast, connectedBlocks, visitor, bindingParser) {
24430
25054
  errors.push(new ParseError(block.sourceSpan, '@empty block cannot have parameters'));
24431
25055
  }
24432
25056
  else {
24433
- empty = new ForLoopBlockEmpty(visitAll(visitor, block.children, block.children), block.sourceSpan, block.startSourceSpan, block.endSourceSpan);
25057
+ empty = new ForLoopBlockEmpty(visitAll(visitor, block.children, block.children), block.sourceSpan, block.startSourceSpan, block.endSourceSpan, block.nameSpan);
24434
25058
  }
24435
25059
  }
24436
25060
  else {
@@ -24448,7 +25072,7 @@ function createForLoop(ast, connectedBlocks, visitor, bindingParser) {
24448
25072
  // main `for` body, use `mainSourceSpan`.
24449
25073
  const endSpan = empty?.endSourceSpan ?? ast.endSourceSpan;
24450
25074
  const sourceSpan = new ParseSourceSpan(ast.sourceSpan.start, endSpan?.end ?? ast.sourceSpan.end);
24451
- node = new ForLoopBlock(params.itemName, params.expression, params.trackBy, params.context, visitAll(visitor, ast.children, ast.children), empty, sourceSpan, ast.sourceSpan, ast.startSourceSpan, endSpan);
25075
+ node = new ForLoopBlock(params.itemName, params.expression, params.trackBy.expression, params.trackBy.keywordSpan, params.context, visitAll(visitor, ast.children, ast.children), empty, sourceSpan, ast.sourceSpan, ast.startSourceSpan, endSpan, ast.nameSpan);
24452
25076
  }
24453
25077
  }
24454
25078
  return { node, errors };
@@ -24474,7 +25098,7 @@ function createSwitchBlock(ast, visitor, bindingParser) {
24474
25098
  const expression = node.name === 'case' ?
24475
25099
  parseBlockParameterToBinding(node.parameters[0], bindingParser) :
24476
25100
  null;
24477
- const ast = new SwitchBlockCase(expression, visitAll(visitor, node.children, node.children), node.sourceSpan, node.startSourceSpan, node.endSourceSpan);
25101
+ const ast = new SwitchBlockCase(expression, visitAll(visitor, node.children, node.children), node.sourceSpan, node.startSourceSpan, node.endSourceSpan, node.nameSpan);
24478
25102
  if (expression === null) {
24479
25103
  defaultCase = ast;
24480
25104
  }
@@ -24487,7 +25111,7 @@ function createSwitchBlock(ast, visitor, bindingParser) {
24487
25111
  cases.push(defaultCase);
24488
25112
  }
24489
25113
  return {
24490
- node: new SwitchBlock(primaryExpression, cases, unknownBlocks, ast.sourceSpan, ast.startSourceSpan, ast.endSourceSpan),
25114
+ node: new SwitchBlock(primaryExpression, cases, unknownBlocks, ast.sourceSpan, ast.startSourceSpan, ast.endSourceSpan, ast.nameSpan),
24491
25115
  errors
24492
25116
  };
24493
25117
  }
@@ -24522,7 +25146,9 @@ function parseForLoopParameters(block, errors, bindingParser) {
24522
25146
  errors.push(new ParseError(param.sourceSpan, '@for loop can only have one "track" expression'));
24523
25147
  }
24524
25148
  else {
24525
- result.trackBy = parseBlockParameterToBinding(param, bindingParser, trackMatch[1]);
25149
+ const expression = parseBlockParameterToBinding(param, bindingParser, trackMatch[1]);
25150
+ const keywordSpan = new ParseSourceSpan(param.sourceSpan.start, param.sourceSpan.start.moveBy('track'.length));
25151
+ result.trackBy = { expression, keywordSpan };
24526
25152
  }
24527
25153
  continue;
24528
25154
  }
@@ -24596,8 +25222,10 @@ function validateSwitchBlock(ast) {
24596
25222
  return errors;
24597
25223
  }
24598
25224
  for (const node of ast.children) {
24599
- // Skip over empty text nodes inside the switch block since they can be used for formatting.
24600
- if (node instanceof Text && node.value.trim().length === 0) {
25225
+ // Skip over comments and empty text nodes inside the switch block.
25226
+ // Empty text nodes can be used for formatting while comments don't affect the runtime.
25227
+ if (node instanceof Comment ||
25228
+ (node instanceof Text && node.value.trim().length === 0)) {
24601
25229
  continue;
24602
25230
  }
24603
25231
  if (!(node instanceof Block) || (node.name !== 'case' && node.name !== 'default')) {
@@ -24720,7 +25348,7 @@ function stripOptionalParentheses(param, errors) {
24720
25348
  }
24721
25349
 
24722
25350
  /** Pattern for a timing value in a trigger. */
24723
- const TIME_PATTERN = /^\d+(ms|s)?$/;
25351
+ const TIME_PATTERN = /^\d+\.?\d*(ms|s)?$/;
24724
25352
  /** Pattern for a separator between keywords in a trigger expression. */
24725
25353
  const SEPARATOR_PATTERN = /^\s$/;
24726
25354
  /** Pairs of characters that form syntax that is comma-delimited. */
@@ -24742,6 +25370,8 @@ var OnTriggerType;
24742
25370
  /** Parses a `when` deferred trigger. */
24743
25371
  function parseWhenTrigger({ expression, sourceSpan }, bindingParser, triggers, errors) {
24744
25372
  const whenIndex = expression.indexOf('when');
25373
+ const whenSourceSpan = new ParseSourceSpan(sourceSpan.start.moveBy(whenIndex), sourceSpan.start.moveBy(whenIndex + 'when'.length));
25374
+ const prefetchSpan = getPrefetchSpan(expression, sourceSpan);
24745
25375
  // This is here just to be safe, we shouldn't enter this function
24746
25376
  // in the first place if a block doesn't have the "when" keyword.
24747
25377
  if (whenIndex === -1) {
@@ -24750,12 +25380,14 @@ function parseWhenTrigger({ expression, sourceSpan }, bindingParser, triggers, e
24750
25380
  else {
24751
25381
  const start = getTriggerParametersStart(expression, whenIndex + 1);
24752
25382
  const parsed = bindingParser.parseBinding(expression.slice(start), false, sourceSpan, sourceSpan.start.offset + start);
24753
- trackTrigger('when', triggers, errors, new BoundDeferredTrigger(parsed, sourceSpan));
25383
+ trackTrigger('when', triggers, errors, new BoundDeferredTrigger(parsed, sourceSpan, prefetchSpan, whenSourceSpan));
24754
25384
  }
24755
25385
  }
24756
25386
  /** Parses an `on` trigger */
24757
25387
  function parseOnTrigger({ expression, sourceSpan }, triggers, errors, placeholder) {
24758
25388
  const onIndex = expression.indexOf('on');
25389
+ const onSourceSpan = new ParseSourceSpan(sourceSpan.start.moveBy(onIndex), sourceSpan.start.moveBy(onIndex + 'on'.length));
25390
+ const prefetchSpan = getPrefetchSpan(expression, sourceSpan);
24759
25391
  // This is here just to be safe, we shouldn't enter this function
24760
25392
  // in the first place if a block doesn't have the "on" keyword.
24761
25393
  if (onIndex === -1) {
@@ -24763,18 +25395,26 @@ function parseOnTrigger({ expression, sourceSpan }, triggers, errors, placeholde
24763
25395
  }
24764
25396
  else {
24765
25397
  const start = getTriggerParametersStart(expression, onIndex + 1);
24766
- const parser = new OnTriggerParser(expression, start, sourceSpan, triggers, errors, placeholder);
25398
+ const parser = new OnTriggerParser(expression, start, sourceSpan, triggers, errors, placeholder, prefetchSpan, onSourceSpan);
24767
25399
  parser.parse();
24768
25400
  }
24769
25401
  }
25402
+ function getPrefetchSpan(expression, sourceSpan) {
25403
+ if (!expression.startsWith('prefetch')) {
25404
+ return null;
25405
+ }
25406
+ return new ParseSourceSpan(sourceSpan.start, sourceSpan.start.moveBy('prefetch'.length));
25407
+ }
24770
25408
  class OnTriggerParser {
24771
- constructor(expression, start, span, triggers, errors, placeholder) {
25409
+ constructor(expression, start, span, triggers, errors, placeholder, prefetchSpan, onSourceSpan) {
24772
25410
  this.expression = expression;
24773
25411
  this.start = start;
24774
25412
  this.span = span;
24775
25413
  this.triggers = triggers;
24776
25414
  this.errors = errors;
24777
25415
  this.placeholder = placeholder;
25416
+ this.prefetchSpan = prefetchSpan;
25417
+ this.onSourceSpan = onSourceSpan;
24778
25418
  this.index = 0;
24779
25419
  this.tokens = new Lexer().tokenize(expression.slice(start));
24780
25420
  }
@@ -24820,28 +25460,35 @@ class OnTriggerParser {
24820
25460
  return this.tokens[Math.min(this.index, this.tokens.length - 1)];
24821
25461
  }
24822
25462
  consumeTrigger(identifier, parameters) {
24823
- const startSpan = this.span.start.moveBy(this.start + identifier.index - this.tokens[0].index);
24824
- const endSpan = startSpan.moveBy(this.token().end - identifier.index);
24825
- const sourceSpan = new ParseSourceSpan(startSpan, endSpan);
25463
+ const triggerNameStartSpan = this.span.start.moveBy(this.start + identifier.index - this.tokens[0].index);
25464
+ const nameSpan = new ParseSourceSpan(triggerNameStartSpan, triggerNameStartSpan.moveBy(identifier.strValue.length));
25465
+ const endSpan = triggerNameStartSpan.moveBy(this.token().end - identifier.index);
25466
+ // Put the prefetch and on spans with the first trigger
25467
+ // This should maybe be refactored to have something like an outer OnGroup AST
25468
+ // Since triggers can be grouped with commas "on hover(x), interaction(y)"
25469
+ const isFirstTrigger = identifier.index === 0;
25470
+ const onSourceSpan = isFirstTrigger ? this.onSourceSpan : null;
25471
+ const prefetchSourceSpan = isFirstTrigger ? this.prefetchSpan : null;
25472
+ const sourceSpan = new ParseSourceSpan(isFirstTrigger ? this.span.start : triggerNameStartSpan, endSpan);
24826
25473
  try {
24827
25474
  switch (identifier.toString()) {
24828
25475
  case OnTriggerType.IDLE:
24829
- this.trackTrigger('idle', createIdleTrigger(parameters, sourceSpan));
25476
+ this.trackTrigger('idle', createIdleTrigger(parameters, nameSpan, sourceSpan, prefetchSourceSpan, onSourceSpan));
24830
25477
  break;
24831
25478
  case OnTriggerType.TIMER:
24832
- this.trackTrigger('timer', createTimerTrigger(parameters, sourceSpan));
25479
+ this.trackTrigger('timer', createTimerTrigger(parameters, nameSpan, sourceSpan, this.prefetchSpan, this.onSourceSpan));
24833
25480
  break;
24834
25481
  case OnTriggerType.INTERACTION:
24835
- this.trackTrigger('interaction', createInteractionTrigger(parameters, sourceSpan, this.placeholder));
25482
+ this.trackTrigger('interaction', createInteractionTrigger(parameters, nameSpan, sourceSpan, this.prefetchSpan, this.onSourceSpan, this.placeholder));
24836
25483
  break;
24837
25484
  case OnTriggerType.IMMEDIATE:
24838
- this.trackTrigger('immediate', createImmediateTrigger(parameters, sourceSpan));
25485
+ this.trackTrigger('immediate', createImmediateTrigger(parameters, nameSpan, sourceSpan, this.prefetchSpan, this.onSourceSpan));
24839
25486
  break;
24840
25487
  case OnTriggerType.HOVER:
24841
- this.trackTrigger('hover', createHoverTrigger(parameters, sourceSpan, this.placeholder));
25488
+ this.trackTrigger('hover', createHoverTrigger(parameters, nameSpan, sourceSpan, this.prefetchSpan, this.onSourceSpan, this.placeholder));
24842
25489
  break;
24843
25490
  case OnTriggerType.VIEWPORT:
24844
- this.trackTrigger('viewport', createViewportTrigger(parameters, sourceSpan, this.placeholder));
25491
+ this.trackTrigger('viewport', createViewportTrigger(parameters, nameSpan, sourceSpan, this.prefetchSpan, this.onSourceSpan, this.placeholder));
24845
25492
  break;
24846
25493
  default:
24847
25494
  throw new Error(`Unrecognized trigger type "${identifier}"`);
@@ -24931,13 +25578,13 @@ function trackTrigger(name, allTriggers, errors, trigger) {
24931
25578
  allTriggers[name] = trigger;
24932
25579
  }
24933
25580
  }
24934
- function createIdleTrigger(parameters, sourceSpan) {
25581
+ function createIdleTrigger(parameters, nameSpan, sourceSpan, prefetchSpan, onSourceSpan) {
24935
25582
  if (parameters.length > 0) {
24936
25583
  throw new Error(`"${OnTriggerType.IDLE}" trigger cannot have parameters`);
24937
25584
  }
24938
- return new IdleDeferredTrigger(sourceSpan);
25585
+ return new IdleDeferredTrigger(nameSpan, sourceSpan, prefetchSpan, onSourceSpan);
24939
25586
  }
24940
- function createTimerTrigger(parameters, sourceSpan) {
25587
+ function createTimerTrigger(parameters, nameSpan, sourceSpan, prefetchSpan, onSourceSpan) {
24941
25588
  if (parameters.length !== 1) {
24942
25589
  throw new Error(`"${OnTriggerType.TIMER}" trigger must have exactly one parameter`);
24943
25590
  }
@@ -24945,25 +25592,25 @@ function createTimerTrigger(parameters, sourceSpan) {
24945
25592
  if (delay === null) {
24946
25593
  throw new Error(`Could not parse time value of trigger "${OnTriggerType.TIMER}"`);
24947
25594
  }
24948
- return new TimerDeferredTrigger(delay, sourceSpan);
25595
+ return new TimerDeferredTrigger(delay, nameSpan, sourceSpan, prefetchSpan, onSourceSpan);
24949
25596
  }
24950
- function createImmediateTrigger(parameters, sourceSpan) {
25597
+ function createImmediateTrigger(parameters, nameSpan, sourceSpan, prefetchSpan, onSourceSpan) {
24951
25598
  if (parameters.length > 0) {
24952
25599
  throw new Error(`"${OnTriggerType.IMMEDIATE}" trigger cannot have parameters`);
24953
25600
  }
24954
- return new ImmediateDeferredTrigger(sourceSpan);
25601
+ return new ImmediateDeferredTrigger(nameSpan, sourceSpan, prefetchSpan, onSourceSpan);
24955
25602
  }
24956
- function createHoverTrigger(parameters, sourceSpan, placeholder) {
25603
+ function createHoverTrigger(parameters, nameSpan, sourceSpan, prefetchSpan, onSourceSpan, placeholder) {
24957
25604
  validateReferenceBasedTrigger(OnTriggerType.HOVER, parameters, placeholder);
24958
- return new HoverDeferredTrigger(parameters[0] ?? null, sourceSpan);
25605
+ return new HoverDeferredTrigger(parameters[0] ?? null, nameSpan, sourceSpan, prefetchSpan, onSourceSpan);
24959
25606
  }
24960
- function createInteractionTrigger(parameters, sourceSpan, placeholder) {
25607
+ function createInteractionTrigger(parameters, nameSpan, sourceSpan, prefetchSpan, onSourceSpan, placeholder) {
24961
25608
  validateReferenceBasedTrigger(OnTriggerType.INTERACTION, parameters, placeholder);
24962
- return new InteractionDeferredTrigger(parameters[0] ?? null, sourceSpan);
25609
+ return new InteractionDeferredTrigger(parameters[0] ?? null, nameSpan, sourceSpan, prefetchSpan, onSourceSpan);
24963
25610
  }
24964
- function createViewportTrigger(parameters, sourceSpan, placeholder) {
25611
+ function createViewportTrigger(parameters, nameSpan, sourceSpan, prefetchSpan, onSourceSpan, placeholder) {
24965
25612
  validateReferenceBasedTrigger(OnTriggerType.VIEWPORT, parameters, placeholder);
24966
- return new ViewportDeferredTrigger(parameters[0] ?? null, sourceSpan);
25613
+ return new ViewportDeferredTrigger(parameters[0] ?? null, nameSpan, sourceSpan, prefetchSpan, onSourceSpan);
24967
25614
  }
24968
25615
  function validateReferenceBasedTrigger(type, parameters, placeholder) {
24969
25616
  if (parameters.length > 1) {
@@ -25002,7 +25649,7 @@ function parseDeferredTime(value) {
25002
25649
  return null;
25003
25650
  }
25004
25651
  const [time, units] = match;
25005
- return parseInt(time) * (units === 's' ? 1000 : 1);
25652
+ return parseFloat(time) * (units === 's' ? 1000 : 1);
25006
25653
  }
25007
25654
 
25008
25655
  /** Pattern to identify a `prefetch when` trigger. */
@@ -25029,8 +25676,7 @@ function createDeferredBlock(ast, connectedBlocks, visitor, bindingParser) {
25029
25676
  const errors = [];
25030
25677
  const { placeholder, loading, error } = parseConnectedBlocks(connectedBlocks, errors, visitor);
25031
25678
  const { triggers, prefetchTriggers } = parsePrimaryTriggers(ast.parameters, bindingParser, errors, placeholder);
25032
- // The `defer` block has a main span encompassing all of the connected branches as well. For the
25033
- // span of only the first "main" branch, use `mainSourceSpan`.
25679
+ // The `defer` block has a main span encompassing all of the connected branches as well.
25034
25680
  let lastEndSourceSpan = ast.endSourceSpan;
25035
25681
  let endOfLastSourceSpan = ast.sourceSpan.end;
25036
25682
  if (connectedBlocks.length > 0) {
@@ -25038,8 +25684,8 @@ function createDeferredBlock(ast, connectedBlocks, visitor, bindingParser) {
25038
25684
  lastEndSourceSpan = lastConnectedBlock.endSourceSpan;
25039
25685
  endOfLastSourceSpan = lastConnectedBlock.sourceSpan.end;
25040
25686
  }
25041
- const mainDeferredSourceSpan = new ParseSourceSpan(ast.sourceSpan.start, endOfLastSourceSpan);
25042
- const node = new DeferredBlock(visitAll(visitor, ast.children, ast.children), triggers, prefetchTriggers, placeholder, loading, error, mainDeferredSourceSpan, ast.sourceSpan, ast.startSourceSpan, lastEndSourceSpan);
25687
+ const sourceSpanWithConnectedBlocks = new ParseSourceSpan(ast.sourceSpan.start, endOfLastSourceSpan);
25688
+ const node = new DeferredBlock(visitAll(visitor, ast.children, ast.children), triggers, prefetchTriggers, placeholder, loading, error, ast.nameSpan, sourceSpanWithConnectedBlocks, ast.sourceSpan, ast.startSourceSpan, lastEndSourceSpan);
25043
25689
  return { node, errors };
25044
25690
  }
25045
25691
  function parseConnectedBlocks(connectedBlocks, errors, visitor) {
@@ -25102,7 +25748,7 @@ function parsePlaceholderBlock(ast, visitor) {
25102
25748
  throw new Error(`Unrecognized parameter in @placeholder block: "${param.expression}"`);
25103
25749
  }
25104
25750
  }
25105
- return new DeferredBlockPlaceholder(visitAll(visitor, ast.children, ast.children), minimumTime, ast.sourceSpan, ast.startSourceSpan, ast.endSourceSpan);
25751
+ return new DeferredBlockPlaceholder(visitAll(visitor, ast.children, ast.children), minimumTime, ast.nameSpan, ast.sourceSpan, ast.startSourceSpan, ast.endSourceSpan);
25106
25752
  }
25107
25753
  function parseLoadingBlock(ast, visitor) {
25108
25754
  let afterTime = null;
@@ -25132,13 +25778,13 @@ function parseLoadingBlock(ast, visitor) {
25132
25778
  throw new Error(`Unrecognized parameter in @loading block: "${param.expression}"`);
25133
25779
  }
25134
25780
  }
25135
- return new DeferredBlockLoading(visitAll(visitor, ast.children, ast.children), afterTime, minimumTime, ast.sourceSpan, ast.startSourceSpan, ast.endSourceSpan);
25781
+ return new DeferredBlockLoading(visitAll(visitor, ast.children, ast.children), afterTime, minimumTime, ast.nameSpan, ast.sourceSpan, ast.startSourceSpan, ast.endSourceSpan);
25136
25782
  }
25137
25783
  function parseErrorBlock(ast, visitor) {
25138
25784
  if (ast.parameters.length > 0) {
25139
25785
  throw new Error(`@error block cannot have parameters`);
25140
25786
  }
25141
- return new DeferredBlockError(visitAll(visitor, ast.children, ast.children), ast.sourceSpan, ast.startSourceSpan, ast.endSourceSpan);
25787
+ return new DeferredBlockError(visitAll(visitor, ast.children, ast.children), ast.nameSpan, ast.sourceSpan, ast.startSourceSpan, ast.endSourceSpan);
25142
25788
  }
25143
25789
  function parsePrimaryTriggers(params, bindingParser, errors, placeholder) {
25144
25790
  const triggers = {};
@@ -25887,6 +26533,8 @@ const NG_CONTENT_SELECT_ATTR = 'select';
25887
26533
  const NG_PROJECT_AS_ATTR_NAME = 'ngProjectAs';
25888
26534
  // Global symbols available only inside event bindings.
25889
26535
  const EVENT_BINDING_SCOPE_GLOBALS = new Set(['$event']);
26536
+ // Tag name of the `ng-template` element.
26537
+ const NG_TEMPLATE_TAG_NAME = 'ng-template';
25890
26538
  // List of supported global targets for event listeners
25891
26539
  const GLOBAL_TARGET_RESOLVERS = new Map([['window', Identifiers.resolveWindow], ['document', Identifiers.resolveDocument], ['body', Identifiers.resolveBody]]);
25892
26540
  const LEADING_TRIVIA_CHARS = [' ', '\n', '\r', '\t'];
@@ -26658,7 +27306,6 @@ class TemplateDefinitionBuilder {
26658
27306
  // it based on the parent nodes inside the template instruction.
26659
27307
  const tagNameWithoutNamespace = template.tagName ? splitNsName(template.tagName)[1] : template.tagName;
26660
27308
  const contextNameSuffix = template.tagName ? '_' + sanitizeIdentifier(template.tagName) : '';
26661
- const NG_TEMPLATE_TAG_NAME = 'ng-template';
26662
27309
  // prepare attributes parameter (including attributes used for directive matching)
26663
27310
  const attrsExprs = this.getAttributeExpressions(NG_TEMPLATE_TAG_NAME, template.attributes, template.inputs, template.outputs, undefined /* styles */, template.templateAttrs);
26664
27311
  const templateIndex = this.createEmbeddedTemplateFn(tagNameWithoutNamespace, template.children, contextNameSuffix, template.sourceSpan, template.variables, attrsExprs, template.references, template.i18n);
@@ -26761,18 +27408,28 @@ class TemplateDefinitionBuilder {
26761
27408
  // We have to process the block in two steps: once here and again in the update instruction
26762
27409
  // callback in order to generate the correct expressions when pipes or pure functions are
26763
27410
  // used inside the branch expressions.
26764
- const branchData = block.branches.map(({ expression, expressionAlias, children, sourceSpan }) => {
27411
+ const branchData = block.branches.map((branch, branchIndex) => {
27412
+ const { expression, expressionAlias, children, sourceSpan } = branch;
26765
27413
  // If the branch has an alias, it'll be assigned directly to the container's context.
26766
27414
  // We define a variable referring directly to the context so that any nested usages can be
26767
27415
  // rewritten to refer to it.
26768
27416
  const variables = expressionAlias !== null ?
26769
27417
  [new Variable(expressionAlias.name, DIRECT_CONTEXT_REFERENCE, expressionAlias.sourceSpan, expressionAlias.keySpan)] :
26770
27418
  undefined;
27419
+ let tagName = null;
27420
+ let attrsExprs;
27421
+ // Only the first branch can be used for projection, because the conditional
27422
+ // uses the container of the first branch as the insertion point for all branches.
27423
+ if (branchIndex === 0) {
27424
+ const inferredData = this.inferProjectionDataFromInsertionPoint(branch);
27425
+ tagName = inferredData.tagName;
27426
+ attrsExprs = inferredData.attrsExprs;
27427
+ }
26771
27428
  // Note: the template needs to be created *before* we process the expression,
26772
27429
  // otherwise pipes injecting some symbols won't work (see #52102).
26773
- const index = this.createEmbeddedTemplateFn(null, children, '_Conditional', sourceSpan, variables);
27430
+ const templateIndex = this.createEmbeddedTemplateFn(tagName, children, '_Conditional', sourceSpan, variables, attrsExprs);
26774
27431
  const processedExpression = expression === null ? null : expression.visit(this._valueConverter);
26775
- return { index, expression: processedExpression, alias: expressionAlias };
27432
+ return { index: templateIndex, expression: processedExpression, alias: expressionAlias };
26776
27433
  });
26777
27434
  // Use the index of the first block as the index for the entire container.
26778
27435
  const containerIndex = branchData[0].index;
@@ -26994,6 +27651,43 @@ class TemplateDefinitionBuilder {
26994
27651
  return params;
26995
27652
  });
26996
27653
  }
27654
+ /**
27655
+ * Infers the data used for content projection (tag name and attributes) from the content of a
27656
+ * node.
27657
+ * @param node Node for which to infer the projection data.
27658
+ */
27659
+ inferProjectionDataFromInsertionPoint(node) {
27660
+ let root = null;
27661
+ let tagName = null;
27662
+ let attrsExprs;
27663
+ for (const child of node.children) {
27664
+ // Skip over comment nodes.
27665
+ if (child instanceof Comment$1) {
27666
+ continue;
27667
+ }
27668
+ // We can only infer the tag name/attributes if there's a single root node.
27669
+ if (root !== null) {
27670
+ root = null;
27671
+ break;
27672
+ }
27673
+ // Root nodes can only elements or templates with a tag name (e.g. `<div *foo></div>`).
27674
+ if (child instanceof Element$1 || (child instanceof Template && child.tagName !== null)) {
27675
+ root = child;
27676
+ }
27677
+ }
27678
+ // If we've found a single root node, its tag name and *static* attributes can be copied
27679
+ // to the surrounding template to be used for content projection. Note that it's important
27680
+ // that we don't copy any bound attributes since they don't participate in content projection
27681
+ // and they can be used in directive matching (in the case of `Template.templateAttrs`).
27682
+ if (root !== null) {
27683
+ const name = root instanceof Element$1 ? root.name : root.tagName;
27684
+ // Don't pass along `ng-template` tag name since it enables directive matching.
27685
+ tagName = name === NG_TEMPLATE_TAG_NAME ? null : name;
27686
+ attrsExprs =
27687
+ this.getAttributeExpressions(NG_TEMPLATE_TAG_NAME, root.attributes, root.inputs, []);
27688
+ }
27689
+ return { tagName, attrsExprs };
27690
+ }
26997
27691
  allocateDataSlot() {
26998
27692
  return this._dataIndex++;
26999
27693
  }
@@ -27001,6 +27695,7 @@ class TemplateDefinitionBuilder {
27001
27695
  // Allocate one slot for the repeater metadata. The slots for the primary and empty block
27002
27696
  // are implicitly inferred by the runtime to index + 1 and index + 2.
27003
27697
  const blockIndex = this.allocateDataSlot();
27698
+ const { tagName, attrsExprs } = this.inferProjectionDataFromInsertionPoint(block);
27004
27699
  const primaryData = this.prepareEmbeddedTemplateFn(block.children, '_For', [block.item, block.contextVariables.$index, block.contextVariables.$count]);
27005
27700
  const { expression: trackByExpression, usesComponentInstance: trackByUsesComponentInstance } = this.createTrackByFunction(block);
27006
27701
  let emptyData = null;
@@ -27017,6 +27712,8 @@ class TemplateDefinitionBuilder {
27017
27712
  variable(primaryData.name),
27018
27713
  literal(primaryData.getConstCount()),
27019
27714
  literal(primaryData.getVarCount()),
27715
+ literal(tagName),
27716
+ this.addAttrsToConsts(attrsExprs || null),
27020
27717
  trackByExpression,
27021
27718
  ];
27022
27719
  if (emptyData !== null) {
@@ -28251,7 +28948,7 @@ function compileComponentFromMetadata(meta, constantPool, bindingParser) {
28251
28948
  else {
28252
28949
  // This path compiles the template using the prototype template pipeline. First the template is
28253
28950
  // ingested into IR:
28254
- const tpl = ingestComponent(meta.name, meta.template.nodes, constantPool, meta.relativeContextFilePath, meta.i18nUseExternalIds);
28951
+ const tpl = ingestComponent(meta.name, meta.template.nodes, constantPool, meta.relativeContextFilePath, meta.i18nUseExternalIds, meta.deferBlocks);
28255
28952
  // Then the IR is transformed to prepare it for cod egeneration.
28256
28953
  transform(tpl, CompilationJobKind.Tmpl);
28257
28954
  // Finally we emit the template function:
@@ -29549,13 +30246,25 @@ class R3BoundTarget {
29549
30246
  }
29550
30247
  const name = trigger.reference;
29551
30248
  if (name === null) {
29552
- const children = block.placeholder ? block.placeholder.children : null;
29553
- // If the trigger doesn't have a reference, it is inferred as the root element node of the
29554
- // placeholder, if it only has one root node. Otherwise it's ambiguous so we don't
29555
- // attempt to resolve further.
29556
- return children !== null && children.length === 1 && children[0] instanceof Element$1 ?
29557
- children[0] :
29558
- null;
30249
+ let trigger = null;
30250
+ if (block.placeholder !== null) {
30251
+ for (const child of block.placeholder.children) {
30252
+ // Skip over comment nodes. Currently by default the template parser doesn't capture
30253
+ // comments, but we have a safeguard here just in case since it can be enabled.
30254
+ if (child instanceof Comment$1) {
30255
+ continue;
30256
+ }
30257
+ // We can only infer the trigger if there's one root element node. Any other
30258
+ // nodes at the root make it so that we can't infer the trigger anymore.
30259
+ if (trigger !== null) {
30260
+ return null;
30261
+ }
30262
+ if (child instanceof Element$1) {
30263
+ trigger = child;
30264
+ }
30265
+ }
30266
+ }
30267
+ return trigger;
29559
30268
  }
29560
30269
  const outsideRef = this.findEntityInScope(block, name);
29561
30270
  // First try to resolve the target in the scope of the main deferred block. Note that we
@@ -29567,7 +30276,7 @@ class R3BoundTarget {
29567
30276
  }
29568
30277
  }
29569
30278
  // If the trigger couldn't be found in the main block, check the
29570
- // placeholder block which is shown before the main block has loaded.
30279
+ // placeholder block which is shown before the main block has loaded.
29571
30280
  if (block.placeholder !== null) {
29572
30281
  const refInPlaceholder = this.findEntityInScope(block.placeholder, name);
29573
30282
  const targetInPlaceholder = refInPlaceholder instanceof Reference ? this.getReferenceTarget(refInPlaceholder) : null;
@@ -30262,7 +30971,7 @@ function publishFacade(global) {
30262
30971
  * @description
30263
30972
  * Entry point for all public APIs of the compiler package.
30264
30973
  */
30265
- const VERSION = new Version('17.0.0-rc.1');
30974
+ const VERSION = new Version('17.0.0-rc.3');
30266
30975
 
30267
30976
  class CompilerConfig {
30268
30977
  constructor({ defaultEncapsulation = ViewEncapsulation.Emulated, preserveWhitespaces, strictInjectionParameters } = {}) {
@@ -31792,7 +32501,7 @@ const MINIMUM_PARTIAL_LINKER_VERSION$6 = '12.0.0';
31792
32501
  function compileDeclareClassMetadata(metadata) {
31793
32502
  const definitionMap = new DefinitionMap();
31794
32503
  definitionMap.set('minVersion', literal(MINIMUM_PARTIAL_LINKER_VERSION$6));
31795
- definitionMap.set('version', literal('17.0.0-rc.1'));
32504
+ definitionMap.set('version', literal('17.0.0-rc.3'));
31796
32505
  definitionMap.set('ngImport', importExpr(Identifiers.core));
31797
32506
  definitionMap.set('type', metadata.type);
31798
32507
  definitionMap.set('decorators', metadata.decorators);
@@ -31900,7 +32609,7 @@ function createDirectiveDefinitionMap(meta) {
31900
32609
  // in 16.1 is actually used.
31901
32610
  const minVersion = hasTransformFunctions ? MINIMUM_PARTIAL_LINKER_VERSION$5 : '14.0.0';
31902
32611
  definitionMap.set('minVersion', literal(minVersion));
31903
- definitionMap.set('version', literal('17.0.0-rc.1'));
32612
+ definitionMap.set('version', literal('17.0.0-rc.3'));
31904
32613
  // e.g. `type: MyDirective`
31905
32614
  definitionMap.set('type', meta.type.value);
31906
32615
  if (meta.isStandalone) {
@@ -32177,7 +32886,7 @@ const MINIMUM_PARTIAL_LINKER_VERSION$4 = '12.0.0';
32177
32886
  function compileDeclareFactoryFunction(meta) {
32178
32887
  const definitionMap = new DefinitionMap();
32179
32888
  definitionMap.set('minVersion', literal(MINIMUM_PARTIAL_LINKER_VERSION$4));
32180
- definitionMap.set('version', literal('17.0.0-rc.1'));
32889
+ definitionMap.set('version', literal('17.0.0-rc.3'));
32181
32890
  definitionMap.set('ngImport', importExpr(Identifiers.core));
32182
32891
  definitionMap.set('type', meta.type.value);
32183
32892
  definitionMap.set('deps', compileDependencies(meta.deps));
@@ -32212,7 +32921,7 @@ function compileDeclareInjectableFromMetadata(meta) {
32212
32921
  function createInjectableDefinitionMap(meta) {
32213
32922
  const definitionMap = new DefinitionMap();
32214
32923
  definitionMap.set('minVersion', literal(MINIMUM_PARTIAL_LINKER_VERSION$3));
32215
- definitionMap.set('version', literal('17.0.0-rc.1'));
32924
+ definitionMap.set('version', literal('17.0.0-rc.3'));
32216
32925
  definitionMap.set('ngImport', importExpr(Identifiers.core));
32217
32926
  definitionMap.set('type', meta.type.value);
32218
32927
  // Only generate providedIn property if it has a non-null value
@@ -32263,7 +32972,7 @@ function compileDeclareInjectorFromMetadata(meta) {
32263
32972
  function createInjectorDefinitionMap(meta) {
32264
32973
  const definitionMap = new DefinitionMap();
32265
32974
  definitionMap.set('minVersion', literal(MINIMUM_PARTIAL_LINKER_VERSION$2));
32266
- definitionMap.set('version', literal('17.0.0-rc.1'));
32975
+ definitionMap.set('version', literal('17.0.0-rc.3'));
32267
32976
  definitionMap.set('ngImport', importExpr(Identifiers.core));
32268
32977
  definitionMap.set('type', meta.type.value);
32269
32978
  definitionMap.set('providers', meta.providers);
@@ -32296,7 +33005,7 @@ function createNgModuleDefinitionMap(meta) {
32296
33005
  throw new Error('Invalid path! Local compilation mode should not get into the partial compilation path');
32297
33006
  }
32298
33007
  definitionMap.set('minVersion', literal(MINIMUM_PARTIAL_LINKER_VERSION$1));
32299
- definitionMap.set('version', literal('17.0.0-rc.1'));
33008
+ definitionMap.set('version', literal('17.0.0-rc.3'));
32300
33009
  definitionMap.set('ngImport', importExpr(Identifiers.core));
32301
33010
  definitionMap.set('type', meta.type.value);
32302
33011
  // We only generate the keys in the metadata if the arrays contain values.
@@ -32347,7 +33056,7 @@ function compileDeclarePipeFromMetadata(meta) {
32347
33056
  function createPipeDefinitionMap(meta) {
32348
33057
  const definitionMap = new DefinitionMap();
32349
33058
  definitionMap.set('minVersion', literal(MINIMUM_PARTIAL_LINKER_VERSION));
32350
- definitionMap.set('version', literal('17.0.0-rc.1'));
33059
+ definitionMap.set('version', literal('17.0.0-rc.3'));
32351
33060
  definitionMap.set('ngImport', importExpr(Identifiers.core));
32352
33061
  // e.g. `type: MyPipe`
32353
33062
  definitionMap.set('type', meta.type.value);