@angular/compiler 17.0.0-next.8 → 17.0.0-rc.1
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/ml_parser/ast.mjs +3 -2
- package/esm2022/src/ml_parser/html_tags.mjs +4 -4
- package/esm2022/src/ml_parser/html_whitespaces.mjs +2 -2
- package/esm2022/src/ml_parser/icu_ast_expander.mjs +2 -2
- package/esm2022/src/ml_parser/lexer.mjs +2 -13
- package/esm2022/src/ml_parser/parser.mjs +3 -3
- 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 +3 -2
- package/esm2022/src/render3/r3_control_flow.mjs +2 -2
- package/esm2022/src/render3/r3_template_transform.mjs +2 -2
- package/esm2022/src/shadow_css.mjs +10 -5
- package/esm2022/src/template/pipeline/ir/src/enums.mjs +81 -26
- package/esm2022/src/template/pipeline/ir/src/expression.mjs +65 -3
- package/esm2022/src/template/pipeline/ir/src/ops/create.mjs +41 -6
- package/esm2022/src/template/pipeline/ir/src/ops/shared.mjs +3 -2
- package/esm2022/src/template/pipeline/ir/src/ops/update.mjs +25 -3
- package/esm2022/src/template/pipeline/ir/src/variable.mjs +1 -1
- package/esm2022/src/template/pipeline/src/compilation.mjs +6 -1
- package/esm2022/src/template/pipeline/src/emit.mjs +17 -6
- package/esm2022/src/template/pipeline/src/ingest.mjs +92 -11
- package/esm2022/src/template/pipeline/src/instruction.mjs +28 -7
- package/esm2022/src/template/pipeline/src/phases/attribute_extraction.mjs +3 -3
- package/esm2022/src/template/pipeline/src/phases/binding_specialization.mjs +1 -1
- package/esm2022/src/template/pipeline/src/phases/collapse_singleton_interpolations.mjs +31 -0
- package/esm2022/src/template/pipeline/src/phases/conditionals.mjs +4 -1
- package/esm2022/src/template/pipeline/src/phases/generate_variables.mjs +11 -6
- package/esm2022/src/template/pipeline/src/phases/i18n_const_collection.mjs +2 -2
- package/esm2022/src/template/pipeline/src/phases/i18n_message_extraction.mjs +14 -4
- package/esm2022/src/template/pipeline/src/phases/i18n_text_extraction.mjs +2 -2
- package/esm2022/src/template/pipeline/src/phases/icu_extraction.mjs +53 -0
- package/esm2022/src/template/pipeline/src/phases/local_refs.mjs +1 -2
- package/esm2022/src/template/pipeline/src/phases/namespace.mjs +2 -2
- package/esm2022/src/template/pipeline/src/phases/naming.mjs +23 -4
- package/esm2022/src/template/pipeline/src/phases/ng_container.mjs +1 -6
- package/esm2022/src/template/pipeline/src/phases/ordering.mjs +40 -31
- package/esm2022/src/template/pipeline/src/phases/phase_remove_content_selectors.mjs +8 -8
- package/esm2022/src/template/pipeline/src/phases/pipe_creation.mjs +15 -3
- package/esm2022/src/template/pipeline/src/phases/pure_function_extraction.mjs +2 -1
- package/esm2022/src/template/pipeline/src/phases/reify.mjs +33 -2
- package/esm2022/src/template/pipeline/src/phases/repeater_derived_vars.mjs +40 -0
- package/esm2022/src/template/pipeline/src/phases/resolve_contexts.mjs +5 -1
- package/esm2022/src/template/pipeline/src/phases/resolve_i18n_placeholders.mjs +134 -36
- package/esm2022/src/template/pipeline/src/phases/resolve_names.mjs +2 -1
- package/esm2022/src/template/pipeline/src/phases/resolve_sanitizers.mjs +5 -6
- package/esm2022/src/template/pipeline/src/phases/save_restore_view.mjs +3 -3
- package/esm2022/src/template/pipeline/src/phases/slot_allocation.mjs +3 -3
- package/esm2022/src/template/pipeline/src/phases/track_fn_generation.mjs +49 -0
- package/esm2022/src/template/pipeline/src/phases/track_fn_optimization.mjs +75 -0
- package/esm2022/src/template/pipeline/src/phases/track_variables.mjs +31 -0
- package/esm2022/src/template/pipeline/src/phases/var_counting.mjs +36 -3
- package/esm2022/src/template/pipeline/src/phases/variable_optimization.mjs +43 -3
- package/esm2022/src/template/pipeline/src/phases/wrap_icus.mjs +34 -0
- package/esm2022/src/template/pipeline/src/util/elements.mjs +6 -6
- package/esm2022/src/version.mjs +1 -1
- package/fesm2022/compiler.mjs +1106 -370
- package/fesm2022/compiler.mjs.map +1 -1
- package/index.d.ts +5 -3
- package/package.json +2 -2
- package/esm2022/src/template/pipeline/src/phases/align_pipe_variadic_var_offset.mjs +0 -42
- package/esm2022/src/template/pipeline/src/phases/propagate_i18n_placeholders.mjs +0 -39
package/fesm2022/compiler.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @license Angular v17.0.0-
|
|
2
|
+
* @license Angular v17.0.0-rc.1
|
|
3
3
|
* (c) 2010-2022 Google LLC. https://angular.io/
|
|
4
4
|
* License: MIT
|
|
5
5
|
*/
|
|
@@ -3983,9 +3983,10 @@ class IfBlockBranch {
|
|
|
3983
3983
|
}
|
|
3984
3984
|
}
|
|
3985
3985
|
class UnknownBlock {
|
|
3986
|
-
constructor(name, sourceSpan) {
|
|
3986
|
+
constructor(name, sourceSpan, nameSpan) {
|
|
3987
3987
|
this.name = name;
|
|
3988
3988
|
this.sourceSpan = sourceSpan;
|
|
3989
|
+
this.nameSpan = nameSpan;
|
|
3989
3990
|
}
|
|
3990
3991
|
visit(visitor) {
|
|
3991
3992
|
return visitor.visitUnknownBlock(this);
|
|
@@ -8317,7 +8318,7 @@ class ShadowCss {
|
|
|
8317
8318
|
// (ie: ".\fc ber" for ".über") is not a separator between 2 selectors
|
|
8318
8319
|
// also keep in mind that backslashes are replaced by a placeholder by SafeSelector
|
|
8319
8320
|
// These escaped selectors happen for example when esbuild runs with optimization.minify.
|
|
8320
|
-
if (part.match(
|
|
8321
|
+
if (part.match(/__esc-ph-(\d+)__/) && selector[res.index + 1]?.match(/[a-fA-F\d]/)) {
|
|
8321
8322
|
continue;
|
|
8322
8323
|
}
|
|
8323
8324
|
shouldScope = shouldScope || part.indexOf(_polyfillHostNoCombinator) > -1;
|
|
@@ -8348,7 +8349,13 @@ class SafeSelector {
|
|
|
8348
8349
|
// pseudo-class, but writing `.foo\:blue` will match, because the colon was escaped.
|
|
8349
8350
|
// Replace all escape sequences (`\` followed by a character) with a placeholder so
|
|
8350
8351
|
// that our handling of pseudo-selectors doesn't mess with them.
|
|
8351
|
-
|
|
8352
|
+
// Escaped characters have a specific placeholder so they can be detected separately.
|
|
8353
|
+
selector = selector.replace(/(\\.)/g, (_, keep) => {
|
|
8354
|
+
const replaceBy = `__esc-ph-${this.index}__`;
|
|
8355
|
+
this.placeholders.push(keep);
|
|
8356
|
+
this.index++;
|
|
8357
|
+
return replaceBy;
|
|
8358
|
+
});
|
|
8352
8359
|
// Replaces the expression in `:nth-child(2n + 1)` with a placeholder.
|
|
8353
8360
|
// WS and "+" would otherwise be interpreted as selector separators.
|
|
8354
8361
|
this._content = selector.replace(/(:nth-[-\w]+)(\([^)]+\))/g, (_, pseudo, exp) => {
|
|
@@ -8359,7 +8366,7 @@ class SafeSelector {
|
|
|
8359
8366
|
});
|
|
8360
8367
|
}
|
|
8361
8368
|
restore(content) {
|
|
8362
|
-
return content.replace(
|
|
8369
|
+
return content.replace(/__(?:ph|esc-ph)-(\d+)__/g, (_ph, index) => this.placeholders[+index]);
|
|
8363
8370
|
}
|
|
8364
8371
|
content() {
|
|
8365
8372
|
return this._content;
|
|
@@ -8411,7 +8418,6 @@ const _commentRe = /\/\*[\s\S]*?\*\//g;
|
|
|
8411
8418
|
const _commentWithHashRe = /\/\*\s*#\s*source(Mapping)?URL=/g;
|
|
8412
8419
|
const COMMENT_PLACEHOLDER = '%COMMENT%';
|
|
8413
8420
|
const _commentWithHashPlaceHolderRe = new RegExp(COMMENT_PLACEHOLDER, 'g');
|
|
8414
|
-
const _placeholderRe = /__ph-(\d+)__/g;
|
|
8415
8421
|
const BLOCK_PLACEHOLDER = '%BLOCK%';
|
|
8416
8422
|
const _ruleRe = new RegExp(`(\\s*(?:${COMMENT_PLACEHOLDER}\\s*)*)([^;\\{\\}]+?)(\\s*)((?:{%BLOCK%}?\\s*;?)|(?:\\s*;))`, 'g');
|
|
8417
8423
|
const CONTENT_PAIRS = new Map([['{', '}']]);
|
|
@@ -8821,26 +8827,42 @@ var OpKind;
|
|
|
8821
8827
|
* Create a content projection slot.
|
|
8822
8828
|
*/
|
|
8823
8829
|
OpKind[OpKind["Projection"] = 33] = "Projection";
|
|
8830
|
+
/**
|
|
8831
|
+
* Create a repeater creation instruction op.
|
|
8832
|
+
*/
|
|
8833
|
+
OpKind[OpKind["RepeaterCreate"] = 34] = "RepeaterCreate";
|
|
8834
|
+
/**
|
|
8835
|
+
* An update up for a repeater.
|
|
8836
|
+
*/
|
|
8837
|
+
OpKind[OpKind["Repeater"] = 35] = "Repeater";
|
|
8824
8838
|
/**
|
|
8825
8839
|
* The start of an i18n block.
|
|
8826
8840
|
*/
|
|
8827
|
-
OpKind[OpKind["I18nStart"] =
|
|
8841
|
+
OpKind[OpKind["I18nStart"] = 36] = "I18nStart";
|
|
8828
8842
|
/**
|
|
8829
8843
|
* A self-closing i18n on a single element.
|
|
8830
8844
|
*/
|
|
8831
|
-
OpKind[OpKind["I18n"] =
|
|
8845
|
+
OpKind[OpKind["I18n"] = 37] = "I18n";
|
|
8832
8846
|
/**
|
|
8833
8847
|
* The end of an i18n block.
|
|
8834
8848
|
*/
|
|
8835
|
-
OpKind[OpKind["I18nEnd"] =
|
|
8849
|
+
OpKind[OpKind["I18nEnd"] = 38] = "I18nEnd";
|
|
8836
8850
|
/**
|
|
8837
8851
|
* An expression in an i18n message.
|
|
8838
8852
|
*/
|
|
8839
|
-
OpKind[OpKind["I18nExpression"] =
|
|
8853
|
+
OpKind[OpKind["I18nExpression"] = 39] = "I18nExpression";
|
|
8840
8854
|
/**
|
|
8841
8855
|
* An instruction that applies a set of i18n expressions.
|
|
8842
8856
|
*/
|
|
8843
|
-
OpKind[OpKind["I18nApply"] =
|
|
8857
|
+
OpKind[OpKind["I18nApply"] = 40] = "I18nApply";
|
|
8858
|
+
/**
|
|
8859
|
+
* An instruction to create an ICU expression.
|
|
8860
|
+
*/
|
|
8861
|
+
OpKind[OpKind["Icu"] = 41] = "Icu";
|
|
8862
|
+
/**
|
|
8863
|
+
* An instruction to update an ICU expression.
|
|
8864
|
+
*/
|
|
8865
|
+
OpKind[OpKind["IcuUpdate"] = 42] = "IcuUpdate";
|
|
8844
8866
|
})(OpKind || (OpKind = {}));
|
|
8845
8867
|
/**
|
|
8846
8868
|
* Distinguishes different kinds of IR expressions.
|
|
@@ -8855,87 +8877,106 @@ var ExpressionKind;
|
|
|
8855
8877
|
* A reference to the current view context.
|
|
8856
8878
|
*/
|
|
8857
8879
|
ExpressionKind[ExpressionKind["Context"] = 1] = "Context";
|
|
8880
|
+
/**
|
|
8881
|
+
* A reference to the view context, for use inside a track function.
|
|
8882
|
+
*/
|
|
8883
|
+
ExpressionKind[ExpressionKind["TrackContext"] = 2] = "TrackContext";
|
|
8858
8884
|
/**
|
|
8859
8885
|
* Read of a variable declared in a `VariableOp`.
|
|
8860
8886
|
*/
|
|
8861
|
-
ExpressionKind[ExpressionKind["ReadVariable"] =
|
|
8887
|
+
ExpressionKind[ExpressionKind["ReadVariable"] = 3] = "ReadVariable";
|
|
8862
8888
|
/**
|
|
8863
8889
|
* Runtime operation to navigate to the next view context in the view hierarchy.
|
|
8864
8890
|
*/
|
|
8865
|
-
ExpressionKind[ExpressionKind["NextContext"] =
|
|
8891
|
+
ExpressionKind[ExpressionKind["NextContext"] = 4] = "NextContext";
|
|
8866
8892
|
/**
|
|
8867
8893
|
* Runtime operation to retrieve the value of a local reference.
|
|
8868
8894
|
*/
|
|
8869
|
-
ExpressionKind[ExpressionKind["Reference"] =
|
|
8895
|
+
ExpressionKind[ExpressionKind["Reference"] = 5] = "Reference";
|
|
8870
8896
|
/**
|
|
8871
8897
|
* Runtime operation to snapshot the current view context.
|
|
8872
8898
|
*/
|
|
8873
|
-
ExpressionKind[ExpressionKind["GetCurrentView"] =
|
|
8899
|
+
ExpressionKind[ExpressionKind["GetCurrentView"] = 6] = "GetCurrentView";
|
|
8874
8900
|
/**
|
|
8875
8901
|
* Runtime operation to restore a snapshotted view.
|
|
8876
8902
|
*/
|
|
8877
|
-
ExpressionKind[ExpressionKind["RestoreView"] =
|
|
8903
|
+
ExpressionKind[ExpressionKind["RestoreView"] = 7] = "RestoreView";
|
|
8878
8904
|
/**
|
|
8879
8905
|
* Runtime operation to reset the current view context after `RestoreView`.
|
|
8880
8906
|
*/
|
|
8881
|
-
ExpressionKind[ExpressionKind["ResetView"] =
|
|
8907
|
+
ExpressionKind[ExpressionKind["ResetView"] = 8] = "ResetView";
|
|
8882
8908
|
/**
|
|
8883
8909
|
* Defines and calls a function with change-detected arguments.
|
|
8884
8910
|
*/
|
|
8885
|
-
ExpressionKind[ExpressionKind["PureFunctionExpr"] =
|
|
8911
|
+
ExpressionKind[ExpressionKind["PureFunctionExpr"] = 9] = "PureFunctionExpr";
|
|
8886
8912
|
/**
|
|
8887
8913
|
* Indicates a positional parameter to a pure function definition.
|
|
8888
8914
|
*/
|
|
8889
|
-
ExpressionKind[ExpressionKind["PureFunctionParameterExpr"] =
|
|
8915
|
+
ExpressionKind[ExpressionKind["PureFunctionParameterExpr"] = 10] = "PureFunctionParameterExpr";
|
|
8890
8916
|
/**
|
|
8891
8917
|
* Binding to a pipe transformation.
|
|
8892
8918
|
*/
|
|
8893
|
-
ExpressionKind[ExpressionKind["PipeBinding"] =
|
|
8919
|
+
ExpressionKind[ExpressionKind["PipeBinding"] = 11] = "PipeBinding";
|
|
8894
8920
|
/**
|
|
8895
8921
|
* Binding to a pipe transformation with a variable number of arguments.
|
|
8896
8922
|
*/
|
|
8897
|
-
ExpressionKind[ExpressionKind["PipeBindingVariadic"] =
|
|
8923
|
+
ExpressionKind[ExpressionKind["PipeBindingVariadic"] = 12] = "PipeBindingVariadic";
|
|
8898
8924
|
/*
|
|
8899
8925
|
* A safe property read requiring expansion into a null check.
|
|
8900
8926
|
*/
|
|
8901
|
-
ExpressionKind[ExpressionKind["SafePropertyRead"] =
|
|
8927
|
+
ExpressionKind[ExpressionKind["SafePropertyRead"] = 13] = "SafePropertyRead";
|
|
8902
8928
|
/**
|
|
8903
8929
|
* A safe keyed read requiring expansion into a null check.
|
|
8904
8930
|
*/
|
|
8905
|
-
ExpressionKind[ExpressionKind["SafeKeyedRead"] =
|
|
8931
|
+
ExpressionKind[ExpressionKind["SafeKeyedRead"] = 14] = "SafeKeyedRead";
|
|
8906
8932
|
/**
|
|
8907
8933
|
* A safe function call requiring expansion into a null check.
|
|
8908
8934
|
*/
|
|
8909
|
-
ExpressionKind[ExpressionKind["SafeInvokeFunction"] =
|
|
8935
|
+
ExpressionKind[ExpressionKind["SafeInvokeFunction"] = 15] = "SafeInvokeFunction";
|
|
8910
8936
|
/**
|
|
8911
8937
|
* An intermediate expression that will be expanded from a safe read into an explicit ternary.
|
|
8912
8938
|
*/
|
|
8913
|
-
ExpressionKind[ExpressionKind["SafeTernaryExpr"] =
|
|
8939
|
+
ExpressionKind[ExpressionKind["SafeTernaryExpr"] = 16] = "SafeTernaryExpr";
|
|
8914
8940
|
/**
|
|
8915
8941
|
* An empty expression that will be stipped before generating the final output.
|
|
8916
8942
|
*/
|
|
8917
|
-
ExpressionKind[ExpressionKind["EmptyExpr"] =
|
|
8943
|
+
ExpressionKind[ExpressionKind["EmptyExpr"] = 17] = "EmptyExpr";
|
|
8918
8944
|
/*
|
|
8919
8945
|
* An assignment to a temporary variable.
|
|
8920
8946
|
*/
|
|
8921
|
-
ExpressionKind[ExpressionKind["AssignTemporaryExpr"] =
|
|
8947
|
+
ExpressionKind[ExpressionKind["AssignTemporaryExpr"] = 18] = "AssignTemporaryExpr";
|
|
8922
8948
|
/**
|
|
8923
8949
|
* A reference to a temporary variable.
|
|
8924
8950
|
*/
|
|
8925
|
-
ExpressionKind[ExpressionKind["ReadTemporaryExpr"] =
|
|
8951
|
+
ExpressionKind[ExpressionKind["ReadTemporaryExpr"] = 19] = "ReadTemporaryExpr";
|
|
8926
8952
|
/**
|
|
8927
8953
|
* An expression representing a sanitizer function.
|
|
8928
8954
|
*/
|
|
8929
|
-
ExpressionKind[ExpressionKind["SanitizerExpr"] =
|
|
8955
|
+
ExpressionKind[ExpressionKind["SanitizerExpr"] = 20] = "SanitizerExpr";
|
|
8930
8956
|
/**
|
|
8931
8957
|
* An expression that will cause a literal slot index to be emitted.
|
|
8932
8958
|
*/
|
|
8933
|
-
ExpressionKind[ExpressionKind["SlotLiteralExpr"] =
|
|
8959
|
+
ExpressionKind[ExpressionKind["SlotLiteralExpr"] = 21] = "SlotLiteralExpr";
|
|
8934
8960
|
/**
|
|
8935
8961
|
* A test expression for a conditional op.
|
|
8936
8962
|
*/
|
|
8937
|
-
ExpressionKind[ExpressionKind["ConditionalCase"] =
|
|
8963
|
+
ExpressionKind[ExpressionKind["ConditionalCase"] = 22] = "ConditionalCase";
|
|
8964
|
+
/**
|
|
8965
|
+
* A variable for use inside a repeater, providing one of the ambiently-available context
|
|
8966
|
+
* properties ($even, $first, etc.).
|
|
8967
|
+
*/
|
|
8968
|
+
ExpressionKind[ExpressionKind["DerivedRepeaterVar"] = 23] = "DerivedRepeaterVar";
|
|
8938
8969
|
})(ExpressionKind || (ExpressionKind = {}));
|
|
8970
|
+
var VariableFlags;
|
|
8971
|
+
(function (VariableFlags) {
|
|
8972
|
+
VariableFlags[VariableFlags["None"] = 0] = "None";
|
|
8973
|
+
/**
|
|
8974
|
+
* Always inline this variable, regardless of the number of times it's used.
|
|
8975
|
+
* An `AlwaysInline` variable may not depend on context, because doing so may cause side effects
|
|
8976
|
+
* that are illegal when multi-inlined. (The optimizer will enforce this constraint.)
|
|
8977
|
+
*/
|
|
8978
|
+
VariableFlags[VariableFlags["AlwaysInline"] = 1] = "AlwaysInline";
|
|
8979
|
+
})(VariableFlags || (VariableFlags = {}));
|
|
8939
8980
|
/**
|
|
8940
8981
|
* Distinguishes between different kinds of `SemanticVariable`s.
|
|
8941
8982
|
*/
|
|
@@ -8953,6 +8994,10 @@ var SemanticVariableKind;
|
|
|
8953
8994
|
* Represents a saved state that can be used to restore a view in a listener handler function.
|
|
8954
8995
|
*/
|
|
8955
8996
|
SemanticVariableKind[SemanticVariableKind["SavedView"] = 2] = "SavedView";
|
|
8997
|
+
/**
|
|
8998
|
+
* An alias generated by a special embedded view type (e.g. a `@for` block).
|
|
8999
|
+
*/
|
|
9000
|
+
SemanticVariableKind[SemanticVariableKind["Alias"] = 3] = "Alias";
|
|
8956
9001
|
})(SemanticVariableKind || (SemanticVariableKind = {}));
|
|
8957
9002
|
/**
|
|
8958
9003
|
* Whether to compile in compatibilty mode. In compatibility mode, the template pipeline will
|
|
@@ -9019,6 +9064,22 @@ var BindingKind;
|
|
|
9019
9064
|
*/
|
|
9020
9065
|
BindingKind[BindingKind["Animation"] = 6] = "Animation";
|
|
9021
9066
|
})(BindingKind || (BindingKind = {}));
|
|
9067
|
+
/**
|
|
9068
|
+
* Enumeration of possible times i18n params can be resolved.
|
|
9069
|
+
*/
|
|
9070
|
+
var I18nParamResolutionTime;
|
|
9071
|
+
(function (I18nParamResolutionTime) {
|
|
9072
|
+
/**
|
|
9073
|
+
* Param is resolved at message creation time. Most params should be resolved at message creation
|
|
9074
|
+
* time. However, ICU params need to be handled in post-processing.
|
|
9075
|
+
*/
|
|
9076
|
+
I18nParamResolutionTime[I18nParamResolutionTime["Creation"] = 0] = "Creation";
|
|
9077
|
+
/**
|
|
9078
|
+
* Param is resolved during post-processing. This should be used for params who's value comes from
|
|
9079
|
+
* an ICU.
|
|
9080
|
+
*/
|
|
9081
|
+
I18nParamResolutionTime[I18nParamResolutionTime["Postproccessing"] = 1] = "Postproccessing";
|
|
9082
|
+
})(I18nParamResolutionTime || (I18nParamResolutionTime = {}));
|
|
9022
9083
|
|
|
9023
9084
|
/**
|
|
9024
9085
|
* Marker symbol for `ConsumesSlotOpTrait`.
|
|
@@ -9132,12 +9193,13 @@ function createStatementOp(statement) {
|
|
|
9132
9193
|
/**
|
|
9133
9194
|
* Create a `VariableOp`.
|
|
9134
9195
|
*/
|
|
9135
|
-
function createVariableOp(xref, variable, initializer) {
|
|
9196
|
+
function createVariableOp(xref, variable, initializer, flags) {
|
|
9136
9197
|
return {
|
|
9137
9198
|
kind: OpKind.Variable,
|
|
9138
9199
|
xref,
|
|
9139
9200
|
variable,
|
|
9140
9201
|
initializer,
|
|
9202
|
+
flags,
|
|
9141
9203
|
...NEW_OP,
|
|
9142
9204
|
};
|
|
9143
9205
|
}
|
|
@@ -9314,16 +9376,27 @@ function createConditionalOp(target, test, conditions, sourceSpan) {
|
|
|
9314
9376
|
...TRAIT_CONSUMES_VARS,
|
|
9315
9377
|
};
|
|
9316
9378
|
}
|
|
9379
|
+
function createRepeaterOp(repeaterCreate, collection, sourceSpan) {
|
|
9380
|
+
return {
|
|
9381
|
+
kind: OpKind.Repeater,
|
|
9382
|
+
target: repeaterCreate,
|
|
9383
|
+
collection,
|
|
9384
|
+
sourceSpan,
|
|
9385
|
+
...NEW_OP,
|
|
9386
|
+
...TRAIT_USES_SLOT_INDEX,
|
|
9387
|
+
};
|
|
9388
|
+
}
|
|
9317
9389
|
/**
|
|
9318
9390
|
* Create an i18n expression op.
|
|
9319
9391
|
*/
|
|
9320
|
-
function createI18nExpressionOp(owner, expression, i18nPlaceholder, sourceSpan) {
|
|
9392
|
+
function createI18nExpressionOp(owner, expression, i18nPlaceholder, resolutionTime, sourceSpan) {
|
|
9321
9393
|
return {
|
|
9322
9394
|
kind: OpKind.I18nExpression,
|
|
9323
9395
|
owner,
|
|
9324
9396
|
target: owner,
|
|
9325
9397
|
expression,
|
|
9326
9398
|
i18nPlaceholder,
|
|
9399
|
+
resolutionTime,
|
|
9327
9400
|
sourceSpan,
|
|
9328
9401
|
...NEW_OP,
|
|
9329
9402
|
...TRAIT_CONSUMES_VARS,
|
|
@@ -9331,7 +9404,7 @@ function createI18nExpressionOp(owner, expression, i18nPlaceholder, sourceSpan)
|
|
|
9331
9404
|
};
|
|
9332
9405
|
}
|
|
9333
9406
|
/**
|
|
9334
|
-
*
|
|
9407
|
+
*Creates an op to apply i18n expression ops
|
|
9335
9408
|
*/
|
|
9336
9409
|
function createI18nApplyOp(target, sourceSpan) {
|
|
9337
9410
|
return {
|
|
@@ -9342,6 +9415,17 @@ function createI18nApplyOp(target, sourceSpan) {
|
|
|
9342
9415
|
...TRAIT_USES_SLOT_INDEX,
|
|
9343
9416
|
};
|
|
9344
9417
|
}
|
|
9418
|
+
/**
|
|
9419
|
+
* Creates an op to update an ICU expression.
|
|
9420
|
+
*/
|
|
9421
|
+
function createIcuUpdateOp(xref, sourceSpan) {
|
|
9422
|
+
return {
|
|
9423
|
+
kind: OpKind.IcuUpdate,
|
|
9424
|
+
xref,
|
|
9425
|
+
sourceSpan,
|
|
9426
|
+
...NEW_OP,
|
|
9427
|
+
};
|
|
9428
|
+
}
|
|
9345
9429
|
|
|
9346
9430
|
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k;
|
|
9347
9431
|
/**
|
|
@@ -9368,8 +9452,11 @@ class LexicalReadExpr extends ExpressionBase {
|
|
|
9368
9452
|
this.kind = ExpressionKind.LexicalRead;
|
|
9369
9453
|
}
|
|
9370
9454
|
visitExpression(visitor, context) { }
|
|
9371
|
-
isEquivalent() {
|
|
9372
|
-
|
|
9455
|
+
isEquivalent(other) {
|
|
9456
|
+
// We assume that the lexical reads are in the same context, which must be true for parent
|
|
9457
|
+
// expressions to be equivalent.
|
|
9458
|
+
// TODO: is this generally safe?
|
|
9459
|
+
return this.name === other.name;
|
|
9373
9460
|
}
|
|
9374
9461
|
isConstant() {
|
|
9375
9462
|
return false;
|
|
@@ -9427,6 +9514,27 @@ class ContextExpr extends ExpressionBase {
|
|
|
9427
9514
|
return new ContextExpr(this.view);
|
|
9428
9515
|
}
|
|
9429
9516
|
}
|
|
9517
|
+
/**
|
|
9518
|
+
* A reference to the current view context inside a track function.
|
|
9519
|
+
*/
|
|
9520
|
+
class TrackContextExpr extends ExpressionBase {
|
|
9521
|
+
constructor(view) {
|
|
9522
|
+
super();
|
|
9523
|
+
this.view = view;
|
|
9524
|
+
this.kind = ExpressionKind.TrackContext;
|
|
9525
|
+
}
|
|
9526
|
+
visitExpression() { }
|
|
9527
|
+
isEquivalent(e) {
|
|
9528
|
+
return e instanceof TrackContextExpr && e.view === this.view;
|
|
9529
|
+
}
|
|
9530
|
+
isConstant() {
|
|
9531
|
+
return false;
|
|
9532
|
+
}
|
|
9533
|
+
transformInternalExpressions() { }
|
|
9534
|
+
clone() {
|
|
9535
|
+
return new TrackContextExpr(this.view);
|
|
9536
|
+
}
|
|
9537
|
+
}
|
|
9430
9538
|
/**
|
|
9431
9539
|
* Runtime operation to navigate to the next view context in the view hierarchy.
|
|
9432
9540
|
*/
|
|
@@ -9944,6 +10052,33 @@ class ConditionalCaseExpr extends ExpressionBase {
|
|
|
9944
10052
|
}
|
|
9945
10053
|
}
|
|
9946
10054
|
}
|
|
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
|
+
class DerivedRepeaterVarExpr extends ExpressionBase {
|
|
10063
|
+
constructor(xref, identity) {
|
|
10064
|
+
super();
|
|
10065
|
+
this.xref = xref;
|
|
10066
|
+
this.identity = identity;
|
|
10067
|
+
this.kind = ExpressionKind.DerivedRepeaterVar;
|
|
10068
|
+
}
|
|
10069
|
+
transformInternalExpressions(transform, flags) { }
|
|
10070
|
+
visitExpression(visitor, context) { }
|
|
10071
|
+
isEquivalent(e) {
|
|
10072
|
+
return e instanceof DerivedRepeaterVarExpr && e.identity === this.identity &&
|
|
10073
|
+
e.xref === this.xref;
|
|
10074
|
+
}
|
|
10075
|
+
isConstant() {
|
|
10076
|
+
return false;
|
|
10077
|
+
}
|
|
10078
|
+
clone() {
|
|
10079
|
+
return new DerivedRepeaterVarExpr(this.xref, this.identity);
|
|
10080
|
+
}
|
|
10081
|
+
}
|
|
9947
10082
|
/**
|
|
9948
10083
|
* Visits all `Expression`s in the AST of `op` with the `visitor` function.
|
|
9949
10084
|
*/
|
|
@@ -10038,6 +10173,15 @@ function transformExpressionsInOp(op, transform, flags) {
|
|
|
10038
10173
|
transformExpressionsInStatement(statement, transform, flags);
|
|
10039
10174
|
}
|
|
10040
10175
|
break;
|
|
10176
|
+
case OpKind.RepeaterCreate:
|
|
10177
|
+
op.track = transformExpressionsInExpression(op.track, transform, flags);
|
|
10178
|
+
if (op.trackByFn !== null) {
|
|
10179
|
+
op.trackByFn = transformExpressionsInExpression(op.trackByFn, transform, flags);
|
|
10180
|
+
}
|
|
10181
|
+
break;
|
|
10182
|
+
case OpKind.Repeater:
|
|
10183
|
+
op.collection = transformExpressionsInExpression(op.collection, transform, flags);
|
|
10184
|
+
break;
|
|
10041
10185
|
case OpKind.I18n:
|
|
10042
10186
|
case OpKind.I18nStart:
|
|
10043
10187
|
for (const [placeholder, expression] of op.params) {
|
|
@@ -10064,6 +10208,8 @@ function transformExpressionsInOp(op, transform, flags) {
|
|
|
10064
10208
|
case OpKind.Advance:
|
|
10065
10209
|
case OpKind.Namespace:
|
|
10066
10210
|
case OpKind.I18nApply:
|
|
10211
|
+
case OpKind.Icu:
|
|
10212
|
+
case OpKind.IcuUpdate:
|
|
10067
10213
|
// These operations contain no expressions.
|
|
10068
10214
|
break;
|
|
10069
10215
|
default:
|
|
@@ -10440,7 +10586,7 @@ class OpList {
|
|
|
10440
10586
|
*/
|
|
10441
10587
|
const elementContainerOpKinds = new Set([
|
|
10442
10588
|
OpKind.Element, OpKind.ElementStart, OpKind.Container, OpKind.ContainerStart, OpKind.Template,
|
|
10443
|
-
OpKind.
|
|
10589
|
+
OpKind.RepeaterCreate
|
|
10444
10590
|
]);
|
|
10445
10591
|
/**
|
|
10446
10592
|
* Checks whether the given operation represents the creation of an element or container.
|
|
@@ -10487,6 +10633,28 @@ function createTemplateOp(xref, tag, namespace, generatedInBlock, i18nPlaceholde
|
|
|
10487
10633
|
...NEW_OP,
|
|
10488
10634
|
};
|
|
10489
10635
|
}
|
|
10636
|
+
function createRepeaterCreateOp(primaryView, emptyView, track, varNames, sourceSpan) {
|
|
10637
|
+
return {
|
|
10638
|
+
kind: OpKind.RepeaterCreate,
|
|
10639
|
+
attributes: null,
|
|
10640
|
+
xref: primaryView,
|
|
10641
|
+
emptyView,
|
|
10642
|
+
track,
|
|
10643
|
+
trackByFn: null,
|
|
10644
|
+
tag: 'For',
|
|
10645
|
+
namespace: Namespace.HTML,
|
|
10646
|
+
nonBindable: false,
|
|
10647
|
+
localRefs: [],
|
|
10648
|
+
decls: null,
|
|
10649
|
+
vars: null,
|
|
10650
|
+
varNames,
|
|
10651
|
+
usesComponentInstance: false,
|
|
10652
|
+
sourceSpan,
|
|
10653
|
+
...TRAIT_CONSUMES_SLOT,
|
|
10654
|
+
...NEW_OP,
|
|
10655
|
+
numSlotsUsed: emptyView === null ? 2 : 3,
|
|
10656
|
+
};
|
|
10657
|
+
}
|
|
10490
10658
|
/**
|
|
10491
10659
|
* Create an `ElementEndOp`.
|
|
10492
10660
|
*/
|
|
@@ -10577,16 +10745,15 @@ function createProjectionDefOp(def) {
|
|
|
10577
10745
|
...NEW_OP,
|
|
10578
10746
|
};
|
|
10579
10747
|
}
|
|
10580
|
-
function createProjectionOp(xref, selector) {
|
|
10748
|
+
function createProjectionOp(xref, selector, sourceSpan) {
|
|
10581
10749
|
return {
|
|
10582
10750
|
kind: OpKind.Projection,
|
|
10583
10751
|
xref,
|
|
10584
10752
|
selector,
|
|
10585
10753
|
projectionSlotIndex: 0,
|
|
10586
|
-
attributes:
|
|
10754
|
+
attributes: [],
|
|
10587
10755
|
localRefs: [],
|
|
10588
|
-
|
|
10589
|
-
sourceSpan: null,
|
|
10756
|
+
sourceSpan,
|
|
10590
10757
|
...NEW_OP,
|
|
10591
10758
|
...TRAIT_CONSUMES_SLOT,
|
|
10592
10759
|
};
|
|
@@ -10662,8 +10829,10 @@ function createI18nStartOp(xref, message, root) {
|
|
|
10662
10829
|
root: root ?? xref,
|
|
10663
10830
|
message,
|
|
10664
10831
|
params: new Map(),
|
|
10832
|
+
postprocessingParams: new Map(),
|
|
10665
10833
|
messageIndex: null,
|
|
10666
10834
|
subTemplateIndex: null,
|
|
10835
|
+
needsPostprocessing: false,
|
|
10667
10836
|
...NEW_OP,
|
|
10668
10837
|
...TRAIT_CONSUMES_SLOT,
|
|
10669
10838
|
};
|
|
@@ -10678,6 +10847,18 @@ function createI18nEndOp(xref) {
|
|
|
10678
10847
|
...NEW_OP,
|
|
10679
10848
|
};
|
|
10680
10849
|
}
|
|
10850
|
+
/**
|
|
10851
|
+
* Creates an op to create an ICU expression.
|
|
10852
|
+
*/
|
|
10853
|
+
function createIcuOp(xref, message, sourceSpan) {
|
|
10854
|
+
return {
|
|
10855
|
+
kind: OpKind.Icu,
|
|
10856
|
+
xref,
|
|
10857
|
+
message,
|
|
10858
|
+
sourceSpan,
|
|
10859
|
+
...NEW_OP,
|
|
10860
|
+
};
|
|
10861
|
+
}
|
|
10681
10862
|
function literalOrArrayLiteral$1(value) {
|
|
10682
10863
|
if (Array.isArray(value)) {
|
|
10683
10864
|
return literalArr(value.map(literalOrArrayLiteral$1));
|
|
@@ -10850,6 +11031,11 @@ class ViewCompilationUnit extends CompilationUnit {
|
|
|
10850
11031
|
* which they alias.
|
|
10851
11032
|
*/
|
|
10852
11033
|
this.contextVariables = new Map();
|
|
11034
|
+
/**
|
|
11035
|
+
* Set of aliases available within this view. An alias is a variable whose provided expression is
|
|
11036
|
+
* inlined at every location it is used. It may also depend on context variables, by name.
|
|
11037
|
+
*/
|
|
11038
|
+
this.aliases = new Set();
|
|
10853
11039
|
/**
|
|
10854
11040
|
* Number of declaration slots used within this view, or `null` if slots have not yet been
|
|
10855
11041
|
* allocated.
|
|
@@ -10882,137 +11068,6 @@ class HostBindingCompilationUnit extends CompilationUnit {
|
|
|
10882
11068
|
}
|
|
10883
11069
|
}
|
|
10884
11070
|
|
|
10885
|
-
/**
|
|
10886
|
-
* Counts the number of variable slots used within each view, and stores that on the view itself, as
|
|
10887
|
-
* well as propagates it to the `ir.TemplateOp` for embedded views.
|
|
10888
|
-
*/
|
|
10889
|
-
function phaseVarCounting(job) {
|
|
10890
|
-
// First, count the vars used in each view, and update the view-level counter.
|
|
10891
|
-
for (const unit of job.units) {
|
|
10892
|
-
let varCount = 0;
|
|
10893
|
-
// Count variables on top-level ops first. Don't explore nested expressions just yet.
|
|
10894
|
-
for (const op of unit.ops()) {
|
|
10895
|
-
if (hasConsumesVarsTrait(op)) {
|
|
10896
|
-
varCount += varsUsedByOp(op);
|
|
10897
|
-
}
|
|
10898
|
-
}
|
|
10899
|
-
// Count variables on expressions inside ops. We do this later because some of these expressions
|
|
10900
|
-
// might be conditional (e.g. `pipeBinding` inside of a ternary), and we don't want to interfere
|
|
10901
|
-
// with indices for top-level binding slots (e.g. `property`).
|
|
10902
|
-
for (const op of unit.ops()) {
|
|
10903
|
-
visitExpressionsInOp(op, expr => {
|
|
10904
|
-
if (!isIrExpression(expr)) {
|
|
10905
|
-
return;
|
|
10906
|
-
}
|
|
10907
|
-
// Some expressions require knowledge of the number of variable slots consumed.
|
|
10908
|
-
if (hasUsesVarOffsetTrait(expr)) {
|
|
10909
|
-
expr.varOffset = varCount;
|
|
10910
|
-
}
|
|
10911
|
-
if (hasConsumesVarsTrait(expr)) {
|
|
10912
|
-
varCount += varsUsedByIrExpression(expr);
|
|
10913
|
-
}
|
|
10914
|
-
});
|
|
10915
|
-
}
|
|
10916
|
-
unit.vars = varCount;
|
|
10917
|
-
}
|
|
10918
|
-
if (job instanceof ComponentCompilationJob) {
|
|
10919
|
-
// Add var counts for each view to the `ir.TemplateOp` which declares that view (if the view is
|
|
10920
|
-
// an embedded view).
|
|
10921
|
-
for (const unit of job.units) {
|
|
10922
|
-
for (const op of unit.create) {
|
|
10923
|
-
if (op.kind !== OpKind.Template) {
|
|
10924
|
-
continue;
|
|
10925
|
-
}
|
|
10926
|
-
const childView = job.views.get(op.xref);
|
|
10927
|
-
op.vars = childView.vars;
|
|
10928
|
-
}
|
|
10929
|
-
}
|
|
10930
|
-
}
|
|
10931
|
-
}
|
|
10932
|
-
/**
|
|
10933
|
-
* Different operations that implement `ir.UsesVarsTrait` use different numbers of variables, so
|
|
10934
|
-
* count the variables used by any particular `op`.
|
|
10935
|
-
*/
|
|
10936
|
-
function varsUsedByOp(op) {
|
|
10937
|
-
let slots;
|
|
10938
|
-
switch (op.kind) {
|
|
10939
|
-
case OpKind.Property:
|
|
10940
|
-
case OpKind.HostProperty:
|
|
10941
|
-
case OpKind.Attribute:
|
|
10942
|
-
// All of these bindings use 1 variable slot, plus 1 slot for every interpolated expression,
|
|
10943
|
-
// if any.
|
|
10944
|
-
slots = 1;
|
|
10945
|
-
if (op.expression instanceof Interpolation) {
|
|
10946
|
-
slots += op.expression.expressions.length;
|
|
10947
|
-
}
|
|
10948
|
-
return slots;
|
|
10949
|
-
case OpKind.StyleProp:
|
|
10950
|
-
case OpKind.ClassProp:
|
|
10951
|
-
case OpKind.StyleMap:
|
|
10952
|
-
case OpKind.ClassMap:
|
|
10953
|
-
// Style & class bindings use 2 variable slots, plus 1 slot for every interpolated expression,
|
|
10954
|
-
// if any.
|
|
10955
|
-
slots = 2;
|
|
10956
|
-
if (op.expression instanceof Interpolation) {
|
|
10957
|
-
slots += op.expression.expressions.length;
|
|
10958
|
-
}
|
|
10959
|
-
return slots;
|
|
10960
|
-
case OpKind.InterpolateText:
|
|
10961
|
-
// `ir.InterpolateTextOp`s use a variable slot for each dynamic expression.
|
|
10962
|
-
return op.interpolation.expressions.length;
|
|
10963
|
-
case OpKind.I18nExpression:
|
|
10964
|
-
case OpKind.Conditional:
|
|
10965
|
-
return 1;
|
|
10966
|
-
default:
|
|
10967
|
-
throw new Error(`Unhandled op: ${OpKind[op.kind]}`);
|
|
10968
|
-
}
|
|
10969
|
-
}
|
|
10970
|
-
function varsUsedByIrExpression(expr) {
|
|
10971
|
-
switch (expr.kind) {
|
|
10972
|
-
case ExpressionKind.PureFunctionExpr:
|
|
10973
|
-
return 1 + expr.args.length;
|
|
10974
|
-
case ExpressionKind.PipeBinding:
|
|
10975
|
-
return 1 + expr.args.length;
|
|
10976
|
-
case ExpressionKind.PipeBindingVariadic:
|
|
10977
|
-
return 1 + expr.numArgs;
|
|
10978
|
-
default:
|
|
10979
|
-
throw new Error(`AssertionError: unhandled ConsumesVarsTrait expression ${expr.constructor.name}`);
|
|
10980
|
-
}
|
|
10981
|
-
}
|
|
10982
|
-
|
|
10983
|
-
function phaseAlignPipeVariadicVarOffset(job) {
|
|
10984
|
-
for (const unit of job.units) {
|
|
10985
|
-
for (const op of unit.update) {
|
|
10986
|
-
visitExpressionsInOp(op, expr => {
|
|
10987
|
-
if (!(expr instanceof PipeBindingVariadicExpr)) {
|
|
10988
|
-
return expr;
|
|
10989
|
-
}
|
|
10990
|
-
if (!(expr.args instanceof PureFunctionExpr)) {
|
|
10991
|
-
return expr;
|
|
10992
|
-
}
|
|
10993
|
-
if (expr.varOffset === null || expr.args.varOffset === null) {
|
|
10994
|
-
throw new Error(`Must run after variable counting`);
|
|
10995
|
-
}
|
|
10996
|
-
// The structure of this variadic pipe expression is:
|
|
10997
|
-
// PipeBindingVariadic(#, Y, PureFunction(X, ...ARGS))
|
|
10998
|
-
// Where X and Y are the slot offsets for the variables used by these operations, and Y > X.
|
|
10999
|
-
// In `TemplateDefinitionBuilder` the PipeBindingVariadic variable slots are allocated
|
|
11000
|
-
// before the PureFunction slots, which is unusually out-of-order.
|
|
11001
|
-
//
|
|
11002
|
-
// To maintain identical output for the tests in question, we adjust the variable offsets of
|
|
11003
|
-
// these two calls to emulate TDB's behavior. This is not perfect, because the ARGS of the
|
|
11004
|
-
// PureFunction call may also allocate slots which by TDB's ordering would come after X, and
|
|
11005
|
-
// we don't account for that. Still, this should be enough to pass the existing pipe tests.
|
|
11006
|
-
// Put the PipeBindingVariadic vars where the PureFunction vars were previously allocated.
|
|
11007
|
-
expr.varOffset = expr.args.varOffset;
|
|
11008
|
-
// Put the PureFunction vars following the PipeBindingVariadic vars.
|
|
11009
|
-
expr.args.varOffset = expr.varOffset + varsUsedByIrExpression(expr);
|
|
11010
|
-
return undefined;
|
|
11011
|
-
});
|
|
11012
|
-
}
|
|
11013
|
-
}
|
|
11014
|
-
}
|
|
11015
|
-
|
|
11016
11071
|
/**
|
|
11017
11072
|
* Find any function calls to `$any`, excluding `this.$any`, and delete them.
|
|
11018
11073
|
*/
|
|
@@ -11088,18 +11143,41 @@ function phaseAssignI18nSlotDependencies(job) {
|
|
|
11088
11143
|
}
|
|
11089
11144
|
}
|
|
11090
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];
|
|
11164
|
+
}
|
|
11165
|
+
}
|
|
11166
|
+
}
|
|
11167
|
+
}
|
|
11168
|
+
|
|
11091
11169
|
/**
|
|
11092
11170
|
* Gets a map of all elements in the given view by their xref id.
|
|
11093
11171
|
*/
|
|
11094
|
-
function
|
|
11095
|
-
const
|
|
11172
|
+
function createOpXrefMap(unit) {
|
|
11173
|
+
const map = new Map();
|
|
11096
11174
|
for (const op of unit.create) {
|
|
11097
|
-
if (!
|
|
11175
|
+
if (!hasConsumesSlotTrait(op)) {
|
|
11098
11176
|
continue;
|
|
11099
11177
|
}
|
|
11100
|
-
|
|
11178
|
+
map.set(op.xref, op);
|
|
11101
11179
|
}
|
|
11102
|
-
return
|
|
11180
|
+
return map;
|
|
11103
11181
|
}
|
|
11104
11182
|
|
|
11105
11183
|
/**
|
|
@@ -11108,7 +11186,7 @@ function getElementsByXrefId(unit) {
|
|
|
11108
11186
|
*/
|
|
11109
11187
|
function phaseAttributeExtraction(job) {
|
|
11110
11188
|
for (const unit of job.units) {
|
|
11111
|
-
const elements =
|
|
11189
|
+
const elements = createOpXrefMap(unit);
|
|
11112
11190
|
for (const op of unit.ops()) {
|
|
11113
11191
|
switch (op.kind) {
|
|
11114
11192
|
case OpKind.Attribute:
|
|
@@ -11116,7 +11194,7 @@ function phaseAttributeExtraction(job) {
|
|
|
11116
11194
|
break;
|
|
11117
11195
|
case OpKind.Property:
|
|
11118
11196
|
if (!op.isAnimationTrigger) {
|
|
11119
|
-
OpList.insertBefore(createExtractedAttributeOp(op.target, op.isTemplate ? BindingKind.Template : BindingKind.Property, op.name, null), lookupElement$
|
|
11197
|
+
OpList.insertBefore(createExtractedAttributeOp(op.target, op.isTemplate ? BindingKind.Template : BindingKind.Property, op.name, null), lookupElement$2(elements, op.target));
|
|
11120
11198
|
}
|
|
11121
11199
|
break;
|
|
11122
11200
|
case OpKind.StyleProp:
|
|
@@ -11126,7 +11204,7 @@ function phaseAttributeExtraction(job) {
|
|
|
11126
11204
|
// mode.
|
|
11127
11205
|
if (unit.job.compatibility === CompatibilityMode.TemplateDefinitionBuilder &&
|
|
11128
11206
|
op.expression instanceof EmptyExpr) {
|
|
11129
|
-
OpList.insertBefore(createExtractedAttributeOp(op.target, BindingKind.Property, op.name, null), lookupElement$
|
|
11207
|
+
OpList.insertBefore(createExtractedAttributeOp(op.target, BindingKind.Property, op.name, null), lookupElement$2(elements, op.target));
|
|
11130
11208
|
}
|
|
11131
11209
|
break;
|
|
11132
11210
|
case OpKind.Listener:
|
|
@@ -11138,7 +11216,7 @@ function phaseAttributeExtraction(job) {
|
|
|
11138
11216
|
unit.create.push(extractedAttributeOp);
|
|
11139
11217
|
}
|
|
11140
11218
|
else {
|
|
11141
|
-
OpList.insertBefore(extractedAttributeOp, lookupElement$
|
|
11219
|
+
OpList.insertBefore(extractedAttributeOp, lookupElement$2(elements, op.target));
|
|
11142
11220
|
}
|
|
11143
11221
|
}
|
|
11144
11222
|
break;
|
|
@@ -11149,7 +11227,7 @@ function phaseAttributeExtraction(job) {
|
|
|
11149
11227
|
/**
|
|
11150
11228
|
* Looks up an element in the given map by xref ID.
|
|
11151
11229
|
*/
|
|
11152
|
-
function lookupElement$
|
|
11230
|
+
function lookupElement$2(elements, xref) {
|
|
11153
11231
|
const el = elements.get(xref);
|
|
11154
11232
|
if (el === undefined) {
|
|
11155
11233
|
throw new Error('All attributes should have an element-like target.');
|
|
@@ -11187,7 +11265,7 @@ function extractAttributeOp(unit, op, elements) {
|
|
|
11187
11265
|
unit.create.push(extractedAttributeOp);
|
|
11188
11266
|
}
|
|
11189
11267
|
else {
|
|
11190
|
-
const ownerOp = lookupElement$
|
|
11268
|
+
const ownerOp = lookupElement$2(elements, op.target);
|
|
11191
11269
|
OpList.insertBefore(extractedAttributeOp, ownerOp);
|
|
11192
11270
|
}
|
|
11193
11271
|
OpList.remove(op);
|
|
@@ -11197,7 +11275,7 @@ function extractAttributeOp(unit, op, elements) {
|
|
|
11197
11275
|
/**
|
|
11198
11276
|
* Looks up an element in the given map by xref ID.
|
|
11199
11277
|
*/
|
|
11200
|
-
function lookupElement$
|
|
11278
|
+
function lookupElement$1(elements, xref) {
|
|
11201
11279
|
const el = elements.get(xref);
|
|
11202
11280
|
if (el === undefined) {
|
|
11203
11281
|
throw new Error('All attributes should have an element-like target.');
|
|
@@ -11223,7 +11301,7 @@ function phaseBindingSpecialization(job) {
|
|
|
11223
11301
|
case BindingKind.Attribute:
|
|
11224
11302
|
if (op.name === 'ngNonBindable') {
|
|
11225
11303
|
OpList.remove(op);
|
|
11226
|
-
const target = lookupElement$
|
|
11304
|
+
const target = lookupElement$1(elements, op.target);
|
|
11227
11305
|
target.nonBindable = true;
|
|
11228
11306
|
}
|
|
11229
11307
|
else {
|
|
@@ -11383,6 +11461,9 @@ function phaseConditionals(job) {
|
|
|
11383
11461
|
}
|
|
11384
11462
|
// Save the resulting aggregate Joost-expression.
|
|
11385
11463
|
op.processed = test;
|
|
11464
|
+
// Clear the original conditions array, since we no longer need it, and don't want it to
|
|
11465
|
+
// affect subsequent phases (e.g. pipe creation).
|
|
11466
|
+
op.conditions = [];
|
|
11386
11467
|
}
|
|
11387
11468
|
}
|
|
11388
11469
|
}
|
|
@@ -11834,6 +11915,37 @@ function ternaryTransform(e) {
|
|
|
11834
11915
|
return new ConditionalExpr(new BinaryOperatorExpr(BinaryOperator.Equals, e.guard, NULL_EXPR), NULL_EXPR, e.expr);
|
|
11835
11916
|
}
|
|
11836
11917
|
|
|
11918
|
+
function phaseRepeaterDerivedVars(job) {
|
|
11919
|
+
const repeaters = new Map();
|
|
11920
|
+
for (const unit of job.units) {
|
|
11921
|
+
for (const op of unit.ops()) {
|
|
11922
|
+
if (op.kind === OpKind.RepeaterCreate) {
|
|
11923
|
+
repeaters.set(op.xref, op);
|
|
11924
|
+
}
|
|
11925
|
+
}
|
|
11926
|
+
}
|
|
11927
|
+
for (const unit of job.units) {
|
|
11928
|
+
for (const op of unit.ops()) {
|
|
11929
|
+
transformExpressionsInOp(op, expr => {
|
|
11930
|
+
if (!(expr instanceof DerivedRepeaterVarExpr)) {
|
|
11931
|
+
return expr;
|
|
11932
|
+
}
|
|
11933
|
+
const repeaterOp = repeaters.get(expr.xref);
|
|
11934
|
+
switch (expr.identity) {
|
|
11935
|
+
case DerivedRepeaterVarIdentity.First:
|
|
11936
|
+
return new BinaryOperatorExpr(BinaryOperator.Identical, new LexicalReadExpr(repeaterOp.varNames.$index), literal(0));
|
|
11937
|
+
case DerivedRepeaterVarIdentity.Last:
|
|
11938
|
+
return new BinaryOperatorExpr(BinaryOperator.Identical, new LexicalReadExpr(repeaterOp.varNames.$index), new BinaryOperatorExpr(BinaryOperator.Minus, new LexicalReadExpr(repeaterOp.varNames.$count), literal(1)));
|
|
11939
|
+
case DerivedRepeaterVarIdentity.Even:
|
|
11940
|
+
return new BinaryOperatorExpr(BinaryOperator.Identical, new BinaryOperatorExpr(BinaryOperator.Modulo, new LexicalReadExpr(repeaterOp.varNames.$index), literal(2)), literal(0));
|
|
11941
|
+
case DerivedRepeaterVarIdentity.Odd:
|
|
11942
|
+
return new BinaryOperatorExpr(BinaryOperator.NotIdentical, new BinaryOperatorExpr(BinaryOperator.Modulo, new LexicalReadExpr(repeaterOp.varNames.$index), literal(2)), literal(0));
|
|
11943
|
+
}
|
|
11944
|
+
}, VisitorContextFlag.None);
|
|
11945
|
+
}
|
|
11946
|
+
}
|
|
11947
|
+
}
|
|
11948
|
+
|
|
11837
11949
|
/**
|
|
11838
11950
|
* Generate `ir.AdvanceOp`s in between `ir.UpdateOp`s that ensure the runtime's implicit slot
|
|
11839
11951
|
* context will be advanced correctly.
|
|
@@ -11948,6 +12060,7 @@ function recursivelyProcessView(view, parentScope) {
|
|
|
11948
12060
|
for (const op of view.create) {
|
|
11949
12061
|
switch (op.kind) {
|
|
11950
12062
|
case OpKind.Template:
|
|
12063
|
+
case OpKind.RepeaterCreate:
|
|
11951
12064
|
// Descend into child embedded views.
|
|
11952
12065
|
recursivelyProcessView(view.job.views.get(op.xref), scope);
|
|
11953
12066
|
break;
|
|
@@ -11974,6 +12087,7 @@ function getScopeForView(view, parent) {
|
|
|
11974
12087
|
view: view.xref,
|
|
11975
12088
|
},
|
|
11976
12089
|
contextVariables: new Map(),
|
|
12090
|
+
aliases: view.aliases,
|
|
11977
12091
|
references: [],
|
|
11978
12092
|
parent,
|
|
11979
12093
|
};
|
|
@@ -11986,7 +12100,6 @@ function getScopeForView(view, parent) {
|
|
|
11986
12100
|
}
|
|
11987
12101
|
for (const op of view.create) {
|
|
11988
12102
|
switch (op.kind) {
|
|
11989
|
-
case OpKind.Element:
|
|
11990
12103
|
case OpKind.ElementStart:
|
|
11991
12104
|
case OpKind.Template:
|
|
11992
12105
|
if (!Array.isArray(op.localRefs)) {
|
|
@@ -12022,19 +12135,23 @@ function generateVariablesInScopeForView(view, scope) {
|
|
|
12022
12135
|
// Before generating variables for a parent view, we need to switch to the context of the parent
|
|
12023
12136
|
// view with a `nextContext` expression. This context switching operation itself declares a
|
|
12024
12137
|
// variable, because the context of the view may be referenced directly.
|
|
12025
|
-
newOps.push(createVariableOp(view.job.allocateXrefId(), scope.viewContextVariable, new NextContextExpr()));
|
|
12138
|
+
newOps.push(createVariableOp(view.job.allocateXrefId(), scope.viewContextVariable, new NextContextExpr(), VariableFlags.None));
|
|
12026
12139
|
}
|
|
12027
12140
|
// Add variables for all context variables available in this scope's view.
|
|
12028
|
-
|
|
12141
|
+
const scopeView = view.job.views.get(scope.view);
|
|
12142
|
+
for (const [name, value] of scopeView.contextVariables) {
|
|
12029
12143
|
const context = new ContextExpr(scope.view);
|
|
12030
12144
|
// We either read the context, or, if the variable is CTX_REF, use the context directly.
|
|
12031
12145
|
const variable = value === CTX_REF ? context : new ReadPropExpr(context, value);
|
|
12032
12146
|
// Add the variable declaration.
|
|
12033
|
-
newOps.push(createVariableOp(view.job.allocateXrefId(), scope.contextVariables.get(name), variable));
|
|
12147
|
+
newOps.push(createVariableOp(view.job.allocateXrefId(), scope.contextVariables.get(name), variable, VariableFlags.None));
|
|
12148
|
+
}
|
|
12149
|
+
for (const alias of scopeView.aliases) {
|
|
12150
|
+
newOps.push(createVariableOp(view.job.allocateXrefId(), alias, alias.expression.clone(), VariableFlags.AlwaysInline));
|
|
12034
12151
|
}
|
|
12035
12152
|
// Add variables for all local references declared for elements in this scope.
|
|
12036
12153
|
for (const ref of scope.references) {
|
|
12037
|
-
newOps.push(createVariableOp(view.job.allocateXrefId(), ref.variable, new ReferenceExpr(ref.targetId, ref.offset)));
|
|
12154
|
+
newOps.push(createVariableOp(view.job.allocateXrefId(), ref.variable, new ReferenceExpr(ref.targetId, ref.offset), VariableFlags.None));
|
|
12038
12155
|
}
|
|
12039
12156
|
if (scope.parent !== null) {
|
|
12040
12157
|
// Recursively add variables from the parent scope.
|
|
@@ -12154,7 +12271,7 @@ function phaseI18nConstCollection(job) {
|
|
|
12154
12271
|
// Assign const index to i18n ops that messages were extracted from.
|
|
12155
12272
|
for (const unit of job.units) {
|
|
12156
12273
|
for (const op of unit.create) {
|
|
12157
|
-
if (op.kind === OpKind.I18nStart
|
|
12274
|
+
if (op.kind === OpKind.I18nStart) {
|
|
12158
12275
|
op.messageIndex = messageConstIndices[op.root];
|
|
12159
12276
|
}
|
|
12160
12277
|
}
|
|
@@ -13916,11 +14033,12 @@ class Comment {
|
|
|
13916
14033
|
}
|
|
13917
14034
|
}
|
|
13918
14035
|
class Block {
|
|
13919
|
-
constructor(name, parameters, children, sourceSpan, startSourceSpan, endSourceSpan = null) {
|
|
14036
|
+
constructor(name, parameters, children, sourceSpan, nameSpan, startSourceSpan, endSourceSpan = null) {
|
|
13920
14037
|
this.name = name;
|
|
13921
14038
|
this.parameters = parameters;
|
|
13922
14039
|
this.children = children;
|
|
13923
14040
|
this.sourceSpan = sourceSpan;
|
|
14041
|
+
this.nameSpan = nameSpan;
|
|
13924
14042
|
this.startSourceSpan = startSourceSpan;
|
|
13925
14043
|
this.endSourceSpan = endSourceSpan;
|
|
13926
14044
|
}
|
|
@@ -14451,7 +14569,7 @@ let TAG_DEFINITIONS;
|
|
|
14451
14569
|
function getHtmlTagDefinition(tagName) {
|
|
14452
14570
|
if (!TAG_DEFINITIONS) {
|
|
14453
14571
|
DEFAULT_TAG_DEFINITION = new HtmlTagDefinition({ canSelfClose: true });
|
|
14454
|
-
TAG_DEFINITIONS = {
|
|
14572
|
+
TAG_DEFINITIONS = Object.assign(Object.create(null), {
|
|
14455
14573
|
'base': new HtmlTagDefinition({ isVoid: true }),
|
|
14456
14574
|
'meta': new HtmlTagDefinition({ isVoid: true }),
|
|
14457
14575
|
'area': new HtmlTagDefinition({ isVoid: true }),
|
|
@@ -14513,9 +14631,9 @@ function getHtmlTagDefinition(tagName) {
|
|
|
14513
14631
|
contentType: { default: TagContentType.ESCAPABLE_RAW_TEXT, svg: TagContentType.PARSABLE_DATA }
|
|
14514
14632
|
}),
|
|
14515
14633
|
'textarea': new HtmlTagDefinition({ contentType: TagContentType.ESCAPABLE_RAW_TEXT, ignoreFirstLf: true }),
|
|
14516
|
-
};
|
|
14634
|
+
});
|
|
14517
14635
|
new DomElementSchemaRegistry().allKnownElementNames().forEach(knownTagName => {
|
|
14518
|
-
if (!TAG_DEFINITIONS
|
|
14636
|
+
if (!TAG_DEFINITIONS[knownTagName] && getNsPrefix(knownTagName) === null) {
|
|
14519
14637
|
TAG_DEFINITIONS[knownTagName] = new HtmlTagDefinition({ canSelfClose: false });
|
|
14520
14638
|
}
|
|
14521
14639
|
});
|
|
@@ -17734,7 +17852,7 @@ class _Tokenizer {
|
|
|
17734
17852
|
}
|
|
17735
17853
|
}
|
|
17736
17854
|
if (this._tokenizeBlocks && !this._inInterpolation && !this._isInExpansion() &&
|
|
17737
|
-
(this.
|
|
17855
|
+
(this._cursor.peek() === $AT || this._cursor.peek() === $RBRACE)) {
|
|
17738
17856
|
return true;
|
|
17739
17857
|
}
|
|
17740
17858
|
return false;
|
|
@@ -17757,17 +17875,6 @@ class _Tokenizer {
|
|
|
17757
17875
|
}
|
|
17758
17876
|
return false;
|
|
17759
17877
|
}
|
|
17760
|
-
_isBlockStart() {
|
|
17761
|
-
if (this._tokenizeBlocks && this._cursor.peek() === $AT) {
|
|
17762
|
-
const tmp = this._cursor.clone();
|
|
17763
|
-
// If it is, also verify that the next character is a valid block identifier.
|
|
17764
|
-
tmp.advance();
|
|
17765
|
-
if (isBlockNameChar(tmp.peek())) {
|
|
17766
|
-
return true;
|
|
17767
|
-
}
|
|
17768
|
-
}
|
|
17769
|
-
return false;
|
|
17770
|
-
}
|
|
17771
17878
|
_readUntil(char) {
|
|
17772
17879
|
const start = this._cursor.clone();
|
|
17773
17880
|
this._attemptUntilChar(char);
|
|
@@ -18474,7 +18581,7 @@ class _TreeBuilder {
|
|
|
18474
18581
|
const span = new ParseSourceSpan(token.sourceSpan.start, end, token.sourceSpan.fullStart);
|
|
18475
18582
|
// Create a separate `startSpan` because `span` will be modified when there is an `end` span.
|
|
18476
18583
|
const startSpan = new ParseSourceSpan(token.sourceSpan.start, end, token.sourceSpan.fullStart);
|
|
18477
|
-
const block = new Block(token.parts[0], parameters, [], span, startSpan);
|
|
18584
|
+
const block = new Block(token.parts[0], parameters, [], span, token.sourceSpan, startSpan);
|
|
18478
18585
|
this._pushContainer(block, false);
|
|
18479
18586
|
}
|
|
18480
18587
|
_consumeBlockClose(token) {
|
|
@@ -18494,7 +18601,7 @@ class _TreeBuilder {
|
|
|
18494
18601
|
const span = new ParseSourceSpan(token.sourceSpan.start, end, token.sourceSpan.fullStart);
|
|
18495
18602
|
// Create a separate `startSpan` because `span` will be modified when there is an `end` span.
|
|
18496
18603
|
const startSpan = new ParseSourceSpan(token.sourceSpan.start, end, token.sourceSpan.fullStart);
|
|
18497
|
-
const block = new Block(token.parts[0], parameters, [], span, startSpan);
|
|
18604
|
+
const block = new Block(token.parts[0], parameters, [], span, token.sourceSpan, startSpan);
|
|
18498
18605
|
this._pushContainer(block, false);
|
|
18499
18606
|
// Incomplete blocks don't have children so we close them immediately and report an error.
|
|
18500
18607
|
this._popContainer(null, Block, null);
|
|
@@ -19053,7 +19160,7 @@ function phaseI18nMessageExtraction(job) {
|
|
|
19053
19160
|
const fileBasedI18nSuffix = job.relativeContextFilePath.replace(/[^A-Za-z0-9]/g, '_').toUpperCase() + '_';
|
|
19054
19161
|
for (const unit of job.units) {
|
|
19055
19162
|
for (const op of unit.create) {
|
|
19056
|
-
if (
|
|
19163
|
+
if (op.kind === OpKind.I18nStart) {
|
|
19057
19164
|
// Only extract messages from root i18n ops, not sub-template ones.
|
|
19058
19165
|
if (op.xref === op.root) {
|
|
19059
19166
|
// Sort the params map to match the ordering in TemplateDefinitionBuilder.
|
|
@@ -19063,8 +19170,17 @@ function phaseI18nMessageExtraction(job) {
|
|
|
19063
19170
|
// const to start with `MSG_`. We define a variable starting with `MSG_` just for the
|
|
19064
19171
|
// `goog.getMsg` call
|
|
19065
19172
|
const closureVar = i18nGenerateClosureVar(job.pool, op.message.id, fileBasedI18nSuffix, job.i18nUseExternalIds);
|
|
19066
|
-
|
|
19067
|
-
|
|
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);
|
|
19068
19184
|
unit.create.push(createExtractedMessageOp(op.xref, mainVar, statements));
|
|
19069
19185
|
}
|
|
19070
19186
|
}
|
|
@@ -19177,7 +19293,7 @@ function phaseI18nTextExtraction(job) {
|
|
|
19177
19293
|
for (let i = 0; i < op.interpolation.expressions.length; i++) {
|
|
19178
19294
|
const expr = op.interpolation.expressions[i];
|
|
19179
19295
|
const placeholder = op.i18nPlaceholders[i];
|
|
19180
|
-
ops.push(createI18nExpressionOp(i18nBlockId, expr, placeholder, expr.sourceSpan ?? op.sourceSpan));
|
|
19296
|
+
ops.push(createI18nExpressionOp(i18nBlockId, expr, placeholder.name, I18nParamResolutionTime.Creation, expr.sourceSpan ?? op.sourceSpan));
|
|
19181
19297
|
}
|
|
19182
19298
|
if (ops.length > 0) {
|
|
19183
19299
|
// ops.push(ir.createI18nApplyOp(i18nBlockId, op.i18nPlaceholders, op.sourceSpan));
|
|
@@ -19189,6 +19305,50 @@ function phaseI18nTextExtraction(job) {
|
|
|
19189
19305
|
}
|
|
19190
19306
|
}
|
|
19191
19307
|
|
|
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
|
+
|
|
19192
19352
|
/**
|
|
19193
19353
|
* Lifts local reference declarations on element-like structures within each view into an entry in
|
|
19194
19354
|
* the `consts` array for the whole component.
|
|
@@ -19198,7 +19358,6 @@ function phaseLocalRefs(job) {
|
|
|
19198
19358
|
for (const op of unit.create) {
|
|
19199
19359
|
switch (op.kind) {
|
|
19200
19360
|
case OpKind.ElementStart:
|
|
19201
|
-
case OpKind.Element:
|
|
19202
19361
|
case OpKind.Template:
|
|
19203
19362
|
if (!Array.isArray(op.localRefs)) {
|
|
19204
19363
|
throw new Error(`AssertionError: expected localRefs to be an array still`);
|
|
@@ -19231,7 +19390,7 @@ function phaseNamespace(job) {
|
|
|
19231
19390
|
for (const unit of job.units) {
|
|
19232
19391
|
let activeNamespace = Namespace.HTML;
|
|
19233
19392
|
for (const op of unit.create) {
|
|
19234
|
-
if (op.kind !== OpKind.
|
|
19393
|
+
if (op.kind !== OpKind.ElementStart) {
|
|
19235
19394
|
continue;
|
|
19236
19395
|
}
|
|
19237
19396
|
if (op.namespace !== activeNamespace) {
|
|
@@ -19368,6 +19527,22 @@ function addNamesToView(unit, baseName, state, compatibility) {
|
|
|
19368
19527
|
case OpKind.Variable:
|
|
19369
19528
|
varNames.set(op.xref, getVariableName(op.variable, state));
|
|
19370
19529
|
break;
|
|
19530
|
+
case OpKind.RepeaterCreate:
|
|
19531
|
+
if (!(unit instanceof ViewCompilationUnit)) {
|
|
19532
|
+
throw new Error(`AssertionError: must be compiling a component`);
|
|
19533
|
+
}
|
|
19534
|
+
if (op.slot === null) {
|
|
19535
|
+
throw new Error(`Expected slot to be assigned`);
|
|
19536
|
+
}
|
|
19537
|
+
if (op.emptyView !== null) {
|
|
19538
|
+
const emptyView = unit.job.views.get(op.emptyView);
|
|
19539
|
+
// Repeater empty view function is at slot +2 (metadata is in the first slot).
|
|
19540
|
+
addNamesToView(emptyView, `${baseName}_${prefixWithNamespace(`${op.tag}Empty`, op.namespace)}_${op.slot + 2}`, state, compatibility);
|
|
19541
|
+
}
|
|
19542
|
+
const repeaterToken = op.tag === null ? '' : '_' + prefixWithNamespace(op.tag, op.namespace);
|
|
19543
|
+
// Repeater primary view function is at slot +1 (metadata is in the first slot).
|
|
19544
|
+
addNamesToView(unit.job.views.get(op.xref), `${baseName}${repeaterToken}_${op.slot + 1}`, state, compatibility);
|
|
19545
|
+
break;
|
|
19371
19546
|
case OpKind.Template:
|
|
19372
19547
|
if (!(unit instanceof ViewCompilationUnit)) {
|
|
19373
19548
|
throw new Error(`AssertionError: must be compiling a component`);
|
|
@@ -19376,7 +19551,8 @@ function addNamesToView(unit, baseName, state, compatibility) {
|
|
|
19376
19551
|
if (op.slot === null) {
|
|
19377
19552
|
throw new Error(`Expected slot to be assigned`);
|
|
19378
19553
|
}
|
|
19379
|
-
|
|
19554
|
+
const tagToken = op.tag === null ? '' : '_' + prefixWithNamespace(op.tag, op.namespace);
|
|
19555
|
+
addNamesToView(childView, `${baseName}${tagToken}_${op.slot}`, state, compatibility);
|
|
19380
19556
|
break;
|
|
19381
19557
|
case OpKind.StyleProp:
|
|
19382
19558
|
op.name = normalizeStylePropName(op.name);
|
|
@@ -19412,10 +19588,12 @@ function getVariableName(variable, state) {
|
|
|
19412
19588
|
variable.name = `ctx_r${state.index++}`;
|
|
19413
19589
|
break;
|
|
19414
19590
|
case SemanticVariableKind.Identifier:
|
|
19415
|
-
|
|
19591
|
+
// TODO: Prefix increment and `_r` for compatiblity only.
|
|
19592
|
+
variable.name = `${variable.identifier}_r${++state.index}`;
|
|
19416
19593
|
break;
|
|
19417
19594
|
default:
|
|
19418
|
-
|
|
19595
|
+
// TODO: Prefix increment for compatibility only.
|
|
19596
|
+
variable.name = `_r${++state.index}`;
|
|
19419
19597
|
break;
|
|
19420
19598
|
}
|
|
19421
19599
|
}
|
|
@@ -19510,11 +19688,6 @@ function phaseNgContainer(job) {
|
|
|
19510
19688
|
for (const unit of job.units) {
|
|
19511
19689
|
const updatedElementXrefs = new Set();
|
|
19512
19690
|
for (const op of unit.create) {
|
|
19513
|
-
if (op.kind === OpKind.Element && op.tag === CONTAINER_TAG) {
|
|
19514
|
-
// Transmute the `Element` instruction to `Container`.
|
|
19515
|
-
op.kind = OpKind.Container;
|
|
19516
|
-
updatedElementXrefs.add(op.xref);
|
|
19517
|
-
}
|
|
19518
19691
|
if (op.kind === OpKind.ElementStart && op.tag === CONTAINER_TAG) {
|
|
19519
19692
|
// Transmute the `ElementStart` instruction to `ContainerStart`.
|
|
19520
19693
|
op.kind = OpKind.ContainerStart;
|
|
@@ -19531,7 +19704,7 @@ function phaseNgContainer(job) {
|
|
|
19531
19704
|
/**
|
|
19532
19705
|
* Looks up an element in the given map by xref ID.
|
|
19533
19706
|
*/
|
|
19534
|
-
function lookupElement
|
|
19707
|
+
function lookupElement(elements, xref) {
|
|
19535
19708
|
const el = elements.get(xref);
|
|
19536
19709
|
if (el === undefined) {
|
|
19537
19710
|
throw new Error('All attributes should have an element-like target.');
|
|
@@ -19560,7 +19733,7 @@ function phaseNonbindable(job) {
|
|
|
19560
19733
|
OpList.insertAfter(createDisableBindingsOp(op.xref), op);
|
|
19561
19734
|
}
|
|
19562
19735
|
if ((op.kind === OpKind.ElementEnd || op.kind === OpKind.ContainerEnd) &&
|
|
19563
|
-
lookupElement
|
|
19736
|
+
lookupElement(elements, op.xref).nonBindable) {
|
|
19564
19737
|
OpList.insertBefore(createEnableBindingsOp(op.xref), op);
|
|
19565
19738
|
}
|
|
19566
19739
|
}
|
|
@@ -19588,9 +19761,14 @@ function phaseNullishCoalescing(job) {
|
|
|
19588
19761
|
function kindTest(kind) {
|
|
19589
19762
|
return (op) => op.kind === kind;
|
|
19590
19763
|
}
|
|
19764
|
+
function kindWithInterpolationTest(kind, interpolation) {
|
|
19765
|
+
return (op) => {
|
|
19766
|
+
return op.kind === kind && interpolation === op.expression instanceof Interpolation;
|
|
19767
|
+
};
|
|
19768
|
+
}
|
|
19591
19769
|
/**
|
|
19592
19770
|
* Defines the groups based on `OpKind` that ops will be divided into, for the various create
|
|
19593
|
-
*
|
|
19771
|
+
* op kinds. Ops will be collected into groups, then optionally transformed, before recombining
|
|
19594
19772
|
* the groups in the order defined here.
|
|
19595
19773
|
*/
|
|
19596
19774
|
const CREATE_ORDERING = [
|
|
@@ -19598,18 +19776,20 @@ const CREATE_ORDERING = [
|
|
|
19598
19776
|
{ test: op => op.kind === OpKind.Listener && !(op.hostListener && op.isAnimationListener) },
|
|
19599
19777
|
];
|
|
19600
19778
|
/**
|
|
19601
|
-
*
|
|
19779
|
+
* Defines the groups based on `OpKind` that ops will be divided into, for the various update
|
|
19780
|
+
* op kinds.
|
|
19602
19781
|
*/
|
|
19603
19782
|
const UPDATE_ORDERING = [
|
|
19604
|
-
{ test:
|
|
19605
|
-
{ test:
|
|
19783
|
+
{ test: kindWithInterpolationTest(OpKind.HostProperty, true) },
|
|
19784
|
+
{ test: kindWithInterpolationTest(OpKind.HostProperty, false) },
|
|
19606
19785
|
{ test: kindTest(OpKind.StyleMap), transform: keepLast },
|
|
19607
19786
|
{ test: kindTest(OpKind.ClassMap), transform: keepLast },
|
|
19608
19787
|
{ test: kindTest(OpKind.StyleProp) },
|
|
19609
19788
|
{ test: kindTest(OpKind.ClassProp) },
|
|
19610
|
-
{ test:
|
|
19611
|
-
{ test:
|
|
19612
|
-
{ test:
|
|
19789
|
+
{ test: kindWithInterpolationTest(OpKind.Property, true) },
|
|
19790
|
+
{ test: kindWithInterpolationTest(OpKind.Attribute, true) },
|
|
19791
|
+
{ test: kindWithInterpolationTest(OpKind.Property, false) },
|
|
19792
|
+
{ test: kindWithInterpolationTest(OpKind.Attribute, false) },
|
|
19613
19793
|
];
|
|
19614
19794
|
/**
|
|
19615
19795
|
* The set of all op kinds we handle in the reordering phase.
|
|
@@ -19624,32 +19804,34 @@ function phaseOrdering(job) {
|
|
|
19624
19804
|
// be reordered, put the ones we've pulled so far back in the correct order. Finally, if we
|
|
19625
19805
|
// still have ops pulled at the end, put them back in the correct order.
|
|
19626
19806
|
// Create mode:
|
|
19627
|
-
|
|
19628
|
-
for (const op of unit.create) {
|
|
19629
|
-
if (handledOpKinds.has(op.kind)) {
|
|
19630
|
-
opsToOrder.push(op);
|
|
19631
|
-
OpList.remove(op);
|
|
19632
|
-
}
|
|
19633
|
-
else {
|
|
19634
|
-
OpList.insertBefore(reorder(opsToOrder, CREATE_ORDERING), op);
|
|
19635
|
-
opsToOrder = [];
|
|
19636
|
-
}
|
|
19637
|
-
}
|
|
19638
|
-
unit.create.push(reorder(opsToOrder, CREATE_ORDERING));
|
|
19807
|
+
orderWithin(unit.create, CREATE_ORDERING);
|
|
19639
19808
|
// Update mode:
|
|
19640
|
-
|
|
19641
|
-
|
|
19642
|
-
|
|
19643
|
-
|
|
19644
|
-
|
|
19645
|
-
|
|
19646
|
-
|
|
19647
|
-
|
|
19648
|
-
|
|
19649
|
-
|
|
19809
|
+
orderWithin(unit.update, UPDATE_ORDERING);
|
|
19810
|
+
}
|
|
19811
|
+
}
|
|
19812
|
+
/**
|
|
19813
|
+
* Order all the ops within the specified group.
|
|
19814
|
+
*/
|
|
19815
|
+
function orderWithin(opList, ordering) {
|
|
19816
|
+
let opsToOrder = [];
|
|
19817
|
+
// Only reorder ops that target the same xref; do not mix ops that target different xrefs.
|
|
19818
|
+
let firstTargetInGroup = null;
|
|
19819
|
+
for (const op of opList) {
|
|
19820
|
+
const currentTarget = hasDependsOnSlotContextTrait(op) ? op.target : null;
|
|
19821
|
+
if (!handledOpKinds.has(op.kind) ||
|
|
19822
|
+
(currentTarget !== firstTargetInGroup &&
|
|
19823
|
+
(firstTargetInGroup !== null && currentTarget !== null))) {
|
|
19824
|
+
OpList.insertBefore(reorder(opsToOrder, ordering), op);
|
|
19825
|
+
opsToOrder = [];
|
|
19826
|
+
firstTargetInGroup = null;
|
|
19827
|
+
}
|
|
19828
|
+
if (handledOpKinds.has(op.kind)) {
|
|
19829
|
+
opsToOrder.push(op);
|
|
19830
|
+
OpList.remove(op);
|
|
19831
|
+
firstTargetInGroup = currentTarget ?? firstTargetInGroup;
|
|
19650
19832
|
}
|
|
19651
|
-
unit.update.push(reorder(opsToOrder, UPDATE_ORDERING));
|
|
19652
19833
|
}
|
|
19834
|
+
opList.push(reorder(opsToOrder, ordering));
|
|
19653
19835
|
}
|
|
19654
19836
|
/**
|
|
19655
19837
|
* Reorders the given list of ops according to the ordering defined by `ORDERING`.
|
|
@@ -19708,15 +19890,15 @@ function phaseParseExtractedStyles(cpl) {
|
|
|
19708
19890
|
*/
|
|
19709
19891
|
function phaseRemoveContentSelectors(job) {
|
|
19710
19892
|
for (const unit of job.units) {
|
|
19711
|
-
const elements =
|
|
19893
|
+
const elements = createOpXrefMap(unit);
|
|
19712
19894
|
for (const op of unit.update) {
|
|
19713
19895
|
switch (op.kind) {
|
|
19714
19896
|
case OpKind.Binding:
|
|
19715
|
-
const target =
|
|
19897
|
+
const target = lookupInXrefMap(elements, op.target);
|
|
19716
19898
|
if (op.name.toLowerCase() === 'select' && target.kind === OpKind.Projection) {
|
|
19717
19899
|
OpList.remove(op);
|
|
19718
19900
|
}
|
|
19719
|
-
|
|
19901
|
+
break;
|
|
19720
19902
|
}
|
|
19721
19903
|
}
|
|
19722
19904
|
}
|
|
@@ -19724,10 +19906,10 @@ function phaseRemoveContentSelectors(job) {
|
|
|
19724
19906
|
/**
|
|
19725
19907
|
* Looks up an element in the given map by xref ID.
|
|
19726
19908
|
*/
|
|
19727
|
-
function
|
|
19728
|
-
const el =
|
|
19909
|
+
function lookupInXrefMap(map, xref) {
|
|
19910
|
+
const el = map.get(xref);
|
|
19729
19911
|
if (el === undefined) {
|
|
19730
|
-
throw new Error('All attributes should have an
|
|
19912
|
+
throw new Error('All attributes should have an slottable target.');
|
|
19731
19913
|
}
|
|
19732
19914
|
return el;
|
|
19733
19915
|
}
|
|
@@ -19749,10 +19931,22 @@ function processPipeBindingsInView(unit) {
|
|
|
19749
19931
|
if (flags & VisitorContextFlag.InChildOperation) {
|
|
19750
19932
|
throw new Error(`AssertionError: pipe bindings should not appear in child expressions`);
|
|
19751
19933
|
}
|
|
19752
|
-
|
|
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)) {
|
|
19753
19939
|
throw new Error(`AssertionError: pipe binding associated with non-slot operation ${OpKind[updateOp.kind]}`);
|
|
19754
19940
|
}
|
|
19755
|
-
|
|
19941
|
+
if (unit.job.compatibility) {
|
|
19942
|
+
addPipeToCreationBlock(unit, updateOp.target, expr);
|
|
19943
|
+
}
|
|
19944
|
+
else {
|
|
19945
|
+
// When not in compatibility mode, we just add the pipe to the end of the create block. This
|
|
19946
|
+
// is not only simpler and faster, but allows more chaining opportunities for other
|
|
19947
|
+
// instructions.
|
|
19948
|
+
unit.create.push(createPipeOp(expr.target, expr.name));
|
|
19949
|
+
}
|
|
19756
19950
|
});
|
|
19757
19951
|
}
|
|
19758
19952
|
}
|
|
@@ -19847,37 +20041,6 @@ function wrapTemplateWithI18n(unit, parentI18n) {
|
|
|
19847
20041
|
}
|
|
19848
20042
|
}
|
|
19849
20043
|
|
|
19850
|
-
function phasePropagateI18nPlaceholders(job) {
|
|
19851
|
-
// Get all of the i18n ops.
|
|
19852
|
-
const i18nOps = new Map();
|
|
19853
|
-
for (const unit of job.units) {
|
|
19854
|
-
for (const op of unit.create) {
|
|
19855
|
-
if (op.kind === OpKind.I18nStart) {
|
|
19856
|
-
i18nOps.set(op.xref, op);
|
|
19857
|
-
}
|
|
19858
|
-
}
|
|
19859
|
-
}
|
|
19860
|
-
// Propagate i18n params from sub-templates up to the root i18n op.
|
|
19861
|
-
for (const op of i18nOps.values()) {
|
|
19862
|
-
if (op.xref !== op.root) {
|
|
19863
|
-
const rootOp = i18nOps.get(op.root);
|
|
19864
|
-
for (const [placeholder, value] of op.params) {
|
|
19865
|
-
rootOp.params.set(placeholder, value);
|
|
19866
|
-
}
|
|
19867
|
-
}
|
|
19868
|
-
}
|
|
19869
|
-
// Validate the root i18n ops have all placeholders filled in.
|
|
19870
|
-
for (const op of i18nOps.values()) {
|
|
19871
|
-
if (op.xref === op.root) {
|
|
19872
|
-
for (const placeholder in op.message.placeholders) {
|
|
19873
|
-
if (!op.params.has(placeholder)) {
|
|
19874
|
-
throw Error(`Failed to resolve i18n placeholder: ${placeholder}`);
|
|
19875
|
-
}
|
|
19876
|
-
}
|
|
19877
|
-
}
|
|
19878
|
-
}
|
|
19879
|
-
}
|
|
19880
|
-
|
|
19881
20044
|
function phasePureFunctionExtraction(job) {
|
|
19882
20045
|
for (const view of job.units) {
|
|
19883
20046
|
for (const op of view.ops()) {
|
|
@@ -19905,6 +20068,7 @@ class PureFunctionConstant extends GenericKeyFn {
|
|
|
19905
20068
|
return super.keyOf(expr);
|
|
19906
20069
|
}
|
|
19907
20070
|
}
|
|
20071
|
+
// TODO: Use the new pool method `getSharedFunctionReference`
|
|
19908
20072
|
toSharedConstantDeclaration(declName, keyExpr) {
|
|
19909
20073
|
const fnParams = [];
|
|
19910
20074
|
for (let idx = 0; idx < this.numArgs; idx++) {
|
|
@@ -20008,7 +20172,7 @@ function elementContainerEnd() {
|
|
|
20008
20172
|
}
|
|
20009
20173
|
function template(slot, templateFnRef, decls, vars, tag, constIndex, sourceSpan) {
|
|
20010
20174
|
const args = [literal(slot), templateFnRef, literal(decls), literal(vars)];
|
|
20011
|
-
if (tag !== null) {
|
|
20175
|
+
if (tag !== null || constIndex !== null) {
|
|
20012
20176
|
args.push(literal(tag));
|
|
20013
20177
|
if (constIndex !== null) {
|
|
20014
20178
|
args.push(literal(constIndex));
|
|
@@ -20104,15 +20268,15 @@ function deferOn(sourceSpan) {
|
|
|
20104
20268
|
function projectionDef(def) {
|
|
20105
20269
|
return call(Identifiers.projectionDef, def ? [def] : [], null);
|
|
20106
20270
|
}
|
|
20107
|
-
function projection(slot, projectionSlotIndex, attributes) {
|
|
20271
|
+
function projection(slot, projectionSlotIndex, attributes, sourceSpan) {
|
|
20108
20272
|
const args = [literal(slot)];
|
|
20109
|
-
if (projectionSlotIndex !== 0 || attributes
|
|
20273
|
+
if (projectionSlotIndex !== 0 || attributes.length > 0) {
|
|
20110
20274
|
args.push(literal(projectionSlotIndex));
|
|
20111
|
-
if (attributes
|
|
20112
|
-
args.push(literal(
|
|
20275
|
+
if (attributes.length > 0) {
|
|
20276
|
+
args.push(literalArr(attributes.map(attr => literal(attr))));
|
|
20113
20277
|
}
|
|
20114
20278
|
}
|
|
20115
|
-
return call(Identifiers.projection, args,
|
|
20279
|
+
return call(Identifiers.projection, args, sourceSpan);
|
|
20116
20280
|
}
|
|
20117
20281
|
function i18nStart(slot, constIndex, subTemplateIndex) {
|
|
20118
20282
|
const args = [literal(slot), literal(constIndex)];
|
|
@@ -20121,6 +20285,27 @@ function i18nStart(slot, constIndex, subTemplateIndex) {
|
|
|
20121
20285
|
}
|
|
20122
20286
|
return call(Identifiers.i18nStart, args, null);
|
|
20123
20287
|
}
|
|
20288
|
+
function repeaterCreate(slot, viewFnName, decls, vars, trackByFn, trackByUsesComponentInstance, emptyViewFnName, emptyDecls, emptyVars, sourceSpan) {
|
|
20289
|
+
let args = [
|
|
20290
|
+
literal(slot),
|
|
20291
|
+
variable(viewFnName),
|
|
20292
|
+
literal(decls),
|
|
20293
|
+
literal(vars),
|
|
20294
|
+
trackByFn,
|
|
20295
|
+
];
|
|
20296
|
+
if (trackByUsesComponentInstance || emptyViewFnName !== null) {
|
|
20297
|
+
args.push(literal(trackByUsesComponentInstance));
|
|
20298
|
+
if (emptyViewFnName !== null) {
|
|
20299
|
+
args.push(variable(emptyViewFnName));
|
|
20300
|
+
args.push(literal(emptyDecls));
|
|
20301
|
+
args.push(literal(emptyVars));
|
|
20302
|
+
}
|
|
20303
|
+
}
|
|
20304
|
+
return call(Identifiers.repeaterCreate, args, sourceSpan);
|
|
20305
|
+
}
|
|
20306
|
+
function repeater(metadataSlot, collection, sourceSpan) {
|
|
20307
|
+
return call(Identifiers.repeater, [literal(metadataSlot), collection], sourceSpan);
|
|
20308
|
+
}
|
|
20124
20309
|
function i18n(slot, constIndex, subTemplateIndex) {
|
|
20125
20310
|
const args = [literal(slot), literal(constIndex)];
|
|
20126
20311
|
if (subTemplateIndex) {
|
|
@@ -20575,7 +20760,35 @@ function reifyCreateOperations(unit, ops) {
|
|
|
20575
20760
|
if (op.slot === null) {
|
|
20576
20761
|
throw new Error('No slot was assigned for project instruction');
|
|
20577
20762
|
}
|
|
20578
|
-
OpList.replace(op, projection(op.slot, op.projectionSlotIndex, op.attributes));
|
|
20763
|
+
OpList.replace(op, projection(op.slot, op.projectionSlotIndex, op.attributes, op.sourceSpan));
|
|
20764
|
+
break;
|
|
20765
|
+
case OpKind.RepeaterCreate:
|
|
20766
|
+
if (op.slot === null) {
|
|
20767
|
+
throw new Error('No slot was assigned for repeater instruction');
|
|
20768
|
+
}
|
|
20769
|
+
if (!(unit instanceof ViewCompilationUnit)) {
|
|
20770
|
+
throw new Error(`AssertionError: must be compiling a component`);
|
|
20771
|
+
}
|
|
20772
|
+
const repeaterView = unit.job.views.get(op.xref);
|
|
20773
|
+
if (repeaterView.fnName === null) {
|
|
20774
|
+
throw new Error(`AssertionError: expected repeater primary view to have been named`);
|
|
20775
|
+
}
|
|
20776
|
+
let emptyViewFnName = null;
|
|
20777
|
+
let emptyDecls = null;
|
|
20778
|
+
let emptyVars = null;
|
|
20779
|
+
if (op.emptyView !== null) {
|
|
20780
|
+
const emptyView = unit.job.views.get(op.emptyView);
|
|
20781
|
+
if (emptyView === undefined) {
|
|
20782
|
+
throw new Error('AssertionError: repeater had empty view xref, but empty view was not found');
|
|
20783
|
+
}
|
|
20784
|
+
if (emptyView.fnName === null || emptyView.decls === null || emptyView.vars === null) {
|
|
20785
|
+
throw new Error(`AssertionError: expected repeater empty view to have been named and counted`);
|
|
20786
|
+
}
|
|
20787
|
+
emptyViewFnName = emptyView.fnName;
|
|
20788
|
+
emptyDecls = emptyView.decls;
|
|
20789
|
+
emptyVars = emptyView.vars;
|
|
20790
|
+
}
|
|
20791
|
+
OpList.replace(op, repeaterCreate(op.slot, repeaterView.fnName, op.decls, op.vars, op.trackByFn, op.usesComponentInstance, emptyViewFnName, emptyDecls, emptyVars, op.sourceSpan));
|
|
20579
20792
|
break;
|
|
20580
20793
|
case OpKind.Statement:
|
|
20581
20794
|
// Pass statement operations directly through.
|
|
@@ -20672,6 +20885,9 @@ function reifyUpdateOperations(_unit, ops) {
|
|
|
20672
20885
|
}
|
|
20673
20886
|
OpList.replace(op, conditional(op.targetSlot, op.processed, op.contextValue, op.sourceSpan));
|
|
20674
20887
|
break;
|
|
20888
|
+
case OpKind.Repeater:
|
|
20889
|
+
OpList.replace(op, repeater(op.targetSlot, op.collection, op.sourceSpan));
|
|
20890
|
+
break;
|
|
20675
20891
|
case OpKind.Statement:
|
|
20676
20892
|
// Pass statement operations directly through.
|
|
20677
20893
|
break;
|
|
@@ -20810,6 +21026,10 @@ function processLexicalScope$1(view, ops) {
|
|
|
20810
21026
|
break;
|
|
20811
21027
|
}
|
|
20812
21028
|
}
|
|
21029
|
+
if (view === view.job.root) {
|
|
21030
|
+
// Prefer `ctx` of the root view to any variables which happen to contain the root context.
|
|
21031
|
+
scope.set(view.xref, variable('ctx'));
|
|
21032
|
+
}
|
|
20813
21033
|
for (const op of ops) {
|
|
20814
21034
|
transformExpressionsInOp(op, expr => {
|
|
20815
21035
|
if (expr instanceof ContextExpr) {
|
|
@@ -20881,6 +21101,10 @@ const LIST_END_MARKER = ']';
|
|
|
20881
21101
|
* Delimiter used to separate multiple values in a list.
|
|
20882
21102
|
*/
|
|
20883
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
|
+
*/
|
|
20884
21108
|
var I18nParamValueFlags;
|
|
20885
21109
|
(function (I18nParamValueFlags) {
|
|
20886
21110
|
I18nParamValueFlags[I18nParamValueFlags["None"] = 0] = "None";
|
|
@@ -20893,10 +21117,13 @@ var I18nParamValueFlags;
|
|
|
20893
21117
|
*/
|
|
20894
21118
|
I18nParamValueFlags[I18nParamValueFlags["TemplateTag"] = 2] = "TemplateTag";
|
|
20895
21119
|
/**
|
|
20896
|
-
* This value represents the
|
|
20897
|
-
|
|
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.
|
|
20898
21125
|
*/
|
|
20899
|
-
I18nParamValueFlags[I18nParamValueFlags["CloseTag"] =
|
|
21126
|
+
I18nParamValueFlags[I18nParamValueFlags["CloseTag"] = 8] = "CloseTag";
|
|
20900
21127
|
})(I18nParamValueFlags || (I18nParamValueFlags = {}));
|
|
20901
21128
|
/**
|
|
20902
21129
|
* Represents the complete i18n params map for an i18n op.
|
|
@@ -20908,9 +21135,9 @@ class I18nPlaceholderParams {
|
|
|
20908
21135
|
/**
|
|
20909
21136
|
* Adds a new value to the params map.
|
|
20910
21137
|
*/
|
|
20911
|
-
addValue(placeholder, value, subTemplateIndex, flags) {
|
|
21138
|
+
addValue(placeholder, value, subTemplateIndex, resolutionTime, flags) {
|
|
20912
21139
|
const placeholderValues = this.values.get(placeholder) ?? [];
|
|
20913
|
-
placeholderValues.push({ value, subTemplateIndex, flags });
|
|
21140
|
+
placeholderValues.push({ value, subTemplateIndex, resolutionTime, flags });
|
|
20914
21141
|
this.values.set(placeholder, placeholderValues);
|
|
20915
21142
|
}
|
|
20916
21143
|
/**
|
|
@@ -20918,13 +21145,49 @@ class I18nPlaceholderParams {
|
|
|
20918
21145
|
*/
|
|
20919
21146
|
saveToOp(op) {
|
|
20920
21147
|
for (const [placeholder, placeholderValues] of this.values) {
|
|
20921
|
-
|
|
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
|
+
}
|
|
20922
21182
|
}
|
|
20923
21183
|
}
|
|
20924
21184
|
/**
|
|
20925
21185
|
* Serializes a list of i18n placeholder values.
|
|
20926
21186
|
*/
|
|
20927
21187
|
serializeValues(values) {
|
|
21188
|
+
if (values.length === 0) {
|
|
21189
|
+
return null;
|
|
21190
|
+
}
|
|
20928
21191
|
const serializedValues = values.map(value => this.serializeValue(value));
|
|
20929
21192
|
return serializedValues.length === 1 ?
|
|
20930
21193
|
serializedValues[0] :
|
|
@@ -20946,6 +21209,11 @@ class I18nPlaceholderParams {
|
|
|
20946
21209
|
closeMarker = value.flags & I18nParamValueFlags.CloseTag ? TAG_CLOSE_MARKER : '';
|
|
20947
21210
|
}
|
|
20948
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
|
+
}
|
|
20949
21217
|
return `${ESCAPE}${closeMarker}${tagMarker}${value.value}${context}${ESCAPE}`;
|
|
20950
21218
|
}
|
|
20951
21219
|
}
|
|
@@ -20953,36 +21221,81 @@ class I18nPlaceholderParams {
|
|
|
20953
21221
|
* Resolve the placeholders in i18n messages.
|
|
20954
21222
|
*/
|
|
20955
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
|
+
}
|
|
21239
|
+
}
|
|
21240
|
+
}
|
|
21241
|
+
}
|
|
21242
|
+
}
|
|
21243
|
+
/**
|
|
21244
|
+
* Resolve placeholders for each i18n op.
|
|
21245
|
+
*/
|
|
21246
|
+
function resolvePlaceholders(job, params, i18nOps) {
|
|
20956
21247
|
for (const unit of job.units) {
|
|
20957
|
-
const
|
|
20958
|
-
const params = new Map();
|
|
21248
|
+
const elements = new Map();
|
|
20959
21249
|
let currentI18nOp = null;
|
|
20960
21250
|
// Record slots for tag name placeholders.
|
|
20961
21251
|
for (const op of unit.create) {
|
|
20962
21252
|
switch (op.kind) {
|
|
20963
21253
|
case OpKind.I18nStart:
|
|
20964
|
-
case OpKind.I18n:
|
|
20965
21254
|
i18nOps.set(op.xref, op);
|
|
20966
21255
|
currentI18nOp = op.kind === OpKind.I18nStart ? op : null;
|
|
20967
21256
|
break;
|
|
20968
21257
|
case OpKind.I18nEnd:
|
|
20969
21258
|
currentI18nOp = null;
|
|
20970
21259
|
break;
|
|
20971
|
-
case OpKind.Element:
|
|
20972
21260
|
case OpKind.ElementStart:
|
|
20973
|
-
|
|
20974
|
-
//
|
|
20975
|
-
// the start and close placeholders.
|
|
21261
|
+
// For elements with i18n placeholders, record its slot value in the params map under the
|
|
21262
|
+
// corresponding tag start placeholder.
|
|
20976
21263
|
if (op.i18nPlaceholder !== undefined) {
|
|
20977
21264
|
if (currentI18nOp === null) {
|
|
20978
21265
|
throw Error('i18n tag placeholder should only occur inside an i18n block');
|
|
20979
21266
|
}
|
|
21267
|
+
elements.set(op.xref, op);
|
|
20980
21268
|
const { startName, closeName } = op.i18nPlaceholder;
|
|
20981
|
-
|
|
20982
|
-
|
|
20983
|
-
|
|
20984
|
-
|
|
20985
|
-
|
|
21269
|
+
let flags = I18nParamValueFlags.ElementTag | I18nParamValueFlags.OpenTag;
|
|
21270
|
+
// For self-closing tags, there is no close tag placeholder. Instead, the start tag
|
|
21271
|
+
// placeholder accounts for the start and close of the element.
|
|
21272
|
+
if (closeName === '') {
|
|
21273
|
+
flags |= I18nParamValueFlags.CloseTag;
|
|
21274
|
+
}
|
|
21275
|
+
addParam(params, currentI18nOp, startName, op.slot, currentI18nOp.subTemplateIndex, I18nParamResolutionTime.Creation, flags);
|
|
21276
|
+
}
|
|
21277
|
+
break;
|
|
21278
|
+
case OpKind.ElementEnd:
|
|
21279
|
+
const startOp = elements.get(op.xref);
|
|
21280
|
+
if (startOp && startOp.i18nPlaceholder !== undefined) {
|
|
21281
|
+
if (currentI18nOp === null) {
|
|
21282
|
+
throw Error('i18n tag placeholder should only occur inside an i18n block');
|
|
21283
|
+
}
|
|
21284
|
+
const { closeName } = startOp.i18nPlaceholder;
|
|
21285
|
+
// Self-closing tags don't have a closing tag placeholder.
|
|
21286
|
+
if (closeName !== '') {
|
|
21287
|
+
addParam(params, currentI18nOp, closeName, startOp.slot, currentI18nOp.subTemplateIndex, I18nParamResolutionTime.Creation, I18nParamValueFlags.ElementTag | I18nParamValueFlags.CloseTag);
|
|
21288
|
+
}
|
|
21289
|
+
}
|
|
21290
|
+
break;
|
|
21291
|
+
case OpKind.Template:
|
|
21292
|
+
if (op.i18nPlaceholder !== undefined) {
|
|
21293
|
+
if (currentI18nOp === null) {
|
|
21294
|
+
throw Error('i18n tag placeholder should only occur inside an i18n block');
|
|
21295
|
+
}
|
|
21296
|
+
const subTemplateIndex = getSubTemplateIndexForTemplateTag(job, currentI18nOp, op);
|
|
21297
|
+
addParam(params, currentI18nOp, op.i18nPlaceholder.startName, op.slot, subTemplateIndex, I18nParamResolutionTime.Creation, I18nParamValueFlags.TemplateTag);
|
|
21298
|
+
addParam(params, currentI18nOp, op.i18nPlaceholder.closeName, op.slot, subTemplateIndex, I18nParamResolutionTime.Creation, I18nParamValueFlags.TemplateTag | I18nParamValueFlags.CloseTag);
|
|
20986
21299
|
}
|
|
20987
21300
|
break;
|
|
20988
21301
|
}
|
|
@@ -20996,39 +21309,44 @@ function phaseResolveI18nPlaceholders(job) {
|
|
|
20996
21309
|
if (!i18nOp) {
|
|
20997
21310
|
throw Error('Cannot find corresponding i18nStart for i18nExpr');
|
|
20998
21311
|
}
|
|
20999
|
-
addParam(params, i18nOp, op.i18nPlaceholder
|
|
21312
|
+
addParam(params, i18nOp, op.i18nPlaceholder, index++, i18nOp.subTemplateIndex, op.resolutionTime);
|
|
21000
21313
|
i18nBlockPlaceholderIndices.set(op.owner, index);
|
|
21001
21314
|
}
|
|
21002
21315
|
}
|
|
21003
|
-
// After colleccting all params, save them to the i18n ops.
|
|
21004
|
-
for (const [xref, i18nOpParams] of params) {
|
|
21005
|
-
i18nOpParams.saveToOp(i18nOps.get(xref));
|
|
21006
|
-
}
|
|
21007
21316
|
}
|
|
21008
21317
|
}
|
|
21009
21318
|
/**
|
|
21010
21319
|
* Add a param to the params map for the given i18n op.
|
|
21011
21320
|
*/
|
|
21012
|
-
function addParam(params, i18nOp, placeholder, value, subTemplateIndex, flags = I18nParamValueFlags.None) {
|
|
21013
|
-
const i18nOpParams = params.get(i18nOp.xref)
|
|
21014
|
-
i18nOpParams.addValue(placeholder, value, subTemplateIndex, flags);
|
|
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);
|
|
21015
21324
|
params.set(i18nOp.xref, i18nOpParams);
|
|
21016
21325
|
}
|
|
21017
21326
|
/**
|
|
21018
|
-
* Get the subTemplateIndex for the given op. For template ops, use the subTemplateIndex of
|
|
21019
|
-
* child i18n block inside the template.
|
|
21020
|
-
* block the op belongs to.
|
|
21327
|
+
* Get the subTemplateIndex for the given template op. For template ops, use the subTemplateIndex of
|
|
21328
|
+
* the child i18n block inside the template.
|
|
21021
21329
|
*/
|
|
21022
|
-
function
|
|
21023
|
-
|
|
21024
|
-
|
|
21025
|
-
|
|
21026
|
-
return childOp.subTemplateIndex;
|
|
21027
|
-
}
|
|
21330
|
+
function getSubTemplateIndexForTemplateTag(job, i18nOp, op) {
|
|
21331
|
+
for (const childOp of job.views.get(op.xref).create) {
|
|
21332
|
+
if (childOp.kind === OpKind.I18nStart) {
|
|
21333
|
+
return childOp.subTemplateIndex;
|
|
21028
21334
|
}
|
|
21029
21335
|
}
|
|
21030
21336
|
return i18nOp.subTemplateIndex;
|
|
21031
21337
|
}
|
|
21338
|
+
/**
|
|
21339
|
+
* Propagate placeholders up to their root i18n op.
|
|
21340
|
+
*/
|
|
21341
|
+
function propagatePlaceholders(params, i18nOps) {
|
|
21342
|
+
for (const [xref, opParams] of params) {
|
|
21343
|
+
const op = i18nOps.get(xref);
|
|
21344
|
+
if (op.xref !== op.root) {
|
|
21345
|
+
const rootParams = params.get(op.root) || new I18nPlaceholderParams();
|
|
21346
|
+
rootParams.merge(opParams);
|
|
21347
|
+
}
|
|
21348
|
+
}
|
|
21349
|
+
}
|
|
21032
21350
|
|
|
21033
21351
|
/**
|
|
21034
21352
|
* Resolves lexical references in views (`ir.LexicalReadExpr`) to either a target variable or to
|
|
@@ -21058,6 +21376,7 @@ function processLexicalScope(unit, ops, savedView) {
|
|
|
21058
21376
|
case OpKind.Variable:
|
|
21059
21377
|
switch (op.variable.kind) {
|
|
21060
21378
|
case SemanticVariableKind.Identifier:
|
|
21379
|
+
case SemanticVariableKind.Alias:
|
|
21061
21380
|
// This variable represents some kind of identifier which can be used in the template.
|
|
21062
21381
|
if (scope.has(op.variable.identifier)) {
|
|
21063
21382
|
continue;
|
|
@@ -21140,7 +21459,7 @@ const sanitizers = new Map([
|
|
|
21140
21459
|
*/
|
|
21141
21460
|
function phaseResolveSanitizers(job) {
|
|
21142
21461
|
for (const unit of job.units) {
|
|
21143
|
-
const elements =
|
|
21462
|
+
const elements = createOpXrefMap(unit);
|
|
21144
21463
|
let sanitizerFn;
|
|
21145
21464
|
for (const op of unit.update) {
|
|
21146
21465
|
switch (op.kind) {
|
|
@@ -21154,7 +21473,7 @@ function phaseResolveSanitizers(job) {
|
|
|
21154
21473
|
// <iframe>).
|
|
21155
21474
|
if (op.sanitizer === null) {
|
|
21156
21475
|
const ownerOp = elements.get(op.target);
|
|
21157
|
-
if (ownerOp === undefined) {
|
|
21476
|
+
if (ownerOp === undefined || !isElementOrContainerOp(ownerOp)) {
|
|
21158
21477
|
throw Error('Property should have an element-like owner');
|
|
21159
21478
|
}
|
|
21160
21479
|
if (isIframeElement$1(ownerOp) && isIframeSecuritySensitiveAttr(op.name)) {
|
|
@@ -21170,8 +21489,7 @@ function phaseResolveSanitizers(job) {
|
|
|
21170
21489
|
* Checks whether the given op represents an iframe element.
|
|
21171
21490
|
*/
|
|
21172
21491
|
function isIframeElement$1(op) {
|
|
21173
|
-
return
|
|
21174
|
-
op.tag.toLowerCase() === 'iframe';
|
|
21492
|
+
return op.kind === OpKind.ElementStart && op.tag?.toLowerCase() === 'iframe';
|
|
21175
21493
|
}
|
|
21176
21494
|
|
|
21177
21495
|
function phaseSaveRestoreView(job) {
|
|
@@ -21181,7 +21499,7 @@ function phaseSaveRestoreView(job) {
|
|
|
21181
21499
|
kind: SemanticVariableKind.SavedView,
|
|
21182
21500
|
name: null,
|
|
21183
21501
|
view: view.xref,
|
|
21184
|
-
}, new GetCurrentViewExpr()),
|
|
21502
|
+
}, new GetCurrentViewExpr(), VariableFlags.None),
|
|
21185
21503
|
]);
|
|
21186
21504
|
for (const op of view.create) {
|
|
21187
21505
|
if (op.kind !== OpKind.Listener) {
|
|
@@ -21211,7 +21529,7 @@ function addSaveRestoreViewOperationToListener(unit, op) {
|
|
|
21211
21529
|
kind: SemanticVariableKind.Context,
|
|
21212
21530
|
name: null,
|
|
21213
21531
|
view: unit.xref,
|
|
21214
|
-
}, new RestoreViewExpr(unit.xref)),
|
|
21532
|
+
}, new RestoreViewExpr(unit.xref), VariableFlags.None),
|
|
21215
21533
|
]);
|
|
21216
21534
|
// The "restore view" operation in listeners requires a call to `resetView` to reset the
|
|
21217
21535
|
// context prior to returning from the listener operation. Find any `return` statements in
|
|
@@ -21266,13 +21584,13 @@ function phaseSlotAllocation(job) {
|
|
|
21266
21584
|
// propagate the number of slots used for each view into the operation which declares it.
|
|
21267
21585
|
for (const unit of job.units) {
|
|
21268
21586
|
for (const op of unit.ops()) {
|
|
21269
|
-
if (op.kind === OpKind.Template) {
|
|
21587
|
+
if (op.kind === OpKind.Template || op.kind === OpKind.RepeaterCreate) {
|
|
21270
21588
|
// Record the number of slots used by the view this `ir.TemplateOp` declares in the
|
|
21271
21589
|
// operation itself, so it can be emitted later.
|
|
21272
21590
|
const childView = job.views.get(op.xref);
|
|
21273
21591
|
op.decls = childView.decls;
|
|
21274
21592
|
}
|
|
21275
|
-
if (hasUsesSlotIndexTrait(op) && op.targetSlot === null) {
|
|
21593
|
+
if (hasUsesSlotIndexTrait(op) && op.target !== null && op.targetSlot === null) {
|
|
21276
21594
|
if (!slotMap.has(op.target)) {
|
|
21277
21595
|
// We do expect to find a slot allocated for everything which might be referenced.
|
|
21278
21596
|
throw new Error(`AssertionError: no slot allocated for ${OpKind[op.kind]} target ${op.target}`);
|
|
@@ -21414,6 +21732,137 @@ function assignName(names, expr) {
|
|
|
21414
21732
|
expr.name = name;
|
|
21415
21733
|
}
|
|
21416
21734
|
|
|
21735
|
+
/**
|
|
21736
|
+
* Counts the number of variable slots used within each view, and stores that on the view itself, as
|
|
21737
|
+
* well as propagates it to the `ir.TemplateOp` for embedded views.
|
|
21738
|
+
*/
|
|
21739
|
+
function phaseVarCounting(job) {
|
|
21740
|
+
// First, count the vars used in each view, and update the view-level counter.
|
|
21741
|
+
for (const unit of job.units) {
|
|
21742
|
+
let varCount = 0;
|
|
21743
|
+
// Count variables on top-level ops first. Don't explore nested expressions just yet.
|
|
21744
|
+
for (const op of unit.ops()) {
|
|
21745
|
+
if (hasConsumesVarsTrait(op)) {
|
|
21746
|
+
varCount += varsUsedByOp(op);
|
|
21747
|
+
}
|
|
21748
|
+
}
|
|
21749
|
+
// Count variables on expressions inside ops. We do this later because some of these expressions
|
|
21750
|
+
// might be conditional (e.g. `pipeBinding` inside of a ternary), and we don't want to interfere
|
|
21751
|
+
// with indices for top-level binding slots (e.g. `property`).
|
|
21752
|
+
for (const op of unit.ops()) {
|
|
21753
|
+
visitExpressionsInOp(op, expr => {
|
|
21754
|
+
if (!isIrExpression(expr)) {
|
|
21755
|
+
return;
|
|
21756
|
+
}
|
|
21757
|
+
// TemplateDefinitionBuilder assigns variable offsets for everything but pure functions
|
|
21758
|
+
// first, and then assigns offsets to pure functions lazily. We emulate that behavior by
|
|
21759
|
+
// assigning offsets in two passes instead of one, only in compatibility mode.
|
|
21760
|
+
if (job.compatibility === CompatibilityMode.TemplateDefinitionBuilder &&
|
|
21761
|
+
expr instanceof PureFunctionExpr) {
|
|
21762
|
+
return;
|
|
21763
|
+
}
|
|
21764
|
+
// Some expressions require knowledge of the number of variable slots consumed.
|
|
21765
|
+
if (hasUsesVarOffsetTrait(expr)) {
|
|
21766
|
+
expr.varOffset = varCount;
|
|
21767
|
+
}
|
|
21768
|
+
if (hasConsumesVarsTrait(expr)) {
|
|
21769
|
+
varCount += varsUsedByIrExpression(expr);
|
|
21770
|
+
}
|
|
21771
|
+
});
|
|
21772
|
+
}
|
|
21773
|
+
// Compatiblity mode pass for pure function offsets (as explained above).
|
|
21774
|
+
if (job.compatibility === CompatibilityMode.TemplateDefinitionBuilder) {
|
|
21775
|
+
for (const op of unit.ops()) {
|
|
21776
|
+
visitExpressionsInOp(op, expr => {
|
|
21777
|
+
if (!isIrExpression(expr) || !(expr instanceof PureFunctionExpr)) {
|
|
21778
|
+
return;
|
|
21779
|
+
}
|
|
21780
|
+
// Some expressions require knowledge of the number of variable slots consumed.
|
|
21781
|
+
if (hasUsesVarOffsetTrait(expr)) {
|
|
21782
|
+
expr.varOffset = varCount;
|
|
21783
|
+
}
|
|
21784
|
+
if (hasConsumesVarsTrait(expr)) {
|
|
21785
|
+
varCount += varsUsedByIrExpression(expr);
|
|
21786
|
+
}
|
|
21787
|
+
});
|
|
21788
|
+
}
|
|
21789
|
+
}
|
|
21790
|
+
unit.vars = varCount;
|
|
21791
|
+
}
|
|
21792
|
+
if (job instanceof ComponentCompilationJob) {
|
|
21793
|
+
// Add var counts for each view to the `ir.TemplateOp` which declares that view (if the view is
|
|
21794
|
+
// an embedded view).
|
|
21795
|
+
for (const unit of job.units) {
|
|
21796
|
+
for (const op of unit.create) {
|
|
21797
|
+
if (op.kind !== OpKind.Template && op.kind !== OpKind.RepeaterCreate) {
|
|
21798
|
+
continue;
|
|
21799
|
+
}
|
|
21800
|
+
const childView = job.views.get(op.xref);
|
|
21801
|
+
op.vars = childView.vars;
|
|
21802
|
+
}
|
|
21803
|
+
}
|
|
21804
|
+
}
|
|
21805
|
+
}
|
|
21806
|
+
/**
|
|
21807
|
+
* Different operations that implement `ir.UsesVarsTrait` use different numbers of variables, so
|
|
21808
|
+
* count the variables used by any particular `op`.
|
|
21809
|
+
*/
|
|
21810
|
+
function varsUsedByOp(op) {
|
|
21811
|
+
let slots;
|
|
21812
|
+
switch (op.kind) {
|
|
21813
|
+
case OpKind.Property:
|
|
21814
|
+
case OpKind.HostProperty:
|
|
21815
|
+
case OpKind.Attribute:
|
|
21816
|
+
// All of these bindings use 1 variable slot, plus 1 slot for every interpolated expression,
|
|
21817
|
+
// if any.
|
|
21818
|
+
slots = 1;
|
|
21819
|
+
if (op.expression instanceof Interpolation && !isSingletonInterpolation(op.expression)) {
|
|
21820
|
+
slots += op.expression.expressions.length;
|
|
21821
|
+
}
|
|
21822
|
+
return slots;
|
|
21823
|
+
case OpKind.StyleProp:
|
|
21824
|
+
case OpKind.ClassProp:
|
|
21825
|
+
case OpKind.StyleMap:
|
|
21826
|
+
case OpKind.ClassMap:
|
|
21827
|
+
// Style & class bindings use 2 variable slots, plus 1 slot for every interpolated expression,
|
|
21828
|
+
// if any.
|
|
21829
|
+
slots = 2;
|
|
21830
|
+
if (op.expression instanceof Interpolation) {
|
|
21831
|
+
slots += op.expression.expressions.length;
|
|
21832
|
+
}
|
|
21833
|
+
return slots;
|
|
21834
|
+
case OpKind.InterpolateText:
|
|
21835
|
+
// `ir.InterpolateTextOp`s use a variable slot for each dynamic expression.
|
|
21836
|
+
return op.interpolation.expressions.length;
|
|
21837
|
+
case OpKind.I18nExpression:
|
|
21838
|
+
case OpKind.Conditional:
|
|
21839
|
+
return 1;
|
|
21840
|
+
default:
|
|
21841
|
+
throw new Error(`Unhandled op: ${OpKind[op.kind]}`);
|
|
21842
|
+
}
|
|
21843
|
+
}
|
|
21844
|
+
function varsUsedByIrExpression(expr) {
|
|
21845
|
+
switch (expr.kind) {
|
|
21846
|
+
case ExpressionKind.PureFunctionExpr:
|
|
21847
|
+
return 1 + expr.args.length;
|
|
21848
|
+
case ExpressionKind.PipeBinding:
|
|
21849
|
+
return 1 + expr.args.length;
|
|
21850
|
+
case ExpressionKind.PipeBindingVariadic:
|
|
21851
|
+
return 1 + expr.numArgs;
|
|
21852
|
+
default:
|
|
21853
|
+
throw new Error(`AssertionError: unhandled ConsumesVarsTrait expression ${expr.constructor.name}`);
|
|
21854
|
+
}
|
|
21855
|
+
}
|
|
21856
|
+
function isSingletonInterpolation(expr) {
|
|
21857
|
+
if (expr.expressions.length !== 1 || expr.strings.length !== 2) {
|
|
21858
|
+
return false;
|
|
21859
|
+
}
|
|
21860
|
+
if (expr.strings[0] !== '' || expr.strings[1] !== '') {
|
|
21861
|
+
return false;
|
|
21862
|
+
}
|
|
21863
|
+
return true;
|
|
21864
|
+
}
|
|
21865
|
+
|
|
21417
21866
|
/**
|
|
21418
21867
|
* Optimize variables declared and used in the IR.
|
|
21419
21868
|
*
|
|
@@ -21434,6 +21883,13 @@ function assignName(names, expr) {
|
|
|
21434
21883
|
*/
|
|
21435
21884
|
function phaseVariableOptimization(job) {
|
|
21436
21885
|
for (const unit of job.units) {
|
|
21886
|
+
inlineAlwaysInlineVariables(unit.create);
|
|
21887
|
+
inlineAlwaysInlineVariables(unit.update);
|
|
21888
|
+
for (const op of unit.create) {
|
|
21889
|
+
if (op.kind === OpKind.Listener) {
|
|
21890
|
+
inlineAlwaysInlineVariables(op.handlerOps);
|
|
21891
|
+
}
|
|
21892
|
+
}
|
|
21437
21893
|
optimizeVariablesInOpList(unit.create, job.compatibility);
|
|
21438
21894
|
optimizeVariablesInOpList(unit.update, job.compatibility);
|
|
21439
21895
|
for (const op of unit.create) {
|
|
@@ -21476,6 +21932,30 @@ var Fence;
|
|
|
21476
21932
|
*/
|
|
21477
21933
|
Fence[Fence["SideEffectful"] = 4] = "SideEffectful";
|
|
21478
21934
|
})(Fence || (Fence = {}));
|
|
21935
|
+
function inlineAlwaysInlineVariables(ops) {
|
|
21936
|
+
const vars = new Map();
|
|
21937
|
+
for (const op of ops) {
|
|
21938
|
+
if (op.kind === OpKind.Variable && op.flags & VariableFlags.AlwaysInline) {
|
|
21939
|
+
visitExpressionsInOp(op, expr => {
|
|
21940
|
+
if (isIrExpression(expr) && fencesForIrExpression(expr) !== Fence.None) {
|
|
21941
|
+
throw new Error(`AssertionError: A context-sensitive variable was marked AlwaysInline`);
|
|
21942
|
+
}
|
|
21943
|
+
});
|
|
21944
|
+
vars.set(op.xref, op);
|
|
21945
|
+
}
|
|
21946
|
+
transformExpressionsInOp(op, expr => {
|
|
21947
|
+
if (expr instanceof ReadVariableExpr && vars.has(expr.xref)) {
|
|
21948
|
+
const varOp = vars.get(expr.xref);
|
|
21949
|
+
// Inline by cloning, because we might inline into multiple places.
|
|
21950
|
+
return varOp.initializer.clone();
|
|
21951
|
+
}
|
|
21952
|
+
return expr;
|
|
21953
|
+
}, VisitorContextFlag.None);
|
|
21954
|
+
}
|
|
21955
|
+
for (const op of vars.values()) {
|
|
21956
|
+
OpList.remove(op);
|
|
21957
|
+
}
|
|
21958
|
+
}
|
|
21479
21959
|
/**
|
|
21480
21960
|
* Process a list of operations and optimize variables within that list.
|
|
21481
21961
|
*/
|
|
@@ -21545,10 +22025,15 @@ function optimizeVariablesInOpList(ops, compatibility) {
|
|
|
21545
22025
|
// Next, inline any remaining variables with exactly one usage.
|
|
21546
22026
|
const toInline = [];
|
|
21547
22027
|
for (const [id, count] of varUsages) {
|
|
22028
|
+
const decl = varDecls.get(id);
|
|
22029
|
+
const varInfo = opMap.get(decl);
|
|
21548
22030
|
// We can inline variables that:
|
|
21549
|
-
// - are used once
|
|
22031
|
+
// - are used exactly once, and
|
|
21550
22032
|
// - are not used remotely
|
|
21551
|
-
|
|
22033
|
+
// OR
|
|
22034
|
+
// - are marked for always inlining
|
|
22035
|
+
const isAlwaysInline = !!(decl.flags & VariableFlags.AlwaysInline);
|
|
22036
|
+
if (count !== 1 || isAlwaysInline) {
|
|
21552
22037
|
// We can't inline this variable as it's used more than once.
|
|
21553
22038
|
continue;
|
|
21554
22039
|
}
|
|
@@ -21564,6 +22049,10 @@ function optimizeVariablesInOpList(ops, compatibility) {
|
|
|
21564
22049
|
// no future operation will make inlining legal.
|
|
21565
22050
|
const decl = varDecls.get(candidate);
|
|
21566
22051
|
const varInfo = opMap.get(decl);
|
|
22052
|
+
const isAlwaysInline = !!(decl.flags & VariableFlags.AlwaysInline);
|
|
22053
|
+
if (isAlwaysInline) {
|
|
22054
|
+
throw new Error(`AssertionError: Found an 'AlwaysInline' variable after the always inlining pass.`);
|
|
22055
|
+
}
|
|
21567
22056
|
// Scan operations following the variable declaration and look for the point where that variable
|
|
21568
22057
|
// is used. There should only be one usage given the precondition above.
|
|
21569
22058
|
for (let targetOp = decl.next; targetOp.kind !== OpKind.ListEnd; targetOp = targetOp.next) {
|
|
@@ -21785,6 +22274,167 @@ function allowConservativeInlining(decl, target) {
|
|
|
21785
22274
|
}
|
|
21786
22275
|
}
|
|
21787
22276
|
|
|
22277
|
+
/**
|
|
22278
|
+
* Wraps ICUs that do not already belong to an i18n block in a new i18n block.
|
|
22279
|
+
*/
|
|
22280
|
+
function phaseWrapIcus(job) {
|
|
22281
|
+
for (const unit of job.units) {
|
|
22282
|
+
let currentI18nOp = null;
|
|
22283
|
+
for (const op of unit.create) {
|
|
22284
|
+
switch (op.kind) {
|
|
22285
|
+
case OpKind.I18nStart:
|
|
22286
|
+
currentI18nOp = op;
|
|
22287
|
+
break;
|
|
22288
|
+
case OpKind.I18nEnd:
|
|
22289
|
+
currentI18nOp = null;
|
|
22290
|
+
break;
|
|
22291
|
+
case OpKind.Icu:
|
|
22292
|
+
if (currentI18nOp === null) {
|
|
22293
|
+
const id = job.allocateXrefId();
|
|
22294
|
+
OpList.insertBefore(createI18nStartOp(id, op.message), op);
|
|
22295
|
+
OpList.insertAfter(createI18nEndOp(id), op);
|
|
22296
|
+
}
|
|
22297
|
+
break;
|
|
22298
|
+
}
|
|
22299
|
+
}
|
|
22300
|
+
}
|
|
22301
|
+
}
|
|
22302
|
+
|
|
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
|
+
/**
|
|
22431
|
+
*
|
|
22432
|
+
* @license
|
|
22433
|
+
* Copyright Google LLC All Rights Reserved.
|
|
22434
|
+
*
|
|
22435
|
+
* Use of this source code is governed by an MIT-style license that can be
|
|
22436
|
+
* found in the LICENSE file at https://angular.io/license
|
|
22437
|
+
*/
|
|
21788
22438
|
const phases = [
|
|
21789
22439
|
{ kind: CompilationJobKind.Tmpl, fn: phaseRemoveContentSelectors },
|
|
21790
22440
|
{ kind: CompilationJobKind.Host, fn: phaseHostStylePropertyParsing },
|
|
@@ -21792,12 +22442,16 @@ const phases = [
|
|
|
21792
22442
|
{ kind: CompilationJobKind.Both, fn: phaseStyleBindingSpecialization },
|
|
21793
22443
|
{ kind: CompilationJobKind.Both, fn: phaseBindingSpecialization },
|
|
21794
22444
|
{ kind: CompilationJobKind.Tmpl, fn: phasePropagateI18nBlocks },
|
|
22445
|
+
{ kind: CompilationJobKind.Tmpl, fn: phaseWrapIcus },
|
|
21795
22446
|
{ kind: CompilationJobKind.Both, fn: phaseAttributeExtraction },
|
|
21796
22447
|
{ kind: CompilationJobKind.Both, fn: phaseParseExtractedStyles },
|
|
21797
22448
|
{ kind: CompilationJobKind.Tmpl, fn: phaseRemoveEmptyBindings },
|
|
22449
|
+
{ kind: CompilationJobKind.Both, fn: phaseCollapseSingletonInterpolations },
|
|
22450
|
+
{ kind: CompilationJobKind.Both, fn: phaseOrdering },
|
|
21798
22451
|
{ kind: CompilationJobKind.Tmpl, fn: phaseConditionals },
|
|
21799
22452
|
{ kind: CompilationJobKind.Tmpl, fn: phasePipeCreation },
|
|
21800
22453
|
{ kind: CompilationJobKind.Tmpl, fn: phaseI18nTextExtraction },
|
|
22454
|
+
{ kind: CompilationJobKind.Tmpl, fn: phaseIcuExtraction },
|
|
21801
22455
|
{ kind: CompilationJobKind.Tmpl, fn: phaseApplyI18nExpressions },
|
|
21802
22456
|
{ kind: CompilationJobKind.Tmpl, fn: phasePipeVariadic },
|
|
21803
22457
|
{ kind: CompilationJobKind.Both, fn: phasePureLiteralStructures },
|
|
@@ -21806,7 +22460,10 @@ const phases = [
|
|
|
21806
22460
|
{ kind: CompilationJobKind.Tmpl, fn: phaseSaveRestoreView },
|
|
21807
22461
|
{ kind: CompilationJobKind.Tmpl, fn: phaseFindAnyCasts },
|
|
21808
22462
|
{ kind: CompilationJobKind.Both, fn: phaseResolveDollarEvent },
|
|
22463
|
+
{ kind: CompilationJobKind.Tmpl, fn: phaseRepeaterDerivedVars },
|
|
22464
|
+
{ kind: CompilationJobKind.Tmpl, fn: phaseTrackVariables },
|
|
21809
22465
|
{ kind: CompilationJobKind.Both, fn: phaseResolveNames },
|
|
22466
|
+
{ kind: CompilationJobKind.Tmpl, fn: phaseTrackFnOptimization },
|
|
21810
22467
|
{ kind: CompilationJobKind.Both, fn: phaseResolveContexts },
|
|
21811
22468
|
{ kind: CompilationJobKind.Tmpl, fn: phaseResolveSanitizers },
|
|
21812
22469
|
{ kind: CompilationJobKind.Tmpl, fn: phaseLocalRefs },
|
|
@@ -21815,7 +22472,7 @@ const phases = [
|
|
|
21815
22472
|
{ kind: CompilationJobKind.Both, fn: phaseTemporaryVariables },
|
|
21816
22473
|
{ kind: CompilationJobKind.Tmpl, fn: phaseSlotAllocation },
|
|
21817
22474
|
{ kind: CompilationJobKind.Tmpl, fn: phaseResolveI18nPlaceholders },
|
|
21818
|
-
{ kind: CompilationJobKind.Tmpl, fn:
|
|
22475
|
+
{ kind: CompilationJobKind.Tmpl, fn: phaseTrackFnGeneration },
|
|
21819
22476
|
{ kind: CompilationJobKind.Tmpl, fn: phaseI18nMessageExtraction },
|
|
21820
22477
|
{ kind: CompilationJobKind.Tmpl, fn: phaseI18nConstCollection },
|
|
21821
22478
|
{ kind: CompilationJobKind.Tmpl, fn: phaseConstTraitCollection },
|
|
@@ -21830,8 +22487,6 @@ const phases = [
|
|
|
21830
22487
|
{ kind: CompilationJobKind.Tmpl, fn: phaseEmptyElements },
|
|
21831
22488
|
{ kind: CompilationJobKind.Tmpl, fn: phaseNonbindable },
|
|
21832
22489
|
{ kind: CompilationJobKind.Both, fn: phasePureFunctionExtraction },
|
|
21833
|
-
{ kind: CompilationJobKind.Tmpl, fn: phaseAlignPipeVariadicVarOffset },
|
|
21834
|
-
{ kind: CompilationJobKind.Both, fn: phaseOrdering },
|
|
21835
22490
|
{ kind: CompilationJobKind.Both, fn: phaseReify },
|
|
21836
22491
|
{ kind: CompilationJobKind.Both, fn: phaseChaining },
|
|
21837
22492
|
];
|
|
@@ -22033,6 +22688,12 @@ function ingestNodes(unit, template) {
|
|
|
22033
22688
|
else if (node instanceof DeferredBlock) {
|
|
22034
22689
|
ingestDeferBlock(unit, node);
|
|
22035
22690
|
}
|
|
22691
|
+
else if (node instanceof Icu$1) {
|
|
22692
|
+
ingestIcu(unit, node);
|
|
22693
|
+
}
|
|
22694
|
+
else if (node instanceof ForLoopBlock) {
|
|
22695
|
+
ingestForBlock(unit, node);
|
|
22696
|
+
}
|
|
22036
22697
|
else {
|
|
22037
22698
|
throw new Error(`Unsupported template node: ${node.constructor.name}`);
|
|
22038
22699
|
}
|
|
@@ -22081,17 +22742,18 @@ function ingestTemplate(unit, tmpl) {
|
|
|
22081
22742
|
[namespacePrefix, tagNameWithoutNamespace] = splitNsName(tmpl.tagName);
|
|
22082
22743
|
}
|
|
22083
22744
|
const i18nPlaceholder = tmpl.i18n instanceof TagPlaceholder ? tmpl.i18n : undefined;
|
|
22084
|
-
|
|
22085
|
-
const tplOp = createTemplateOp(childView.xref, tagNameWithoutNamespace ?? 'ng-template', namespaceForKey(namespacePrefix), false, i18nPlaceholder, tmpl.startSourceSpan);
|
|
22745
|
+
const tplOp = createTemplateOp(childView.xref, tagNameWithoutNamespace, namespaceForKey(namespacePrefix), false, i18nPlaceholder, tmpl.startSourceSpan);
|
|
22086
22746
|
unit.create.push(tplOp);
|
|
22087
22747
|
ingestBindings(unit, tplOp, tmpl);
|
|
22088
22748
|
ingestReferences(tplOp, tmpl);
|
|
22089
22749
|
ingestNodes(childView, tmpl.children);
|
|
22090
22750
|
for (const { name, value } of tmpl.variables) {
|
|
22091
|
-
childView.contextVariables.set(name, value);
|
|
22751
|
+
childView.contextVariables.set(name, value !== '' ? value : '$implicit');
|
|
22092
22752
|
}
|
|
22093
|
-
// If there is an i18n message associated with
|
|
22094
|
-
|
|
22753
|
+
// If this is a plain template and there is an i18n message associated with it, insert i18n start
|
|
22754
|
+
// and end ops. For structural directive templates, the i18n ops will be added when ingesting the
|
|
22755
|
+
// element/template the directive is placed on.
|
|
22756
|
+
if (isPlainTemplate(tmpl) && tmpl.i18n instanceof Message) {
|
|
22095
22757
|
const id = unit.job.allocateXrefId();
|
|
22096
22758
|
OpList.insertAfter(createI18nStartOp(id, tmpl.i18n), childView.create.head);
|
|
22097
22759
|
OpList.insertBefore(createI18nEndOp(id), childView.create.tail);
|
|
@@ -22101,7 +22763,7 @@ function ingestTemplate(unit, tmpl) {
|
|
|
22101
22763
|
* Ingest a literal text node from the AST into the given `ViewCompilation`.
|
|
22102
22764
|
*/
|
|
22103
22765
|
function ingestContent(unit, content) {
|
|
22104
|
-
const op = createProjectionOp(unit.job.allocateXrefId(), content.selector);
|
|
22766
|
+
const op = createProjectionOp(unit.job.allocateXrefId(), content.selector, content.sourceSpan);
|
|
22105
22767
|
for (const attr of content.attributes) {
|
|
22106
22768
|
ingestBinding(unit, op.xref, attr.name, literal(attr.value), 1 /* e.BindingType.Attribute */, null, SecurityContext.NONE, attr.sourceSpan, BindingFlags.TextValue);
|
|
22107
22769
|
}
|
|
@@ -22227,6 +22889,60 @@ function ingestDeferBlock(unit, deferBlock) {
|
|
|
22227
22889
|
// Add all ops to the view.
|
|
22228
22890
|
unit.create.push(deferOnOp);
|
|
22229
22891
|
}
|
|
22892
|
+
function ingestIcu(unit, icu) {
|
|
22893
|
+
if (icu.i18n instanceof Message) {
|
|
22894
|
+
const xref = unit.job.allocateXrefId();
|
|
22895
|
+
unit.create.push(createIcuOp(xref, icu.i18n, null));
|
|
22896
|
+
unit.update.push(createIcuUpdateOp(xref, null));
|
|
22897
|
+
}
|
|
22898
|
+
else {
|
|
22899
|
+
throw Error(`Unhandled i18n metadata type for ICU: ${icu.i18n?.constructor.name}`);
|
|
22900
|
+
}
|
|
22901
|
+
}
|
|
22902
|
+
/**
|
|
22903
|
+
* Ingest an `@for` block into the given `ViewCompilation`.
|
|
22904
|
+
*/
|
|
22905
|
+
function ingestForBlock(unit, forBlock) {
|
|
22906
|
+
const repeaterView = unit.job.allocateView(unit.xref);
|
|
22907
|
+
const createRepeaterAlias = (ident, repeaterVar) => {
|
|
22908
|
+
repeaterView.aliases.add({
|
|
22909
|
+
kind: SemanticVariableKind.Alias,
|
|
22910
|
+
name: null,
|
|
22911
|
+
identifier: ident,
|
|
22912
|
+
expression: new DerivedRepeaterVarExpr(repeaterView.xref, repeaterVar),
|
|
22913
|
+
});
|
|
22914
|
+
};
|
|
22915
|
+
// Set all the context variables and aliases available in the repeater.
|
|
22916
|
+
repeaterView.contextVariables.set(forBlock.item.name, forBlock.item.value);
|
|
22917
|
+
repeaterView.contextVariables.set(forBlock.contextVariables.$index.name, forBlock.contextVariables.$index.value);
|
|
22918
|
+
repeaterView.contextVariables.set(forBlock.contextVariables.$count.name, forBlock.contextVariables.$count.value);
|
|
22919
|
+
createRepeaterAlias(forBlock.contextVariables.$first.name, DerivedRepeaterVarIdentity.First);
|
|
22920
|
+
createRepeaterAlias(forBlock.contextVariables.$last.name, DerivedRepeaterVarIdentity.Last);
|
|
22921
|
+
createRepeaterAlias(forBlock.contextVariables.$even.name, DerivedRepeaterVarIdentity.Even);
|
|
22922
|
+
createRepeaterAlias(forBlock.contextVariables.$odd.name, DerivedRepeaterVarIdentity.Odd);
|
|
22923
|
+
const sourceSpan = convertSourceSpan(forBlock.trackBy.span, forBlock.sourceSpan);
|
|
22924
|
+
const track = convertAst(forBlock.trackBy, unit.job, sourceSpan);
|
|
22925
|
+
ingestNodes(repeaterView, forBlock.children);
|
|
22926
|
+
let emptyView = null;
|
|
22927
|
+
if (forBlock.empty !== null) {
|
|
22928
|
+
emptyView = unit.job.allocateView(unit.xref);
|
|
22929
|
+
ingestNodes(emptyView, forBlock.empty.children);
|
|
22930
|
+
}
|
|
22931
|
+
const varNames = {
|
|
22932
|
+
$index: forBlock.contextVariables.$index.name,
|
|
22933
|
+
$count: forBlock.contextVariables.$count.name,
|
|
22934
|
+
$first: forBlock.contextVariables.$first.name,
|
|
22935
|
+
$last: forBlock.contextVariables.$last.name,
|
|
22936
|
+
$even: forBlock.contextVariables.$even.name,
|
|
22937
|
+
$odd: forBlock.contextVariables.$odd.name,
|
|
22938
|
+
$implicit: forBlock.item.name,
|
|
22939
|
+
};
|
|
22940
|
+
const repeaterCreate = createRepeaterCreateOp(repeaterView.xref, emptyView?.xref ?? null, track, varNames, forBlock.sourceSpan);
|
|
22941
|
+
unit.create.push(repeaterCreate);
|
|
22942
|
+
const expression = convertAst(forBlock.expression, unit.job, convertSourceSpan(forBlock.expression.span, forBlock.sourceSpan));
|
|
22943
|
+
const repeater = createRepeaterOp(repeaterCreate.xref, expression, forBlock.sourceSpan);
|
|
22944
|
+
unit.update.push(repeater);
|
|
22945
|
+
}
|
|
22230
22946
|
/**
|
|
22231
22947
|
* Convert a template AST expression into an output AST expression.
|
|
22232
22948
|
*/
|
|
@@ -22279,7 +22995,8 @@ function convertAst(ast, job, baseSourceSpan) {
|
|
|
22279
22995
|
else if (ast instanceof LiteralMap) {
|
|
22280
22996
|
const entries = ast.keys.map((key, idx) => {
|
|
22281
22997
|
const value = ast.values[idx];
|
|
22282
|
-
// TODO: should literals have source maps, or do we just map the whole surrounding
|
|
22998
|
+
// TODO: should literals have source maps, or do we just map the whole surrounding
|
|
22999
|
+
// expression?
|
|
22283
23000
|
return new LiteralMapEntry(key.key, convertAst(value, job, baseSourceSpan), key.quoted);
|
|
22284
23001
|
});
|
|
22285
23002
|
return new LiteralMapExpr(entries, undefined, convertSourceSpan(ast.span, baseSourceSpan));
|
|
@@ -22320,16 +23037,35 @@ function convertAst(ast, job, baseSourceSpan) {
|
|
|
22320
23037
|
throw new Error(`Unhandled expression type: ${ast.constructor.name}`);
|
|
22321
23038
|
}
|
|
22322
23039
|
}
|
|
23040
|
+
/**
|
|
23041
|
+
* Checks whether the given template is a plain ng-template (as opposed to another kind of template
|
|
23042
|
+
* such as a structural directive template or control flow template). This is checked based on the
|
|
23043
|
+
* tagName. We can expect that only plain ng-templates will come through with a tagName of
|
|
23044
|
+
* 'ng-template'.
|
|
23045
|
+
*
|
|
23046
|
+
* Here are some of the cases we expect:
|
|
23047
|
+
*
|
|
23048
|
+
* | Angular HTML | Template tagName |
|
|
23049
|
+
* | ---------------------------------- | ------------------ |
|
|
23050
|
+
* | `<ng-template>` | 'ng-template' |
|
|
23051
|
+
* | `<div *ngIf="true">` | 'div' |
|
|
23052
|
+
* | `<svg><ng-template>` | 'svg:ng-template' |
|
|
23053
|
+
* | `@if (true) {` | 'Conditional' |
|
|
23054
|
+
* | `<ng-template *ngIf>` (plain) | 'ng-template' |
|
|
23055
|
+
* | `<ng-template *ngIf>` (structural) | null |
|
|
23056
|
+
*/
|
|
23057
|
+
function isPlainTemplate(tmpl) {
|
|
23058
|
+
return splitNsName(tmpl.tagName ?? '')[1] === 'ng-template';
|
|
23059
|
+
}
|
|
22323
23060
|
/**
|
|
22324
23061
|
* Process all of the bindings on an element-like structure in the template AST and convert them
|
|
22325
23062
|
* to their IR representation.
|
|
22326
23063
|
*/
|
|
22327
23064
|
function ingestBindings(unit, op, element) {
|
|
22328
23065
|
let flags = BindingFlags.None;
|
|
22329
|
-
const isPlainTemplate = element instanceof Template && splitNsName(element.tagName ?? '')[1] === 'ng-template';
|
|
22330
23066
|
if (element instanceof Template) {
|
|
22331
23067
|
flags |= BindingFlags.OnNgTemplateElement;
|
|
22332
|
-
if (isPlainTemplate) {
|
|
23068
|
+
if (element instanceof Template && isPlainTemplate(element)) {
|
|
22333
23069
|
flags |= BindingFlags.BindingTargetsTemplate;
|
|
22334
23070
|
}
|
|
22335
23071
|
const templateAttrFlags = flags | BindingFlags.BindingTargetsTemplate | BindingFlags.IsStructuralTemplateAttribute;
|
|
@@ -22358,7 +23094,7 @@ function ingestBindings(unit, op, element) {
|
|
|
22358
23094
|
throw Error('Animation listener should have a phase');
|
|
22359
23095
|
}
|
|
22360
23096
|
}
|
|
22361
|
-
if (element instanceof Template && !isPlainTemplate) {
|
|
23097
|
+
if (element instanceof Template && !isPlainTemplate(element)) {
|
|
22362
23098
|
unit.create.push(createExtractedAttributeOp(op.xref, BindingKind.Property, output.name, null));
|
|
22363
23099
|
continue;
|
|
22364
23100
|
}
|
|
@@ -23093,7 +23829,7 @@ class WhitespaceVisitor {
|
|
|
23093
23829
|
return expansionCase;
|
|
23094
23830
|
}
|
|
23095
23831
|
visitBlock(block, context) {
|
|
23096
|
-
return new Block(block.name, block.parameters, visitAllWithSiblings(this, block.children), block.sourceSpan, block.startSourceSpan, block.endSourceSpan);
|
|
23832
|
+
return new Block(block.name, block.parameters, visitAllWithSiblings(this, block.children), block.sourceSpan, block.nameSpan, block.startSourceSpan, block.endSourceSpan);
|
|
23097
23833
|
}
|
|
23098
23834
|
visitBlockParameter(parameter, context) {
|
|
23099
23835
|
return parameter;
|
|
@@ -23732,7 +24468,7 @@ function createSwitchBlock(ast, visitor, bindingParser) {
|
|
|
23732
24468
|
continue;
|
|
23733
24469
|
}
|
|
23734
24470
|
if ((node.name !== 'case' || node.parameters.length === 0) && node.name !== 'default') {
|
|
23735
|
-
unknownBlocks.push(new UnknownBlock(node.name, node.sourceSpan));
|
|
24471
|
+
unknownBlocks.push(new UnknownBlock(node.name, node.sourceSpan, node.nameSpan));
|
|
23736
24472
|
continue;
|
|
23737
24473
|
}
|
|
23738
24474
|
const expression = node.name === 'case' ?
|
|
@@ -24710,7 +25446,7 @@ class HtmlAstToIvyAst {
|
|
|
24710
25446
|
errorMessage = `Unrecognized block @${block.name}.`;
|
|
24711
25447
|
}
|
|
24712
25448
|
result = {
|
|
24713
|
-
node: new UnknownBlock(block.name, block.sourceSpan),
|
|
25449
|
+
node: new UnknownBlock(block.name, block.sourceSpan, block.nameSpan),
|
|
24714
25450
|
errors: [new ParseError(block.sourceSpan, errorMessage)],
|
|
24715
25451
|
};
|
|
24716
25452
|
break;
|
|
@@ -29526,7 +30262,7 @@ function publishFacade(global) {
|
|
|
29526
30262
|
* @description
|
|
29527
30263
|
* Entry point for all public APIs of the compiler package.
|
|
29528
30264
|
*/
|
|
29529
|
-
const VERSION = new Version('17.0.0-
|
|
30265
|
+
const VERSION = new Version('17.0.0-rc.1');
|
|
29530
30266
|
|
|
29531
30267
|
class CompilerConfig {
|
|
29532
30268
|
constructor({ defaultEncapsulation = ViewEncapsulation.Emulated, preserveWhitespaces, strictInjectionParameters } = {}) {
|
|
@@ -31056,7 +31792,7 @@ const MINIMUM_PARTIAL_LINKER_VERSION$6 = '12.0.0';
|
|
|
31056
31792
|
function compileDeclareClassMetadata(metadata) {
|
|
31057
31793
|
const definitionMap = new DefinitionMap();
|
|
31058
31794
|
definitionMap.set('minVersion', literal(MINIMUM_PARTIAL_LINKER_VERSION$6));
|
|
31059
|
-
definitionMap.set('version', literal('17.0.0-
|
|
31795
|
+
definitionMap.set('version', literal('17.0.0-rc.1'));
|
|
31060
31796
|
definitionMap.set('ngImport', importExpr(Identifiers.core));
|
|
31061
31797
|
definitionMap.set('type', metadata.type);
|
|
31062
31798
|
definitionMap.set('decorators', metadata.decorators);
|
|
@@ -31164,7 +31900,7 @@ function createDirectiveDefinitionMap(meta) {
|
|
|
31164
31900
|
// in 16.1 is actually used.
|
|
31165
31901
|
const minVersion = hasTransformFunctions ? MINIMUM_PARTIAL_LINKER_VERSION$5 : '14.0.0';
|
|
31166
31902
|
definitionMap.set('minVersion', literal(minVersion));
|
|
31167
|
-
definitionMap.set('version', literal('17.0.0-
|
|
31903
|
+
definitionMap.set('version', literal('17.0.0-rc.1'));
|
|
31168
31904
|
// e.g. `type: MyDirective`
|
|
31169
31905
|
definitionMap.set('type', meta.type.value);
|
|
31170
31906
|
if (meta.isStandalone) {
|
|
@@ -31441,7 +32177,7 @@ const MINIMUM_PARTIAL_LINKER_VERSION$4 = '12.0.0';
|
|
|
31441
32177
|
function compileDeclareFactoryFunction(meta) {
|
|
31442
32178
|
const definitionMap = new DefinitionMap();
|
|
31443
32179
|
definitionMap.set('minVersion', literal(MINIMUM_PARTIAL_LINKER_VERSION$4));
|
|
31444
|
-
definitionMap.set('version', literal('17.0.0-
|
|
32180
|
+
definitionMap.set('version', literal('17.0.0-rc.1'));
|
|
31445
32181
|
definitionMap.set('ngImport', importExpr(Identifiers.core));
|
|
31446
32182
|
definitionMap.set('type', meta.type.value);
|
|
31447
32183
|
definitionMap.set('deps', compileDependencies(meta.deps));
|
|
@@ -31476,7 +32212,7 @@ function compileDeclareInjectableFromMetadata(meta) {
|
|
|
31476
32212
|
function createInjectableDefinitionMap(meta) {
|
|
31477
32213
|
const definitionMap = new DefinitionMap();
|
|
31478
32214
|
definitionMap.set('minVersion', literal(MINIMUM_PARTIAL_LINKER_VERSION$3));
|
|
31479
|
-
definitionMap.set('version', literal('17.0.0-
|
|
32215
|
+
definitionMap.set('version', literal('17.0.0-rc.1'));
|
|
31480
32216
|
definitionMap.set('ngImport', importExpr(Identifiers.core));
|
|
31481
32217
|
definitionMap.set('type', meta.type.value);
|
|
31482
32218
|
// Only generate providedIn property if it has a non-null value
|
|
@@ -31527,7 +32263,7 @@ function compileDeclareInjectorFromMetadata(meta) {
|
|
|
31527
32263
|
function createInjectorDefinitionMap(meta) {
|
|
31528
32264
|
const definitionMap = new DefinitionMap();
|
|
31529
32265
|
definitionMap.set('minVersion', literal(MINIMUM_PARTIAL_LINKER_VERSION$2));
|
|
31530
|
-
definitionMap.set('version', literal('17.0.0-
|
|
32266
|
+
definitionMap.set('version', literal('17.0.0-rc.1'));
|
|
31531
32267
|
definitionMap.set('ngImport', importExpr(Identifiers.core));
|
|
31532
32268
|
definitionMap.set('type', meta.type.value);
|
|
31533
32269
|
definitionMap.set('providers', meta.providers);
|
|
@@ -31560,7 +32296,7 @@ function createNgModuleDefinitionMap(meta) {
|
|
|
31560
32296
|
throw new Error('Invalid path! Local compilation mode should not get into the partial compilation path');
|
|
31561
32297
|
}
|
|
31562
32298
|
definitionMap.set('minVersion', literal(MINIMUM_PARTIAL_LINKER_VERSION$1));
|
|
31563
|
-
definitionMap.set('version', literal('17.0.0-
|
|
32299
|
+
definitionMap.set('version', literal('17.0.0-rc.1'));
|
|
31564
32300
|
definitionMap.set('ngImport', importExpr(Identifiers.core));
|
|
31565
32301
|
definitionMap.set('type', meta.type.value);
|
|
31566
32302
|
// We only generate the keys in the metadata if the arrays contain values.
|
|
@@ -31611,7 +32347,7 @@ function compileDeclarePipeFromMetadata(meta) {
|
|
|
31611
32347
|
function createPipeDefinitionMap(meta) {
|
|
31612
32348
|
const definitionMap = new DefinitionMap();
|
|
31613
32349
|
definitionMap.set('minVersion', literal(MINIMUM_PARTIAL_LINKER_VERSION));
|
|
31614
|
-
definitionMap.set('version', literal('17.0.0-
|
|
32350
|
+
definitionMap.set('version', literal('17.0.0-rc.1'));
|
|
31615
32351
|
definitionMap.set('ngImport', importExpr(Identifiers.core));
|
|
31616
32352
|
// e.g. `type: MyPipe`
|
|
31617
32353
|
definitionMap.set('type', meta.type.value);
|