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

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 (90) 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/t2_api.mjs +1 -1
  13. package/esm2022/src/render3/view/t2_binder.mjs +22 -10
  14. package/esm2022/src/render3/view/template.mjs +54 -5
  15. package/esm2022/src/template/pipeline/ir/index.mjs +2 -1
  16. package/esm2022/src/template/pipeline/ir/src/enums.mjs +75 -20
  17. package/esm2022/src/template/pipeline/ir/src/expression.mjs +74 -71
  18. package/esm2022/src/template/pipeline/ir/src/handle.mjs +13 -0
  19. package/esm2022/src/template/pipeline/ir/src/ops/create.mjs +47 -47
  20. package/esm2022/src/template/pipeline/ir/src/ops/update.mjs +10 -9
  21. package/esm2022/src/template/pipeline/ir/src/traits.mjs +1 -32
  22. package/esm2022/src/template/pipeline/src/conversion.mjs +2 -2
  23. package/esm2022/src/template/pipeline/src/emit.mjs +117 -107
  24. package/esm2022/src/template/pipeline/src/ingest.mjs +155 -49
  25. package/esm2022/src/template/pipeline/src/instruction.mjs +36 -12
  26. package/esm2022/src/template/pipeline/src/phases/any_cast.mjs +4 -3
  27. package/esm2022/src/template/pipeline/src/phases/apply_i18n_expressions.mjs +3 -3
  28. package/esm2022/src/template/pipeline/src/phases/assign_i18n_slot_dependencies.mjs +2 -2
  29. package/esm2022/src/template/pipeline/src/phases/attribute_extraction.mjs +2 -2
  30. package/esm2022/src/template/pipeline/src/phases/binding_specialization.mjs +2 -2
  31. package/esm2022/src/template/pipeline/src/phases/chaining.mjs +2 -2
  32. package/esm2022/src/template/pipeline/src/phases/collapse_singleton_interpolations.mjs +2 -2
  33. package/esm2022/src/template/pipeline/src/phases/conditionals.mjs +6 -6
  34. package/esm2022/src/template/pipeline/src/phases/const_collection.mjs +2 -2
  35. package/esm2022/src/template/pipeline/src/phases/defer_configs.mjs +30 -0
  36. package/esm2022/src/template/pipeline/src/phases/defer_resolve_targets.mjs +93 -0
  37. package/esm2022/src/template/pipeline/src/phases/empty_elements.mjs +4 -4
  38. package/esm2022/src/template/pipeline/src/phases/expand_safe_reads.mjs +7 -4
  39. package/esm2022/src/template/pipeline/src/phases/format_i18n_params.mjs +114 -0
  40. package/esm2022/src/template/pipeline/src/phases/generate_advance.mjs +4 -4
  41. package/esm2022/src/template/pipeline/src/phases/generate_projection_def.mjs +2 -2
  42. package/esm2022/src/template/pipeline/src/phases/generate_variables.mjs +4 -3
  43. package/esm2022/src/template/pipeline/src/phases/has_const_expression_collection.mjs +28 -0
  44. package/esm2022/src/template/pipeline/src/phases/host_style_property_parsing.mjs +8 -2
  45. package/esm2022/src/template/pipeline/src/phases/i18n_const_collection.mjs +129 -7
  46. package/esm2022/src/template/pipeline/src/phases/i18n_message_extraction.mjs +4 -105
  47. package/esm2022/src/template/pipeline/src/phases/i18n_text_extraction.mjs +9 -6
  48. package/esm2022/src/template/pipeline/src/phases/icu_extraction.mjs +8 -5
  49. package/esm2022/src/template/pipeline/src/phases/local_refs.mjs +2 -2
  50. package/esm2022/src/template/pipeline/src/phases/namespace.mjs +2 -2
  51. package/esm2022/src/template/pipeline/src/phases/naming.mjs +11 -13
  52. package/esm2022/src/template/pipeline/src/phases/next_context_merging.mjs +2 -2
  53. package/esm2022/src/template/pipeline/src/phases/ng_container.mjs +2 -2
  54. package/esm2022/src/template/pipeline/src/phases/nonbindable.mjs +2 -2
  55. package/esm2022/src/template/pipeline/src/phases/nullish_coalescing.mjs +8 -2
  56. package/esm2022/src/template/pipeline/src/phases/ordering.mjs +7 -2
  57. package/esm2022/src/template/pipeline/src/phases/parse_extracted_styles.mjs +3 -3
  58. package/esm2022/src/template/pipeline/src/phases/phase_remove_content_selectors.mjs +2 -2
  59. package/esm2022/src/template/pipeline/src/phases/pipe_creation.mjs +16 -11
  60. package/esm2022/src/template/pipeline/src/phases/pipe_variadic.mjs +7 -3
  61. package/esm2022/src/template/pipeline/src/phases/propagate_i18n_blocks.mjs +2 -2
  62. package/esm2022/src/template/pipeline/src/phases/propagate_i18n_placeholders.mjs +61 -0
  63. package/esm2022/src/template/pipeline/src/phases/pure_function_extraction.mjs +2 -2
  64. package/esm2022/src/template/pipeline/src/phases/pure_literal_structures.mjs +4 -4
  65. package/esm2022/src/template/pipeline/src/phases/reify.mjs +49 -29
  66. package/esm2022/src/template/pipeline/src/phases/remove_empty_bindings.mjs +5 -2
  67. package/esm2022/src/template/pipeline/src/phases/repeater_derived_vars.mjs +7 -2
  68. package/esm2022/src/template/pipeline/src/phases/resolve_contexts.mjs +3 -3
  69. package/esm2022/src/template/pipeline/src/phases/resolve_dollar_event.mjs +5 -5
  70. package/esm2022/src/template/pipeline/src/phases/resolve_i18n_element_placeholders.mjs +109 -0
  71. package/esm2022/src/template/pipeline/src/phases/resolve_i18n_expression_placeholders.mjs +58 -0
  72. package/esm2022/src/template/pipeline/src/phases/resolve_names.mjs +3 -3
  73. package/esm2022/src/template/pipeline/src/phases/resolve_sanitizers.mjs +2 -2
  74. package/esm2022/src/template/pipeline/src/phases/save_restore_view.mjs +14 -9
  75. package/esm2022/src/template/pipeline/src/phases/slot_allocation.mjs +4 -29
  76. package/esm2022/src/template/pipeline/src/phases/style_binding_specialization.mjs +3 -3
  77. package/esm2022/src/template/pipeline/src/phases/temporary_variables.mjs +3 -3
  78. package/esm2022/src/template/pipeline/src/phases/track_fn_generation.mjs +2 -2
  79. package/esm2022/src/template/pipeline/src/phases/track_fn_optimization.mjs +8 -2
  80. package/esm2022/src/template/pipeline/src/phases/track_variables.mjs +7 -2
  81. package/esm2022/src/template/pipeline/src/phases/var_counting.mjs +2 -2
  82. package/esm2022/src/template/pipeline/src/phases/variable_optimization.mjs +5 -5
  83. package/esm2022/src/template/pipeline/src/phases/wrap_icus.mjs +2 -2
  84. package/esm2022/src/version.mjs +1 -1
  85. package/fesm2022/compiler.mjs +1467 -1017
  86. package/fesm2022/compiler.mjs.map +1 -1
  87. package/index.d.ts +41 -58
  88. package/package.json +3 -3
  89. package/esm2022/src/template/pipeline/src/phases/has_const_trait_collection.mjs +0 -34
  90. 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.2
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);
@@ -8799,70 +8793,66 @@ var OpKind;
8799
8793
  * An operation that configures a `@defer` block.
8800
8794
  */
8801
8795
  OpKind[OpKind["Defer"] = 26] = "Defer";
8802
- /**
8803
- * An IR operation that provides secondary templates of a `@defer` block.
8804
- */
8805
- OpKind[OpKind["DeferSecondaryBlock"] = 27] = "DeferSecondaryBlock";
8806
8796
  /**
8807
8797
  * An operation that controls when a `@defer` loads.
8808
8798
  */
8809
- OpKind[OpKind["DeferOn"] = 28] = "DeferOn";
8799
+ OpKind[OpKind["DeferOn"] = 27] = "DeferOn";
8810
8800
  /**
8811
8801
  * An i18n message that has been extracted for inclusion in the consts array.
8812
8802
  */
8813
- OpKind[OpKind["ExtractedMessage"] = 29] = "ExtractedMessage";
8803
+ OpKind[OpKind["ExtractedMessage"] = 28] = "ExtractedMessage";
8814
8804
  /**
8815
8805
  * A host binding property.
8816
8806
  */
8817
- OpKind[OpKind["HostProperty"] = 30] = "HostProperty";
8807
+ OpKind[OpKind["HostProperty"] = 29] = "HostProperty";
8818
8808
  /**
8819
8809
  * A namespace change, which causes the subsequent elements to be processed as either HTML or SVG.
8820
8810
  */
8821
- OpKind[OpKind["Namespace"] = 31] = "Namespace";
8811
+ OpKind[OpKind["Namespace"] = 30] = "Namespace";
8822
8812
  /**
8823
8813
  * Configure a content projeciton definition for the view.
8824
8814
  */
8825
- OpKind[OpKind["ProjectionDef"] = 32] = "ProjectionDef";
8815
+ OpKind[OpKind["ProjectionDef"] = 31] = "ProjectionDef";
8826
8816
  /**
8827
8817
  * Create a content projection slot.
8828
8818
  */
8829
- OpKind[OpKind["Projection"] = 33] = "Projection";
8819
+ OpKind[OpKind["Projection"] = 32] = "Projection";
8830
8820
  /**
8831
8821
  * Create a repeater creation instruction op.
8832
8822
  */
8833
- OpKind[OpKind["RepeaterCreate"] = 34] = "RepeaterCreate";
8823
+ OpKind[OpKind["RepeaterCreate"] = 33] = "RepeaterCreate";
8834
8824
  /**
8835
8825
  * An update up for a repeater.
8836
8826
  */
8837
- OpKind[OpKind["Repeater"] = 35] = "Repeater";
8827
+ OpKind[OpKind["Repeater"] = 34] = "Repeater";
8838
8828
  /**
8839
8829
  * The start of an i18n block.
8840
8830
  */
8841
- OpKind[OpKind["I18nStart"] = 36] = "I18nStart";
8831
+ OpKind[OpKind["I18nStart"] = 35] = "I18nStart";
8842
8832
  /**
8843
8833
  * A self-closing i18n on a single element.
8844
8834
  */
8845
- OpKind[OpKind["I18n"] = 37] = "I18n";
8835
+ OpKind[OpKind["I18n"] = 36] = "I18n";
8846
8836
  /**
8847
8837
  * The end of an i18n block.
8848
8838
  */
8849
- OpKind[OpKind["I18nEnd"] = 38] = "I18nEnd";
8839
+ OpKind[OpKind["I18nEnd"] = 37] = "I18nEnd";
8850
8840
  /**
8851
8841
  * An expression in an i18n message.
8852
8842
  */
8853
- OpKind[OpKind["I18nExpression"] = 39] = "I18nExpression";
8843
+ OpKind[OpKind["I18nExpression"] = 38] = "I18nExpression";
8854
8844
  /**
8855
8845
  * An instruction that applies a set of i18n expressions.
8856
8846
  */
8857
- OpKind[OpKind["I18nApply"] = 40] = "I18nApply";
8847
+ OpKind[OpKind["I18nApply"] = 39] = "I18nApply";
8858
8848
  /**
8859
8849
  * An instruction to create an ICU expression.
8860
8850
  */
8861
- OpKind[OpKind["Icu"] = 41] = "Icu";
8851
+ OpKind[OpKind["Icu"] = 40] = "Icu";
8862
8852
  /**
8863
8853
  * An instruction to update an ICU expression.
8864
8854
  */
8865
- OpKind[OpKind["IcuUpdate"] = 42] = "IcuUpdate";
8855
+ OpKind[OpKind["IcuUpdate"] = 41] = "IcuUpdate";
8866
8856
  })(OpKind || (OpKind = {}));
8867
8857
  /**
8868
8858
  * Distinguishes different kinds of IR expressions.
@@ -8966,6 +8956,10 @@ var ExpressionKind;
8966
8956
  * properties ($even, $first, etc.).
8967
8957
  */
8968
8958
  ExpressionKind[ExpressionKind["DerivedRepeaterVar"] = 23] = "DerivedRepeaterVar";
8959
+ /**
8960
+ * An expression that will be automatically extracted to the component const array.
8961
+ */
8962
+ ExpressionKind[ExpressionKind["ConstCollected"] = 24] = "ConstCollected";
8969
8963
  })(ExpressionKind || (ExpressionKind = {}));
8970
8964
  var VariableFlags;
8971
8965
  (function (VariableFlags) {
@@ -9080,6 +9074,61 @@ var I18nParamResolutionTime;
9080
9074
  */
9081
9075
  I18nParamResolutionTime[I18nParamResolutionTime["Postproccessing"] = 1] = "Postproccessing";
9082
9076
  })(I18nParamResolutionTime || (I18nParamResolutionTime = {}));
9077
+ /**
9078
+ * Flags that describe what an i18n param value. These determine how the value is serialized into
9079
+ * the final map.
9080
+ */
9081
+ var I18nParamValueFlags;
9082
+ (function (I18nParamValueFlags) {
9083
+ I18nParamValueFlags[I18nParamValueFlags["None"] = 0] = "None";
9084
+ /**
9085
+ * This value represtents an element tag.
9086
+ */
9087
+ I18nParamValueFlags[I18nParamValueFlags["ElementTag"] = 1] = "ElementTag";
9088
+ /**
9089
+ * This value represents a template tag.
9090
+ */
9091
+ I18nParamValueFlags[I18nParamValueFlags["TemplateTag"] = 2] = "TemplateTag";
9092
+ /**
9093
+ * This value represents the opening of a tag.
9094
+ */
9095
+ I18nParamValueFlags[I18nParamValueFlags["OpenTag"] = 4] = "OpenTag";
9096
+ /**
9097
+ * This value represents the closing of a tag.
9098
+ */
9099
+ I18nParamValueFlags[I18nParamValueFlags["CloseTag"] = 8] = "CloseTag";
9100
+ })(I18nParamValueFlags || (I18nParamValueFlags = {}));
9101
+ /**
9102
+ * Whether the active namespace is HTML, MathML, or SVG mode.
9103
+ */
9104
+ var Namespace;
9105
+ (function (Namespace) {
9106
+ Namespace[Namespace["HTML"] = 0] = "HTML";
9107
+ Namespace[Namespace["SVG"] = 1] = "SVG";
9108
+ Namespace[Namespace["Math"] = 2] = "Math";
9109
+ })(Namespace || (Namespace = {}));
9110
+ /**
9111
+ * The type of a `@defer` trigger, for use in the ir.
9112
+ */
9113
+ var DeferTriggerKind;
9114
+ (function (DeferTriggerKind) {
9115
+ DeferTriggerKind[DeferTriggerKind["Idle"] = 0] = "Idle";
9116
+ DeferTriggerKind[DeferTriggerKind["Immediate"] = 1] = "Immediate";
9117
+ DeferTriggerKind[DeferTriggerKind["Timer"] = 2] = "Timer";
9118
+ DeferTriggerKind[DeferTriggerKind["Hover"] = 3] = "Hover";
9119
+ DeferTriggerKind[DeferTriggerKind["Interaction"] = 4] = "Interaction";
9120
+ DeferTriggerKind[DeferTriggerKind["Viewport"] = 5] = "Viewport";
9121
+ })(DeferTriggerKind || (DeferTriggerKind = {}));
9122
+ /**
9123
+ * Repeaters implicitly define these derived variables, and child nodes may read them.
9124
+ */
9125
+ var DerivedRepeaterVarIdentity;
9126
+ (function (DerivedRepeaterVarIdentity) {
9127
+ DerivedRepeaterVarIdentity[DerivedRepeaterVarIdentity["First"] = 0] = "First";
9128
+ DerivedRepeaterVarIdentity[DerivedRepeaterVarIdentity["Last"] = 1] = "Last";
9129
+ DerivedRepeaterVarIdentity[DerivedRepeaterVarIdentity["Even"] = 2] = "Even";
9130
+ DerivedRepeaterVarIdentity[DerivedRepeaterVarIdentity["Odd"] = 3] = "Odd";
9131
+ })(DerivedRepeaterVarIdentity || (DerivedRepeaterVarIdentity = {}));
9083
9132
 
9084
9133
  /**
9085
9134
  * Marker symbol for `ConsumesSlotOpTrait`.
@@ -9089,10 +9138,6 @@ const ConsumesSlot = Symbol('ConsumesSlot');
9089
9138
  * Marker symbol for `DependsOnSlotContextOpTrait`.
9090
9139
  */
9091
9140
  const DependsOnSlotContext = Symbol('DependsOnSlotContext');
9092
- /**
9093
- * Marker symbol for `UsesSlotIndex` trait.
9094
- */
9095
- const UsesSlotIndex = Symbol('UsesSlotIndex');
9096
9141
  /**
9097
9142
  * Marker symbol for `ConsumesVars` trait.
9098
9143
  */
@@ -9101,27 +9146,14 @@ const ConsumesVarsTrait = Symbol('ConsumesVars');
9101
9146
  * Marker symbol for `UsesVarOffset` trait.
9102
9147
  */
9103
9148
  const UsesVarOffset = Symbol('UsesVarOffset');
9104
- /**
9105
- * Marker symbol for `HasConst` trait.
9106
- */
9107
- const HasConst = Symbol('HasConst');
9108
9149
  /**
9109
9150
  * Default values for most `ConsumesSlotOpTrait` fields (used with the spread operator to initialize
9110
9151
  * implementors of the trait).
9111
9152
  */
9112
9153
  const TRAIT_CONSUMES_SLOT = {
9113
9154
  [ConsumesSlot]: true,
9114
- slot: null,
9115
9155
  numSlotsUsed: 1,
9116
9156
  };
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
9157
  /**
9126
9158
  * Default values for most `DependsOnSlotContextOpTrait` fields (used with the spread operator to
9127
9159
  * initialize implementors of the trait).
@@ -9144,14 +9176,6 @@ const TRAIT_USES_VAR_OFFSET = {
9144
9176
  [UsesVarOffset]: true,
9145
9177
  varOffset: null,
9146
9178
  };
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
9179
  /**
9156
9180
  * Test whether an operation implements `ConsumesSlotOpTrait`.
9157
9181
  */
@@ -9173,12 +9197,6 @@ function hasConsumesVarsTrait(value) {
9173
9197
  function hasUsesVarOffsetTrait(expr) {
9174
9198
  return expr[UsesVarOffset] === true;
9175
9199
  }
9176
- function hasUsesSlotIndexTrait(value) {
9177
- return value[UsesSlotIndex] === true;
9178
- }
9179
- function hasConstTrait(value) {
9180
- return value[HasConst] === true;
9181
- }
9182
9200
 
9183
9201
  /**
9184
9202
  * Create a `StatementOp`.
@@ -9361,38 +9379,39 @@ function createAdvanceOp(delta, sourceSpan) {
9361
9379
  /**
9362
9380
  * Create a conditional op, which will display an embedded view according to a condtion.
9363
9381
  */
9364
- function createConditionalOp(target, test, conditions, sourceSpan) {
9382
+ function createConditionalOp(target, targetSlot, test, conditions, sourceSpan) {
9365
9383
  return {
9366
9384
  kind: OpKind.Conditional,
9367
9385
  target,
9386
+ targetSlot,
9368
9387
  test,
9369
9388
  conditions,
9370
9389
  processed: null,
9371
9390
  sourceSpan,
9372
9391
  contextValue: null,
9373
9392
  ...NEW_OP,
9374
- ...TRAIT_USES_SLOT_INDEX,
9375
9393
  ...TRAIT_DEPENDS_ON_SLOT_CONTEXT,
9376
9394
  ...TRAIT_CONSUMES_VARS,
9377
9395
  };
9378
9396
  }
9379
- function createRepeaterOp(repeaterCreate, collection, sourceSpan) {
9397
+ function createRepeaterOp(repeaterCreate, targetSlot, collection, sourceSpan) {
9380
9398
  return {
9381
9399
  kind: OpKind.Repeater,
9382
9400
  target: repeaterCreate,
9401
+ targetSlot,
9383
9402
  collection,
9384
9403
  sourceSpan,
9385
9404
  ...NEW_OP,
9386
- ...TRAIT_USES_SLOT_INDEX,
9387
9405
  };
9388
9406
  }
9389
9407
  /**
9390
9408
  * Create an i18n expression op.
9391
9409
  */
9392
- function createI18nExpressionOp(owner, expression, i18nPlaceholder, resolutionTime, sourceSpan) {
9410
+ function createI18nExpressionOp(owner, ownerSlot, expression, i18nPlaceholder, resolutionTime, sourceSpan) {
9393
9411
  return {
9394
9412
  kind: OpKind.I18nExpression,
9395
9413
  owner,
9414
+ ownerSlot,
9396
9415
  target: owner,
9397
9416
  expression,
9398
9417
  i18nPlaceholder,
@@ -9406,13 +9425,13 @@ function createI18nExpressionOp(owner, expression, i18nPlaceholder, resolutionTi
9406
9425
  /**
9407
9426
  *Creates an op to apply i18n expression ops
9408
9427
  */
9409
- function createI18nApplyOp(target, sourceSpan) {
9428
+ function createI18nApplyOp(target, targetSlot, sourceSpan) {
9410
9429
  return {
9411
9430
  kind: OpKind.I18nApply,
9412
9431
  target,
9432
+ targetSlot,
9413
9433
  sourceSpan,
9414
9434
  ...NEW_OP,
9415
- ...TRAIT_USES_SLOT_INDEX,
9416
9435
  };
9417
9436
  }
9418
9437
  /**
@@ -9427,7 +9446,7 @@ function createIcuUpdateOp(xref, sourceSpan) {
9427
9446
  };
9428
9447
  }
9429
9448
 
9430
- var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k;
9449
+ var _a, _b, _c, _d, _e, _f;
9431
9450
  /**
9432
9451
  * Check whether a given `o.Expression` is a logical IR expression type.
9433
9452
  */
@@ -9470,14 +9489,12 @@ class LexicalReadExpr extends ExpressionBase {
9470
9489
  * Runtime operation to retrieve the value of a local reference.
9471
9490
  */
9472
9491
  class ReferenceExpr extends ExpressionBase {
9473
- static { _a = UsesSlotIndex; }
9474
- constructor(target, offset) {
9492
+ constructor(target, targetSlot, offset) {
9475
9493
  super();
9476
9494
  this.target = target;
9495
+ this.targetSlot = targetSlot;
9477
9496
  this.offset = offset;
9478
9497
  this.kind = ExpressionKind.Reference;
9479
- this[_a] = true;
9480
- this.targetSlot = null;
9481
9498
  }
9482
9499
  visitExpression() { }
9483
9500
  isEquivalent(e) {
@@ -9488,9 +9505,7 @@ class ReferenceExpr extends ExpressionBase {
9488
9505
  }
9489
9506
  transformInternalExpressions() { }
9490
9507
  clone() {
9491
- const expr = new ReferenceExpr(this.target, this.offset);
9492
- expr.targetSlot = this.targetSlot;
9493
- return expr;
9508
+ return new ReferenceExpr(this.target, this.targetSlot, this.offset);
9494
9509
  }
9495
9510
  }
9496
9511
  /**
@@ -9668,12 +9683,12 @@ class ReadVariableExpr extends ExpressionBase {
9668
9683
  }
9669
9684
  }
9670
9685
  class PureFunctionExpr extends ExpressionBase {
9671
- static { _b = ConsumesVarsTrait, _c = UsesVarOffset; }
9686
+ static { _a = ConsumesVarsTrait, _b = UsesVarOffset; }
9672
9687
  constructor(expression, args) {
9673
9688
  super();
9674
9689
  this.kind = ExpressionKind.PureFunctionExpr;
9690
+ this[_a] = true;
9675
9691
  this[_b] = true;
9676
- this[_c] = true;
9677
9692
  this.varOffset = null;
9678
9693
  /**
9679
9694
  * Once extracted to the `ConstantPool`, a reference to the function which defines the computation
@@ -9737,17 +9752,16 @@ class PureFunctionParameterExpr extends ExpressionBase {
9737
9752
  }
9738
9753
  }
9739
9754
  class PipeBindingExpr extends ExpressionBase {
9740
- static { _d = UsesSlotIndex, _e = ConsumesVarsTrait, _f = UsesVarOffset; }
9741
- constructor(target, name, args) {
9755
+ static { _c = ConsumesVarsTrait, _d = UsesVarOffset; }
9756
+ constructor(target, targetSlot, name, args) {
9742
9757
  super();
9743
9758
  this.target = target;
9759
+ this.targetSlot = targetSlot;
9744
9760
  this.name = name;
9745
9761
  this.args = args;
9746
9762
  this.kind = ExpressionKind.PipeBinding;
9763
+ this[_c] = true;
9747
9764
  this[_d] = true;
9748
- this[_e] = true;
9749
- this[_f] = true;
9750
- this.targetSlot = null;
9751
9765
  this.varOffset = null;
9752
9766
  }
9753
9767
  visitExpression(visitor, context) {
@@ -9767,25 +9781,23 @@ class PipeBindingExpr extends ExpressionBase {
9767
9781
  }
9768
9782
  }
9769
9783
  clone() {
9770
- const r = new PipeBindingExpr(this.target, this.name, this.args.map(a => a.clone()));
9771
- r.targetSlot = this.targetSlot;
9784
+ const r = new PipeBindingExpr(this.target, this.targetSlot, this.name, this.args.map(a => a.clone()));
9772
9785
  r.varOffset = this.varOffset;
9773
9786
  return r;
9774
9787
  }
9775
9788
  }
9776
9789
  class PipeBindingVariadicExpr extends ExpressionBase {
9777
- static { _g = UsesSlotIndex, _h = ConsumesVarsTrait, _j = UsesVarOffset; }
9778
- constructor(target, name, args, numArgs) {
9790
+ static { _e = ConsumesVarsTrait, _f = UsesVarOffset; }
9791
+ constructor(target, targetSlot, name, args, numArgs) {
9779
9792
  super();
9780
9793
  this.target = target;
9794
+ this.targetSlot = targetSlot;
9781
9795
  this.name = name;
9782
9796
  this.args = args;
9783
9797
  this.numArgs = numArgs;
9784
9798
  this.kind = ExpressionKind.PipeBindingVariadic;
9785
- this[_g] = true;
9786
- this[_h] = true;
9787
- this[_j] = true;
9788
- this.targetSlot = null;
9799
+ this[_e] = true;
9800
+ this[_f] = true;
9789
9801
  this.varOffset = null;
9790
9802
  }
9791
9803
  visitExpression(visitor, context) {
@@ -9801,8 +9813,7 @@ class PipeBindingVariadicExpr extends ExpressionBase {
9801
9813
  this.args = transformExpressionsInExpression(this.args, transform, flags);
9802
9814
  }
9803
9815
  clone() {
9804
- const r = new PipeBindingVariadicExpr(this.target, this.name, this.args.clone(), this.numArgs);
9805
- r.targetSlot = this.targetSlot;
9816
+ const r = new PipeBindingVariadicExpr(this.target, this.targetSlot, this.name, this.args.clone(), this.numArgs);
9806
9817
  r.varOffset = this.varOffset;
9807
9818
  return r;
9808
9819
  }
@@ -9996,26 +10007,20 @@ class SanitizerExpr extends ExpressionBase {
9996
10007
  transformInternalExpressions() { }
9997
10008
  }
9998
10009
  class SlotLiteralExpr extends ExpressionBase {
9999
- static { _k = UsesSlotIndex; }
10000
- constructor(target) {
10010
+ constructor(slot) {
10001
10011
  super();
10002
- this.target = target;
10012
+ this.slot = slot;
10003
10013
  this.kind = ExpressionKind.SlotLiteralExpr;
10004
- this[_k] = true;
10005
- this.targetSlot = null;
10006
10014
  }
10007
10015
  visitExpression(visitor, context) { }
10008
10016
  isEquivalent(e) {
10009
- return e instanceof SlotLiteralExpr && e.target === this.target &&
10010
- e.targetSlot === this.targetSlot;
10017
+ return e instanceof SlotLiteralExpr && e.slot === this.slot;
10011
10018
  }
10012
10019
  isConstant() {
10013
10020
  return true;
10014
10021
  }
10015
10022
  clone() {
10016
- const copy = new SlotLiteralExpr(this.target);
10017
- copy.targetSlot = this.targetSlot;
10018
- return copy;
10023
+ return new SlotLiteralExpr(this.slot);
10019
10024
  }
10020
10025
  transformInternalExpressions() { }
10021
10026
  }
@@ -10025,10 +10030,11 @@ class ConditionalCaseExpr extends ExpressionBase {
10025
10030
  * @param expr The expression to be tested for this case. Might be null, as in an `else` case.
10026
10031
  * @param target The Xref of the view to be displayed if this condition is true.
10027
10032
  */
10028
- constructor(expr, target, alias = null) {
10033
+ constructor(expr, target, targetSlot, alias = null) {
10029
10034
  super();
10030
10035
  this.expr = expr;
10031
10036
  this.target = target;
10037
+ this.targetSlot = targetSlot;
10032
10038
  this.alias = alias;
10033
10039
  this.kind = ExpressionKind.ConditionalCase;
10034
10040
  }
@@ -10044,7 +10050,7 @@ class ConditionalCaseExpr extends ExpressionBase {
10044
10050
  return true;
10045
10051
  }
10046
10052
  clone() {
10047
- return new ConditionalCaseExpr(this.expr, this.target);
10053
+ return new ConditionalCaseExpr(this.expr, this.target, this.targetSlot);
10048
10054
  }
10049
10055
  transformInternalExpressions(transform, flags) {
10050
10056
  if (this.expr !== null) {
@@ -10052,13 +10058,6 @@ class ConditionalCaseExpr extends ExpressionBase {
10052
10058
  }
10053
10059
  }
10054
10060
  }
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
10061
  class DerivedRepeaterVarExpr extends ExpressionBase {
10063
10062
  constructor(xref, identity) {
10064
10063
  super();
@@ -10079,6 +10078,31 @@ class DerivedRepeaterVarExpr extends ExpressionBase {
10079
10078
  return new DerivedRepeaterVarExpr(this.xref, this.identity);
10080
10079
  }
10081
10080
  }
10081
+ class ConstCollectedExpr extends ExpressionBase {
10082
+ constructor(expr) {
10083
+ super();
10084
+ this.expr = expr;
10085
+ this.kind = ExpressionKind.ConstCollected;
10086
+ }
10087
+ transformInternalExpressions(transform, flags) {
10088
+ this.expr = transform(this.expr, flags);
10089
+ }
10090
+ visitExpression(visitor, context) {
10091
+ this.expr.visitExpression(visitor, context);
10092
+ }
10093
+ isEquivalent(e) {
10094
+ if (!(e instanceof ConstCollectedExpr)) {
10095
+ return false;
10096
+ }
10097
+ return this.expr.isEquivalent(e.expr);
10098
+ }
10099
+ isConstant() {
10100
+ return this.expr.isConstant();
10101
+ }
10102
+ clone() {
10103
+ return new ConstCollectedExpr(this.expr);
10104
+ }
10105
+ }
10082
10106
  /**
10083
10107
  * Visits all `Expression`s in the AST of `op` with the `visitor` function.
10084
10108
  */
@@ -10167,12 +10191,6 @@ function transformExpressionsInOp(op, transform, flags) {
10167
10191
  op.expression =
10168
10192
  op.expression && transformExpressionsInExpression(op.expression, transform, flags);
10169
10193
  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
10194
  case OpKind.RepeaterCreate:
10177
10195
  op.track = transformExpressionsInExpression(op.track, transform, flags);
10178
10196
  if (op.trackByFn !== null) {
@@ -10182,34 +10200,38 @@ function transformExpressionsInOp(op, transform, flags) {
10182
10200
  case OpKind.Repeater:
10183
10201
  op.collection = transformExpressionsInExpression(op.collection, transform, flags);
10184
10202
  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));
10203
+ case OpKind.Defer:
10204
+ if (op.loadingConfig !== null) {
10205
+ op.loadingConfig = transformExpressionsInExpression(op.loadingConfig, transform, flags);
10206
+ }
10207
+ if (op.placeholderConfig !== null) {
10208
+ op.placeholderConfig =
10209
+ transformExpressionsInExpression(op.placeholderConfig, transform, flags);
10189
10210
  }
10190
10211
  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:
10212
+ case OpKind.Advance:
10200
10213
  case OpKind.Container:
10201
- case OpKind.ContainerStart:
10202
10214
  case OpKind.ContainerEnd:
10203
- case OpKind.Template:
10215
+ case OpKind.ContainerStart:
10216
+ case OpKind.DeferOn:
10204
10217
  case OpKind.DisableBindings:
10218
+ case OpKind.Element:
10219
+ case OpKind.ElementEnd:
10220
+ case OpKind.ElementStart:
10205
10221
  case OpKind.EnableBindings:
10206
- case OpKind.Text:
10207
- case OpKind.Pipe:
10208
- case OpKind.Advance:
10209
- case OpKind.Namespace:
10222
+ case OpKind.ExtractedMessage:
10223
+ case OpKind.I18n:
10210
10224
  case OpKind.I18nApply:
10225
+ case OpKind.I18nEnd:
10226
+ case OpKind.I18nStart:
10211
10227
  case OpKind.Icu:
10212
10228
  case OpKind.IcuUpdate:
10229
+ case OpKind.Namespace:
10230
+ case OpKind.Pipe:
10231
+ case OpKind.Projection:
10232
+ case OpKind.ProjectionDef:
10233
+ case OpKind.Template:
10234
+ case OpKind.Text:
10213
10235
  // These operations contain no expressions.
10214
10236
  break;
10215
10237
  default:
@@ -10581,6 +10603,12 @@ class OpList {
10581
10603
  }
10582
10604
  }
10583
10605
 
10606
+ class SlotHandle {
10607
+ constructor() {
10608
+ this.slot = null;
10609
+ }
10610
+ }
10611
+
10584
10612
  /**
10585
10613
  * The set of OpKinds that represent the creation of an element or container
10586
10614
  */
@@ -10602,6 +10630,7 @@ function createElementStartOp(tag, xref, namespace, i18nPlaceholder, sourceSpan)
10602
10630
  kind: OpKind.ElementStart,
10603
10631
  xref,
10604
10632
  tag,
10633
+ handle: new SlotHandle(),
10605
10634
  attributes: null,
10606
10635
  localRefs: [],
10607
10636
  nonBindable: false,
@@ -10615,13 +10644,14 @@ function createElementStartOp(tag, xref, namespace, i18nPlaceholder, sourceSpan)
10615
10644
  /**
10616
10645
  * Create a `TemplateOp`.
10617
10646
  */
10618
- function createTemplateOp(xref, tag, namespace, generatedInBlock, i18nPlaceholder, sourceSpan) {
10647
+ function createTemplateOp(xref, tag, functionNameSuffix, namespace, i18nPlaceholder, sourceSpan) {
10619
10648
  return {
10620
10649
  kind: OpKind.Template,
10621
10650
  xref,
10622
10651
  attributes: null,
10623
10652
  tag,
10624
- block: generatedInBlock,
10653
+ handle: new SlotHandle(),
10654
+ functionNameSuffix,
10625
10655
  decls: null,
10626
10656
  vars: null,
10627
10657
  localRefs: [],
@@ -10633,15 +10663,17 @@ function createTemplateOp(xref, tag, namespace, generatedInBlock, i18nPlaceholde
10633
10663
  ...NEW_OP,
10634
10664
  };
10635
10665
  }
10636
- function createRepeaterCreateOp(primaryView, emptyView, track, varNames, sourceSpan) {
10666
+ function createRepeaterCreateOp(primaryView, emptyView, tag, track, varNames, sourceSpan) {
10637
10667
  return {
10638
10668
  kind: OpKind.RepeaterCreate,
10639
10669
  attributes: null,
10640
10670
  xref: primaryView,
10671
+ handle: new SlotHandle(),
10641
10672
  emptyView,
10642
10673
  track,
10643
10674
  trackByFn: null,
10644
- tag: 'For',
10675
+ tag,
10676
+ functionNameSuffix: 'For',
10645
10677
  namespace: Namespace.HTML,
10646
10678
  nonBindable: false,
10647
10679
  localRefs: [],
@@ -10687,6 +10719,7 @@ function createTextOp(xref, initialValue, sourceSpan) {
10687
10719
  return {
10688
10720
  kind: OpKind.Text,
10689
10721
  xref,
10722
+ handle: new SlotHandle(),
10690
10723
  initialValue,
10691
10724
  sourceSpan,
10692
10725
  ...TRAIT_CONSUMES_SLOT,
@@ -10696,10 +10729,11 @@ function createTextOp(xref, initialValue, sourceSpan) {
10696
10729
  /**
10697
10730
  * Create a `ListenerOp`. Host bindings reuse all the listener logic.
10698
10731
  */
10699
- function createListenerOp(target, name, tag, animationPhase, hostListener, sourceSpan) {
10732
+ function createListenerOp(target, targetSlot, name, tag, animationPhase, hostListener, sourceSpan) {
10700
10733
  return {
10701
10734
  kind: OpKind.Listener,
10702
10735
  target,
10736
+ targetSlot,
10703
10737
  tag,
10704
10738
  hostListener,
10705
10739
  name,
@@ -10710,27 +10744,18 @@ function createListenerOp(target, name, tag, animationPhase, hostListener, sourc
10710
10744
  animationPhase: animationPhase,
10711
10745
  sourceSpan,
10712
10746
  ...NEW_OP,
10713
- ...TRAIT_USES_SLOT_INDEX,
10714
10747
  };
10715
10748
  }
10716
- function createPipeOp(xref, name) {
10749
+ function createPipeOp(xref, slot, name) {
10717
10750
  return {
10718
10751
  kind: OpKind.Pipe,
10719
10752
  xref,
10753
+ handle: slot,
10720
10754
  name,
10721
10755
  ...NEW_OP,
10722
10756
  ...TRAIT_CONSUMES_SLOT,
10723
10757
  };
10724
10758
  }
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
10759
  function createNamespaceOp(namespace) {
10735
10760
  return {
10736
10761
  kind: OpKind.Namespace,
@@ -10749,6 +10774,7 @@ function createProjectionOp(xref, selector, sourceSpan) {
10749
10774
  return {
10750
10775
  kind: OpKind.Projection,
10751
10776
  xref,
10777
+ handle: new SlotHandle(),
10752
10778
  selector,
10753
10779
  projectionSlotIndex: 0,
10754
10780
  attributes: [],
@@ -10771,51 +10797,54 @@ function createExtractedAttributeOp(target, bindingKind, name, expression) {
10771
10797
  ...NEW_OP,
10772
10798
  };
10773
10799
  }
10774
- function createDeferOp(xref, main, sourceSpan) {
10800
+ function createDeferOp(xref, main, mainSlot, sourceSpan) {
10775
10801
  return {
10776
10802
  kind: OpKind.Defer,
10777
10803
  xref,
10778
- target: main,
10779
- loading: null,
10780
- placeholder: null,
10781
- error: null,
10804
+ handle: new SlotHandle(),
10805
+ mainView: main,
10806
+ mainSlot,
10807
+ loadingView: null,
10808
+ loadingSlot: null,
10809
+ loadingConfig: null,
10810
+ loadingMinimumTime: null,
10811
+ loadingAfterTime: null,
10812
+ placeholderView: null,
10813
+ placeholderSlot: null,
10814
+ placeholderConfig: null,
10815
+ placeholderMinimumTime: null,
10816
+ errorView: null,
10817
+ errorSlot: null,
10782
10818
  sourceSpan,
10783
10819
  ...NEW_OP,
10784
10820
  ...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,
10821
+ numSlotsUsed: 2,
10799
10822
  };
10800
10823
  }
10801
- function createDeferOnOp(xref, sourceSpan) {
10824
+ function createDeferOnOp(defer, trigger, prefetch, sourceSpan) {
10802
10825
  return {
10803
10826
  kind: OpKind.DeferOn,
10804
- xref,
10827
+ defer,
10828
+ trigger,
10829
+ prefetch,
10805
10830
  sourceSpan,
10806
10831
  ...NEW_OP,
10807
- ...TRAIT_CONSUMES_SLOT,
10808
10832
  };
10809
10833
  }
10810
10834
  /**
10811
10835
  * Create an `ExtractedMessageOp`.
10812
10836
  */
10813
- function createExtractedMessageOp(owner, expression, statements) {
10837
+ function createExtractedMessageOp(owner, message, isRoot) {
10814
10838
  return {
10815
10839
  kind: OpKind.ExtractedMessage,
10816
10840
  owner,
10817
- expression,
10818
- statements,
10841
+ message,
10842
+ isRoot,
10843
+ params: new Map(),
10844
+ postprocessingParams: new Map(),
10845
+ needsPostprocessing: false,
10846
+ formattedParams: null,
10847
+ formattedPostprocessingParams: null,
10819
10848
  ...NEW_OP,
10820
10849
  };
10821
10850
  }
@@ -10826,13 +10855,11 @@ function createI18nStartOp(xref, message, root) {
10826
10855
  return {
10827
10856
  kind: OpKind.I18nStart,
10828
10857
  xref,
10858
+ handle: new SlotHandle(),
10829
10859
  root: root ?? xref,
10830
10860
  message,
10831
- params: new Map(),
10832
- postprocessingParams: new Map(),
10833
10861
  messageIndex: null,
10834
10862
  subTemplateIndex: null,
10835
- needsPostprocessing: false,
10836
10863
  ...NEW_OP,
10837
10864
  ...TRAIT_CONSUMES_SLOT,
10838
10865
  };
@@ -11069,9 +11096,10 @@ class HostBindingCompilationUnit extends CompilationUnit {
11069
11096
  }
11070
11097
 
11071
11098
  /**
11072
- * Find any function calls to `$any`, excluding `this.$any`, and delete them.
11099
+ * Find any function calls to `$any`, excluding `this.$any`, and delete them, since they have no
11100
+ * runtime effects.
11073
11101
  */
11074
- function phaseFindAnyCasts(job) {
11102
+ function deleteAnyCasts(job) {
11075
11103
  for (const unit of job.units) {
11076
11104
  for (const op of unit.ops()) {
11077
11105
  transformExpressionsInOp(op, removeAnys, VisitorContextFlag.None);
@@ -11092,13 +11120,13 @@ function removeAnys(e) {
11092
11120
  /**
11093
11121
  * Adds apply operations after i18n expressions.
11094
11122
  */
11095
- function phaseApplyI18nExpressions(job) {
11123
+ function applyI18nExpressions(job) {
11096
11124
  for (const unit of job.units) {
11097
11125
  for (const op of unit.update) {
11098
11126
  // Only add apply after expressions that are not followed by more expressions.
11099
11127
  if (op.kind === OpKind.I18nExpression && needsApplication(op)) {
11100
11128
  // TODO: what should be the source span for the apply op?
11101
- OpList.insertAfter(createI18nApplyOp(op.owner, null), op);
11129
+ OpList.insertAfter(createI18nApplyOp(op.owner, op.ownerSlot, null), op);
11102
11130
  }
11103
11131
  }
11104
11132
  }
@@ -11121,7 +11149,7 @@ function needsApplication(op) {
11121
11149
  /**
11122
11150
  * Updates i18n expression ops to depend on the last slot in their owning i18n block.
11123
11151
  */
11124
- function phaseAssignI18nSlotDependencies(job) {
11152
+ function assignI18nSlotDependencies(job) {
11125
11153
  const i18nLastSlotConsumers = new Map();
11126
11154
  let lastSlotConsumer = null;
11127
11155
  for (const unit of job.units) {
@@ -11143,29 +11171,6 @@ function phaseAssignI18nSlotDependencies(job) {
11143
11171
  }
11144
11172
  }
11145
11173
 
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];
11164
- }
11165
- }
11166
- }
11167
- }
11168
-
11169
11174
  /**
11170
11175
  * Gets a map of all elements in the given view by their xref id.
11171
11176
  */
@@ -11184,7 +11189,7 @@ function createOpXrefMap(unit) {
11184
11189
  * Find all extractable attribute and binding ops, and create ExtractedAttributeOps for them.
11185
11190
  * In cases where no instruction needs to be generated for the attribute or binding, it is removed.
11186
11191
  */
11187
- function phaseAttributeExtraction(job) {
11192
+ function extractAttributes(job) {
11188
11193
  for (const unit of job.units) {
11189
11194
  const elements = createOpXrefMap(unit);
11190
11195
  for (const op of unit.ops()) {
@@ -11282,7 +11287,7 @@ function lookupElement$1(elements, xref) {
11282
11287
  }
11283
11288
  return el;
11284
11289
  }
11285
- function phaseBindingSpecialization(job) {
11290
+ function specializeBindings(job) {
11286
11291
  const elements = new Map();
11287
11292
  for (const unit of job.units) {
11288
11293
  for (const op of unit.create) {
@@ -11371,7 +11376,7 @@ const CHAINABLE = new Set([
11371
11376
  * elementStart(0, 'div')(1, 'span');
11372
11377
  * ```
11373
11378
  */
11374
- function phaseChaining(job) {
11379
+ function chain(job) {
11375
11380
  for (const unit of job.units) {
11376
11381
  chainOperationsInList(unit.create);
11377
11382
  chainOperationsInList(unit.update);
@@ -11418,9 +11423,32 @@ function chainOperationsInList(opList) {
11418
11423
  }
11419
11424
 
11420
11425
  /**
11421
- * Collapse the various conditions of conditional ops into a single test expression.
11426
+ * Attribute interpolations of the form `[attr.foo]="{{foo}}""` should be "collapsed" into a plain
11427
+ * attribute instruction, instead of an `attributeInterpolate` instruction.
11428
+ *
11429
+ * (We cannot do this for singleton property interpolations, because `propertyInterpolate`
11430
+ * stringifies its expression.)
11431
+ *
11432
+ * The reification step is also capable of performing this transformation, but doing it early in the
11433
+ * pipeline allows other phases to accurately know what instruction will be emitted.
11434
+ */
11435
+ function collapseSingletonInterpolations(job) {
11436
+ for (const unit of job.units) {
11437
+ for (const op of unit.update) {
11438
+ const eligibleOpKind = op.kind === OpKind.Attribute;
11439
+ if (eligibleOpKind && op.expression instanceof Interpolation &&
11440
+ op.expression.strings.length === 2 &&
11441
+ op.expression.strings.every((s) => s === '')) {
11442
+ op.expression = op.expression.expressions[0];
11443
+ }
11444
+ }
11445
+ }
11446
+ }
11447
+
11448
+ /**
11449
+ * Collapse the various conditions of conditional ops (if, switch) into a single test expression.
11422
11450
  */
11423
- function phaseConditionals(job) {
11451
+ function generateConditionalExpressions(job) {
11424
11452
  for (const unit of job.units) {
11425
11453
  for (const op of unit.ops()) {
11426
11454
  if (op.kind !== OpKind.Conditional) {
@@ -11430,8 +11458,8 @@ function phaseConditionals(job) {
11430
11458
  // Any case with a `null` condition is `default`. If one exists, default to it instead.
11431
11459
  const defaultCase = op.conditions.findIndex((cond) => cond.expr === null);
11432
11460
  if (defaultCase >= 0) {
11433
- const xref = op.conditions.splice(defaultCase, 1)[0].target;
11434
- test = new SlotLiteralExpr(xref);
11461
+ const slot = op.conditions.splice(defaultCase, 1)[0].targetSlot;
11462
+ test = new SlotLiteralExpr(slot);
11435
11463
  }
11436
11464
  else {
11437
11465
  // By default, a switch evaluates to `-1`, causing no template to be displayed.
@@ -11457,7 +11485,7 @@ function phaseConditionals(job) {
11457
11485
  new AssignTemporaryExpr(conditionalCase.expr, caseExpressionTemporaryXref);
11458
11486
  op.contextValue = new ReadTemporaryExpr(caseExpressionTemporaryXref);
11459
11487
  }
11460
- test = new ConditionalExpr(conditionalCase.expr, new SlotLiteralExpr(conditionalCase.target), test);
11488
+ test = new ConditionalExpr(conditionalCase.expr, new SlotLiteralExpr(conditionalCase.targetSlot), test);
11461
11489
  }
11462
11490
  // Save the resulting aggregate Joost-expression.
11463
11491
  op.processed = test;
@@ -11548,14 +11576,14 @@ function literalOrArrayLiteral(value) {
11548
11576
  if (Array.isArray(value)) {
11549
11577
  return literalArr(value.map(literalOrArrayLiteral));
11550
11578
  }
11551
- return literal(value, INFERRED_TYPE);
11579
+ return literal(value);
11552
11580
  }
11553
11581
 
11554
11582
  /**
11555
11583
  * Converts the semantic attributes of element-like operations (elements, templates) into constant
11556
11584
  * array expressions, and lifts them into the overall component `consts`.
11557
11585
  */
11558
- function phaseConstCollection(job) {
11586
+ function collectElementConsts(job) {
11559
11587
  // Collect all extracted attributes.
11560
11588
  const allElementAttributes = new Map();
11561
11589
  for (const unit of job.units) {
@@ -11701,20 +11729,126 @@ function serializeAttributes({ attributes, bindings, classes, i18n, projectAs, s
11701
11729
  return literalArr(attrArray);
11702
11730
  }
11703
11731
 
11704
- const REPLACEMENTS = new Map([
11705
- [OpKind.ElementEnd, [OpKind.ElementStart, OpKind.Element]],
11706
- [OpKind.ContainerEnd, [OpKind.ContainerStart, OpKind.Container]],
11707
- [OpKind.I18nEnd, [OpKind.I18nStart, OpKind.I18n]],
11708
- ]);
11709
11732
  /**
11710
- * Op kinds that should not prevent merging of start/end ops.
11733
+ * Defer instructions take a configuration array, which should be collected into the component
11734
+ * consts. This phase finds the config options, and creates the corresponding const array.
11735
+ */
11736
+ function configureDeferInstructions(job) {
11737
+ for (const unit of job.units) {
11738
+ for (const op of unit.create) {
11739
+ if (op.kind !== OpKind.Defer) {
11740
+ continue;
11741
+ }
11742
+ if (op.placeholderMinimumTime !== null) {
11743
+ op.placeholderConfig =
11744
+ new ConstCollectedExpr(literalOrArrayLiteral([op.placeholderMinimumTime]));
11745
+ }
11746
+ if (op.loadingMinimumTime !== null || op.loadingAfterTime !== null) {
11747
+ op.loadingConfig = new ConstCollectedExpr(literalOrArrayLiteral([op.loadingMinimumTime, op.loadingAfterTime]));
11748
+ }
11749
+ }
11750
+ }
11751
+ }
11752
+
11753
+ /**
11754
+ * Some `defer` conditions can reference other elements in the template, using their local reference
11755
+ * names. However, the semantics are quite different from the normal local reference system: in
11756
+ * particular, we need to look at local reference names in enclosing views. This phase resolves
11757
+ * all such references to actual xrefs.
11758
+ */
11759
+ function resolveDeferTargetNames(job) {
11760
+ const scopes = new Map();
11761
+ function getScopeForView(view) {
11762
+ if (scopes.has(view.xref)) {
11763
+ return scopes.get(view.xref);
11764
+ }
11765
+ const scope = new Scope$1();
11766
+ for (const op of view.create) {
11767
+ // add everything that can be referenced.
11768
+ if (!isElementOrContainerOp(op) || op.localRefs === null) {
11769
+ continue;
11770
+ }
11771
+ if (!Array.isArray(op.localRefs)) {
11772
+ throw new Error('LocalRefs were already processed, but were needed to resolve defer targets.');
11773
+ }
11774
+ for (const ref of op.localRefs) {
11775
+ if (ref.target !== '') {
11776
+ continue;
11777
+ }
11778
+ scope.targets.set(ref.name, { xref: op.xref, slot: op.handle });
11779
+ }
11780
+ }
11781
+ scopes.set(view.xref, scope);
11782
+ return scope;
11783
+ }
11784
+ function resolveTrigger(deferOwnerView, op, placeholderView) {
11785
+ switch (op.trigger.kind) {
11786
+ case DeferTriggerKind.Idle:
11787
+ case DeferTriggerKind.Immediate:
11788
+ case DeferTriggerKind.Timer:
11789
+ return;
11790
+ case DeferTriggerKind.Hover:
11791
+ case DeferTriggerKind.Interaction:
11792
+ case DeferTriggerKind.Viewport:
11793
+ if (op.trigger.targetName === null) {
11794
+ return;
11795
+ }
11796
+ let view = placeholderView !== null ? job.views.get(placeholderView) : deferOwnerView;
11797
+ let step = placeholderView !== null ? -1 : 0;
11798
+ while (view !== null) {
11799
+ const scope = getScopeForView(view);
11800
+ if (scope.targets.has(op.trigger.targetName)) {
11801
+ const { xref, slot } = scope.targets.get(op.trigger.targetName);
11802
+ op.trigger.targetXref = xref;
11803
+ op.trigger.targetView = view.xref;
11804
+ op.trigger.targetSlotViewSteps = step;
11805
+ op.trigger.targetSlot = slot;
11806
+ return;
11807
+ }
11808
+ view = view.parent !== null ? job.views.get(view.parent) : null;
11809
+ step++;
11810
+ }
11811
+ break;
11812
+ default:
11813
+ throw new Error(`Trigger kind ${op.trigger.kind} not handled`);
11814
+ }
11815
+ }
11816
+ // Find the defer ops, and assign the data about their targets.
11817
+ for (const unit of job.units) {
11818
+ const defers = new Map();
11819
+ for (const op of unit.create) {
11820
+ switch (op.kind) {
11821
+ case OpKind.Defer:
11822
+ defers.set(op.xref, op);
11823
+ break;
11824
+ case OpKind.DeferOn:
11825
+ const deferOp = defers.get(op.defer);
11826
+ resolveTrigger(unit, op, deferOp.placeholderView);
11827
+ break;
11828
+ }
11829
+ }
11830
+ }
11831
+ }
11832
+ class Scope$1 {
11833
+ constructor() {
11834
+ this.targets = new Map();
11835
+ }
11836
+ }
11837
+
11838
+ const REPLACEMENTS = new Map([
11839
+ [OpKind.ElementEnd, [OpKind.ElementStart, OpKind.Element]],
11840
+ [OpKind.ContainerEnd, [OpKind.ContainerStart, OpKind.Container]],
11841
+ [OpKind.I18nEnd, [OpKind.I18nStart, OpKind.I18n]],
11842
+ ]);
11843
+ /**
11844
+ * Op kinds that should not prevent merging of start/end ops.
11711
11845
  */
11712
11846
  const IGNORED_OP_KINDS = new Set([OpKind.Pipe]);
11713
11847
  /**
11714
- * Replace sequences of mergable elements (e.g. `ElementStart` and `ElementEnd`) with a consolidated
11715
- * element (e.g. `Element`).
11848
+ * Replace sequences of mergable instructions (e.g. `ElementStart` and `ElementEnd`) with a
11849
+ * consolidated instruction (e.g. `Element`).
11716
11850
  */
11717
- function phaseEmptyElements(job) {
11851
+ function collapseEmptyInstructions(job) {
11718
11852
  for (const unit of job.units) {
11719
11853
  for (const op of unit.create) {
11720
11854
  // Find end ops that may be able to be merged.
@@ -11741,10 +11875,13 @@ function phaseEmptyElements(job) {
11741
11875
  }
11742
11876
 
11743
11877
  /**
11744
- * Finds all unresolved safe read expressions, and converts them into the appropriate output AST
11745
- * reads, guarded by null checks.
11878
+ * Safe read expressions such as `a?.b` have different semantics in Angular templates as
11879
+ * compared to JavaScript. In particular, they default to `null` instead of `undefined`. This phase
11880
+ * finds all unresolved safe read expressions, and converts them into the appropriate output AST
11881
+ * reads, guarded by null checks. We generate temporaries as needed, to avoid re-evaluating the same
11882
+ * sub-expression multiple times.
11746
11883
  */
11747
- function phaseExpandSafeReads(job) {
11884
+ function expandSafeReads(job) {
11748
11885
  for (const unit of job.units) {
11749
11886
  for (const op of unit.ops()) {
11750
11887
  transformExpressionsInOp(op, e => safeTransform(e, { job }), VisitorContextFlag.None);
@@ -11915,42 +12052,116 @@ function ternaryTransform(e) {
11915
12052
  return new ConditionalExpr(new BinaryOperatorExpr(BinaryOperator.Equals, e.guard, NULL_EXPR), NULL_EXPR, e.expr);
11916
12053
  }
11917
12054
 
11918
- function phaseRepeaterDerivedVars(job) {
11919
- const repeaters = new Map();
12055
+ /**
12056
+ * The escape sequence used indicate message param values.
12057
+ */
12058
+ const ESCAPE = '\uFFFD';
12059
+ /**
12060
+ * Marker used to indicate an element tag.
12061
+ */
12062
+ const ELEMENT_MARKER = '#';
12063
+ /**
12064
+ * Marker used to indicate a template tag.
12065
+ */
12066
+ const TEMPLATE_MARKER = '*';
12067
+ /**
12068
+ * Marker used to indicate closing of an element or template tag.
12069
+ */
12070
+ const TAG_CLOSE_MARKER = '/';
12071
+ /**
12072
+ * Marker used to indicate the sub-template context.
12073
+ */
12074
+ const CONTEXT_MARKER = ':';
12075
+ /**
12076
+ * Marker used to indicate the start of a list of values.
12077
+ */
12078
+ const LIST_START_MARKER = '[';
12079
+ /**
12080
+ * Marker used to indicate the end of a list of values.
12081
+ */
12082
+ const LIST_END_MARKER = ']';
12083
+ /**
12084
+ * Delimiter used to separate multiple values in a list.
12085
+ */
12086
+ const LIST_DELIMITER = '|';
12087
+ /**
12088
+ * Formats the param maps on extracted message ops into a maps of `Expression` objects that can be
12089
+ * used in the final output.
12090
+ */
12091
+ function formatI18nParams(job) {
11920
12092
  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);
12093
+ for (const op of unit.create) {
12094
+ if (op.kind === OpKind.ExtractedMessage) {
12095
+ if (op.isRoot) {
12096
+ op.formattedParams = formatParams(op.params);
12097
+ op.formattedPostprocessingParams = formatParams(op.postprocessingParams);
12098
+ // The message will need post-processing if there are any post-processing params, or if
12099
+ // there are any normal params that have multiple values
12100
+ op.needsPostprocessing = op.postprocessingParams.size > 0;
12101
+ for (const [param, values] of op.params) {
12102
+ if (values.length > 1) {
12103
+ op.needsPostprocessing = true;
12104
+ }
12105
+ }
12106
+ }
11924
12107
  }
11925
12108
  }
11926
12109
  }
11927
- 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;
11932
- }
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));
11943
- }
11944
- }, VisitorContextFlag.None);
12110
+ }
12111
+ /**
12112
+ * Formats a map of `I18nParamValue[]` values into a map of `Expression` values.
12113
+ */
12114
+ function formatParams(params) {
12115
+ const result = new Map();
12116
+ for (const [placeholder, placeholderValues] of [...params].sort()) {
12117
+ const serializedValues = formatParamValues(placeholderValues);
12118
+ if (serializedValues !== null) {
12119
+ result.set(placeholder, literal(formatParamValues(placeholderValues)));
11945
12120
  }
11946
12121
  }
12122
+ return result;
12123
+ }
12124
+ /**
12125
+ * Formats an `I18nParamValue[]` into a string (or null for empty array).
12126
+ */
12127
+ function formatParamValues(values) {
12128
+ if (values.length === 0) {
12129
+ return null;
12130
+ }
12131
+ const serializedValues = values.map(value => formatValue(value));
12132
+ return serializedValues.length === 1 ?
12133
+ serializedValues[0] :
12134
+ `${LIST_START_MARKER}${serializedValues.join(LIST_DELIMITER)}${LIST_END_MARKER}`;
12135
+ }
12136
+ /**
12137
+ * Formats a single `I18nParamValue` into a string
12138
+ */
12139
+ function formatValue(value) {
12140
+ let tagMarker = '';
12141
+ let closeMarker = '';
12142
+ if (value.flags & I18nParamValueFlags.ElementTag) {
12143
+ tagMarker = ELEMENT_MARKER;
12144
+ }
12145
+ else if (value.flags & I18nParamValueFlags.TemplateTag) {
12146
+ tagMarker = TEMPLATE_MARKER;
12147
+ }
12148
+ if (tagMarker !== '') {
12149
+ closeMarker = value.flags & I18nParamValueFlags.CloseTag ? TAG_CLOSE_MARKER : '';
12150
+ }
12151
+ const context = value.subTemplateIndex === null ? '' : `${CONTEXT_MARKER}${value.subTemplateIndex}`;
12152
+ // Self-closing tags use a special form that concatenates the start and close tag values.
12153
+ if ((value.flags & I18nParamValueFlags.OpenTag) &&
12154
+ (value.flags & I18nParamValueFlags.CloseTag)) {
12155
+ return `${ESCAPE}${tagMarker}${value.value}${context}${ESCAPE}${ESCAPE}${closeMarker}${tagMarker}${value.value}${context}${ESCAPE}`;
12156
+ }
12157
+ return `${ESCAPE}${closeMarker}${tagMarker}${value.value}${context}${ESCAPE}`;
11947
12158
  }
11948
12159
 
11949
12160
  /**
11950
12161
  * Generate `ir.AdvanceOp`s in between `ir.UpdateOp`s that ensure the runtime's implicit slot
11951
12162
  * context will be advanced correctly.
11952
12163
  */
11953
- function phaseGenerateAdvance(job) {
12164
+ function generateAdvance(job) {
11954
12165
  for (const unit of job.units) {
11955
12166
  // First build a map of all of the declarations in the view that have assigned slots.
11956
12167
  const slotMap = new Map();
@@ -11958,10 +12169,10 @@ function phaseGenerateAdvance(job) {
11958
12169
  if (!hasConsumesSlotTrait(op)) {
11959
12170
  continue;
11960
12171
  }
11961
- else if (op.slot === null) {
12172
+ else if (op.handle.slot === null) {
11962
12173
  throw new Error(`AssertionError: expected slots to have been allocated before generating advance() calls`);
11963
12174
  }
11964
- slotMap.set(op.xref, op.slot);
12175
+ slotMap.set(op.xref, op.handle.slot);
11965
12176
  }
11966
12177
  // Next, step through the update operations and generate `ir.AdvanceOp`s as required to ensure
11967
12178
  // the runtime's implicit slot counter will be set to the correct slot before executing each
@@ -11999,7 +12210,7 @@ function phaseGenerateAdvance(job) {
11999
12210
  * populate `project` arguments, and generate the required `projectionDef` instruction for the job's
12000
12211
  * root view.
12001
12212
  */
12002
- function phaseGenerateProjectionDef(job) {
12213
+ function generateProjectionDefs(job) {
12003
12214
  // TODO: Why does TemplateDefinitionBuilder force a shared constant?
12004
12215
  const share = job.compatibility === CompatibilityMode.TemplateDefinitionBuilder;
12005
12216
  // Collect all selectors from this component, and its nested views. Also, assign each projection a
@@ -12044,7 +12255,7 @@ function phaseGenerateProjectionDef(job) {
12044
12255
  * Variables are generated here unconditionally, and may optimized away in future operations if it
12045
12256
  * turns out their values (and any side effects) are unused.
12046
12257
  */
12047
- function phaseGenerateVariables(job) {
12258
+ function generateVariables(job) {
12048
12259
  recursivelyProcessView(job.root, /* there is no parent scope for the root view */ null);
12049
12260
  }
12050
12261
  /**
@@ -12110,6 +12321,7 @@ function getScopeForView(view, parent) {
12110
12321
  scope.references.push({
12111
12322
  name: op.localRefs[offset].name,
12112
12323
  targetId: op.xref,
12324
+ targetSlot: op.handle,
12113
12325
  offset,
12114
12326
  variable: {
12115
12327
  kind: SemanticVariableKind.Identifier,
@@ -12151,7 +12363,7 @@ function generateVariablesInScopeForView(view, scope) {
12151
12363
  }
12152
12364
  // Add variables for all local references declared for elements in this scope.
12153
12365
  for (const ref of scope.references) {
12154
- newOps.push(createVariableOp(view.job.allocateXrefId(), ref.variable, new ReferenceExpr(ref.targetId, ref.offset), VariableFlags.None));
12366
+ newOps.push(createVariableOp(view.job.allocateXrefId(), ref.variable, new ReferenceExpr(ref.targetId, ref.targetSlot, ref.offset), VariableFlags.None));
12155
12367
  }
12156
12368
  if (scope.parent !== null) {
12157
12369
  // Recursively add variables from the parent scope.
@@ -12161,27 +12373,20 @@ function generateVariablesInScopeForView(view, scope) {
12161
12373
  }
12162
12374
 
12163
12375
  /**
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.
12376
+ * `ir.ConstCollectedExpr` may be present in any IR expression. This means that expression needs to
12377
+ * be lifted into the component const array, and replaced with a reference to the const array at its
12378
+ *
12379
+ * usage site. This phase walks the IR and performs this transformation.
12167
12380
  */
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
- };
12381
+ function collectConstExpressions(job) {
12179
12382
  for (const unit of job.units) {
12180
12383
  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);
12384
+ transformExpressionsInOp(op, expr => {
12385
+ if (!(expr instanceof ConstCollectedExpr)) {
12386
+ return expr;
12387
+ }
12388
+ return literal(job.addConst(expr.expr));
12389
+ }, VisitorContextFlag.None);
12185
12390
  }
12186
12391
  }
12187
12392
  }
@@ -12191,7 +12396,13 @@ const CLASS_DOT = 'class.';
12191
12396
  const STYLE_BANG = 'style!';
12192
12397
  const CLASS_BANG = 'class!';
12193
12398
  const BANG_IMPORTANT = '!important';
12194
- function phaseHostStylePropertyParsing(job) {
12399
+ /**
12400
+ * Host bindings are compiled using a different parser entrypoint, and are parsed quite differently
12401
+ * as a result. Therefore, we need to do some extra parsing for host style properties, as compared
12402
+ * to non-host style properties.
12403
+ * TODO: Unify host bindings and non-host bindings in the parser.
12404
+ */
12405
+ function parseHostStyleProperties(job) {
12195
12406
  for (const op of job.root.update) {
12196
12407
  if (op.kind !== OpKind.Binding) {
12197
12408
  continue;
@@ -12253,31 +12464,6 @@ function parseProperty$1(name) {
12253
12464
  return { property, suffix };
12254
12465
  }
12255
12466
 
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
12467
  function mapEntry(key, value) {
12282
12468
  return { key, value, quoted: false };
12283
12469
  }
@@ -19155,16 +19341,19 @@ const NG_I18N_CLOSURE_MODE$1 = 'ngI18nClosureMode';
19155
19341
  * considers variables like `I18N_0` as constants and throws an error when their value changes.
19156
19342
  */
19157
19343
  const TRANSLATION_VAR_PREFIX = 'i18n_';
19158
- /** Extracts i18n messages into the consts array. */
19159
- function phaseI18nMessageExtraction(job) {
19344
+ /**
19345
+ * Lifts i18n properties into the consts array.
19346
+ * TODO: Can we use `ConstCollectedExpr`?
19347
+ */
19348
+ function collectI18nConsts(job) {
19160
19349
  const fileBasedI18nSuffix = job.relativeContextFilePath.replace(/[^A-Za-z0-9]/g, '_').toUpperCase() + '_';
19350
+ const messageConstIndices = new Map();
19161
19351
  for (const unit of job.units) {
19162
19352
  for (const op of unit.create) {
19163
- 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());
19353
+ if (op.kind === OpKind.ExtractedMessage) {
19354
+ // Serialize the extracted root messages into the const array.
19355
+ if (op.isRoot) {
19356
+ assertAllParamsResolved(op);
19168
19357
  const mainVar = variable(job.pool.uniqueName(TRANSLATION_VAR_PREFIX));
19169
19358
  // Closure Compiler requires const names to start with `MSG_` but disallows any other
19170
19359
  // const to start with `MSG_`. We define a variable starting with `MSG_` just for the
@@ -19175,14 +19364,24 @@ function phaseI18nMessageExtraction(job) {
19175
19364
  // set in post-processing.
19176
19365
  if (op.needsPostprocessing) {
19177
19366
  const extraTransformFnParams = [];
19178
- if (op.postprocessingParams.size > 0) {
19179
- extraTransformFnParams.push(literalMap([...op.postprocessingParams.entries()].map(([key, value]) => ({ key, value, quoted: true }))));
19367
+ if (op.formattedPostprocessingParams.size > 0) {
19368
+ extraTransformFnParams.push(literalMap([...op.formattedPostprocessingParams].map(([key, value]) => ({ key, value, quoted: true }))));
19180
19369
  }
19181
19370
  transformFn = (expr) => importExpr(Identifiers.i18nPostprocess).callFn([expr, ...extraTransformFnParams]);
19182
19371
  }
19183
- const statements = getTranslationDeclStmts$1(op.message, mainVar, closureVar, params, transformFn);
19184
- unit.create.push(createExtractedMessageOp(op.xref, mainVar, statements));
19372
+ const statements = getTranslationDeclStmts$1(op.message, mainVar, closureVar, op.formattedParams, transformFn);
19373
+ messageConstIndices.set(op.owner, job.addConst(mainVar, statements));
19185
19374
  }
19375
+ // Remove the extracted messages from the IR now that they have been collected.
19376
+ OpList.remove(op);
19377
+ }
19378
+ }
19379
+ }
19380
+ // Assign const index to i18n ops that messages were extracted from.
19381
+ for (const unit of job.units) {
19382
+ for (const op of unit.create) {
19383
+ if (op.kind === OpKind.I18nStart) {
19384
+ op.messageIndex = messageConstIndices.get(op.root);
19186
19385
  }
19187
19386
  }
19188
19387
  }
@@ -19254,27 +19453,63 @@ function i18nGenerateClosureVar(pool, messageId, fileBasedI18nSuffix, useExterna
19254
19453
  }
19255
19454
  return variable(name);
19256
19455
  }
19456
+ /**
19457
+ * Asserts that all of the message's placeholders have values.
19458
+ */
19459
+ function assertAllParamsResolved(op) {
19460
+ if (op.formattedParams === null || op.formattedPostprocessingParams === null) {
19461
+ throw Error('Params should have been formatted.');
19462
+ }
19463
+ for (const placeholder in op.message.placeholders) {
19464
+ if (!op.formattedParams.has(placeholder) &&
19465
+ !op.formattedPostprocessingParams.has(placeholder)) {
19466
+ throw Error(`Failed to resolve i18n placeholder: ${placeholder}`);
19467
+ }
19468
+ }
19469
+ for (const placeholder in op.message.placeholderToMessage) {
19470
+ if (!op.formattedParams.has(placeholder) &&
19471
+ !op.formattedPostprocessingParams.has(placeholder)) {
19472
+ throw Error(`Failed to resolve i18n message placeholder: ${placeholder}`);
19473
+ }
19474
+ }
19475
+ }
19476
+
19477
+ /**
19478
+ * Extracts i18n messages into their own op.
19479
+ */
19480
+ function extractI18nMessages(job) {
19481
+ for (const unit of job.units) {
19482
+ for (const op of unit.create) {
19483
+ if (op.kind === OpKind.I18nStart) {
19484
+ unit.create.push(createExtractedMessageOp(op.xref, op.message, op.xref === op.root));
19485
+ }
19486
+ }
19487
+ }
19488
+ }
19257
19489
 
19258
19490
  /**
19259
19491
  * Removes text nodes within i18n blocks since they are already hardcoded into the i18n message.
19260
19492
  */
19261
- function phaseI18nTextExtraction(job) {
19493
+ function extractI18nText(job) {
19262
19494
  for (const unit of job.units) {
19263
19495
  // Remove all text nodes within i18n blocks, their content is already captured in the i18n
19264
19496
  // message.
19265
19497
  let currentI18nId = null;
19498
+ let currentI18nSlot = null;
19266
19499
  const textNodes = new Map();
19267
19500
  for (const op of unit.create) {
19268
19501
  switch (op.kind) {
19269
19502
  case OpKind.I18nStart:
19270
19503
  currentI18nId = op.xref;
19504
+ currentI18nSlot = op.handle;
19271
19505
  break;
19272
19506
  case OpKind.I18nEnd:
19273
19507
  currentI18nId = null;
19508
+ currentI18nSlot = null;
19274
19509
  break;
19275
19510
  case OpKind.Text:
19276
- if (currentI18nId !== null) {
19277
- textNodes.set(op.xref, currentI18nId);
19511
+ if (currentI18nId !== null && currentI18nSlot !== null) {
19512
+ textNodes.set(op.xref, { xref: currentI18nId, slot: currentI18nSlot });
19278
19513
  OpList.remove(op);
19279
19514
  }
19280
19515
  break;
@@ -19288,12 +19523,12 @@ function phaseI18nTextExtraction(job) {
19288
19523
  if (!textNodes.has(op.target)) {
19289
19524
  continue;
19290
19525
  }
19291
- const i18nBlockId = textNodes.get(op.target);
19526
+ const i18nBlock = textNodes.get(op.target);
19292
19527
  const ops = [];
19293
19528
  for (let i = 0; i < op.interpolation.expressions.length; i++) {
19294
19529
  const expr = op.interpolation.expressions[i];
19295
19530
  const placeholder = op.i18nPlaceholders[i];
19296
- ops.push(createI18nExpressionOp(i18nBlockId, expr, placeholder.name, I18nParamResolutionTime.Creation, expr.sourceSpan ?? op.sourceSpan));
19531
+ ops.push(createI18nExpressionOp(i18nBlock.xref, i18nBlock.slot, expr, placeholder.name, I18nParamResolutionTime.Creation, expr.sourceSpan ?? op.sourceSpan));
19297
19532
  }
19298
19533
  if (ops.length > 0) {
19299
19534
  // ops.push(ir.createI18nApplyOp(i18nBlockId, op.i18nPlaceholders, op.sourceSpan));
@@ -19308,24 +19543,27 @@ function phaseI18nTextExtraction(job) {
19308
19543
  /**
19309
19544
  * Extracts ICUs into i18n expressions.
19310
19545
  */
19311
- function phaseIcuExtraction(job) {
19546
+ function extractI18nICUs(job) {
19312
19547
  for (const unit of job.units) {
19313
19548
  // Build a map of ICU to the i18n block they belong to, then remove the `Icu` ops.
19314
19549
  const icus = new Map();
19315
19550
  let currentI18nId = null;
19551
+ let currentI18nSlot = null;
19316
19552
  for (const op of unit.create) {
19317
19553
  switch (op.kind) {
19318
19554
  case OpKind.I18nStart:
19319
19555
  currentI18nId = op.xref;
19556
+ currentI18nSlot = op.handle;
19320
19557
  break;
19321
19558
  case OpKind.I18nEnd:
19322
19559
  currentI18nId = null;
19560
+ currentI18nSlot = null;
19323
19561
  break;
19324
19562
  case OpKind.Icu:
19325
19563
  if (currentI18nId === null) {
19326
19564
  throw Error('Unexpected ICU outside of an i18n block.');
19327
19565
  }
19328
- icus.set(op.xref, { message: op.message, i18nBlockId: currentI18nId });
19566
+ icus.set(op.xref, { message: op.message, i18nBlockId: currentI18nId, i18nBlockSlot: currentI18nSlot });
19329
19567
  OpList.remove(op);
19330
19568
  break;
19331
19569
  }
@@ -19334,7 +19572,7 @@ function phaseIcuExtraction(job) {
19334
19572
  for (const op of unit.update) {
19335
19573
  switch (op.kind) {
19336
19574
  case OpKind.IcuUpdate:
19337
- const { message, i18nBlockId } = icus.get(op.xref);
19575
+ const { message, i18nBlockId, i18nBlockSlot } = icus.get(op.xref);
19338
19576
  const icuNode = message.nodes.find((n) => n instanceof Icu);
19339
19577
  if (icuNode === undefined) {
19340
19578
  throw Error('Could not find ICU in i18n AST');
@@ -19342,7 +19580,7 @@ function phaseIcuExtraction(job) {
19342
19580
  if (icuNode.expressionPlaceholder === undefined) {
19343
19581
  throw Error('ICU is missing an i18n placeholder');
19344
19582
  }
19345
- OpList.replace(op, createI18nExpressionOp(i18nBlockId, new LexicalReadExpr(icuNode.expression), icuNode.expressionPlaceholder, I18nParamResolutionTime.Postproccessing, null));
19583
+ OpList.replace(op, createI18nExpressionOp(i18nBlockId, i18nBlockSlot, new LexicalReadExpr(icuNode.expression), icuNode.expressionPlaceholder, I18nParamResolutionTime.Postproccessing, null));
19346
19584
  break;
19347
19585
  }
19348
19586
  }
@@ -19353,7 +19591,7 @@ function phaseIcuExtraction(job) {
19353
19591
  * Lifts local reference declarations on element-like structures within each view into an entry in
19354
19592
  * the `consts` array for the whole component.
19355
19593
  */
19356
- function phaseLocalRefs(job) {
19594
+ function liftLocalRefs(job) {
19357
19595
  for (const unit of job.units) {
19358
19596
  for (const op of unit.create) {
19359
19597
  switch (op.kind) {
@@ -19386,7 +19624,7 @@ function serializeLocalRefs(refs) {
19386
19624
  /**
19387
19625
  * Change namespaces between HTML, SVG and MathML, depending on the next element.
19388
19626
  */
19389
- function phaseNamespace(job) {
19627
+ function emitNamespaceChanges(job) {
19390
19628
  for (const unit of job.units) {
19391
19629
  let activeNamespace = Namespace.HTML;
19392
19630
  for (const op of unit.create) {
@@ -19486,8 +19724,8 @@ function hyphenate(value) {
19486
19724
  * This includes propagating those names into any `ir.ReadVariableExpr`s of those variables, so that
19487
19725
  * the reads can be emitted correctly.
19488
19726
  */
19489
- function phaseNaming(cpl) {
19490
- addNamesToView(cpl.root, cpl.componentName, { index: 0 }, cpl.compatibility === CompatibilityMode.TemplateDefinitionBuilder);
19727
+ function nameFunctionsAndVariables(job) {
19728
+ addNamesToView(job.root, job.componentName, { index: 0 }, job.compatibility === CompatibilityMode.TemplateDefinitionBuilder);
19491
19729
  }
19492
19730
  function addNamesToView(unit, baseName, state, compatibility) {
19493
19731
  if (unit.fnName === null) {
@@ -19508,7 +19746,7 @@ function addNamesToView(unit, baseName, state, compatibility) {
19508
19746
  if (op.handlerFnName !== null) {
19509
19747
  break;
19510
19748
  }
19511
- if (!op.hostListener && op.targetSlot === null) {
19749
+ if (!op.hostListener && op.targetSlot.slot === null) {
19512
19750
  throw new Error(`Expected a slot to be assigned`);
19513
19751
  }
19514
19752
  let animation = '';
@@ -19520,7 +19758,7 @@ function addNamesToView(unit, baseName, state, compatibility) {
19520
19758
  op.handlerFnName = `${baseName}_${animation}${op.name}_HostBindingHandler`;
19521
19759
  }
19522
19760
  else {
19523
- op.handlerFnName = `${unit.fnName}_${op.tag.replace('-', '_')}_${animation}${op.name}_${op.targetSlot}_listener`;
19761
+ op.handlerFnName = `${unit.fnName}_${op.tag.replace('-', '_')}_${animation}${op.name}_${op.targetSlot.slot}_listener`;
19524
19762
  }
19525
19763
  op.handlerFnName = sanitizeIdentifier(op.handlerFnName);
19526
19764
  break;
@@ -19531,28 +19769,27 @@ function addNamesToView(unit, baseName, state, compatibility) {
19531
19769
  if (!(unit instanceof ViewCompilationUnit)) {
19532
19770
  throw new Error(`AssertionError: must be compiling a component`);
19533
19771
  }
19534
- if (op.slot === null) {
19772
+ if (op.handle.slot === null) {
19535
19773
  throw new Error(`Expected slot to be assigned`);
19536
19774
  }
19537
19775
  if (op.emptyView !== null) {
19538
19776
  const emptyView = unit.job.views.get(op.emptyView);
19539
19777
  // 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);
19778
+ addNamesToView(emptyView, `${baseName}_${`${op.functionNameSuffix}Empty`}_${op.handle.slot + 2}`, state, compatibility);
19541
19779
  }
19542
- const repeaterToken = op.tag === null ? '' : '_' + prefixWithNamespace(op.tag, op.namespace);
19543
19780
  // 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);
19781
+ addNamesToView(unit.job.views.get(op.xref), `${baseName}_${op.functionNameSuffix}_${op.handle.slot + 1}`, state, compatibility);
19545
19782
  break;
19546
19783
  case OpKind.Template:
19547
19784
  if (!(unit instanceof ViewCompilationUnit)) {
19548
19785
  throw new Error(`AssertionError: must be compiling a component`);
19549
19786
  }
19550
19787
  const childView = unit.job.views.get(op.xref);
19551
- if (op.slot === null) {
19788
+ if (op.handle.slot === null) {
19552
19789
  throw new Error(`Expected slot to be assigned`);
19553
19790
  }
19554
- const tagToken = op.tag === null ? '' : '_' + prefixWithNamespace(op.tag, op.namespace);
19555
- addNamesToView(childView, `${baseName}${tagToken}_${op.slot}`, state, compatibility);
19791
+ const suffix = op.functionNameSuffix.length === 0 ? '' : `_${op.functionNameSuffix}`;
19792
+ addNamesToView(childView, `${baseName}${suffix}_${op.handle.slot}`, state, compatibility);
19556
19793
  break;
19557
19794
  case OpKind.StyleProp:
19558
19795
  op.name = normalizeStylePropName(op.name);
@@ -19628,7 +19865,7 @@ function stripImportant(name) {
19628
19865
  * is, the call is purely side-effectful).
19629
19866
  * * No operations in between them uses the implicit context.
19630
19867
  */
19631
- function phaseMergeNextContext(job) {
19868
+ function mergeNextContextExpressions(job) {
19632
19869
  for (const unit of job.units) {
19633
19870
  for (const op of unit.create) {
19634
19871
  if (op.kind === OpKind.Listener) {
@@ -19684,7 +19921,7 @@ const CONTAINER_TAG = 'ng-container';
19684
19921
  /**
19685
19922
  * Replace an `Element` or `ElementStart` whose tag is `ng-container` with a specific op.
19686
19923
  */
19687
- function phaseNgContainer(job) {
19924
+ function generateNgContainerOps(job) {
19688
19925
  for (const unit of job.units) {
19689
19926
  const updatedElementXrefs = new Set();
19690
19927
  for (const op of unit.create) {
@@ -19716,7 +19953,7 @@ function lookupElement(elements, xref) {
19716
19953
  * all descendants of that container. Therefore, we must emit `disableBindings` and `enableBindings`
19717
19954
  * instructions for every such container.
19718
19955
  */
19719
- function phaseNonbindable(job) {
19956
+ function disableBindings$1(job) {
19720
19957
  const elements = new Map();
19721
19958
  for (const view of job.units) {
19722
19959
  for (const op of view.create) {
@@ -19740,7 +19977,13 @@ function phaseNonbindable(job) {
19740
19977
  }
19741
19978
  }
19742
19979
 
19743
- function phaseNullishCoalescing(job) {
19980
+ /**
19981
+ * Nullish coalescing expressions such as `a ?? b` have different semantics in Angular templates as
19982
+ * compared to JavaScript. In particular, they default to `null` instead of `undefined`. Therefore,
19983
+ * we replace them with ternary expressions, assigning temporaries as needed to avoid re-evaluating
19984
+ * the same sub-expression multiple times.
19985
+ */
19986
+ function generateNullishCoalesceExpressions(job) {
19744
19987
  for (const unit of job.units) {
19745
19988
  for (const op of unit.ops()) {
19746
19989
  transformExpressionsInOp(op, expr => {
@@ -19798,7 +20041,12 @@ const handledOpKinds = new Set([
19798
20041
  OpKind.Listener, OpKind.StyleMap, OpKind.ClassMap, OpKind.StyleProp,
19799
20042
  OpKind.ClassProp, OpKind.Property, OpKind.HostProperty, OpKind.Attribute
19800
20043
  ]);
19801
- function phaseOrdering(job) {
20044
+ /**
20045
+ * Many type of operations have ordering constraints that must be respected. For example, a
20046
+ * `ClassMap` instruction must be ordered after a `StyleMap` instruction, in order to have
20047
+ * predictable semantics that match TemplateDefinitionBuilder and don't break applications.
20048
+ */
20049
+ function orderOps(job) {
19802
20050
  for (const unit of job.units) {
19803
20051
  // First, we pull out ops that need to be ordered. Then, when we encounter an op that shouldn't
19804
20052
  // be reordered, put the ones we've pulled so far back in the correct order. Finally, if we
@@ -19860,8 +20108,8 @@ function keepLast(ops) {
19860
20108
  * Parses extracted style and class attributes into separate ExtractedAttributeOps per style or
19861
20109
  * class property.
19862
20110
  */
19863
- function phaseParseExtractedStyles(cpl) {
19864
- for (const unit of cpl.units) {
20111
+ function parseExtractedStyles(job) {
20112
+ for (const unit of job.units) {
19865
20113
  for (const op of unit.create) {
19866
20114
  if (op.kind === OpKind.ExtractedAttribute && op.bindingKind === BindingKind.Attribute &&
19867
20115
  isStringLiteral(op.expression)) {
@@ -19888,7 +20136,7 @@ function phaseParseExtractedStyles(cpl) {
19888
20136
  * Attributes of `ng-content` named 'select' are specifically removed, because they control which
19889
20137
  * content matches as a property of the `projection`, and are not a plain attribute.
19890
20138
  */
19891
- function phaseRemoveContentSelectors(job) {
20139
+ function removeContentSelectors(job) {
19892
20140
  for (const unit of job.units) {
19893
20141
  const elements = createOpXrefMap(unit);
19894
20142
  for (const op of unit.update) {
@@ -19914,7 +20162,14 @@ function lookupInXrefMap(map, xref) {
19914
20162
  return el;
19915
20163
  }
19916
20164
 
19917
- function phasePipeCreation(job) {
20165
+ /**
20166
+ * This phase generates pipe creation instructions. We do this based on the pipe bindings found in
20167
+ * the update block, in the order we see them.
20168
+ *
20169
+ * When not in compatibility mode, we can simply group all these creation instructions together, to
20170
+ * maximize chaining opportunities.
20171
+ */
20172
+ function createPipes(job) {
19918
20173
  for (const unit of job.units) {
19919
20174
  processPipeBindingsInView(unit);
19920
20175
  }
@@ -19931,21 +20186,19 @@ function processPipeBindingsInView(unit) {
19931
20186
  if (flags & VisitorContextFlag.InChildOperation) {
19932
20187
  throw new Error(`AssertionError: pipe bindings should not appear in child expressions`);
19933
20188
  }
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
20189
  if (unit.job.compatibility) {
20190
+ // TODO: We can delete this cast and check once compatibility mode is removed.
20191
+ const slotHandle = updateOp.target;
20192
+ if (slotHandle == undefined) {
20193
+ throw new Error(`AssertionError: expected slot handle to be assigned for pipe creation`);
20194
+ }
19942
20195
  addPipeToCreationBlock(unit, updateOp.target, expr);
19943
20196
  }
19944
20197
  else {
19945
20198
  // When not in compatibility mode, we just add the pipe to the end of the create block. This
19946
20199
  // is not only simpler and faster, but allows more chaining opportunities for other
19947
20200
  // instructions.
19948
- unit.create.push(createPipeOp(expr.target, expr.name));
20201
+ unit.create.push(createPipeOp(expr.target, expr.targetSlot, expr.name));
19949
20202
  }
19950
20203
  });
19951
20204
  }
@@ -19966,7 +20219,7 @@ function addPipeToCreationBlock(unit, afterTargetXref, binding) {
19966
20219
  while (op.next.kind === OpKind.Pipe) {
19967
20220
  op = op.next;
19968
20221
  }
19969
- const pipe = createPipeOp(binding.target, binding.name);
20222
+ const pipe = createPipeOp(binding.target, binding.targetSlot, binding.name);
19970
20223
  OpList.insertBefore(pipe, op.next);
19971
20224
  // This completes adding the pipe to the creation block.
19972
20225
  return;
@@ -19975,7 +20228,11 @@ function addPipeToCreationBlock(unit, afterTargetXref, binding) {
19975
20228
  throw new Error(`AssertionError: unable to find insertion point for pipe ${binding.name}`);
19976
20229
  }
19977
20230
 
19978
- function phasePipeVariadic(job) {
20231
+ /**
20232
+ * Pipes that accept more than 4 arguments are variadic, and are handled with a different runtime
20233
+ * instruction.
20234
+ */
20235
+ function createVariadicPipes(job) {
19979
20236
  for (const unit of job.units) {
19980
20237
  for (const op of unit.update) {
19981
20238
  transformExpressionsInOp(op, expr => {
@@ -19986,7 +20243,7 @@ function phasePipeVariadic(job) {
19986
20243
  if (expr.args.length <= 4) {
19987
20244
  return expr;
19988
20245
  }
19989
- return new PipeBindingVariadicExpr(expr.target, expr.name, literalArr(expr.args), expr.args.length);
20246
+ return new PipeBindingVariadicExpr(expr.target, expr.targetSlot, expr.name, literalArr(expr.args), expr.args.length);
19990
20247
  }, VisitorContextFlag.None);
19991
20248
  }
19992
20249
  }
@@ -19996,7 +20253,7 @@ function phasePipeVariadic(job) {
19996
20253
  * Propagate i18n blocks down through child templates that act as placeholders in the root i18n
19997
20254
  * message.
19998
20255
  */
19999
- function phasePropagateI18nBlocks(job) {
20256
+ function propagateI18nBlocks(job) {
20000
20257
  propagateI18nBlocksToTemplates(job.root, 0);
20001
20258
  }
20002
20259
  /**
@@ -20041,7 +20298,60 @@ function wrapTemplateWithI18n(unit, parentI18n) {
20041
20298
  }
20042
20299
  }
20043
20300
 
20044
- function phasePureFunctionExtraction(job) {
20301
+ /**
20302
+ * Propagate extractd message placeholders up to their root extracted message op.
20303
+ */
20304
+ function propogateI18nPlaceholders(job) {
20305
+ // Record all of the i18n and extracted message ops for use later.
20306
+ const i18nOps = new Map();
20307
+ const extractedMessageOps = new Map();
20308
+ for (const unit of job.units) {
20309
+ for (const op of unit.create) {
20310
+ switch (op.kind) {
20311
+ case OpKind.I18nStart:
20312
+ i18nOps.set(op.xref, op);
20313
+ break;
20314
+ case OpKind.ExtractedMessage:
20315
+ extractedMessageOps.set(op.owner, op);
20316
+ break;
20317
+ }
20318
+ }
20319
+ }
20320
+ // For each non-root message, merge its params into the root message's params.
20321
+ for (const [xref, childExtractedMessageOp] of extractedMessageOps) {
20322
+ if (!childExtractedMessageOp.isRoot) {
20323
+ const i18nOp = i18nOps.get(xref);
20324
+ if (i18nOp === undefined) {
20325
+ throw Error('Could not find owner i18n block for extracted message.');
20326
+ }
20327
+ const rootExtractedMessageOp = extractedMessageOps.get(i18nOp.root);
20328
+ if (rootExtractedMessageOp === undefined) {
20329
+ throw Error('Could not find extracted message op for root i18n block.');
20330
+ }
20331
+ mergeParams(rootExtractedMessageOp.params, childExtractedMessageOp.params);
20332
+ mergeParams(rootExtractedMessageOp.postprocessingParams, childExtractedMessageOp.postprocessingParams);
20333
+ }
20334
+ }
20335
+ }
20336
+ /**
20337
+ * Merges the params in the `from` map to into the `to` map.
20338
+ */
20339
+ function mergeParams(to, from) {
20340
+ for (const [placeholder, fromValues] of from) {
20341
+ const toValues = to.get(placeholder) || [];
20342
+ // TODO(mmalerba): Child element close tag params should be prepended to maintain the same order
20343
+ // as TemplateDefinitionBuilder. Can be cleaned up when compatibility is no longer required.
20344
+ const flags = fromValues[0].flags;
20345
+ if ((flags & I18nParamValueFlags.CloseTag) && !(flags & I18nParamValueFlags.OpenTag)) {
20346
+ to.set(placeholder, [...fromValues, ...toValues]);
20347
+ }
20348
+ else {
20349
+ to.set(placeholder, [...toValues, ...fromValues]);
20350
+ }
20351
+ }
20352
+ }
20353
+
20354
+ function extractPureFunctions(job) {
20045
20355
  for (const view of job.units) {
20046
20356
  for (const op of view.ops()) {
20047
20357
  visitExpressionsInOp(op, expr => {
@@ -20086,9 +20396,9 @@ class PureFunctionConstant extends GenericKeyFn {
20086
20396
  }
20087
20397
  }
20088
20398
 
20089
- function phasePureLiteralStructures(job) {
20090
- for (const view of job.units) {
20091
- for (const op of view.update) {
20399
+ function generatePureLiteralStructures(job) {
20400
+ for (const unit of job.units) {
20401
+ for (const op of unit.update) {
20092
20402
  transformExpressionsInOp(op, (expr, flags) => {
20093
20403
  if (flags & VisitorContextFlag.InChildOperation) {
20094
20404
  return expr;
@@ -20246,7 +20556,7 @@ function text(slot, initialValue, sourceSpan) {
20246
20556
  }
20247
20557
  return call(Identifiers.text, args, sourceSpan);
20248
20558
  }
20249
- function defer(selfSlot, primarySlot, dependencyResolverFn, loadingSlot, placeholderSlot, errorSlot, loadingConfigIndex, placeholderConfigIndex, sourceSpan) {
20559
+ function defer(selfSlot, primarySlot, dependencyResolverFn, loadingSlot, placeholderSlot, errorSlot, loadingConfig, placeholderConfig, enableTimerScheduling, sourceSpan) {
20250
20560
  const args = [
20251
20561
  literal(selfSlot),
20252
20562
  literal(primarySlot),
@@ -20254,16 +20564,40 @@ function defer(selfSlot, primarySlot, dependencyResolverFn, loadingSlot, placeho
20254
20564
  literal(loadingSlot),
20255
20565
  literal(placeholderSlot),
20256
20566
  literal(errorSlot),
20257
- literal(loadingConfigIndex),
20258
- literal(placeholderConfigIndex),
20567
+ loadingConfig ?? literal(null),
20568
+ placeholderConfig ?? literal(null),
20569
+ enableTimerScheduling ? importExpr(Identifiers.deferEnableTimerScheduling) : literal(null),
20259
20570
  ];
20260
- while (args[args.length - 1].value === null) {
20571
+ let expr;
20572
+ while ((expr = args[args.length - 1]) !== null && expr instanceof LiteralExpr &&
20573
+ expr.value === null) {
20261
20574
  args.pop();
20262
20575
  }
20263
20576
  return call(Identifiers.defer, args, sourceSpan);
20264
20577
  }
20265
- function deferOn(sourceSpan) {
20266
- return call(Identifiers.deferOnIdle, [], sourceSpan);
20578
+ const deferTriggerToR3TriggerInstructionsMap = new Map([
20579
+ [DeferTriggerKind.Idle, [Identifiers.deferOnIdle, Identifiers.deferPrefetchOnIdle]],
20580
+ [
20581
+ DeferTriggerKind.Immediate,
20582
+ [Identifiers.deferOnImmediate, Identifiers.deferPrefetchOnImmediate]
20583
+ ],
20584
+ [DeferTriggerKind.Timer, [Identifiers.deferOnTimer, Identifiers.deferPrefetchOnTimer]],
20585
+ [DeferTriggerKind.Hover, [Identifiers.deferOnHover, Identifiers.deferPrefetchOnHover]],
20586
+ [
20587
+ DeferTriggerKind.Interaction,
20588
+ [Identifiers.deferOnInteraction, Identifiers.deferPrefetchOnInteraction]
20589
+ ],
20590
+ [
20591
+ DeferTriggerKind.Viewport, [Identifiers.deferOnViewport, Identifiers.deferPrefetchOnViewport]
20592
+ ],
20593
+ ]);
20594
+ function deferOn(trigger, args, prefetch, sourceSpan) {
20595
+ const instructions = deferTriggerToR3TriggerInstructionsMap.get(trigger);
20596
+ if (instructions === undefined) {
20597
+ throw new Error(`Unable to determine instruction for trigger ${trigger}`);
20598
+ }
20599
+ const instructionToCall = prefetch ? instructions[1] : instructions[0];
20600
+ return call(instructionToCall, args.map(a => literal(a)), sourceSpan);
20267
20601
  }
20268
20602
  function projectionDef(def) {
20269
20603
  return call(Identifiers.projectionDef, def ? [def] : [], null);
@@ -20285,20 +20619,20 @@ function i18nStart(slot, constIndex, subTemplateIndex) {
20285
20619
  }
20286
20620
  return call(Identifiers.i18nStart, args, null);
20287
20621
  }
20288
- function repeaterCreate(slot, viewFnName, decls, vars, trackByFn, trackByUsesComponentInstance, emptyViewFnName, emptyDecls, emptyVars, sourceSpan) {
20289
- let args = [
20622
+ function repeaterCreate(slot, viewFnName, decls, vars, tag, constIndex, trackByFn, trackByUsesComponentInstance, emptyViewFnName, emptyDecls, emptyVars, sourceSpan) {
20623
+ const args = [
20290
20624
  literal(slot),
20291
20625
  variable(viewFnName),
20292
20626
  literal(decls),
20293
20627
  literal(vars),
20628
+ literal(tag),
20629
+ literal(constIndex),
20294
20630
  trackByFn,
20295
20631
  ];
20296
20632
  if (trackByUsesComponentInstance || emptyViewFnName !== null) {
20297
20633
  args.push(literal(trackByUsesComponentInstance));
20298
20634
  if (emptyViewFnName !== null) {
20299
- args.push(variable(emptyViewFnName));
20300
- args.push(literal(emptyDecls));
20301
- args.push(literal(emptyVars));
20635
+ args.push(variable(emptyViewFnName), literal(emptyDecls), literal(emptyVars));
20302
20636
  }
20303
20637
  }
20304
20638
  return call(Identifiers.repeaterCreate, args, sourceSpan);
@@ -20662,8 +20996,8 @@ const sanitizerIdentifierMap = new Map([
20662
20996
  * structures. After reification, the create/update operation lists of all views should only contain
20663
20997
  * `ir.StatementOp`s (which wrap generated `o.Statement`s).
20664
20998
  */
20665
- function phaseReify(cpl) {
20666
- for (const unit of cpl.units) {
20999
+ function reify(job) {
21000
+ for (const unit of job.units) {
20667
21001
  reifyCreateOperations(unit, unit.create);
20668
21002
  reifyUpdateOperations(unit, unit.update);
20669
21003
  }
@@ -20673,41 +21007,41 @@ function reifyCreateOperations(unit, ops) {
20673
21007
  transformExpressionsInOp(op, reifyIrExpression, VisitorContextFlag.None);
20674
21008
  switch (op.kind) {
20675
21009
  case OpKind.Text:
20676
- OpList.replace(op, text(op.slot, op.initialValue, op.sourceSpan));
21010
+ OpList.replace(op, text(op.handle.slot, op.initialValue, op.sourceSpan));
20677
21011
  break;
20678
21012
  case OpKind.ElementStart:
20679
- OpList.replace(op, elementStart(op.slot, op.tag, op.attributes, op.localRefs, op.sourceSpan));
21013
+ OpList.replace(op, elementStart(op.handle.slot, op.tag, op.attributes, op.localRefs, op.sourceSpan));
20680
21014
  break;
20681
21015
  case OpKind.Element:
20682
- OpList.replace(op, element(op.slot, op.tag, op.attributes, op.localRefs, op.sourceSpan));
21016
+ OpList.replace(op, element(op.handle.slot, op.tag, op.attributes, op.localRefs, op.sourceSpan));
20683
21017
  break;
20684
21018
  case OpKind.ElementEnd:
20685
21019
  OpList.replace(op, elementEnd(op.sourceSpan));
20686
21020
  break;
20687
21021
  case OpKind.ContainerStart:
20688
- OpList.replace(op, elementContainerStart(op.slot, op.attributes, op.localRefs, op.sourceSpan));
21022
+ OpList.replace(op, elementContainerStart(op.handle.slot, op.attributes, op.localRefs, op.sourceSpan));
20689
21023
  break;
20690
21024
  case OpKind.Container:
20691
- OpList.replace(op, elementContainer(op.slot, op.attributes, op.localRefs, op.sourceSpan));
21025
+ OpList.replace(op, elementContainer(op.handle.slot, op.attributes, op.localRefs, op.sourceSpan));
20692
21026
  break;
20693
21027
  case OpKind.ContainerEnd:
20694
21028
  OpList.replace(op, elementContainerEnd());
20695
21029
  break;
20696
21030
  case OpKind.I18nStart:
20697
- OpList.replace(op, i18nStart(op.slot, op.messageIndex, op.subTemplateIndex));
21031
+ OpList.replace(op, i18nStart(op.handle.slot, op.messageIndex, op.subTemplateIndex));
20698
21032
  break;
20699
21033
  case OpKind.I18nEnd:
20700
21034
  OpList.replace(op, i18nEnd());
20701
21035
  break;
20702
21036
  case OpKind.I18n:
20703
- OpList.replace(op, i18n(op.slot, op.messageIndex, op.subTemplateIndex));
21037
+ OpList.replace(op, i18n(op.handle.slot, op.messageIndex, op.subTemplateIndex));
20704
21038
  break;
20705
21039
  case OpKind.Template:
20706
21040
  if (!(unit instanceof ViewCompilationUnit)) {
20707
21041
  throw new Error(`AssertionError: must be compiling a component`);
20708
21042
  }
20709
21043
  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));
21044
+ OpList.replace(op, template(op.handle.slot, variable(childView.fnName), childView.decls, childView.vars, op.tag, op.attributes, op.sourceSpan));
20711
21045
  break;
20712
21046
  case OpKind.DisableBindings:
20713
21047
  OpList.replace(op, disableBindings());
@@ -20716,7 +21050,7 @@ function reifyCreateOperations(unit, ops) {
20716
21050
  OpList.replace(op, enableBindings());
20717
21051
  break;
20718
21052
  case OpKind.Pipe:
20719
- OpList.replace(op, pipe(op.slot, op.name));
21053
+ OpList.replace(op, pipe(op.handle.slot, op.name));
20720
21054
  break;
20721
21055
  case OpKind.Listener:
20722
21056
  const listenerFn = reifyListenerHandler(unit, op.handlerFnName, op.handlerOps, op.consumesDollarEvent);
@@ -20745,25 +21079,45 @@ function reifyCreateOperations(unit, ops) {
20745
21079
  }
20746
21080
  break;
20747
21081
  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);
21082
+ const timerScheduling = !!op.loadingMinimumTime || !!op.loadingAfterTime || !!op.placeholderMinimumTime;
21083
+ OpList.replace(op, defer(op.handle.slot, op.mainSlot.slot, null, op.loadingSlot?.slot ?? null, op.placeholderSlot?.slot ?? null, op.errorSlot?.slot ?? null, op.loadingConfig, op.placeholderConfig, timerScheduling, op.sourceSpan));
20752
21084
  break;
20753
21085
  case OpKind.DeferOn:
20754
- OpList.replace(op, deferOn(op.sourceSpan));
21086
+ let args = [];
21087
+ switch (op.trigger.kind) {
21088
+ case DeferTriggerKind.Idle:
21089
+ case DeferTriggerKind.Immediate:
21090
+ break;
21091
+ case DeferTriggerKind.Timer:
21092
+ args = [op.trigger.delay];
21093
+ break;
21094
+ case DeferTriggerKind.Interaction:
21095
+ case DeferTriggerKind.Hover:
21096
+ case DeferTriggerKind.Viewport:
21097
+ if (op.trigger.targetSlot?.slot == null || op.trigger.targetSlotViewSteps === null) {
21098
+ throw new Error(`Slot or view steps not set in trigger reification for trigger kind ${op.trigger.kind}`);
21099
+ }
21100
+ args = [op.trigger.targetSlot.slot];
21101
+ if (op.trigger.targetSlotViewSteps !== 0) {
21102
+ args.push(op.trigger.targetSlotViewSteps);
21103
+ }
21104
+ break;
21105
+ default:
21106
+ throw new Error(`AssertionError: Unsupported reification of defer trigger kind ${op.trigger.kind}`);
21107
+ }
21108
+ OpList.replace(op, deferOn(op.trigger.kind, args, op.prefetch, op.sourceSpan));
20755
21109
  break;
20756
21110
  case OpKind.ProjectionDef:
20757
21111
  OpList.replace(op, projectionDef(op.def));
20758
21112
  break;
20759
21113
  case OpKind.Projection:
20760
- if (op.slot === null) {
21114
+ if (op.handle.slot === null) {
20761
21115
  throw new Error('No slot was assigned for project instruction');
20762
21116
  }
20763
- OpList.replace(op, projection(op.slot, op.projectionSlotIndex, op.attributes, op.sourceSpan));
21117
+ OpList.replace(op, projection(op.handle.slot, op.projectionSlotIndex, op.attributes, op.sourceSpan));
20764
21118
  break;
20765
21119
  case OpKind.RepeaterCreate:
20766
- if (op.slot === null) {
21120
+ if (op.handle.slot === null) {
20767
21121
  throw new Error('No slot was assigned for repeater instruction');
20768
21122
  }
20769
21123
  if (!(unit instanceof ViewCompilationUnit)) {
@@ -20788,7 +21142,7 @@ function reifyCreateOperations(unit, ops) {
20788
21142
  emptyDecls = emptyView.decls;
20789
21143
  emptyVars = emptyView.vars;
20790
21144
  }
20791
- OpList.replace(op, repeaterCreate(op.slot, repeaterView.fnName, op.decls, op.vars, op.trackByFn, op.usesComponentInstance, emptyViewFnName, emptyDecls, emptyVars, op.sourceSpan));
21145
+ 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
21146
  break;
20793
21147
  case OpKind.Statement:
20794
21148
  // Pass statement operations directly through.
@@ -20844,7 +21198,7 @@ function reifyUpdateOperations(_unit, ops) {
20844
21198
  OpList.replace(op, i18nExp(op.expression, op.sourceSpan));
20845
21199
  break;
20846
21200
  case OpKind.I18nApply:
20847
- OpList.replace(op, i18nApply(op.targetSlot, op.sourceSpan));
21201
+ OpList.replace(op, i18nApply(op.targetSlot.slot, op.sourceSpan));
20848
21202
  break;
20849
21203
  case OpKind.InterpolateText:
20850
21204
  OpList.replace(op, textInterpolate(op.interpolation.strings, op.interpolation.expressions, op.sourceSpan));
@@ -20880,13 +21234,13 @@ function reifyUpdateOperations(_unit, ops) {
20880
21234
  if (op.processed === null) {
20881
21235
  throw new Error(`Conditional test was not set.`);
20882
21236
  }
20883
- if (op.targetSlot === null) {
21237
+ if (op.targetSlot.slot === null) {
20884
21238
  throw new Error(`Conditional slot was not set.`);
20885
21239
  }
20886
- OpList.replace(op, conditional(op.targetSlot, op.processed, op.contextValue, op.sourceSpan));
21240
+ OpList.replace(op, conditional(op.targetSlot.slot, op.processed, op.contextValue, op.sourceSpan));
20887
21241
  break;
20888
21242
  case OpKind.Repeater:
20889
- OpList.replace(op, repeater(op.targetSlot, op.collection, op.sourceSpan));
21243
+ OpList.replace(op, repeater(op.targetSlot.slot, op.collection, op.sourceSpan));
20890
21244
  break;
20891
21245
  case OpKind.Statement:
20892
21246
  // Pass statement operations directly through.
@@ -20904,7 +21258,7 @@ function reifyIrExpression(expr) {
20904
21258
  case ExpressionKind.NextContext:
20905
21259
  return nextContext(expr.steps);
20906
21260
  case ExpressionKind.Reference:
20907
- return reference(expr.targetSlot + 1 + expr.offset);
21261
+ return reference(expr.targetSlot.slot + 1 + expr.offset);
20908
21262
  case ExpressionKind.LexicalRead:
20909
21263
  throw new Error(`AssertionError: unresolved LexicalRead of ${expr.name}`);
20910
21264
  case ExpressionKind.RestoreView:
@@ -20939,13 +21293,13 @@ function reifyIrExpression(expr) {
20939
21293
  case ExpressionKind.PureFunctionParameterExpr:
20940
21294
  throw new Error(`AssertionError: expected PureFunctionParameterExpr to have been extracted`);
20941
21295
  case ExpressionKind.PipeBinding:
20942
- return pipeBind(expr.targetSlot, expr.varOffset, expr.args);
21296
+ return pipeBind(expr.targetSlot.slot, expr.varOffset, expr.args);
20943
21297
  case ExpressionKind.PipeBindingVariadic:
20944
- return pipeBindV(expr.targetSlot, expr.varOffset, expr.args);
21298
+ return pipeBindV(expr.targetSlot.slot, expr.varOffset, expr.args);
20945
21299
  case ExpressionKind.SanitizerExpr:
20946
21300
  return importExpr(sanitizerIdentifierMap.get(expr.fn));
20947
21301
  case ExpressionKind.SlotLiteralExpr:
20948
- return literal(expr.targetSlot);
21302
+ return literal(expr.slot.slot);
20949
21303
  default:
20950
21304
  throw new Error(`AssertionError: Unsupported reification of ir.Expression kind: ${ExpressionKind[expr.kind]}`);
20951
21305
  }
@@ -20975,7 +21329,10 @@ function reifyListenerHandler(unit, name, handlerOps, consumesDollarEvent) {
20975
21329
  return fn(params, handlerStmts, undefined, undefined, name);
20976
21330
  }
20977
21331
 
20978
- function phaseRemoveEmptyBindings(job) {
21332
+ /**
21333
+ * Bidningd with no content can be safely deleted.
21334
+ */
21335
+ function removeEmptyBindings(job) {
20979
21336
  for (const unit of job.units) {
20980
21337
  for (const op of unit.update) {
20981
21338
  switch (op.kind) {
@@ -20996,14 +21353,50 @@ function phaseRemoveEmptyBindings(job) {
20996
21353
  }
20997
21354
 
20998
21355
  /**
20999
- * Resolves `ir.ContextExpr` expressions (which represent embedded view or component contexts) to
21000
- * either the `ctx` parameter to component functions (for the current view context) or to variables
21001
- * that store those contexts (for contexts accessed via the `nextContext()` instruction).
21356
+ * Inside the body of a repeater, certain context variables (such as `$first`) are ambiently
21357
+ * available. This phase finds those variable usages, and replaces them with the appropriate
21358
+ * expression.
21002
21359
  */
21003
- function phaseResolveContexts(cpl) {
21004
- for (const unit of cpl.units) {
21005
- processLexicalScope$1(unit, unit.create);
21006
- processLexicalScope$1(unit, unit.update);
21360
+ function generateRepeaterDerivedVars(job) {
21361
+ const repeaters = new Map();
21362
+ for (const unit of job.units) {
21363
+ for (const op of unit.ops()) {
21364
+ if (op.kind === OpKind.RepeaterCreate) {
21365
+ repeaters.set(op.xref, op);
21366
+ }
21367
+ }
21368
+ }
21369
+ for (const unit of job.units) {
21370
+ for (const op of unit.ops()) {
21371
+ transformExpressionsInOp(op, expr => {
21372
+ if (!(expr instanceof DerivedRepeaterVarExpr)) {
21373
+ return expr;
21374
+ }
21375
+ const repeaterOp = repeaters.get(expr.xref);
21376
+ switch (expr.identity) {
21377
+ case DerivedRepeaterVarIdentity.First:
21378
+ return new BinaryOperatorExpr(BinaryOperator.Identical, new LexicalReadExpr(repeaterOp.varNames.$index), literal(0));
21379
+ case DerivedRepeaterVarIdentity.Last:
21380
+ return new BinaryOperatorExpr(BinaryOperator.Identical, new LexicalReadExpr(repeaterOp.varNames.$index), new BinaryOperatorExpr(BinaryOperator.Minus, new LexicalReadExpr(repeaterOp.varNames.$count), literal(1)));
21381
+ case DerivedRepeaterVarIdentity.Even:
21382
+ return new BinaryOperatorExpr(BinaryOperator.Identical, new BinaryOperatorExpr(BinaryOperator.Modulo, new LexicalReadExpr(repeaterOp.varNames.$index), literal(2)), literal(0));
21383
+ case DerivedRepeaterVarIdentity.Odd:
21384
+ return new BinaryOperatorExpr(BinaryOperator.NotIdentical, new BinaryOperatorExpr(BinaryOperator.Modulo, new LexicalReadExpr(repeaterOp.varNames.$index), literal(2)), literal(0));
21385
+ }
21386
+ }, VisitorContextFlag.None);
21387
+ }
21388
+ }
21389
+ }
21390
+
21391
+ /**
21392
+ * Resolves `ir.ContextExpr` expressions (which represent embedded view or component contexts) to
21393
+ * either the `ctx` parameter to component functions (for the current view context) or to variables
21394
+ * that store those contexts (for contexts accessed via the `nextContext()` instruction).
21395
+ */
21396
+ function resolveContexts(job) {
21397
+ for (const unit of job.units) {
21398
+ processLexicalScope$1(unit, unit.create);
21399
+ processLexicalScope$1(unit, unit.update);
21007
21400
  }
21008
21401
  }
21009
21402
  function processLexicalScope$1(view, ops) {
@@ -21049,13 +21442,13 @@ function processLexicalScope$1(view, ops) {
21049
21442
  * Any variable inside a listener with the name `$event` will be transformed into a output lexical
21050
21443
  * read immediately, and does not participate in any of the normal logic for handling variables.
21051
21444
  */
21052
- function phaseResolveDollarEvent(job) {
21445
+ function resolveDollarEvent(job) {
21053
21446
  for (const unit of job.units) {
21054
- resolveDollarEvent(unit, unit.create);
21055
- resolveDollarEvent(unit, unit.update);
21447
+ transformDollarEvent(unit, unit.create);
21448
+ transformDollarEvent(unit, unit.update);
21056
21449
  }
21057
21450
  }
21058
- function resolveDollarEvent(unit, ops) {
21451
+ function transformDollarEvent(unit, ops) {
21059
21452
  for (const op of ops) {
21060
21453
  if (op.kind === OpKind.Listener) {
21061
21454
  transformExpressionsInOp(op, (expr) => {
@@ -21070,201 +21463,46 @@ function resolveDollarEvent(unit, ops) {
21070
21463
  }
21071
21464
 
21072
21465
  /**
21073
- * The escape sequence used indicate message param values.
21074
- */
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.
21466
+ * Resolve the element placeholders in i18n messages.
21222
21467
  */
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
- }
21468
+ function resolveI18nElementPlaceholders(job) {
21469
+ // Record all of the element and extracted message ops for use later.
21470
+ const extractedMessageOps = new Map();
21471
+ const elements = new Map();
21472
+ for (const unit of job.units) {
21473
+ for (const op of unit.create) {
21474
+ switch (op.kind) {
21475
+ case OpKind.ExtractedMessage:
21476
+ extractedMessageOps.set(op.owner, op);
21477
+ break;
21478
+ case OpKind.ElementStart:
21479
+ elements.set(op.xref, op);
21480
+ break;
21239
21481
  }
21240
21482
  }
21241
21483
  }
21242
- }
21243
- /**
21244
- * Resolve placeholders for each i18n op.
21245
- */
21246
- function resolvePlaceholders(job, params, i18nOps) {
21247
21484
  for (const unit of job.units) {
21248
- const elements = new Map();
21249
- let currentI18nOp = null;
21250
- // Record slots for tag name placeholders.
21485
+ // Track the current i18n op and corresponding extracted message op as we step through the
21486
+ // creation IR.
21487
+ let currentOps = null;
21251
21488
  for (const op of unit.create) {
21252
21489
  switch (op.kind) {
21253
21490
  case OpKind.I18nStart:
21254
- i18nOps.set(op.xref, op);
21255
- currentI18nOp = op.kind === OpKind.I18nStart ? op : null;
21491
+ if (!extractedMessageOps.has(op.xref)) {
21492
+ throw Error('Could not find extracted message for i18n op');
21493
+ }
21494
+ currentOps = { i18n: op, extractedMessage: extractedMessageOps.get(op.xref) };
21256
21495
  break;
21257
21496
  case OpKind.I18nEnd:
21258
- currentI18nOp = null;
21497
+ currentOps = null;
21259
21498
  break;
21260
21499
  case OpKind.ElementStart:
21261
21500
  // For elements with i18n placeholders, record its slot value in the params map under the
21262
21501
  // corresponding tag start placeholder.
21263
21502
  if (op.i18nPlaceholder !== undefined) {
21264
- if (currentI18nOp === null) {
21503
+ if (currentOps === null) {
21265
21504
  throw Error('i18n tag placeholder should only occur inside an i18n block');
21266
21505
  }
21267
- elements.set(op.xref, op);
21268
21506
  const { startName, closeName } = op.i18nPlaceholder;
21269
21507
  let flags = I18nParamValueFlags.ElementTag | I18nParamValueFlags.OpenTag;
21270
21508
  // For self-closing tags, there is no close tag placeholder. Instead, the start tag
@@ -21272,57 +21510,40 @@ function resolvePlaceholders(job, params, i18nOps) {
21272
21510
  if (closeName === '') {
21273
21511
  flags |= I18nParamValueFlags.CloseTag;
21274
21512
  }
21275
- addParam(params, currentI18nOp, startName, op.slot, currentI18nOp.subTemplateIndex, I18nParamResolutionTime.Creation, flags);
21513
+ addParam(currentOps.extractedMessage.params, startName, op.handle.slot, currentOps.i18n.subTemplateIndex, flags);
21276
21514
  }
21277
21515
  break;
21278
21516
  case OpKind.ElementEnd:
21517
+ // For elements with i18n placeholders, record its slot value in the params map under the
21518
+ // corresponding tag close placeholder.
21279
21519
  const startOp = elements.get(op.xref);
21280
21520
  if (startOp && startOp.i18nPlaceholder !== undefined) {
21281
- if (currentI18nOp === null) {
21521
+ if (currentOps === null) {
21282
21522
  throw Error('i18n tag placeholder should only occur inside an i18n block');
21283
21523
  }
21284
21524
  const { closeName } = startOp.i18nPlaceholder;
21285
21525
  // Self-closing tags don't have a closing tag placeholder.
21286
21526
  if (closeName !== '') {
21287
- addParam(params, currentI18nOp, closeName, startOp.slot, currentI18nOp.subTemplateIndex, I18nParamResolutionTime.Creation, I18nParamValueFlags.ElementTag | I18nParamValueFlags.CloseTag);
21527
+ addParam(currentOps.extractedMessage.params, closeName, startOp.handle.slot, currentOps.i18n.subTemplateIndex, I18nParamValueFlags.ElementTag | I18nParamValueFlags.CloseTag);
21288
21528
  }
21289
21529
  }
21290
21530
  break;
21291
21531
  case OpKind.Template:
21532
+ // For templates with i18n placeholders, record its slot value in the params map under the
21533
+ // corresponding template start and close placeholders.
21292
21534
  if (op.i18nPlaceholder !== undefined) {
21293
- if (currentI18nOp === null) {
21535
+ if (currentOps === null) {
21294
21536
  throw Error('i18n tag placeholder should only occur inside an i18n block');
21295
21537
  }
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);
21538
+ const subTemplateIndex = getSubTemplateIndexForTemplateTag(job, currentOps.i18n, op);
21539
+ addParam(currentOps.extractedMessage.params, op.i18nPlaceholder.startName, op.handle.slot, subTemplateIndex, I18nParamValueFlags.TemplateTag);
21540
+ addParam(currentOps.extractedMessage.params, op.i18nPlaceholder.closeName, op.handle.slot, subTemplateIndex, I18nParamValueFlags.TemplateTag | I18nParamValueFlags.CloseTag);
21299
21541
  }
21300
21542
  break;
21301
21543
  }
21302
21544
  }
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
21545
  }
21317
21546
  }
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
21547
  /**
21327
21548
  * Get the subTemplateIndex for the given template op. For template ops, use the subTemplateIndex of
21328
21549
  * the child i18n block inside the template.
@@ -21335,15 +21556,59 @@ function getSubTemplateIndexForTemplateTag(job, i18nOp, op) {
21335
21556
  }
21336
21557
  return i18nOp.subTemplateIndex;
21337
21558
  }
21559
+ /** Add a param value to the given params map. */
21560
+ function addParam(params, placeholder, value, subTemplateIndex, flags = I18nParamValueFlags.None) {
21561
+ const values = params.get(placeholder) ?? [];
21562
+ values.push({ value, subTemplateIndex, flags });
21563
+ params.set(placeholder, values);
21564
+ }
21565
+
21338
21566
  /**
21339
- * Propagate placeholders up to their root i18n op.
21567
+ * Resolve the i18n expression placeholders in i18n messages.
21340
21568
  */
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);
21569
+ function resolveI18nExpressionPlaceholders(job) {
21570
+ // Record all of the i18n and extracted message ops for use later.
21571
+ const i18nOps = new Map();
21572
+ const extractedMessageOps = new Map();
21573
+ for (const unit of job.units) {
21574
+ for (const op of unit.create) {
21575
+ switch (op.kind) {
21576
+ case OpKind.I18nStart:
21577
+ i18nOps.set(op.xref, op);
21578
+ break;
21579
+ case OpKind.ExtractedMessage:
21580
+ extractedMessageOps.set(op.owner, op);
21581
+ break;
21582
+ }
21583
+ }
21584
+ }
21585
+ // Keep track of the next available expression index per i18n block.
21586
+ const expressionIndices = new Map();
21587
+ for (const unit of job.units) {
21588
+ for (const op of unit.update) {
21589
+ if (op.kind === OpKind.I18nExpression) {
21590
+ const i18nOp = i18nOps.get(op.owner);
21591
+ let index = expressionIndices.get(op.owner) || 0;
21592
+ if (!i18nOp) {
21593
+ throw Error('Cannot find corresponding i18n block for i18nExpr');
21594
+ }
21595
+ const extractedMessageOp = extractedMessageOps.get(i18nOp.xref);
21596
+ if (!extractedMessageOp) {
21597
+ throw Error('Cannot find extracted message for i18n block');
21598
+ }
21599
+ // Add the expression index in the appropriate params map.
21600
+ const params = op.resolutionTime === I18nParamResolutionTime.Creation ?
21601
+ extractedMessageOp.params :
21602
+ extractedMessageOp.postprocessingParams;
21603
+ const values = params.get(op.i18nPlaceholder) || [];
21604
+ values.push({
21605
+ value: index,
21606
+ subTemplateIndex: i18nOp.subTemplateIndex,
21607
+ flags: I18nParamValueFlags.None
21608
+ });
21609
+ params.set(op.i18nPlaceholder, values);
21610
+ expressionIndices.set(op.owner, index + 1);
21611
+ }
21347
21612
  }
21348
21613
  }
21349
21614
  }
@@ -21355,8 +21620,8 @@ function propagatePlaceholders(params, i18nOps) {
21355
21620
  * Also matches `ir.RestoreViewExpr` expressions with the variables of their corresponding saved
21356
21621
  * views.
21357
21622
  */
21358
- function phaseResolveNames(cpl) {
21359
- for (const unit of cpl.units) {
21623
+ function resolveNames(job) {
21624
+ for (const unit of job.units) {
21360
21625
  processLexicalScope(unit, unit.create, null);
21361
21626
  processLexicalScope(unit, unit.update, null);
21362
21627
  }
@@ -21457,7 +21722,7 @@ const sanitizers = new Map([
21457
21722
  /**
21458
21723
  * Resolves sanitization functions for ops that need them.
21459
21724
  */
21460
- function phaseResolveSanitizers(job) {
21725
+ function resolveSanitizers(job) {
21461
21726
  for (const unit of job.units) {
21462
21727
  const elements = createOpXrefMap(unit);
21463
21728
  let sanitizerFn;
@@ -21492,21 +21757,26 @@ function isIframeElement$1(op) {
21492
21757
  return op.kind === OpKind.ElementStart && op.tag?.toLowerCase() === 'iframe';
21493
21758
  }
21494
21759
 
21495
- function phaseSaveRestoreView(job) {
21496
- for (const view of job.views.values()) {
21497
- view.create.prepend([
21498
- createVariableOp(view.job.allocateXrefId(), {
21760
+ /**
21761
+ * When inside of a listener, we may need access to one or more enclosing views. Therefore, each
21762
+ * view should save the current view, and each listener must have the ability to restore the
21763
+ * appropriate view. We eagerly generate all save view variables; they will be optimized away later.
21764
+ */
21765
+ function saveAndRestoreView(job) {
21766
+ for (const unit of job.units) {
21767
+ unit.create.prepend([
21768
+ createVariableOp(unit.job.allocateXrefId(), {
21499
21769
  kind: SemanticVariableKind.SavedView,
21500
21770
  name: null,
21501
- view: view.xref,
21771
+ view: unit.xref,
21502
21772
  }, new GetCurrentViewExpr(), VariableFlags.None),
21503
21773
  ]);
21504
- for (const op of view.create) {
21774
+ for (const op of unit.create) {
21505
21775
  if (op.kind !== OpKind.Listener) {
21506
21776
  continue;
21507
21777
  }
21508
21778
  // Embedded views always need the save/restore view operation.
21509
- let needsRestoreView = view !== job.root;
21779
+ let needsRestoreView = unit !== job.root;
21510
21780
  if (!needsRestoreView) {
21511
21781
  for (const handlerOp of op.handlerOps) {
21512
21782
  visitExpressionsInOp(handlerOp, expr => {
@@ -21518,7 +21788,7 @@ function phaseSaveRestoreView(job) {
21518
21788
  }
21519
21789
  }
21520
21790
  if (needsRestoreView) {
21521
- addSaveRestoreViewOperationToListener(view, op);
21791
+ addSaveRestoreViewOperationToListener(unit, op);
21522
21792
  }
21523
21793
  }
21524
21794
  }
@@ -21550,7 +21820,7 @@ function addSaveRestoreViewOperationToListener(unit, op) {
21550
21820
  * This phase is also responsible for counting the number of slots used for each view (its `decls`)
21551
21821
  * and propagating that number into the `Template` operations which declare embedded views.
21552
21822
  */
21553
- function phaseSlotAllocation(job) {
21823
+ function allocateSlots(job) {
21554
21824
  // Map of all declarations in all views within the component which require an assigned slot index.
21555
21825
  // This map needs to be global (across all views within the component) since it's possible to
21556
21826
  // reference a slot from one view from an expression within another (e.g. local references work
@@ -21566,9 +21836,9 @@ function phaseSlotAllocation(job) {
21566
21836
  continue;
21567
21837
  }
21568
21838
  // Assign slots to this declaration starting at the current `slotCount`.
21569
- op.slot = slotCount;
21839
+ op.handle.slot = slotCount;
21570
21840
  // And track its assigned slot in the `slotMap`.
21571
- slotMap.set(op.xref, op.slot);
21841
+ slotMap.set(op.xref, op.handle.slot);
21572
21842
  // Each declaration may use more than 1 slot, so increment `slotCount` to reserve the number
21573
21843
  // of slots required.
21574
21844
  slotCount += op.numSlotsUsed;
@@ -21590,31 +21860,6 @@ function phaseSlotAllocation(job) {
21590
21860
  const childView = job.views.get(op.xref);
21591
21861
  op.decls = childView.decls;
21592
21862
  }
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
21863
  }
21619
21864
  }
21620
21865
  }
@@ -21623,8 +21868,8 @@ function phaseSlotAllocation(job) {
21623
21868
  * Transforms special-case bindings with 'style' or 'class' in their names. Must run before the
21624
21869
  * main binding specialization pass.
21625
21870
  */
21626
- function phaseStyleBindingSpecialization(cpl) {
21627
- for (const unit of cpl.units) {
21871
+ function specializeStyleBindings(job) {
21872
+ for (const unit of job.units) {
21628
21873
  for (const op of unit.update) {
21629
21874
  if (op.kind !== OpKind.Binding) {
21630
21875
  continue;
@@ -21662,8 +21907,8 @@ function phaseStyleBindingSpecialization(cpl) {
21662
21907
  * in the double keyed read `a?.[f()]?.[f()]`, the two function calls have non-overlapping scopes.
21663
21908
  * Implement an algorithm for reuse.
21664
21909
  */
21665
- function phaseTemporaryVariables(cpl) {
21666
- for (const unit of cpl.units) {
21910
+ function generateTemporaryVariables(job) {
21911
+ for (const unit of job.units) {
21667
21912
  unit.create.prepend(generateTemporaries(unit.create));
21668
21913
  unit.update.prepend(generateTemporaries(unit.update));
21669
21914
  }
@@ -21732,11 +21977,149 @@ function assignName(names, expr) {
21732
21977
  expr.name = name;
21733
21978
  }
21734
21979
 
21980
+ /**
21981
+ * Generate track functions that need to be extracted to the constant pool. This entails wrapping
21982
+ * them in an arrow (or traditional) function, replacing context reads with `this.`, and storing
21983
+ * them in the constant pool.
21984
+ *
21985
+ * Note that, if a track function was previously optimized, it will not need to be extracted, and
21986
+ * this phase is a no-op.
21987
+ */
21988
+ function generateTrackFns(job) {
21989
+ for (const unit of job.units) {
21990
+ for (const op of unit.create) {
21991
+ if (op.kind !== OpKind.RepeaterCreate) {
21992
+ continue;
21993
+ }
21994
+ if (op.trackByFn !== null) {
21995
+ // The final track function was already set, probably because it was optimized.
21996
+ continue;
21997
+ }
21998
+ // Find all component context reads.
21999
+ let usesComponentContext = false;
22000
+ op.track = transformExpressionsInExpression(op.track, expr => {
22001
+ if (expr instanceof TrackContextExpr) {
22002
+ usesComponentContext = true;
22003
+ return variable('this');
22004
+ }
22005
+ return expr;
22006
+ }, VisitorContextFlag.None);
22007
+ let fn;
22008
+ const fnParams = [new FnParam('$index'), new FnParam('$item')];
22009
+ if (usesComponentContext) {
22010
+ fn = new FunctionExpr(fnParams, [new ReturnStatement(op.track)]);
22011
+ }
22012
+ else {
22013
+ fn = arrowFn(fnParams, op.track);
22014
+ }
22015
+ op.trackByFn = job.pool.getSharedFunctionReference(fn, '_forTrack');
22016
+ }
22017
+ }
22018
+ }
22019
+
22020
+ /**
22021
+ * `track` functions in `for` repeaters can sometimes be "optimized," i.e. transformed into inline
22022
+ * expressions, in lieu of an external function call. For example, tracking by `$index` can be be
22023
+ * optimized into an inline `trackByIndex` reference. This phase checks track expressions for
22024
+ * optimizable cases.
22025
+ */
22026
+ function optimizeTrackFns(job) {
22027
+ for (const unit of job.units) {
22028
+ for (const op of unit.create) {
22029
+ if (op.kind !== OpKind.RepeaterCreate) {
22030
+ continue;
22031
+ }
22032
+ if (op.track instanceof ReadVarExpr && op.track.name === '$index') {
22033
+ // Top-level access of `$index` uses the built in `repeaterTrackByIndex`.
22034
+ op.trackByFn = importExpr(Identifiers.repeaterTrackByIndex);
22035
+ }
22036
+ else if (op.track instanceof ReadVarExpr && op.track.name === '$item') {
22037
+ // Top-level access of the item uses the built in `repeaterTrackByIdentity`.
22038
+ op.trackByFn = importExpr(Identifiers.repeaterTrackByIdentity);
22039
+ }
22040
+ else if (isTrackByFunctionCall(job.root.xref, op.track)) {
22041
+ // Top-level method calls in the form of `fn($index, item)` can be passed in directly.
22042
+ if (op.track.receiver.receiver.view === unit.xref) {
22043
+ // TODO: this may be wrong
22044
+ op.trackByFn = op.track.receiver;
22045
+ }
22046
+ else {
22047
+ // This is a plain method call, but not in the component's root view.
22048
+ // We need to get the component instance, and then call the method on it.
22049
+ op.trackByFn =
22050
+ importExpr(Identifiers.componentInstance).callFn([]).prop(op.track.receiver.name);
22051
+ // Because the context is not avaiable (without a special function), we don't want to
22052
+ // try to resolve it later. Let's get rid of it by overwriting the original track
22053
+ // expression (which won't be used anyway).
22054
+ op.track = op.trackByFn;
22055
+ }
22056
+ }
22057
+ else {
22058
+ // The track function could not be optimized.
22059
+ // Replace context reads with a special IR expression, since context reads in a track
22060
+ // function are emitted specially.
22061
+ op.track = transformExpressionsInExpression(op.track, expr => {
22062
+ if (expr instanceof ContextExpr) {
22063
+ op.usesComponentInstance = true;
22064
+ return new TrackContextExpr(expr.view);
22065
+ }
22066
+ return expr;
22067
+ }, VisitorContextFlag.None);
22068
+ }
22069
+ }
22070
+ }
22071
+ }
22072
+ function isTrackByFunctionCall(rootView, expr) {
22073
+ if (!(expr instanceof InvokeFunctionExpr) || expr.args.length !== 2) {
22074
+ return false;
22075
+ }
22076
+ if (!(expr.receiver instanceof ReadPropExpr &&
22077
+ expr.receiver.receiver instanceof ContextExpr) ||
22078
+ expr.receiver.receiver.view !== rootView) {
22079
+ return false;
22080
+ }
22081
+ const [arg0, arg1] = expr.args;
22082
+ if (!(arg0 instanceof ReadVarExpr) || arg0.name !== '$index') {
22083
+ return false;
22084
+ }
22085
+ if (!(arg1 instanceof ReadVarExpr) || arg1.name !== '$item') {
22086
+ return false;
22087
+ }
22088
+ return true;
22089
+ }
22090
+
22091
+ /**
22092
+ * Inside the `track` expression on a `for` repeater, the `$index` and `$item` variables are
22093
+ * ambiently available. In this phase, we find those variable usages, and replace them with the
22094
+ * appropriate output read.
22095
+ */
22096
+ function generateTrackVariables(job) {
22097
+ for (const unit of job.units) {
22098
+ for (const op of unit.create) {
22099
+ if (op.kind !== OpKind.RepeaterCreate) {
22100
+ continue;
22101
+ }
22102
+ op.track = transformExpressionsInExpression(op.track, expr => {
22103
+ if (expr instanceof LexicalReadExpr) {
22104
+ if (expr.name === op.varNames.$index) {
22105
+ return variable('$index');
22106
+ }
22107
+ else if (expr.name === op.varNames.$implicit) {
22108
+ return variable('$item');
22109
+ }
22110
+ // TODO: handle prohibited context variables (emit as globals?)
22111
+ }
22112
+ return expr;
22113
+ }, VisitorContextFlag.None);
22114
+ }
22115
+ }
22116
+ }
22117
+
21735
22118
  /**
21736
22119
  * Counts the number of variable slots used within each view, and stores that on the view itself, as
21737
22120
  * well as propagates it to the `ir.TemplateOp` for embedded views.
21738
22121
  */
21739
- function phaseVarCounting(job) {
22122
+ function countVariables(job) {
21740
22123
  // First, count the vars used in each view, and update the view-level counter.
21741
22124
  for (const unit of job.units) {
21742
22125
  let varCount = 0;
@@ -21881,7 +22264,7 @@ function isSingletonInterpolation(expr) {
21881
22264
  * To guarantee correctness, analysis of "fences" in the instruction lists is used to determine
21882
22265
  * which optimizations are safe to perform.
21883
22266
  */
21884
- function phaseVariableOptimization(job) {
22267
+ function optimizeVariables(job) {
21885
22268
  for (const unit of job.units) {
21886
22269
  inlineAlwaysInlineVariables(unit.create);
21887
22270
  inlineAlwaysInlineVariables(unit.update);
@@ -21923,7 +22306,7 @@ var Fence;
21923
22306
  * Note that all `ContextWrite` fences are implicitly `ContextRead` fences as operations which
21924
22307
  * change the view context do so based on the current one.
21925
22308
  */
21926
- Fence[Fence["ViewContextWrite"] = 3] = "ViewContextWrite";
22309
+ Fence[Fence["ViewContextWrite"] = 2] = "ViewContextWrite";
21927
22310
  /**
21928
22311
  * Indicates that a call is required for its side-effects, even if nothing reads its result.
21929
22312
  *
@@ -22103,9 +22486,9 @@ function optimizeVariablesInOpList(ops, compatibility) {
22103
22486
  function fencesForIrExpression(expr) {
22104
22487
  switch (expr.kind) {
22105
22488
  case ExpressionKind.NextContext:
22106
- return Fence.ViewContextWrite;
22489
+ return Fence.ViewContextRead | Fence.ViewContextWrite;
22107
22490
  case ExpressionKind.RestoreView:
22108
- return Fence.ViewContextWrite | Fence.SideEffectful;
22491
+ return Fence.ViewContextRead | Fence.ViewContextWrite | Fence.SideEffectful;
22109
22492
  case ExpressionKind.Reference:
22110
22493
  return Fence.ViewContextRead;
22111
22494
  default:
@@ -22277,7 +22660,7 @@ function allowConservativeInlining(decl, target) {
22277
22660
  /**
22278
22661
  * Wraps ICUs that do not already belong to an i18n block in a new i18n block.
22279
22662
  */
22280
- function phaseWrapIcus(job) {
22663
+ function wrapI18nIcus(job) {
22281
22664
  for (const unit of job.units) {
22282
22665
  let currentI18nOp = null;
22283
22666
  for (const op of unit.create) {
@@ -22300,133 +22683,6 @@ function phaseWrapIcus(job) {
22300
22683
  }
22301
22684
  }
22302
22685
 
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
22686
  /**
22431
22687
  *
22432
22688
  * @license
@@ -22436,59 +22692,64 @@ function isTrackByFunctionCall(rootView, expr) {
22436
22692
  * found in the LICENSE file at https://angular.io/license
22437
22693
  */
22438
22694
  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 },
22695
+ { kind: CompilationJobKind.Tmpl, fn: removeContentSelectors },
22696
+ { kind: CompilationJobKind.Host, fn: parseHostStyleProperties },
22697
+ { kind: CompilationJobKind.Tmpl, fn: emitNamespaceChanges },
22698
+ { kind: CompilationJobKind.Both, fn: specializeStyleBindings },
22699
+ { kind: CompilationJobKind.Both, fn: specializeBindings },
22700
+ { kind: CompilationJobKind.Tmpl, fn: propagateI18nBlocks },
22701
+ { kind: CompilationJobKind.Tmpl, fn: wrapI18nIcus },
22702
+ { kind: CompilationJobKind.Both, fn: extractAttributes },
22703
+ { kind: CompilationJobKind.Both, fn: parseExtractedStyles },
22704
+ { kind: CompilationJobKind.Tmpl, fn: removeEmptyBindings },
22705
+ { kind: CompilationJobKind.Both, fn: collapseSingletonInterpolations },
22706
+ { kind: CompilationJobKind.Both, fn: orderOps },
22707
+ { kind: CompilationJobKind.Tmpl, fn: generateConditionalExpressions },
22708
+ { kind: CompilationJobKind.Tmpl, fn: createPipes },
22709
+ { kind: CompilationJobKind.Tmpl, fn: configureDeferInstructions },
22710
+ { kind: CompilationJobKind.Tmpl, fn: extractI18nText },
22711
+ { kind: CompilationJobKind.Tmpl, fn: extractI18nICUs },
22712
+ { kind: CompilationJobKind.Tmpl, fn: applyI18nExpressions },
22713
+ { kind: CompilationJobKind.Tmpl, fn: createVariadicPipes },
22714
+ { kind: CompilationJobKind.Both, fn: generatePureLiteralStructures },
22715
+ { kind: CompilationJobKind.Tmpl, fn: generateProjectionDefs },
22716
+ { kind: CompilationJobKind.Tmpl, fn: generateVariables },
22717
+ { kind: CompilationJobKind.Tmpl, fn: saveAndRestoreView },
22718
+ { kind: CompilationJobKind.Tmpl, fn: deleteAnyCasts },
22719
+ { kind: CompilationJobKind.Both, fn: resolveDollarEvent },
22720
+ { kind: CompilationJobKind.Tmpl, fn: generateRepeaterDerivedVars },
22721
+ { kind: CompilationJobKind.Tmpl, fn: generateTrackVariables },
22722
+ { kind: CompilationJobKind.Both, fn: resolveNames },
22723
+ { kind: CompilationJobKind.Tmpl, fn: resolveDeferTargetNames },
22724
+ { kind: CompilationJobKind.Tmpl, fn: optimizeTrackFns },
22725
+ { kind: CompilationJobKind.Both, fn: resolveContexts },
22726
+ { kind: CompilationJobKind.Tmpl, fn: resolveSanitizers },
22727
+ { kind: CompilationJobKind.Tmpl, fn: liftLocalRefs },
22728
+ { kind: CompilationJobKind.Both, fn: generateNullishCoalesceExpressions },
22729
+ { kind: CompilationJobKind.Both, fn: expandSafeReads },
22730
+ { kind: CompilationJobKind.Both, fn: generateTemporaryVariables },
22731
+ { kind: CompilationJobKind.Tmpl, fn: allocateSlots },
22732
+ { kind: CompilationJobKind.Tmpl, fn: extractI18nMessages },
22733
+ { kind: CompilationJobKind.Tmpl, fn: resolveI18nElementPlaceholders },
22734
+ { kind: CompilationJobKind.Tmpl, fn: resolveI18nExpressionPlaceholders },
22735
+ { kind: CompilationJobKind.Tmpl, fn: propogateI18nPlaceholders },
22736
+ { kind: CompilationJobKind.Tmpl, fn: formatI18nParams },
22737
+ { kind: CompilationJobKind.Tmpl, fn: generateTrackFns },
22738
+ { kind: CompilationJobKind.Tmpl, fn: collectI18nConsts },
22739
+ { kind: CompilationJobKind.Tmpl, fn: collectConstExpressions },
22740
+ { kind: CompilationJobKind.Both, fn: collectElementConsts },
22741
+ { kind: CompilationJobKind.Tmpl, fn: assignI18nSlotDependencies },
22742
+ { kind: CompilationJobKind.Both, fn: countVariables },
22743
+ { kind: CompilationJobKind.Tmpl, fn: generateAdvance },
22744
+ { kind: CompilationJobKind.Both, fn: optimizeVariables },
22745
+ { kind: CompilationJobKind.Both, fn: nameFunctionsAndVariables },
22746
+ { kind: CompilationJobKind.Tmpl, fn: mergeNextContextExpressions },
22747
+ { kind: CompilationJobKind.Tmpl, fn: generateNgContainerOps },
22748
+ { kind: CompilationJobKind.Tmpl, fn: collapseEmptyInstructions },
22749
+ { kind: CompilationJobKind.Tmpl, fn: disableBindings$1 },
22750
+ { kind: CompilationJobKind.Both, fn: extractPureFunctions },
22751
+ { kind: CompilationJobKind.Both, fn: reify },
22752
+ { kind: CompilationJobKind.Both, fn: chain },
22492
22753
  ];
22493
22754
  /**
22494
22755
  * Run all transformation phases in the correct order against a compilation job. After this
@@ -22604,9 +22865,9 @@ const compatibilityMode = CompatibilityMode.TemplateDefinitionBuilder;
22604
22865
  * TODO: Refactor more of the ingestion code into phases.
22605
22866
  */
22606
22867
  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;
22868
+ const job = new ComponentCompilationJob(componentName, constantPool, compatibilityMode, relativeContextFilePath, i18nUseExternalIds);
22869
+ ingestNodes(job.root, template);
22870
+ return job;
22610
22871
  }
22611
22872
  /**
22612
22873
  * Process a host binding AST and convert it into a `HostBindingCompilationJob` in the intermediate
@@ -22654,7 +22915,7 @@ function ingestHostAttribute(job, name, value) {
22654
22915
  job.root.update.push(attrBinding);
22655
22916
  }
22656
22917
  function ingestHostEvent(job, event) {
22657
- const eventBinding = createListenerOp(job.root.xref, event.name, null, event.targetOrPhase, true, event.sourceSpan);
22918
+ const eventBinding = createListenerOp(job.root.xref, new SlotHandle(), event.name, null, event.targetOrPhase, true, event.sourceSpan);
22658
22919
  // TODO: Can this be a chain?
22659
22920
  eventBinding.handlerOps.push(createStatementOp(new ReturnStatement(convertAst(event.handler.ast, job, event.sourceSpan), event.handlerSpan)));
22660
22921
  job.root.create.push(eventBinding);
@@ -22707,10 +22968,6 @@ function ingestElement(unit, element) {
22707
22968
  !(element.i18n instanceof Message || element.i18n instanceof TagPlaceholder)) {
22708
22969
  throw Error(`Unhandled i18n metadata type for element: ${element.i18n.constructor.name}`);
22709
22970
  }
22710
- const staticAttributes = {};
22711
- for (const attr of element.attributes) {
22712
- staticAttributes[attr.name] = attr.value;
22713
- }
22714
22971
  const id = unit.job.allocateXrefId();
22715
22972
  const [namespaceKey, elementName] = splitNsName(element.name);
22716
22973
  const startOp = createElementStartOp(elementName, id, namespaceForKey(namespaceKey), element.i18n instanceof TagPlaceholder ? element.i18n : undefined, element.startSourceSpan);
@@ -22742,7 +22999,11 @@ function ingestTemplate(unit, tmpl) {
22742
22999
  [namespacePrefix, tagNameWithoutNamespace] = splitNsName(tmpl.tagName);
22743
23000
  }
22744
23001
  const i18nPlaceholder = tmpl.i18n instanceof TagPlaceholder ? tmpl.i18n : undefined;
22745
- const tplOp = createTemplateOp(childView.xref, tagNameWithoutNamespace, namespaceForKey(namespacePrefix), false, i18nPlaceholder, tmpl.startSourceSpan);
23002
+ const namespace = namespaceForKey(namespacePrefix);
23003
+ const functionNameSuffix = tagNameWithoutNamespace === null ?
23004
+ '' :
23005
+ prefixWithNamespace(tagNameWithoutNamespace, namespace);
23006
+ const tplOp = createTemplateOp(childView.xref, tagNameWithoutNamespace, functionNameSuffix, namespace, i18nPlaceholder, tmpl.startSourceSpan);
22746
23007
  unit.create.push(tplOp);
22747
23008
  ingestBindings(unit, tplOp, tmpl);
22748
23009
  ingestReferences(tplOp, tmpl);
@@ -22805,22 +23066,32 @@ function ingestBoundText(unit, text) {
22805
23066
  */
22806
23067
  function ingestIfBlock(unit, ifBlock) {
22807
23068
  let firstXref = null;
23069
+ let firstSlotHandle = null;
22808
23070
  let conditions = [];
22809
- for (const ifCase of ifBlock.branches) {
23071
+ for (let i = 0; i < ifBlock.branches.length; i++) {
23072
+ const ifCase = ifBlock.branches[i];
22810
23073
  const cView = unit.job.allocateView(unit.xref);
23074
+ let tagName = null;
23075
+ // Only the first branch can be used for projection, because the conditional
23076
+ // uses the container of the first branch as the insertion point for all branches.
23077
+ if (i === 0) {
23078
+ tagName = ingestControlFlowInsertionPoint(unit, cView.xref, ifCase);
23079
+ }
22811
23080
  if (ifCase.expressionAlias !== null) {
22812
23081
  cView.contextVariables.set(ifCase.expressionAlias.name, CTX_REF);
22813
23082
  }
23083
+ const tmplOp = createTemplateOp(cView.xref, tagName, 'Conditional', Namespace.HTML, undefined /* TODO: figure out how i18n works with new control flow */, ifCase.sourceSpan);
23084
+ unit.create.push(tmplOp);
22814
23085
  if (firstXref === null) {
22815
23086
  firstXref = cView.xref;
23087
+ firstSlotHandle = tmplOp.handle;
22816
23088
  }
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
23089
  const caseExpr = ifCase.expression ? convertAst(ifCase.expression, unit.job, null) : null;
22819
- const conditionalCaseExpr = new ConditionalCaseExpr(caseExpr, cView.xref, ifCase.expressionAlias);
23090
+ const conditionalCaseExpr = new ConditionalCaseExpr(caseExpr, tmplOp.xref, tmplOp.handle, ifCase.expressionAlias);
22820
23091
  conditions.push(conditionalCaseExpr);
22821
23092
  ingestNodes(cView, ifCase.children);
22822
23093
  }
22823
- const conditional = createConditionalOp(firstXref, null, conditions, ifBlock.sourceSpan);
23094
+ const conditional = createConditionalOp(firstXref, firstSlotHandle, null, conditions, ifBlock.sourceSpan);
22824
23095
  unit.update.push(conditional);
22825
23096
  }
22826
23097
  /**
@@ -22828,21 +23099,24 @@ function ingestIfBlock(unit, ifBlock) {
22828
23099
  */
22829
23100
  function ingestSwitchBlock(unit, switchBlock) {
22830
23101
  let firstXref = null;
23102
+ let firstSlotHandle = null;
22831
23103
  let conditions = [];
22832
23104
  for (const switchCase of switchBlock.cases) {
22833
23105
  const cView = unit.job.allocateView(unit.xref);
23106
+ const tmplOp = createTemplateOp(cView.xref, null, 'Case', Namespace.HTML, undefined /* TODO: figure out how i18n works with new control flow */, switchCase.sourceSpan);
23107
+ unit.create.push(tmplOp);
22834
23108
  if (firstXref === null) {
22835
23109
  firstXref = cView.xref;
23110
+ firstSlotHandle = tmplOp.handle;
22836
23111
  }
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
23112
  const caseExpr = switchCase.expression ?
22839
23113
  convertAst(switchCase.expression, unit.job, switchBlock.startSourceSpan) :
22840
23114
  null;
22841
- const conditionalCaseExpr = new ConditionalCaseExpr(caseExpr, cView.xref);
23115
+ const conditionalCaseExpr = new ConditionalCaseExpr(caseExpr, tmplOp.xref, tmplOp.handle);
22842
23116
  conditions.push(conditionalCaseExpr);
22843
23117
  ingestNodes(cView, switchCase.children);
22844
23118
  }
22845
- const conditional = createConditionalOp(firstXref, convertAst(switchBlock.expression, unit.job, null), conditions, switchBlock.sourceSpan);
23119
+ const conditional = createConditionalOp(firstXref, firstSlotHandle, convertAst(switchBlock.expression, unit.job, null), conditions, switchBlock.sourceSpan);
22846
23120
  unit.update.push(conditional);
22847
23121
  }
22848
23122
  function ingestDeferView(unit, suffix, children, sourceSpan) {
@@ -22851,7 +23125,7 @@ function ingestDeferView(unit, suffix, children, sourceSpan) {
22851
23125
  }
22852
23126
  const secondaryView = unit.job.allocateView(unit.xref);
22853
23127
  ingestNodes(secondaryView, children);
22854
- const templateOp = createTemplateOp(secondaryView.xref, `Defer${suffix}`, Namespace.HTML, true, undefined, sourceSpan);
23128
+ const templateOp = createTemplateOp(secondaryView.xref, null, `Defer${suffix}`, Namespace.HTML, undefined, sourceSpan);
22855
23129
  unit.create.push(templateOp);
22856
23130
  return templateOp;
22857
23131
  }
@@ -22862,32 +23136,74 @@ function ingestDeferBlock(unit, deferBlock) {
22862
23136
  const placeholder = ingestDeferView(unit, 'Placeholder', deferBlock.placeholder?.children, deferBlock.placeholder?.sourceSpan);
22863
23137
  const error = ingestDeferView(unit, 'Error', deferBlock.error?.children, deferBlock.error?.sourceSpan);
22864
23138
  // Create the main defer op, and ops for all secondary views.
22865
- const deferOp = createDeferOp(unit.job.allocateXrefId(), main.xref, deferBlock.sourceSpan);
23139
+ const deferXref = unit.job.allocateXrefId();
23140
+ const deferOp = createDeferOp(deferXref, main.xref, main.handle, deferBlock.sourceSpan);
23141
+ deferOp.placeholderView = placeholder?.xref ?? null;
23142
+ deferOp.placeholderSlot = placeholder?.handle ?? null;
23143
+ deferOp.loadingSlot = loading?.handle ?? null;
23144
+ deferOp.errorSlot = error?.handle ?? null;
23145
+ deferOp.placeholderMinimumTime = deferBlock.placeholder?.minimumTime ?? null;
23146
+ deferOp.loadingMinimumTime = deferBlock.loading?.minimumTime ?? null;
23147
+ deferOp.loadingAfterTime = deferBlock.loading?.afterTime ?? null;
22866
23148
  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);
23149
+ // Configure all defer `on` conditions.
23150
+ // TODO: refactor prefetch triggers to use a separate op type, with a shared superclass. This will
23151
+ // make it easier to refactor prefetch behavior in the future.
23152
+ let prefetch = false;
23153
+ let deferOnOps = [];
23154
+ for (const triggers of [deferBlock.triggers, deferBlock.prefetchTriggers]) {
23155
+ if (triggers.idle !== undefined) {
23156
+ const deferOnOp = createDeferOnOp(deferXref, { kind: DeferTriggerKind.Idle }, prefetch, null);
23157
+ deferOnOps.push(deferOnOp);
23158
+ }
23159
+ if (triggers.immediate !== undefined) {
23160
+ const deferOnOp = createDeferOnOp(deferXref, { kind: DeferTriggerKind.Immediate }, prefetch, null);
23161
+ deferOnOps.push(deferOnOp);
23162
+ }
23163
+ if (triggers.timer !== undefined) {
23164
+ const deferOnOp = createDeferOnOp(deferXref, { kind: DeferTriggerKind.Timer, delay: triggers.timer.delay }, prefetch, null);
23165
+ deferOnOps.push(deferOnOp);
23166
+ }
23167
+ if (triggers.hover !== undefined) {
23168
+ const deferOnOp = createDeferOnOp(deferXref, {
23169
+ kind: DeferTriggerKind.Hover,
23170
+ targetName: triggers.hover.reference,
23171
+ targetXref: null,
23172
+ targetSlot: null,
23173
+ targetView: null,
23174
+ targetSlotViewSteps: null,
23175
+ }, prefetch, null);
23176
+ deferOnOps.push(deferOnOp);
23177
+ }
23178
+ if (triggers.interaction !== undefined) {
23179
+ const deferOnOp = createDeferOnOp(deferXref, {
23180
+ kind: DeferTriggerKind.Interaction,
23181
+ targetName: triggers.interaction.reference,
23182
+ targetXref: null,
23183
+ targetSlot: null,
23184
+ targetView: null,
23185
+ targetSlotViewSteps: null,
23186
+ }, prefetch, null);
23187
+ deferOnOps.push(deferOnOp);
23188
+ }
23189
+ if (triggers.viewport !== undefined) {
23190
+ const deferOnOp = createDeferOnOp(deferXref, {
23191
+ kind: DeferTriggerKind.Viewport,
23192
+ targetName: triggers.viewport.reference,
23193
+ targetXref: null,
23194
+ targetSlot: null,
23195
+ targetView: null,
23196
+ targetSlotViewSteps: null,
23197
+ }, prefetch, null);
23198
+ deferOnOps.push(deferOnOp);
23199
+ }
23200
+ // If no (non-prefetching) defer triggers were provided, default to `idle`.
23201
+ if (deferOnOps.length === 0) {
23202
+ deferOnOps.push(createDeferOnOp(deferXref, { kind: DeferTriggerKind.Idle }, false, null));
23203
+ }
23204
+ prefetch = true;
23205
+ }
23206
+ unit.create.push(deferOnOps);
22891
23207
  }
22892
23208
  function ingestIcu(unit, icu) {
22893
23209
  if (icu.i18n instanceof Message) {
@@ -22937,10 +23253,11 @@ function ingestForBlock(unit, forBlock) {
22937
23253
  $odd: forBlock.contextVariables.$odd.name,
22938
23254
  $implicit: forBlock.item.name,
22939
23255
  };
22940
- const repeaterCreate = createRepeaterCreateOp(repeaterView.xref, emptyView?.xref ?? null, track, varNames, forBlock.sourceSpan);
23256
+ const tagName = ingestControlFlowInsertionPoint(unit, repeaterView.xref, forBlock);
23257
+ const repeaterCreate = createRepeaterCreateOp(repeaterView.xref, emptyView?.xref ?? null, tagName, track, varNames, forBlock.sourceSpan);
22941
23258
  unit.create.push(repeaterCreate);
22942
23259
  const expression = convertAst(forBlock.expression, unit.job, convertSourceSpan(forBlock.expression.span, forBlock.sourceSpan));
22943
- const repeater = createRepeaterOp(repeaterCreate.xref, expression, forBlock.sourceSpan);
23260
+ const repeater = createRepeaterOp(repeaterCreate.xref, repeaterCreate.handle, expression, forBlock.sourceSpan);
22944
23261
  unit.update.push(repeater);
22945
23262
  }
22946
23263
  /**
@@ -23014,7 +23331,7 @@ function convertAst(ast, job, baseSourceSpan) {
23014
23331
  }
23015
23332
  else if (ast instanceof BindingPipe) {
23016
23333
  // TODO: pipes should probably have source maps; figure out details.
23017
- return new PipeBindingExpr(job.allocateXrefId(), ast.name, [
23334
+ return new PipeBindingExpr(job.allocateXrefId(), new SlotHandle(), ast.name, [
23018
23335
  convertAst(ast.exp, job, baseSourceSpan),
23019
23336
  ...ast.args.map(arg => convertAst(arg, job, baseSourceSpan)),
23020
23337
  ]);
@@ -23098,8 +23415,7 @@ function ingestBindings(unit, op, element) {
23098
23415
  unit.create.push(createExtractedAttributeOp(op.xref, BindingKind.Property, output.name, null));
23099
23416
  continue;
23100
23417
  }
23101
- listenerOp =
23102
- createListenerOp(op.xref, output.name, op.tag, output.phase, false, output.sourceSpan);
23418
+ listenerOp = createListenerOp(op.xref, op.handle, output.name, op.tag, output.phase, false, output.sourceSpan);
23103
23419
  // if output.handler is a chain, then push each statement from the chain separately, and
23104
23420
  // return the last one?
23105
23421
  let handlerExprs;
@@ -23220,6 +23536,57 @@ function convertSourceSpan(span, baseSourceSpan) {
23220
23536
  const fullStart = baseSourceSpan.fullStart.moveBy(span.start);
23221
23537
  return new ParseSourceSpan(start, end, fullStart);
23222
23538
  }
23539
+ /**
23540
+ * With the directive-based control flow users were able to conditionally project content using
23541
+ * the `*` syntax. E.g. `<div *ngIf="expr" projectMe></div>` will be projected into
23542
+ * `<ng-content select="[projectMe]"/>`, because the attributes and tag name from the `div` are
23543
+ * copied to the template via the template creation instruction. With `@if` and `@for` that is
23544
+ * not the case, because the conditional is placed *around* elements, rather than *on* them.
23545
+ * The result is that content projection won't work in the same way if a user converts from
23546
+ * `*ngIf` to `@if`.
23547
+ *
23548
+ * This function aims to cover the most common case by doing the same copying when a control flow
23549
+ * node has *one and only one* root element or template node.
23550
+ *
23551
+ * This approach comes with some caveats:
23552
+ * 1. As soon as any other node is added to the root, the copying behavior won't work anymore.
23553
+ * A diagnostic will be added to flag cases like this and to explain how to work around it.
23554
+ * 2. If `preserveWhitespaces` is enabled, it's very likely that indentation will break this
23555
+ * workaround, because it'll include an additional text node as the first child. We can work
23556
+ * around it here, but in a discussion it was decided not to, because the user explicitly opted
23557
+ * into preserving the whitespace and we would have to drop it from the generated code.
23558
+ * The diagnostic mentioned point #1 will flag such cases to users.
23559
+ *
23560
+ * @returns Tag name to be used for the control flow template.
23561
+ */
23562
+ function ingestControlFlowInsertionPoint(unit, xref, node) {
23563
+ let root = null;
23564
+ for (const child of node.children) {
23565
+ // Skip over comment nodes.
23566
+ if (child instanceof Comment$1) {
23567
+ continue;
23568
+ }
23569
+ // We can only infer the tag name/attributes if there's a single root node.
23570
+ if (root !== null) {
23571
+ return null;
23572
+ }
23573
+ // Root nodes can only elements or templates with a tag name (e.g. `<div *foo></div>`).
23574
+ if (child instanceof Element$1 || (child instanceof Template && child.tagName !== null)) {
23575
+ root = child;
23576
+ }
23577
+ }
23578
+ // If we've found a single root node, its tag name and *static* attributes can be copied
23579
+ // to the surrounding template to be used for content projection. Note that it's important
23580
+ // that we don't copy any bound attributes since they don't participate in content projection
23581
+ // and they can be used in directive matching (in the case of `Template.templateAttrs`).
23582
+ if (root !== null) {
23583
+ for (const attr of root.attributes) {
23584
+ ingestBinding(unit, xref, attr.name, literal(attr.value), 1 /* e.BindingType.Attribute */, null, SecurityContext.NONE, attr.sourceSpan, BindingFlags.TextValue);
23585
+ }
23586
+ return root instanceof Element$1 ? root.name : root.tagName;
23587
+ }
23588
+ return null;
23589
+ }
23223
23590
 
23224
23591
  const USE_TEMPLATE_PIPELINE = false;
23225
23592
 
@@ -24356,7 +24723,7 @@ function normalizeNgContentSelect(selectAttr) {
24356
24723
  }
24357
24724
 
24358
24725
  /** Pattern for the expression in a for loop block. */
24359
- const FOR_LOOP_EXPRESSION_PATTERN = /^\s*([0-9A-Za-z_$]*)\s+of\s+(.*)/;
24726
+ const FOR_LOOP_EXPRESSION_PATTERN = /^\s*([0-9A-Za-z_$]*)\s+of\s+([\S\s]*)/;
24360
24727
  /** Pattern for the tracking expression in a for loop block. */
24361
24728
  const FOR_LOOP_TRACK_PATTERN = /^track\s+([\S\s]*)/;
24362
24729
  /** Pattern for the `as` expression in a conditional block. */
@@ -24387,19 +24754,19 @@ function createIfBlock(ast, connectedBlocks, visitor, bindingParser) {
24387
24754
  const branches = [];
24388
24755
  const mainBlockParams = parseConditionalBlockParameters(ast, errors, bindingParser);
24389
24756
  if (mainBlockParams !== null) {
24390
- branches.push(new IfBlockBranch(mainBlockParams.expression, visitAll(visitor, ast.children, ast.children), mainBlockParams.expressionAlias, ast.sourceSpan, ast.startSourceSpan, ast.endSourceSpan));
24757
+ branches.push(new IfBlockBranch(mainBlockParams.expression, visitAll(visitor, ast.children, ast.children), mainBlockParams.expressionAlias, ast.sourceSpan, ast.startSourceSpan, ast.endSourceSpan, ast.nameSpan));
24391
24758
  }
24392
24759
  for (const block of connectedBlocks) {
24393
24760
  if (ELSE_IF_PATTERN.test(block.name)) {
24394
24761
  const params = parseConditionalBlockParameters(block, errors, bindingParser);
24395
24762
  if (params !== null) {
24396
24763
  const children = visitAll(visitor, block.children, block.children);
24397
- branches.push(new IfBlockBranch(params.expression, children, params.expressionAlias, block.sourceSpan, block.startSourceSpan, block.endSourceSpan));
24764
+ branches.push(new IfBlockBranch(params.expression, children, params.expressionAlias, block.sourceSpan, block.startSourceSpan, block.endSourceSpan, block.nameSpan));
24398
24765
  }
24399
24766
  }
24400
24767
  else if (block.name === 'else') {
24401
24768
  const children = visitAll(visitor, block.children, block.children);
24402
- branches.push(new IfBlockBranch(null, children, null, block.sourceSpan, block.startSourceSpan, block.endSourceSpan));
24769
+ branches.push(new IfBlockBranch(null, children, null, block.sourceSpan, block.startSourceSpan, block.endSourceSpan, block.nameSpan));
24403
24770
  }
24404
24771
  }
24405
24772
  // The outer IfBlock should have a span that encapsulates all branches.
@@ -24411,7 +24778,7 @@ function createIfBlock(ast, connectedBlocks, visitor, bindingParser) {
24411
24778
  wholeSourceSpan = new ParseSourceSpan(ifBlockStartSourceSpan.start, lastBranch.sourceSpan.end);
24412
24779
  }
24413
24780
  return {
24414
- node: new IfBlock(branches, wholeSourceSpan, ast.startSourceSpan, ifBlockEndSourceSpan),
24781
+ node: new IfBlock(branches, wholeSourceSpan, ast.startSourceSpan, ifBlockEndSourceSpan, ast.nameSpan),
24415
24782
  errors,
24416
24783
  };
24417
24784
  }
@@ -24430,7 +24797,7 @@ function createForLoop(ast, connectedBlocks, visitor, bindingParser) {
24430
24797
  errors.push(new ParseError(block.sourceSpan, '@empty block cannot have parameters'));
24431
24798
  }
24432
24799
  else {
24433
- empty = new ForLoopBlockEmpty(visitAll(visitor, block.children, block.children), block.sourceSpan, block.startSourceSpan, block.endSourceSpan);
24800
+ empty = new ForLoopBlockEmpty(visitAll(visitor, block.children, block.children), block.sourceSpan, block.startSourceSpan, block.endSourceSpan, block.nameSpan);
24434
24801
  }
24435
24802
  }
24436
24803
  else {
@@ -24448,7 +24815,7 @@ function createForLoop(ast, connectedBlocks, visitor, bindingParser) {
24448
24815
  // main `for` body, use `mainSourceSpan`.
24449
24816
  const endSpan = empty?.endSourceSpan ?? ast.endSourceSpan;
24450
24817
  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);
24818
+ 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
24819
  }
24453
24820
  }
24454
24821
  return { node, errors };
@@ -24474,7 +24841,7 @@ function createSwitchBlock(ast, visitor, bindingParser) {
24474
24841
  const expression = node.name === 'case' ?
24475
24842
  parseBlockParameterToBinding(node.parameters[0], bindingParser) :
24476
24843
  null;
24477
- const ast = new SwitchBlockCase(expression, visitAll(visitor, node.children, node.children), node.sourceSpan, node.startSourceSpan, node.endSourceSpan);
24844
+ const ast = new SwitchBlockCase(expression, visitAll(visitor, node.children, node.children), node.sourceSpan, node.startSourceSpan, node.endSourceSpan, node.nameSpan);
24478
24845
  if (expression === null) {
24479
24846
  defaultCase = ast;
24480
24847
  }
@@ -24487,7 +24854,7 @@ function createSwitchBlock(ast, visitor, bindingParser) {
24487
24854
  cases.push(defaultCase);
24488
24855
  }
24489
24856
  return {
24490
- node: new SwitchBlock(primaryExpression, cases, unknownBlocks, ast.sourceSpan, ast.startSourceSpan, ast.endSourceSpan),
24857
+ node: new SwitchBlock(primaryExpression, cases, unknownBlocks, ast.sourceSpan, ast.startSourceSpan, ast.endSourceSpan, ast.nameSpan),
24491
24858
  errors
24492
24859
  };
24493
24860
  }
@@ -24522,7 +24889,9 @@ function parseForLoopParameters(block, errors, bindingParser) {
24522
24889
  errors.push(new ParseError(param.sourceSpan, '@for loop can only have one "track" expression'));
24523
24890
  }
24524
24891
  else {
24525
- result.trackBy = parseBlockParameterToBinding(param, bindingParser, trackMatch[1]);
24892
+ const expression = parseBlockParameterToBinding(param, bindingParser, trackMatch[1]);
24893
+ const keywordSpan = new ParseSourceSpan(param.sourceSpan.start, param.sourceSpan.start.moveBy('track'.length));
24894
+ result.trackBy = { expression, keywordSpan };
24526
24895
  }
24527
24896
  continue;
24528
24897
  }
@@ -24596,8 +24965,10 @@ function validateSwitchBlock(ast) {
24596
24965
  return errors;
24597
24966
  }
24598
24967
  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) {
24968
+ // Skip over comments and empty text nodes inside the switch block.
24969
+ // Empty text nodes can be used for formatting while comments don't affect the runtime.
24970
+ if (node instanceof Comment ||
24971
+ (node instanceof Text && node.value.trim().length === 0)) {
24601
24972
  continue;
24602
24973
  }
24603
24974
  if (!(node instanceof Block) || (node.name !== 'case' && node.name !== 'default')) {
@@ -24720,7 +25091,7 @@ function stripOptionalParentheses(param, errors) {
24720
25091
  }
24721
25092
 
24722
25093
  /** Pattern for a timing value in a trigger. */
24723
- const TIME_PATTERN = /^\d+(ms|s)?$/;
25094
+ const TIME_PATTERN = /^\d+\.?\d*(ms|s)?$/;
24724
25095
  /** Pattern for a separator between keywords in a trigger expression. */
24725
25096
  const SEPARATOR_PATTERN = /^\s$/;
24726
25097
  /** Pairs of characters that form syntax that is comma-delimited. */
@@ -24742,6 +25113,8 @@ var OnTriggerType;
24742
25113
  /** Parses a `when` deferred trigger. */
24743
25114
  function parseWhenTrigger({ expression, sourceSpan }, bindingParser, triggers, errors) {
24744
25115
  const whenIndex = expression.indexOf('when');
25116
+ const whenSourceSpan = new ParseSourceSpan(sourceSpan.start.moveBy(whenIndex), sourceSpan.start.moveBy(whenIndex + 'when'.length));
25117
+ const prefetchSpan = getPrefetchSpan(expression, sourceSpan);
24745
25118
  // This is here just to be safe, we shouldn't enter this function
24746
25119
  // in the first place if a block doesn't have the "when" keyword.
24747
25120
  if (whenIndex === -1) {
@@ -24750,12 +25123,14 @@ function parseWhenTrigger({ expression, sourceSpan }, bindingParser, triggers, e
24750
25123
  else {
24751
25124
  const start = getTriggerParametersStart(expression, whenIndex + 1);
24752
25125
  const parsed = bindingParser.parseBinding(expression.slice(start), false, sourceSpan, sourceSpan.start.offset + start);
24753
- trackTrigger('when', triggers, errors, new BoundDeferredTrigger(parsed, sourceSpan));
25126
+ trackTrigger('when', triggers, errors, new BoundDeferredTrigger(parsed, sourceSpan, prefetchSpan, whenSourceSpan));
24754
25127
  }
24755
25128
  }
24756
25129
  /** Parses an `on` trigger */
24757
25130
  function parseOnTrigger({ expression, sourceSpan }, triggers, errors, placeholder) {
24758
25131
  const onIndex = expression.indexOf('on');
25132
+ const onSourceSpan = new ParseSourceSpan(sourceSpan.start.moveBy(onIndex), sourceSpan.start.moveBy(onIndex + 'on'.length));
25133
+ const prefetchSpan = getPrefetchSpan(expression, sourceSpan);
24759
25134
  // This is here just to be safe, we shouldn't enter this function
24760
25135
  // in the first place if a block doesn't have the "on" keyword.
24761
25136
  if (onIndex === -1) {
@@ -24763,18 +25138,26 @@ function parseOnTrigger({ expression, sourceSpan }, triggers, errors, placeholde
24763
25138
  }
24764
25139
  else {
24765
25140
  const start = getTriggerParametersStart(expression, onIndex + 1);
24766
- const parser = new OnTriggerParser(expression, start, sourceSpan, triggers, errors, placeholder);
25141
+ const parser = new OnTriggerParser(expression, start, sourceSpan, triggers, errors, placeholder, prefetchSpan, onSourceSpan);
24767
25142
  parser.parse();
24768
25143
  }
24769
25144
  }
25145
+ function getPrefetchSpan(expression, sourceSpan) {
25146
+ if (!expression.startsWith('prefetch')) {
25147
+ return null;
25148
+ }
25149
+ return new ParseSourceSpan(sourceSpan.start, sourceSpan.start.moveBy('prefetch'.length));
25150
+ }
24770
25151
  class OnTriggerParser {
24771
- constructor(expression, start, span, triggers, errors, placeholder) {
25152
+ constructor(expression, start, span, triggers, errors, placeholder, prefetchSpan, onSourceSpan) {
24772
25153
  this.expression = expression;
24773
25154
  this.start = start;
24774
25155
  this.span = span;
24775
25156
  this.triggers = triggers;
24776
25157
  this.errors = errors;
24777
25158
  this.placeholder = placeholder;
25159
+ this.prefetchSpan = prefetchSpan;
25160
+ this.onSourceSpan = onSourceSpan;
24778
25161
  this.index = 0;
24779
25162
  this.tokens = new Lexer().tokenize(expression.slice(start));
24780
25163
  }
@@ -24820,28 +25203,35 @@ class OnTriggerParser {
24820
25203
  return this.tokens[Math.min(this.index, this.tokens.length - 1)];
24821
25204
  }
24822
25205
  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);
25206
+ const triggerNameStartSpan = this.span.start.moveBy(this.start + identifier.index - this.tokens[0].index);
25207
+ const nameSpan = new ParseSourceSpan(triggerNameStartSpan, triggerNameStartSpan.moveBy(identifier.strValue.length));
25208
+ const endSpan = triggerNameStartSpan.moveBy(this.token().end - identifier.index);
25209
+ // Put the prefetch and on spans with the first trigger
25210
+ // This should maybe be refactored to have something like an outer OnGroup AST
25211
+ // Since triggers can be grouped with commas "on hover(x), interaction(y)"
25212
+ const isFirstTrigger = identifier.index === 0;
25213
+ const onSourceSpan = isFirstTrigger ? this.onSourceSpan : null;
25214
+ const prefetchSourceSpan = isFirstTrigger ? this.prefetchSpan : null;
25215
+ const sourceSpan = new ParseSourceSpan(isFirstTrigger ? this.span.start : triggerNameStartSpan, endSpan);
24826
25216
  try {
24827
25217
  switch (identifier.toString()) {
24828
25218
  case OnTriggerType.IDLE:
24829
- this.trackTrigger('idle', createIdleTrigger(parameters, sourceSpan));
25219
+ this.trackTrigger('idle', createIdleTrigger(parameters, nameSpan, sourceSpan, prefetchSourceSpan, onSourceSpan));
24830
25220
  break;
24831
25221
  case OnTriggerType.TIMER:
24832
- this.trackTrigger('timer', createTimerTrigger(parameters, sourceSpan));
25222
+ this.trackTrigger('timer', createTimerTrigger(parameters, nameSpan, sourceSpan, this.prefetchSpan, this.onSourceSpan));
24833
25223
  break;
24834
25224
  case OnTriggerType.INTERACTION:
24835
- this.trackTrigger('interaction', createInteractionTrigger(parameters, sourceSpan, this.placeholder));
25225
+ this.trackTrigger('interaction', createInteractionTrigger(parameters, nameSpan, sourceSpan, this.prefetchSpan, this.onSourceSpan, this.placeholder));
24836
25226
  break;
24837
25227
  case OnTriggerType.IMMEDIATE:
24838
- this.trackTrigger('immediate', createImmediateTrigger(parameters, sourceSpan));
25228
+ this.trackTrigger('immediate', createImmediateTrigger(parameters, nameSpan, sourceSpan, this.prefetchSpan, this.onSourceSpan));
24839
25229
  break;
24840
25230
  case OnTriggerType.HOVER:
24841
- this.trackTrigger('hover', createHoverTrigger(parameters, sourceSpan, this.placeholder));
25231
+ this.trackTrigger('hover', createHoverTrigger(parameters, nameSpan, sourceSpan, this.prefetchSpan, this.onSourceSpan, this.placeholder));
24842
25232
  break;
24843
25233
  case OnTriggerType.VIEWPORT:
24844
- this.trackTrigger('viewport', createViewportTrigger(parameters, sourceSpan, this.placeholder));
25234
+ this.trackTrigger('viewport', createViewportTrigger(parameters, nameSpan, sourceSpan, this.prefetchSpan, this.onSourceSpan, this.placeholder));
24845
25235
  break;
24846
25236
  default:
24847
25237
  throw new Error(`Unrecognized trigger type "${identifier}"`);
@@ -24931,13 +25321,13 @@ function trackTrigger(name, allTriggers, errors, trigger) {
24931
25321
  allTriggers[name] = trigger;
24932
25322
  }
24933
25323
  }
24934
- function createIdleTrigger(parameters, sourceSpan) {
25324
+ function createIdleTrigger(parameters, nameSpan, sourceSpan, prefetchSpan, onSourceSpan) {
24935
25325
  if (parameters.length > 0) {
24936
25326
  throw new Error(`"${OnTriggerType.IDLE}" trigger cannot have parameters`);
24937
25327
  }
24938
- return new IdleDeferredTrigger(sourceSpan);
25328
+ return new IdleDeferredTrigger(nameSpan, sourceSpan, prefetchSpan, onSourceSpan);
24939
25329
  }
24940
- function createTimerTrigger(parameters, sourceSpan) {
25330
+ function createTimerTrigger(parameters, nameSpan, sourceSpan, prefetchSpan, onSourceSpan) {
24941
25331
  if (parameters.length !== 1) {
24942
25332
  throw new Error(`"${OnTriggerType.TIMER}" trigger must have exactly one parameter`);
24943
25333
  }
@@ -24945,25 +25335,25 @@ function createTimerTrigger(parameters, sourceSpan) {
24945
25335
  if (delay === null) {
24946
25336
  throw new Error(`Could not parse time value of trigger "${OnTriggerType.TIMER}"`);
24947
25337
  }
24948
- return new TimerDeferredTrigger(delay, sourceSpan);
25338
+ return new TimerDeferredTrigger(delay, nameSpan, sourceSpan, prefetchSpan, onSourceSpan);
24949
25339
  }
24950
- function createImmediateTrigger(parameters, sourceSpan) {
25340
+ function createImmediateTrigger(parameters, nameSpan, sourceSpan, prefetchSpan, onSourceSpan) {
24951
25341
  if (parameters.length > 0) {
24952
25342
  throw new Error(`"${OnTriggerType.IMMEDIATE}" trigger cannot have parameters`);
24953
25343
  }
24954
- return new ImmediateDeferredTrigger(sourceSpan);
25344
+ return new ImmediateDeferredTrigger(nameSpan, sourceSpan, prefetchSpan, onSourceSpan);
24955
25345
  }
24956
- function createHoverTrigger(parameters, sourceSpan, placeholder) {
25346
+ function createHoverTrigger(parameters, nameSpan, sourceSpan, prefetchSpan, onSourceSpan, placeholder) {
24957
25347
  validateReferenceBasedTrigger(OnTriggerType.HOVER, parameters, placeholder);
24958
- return new HoverDeferredTrigger(parameters[0] ?? null, sourceSpan);
25348
+ return new HoverDeferredTrigger(parameters[0] ?? null, nameSpan, sourceSpan, prefetchSpan, onSourceSpan);
24959
25349
  }
24960
- function createInteractionTrigger(parameters, sourceSpan, placeholder) {
25350
+ function createInteractionTrigger(parameters, nameSpan, sourceSpan, prefetchSpan, onSourceSpan, placeholder) {
24961
25351
  validateReferenceBasedTrigger(OnTriggerType.INTERACTION, parameters, placeholder);
24962
- return new InteractionDeferredTrigger(parameters[0] ?? null, sourceSpan);
25352
+ return new InteractionDeferredTrigger(parameters[0] ?? null, nameSpan, sourceSpan, prefetchSpan, onSourceSpan);
24963
25353
  }
24964
- function createViewportTrigger(parameters, sourceSpan, placeholder) {
25354
+ function createViewportTrigger(parameters, nameSpan, sourceSpan, prefetchSpan, onSourceSpan, placeholder) {
24965
25355
  validateReferenceBasedTrigger(OnTriggerType.VIEWPORT, parameters, placeholder);
24966
- return new ViewportDeferredTrigger(parameters[0] ?? null, sourceSpan);
25356
+ return new ViewportDeferredTrigger(parameters[0] ?? null, nameSpan, sourceSpan, prefetchSpan, onSourceSpan);
24967
25357
  }
24968
25358
  function validateReferenceBasedTrigger(type, parameters, placeholder) {
24969
25359
  if (parameters.length > 1) {
@@ -25002,7 +25392,7 @@ function parseDeferredTime(value) {
25002
25392
  return null;
25003
25393
  }
25004
25394
  const [time, units] = match;
25005
- return parseInt(time) * (units === 's' ? 1000 : 1);
25395
+ return parseFloat(time) * (units === 's' ? 1000 : 1);
25006
25396
  }
25007
25397
 
25008
25398
  /** Pattern to identify a `prefetch when` trigger. */
@@ -25029,8 +25419,7 @@ function createDeferredBlock(ast, connectedBlocks, visitor, bindingParser) {
25029
25419
  const errors = [];
25030
25420
  const { placeholder, loading, error } = parseConnectedBlocks(connectedBlocks, errors, visitor);
25031
25421
  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`.
25422
+ // The `defer` block has a main span encompassing all of the connected branches as well.
25034
25423
  let lastEndSourceSpan = ast.endSourceSpan;
25035
25424
  let endOfLastSourceSpan = ast.sourceSpan.end;
25036
25425
  if (connectedBlocks.length > 0) {
@@ -25038,8 +25427,8 @@ function createDeferredBlock(ast, connectedBlocks, visitor, bindingParser) {
25038
25427
  lastEndSourceSpan = lastConnectedBlock.endSourceSpan;
25039
25428
  endOfLastSourceSpan = lastConnectedBlock.sourceSpan.end;
25040
25429
  }
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);
25430
+ const sourceSpanWithConnectedBlocks = new ParseSourceSpan(ast.sourceSpan.start, endOfLastSourceSpan);
25431
+ const node = new DeferredBlock(visitAll(visitor, ast.children, ast.children), triggers, prefetchTriggers, placeholder, loading, error, ast.nameSpan, sourceSpanWithConnectedBlocks, ast.sourceSpan, ast.startSourceSpan, lastEndSourceSpan);
25043
25432
  return { node, errors };
25044
25433
  }
25045
25434
  function parseConnectedBlocks(connectedBlocks, errors, visitor) {
@@ -25102,7 +25491,7 @@ function parsePlaceholderBlock(ast, visitor) {
25102
25491
  throw new Error(`Unrecognized parameter in @placeholder block: "${param.expression}"`);
25103
25492
  }
25104
25493
  }
25105
- return new DeferredBlockPlaceholder(visitAll(visitor, ast.children, ast.children), minimumTime, ast.sourceSpan, ast.startSourceSpan, ast.endSourceSpan);
25494
+ return new DeferredBlockPlaceholder(visitAll(visitor, ast.children, ast.children), minimumTime, ast.nameSpan, ast.sourceSpan, ast.startSourceSpan, ast.endSourceSpan);
25106
25495
  }
25107
25496
  function parseLoadingBlock(ast, visitor) {
25108
25497
  let afterTime = null;
@@ -25132,13 +25521,13 @@ function parseLoadingBlock(ast, visitor) {
25132
25521
  throw new Error(`Unrecognized parameter in @loading block: "${param.expression}"`);
25133
25522
  }
25134
25523
  }
25135
- return new DeferredBlockLoading(visitAll(visitor, ast.children, ast.children), afterTime, minimumTime, ast.sourceSpan, ast.startSourceSpan, ast.endSourceSpan);
25524
+ return new DeferredBlockLoading(visitAll(visitor, ast.children, ast.children), afterTime, minimumTime, ast.nameSpan, ast.sourceSpan, ast.startSourceSpan, ast.endSourceSpan);
25136
25525
  }
25137
25526
  function parseErrorBlock(ast, visitor) {
25138
25527
  if (ast.parameters.length > 0) {
25139
25528
  throw new Error(`@error block cannot have parameters`);
25140
25529
  }
25141
- return new DeferredBlockError(visitAll(visitor, ast.children, ast.children), ast.sourceSpan, ast.startSourceSpan, ast.endSourceSpan);
25530
+ return new DeferredBlockError(visitAll(visitor, ast.children, ast.children), ast.nameSpan, ast.sourceSpan, ast.startSourceSpan, ast.endSourceSpan);
25142
25531
  }
25143
25532
  function parsePrimaryTriggers(params, bindingParser, errors, placeholder) {
25144
25533
  const triggers = {};
@@ -25887,6 +26276,8 @@ const NG_CONTENT_SELECT_ATTR = 'select';
25887
26276
  const NG_PROJECT_AS_ATTR_NAME = 'ngProjectAs';
25888
26277
  // Global symbols available only inside event bindings.
25889
26278
  const EVENT_BINDING_SCOPE_GLOBALS = new Set(['$event']);
26279
+ // Tag name of the `ng-template` element.
26280
+ const NG_TEMPLATE_TAG_NAME = 'ng-template';
25890
26281
  // List of supported global targets for event listeners
25891
26282
  const GLOBAL_TARGET_RESOLVERS = new Map([['window', Identifiers.resolveWindow], ['document', Identifiers.resolveDocument], ['body', Identifiers.resolveBody]]);
25892
26283
  const LEADING_TRIVIA_CHARS = [' ', '\n', '\r', '\t'];
@@ -26658,7 +27049,6 @@ class TemplateDefinitionBuilder {
26658
27049
  // it based on the parent nodes inside the template instruction.
26659
27050
  const tagNameWithoutNamespace = template.tagName ? splitNsName(template.tagName)[1] : template.tagName;
26660
27051
  const contextNameSuffix = template.tagName ? '_' + sanitizeIdentifier(template.tagName) : '';
26661
- const NG_TEMPLATE_TAG_NAME = 'ng-template';
26662
27052
  // prepare attributes parameter (including attributes used for directive matching)
26663
27053
  const attrsExprs = this.getAttributeExpressions(NG_TEMPLATE_TAG_NAME, template.attributes, template.inputs, template.outputs, undefined /* styles */, template.templateAttrs);
26664
27054
  const templateIndex = this.createEmbeddedTemplateFn(tagNameWithoutNamespace, template.children, contextNameSuffix, template.sourceSpan, template.variables, attrsExprs, template.references, template.i18n);
@@ -26761,18 +27151,28 @@ class TemplateDefinitionBuilder {
26761
27151
  // We have to process the block in two steps: once here and again in the update instruction
26762
27152
  // callback in order to generate the correct expressions when pipes or pure functions are
26763
27153
  // used inside the branch expressions.
26764
- const branchData = block.branches.map(({ expression, expressionAlias, children, sourceSpan }) => {
27154
+ const branchData = block.branches.map((branch, branchIndex) => {
27155
+ const { expression, expressionAlias, children, sourceSpan } = branch;
26765
27156
  // If the branch has an alias, it'll be assigned directly to the container's context.
26766
27157
  // We define a variable referring directly to the context so that any nested usages can be
26767
27158
  // rewritten to refer to it.
26768
27159
  const variables = expressionAlias !== null ?
26769
27160
  [new Variable(expressionAlias.name, DIRECT_CONTEXT_REFERENCE, expressionAlias.sourceSpan, expressionAlias.keySpan)] :
26770
27161
  undefined;
27162
+ let tagName = null;
27163
+ let attrsExprs;
27164
+ // Only the first branch can be used for projection, because the conditional
27165
+ // uses the container of the first branch as the insertion point for all branches.
27166
+ if (branchIndex === 0) {
27167
+ const inferredData = this.inferProjectionDataFromInsertionPoint(branch);
27168
+ tagName = inferredData.tagName;
27169
+ attrsExprs = inferredData.attrsExprs;
27170
+ }
26771
27171
  // Note: the template needs to be created *before* we process the expression,
26772
27172
  // otherwise pipes injecting some symbols won't work (see #52102).
26773
- const index = this.createEmbeddedTemplateFn(null, children, '_Conditional', sourceSpan, variables);
27173
+ const templateIndex = this.createEmbeddedTemplateFn(tagName, children, '_Conditional', sourceSpan, variables, attrsExprs);
26774
27174
  const processedExpression = expression === null ? null : expression.visit(this._valueConverter);
26775
- return { index, expression: processedExpression, alias: expressionAlias };
27175
+ return { index: templateIndex, expression: processedExpression, alias: expressionAlias };
26776
27176
  });
26777
27177
  // Use the index of the first block as the index for the entire container.
26778
27178
  const containerIndex = branchData[0].index;
@@ -26994,6 +27394,41 @@ class TemplateDefinitionBuilder {
26994
27394
  return params;
26995
27395
  });
26996
27396
  }
27397
+ /**
27398
+ * Infers the data used for content projection (tag name and attributes) from the content of a
27399
+ * node.
27400
+ * @param node Node for which to infer the projection data.
27401
+ */
27402
+ inferProjectionDataFromInsertionPoint(node) {
27403
+ let root = null;
27404
+ let tagName = null;
27405
+ let attrsExprs;
27406
+ for (const child of node.children) {
27407
+ // Skip over comment nodes.
27408
+ if (child instanceof Comment$1) {
27409
+ continue;
27410
+ }
27411
+ // We can only infer the tag name/attributes if there's a single root node.
27412
+ if (root !== null) {
27413
+ root = null;
27414
+ break;
27415
+ }
27416
+ // Root nodes can only elements or templates with a tag name (e.g. `<div *foo></div>`).
27417
+ if (child instanceof Element$1 || (child instanceof Template && child.tagName !== null)) {
27418
+ root = child;
27419
+ }
27420
+ }
27421
+ // If we've found a single root node, its tag name and *static* attributes can be copied
27422
+ // to the surrounding template to be used for content projection. Note that it's important
27423
+ // that we don't copy any bound attributes since they don't participate in content projection
27424
+ // and they can be used in directive matching (in the case of `Template.templateAttrs`).
27425
+ if (root !== null) {
27426
+ tagName = root instanceof Element$1 ? root.name : root.tagName;
27427
+ attrsExprs =
27428
+ this.getAttributeExpressions(NG_TEMPLATE_TAG_NAME, root.attributes, root.inputs, []);
27429
+ }
27430
+ return { tagName, attrsExprs };
27431
+ }
26997
27432
  allocateDataSlot() {
26998
27433
  return this._dataIndex++;
26999
27434
  }
@@ -27001,6 +27436,7 @@ class TemplateDefinitionBuilder {
27001
27436
  // Allocate one slot for the repeater metadata. The slots for the primary and empty block
27002
27437
  // are implicitly inferred by the runtime to index + 1 and index + 2.
27003
27438
  const blockIndex = this.allocateDataSlot();
27439
+ const { tagName, attrsExprs } = this.inferProjectionDataFromInsertionPoint(block);
27004
27440
  const primaryData = this.prepareEmbeddedTemplateFn(block.children, '_For', [block.item, block.contextVariables.$index, block.contextVariables.$count]);
27005
27441
  const { expression: trackByExpression, usesComponentInstance: trackByUsesComponentInstance } = this.createTrackByFunction(block);
27006
27442
  let emptyData = null;
@@ -27017,6 +27453,8 @@ class TemplateDefinitionBuilder {
27017
27453
  variable(primaryData.name),
27018
27454
  literal(primaryData.getConstCount()),
27019
27455
  literal(primaryData.getVarCount()),
27456
+ literal(tagName),
27457
+ this.addAttrsToConsts(attrsExprs || null),
27020
27458
  trackByExpression,
27021
27459
  ];
27022
27460
  if (emptyData !== null) {
@@ -29549,13 +29987,25 @@ class R3BoundTarget {
29549
29987
  }
29550
29988
  const name = trigger.reference;
29551
29989
  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;
29990
+ let trigger = null;
29991
+ if (block.placeholder !== null) {
29992
+ for (const child of block.placeholder.children) {
29993
+ // Skip over comment nodes. Currently by default the template parser doesn't capture
29994
+ // comments, but we have a safeguard here just in case since it can be enabled.
29995
+ if (child instanceof Comment$1) {
29996
+ continue;
29997
+ }
29998
+ // We can only infer the trigger if there's one root element node. Any other
29999
+ // nodes at the root make it so that we can't infer the trigger anymore.
30000
+ if (trigger !== null) {
30001
+ return null;
30002
+ }
30003
+ if (child instanceof Element$1) {
30004
+ trigger = child;
30005
+ }
30006
+ }
30007
+ }
30008
+ return trigger;
29559
30009
  }
29560
30010
  const outsideRef = this.findEntityInScope(block, name);
29561
30011
  // First try to resolve the target in the scope of the main deferred block. Note that we
@@ -29567,7 +30017,7 @@ class R3BoundTarget {
29567
30017
  }
29568
30018
  }
29569
30019
  // 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.
30020
+ // placeholder block which is shown before the main block has loaded.
29571
30021
  if (block.placeholder !== null) {
29572
30022
  const refInPlaceholder = this.findEntityInScope(block.placeholder, name);
29573
30023
  const targetInPlaceholder = refInPlaceholder instanceof Reference ? this.getReferenceTarget(refInPlaceholder) : null;
@@ -30262,7 +30712,7 @@ function publishFacade(global) {
30262
30712
  * @description
30263
30713
  * Entry point for all public APIs of the compiler package.
30264
30714
  */
30265
- const VERSION = new Version('17.0.0-rc.1');
30715
+ const VERSION = new Version('17.0.0-rc.2');
30266
30716
 
30267
30717
  class CompilerConfig {
30268
30718
  constructor({ defaultEncapsulation = ViewEncapsulation.Emulated, preserveWhitespaces, strictInjectionParameters } = {}) {
@@ -31792,7 +32242,7 @@ const MINIMUM_PARTIAL_LINKER_VERSION$6 = '12.0.0';
31792
32242
  function compileDeclareClassMetadata(metadata) {
31793
32243
  const definitionMap = new DefinitionMap();
31794
32244
  definitionMap.set('minVersion', literal(MINIMUM_PARTIAL_LINKER_VERSION$6));
31795
- definitionMap.set('version', literal('17.0.0-rc.1'));
32245
+ definitionMap.set('version', literal('17.0.0-rc.2'));
31796
32246
  definitionMap.set('ngImport', importExpr(Identifiers.core));
31797
32247
  definitionMap.set('type', metadata.type);
31798
32248
  definitionMap.set('decorators', metadata.decorators);
@@ -31900,7 +32350,7 @@ function createDirectiveDefinitionMap(meta) {
31900
32350
  // in 16.1 is actually used.
31901
32351
  const minVersion = hasTransformFunctions ? MINIMUM_PARTIAL_LINKER_VERSION$5 : '14.0.0';
31902
32352
  definitionMap.set('minVersion', literal(minVersion));
31903
- definitionMap.set('version', literal('17.0.0-rc.1'));
32353
+ definitionMap.set('version', literal('17.0.0-rc.2'));
31904
32354
  // e.g. `type: MyDirective`
31905
32355
  definitionMap.set('type', meta.type.value);
31906
32356
  if (meta.isStandalone) {
@@ -32177,7 +32627,7 @@ const MINIMUM_PARTIAL_LINKER_VERSION$4 = '12.0.0';
32177
32627
  function compileDeclareFactoryFunction(meta) {
32178
32628
  const definitionMap = new DefinitionMap();
32179
32629
  definitionMap.set('minVersion', literal(MINIMUM_PARTIAL_LINKER_VERSION$4));
32180
- definitionMap.set('version', literal('17.0.0-rc.1'));
32630
+ definitionMap.set('version', literal('17.0.0-rc.2'));
32181
32631
  definitionMap.set('ngImport', importExpr(Identifiers.core));
32182
32632
  definitionMap.set('type', meta.type.value);
32183
32633
  definitionMap.set('deps', compileDependencies(meta.deps));
@@ -32212,7 +32662,7 @@ function compileDeclareInjectableFromMetadata(meta) {
32212
32662
  function createInjectableDefinitionMap(meta) {
32213
32663
  const definitionMap = new DefinitionMap();
32214
32664
  definitionMap.set('minVersion', literal(MINIMUM_PARTIAL_LINKER_VERSION$3));
32215
- definitionMap.set('version', literal('17.0.0-rc.1'));
32665
+ definitionMap.set('version', literal('17.0.0-rc.2'));
32216
32666
  definitionMap.set('ngImport', importExpr(Identifiers.core));
32217
32667
  definitionMap.set('type', meta.type.value);
32218
32668
  // Only generate providedIn property if it has a non-null value
@@ -32263,7 +32713,7 @@ function compileDeclareInjectorFromMetadata(meta) {
32263
32713
  function createInjectorDefinitionMap(meta) {
32264
32714
  const definitionMap = new DefinitionMap();
32265
32715
  definitionMap.set('minVersion', literal(MINIMUM_PARTIAL_LINKER_VERSION$2));
32266
- definitionMap.set('version', literal('17.0.0-rc.1'));
32716
+ definitionMap.set('version', literal('17.0.0-rc.2'));
32267
32717
  definitionMap.set('ngImport', importExpr(Identifiers.core));
32268
32718
  definitionMap.set('type', meta.type.value);
32269
32719
  definitionMap.set('providers', meta.providers);
@@ -32296,7 +32746,7 @@ function createNgModuleDefinitionMap(meta) {
32296
32746
  throw new Error('Invalid path! Local compilation mode should not get into the partial compilation path');
32297
32747
  }
32298
32748
  definitionMap.set('minVersion', literal(MINIMUM_PARTIAL_LINKER_VERSION$1));
32299
- definitionMap.set('version', literal('17.0.0-rc.1'));
32749
+ definitionMap.set('version', literal('17.0.0-rc.2'));
32300
32750
  definitionMap.set('ngImport', importExpr(Identifiers.core));
32301
32751
  definitionMap.set('type', meta.type.value);
32302
32752
  // We only generate the keys in the metadata if the arrays contain values.
@@ -32347,7 +32797,7 @@ function compileDeclarePipeFromMetadata(meta) {
32347
32797
  function createPipeDefinitionMap(meta) {
32348
32798
  const definitionMap = new DefinitionMap();
32349
32799
  definitionMap.set('minVersion', literal(MINIMUM_PARTIAL_LINKER_VERSION));
32350
- definitionMap.set('version', literal('17.0.0-rc.1'));
32800
+ definitionMap.set('version', literal('17.0.0-rc.2'));
32351
32801
  definitionMap.set('ngImport', importExpr(Identifiers.core));
32352
32802
  // e.g. `type: MyPipe`
32353
32803
  definitionMap.set('type', meta.type.value);