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