@angular/compiler 17.1.0-next.3 → 17.1.0-next.5

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 (59) hide show
  1. package/esm2022/src/compiler.mjs +2 -2
  2. package/esm2022/src/compiler_facade_interface.mjs +1 -1
  3. package/esm2022/src/constant_pool.mjs +5 -3
  4. package/esm2022/src/jit_compiler_facade.mjs +46 -17
  5. package/esm2022/src/ml_parser/tags.mjs +8 -3
  6. package/esm2022/src/render3/partial/api.mjs +1 -1
  7. package/esm2022/src/render3/partial/class_metadata.mjs +1 -1
  8. package/esm2022/src/render3/partial/directive.mjs +112 -17
  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_identifiers.mjs +4 -1
  15. package/esm2022/src/render3/view/api.mjs +1 -1
  16. package/esm2022/src/render3/view/compiler.mjs +24 -9
  17. package/esm2022/src/render3/view/template.mjs +14 -9
  18. package/esm2022/src/render3/view/util.mjs +8 -2
  19. package/esm2022/src/template/pipeline/ir/src/enums.mjs +15 -19
  20. package/esm2022/src/template/pipeline/ir/src/expression.mjs +26 -20
  21. package/esm2022/src/template/pipeline/ir/src/ops/create.mjs +47 -17
  22. package/esm2022/src/template/pipeline/ir/src/ops/host.mjs +4 -2
  23. package/esm2022/src/template/pipeline/ir/src/ops/update.mjs +18 -10
  24. package/esm2022/src/template/pipeline/src/emit.mjs +6 -6
  25. package/esm2022/src/template/pipeline/src/ingest.mjs +295 -184
  26. package/esm2022/src/template/pipeline/src/instruction.mjs +30 -24
  27. package/esm2022/src/template/pipeline/src/phases/assign_i18n_slot_dependencies.mjs +40 -56
  28. package/esm2022/src/template/pipeline/src/phases/attribute_extraction.mjs +24 -22
  29. package/esm2022/src/template/pipeline/src/phases/binding_specialization.mjs +4 -4
  30. package/esm2022/src/template/pipeline/src/phases/const_collection.mjs +60 -15
  31. package/esm2022/src/template/pipeline/src/phases/convert_i18n_bindings.mjs +2 -2
  32. package/esm2022/src/template/pipeline/src/phases/create_defer_deps_fns.mjs +3 -2
  33. package/esm2022/src/template/pipeline/src/phases/create_i18n_contexts.mjs +63 -29
  34. package/esm2022/src/template/pipeline/src/phases/deduplicate_text_bindings.mjs +40 -0
  35. package/esm2022/src/template/pipeline/src/phases/extract_i18n_messages.mjs +52 -49
  36. package/esm2022/src/template/pipeline/src/phases/generate_variables.mjs +7 -1
  37. package/esm2022/src/template/pipeline/src/phases/host_style_property_parsing.mjs +2 -2
  38. package/esm2022/src/template/pipeline/src/phases/i18n_const_collection.mjs +11 -8
  39. package/esm2022/src/template/pipeline/src/phases/i18n_text_extraction.mjs +22 -3
  40. package/esm2022/src/template/pipeline/src/phases/naming.mjs +13 -5
  41. package/esm2022/src/template/pipeline/src/phases/ordering.mjs +17 -5
  42. package/esm2022/src/template/pipeline/src/phases/parse_extracted_styles.mjs +22 -3
  43. package/esm2022/src/template/pipeline/src/phases/phase_remove_content_selectors.mjs +1 -10
  44. package/esm2022/src/template/pipeline/src/phases/propagate_i18n_blocks.mjs +31 -14
  45. package/esm2022/src/template/pipeline/src/phases/reify.mjs +46 -24
  46. package/esm2022/src/template/pipeline/src/phases/resolve_i18n_element_placeholders.mjs +60 -14
  47. package/esm2022/src/template/pipeline/src/phases/resolve_i18n_expression_placeholders.mjs +23 -10
  48. package/esm2022/src/template/pipeline/src/phases/resolve_sanitizers.mjs +78 -14
  49. package/esm2022/src/template/pipeline/src/phases/slot_allocation.mjs +3 -1
  50. package/esm2022/src/template/pipeline/src/phases/track_fn_generation.mjs +4 -1
  51. package/esm2022/src/template/pipeline/src/phases/var_counting.mjs +10 -1
  52. package/esm2022/src/template/pipeline/src/phases/wrap_icus.mjs +4 -3
  53. package/esm2022/src/template/pipeline/src/util/elements.mjs +8 -1
  54. package/esm2022/src/version.mjs +1 -1
  55. package/fesm2022/compiler.mjs +1252 -650
  56. package/fesm2022/compiler.mjs.map +1 -1
  57. package/index.d.ts +41 -8
  58. package/package.json +2 -2
  59. package/esm2022/src/template/pipeline/src/phases/resolve_i18n_icu_placeholders.mjs +0 -62
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @license Angular v17.1.0-next.3
2
+ * @license Angular v17.1.0-next.5
3
3
  * (c) 2010-2022 Google LLC. https://angular.io/
4
4
  * License: MIT
5
5
  */
@@ -2308,7 +2308,9 @@ class ConstantPool {
2308
2308
  }))));
2309
2309
  }
2310
2310
  }
2311
- getSharedFunctionReference(fn, prefix) {
2311
+ // TODO: useUniqueName(false) is necessary for naming compatibility with
2312
+ // TemplateDefinitionBuilder, but should be removed once Template Pipeline is the default.
2313
+ getSharedFunctionReference(fn, prefix, useUniqueName = true) {
2312
2314
  const isArrow = fn instanceof ArrowFunctionExpr;
2313
2315
  for (const current of this.statements) {
2314
2316
  // Arrow functions are saved as variables so we check if the
@@ -2323,7 +2325,7 @@ class ConstantPool {
2323
2325
  }
2324
2326
  }
2325
2327
  // Otherwise declare the function.
2326
- const name = this.uniqueName(prefix);
2328
+ const name = useUniqueName ? this.uniqueName(prefix) : prefix;
2327
2329
  this.statements.push(fn.toDeclStmt(name, StmtModifier.Final));
2328
2330
  return variable(name);
2329
2331
  }
@@ -2636,6 +2638,9 @@ class Identifiers {
2636
2638
  static { this.trustConstantHtml = { name: 'ɵɵtrustConstantHtml', moduleName: CORE }; }
2637
2639
  static { this.trustConstantResourceUrl = { name: 'ɵɵtrustConstantResourceUrl', moduleName: CORE }; }
2638
2640
  static { this.validateIframeAttribute = { name: 'ɵɵvalidateIframeAttribute', moduleName: CORE }; }
2641
+ // type-checking
2642
+ static { this.InputSignalBrandWriteType = { name: 'ɵINPUT_SIGNAL_BRAND_WRITE_TYPE', moduleName: CORE }; }
2643
+ static { this.UnwrapDirectiveSignalInputs = { name: 'ɵUnwrapDirectiveSignalInputs', moduleName: CORE }; }
2639
2644
  }
2640
2645
 
2641
2646
  const DASH_CASE_REGEXP = /-+([a-z0-9])/g;
@@ -3685,13 +3690,18 @@ var TagContentType;
3685
3690
  TagContentType[TagContentType["ESCAPABLE_RAW_TEXT"] = 1] = "ESCAPABLE_RAW_TEXT";
3686
3691
  TagContentType[TagContentType["PARSABLE_DATA"] = 2] = "PARSABLE_DATA";
3687
3692
  })(TagContentType || (TagContentType = {}));
3688
- function splitNsName(elementName) {
3693
+ function splitNsName(elementName, fatal = true) {
3689
3694
  if (elementName[0] != ':') {
3690
3695
  return [null, elementName];
3691
3696
  }
3692
3697
  const colonIndex = elementName.indexOf(':', 1);
3693
3698
  if (colonIndex === -1) {
3694
- throw new Error(`Unsupported format "${elementName}" expecting ":namespace:name"`);
3699
+ if (fatal) {
3700
+ throw new Error(`Unsupported format "${elementName}" expecting ":namespace:name"`);
3701
+ }
3702
+ else {
3703
+ return [null, elementName];
3704
+ }
3695
3705
  }
3696
3706
  return [elementName.slice(1, colonIndex), elementName.slice(colonIndex + 1)];
3697
3707
  }
@@ -4957,6 +4967,12 @@ function asLiteral(value) {
4957
4967
  }
4958
4968
  return literal(value, INFERRED_TYPE);
4959
4969
  }
4970
+ /**
4971
+ * Serializes inputs and outputs for `defineDirective` and `defineComponent`.
4972
+ *
4973
+ * This will attempt to generate optimized data structures to minimize memory or
4974
+ * file size of fully compiled applications.
4975
+ */
4960
4976
  function conditionallyCreateDirectiveBindingLiteral(map, keepDeclared) {
4961
4977
  const keys = Object.getOwnPropertyNames(map);
4962
4978
  if (keys.length === 0) {
@@ -8966,14 +8982,18 @@ var OpKind;
8966
8982
  * An instruction to update an ICU expression.
8967
8983
  */
8968
8984
  OpKind[OpKind["IcuEnd"] = 42] = "IcuEnd";
8985
+ /**
8986
+ * An instruction representing a placeholder in an ICU expression.
8987
+ */
8988
+ OpKind[OpKind["IcuPlaceholder"] = 43] = "IcuPlaceholder";
8969
8989
  /**
8970
8990
  * An i18n context containing information needed to generate an i18n message.
8971
8991
  */
8972
- OpKind[OpKind["I18nContext"] = 43] = "I18nContext";
8992
+ OpKind[OpKind["I18nContext"] = 44] = "I18nContext";
8973
8993
  /**
8974
8994
  * A creation op that corresponds to i18n attributes on an element.
8975
8995
  */
8976
- OpKind[OpKind["I18nAttributes"] = 44] = "I18nAttributes";
8996
+ OpKind[OpKind["I18nAttributes"] = 45] = "I18nAttributes";
8977
8997
  })(OpKind || (OpKind = {}));
8978
8998
  /**
8979
8999
  * Distinguishes different kinds of IR expressions.
@@ -9064,23 +9084,27 @@ var ExpressionKind;
9064
9084
  * An expression representing a sanitizer function.
9065
9085
  */
9066
9086
  ExpressionKind[ExpressionKind["SanitizerExpr"] = 20] = "SanitizerExpr";
9087
+ /**
9088
+ * An expression representing a function to create trusted values.
9089
+ */
9090
+ ExpressionKind[ExpressionKind["TrustedValueFnExpr"] = 21] = "TrustedValueFnExpr";
9067
9091
  /**
9068
9092
  * An expression that will cause a literal slot index to be emitted.
9069
9093
  */
9070
- ExpressionKind[ExpressionKind["SlotLiteralExpr"] = 21] = "SlotLiteralExpr";
9094
+ ExpressionKind[ExpressionKind["SlotLiteralExpr"] = 22] = "SlotLiteralExpr";
9071
9095
  /**
9072
9096
  * A test expression for a conditional op.
9073
9097
  */
9074
- ExpressionKind[ExpressionKind["ConditionalCase"] = 22] = "ConditionalCase";
9098
+ ExpressionKind[ExpressionKind["ConditionalCase"] = 23] = "ConditionalCase";
9075
9099
  /**
9076
9100
  * A variable for use inside a repeater, providing one of the ambiently-available context
9077
9101
  * properties ($even, $first, etc.).
9078
9102
  */
9079
- ExpressionKind[ExpressionKind["DerivedRepeaterVar"] = 23] = "DerivedRepeaterVar";
9103
+ ExpressionKind[ExpressionKind["DerivedRepeaterVar"] = 24] = "DerivedRepeaterVar";
9080
9104
  /**
9081
9105
  * An expression that will be automatically extracted to the component const array.
9082
9106
  */
9083
- ExpressionKind[ExpressionKind["ConstCollected"] = 24] = "ConstCollected";
9107
+ ExpressionKind[ExpressionKind["ConstCollected"] = 25] = "ConstCollected";
9084
9108
  })(ExpressionKind || (ExpressionKind = {}));
9085
9109
  var VariableFlags;
9086
9110
  (function (VariableFlags) {
@@ -9124,18 +9148,6 @@ var CompatibilityMode;
9124
9148
  CompatibilityMode[CompatibilityMode["Normal"] = 0] = "Normal";
9125
9149
  CompatibilityMode[CompatibilityMode["TemplateDefinitionBuilder"] = 1] = "TemplateDefinitionBuilder";
9126
9150
  })(CompatibilityMode || (CompatibilityMode = {}));
9127
- /**
9128
- * Represents functions used to sanitize different pieces of a template.
9129
- */
9130
- var SanitizerFn;
9131
- (function (SanitizerFn) {
9132
- SanitizerFn[SanitizerFn["Html"] = 0] = "Html";
9133
- SanitizerFn[SanitizerFn["Script"] = 1] = "Script";
9134
- SanitizerFn[SanitizerFn["Style"] = 2] = "Style";
9135
- SanitizerFn[SanitizerFn["Url"] = 3] = "Url";
9136
- SanitizerFn[SanitizerFn["ResourceUrl"] = 4] = "ResourceUrl";
9137
- SanitizerFn[SanitizerFn["IframeAttribute"] = 5] = "IframeAttribute";
9138
- })(SanitizerFn || (SanitizerFn = {}));
9139
9151
  /**
9140
9152
  * Enumeration of the different kinds of `@defer` secondary blocks.
9141
9153
  */
@@ -9414,7 +9426,7 @@ class Interpolation {
9414
9426
  /**
9415
9427
  * Create a `BindingOp`, not yet transformed into a particular type of binding.
9416
9428
  */
9417
- function createBindingOp(target, kind, name, expression, unit, securityContext, isTextAttribute, isStructuralTemplate, i18nContext, sourceSpan) {
9429
+ function createBindingOp(target, kind, name, expression, unit, securityContext, isTextAttribute, isStructuralTemplateAttribute, templateKind, i18nMessage, sourceSpan) {
9418
9430
  return {
9419
9431
  kind: OpKind.Binding,
9420
9432
  bindingKind: kind,
@@ -9424,8 +9436,10 @@ function createBindingOp(target, kind, name, expression, unit, securityContext,
9424
9436
  unit,
9425
9437
  securityContext,
9426
9438
  isTextAttribute,
9427
- isStructuralTemplate: isStructuralTemplate,
9428
- i18nContext,
9439
+ isStructuralTemplateAttribute,
9440
+ templateKind,
9441
+ i18nContext: null,
9442
+ i18nMessage,
9429
9443
  sourceSpan,
9430
9444
  ...NEW_OP,
9431
9445
  };
@@ -9433,7 +9447,7 @@ function createBindingOp(target, kind, name, expression, unit, securityContext,
9433
9447
  /**
9434
9448
  * Create a `PropertyOp`.
9435
9449
  */
9436
- function createPropertyOp(target, name, expression, isAnimationTrigger, securityContext, isStructuralTemplate, i18nContext, sourceSpan) {
9450
+ function createPropertyOp(target, name, expression, isAnimationTrigger, securityContext, isStructuralTemplateAttribute, templateKind, i18nContext, i18nMessage, sourceSpan) {
9437
9451
  return {
9438
9452
  kind: OpKind.Property,
9439
9453
  target,
@@ -9442,8 +9456,10 @@ function createPropertyOp(target, name, expression, isAnimationTrigger, security
9442
9456
  isAnimationTrigger,
9443
9457
  securityContext,
9444
9458
  sanitizer: null,
9445
- isStructuralTemplate,
9459
+ isStructuralTemplateAttribute,
9460
+ templateKind,
9446
9461
  i18nContext,
9462
+ i18nMessage,
9447
9463
  sourceSpan,
9448
9464
  ...TRAIT_DEPENDS_ON_SLOT_CONTEXT,
9449
9465
  ...TRAIT_CONSUMES_VARS,
@@ -9508,7 +9524,7 @@ function createClassMapOp(xref, expression, sourceSpan) {
9508
9524
  /**
9509
9525
  * Create an `AttributeOp`.
9510
9526
  */
9511
- function createAttributeOp(target, name, expression, securityContext, isTextAttribute, isStructuralTemplate, i18nContext, sourceSpan) {
9527
+ function createAttributeOp(target, name, expression, securityContext, isTextAttribute, isStructuralTemplateAttribute, templateKind, i18nMessage, sourceSpan) {
9512
9528
  return {
9513
9529
  kind: OpKind.Attribute,
9514
9530
  target,
@@ -9517,8 +9533,10 @@ function createAttributeOp(target, name, expression, securityContext, isTextAttr
9517
9533
  securityContext,
9518
9534
  sanitizer: null,
9519
9535
  isTextAttribute,
9520
- isStructuralTemplate,
9521
- i18nContext,
9536
+ isStructuralTemplateAttribute,
9537
+ templateKind,
9538
+ i18nContext: null,
9539
+ i18nMessage,
9522
9540
  sourceSpan,
9523
9541
  ...TRAIT_DEPENDS_ON_SLOT_CONTEXT,
9524
9542
  ...TRAIT_CONSUMES_VARS,
@@ -9574,12 +9592,13 @@ function createDeferWhenOp(target, expr, prefetch, sourceSpan) {
9574
9592
  sourceSpan,
9575
9593
  ...NEW_OP,
9576
9594
  ...TRAIT_DEPENDS_ON_SLOT_CONTEXT,
9595
+ ...TRAIT_CONSUMES_VARS,
9577
9596
  };
9578
9597
  }
9579
9598
  /**
9580
9599
  * Create an i18n expression op.
9581
9600
  */
9582
- function createI18nExpressionOp(context, target, i18nOwner, handle, expression, i18nPlaceholder, resolutionTime, usage, name, sourceSpan) {
9601
+ function createI18nExpressionOp(context, target, i18nOwner, handle, expression, icuPlaceholder, i18nPlaceholder, resolutionTime, usage, name, sourceSpan) {
9583
9602
  return {
9584
9603
  kind: OpKind.I18nExpression,
9585
9604
  context,
@@ -9587,6 +9606,7 @@ function createI18nExpressionOp(context, target, i18nOwner, handle, expression,
9587
9606
  i18nOwner,
9588
9607
  handle,
9589
9608
  expression,
9609
+ icuPlaceholder,
9590
9610
  i18nPlaceholder,
9591
9611
  resolutionTime,
9592
9612
  usage,
@@ -10152,24 +10172,6 @@ class ReadTemporaryExpr extends ExpressionBase {
10152
10172
  return r;
10153
10173
  }
10154
10174
  }
10155
- class SanitizerExpr extends ExpressionBase {
10156
- constructor(fn) {
10157
- super();
10158
- this.fn = fn;
10159
- this.kind = ExpressionKind.SanitizerExpr;
10160
- }
10161
- visitExpression(visitor, context) { }
10162
- isEquivalent(e) {
10163
- return e instanceof SanitizerExpr && e.fn === this.fn;
10164
- }
10165
- isConstant() {
10166
- return true;
10167
- }
10168
- clone() {
10169
- return new SanitizerExpr(this.fn);
10170
- }
10171
- transformInternalExpressions() { }
10172
- }
10173
10175
  class SlotLiteralExpr extends ExpressionBase {
10174
10176
  constructor(slot) {
10175
10177
  super();
@@ -10300,7 +10302,6 @@ function transformExpressionsInOp(op, transform, flags) {
10300
10302
  case OpKind.ClassProp:
10301
10303
  case OpKind.ClassMap:
10302
10304
  case OpKind.Binding:
10303
- case OpKind.HostProperty:
10304
10305
  if (op.expression instanceof Interpolation) {
10305
10306
  transformExpressionsInInterpolation(op.expression, transform, flags);
10306
10307
  }
@@ -10309,6 +10310,7 @@ function transformExpressionsInOp(op, transform, flags) {
10309
10310
  }
10310
10311
  break;
10311
10312
  case OpKind.Property:
10313
+ case OpKind.HostProperty:
10312
10314
  case OpKind.Attribute:
10313
10315
  if (op.expression instanceof Interpolation) {
10314
10316
  transformExpressionsInInterpolation(op.expression, transform, flags);
@@ -10354,6 +10356,8 @@ function transformExpressionsInOp(op, transform, flags) {
10354
10356
  case OpKind.ExtractedAttribute:
10355
10357
  op.expression =
10356
10358
  op.expression && transformExpressionsInExpression(op.expression, transform, flags);
10359
+ op.trustedValueFn = op.trustedValueFn &&
10360
+ transformExpressionsInExpression(op.trustedValueFn, transform, flags);
10357
10361
  break;
10358
10362
  case OpKind.RepeaterCreate:
10359
10363
  op.track = transformExpressionsInExpression(op.track, transform, flags);
@@ -10372,6 +10376,9 @@ function transformExpressionsInOp(op, transform, flags) {
10372
10376
  op.placeholderConfig =
10373
10377
  transformExpressionsInExpression(op.placeholderConfig, transform, flags);
10374
10378
  }
10379
+ if (op.resolverFn !== null) {
10380
+ op.resolverFn = transformExpressionsInExpression(op.resolverFn, transform, flags);
10381
+ }
10375
10382
  break;
10376
10383
  case OpKind.I18nMessage:
10377
10384
  for (const [placeholder, expr] of op.params) {
@@ -10408,6 +10415,7 @@ function transformExpressionsInOp(op, transform, flags) {
10408
10415
  case OpKind.Template:
10409
10416
  case OpKind.Text:
10410
10417
  case OpKind.I18nAttributes:
10418
+ case OpKind.IcuPlaceholder:
10411
10419
  // These operations contain no expressions.
10412
10420
  break;
10413
10421
  default:
@@ -10485,6 +10493,24 @@ function transformExpressionsInExpression(expr, transform, flags) {
10485
10493
  else if (expr instanceof NotExpr) {
10486
10494
  expr.condition = transformExpressionsInExpression(expr.condition, transform, flags);
10487
10495
  }
10496
+ else if (expr instanceof TaggedTemplateExpr) {
10497
+ expr.tag = transformExpressionsInExpression(expr.tag, transform, flags);
10498
+ expr.template.expressions =
10499
+ expr.template.expressions.map(e => transformExpressionsInExpression(e, transform, flags));
10500
+ }
10501
+ else if (expr instanceof ArrowFunctionExpr) {
10502
+ if (Array.isArray(expr.body)) {
10503
+ for (let i = 0; i < expr.body.length; i++) {
10504
+ transformExpressionsInStatement(expr.body[i], transform, flags);
10505
+ }
10506
+ }
10507
+ else {
10508
+ expr.body = transformExpressionsInExpression(expr.body, transform, flags);
10509
+ }
10510
+ }
10511
+ else if (expr instanceof WrappedNodeExpr) {
10512
+ // TODO: Do we need to transform any TS nodes nested inside of this expression?
10513
+ }
10488
10514
  else if (expr instanceof ReadVarExpr || expr instanceof ExternalExpr ||
10489
10515
  expr instanceof LiteralExpr) {
10490
10516
  // No action for these types.
@@ -10807,7 +10833,7 @@ function isElementOrContainerOp(op) {
10807
10833
  /**
10808
10834
  * Create an `ElementStartOp`.
10809
10835
  */
10810
- function createElementStartOp(tag, xref, namespace, i18nPlaceholder, sourceSpan) {
10836
+ function createElementStartOp(tag, xref, namespace, i18nPlaceholder, startSourceSpan, wholeSourceSpan) {
10811
10837
  return {
10812
10838
  kind: OpKind.ElementStart,
10813
10839
  xref,
@@ -10818,7 +10844,8 @@ function createElementStartOp(tag, xref, namespace, i18nPlaceholder, sourceSpan)
10818
10844
  nonBindable: false,
10819
10845
  namespace,
10820
10846
  i18nPlaceholder,
10821
- sourceSpan,
10847
+ startSourceSpan,
10848
+ wholeSourceSpan,
10822
10849
  ...TRAIT_CONSUMES_SLOT,
10823
10850
  ...NEW_OP,
10824
10851
  };
@@ -10826,7 +10853,7 @@ function createElementStartOp(tag, xref, namespace, i18nPlaceholder, sourceSpan)
10826
10853
  /**
10827
10854
  * Create a `TemplateOp`.
10828
10855
  */
10829
- function createTemplateOp(xref, templateKind, tag, functionNameSuffix, namespace, i18nPlaceholder, sourceSpan) {
10856
+ function createTemplateOp(xref, templateKind, tag, functionNameSuffix, namespace, i18nPlaceholder, startSourceSpan, wholeSourceSpan) {
10830
10857
  return {
10831
10858
  kind: OpKind.Template,
10832
10859
  xref,
@@ -10841,12 +10868,13 @@ function createTemplateOp(xref, templateKind, tag, functionNameSuffix, namespace
10841
10868
  nonBindable: false,
10842
10869
  namespace,
10843
10870
  i18nPlaceholder,
10844
- sourceSpan,
10871
+ startSourceSpan,
10872
+ wholeSourceSpan,
10845
10873
  ...TRAIT_CONSUMES_SLOT,
10846
10874
  ...NEW_OP,
10847
10875
  };
10848
10876
  }
10849
- function createRepeaterCreateOp(primaryView, emptyView, tag, track, varNames, sourceSpan) {
10877
+ function createRepeaterCreateOp(primaryView, emptyView, tag, track, varNames, emptyTag, i18nPlaceholder, emptyI18nPlaceholder, startSourceSpan, wholeSourceSpan) {
10850
10878
  return {
10851
10879
  kind: OpKind.RepeaterCreate,
10852
10880
  attributes: null,
@@ -10856,6 +10884,8 @@ function createRepeaterCreateOp(primaryView, emptyView, tag, track, varNames, so
10856
10884
  track,
10857
10885
  trackByFn: null,
10858
10886
  tag,
10887
+ emptyTag,
10888
+ emptyAttributes: null,
10859
10889
  functionNameSuffix: 'For',
10860
10890
  namespace: Namespace.HTML,
10861
10891
  nonBindable: false,
@@ -10864,9 +10894,13 @@ function createRepeaterCreateOp(primaryView, emptyView, tag, track, varNames, so
10864
10894
  vars: null,
10865
10895
  varNames,
10866
10896
  usesComponentInstance: false,
10867
- sourceSpan,
10897
+ i18nPlaceholder,
10898
+ emptyI18nPlaceholder,
10899
+ startSourceSpan,
10900
+ wholeSourceSpan,
10868
10901
  ...TRAIT_CONSUMES_SLOT,
10869
10902
  ...NEW_OP,
10903
+ ...TRAIT_CONSUMES_VARS,
10870
10904
  numSlotsUsed: emptyView === null ? 2 : 3,
10871
10905
  };
10872
10906
  }
@@ -10898,12 +10932,13 @@ function createEnableBindingsOp(xref) {
10898
10932
  /**
10899
10933
  * Create a `TextOp`.
10900
10934
  */
10901
- function createTextOp(xref, initialValue, sourceSpan) {
10935
+ function createTextOp(xref, initialValue, icuPlaceholder, sourceSpan) {
10902
10936
  return {
10903
10937
  kind: OpKind.Text,
10904
10938
  xref,
10905
10939
  handle: new SlotHandle(),
10906
10940
  initialValue,
10941
+ icuPlaceholder,
10907
10942
  sourceSpan,
10908
10943
  ...TRAIT_CONSUMES_SLOT,
10909
10944
  ...NEW_OP,
@@ -10912,7 +10947,9 @@ function createTextOp(xref, initialValue, sourceSpan) {
10912
10947
  /**
10913
10948
  * Create a `ListenerOp`. Host bindings reuse all the listener logic.
10914
10949
  */
10915
- function createListenerOp(target, targetSlot, name, tag, animationPhase, hostListener, sourceSpan) {
10950
+ function createListenerOp(target, targetSlot, name, tag, handlerOps, animationPhase, eventTarget, hostListener, sourceSpan) {
10951
+ const handlerList = new OpList();
10952
+ handlerList.push(handlerOps);
10916
10953
  return {
10917
10954
  kind: OpKind.Listener,
10918
10955
  target,
@@ -10920,11 +10957,12 @@ function createListenerOp(target, targetSlot, name, tag, animationPhase, hostLis
10920
10957
  tag,
10921
10958
  hostListener,
10922
10959
  name,
10923
- handlerOps: new OpList(),
10960
+ handlerOps: handlerList,
10924
10961
  handlerFnName: null,
10925
10962
  consumesDollarEvent: false,
10926
10963
  isAnimationListener: animationPhase !== null,
10927
- animationPhase: animationPhase,
10964
+ animationPhase,
10965
+ eventTarget,
10928
10966
  sourceSpan,
10929
10967
  ...NEW_OP,
10930
10968
  };
@@ -10953,7 +10991,7 @@ function createProjectionDefOp(def) {
10953
10991
  ...NEW_OP,
10954
10992
  };
10955
10993
  }
10956
- function createProjectionOp(xref, selector, i18nPlaceholder, attributes, sourceSpan) {
10994
+ function createProjectionOp(xref, selector, i18nPlaceholder, sourceSpan) {
10957
10995
  return {
10958
10996
  kind: OpKind.Projection,
10959
10997
  xref,
@@ -10961,7 +10999,7 @@ function createProjectionOp(xref, selector, i18nPlaceholder, attributes, sourceS
10961
10999
  selector,
10962
11000
  i18nPlaceholder,
10963
11001
  projectionSlotIndex: 0,
10964
- attributes,
11002
+ attributes: null,
10965
11003
  localRefs: [],
10966
11004
  sourceSpan,
10967
11005
  ...NEW_OP,
@@ -10971,7 +11009,7 @@ function createProjectionOp(xref, selector, i18nPlaceholder, attributes, sourceS
10971
11009
  /**
10972
11010
  * Create an `ExtractedAttributeOp`.
10973
11011
  */
10974
- function createExtractedAttributeOp(target, bindingKind, name, expression, i18nContext) {
11012
+ function createExtractedAttributeOp(target, bindingKind, name, expression, i18nContext, i18nMessage, securityContext) {
10975
11013
  return {
10976
11014
  kind: OpKind.ExtractedAttribute,
10977
11015
  target,
@@ -10979,6 +11017,9 @@ function createExtractedAttributeOp(target, bindingKind, name, expression, i18nC
10979
11017
  name,
10980
11018
  expression,
10981
11019
  i18nContext,
11020
+ i18nMessage,
11021
+ securityContext,
11022
+ trustedValueFn: null,
10982
11023
  ...NEW_OP,
10983
11024
  };
10984
11025
  }
@@ -11039,7 +11080,7 @@ function createI18nMessageOp(xref, i18nContext, i18nBlock, message, messagePlace
11039
11080
  /**
11040
11081
  * Create an `I18nStartOp`.
11041
11082
  */
11042
- function createI18nStartOp(xref, message, root) {
11083
+ function createI18nStartOp(xref, message, root, sourceSpan) {
11043
11084
  return {
11044
11085
  kind: OpKind.I18nStart,
11045
11086
  xref,
@@ -11049,6 +11090,7 @@ function createI18nStartOp(xref, message, root) {
11049
11090
  messageIndex: null,
11050
11091
  subTemplateIndex: null,
11051
11092
  context: null,
11093
+ sourceSpan,
11052
11094
  ...NEW_OP,
11053
11095
  ...TRAIT_CONSUMES_SLOT,
11054
11096
  };
@@ -11056,10 +11098,11 @@ function createI18nStartOp(xref, message, root) {
11056
11098
  /**
11057
11099
  * Create an `I18nEndOp`.
11058
11100
  */
11059
- function createI18nEndOp(xref) {
11101
+ function createI18nEndOp(xref, sourceSpan) {
11060
11102
  return {
11061
11103
  kind: OpKind.I18nEnd,
11062
11104
  xref,
11105
+ sourceSpan,
11063
11106
  ...NEW_OP,
11064
11107
  };
11065
11108
  }
@@ -11087,6 +11130,19 @@ function createIcuEndOp(xref) {
11087
11130
  ...NEW_OP,
11088
11131
  };
11089
11132
  }
11133
+ /**
11134
+ * Creates an ICU placeholder op.
11135
+ */
11136
+ function createIcuPlaceholderOp(xref, name, strings) {
11137
+ return {
11138
+ kind: OpKind.IcuPlaceholder,
11139
+ xref,
11140
+ name,
11141
+ strings,
11142
+ expressionPlaceholders: [],
11143
+ ...NEW_OP,
11144
+ };
11145
+ }
11090
11146
  function createI18nContextOp(contextKind, xref, i18nBlock, message, sourceSpan) {
11091
11147
  if (i18nBlock === null && contextKind !== I18nContextKind.Attr) {
11092
11148
  throw new Error('AssertionError: i18nBlock must be provided for non-attribute contexts.');
@@ -11121,13 +11177,15 @@ function literalOrArrayLiteral$1(value) {
11121
11177
  return literal(value, INFERRED_TYPE);
11122
11178
  }
11123
11179
 
11124
- function createHostPropertyOp(name, expression, isAnimationTrigger, i18nContext, sourceSpan) {
11180
+ function createHostPropertyOp(name, expression, isAnimationTrigger, i18nContext, securityContext, sourceSpan) {
11125
11181
  return {
11126
11182
  kind: OpKind.HostProperty,
11127
11183
  name,
11128
11184
  expression,
11129
11185
  isAnimationTrigger,
11130
11186
  i18nContext,
11187
+ securityContext,
11188
+ sanitizer: null,
11131
11189
  sourceSpan,
11132
11190
  ...TRAIT_CONSUMES_VARS,
11133
11191
  ...NEW_OP,
@@ -11407,68 +11465,52 @@ function needsApplication(i18nContexts, op) {
11407
11465
  * after the last update instruction that depends on that slot.
11408
11466
  */
11409
11467
  function assignI18nSlotDependencies(job) {
11410
- const i18nLastSlotConsumers = new Map();
11411
- let lastSlotConsumer = null;
11412
- let currentI18nOp = null;
11413
11468
  for (const unit of job.units) {
11414
- // Record the last consumed slot before each i18n end instruction.
11415
- for (const op of unit.create) {
11416
- if (hasConsumesSlotTrait(op)) {
11417
- lastSlotConsumer = op.xref;
11469
+ // The first update op.
11470
+ let updateOp = unit.update.head;
11471
+ // I18n expressions currently being moved during the iteration.
11472
+ let i18nExpressionsInProgress = [];
11473
+ // Non-null while we are iterating through an i18nStart/i18nEnd pair
11474
+ let state = null;
11475
+ for (const createOp of unit.create) {
11476
+ if (createOp.kind === OpKind.I18nStart) {
11477
+ state = {
11478
+ blockXref: createOp.xref,
11479
+ lastSlotConsumer: createOp.xref,
11480
+ };
11418
11481
  }
11419
- switch (op.kind) {
11420
- case OpKind.I18nStart:
11421
- currentI18nOp = op;
11422
- break;
11423
- case OpKind.I18nEnd:
11424
- if (currentI18nOp === null) {
11425
- throw new Error('AssertionError: Expected an active I18n block while calculating last slot consumers');
11482
+ else if (createOp.kind === OpKind.I18nEnd) {
11483
+ for (const op of i18nExpressionsInProgress) {
11484
+ op.target = state.lastSlotConsumer;
11485
+ OpList.insertBefore(op, updateOp);
11486
+ }
11487
+ i18nExpressionsInProgress.length = 0;
11488
+ state = null;
11489
+ }
11490
+ if (hasConsumesSlotTrait(createOp)) {
11491
+ if (state !== null) {
11492
+ state.lastSlotConsumer = createOp.xref;
11493
+ }
11494
+ while (true) {
11495
+ if (updateOp.next === null) {
11496
+ break;
11426
11497
  }
11427
- if (lastSlotConsumer === null) {
11428
- throw new Error('AssertionError: Expected a last slot consumer while calculating last slot consumers');
11498
+ if (state !== null && updateOp.kind === OpKind.I18nExpression &&
11499
+ updateOp.usage === I18nExpressionFor.I18nText &&
11500
+ updateOp.i18nOwner === state.blockXref) {
11501
+ const opToRemove = updateOp;
11502
+ updateOp = updateOp.next;
11503
+ OpList.remove(opToRemove);
11504
+ i18nExpressionsInProgress.push(opToRemove);
11505
+ continue;
11429
11506
  }
11430
- i18nLastSlotConsumers.set(currentI18nOp.xref, lastSlotConsumer);
11431
- currentI18nOp = null;
11432
- break;
11433
- }
11434
- }
11435
- // Expresions that are currently being moved.
11436
- let opsToMove = [];
11437
- // Previously we found the last slot-consuming create mode op in the i18n block. That op will be
11438
- // the new target for any moved i18n expresion inside the i18n block, and that op's slot is
11439
- // stored here.
11440
- let moveAfterTarget = null;
11441
- // This is the last target in the create IR that we saw during iteration. Eventally, it will be
11442
- // equal to moveAfterTarget. But wait! We need to find the *last* such op whose target is equal
11443
- // to `moveAfterTarget`.
11444
- let previousTarget = null;
11445
- for (const op of unit.update) {
11446
- if (hasDependsOnSlotContextTrait(op)) {
11447
- // We've found an op that depends on another slot other than the one that we want to move
11448
- // the expressions to, after previously having seen the one we want to move to.
11449
- if (moveAfterTarget !== null && previousTarget === moveAfterTarget &&
11450
- op.target !== previousTarget) {
11451
- OpList.insertBefore(opsToMove, op);
11452
- moveAfterTarget = null;
11453
- opsToMove = [];
11454
- }
11455
- previousTarget = op.target;
11456
- }
11457
- if (op.kind === OpKind.I18nExpression && op.usage === I18nExpressionFor.I18nText) {
11458
- // This is an I18nExpressionOps that is used for text (not attributes).
11459
- OpList.remove(op);
11460
- opsToMove.push(op);
11461
- const target = i18nLastSlotConsumers.get(op.i18nOwner);
11462
- if (target === undefined) {
11463
- throw new Error('AssertionError: Expected to find a last slot consumer for an I18nExpressionOp');
11507
+ if (hasDependsOnSlotContextTrait(updateOp) && updateOp.target !== createOp.xref) {
11508
+ break;
11509
+ }
11510
+ updateOp = updateOp.next;
11464
11511
  }
11465
- op.target = target;
11466
- moveAfterTarget = op.target;
11467
11512
  }
11468
11513
  }
11469
- if (moveAfterTarget !== null) {
11470
- unit.update.push(opsToMove);
11471
- }
11472
11514
  }
11473
11515
  }
11474
11516
 
@@ -11482,6 +11524,13 @@ function createOpXrefMap(unit) {
11482
11524
  continue;
11483
11525
  }
11484
11526
  map.set(op.xref, op);
11527
+ // TODO(dylhunn): `@for` loops with `@empty` blocks need to be special-cased here,
11528
+ // because the slot consumer trait currently only supports one slot per consumer and we
11529
+ // need two. This should be revisited when making the refactors mentioned in:
11530
+ // https://github.com/angular/angular/pull/53620#discussion_r1430918822
11531
+ if (op.kind === OpKind.RepeaterCreate && op.emptyView !== null) {
11532
+ map.set(op.emptyView, op);
11533
+ }
11485
11534
  }
11486
11535
  return map;
11487
11536
  }
@@ -11501,19 +11550,21 @@ function extractAttributes(job) {
11501
11550
  case OpKind.Property:
11502
11551
  if (!op.isAnimationTrigger) {
11503
11552
  let bindingKind;
11504
- if (op.i18nContext !== null) {
11553
+ if (op.i18nMessage !== null && op.templateKind === null) {
11505
11554
  // If the binding has an i18n context, it is an i18n attribute, and should have that
11506
11555
  // kind in the consts array.
11507
11556
  bindingKind = BindingKind.I18n;
11508
11557
  }
11509
- else if (op.isStructuralTemplate) {
11510
- // TODO: How do i18n attributes on templates work?!
11558
+ else if (op.isStructuralTemplateAttribute) {
11511
11559
  bindingKind = BindingKind.Template;
11512
11560
  }
11513
11561
  else {
11514
11562
  bindingKind = BindingKind.Property;
11515
11563
  }
11516
- OpList.insertBefore(createExtractedAttributeOp(op.target, bindingKind, op.name, null, null), lookupElement$2(elements, op.target));
11564
+ OpList.insertBefore(
11565
+ // Deliberaly null i18nMessage value
11566
+ createExtractedAttributeOp(op.target, bindingKind, op.name, /* expression */ null, /* i18nContext */ null,
11567
+ /* i18nMessage */ null, op.securityContext), lookupElement$2(elements, op.target));
11517
11568
  }
11518
11569
  break;
11519
11570
  case OpKind.StyleProp:
@@ -11524,13 +11575,22 @@ function extractAttributes(job) {
11524
11575
  // mode.
11525
11576
  if (unit.job.compatibility === CompatibilityMode.TemplateDefinitionBuilder &&
11526
11577
  op.expression instanceof EmptyExpr) {
11527
- OpList.insertBefore(createExtractedAttributeOp(op.target, BindingKind.Property, op.name, null, null), lookupElement$2(elements, op.target));
11578
+ OpList.insertBefore(createExtractedAttributeOp(op.target, BindingKind.Property, op.name, /* expression */ null,
11579
+ /* i18nContext */ null,
11580
+ /* i18nMessage */ null, SecurityContext.STYLE), lookupElement$2(elements, op.target));
11528
11581
  }
11529
11582
  break;
11530
11583
  case OpKind.Listener:
11531
11584
  if (!op.isAnimationListener) {
11532
- const extractedAttributeOp = createExtractedAttributeOp(op.target, BindingKind.Property, op.name, null, null);
11585
+ const extractedAttributeOp = createExtractedAttributeOp(op.target, BindingKind.Property, op.name, /* expression */ null,
11586
+ /* i18nContext */ null,
11587
+ /* i18nMessage */ null, SecurityContext.NONE);
11533
11588
  if (job.kind === CompilationJobKind.Host) {
11589
+ if (job.compatibility) {
11590
+ // TemplateDefinitionBuilder does not extract listener bindings to the const array
11591
+ // (which is honestly pretty inconsistent).
11592
+ break;
11593
+ }
11534
11594
  // This attribute will apply to the enclosing host binding compilation unit, so order
11535
11595
  // doesn't matter.
11536
11596
  unit.create.push(extractedAttributeOp);
@@ -11561,24 +11621,14 @@ function extractAttributeOp(unit, op, elements) {
11561
11621
  if (op.expression instanceof Interpolation) {
11562
11622
  return;
11563
11623
  }
11564
- let extractable = op.expression.isConstant();
11624
+ let extractable = op.isTextAttribute || op.expression.isConstant();
11565
11625
  if (unit.job.compatibility === CompatibilityMode.TemplateDefinitionBuilder) {
11566
- // TemplateDefinitionBuilder only extracted attributes that were string literals.
11567
- extractable = isStringLiteral(op.expression);
11568
- if (op.name === 'style' || op.name === 'class') {
11569
- // For style and class attributes, TemplateDefinitionBuilder only extracted them if they were
11570
- // text attributes. For example, `[attr.class]="'my-class'"` was not extracted despite being a
11571
- // string literal, because it is not a text attribute.
11572
- extractable &&= op.isTextAttribute;
11573
- }
11574
- if (unit.job.kind === CompilationJobKind.Host) {
11575
- // TemplateDefinitionBuilder also does not seem to extract string literals if they are part of
11576
- // a host attribute.
11577
- extractable &&= op.isTextAttribute;
11578
- }
11626
+ // TemplateDefinitionBuilder only extracts text attributes. It does not extract attriibute
11627
+ // bindings, even if they are constants.
11628
+ extractable &&= op.isTextAttribute;
11579
11629
  }
11580
11630
  if (extractable) {
11581
- const extractedAttributeOp = createExtractedAttributeOp(op.target, op.isStructuralTemplate ? BindingKind.Template : BindingKind.Attribute, op.name, op.expression, op.i18nContext);
11631
+ const extractedAttributeOp = createExtractedAttributeOp(op.target, op.isStructuralTemplateAttribute ? BindingKind.Template : BindingKind.Attribute, op.name, op.expression, op.i18nContext, op.i18nMessage, op.securityContext);
11582
11632
  if (unit.job.kind === CompilationJobKind.Host) {
11583
11633
  // This attribute will apply to the enclosing host binding compilation unit, so order doesn't
11584
11634
  // matter.
@@ -11625,16 +11675,16 @@ function specializeBindings(job) {
11625
11675
  target.nonBindable = true;
11626
11676
  }
11627
11677
  else {
11628
- OpList.replace(op, createAttributeOp(op.target, op.name, op.expression, op.securityContext, op.isTextAttribute, op.isStructuralTemplate, op.i18nContext, op.sourceSpan));
11678
+ OpList.replace(op, createAttributeOp(op.target, op.name, op.expression, op.securityContext, op.isTextAttribute, op.isStructuralTemplateAttribute, op.templateKind, op.i18nMessage, op.sourceSpan));
11629
11679
  }
11630
11680
  break;
11631
11681
  case BindingKind.Property:
11632
11682
  case BindingKind.Animation:
11633
11683
  if (job.kind === CompilationJobKind.Host) {
11634
- OpList.replace(op, createHostPropertyOp(op.name, op.expression, op.bindingKind === BindingKind.Animation, op.i18nContext, op.sourceSpan));
11684
+ OpList.replace(op, createHostPropertyOp(op.name, op.expression, op.bindingKind === BindingKind.Animation, op.i18nContext, op.securityContext, op.sourceSpan));
11635
11685
  }
11636
11686
  else {
11637
- OpList.replace(op, createPropertyOp(op.target, op.name, op.expression, op.bindingKind === BindingKind.Animation, op.securityContext, op.isStructuralTemplate, op.i18nContext, op.sourceSpan));
11687
+ OpList.replace(op, createPropertyOp(op.target, op.name, op.expression, op.bindingKind === BindingKind.Animation, op.securityContext, op.isStructuralTemplateAttribute, op.templateKind, op.i18nContext, op.i18nMessage, op.sourceSpan));
11638
11688
  }
11639
11689
  break;
11640
11690
  case BindingKind.I18n:
@@ -11869,9 +11919,9 @@ function collectElementConsts(job) {
11869
11919
  for (const unit of job.units) {
11870
11920
  for (const op of unit.create) {
11871
11921
  if (op.kind === OpKind.ExtractedAttribute) {
11872
- const attributes = allElementAttributes.get(op.target) || new ElementAttributes();
11922
+ const attributes = allElementAttributes.get(op.target) || new ElementAttributes(job.compatibility);
11873
11923
  allElementAttributes.set(op.target, attributes);
11874
- attributes.add(op.bindingKind, op.name, op.expression);
11924
+ attributes.add(op.bindingKind, op.name, op.expression, op.trustedValueFn);
11875
11925
  OpList.remove(op);
11876
11926
  }
11877
11927
  }
@@ -11880,15 +11930,26 @@ function collectElementConsts(job) {
11880
11930
  if (job instanceof ComponentCompilationJob) {
11881
11931
  for (const unit of job.units) {
11882
11932
  for (const op of unit.create) {
11883
- if (isElementOrContainerOp(op)) {
11933
+ // TODO: Simplify and combine these cases.
11934
+ if (op.kind == OpKind.Projection) {
11884
11935
  const attributes = allElementAttributes.get(op.xref);
11885
11936
  if (attributes !== undefined) {
11886
11937
  const attrArray = serializeAttributes(attributes);
11887
11938
  if (attrArray.entries.length > 0) {
11888
- op.attributes = job.addConst(attrArray);
11939
+ op.attributes = attrArray;
11889
11940
  }
11890
11941
  }
11891
11942
  }
11943
+ else if (isElementOrContainerOp(op)) {
11944
+ op.attributes = getConstIndex(job, allElementAttributes, op.xref);
11945
+ // TODO(dylhunn): `@for` loops with `@empty` blocks need to be special-cased here,
11946
+ // because the slot consumer trait currently only supports one slot per consumer and we
11947
+ // need two. This should be revisited when making the refactors mentioned in:
11948
+ // https://github.com/angular/angular/pull/53620#discussion_r1430918822
11949
+ if (op.kind === OpKind.RepeaterCreate && op.emptyView !== null) {
11950
+ op.emptyAttributes = getConstIndex(job, allElementAttributes, op.emptyView);
11951
+ }
11952
+ }
11892
11953
  }
11893
11954
  }
11894
11955
  }
@@ -11906,6 +11967,16 @@ function collectElementConsts(job) {
11906
11967
  }
11907
11968
  }
11908
11969
  }
11970
+ function getConstIndex(job, allElementAttributes, xref) {
11971
+ const attributes = allElementAttributes.get(xref);
11972
+ if (attributes !== undefined) {
11973
+ const attrArray = serializeAttributes(attributes);
11974
+ if (attrArray.entries.length > 0) {
11975
+ return job.addConst(attrArray);
11976
+ }
11977
+ }
11978
+ return null;
11979
+ }
11909
11980
  /**
11910
11981
  * Shared instance of an empty array to avoid unnecessary array allocations.
11911
11982
  */
@@ -11914,11 +11985,6 @@ const FLYWEIGHT_ARRAY = Object.freeze([]);
11914
11985
  * Container for all of the various kinds of attributes which are applied on an element.
11915
11986
  */
11916
11987
  class ElementAttributes {
11917
- constructor() {
11918
- this.known = new Set();
11919
- this.byKind = new Map;
11920
- this.projectAs = null;
11921
- }
11922
11988
  get attributes() {
11923
11989
  return this.byKind.get(BindingKind.Attribute) ?? FLYWEIGHT_ARRAY;
11924
11990
  }
@@ -11937,11 +12003,32 @@ class ElementAttributes {
11937
12003
  get i18n() {
11938
12004
  return this.byKind.get(BindingKind.I18n) ?? FLYWEIGHT_ARRAY;
11939
12005
  }
11940
- add(kind, name, value) {
11941
- if (this.known.has(name)) {
12006
+ constructor(compatibility) {
12007
+ this.compatibility = compatibility;
12008
+ this.known = new Map();
12009
+ this.byKind = new Map;
12010
+ this.projectAs = null;
12011
+ }
12012
+ isKnown(kind, name, value) {
12013
+ const nameToValue = this.known.get(kind) ?? new Set();
12014
+ this.known.set(kind, nameToValue);
12015
+ if (nameToValue.has(name)) {
12016
+ return true;
12017
+ }
12018
+ nameToValue.add(name);
12019
+ return false;
12020
+ }
12021
+ add(kind, name, value, trustedValueFn) {
12022
+ // TemplateDefinitionBuilder puts duplicate attribute, class, and style values into the consts
12023
+ // array. This seems inefficient, we can probably keep just the first one or the last value
12024
+ // (whichever actually gets applied when multiple values are listed for the same attribute).
12025
+ const allowDuplicates = this.compatibility === CompatibilityMode.TemplateDefinitionBuilder &&
12026
+ (kind === BindingKind.Attribute || kind === BindingKind.ClassName ||
12027
+ kind === BindingKind.StyleProperty);
12028
+ if (!allowDuplicates && this.isKnown(kind, name, value)) {
11942
12029
  return;
11943
12030
  }
11944
- this.known.add(name);
12031
+ // TODO: Can this be its own phase
11945
12032
  if (name === 'ngProjectAs') {
11946
12033
  if (value === null || !(value instanceof LiteralExpr) || (value.value == null) ||
11947
12034
  (typeof value.value?.toString() !== 'string')) {
@@ -11957,7 +12044,15 @@ class ElementAttributes {
11957
12044
  if (value === null) {
11958
12045
  throw Error('Attribute, i18n attribute, & style element attributes must have a value');
11959
12046
  }
11960
- array.push(value);
12047
+ if (trustedValueFn !== null) {
12048
+ if (!isStringLiteral(value)) {
12049
+ throw Error('AssertionError: extracted attribute value should be string literal');
12050
+ }
12051
+ array.push(taggedTemplate(trustedValueFn, new TemplateLiteral([new TemplateLiteralElement(value.value)], []), undefined, value.sourceSpan));
12052
+ }
12053
+ else {
12054
+ array.push(value);
12055
+ }
11961
12056
  }
11962
12057
  }
11963
12058
  arrayFor(kind) {
@@ -11971,7 +12066,7 @@ class ElementAttributes {
11971
12066
  * Gets an array of literal expressions representing the attribute's namespaced name.
11972
12067
  */
11973
12068
  function getAttributeNameLiterals$1(name) {
11974
- const [attributeNamespace, attributeName] = splitNsName(name);
12069
+ const [attributeNamespace, attributeName] = splitNsName(name, false);
11975
12070
  const nameLiteral = literal(attributeName);
11976
12071
  if (attributeNamespace) {
11977
12072
  return [
@@ -12044,7 +12139,7 @@ function convertI18nBindings(job) {
12044
12139
  if (op.expression.i18nPlaceholders.length !== op.expression.expressions.length) {
12045
12140
  throw new Error(`AssertionError: An i18n attribute binding instruction requires the same number of expressions and placeholders, but found ${op.expression.i18nPlaceholders.length} placeholders and ${op.expression.expressions.length} expressions`);
12046
12141
  }
12047
- ops.push(createI18nExpressionOp(op.i18nContext, i18nAttributesForElem.target, i18nAttributesForElem.xref, i18nAttributesForElem.handle, expr, op.expression.i18nPlaceholders[i], I18nParamResolutionTime.Creation, I18nExpressionFor.I18nAttribute, op.name, op.sourceSpan));
12142
+ ops.push(createI18nExpressionOp(op.i18nContext, i18nAttributesForElem.target, i18nAttributesForElem.xref, i18nAttributesForElem.handle, expr, null, op.expression.i18nPlaceholders[i], I18nParamResolutionTime.Creation, I18nExpressionFor.I18nAttribute, op.name, op.sourceSpan));
12048
12143
  }
12049
12144
  OpList.replaceWithMany(op, ops);
12050
12145
  break;
@@ -12081,7 +12176,8 @@ function createDeferDepsFns(job) {
12081
12176
  if (op.handle.slot === null) {
12082
12177
  throw new Error('AssertionError: slot must be assigned bfore extracting defer deps functions');
12083
12178
  }
12084
- op.resolverFn = job.pool.getSharedFunctionReference(depsFnExpr, `${job.componentName}_Defer_${op.handle.slot}_DepsFn`);
12179
+ op.resolverFn = job.pool.getSharedFunctionReference(depsFnExpr, `${job.componentName}_Defer_${op.handle.slot}_DepsFn`,
12180
+ /* Don't use unique names for TDB compatibility */ false);
12085
12181
  }
12086
12182
  }
12087
12183
  }
@@ -12098,53 +12194,119 @@ function createDeferDepsFns(job) {
12098
12194
  * message.)
12099
12195
  */
12100
12196
  function createI18nContexts(job) {
12101
- const rootContexts = new Map();
12102
- let currentI18nOp = null;
12103
- let xref;
12197
+ // Create i18n context ops for i18n attrs.
12198
+ const attrContextByMessage = new Map();
12199
+ for (const unit of job.units) {
12200
+ for (const op of unit.ops()) {
12201
+ switch (op.kind) {
12202
+ case OpKind.Binding:
12203
+ case OpKind.Property:
12204
+ case OpKind.Attribute:
12205
+ case OpKind.ExtractedAttribute:
12206
+ if (op.i18nMessage === null) {
12207
+ continue;
12208
+ }
12209
+ if (!attrContextByMessage.has(op.i18nMessage)) {
12210
+ const i18nContext = createI18nContextOp(I18nContextKind.Attr, job.allocateXrefId(), null, op.i18nMessage, null);
12211
+ unit.create.push(i18nContext);
12212
+ attrContextByMessage.set(op.i18nMessage, i18nContext.xref);
12213
+ }
12214
+ op.i18nContext = attrContextByMessage.get(op.i18nMessage);
12215
+ break;
12216
+ }
12217
+ }
12218
+ }
12219
+ // Create i18n context ops for root i18n blocks.
12220
+ const blockContextByI18nBlock = new Map();
12104
12221
  for (const unit of job.units) {
12105
12222
  for (const op of unit.create) {
12106
12223
  switch (op.kind) {
12107
12224
  case OpKind.I18nStart:
12108
- currentI18nOp = op;
12109
- // Each root i18n block gets its own context, child ones refer to the context for their
12110
- // root block.
12111
12225
  if (op.xref === op.root) {
12112
- xref = job.allocateXrefId();
12113
- unit.create.push(createI18nContextOp(I18nContextKind.RootI18n, xref, op.xref, op.message, null));
12114
- op.context = xref;
12115
- rootContexts.set(op.xref, xref);
12226
+ const contextOp = createI18nContextOp(I18nContextKind.RootI18n, job.allocateXrefId(), op.xref, op.message, null);
12227
+ unit.create.push(contextOp);
12228
+ op.context = contextOp.xref;
12229
+ blockContextByI18nBlock.set(op.xref, contextOp);
12116
12230
  }
12117
12231
  break;
12232
+ }
12233
+ }
12234
+ }
12235
+ // Assign i18n contexts for child i18n blocks. These don't need their own conext, instead they
12236
+ // should inherit from their root i18n block.
12237
+ for (const unit of job.units) {
12238
+ for (const op of unit.create) {
12239
+ if (op.kind === OpKind.I18nStart && op.xref !== op.root) {
12240
+ const rootContext = blockContextByI18nBlock.get(op.root);
12241
+ if (rootContext === undefined) {
12242
+ throw Error('AssertionError: Root i18n block i18n context should have been created.');
12243
+ }
12244
+ op.context = rootContext.xref;
12245
+ blockContextByI18nBlock.set(op.xref, rootContext);
12246
+ }
12247
+ }
12248
+ }
12249
+ // Create or assign i18n contexts for ICUs.
12250
+ let currentI18nOp = null;
12251
+ for (const unit of job.units) {
12252
+ for (const op of unit.create) {
12253
+ switch (op.kind) {
12254
+ case OpKind.I18nStart:
12255
+ currentI18nOp = op;
12256
+ break;
12118
12257
  case OpKind.I18nEnd:
12119
12258
  currentI18nOp = null;
12120
12259
  break;
12121
12260
  case OpKind.IcuStart:
12122
- // If an ICU represents a different message than its containing block, we give it its own
12123
- // i18n context.
12124
12261
  if (currentI18nOp === null) {
12125
- throw Error('Unexpected ICU outside of an i18n block.');
12262
+ throw Error('AssertionError: Unexpected ICU outside of an i18n block.');
12126
12263
  }
12127
12264
  if (op.message.id !== currentI18nOp.message.id) {
12128
- // There was an enclosing i18n block around this ICU somewhere.
12129
- xref = job.allocateXrefId();
12130
- unit.create.push(createI18nContextOp(I18nContextKind.Icu, xref, currentI18nOp.xref, op.message, null));
12131
- op.context = xref;
12265
+ // This ICU is a sub-message inside its parent i18n block message. We need to give it
12266
+ // its own context.
12267
+ const contextOp = createI18nContextOp(I18nContextKind.Icu, job.allocateXrefId(), currentI18nOp.xref, op.message, null);
12268
+ unit.create.push(contextOp);
12269
+ op.context = contextOp.xref;
12132
12270
  }
12133
12271
  else {
12134
- // The i18n block was generated because of this ICU, OR it was explicit, but the ICU is
12135
- // the only localizable content inside of it.
12272
+ // This ICU is the only translatable content in its parent i18n block. We need to
12273
+ // convert the parent's context into an ICU context.
12136
12274
  op.context = currentI18nOp.context;
12275
+ blockContextByI18nBlock.get(currentI18nOp.xref).contextKind = I18nContextKind.Icu;
12137
12276
  }
12138
12277
  break;
12139
12278
  }
12140
12279
  }
12141
12280
  }
12142
- // Assign contexts to child i18n blocks, now that all root i18n blocks have their context
12143
- // assigned.
12281
+ }
12282
+
12283
+ /**
12284
+ * Deduplicate text bindings, e.g. <div class="cls1" class="cls2">
12285
+ */
12286
+ function deduplicateTextBindings(job) {
12287
+ const seen = new Map();
12144
12288
  for (const unit of job.units) {
12145
- for (const op of unit.create) {
12146
- if (op.kind === OpKind.I18nStart && op.xref !== op.root) {
12147
- op.context = rootContexts.get(op.root);
12289
+ for (const op of unit.update.reversed()) {
12290
+ if (op.kind === OpKind.Binding && op.isTextAttribute) {
12291
+ const seenForElement = seen.get(op.target) || new Set();
12292
+ if (seenForElement.has(op.name)) {
12293
+ if (job.compatibility === CompatibilityMode.TemplateDefinitionBuilder) {
12294
+ // For most duplicated attributes, TemplateDefinitionBuilder lists all of the values in
12295
+ // the consts array. However, for style and class attributes it only keeps the last one.
12296
+ // We replicate that behavior here since it has actual consequences for apps with
12297
+ // duplicate class or style attrs.
12298
+ if (op.name === 'style' || op.name === 'class') {
12299
+ OpList.remove(op);
12300
+ }
12301
+ }
12302
+ else {
12303
+ // TODO: Determine the correct behavior. It would probably make sense to merge multiple
12304
+ // style and class attributes. Alternatively we could just throw an error, as HTML
12305
+ // doesn't permit duplicate attributes.
12306
+ }
12307
+ }
12308
+ seenForElement.add(op.name);
12309
+ seen.set(op.target, seenForElement);
12148
12310
  }
12149
12311
  }
12150
12312
  }
@@ -12530,13 +12692,18 @@ const LIST_DELIMITER = '|';
12530
12692
  * used in the final output.
12531
12693
  */
12532
12694
  function extractI18nMessages(job) {
12533
- // Save the i18n start and i18n context ops for later use.
12534
- const i18nContexts = new Map();
12695
+ // Create an i18n message for each context.
12696
+ // TODO: Merge the context op with the message op since they're 1:1 anyways.
12697
+ const i18nMessagesByContext = new Map();
12535
12698
  const i18nBlocks = new Map();
12699
+ const i18nContexts = new Map();
12536
12700
  for (const unit of job.units) {
12537
12701
  for (const op of unit.create) {
12538
12702
  switch (op.kind) {
12539
12703
  case OpKind.I18nContext:
12704
+ const i18nMessageOp = createI18nMessage(job, op);
12705
+ unit.create.push(i18nMessageOp);
12706
+ i18nMessagesByContext.set(op.xref, i18nMessageOp);
12540
12707
  i18nContexts.set(op.xref, op);
12541
12708
  break;
12542
12709
  case OpKind.I18nStart:
@@ -12545,54 +12712,47 @@ function extractI18nMessages(job) {
12545
12712
  }
12546
12713
  }
12547
12714
  }
12548
- // TODO: Miles and I think that contexts have a 1-to-1 correspondence with extracted messages, so
12549
- // this phase can probably be simplified.
12550
- // Extract messages from contexts of i18n attributes.
12551
- for (const unit of job.units) {
12552
- for (const op of unit.create) {
12553
- if (op.kind !== OpKind.I18nContext || op.contextKind !== I18nContextKind.Attr) {
12554
- continue;
12555
- }
12556
- const i18nMessageOp = createI18nMessage(job, op);
12557
- unit.create.push(i18nMessageOp);
12558
- }
12559
- }
12560
- // Extract messages from root i18n blocks.
12561
- const i18nBlockMessages = new Map();
12562
- for (const unit of job.units) {
12563
- for (const op of unit.create) {
12564
- if (op.kind === OpKind.I18nStart && op.xref === op.root) {
12565
- if (!op.context) {
12566
- throw Error('I18n start op should have its context set.');
12567
- }
12568
- const i18nMessageOp = createI18nMessage(job, i18nContexts.get(op.context));
12569
- i18nBlockMessages.set(op.xref, i18nMessageOp);
12570
- unit.create.push(i18nMessageOp);
12571
- }
12572
- }
12573
- }
12574
- // Extract messages from ICUs with their own sub-context.
12715
+ // Associate sub-messages for ICUs with their root message. At this point we can also remove the
12716
+ // ICU start/end ops, as they are no longer needed.
12717
+ let currentIcu = null;
12575
12718
  for (const unit of job.units) {
12576
12719
  for (const op of unit.create) {
12577
12720
  switch (op.kind) {
12578
12721
  case OpKind.IcuStart:
12579
- if (!op.context) {
12580
- throw Error('ICU op should have its context set.');
12722
+ currentIcu = op;
12723
+ OpList.remove(op);
12724
+ // Skip any contexts not associated with an ICU.
12725
+ const icuContext = i18nContexts.get(op.context);
12726
+ if (icuContext.contextKind !== I18nContextKind.Icu) {
12727
+ continue;
12581
12728
  }
12582
- const i18nContext = i18nContexts.get(op.context);
12583
- if (i18nContext.contextKind === I18nContextKind.Icu) {
12584
- if (i18nContext.i18nBlock === null) {
12585
- throw Error('ICU context should have its i18n block set.');
12586
- }
12587
- const subMessage = createI18nMessage(job, i18nContext, op.messagePlaceholder);
12588
- unit.create.push(subMessage);
12589
- const rootI18nId = i18nBlocks.get(i18nContext.i18nBlock).root;
12590
- const parentMessage = i18nBlockMessages.get(rootI18nId);
12591
- parentMessage?.subMessages.push(subMessage.xref);
12729
+ // Skip ICUs that share a context with their i18n message. These represent root-level
12730
+ // ICUs, not sub-messages.
12731
+ const i18nBlock = i18nBlocks.get(icuContext.i18nBlock);
12732
+ if (i18nBlock.context === icuContext.xref) {
12733
+ continue;
12592
12734
  }
12593
- OpList.remove(op);
12735
+ // Find the root message and push this ICUs message as a sub-message.
12736
+ const rootI18nBlock = i18nBlocks.get(i18nBlock.root);
12737
+ const rootMessage = i18nMessagesByContext.get(rootI18nBlock.context);
12738
+ if (rootMessage === undefined) {
12739
+ throw Error('AssertionError: ICU sub-message should belong to a root message.');
12740
+ }
12741
+ const subMessage = i18nMessagesByContext.get(icuContext.xref);
12742
+ subMessage.messagePlaceholder = op.messagePlaceholder;
12743
+ rootMessage.subMessages.push(subMessage.xref);
12594
12744
  break;
12595
12745
  case OpKind.IcuEnd:
12746
+ currentIcu = null;
12747
+ OpList.remove(op);
12748
+ break;
12749
+ case OpKind.IcuPlaceholder:
12750
+ // Add ICU placeholders to the message, then remove the ICU placeholder ops.
12751
+ if (currentIcu === null || currentIcu.context == null) {
12752
+ throw Error('AssertionError: Unexpected ICU placeholder outside of i18n context');
12753
+ }
12754
+ const msg = i18nMessagesByContext.get(currentIcu.context);
12755
+ msg.postprocessingParams.set(op.name, literal(formatIcuPlaceholder(op)));
12596
12756
  OpList.remove(op);
12597
12757
  break;
12598
12758
  }
@@ -12605,14 +12765,19 @@ function extractI18nMessages(job) {
12605
12765
  function createI18nMessage(job, context, messagePlaceholder) {
12606
12766
  let formattedParams = formatParams(context.params);
12607
12767
  const formattedPostprocessingParams = formatParams(context.postprocessingParams);
12608
- let needsPostprocessing = formattedPostprocessingParams.size > 0;
12609
- for (const values of context.params.values()) {
12610
- if (values.length > 1) {
12611
- needsPostprocessing = true;
12612
- }
12613
- }
12768
+ let needsPostprocessing = [...context.params.values()].some(v => v.length > 1);
12614
12769
  return createI18nMessageOp(job.allocateXrefId(), context.xref, context.i18nBlock, context.message, messagePlaceholder ?? null, formattedParams, formattedPostprocessingParams, needsPostprocessing);
12615
12770
  }
12771
+ /**
12772
+ * Formats an ICU placeholder into a single string with expression placeholders.
12773
+ */
12774
+ function formatIcuPlaceholder(op) {
12775
+ if (op.strings.length !== op.expressionPlaceholders.length + 1) {
12776
+ throw Error(`AsserionError: Invalid ICU placeholder with ${op.strings.length} strings and ${op.expressionPlaceholders.length} expressions`);
12777
+ }
12778
+ const values = op.expressionPlaceholders.map(formatValue);
12779
+ return op.strings.flatMap((str, i) => [str, values[i] || '']).join('');
12780
+ }
12616
12781
  /**
12617
12782
  * Formats a map of `I18nParamValue[]` values into a map of `Expression` values.
12618
12783
  */
@@ -12812,9 +12977,15 @@ function recursivelyProcessView(view, parentScope) {
12812
12977
  for (const op of view.create) {
12813
12978
  switch (op.kind) {
12814
12979
  case OpKind.Template:
12980
+ // Descend into child embedded views.
12981
+ recursivelyProcessView(view.job.views.get(op.xref), scope);
12982
+ break;
12815
12983
  case OpKind.RepeaterCreate:
12816
12984
  // Descend into child embedded views.
12817
12985
  recursivelyProcessView(view.job.views.get(op.xref), scope);
12986
+ if (op.emptyView) {
12987
+ recursivelyProcessView(view.job.views.get(op.emptyView), scope);
12988
+ }
12818
12989
  break;
12819
12990
  case OpKind.Listener:
12820
12991
  // Prepend variables to listener handler functions.
@@ -12945,7 +13116,7 @@ const BANG_IMPORTANT = '!important';
12945
13116
  */
12946
13117
  function parseHostStyleProperties(job) {
12947
13118
  for (const op of job.root.update) {
12948
- if (op.kind !== OpKind.Binding) {
13119
+ if (!(op.kind === OpKind.Binding && op.bindingKind === BindingKind.Property)) {
12949
13120
  continue;
12950
13121
  }
12951
13122
  if (op.name.endsWith(BANG_IMPORTANT)) {
@@ -19954,7 +20125,7 @@ const ESCAPE = '\uFFFD';
19954
20125
  function collectI18nConsts(job) {
19955
20126
  const fileBasedI18nSuffix = job.relativeContextFilePath.replace(/[^A-Za-z0-9]/g, '_').toUpperCase() + '_';
19956
20127
  // Step One: Build up various lookup maps we need to collect all the consts.
19957
- // Context Xref -> Extracted Attribute Op
20128
+ // Context Xref -> Extracted Attribute Ops
19958
20129
  const extractedAttributesByI18nContext = new Map();
19959
20130
  // Element/ElementStart Xref -> I18n Attributes config op
19960
20131
  const i18nAttributesByElement = new Map();
@@ -19965,7 +20136,9 @@ function collectI18nConsts(job) {
19965
20136
  for (const unit of job.units) {
19966
20137
  for (const op of unit.ops()) {
19967
20138
  if (op.kind === OpKind.ExtractedAttribute && op.i18nContext !== null) {
19968
- extractedAttributesByI18nContext.set(op.i18nContext, op);
20139
+ const attributes = extractedAttributesByI18nContext.get(op.i18nContext) ?? [];
20140
+ attributes.push(op);
20141
+ extractedAttributesByI18nContext.set(op.i18nContext, attributes);
19969
20142
  }
19970
20143
  else if (op.kind === OpKind.I18nAttributes) {
19971
20144
  i18nAttributesByElement.set(op.target, op);
@@ -20010,9 +20183,11 @@ function collectI18nConsts(job) {
20010
20183
  i18nValuesByContext.set(op.i18nContext, mainVar);
20011
20184
  // This i18n message may correspond to an individual extracted attribute. If so, The
20012
20185
  // value of that attribute is updated to read the extracted i18n variable.
20013
- const attributeForMessage = extractedAttributesByI18nContext.get(op.i18nContext);
20014
- if (attributeForMessage !== undefined) {
20015
- attributeForMessage.expression = mainVar;
20186
+ const attributesForMessage = extractedAttributesByI18nContext.get(op.i18nContext);
20187
+ if (attributesForMessage !== undefined) {
20188
+ for (const attr of attributesForMessage) {
20189
+ attr.expression = mainVar.clone();
20190
+ }
20016
20191
  }
20017
20192
  }
20018
20193
  }
@@ -20099,7 +20274,7 @@ function collectMessage(job, fileBasedI18nSuffix, messages, messageOp) {
20099
20274
  let transformFn = undefined;
20100
20275
  // If nescessary, add a post-processing step and resolve any placeholder params that are
20101
20276
  // set in post-processing.
20102
- if (messageOp.needsPostprocessing) {
20277
+ if (messageOp.needsPostprocessing || messageOp.postprocessingParams.size > 0) {
20103
20278
  // Sort the post-processing params for consistency with TemaplateDefinitionBuilder output.
20104
20279
  const postprocessingParams = Object.fromEntries([...messageOp.postprocessingParams.entries()].sort());
20105
20280
  const formattedPostprocessingParams = formatI18nPlaceholderNamesInMap(postprocessingParams, /* useCamelCase */ false);
@@ -20129,7 +20304,6 @@ function addSubMessageParams(messageOp, subMessagePlaceholders) {
20129
20304
  else {
20130
20305
  messageOp.params.set(placeholder, literal(`${ESCAPE}${I18N_ICU_MAPPING_PREFIX}${placeholder}${ESCAPE}`));
20131
20306
  messageOp.postprocessingParams.set(placeholder, literalArr(subMessages));
20132
- messageOp.needsPostprocessing = true;
20133
20307
  }
20134
20308
  }
20135
20309
  }
@@ -20215,6 +20389,7 @@ function convertI18nText(job) {
20215
20389
  let currentIcu = null;
20216
20390
  const textNodeI18nBlocks = new Map();
20217
20391
  const textNodeIcus = new Map();
20392
+ const icuPlaceholderByText = new Map();
20218
20393
  for (const op of unit.create) {
20219
20394
  switch (op.kind) {
20220
20395
  case OpKind.I18nStart:
@@ -20239,7 +20414,19 @@ function convertI18nText(job) {
20239
20414
  if (currentI18n !== null) {
20240
20415
  textNodeI18nBlocks.set(op.xref, currentI18n);
20241
20416
  textNodeIcus.set(op.xref, currentIcu);
20242
- OpList.remove(op);
20417
+ if (op.icuPlaceholder !== null) {
20418
+ // Create an op to represent the ICU placeholder. Initially set its static text to the
20419
+ // value of the text op, though this may be overwritten later if this text op is a
20420
+ // placeholder for an interpolation.
20421
+ const icuPlaceholderOp = createIcuPlaceholderOp(job.allocateXrefId(), op.icuPlaceholder, [op.initialValue]);
20422
+ OpList.replace(op, icuPlaceholderOp);
20423
+ icuPlaceholderByText.set(op.xref, icuPlaceholderOp);
20424
+ }
20425
+ else {
20426
+ // Otherwise just remove the text op, since its value is already accounted for in the
20427
+ // translated message.
20428
+ OpList.remove(op);
20429
+ }
20243
20430
  }
20244
20431
  break;
20245
20432
  }
@@ -20254,6 +20441,7 @@ function convertI18nText(job) {
20254
20441
  }
20255
20442
  const i18nOp = textNodeI18nBlocks.get(op.target);
20256
20443
  const icuOp = textNodeIcus.get(op.target);
20444
+ const icuPlaceholder = icuPlaceholderByText.get(op.target);
20257
20445
  const contextId = icuOp ? icuOp.context : i18nOp.context;
20258
20446
  const resolutionTime = icuOp ? I18nParamResolutionTime.Postproccessing :
20259
20447
  I18nParamResolutionTime.Creation;
@@ -20262,9 +20450,14 @@ function convertI18nText(job) {
20262
20450
  const expr = op.interpolation.expressions[i];
20263
20451
  // For now, this i18nExpression depends on the slot context of the enclosing i18n block.
20264
20452
  // Later, we will modify this, and advance to a different point.
20265
- ops.push(createI18nExpressionOp(contextId, i18nOp.xref, i18nOp.xref, i18nOp.handle, expr, op.interpolation.i18nPlaceholders[i], resolutionTime, I18nExpressionFor.I18nText, '', expr.sourceSpan ?? op.sourceSpan));
20453
+ ops.push(createI18nExpressionOp(contextId, i18nOp.xref, i18nOp.xref, i18nOp.handle, expr, icuPlaceholder?.xref ?? null, op.interpolation.i18nPlaceholders[i] ?? null, resolutionTime, I18nExpressionFor.I18nText, '', expr.sourceSpan ?? op.sourceSpan));
20266
20454
  }
20267
20455
  OpList.replaceWithMany(op, ops);
20456
+ // If this interpolation is part of an ICU placeholder, add the strings and expressions to
20457
+ // the placeholder.
20458
+ if (icuPlaceholder !== undefined) {
20459
+ icuPlaceholder.strings = op.interpolation.strings;
20460
+ }
20268
20461
  break;
20269
20462
  }
20270
20463
  }
@@ -20447,7 +20640,7 @@ function addNamesToView(unit, baseName, state, compatibility) {
20447
20640
  op.handlerFnName = sanitizeIdentifier(op.handlerFnName);
20448
20641
  break;
20449
20642
  case OpKind.Variable:
20450
- varNames.set(op.xref, getVariableName(op.variable, state));
20643
+ varNames.set(op.xref, getVariableName(unit, op.variable, state));
20451
20644
  break;
20452
20645
  case OpKind.RepeaterCreate:
20453
20646
  if (!(unit instanceof ViewCompilationUnit)) {
@@ -20502,15 +20695,23 @@ function addNamesToView(unit, baseName, state, compatibility) {
20502
20695
  });
20503
20696
  }
20504
20697
  }
20505
- function getVariableName(variable, state) {
20698
+ function getVariableName(unit, variable, state) {
20506
20699
  if (variable.name === null) {
20507
20700
  switch (variable.kind) {
20508
20701
  case SemanticVariableKind.Context:
20509
20702
  variable.name = `ctx_r${state.index++}`;
20510
20703
  break;
20511
20704
  case SemanticVariableKind.Identifier:
20512
- // TODO: Prefix increment and `_r` for compatiblity only.
20513
- variable.name = `${variable.identifier}_r${++state.index}`;
20705
+ if (unit.job.compatibility === CompatibilityMode.TemplateDefinitionBuilder) {
20706
+ // TODO: Prefix increment and `_r` are for compatiblity with the old naming scheme.
20707
+ // This has the potential to cause collisions when `ctx` is the identifier, so we need a
20708
+ // special check for that as well.
20709
+ const compatPrefix = variable.identifier === 'ctx' ? 'i' : '';
20710
+ variable.name = `${variable.identifier}_${compatPrefix}r${++state.index}`;
20711
+ }
20712
+ else {
20713
+ variable.name = `${variable.identifier}_i${state.index++}`;
20714
+ }
20514
20715
  break;
20515
20716
  default:
20516
20717
  // TODO: Prefix increment for compatibility only.
@@ -20707,17 +20908,27 @@ const CREATE_ORDERING = [
20707
20908
  * op kinds.
20708
20909
  */
20709
20910
  const UPDATE_ORDERING = [
20710
- { test: kindWithInterpolationTest(OpKind.HostProperty, true) },
20711
- { test: kindWithInterpolationTest(OpKind.HostProperty, false) },
20712
20911
  { test: kindTest(OpKind.StyleMap), transform: keepLast },
20713
20912
  { test: kindTest(OpKind.ClassMap), transform: keepLast },
20714
20913
  { test: kindTest(OpKind.StyleProp) },
20715
20914
  { test: kindTest(OpKind.ClassProp) },
20716
- { test: kindWithInterpolationTest(OpKind.Property, true) },
20717
20915
  { test: kindWithInterpolationTest(OpKind.Attribute, true) },
20916
+ { test: kindWithInterpolationTest(OpKind.Property, true) },
20718
20917
  { test: kindWithInterpolationTest(OpKind.Property, false) },
20719
20918
  { test: kindWithInterpolationTest(OpKind.Attribute, false) },
20720
20919
  ];
20920
+ /**
20921
+ * Host bindings have their own update ordering.
20922
+ */
20923
+ const UPDATE_HOST_ORDERING = [
20924
+ { test: kindWithInterpolationTest(OpKind.HostProperty, true) },
20925
+ { test: kindWithInterpolationTest(OpKind.HostProperty, false) },
20926
+ { test: kindTest(OpKind.Attribute) },
20927
+ { test: kindTest(OpKind.StyleMap), transform: keepLast },
20928
+ { test: kindTest(OpKind.ClassMap), transform: keepLast },
20929
+ { test: kindTest(OpKind.StyleProp) },
20930
+ { test: kindTest(OpKind.ClassProp) },
20931
+ ];
20721
20932
  /**
20722
20933
  * The set of all op kinds we handle in the reordering phase.
20723
20934
  */
@@ -20738,7 +20949,8 @@ function orderOps(job) {
20738
20949
  // Create mode:
20739
20950
  orderWithin(unit.create, CREATE_ORDERING);
20740
20951
  // Update mode:
20741
- orderWithin(unit.update, UPDATE_ORDERING);
20952
+ const ordering = unit.job.kind === CompilationJobKind.Host ? UPDATE_HOST_ORDERING : UPDATE_ORDERING;
20953
+ orderWithin(unit.update, ordering);
20742
20954
  }
20743
20955
  }
20744
20956
  /**
@@ -20793,21 +21005,39 @@ function keepLast(ops) {
20793
21005
  * class property.
20794
21006
  */
20795
21007
  function parseExtractedStyles(job) {
21008
+ const elements = new Map();
21009
+ for (const unit of job.units) {
21010
+ for (const op of unit.create) {
21011
+ if (isElementOrContainerOp(op)) {
21012
+ elements.set(op.xref, op);
21013
+ }
21014
+ }
21015
+ }
20796
21016
  for (const unit of job.units) {
20797
21017
  for (const op of unit.create) {
20798
21018
  if (op.kind === OpKind.ExtractedAttribute && op.bindingKind === BindingKind.Attribute &&
20799
21019
  isStringLiteral(op.expression)) {
21020
+ const target = elements.get(op.target);
21021
+ if (target !== undefined && target.kind === OpKind.Template &&
21022
+ target.templateKind === TemplateKind.Structural) {
21023
+ // TemplateDefinitionBuilder will not apply class and style bindings to structural
21024
+ // directives; instead, it will leave them as attributes.
21025
+ // (It's not clear what that would mean, anyway -- classes and styles on a structural
21026
+ // element should probably be a parse error.)
21027
+ // TODO: We may be able to remove this once Template Pipeline is the default.
21028
+ continue;
21029
+ }
20800
21030
  if (op.name === 'style') {
20801
21031
  const parsedStyles = parse(op.expression.value);
20802
21032
  for (let i = 0; i < parsedStyles.length - 1; i += 2) {
20803
- OpList.insertBefore(createExtractedAttributeOp(op.target, BindingKind.StyleProperty, parsedStyles[i], literal(parsedStyles[i + 1]), null), op);
21033
+ OpList.insertBefore(createExtractedAttributeOp(op.target, BindingKind.StyleProperty, parsedStyles[i], literal(parsedStyles[i + 1]), null, null, SecurityContext.STYLE), op);
20804
21034
  }
20805
21035
  OpList.remove(op);
20806
21036
  }
20807
21037
  else if (op.name === 'class') {
20808
21038
  const parsedClasses = op.expression.value.trim().split(/\s+/g);
20809
21039
  for (const parsedClass of parsedClasses) {
20810
- OpList.insertBefore(createExtractedAttributeOp(op.target, BindingKind.ClassName, parsedClass, null, null), op);
21040
+ OpList.insertBefore(createExtractedAttributeOp(op.target, BindingKind.ClassName, parsedClass, null, null, null, SecurityContext.NONE), op);
20811
21041
  }
20812
21042
  OpList.remove(op);
20813
21043
  }
@@ -20831,15 +21061,6 @@ function removeContentSelectors(job) {
20831
21061
  OpList.remove(op);
20832
21062
  }
20833
21063
  break;
20834
- case OpKind.Projection:
20835
- // op.attributes is an array of [attr1-name, attr1-value, attr2-name, attr2-value, ...],
20836
- // find the "select" attribute and remove its name and corresponding value.
20837
- for (let i = op.attributes.length - 2; i >= 0; i -= 2) {
20838
- if (isSelectAttribute(op.attributes[i])) {
20839
- op.attributes.splice(i, 2);
20840
- }
20841
- }
20842
- break;
20843
21064
  }
20844
21065
  }
20845
21066
  }
@@ -20973,22 +21194,37 @@ function propagateI18nBlocksToTemplates(unit, subTemplateIndex) {
20973
21194
  i18nBlock = null;
20974
21195
  break;
20975
21196
  case OpKind.Template:
20976
- const templateView = unit.job.views.get(op.xref);
20977
- // We found an <ng-template> inside an i18n block; increment the sub-template counter and
20978
- // wrap the template's view in a child i18n block.
20979
- if (op.i18nPlaceholder !== undefined) {
20980
- if (i18nBlock === null) {
20981
- throw Error('Expected template with i18n placeholder to be in an i18n block.');
20982
- }
20983
- subTemplateIndex++;
20984
- wrapTemplateWithI18n(templateView, i18nBlock);
21197
+ subTemplateIndex = propagateI18nBlocksForView(unit.job.views.get(op.xref), i18nBlock, op.i18nPlaceholder, subTemplateIndex);
21198
+ break;
21199
+ case OpKind.RepeaterCreate:
21200
+ // Propagate i18n blocks to the @for template.
21201
+ const forView = unit.job.views.get(op.xref);
21202
+ subTemplateIndex = propagateI18nBlocksForView(unit.job.views.get(op.xref), i18nBlock, op.i18nPlaceholder, subTemplateIndex);
21203
+ // Then if there's an @empty template, propagate the i18n blocks for it as well.
21204
+ if (op.emptyView !== null) {
21205
+ subTemplateIndex = propagateI18nBlocksForView(unit.job.views.get(op.emptyView), i18nBlock, op.emptyI18nPlaceholder, subTemplateIndex);
20985
21206
  }
20986
- // Continue traversing inside the template's view.
20987
- subTemplateIndex = propagateI18nBlocksToTemplates(templateView, subTemplateIndex);
21207
+ break;
20988
21208
  }
20989
21209
  }
20990
21210
  return subTemplateIndex;
20991
21211
  }
21212
+ /**
21213
+ * Propagate i18n blocks for a view.
21214
+ */
21215
+ function propagateI18nBlocksForView(view, i18nBlock, i18nPlaceholder, subTemplateIndex) {
21216
+ // We found an <ng-template> inside an i18n block; increment the sub-template counter and
21217
+ // wrap the template's view in a child i18n block.
21218
+ if (i18nPlaceholder !== undefined) {
21219
+ if (i18nBlock === null) {
21220
+ throw Error('Expected template with i18n placeholder to be in an i18n block.');
21221
+ }
21222
+ subTemplateIndex++;
21223
+ wrapTemplateWithI18n(view, i18nBlock);
21224
+ }
21225
+ // Continue traversing inside the template's view.
21226
+ return propagateI18nBlocksToTemplates(view, subTemplateIndex);
21227
+ }
20992
21228
  /**
20993
21229
  * Wraps a template view with i18n start and end ops.
20994
21230
  */
@@ -20996,8 +21232,10 @@ function wrapTemplateWithI18n(unit, parentI18n) {
20996
21232
  // Only add i18n ops if they have not already been propagated to this template.
20997
21233
  if (unit.create.head.next?.kind !== OpKind.I18nStart) {
20998
21234
  const id = unit.job.allocateXrefId();
20999
- OpList.insertAfter(createI18nStartOp(id, parentI18n.message, parentI18n.root), unit.create.head);
21000
- OpList.insertBefore(createI18nEndOp(id), unit.create.tail);
21235
+ OpList.insertAfter(
21236
+ // Nested ng-template i18n start/end ops should not recieve source spans.
21237
+ createI18nStartOp(id, parentI18n.message, parentI18n.root, null), unit.create.head);
21238
+ OpList.insertBefore(createI18nEndOp(id, null), unit.create.tail);
21001
21239
  }
21002
21240
  }
21003
21241
 
@@ -21154,17 +21392,13 @@ function disableBindings() {
21154
21392
  function enableBindings() {
21155
21393
  return call(Identifiers.enableBindings, [], null);
21156
21394
  }
21157
- function listener(name, handlerFn, sourceSpan) {
21158
- return call(Identifiers.listener, [
21159
- literal(name),
21160
- handlerFn,
21161
- ], sourceSpan);
21162
- }
21163
- function syntheticHostListener(name, handlerFn, sourceSpan) {
21164
- return call(Identifiers.syntheticHostListener, [
21165
- literal(name),
21166
- handlerFn,
21167
- ], sourceSpan);
21395
+ function listener(name, handlerFn, eventTargetResolver, syntheticHost, sourceSpan) {
21396
+ const args = [literal(name), handlerFn];
21397
+ if (eventTargetResolver !== null) {
21398
+ args.push(literal(false)); // `useCapture` flag, defaults to `false`
21399
+ args.push(importExpr(eventTargetResolver));
21400
+ }
21401
+ return call(syntheticHost ? Identifiers.syntheticHostListener : Identifiers.listener, args, sourceSpan);
21168
21402
  }
21169
21403
  function pipe(slot, name) {
21170
21404
  return call(Identifiers.pipe, [
@@ -21262,22 +21496,22 @@ function projectionDef(def) {
21262
21496
  }
21263
21497
  function projection(slot, projectionSlotIndex, attributes, sourceSpan) {
21264
21498
  const args = [literal(slot)];
21265
- if (projectionSlotIndex !== 0 || attributes.length > 0) {
21499
+ if (projectionSlotIndex !== 0 || attributes !== null) {
21266
21500
  args.push(literal(projectionSlotIndex));
21267
- if (attributes.length > 0) {
21268
- args.push(literalArr(attributes.map(attr => literal(attr))));
21501
+ if (attributes !== null) {
21502
+ args.push(attributes);
21269
21503
  }
21270
21504
  }
21271
21505
  return call(Identifiers.projection, args, sourceSpan);
21272
21506
  }
21273
- function i18nStart(slot, constIndex, subTemplateIndex) {
21507
+ function i18nStart(slot, constIndex, subTemplateIndex, sourceSpan) {
21274
21508
  const args = [literal(slot), literal(constIndex)];
21275
21509
  if (subTemplateIndex !== null) {
21276
21510
  args.push(literal(subTemplateIndex));
21277
21511
  }
21278
- return call(Identifiers.i18nStart, args, null);
21512
+ return call(Identifiers.i18nStart, args, sourceSpan);
21279
21513
  }
21280
- function repeaterCreate(slot, viewFnName, decls, vars, tag, constIndex, trackByFn, trackByUsesComponentInstance, emptyViewFnName, emptyDecls, emptyVars, sourceSpan) {
21514
+ function repeaterCreate(slot, viewFnName, decls, vars, tag, constIndex, trackByFn, trackByUsesComponentInstance, emptyViewFnName, emptyDecls, emptyVars, emptyTag, emptyConstIndex, sourceSpan) {
21281
21515
  const args = [
21282
21516
  literal(slot),
21283
21517
  variable(viewFnName),
@@ -21291,6 +21525,12 @@ function repeaterCreate(slot, viewFnName, decls, vars, tag, constIndex, trackByF
21291
21525
  args.push(literal(trackByUsesComponentInstance));
21292
21526
  if (emptyViewFnName !== null) {
21293
21527
  args.push(variable(emptyViewFnName), literal(emptyDecls), literal(emptyVars));
21528
+ if (emptyTag !== null || emptyConstIndex !== null) {
21529
+ args.push(literal(emptyTag));
21530
+ }
21531
+ if (emptyConstIndex !== null) {
21532
+ args.push(literal(emptyConstIndex));
21533
+ }
21294
21534
  }
21295
21535
  }
21296
21536
  return call(Identifiers.repeaterCreate, args, sourceSpan);
@@ -21301,15 +21541,15 @@ function repeater(collection, sourceSpan) {
21301
21541
  function deferWhen(prefetch, expr, sourceSpan) {
21302
21542
  return call(prefetch ? Identifiers.deferPrefetchWhen : Identifiers.deferWhen, [expr], sourceSpan);
21303
21543
  }
21304
- function i18n(slot, constIndex, subTemplateIndex) {
21544
+ function i18n(slot, constIndex, subTemplateIndex, sourceSpan) {
21305
21545
  const args = [literal(slot), literal(constIndex)];
21306
21546
  if (subTemplateIndex) {
21307
21547
  args.push(literal(subTemplateIndex));
21308
21548
  }
21309
- return call(Identifiers.i18n, args, null);
21549
+ return call(Identifiers.i18n, args, sourceSpan);
21310
21550
  }
21311
- function i18nEnd() {
21312
- return call(Identifiers.i18nEnd, [], null);
21551
+ function i18nEnd(endSourceSpan) {
21552
+ return call(Identifiers.i18nEnd, [], endSourceSpan);
21313
21553
  }
21314
21554
  function i18nAttributes(slot, i18nAttributesConfig) {
21315
21555
  const args = [literal(slot), literal(i18nAttributesConfig)];
@@ -21425,8 +21665,12 @@ function classMapInterpolate(strings, expressions, sourceSpan) {
21425
21665
  const interpolationArgs = collateInterpolationArgs(strings, expressions);
21426
21666
  return callVariadicInstruction(CLASS_MAP_INTERPOLATE_CONFIG, [], interpolationArgs, [], sourceSpan);
21427
21667
  }
21428
- function hostProperty(name, expression, sourceSpan) {
21429
- return call(Identifiers.hostProperty, [literal(name), expression], sourceSpan);
21668
+ function hostProperty(name, expression, sanitizer, sourceSpan) {
21669
+ const args = [literal(name), expression];
21670
+ if (sanitizer !== null) {
21671
+ args.push(sanitizer);
21672
+ }
21673
+ return call(Identifiers.hostProperty, args, sourceSpan);
21430
21674
  }
21431
21675
  function syntheticHostProperty(name, expression, sourceSpan) {
21432
21676
  return call(Identifiers.syntheticHostProperty, [literal(name), expression], sourceSpan);
@@ -21644,14 +21888,12 @@ function callVariadicInstruction(config, baseArgs, interpolationArgs, extraArgs,
21644
21888
  }
21645
21889
 
21646
21890
  /**
21647
- * Map of sanitizers to their identifier.
21891
+ * Map of target resolvers for event listeners.
21648
21892
  */
21649
- const sanitizerIdentifierMap = new Map([
21650
- [SanitizerFn.Html, Identifiers.sanitizeHtml],
21651
- [SanitizerFn.IframeAttribute, Identifiers.validateIframeAttribute],
21652
- [SanitizerFn.ResourceUrl, Identifiers.sanitizeResourceUrl],
21653
- [SanitizerFn.Script, Identifiers.sanitizeScript],
21654
- [SanitizerFn.Style, Identifiers.sanitizeStyle], [SanitizerFn.Url, Identifiers.sanitizeUrl]
21893
+ const GLOBAL_TARGET_RESOLVERS$1 = new Map([
21894
+ ['window', Identifiers.resolveWindow],
21895
+ ['document', Identifiers.resolveDocument],
21896
+ ['body', Identifiers.resolveBody],
21655
21897
  ]);
21656
21898
  /**
21657
21899
  * Compiles semantic operations across all views and generates output `o.Statement`s with actual
@@ -21667,6 +21909,31 @@ function reify(job) {
21667
21909
  reifyUpdateOperations(unit, unit.update);
21668
21910
  }
21669
21911
  }
21912
+ /**
21913
+ * This function can be used a sanity check -- it walks every expression in the const pool, and
21914
+ * every expression reachable from an op, and makes sure that there are no IR expressions
21915
+ * left. This is nice to use for debugging mysterious failures where an IR expression cannot be
21916
+ * output from the output AST code.
21917
+ */
21918
+ function ensureNoIrForDebug(job) {
21919
+ for (const stmt of job.pool.statements) {
21920
+ transformExpressionsInStatement(stmt, expr => {
21921
+ if (isIrExpression(expr)) {
21922
+ throw new Error(`AssertionError: IR expression found during reify: ${ExpressionKind[expr.kind]}`);
21923
+ }
21924
+ return expr;
21925
+ }, VisitorContextFlag.None);
21926
+ }
21927
+ for (const unit of job.units) {
21928
+ for (const op of unit.ops()) {
21929
+ visitExpressionsInOp(op, expr => {
21930
+ if (isIrExpression(expr)) {
21931
+ throw new Error(`AssertionError: IR expression found during reify: ${ExpressionKind[expr.kind]}`);
21932
+ }
21933
+ });
21934
+ }
21935
+ }
21936
+ }
21670
21937
  function reifyCreateOperations(unit, ops) {
21671
21938
  for (const op of ops) {
21672
21939
  transformExpressionsInOp(op, reifyIrExpression, VisitorContextFlag.None);
@@ -21675,31 +21942,31 @@ function reifyCreateOperations(unit, ops) {
21675
21942
  OpList.replace(op, text(op.handle.slot, op.initialValue, op.sourceSpan));
21676
21943
  break;
21677
21944
  case OpKind.ElementStart:
21678
- OpList.replace(op, elementStart(op.handle.slot, op.tag, op.attributes, op.localRefs, op.sourceSpan));
21945
+ OpList.replace(op, elementStart(op.handle.slot, op.tag, op.attributes, op.localRefs, op.startSourceSpan));
21679
21946
  break;
21680
21947
  case OpKind.Element:
21681
- OpList.replace(op, element(op.handle.slot, op.tag, op.attributes, op.localRefs, op.sourceSpan));
21948
+ OpList.replace(op, element(op.handle.slot, op.tag, op.attributes, op.localRefs, op.wholeSourceSpan));
21682
21949
  break;
21683
21950
  case OpKind.ElementEnd:
21684
21951
  OpList.replace(op, elementEnd(op.sourceSpan));
21685
21952
  break;
21686
21953
  case OpKind.ContainerStart:
21687
- OpList.replace(op, elementContainerStart(op.handle.slot, op.attributes, op.localRefs, op.sourceSpan));
21954
+ OpList.replace(op, elementContainerStart(op.handle.slot, op.attributes, op.localRefs, op.startSourceSpan));
21688
21955
  break;
21689
21956
  case OpKind.Container:
21690
- OpList.replace(op, elementContainer(op.handle.slot, op.attributes, op.localRefs, op.sourceSpan));
21957
+ OpList.replace(op, elementContainer(op.handle.slot, op.attributes, op.localRefs, op.wholeSourceSpan));
21691
21958
  break;
21692
21959
  case OpKind.ContainerEnd:
21693
21960
  OpList.replace(op, elementContainerEnd());
21694
21961
  break;
21695
21962
  case OpKind.I18nStart:
21696
- OpList.replace(op, i18nStart(op.handle.slot, op.messageIndex, op.subTemplateIndex));
21963
+ OpList.replace(op, i18nStart(op.handle.slot, op.messageIndex, op.subTemplateIndex, op.sourceSpan));
21697
21964
  break;
21698
21965
  case OpKind.I18nEnd:
21699
- OpList.replace(op, i18nEnd());
21966
+ OpList.replace(op, i18nEnd(op.sourceSpan));
21700
21967
  break;
21701
21968
  case OpKind.I18n:
21702
- OpList.replace(op, i18n(op.handle.slot, op.messageIndex, op.subTemplateIndex));
21969
+ OpList.replace(op, i18n(op.handle.slot, op.messageIndex, op.subTemplateIndex, op.sourceSpan));
21703
21970
  break;
21704
21971
  case OpKind.I18nAttributes:
21705
21972
  if (op.i18nAttributesConfig === null) {
@@ -21715,7 +21982,7 @@ function reifyCreateOperations(unit, ops) {
21715
21982
  throw new Error(`AssertionError: local refs array should have been extracted into a constant`);
21716
21983
  }
21717
21984
  const childView = unit.job.views.get(op.xref);
21718
- OpList.replace(op, template(op.handle.slot, variable(childView.fnName), childView.decls, childView.vars, op.tag, op.attributes, op.localRefs, op.sourceSpan));
21985
+ OpList.replace(op, template(op.handle.slot, variable(childView.fnName), childView.decls, childView.vars, op.tag, op.attributes, op.localRefs, op.startSourceSpan));
21719
21986
  break;
21720
21987
  case OpKind.DisableBindings:
21721
21988
  OpList.replace(op, disableBindings());
@@ -21728,10 +21995,11 @@ function reifyCreateOperations(unit, ops) {
21728
21995
  break;
21729
21996
  case OpKind.Listener:
21730
21997
  const listenerFn = reifyListenerHandler(unit, op.handlerFnName, op.handlerOps, op.consumesDollarEvent);
21731
- const reified = op.hostListener && op.isAnimationListener ?
21732
- syntheticHostListener(op.name, listenerFn, op.sourceSpan) :
21733
- listener(op.name, listenerFn, op.sourceSpan);
21734
- OpList.replace(op, reified);
21998
+ const eventTargetResolver = op.eventTarget ? GLOBAL_TARGET_RESOLVERS$1.get(op.eventTarget) : null;
21999
+ if (eventTargetResolver === undefined) {
22000
+ throw new Error(`Unexpected global target '${op.eventTarget}' defined for '${op.name}' event. Supported list of global targets: window,document,body.`);
22001
+ }
22002
+ OpList.replace(op, listener(op.name, listenerFn, eventTargetResolver, op.hostListener && op.isAnimationListener, op.sourceSpan));
21735
22003
  break;
21736
22004
  case OpKind.Variable:
21737
22005
  if (op.variable.name === null) {
@@ -21816,7 +22084,7 @@ function reifyCreateOperations(unit, ops) {
21816
22084
  emptyDecls = emptyView.decls;
21817
22085
  emptyVars = emptyView.vars;
21818
22086
  }
21819
- OpList.replace(op, repeaterCreate(op.handle.slot, repeaterView.fnName, op.decls, op.vars, op.tag, op.attributes, op.trackByFn, op.usesComponentInstance, emptyViewFnName, emptyDecls, emptyVars, op.sourceSpan));
22087
+ OpList.replace(op, repeaterCreate(op.handle.slot, repeaterView.fnName, op.decls, op.vars, op.tag, op.attributes, op.trackByFn, op.usesComponentInstance, emptyViewFnName, emptyDecls, emptyVars, op.emptyTag, op.emptyAttributes, op.wholeSourceSpan));
21820
22088
  break;
21821
22089
  case OpKind.Statement:
21822
22090
  // Pass statement operations directly through.
@@ -21894,7 +22162,7 @@ function reifyUpdateOperations(_unit, ops) {
21894
22162
  OpList.replace(op, syntheticHostProperty(op.name, op.expression, op.sourceSpan));
21895
22163
  }
21896
22164
  else {
21897
- OpList.replace(op, hostProperty(op.name, op.expression, op.sourceSpan));
22165
+ OpList.replace(op, hostProperty(op.name, op.expression, op.sanitizer, op.sourceSpan));
21898
22166
  }
21899
22167
  }
21900
22168
  break;
@@ -21973,8 +22241,6 @@ function reifyIrExpression(expr) {
21973
22241
  return pipeBind(expr.targetSlot.slot, expr.varOffset, expr.args);
21974
22242
  case ExpressionKind.PipeBindingVariadic:
21975
22243
  return pipeBindV(expr.targetSlot.slot, expr.varOffset, expr.args);
21976
- case ExpressionKind.SanitizerExpr:
21977
- return importExpr(sanitizerIdentifierMap.get(expr.fn));
21978
22244
  case ExpressionKind.SlotLiteralExpr:
21979
22245
  return literal(expr.slot.slot);
21980
22246
  default:
@@ -22267,10 +22533,11 @@ function resolvePlaceholdersForView(job, unit, i18nContexts, elements, pendingSt
22267
22533
  }
22268
22534
  break;
22269
22535
  case OpKind.Template:
22536
+ const view = job.views.get(op.xref);
22270
22537
  if (op.i18nPlaceholder === undefined) {
22271
22538
  // If there is no i18n placeholder, just recurse into the view in case it contains i18n
22272
22539
  // blocks.
22273
- resolvePlaceholdersForView(job, job.views.get(op.xref), i18nContexts, elements);
22540
+ resolvePlaceholdersForView(job, view, i18nContexts, elements);
22274
22541
  }
22275
22542
  else {
22276
22543
  if (currentOps === null) {
@@ -22281,14 +22548,59 @@ function resolvePlaceholdersForView(job, unit, i18nContexts, elements, pendingSt
22281
22548
  // the current template as a pending structural directive to be recorded when we find
22282
22549
  // the element, content, or template it belongs to. This allows us to create combined
22283
22550
  // values that represent, e.g. the start of a template and element at the same time.
22284
- resolvePlaceholdersForView(job, job.views.get(op.xref), i18nContexts, elements, op);
22551
+ resolvePlaceholdersForView(job, view, i18nContexts, elements, op);
22285
22552
  }
22286
22553
  else {
22287
22554
  // If this is some other kind of template, we can record its start, recurse into its
22288
22555
  // view, and then record its end.
22289
- recordTemplateStart(job, op, currentOps.i18nContext, currentOps.i18nBlock, pendingStructuralDirective);
22290
- resolvePlaceholdersForView(job, job.views.get(op.xref), i18nContexts, elements);
22291
- recordTemplateClose(job, op, currentOps.i18nContext, currentOps.i18nBlock, pendingStructuralDirective);
22556
+ recordTemplateStart(job, view, op.handle.slot, op.i18nPlaceholder, currentOps.i18nContext, currentOps.i18nBlock, pendingStructuralDirective);
22557
+ resolvePlaceholdersForView(job, view, i18nContexts, elements);
22558
+ recordTemplateClose(job, view, op.handle.slot, op.i18nPlaceholder, currentOps.i18nContext, currentOps.i18nBlock, pendingStructuralDirective);
22559
+ pendingStructuralDirective = undefined;
22560
+ }
22561
+ }
22562
+ break;
22563
+ case OpKind.RepeaterCreate:
22564
+ if (pendingStructuralDirective !== undefined) {
22565
+ throw Error('AssertionError: Unexpected structural directive associated with @for block');
22566
+ }
22567
+ // RepeaterCreate has 3 slots: the first is for the op itself, the second is for the @for
22568
+ // template and the (optional) third is for the @empty template.
22569
+ const forSlot = op.handle.slot + 1;
22570
+ const forView = job.views.get(op.xref);
22571
+ // First record all of the placeholders for the @for template.
22572
+ if (op.i18nPlaceholder === undefined) {
22573
+ // If there is no i18n placeholder, just recurse into the view in case it contains i18n
22574
+ // blocks.
22575
+ resolvePlaceholdersForView(job, forView, i18nContexts, elements);
22576
+ }
22577
+ else {
22578
+ if (currentOps === null) {
22579
+ throw Error('i18n tag placeholder should only occur inside an i18n block');
22580
+ }
22581
+ recordTemplateStart(job, forView, forSlot, op.i18nPlaceholder, currentOps.i18nContext, currentOps.i18nBlock, pendingStructuralDirective);
22582
+ resolvePlaceholdersForView(job, forView, i18nContexts, elements);
22583
+ recordTemplateClose(job, forView, forSlot, op.i18nPlaceholder, currentOps.i18nContext, currentOps.i18nBlock, pendingStructuralDirective);
22584
+ pendingStructuralDirective = undefined;
22585
+ }
22586
+ // Then if there's an @empty template, add its placeholders as well.
22587
+ if (op.emptyView !== null) {
22588
+ // RepeaterCreate has 3 slots: the first is for the op itself, the second is for the @for
22589
+ // template and the (optional) third is for the @empty template.
22590
+ const emptySlot = op.handle.slot + 2;
22591
+ const emptyView = job.views.get(op.emptyView);
22592
+ if (op.emptyI18nPlaceholder === undefined) {
22593
+ // If there is no i18n placeholder, just recurse into the view in case it contains i18n
22594
+ // blocks.
22595
+ resolvePlaceholdersForView(job, emptyView, i18nContexts, elements);
22596
+ }
22597
+ else {
22598
+ if (currentOps === null) {
22599
+ throw Error('i18n tag placeholder should only occur inside an i18n block');
22600
+ }
22601
+ recordTemplateStart(job, emptyView, emptySlot, op.emptyI18nPlaceholder, currentOps.i18nContext, currentOps.i18nBlock, pendingStructuralDirective);
22602
+ resolvePlaceholdersForView(job, emptyView, i18nContexts, elements);
22603
+ recordTemplateClose(job, emptyView, emptySlot, op.emptyI18nPlaceholder, currentOps.i18nContext, currentOps.i18nBlock, pendingStructuralDirective);
22292
22604
  pendingStructuralDirective = undefined;
22293
22605
  }
22294
22606
  }
@@ -22336,8 +22648,8 @@ function recordElementClose(op, i18nContext, i18nBlock, structuralDirective) {
22336
22648
  /**
22337
22649
  * Records an i18n param value for the start of a template.
22338
22650
  */
22339
- function recordTemplateStart(job, op, i18nContext, i18nBlock, structuralDirective) {
22340
- let { startName, closeName } = op.i18nPlaceholder;
22651
+ function recordTemplateStart(job, view, slot, i18nPlaceholder, i18nContext, i18nBlock, structuralDirective) {
22652
+ let { startName, closeName } = i18nPlaceholder;
22341
22653
  let flags = I18nParamValueFlags.TemplateTag | I18nParamValueFlags.OpenTag;
22342
22654
  // For self-closing tags, there is no close tag placeholder. Instead, the start tag
22343
22655
  // placeholder accounts for the start and close of the element.
@@ -22352,20 +22664,20 @@ function recordTemplateStart(job, op, i18nContext, i18nBlock, structuralDirectiv
22352
22664
  }
22353
22665
  // Record the start of the template. For the sub-template index, pass the index for the template's
22354
22666
  // view, rather than the current i18n block's index.
22355
- addParam(i18nContext.params, startName, op.handle.slot, getSubTemplateIndexForTemplateTag(job, i18nBlock, op), flags);
22667
+ addParam(i18nContext.params, startName, slot, getSubTemplateIndexForTemplateTag(job, i18nBlock, view), flags);
22356
22668
  }
22357
22669
  /**
22358
22670
  * Records an i18n param value for the closing of a template.
22359
22671
  */
22360
- function recordTemplateClose(job, op, i18nContext, i18nBlock, structuralDirective) {
22361
- const { startName, closeName } = op.i18nPlaceholder;
22672
+ function recordTemplateClose(job, view, slot, i18nPlaceholder, i18nContext, i18nBlock, structuralDirective) {
22673
+ const { startName, closeName } = i18nPlaceholder;
22362
22674
  const flags = I18nParamValueFlags.TemplateTag | I18nParamValueFlags.CloseTag;
22363
22675
  // Self-closing tags don't have a closing tag placeholder, instead the template's closing is
22364
22676
  // recorded via an additional flag on the template start value.
22365
22677
  if (closeName) {
22366
22678
  // Record the closing of the template. For the sub-template index, pass the index for the
22367
22679
  // template's view, rather than the current i18n block's index.
22368
- addParam(i18nContext.params, closeName, op.handle.slot, getSubTemplateIndexForTemplateTag(job, i18nBlock, op), flags);
22680
+ addParam(i18nContext.params, closeName, slot, getSubTemplateIndexForTemplateTag(job, i18nBlock, view), flags);
22369
22681
  // If the template is associated with a structural directive, record the structural directive's
22370
22682
  // closing after. Since this template must be in the structural directive's view, we can just
22371
22683
  // directly use the current i18n block's sub-template index.
@@ -22378,8 +22690,8 @@ function recordTemplateClose(job, op, i18nContext, i18nBlock, structuralDirectiv
22378
22690
  * Get the subTemplateIndex for the given template op. For template ops, use the subTemplateIndex of
22379
22691
  * the child i18n block inside the template.
22380
22692
  */
22381
- function getSubTemplateIndexForTemplateTag(job, i18nOp, op) {
22382
- for (const childOp of job.views.get(op.xref).create) {
22693
+ function getSubTemplateIndexForTemplateTag(job, i18nOp, view) {
22694
+ for (const childOp of view.create) {
22383
22695
  if (childOp.kind === OpKind.I18nStart) {
22384
22696
  return childOp.subTemplateIndex;
22385
22697
  }
@@ -22402,6 +22714,7 @@ function resolveI18nExpressionPlaceholders(job) {
22402
22714
  // Record all of the i18n context ops, and the sub-template index for each i18n op.
22403
22715
  const subTemplateIndicies = new Map();
22404
22716
  const i18nContexts = new Map();
22717
+ const icuPlaceholders = new Map();
22405
22718
  for (const unit of job.units) {
22406
22719
  for (const op of unit.create) {
22407
22720
  switch (op.kind) {
@@ -22411,6 +22724,9 @@ function resolveI18nExpressionPlaceholders(job) {
22411
22724
  case OpKind.I18nContext:
22412
22725
  i18nContexts.set(op.xref, op);
22413
22726
  break;
22727
+ case OpKind.IcuPlaceholder:
22728
+ icuPlaceholders.set(op.xref, op);
22729
+ break;
22414
22730
  }
22415
22731
  }
22416
22732
  }
@@ -22424,76 +22740,32 @@ function resolveI18nExpressionPlaceholders(job) {
22424
22740
  for (const unit of job.units) {
22425
22741
  for (const op of unit.update) {
22426
22742
  if (op.kind === OpKind.I18nExpression) {
22427
- const i18nContext = i18nContexts.get(op.context);
22428
22743
  const index = expressionIndices.get(referenceIndex(op)) || 0;
22429
22744
  const subTemplateIndex = subTemplateIndicies.get(op.i18nOwner) ?? null;
22430
- // Add the expression index in the appropriate params map.
22431
- const params = op.resolutionTime === I18nParamResolutionTime.Creation ?
22432
- i18nContext.params :
22433
- i18nContext.postprocessingParams;
22434
- const values = params.get(op.i18nPlaceholder) || [];
22435
- values.push({
22745
+ const value = {
22436
22746
  value: index,
22437
22747
  subTemplateIndex: subTemplateIndex,
22438
22748
  flags: I18nParamValueFlags.ExpressionIndex
22439
- });
22440
- params.set(op.i18nPlaceholder, values);
22749
+ };
22750
+ updatePlaceholder(op, value, i18nContexts, icuPlaceholders);
22441
22751
  expressionIndices.set(referenceIndex(op), index + 1);
22442
22752
  }
22443
22753
  }
22444
22754
  }
22445
22755
  }
22446
-
22447
- /**
22448
- * Resolves placeholders for element tags inside of an ICU.
22449
- */
22450
- function resolveI18nIcuPlaceholders(job) {
22451
- for (const unit of job.units) {
22452
- for (const op of unit.create) {
22453
- if (op.kind === OpKind.I18nContext && op.contextKind === I18nContextKind.Icu) {
22454
- for (const node of op.message.nodes) {
22455
- node.visit(new ResolveIcuPlaceholdersVisitor(op.postprocessingParams));
22456
- }
22457
- }
22458
- }
22756
+ function updatePlaceholder(op, value, i18nContexts, icuPlaceholders) {
22757
+ if (op.i18nPlaceholder !== null) {
22758
+ const i18nContext = i18nContexts.get(op.context);
22759
+ const params = op.resolutionTime === I18nParamResolutionTime.Creation ?
22760
+ i18nContext.params :
22761
+ i18nContext.postprocessingParams;
22762
+ const values = params.get(op.i18nPlaceholder) || [];
22763
+ values.push(value);
22764
+ params.set(op.i18nPlaceholder, values);
22459
22765
  }
22460
- }
22461
- /**
22462
- * Visitor for i18n AST that resolves ICU params into the given map.
22463
- */
22464
- class ResolveIcuPlaceholdersVisitor extends RecurseVisitor {
22465
- constructor(params) {
22466
- super();
22467
- this.params = params;
22468
- }
22469
- visitContainerPlaceholder(placeholder) {
22470
- // Add the start and end source span for container placeholders. These need to be recorded for
22471
- // elements inside ICUs. The slots for the nodes were recorded separately under the i18n
22472
- // block's context as part of the `resolveI18nElementPlaceholders` phase.
22473
- if (placeholder.startName && placeholder.startSourceSpan &&
22474
- !this.params.has(placeholder.startName)) {
22475
- this.params.set(placeholder.startName, [{
22476
- value: placeholder.startSourceSpan?.toString(),
22477
- subTemplateIndex: null,
22478
- flags: I18nParamValueFlags.None
22479
- }]);
22480
- }
22481
- if (placeholder.closeName && placeholder.endSourceSpan &&
22482
- !this.params.has(placeholder.closeName)) {
22483
- this.params.set(placeholder.closeName, [{
22484
- value: placeholder.endSourceSpan?.toString(),
22485
- subTemplateIndex: null,
22486
- flags: I18nParamValueFlags.None
22487
- }]);
22488
- }
22489
- }
22490
- visitTagPlaceholder(placeholder) {
22491
- super.visitTagPlaceholder(placeholder);
22492
- this.visitContainerPlaceholder(placeholder);
22493
- }
22494
- visitBlockPlaceholder(placeholder) {
22495
- super.visitBlockPlaceholder(placeholder);
22496
- this.visitContainerPlaceholder(placeholder);
22766
+ if (op.icuPlaceholder !== null) {
22767
+ const icuPlaceholderOp = icuPlaceholders.get(op.icuPlaceholder);
22768
+ icuPlaceholderOp?.expressionPlaceholders.push(value);
22497
22769
  }
22498
22770
  }
22499
22771
 
@@ -22596,12 +22868,20 @@ function processLexicalScope(unit, ops, savedView) {
22596
22868
  }
22597
22869
 
22598
22870
  /**
22599
- * Mapping of security contexts to sanitizer function for that context.
22871
+ * Map of security contexts to their sanitizer function.
22872
+ */
22873
+ const sanitizerFns = new Map([
22874
+ [SecurityContext.HTML, Identifiers.sanitizeHtml],
22875
+ [SecurityContext.RESOURCE_URL, Identifiers.sanitizeResourceUrl],
22876
+ [SecurityContext.SCRIPT, Identifiers.sanitizeScript],
22877
+ [SecurityContext.STYLE, Identifiers.sanitizeStyle], [SecurityContext.URL, Identifiers.sanitizeUrl]
22878
+ ]);
22879
+ /**
22880
+ * Map of security contexts to their trusted value function.
22600
22881
  */
22601
- const sanitizers = new Map([
22602
- [SecurityContext.HTML, SanitizerFn.Html], [SecurityContext.SCRIPT, SanitizerFn.Script],
22603
- [SecurityContext.STYLE, SanitizerFn.Style], [SecurityContext.URL, SanitizerFn.Url],
22604
- [SecurityContext.RESOURCE_URL, SanitizerFn.ResourceUrl]
22882
+ const trustedValueFns = new Map([
22883
+ [SecurityContext.HTML, Identifiers.trustConstantHtml],
22884
+ [SecurityContext.RESOURCE_URL, Identifiers.trustConstantResourceUrl],
22605
22885
  ]);
22606
22886
  /**
22607
22887
  * Resolves sanitization functions for ops that need them.
@@ -22609,24 +22889,61 @@ const sanitizers = new Map([
22609
22889
  function resolveSanitizers(job) {
22610
22890
  for (const unit of job.units) {
22611
22891
  const elements = createOpXrefMap(unit);
22612
- let sanitizerFn;
22892
+ // For normal element bindings we create trusted values for security sensitive constant
22893
+ // attributes. However, for host bindings we skip this step (this matches what
22894
+ // TemplateDefinitionBuilder does).
22895
+ // TODO: Is the TDB behavior correct here?
22896
+ if (job.kind !== CompilationJobKind.Host) {
22897
+ for (const op of unit.create) {
22898
+ if (op.kind === OpKind.ExtractedAttribute) {
22899
+ const trustedValueFn = trustedValueFns.get(getOnlySecurityContext(op.securityContext)) ?? null;
22900
+ op.trustedValueFn = trustedValueFn !== null ? importExpr(trustedValueFn) : null;
22901
+ }
22902
+ }
22903
+ }
22613
22904
  for (const op of unit.update) {
22614
22905
  switch (op.kind) {
22615
22906
  case OpKind.Property:
22616
22907
  case OpKind.Attribute:
22617
- sanitizerFn = sanitizers.get(op.securityContext) || null;
22618
- op.sanitizer = sanitizerFn ? new SanitizerExpr(sanitizerFn) : null;
22908
+ case OpKind.HostProperty:
22909
+ let sanitizerFn = null;
22910
+ if (Array.isArray(op.securityContext) && op.securityContext.length === 2 &&
22911
+ op.securityContext.indexOf(SecurityContext.URL) > -1 &&
22912
+ op.securityContext.indexOf(SecurityContext.RESOURCE_URL) > -1) {
22913
+ // When the host element isn't known, some URL attributes (such as "src" and "href") may
22914
+ // be part of multiple different security contexts. In this case we use special
22915
+ // sanitization function and select the actual sanitizer at runtime based on a tag name
22916
+ // that is provided while invoking sanitization function.
22917
+ sanitizerFn = Identifiers.sanitizeUrlOrResourceUrl;
22918
+ }
22919
+ else {
22920
+ sanitizerFn = sanitizerFns.get(getOnlySecurityContext(op.securityContext)) ?? null;
22921
+ }
22922
+ op.sanitizer = sanitizerFn !== null ? importExpr(sanitizerFn) : null;
22619
22923
  // If there was no sanitization function found based on the security context of an
22620
22924
  // attribute/property, check whether this attribute/property is one of the
22621
22925
  // security-sensitive <iframe> attributes (and that the current element is actually an
22622
22926
  // <iframe>).
22623
22927
  if (op.sanitizer === null) {
22624
- const ownerOp = elements.get(op.target);
22625
- if (ownerOp === undefined || !isElementOrContainerOp(ownerOp)) {
22626
- throw Error('Property should have an element-like owner');
22928
+ let isIframe = false;
22929
+ if (job.kind === CompilationJobKind.Host || op.kind === OpKind.HostProperty) {
22930
+ // Note: for host bindings defined on a directive, we do not try to find all
22931
+ // possible places where it can be matched, so we can not determine whether
22932
+ // the host element is an <iframe>. In this case, we just assume it is and append a
22933
+ // validation function, which is invoked at runtime and would have access to the
22934
+ // underlying DOM element to check if it's an <iframe> and if so - run extra checks.
22935
+ isIframe = true;
22936
+ }
22937
+ else {
22938
+ // For a normal binding we can just check if the element its on is an iframe.
22939
+ const ownerOp = elements.get(op.target);
22940
+ if (ownerOp === undefined || !isElementOrContainerOp(ownerOp)) {
22941
+ throw Error('Property should have an element-like owner');
22942
+ }
22943
+ isIframe = isIframeElement$1(ownerOp);
22627
22944
  }
22628
- if (isIframeElement$1(ownerOp) && isIframeSecuritySensitiveAttr(op.name)) {
22629
- op.sanitizer = new SanitizerExpr(SanitizerFn.IframeAttribute);
22945
+ if (isIframe && isIframeSecuritySensitiveAttr(op.name)) {
22946
+ op.sanitizer = importExpr(Identifiers.validateIframeAttribute);
22630
22947
  }
22631
22948
  }
22632
22949
  break;
@@ -22640,6 +22957,22 @@ function resolveSanitizers(job) {
22640
22957
  function isIframeElement$1(op) {
22641
22958
  return op.kind === OpKind.ElementStart && op.tag?.toLowerCase() === 'iframe';
22642
22959
  }
22960
+ /**
22961
+ * Asserts that there is only a single security context and returns it.
22962
+ */
22963
+ function getOnlySecurityContext(securityContext) {
22964
+ if (Array.isArray(securityContext)) {
22965
+ if (securityContext.length > 1) {
22966
+ // TODO: What should we do here? TDB just took the first one, but this feels like something we
22967
+ // would want to know about and create a special case for like we did for Url/ResourceUrl. My
22968
+ // guess is that, outside of the Url/ResourceUrl case, this never actually happens. If there
22969
+ // do turn out to be other cases, throwing an error until we can address it feels safer.
22970
+ throw Error(`AssertionError: Ambiguous security context`);
22971
+ }
22972
+ return securityContext[0] || SecurityContext.NONE;
22973
+ }
22974
+ return securityContext;
22975
+ }
22643
22976
 
22644
22977
  /**
22645
22978
  * When inside of a listener, we may need access to one or more enclosing views. Therefore, each
@@ -22743,6 +23076,8 @@ function allocateSlots(job) {
22743
23076
  // operation itself, so it can be emitted later.
22744
23077
  const childView = job.views.get(op.xref);
22745
23078
  op.decls = childView.decls;
23079
+ // TODO: currently we handle the decls for the RepeaterCreate empty template in the reify
23080
+ // phase. We should handle that here instead.
22746
23081
  }
22747
23082
  }
22748
23083
  }
@@ -22882,6 +23217,9 @@ function generateTrackFns(job) {
22882
23217
  // Find all component context reads.
22883
23218
  let usesComponentContext = false;
22884
23219
  op.track = transformExpressionsInExpression(op.track, expr => {
23220
+ if (expr instanceof PipeBindingExpr || expr instanceof PipeBindingVariadicExpr) {
23221
+ throw new Error(`Illegal State: Pipes are not allowed in this context`);
23222
+ }
22885
23223
  if (expr instanceof TrackContextExpr) {
22886
23224
  usesComponentContext = true;
22887
23225
  return variable('this');
@@ -23066,6 +23404,8 @@ function countVariables(job) {
23066
23404
  }
23067
23405
  const childView = job.views.get(op.xref);
23068
23406
  op.vars = childView.vars;
23407
+ // TODO: currently we handle the vars for the RepeaterCreate empty template in the reify
23408
+ // phase. We should handle that here instead.
23069
23409
  }
23070
23410
  }
23071
23411
  }
@@ -23103,7 +23443,14 @@ function varsUsedByOp(op) {
23103
23443
  return op.interpolation.expressions.length;
23104
23444
  case OpKind.I18nExpression:
23105
23445
  case OpKind.Conditional:
23446
+ case OpKind.DeferWhen:
23106
23447
  return 1;
23448
+ case OpKind.RepeaterCreate:
23449
+ // Repeaters may require an extra variable binding slot, if they have an empty view, for the
23450
+ // empty block tracking.
23451
+ // TODO: It's a bit odd to have a create mode instruction consume variable slots. Maybe we can
23452
+ // find a way to use the Repeater update op instead.
23453
+ return op.emptyView ? 1 : 0;
23107
23454
  default:
23108
23455
  throw new Error(`Unhandled op: ${OpKind[op.kind]}`);
23109
23456
  }
@@ -23559,12 +23906,13 @@ function wrapI18nIcus(job) {
23559
23906
  case OpKind.IcuStart:
23560
23907
  if (currentI18nOp === null) {
23561
23908
  addedI18nId = job.allocateXrefId();
23562
- OpList.insertBefore(createI18nStartOp(addedI18nId, op.message), op);
23909
+ // ICU i18n start/end ops should not recieve source spans.
23910
+ OpList.insertBefore(createI18nStartOp(addedI18nId, op.message, undefined, null), op);
23563
23911
  }
23564
23912
  break;
23565
23913
  case OpKind.IcuEnd:
23566
23914
  if (addedI18nId !== null) {
23567
- OpList.insertAfter(createI18nEndOp(addedI18nId), op);
23915
+ OpList.insertAfter(createI18nEndOp(addedI18nId, null), op);
23568
23916
  addedI18nId = null;
23569
23917
  }
23570
23918
  break;
@@ -23587,10 +23935,11 @@ const phases = [
23587
23935
  { kind: CompilationJobKind.Tmpl, fn: emitNamespaceChanges },
23588
23936
  { kind: CompilationJobKind.Tmpl, fn: propagateI18nBlocks },
23589
23937
  { kind: CompilationJobKind.Tmpl, fn: wrapI18nIcus },
23590
- { kind: CompilationJobKind.Tmpl, fn: createI18nContexts },
23938
+ { kind: CompilationJobKind.Both, fn: deduplicateTextBindings },
23591
23939
  { kind: CompilationJobKind.Both, fn: specializeStyleBindings },
23592
23940
  { kind: CompilationJobKind.Both, fn: specializeBindings },
23593
23941
  { kind: CompilationJobKind.Both, fn: extractAttributes },
23942
+ { kind: CompilationJobKind.Tmpl, fn: createI18nContexts },
23594
23943
  { kind: CompilationJobKind.Both, fn: parseExtractedStyles },
23595
23944
  { kind: CompilationJobKind.Tmpl, fn: removeEmptyBindings },
23596
23945
  { kind: CompilationJobKind.Both, fn: collapseSingletonInterpolations },
@@ -23608,7 +23957,7 @@ const phases = [
23608
23957
  { kind: CompilationJobKind.Tmpl, fn: generateProjectionDefs },
23609
23958
  { kind: CompilationJobKind.Tmpl, fn: generateVariables },
23610
23959
  { kind: CompilationJobKind.Tmpl, fn: saveAndRestoreView },
23611
- { kind: CompilationJobKind.Tmpl, fn: deleteAnyCasts },
23960
+ { kind: CompilationJobKind.Both, fn: deleteAnyCasts },
23612
23961
  { kind: CompilationJobKind.Both, fn: resolveDollarEvent },
23613
23962
  { kind: CompilationJobKind.Tmpl, fn: generateRepeaterDerivedVars },
23614
23963
  { kind: CompilationJobKind.Tmpl, fn: generateTrackVariables },
@@ -23616,7 +23965,7 @@ const phases = [
23616
23965
  { kind: CompilationJobKind.Tmpl, fn: resolveDeferTargetNames },
23617
23966
  { kind: CompilationJobKind.Tmpl, fn: optimizeTrackFns },
23618
23967
  { kind: CompilationJobKind.Both, fn: resolveContexts },
23619
- { kind: CompilationJobKind.Tmpl, fn: resolveSanitizers }, // TODO: run in both
23968
+ { kind: CompilationJobKind.Both, fn: resolveSanitizers },
23620
23969
  { kind: CompilationJobKind.Tmpl, fn: liftLocalRefs },
23621
23970
  { kind: CompilationJobKind.Both, fn: generateNullishCoalesceExpressions },
23622
23971
  { kind: CompilationJobKind.Both, fn: expandSafeReads },
@@ -23625,7 +23974,6 @@ const phases = [
23625
23974
  { kind: CompilationJobKind.Tmpl, fn: createDeferDepsFns },
23626
23975
  { kind: CompilationJobKind.Tmpl, fn: resolveI18nElementPlaceholders },
23627
23976
  { kind: CompilationJobKind.Tmpl, fn: resolveI18nExpressionPlaceholders },
23628
- { kind: CompilationJobKind.Tmpl, fn: resolveI18nIcuPlaceholders },
23629
23977
  { kind: CompilationJobKind.Tmpl, fn: extractI18nMessages },
23630
23978
  { kind: CompilationJobKind.Tmpl, fn: generateTrackFns },
23631
23979
  { kind: CompilationJobKind.Tmpl, fn: collectI18nConsts },
@@ -23752,6 +24100,10 @@ function emitHostBindingFunction(job) {
23752
24100
  }
23753
24101
 
23754
24102
  const compatibilityMode = CompatibilityMode.TemplateDefinitionBuilder;
24103
+ // Schema containing DOM elements and their properties.
24104
+ const domSchema = new DomElementSchemaRegistry();
24105
+ // Tag name of the `ng-template` element.
24106
+ const NG_TEMPLATE_TAG_NAME$1 = 'ng-template';
23755
24107
  /**
23756
24108
  * Process a template AST and convert it into a `ComponentCompilation` in the intermediate
23757
24109
  * representation.
@@ -23769,10 +24121,24 @@ function ingestComponent(componentName, template, constantPool, relativeContextF
23769
24121
  function ingestHostBinding(input, bindingParser, constantPool) {
23770
24122
  const job = new HostBindingCompilationJob(input.componentName, constantPool, compatibilityMode);
23771
24123
  for (const property of input.properties ?? []) {
23772
- ingestHostProperty(job, property, false);
24124
+ let bindingKind = BindingKind.Property;
24125
+ // TODO: this should really be handled in the parser.
24126
+ if (property.name.startsWith('attr.')) {
24127
+ property.name = property.name.substring('attr.'.length);
24128
+ bindingKind = BindingKind.Attribute;
24129
+ }
24130
+ if (property.isAnimation) {
24131
+ bindingKind = BindingKind.Animation;
24132
+ }
24133
+ const securityContexts = bindingParser
24134
+ .calcPossibleSecurityContexts(input.componentSelector, property.name, bindingKind === BindingKind.Attribute)
24135
+ .filter(context => context !== SecurityContext.NONE);
24136
+ ingestHostProperty(job, property, bindingKind, securityContexts);
23773
24137
  }
23774
24138
  for (const [name, expr] of Object.entries(input.attributes) ?? []) {
23775
- ingestHostAttribute(job, name, expr);
24139
+ const securityContexts = bindingParser.calcPossibleSecurityContexts(input.componentSelector, name, true)
24140
+ .filter(context => context !== SecurityContext.NONE);
24141
+ ingestHostAttribute(job, name, expr, securityContexts);
23776
24142
  }
23777
24143
  for (const event of input.events ?? []) {
23778
24144
  ingestHostEvent(job, event);
@@ -23781,7 +24147,7 @@ function ingestHostBinding(input, bindingParser, constantPool) {
23781
24147
  }
23782
24148
  // TODO: We should refactor the parser to use the same types and structures for host bindings as
23783
24149
  // with ordinary components. This would allow us to share a lot more ingestion code.
23784
- function ingestHostProperty(job, property, isTextAttribute) {
24150
+ function ingestHostProperty(job, property, bindingKind, securityContexts) {
23785
24151
  let expression;
23786
24152
  const ast = property.expression.ast;
23787
24153
  if (ast instanceof Interpolation$1) {
@@ -23790,28 +24156,21 @@ function ingestHostProperty(job, property, isTextAttribute) {
23790
24156
  else {
23791
24157
  expression = convertAst(ast, job, property.sourceSpan);
23792
24158
  }
23793
- let bindingKind = BindingKind.Property;
23794
- // TODO: this should really be handled in the parser.
23795
- if (property.name.startsWith('attr.')) {
23796
- property.name = property.name.substring('attr.'.length);
23797
- bindingKind = BindingKind.Attribute;
23798
- }
23799
- if (property.isAnimation) {
23800
- bindingKind = BindingKind.Animation;
23801
- }
23802
- job.root.update.push(createBindingOp(job.root.xref, bindingKind, property.name, expression, null, SecurityContext
23803
- .NONE /* TODO: what should we pass as security context? Passing NONE for now. */, isTextAttribute, false, /* TODO: How do Host bindings handle i18n attrs? */ null, property.sourceSpan));
24159
+ job.root.update.push(createBindingOp(job.root.xref, bindingKind, property.name, expression, null, securityContexts, false, false, null, /* TODO: How do Host bindings handle i18n attrs? */ null, property.sourceSpan));
23804
24160
  }
23805
- function ingestHostAttribute(job, name, value) {
23806
- const attrBinding = createBindingOp(job.root.xref, BindingKind.Attribute, name, value, null, SecurityContext.NONE, true, false,
24161
+ function ingestHostAttribute(job, name, value, securityContexts) {
24162
+ const attrBinding = createBindingOp(job.root.xref, BindingKind.Attribute, name, value, null, securityContexts,
24163
+ /* Host attributes should always be extracted to const hostAttrs, even if they are not
24164
+ *strictly* text literals */
24165
+ true, false, null,
23807
24166
  /* TODO */ null,
23808
- /* TODO: host attribute source spans */ null);
24167
+ /** TODO: May be null? */ value.sourceSpan);
23809
24168
  job.root.update.push(attrBinding);
23810
24169
  }
23811
24170
  function ingestHostEvent(job, event) {
23812
- const eventBinding = createListenerOp(job.root.xref, new SlotHandle(), event.name, null, event.targetOrPhase, true, event.sourceSpan);
23813
- // TODO: Can this be a chain?
23814
- eventBinding.handlerOps.push(createStatementOp(new ReturnStatement(convertAst(event.handler.ast, job, event.sourceSpan), event.handlerSpan)));
24171
+ const [phase, target] = event.type === 0 /* e.ParsedEventType.Regular */ ? [null, event.targetOrPhase] :
24172
+ [event.targetOrPhase, null];
24173
+ const eventBinding = createListenerOp(job.root.xref, new SlotHandle(), event.name, null, makeListenerHandlerOps(job.root, event.handler, event.handlerSpan), phase, target, true, event.sourceSpan);
23815
24174
  job.root.create.push(eventBinding);
23816
24175
  }
23817
24176
  /**
@@ -23829,10 +24188,10 @@ function ingestNodes(unit, template) {
23829
24188
  ingestContent(unit, node);
23830
24189
  }
23831
24190
  else if (node instanceof Text$3) {
23832
- ingestText(unit, node);
24191
+ ingestText(unit, node, null);
23833
24192
  }
23834
24193
  else if (node instanceof BoundText) {
23835
- ingestBoundText(unit, node);
24194
+ ingestBoundText(unit, node, null);
23836
24195
  }
23837
24196
  else if (node instanceof IfBlock) {
23838
24197
  ingestIfBlock(unit, node);
@@ -23864,10 +24223,16 @@ function ingestElement(unit, element) {
23864
24223
  }
23865
24224
  const id = unit.job.allocateXrefId();
23866
24225
  const [namespaceKey, elementName] = splitNsName(element.name);
23867
- const startOp = createElementStartOp(elementName, id, namespaceForKey(namespaceKey), element.i18n instanceof TagPlaceholder ? element.i18n : undefined, element.startSourceSpan);
24226
+ const startOp = createElementStartOp(elementName, id, namespaceForKey(namespaceKey), element.i18n instanceof TagPlaceholder ? element.i18n : undefined, element.startSourceSpan, element.sourceSpan);
23868
24227
  unit.create.push(startOp);
23869
- ingestBindings(unit, startOp, element);
24228
+ ingestElementBindings(unit, startOp, element);
23870
24229
  ingestReferences(startOp, element);
24230
+ // Start i18n, if needed, goes after the element create and bindings, but before the nodes
24231
+ let i18nBlockId = null;
24232
+ if (element.i18n instanceof Message) {
24233
+ i18nBlockId = unit.job.allocateXrefId();
24234
+ unit.create.push(createI18nStartOp(i18nBlockId, element.i18n, undefined, element.startSourceSpan));
24235
+ }
23871
24236
  ingestNodes(unit, element.children);
23872
24237
  // The source span for the end op is typically the element closing tag. However, if no closing tag
23873
24238
  // exists, such as in `<input>`, we use the start source span instead. Usually the start and end
@@ -23877,10 +24242,8 @@ function ingestElement(unit, element) {
23877
24242
  const endOp = createElementEndOp(id, element.endSourceSpan ?? element.startSourceSpan);
23878
24243
  unit.create.push(endOp);
23879
24244
  // If there is an i18n message associated with this element, insert i18n start and end ops.
23880
- if (element.i18n instanceof Message) {
23881
- const i18nBlockId = unit.job.allocateXrefId();
23882
- OpList.insertAfter(createI18nStartOp(i18nBlockId, element.i18n), startOp);
23883
- OpList.insertBefore(createI18nEndOp(i18nBlockId), endOp);
24245
+ if (i18nBlockId !== null) {
24246
+ OpList.insertBefore(createI18nEndOp(i18nBlockId, element.endSourceSpan ?? element.startSourceSpan), endOp);
23884
24247
  }
23885
24248
  }
23886
24249
  /**
@@ -23903,9 +24266,9 @@ function ingestTemplate(unit, tmpl) {
23903
24266
  '' :
23904
24267
  prefixWithNamespace(tagNameWithoutNamespace, namespace);
23905
24268
  const templateKind = isPlainTemplate(tmpl) ? TemplateKind.NgTemplate : TemplateKind.Structural;
23906
- const templateOp = createTemplateOp(childView.xref, templateKind, tagNameWithoutNamespace, functionNameSuffix, namespace, i18nPlaceholder, tmpl.startSourceSpan);
24269
+ const templateOp = createTemplateOp(childView.xref, templateKind, tagNameWithoutNamespace, functionNameSuffix, namespace, i18nPlaceholder, tmpl.startSourceSpan, tmpl.sourceSpan);
23907
24270
  unit.create.push(templateOp);
23908
- ingestBindings(unit, templateOp, tmpl);
24271
+ ingestTemplateBindings(unit, templateOp, tmpl, templateKind);
23909
24272
  ingestReferences(templateOp, tmpl);
23910
24273
  ingestNodes(childView, tmpl.children);
23911
24274
  for (const { name, value } of tmpl.variables) {
@@ -23916,34 +24279,34 @@ function ingestTemplate(unit, tmpl) {
23916
24279
  // element/template the directive is placed on.
23917
24280
  if (templateKind === TemplateKind.NgTemplate && tmpl.i18n instanceof Message) {
23918
24281
  const id = unit.job.allocateXrefId();
23919
- OpList.insertAfter(createI18nStartOp(id, tmpl.i18n), childView.create.head);
23920
- OpList.insertBefore(createI18nEndOp(id), childView.create.tail);
24282
+ OpList.insertAfter(createI18nStartOp(id, tmpl.i18n, undefined, tmpl.startSourceSpan), childView.create.head);
24283
+ OpList.insertBefore(createI18nEndOp(id, tmpl.endSourceSpan ?? tmpl.startSourceSpan), childView.create.tail);
23921
24284
  }
23922
24285
  }
23923
24286
  /**
23924
- * Ingest a literal text node from the AST into the given `ViewCompilation`.
24287
+ * Ingest a content node from the AST into the given `ViewCompilation`.
23925
24288
  */
23926
24289
  function ingestContent(unit, content) {
23927
24290
  if (content.i18n !== undefined && !(content.i18n instanceof TagPlaceholder)) {
23928
24291
  throw Error(`Unhandled i18n metadata type for element: ${content.i18n.constructor.name}`);
23929
24292
  }
23930
- const attrs = content.attributes.flatMap(a => [a.name, a.value]);
23931
- const op = createProjectionOp(unit.job.allocateXrefId(), content.selector, content.i18n, attrs, content.sourceSpan);
24293
+ const op = createProjectionOp(unit.job.allocateXrefId(), content.selector, content.i18n, content.sourceSpan);
23932
24294
  for (const attr of content.attributes) {
23933
- ingestBinding(unit, op.xref, attr.name, literal(attr.value), 1 /* e.BindingType.Attribute */, null, SecurityContext.NONE, attr.sourceSpan, BindingFlags.TextValue, attr.i18n);
24295
+ const securityContext = domSchema.securityContext(content.name, attr.name, true);
24296
+ unit.update.push(createBindingOp(op.xref, BindingKind.Attribute, attr.name, literal(attr.value), null, securityContext, true, false, null, asMessage(attr.i18n), attr.sourceSpan));
23934
24297
  }
23935
24298
  unit.create.push(op);
23936
24299
  }
23937
24300
  /**
23938
24301
  * Ingest a literal text node from the AST into the given `ViewCompilation`.
23939
24302
  */
23940
- function ingestText(unit, text) {
23941
- unit.create.push(createTextOp(unit.job.allocateXrefId(), text.value, text.sourceSpan));
24303
+ function ingestText(unit, text, icuPlaceholder) {
24304
+ unit.create.push(createTextOp(unit.job.allocateXrefId(), text.value, icuPlaceholder, text.sourceSpan));
23942
24305
  }
23943
24306
  /**
23944
24307
  * Ingest an interpolated text node from the AST into the given `ViewCompilation`.
23945
24308
  */
23946
- function ingestBoundText(unit, text, i18nPlaceholders) {
24309
+ function ingestBoundText(unit, text, icuPlaceholder) {
23947
24310
  let value = text.value;
23948
24311
  if (value instanceof ASTWithSource) {
23949
24312
  value = value.ast;
@@ -23954,19 +24317,16 @@ function ingestBoundText(unit, text, i18nPlaceholders) {
23954
24317
  if (text.i18n !== undefined && !(text.i18n instanceof Container)) {
23955
24318
  throw Error(`Unhandled i18n metadata type for text interpolation: ${text.i18n?.constructor.name}`);
23956
24319
  }
23957
- if (i18nPlaceholders === undefined) {
23958
- // TODO: We probably can just use the placeholders field, instead of walking the AST.
23959
- i18nPlaceholders = text.i18n instanceof Container ?
23960
- text.i18n.children
23961
- .filter((node) => node instanceof Placeholder)
23962
- .map(placeholder => placeholder.name) :
23963
- [];
23964
- }
24320
+ const i18nPlaceholders = text.i18n instanceof Container ?
24321
+ text.i18n.children
24322
+ .filter((node) => node instanceof Placeholder)
24323
+ .map(placeholder => placeholder.name) :
24324
+ [];
23965
24325
  if (i18nPlaceholders.length > 0 && i18nPlaceholders.length !== value.expressions.length) {
23966
24326
  throw Error(`Unexpected number of i18n placeholders (${value.expressions.length}) for BoundText with ${value.expressions.length} expressions`);
23967
24327
  }
23968
24328
  const textXref = unit.job.allocateXrefId();
23969
- unit.create.push(createTextOp(textXref, '', text.sourceSpan));
24329
+ unit.create.push(createTextOp(textXref, '', icuPlaceholder, text.sourceSpan));
23970
24330
  // TemplateDefinitionBuilder does not generate source maps for sub-expressions inside an
23971
24331
  // interpolation. We copy that behavior in compatibility mode.
23972
24332
  // TODO: is it actually correct to generate these extra maps in modern mode?
@@ -23999,7 +24359,7 @@ function ingestIfBlock(unit, ifBlock) {
23999
24359
  }
24000
24360
  ifCaseI18nMeta = ifCase.i18n;
24001
24361
  }
24002
- const templateOp = createTemplateOp(cView.xref, TemplateKind.Block, tagName, 'Conditional', Namespace.HTML, ifCaseI18nMeta, ifCase.sourceSpan);
24362
+ const templateOp = createTemplateOp(cView.xref, TemplateKind.Block, tagName, 'Conditional', Namespace.HTML, ifCaseI18nMeta, ifCase.startSourceSpan, ifCase.sourceSpan);
24003
24363
  unit.create.push(templateOp);
24004
24364
  if (firstXref === null) {
24005
24365
  firstXref = cView.xref;
@@ -24029,7 +24389,7 @@ function ingestSwitchBlock(unit, switchBlock) {
24029
24389
  }
24030
24390
  switchCaseI18nMeta = switchCase.i18n;
24031
24391
  }
24032
- const templateOp = createTemplateOp(cView.xref, TemplateKind.Block, null, 'Case', Namespace.HTML, switchCaseI18nMeta, switchCase.sourceSpan);
24392
+ const templateOp = createTemplateOp(cView.xref, TemplateKind.Block, null, 'Case', Namespace.HTML, switchCaseI18nMeta, switchCase.startSourceSpan, switchCase.sourceSpan);
24033
24393
  unit.create.push(templateOp);
24034
24394
  if (firstXref === null) {
24035
24395
  firstXref = cView.xref;
@@ -24045,13 +24405,16 @@ function ingestSwitchBlock(unit, switchBlock) {
24045
24405
  const conditional = createConditionalOp(firstXref, firstSlotHandle, convertAst(switchBlock.expression, unit.job, null), conditions, switchBlock.sourceSpan);
24046
24406
  unit.update.push(conditional);
24047
24407
  }
24048
- function ingestDeferView(unit, suffix, children, sourceSpan) {
24408
+ function ingestDeferView(unit, suffix, i18nMeta, children, sourceSpan) {
24409
+ if (i18nMeta !== undefined && !(i18nMeta instanceof BlockPlaceholder)) {
24410
+ throw Error('Unhandled i18n metadata type for defer block');
24411
+ }
24049
24412
  if (children === undefined) {
24050
24413
  return null;
24051
24414
  }
24052
24415
  const secondaryView = unit.job.allocateView(unit.xref);
24053
24416
  ingestNodes(secondaryView, children);
24054
- const templateOp = createTemplateOp(secondaryView.xref, TemplateKind.Block, null, `Defer${suffix}`, Namespace.HTML, undefined, sourceSpan);
24417
+ const templateOp = createTemplateOp(secondaryView.xref, TemplateKind.Block, null, `Defer${suffix}`, Namespace.HTML, i18nMeta, sourceSpan, sourceSpan);
24055
24418
  unit.create.push(templateOp);
24056
24419
  return templateOp;
24057
24420
  }
@@ -24061,10 +24424,10 @@ function ingestDeferBlock(unit, deferBlock) {
24061
24424
  throw new Error(`AssertionError: unable to find metadata for deferred block`);
24062
24425
  }
24063
24426
  // Generate the defer main view and all secondary views.
24064
- const main = ingestDeferView(unit, '', deferBlock.children, deferBlock.sourceSpan);
24065
- const loading = ingestDeferView(unit, 'Loading', deferBlock.loading?.children, deferBlock.loading?.sourceSpan);
24066
- const placeholder = ingestDeferView(unit, 'Placeholder', deferBlock.placeholder?.children, deferBlock.placeholder?.sourceSpan);
24067
- const error = ingestDeferView(unit, 'Error', deferBlock.error?.children, deferBlock.error?.sourceSpan);
24427
+ const main = ingestDeferView(unit, '', deferBlock.i18n, deferBlock.children, deferBlock.sourceSpan);
24428
+ const loading = ingestDeferView(unit, 'Loading', deferBlock.loading?.i18n, deferBlock.loading?.children, deferBlock.loading?.sourceSpan);
24429
+ const placeholder = ingestDeferView(unit, 'Placeholder', deferBlock.placeholder?.i18n, deferBlock.placeholder?.children, deferBlock.placeholder?.sourceSpan);
24430
+ const error = ingestDeferView(unit, 'Error', deferBlock.error?.i18n, deferBlock.error?.children, deferBlock.error?.sourceSpan);
24068
24431
  // Create the main defer op, and ops for all secondary views.
24069
24432
  const deferXref = unit.job.allocateXrefId();
24070
24433
  const deferOp = createDeferOp(deferXref, main.xref, main.handle, blockMeta, deferBlock.sourceSpan);
@@ -24129,6 +24492,11 @@ function ingestDeferBlock(unit, deferBlock) {
24129
24492
  deferOnOps.push(deferOnOp);
24130
24493
  }
24131
24494
  if (triggers.when !== undefined) {
24495
+ if (triggers.when.value instanceof Interpolation$1) {
24496
+ // TemplateDefinitionBuilder supports this case, but it's very strange to me. What would it
24497
+ // even mean?
24498
+ throw new Error(`Unexpected interpolation in defer block when trigger`);
24499
+ }
24132
24500
  const deferOnOp = createDeferWhenOp(deferXref, convertAst(triggers.when.value, unit.job, triggers.when.sourceSpan), prefetch, triggers.when.sourceSpan);
24133
24501
  deferWhenOps.push(deferOnOp);
24134
24502
  }
@@ -24148,10 +24516,10 @@ function ingestIcu(unit, icu) {
24148
24516
  unit.create.push(createIcuStartOp(xref, icu.i18n, icuFromI18nMessage(icu.i18n).name, null));
24149
24517
  for (const [placeholder, text] of Object.entries({ ...icu.vars, ...icu.placeholders })) {
24150
24518
  if (text instanceof BoundText) {
24151
- ingestBoundText(unit, text, [placeholder]);
24519
+ ingestBoundText(unit, text, placeholder);
24152
24520
  }
24153
24521
  else {
24154
- ingestText(unit, text);
24522
+ ingestText(unit, text, placeholder);
24155
24523
  }
24156
24524
  }
24157
24525
  unit.create.push(createIcuEndOp(xref));
@@ -24185,9 +24553,11 @@ function ingestForBlock(unit, forBlock) {
24185
24553
  const track = convertAst(forBlock.trackBy, unit.job, sourceSpan);
24186
24554
  ingestNodes(repeaterView, forBlock.children);
24187
24555
  let emptyView = null;
24556
+ let emptyTagName = null;
24188
24557
  if (forBlock.empty !== null) {
24189
24558
  emptyView = unit.job.allocateView(unit.xref);
24190
24559
  ingestNodes(emptyView, forBlock.empty.children);
24560
+ emptyTagName = ingestControlFlowInsertionPoint(unit, emptyView.xref, forBlock.empty);
24191
24561
  }
24192
24562
  const varNames = {
24193
24563
  $index: forBlock.contextVariables.$index.name,
@@ -24198,8 +24568,17 @@ function ingestForBlock(unit, forBlock) {
24198
24568
  $odd: forBlock.contextVariables.$odd.name,
24199
24569
  $implicit: forBlock.item.name,
24200
24570
  };
24571
+ if (forBlock.i18n !== undefined && !(forBlock.i18n instanceof BlockPlaceholder)) {
24572
+ throw Error('AssertionError: Unhandled i18n metadata type or @for');
24573
+ }
24574
+ if (forBlock.empty?.i18n !== undefined &&
24575
+ !(forBlock.empty.i18n instanceof BlockPlaceholder)) {
24576
+ throw Error('AssertionError: Unhandled i18n metadata type or @empty');
24577
+ }
24578
+ const i18nPlaceholder = forBlock.i18n;
24579
+ const emptyI18nPlaceholder = forBlock.empty?.i18n;
24201
24580
  const tagName = ingestControlFlowInsertionPoint(unit, repeaterView.xref, forBlock);
24202
- const repeaterCreate = createRepeaterCreateOp(repeaterView.xref, emptyView?.xref ?? null, tagName, track, varNames, forBlock.sourceSpan);
24581
+ const repeaterCreate = createRepeaterCreateOp(repeaterView.xref, emptyView?.xref ?? null, tagName, track, varNames, emptyTagName, i18nPlaceholder, emptyI18nPlaceholder, forBlock.startSourceSpan, forBlock.sourceSpan);
24203
24582
  unit.create.push(repeaterCreate);
24204
24583
  const expression = convertAst(forBlock.expression, unit.job, convertSourceSpan(forBlock.expression.span, forBlock.sourceSpan));
24205
24584
  const repeater = createRepeaterOp(repeaterCreate.xref, repeaterCreate.handle, expression, forBlock.sourceSpan);
@@ -24213,7 +24592,29 @@ function convertAst(ast, job, baseSourceSpan) {
24213
24592
  return convertAst(ast.ast, job, baseSourceSpan);
24214
24593
  }
24215
24594
  else if (ast instanceof PropertyRead) {
24216
- if (ast.receiver instanceof ImplicitReceiver && !(ast.receiver instanceof ThisReceiver)) {
24595
+ const isThisReceiver = ast.receiver instanceof ThisReceiver;
24596
+ // Whether this is an implicit receiver, *excluding* explicit reads of `this`.
24597
+ const isImplicitReceiver = ast.receiver instanceof ImplicitReceiver && !(ast.receiver instanceof ThisReceiver);
24598
+ // Whether the name of the read is a node that should be never retain its explicit this
24599
+ // receiver.
24600
+ const isSpecialNode = ast.name === '$any' || ast.name === '$event';
24601
+ // TODO: The most sensible condition here would be simply `isImplicitReceiver`, to convert only
24602
+ // actual implicit `this` reads, and not explicit ones. However, TemplateDefinitionBuilder (and
24603
+ // the Typecheck block!) both have the same bug, in which they also consider explicit `this`
24604
+ // reads to be implicit. This causes problems when the explicit `this` read is inside a
24605
+ // template with a context that also provides the variable name being read:
24606
+ // ```
24607
+ // <ng-template let-a>{{this.a}}</ng-template>
24608
+ // ```
24609
+ // The whole point of the explicit `this` was to access the class property, but TDB and the
24610
+ // current TCB treat the read as implicit, and give you the context property instead!
24611
+ //
24612
+ // For now, we emulate this old behvaior by aggressively converting explicit reads to to
24613
+ // implicit reads, except for the special cases that TDB and the current TCB protect. However,
24614
+ // it would be an improvement to fix this.
24615
+ //
24616
+ // See also the corresponding comment for the TCB, in `type_check_block.ts`.
24617
+ if (isImplicitReceiver || (isThisReceiver && !isSpecialNode)) {
24217
24618
  return new LexicalReadExpr(ast.name);
24218
24619
  }
24219
24620
  else {
@@ -24317,6 +24718,27 @@ function convertAst(ast, job, baseSourceSpan) {
24317
24718
  throw new Error(`Unhandled expression type "${ast.constructor.name}" in file "${baseSourceSpan?.start.file.url}"`);
24318
24719
  }
24319
24720
  }
24721
+ function convertAstWithInterpolation(job, value, i18nMeta, sourceSpan) {
24722
+ let expression;
24723
+ if (value instanceof Interpolation$1) {
24724
+ expression = new Interpolation(value.strings, value.expressions.map(e => convertAst(e, job, sourceSpan ?? null)), Object.keys(asMessage(i18nMeta)?.placeholders ?? {}));
24725
+ }
24726
+ else if (value instanceof AST) {
24727
+ expression = convertAst(value, job, sourceSpan ?? null);
24728
+ }
24729
+ else {
24730
+ expression = literal(value);
24731
+ }
24732
+ return expression;
24733
+ }
24734
+ // TODO: Can we populate Template binding kinds in ingest?
24735
+ const BINDING_KINDS = new Map([
24736
+ [0 /* e.BindingType.Property */, BindingKind.Property],
24737
+ [1 /* e.BindingType.Attribute */, BindingKind.Attribute],
24738
+ [2 /* e.BindingType.Class */, BindingKind.ClassName],
24739
+ [3 /* e.BindingType.Style */, BindingKind.StyleProperty],
24740
+ [4 /* e.BindingType.Animation */, BindingKind.Animation],
24741
+ ]);
24320
24742
  /**
24321
24743
  * Checks whether the given template is a plain ng-template (as opposed to another kind of template
24322
24744
  * such as a structural directive template or control flow template). This is checked based on the
@@ -24335,149 +24757,184 @@ function convertAst(ast, job, baseSourceSpan) {
24335
24757
  * | `<ng-template *ngIf>` (structural) | null |
24336
24758
  */
24337
24759
  function isPlainTemplate(tmpl) {
24338
- return splitNsName(tmpl.tagName ?? '')[1] === 'ng-template';
24760
+ return splitNsName(tmpl.tagName ?? '')[1] === NG_TEMPLATE_TAG_NAME$1;
24339
24761
  }
24340
24762
  /**
24341
- * Process all of the bindings on an element-like structure in the template AST and convert them
24342
- * to their IR representation.
24763
+ * Ensures that the i18nMeta, if provided, is an i18n.Message.
24343
24764
  */
24344
- function ingestBindings(unit, op, element) {
24345
- let flags = BindingFlags.None;
24346
- let hasI18nAttributes = false;
24347
- if (element instanceof Template) {
24348
- flags |= BindingFlags.OnNgTemplateElement;
24349
- if (element instanceof Template && isPlainTemplate(element)) {
24350
- flags |= BindingFlags.BindingTargetsTemplate;
24351
- }
24352
- const templateAttrFlags = flags | BindingFlags.BindingTargetsTemplate | BindingFlags.IsStructuralTemplateAttribute;
24353
- for (const attr of element.templateAttrs) {
24354
- if (attr instanceof TextAttribute) {
24355
- ingestBinding(unit, op.xref, attr.name, literal(attr.value), 1 /* e.BindingType.Attribute */, null, SecurityContext.NONE, attr.sourceSpan, templateAttrFlags | BindingFlags.TextValue, attr.i18n);
24356
- hasI18nAttributes ||= attr.i18n !== undefined;
24357
- }
24358
- else {
24359
- ingestBinding(unit, op.xref, attr.name, attr.value, attr.type, attr.unit, attr.securityContext, attr.sourceSpan, templateAttrFlags, attr.i18n);
24360
- hasI18nAttributes ||= attr.i18n !== undefined;
24361
- }
24362
- }
24765
+ function asMessage(i18nMeta) {
24766
+ if (i18nMeta == null) {
24767
+ return null;
24363
24768
  }
24769
+ if (!(i18nMeta instanceof Message)) {
24770
+ throw Error(`Expected i18n meta to be a Message, but got: ${i18nMeta.constructor.name}`);
24771
+ }
24772
+ return i18nMeta;
24773
+ }
24774
+ /**
24775
+ * Process all of the bindings on an element in the template AST and convert them to their IR
24776
+ * representation.
24777
+ */
24778
+ function ingestElementBindings(unit, op, element) {
24779
+ let bindings = new Array();
24364
24780
  for (const attr of element.attributes) {
24365
- // This is only attribute TextLiteral bindings, such as `attr.foo="bar"`. This can never be
24366
- // `[attr.foo]="bar"` or `attr.foo="{{bar}}"`, both of which will be handled as inputs with
24367
- // `BindingType.Attribute`.
24368
- ingestBinding(unit, op.xref, attr.name, literal(attr.value), 1 /* e.BindingType.Attribute */, null, SecurityContext.NONE, attr.sourceSpan, flags | BindingFlags.TextValue, attr.i18n);
24369
- hasI18nAttributes ||= attr.i18n !== undefined;
24781
+ // Attribute literal bindings, such as `attr.foo="bar"`.
24782
+ const securityContext = domSchema.securityContext(element.name, attr.name, true);
24783
+ bindings.push(createBindingOp(op.xref, BindingKind.Attribute, attr.name, convertAstWithInterpolation(unit.job, attr.value, attr.i18n), null, securityContext, true, false, null, asMessage(attr.i18n), attr.sourceSpan));
24370
24784
  }
24371
24785
  for (const input of element.inputs) {
24372
- ingestBinding(unit, op.xref, input.name, input.value, input.type, input.unit, input.securityContext, input.sourceSpan, flags, input.i18n);
24373
- hasI18nAttributes ||= input.i18n !== undefined;
24786
+ // All dynamic bindings (both attribute and property bindings).
24787
+ bindings.push(createBindingOp(op.xref, BINDING_KINDS.get(input.type), input.name, convertAstWithInterpolation(unit.job, astOf(input.value), input.i18n), input.unit, input.securityContext, false, false, null, asMessage(input.i18n) ?? null, input.sourceSpan));
24374
24788
  }
24789
+ unit.create.push(bindings.filter((b) => b?.kind === OpKind.ExtractedAttribute));
24790
+ unit.update.push(bindings.filter((b) => b?.kind === OpKind.Binding));
24375
24791
  for (const output of element.outputs) {
24376
- let listenerOp;
24377
- if (output.type === 1 /* e.ParsedEventType.Animation */) {
24378
- if (output.phase === null) {
24379
- throw Error('Animation listener should have a phase');
24380
- }
24381
- }
24382
- if (element instanceof Template && !isPlainTemplate(element)) {
24383
- unit.create.push(createExtractedAttributeOp(op.xref, BindingKind.Property, output.name, null, null));
24384
- continue;
24385
- }
24386
- listenerOp = createListenerOp(op.xref, op.handle, output.name, op.tag, output.phase, false, output.sourceSpan);
24387
- // if output.handler is a chain, then push each statement from the chain separately, and
24388
- // return the last one?
24389
- let handlerExprs;
24390
- let handler = output.handler;
24391
- if (handler instanceof ASTWithSource) {
24392
- handler = handler.ast;
24792
+ if (output.type === 1 /* e.ParsedEventType.Animation */ && output.phase === null) {
24793
+ throw Error('Animation listener should have a phase');
24393
24794
  }
24394
- if (handler instanceof Chain) {
24395
- handlerExprs = handler.expressions;
24795
+ unit.create.push(createListenerOp(op.xref, op.handle, output.name, op.tag, makeListenerHandlerOps(unit, output.handler, output.handlerSpan), output.phase, output.target, false, output.sourceSpan));
24796
+ }
24797
+ // If any of the bindings on this element have an i18n message, then an i18n attrs configuration
24798
+ // op is also required.
24799
+ if (bindings.some(b => b?.i18nMessage) !== null) {
24800
+ unit.create.push(createI18nAttributesOp(unit.job.allocateXrefId(), new SlotHandle(), op.xref));
24801
+ }
24802
+ }
24803
+ /**
24804
+ * Process all of the bindings on a template in the template AST and convert them to their IR
24805
+ * representation.
24806
+ */
24807
+ function ingestTemplateBindings(unit, op, template, templateKind) {
24808
+ let bindings = new Array();
24809
+ for (const attr of template.templateAttrs) {
24810
+ if (attr instanceof TextAttribute) {
24811
+ const securityContext = domSchema.securityContext(NG_TEMPLATE_TAG_NAME$1, attr.name, true);
24812
+ bindings.push(createTemplateBinding(unit, op.xref, 1 /* e.BindingType.Attribute */, attr.name, attr.value, null, securityContext, true, templateKind, asMessage(attr.i18n), attr.sourceSpan));
24396
24813
  }
24397
24814
  else {
24398
- handlerExprs = [handler];
24815
+ bindings.push(createTemplateBinding(unit, op.xref, attr.type, attr.name, astOf(attr.value), attr.unit, attr.securityContext, true, templateKind, asMessage(attr.i18n), attr.sourceSpan));
24816
+ }
24817
+ }
24818
+ for (const attr of template.attributes) {
24819
+ // Attribute literal bindings, such as `attr.foo="bar"`.
24820
+ const securityContext = domSchema.securityContext(NG_TEMPLATE_TAG_NAME$1, attr.name, true);
24821
+ bindings.push(createTemplateBinding(unit, op.xref, 1 /* e.BindingType.Attribute */, attr.name, attr.value, null, securityContext, false, templateKind, asMessage(attr.i18n), attr.sourceSpan));
24822
+ }
24823
+ for (const input of template.inputs) {
24824
+ // Dynamic bindings (both attribute and property bindings).
24825
+ bindings.push(createTemplateBinding(unit, op.xref, input.type, input.name, astOf(input.value), input.unit, input.securityContext, false, templateKind, asMessage(input.i18n), input.sourceSpan));
24826
+ }
24827
+ unit.create.push(bindings.filter((b) => b?.kind === OpKind.ExtractedAttribute));
24828
+ unit.update.push(bindings.filter((b) => b?.kind === OpKind.Binding));
24829
+ for (const output of template.outputs) {
24830
+ if (output.type === 1 /* e.ParsedEventType.Animation */ && output.phase === null) {
24831
+ throw Error('Animation listener should have a phase');
24399
24832
  }
24400
- if (handlerExprs.length === 0) {
24401
- throw new Error('Expected listener to have non-empty expression list.');
24833
+ if (templateKind === TemplateKind.NgTemplate) {
24834
+ unit.create.push(createListenerOp(op.xref, op.handle, output.name, op.tag, makeListenerHandlerOps(unit, output.handler, output.handlerSpan), output.phase, output.target, false, output.sourceSpan));
24402
24835
  }
24403
- const expressions = handlerExprs.map(expr => convertAst(expr, unit.job, output.handlerSpan));
24404
- const returnExpr = expressions.pop();
24405
- for (const expr of expressions) {
24406
- const stmtOp = createStatementOp(new ExpressionStatement(expr, expr.sourceSpan));
24407
- listenerOp.handlerOps.push(stmtOp);
24836
+ if (templateKind === TemplateKind.Structural &&
24837
+ output.type !== 1 /* e.ParsedEventType.Animation */) {
24838
+ // Animation bindings are excluded from the structural template's const array.
24839
+ const securityContext = domSchema.securityContext(NG_TEMPLATE_TAG_NAME$1, output.name, false);
24840
+ unit.create.push(createExtractedAttributeOp(op.xref, BindingKind.Property, output.name, null, null, null, securityContext));
24408
24841
  }
24409
- listenerOp.handlerOps.push(createStatementOp(new ReturnStatement(returnExpr, returnExpr.sourceSpan)));
24410
- unit.create.push(listenerOp);
24411
24842
  }
24412
24843
  // TODO: Perhaps we could do this in a phase? (It likely wouldn't change the slot indices.)
24413
- if (hasI18nAttributes) {
24844
+ if (bindings.some(b => b?.i18nMessage) !== null) {
24414
24845
  unit.create.push(createI18nAttributesOp(unit.job.allocateXrefId(), new SlotHandle(), op.xref));
24415
24846
  }
24416
24847
  }
24417
- const BINDING_KINDS = new Map([
24418
- [0 /* e.BindingType.Property */, BindingKind.Property],
24419
- [1 /* e.BindingType.Attribute */, BindingKind.Attribute],
24420
- [2 /* e.BindingType.Class */, BindingKind.ClassName],
24421
- [3 /* e.BindingType.Style */, BindingKind.StyleProperty],
24422
- [4 /* e.BindingType.Animation */, BindingKind.Animation],
24423
- ]);
24424
- var BindingFlags;
24425
- (function (BindingFlags) {
24426
- BindingFlags[BindingFlags["None"] = 0] = "None";
24427
- /**
24428
- * The binding is to a static text literal and not to an expression.
24429
- */
24430
- BindingFlags[BindingFlags["TextValue"] = 1] = "TextValue";
24431
- /**
24432
- * The binding belongs to the `<ng-template>` side of a `t.Template`.
24433
- */
24434
- BindingFlags[BindingFlags["BindingTargetsTemplate"] = 2] = "BindingTargetsTemplate";
24435
- /**
24436
- * The binding is on a structural directive.
24437
- */
24438
- BindingFlags[BindingFlags["IsStructuralTemplateAttribute"] = 4] = "IsStructuralTemplateAttribute";
24439
- /**
24440
- * The binding is on a `t.Template`.
24441
- */
24442
- BindingFlags[BindingFlags["OnNgTemplateElement"] = 8] = "OnNgTemplateElement";
24443
- })(BindingFlags || (BindingFlags = {}));
24444
- function ingestBinding(view, xref, name, value, type, unit, securityContext, sourceSpan, flags, i18nMeta) {
24445
- if (value instanceof ASTWithSource) {
24446
- value = value.ast;
24447
- }
24448
- let i18nContext = null;
24449
- if (i18nMeta !== undefined) {
24450
- if (!(i18nMeta instanceof Message)) {
24451
- throw Error(`Unhandled i18n metadata type for binding: ${i18nMeta.constructor.name}`);
24848
+ /**
24849
+ * Helper to ingest an individual binding on a template, either an explicit `ng-template`, or an
24850
+ * implicit template created via structural directive.
24851
+ *
24852
+ * Bindings on templates are *extremely* tricky. I have tried to isolate all of the confusing edge
24853
+ * cases into this function, and to comment it well to document the behavior.
24854
+ *
24855
+ * Some of this behavior is intuitively incorrect, and we should consider changing it in the future.
24856
+ *
24857
+ * @param view The compilation unit for the view containing the template.
24858
+ * @param xref The xref of the template op.
24859
+ * @param type The binding type, according to the parser. This is fairly reasonable, e.g. both
24860
+ * dynamic and static attributes have e.BindingType.Attribute.
24861
+ * @param name The binding's name.
24862
+ * @param value The bindings's value, which will either be an input AST expression, or a string
24863
+ * literal. Note that the input AST expression may or may not be const -- it will only be a
24864
+ * string literal if the parser considered it a text binding.
24865
+ * @param unit If the binding has a unit (e.g. `px` for style bindings), then this is the unit.
24866
+ * @param securityContext The security context of the binding.
24867
+ * @param isStructuralTemplateAttribute Whether this binding actually applies to the structural
24868
+ * ng-template. For example, an `ngFor` would actually apply to the structural template. (Most
24869
+ * bindings on structural elements target the inner element, not the template.)
24870
+ * @param templateKind Whether this is an explicit `ng-template` or an implicit template created by
24871
+ * a structural directive. This should never be a block template.
24872
+ * @param i18nMessage The i18n metadata for the binding, if any.
24873
+ * @param sourceSpan The source span of the binding.
24874
+ * @returns An IR binding op, or null if the binding should be skipped.
24875
+ */
24876
+ function createTemplateBinding(view, xref, type, name, value, unit, securityContext, isStructuralTemplateAttribute, templateKind, i18nMessage, sourceSpan) {
24877
+ const isTextBinding = typeof value === 'string';
24878
+ // If this is a structural template, then several kinds of bindings should not result in an
24879
+ // update instruction.
24880
+ if (templateKind === TemplateKind.Structural) {
24881
+ if (!isStructuralTemplateAttribute &&
24882
+ (type === 0 /* e.BindingType.Property */ || type === 2 /* e.BindingType.Class */ ||
24883
+ type === 3 /* e.BindingType.Style */)) {
24884
+ // Because this binding doesn't really target the ng-template, it must be a binding on an
24885
+ // inner node of a structural template. We can't skip it entirely, because we still need it on
24886
+ // the ng-template's consts (e.g. for the purposes of directive matching). However, we should
24887
+ // not generate an update instruction for it.
24888
+ return createExtractedAttributeOp(xref, BindingKind.Property, name, null, null, i18nMessage, securityContext);
24889
+ }
24890
+ if (!isTextBinding && (type === 1 /* e.BindingType.Attribute */ || type === 4 /* e.BindingType.Animation */)) {
24891
+ // Again, this binding doesn't really target the ng-template; it actually targets the element
24892
+ // inside the structural template. In the case of non-text attribute or animation bindings,
24893
+ // the binding doesn't even show up on the ng-template const array, so we just skip it
24894
+ // entirely.
24895
+ return null;
24452
24896
  }
24453
- i18nContext = view.job.allocateXrefId();
24454
- view.create.push(createI18nContextOp(I18nContextKind.Attr, i18nContext, null, i18nMeta, null));
24455
- }
24456
- if (flags & BindingFlags.OnNgTemplateElement && !(flags & BindingFlags.BindingTargetsTemplate) &&
24457
- type === 0 /* e.BindingType.Property */) {
24458
- // This binding only exists for later const extraction, and is not an actual binding to be
24459
- // created.
24460
- view.create.push(createExtractedAttributeOp(xref, BindingKind.Property, name, null, i18nContext));
24461
- return;
24462
24897
  }
24463
- let expression;
24464
- // TODO: We could easily generate source maps for subexpressions in these cases, but
24465
- // TemplateDefinitionBuilder does not. Should we do so?
24466
- if (value instanceof Interpolation$1) {
24467
- let i18nPlaceholders = [];
24468
- if (i18nMeta !== undefined) {
24469
- i18nPlaceholders = Object.keys(i18nMeta.placeholders);
24898
+ let bindingType = BINDING_KINDS.get(type);
24899
+ if (templateKind === TemplateKind.NgTemplate) {
24900
+ // We know we are dealing with bindings directly on an explicit ng-template.
24901
+ // Static attribute bindings should be collected into the const array as k/v pairs. Property
24902
+ // bindings should result in a `property` instruction, and `AttributeMarker.Bindings` const
24903
+ // entries.
24904
+ //
24905
+ // The difficulty is with dynamic attribute, style, and class bindings. These don't really make
24906
+ // sense on an `ng-template` and should probably be parser errors. However,
24907
+ // TemplateDefinitionBuilder generates `property` instructions for them, and so we do that as
24908
+ // well.
24909
+ //
24910
+ // Note that we do have a slight behavior difference with TemplateDefinitionBuilder: although
24911
+ // TDB emits `property` instructions for dynamic attributes, styles, and classes, only styles
24912
+ // and classes also get const collected into the `AttributeMarker.Bindings` field. Dynamic
24913
+ // attribute bindings are missing from the consts entirely. We choose to emit them into the
24914
+ // consts field anyway, to avoid creating special cases for something so arcane and nonsensical.
24915
+ if (type === 2 /* e.BindingType.Class */ || type === 3 /* e.BindingType.Style */ ||
24916
+ (type === 1 /* e.BindingType.Attribute */ && !isTextBinding)) {
24917
+ // TODO: These cases should be parse errors.
24918
+ bindingType = BindingKind.Property;
24470
24919
  }
24471
- expression = new Interpolation(value.strings, value.expressions.map(expr => convertAst(expr, view.job, null)), i18nPlaceholders);
24472
24920
  }
24473
- else if (value instanceof AST) {
24474
- expression = convertAst(value, view.job, null);
24475
- }
24476
- else {
24477
- expression = value;
24921
+ return createBindingOp(xref, bindingType, name, convertAstWithInterpolation(view.job, value, i18nMessage), unit, securityContext, isTextBinding, isStructuralTemplateAttribute, templateKind, i18nMessage, sourceSpan);
24922
+ }
24923
+ function makeListenerHandlerOps(unit, handler, handlerSpan) {
24924
+ handler = astOf(handler);
24925
+ const handlerOps = new Array();
24926
+ let handlerExprs = handler instanceof Chain ? handler.expressions : [handler];
24927
+ if (handlerExprs.length === 0) {
24928
+ throw new Error('Expected listener to have non-empty expression list.');
24478
24929
  }
24479
- const kind = BINDING_KINDS.get(type);
24480
- view.update.push(createBindingOp(xref, kind, name, expression, unit, securityContext, !!(flags & BindingFlags.TextValue), !!(flags & BindingFlags.IsStructuralTemplateAttribute), i18nContext, sourceSpan));
24930
+ const expressions = handlerExprs.map(expr => convertAst(expr, unit.job, handlerSpan));
24931
+ const returnExpr = expressions.pop();
24932
+ handlerOps.push(...expressions.map(e => createStatementOp(new ExpressionStatement(e, e.sourceSpan))));
24933
+ handlerOps.push(createStatementOp(new ReturnStatement(returnExpr, returnExpr.sourceSpan)));
24934
+ return handlerOps;
24935
+ }
24936
+ function astOf(ast) {
24937
+ return ast instanceof ASTWithSource ? ast.ast : ast;
24481
24938
  }
24482
24939
  /**
24483
24940
  * Process all of the local references on an element-like structure in the template AST and
@@ -24565,11 +25022,12 @@ function ingestControlFlowInsertionPoint(unit, xref, node) {
24565
25022
  // and they can be used in directive matching (in the case of `Template.templateAttrs`).
24566
25023
  if (root !== null) {
24567
25024
  for (const attr of root.attributes) {
24568
- ingestBinding(unit, xref, attr.name, literal(attr.value), 1 /* e.BindingType.Attribute */, null, SecurityContext.NONE, attr.sourceSpan, BindingFlags.TextValue, attr.i18n);
25025
+ const securityContext = domSchema.securityContext(NG_TEMPLATE_TAG_NAME$1, attr.name, true);
25026
+ unit.update.push(createBindingOp(xref, BindingKind.Attribute, attr.name, literal(attr.value), null, securityContext, true, false, null, asMessage(attr.i18n), attr.sourceSpan));
24569
25027
  }
24570
25028
  const tagName = root instanceof Element$1 ? root.name : root.tagName;
24571
25029
  // Don't pass along `ng-template` tag name since it enables directive matching.
24572
- return tagName === 'ng-template' ? null : tagName;
25030
+ return tagName === NG_TEMPLATE_TAG_NAME$1 ? null : tagName;
24573
25031
  }
24574
25032
  return null;
24575
25033
  }
@@ -28458,7 +28916,12 @@ class TemplateDefinitionBuilder {
28458
28916
  });
28459
28917
  const { expression: trackByExpression, usesComponentInstance: trackByUsesComponentInstance } = this.createTrackByFunction(block);
28460
28918
  let emptyData = null;
28919
+ let emptyTagName = null;
28920
+ let emptyAttrsExprs;
28461
28921
  if (block.empty !== null) {
28922
+ const emptyInferred = this.inferProjectionDataFromInsertionPoint(block.empty);
28923
+ emptyTagName = emptyInferred.tagName;
28924
+ emptyAttrsExprs = emptyInferred.attrsExprs;
28462
28925
  emptyData = this.prepareEmbeddedTemplateFn(block.empty.children, '_ForEmpty', undefined, block.empty.i18n);
28463
28926
  // Allocate an extra slot for the empty block tracking.
28464
28927
  this.allocateBindingSlots(null);
@@ -28476,13 +28939,13 @@ class TemplateDefinitionBuilder {
28476
28939
  trackByExpression,
28477
28940
  ];
28478
28941
  if (emptyData !== null) {
28479
- params.push(literal(trackByUsesComponentInstance), variable(emptyData.name), literal(emptyData.getConstCount()), literal(emptyData.getVarCount()));
28942
+ params.push(literal(trackByUsesComponentInstance), variable(emptyData.name), literal(emptyData.getConstCount()), literal(emptyData.getVarCount()), literal(emptyTagName), this.addAttrsToConsts(emptyAttrsExprs || null));
28480
28943
  }
28481
28944
  else if (trackByUsesComponentInstance) {
28482
28945
  // If the tracking function doesn't use the component instance, we can omit the flag.
28483
28946
  params.push(literal(trackByUsesComponentInstance));
28484
28947
  }
28485
- return params;
28948
+ return trimTrailingNulls(params);
28486
28949
  });
28487
28950
  // Note: the expression needs to be processed *after* the template,
28488
28951
  // otherwise pipes injecting some symbols won't work (see #52102).
@@ -29241,12 +29704,16 @@ class BindingScope {
29241
29704
  }
29242
29705
  /** Binding scope of a `track` function inside a `for` loop block. */
29243
29706
  class TrackByBindingScope extends BindingScope {
29244
- constructor(parentScope, globalAliases) {
29707
+ constructor(parentScope, globalOverrides) {
29245
29708
  super(parentScope.bindingLevel + 1, parentScope);
29246
- this.globalAliases = globalAliases;
29709
+ this.globalOverrides = globalOverrides;
29247
29710
  this.componentAccessCount = 0;
29248
29711
  }
29249
29712
  get(name) {
29713
+ // Intercept any overridden globals.
29714
+ if (this.globalOverrides.hasOwnProperty(name)) {
29715
+ return variable(this.globalOverrides[name]);
29716
+ }
29250
29717
  let current = this.parent;
29251
29718
  // Prevent accesses of template variables outside the `for` loop.
29252
29719
  while (current) {
@@ -29255,10 +29722,6 @@ class TrackByBindingScope extends BindingScope {
29255
29722
  }
29256
29723
  current = current.parent;
29257
29724
  }
29258
- // Intercept any aliased globals.
29259
- if (this.globalAliases[name]) {
29260
- return variable(this.globalAliases[name]);
29261
- }
29262
29725
  // When the component scope is accessed, we redirect it through `this`.
29263
29726
  this.componentAccessCount++;
29264
29727
  return variable('this').prop(name);
@@ -29923,14 +30386,16 @@ function createBaseDirectiveTypeParams(meta) {
29923
30386
  function getInputsTypeExpression(meta) {
29924
30387
  return literalMap(Object.keys(meta.inputs).map(key => {
29925
30388
  const value = meta.inputs[key];
29926
- return {
29927
- key,
29928
- value: literalMap([
29929
- { key: 'alias', value: literal(value.bindingPropertyName), quoted: true },
29930
- { key: 'required', value: literal(value.required), quoted: true }
29931
- ]),
29932
- quoted: true
29933
- };
30389
+ const values = [
30390
+ { key: 'alias', value: literal(value.bindingPropertyName), quoted: true },
30391
+ { key: 'required', value: literal(value.required), quoted: true },
30392
+ ];
30393
+ // TODO(legacy-partial-output-inputs): Consider always emitting this information,
30394
+ // or leaving it as is.
30395
+ if (value.isSignal) {
30396
+ values.push({ key: 'isSignal', value: literal(value.isSignal), quoted: true });
30397
+ }
30398
+ return { key, value: literalMap(values), quoted: true };
29934
30399
  }));
29935
30400
  }
29936
30401
  /**
@@ -29996,6 +30461,7 @@ function createHostBindingsFunction(hostBindingsMetadata, typeSourceSpan, bindin
29996
30461
  }
29997
30462
  const hostJob = ingestHostBinding({
29998
30463
  componentName: name,
30464
+ componentSelector: selector,
29999
30465
  properties: bindings,
30000
30466
  events: eventBindings,
30001
30467
  attributes: hostBindingsMetadata.attributes,
@@ -30306,6 +30772,18 @@ function compileStyles(styles, selector, hostSelector) {
30306
30772
  return shadowCss.shimCssText(style, selector, hostSelector);
30307
30773
  });
30308
30774
  }
30775
+ /**
30776
+ * Encapsulates a CSS stylesheet with emulated view encapsulation.
30777
+ * This allows a stylesheet to be used with an Angular component that
30778
+ * is using the `ViewEncapsulation.Emulated` mode.
30779
+ *
30780
+ * @param style The content of a CSS stylesheet.
30781
+ * @returns The encapsulated content for the style.
30782
+ */
30783
+ function encapsulateStyle(style) {
30784
+ const shadowCss = new ShadowCss();
30785
+ return shadowCss.shimCssText(style, CONTENT_ATTR, HOST_ATTR);
30786
+ }
30309
30787
  function createHostDirectivesType(meta) {
30310
30788
  if (!meta.hostDirectives?.length) {
30311
30789
  return NONE_TYPE;
@@ -31371,6 +31849,8 @@ function convertDirectiveFacadeToMetadata(facade) {
31371
31849
  bindingPropertyName: ann.alias || field,
31372
31850
  classPropertyName: field,
31373
31851
  required: ann.required || false,
31852
+ // TODO(signals): Support JIT signal inputs via decorator transform.
31853
+ isSignal: false,
31374
31854
  transformFunction: ann.transform != null ? new WrappedNodeExpr(ann.transform) : null,
31375
31855
  };
31376
31856
  }
@@ -31402,7 +31882,7 @@ function convertDeclareDirectiveFacadeToMetadata(declaration, typeSourceSpan) {
31402
31882
  type: wrapReference(declaration.type),
31403
31883
  typeSourceSpan,
31404
31884
  selector: declaration.selector ?? null,
31405
- inputs: declaration.inputs ? inputsMappingToInputMetadata(declaration.inputs) : {},
31885
+ inputs: declaration.inputs ? inputsPartialMetadataToInputMetadata(declaration.inputs) : {},
31406
31886
  outputs: declaration.outputs ?? {},
31407
31887
  host: convertHostDeclarationToMetadata(declaration.host),
31408
31888
  queries: (declaration.queries ?? []).map(convertQueryDeclarationToMetadata),
@@ -31665,28 +32145,51 @@ function isInput(value) {
31665
32145
  function isOutput(value) {
31666
32146
  return value.ngMetadataName === 'Output';
31667
32147
  }
31668
- function inputsMappingToInputMetadata(inputs) {
31669
- return Object.keys(inputs).reduce((result, key) => {
31670
- const value = inputs[key];
31671
- if (typeof value === 'string') {
31672
- result[key] = {
31673
- bindingPropertyName: value,
31674
- classPropertyName: value,
31675
- transformFunction: null,
31676
- required: false,
31677
- };
32148
+ function inputsPartialMetadataToInputMetadata(inputs) {
32149
+ return Object.keys(inputs).reduce((result, minifiedClassName) => {
32150
+ const value = inputs[minifiedClassName];
32151
+ // Handle legacy partial input output.
32152
+ if (typeof value === 'string' || Array.isArray(value)) {
32153
+ result[minifiedClassName] = parseLegacyInputPartialOutput(value);
31678
32154
  }
31679
32155
  else {
31680
- result[key] = {
31681
- bindingPropertyName: value[0],
31682
- classPropertyName: value[1],
31683
- transformFunction: value[2] ? new WrappedNodeExpr(value[2]) : null,
31684
- required: false,
32156
+ result[minifiedClassName] = {
32157
+ bindingPropertyName: value.publicName,
32158
+ classPropertyName: minifiedClassName,
32159
+ transformFunction: value.transformFunction !== null ?
32160
+ new WrappedNodeExpr(value.transformFunction) :
32161
+ null,
32162
+ required: value.isRequired,
32163
+ isSignal: value.isSignal,
31685
32164
  };
31686
32165
  }
31687
32166
  return result;
31688
32167
  }, {});
31689
32168
  }
32169
+ /**
32170
+ * Parses the legacy input partial output. For more details see `partial/directive.ts`.
32171
+ * TODO(legacy-partial-output-inputs): Remove in v18.
32172
+ */
32173
+ function parseLegacyInputPartialOutput(value) {
32174
+ if (typeof value === 'string') {
32175
+ return {
32176
+ bindingPropertyName: value,
32177
+ classPropertyName: value,
32178
+ transformFunction: null,
32179
+ required: false,
32180
+ // legacy partial output does not capture signal inputs.
32181
+ isSignal: false,
32182
+ };
32183
+ }
32184
+ return {
32185
+ bindingPropertyName: value[0],
32186
+ classPropertyName: value[1],
32187
+ transformFunction: value[2] ? new WrappedNodeExpr(value[2]) : null,
32188
+ required: false,
32189
+ // legacy partial output does not capture signal inputs.
32190
+ isSignal: false,
32191
+ };
32192
+ }
31690
32193
  function parseInputsArray(values) {
31691
32194
  return values.reduce((results, value) => {
31692
32195
  if (typeof value === 'string') {
@@ -31695,6 +32198,8 @@ function parseInputsArray(values) {
31695
32198
  bindingPropertyName,
31696
32199
  classPropertyName,
31697
32200
  required: false,
32201
+ // Signal inputs not supported for the inputs array.
32202
+ isSignal: false,
31698
32203
  transformFunction: null,
31699
32204
  };
31700
32205
  }
@@ -31703,6 +32208,8 @@ function parseInputsArray(values) {
31703
32208
  bindingPropertyName: value.alias || value.name,
31704
32209
  classPropertyName: value.name,
31705
32210
  required: value.required || false,
32211
+ // Signal inputs not supported for the inputs array.
32212
+ isSignal: false,
31706
32213
  transformFunction: value.transform != null ? new WrappedNodeExpr(value.transform) : null,
31707
32214
  };
31708
32215
  }
@@ -31755,7 +32262,7 @@ function publishFacade(global) {
31755
32262
  * @description
31756
32263
  * Entry point for all public APIs of the compiler package.
31757
32264
  */
31758
- const VERSION = new Version('17.1.0-next.3');
32265
+ const VERSION = new Version('17.1.0-next.5');
31759
32266
 
31760
32267
  class CompilerConfig {
31761
32268
  constructor({ defaultEncapsulation = ViewEncapsulation.Emulated, preserveWhitespaces, strictInjectionParameters } = {}) {
@@ -33317,11 +33824,11 @@ function compileClassDebugInfo(debugInfo) {
33317
33824
  *
33318
33825
  * Do not include any prerelease in these versions as they are ignored.
33319
33826
  */
33320
- const MINIMUM_PARTIAL_LINKER_VERSION$6 = '12.0.0';
33827
+ const MINIMUM_PARTIAL_LINKER_VERSION$5 = '12.0.0';
33321
33828
  function compileDeclareClassMetadata(metadata) {
33322
33829
  const definitionMap = new DefinitionMap();
33323
- definitionMap.set('minVersion', literal(MINIMUM_PARTIAL_LINKER_VERSION$6));
33324
- definitionMap.set('version', literal('17.1.0-next.3'));
33830
+ definitionMap.set('minVersion', literal(MINIMUM_PARTIAL_LINKER_VERSION$5));
33831
+ definitionMap.set('version', literal('17.1.0-next.5'));
33325
33832
  definitionMap.set('ngImport', importExpr(Identifiers.core));
33326
33833
  definitionMap.set('type', metadata.type);
33327
33834
  definitionMap.set('decorators', metadata.decorators);
@@ -33400,14 +33907,6 @@ function compileDependency(dep) {
33400
33907
  return depMeta.toLiteralMap();
33401
33908
  }
33402
33909
 
33403
- /**
33404
- * Every time we make a breaking change to the declaration interface or partial-linker behavior, we
33405
- * must update this constant to prevent old partial-linkers from incorrectly processing the
33406
- * declaration.
33407
- *
33408
- * Do not include any prerelease in these versions as they are ignored.
33409
- */
33410
- const MINIMUM_PARTIAL_LINKER_VERSION$5 = '16.1.0';
33411
33910
  /**
33412
33911
  * Compile a directive declaration defined by the `R3DirectiveMetadata`.
33413
33912
  */
@@ -33423,13 +33922,9 @@ function compileDeclareDirectiveFromMetadata(meta) {
33423
33922
  */
33424
33923
  function createDirectiveDefinitionMap(meta) {
33425
33924
  const definitionMap = new DefinitionMap();
33426
- const hasTransformFunctions = Object.values(meta.inputs).some(input => input.transformFunction !== null);
33427
- // Note: in order to allow consuming Angular libraries that have been compiled with 16.1+ in
33428
- // Angular 16.0, we only force a minimum version of 16.1 if input transform feature as introduced
33429
- // in 16.1 is actually used.
33430
- const minVersion = hasTransformFunctions ? MINIMUM_PARTIAL_LINKER_VERSION$5 : '14.0.0';
33925
+ const minVersion = getMinimumVersionForPartialOutput(meta);
33431
33926
  definitionMap.set('minVersion', literal(minVersion));
33432
- definitionMap.set('version', literal('17.1.0-next.3'));
33927
+ definitionMap.set('version', literal('17.1.0-next.5'));
33433
33928
  // e.g. `type: MyDirective`
33434
33929
  definitionMap.set('type', meta.type.value);
33435
33930
  if (meta.isStandalone) {
@@ -33442,7 +33937,8 @@ function createDirectiveDefinitionMap(meta) {
33442
33937
  if (meta.selector !== null) {
33443
33938
  definitionMap.set('selector', literal(meta.selector));
33444
33939
  }
33445
- definitionMap.set('inputs', conditionallyCreateDirectiveBindingLiteral(meta.inputs, true));
33940
+ definitionMap.set('inputs', needsNewInputPartialOutput(meta) ? createInputsPartialMetadata(meta.inputs) :
33941
+ legacyInputsPartialMetadata(meta.inputs));
33446
33942
  definitionMap.set('outputs', conditionallyCreateDirectiveBindingLiteral(meta.outputs));
33447
33943
  definitionMap.set('host', compileHostMetadata(meta.host));
33448
33944
  definitionMap.set('providers', meta.providers);
@@ -33467,6 +33963,44 @@ function createDirectiveDefinitionMap(meta) {
33467
33963
  definitionMap.set('ngImport', importExpr(Identifiers.core));
33468
33964
  return definitionMap;
33469
33965
  }
33966
+ /**
33967
+ * Determines the minimum linker version for the partial output
33968
+ * generated for this directive.
33969
+ *
33970
+ * Every time we make a breaking change to the declaration interface or partial-linker
33971
+ * behavior, we must update the minimum versions to prevent old partial-linkers from
33972
+ * incorrectly processing the declaration.
33973
+ *
33974
+ * NOTE: Do not include any prerelease in these versions as they are ignored.
33975
+ */
33976
+ function getMinimumVersionForPartialOutput(meta) {
33977
+ // We are starting with the oldest minimum version that can work for common
33978
+ // directive partial compilation output. As we discover usages of new features
33979
+ // that require a newer partial output emit, we bump the `minVersion`. Our goal
33980
+ // is to keep libraries as much compatible with older linker versions as possible.
33981
+ let minVersion = '14.0.0';
33982
+ // Note: in order to allow consuming Angular libraries that have been compiled with 16.1+ in
33983
+ // Angular 16.0, we only force a minimum version of 16.1 if input transform feature as introduced
33984
+ // in 16.1 is actually used.
33985
+ const hasTransformFunctions = Object.values(meta.inputs).some(input => input.transformFunction !== null);
33986
+ if (hasTransformFunctions) {
33987
+ minVersion = '16.1.0';
33988
+ }
33989
+ // If there are input flags and we need the new emit, use the actual minimum version,
33990
+ // where this was introduced. i.e. in 17.1.0
33991
+ // TODO(legacy-partial-output-inputs): Remove in v18.
33992
+ if (needsNewInputPartialOutput(meta)) {
33993
+ minVersion = '17.1.0';
33994
+ }
33995
+ return minVersion;
33996
+ }
33997
+ /**
33998
+ * Gets whether the given directive needs the new input partial output structure
33999
+ * that can hold additional metadata like `isRequired`, `isSignal` etc.
34000
+ */
34001
+ function needsNewInputPartialOutput(meta) {
34002
+ return Object.values(meta.inputs).some(input => input.isSignal);
34003
+ }
33470
34004
  /**
33471
34005
  * Compiles the metadata of a single query into its partial declaration form as declared
33472
34006
  * by `R3DeclareQueryMetadata`.
@@ -33540,6 +34074,74 @@ function createHostDirectives(hostDirectives) {
33540
34074
  // otherwise we can save some bytes by using a plain array, e.g. `[{directive: HostDir}]`.
33541
34075
  return literalArr(expressions);
33542
34076
  }
34077
+ /**
34078
+ * Generates partial output metadata for inputs of a directive.
34079
+ *
34080
+ * The generated structure is expected to match `R3DeclareDirectiveFacade['inputs']`.
34081
+ */
34082
+ function createInputsPartialMetadata(inputs) {
34083
+ const keys = Object.getOwnPropertyNames(inputs);
34084
+ if (keys.length === 0) {
34085
+ return null;
34086
+ }
34087
+ return literalMap(keys.map(declaredName => {
34088
+ const value = inputs[declaredName];
34089
+ return {
34090
+ key: declaredName,
34091
+ // put quotes around keys that contain potentially unsafe characters
34092
+ quoted: UNSAFE_OBJECT_KEY_NAME_REGEXP.test(declaredName),
34093
+ value: literalMap([
34094
+ { key: 'classPropertyName', quoted: false, value: asLiteral(value.classPropertyName) },
34095
+ { key: 'publicName', quoted: false, value: asLiteral(value.bindingPropertyName) },
34096
+ { key: 'isSignal', quoted: false, value: asLiteral(value.isSignal) },
34097
+ { key: 'isRequired', quoted: false, value: asLiteral(value.required) },
34098
+ { key: 'transformFunction', quoted: false, value: value.transformFunction ?? NULL_EXPR },
34099
+ ])
34100
+ };
34101
+ }));
34102
+ }
34103
+ /**
34104
+ * Pre v18 legacy partial output for inputs.
34105
+ *
34106
+ * Previously, inputs did not capture metadata like `isSignal` in the partial compilation output.
34107
+ * To enable capturing such metadata, we restructured how input metadata is communicated in the
34108
+ * partial output. This would make libraries incompatible with older Angular FW versions where the
34109
+ * linker would not know how to handle this new "format". For this reason, if we know this metadata
34110
+ * does not need to be captured- we fall back to the old format. This is what this function
34111
+ * generates.
34112
+ *
34113
+ * See:
34114
+ * https://github.com/angular/angular/blob/d4b423690210872b5c32a322a6090beda30b05a3/packages/core/src/compiler/compiler_facade_interface.ts#L197-L199
34115
+ */
34116
+ function legacyInputsPartialMetadata(inputs) {
34117
+ // TODO(legacy-partial-output-inputs): Remove function in v18.
34118
+ const keys = Object.getOwnPropertyNames(inputs);
34119
+ if (keys.length === 0) {
34120
+ return null;
34121
+ }
34122
+ return literalMap(keys.map(declaredName => {
34123
+ const value = inputs[declaredName];
34124
+ const publicName = value.bindingPropertyName;
34125
+ const differentDeclaringName = publicName !== declaredName;
34126
+ let result;
34127
+ if (differentDeclaringName || value.transformFunction !== null) {
34128
+ const values = [asLiteral(publicName), asLiteral(declaredName)];
34129
+ if (value.transformFunction !== null) {
34130
+ values.push(value.transformFunction);
34131
+ }
34132
+ result = literalArr(values);
34133
+ }
34134
+ else {
34135
+ result = asLiteral(publicName);
34136
+ }
34137
+ return {
34138
+ key: declaredName,
34139
+ // put quotes around keys that contain potentially unsafe characters
34140
+ quoted: UNSAFE_OBJECT_KEY_NAME_REGEXP.test(declaredName),
34141
+ value: result,
34142
+ };
34143
+ }));
34144
+ }
33543
34145
 
33544
34146
  /**
33545
34147
  * Compile a component declaration defined by the `R3ComponentMetadata`.
@@ -33706,7 +34308,7 @@ const MINIMUM_PARTIAL_LINKER_VERSION$4 = '12.0.0';
33706
34308
  function compileDeclareFactoryFunction(meta) {
33707
34309
  const definitionMap = new DefinitionMap();
33708
34310
  definitionMap.set('minVersion', literal(MINIMUM_PARTIAL_LINKER_VERSION$4));
33709
- definitionMap.set('version', literal('17.1.0-next.3'));
34311
+ definitionMap.set('version', literal('17.1.0-next.5'));
33710
34312
  definitionMap.set('ngImport', importExpr(Identifiers.core));
33711
34313
  definitionMap.set('type', meta.type.value);
33712
34314
  definitionMap.set('deps', compileDependencies(meta.deps));
@@ -33741,7 +34343,7 @@ function compileDeclareInjectableFromMetadata(meta) {
33741
34343
  function createInjectableDefinitionMap(meta) {
33742
34344
  const definitionMap = new DefinitionMap();
33743
34345
  definitionMap.set('minVersion', literal(MINIMUM_PARTIAL_LINKER_VERSION$3));
33744
- definitionMap.set('version', literal('17.1.0-next.3'));
34346
+ definitionMap.set('version', literal('17.1.0-next.5'));
33745
34347
  definitionMap.set('ngImport', importExpr(Identifiers.core));
33746
34348
  definitionMap.set('type', meta.type.value);
33747
34349
  // Only generate providedIn property if it has a non-null value
@@ -33792,7 +34394,7 @@ function compileDeclareInjectorFromMetadata(meta) {
33792
34394
  function createInjectorDefinitionMap(meta) {
33793
34395
  const definitionMap = new DefinitionMap();
33794
34396
  definitionMap.set('minVersion', literal(MINIMUM_PARTIAL_LINKER_VERSION$2));
33795
- definitionMap.set('version', literal('17.1.0-next.3'));
34397
+ definitionMap.set('version', literal('17.1.0-next.5'));
33796
34398
  definitionMap.set('ngImport', importExpr(Identifiers.core));
33797
34399
  definitionMap.set('type', meta.type.value);
33798
34400
  definitionMap.set('providers', meta.providers);
@@ -33825,7 +34427,7 @@ function createNgModuleDefinitionMap(meta) {
33825
34427
  throw new Error('Invalid path! Local compilation mode should not get into the partial compilation path');
33826
34428
  }
33827
34429
  definitionMap.set('minVersion', literal(MINIMUM_PARTIAL_LINKER_VERSION$1));
33828
- definitionMap.set('version', literal('17.1.0-next.3'));
34430
+ definitionMap.set('version', literal('17.1.0-next.5'));
33829
34431
  definitionMap.set('ngImport', importExpr(Identifiers.core));
33830
34432
  definitionMap.set('type', meta.type.value);
33831
34433
  // We only generate the keys in the metadata if the arrays contain values.
@@ -33876,7 +34478,7 @@ function compileDeclarePipeFromMetadata(meta) {
33876
34478
  function createPipeDefinitionMap(meta) {
33877
34479
  const definitionMap = new DefinitionMap();
33878
34480
  definitionMap.set('minVersion', literal(MINIMUM_PARTIAL_LINKER_VERSION));
33879
- definitionMap.set('version', literal('17.1.0-next.3'));
34481
+ definitionMap.set('version', literal('17.1.0-next.5'));
33880
34482
  definitionMap.set('ngImport', importExpr(Identifiers.core));
33881
34483
  // e.g. `type: MyPipe`
33882
34484
  definitionMap.set('type', meta.type.value);
@@ -33909,5 +34511,5 @@ publishFacade(_global);
33909
34511
 
33910
34512
  // This file is not used to build this module. It is only used during editing
33911
34513
 
33912
- export { AST, ASTWithName, ASTWithSource, AbsoluteSourceSpan, ArrayType, ArrowFunctionExpr, AstMemoryEfficientTransformer, AstTransformer, Attribute, Binary, BinaryOperator, BinaryOperatorExpr, BindingPipe, Block, BlockParameter, BoundElementProperty, BuiltinType, BuiltinTypeName, CUSTOM_ELEMENTS_SCHEMA, Call, Chain, ChangeDetectionStrategy, CommaExpr, Comment, CompilerConfig, Conditional, ConditionalExpr, ConstantPool, CssSelector, DEFAULT_INTERPOLATION_CONFIG, DYNAMIC_TYPE, DeclareFunctionStmt, DeclareVarStmt, DomElementSchemaRegistry, DynamicImportExpr, EOF, Element, ElementSchemaRegistry, EmitterVisitorContext, EmptyExpr$1 as EmptyExpr, Expansion, ExpansionCase, Expression, ExpressionBinding, ExpressionStatement, ExpressionType, ExternalExpr, ExternalReference, FactoryTarget$1 as FactoryTarget, FunctionExpr, HtmlParser, HtmlTagDefinition, I18NHtmlParser, IfStmt, ImplicitReceiver, InstantiateExpr, Interpolation$1 as Interpolation, InterpolationConfig, InvokeFunctionExpr, JSDocComment, JitEvaluator, KeyedRead, KeyedWrite, LeadingComment, Lexer, LiteralArray, LiteralArrayExpr, LiteralExpr, LiteralMap, LiteralMapExpr, LiteralPrimitive, LocalizedString, MapType, MessageBundle, NONE_TYPE, NO_ERRORS_SCHEMA, NodeWithI18n, NonNullAssert, NotExpr, ParseError, ParseErrorLevel, ParseLocation, ParseSourceFile, ParseSourceSpan, ParseSpan, ParseTreeResult, ParsedEvent, ParsedProperty, ParsedPropertyType, ParsedVariable, Parser$1 as Parser, ParserError, PrefixNot, PropertyRead, PropertyWrite, R3BoundTarget, Identifiers as R3Identifiers, R3NgModuleMetadataKind, R3SelectorScopeMode, R3TargetBinder, R3TemplateDependencyKind, ReadKeyExpr, ReadPropExpr, ReadVarExpr, RecursiveAstVisitor, RecursiveVisitor, ResourceLoader, ReturnStatement, STRING_TYPE, SafeCall, SafeKeyedRead, SafePropertyRead, SelectorContext, SelectorListContext, SelectorMatcher, Serializer, SplitInterpolation, Statement, StmtModifier, TagContentType, TaggedTemplateExpr, TemplateBindingParseResult, TemplateLiteral, TemplateLiteralElement, Text, ThisReceiver, BoundAttribute as TmplAstBoundAttribute, BoundDeferredTrigger as TmplAstBoundDeferredTrigger, BoundEvent as TmplAstBoundEvent, BoundText as TmplAstBoundText, Content as TmplAstContent, DeferredBlock as TmplAstDeferredBlock, DeferredBlockError as TmplAstDeferredBlockError, DeferredBlockLoading as TmplAstDeferredBlockLoading, DeferredBlockPlaceholder as TmplAstDeferredBlockPlaceholder, DeferredTrigger as TmplAstDeferredTrigger, Element$1 as TmplAstElement, ForLoopBlock as TmplAstForLoopBlock, ForLoopBlockEmpty as TmplAstForLoopBlockEmpty, HoverDeferredTrigger as TmplAstHoverDeferredTrigger, Icu$1 as TmplAstIcu, IdleDeferredTrigger as TmplAstIdleDeferredTrigger, IfBlock as TmplAstIfBlock, IfBlockBranch as TmplAstIfBlockBranch, ImmediateDeferredTrigger as TmplAstImmediateDeferredTrigger, InteractionDeferredTrigger as TmplAstInteractionDeferredTrigger, RecursiveVisitor$1 as TmplAstRecursiveVisitor, Reference as TmplAstReference, SwitchBlock as TmplAstSwitchBlock, SwitchBlockCase as TmplAstSwitchBlockCase, Template as TmplAstTemplate, Text$3 as TmplAstText, TextAttribute as TmplAstTextAttribute, TimerDeferredTrigger as TmplAstTimerDeferredTrigger, UnknownBlock as TmplAstUnknownBlock, Variable as TmplAstVariable, ViewportDeferredTrigger as TmplAstViewportDeferredTrigger, Token, TokenType, TransplantedType, TreeError, Type, TypeModifier, TypeofExpr, Unary, UnaryOperator, UnaryOperatorExpr, VERSION, VariableBinding, Version, ViewEncapsulation, WrappedNodeExpr, WriteKeyExpr, WritePropExpr, WriteVarExpr, Xliff, Xliff2, Xmb, XmlParser, Xtb, _ParseAST, compileClassDebugInfo, compileClassMetadata, compileComponentClassMetadata, compileComponentFromMetadata, compileDeclareClassMetadata, compileDeclareComponentFromMetadata, compileDeclareDirectiveFromMetadata, compileDeclareFactoryFunction, compileDeclareInjectableFromMetadata, compileDeclareInjectorFromMetadata, compileDeclareNgModuleFromMetadata, compileDeclarePipeFromMetadata, compileDirectiveFromMetadata, compileFactoryFunction, compileInjectable, compileInjector, compileNgModule, compilePipeFromMetadata, computeMsgId, core, createCssSelectorFromNode, createInjectableType, createMayBeForwardRefExpression, devOnlyGuardedExpression, emitDistinctChangesOnlyDefaultValue, getHtmlTagDefinition, getNsPrefix, getSafePropertyAccessString, identifierName, isIdentifier, isNgContainer, isNgContent, isNgTemplate, jsDocComment, leadingComment, literal, literalMap, makeBindingParser, mergeNsAndName, output_ast as outputAst, parseHostBindings, parseTemplate, preserveWhitespacesDefault, publishFacade, r3JitTypeSourceSpan, sanitizeIdentifier, splitNsName, verifyHostBindings, visitAll };
34514
+ export { AST, ASTWithName, ASTWithSource, AbsoluteSourceSpan, ArrayType, ArrowFunctionExpr, AstMemoryEfficientTransformer, AstTransformer, Attribute, Binary, BinaryOperator, BinaryOperatorExpr, BindingPipe, Block, BlockParameter, BoundElementProperty, BuiltinType, BuiltinTypeName, CUSTOM_ELEMENTS_SCHEMA, Call, Chain, ChangeDetectionStrategy, CommaExpr, Comment, CompilerConfig, Conditional, ConditionalExpr, ConstantPool, CssSelector, DEFAULT_INTERPOLATION_CONFIG, DYNAMIC_TYPE, DeclareFunctionStmt, DeclareVarStmt, DomElementSchemaRegistry, DynamicImportExpr, EOF, Element, ElementSchemaRegistry, EmitterVisitorContext, EmptyExpr$1 as EmptyExpr, Expansion, ExpansionCase, Expression, ExpressionBinding, ExpressionStatement, ExpressionType, ExternalExpr, ExternalReference, FactoryTarget$1 as FactoryTarget, FunctionExpr, HtmlParser, HtmlTagDefinition, I18NHtmlParser, IfStmt, ImplicitReceiver, InstantiateExpr, Interpolation$1 as Interpolation, InterpolationConfig, InvokeFunctionExpr, JSDocComment, JitEvaluator, KeyedRead, KeyedWrite, LeadingComment, Lexer, LiteralArray, LiteralArrayExpr, LiteralExpr, LiteralMap, LiteralMapExpr, LiteralPrimitive, LocalizedString, MapType, MessageBundle, NONE_TYPE, NO_ERRORS_SCHEMA, NodeWithI18n, NonNullAssert, NotExpr, ParseError, ParseErrorLevel, ParseLocation, ParseSourceFile, ParseSourceSpan, ParseSpan, ParseTreeResult, ParsedEvent, ParsedProperty, ParsedPropertyType, ParsedVariable, Parser$1 as Parser, ParserError, PrefixNot, PropertyRead, PropertyWrite, R3BoundTarget, Identifiers as R3Identifiers, R3NgModuleMetadataKind, R3SelectorScopeMode, R3TargetBinder, R3TemplateDependencyKind, ReadKeyExpr, ReadPropExpr, ReadVarExpr, RecursiveAstVisitor, RecursiveVisitor, ResourceLoader, ReturnStatement, STRING_TYPE, SafeCall, SafeKeyedRead, SafePropertyRead, SelectorContext, SelectorListContext, SelectorMatcher, Serializer, SplitInterpolation, Statement, StmtModifier, TagContentType, TaggedTemplateExpr, TemplateBindingParseResult, TemplateLiteral, TemplateLiteralElement, Text, ThisReceiver, BoundAttribute as TmplAstBoundAttribute, BoundDeferredTrigger as TmplAstBoundDeferredTrigger, BoundEvent as TmplAstBoundEvent, BoundText as TmplAstBoundText, Content as TmplAstContent, DeferredBlock as TmplAstDeferredBlock, DeferredBlockError as TmplAstDeferredBlockError, DeferredBlockLoading as TmplAstDeferredBlockLoading, DeferredBlockPlaceholder as TmplAstDeferredBlockPlaceholder, DeferredTrigger as TmplAstDeferredTrigger, Element$1 as TmplAstElement, ForLoopBlock as TmplAstForLoopBlock, ForLoopBlockEmpty as TmplAstForLoopBlockEmpty, HoverDeferredTrigger as TmplAstHoverDeferredTrigger, Icu$1 as TmplAstIcu, IdleDeferredTrigger as TmplAstIdleDeferredTrigger, IfBlock as TmplAstIfBlock, IfBlockBranch as TmplAstIfBlockBranch, ImmediateDeferredTrigger as TmplAstImmediateDeferredTrigger, InteractionDeferredTrigger as TmplAstInteractionDeferredTrigger, RecursiveVisitor$1 as TmplAstRecursiveVisitor, Reference as TmplAstReference, SwitchBlock as TmplAstSwitchBlock, SwitchBlockCase as TmplAstSwitchBlockCase, Template as TmplAstTemplate, Text$3 as TmplAstText, TextAttribute as TmplAstTextAttribute, TimerDeferredTrigger as TmplAstTimerDeferredTrigger, UnknownBlock as TmplAstUnknownBlock, Variable as TmplAstVariable, ViewportDeferredTrigger as TmplAstViewportDeferredTrigger, Token, TokenType, TransplantedType, TreeError, Type, TypeModifier, TypeofExpr, Unary, UnaryOperator, UnaryOperatorExpr, VERSION, VariableBinding, Version, ViewEncapsulation, WrappedNodeExpr, WriteKeyExpr, WritePropExpr, WriteVarExpr, Xliff, Xliff2, Xmb, XmlParser, Xtb, _ParseAST, compileClassDebugInfo, compileClassMetadata, compileComponentClassMetadata, compileComponentFromMetadata, compileDeclareClassMetadata, compileDeclareComponentFromMetadata, compileDeclareDirectiveFromMetadata, compileDeclareFactoryFunction, compileDeclareInjectableFromMetadata, compileDeclareInjectorFromMetadata, compileDeclareNgModuleFromMetadata, compileDeclarePipeFromMetadata, compileDirectiveFromMetadata, compileFactoryFunction, compileInjectable, compileInjector, compileNgModule, compilePipeFromMetadata, computeMsgId, core, createCssSelectorFromNode, createInjectableType, createMayBeForwardRefExpression, devOnlyGuardedExpression, emitDistinctChangesOnlyDefaultValue, encapsulateStyle, getHtmlTagDefinition, getNsPrefix, getSafePropertyAccessString, identifierName, isIdentifier, isNgContainer, isNgContent, isNgTemplate, jsDocComment, leadingComment, literal, literalMap, makeBindingParser, mergeNsAndName, output_ast as outputAst, parseHostBindings, parseTemplate, preserveWhitespacesDefault, publishFacade, r3JitTypeSourceSpan, sanitizeIdentifier, splitNsName, verifyHostBindings, visitAll };
33913
34515
  //# sourceMappingURL=compiler.mjs.map