@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.
Files changed (66) hide show
  1. package/esm2022/src/ml_parser/ast.mjs +3 -2
  2. package/esm2022/src/ml_parser/html_tags.mjs +4 -4
  3. package/esm2022/src/ml_parser/html_whitespaces.mjs +2 -2
  4. package/esm2022/src/ml_parser/icu_ast_expander.mjs +2 -2
  5. package/esm2022/src/ml_parser/lexer.mjs +2 -13
  6. package/esm2022/src/ml_parser/parser.mjs +3 -3
  7. package/esm2022/src/render3/partial/class_metadata.mjs +1 -1
  8. package/esm2022/src/render3/partial/directive.mjs +1 -1
  9. package/esm2022/src/render3/partial/factory.mjs +1 -1
  10. package/esm2022/src/render3/partial/injectable.mjs +1 -1
  11. package/esm2022/src/render3/partial/injector.mjs +1 -1
  12. package/esm2022/src/render3/partial/ng_module.mjs +1 -1
  13. package/esm2022/src/render3/partial/pipe.mjs +1 -1
  14. package/esm2022/src/render3/r3_ast.mjs +3 -2
  15. package/esm2022/src/render3/r3_control_flow.mjs +2 -2
  16. package/esm2022/src/render3/r3_template_transform.mjs +2 -2
  17. package/esm2022/src/shadow_css.mjs +10 -5
  18. package/esm2022/src/template/pipeline/ir/src/enums.mjs +81 -26
  19. package/esm2022/src/template/pipeline/ir/src/expression.mjs +65 -3
  20. package/esm2022/src/template/pipeline/ir/src/ops/create.mjs +41 -6
  21. package/esm2022/src/template/pipeline/ir/src/ops/shared.mjs +3 -2
  22. package/esm2022/src/template/pipeline/ir/src/ops/update.mjs +25 -3
  23. package/esm2022/src/template/pipeline/ir/src/variable.mjs +1 -1
  24. package/esm2022/src/template/pipeline/src/compilation.mjs +6 -1
  25. package/esm2022/src/template/pipeline/src/emit.mjs +17 -6
  26. package/esm2022/src/template/pipeline/src/ingest.mjs +92 -11
  27. package/esm2022/src/template/pipeline/src/instruction.mjs +28 -7
  28. package/esm2022/src/template/pipeline/src/phases/attribute_extraction.mjs +3 -3
  29. package/esm2022/src/template/pipeline/src/phases/binding_specialization.mjs +1 -1
  30. package/esm2022/src/template/pipeline/src/phases/collapse_singleton_interpolations.mjs +31 -0
  31. package/esm2022/src/template/pipeline/src/phases/conditionals.mjs +4 -1
  32. package/esm2022/src/template/pipeline/src/phases/generate_variables.mjs +11 -6
  33. package/esm2022/src/template/pipeline/src/phases/i18n_const_collection.mjs +2 -2
  34. package/esm2022/src/template/pipeline/src/phases/i18n_message_extraction.mjs +14 -4
  35. package/esm2022/src/template/pipeline/src/phases/i18n_text_extraction.mjs +2 -2
  36. package/esm2022/src/template/pipeline/src/phases/icu_extraction.mjs +53 -0
  37. package/esm2022/src/template/pipeline/src/phases/local_refs.mjs +1 -2
  38. package/esm2022/src/template/pipeline/src/phases/namespace.mjs +2 -2
  39. package/esm2022/src/template/pipeline/src/phases/naming.mjs +23 -4
  40. package/esm2022/src/template/pipeline/src/phases/ng_container.mjs +1 -6
  41. package/esm2022/src/template/pipeline/src/phases/ordering.mjs +40 -31
  42. package/esm2022/src/template/pipeline/src/phases/phase_remove_content_selectors.mjs +8 -8
  43. package/esm2022/src/template/pipeline/src/phases/pipe_creation.mjs +15 -3
  44. package/esm2022/src/template/pipeline/src/phases/pure_function_extraction.mjs +2 -1
  45. package/esm2022/src/template/pipeline/src/phases/reify.mjs +33 -2
  46. package/esm2022/src/template/pipeline/src/phases/repeater_derived_vars.mjs +40 -0
  47. package/esm2022/src/template/pipeline/src/phases/resolve_contexts.mjs +5 -1
  48. package/esm2022/src/template/pipeline/src/phases/resolve_i18n_placeholders.mjs +134 -36
  49. package/esm2022/src/template/pipeline/src/phases/resolve_names.mjs +2 -1
  50. package/esm2022/src/template/pipeline/src/phases/resolve_sanitizers.mjs +5 -6
  51. package/esm2022/src/template/pipeline/src/phases/save_restore_view.mjs +3 -3
  52. package/esm2022/src/template/pipeline/src/phases/slot_allocation.mjs +3 -3
  53. package/esm2022/src/template/pipeline/src/phases/track_fn_generation.mjs +49 -0
  54. package/esm2022/src/template/pipeline/src/phases/track_fn_optimization.mjs +75 -0
  55. package/esm2022/src/template/pipeline/src/phases/track_variables.mjs +31 -0
  56. package/esm2022/src/template/pipeline/src/phases/var_counting.mjs +36 -3
  57. package/esm2022/src/template/pipeline/src/phases/variable_optimization.mjs +43 -3
  58. package/esm2022/src/template/pipeline/src/phases/wrap_icus.mjs +34 -0
  59. package/esm2022/src/template/pipeline/src/util/elements.mjs +6 -6
  60. package/esm2022/src/version.mjs +1 -1
  61. package/fesm2022/compiler.mjs +1106 -370
  62. package/fesm2022/compiler.mjs.map +1 -1
  63. package/index.d.ts +5 -3
  64. package/package.json +2 -2
  65. package/esm2022/src/template/pipeline/src/phases/align_pipe_variadic_var_offset.mjs +0 -42
  66. package/esm2022/src/template/pipeline/src/phases/propagate_i18n_placeholders.mjs +0 -39
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @license Angular v17.0.0-next.8
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(_placeholderRe) && selector[res.index + 1]?.match(/[a-fA-F\d]/)) {
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
- selector = this._escapeRegexMatches(selector, /(\\.)/g);
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(_placeholderRe, (_ph, index) => this.placeholders[+index]);
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"] = 34] = "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"] = 35] = "I18n";
8845
+ OpKind[OpKind["I18n"] = 37] = "I18n";
8832
8846
  /**
8833
8847
  * The end of an i18n block.
8834
8848
  */
8835
- OpKind[OpKind["I18nEnd"] = 36] = "I18nEnd";
8849
+ OpKind[OpKind["I18nEnd"] = 38] = "I18nEnd";
8836
8850
  /**
8837
8851
  * An expression in an i18n message.
8838
8852
  */
8839
- OpKind[OpKind["I18nExpression"] = 37] = "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"] = 38] = "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"] = 2] = "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"] = 3] = "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"] = 4] = "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"] = 5] = "GetCurrentView";
8899
+ ExpressionKind[ExpressionKind["GetCurrentView"] = 6] = "GetCurrentView";
8874
8900
  /**
8875
8901
  * Runtime operation to restore a snapshotted view.
8876
8902
  */
8877
- ExpressionKind[ExpressionKind["RestoreView"] = 6] = "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"] = 7] = "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"] = 8] = "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"] = 9] = "PureFunctionParameterExpr";
8915
+ ExpressionKind[ExpressionKind["PureFunctionParameterExpr"] = 10] = "PureFunctionParameterExpr";
8890
8916
  /**
8891
8917
  * Binding to a pipe transformation.
8892
8918
  */
8893
- ExpressionKind[ExpressionKind["PipeBinding"] = 10] = "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"] = 11] = "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"] = 12] = "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"] = 13] = "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"] = 14] = "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"] = 15] = "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"] = 16] = "EmptyExpr";
8943
+ ExpressionKind[ExpressionKind["EmptyExpr"] = 17] = "EmptyExpr";
8918
8944
  /*
8919
8945
  * An assignment to a temporary variable.
8920
8946
  */
8921
- ExpressionKind[ExpressionKind["AssignTemporaryExpr"] = 17] = "AssignTemporaryExpr";
8947
+ ExpressionKind[ExpressionKind["AssignTemporaryExpr"] = 18] = "AssignTemporaryExpr";
8922
8948
  /**
8923
8949
  * A reference to a temporary variable.
8924
8950
  */
8925
- ExpressionKind[ExpressionKind["ReadTemporaryExpr"] = 18] = "ReadTemporaryExpr";
8951
+ ExpressionKind[ExpressionKind["ReadTemporaryExpr"] = 19] = "ReadTemporaryExpr";
8926
8952
  /**
8927
8953
  * An expression representing a sanitizer function.
8928
8954
  */
8929
- ExpressionKind[ExpressionKind["SanitizerExpr"] = 19] = "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"] = 20] = "SlotLiteralExpr";
8959
+ ExpressionKind[ExpressionKind["SlotLiteralExpr"] = 21] = "SlotLiteralExpr";
8934
8960
  /**
8935
8961
  * A test expression for a conditional op.
8936
8962
  */
8937
- ExpressionKind[ExpressionKind["ConditionalCase"] = 21] = "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
- * Creates an op to apply i18n expression ops.
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
- return false;
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.Projection
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: null,
10754
+ attributes: [],
10587
10755
  localRefs: [],
10588
- nonBindable: false,
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 getElementsByXrefId(unit) {
11095
- const elements = new Map();
11172
+ function createOpXrefMap(unit) {
11173
+ const map = new Map();
11096
11174
  for (const op of unit.create) {
11097
- if (!isElementOrContainerOp(op)) {
11175
+ if (!hasConsumesSlotTrait(op)) {
11098
11176
  continue;
11099
11177
  }
11100
- elements.set(op.xref, op);
11178
+ map.set(op.xref, op);
11101
11179
  }
11102
- return elements;
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 = getElementsByXrefId(unit);
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$3(elements, op.target));
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$3(elements, op.target));
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$3(elements, op.target));
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$3(elements, xref) {
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$3(elements, op.target);
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$2(elements, xref) {
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$2(elements, op.target);
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
- for (const [name, value] of view.job.views.get(scope.view).contextVariables) {
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 || op.kind === OpKind.I18n) {
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.hasOwnProperty(knownTagName) && getNsPrefix(knownTagName) === null) {
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._isBlockStart() || this._cursor.peek() === $RBRACE)) {
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 ((op.kind === OpKind.I18nStart || op.kind === OpKind.I18n)) {
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
- // TODO: figure out transformFn.
19067
- const statements = getTranslationDeclStmts$1(op.message, mainVar, closureVar, params, undefined /*transformFn*/);
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.Element && op.kind !== OpKind.ElementStart) {
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
- addNamesToView(childView, `${baseName}_${prefixWithNamespace(op.tag, op.namespace)}_${op.slot}`, state, compatibility);
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
- variable.name = `${variable.identifier}_${state.index++}`;
19591
+ // TODO: Prefix increment and `_r` for compatiblity only.
19592
+ variable.name = `${variable.identifier}_r${++state.index}`;
19416
19593
  break;
19417
19594
  default:
19418
- variable.name = `_r${state.index++}`;
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$1(elements, xref) {
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$1(elements, op.xref).nonBindable) {
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
- * binding kinds. Ops will be collected into groups, then optionally transformed, before recombining
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
- * As above, but for update ops.
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: op => op.kind === OpKind.HostProperty && op.expression instanceof Interpolation },
19605
- { test: op => op.kind === OpKind.HostProperty && !(op.expression instanceof Interpolation) },
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: op => op.kind === OpKind.Property && op.expression instanceof Interpolation },
19611
- { test: op => op.kind === OpKind.Property && !(op.expression instanceof Interpolation) },
19612
- { test: kindTest(OpKind.Attribute) },
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
- let opsToOrder = [];
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
- opsToOrder = [];
19641
- for (const op of unit.update) {
19642
- if (handledOpKinds.has(op.kind)) {
19643
- opsToOrder.push(op);
19644
- OpList.remove(op);
19645
- }
19646
- else {
19647
- OpList.insertBefore(reorder(opsToOrder, UPDATE_ORDERING), op);
19648
- opsToOrder = [];
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 = getElementsByXrefId(unit);
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 = lookupElement(elements, op.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
- continue;
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 lookupElement(elements, xref) {
19728
- const el = elements.get(xref);
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 element-like target.');
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
- if (!hasDependsOnSlotContextTrait(updateOp)) {
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
- addPipeToCreationBlock(unit, updateOp.target, expr);
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 !== null) {
20273
+ if (projectionSlotIndex !== 0 || attributes.length > 0) {
20110
20274
  args.push(literal(projectionSlotIndex));
20111
- if (attributes != null) {
20112
- args.push(literal(attributes));
20275
+ if (attributes.length > 0) {
20276
+ args.push(literalArr(attributes.map(attr => literal(attr))));
20113
20277
  }
20114
20278
  }
20115
- return call(Identifiers.projection, args, null);
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 closing of a tag. (Can only be used together with ElementTag or
20897
- * TemplateTag)
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"] = 4] = "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
- op.params.set(placeholder, literal(this.serializeValues(placeholderValues)));
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 i18nOps = new Map();
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
- case OpKind.Template:
20974
- // For elements with i18n placeholders, record its slot value in the params map under both
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
- const subTemplateIndex = getSubTemplateIndexForTag(job, currentI18nOp, op);
20982
- const flags = op.kind === OpKind.Template ? I18nParamValueFlags.TemplateTag :
20983
- I18nParamValueFlags.ElementTag;
20984
- addParam(params, currentI18nOp, startName, op.slot, subTemplateIndex, flags);
20985
- addParam(params, currentI18nOp, closeName, op.slot, subTemplateIndex, flags | I18nParamValueFlags.CloseTag);
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.name, index++, i18nOp.subTemplateIndex);
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) ?? new I18nPlaceholderParams();
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 the
21019
- * child i18n block inside the template. For all other ops, use the subTemplateIndex of the i18n
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 getSubTemplateIndexForTag(job, i18nOp, op) {
21023
- if (op.kind === OpKind.Template) {
21024
- for (const childOp of job.views.get(op.xref).create) {
21025
- if (childOp.kind === OpKind.I18nStart) {
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 = getElementsByXrefId(unit);
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 (op.kind === OpKind.Element || op.kind === OpKind.ElementStart) &&
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
- if (count !== 1) {
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: phasePropagateI18nPlaceholders },
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
- // TODO: validate the fallback tag name here.
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 this template, insert i18n start and end ops.
22094
- if (tmpl.i18n instanceof Message) {
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 expression?
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-next.8');
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-next.8'));
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-next.8'));
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-next.8'));
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-next.8'));
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-next.8'));
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-next.8'));
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-next.8'));
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);