@angular/compiler 17.1.0-next.2 → 17.1.0-next.4

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 (50) hide show
  1. package/esm2022/src/compiler.mjs +2 -2
  2. package/esm2022/src/compiler_facade_interface.mjs +1 -1
  3. package/esm2022/src/jit_compiler_facade.mjs +46 -17
  4. package/esm2022/src/render3/partial/api.mjs +1 -1
  5. package/esm2022/src/render3/partial/class_metadata.mjs +1 -1
  6. package/esm2022/src/render3/partial/directive.mjs +112 -17
  7. package/esm2022/src/render3/partial/factory.mjs +1 -1
  8. package/esm2022/src/render3/partial/injectable.mjs +1 -1
  9. package/esm2022/src/render3/partial/injector.mjs +1 -1
  10. package/esm2022/src/render3/partial/ng_module.mjs +1 -1
  11. package/esm2022/src/render3/partial/pipe.mjs +1 -1
  12. package/esm2022/src/render3/r3_identifiers.mjs +4 -1
  13. package/esm2022/src/render3/view/api.mjs +1 -1
  14. package/esm2022/src/render3/view/compiler.mjs +43 -16
  15. package/esm2022/src/render3/view/util.mjs +8 -2
  16. package/esm2022/src/template/pipeline/ir/src/enums.mjs +34 -17
  17. package/esm2022/src/template/pipeline/ir/src/expression.mjs +19 -20
  18. package/esm2022/src/template/pipeline/ir/src/ops/create.mjs +37 -11
  19. package/esm2022/src/template/pipeline/ir/src/ops/host.mjs +5 -2
  20. package/esm2022/src/template/pipeline/ir/src/ops/update.mjs +29 -14
  21. package/esm2022/src/template/pipeline/src/emit.mjs +13 -9
  22. package/esm2022/src/template/pipeline/src/ingest.mjs +293 -158
  23. package/esm2022/src/template/pipeline/src/instruction.mjs +18 -14
  24. package/esm2022/src/template/pipeline/src/phases/apply_i18n_expressions.mjs +20 -4
  25. package/esm2022/src/template/pipeline/src/phases/assign_i18n_slot_dependencies.mjs +44 -22
  26. package/esm2022/src/template/pipeline/src/phases/attribute_extraction.mjs +26 -5
  27. package/esm2022/src/template/pipeline/src/phases/binding_specialization.mjs +4 -4
  28. package/esm2022/src/template/pipeline/src/phases/const_collection.mjs +15 -6
  29. package/esm2022/src/template/pipeline/src/phases/convert_i18n_bindings.mjs +52 -0
  30. package/esm2022/src/template/pipeline/src/phases/create_i18n_contexts.mjs +23 -1
  31. package/esm2022/src/template/pipeline/src/phases/extract_i18n_messages.mjs +65 -88
  32. package/esm2022/src/template/pipeline/src/phases/generate_advance.mjs +2 -2
  33. package/esm2022/src/template/pipeline/src/phases/generate_variables.mjs +7 -1
  34. package/esm2022/src/template/pipeline/src/phases/i18n_const_collection.mjs +147 -35
  35. package/esm2022/src/template/pipeline/src/phases/i18n_text_extraction.mjs +5 -3
  36. package/esm2022/src/template/pipeline/src/phases/parse_extracted_styles.mjs +4 -3
  37. package/esm2022/src/template/pipeline/src/phases/phase_remove_content_selectors.mjs +15 -3
  38. package/esm2022/src/template/pipeline/src/phases/propagate_i18n_blocks.mjs +31 -12
  39. package/esm2022/src/template/pipeline/src/phases/reify.mjs +18 -15
  40. package/esm2022/src/template/pipeline/src/phases/remove_unused_i18n_attrs.mjs +33 -0
  41. package/esm2022/src/template/pipeline/src/phases/resolve_i18n_element_placeholders.mjs +175 -31
  42. package/esm2022/src/template/pipeline/src/phases/resolve_i18n_expression_placeholders.mjs +10 -5
  43. package/esm2022/src/template/pipeline/src/phases/resolve_sanitizers.mjs +78 -14
  44. package/esm2022/src/template/pipeline/src/phases/slot_allocation.mjs +3 -1
  45. package/esm2022/src/template/pipeline/src/phases/var_counting.mjs +3 -1
  46. package/esm2022/src/version.mjs +1 -1
  47. package/fesm2022/compiler.mjs +1394 -532
  48. package/fesm2022/compiler.mjs.map +1 -1
  49. package/index.d.ts +39 -6
  50. package/package.json +2 -2
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @license Angular v17.1.0-next.2
2
+ * @license Angular v17.1.0-next.4
3
3
  * (c) 2010-2022 Google LLC. https://angular.io/
4
4
  * License: MIT
5
5
  */
@@ -2636,6 +2636,9 @@ class Identifiers {
2636
2636
  static { this.trustConstantHtml = { name: 'ɵɵtrustConstantHtml', moduleName: CORE }; }
2637
2637
  static { this.trustConstantResourceUrl = { name: 'ɵɵtrustConstantResourceUrl', moduleName: CORE }; }
2638
2638
  static { this.validateIframeAttribute = { name: 'ɵɵvalidateIframeAttribute', moduleName: CORE }; }
2639
+ // type-checking
2640
+ static { this.InputSignalBrandWriteType = { name: 'ɵINPUT_SIGNAL_BRAND_WRITE_TYPE', moduleName: CORE }; }
2641
+ static { this.UnwrapDirectiveSignalInputs = { name: 'ɵUnwrapDirectiveSignalInputs', moduleName: CORE }; }
2639
2642
  }
2640
2643
 
2641
2644
  const DASH_CASE_REGEXP = /-+([a-z0-9])/g;
@@ -4712,7 +4715,7 @@ const I18N_ATTR_PREFIX = 'i18n-';
4712
4715
  /** Prefix of var expressions used in ICUs */
4713
4716
  const I18N_ICU_VAR_PREFIX = 'VAR_';
4714
4717
  /** Prefix of ICU expressions for post processing */
4715
- const I18N_ICU_MAPPING_PREFIX = 'I18N_EXP_';
4718
+ const I18N_ICU_MAPPING_PREFIX$1 = 'I18N_EXP_';
4716
4719
  /** Placeholder wrapper for i18n expressions **/
4717
4720
  const I18N_PLACEHOLDER_SYMBOL = '�';
4718
4721
  function isI18nAttribute(name) {
@@ -4957,6 +4960,12 @@ function asLiteral(value) {
4957
4960
  }
4958
4961
  return literal(value, INFERRED_TYPE);
4959
4962
  }
4963
+ /**
4964
+ * Serializes inputs and outputs for `defineDirective` and `defineComponent`.
4965
+ *
4966
+ * This will attempt to generate optimized data structures to minimize memory or
4967
+ * file size of fully compiled applications.
4968
+ */
4960
4969
  function conditionallyCreateDirectiveBindingLiteral(map, keepDeclared) {
4961
4970
  const keys = Object.getOwnPropertyNames(map);
4962
4971
  if (keys.length === 0) {
@@ -8970,6 +8979,10 @@ var OpKind;
8970
8979
  * An i18n context containing information needed to generate an i18n message.
8971
8980
  */
8972
8981
  OpKind[OpKind["I18nContext"] = 43] = "I18nContext";
8982
+ /**
8983
+ * A creation op that corresponds to i18n attributes on an element.
8984
+ */
8985
+ OpKind[OpKind["I18nAttributes"] = 44] = "I18nAttributes";
8973
8986
  })(OpKind || (OpKind = {}));
8974
8987
  /**
8975
8988
  * Distinguishes different kinds of IR expressions.
@@ -9060,23 +9073,27 @@ var ExpressionKind;
9060
9073
  * An expression representing a sanitizer function.
9061
9074
  */
9062
9075
  ExpressionKind[ExpressionKind["SanitizerExpr"] = 20] = "SanitizerExpr";
9076
+ /**
9077
+ * An expression representing a function to create trusted values.
9078
+ */
9079
+ ExpressionKind[ExpressionKind["TrustedValueFnExpr"] = 21] = "TrustedValueFnExpr";
9063
9080
  /**
9064
9081
  * An expression that will cause a literal slot index to be emitted.
9065
9082
  */
9066
- ExpressionKind[ExpressionKind["SlotLiteralExpr"] = 21] = "SlotLiteralExpr";
9083
+ ExpressionKind[ExpressionKind["SlotLiteralExpr"] = 22] = "SlotLiteralExpr";
9067
9084
  /**
9068
9085
  * A test expression for a conditional op.
9069
9086
  */
9070
- ExpressionKind[ExpressionKind["ConditionalCase"] = 22] = "ConditionalCase";
9087
+ ExpressionKind[ExpressionKind["ConditionalCase"] = 23] = "ConditionalCase";
9071
9088
  /**
9072
9089
  * A variable for use inside a repeater, providing one of the ambiently-available context
9073
9090
  * properties ($even, $first, etc.).
9074
9091
  */
9075
- ExpressionKind[ExpressionKind["DerivedRepeaterVar"] = 23] = "DerivedRepeaterVar";
9092
+ ExpressionKind[ExpressionKind["DerivedRepeaterVar"] = 24] = "DerivedRepeaterVar";
9076
9093
  /**
9077
9094
  * An expression that will be automatically extracted to the component const array.
9078
9095
  */
9079
- ExpressionKind[ExpressionKind["ConstCollected"] = 24] = "ConstCollected";
9096
+ ExpressionKind[ExpressionKind["ConstCollected"] = 25] = "ConstCollected";
9080
9097
  })(ExpressionKind || (ExpressionKind = {}));
9081
9098
  var VariableFlags;
9082
9099
  (function (VariableFlags) {
@@ -9120,18 +9137,6 @@ var CompatibilityMode;
9120
9137
  CompatibilityMode[CompatibilityMode["Normal"] = 0] = "Normal";
9121
9138
  CompatibilityMode[CompatibilityMode["TemplateDefinitionBuilder"] = 1] = "TemplateDefinitionBuilder";
9122
9139
  })(CompatibilityMode || (CompatibilityMode = {}));
9123
- /**
9124
- * Represents functions used to sanitize different pieces of a template.
9125
- */
9126
- var SanitizerFn;
9127
- (function (SanitizerFn) {
9128
- SanitizerFn[SanitizerFn["Html"] = 0] = "Html";
9129
- SanitizerFn[SanitizerFn["Script"] = 1] = "Script";
9130
- SanitizerFn[SanitizerFn["Style"] = 2] = "Style";
9131
- SanitizerFn[SanitizerFn["Url"] = 3] = "Url";
9132
- SanitizerFn[SanitizerFn["ResourceUrl"] = 4] = "ResourceUrl";
9133
- SanitizerFn[SanitizerFn["IframeAttribute"] = 5] = "IframeAttribute";
9134
- })(SanitizerFn || (SanitizerFn = {}));
9135
9140
  /**
9136
9141
  * Enumeration of the different kinds of `@defer` secondary blocks.
9137
9142
  */
@@ -9191,6 +9196,20 @@ var I18nParamResolutionTime;
9191
9196
  */
9192
9197
  I18nParamResolutionTime[I18nParamResolutionTime["Postproccessing"] = 1] = "Postproccessing";
9193
9198
  })(I18nParamResolutionTime || (I18nParamResolutionTime = {}));
9199
+ /**
9200
+ * The contexts in which an i18n expression can be used.
9201
+ */
9202
+ var I18nExpressionFor;
9203
+ (function (I18nExpressionFor) {
9204
+ /**
9205
+ * This expression is used as a value (i.e. inside an i18n block).
9206
+ */
9207
+ I18nExpressionFor[I18nExpressionFor["I18nText"] = 0] = "I18nText";
9208
+ /**
9209
+ * This expression is used in a binding.
9210
+ */
9211
+ I18nExpressionFor[I18nExpressionFor["I18nAttribute"] = 1] = "I18nAttribute";
9212
+ })(I18nExpressionFor || (I18nExpressionFor = {}));
9194
9213
  /**
9195
9214
  * Flags that describe what an i18n param value. These determine how the value is serialized into
9196
9215
  * the final map.
@@ -9257,7 +9276,14 @@ var I18nContextKind;
9257
9276
  (function (I18nContextKind) {
9258
9277
  I18nContextKind[I18nContextKind["RootI18n"] = 0] = "RootI18n";
9259
9278
  I18nContextKind[I18nContextKind["Icu"] = 1] = "Icu";
9279
+ I18nContextKind[I18nContextKind["Attr"] = 2] = "Attr";
9260
9280
  })(I18nContextKind || (I18nContextKind = {}));
9281
+ var TemplateKind;
9282
+ (function (TemplateKind) {
9283
+ TemplateKind[TemplateKind["NgTemplate"] = 0] = "NgTemplate";
9284
+ TemplateKind[TemplateKind["Structural"] = 1] = "Structural";
9285
+ TemplateKind[TemplateKind["Block"] = 2] = "Block";
9286
+ })(TemplateKind || (TemplateKind = {}));
9261
9287
 
9262
9288
  /**
9263
9289
  * Marker symbol for `ConsumesSlotOpTrait`.
@@ -9365,12 +9391,11 @@ const NEW_OP = {
9365
9391
  /**
9366
9392
  * Create an `InterpolationTextOp`.
9367
9393
  */
9368
- function createInterpolateTextOp(xref, interpolation, i18nPlaceholders, sourceSpan) {
9394
+ function createInterpolateTextOp(xref, interpolation, sourceSpan) {
9369
9395
  return {
9370
9396
  kind: OpKind.InterpolateText,
9371
9397
  target: xref,
9372
9398
  interpolation,
9373
- i18nPlaceholders,
9374
9399
  sourceSpan,
9375
9400
  ...TRAIT_DEPENDS_ON_SLOT_CONTEXT,
9376
9401
  ...TRAIT_CONSUMES_VARS,
@@ -9378,15 +9403,19 @@ function createInterpolateTextOp(xref, interpolation, i18nPlaceholders, sourceSp
9378
9403
  };
9379
9404
  }
9380
9405
  class Interpolation {
9381
- constructor(strings, expressions) {
9406
+ constructor(strings, expressions, i18nPlaceholders) {
9382
9407
  this.strings = strings;
9383
9408
  this.expressions = expressions;
9409
+ this.i18nPlaceholders = i18nPlaceholders;
9410
+ if (i18nPlaceholders.length !== 0 && i18nPlaceholders.length !== expressions.length) {
9411
+ throw new Error(`Expected ${expressions.length} placeholders to match interpolation expression count, but got ${i18nPlaceholders.length}`);
9412
+ }
9384
9413
  }
9385
9414
  }
9386
9415
  /**
9387
9416
  * Create a `BindingOp`, not yet transformed into a particular type of binding.
9388
9417
  */
9389
- function createBindingOp(target, kind, name, expression, unit, securityContext, isTextAttribute, isTemplate, sourceSpan) {
9418
+ function createBindingOp(target, kind, name, expression, unit, securityContext, isTextAttribute, isStructuralTemplateAttribute, templateKind, i18nMessage, sourceSpan) {
9390
9419
  return {
9391
9420
  kind: OpKind.Binding,
9392
9421
  bindingKind: kind,
@@ -9396,7 +9425,10 @@ function createBindingOp(target, kind, name, expression, unit, securityContext,
9396
9425
  unit,
9397
9426
  securityContext,
9398
9427
  isTextAttribute,
9399
- isTemplate,
9428
+ isStructuralTemplateAttribute,
9429
+ templateKind,
9430
+ i18nContext: null,
9431
+ i18nMessage,
9400
9432
  sourceSpan,
9401
9433
  ...NEW_OP,
9402
9434
  };
@@ -9404,7 +9436,7 @@ function createBindingOp(target, kind, name, expression, unit, securityContext,
9404
9436
  /**
9405
9437
  * Create a `PropertyOp`.
9406
9438
  */
9407
- function createPropertyOp(target, name, expression, isAnimationTrigger, securityContext, isTemplate, sourceSpan) {
9439
+ function createPropertyOp(target, name, expression, isAnimationTrigger, securityContext, isStructuralTemplateAttribute, templateKind, i18nContext, i18nMessage, sourceSpan) {
9408
9440
  return {
9409
9441
  kind: OpKind.Property,
9410
9442
  target,
@@ -9413,7 +9445,10 @@ function createPropertyOp(target, name, expression, isAnimationTrigger, security
9413
9445
  isAnimationTrigger,
9414
9446
  securityContext,
9415
9447
  sanitizer: null,
9416
- isTemplate,
9448
+ isStructuralTemplateAttribute,
9449
+ templateKind,
9450
+ i18nContext,
9451
+ i18nMessage,
9417
9452
  sourceSpan,
9418
9453
  ...TRAIT_DEPENDS_ON_SLOT_CONTEXT,
9419
9454
  ...TRAIT_CONSUMES_VARS,
@@ -9478,7 +9513,7 @@ function createClassMapOp(xref, expression, sourceSpan) {
9478
9513
  /**
9479
9514
  * Create an `AttributeOp`.
9480
9515
  */
9481
- function createAttributeOp(target, name, expression, securityContext, isTextAttribute, isTemplate, sourceSpan) {
9516
+ function createAttributeOp(target, name, expression, securityContext, isTextAttribute, isStructuralTemplateAttribute, templateKind, i18nMessage, sourceSpan) {
9482
9517
  return {
9483
9518
  kind: OpKind.Attribute,
9484
9519
  target,
@@ -9487,7 +9522,10 @@ function createAttributeOp(target, name, expression, securityContext, isTextAttr
9487
9522
  securityContext,
9488
9523
  sanitizer: null,
9489
9524
  isTextAttribute,
9490
- isTemplate,
9525
+ isStructuralTemplateAttribute,
9526
+ templateKind,
9527
+ i18nContext: null,
9528
+ i18nMessage,
9491
9529
  sourceSpan,
9492
9530
  ...TRAIT_DEPENDS_ON_SLOT_CONTEXT,
9493
9531
  ...TRAIT_CONSUMES_VARS,
@@ -9548,15 +9586,18 @@ function createDeferWhenOp(target, expr, prefetch, sourceSpan) {
9548
9586
  /**
9549
9587
  * Create an i18n expression op.
9550
9588
  */
9551
- function createI18nExpressionOp(context, target, handle, expression, i18nPlaceholder, resolutionTime, sourceSpan) {
9589
+ function createI18nExpressionOp(context, target, i18nOwner, handle, expression, i18nPlaceholder, resolutionTime, usage, name, sourceSpan) {
9552
9590
  return {
9553
9591
  kind: OpKind.I18nExpression,
9554
9592
  context,
9555
9593
  target,
9594
+ i18nOwner,
9556
9595
  handle,
9557
9596
  expression,
9558
9597
  i18nPlaceholder,
9559
9598
  resolutionTime,
9599
+ usage,
9600
+ name,
9560
9601
  sourceSpan,
9561
9602
  ...NEW_OP,
9562
9603
  ...TRAIT_CONSUMES_VARS,
@@ -9564,12 +9605,12 @@ function createI18nExpressionOp(context, target, handle, expression, i18nPlaceho
9564
9605
  };
9565
9606
  }
9566
9607
  /**
9567
- *Creates an op to apply i18n expression ops
9608
+ * Creates an op to apply i18n expression ops.
9568
9609
  */
9569
- function createI18nApplyOp(target, handle, sourceSpan) {
9610
+ function createI18nApplyOp(owner, handle, sourceSpan) {
9570
9611
  return {
9571
9612
  kind: OpKind.I18nApply,
9572
- target,
9613
+ owner,
9573
9614
  handle,
9574
9615
  sourceSpan,
9575
9616
  ...NEW_OP,
@@ -10118,24 +10159,6 @@ class ReadTemporaryExpr extends ExpressionBase {
10118
10159
  return r;
10119
10160
  }
10120
10161
  }
10121
- class SanitizerExpr extends ExpressionBase {
10122
- constructor(fn) {
10123
- super();
10124
- this.fn = fn;
10125
- this.kind = ExpressionKind.SanitizerExpr;
10126
- }
10127
- visitExpression(visitor, context) { }
10128
- isEquivalent(e) {
10129
- return e instanceof SanitizerExpr && e.fn === this.fn;
10130
- }
10131
- isConstant() {
10132
- return true;
10133
- }
10134
- clone() {
10135
- return new SanitizerExpr(this.fn);
10136
- }
10137
- transformInternalExpressions() { }
10138
- }
10139
10162
  class SlotLiteralExpr extends ExpressionBase {
10140
10163
  constructor(slot) {
10141
10164
  super();
@@ -10266,7 +10289,6 @@ function transformExpressionsInOp(op, transform, flags) {
10266
10289
  case OpKind.ClassProp:
10267
10290
  case OpKind.ClassMap:
10268
10291
  case OpKind.Binding:
10269
- case OpKind.HostProperty:
10270
10292
  if (op.expression instanceof Interpolation) {
10271
10293
  transformExpressionsInInterpolation(op.expression, transform, flags);
10272
10294
  }
@@ -10275,6 +10297,7 @@ function transformExpressionsInOp(op, transform, flags) {
10275
10297
  }
10276
10298
  break;
10277
10299
  case OpKind.Property:
10300
+ case OpKind.HostProperty:
10278
10301
  case OpKind.Attribute:
10279
10302
  if (op.expression instanceof Interpolation) {
10280
10303
  transformExpressionsInInterpolation(op.expression, transform, flags);
@@ -10320,6 +10343,8 @@ function transformExpressionsInOp(op, transform, flags) {
10320
10343
  case OpKind.ExtractedAttribute:
10321
10344
  op.expression =
10322
10345
  op.expression && transformExpressionsInExpression(op.expression, transform, flags);
10346
+ op.trustedValueFn = op.trustedValueFn &&
10347
+ transformExpressionsInExpression(op.trustedValueFn, transform, flags);
10323
10348
  break;
10324
10349
  case OpKind.RepeaterCreate:
10325
10350
  op.track = transformExpressionsInExpression(op.track, transform, flags);
@@ -10373,6 +10398,7 @@ function transformExpressionsInOp(op, transform, flags) {
10373
10398
  case OpKind.ProjectionDef:
10374
10399
  case OpKind.Template:
10375
10400
  case OpKind.Text:
10401
+ case OpKind.I18nAttributes:
10376
10402
  // These operations contain no expressions.
10377
10403
  break;
10378
10404
  default:
@@ -10393,6 +10419,9 @@ function transformExpressionsInExpression(expr, transform, flags) {
10393
10419
  expr.lhs = transformExpressionsInExpression(expr.lhs, transform, flags);
10394
10420
  expr.rhs = transformExpressionsInExpression(expr.rhs, transform, flags);
10395
10421
  }
10422
+ else if (expr instanceof UnaryOperatorExpr) {
10423
+ expr.expr = transformExpressionsInExpression(expr.expr, transform, flags);
10424
+ }
10396
10425
  else if (expr instanceof ReadPropExpr) {
10397
10426
  expr.receiver = transformExpressionsInExpression(expr.receiver, transform, flags);
10398
10427
  }
@@ -10444,6 +10473,17 @@ function transformExpressionsInExpression(expr, transform, flags) {
10444
10473
  expr.expressions[i] = transformExpressionsInExpression(expr.expressions[i], transform, flags);
10445
10474
  }
10446
10475
  }
10476
+ else if (expr instanceof NotExpr) {
10477
+ expr.condition = transformExpressionsInExpression(expr.condition, transform, flags);
10478
+ }
10479
+ else if (expr instanceof TaggedTemplateExpr) {
10480
+ expr.tag = transformExpressionsInExpression(expr.tag, transform, flags);
10481
+ expr.template.expressions =
10482
+ expr.template.expressions.map(e => transformExpressionsInExpression(e, transform, flags));
10483
+ }
10484
+ else if (expr instanceof WrappedNodeExpr) {
10485
+ // TODO: Do we need to transform any TS nodes nested inside of this expression?
10486
+ }
10447
10487
  else if (expr instanceof ReadVarExpr || expr instanceof ExternalExpr ||
10448
10488
  expr instanceof LiteralExpr) {
10449
10489
  // No action for these types.
@@ -10785,10 +10825,11 @@ function createElementStartOp(tag, xref, namespace, i18nPlaceholder, sourceSpan)
10785
10825
  /**
10786
10826
  * Create a `TemplateOp`.
10787
10827
  */
10788
- function createTemplateOp(xref, tag, functionNameSuffix, namespace, i18nPlaceholder, sourceSpan) {
10828
+ function createTemplateOp(xref, templateKind, tag, functionNameSuffix, namespace, i18nPlaceholder, sourceSpan) {
10789
10829
  return {
10790
10830
  kind: OpKind.Template,
10791
10831
  xref,
10832
+ templateKind,
10792
10833
  attributes: null,
10793
10834
  tag,
10794
10835
  handle: new SlotHandle(),
@@ -10804,7 +10845,7 @@ function createTemplateOp(xref, tag, functionNameSuffix, namespace, i18nPlacehol
10804
10845
  ...NEW_OP,
10805
10846
  };
10806
10847
  }
10807
- function createRepeaterCreateOp(primaryView, emptyView, tag, track, varNames, sourceSpan) {
10848
+ function createRepeaterCreateOp(primaryView, emptyView, tag, track, varNames, i18nPlaceholder, emptyI18nPlaceholder, sourceSpan) {
10808
10849
  return {
10809
10850
  kind: OpKind.RepeaterCreate,
10810
10851
  attributes: null,
@@ -10822,6 +10863,8 @@ function createRepeaterCreateOp(primaryView, emptyView, tag, track, varNames, so
10822
10863
  vars: null,
10823
10864
  varNames,
10824
10865
  usesComponentInstance: false,
10866
+ i18nPlaceholder,
10867
+ emptyI18nPlaceholder,
10825
10868
  sourceSpan,
10826
10869
  ...TRAIT_CONSUMES_SLOT,
10827
10870
  ...NEW_OP,
@@ -10870,7 +10913,9 @@ function createTextOp(xref, initialValue, sourceSpan) {
10870
10913
  /**
10871
10914
  * Create a `ListenerOp`. Host bindings reuse all the listener logic.
10872
10915
  */
10873
- function createListenerOp(target, targetSlot, name, tag, animationPhase, hostListener, sourceSpan) {
10916
+ function createListenerOp(target, targetSlot, name, tag, handlerOps, animationPhase, eventTarget, hostListener, sourceSpan) {
10917
+ const handlerList = new OpList();
10918
+ handlerList.push(handlerOps);
10874
10919
  return {
10875
10920
  kind: OpKind.Listener,
10876
10921
  target,
@@ -10878,11 +10923,12 @@ function createListenerOp(target, targetSlot, name, tag, animationPhase, hostLis
10878
10923
  tag,
10879
10924
  hostListener,
10880
10925
  name,
10881
- handlerOps: new OpList(),
10926
+ handlerOps: handlerList,
10882
10927
  handlerFnName: null,
10883
10928
  consumesDollarEvent: false,
10884
10929
  isAnimationListener: animationPhase !== null,
10885
- animationPhase: animationPhase,
10930
+ animationPhase,
10931
+ eventTarget,
10886
10932
  sourceSpan,
10887
10933
  ...NEW_OP,
10888
10934
  };
@@ -10911,14 +10957,15 @@ function createProjectionDefOp(def) {
10911
10957
  ...NEW_OP,
10912
10958
  };
10913
10959
  }
10914
- function createProjectionOp(xref, selector, sourceSpan) {
10960
+ function createProjectionOp(xref, selector, i18nPlaceholder, attributes, sourceSpan) {
10915
10961
  return {
10916
10962
  kind: OpKind.Projection,
10917
10963
  xref,
10918
10964
  handle: new SlotHandle(),
10919
10965
  selector,
10966
+ i18nPlaceholder,
10920
10967
  projectionSlotIndex: 0,
10921
- attributes: [],
10968
+ attributes,
10922
10969
  localRefs: [],
10923
10970
  sourceSpan,
10924
10971
  ...NEW_OP,
@@ -10928,13 +10975,17 @@ function createProjectionOp(xref, selector, sourceSpan) {
10928
10975
  /**
10929
10976
  * Create an `ExtractedAttributeOp`.
10930
10977
  */
10931
- function createExtractedAttributeOp(target, bindingKind, name, expression) {
10978
+ function createExtractedAttributeOp(target, bindingKind, name, expression, i18nContext, i18nMessage, securityContext) {
10932
10979
  return {
10933
10980
  kind: OpKind.ExtractedAttribute,
10934
10981
  target,
10935
10982
  bindingKind,
10936
10983
  name,
10937
10984
  expression,
10985
+ i18nContext,
10986
+ i18nMessage,
10987
+ securityContext,
10988
+ trustedValueFn: null,
10938
10989
  ...NEW_OP,
10939
10990
  };
10940
10991
  }
@@ -10977,10 +11028,11 @@ function createDeferOnOp(defer, trigger, prefetch, sourceSpan) {
10977
11028
  /**
10978
11029
  * Create an `ExtractedMessageOp`.
10979
11030
  */
10980
- function createI18nMessageOp(xref, i18nBlock, message, messagePlaceholder, params, postprocessingParams, needsPostprocessing) {
11031
+ function createI18nMessageOp(xref, i18nContext, i18nBlock, message, messagePlaceholder, params, postprocessingParams, needsPostprocessing) {
10981
11032
  return {
10982
11033
  kind: OpKind.I18nMessage,
10983
11034
  xref,
11035
+ i18nContext,
10984
11036
  i18nBlock,
10985
11037
  message,
10986
11038
  messagePlaceholder,
@@ -11043,6 +11095,9 @@ function createIcuEndOp(xref) {
11043
11095
  };
11044
11096
  }
11045
11097
  function createI18nContextOp(contextKind, xref, i18nBlock, message, sourceSpan) {
11098
+ if (i18nBlock === null && contextKind !== I18nContextKind.Attr) {
11099
+ throw new Error('AssertionError: i18nBlock must be provided for non-attribute contexts.');
11100
+ }
11046
11101
  return {
11047
11102
  kind: OpKind.I18nContext,
11048
11103
  contextKind,
@@ -11055,6 +11110,17 @@ function createI18nContextOp(contextKind, xref, i18nBlock, message, sourceSpan)
11055
11110
  ...NEW_OP,
11056
11111
  };
11057
11112
  }
11113
+ function createI18nAttributesOp(xref, handle, target) {
11114
+ return {
11115
+ kind: OpKind.I18nAttributes,
11116
+ xref,
11117
+ handle,
11118
+ target,
11119
+ i18nAttributesConfig: null,
11120
+ ...NEW_OP,
11121
+ ...TRAIT_CONSUMES_SLOT,
11122
+ };
11123
+ }
11058
11124
  function literalOrArrayLiteral$1(value) {
11059
11125
  if (Array.isArray(value)) {
11060
11126
  return literalArr(value.map(literalOrArrayLiteral$1));
@@ -11062,12 +11128,15 @@ function literalOrArrayLiteral$1(value) {
11062
11128
  return literal(value, INFERRED_TYPE);
11063
11129
  }
11064
11130
 
11065
- function createHostPropertyOp(name, expression, isAnimationTrigger, sourceSpan) {
11131
+ function createHostPropertyOp(name, expression, isAnimationTrigger, i18nContext, securityContext, sourceSpan) {
11066
11132
  return {
11067
11133
  kind: OpKind.HostProperty,
11068
11134
  name,
11069
11135
  expression,
11070
11136
  isAnimationTrigger,
11137
+ i18nContext,
11138
+ securityContext,
11139
+ sanitizer: null,
11071
11140
  sourceSpan,
11072
11141
  ...TRAIT_CONSUMES_VARS,
11073
11142
  ...NEW_OP,
@@ -11304,7 +11373,7 @@ function applyI18nExpressions(job) {
11304
11373
  // Only add apply after expressions that are not followed by more expressions.
11305
11374
  if (op.kind === OpKind.I18nExpression && needsApplication(i18nContexts, op)) {
11306
11375
  // TODO: what should be the source span for the apply op?
11307
- OpList.insertAfter(createI18nApplyOp(op.target, op.handle, null), op);
11376
+ OpList.insertAfter(createI18nApplyOp(op.i18nOwner, op.handle, null), op);
11308
11377
  }
11309
11378
  }
11310
11379
  }
@@ -11317,42 +11386,80 @@ function needsApplication(i18nContexts, op) {
11317
11386
  if (op.next?.kind !== OpKind.I18nExpression) {
11318
11387
  return true;
11319
11388
  }
11320
- // If the next op is an expression targeting a different i18n block, we need to apply.
11321
11389
  const context = i18nContexts.get(op.context);
11322
11390
  const nextContext = i18nContexts.get(op.next.context);
11323
- if (context.i18nBlock !== nextContext.i18nBlock) {
11391
+ if (context === undefined) {
11392
+ throw new Error('AssertionError: expected an I18nContextOp to exist for the I18nExpressionOp\'s context');
11393
+ }
11394
+ if (nextContext === undefined) {
11395
+ throw new Error('AssertionError: expected an I18nContextOp to exist for the next I18nExpressionOp\'s context');
11396
+ }
11397
+ // If the next op is an expression targeting a different i18n block (or different element, in the
11398
+ // case of i18n attributes), we need to apply.
11399
+ // First, handle the case of i18n blocks.
11400
+ if (context.i18nBlock !== null) {
11401
+ // This is a block context. Compare the blocks.
11402
+ if (context.i18nBlock !== nextContext.i18nBlock) {
11403
+ return true;
11404
+ }
11405
+ return false;
11406
+ }
11407
+ // Second, handle the case of i18n attributes.
11408
+ if (op.i18nOwner !== op.next.i18nOwner) {
11324
11409
  return true;
11325
11410
  }
11326
11411
  return false;
11327
11412
  }
11328
11413
 
11329
11414
  /**
11330
- * Updates i18n expression ops to depend on the last slot in their owning i18n block.
11415
+ * Updates i18n expression ops to target the last slot in their owning i18n block, and moves them
11416
+ * after the last update instruction that depends on that slot.
11331
11417
  */
11332
11418
  function assignI18nSlotDependencies(job) {
11333
- const i18nLastSlotConsumers = new Map();
11334
- let lastSlotConsumer = null;
11335
- let currentI18nOp = null;
11336
11419
  for (const unit of job.units) {
11337
- // Record the last consumed slot before each i18n end instruction.
11338
- for (const op of unit.create) {
11339
- if (hasConsumesSlotTrait(op)) {
11340
- lastSlotConsumer = op.xref;
11420
+ // The first update op.
11421
+ let updateOp = unit.update.head;
11422
+ // I18n expressions currently being moved during the iteration.
11423
+ let i18nExpressionsInProgress = [];
11424
+ // Non-null while we are iterating through an i18nStart/i18nEnd pair
11425
+ let state = null;
11426
+ for (const createOp of unit.create) {
11427
+ if (createOp.kind === OpKind.I18nStart) {
11428
+ state = {
11429
+ blockXref: createOp.xref,
11430
+ lastSlotConsumer: createOp.xref,
11431
+ };
11341
11432
  }
11342
- switch (op.kind) {
11343
- case OpKind.I18nStart:
11344
- currentI18nOp = op;
11345
- break;
11346
- case OpKind.I18nEnd:
11347
- i18nLastSlotConsumers.set(currentI18nOp.xref, lastSlotConsumer);
11348
- currentI18nOp = null;
11349
- break;
11433
+ else if (createOp.kind === OpKind.I18nEnd) {
11434
+ for (const op of i18nExpressionsInProgress) {
11435
+ op.target = state.lastSlotConsumer;
11436
+ OpList.insertBefore(op, updateOp);
11437
+ }
11438
+ i18nExpressionsInProgress.length = 0;
11439
+ state = null;
11350
11440
  }
11351
- }
11352
- // Assign i18n expressions to target the last slot in its owning block.
11353
- for (const op of unit.update) {
11354
- if (op.kind === OpKind.I18nExpression) {
11355
- op.target = i18nLastSlotConsumers.get(op.target);
11441
+ if (hasConsumesSlotTrait(createOp)) {
11442
+ if (state !== null) {
11443
+ state.lastSlotConsumer = createOp.xref;
11444
+ }
11445
+ while (true) {
11446
+ if (updateOp.next === null) {
11447
+ break;
11448
+ }
11449
+ if (state !== null && updateOp.kind === OpKind.I18nExpression &&
11450
+ updateOp.usage === I18nExpressionFor.I18nText &&
11451
+ updateOp.i18nOwner === state.blockXref) {
11452
+ const opToRemove = updateOp;
11453
+ updateOp = updateOp.next;
11454
+ OpList.remove(opToRemove);
11455
+ i18nExpressionsInProgress.push(opToRemove);
11456
+ continue;
11457
+ }
11458
+ if (hasDependsOnSlotContextTrait(updateOp) && updateOp.target !== createOp.xref) {
11459
+ break;
11460
+ }
11461
+ updateOp = updateOp.next;
11462
+ }
11356
11463
  }
11357
11464
  }
11358
11465
  }
@@ -11386,22 +11493,42 @@ function extractAttributes(job) {
11386
11493
  break;
11387
11494
  case OpKind.Property:
11388
11495
  if (!op.isAnimationTrigger) {
11389
- OpList.insertBefore(createExtractedAttributeOp(op.target, op.isTemplate ? BindingKind.Template : BindingKind.Property, op.name, null), lookupElement$2(elements, op.target));
11496
+ let bindingKind;
11497
+ if (op.i18nMessage !== null && op.templateKind === null) {
11498
+ // If the binding has an i18n context, it is an i18n attribute, and should have that
11499
+ // kind in the consts array.
11500
+ bindingKind = BindingKind.I18n;
11501
+ }
11502
+ else if (op.isStructuralTemplateAttribute) {
11503
+ bindingKind = BindingKind.Template;
11504
+ }
11505
+ else {
11506
+ bindingKind = BindingKind.Property;
11507
+ }
11508
+ OpList.insertBefore(
11509
+ // Deliberaly null i18nMessage value
11510
+ createExtractedAttributeOp(op.target, bindingKind, op.name, /* expression */ null, /* i18nContext */ null,
11511
+ /* i18nMessage */ null, op.securityContext), lookupElement$2(elements, op.target));
11390
11512
  }
11391
11513
  break;
11392
11514
  case OpKind.StyleProp:
11393
11515
  case OpKind.ClassProp:
11516
+ // TODO: Can style or class bindings be i18n attributes?
11394
11517
  // The old compiler treated empty style bindings as regular bindings for the purpose of
11395
11518
  // directive matching. That behavior is incorrect, but we emulate it in compatibility
11396
11519
  // mode.
11397
11520
  if (unit.job.compatibility === CompatibilityMode.TemplateDefinitionBuilder &&
11398
11521
  op.expression instanceof EmptyExpr) {
11399
- OpList.insertBefore(createExtractedAttributeOp(op.target, BindingKind.Property, op.name, null), lookupElement$2(elements, op.target));
11522
+ OpList.insertBefore(createExtractedAttributeOp(op.target, BindingKind.Property, op.name, /* expression */ null,
11523
+ /* i18nContext */ null,
11524
+ /* i18nMessage */ null, SecurityContext.STYLE), lookupElement$2(elements, op.target));
11400
11525
  }
11401
11526
  break;
11402
11527
  case OpKind.Listener:
11403
11528
  if (!op.isAnimationListener) {
11404
- const extractedAttributeOp = createExtractedAttributeOp(op.target, BindingKind.Property, op.name, null);
11529
+ const extractedAttributeOp = createExtractedAttributeOp(op.target, BindingKind.Property, op.name, /* expression */ null,
11530
+ /* i18nContext */ null,
11531
+ /* i18nMessage */ null, SecurityContext.NONE);
11405
11532
  if (job.kind === CompilationJobKind.Host) {
11406
11533
  // This attribute will apply to the enclosing host binding compilation unit, so order
11407
11534
  // doesn't matter.
@@ -11450,7 +11577,7 @@ function extractAttributeOp(unit, op, elements) {
11450
11577
  }
11451
11578
  }
11452
11579
  if (extractable) {
11453
- const extractedAttributeOp = createExtractedAttributeOp(op.target, op.isTemplate ? BindingKind.Template : BindingKind.Attribute, op.name, op.expression);
11580
+ const extractedAttributeOp = createExtractedAttributeOp(op.target, op.isStructuralTemplateAttribute ? BindingKind.Template : BindingKind.Attribute, op.name, op.expression, op.i18nContext, op.i18nMessage, op.securityContext);
11454
11581
  if (unit.job.kind === CompilationJobKind.Host) {
11455
11582
  // This attribute will apply to the enclosing host binding compilation unit, so order doesn't
11456
11583
  // matter.
@@ -11497,16 +11624,16 @@ function specializeBindings(job) {
11497
11624
  target.nonBindable = true;
11498
11625
  }
11499
11626
  else {
11500
- OpList.replace(op, createAttributeOp(op.target, op.name, op.expression, op.securityContext, op.isTextAttribute, op.isTemplate, op.sourceSpan));
11627
+ OpList.replace(op, createAttributeOp(op.target, op.name, op.expression, op.securityContext, op.isTextAttribute, op.isStructuralTemplateAttribute, op.templateKind, op.i18nMessage, op.sourceSpan));
11501
11628
  }
11502
11629
  break;
11503
11630
  case BindingKind.Property:
11504
11631
  case BindingKind.Animation:
11505
11632
  if (job.kind === CompilationJobKind.Host) {
11506
- OpList.replace(op, createHostPropertyOp(op.name, op.expression, op.bindingKind === BindingKind.Animation, op.sourceSpan));
11633
+ OpList.replace(op, createHostPropertyOp(op.name, op.expression, op.bindingKind === BindingKind.Animation, op.i18nContext, op.securityContext, op.sourceSpan));
11507
11634
  }
11508
11635
  else {
11509
- OpList.replace(op, createPropertyOp(op.target, op.name, op.expression, op.bindingKind === BindingKind.Animation, op.securityContext, op.isTemplate, op.sourceSpan));
11636
+ 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));
11510
11637
  }
11511
11638
  break;
11512
11639
  case BindingKind.I18n:
@@ -11743,7 +11870,7 @@ function collectElementConsts(job) {
11743
11870
  if (op.kind === OpKind.ExtractedAttribute) {
11744
11871
  const attributes = allElementAttributes.get(op.target) || new ElementAttributes();
11745
11872
  allElementAttributes.set(op.target, attributes);
11746
- attributes.add(op.bindingKind, op.name, op.expression);
11873
+ attributes.add(op.bindingKind, op.name, op.expression, op.trustedValueFn);
11747
11874
  OpList.remove(op);
11748
11875
  }
11749
11876
  }
@@ -11809,11 +11936,12 @@ class ElementAttributes {
11809
11936
  get i18n() {
11810
11937
  return this.byKind.get(BindingKind.I18n) ?? FLYWEIGHT_ARRAY;
11811
11938
  }
11812
- add(kind, name, value) {
11939
+ add(kind, name, value, trustedValueFn) {
11813
11940
  if (this.known.has(name)) {
11814
11941
  return;
11815
11942
  }
11816
11943
  this.known.add(name);
11944
+ // TODO: Can this be its own phase
11817
11945
  if (name === 'ngProjectAs') {
11818
11946
  if (value === null || !(value instanceof LiteralExpr) || (value.value == null) ||
11819
11947
  (typeof value.value?.toString() !== 'string')) {
@@ -11827,9 +11955,17 @@ class ElementAttributes {
11827
11955
  array.push(...getAttributeNameLiterals$1(name));
11828
11956
  if (kind === BindingKind.Attribute || kind === BindingKind.StyleProperty) {
11829
11957
  if (value === null) {
11830
- throw Error('Attribute & style element attributes must have a value');
11958
+ throw Error('Attribute, i18n attribute, & style element attributes must have a value');
11959
+ }
11960
+ if (trustedValueFn !== null) {
11961
+ if (!isStringLiteral(value)) {
11962
+ throw Error('AssertionError: extracted attribute value should be string literal');
11963
+ }
11964
+ array.push(taggedTemplate(trustedValueFn, new TemplateLiteral([new TemplateLiteralElement(value.value)], []), undefined, value.sourceSpan));
11965
+ }
11966
+ else {
11967
+ array.push(value);
11831
11968
  }
11832
- array.push(value);
11833
11969
  }
11834
11970
  }
11835
11971
  arrayFor(kind) {
@@ -11881,6 +12017,50 @@ function serializeAttributes({ attributes, bindings, classes, i18n, projectAs, s
11881
12017
  return literalArr(attrArray);
11882
12018
  }
11883
12019
 
12020
+ /**
12021
+ * Some binding instructions in the update block may actually correspond to i18n bindings. In that
12022
+ * case, they should be replaced with i18nExp instructions for the dynamic portions.
12023
+ */
12024
+ function convertI18nBindings(job) {
12025
+ const i18nAttributesByElem = new Map();
12026
+ for (const unit of job.units) {
12027
+ for (const op of unit.create) {
12028
+ if (op.kind === OpKind.I18nAttributes) {
12029
+ i18nAttributesByElem.set(op.target, op);
12030
+ }
12031
+ }
12032
+ for (const op of unit.update) {
12033
+ switch (op.kind) {
12034
+ case OpKind.Property:
12035
+ case OpKind.Attribute:
12036
+ if (op.i18nContext === null) {
12037
+ continue;
12038
+ }
12039
+ if (!(op.expression instanceof Interpolation)) {
12040
+ continue;
12041
+ }
12042
+ const i18nAttributesForElem = i18nAttributesByElem.get(op.target);
12043
+ if (i18nAttributesForElem === undefined) {
12044
+ throw new Error('AssertionError: An i18n attribute binding instruction requires the owning element to have an I18nAttributes create instruction');
12045
+ }
12046
+ if (i18nAttributesForElem.target !== op.target) {
12047
+ throw new Error('AssertionError: Expected i18nAttributes target element to match binding target element');
12048
+ }
12049
+ const ops = [];
12050
+ for (let i = 0; i < op.expression.expressions.length; i++) {
12051
+ const expr = op.expression.expressions[i];
12052
+ if (op.expression.i18nPlaceholders.length !== op.expression.expressions.length) {
12053
+ 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`);
12054
+ }
12055
+ ops.push(createI18nExpressionOp(op.i18nContext, i18nAttributesForElem.target, i18nAttributesForElem.xref, i18nAttributesForElem.handle, expr, op.expression.i18nPlaceholders[i], I18nParamResolutionTime.Creation, I18nExpressionFor.I18nAttribute, op.name, op.sourceSpan));
12056
+ }
12057
+ OpList.replaceWithMany(op, ops);
12058
+ break;
12059
+ }
12060
+ }
12061
+ }
12062
+ }
12063
+
11884
12064
  /**
11885
12065
  * Create extracted deps functions for defer ops.
11886
12066
  */
@@ -11929,6 +12109,9 @@ function createI18nContexts(job) {
11929
12109
  const rootContexts = new Map();
11930
12110
  let currentI18nOp = null;
11931
12111
  let xref;
12112
+ // We use the message instead of the message ID, because placeholder values might differ even
12113
+ // when IDs are the same.
12114
+ const messageToContext = new Map();
11932
12115
  for (const unit of job.units) {
11933
12116
  for (const op of unit.create) {
11934
12117
  switch (op.kind) {
@@ -11966,6 +12149,25 @@ function createI18nContexts(job) {
11966
12149
  break;
11967
12150
  }
11968
12151
  }
12152
+ for (const op of unit.ops()) {
12153
+ switch (op.kind) {
12154
+ case OpKind.Binding:
12155
+ case OpKind.Property:
12156
+ case OpKind.Attribute:
12157
+ case OpKind.ExtractedAttribute:
12158
+ if (!op.i18nMessage) {
12159
+ continue;
12160
+ }
12161
+ if (!messageToContext.has(op.i18nMessage)) {
12162
+ // create the context
12163
+ const i18nContext = job.allocateXrefId();
12164
+ unit.create.push(createI18nContextOp(I18nContextKind.Attr, i18nContext, null, op.i18nMessage, null));
12165
+ messageToContext.set(op.i18nMessage, i18nContext);
12166
+ }
12167
+ op.i18nContext = messageToContext.get(op.i18nMessage);
12168
+ break;
12169
+ }
12170
+ }
11969
12171
  }
11970
12172
  // Assign contexts to child i18n blocks, now that all root i18n blocks have their context
11971
12173
  // assigned.
@@ -12324,7 +12526,7 @@ function ternaryTransform(e) {
12324
12526
  /**
12325
12527
  * The escape sequence used indicate message param values.
12326
12528
  */
12327
- const ESCAPE = '\uFFFD';
12529
+ const ESCAPE$1 = '\uFFFD';
12328
12530
  /**
12329
12531
  * Marker used to indicate an element tag.
12330
12532
  */
@@ -12373,6 +12575,18 @@ function extractI18nMessages(job) {
12373
12575
  }
12374
12576
  }
12375
12577
  }
12578
+ // TODO: Miles and I think that contexts have a 1-to-1 correspondence with extracted messages, so
12579
+ // this phase can probably be simplified.
12580
+ // Extract messages from contexts of i18n attributes.
12581
+ for (const unit of job.units) {
12582
+ for (const op of unit.create) {
12583
+ if (op.kind !== OpKind.I18nContext || op.contextKind !== I18nContextKind.Attr) {
12584
+ continue;
12585
+ }
12586
+ const i18nMessageOp = createI18nMessage(job, op);
12587
+ unit.create.push(i18nMessageOp);
12588
+ }
12589
+ }
12376
12590
  // Extract messages from root i18n blocks.
12377
12591
  const i18nBlockMessages = new Map();
12378
12592
  for (const unit of job.units) {
@@ -12397,6 +12611,9 @@ function extractI18nMessages(job) {
12397
12611
  }
12398
12612
  const i18nContext = i18nContexts.get(op.context);
12399
12613
  if (i18nContext.contextKind === I18nContextKind.Icu) {
12614
+ if (i18nContext.i18nBlock === null) {
12615
+ throw Error('ICU context should have its i18n block set.');
12616
+ }
12400
12617
  const subMessage = createI18nMessage(job, i18nContext, op.messagePlaceholder);
12401
12618
  unit.create.push(subMessage);
12402
12619
  const rootI18nId = i18nBlocks.get(i18nContext.i18nBlock).root;
@@ -12416,119 +12633,86 @@ function extractI18nMessages(job) {
12416
12633
  * Create an i18n message op from an i18n context op.
12417
12634
  */
12418
12635
  function createI18nMessage(job, context, messagePlaceholder) {
12419
- let [formattedParams, needsPostprocessing] = formatParams(context.params);
12420
- const [formattedPostprocessingParams] = formatParams(context.postprocessingParams);
12421
- needsPostprocessing ||= formattedPostprocessingParams.size > 0;
12422
- return createI18nMessageOp(job.allocateXrefId(), context.i18nBlock, context.message, messagePlaceholder ?? null, formattedParams, formattedPostprocessingParams, needsPostprocessing);
12636
+ let formattedParams = formatParams(context.params);
12637
+ const formattedPostprocessingParams = formatParams(context.postprocessingParams);
12638
+ let needsPostprocessing = formattedPostprocessingParams.size > 0;
12639
+ for (const values of context.params.values()) {
12640
+ if (values.length > 1) {
12641
+ needsPostprocessing = true;
12642
+ }
12643
+ }
12644
+ return createI18nMessageOp(job.allocateXrefId(), context.xref, context.i18nBlock, context.message, messagePlaceholder ?? null, formattedParams, formattedPostprocessingParams, needsPostprocessing);
12423
12645
  }
12424
12646
  /**
12425
12647
  * Formats a map of `I18nParamValue[]` values into a map of `Expression` values.
12426
- * @return A tuple of the formatted params and a boolean indicating whether postprocessing is needed
12427
- * for any of the params
12428
12648
  */
12429
12649
  function formatParams(params) {
12430
12650
  const formattedParams = new Map();
12431
- let needsPostprocessing = false;
12432
12651
  for (const [placeholder, placeholderValues] of params) {
12433
- const [serializedValues, paramNeedsPostprocessing] = formatParamValues(placeholderValues);
12434
- needsPostprocessing ||= paramNeedsPostprocessing;
12652
+ const serializedValues = formatParamValues(placeholderValues);
12435
12653
  if (serializedValues !== null) {
12436
12654
  formattedParams.set(placeholder, literal(serializedValues));
12437
12655
  }
12438
12656
  }
12439
- return [formattedParams, needsPostprocessing];
12657
+ return formattedParams;
12440
12658
  }
12441
12659
  /**
12442
12660
  * Formats an `I18nParamValue[]` into a string (or null for empty array).
12443
- * @return A tuple of the formatted value and a boolean indicating whether postprocessing is needed
12444
- * for the value
12445
12661
  */
12446
12662
  function formatParamValues(values) {
12447
12663
  if (values.length === 0) {
12448
- return [null, false];
12664
+ return null;
12449
12665
  }
12450
- collapseElementTemplatePairs(values);
12451
12666
  const serializedValues = values.map(value => formatValue(value));
12452
12667
  return serializedValues.length === 1 ?
12453
- [serializedValues[0], false] :
12454
- [`${LIST_START_MARKER}${serializedValues.join(LIST_DELIMITER)}${LIST_END_MARKER}`, true];
12455
- }
12456
- /**
12457
- * Collapses element/template pairs that refer to the same subTemplateIndex, i.e. elements and
12458
- * templates that refer to the same element instance.
12459
- *
12460
- * This accounts for the case of a structural directive inside an i18n block, e.g.:
12461
- * ```
12462
- * <div i18n>
12463
- * <div *ngIf="condition">
12464
- * </div>
12465
- * ```
12466
- *
12467
- * In this case, both the element start and template start placeholders are the same,
12468
- * and we collapse them down into a single compound placeholder value. Rather than produce
12469
- * `[\uFFFD#1:1\uFFFD|\uFFFD*2:1\uFFFD]`, we want to produce `\uFFFD#1:1\uFFFD\uFFFD*2:1\uFFFD`,
12470
- * likewise for the closing of the element/template.
12471
- */
12472
- function collapseElementTemplatePairs(values) {
12473
- // Record the indicies of element and template values in the values array by subTemplateIndex.
12474
- const valueIndiciesBySubTemplateIndex = new Map();
12475
- for (let i = 0; i < values.length; i++) {
12476
- const value = values[i];
12477
- if (value.subTemplateIndex !== null &&
12478
- (value.flags & (I18nParamValueFlags.ElementTag | I18nParamValueFlags.TemplateTag))) {
12479
- const valueIndicies = valueIndiciesBySubTemplateIndex.get(value.subTemplateIndex) ?? [];
12480
- valueIndicies.push(i);
12481
- valueIndiciesBySubTemplateIndex.set(value.subTemplateIndex, valueIndicies);
12482
- }
12483
- }
12484
- // For each subTemplateIndex, check if any values can be collapsed.
12485
- for (const [subTemplateIndex, valueIndicies] of valueIndiciesBySubTemplateIndex) {
12486
- if (valueIndicies.length > 1) {
12487
- const elementIndex = valueIndicies.find(index => values[index].flags & I18nParamValueFlags.ElementTag);
12488
- const templateIndex = valueIndicies.find(index => values[index].flags & I18nParamValueFlags.TemplateTag);
12489
- // If the values list contains both an element and template value, we can collapse.
12490
- if (elementIndex !== undefined && templateIndex !== undefined) {
12491
- const elementValue = values[elementIndex];
12492
- const templateValue = values[templateIndex];
12493
- // To match the TemplateDefinitionBuilder output, flip the order depending on whether the
12494
- // values represent a closing or opening tag (or both).
12495
- // TODO(mmalerba): Figure out if this makes a difference in terms of either functionality,
12496
- // or the resulting message ID. If not, we can remove the special-casing in the future.
12497
- let compundValue;
12498
- if ((elementValue.flags & I18nParamValueFlags.OpenTag) &&
12499
- (elementValue.flags & I18nParamValueFlags.CloseTag)) {
12500
- // TODO(mmalerba): Is this a TDB bug? I don't understand why it would put the template
12501
- // value twice.
12502
- compundValue = `${formatValue(templateValue)}${formatValue(elementValue)}${formatValue(templateValue)}`;
12503
- }
12504
- else if (elementValue.flags & I18nParamValueFlags.OpenTag) {
12505
- compundValue = `${formatValue(templateValue)}${formatValue(elementValue)}`;
12506
- }
12507
- else {
12508
- compundValue = `${formatValue(elementValue)}${formatValue(templateValue)}`;
12509
- }
12510
- // Replace the element value with the combined value.
12511
- values.splice(elementIndex, 1, { value: compundValue, subTemplateIndex, flags: I18nParamValueFlags.None });
12512
- // Replace the template value with null to preserve the indicies we calculated earlier.
12513
- values.splice(templateIndex, 1, null);
12514
- }
12515
- }
12516
- }
12517
- // Strip out any nulled out values we introduced above.
12518
- for (let i = values.length - 1; i >= 0; i--) {
12519
- if (values[i] === null) {
12520
- values.splice(i, 1);
12521
- }
12522
- }
12668
+ serializedValues[0] :
12669
+ `${LIST_START_MARKER}${serializedValues.join(LIST_DELIMITER)}${LIST_END_MARKER}`;
12523
12670
  }
12524
12671
  /**
12525
12672
  * Formats a single `I18nParamValue` into a string
12526
12673
  */
12527
12674
  function formatValue(value) {
12675
+ // Element tags with a structural directive use a special form that concatenates the element and
12676
+ // template values.
12677
+ if ((value.flags & I18nParamValueFlags.ElementTag) &&
12678
+ (value.flags & I18nParamValueFlags.TemplateTag)) {
12679
+ if (typeof value.value !== 'object') {
12680
+ throw Error('AssertionError: Expected i18n param value to have an element and template slot');
12681
+ }
12682
+ const elementValue = formatValue({
12683
+ ...value,
12684
+ value: value.value.element,
12685
+ flags: value.flags & ~I18nParamValueFlags.TemplateTag
12686
+ });
12687
+ const templateValue = formatValue({
12688
+ ...value,
12689
+ value: value.value.template,
12690
+ flags: value.flags & ~I18nParamValueFlags.ElementTag
12691
+ });
12692
+ // TODO(mmalerba): This is likely a bug in TemplateDefinitionBuilder, we should not need to
12693
+ // record the template value twice. For now I'm re-implementing the behavior here to keep the
12694
+ // output consistent with TemplateDefinitionBuilder.
12695
+ if ((value.flags & I18nParamValueFlags.OpenTag) &&
12696
+ (value.flags & I18nParamValueFlags.CloseTag)) {
12697
+ return `${templateValue}${elementValue}${templateValue}`;
12698
+ }
12699
+ // To match the TemplateDefinitionBuilder output, flip the order depending on whether the
12700
+ // values represent a closing or opening tag (or both).
12701
+ // TODO(mmalerba): Figure out if this makes a difference in terms of either functionality,
12702
+ // or the resulting message ID. If not, we can remove the special-casing in the future.
12703
+ return value.flags & I18nParamValueFlags.CloseTag ? `${elementValue}${templateValue}` :
12704
+ `${templateValue}${elementValue}`;
12705
+ }
12706
+ // Self-closing tags use a special form that concatenates the start and close tag values.
12707
+ if ((value.flags & I18nParamValueFlags.OpenTag) &&
12708
+ (value.flags & I18nParamValueFlags.CloseTag)) {
12709
+ return `${formatValue({ ...value, flags: value.flags & ~I18nParamValueFlags.CloseTag })}${formatValue({ ...value, flags: value.flags & ~I18nParamValueFlags.OpenTag })}`;
12710
+ }
12528
12711
  // If there are no special flags, just return the raw value.
12529
12712
  if (value.flags === I18nParamValueFlags.None) {
12530
12713
  return `${value.value}`;
12531
12714
  }
12715
+ // Encode the remaining flags as part of the value.
12532
12716
  let tagMarker = '';
12533
12717
  let closeMarker = '';
12534
12718
  if (value.flags & I18nParamValueFlags.ElementTag) {
@@ -12541,12 +12725,7 @@ function formatValue(value) {
12541
12725
  closeMarker = value.flags & I18nParamValueFlags.CloseTag ? TAG_CLOSE_MARKER : '';
12542
12726
  }
12543
12727
  const context = value.subTemplateIndex === null ? '' : `${CONTEXT_MARKER}${value.subTemplateIndex}`;
12544
- // Self-closing tags use a special form that concatenates the start and close tag values.
12545
- if ((value.flags & I18nParamValueFlags.OpenTag) &&
12546
- (value.flags & I18nParamValueFlags.CloseTag)) {
12547
- return `${ESCAPE}${tagMarker}${value.value}${context}${ESCAPE}${ESCAPE}${closeMarker}${tagMarker}${value.value}${context}${ESCAPE}`;
12548
- }
12549
- return `${ESCAPE}${closeMarker}${tagMarker}${value.value}${context}${ESCAPE}`;
12728
+ return `${ESCAPE$1}${closeMarker}${tagMarker}${value.value}${context}${ESCAPE$1}`;
12550
12729
  }
12551
12730
 
12552
12731
  /**
@@ -12580,7 +12759,7 @@ function generateAdvance(job) {
12580
12759
  else if (!slotMap.has(op.target)) {
12581
12760
  // We expect ops that _do_ depend on the slot counter to point at declarations that exist in
12582
12761
  // the `slotMap`.
12583
- throw new Error(`AssertionError: reference to unknown slot for var ${op.target}`);
12762
+ throw new Error(`AssertionError: reference to unknown slot for target ${op.target}`);
12584
12763
  }
12585
12764
  const slot = slotMap.get(op.target);
12586
12765
  // Does the slot counter need to be adjusted?
@@ -12663,9 +12842,15 @@ function recursivelyProcessView(view, parentScope) {
12663
12842
  for (const op of view.create) {
12664
12843
  switch (op.kind) {
12665
12844
  case OpKind.Template:
12845
+ // Descend into child embedded views.
12846
+ recursivelyProcessView(view.job.views.get(op.xref), scope);
12847
+ break;
12666
12848
  case OpKind.RepeaterCreate:
12667
12849
  // Descend into child embedded views.
12668
12850
  recursivelyProcessView(view.job.views.get(op.xref), scope);
12851
+ if (op.emptyView) {
12852
+ recursivelyProcessView(view.job.views.get(op.emptyView), scope);
12853
+ }
12669
12854
  break;
12670
12855
  case OpKind.Listener:
12671
12856
  // Prepend variables to listener handler functions.
@@ -19790,35 +19975,136 @@ const NG_I18N_CLOSURE_MODE$1 = 'ngI18nClosureMode';
19790
19975
  * considers variables like `I18N_0` as constants and throws an error when their value changes.
19791
19976
  */
19792
19977
  const TRANSLATION_VAR_PREFIX = 'i18n_';
19978
+ /** Prefix of ICU expressions for post processing */
19979
+ const I18N_ICU_MAPPING_PREFIX = 'I18N_EXP_';
19980
+ /**
19981
+ * The escape sequence used for message param values.
19982
+ */
19983
+ const ESCAPE = '\uFFFD';
19793
19984
  /**
19794
19985
  * Lifts i18n properties into the consts array.
19795
19986
  * TODO: Can we use `ConstCollectedExpr`?
19987
+ * TODO: The way the various attributes are linked together is very complex. Perhaps we could
19988
+ * simplify the process, maybe by combining the context and message ops?
19796
19989
  */
19797
19990
  function collectI18nConsts(job) {
19798
19991
  const fileBasedI18nSuffix = job.relativeContextFilePath.replace(/[^A-Za-z0-9]/g, '_').toUpperCase() + '_';
19799
- const messageConstIndices = new Map();
19800
- // Remove all of the i18n message ops into a map.
19992
+ // Step One: Build up various lookup maps we need to collect all the consts.
19993
+ // Context Xref -> Extracted Attribute Ops
19994
+ const extractedAttributesByI18nContext = new Map();
19995
+ // Element/ElementStart Xref -> I18n Attributes config op
19996
+ const i18nAttributesByElement = new Map();
19997
+ // Element/ElementStart Xref -> All I18n Expression ops for attrs on that target
19998
+ const i18nExpressionsByElement = new Map();
19999
+ // I18n Message Xref -> I18n Message Op (TODO: use a central op map)
19801
20000
  const messages = new Map();
20001
+ for (const unit of job.units) {
20002
+ for (const op of unit.ops()) {
20003
+ if (op.kind === OpKind.ExtractedAttribute && op.i18nContext !== null) {
20004
+ const attributes = extractedAttributesByI18nContext.get(op.i18nContext) ?? [];
20005
+ attributes.push(op);
20006
+ extractedAttributesByI18nContext.set(op.i18nContext, attributes);
20007
+ }
20008
+ else if (op.kind === OpKind.I18nAttributes) {
20009
+ i18nAttributesByElement.set(op.target, op);
20010
+ }
20011
+ else if (op.kind === OpKind.I18nExpression && op.usage === I18nExpressionFor.I18nAttribute) {
20012
+ const expressions = i18nExpressionsByElement.get(op.target) ?? [];
20013
+ expressions.push(op);
20014
+ i18nExpressionsByElement.set(op.target, expressions);
20015
+ }
20016
+ else if (op.kind === OpKind.I18nMessage) {
20017
+ messages.set(op.xref, op);
20018
+ }
20019
+ }
20020
+ }
20021
+ // Step Two: Serialize the extracted i18n messages for root i18n blocks and i18n attributes into
20022
+ // the const array.
20023
+ //
20024
+ // Also, each i18n message will have a variable expression that can refer to its
20025
+ // value. Store these expressions in the appropriate place:
20026
+ // 1. For normal i18n content, it also goes in the const array. We save the const index to use
20027
+ // later.
20028
+ // 2. For extracted attributes, it becomes the value of the extracted attribute instruction.
20029
+ // 3. For i18n bindings, it will go in a separate const array instruction below; for now, we just
20030
+ // save it.
20031
+ const i18nValuesByContext = new Map();
20032
+ const messageConstIndices = new Map();
19802
20033
  for (const unit of job.units) {
19803
20034
  for (const op of unit.create) {
19804
20035
  if (op.kind === OpKind.I18nMessage) {
19805
- messages.set(op.xref, op);
20036
+ if (op.messagePlaceholder === null) {
20037
+ const { mainVar, statements } = collectMessage(job, fileBasedI18nSuffix, messages, op);
20038
+ if (op.i18nBlock !== null) {
20039
+ // This is a regular i18n message with a corresponding i18n block. Collect it into the
20040
+ // const array.
20041
+ const i18nConst = job.addConst(mainVar, statements);
20042
+ messageConstIndices.set(op.i18nBlock, i18nConst);
20043
+ }
20044
+ else {
20045
+ // This is an i18n attribute. Extract the initializers into the const pool.
20046
+ job.constsInitializers.push(...statements);
20047
+ // Save the i18n variable value for later.
20048
+ i18nValuesByContext.set(op.i18nContext, mainVar);
20049
+ // This i18n message may correspond to an individual extracted attribute. If so, The
20050
+ // value of that attribute is updated to read the extracted i18n variable.
20051
+ const attributesForMessage = extractedAttributesByI18nContext.get(op.i18nContext);
20052
+ if (attributesForMessage !== undefined) {
20053
+ for (const attr of attributesForMessage) {
20054
+ attr.expression = mainVar.clone();
20055
+ }
20056
+ }
20057
+ }
20058
+ }
19806
20059
  OpList.remove(op);
19807
20060
  }
19808
20061
  }
19809
20062
  }
19810
- // Serialize the extracted messages for root i18n blocks into the const array.
19811
- for (const op of messages.values()) {
19812
- if (op.kind === OpKind.I18nMessage && op.messagePlaceholder === null) {
19813
- const { mainVar, statements } = collectMessage(job, fileBasedI18nSuffix, messages, op);
19814
- messageConstIndices.set(op.i18nBlock, job.addConst(mainVar, statements));
20063
+ // Step Three: Serialize I18nAttributes configurations into the const array. Each I18nAttributes
20064
+ // instruction has a config array, which contains k-v pairs describing each binding name, and the
20065
+ // i18n variable that provides the value.
20066
+ for (const unit of job.units) {
20067
+ for (const elem of unit.create) {
20068
+ if (isElementOrContainerOp(elem)) {
20069
+ const i18nAttributes = i18nAttributesByElement.get(elem.xref);
20070
+ if (i18nAttributes === undefined) {
20071
+ // This element is not associated with an i18n attributes configuration instruction.
20072
+ continue;
20073
+ }
20074
+ let i18nExpressions = i18nExpressionsByElement.get(elem.xref);
20075
+ if (i18nExpressions === undefined) {
20076
+ // Unused i18nAttributes should have already been removed.
20077
+ // TODO: Should the removal of those dead instructions be merged with this phase?
20078
+ throw new Error('AssertionError: Could not find any i18n expressions associated with an I18nAttributes instruction');
20079
+ }
20080
+ // Find expressions for all the unique property names, removing duplicates.
20081
+ const seenPropertyNames = new Set();
20082
+ i18nExpressions = i18nExpressions.filter(i18nExpr => {
20083
+ const seen = (seenPropertyNames.has(i18nExpr.name));
20084
+ seenPropertyNames.add(i18nExpr.name);
20085
+ return !seen;
20086
+ });
20087
+ const i18nAttributeConfig = i18nExpressions.flatMap(i18nExpr => {
20088
+ const i18nExprValue = i18nValuesByContext.get(i18nExpr.context);
20089
+ if (i18nExprValue === undefined) {
20090
+ throw new Error('AssertionError: Could not find i18n expression\'s value');
20091
+ }
20092
+ return [literal(i18nExpr.name), i18nExprValue];
20093
+ });
20094
+ i18nAttributes.i18nAttributesConfig =
20095
+ job.addConst(new LiteralArrayExpr(i18nAttributeConfig));
20096
+ }
19815
20097
  }
19816
20098
  }
19817
- // Assign const index to i18n ops that messages were extracted from.
20099
+ // Step Four: Propagate the extracted const index into i18n ops that messages were extracted from.
19818
20100
  for (const unit of job.units) {
19819
20101
  for (const op of unit.create) {
19820
20102
  if (op.kind === OpKind.I18nStart) {
19821
- op.messageIndex = messageConstIndices.get(op.root);
20103
+ const msgIndex = messageConstIndices.get(op.root);
20104
+ if (msgIndex === undefined) {
20105
+ throw new Error('AssertionError: Could not find corresponding i18n block index for an i18n message op; was an i18n message incorrectly assumed to correspond to an attribute?');
20106
+ }
20107
+ op.messageIndex = msgIndex;
19822
20108
  }
19823
20109
  }
19824
20110
  }
@@ -19828,18 +20114,23 @@ function collectI18nConsts(job) {
19828
20114
  * This will recursively collect any sub-messages referenced from the parent message as well.
19829
20115
  */
19830
20116
  function collectMessage(job, fileBasedI18nSuffix, messages, messageOp) {
19831
- // Recursively collect any sub-messages, and fill in their placeholders in this message.
20117
+ // Recursively collect any sub-messages, record each sub-message's main variable under its
20118
+ // placeholder so that we can add them to the params for the parent message. It is possible
20119
+ // that multiple sub-messages will share the same placeholder, so we need to track an array of
20120
+ // variables for each placeholder.
19832
20121
  const statements = [];
20122
+ const subMessagePlaceholders = new Map();
19833
20123
  for (const subMessageId of messageOp.subMessages) {
19834
20124
  const subMessage = messages.get(subMessageId);
19835
20125
  const { mainVar: subMessageVar, statements: subMessageStatements } = collectMessage(job, fileBasedI18nSuffix, messages, subMessage);
19836
20126
  statements.push(...subMessageStatements);
19837
- messageOp.params.set(subMessage.messagePlaceholder, subMessageVar);
20127
+ const subMessages = subMessagePlaceholders.get(subMessage.messagePlaceholder) ?? [];
20128
+ subMessages.push(subMessageVar);
20129
+ subMessagePlaceholders.set(subMessage.messagePlaceholder, subMessages);
19838
20130
  }
20131
+ addSubMessageParams(messageOp, subMessagePlaceholders);
19839
20132
  // Sort the params for consistency with TemaplateDefinitionBuilder output.
19840
20133
  messageOp.params = new Map([...messageOp.params.entries()].sort());
19841
- // Check that the message has all of its parameters filled out.
19842
- assertAllParamsResolved(messageOp);
19843
20134
  const mainVar = variable(job.pool.uniqueName(TRANSLATION_VAR_PREFIX));
19844
20135
  // Closure Compiler requires const names to start with `MSG_` but disallows any other
19845
20136
  // const to start with `MSG_`. We define a variable starting with `MSG_` just for the
@@ -19850,10 +20141,11 @@ function collectMessage(job, fileBasedI18nSuffix, messages, messageOp) {
19850
20141
  // set in post-processing.
19851
20142
  if (messageOp.needsPostprocessing) {
19852
20143
  // Sort the post-processing params for consistency with TemaplateDefinitionBuilder output.
19853
- messageOp.postprocessingParams = new Map([...messageOp.postprocessingParams.entries()].sort());
20144
+ const postprocessingParams = Object.fromEntries([...messageOp.postprocessingParams.entries()].sort());
20145
+ const formattedPostprocessingParams = formatI18nPlaceholderNamesInMap(postprocessingParams, /* useCamelCase */ false);
19854
20146
  const extraTransformFnParams = [];
19855
20147
  if (messageOp.postprocessingParams.size > 0) {
19856
- extraTransformFnParams.push(literalMap([...messageOp.postprocessingParams].map(([key, value]) => ({ key, value, quoted: true }))));
20148
+ extraTransformFnParams.push(mapLiteral(formattedPostprocessingParams, /* quoted */ true));
19857
20149
  }
19858
20150
  transformFn = (expr) => importExpr(Identifiers.i18nPostprocess).callFn([expr, ...extraTransformFnParams]);
19859
20151
  }
@@ -19861,6 +20153,26 @@ function collectMessage(job, fileBasedI18nSuffix, messages, messageOp) {
19861
20153
  statements.push(...getTranslationDeclStmts$1(messageOp.message, mainVar, closureVar, messageOp.params, transformFn));
19862
20154
  return { mainVar, statements };
19863
20155
  }
20156
+ /**
20157
+ * Adds the given subMessage placeholders to the given message op.
20158
+ *
20159
+ * If a placeholder only corresponds to a single sub-message variable, we just set that variable
20160
+ * as the param value. However, if the placeholder corresponds to multiple sub-message
20161
+ * variables, we need to add a special placeholder value that is handled by the post-processing
20162
+ * step. We then add the array of variables as a post-processing param.
20163
+ */
20164
+ function addSubMessageParams(messageOp, subMessagePlaceholders) {
20165
+ for (const [placeholder, subMessages] of subMessagePlaceholders) {
20166
+ if (subMessages.length === 1) {
20167
+ messageOp.params.set(placeholder, subMessages[0]);
20168
+ }
20169
+ else {
20170
+ messageOp.params.set(placeholder, literal(`${ESCAPE}${I18N_ICU_MAPPING_PREFIX}${placeholder}${ESCAPE}`));
20171
+ messageOp.postprocessingParams.set(placeholder, literalArr(subMessages));
20172
+ messageOp.needsPostprocessing = true;
20173
+ }
20174
+ }
20175
+ }
19864
20176
  /**
19865
20177
  * Generate statements that define a given translation message.
19866
20178
  *
@@ -19883,7 +20195,8 @@ function collectMessage(job, fileBasedI18nSuffix, messages, messageOp) {
19883
20195
  * @param closureVar The variable for Closure `goog.getMsg` calls, e.g. `MSG_EXTERNAL_XXX`.
19884
20196
  * @param params Object mapping placeholder names to their values (e.g.
19885
20197
  * `{ "interpolation": "\uFFFD0\uFFFD" }`).
19886
- * @param transformFn Optional transformation function that will be applied to the translation (e.g.
20198
+ * @param transformFn Optional transformation function that will be applied to the translation
20199
+ * (e.g.
19887
20200
  * post-processing).
19888
20201
  * @returns An array of statements that defined a given translation.
19889
20202
  */
@@ -19928,28 +20241,13 @@ function i18nGenerateClosureVar(pool, messageId, fileBasedI18nSuffix, useExterna
19928
20241
  }
19929
20242
  return variable(name);
19930
20243
  }
19931
- /**
19932
- * Asserts that all of the message's placeholders have values.
19933
- */
19934
- function assertAllParamsResolved(op) {
19935
- for (let placeholder in op.message.placeholders) {
19936
- placeholder = placeholder.trimEnd();
19937
- if (!op.params.has(placeholder) && !op.postprocessingParams.has(placeholder)) {
19938
- throw Error(`Failed to resolve i18n placeholder: ${placeholder}`);
19939
- }
19940
- }
19941
- for (let placeholder in op.message.placeholderToMessage) {
19942
- placeholder = placeholder.trimEnd();
19943
- if (!op.params.has(placeholder) && !op.postprocessingParams.has(placeholder)) {
19944
- throw Error(`Failed to resolve i18n message placeholder: ${placeholder}`);
19945
- }
19946
- }
19947
- }
19948
20244
 
19949
20245
  /**
19950
20246
  * Removes text nodes within i18n blocks since they are already hardcoded into the i18n message.
20247
+ * Also, replaces interpolations on these text nodes with i18n expressions of the non-text portions,
20248
+ * which will be applied later.
19951
20249
  */
19952
- function extractI18nText(job) {
20250
+ function convertI18nText(job) {
19953
20251
  for (const unit of job.units) {
19954
20252
  // Remove all text nodes within i18n blocks, their content is already captured in the i18n
19955
20253
  // message.
@@ -20004,7 +20302,7 @@ function extractI18nText(job) {
20004
20302
  const expr = op.interpolation.expressions[i];
20005
20303
  // For now, this i18nExpression depends on the slot context of the enclosing i18n block.
20006
20304
  // Later, we will modify this, and advance to a different point.
20007
- ops.push(createI18nExpressionOp(contextId, i18nOp.xref, i18nOp.handle, expr, op.i18nPlaceholders[i], resolutionTime, expr.sourceSpan ?? op.sourceSpan));
20305
+ ops.push(createI18nExpressionOp(contextId, i18nOp.xref, i18nOp.xref, i18nOp.handle, expr, op.interpolation.i18nPlaceholders[i], resolutionTime, I18nExpressionFor.I18nText, '', expr.sourceSpan ?? op.sourceSpan));
20008
20306
  }
20009
20307
  OpList.replaceWithMany(op, ops);
20010
20308
  break;
@@ -20542,14 +20840,14 @@ function parseExtractedStyles(job) {
20542
20840
  if (op.name === 'style') {
20543
20841
  const parsedStyles = parse(op.expression.value);
20544
20842
  for (let i = 0; i < parsedStyles.length - 1; i += 2) {
20545
- OpList.insertBefore(createExtractedAttributeOp(op.target, BindingKind.StyleProperty, parsedStyles[i], literal(parsedStyles[i + 1])), op);
20843
+ OpList.insertBefore(createExtractedAttributeOp(op.target, BindingKind.StyleProperty, parsedStyles[i], literal(parsedStyles[i + 1]), null, null, SecurityContext.STYLE), op);
20546
20844
  }
20547
20845
  OpList.remove(op);
20548
20846
  }
20549
20847
  else if (op.name === 'class') {
20550
20848
  const parsedClasses = op.expression.value.trim().split(/\s+/g);
20551
20849
  for (const parsedClass of parsedClasses) {
20552
- OpList.insertBefore(createExtractedAttributeOp(op.target, BindingKind.ClassName, parsedClass, null), op);
20850
+ OpList.insertBefore(createExtractedAttributeOp(op.target, BindingKind.ClassName, parsedClass, null, null, null, SecurityContext.NONE), op);
20553
20851
  }
20554
20852
  OpList.remove(op);
20555
20853
  }
@@ -20565,18 +20863,30 @@ function parseExtractedStyles(job) {
20565
20863
  function removeContentSelectors(job) {
20566
20864
  for (const unit of job.units) {
20567
20865
  const elements = createOpXrefMap(unit);
20568
- for (const op of unit.update) {
20866
+ for (const op of unit.ops()) {
20569
20867
  switch (op.kind) {
20570
20868
  case OpKind.Binding:
20571
20869
  const target = lookupInXrefMap(elements, op.target);
20572
- if (op.name.toLowerCase() === 'select' && target.kind === OpKind.Projection) {
20870
+ if (isSelectAttribute(op.name) && target.kind === OpKind.Projection) {
20573
20871
  OpList.remove(op);
20574
20872
  }
20575
20873
  break;
20874
+ case OpKind.Projection:
20875
+ // op.attributes is an array of [attr1-name, attr1-value, attr2-name, attr2-value, ...],
20876
+ // find the "select" attribute and remove its name and corresponding value.
20877
+ for (let i = op.attributes.length - 2; i >= 0; i -= 2) {
20878
+ if (isSelectAttribute(op.attributes[i])) {
20879
+ op.attributes.splice(i, 2);
20880
+ }
20881
+ }
20882
+ break;
20576
20883
  }
20577
20884
  }
20578
20885
  }
20579
20886
  }
20887
+ function isSelectAttribute(name) {
20888
+ return name.toLowerCase() === 'select';
20889
+ }
20580
20890
  /**
20581
20891
  * Looks up an element in the given map by xref ID.
20582
20892
  */
@@ -20696,25 +21006,44 @@ function propagateI18nBlocksToTemplates(unit, subTemplateIndex) {
20696
21006
  i18nBlock = op;
20697
21007
  break;
20698
21008
  case OpKind.I18nEnd:
21009
+ // When we exit a root-level i18n block, reset the sub-template index counter.
21010
+ if (i18nBlock.subTemplateIndex === null) {
21011
+ subTemplateIndex = 0;
21012
+ }
20699
21013
  i18nBlock = null;
20700
21014
  break;
20701
21015
  case OpKind.Template:
20702
- const templateView = unit.job.views.get(op.xref);
20703
- // We found an <ng-template> inside an i18n block; increment the sub-template counter and
20704
- // wrap the template's view in a child i18n block.
20705
- if (op.i18nPlaceholder !== undefined) {
20706
- if (i18nBlock === null) {
20707
- throw Error('Expected template with i18n placeholder to be in an i18n block.');
20708
- }
20709
- subTemplateIndex++;
20710
- wrapTemplateWithI18n(templateView, i18nBlock);
21016
+ subTemplateIndex = propagateI18nBlocksForView(unit.job.views.get(op.xref), i18nBlock, op.i18nPlaceholder, subTemplateIndex);
21017
+ break;
21018
+ case OpKind.RepeaterCreate:
21019
+ // Propagate i18n blocks to the @for template.
21020
+ const forView = unit.job.views.get(op.xref);
21021
+ subTemplateIndex = propagateI18nBlocksForView(unit.job.views.get(op.xref), i18nBlock, op.i18nPlaceholder, subTemplateIndex);
21022
+ // Then if there's an @empty template, propagate the i18n blocks for it as well.
21023
+ if (op.emptyView !== null) {
21024
+ subTemplateIndex = propagateI18nBlocksForView(unit.job.views.get(op.emptyView), i18nBlock, op.emptyI18nPlaceholder, subTemplateIndex);
20711
21025
  }
20712
- // Continue traversing inside the template's view.
20713
- subTemplateIndex = propagateI18nBlocksToTemplates(templateView, subTemplateIndex);
21026
+ break;
20714
21027
  }
20715
21028
  }
20716
21029
  return subTemplateIndex;
20717
21030
  }
21031
+ /**
21032
+ * Propagate i18n blocks for a view.
21033
+ */
21034
+ function propagateI18nBlocksForView(view, i18nBlock, i18nPlaceholder, subTemplateIndex) {
21035
+ // We found an <ng-template> inside an i18n block; increment the sub-template counter and
21036
+ // wrap the template's view in a child i18n block.
21037
+ if (i18nPlaceholder !== undefined) {
21038
+ if (i18nBlock === null) {
21039
+ throw Error('Expected template with i18n placeholder to be in an i18n block.');
21040
+ }
21041
+ subTemplateIndex++;
21042
+ wrapTemplateWithI18n(view, i18nBlock);
21043
+ }
21044
+ // Continue traversing inside the template's view.
21045
+ return propagateI18nBlocksToTemplates(view, subTemplateIndex);
21046
+ }
20718
21047
  /**
20719
21048
  * Wraps a template view with i18n start and end ops.
20720
21049
  */
@@ -20880,17 +21209,13 @@ function disableBindings() {
20880
21209
  function enableBindings() {
20881
21210
  return call(Identifiers.enableBindings, [], null);
20882
21211
  }
20883
- function listener(name, handlerFn, sourceSpan) {
20884
- return call(Identifiers.listener, [
20885
- literal(name),
20886
- handlerFn,
20887
- ], sourceSpan);
20888
- }
20889
- function syntheticHostListener(name, handlerFn, sourceSpan) {
20890
- return call(Identifiers.syntheticHostListener, [
20891
- literal(name),
20892
- handlerFn,
20893
- ], sourceSpan);
21212
+ function listener(name, handlerFn, eventTargetResolver, syntheticHost, sourceSpan) {
21213
+ const args = [literal(name), handlerFn];
21214
+ if (eventTargetResolver !== null) {
21215
+ args.push(literal(false)); // `useCapture` flag, defaults to `false`
21216
+ args.push(importExpr(eventTargetResolver));
21217
+ }
21218
+ return call(syntheticHost ? Identifiers.syntheticHostListener : Identifiers.listener, args, sourceSpan);
20894
21219
  }
20895
21220
  function pipe(slot, name) {
20896
21221
  return call(Identifiers.pipe, [
@@ -21037,6 +21362,10 @@ function i18n(slot, constIndex, subTemplateIndex) {
21037
21362
  function i18nEnd() {
21038
21363
  return call(Identifiers.i18nEnd, [], null);
21039
21364
  }
21365
+ function i18nAttributes(slot, i18nAttributesConfig) {
21366
+ const args = [literal(slot), literal(i18nAttributesConfig)];
21367
+ return call(Identifiers.i18nAttributes, args, null);
21368
+ }
21040
21369
  function property(name, expression, sanitizer, sourceSpan) {
21041
21370
  const args = [literal(name), expression];
21042
21371
  if (sanitizer !== null) {
@@ -21147,8 +21476,12 @@ function classMapInterpolate(strings, expressions, sourceSpan) {
21147
21476
  const interpolationArgs = collateInterpolationArgs(strings, expressions);
21148
21477
  return callVariadicInstruction(CLASS_MAP_INTERPOLATE_CONFIG, [], interpolationArgs, [], sourceSpan);
21149
21478
  }
21150
- function hostProperty(name, expression, sourceSpan) {
21151
- return call(Identifiers.hostProperty, [literal(name), expression], sourceSpan);
21479
+ function hostProperty(name, expression, sanitizer, sourceSpan) {
21480
+ const args = [literal(name), expression];
21481
+ if (sanitizer !== null) {
21482
+ args.push(sanitizer);
21483
+ }
21484
+ return call(Identifiers.hostProperty, args, sourceSpan);
21152
21485
  }
21153
21486
  function syntheticHostProperty(name, expression, sourceSpan) {
21154
21487
  return call(Identifiers.syntheticHostProperty, [literal(name), expression], sourceSpan);
@@ -21366,14 +21699,12 @@ function callVariadicInstruction(config, baseArgs, interpolationArgs, extraArgs,
21366
21699
  }
21367
21700
 
21368
21701
  /**
21369
- * Map of sanitizers to their identifier.
21702
+ * Map of target resolvers for event listeners.
21370
21703
  */
21371
- const sanitizerIdentifierMap = new Map([
21372
- [SanitizerFn.Html, Identifiers.sanitizeHtml],
21373
- [SanitizerFn.IframeAttribute, Identifiers.validateIframeAttribute],
21374
- [SanitizerFn.ResourceUrl, Identifiers.sanitizeResourceUrl],
21375
- [SanitizerFn.Script, Identifiers.sanitizeScript],
21376
- [SanitizerFn.Style, Identifiers.sanitizeStyle], [SanitizerFn.Url, Identifiers.sanitizeUrl]
21704
+ const GLOBAL_TARGET_RESOLVERS$1 = new Map([
21705
+ ['window', Identifiers.resolveWindow],
21706
+ ['document', Identifiers.resolveDocument],
21707
+ ['body', Identifiers.resolveBody],
21377
21708
  ]);
21378
21709
  /**
21379
21710
  * Compiles semantic operations across all views and generates output `o.Statement`s with actual
@@ -21423,6 +21754,12 @@ function reifyCreateOperations(unit, ops) {
21423
21754
  case OpKind.I18n:
21424
21755
  OpList.replace(op, i18n(op.handle.slot, op.messageIndex, op.subTemplateIndex));
21425
21756
  break;
21757
+ case OpKind.I18nAttributes:
21758
+ if (op.i18nAttributesConfig === null) {
21759
+ throw new Error(`AssertionError: i18nAttributesConfig was not set`);
21760
+ }
21761
+ OpList.replace(op, i18nAttributes(op.handle.slot, op.i18nAttributesConfig));
21762
+ break;
21426
21763
  case OpKind.Template:
21427
21764
  if (!(unit instanceof ViewCompilationUnit)) {
21428
21765
  throw new Error(`AssertionError: must be compiling a component`);
@@ -21444,10 +21781,11 @@ function reifyCreateOperations(unit, ops) {
21444
21781
  break;
21445
21782
  case OpKind.Listener:
21446
21783
  const listenerFn = reifyListenerHandler(unit, op.handlerFnName, op.handlerOps, op.consumesDollarEvent);
21447
- const reified = op.hostListener && op.isAnimationListener ?
21448
- syntheticHostListener(op.name, listenerFn, op.sourceSpan) :
21449
- listener(op.name, listenerFn, op.sourceSpan);
21450
- OpList.replace(op, reified);
21784
+ const eventTargetResolver = op.eventTarget ? GLOBAL_TARGET_RESOLVERS$1.get(op.eventTarget) : null;
21785
+ if (eventTargetResolver === undefined) {
21786
+ throw new Error(`AssertionError: unknown event target ${op.eventTarget}`);
21787
+ }
21788
+ OpList.replace(op, listener(op.name, listenerFn, eventTargetResolver, op.hostListener && op.isAnimationListener, op.sourceSpan));
21451
21789
  break;
21452
21790
  case OpKind.Variable:
21453
21791
  if (op.variable.name === null) {
@@ -21610,7 +21948,7 @@ function reifyUpdateOperations(_unit, ops) {
21610
21948
  OpList.replace(op, syntheticHostProperty(op.name, op.expression, op.sourceSpan));
21611
21949
  }
21612
21950
  else {
21613
- OpList.replace(op, hostProperty(op.name, op.expression, op.sourceSpan));
21951
+ OpList.replace(op, hostProperty(op.name, op.expression, op.sanitizer, op.sourceSpan));
21614
21952
  }
21615
21953
  }
21616
21954
  break;
@@ -21689,8 +22027,6 @@ function reifyIrExpression(expr) {
21689
22027
  return pipeBind(expr.targetSlot.slot, expr.varOffset, expr.args);
21690
22028
  case ExpressionKind.PipeBindingVariadic:
21691
22029
  return pipeBindV(expr.targetSlot.slot, expr.varOffset, expr.args);
21692
- case ExpressionKind.SanitizerExpr:
21693
- return importExpr(sanitizerIdentifierMap.get(expr.fn));
21694
22030
  case ExpressionKind.SlotLiteralExpr:
21695
22031
  return literal(expr.slot.slot);
21696
22032
  default:
@@ -21764,6 +22100,31 @@ function removeI18nContexts(job) {
21764
22100
  }
21765
22101
  }
21766
22102
 
22103
+ /**
22104
+ * i18nAttributes ops will be generated for each i18n attribute. However, not all i18n attribues
22105
+ * will contain dynamic content, and so some of these i18nAttributes ops may be unnecessary.
22106
+ */
22107
+ function removeUnusedI18nAttributesOps(job) {
22108
+ for (const unit of job.units) {
22109
+ const ownersWithI18nExpressions = new Set();
22110
+ for (const op of unit.update) {
22111
+ switch (op.kind) {
22112
+ case OpKind.I18nExpression:
22113
+ ownersWithI18nExpressions.add(op.i18nOwner);
22114
+ }
22115
+ }
22116
+ for (const op of unit.create) {
22117
+ switch (op.kind) {
22118
+ case OpKind.I18nAttributes:
22119
+ if (ownersWithI18nExpressions.has(op.xref)) {
22120
+ continue;
22121
+ }
22122
+ OpList.remove(op);
22123
+ }
22124
+ }
22125
+ }
22126
+ }
22127
+
21767
22128
  /**
21768
22129
  * Inside the body of a repeater, certain context variables (such as `$first`) are ambiently
21769
22130
  * available. This phase finds those variable usages, and replaces them with the appropriate
@@ -21895,10 +22256,14 @@ function resolveI18nElementPlaceholders(job) {
21895
22256
  }
21896
22257
  resolvePlaceholdersForView(job, job.root, i18nContexts, elements);
21897
22258
  }
21898
- function resolvePlaceholdersForView(job, unit, i18nContexts, elements) {
22259
+ /**
22260
+ * Recursively resolves element and template tag placeholders in the given view.
22261
+ */
22262
+ function resolvePlaceholdersForView(job, unit, i18nContexts, elements, pendingStructuralDirective) {
21899
22263
  // Track the current i18n op and corresponding i18n context op as we step through the creation
21900
22264
  // IR.
21901
22265
  let currentOps = null;
22266
+ let pendingStructuralDirectiveCloses = new Map();
21902
22267
  for (const op of unit.create) {
21903
22268
  switch (op.kind) {
21904
22269
  case OpKind.I18nStart:
@@ -21917,14 +22282,14 @@ function resolvePlaceholdersForView(job, unit, i18nContexts, elements) {
21917
22282
  if (currentOps === null) {
21918
22283
  throw Error('i18n tag placeholder should only occur inside an i18n block');
21919
22284
  }
21920
- const { startName, closeName } = op.i18nPlaceholder;
21921
- let flags = I18nParamValueFlags.ElementTag | I18nParamValueFlags.OpenTag;
21922
- // For self-closing tags, there is no close tag placeholder. Instead, the start tag
21923
- // placeholder accounts for the start and close of the element.
21924
- if (closeName === '') {
21925
- flags |= I18nParamValueFlags.CloseTag;
22285
+ recordElementStart(op, currentOps.i18nContext, currentOps.i18nBlock, pendingStructuralDirective);
22286
+ // If there is a separate close tag placeholder for this element, save the pending
22287
+ // structural directive so we can pass it to the closing tag as well.
22288
+ if (pendingStructuralDirective && op.i18nPlaceholder.closeName) {
22289
+ pendingStructuralDirectiveCloses.set(op.xref, pendingStructuralDirective);
21926
22290
  }
21927
- addParam(currentOps.i18nContext.params, startName, op.handle.slot, currentOps.i18nBlock.subTemplateIndex, flags);
22291
+ // Clear out the pending structural directive now that its been accounted for.
22292
+ pendingStructuralDirective = undefined;
21928
22293
  }
21929
22294
  break;
21930
22295
  case OpKind.ElementEnd:
@@ -21933,55 +22298,195 @@ function resolvePlaceholdersForView(job, unit, i18nContexts, elements) {
21933
22298
  const startOp = elements.get(op.xref);
21934
22299
  if (startOp && startOp.i18nPlaceholder !== undefined) {
21935
22300
  if (currentOps === null) {
21936
- throw Error('i18n tag placeholder should only occur inside an i18n block');
22301
+ throw Error('AssertionError: i18n tag placeholder should only occur inside an i18n block');
21937
22302
  }
21938
- const { closeName } = startOp.i18nPlaceholder;
21939
- // Self-closing tags don't have a closing tag placeholder.
21940
- if (closeName !== '') {
21941
- addParam(currentOps.i18nContext.params, closeName, startOp.handle.slot, currentOps.i18nBlock.subTemplateIndex, I18nParamValueFlags.ElementTag | I18nParamValueFlags.CloseTag);
22303
+ recordElementClose(startOp, currentOps.i18nContext, currentOps.i18nBlock, pendingStructuralDirectiveCloses.get(op.xref));
22304
+ // Clear out the pending structural directive close that was accounted for.
22305
+ pendingStructuralDirectiveCloses.delete(op.xref);
22306
+ }
22307
+ break;
22308
+ case OpKind.Projection:
22309
+ // For content projections with i18n placeholders, record its slot value in the params map
22310
+ // under the corresponding tag start and close placeholders.
22311
+ if (op.i18nPlaceholder !== undefined) {
22312
+ if (currentOps === null) {
22313
+ throw Error('i18n tag placeholder should only occur inside an i18n block');
21942
22314
  }
22315
+ recordElementStart(op, currentOps.i18nContext, currentOps.i18nBlock, pendingStructuralDirective);
22316
+ recordElementClose(op, currentOps.i18nContext, currentOps.i18nBlock, pendingStructuralDirective);
22317
+ // Clear out the pending structural directive now that its been accounted for.
22318
+ pendingStructuralDirective = undefined;
21943
22319
  }
21944
22320
  break;
21945
22321
  case OpKind.Template:
21946
- // For templates with i18n placeholders, record its slot value in the params map under the
21947
- // corresponding template start and close placeholders.
21948
- if (op.i18nPlaceholder !== undefined) {
22322
+ const view = job.views.get(op.xref);
22323
+ if (op.i18nPlaceholder === undefined) {
22324
+ // If there is no i18n placeholder, just recurse into the view in case it contains i18n
22325
+ // blocks.
22326
+ resolvePlaceholdersForView(job, view, i18nContexts, elements);
22327
+ }
22328
+ else {
21949
22329
  if (currentOps === null) {
21950
22330
  throw Error('i18n tag placeholder should only occur inside an i18n block');
21951
22331
  }
21952
- let startFlags = I18nParamValueFlags.TemplateTag | I18nParamValueFlags.OpenTag;
21953
- const subTemplateIndex = getSubTemplateIndexForTemplateTag(job, currentOps.i18nBlock, op);
21954
- const { startName, closeName } = op.i18nPlaceholder;
21955
- const isSelfClosing = closeName === '';
21956
- if (isSelfClosing) {
21957
- startFlags |= I18nParamValueFlags.CloseTag;
22332
+ if (op.templateKind === TemplateKind.Structural) {
22333
+ // If this is a structural directive template, don't record anything yet. Instead pass
22334
+ // the current template as a pending structural directive to be recorded when we find
22335
+ // the element, content, or template it belongs to. This allows us to create combined
22336
+ // values that represent, e.g. the start of a template and element at the same time.
22337
+ resolvePlaceholdersForView(job, view, i18nContexts, elements, op);
21958
22338
  }
21959
- addParam(currentOps.i18nContext.params, startName, op.handle.slot, subTemplateIndex, startFlags);
21960
- resolvePlaceholdersForView(job, job.views.get(op.xref), i18nContexts, elements);
21961
- if (!isSelfClosing) {
21962
- addParam(currentOps.i18nContext.params, closeName, op.handle.slot, subTemplateIndex, I18nParamValueFlags.TemplateTag | I18nParamValueFlags.CloseTag);
22339
+ else {
22340
+ // If this is some other kind of template, we can record its start, recurse into its
22341
+ // view, and then record its end.
22342
+ recordTemplateStart(job, view, op.handle.slot, op.i18nPlaceholder, currentOps.i18nContext, currentOps.i18nBlock, pendingStructuralDirective);
22343
+ resolvePlaceholdersForView(job, view, i18nContexts, elements);
22344
+ recordTemplateClose(job, view, op.handle.slot, op.i18nPlaceholder, currentOps.i18nContext, currentOps.i18nBlock, pendingStructuralDirective);
22345
+ pendingStructuralDirective = undefined;
21963
22346
  }
21964
22347
  }
22348
+ break;
22349
+ case OpKind.RepeaterCreate:
22350
+ if (pendingStructuralDirective !== undefined) {
22351
+ throw Error('AssertionError: Unexpected structural directive associated with @for block');
22352
+ }
22353
+ // RepeaterCreate has 3 slots: the first is for the op itself, the second is for the @for
22354
+ // template and the (optional) third is for the @empty template.
22355
+ const forSlot = op.handle.slot + 1;
22356
+ const forView = job.views.get(op.xref);
22357
+ // First record all of the placeholders for the @for template.
22358
+ if (op.i18nPlaceholder === undefined) {
22359
+ // If there is no i18n placeholder, just recurse into the view in case it contains i18n
22360
+ // blocks.
22361
+ resolvePlaceholdersForView(job, forView, i18nContexts, elements);
22362
+ }
21965
22363
  else {
21966
- resolvePlaceholdersForView(job, job.views.get(op.xref), i18nContexts, elements);
22364
+ if (currentOps === null) {
22365
+ throw Error('i18n tag placeholder should only occur inside an i18n block');
22366
+ }
22367
+ recordTemplateStart(job, forView, forSlot, op.i18nPlaceholder, currentOps.i18nContext, currentOps.i18nBlock, pendingStructuralDirective);
22368
+ resolvePlaceholdersForView(job, forView, i18nContexts, elements);
22369
+ recordTemplateClose(job, forView, forSlot, op.i18nPlaceholder, currentOps.i18nContext, currentOps.i18nBlock, pendingStructuralDirective);
22370
+ pendingStructuralDirective = undefined;
22371
+ }
22372
+ // Then if there's an @empty template, add its placeholders as well.
22373
+ if (op.emptyView !== null) {
22374
+ // RepeaterCreate has 3 slots: the first is for the op itself, the second is for the @for
22375
+ // template and the (optional) third is for the @empty template.
22376
+ const emptySlot = op.handle.slot + 2;
22377
+ const emptyView = job.views.get(op.emptyView);
22378
+ if (op.emptyI18nPlaceholder === undefined) {
22379
+ // If there is no i18n placeholder, just recurse into the view in case it contains i18n
22380
+ // blocks.
22381
+ resolvePlaceholdersForView(job, emptyView, i18nContexts, elements);
22382
+ }
22383
+ else {
22384
+ if (currentOps === null) {
22385
+ throw Error('i18n tag placeholder should only occur inside an i18n block');
22386
+ }
22387
+ recordTemplateStart(job, emptyView, emptySlot, op.emptyI18nPlaceholder, currentOps.i18nContext, currentOps.i18nBlock, pendingStructuralDirective);
22388
+ resolvePlaceholdersForView(job, emptyView, i18nContexts, elements);
22389
+ recordTemplateClose(job, emptyView, emptySlot, op.emptyI18nPlaceholder, currentOps.i18nContext, currentOps.i18nBlock, pendingStructuralDirective);
22390
+ pendingStructuralDirective = undefined;
22391
+ }
21967
22392
  }
21968
22393
  break;
21969
22394
  }
21970
22395
  }
21971
22396
  }
22397
+ /**
22398
+ * Records an i18n param value for the start of an element.
22399
+ */
22400
+ function recordElementStart(op, i18nContext, i18nBlock, structuralDirective) {
22401
+ const { startName, closeName } = op.i18nPlaceholder;
22402
+ let flags = I18nParamValueFlags.ElementTag | I18nParamValueFlags.OpenTag;
22403
+ let value = op.handle.slot;
22404
+ // If the element is associated with a structural directive, start it as well.
22405
+ if (structuralDirective !== undefined) {
22406
+ flags |= I18nParamValueFlags.TemplateTag;
22407
+ value = { element: value, template: structuralDirective.handle.slot };
22408
+ }
22409
+ // For self-closing tags, there is no close tag placeholder. Instead, the start tag
22410
+ // placeholder accounts for the start and close of the element.
22411
+ if (!closeName) {
22412
+ flags |= I18nParamValueFlags.CloseTag;
22413
+ }
22414
+ addParam(i18nContext.params, startName, value, i18nBlock.subTemplateIndex, flags);
22415
+ }
22416
+ /**
22417
+ * Records an i18n param value for the closing of an element.
22418
+ */
22419
+ function recordElementClose(op, i18nContext, i18nBlock, structuralDirective) {
22420
+ const { closeName } = op.i18nPlaceholder;
22421
+ // Self-closing tags don't have a closing tag placeholder, instead the element closing is
22422
+ // recorded via an additional flag on the element start value.
22423
+ if (closeName) {
22424
+ let flags = I18nParamValueFlags.ElementTag | I18nParamValueFlags.CloseTag;
22425
+ let value = op.handle.slot;
22426
+ // If the element is associated with a structural directive, close it as well.
22427
+ if (structuralDirective !== undefined) {
22428
+ flags |= I18nParamValueFlags.TemplateTag;
22429
+ value = { element: value, template: structuralDirective.handle.slot };
22430
+ }
22431
+ addParam(i18nContext.params, closeName, value, i18nBlock.subTemplateIndex, flags);
22432
+ }
22433
+ }
22434
+ /**
22435
+ * Records an i18n param value for the start of a template.
22436
+ */
22437
+ function recordTemplateStart(job, view, slot, i18nPlaceholder, i18nContext, i18nBlock, structuralDirective) {
22438
+ let { startName, closeName } = i18nPlaceholder;
22439
+ let flags = I18nParamValueFlags.TemplateTag | I18nParamValueFlags.OpenTag;
22440
+ // For self-closing tags, there is no close tag placeholder. Instead, the start tag
22441
+ // placeholder accounts for the start and close of the element.
22442
+ if (!closeName) {
22443
+ flags |= I18nParamValueFlags.CloseTag;
22444
+ }
22445
+ // If the template is associated with a structural directive, record the structural directive's
22446
+ // start first. Since this template must be in the structural directive's view, we can just
22447
+ // directly use the current i18n block's sub-template index.
22448
+ if (structuralDirective !== undefined) {
22449
+ addParam(i18nContext.params, startName, structuralDirective.handle.slot, i18nBlock.subTemplateIndex, flags);
22450
+ }
22451
+ // Record the start of the template. For the sub-template index, pass the index for the template's
22452
+ // view, rather than the current i18n block's index.
22453
+ addParam(i18nContext.params, startName, slot, getSubTemplateIndexForTemplateTag(job, i18nBlock, view), flags);
22454
+ }
22455
+ /**
22456
+ * Records an i18n param value for the closing of a template.
22457
+ */
22458
+ function recordTemplateClose(job, view, slot, i18nPlaceholder, i18nContext, i18nBlock, structuralDirective) {
22459
+ const { startName, closeName } = i18nPlaceholder;
22460
+ const flags = I18nParamValueFlags.TemplateTag | I18nParamValueFlags.CloseTag;
22461
+ // Self-closing tags don't have a closing tag placeholder, instead the template's closing is
22462
+ // recorded via an additional flag on the template start value.
22463
+ if (closeName) {
22464
+ // Record the closing of the template. For the sub-template index, pass the index for the
22465
+ // template's view, rather than the current i18n block's index.
22466
+ addParam(i18nContext.params, closeName, slot, getSubTemplateIndexForTemplateTag(job, i18nBlock, view), flags);
22467
+ // If the template is associated with a structural directive, record the structural directive's
22468
+ // closing after. Since this template must be in the structural directive's view, we can just
22469
+ // directly use the current i18n block's sub-template index.
22470
+ if (structuralDirective !== undefined) {
22471
+ addParam(i18nContext.params, closeName, structuralDirective.handle.slot, i18nBlock.subTemplateIndex, flags);
22472
+ }
22473
+ }
22474
+ }
21972
22475
  /**
21973
22476
  * Get the subTemplateIndex for the given template op. For template ops, use the subTemplateIndex of
21974
22477
  * the child i18n block inside the template.
21975
22478
  */
21976
- function getSubTemplateIndexForTemplateTag(job, i18nOp, op) {
21977
- for (const childOp of job.views.get(op.xref).create) {
22479
+ function getSubTemplateIndexForTemplateTag(job, i18nOp, view) {
22480
+ for (const childOp of view.create) {
21978
22481
  if (childOp.kind === OpKind.I18nStart) {
21979
22482
  return childOp.subTemplateIndex;
21980
22483
  }
21981
22484
  }
21982
22485
  return i18nOp.subTemplateIndex;
21983
22486
  }
21984
- /** Add a param value to the given params map. */
22487
+ /**
22488
+ * Add a param value to the given params map.
22489
+ */
21985
22490
  function addParam(params, placeholder, value, subTemplateIndex, flags) {
21986
22491
  const values = params.get(placeholder) ?? [];
21987
22492
  values.push({ value, subTemplateIndex, flags });
@@ -22007,14 +22512,19 @@ function resolveI18nExpressionPlaceholders(job) {
22007
22512
  }
22008
22513
  }
22009
22514
  }
22010
- // Keep track of the next available expression index per i18n block.
22515
+ // Keep track of the next available expression index for each i18n message.
22011
22516
  const expressionIndices = new Map();
22517
+ // Keep track of a reference index for each expression.
22518
+ // We use different references for normal i18n expressio and attribute i18n expressions. This is
22519
+ // because child i18n blocks in templates don't get their own context, since they're rolled into
22520
+ // the translated message of the parent, but they may target a different slot.
22521
+ const referenceIndex = (op) => op.usage === I18nExpressionFor.I18nText ? op.i18nOwner : op.context;
22012
22522
  for (const unit of job.units) {
22013
22523
  for (const op of unit.update) {
22014
22524
  if (op.kind === OpKind.I18nExpression) {
22015
22525
  const i18nContext = i18nContexts.get(op.context);
22016
- const index = expressionIndices.get(op.target) || 0;
22017
- const subTemplateIndex = subTemplateIndicies.get(op.target);
22526
+ const index = expressionIndices.get(referenceIndex(op)) || 0;
22527
+ const subTemplateIndex = subTemplateIndicies.get(op.i18nOwner) ?? null;
22018
22528
  // Add the expression index in the appropriate params map.
22019
22529
  const params = op.resolutionTime === I18nParamResolutionTime.Creation ?
22020
22530
  i18nContext.params :
@@ -22026,7 +22536,7 @@ function resolveI18nExpressionPlaceholders(job) {
22026
22536
  flags: I18nParamValueFlags.ExpressionIndex
22027
22537
  });
22028
22538
  params.set(op.i18nPlaceholder, values);
22029
- expressionIndices.set(op.target, index + 1);
22539
+ expressionIndices.set(referenceIndex(op), index + 1);
22030
22540
  }
22031
22541
  }
22032
22542
  }
@@ -22184,12 +22694,20 @@ function processLexicalScope(unit, ops, savedView) {
22184
22694
  }
22185
22695
 
22186
22696
  /**
22187
- * Mapping of security contexts to sanitizer function for that context.
22697
+ * Map of security contexts to their sanitizer function.
22188
22698
  */
22189
- const sanitizers = new Map([
22190
- [SecurityContext.HTML, SanitizerFn.Html], [SecurityContext.SCRIPT, SanitizerFn.Script],
22191
- [SecurityContext.STYLE, SanitizerFn.Style], [SecurityContext.URL, SanitizerFn.Url],
22192
- [SecurityContext.RESOURCE_URL, SanitizerFn.ResourceUrl]
22699
+ const sanitizerFns = new Map([
22700
+ [SecurityContext.HTML, Identifiers.sanitizeHtml],
22701
+ [SecurityContext.RESOURCE_URL, Identifiers.sanitizeResourceUrl],
22702
+ [SecurityContext.SCRIPT, Identifiers.sanitizeScript],
22703
+ [SecurityContext.STYLE, Identifiers.sanitizeStyle], [SecurityContext.URL, Identifiers.sanitizeUrl]
22704
+ ]);
22705
+ /**
22706
+ * Map of security contexts to their trusted value function.
22707
+ */
22708
+ const trustedValueFns = new Map([
22709
+ [SecurityContext.HTML, Identifiers.trustConstantHtml],
22710
+ [SecurityContext.RESOURCE_URL, Identifiers.trustConstantResourceUrl],
22193
22711
  ]);
22194
22712
  /**
22195
22713
  * Resolves sanitization functions for ops that need them.
@@ -22197,24 +22715,61 @@ const sanitizers = new Map([
22197
22715
  function resolveSanitizers(job) {
22198
22716
  for (const unit of job.units) {
22199
22717
  const elements = createOpXrefMap(unit);
22200
- let sanitizerFn;
22718
+ // For normal element bindings we create trusted values for security sensitive constant
22719
+ // attributes. However, for host bindings we skip this step (this matches what
22720
+ // TemplateDefinitionBuilder does).
22721
+ // TODO: Is the TDB behavior correct here?
22722
+ if (job.kind !== CompilationJobKind.Host) {
22723
+ for (const op of unit.create) {
22724
+ if (op.kind === OpKind.ExtractedAttribute) {
22725
+ const trustedValueFn = trustedValueFns.get(getOnlySecurityContext(op.securityContext)) ?? null;
22726
+ op.trustedValueFn = trustedValueFn !== null ? importExpr(trustedValueFn) : null;
22727
+ }
22728
+ }
22729
+ }
22201
22730
  for (const op of unit.update) {
22202
22731
  switch (op.kind) {
22203
22732
  case OpKind.Property:
22204
22733
  case OpKind.Attribute:
22205
- sanitizerFn = sanitizers.get(op.securityContext) || null;
22206
- op.sanitizer = sanitizerFn ? new SanitizerExpr(sanitizerFn) : null;
22734
+ case OpKind.HostProperty:
22735
+ let sanitizerFn = null;
22736
+ if (Array.isArray(op.securityContext) && op.securityContext.length === 2 &&
22737
+ op.securityContext.indexOf(SecurityContext.URL) > -1 &&
22738
+ op.securityContext.indexOf(SecurityContext.RESOURCE_URL) > -1) {
22739
+ // When the host element isn't known, some URL attributes (such as "src" and "href") may
22740
+ // be part of multiple different security contexts. In this case we use special
22741
+ // sanitization function and select the actual sanitizer at runtime based on a tag name
22742
+ // that is provided while invoking sanitization function.
22743
+ sanitizerFn = Identifiers.sanitizeUrlOrResourceUrl;
22744
+ }
22745
+ else {
22746
+ sanitizerFn = sanitizerFns.get(getOnlySecurityContext(op.securityContext)) ?? null;
22747
+ }
22748
+ op.sanitizer = sanitizerFn !== null ? importExpr(sanitizerFn) : null;
22207
22749
  // If there was no sanitization function found based on the security context of an
22208
22750
  // attribute/property, check whether this attribute/property is one of the
22209
22751
  // security-sensitive <iframe> attributes (and that the current element is actually an
22210
22752
  // <iframe>).
22211
22753
  if (op.sanitizer === null) {
22212
- const ownerOp = elements.get(op.target);
22213
- if (ownerOp === undefined || !isElementOrContainerOp(ownerOp)) {
22214
- throw Error('Property should have an element-like owner');
22754
+ let isIframe = false;
22755
+ if (job.kind === CompilationJobKind.Host || op.kind === OpKind.HostProperty) {
22756
+ // Note: for host bindings defined on a directive, we do not try to find all
22757
+ // possible places where it can be matched, so we can not determine whether
22758
+ // the host element is an <iframe>. In this case, we just assume it is and append a
22759
+ // validation function, which is invoked at runtime and would have access to the
22760
+ // underlying DOM element to check if it's an <iframe> and if so - run extra checks.
22761
+ isIframe = true;
22762
+ }
22763
+ else {
22764
+ // For a normal binding we can just check if the element its on is an iframe.
22765
+ const ownerOp = elements.get(op.target);
22766
+ if (ownerOp === undefined || !isElementOrContainerOp(ownerOp)) {
22767
+ throw Error('Property should have an element-like owner');
22768
+ }
22769
+ isIframe = isIframeElement$1(ownerOp);
22215
22770
  }
22216
- if (isIframeElement$1(ownerOp) && isIframeSecuritySensitiveAttr(op.name)) {
22217
- op.sanitizer = new SanitizerExpr(SanitizerFn.IframeAttribute);
22771
+ if (isIframe && isIframeSecuritySensitiveAttr(op.name)) {
22772
+ op.sanitizer = importExpr(Identifiers.validateIframeAttribute);
22218
22773
  }
22219
22774
  }
22220
22775
  break;
@@ -22228,6 +22783,22 @@ function resolveSanitizers(job) {
22228
22783
  function isIframeElement$1(op) {
22229
22784
  return op.kind === OpKind.ElementStart && op.tag?.toLowerCase() === 'iframe';
22230
22785
  }
22786
+ /**
22787
+ * Asserts that there is only a single security context and returns it.
22788
+ */
22789
+ function getOnlySecurityContext(securityContext) {
22790
+ if (Array.isArray(securityContext)) {
22791
+ if (securityContext.length > 1) {
22792
+ // TODO: What should we do here? TDB just took the first one, but this feels like something we
22793
+ // would want to know about and create a special case for like we did for Url/ResourceUrl. My
22794
+ // guess is that, outside of the Url/ResourceUrl case, this never actually happens. If there
22795
+ // do turn out to be other cases, throwing an error until we can address it feels safer.
22796
+ throw Error(`AssertionError: Ambiguous security context`);
22797
+ }
22798
+ return securityContext[0] || SecurityContext.NONE;
22799
+ }
22800
+ return securityContext;
22801
+ }
22231
22802
 
22232
22803
  /**
22233
22804
  * When inside of a listener, we may need access to one or more enclosing views. Therefore, each
@@ -22331,6 +22902,8 @@ function allocateSlots(job) {
22331
22902
  // operation itself, so it can be emitted later.
22332
22903
  const childView = job.views.get(op.xref);
22333
22904
  op.decls = childView.decls;
22905
+ // TODO: currently we handle the decls for the RepeaterCreate empty template in the reify
22906
+ // phase. We should handle that here instead.
22334
22907
  }
22335
22908
  }
22336
22909
  }
@@ -22654,6 +23227,8 @@ function countVariables(job) {
22654
23227
  }
22655
23228
  const childView = job.views.get(op.xref);
22656
23229
  op.vars = childView.vars;
23230
+ // TODO: currently we handle the vars for the RepeaterCreate empty template in the reify
23231
+ // phase. We should handle that here instead.
22657
23232
  }
22658
23233
  }
22659
23234
  }
@@ -23173,12 +23748,12 @@ const phases = [
23173
23748
  { kind: CompilationJobKind.Tmpl, fn: removeContentSelectors },
23174
23749
  { kind: CompilationJobKind.Host, fn: parseHostStyleProperties },
23175
23750
  { kind: CompilationJobKind.Tmpl, fn: emitNamespaceChanges },
23176
- { kind: CompilationJobKind.Both, fn: specializeStyleBindings },
23177
- { kind: CompilationJobKind.Both, fn: specializeBindings },
23178
23751
  { kind: CompilationJobKind.Tmpl, fn: propagateI18nBlocks },
23179
23752
  { kind: CompilationJobKind.Tmpl, fn: wrapI18nIcus },
23180
- { kind: CompilationJobKind.Tmpl, fn: createI18nContexts },
23753
+ { kind: CompilationJobKind.Both, fn: specializeStyleBindings },
23754
+ { kind: CompilationJobKind.Both, fn: specializeBindings },
23181
23755
  { kind: CompilationJobKind.Both, fn: extractAttributes },
23756
+ { kind: CompilationJobKind.Tmpl, fn: createI18nContexts },
23182
23757
  { kind: CompilationJobKind.Both, fn: parseExtractedStyles },
23183
23758
  { kind: CompilationJobKind.Tmpl, fn: removeEmptyBindings },
23184
23759
  { kind: CompilationJobKind.Both, fn: collapseSingletonInterpolations },
@@ -23186,14 +23761,17 @@ const phases = [
23186
23761
  { kind: CompilationJobKind.Tmpl, fn: generateConditionalExpressions },
23187
23762
  { kind: CompilationJobKind.Tmpl, fn: createPipes },
23188
23763
  { kind: CompilationJobKind.Tmpl, fn: configureDeferInstructions },
23189
- { kind: CompilationJobKind.Tmpl, fn: extractI18nText },
23764
+ { kind: CompilationJobKind.Tmpl, fn: convertI18nText },
23765
+ { kind: CompilationJobKind.Tmpl, fn: convertI18nBindings },
23766
+ { kind: CompilationJobKind.Tmpl, fn: removeUnusedI18nAttributesOps },
23767
+ { kind: CompilationJobKind.Tmpl, fn: assignI18nSlotDependencies },
23190
23768
  { kind: CompilationJobKind.Tmpl, fn: applyI18nExpressions },
23191
23769
  { kind: CompilationJobKind.Tmpl, fn: createVariadicPipes },
23192
23770
  { kind: CompilationJobKind.Both, fn: generatePureLiteralStructures },
23193
23771
  { kind: CompilationJobKind.Tmpl, fn: generateProjectionDefs },
23194
23772
  { kind: CompilationJobKind.Tmpl, fn: generateVariables },
23195
23773
  { kind: CompilationJobKind.Tmpl, fn: saveAndRestoreView },
23196
- { kind: CompilationJobKind.Tmpl, fn: deleteAnyCasts },
23774
+ { kind: CompilationJobKind.Both, fn: deleteAnyCasts },
23197
23775
  { kind: CompilationJobKind.Both, fn: resolveDollarEvent },
23198
23776
  { kind: CompilationJobKind.Tmpl, fn: generateRepeaterDerivedVars },
23199
23777
  { kind: CompilationJobKind.Tmpl, fn: generateTrackVariables },
@@ -23201,7 +23779,7 @@ const phases = [
23201
23779
  { kind: CompilationJobKind.Tmpl, fn: resolveDeferTargetNames },
23202
23780
  { kind: CompilationJobKind.Tmpl, fn: optimizeTrackFns },
23203
23781
  { kind: CompilationJobKind.Both, fn: resolveContexts },
23204
- { kind: CompilationJobKind.Tmpl, fn: resolveSanitizers }, // TODO: run in both
23782
+ { kind: CompilationJobKind.Both, fn: resolveSanitizers },
23205
23783
  { kind: CompilationJobKind.Tmpl, fn: liftLocalRefs },
23206
23784
  { kind: CompilationJobKind.Both, fn: generateNullishCoalesceExpressions },
23207
23785
  { kind: CompilationJobKind.Both, fn: expandSafeReads },
@@ -23216,7 +23794,6 @@ const phases = [
23216
23794
  { kind: CompilationJobKind.Tmpl, fn: collectI18nConsts },
23217
23795
  { kind: CompilationJobKind.Tmpl, fn: collectConstExpressions },
23218
23796
  { kind: CompilationJobKind.Both, fn: collectElementConsts },
23219
- { kind: CompilationJobKind.Tmpl, fn: assignI18nSlotDependencies },
23220
23797
  { kind: CompilationJobKind.Tmpl, fn: removeI18nContexts },
23221
23798
  { kind: CompilationJobKind.Both, fn: countVariables },
23222
23799
  { kind: CompilationJobKind.Tmpl, fn: generateAdvance },
@@ -23338,6 +23915,10 @@ function emitHostBindingFunction(job) {
23338
23915
  }
23339
23916
 
23340
23917
  const compatibilityMode = CompatibilityMode.TemplateDefinitionBuilder;
23918
+ // Schema containing DOM elements and their properties.
23919
+ const domSchema = new DomElementSchemaRegistry();
23920
+ // Tag name of the `ng-template` element.
23921
+ const NG_TEMPLATE_TAG_NAME$1 = 'ng-template';
23341
23922
  /**
23342
23923
  * Process a template AST and convert it into a `ComponentCompilation` in the intermediate
23343
23924
  * representation.
@@ -23355,10 +23936,24 @@ function ingestComponent(componentName, template, constantPool, relativeContextF
23355
23936
  function ingestHostBinding(input, bindingParser, constantPool) {
23356
23937
  const job = new HostBindingCompilationJob(input.componentName, constantPool, compatibilityMode);
23357
23938
  for (const property of input.properties ?? []) {
23358
- ingestHostProperty(job, property, false);
23939
+ let bindingKind = BindingKind.Property;
23940
+ // TODO: this should really be handled in the parser.
23941
+ if (property.name.startsWith('attr.')) {
23942
+ property.name = property.name.substring('attr.'.length);
23943
+ bindingKind = BindingKind.Attribute;
23944
+ }
23945
+ if (property.isAnimation) {
23946
+ bindingKind = BindingKind.Animation;
23947
+ }
23948
+ const securityContexts = bindingParser
23949
+ .calcPossibleSecurityContexts(input.componentSelector, property.name, bindingKind === BindingKind.Attribute)
23950
+ .filter(context => context !== SecurityContext.NONE);
23951
+ ingestHostProperty(job, property, bindingKind, false, securityContexts);
23359
23952
  }
23360
23953
  for (const [name, expr] of Object.entries(input.attributes) ?? []) {
23361
- ingestHostAttribute(job, name, expr);
23954
+ const securityContexts = bindingParser.calcPossibleSecurityContexts(input.componentSelector, name, true)
23955
+ .filter(context => context !== SecurityContext.NONE);
23956
+ ingestHostAttribute(job, name, expr, securityContexts);
23362
23957
  }
23363
23958
  for (const event of input.events ?? []) {
23364
23959
  ingestHostEvent(job, event);
@@ -23367,34 +23962,27 @@ function ingestHostBinding(input, bindingParser, constantPool) {
23367
23962
  }
23368
23963
  // TODO: We should refactor the parser to use the same types and structures for host bindings as
23369
23964
  // with ordinary components. This would allow us to share a lot more ingestion code.
23370
- function ingestHostProperty(job, property, isTextAttribute) {
23965
+ function ingestHostProperty(job, property, bindingKind, isTextAttribute, securityContexts) {
23371
23966
  let expression;
23372
23967
  const ast = property.expression.ast;
23373
23968
  if (ast instanceof Interpolation$1) {
23374
- expression = new Interpolation(ast.strings, ast.expressions.map(expr => convertAst(expr, job, property.sourceSpan)));
23969
+ expression = new Interpolation(ast.strings, ast.expressions.map(expr => convertAst(expr, job, property.sourceSpan)), []);
23375
23970
  }
23376
23971
  else {
23377
23972
  expression = convertAst(ast, job, property.sourceSpan);
23378
23973
  }
23379
- let bindingKind = BindingKind.Property;
23380
- // TODO: this should really be handled in the parser.
23381
- if (property.name.startsWith('attr.')) {
23382
- property.name = property.name.substring('attr.'.length);
23383
- bindingKind = BindingKind.Attribute;
23384
- }
23385
- if (property.isAnimation) {
23386
- bindingKind = BindingKind.Animation;
23387
- }
23388
- job.root.update.push(createBindingOp(job.root.xref, bindingKind, property.name, expression, null, SecurityContext
23389
- .NONE /* TODO: what should we pass as security context? Passing NONE for now. */, isTextAttribute, false, property.sourceSpan));
23974
+ job.root.update.push(createBindingOp(job.root.xref, bindingKind, property.name, expression, null, securityContexts, isTextAttribute, false, null, /* TODO: How do Host bindings handle i18n attrs? */ null, property.sourceSpan));
23390
23975
  }
23391
- function ingestHostAttribute(job, name, value) {
23392
- const attrBinding = createBindingOp(job.root.xref, BindingKind.Attribute, name, value, null, SecurityContext.NONE, true, false,
23976
+ function ingestHostAttribute(job, name, value, securityContexts) {
23977
+ const attrBinding = createBindingOp(job.root.xref, BindingKind.Attribute, name, value, null, securityContexts, true, false, null,
23978
+ /* TODO */ null,
23393
23979
  /* TODO: host attribute source spans */ null);
23394
23980
  job.root.update.push(attrBinding);
23395
23981
  }
23396
23982
  function ingestHostEvent(job, event) {
23397
- const eventBinding = createListenerOp(job.root.xref, new SlotHandle(), event.name, null, event.targetOrPhase, true, event.sourceSpan);
23983
+ const [phase, target] = event.type === 0 /* e.ParsedEventType.Regular */ ? [null, event.targetOrPhase] :
23984
+ [event.targetOrPhase, null];
23985
+ const eventBinding = createListenerOp(job.root.xref, new SlotHandle(), event.name, null, [], phase, target, true, event.sourceSpan);
23398
23986
  // TODO: Can this be a chain?
23399
23987
  eventBinding.handlerOps.push(createStatementOp(new ReturnStatement(convertAst(event.handler.ast, job, event.sourceSpan), event.handlerSpan)));
23400
23988
  job.root.create.push(eventBinding);
@@ -23451,8 +24039,14 @@ function ingestElement(unit, element) {
23451
24039
  const [namespaceKey, elementName] = splitNsName(element.name);
23452
24040
  const startOp = createElementStartOp(elementName, id, namespaceForKey(namespaceKey), element.i18n instanceof TagPlaceholder ? element.i18n : undefined, element.startSourceSpan);
23453
24041
  unit.create.push(startOp);
23454
- ingestBindings(unit, startOp, element);
24042
+ ingestElementBindings(unit, startOp, element);
23455
24043
  ingestReferences(startOp, element);
24044
+ // Start i18n, if needed, goes after the element create and bindings, but before the nodes
24045
+ let i18nBlockId = null;
24046
+ if (element.i18n instanceof Message) {
24047
+ i18nBlockId = unit.job.allocateXrefId();
24048
+ unit.create.push(createI18nStartOp(i18nBlockId, element.i18n));
24049
+ }
23456
24050
  ingestNodes(unit, element.children);
23457
24051
  // The source span for the end op is typically the element closing tag. However, if no closing tag
23458
24052
  // exists, such as in `<input>`, we use the start source span instead. Usually the start and end
@@ -23462,9 +24056,7 @@ function ingestElement(unit, element) {
23462
24056
  const endOp = createElementEndOp(id, element.endSourceSpan ?? element.startSourceSpan);
23463
24057
  unit.create.push(endOp);
23464
24058
  // If there is an i18n message associated with this element, insert i18n start and end ops.
23465
- if (element.i18n instanceof Message) {
23466
- const i18nBlockId = unit.job.allocateXrefId();
23467
- OpList.insertAfter(createI18nStartOp(i18nBlockId, element.i18n), startOp);
24059
+ if (i18nBlockId !== null) {
23468
24060
  OpList.insertBefore(createI18nEndOp(i18nBlockId), endOp);
23469
24061
  }
23470
24062
  }
@@ -23487,10 +24079,11 @@ function ingestTemplate(unit, tmpl) {
23487
24079
  const functionNameSuffix = tagNameWithoutNamespace === null ?
23488
24080
  '' :
23489
24081
  prefixWithNamespace(tagNameWithoutNamespace, namespace);
23490
- const tplOp = createTemplateOp(childView.xref, tagNameWithoutNamespace, functionNameSuffix, namespace, i18nPlaceholder, tmpl.startSourceSpan);
23491
- unit.create.push(tplOp);
23492
- ingestBindings(unit, tplOp, tmpl);
23493
- ingestReferences(tplOp, tmpl);
24082
+ const templateKind = isPlainTemplate(tmpl) ? TemplateKind.NgTemplate : TemplateKind.Structural;
24083
+ const templateOp = createTemplateOp(childView.xref, templateKind, tagNameWithoutNamespace, functionNameSuffix, namespace, i18nPlaceholder, tmpl.startSourceSpan);
24084
+ unit.create.push(templateOp);
24085
+ ingestTemplateBindings(unit, templateOp, tmpl, templateKind);
24086
+ ingestReferences(templateOp, tmpl);
23494
24087
  ingestNodes(childView, tmpl.children);
23495
24088
  for (const { name, value } of tmpl.variables) {
23496
24089
  childView.contextVariables.set(name, value !== '' ? value : '$implicit');
@@ -23498,19 +24091,24 @@ function ingestTemplate(unit, tmpl) {
23498
24091
  // If this is a plain template and there is an i18n message associated with it, insert i18n start
23499
24092
  // and end ops. For structural directive templates, the i18n ops will be added when ingesting the
23500
24093
  // element/template the directive is placed on.
23501
- if (isPlainTemplate(tmpl) && tmpl.i18n instanceof Message) {
24094
+ if (templateKind === TemplateKind.NgTemplate && tmpl.i18n instanceof Message) {
23502
24095
  const id = unit.job.allocateXrefId();
23503
24096
  OpList.insertAfter(createI18nStartOp(id, tmpl.i18n), childView.create.head);
23504
24097
  OpList.insertBefore(createI18nEndOp(id), childView.create.tail);
23505
24098
  }
23506
24099
  }
23507
24100
  /**
23508
- * Ingest a literal text node from the AST into the given `ViewCompilation`.
24101
+ * Ingest a content node from the AST into the given `ViewCompilation`.
23509
24102
  */
23510
24103
  function ingestContent(unit, content) {
23511
- const op = createProjectionOp(unit.job.allocateXrefId(), content.selector, content.sourceSpan);
24104
+ if (content.i18n !== undefined && !(content.i18n instanceof TagPlaceholder)) {
24105
+ throw Error(`Unhandled i18n metadata type for element: ${content.i18n.constructor.name}`);
24106
+ }
24107
+ const attrs = content.attributes.flatMap(a => [a.name, a.value]);
24108
+ const op = createProjectionOp(unit.job.allocateXrefId(), content.selector, content.i18n, attrs, content.sourceSpan);
23512
24109
  for (const attr of content.attributes) {
23513
- ingestBinding(unit, op.xref, attr.name, literal(attr.value), 1 /* e.BindingType.Attribute */, null, SecurityContext.NONE, attr.sourceSpan, BindingFlags.TextValue);
24110
+ const securityContext = domSchema.securityContext(content.name, attr.name, true);
24111
+ unit.update.push(createBindingOp(op.xref, BindingKind.Attribute, attr.name, literal(attr.value), null, securityContext, true, false, null, asMessage(attr.i18n), attr.sourceSpan));
23514
24112
  }
23515
24113
  unit.create.push(op);
23516
24114
  }
@@ -23535,6 +24133,7 @@ function ingestBoundText(unit, text, i18nPlaceholders) {
23535
24133
  throw Error(`Unhandled i18n metadata type for text interpolation: ${text.i18n?.constructor.name}`);
23536
24134
  }
23537
24135
  if (i18nPlaceholders === undefined) {
24136
+ // TODO: We probably can just use the placeholders field, instead of walking the AST.
23538
24137
  i18nPlaceholders = text.i18n instanceof Container ?
23539
24138
  text.i18n.children
23540
24139
  .filter((node) => node instanceof Placeholder)
@@ -23550,7 +24149,7 @@ function ingestBoundText(unit, text, i18nPlaceholders) {
23550
24149
  // interpolation. We copy that behavior in compatibility mode.
23551
24150
  // TODO: is it actually correct to generate these extra maps in modern mode?
23552
24151
  const baseSourceSpan = unit.job.compatibility ? null : text.sourceSpan;
23553
- unit.update.push(createInterpolateTextOp(textXref, new Interpolation(value.strings, value.expressions.map(expr => convertAst(expr, unit.job, baseSourceSpan))), i18nPlaceholders, text.sourceSpan));
24152
+ unit.update.push(createInterpolateTextOp(textXref, new Interpolation(value.strings, value.expressions.map(expr => convertAst(expr, unit.job, baseSourceSpan)), i18nPlaceholders), text.sourceSpan));
23554
24153
  }
23555
24154
  /**
23556
24155
  * Ingest an `@if` block into the given `ViewCompilation`.
@@ -23571,14 +24170,21 @@ function ingestIfBlock(unit, ifBlock) {
23571
24170
  if (ifCase.expressionAlias !== null) {
23572
24171
  cView.contextVariables.set(ifCase.expressionAlias.name, CTX_REF);
23573
24172
  }
23574
- const tmplOp = createTemplateOp(cView.xref, tagName, 'Conditional', Namespace.HTML, undefined /* TODO: figure out how i18n works with new control flow */, ifCase.sourceSpan);
23575
- unit.create.push(tmplOp);
24173
+ let ifCaseI18nMeta = undefined;
24174
+ if (ifCase.i18n !== undefined) {
24175
+ if (!(ifCase.i18n instanceof BlockPlaceholder)) {
24176
+ throw Error(`Unhandled i18n metadata type for if block: ${ifCase.i18n?.constructor.name}`);
24177
+ }
24178
+ ifCaseI18nMeta = ifCase.i18n;
24179
+ }
24180
+ const templateOp = createTemplateOp(cView.xref, TemplateKind.Block, tagName, 'Conditional', Namespace.HTML, ifCaseI18nMeta, ifCase.sourceSpan);
24181
+ unit.create.push(templateOp);
23576
24182
  if (firstXref === null) {
23577
24183
  firstXref = cView.xref;
23578
- firstSlotHandle = tmplOp.handle;
24184
+ firstSlotHandle = templateOp.handle;
23579
24185
  }
23580
24186
  const caseExpr = ifCase.expression ? convertAst(ifCase.expression, unit.job, null) : null;
23581
- const conditionalCaseExpr = new ConditionalCaseExpr(caseExpr, tmplOp.xref, tmplOp.handle, ifCase.expressionAlias);
24187
+ const conditionalCaseExpr = new ConditionalCaseExpr(caseExpr, templateOp.xref, templateOp.handle, ifCase.expressionAlias);
23582
24188
  conditions.push(conditionalCaseExpr);
23583
24189
  ingestNodes(cView, ifCase.children);
23584
24190
  }
@@ -23594,29 +24200,39 @@ function ingestSwitchBlock(unit, switchBlock) {
23594
24200
  let conditions = [];
23595
24201
  for (const switchCase of switchBlock.cases) {
23596
24202
  const cView = unit.job.allocateView(unit.xref);
23597
- const tmplOp = createTemplateOp(cView.xref, null, 'Case', Namespace.HTML, undefined /* TODO: figure out how i18n works with new control flow */, switchCase.sourceSpan);
23598
- unit.create.push(tmplOp);
24203
+ let switchCaseI18nMeta = undefined;
24204
+ if (switchCase.i18n !== undefined) {
24205
+ if (!(switchCase.i18n instanceof BlockPlaceholder)) {
24206
+ throw Error(`Unhandled i18n metadata type for switch block: ${switchCase.i18n?.constructor.name}`);
24207
+ }
24208
+ switchCaseI18nMeta = switchCase.i18n;
24209
+ }
24210
+ const templateOp = createTemplateOp(cView.xref, TemplateKind.Block, null, 'Case', Namespace.HTML, switchCaseI18nMeta, switchCase.sourceSpan);
24211
+ unit.create.push(templateOp);
23599
24212
  if (firstXref === null) {
23600
24213
  firstXref = cView.xref;
23601
- firstSlotHandle = tmplOp.handle;
24214
+ firstSlotHandle = templateOp.handle;
23602
24215
  }
23603
24216
  const caseExpr = switchCase.expression ?
23604
24217
  convertAst(switchCase.expression, unit.job, switchBlock.startSourceSpan) :
23605
24218
  null;
23606
- const conditionalCaseExpr = new ConditionalCaseExpr(caseExpr, tmplOp.xref, tmplOp.handle);
24219
+ const conditionalCaseExpr = new ConditionalCaseExpr(caseExpr, templateOp.xref, templateOp.handle);
23607
24220
  conditions.push(conditionalCaseExpr);
23608
24221
  ingestNodes(cView, switchCase.children);
23609
24222
  }
23610
24223
  const conditional = createConditionalOp(firstXref, firstSlotHandle, convertAst(switchBlock.expression, unit.job, null), conditions, switchBlock.sourceSpan);
23611
24224
  unit.update.push(conditional);
23612
24225
  }
23613
- function ingestDeferView(unit, suffix, children, sourceSpan) {
24226
+ function ingestDeferView(unit, suffix, i18nMeta, children, sourceSpan) {
24227
+ if (i18nMeta !== undefined && !(i18nMeta instanceof BlockPlaceholder)) {
24228
+ throw Error('Unhandled i18n metadata type for defer block');
24229
+ }
23614
24230
  if (children === undefined) {
23615
24231
  return null;
23616
24232
  }
23617
24233
  const secondaryView = unit.job.allocateView(unit.xref);
23618
24234
  ingestNodes(secondaryView, children);
23619
- const templateOp = createTemplateOp(secondaryView.xref, null, `Defer${suffix}`, Namespace.HTML, undefined, sourceSpan);
24235
+ const templateOp = createTemplateOp(secondaryView.xref, TemplateKind.Block, null, `Defer${suffix}`, Namespace.HTML, i18nMeta, sourceSpan);
23620
24236
  unit.create.push(templateOp);
23621
24237
  return templateOp;
23622
24238
  }
@@ -23626,10 +24242,10 @@ function ingestDeferBlock(unit, deferBlock) {
23626
24242
  throw new Error(`AssertionError: unable to find metadata for deferred block`);
23627
24243
  }
23628
24244
  // Generate the defer main view and all secondary views.
23629
- const main = ingestDeferView(unit, '', deferBlock.children, deferBlock.sourceSpan);
23630
- const loading = ingestDeferView(unit, 'Loading', deferBlock.loading?.children, deferBlock.loading?.sourceSpan);
23631
- const placeholder = ingestDeferView(unit, 'Placeholder', deferBlock.placeholder?.children, deferBlock.placeholder?.sourceSpan);
23632
- const error = ingestDeferView(unit, 'Error', deferBlock.error?.children, deferBlock.error?.sourceSpan);
24245
+ const main = ingestDeferView(unit, '', deferBlock.i18n, deferBlock.children, deferBlock.sourceSpan);
24246
+ const loading = ingestDeferView(unit, 'Loading', deferBlock.loading?.i18n, deferBlock.loading?.children, deferBlock.loading?.sourceSpan);
24247
+ const placeholder = ingestDeferView(unit, 'Placeholder', deferBlock.placeholder?.i18n, deferBlock.placeholder?.children, deferBlock.placeholder?.sourceSpan);
24248
+ const error = ingestDeferView(unit, 'Error', deferBlock.error?.i18n, deferBlock.error?.children, deferBlock.error?.sourceSpan);
23633
24249
  // Create the main defer op, and ops for all secondary views.
23634
24250
  const deferXref = unit.job.allocateXrefId();
23635
24251
  const deferOp = createDeferOp(deferXref, main.xref, main.handle, blockMeta, deferBlock.sourceSpan);
@@ -23711,12 +24327,7 @@ function ingestIcu(unit, icu) {
23711
24327
  const xref = unit.job.allocateXrefId();
23712
24328
  const icuNode = icu.i18n.nodes[0];
23713
24329
  unit.create.push(createIcuStartOp(xref, icu.i18n, icuFromI18nMessage(icu.i18n).name, null));
23714
- const expressionPlaceholder = icuNode.expressionPlaceholder?.trimEnd();
23715
- if (expressionPlaceholder === undefined || icu.vars[expressionPlaceholder] === undefined) {
23716
- throw Error('ICU should have a text binding');
23717
- }
23718
- ingestBoundText(unit, icu.vars[expressionPlaceholder], [expressionPlaceholder]);
23719
- for (const [placeholder, text] of Object.entries(icu.placeholders)) {
24330
+ for (const [placeholder, text] of Object.entries({ ...icu.vars, ...icu.placeholders })) {
23720
24331
  if (text instanceof BoundText) {
23721
24332
  ingestBoundText(unit, text, [placeholder]);
23722
24333
  }
@@ -23768,8 +24379,17 @@ function ingestForBlock(unit, forBlock) {
23768
24379
  $odd: forBlock.contextVariables.$odd.name,
23769
24380
  $implicit: forBlock.item.name,
23770
24381
  };
24382
+ if (forBlock.i18n !== undefined && !(forBlock.i18n instanceof BlockPlaceholder)) {
24383
+ throw Error('AssertionError: Unhandled i18n metadata type or @for');
24384
+ }
24385
+ if (forBlock.empty?.i18n !== undefined &&
24386
+ !(forBlock.empty.i18n instanceof BlockPlaceholder)) {
24387
+ throw Error('AssertionError: Unhandled i18n metadata type or @empty');
24388
+ }
24389
+ const i18nPlaceholder = forBlock.i18n;
24390
+ const emptyI18nPlaceholder = forBlock.empty?.i18n;
23771
24391
  const tagName = ingestControlFlowInsertionPoint(unit, repeaterView.xref, forBlock);
23772
- const repeaterCreate = createRepeaterCreateOp(repeaterView.xref, emptyView?.xref ?? null, tagName, track, varNames, forBlock.sourceSpan);
24392
+ const repeaterCreate = createRepeaterCreateOp(repeaterView.xref, emptyView?.xref ?? null, tagName, track, varNames, i18nPlaceholder, emptyI18nPlaceholder, forBlock.sourceSpan);
23773
24393
  unit.create.push(repeaterCreate);
23774
24394
  const expression = convertAst(forBlock.expression, unit.job, convertSourceSpan(forBlock.expression.span, forBlock.sourceSpan));
23775
24395
  const repeater = createRepeaterOp(repeaterCreate.xref, repeaterCreate.handle, expression, forBlock.sourceSpan);
@@ -23812,6 +24432,16 @@ function convertAst(ast, job, baseSourceSpan) {
23812
24432
  else if (ast instanceof LiteralPrimitive) {
23813
24433
  return literal(ast.value, undefined, convertSourceSpan(ast.span, baseSourceSpan));
23814
24434
  }
24435
+ else if (ast instanceof Unary) {
24436
+ switch (ast.operator) {
24437
+ case '+':
24438
+ return new UnaryOperatorExpr(UnaryOperator.Plus, convertAst(ast.expr, job, baseSourceSpan), undefined, convertSourceSpan(ast.span, baseSourceSpan));
24439
+ case '-':
24440
+ return new UnaryOperatorExpr(UnaryOperator.Minus, convertAst(ast.expr, job, baseSourceSpan), undefined, convertSourceSpan(ast.span, baseSourceSpan));
24441
+ default:
24442
+ throw new Error(`AssertionError: unknown unary operator ${ast.operator}`);
24443
+ }
24444
+ }
23815
24445
  else if (ast instanceof Binary) {
23816
24446
  const operator = BINARY_OPERATORS.get(ast.operation);
23817
24447
  if (operator === undefined) {
@@ -23870,10 +24500,34 @@ function convertAst(ast, job, baseSourceSpan) {
23870
24500
  else if (ast instanceof EmptyExpr$1) {
23871
24501
  return new EmptyExpr(convertSourceSpan(ast.span, baseSourceSpan));
23872
24502
  }
24503
+ else if (ast instanceof PrefixNot) {
24504
+ return not(convertAst(ast.expression, job, baseSourceSpan), convertSourceSpan(ast.span, baseSourceSpan));
24505
+ }
23873
24506
  else {
23874
24507
  throw new Error(`Unhandled expression type "${ast.constructor.name}" in file "${baseSourceSpan?.start.file.url}"`);
23875
24508
  }
23876
24509
  }
24510
+ function convertAstWithInterpolation(job, value, i18nMeta) {
24511
+ let expression;
24512
+ if (value instanceof Interpolation$1) {
24513
+ expression = new Interpolation(value.strings, value.expressions.map(e => convertAst(e, job, null)), Object.keys(asMessage(i18nMeta)?.placeholders ?? {}));
24514
+ }
24515
+ else if (value instanceof AST) {
24516
+ expression = convertAst(value, job, null);
24517
+ }
24518
+ else {
24519
+ expression = literal(value);
24520
+ }
24521
+ return expression;
24522
+ }
24523
+ // TODO: Can we populate Template binding kinds in ingest?
24524
+ const BINDING_KINDS = new Map([
24525
+ [0 /* e.BindingType.Property */, BindingKind.Property],
24526
+ [1 /* e.BindingType.Attribute */, BindingKind.Attribute],
24527
+ [2 /* e.BindingType.Class */, BindingKind.ClassName],
24528
+ [3 /* e.BindingType.Style */, BindingKind.StyleProperty],
24529
+ [4 /* e.BindingType.Animation */, BindingKind.Animation],
24530
+ ]);
23877
24531
  /**
23878
24532
  * Checks whether the given template is a plain ng-template (as opposed to another kind of template
23879
24533
  * such as a structural directive template or control flow template). This is checked based on the
@@ -23892,128 +24546,184 @@ function convertAst(ast, job, baseSourceSpan) {
23892
24546
  * | `<ng-template *ngIf>` (structural) | null |
23893
24547
  */
23894
24548
  function isPlainTemplate(tmpl) {
23895
- return splitNsName(tmpl.tagName ?? '')[1] === 'ng-template';
24549
+ return splitNsName(tmpl.tagName ?? '')[1] === NG_TEMPLATE_TAG_NAME$1;
23896
24550
  }
23897
24551
  /**
23898
- * Process all of the bindings on an element-like structure in the template AST and convert them
23899
- * to their IR representation.
24552
+ * Ensures that the i18nMeta, if provided, is an i18n.Message.
23900
24553
  */
23901
- function ingestBindings(unit, op, element) {
23902
- let flags = BindingFlags.None;
23903
- if (element instanceof Template) {
23904
- flags |= BindingFlags.OnNgTemplateElement;
23905
- if (element instanceof Template && isPlainTemplate(element)) {
23906
- flags |= BindingFlags.BindingTargetsTemplate;
23907
- }
23908
- const templateAttrFlags = flags | BindingFlags.BindingTargetsTemplate | BindingFlags.IsStructuralTemplateAttribute;
23909
- for (const attr of element.templateAttrs) {
23910
- if (attr instanceof TextAttribute) {
23911
- ingestBinding(unit, op.xref, attr.name, literal(attr.value), 1 /* e.BindingType.Attribute */, null, SecurityContext.NONE, attr.sourceSpan, templateAttrFlags | BindingFlags.TextValue);
23912
- }
23913
- else {
23914
- ingestBinding(unit, op.xref, attr.name, attr.value, attr.type, attr.unit, attr.securityContext, attr.sourceSpan, templateAttrFlags);
23915
- }
23916
- }
24554
+ function asMessage(i18nMeta) {
24555
+ if (i18nMeta == null) {
24556
+ return null;
23917
24557
  }
24558
+ if (!(i18nMeta instanceof Message)) {
24559
+ throw Error(`Expected i18n meta to be a Message, but got: ${i18nMeta.constructor.name}`);
24560
+ }
24561
+ return i18nMeta;
24562
+ }
24563
+ /**
24564
+ * Process all of the bindings on an element in the template AST and convert them to their IR
24565
+ * representation.
24566
+ */
24567
+ function ingestElementBindings(unit, op, element) {
24568
+ let bindings = new Array();
23918
24569
  for (const attr of element.attributes) {
23919
- // This is only attribute TextLiteral bindings, such as `attr.foo="bar"`. This can never be
23920
- // `[attr.foo]="bar"` or `attr.foo="{{bar}}"`, both of which will be handled as inputs with
23921
- // `BindingType.Attribute`.
23922
- ingestBinding(unit, op.xref, attr.name, literal(attr.value), 1 /* e.BindingType.Attribute */, null, SecurityContext.NONE, attr.sourceSpan, flags | BindingFlags.TextValue);
24570
+ // Attribute literal bindings, such as `attr.foo="bar"`.
24571
+ const securityContext = domSchema.securityContext(element.name, attr.name, true);
24572
+ 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));
23923
24573
  }
23924
24574
  for (const input of element.inputs) {
23925
- ingestBinding(unit, op.xref, input.name, input.value, input.type, input.unit, input.securityContext, input.sourceSpan, flags);
24575
+ // All dynamic bindings (both attribute and property bindings).
24576
+ 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));
23926
24577
  }
24578
+ unit.create.push(bindings.filter((b) => b?.kind === OpKind.ExtractedAttribute));
24579
+ unit.update.push(bindings.filter((b) => b?.kind === OpKind.Binding));
23927
24580
  for (const output of element.outputs) {
23928
- let listenerOp;
23929
- if (output.type === 1 /* e.ParsedEventType.Animation */) {
23930
- if (output.phase === null) {
23931
- throw Error('Animation listener should have a phase');
23932
- }
24581
+ if (output.type === 1 /* e.ParsedEventType.Animation */ && output.phase === null) {
24582
+ throw Error('Animation listener should have a phase');
23933
24583
  }
23934
- if (element instanceof Template && !isPlainTemplate(element)) {
23935
- unit.create.push(createExtractedAttributeOp(op.xref, BindingKind.Property, output.name, null));
23936
- continue;
23937
- }
23938
- listenerOp = createListenerOp(op.xref, op.handle, output.name, op.tag, output.phase, false, output.sourceSpan);
23939
- // if output.handler is a chain, then push each statement from the chain separately, and
23940
- // return the last one?
23941
- let handlerExprs;
23942
- let handler = output.handler;
23943
- if (handler instanceof ASTWithSource) {
23944
- handler = handler.ast;
23945
- }
23946
- if (handler instanceof Chain) {
23947
- handlerExprs = handler.expressions;
24584
+ 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));
24585
+ }
24586
+ // If any of the bindings on this element have an i18n message, then an i18n attrs configuration
24587
+ // op is also required.
24588
+ if (bindings.some(b => b?.i18nMessage) !== null) {
24589
+ unit.create.push(createI18nAttributesOp(unit.job.allocateXrefId(), new SlotHandle(), op.xref));
24590
+ }
24591
+ }
24592
+ /**
24593
+ * Process all of the bindings on a template in the template AST and convert them to their IR
24594
+ * representation.
24595
+ */
24596
+ function ingestTemplateBindings(unit, op, template, templateKind) {
24597
+ let bindings = new Array();
24598
+ for (const attr of template.templateAttrs) {
24599
+ if (attr instanceof TextAttribute) {
24600
+ const securityContext = domSchema.securityContext(NG_TEMPLATE_TAG_NAME$1, attr.name, true);
24601
+ bindings.push(createTemplateBinding(unit, op.xref, 1 /* e.BindingType.Attribute */, attr.name, attr.value, null, securityContext, true, templateKind, asMessage(attr.i18n), attr.sourceSpan));
23948
24602
  }
23949
24603
  else {
23950
- handlerExprs = [handler];
24604
+ bindings.push(createTemplateBinding(unit, op.xref, attr.type, attr.name, astOf(attr.value), attr.unit, attr.securityContext, true, templateKind, asMessage(attr.i18n), attr.sourceSpan));
23951
24605
  }
23952
- if (handlerExprs.length === 0) {
23953
- throw new Error('Expected listener to have non-empty expression list.');
24606
+ }
24607
+ for (const attr of template.attributes) {
24608
+ // Attribute literal bindings, such as `attr.foo="bar"`.
24609
+ const securityContext = domSchema.securityContext(NG_TEMPLATE_TAG_NAME$1, attr.name, true);
24610
+ bindings.push(createTemplateBinding(unit, op.xref, 1 /* e.BindingType.Attribute */, attr.name, attr.value, null, securityContext, false, templateKind, asMessage(attr.i18n), attr.sourceSpan));
24611
+ }
24612
+ for (const input of template.inputs) {
24613
+ // Dynamic bindings (both attribute and property bindings).
24614
+ bindings.push(createTemplateBinding(unit, op.xref, input.type, input.name, astOf(input.value), input.unit, input.securityContext, false, templateKind, asMessage(input.i18n), input.sourceSpan));
24615
+ }
24616
+ unit.create.push(bindings.filter((b) => b?.kind === OpKind.ExtractedAttribute));
24617
+ unit.update.push(bindings.filter((b) => b?.kind === OpKind.Binding));
24618
+ for (const output of template.outputs) {
24619
+ if (output.type === 1 /* e.ParsedEventType.Animation */ && output.phase === null) {
24620
+ throw Error('Animation listener should have a phase');
23954
24621
  }
23955
- const expressions = handlerExprs.map(expr => convertAst(expr, unit.job, output.handlerSpan));
23956
- const returnExpr = expressions.pop();
23957
- for (const expr of expressions) {
23958
- const stmtOp = createStatementOp(new ExpressionStatement(expr, expr.sourceSpan));
23959
- listenerOp.handlerOps.push(stmtOp);
24622
+ if (templateKind === TemplateKind.NgTemplate) {
24623
+ 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));
24624
+ }
24625
+ if (templateKind === TemplateKind.Structural &&
24626
+ output.type !== 1 /* e.ParsedEventType.Animation */) {
24627
+ // Animation bindings are excluded from the structural template's const array.
24628
+ const securityContext = domSchema.securityContext(NG_TEMPLATE_TAG_NAME$1, output.name, false);
24629
+ unit.create.push(createExtractedAttributeOp(op.xref, BindingKind.Property, output.name, null, null, null, securityContext));
23960
24630
  }
23961
- listenerOp.handlerOps.push(createStatementOp(new ReturnStatement(returnExpr, returnExpr.sourceSpan)));
23962
- unit.create.push(listenerOp);
23963
- }
23964
- }
23965
- const BINDING_KINDS = new Map([
23966
- [0 /* e.BindingType.Property */, BindingKind.Property],
23967
- [1 /* e.BindingType.Attribute */, BindingKind.Attribute],
23968
- [2 /* e.BindingType.Class */, BindingKind.ClassName],
23969
- [3 /* e.BindingType.Style */, BindingKind.StyleProperty],
23970
- [4 /* e.BindingType.Animation */, BindingKind.Animation],
23971
- ]);
23972
- var BindingFlags;
23973
- (function (BindingFlags) {
23974
- BindingFlags[BindingFlags["None"] = 0] = "None";
23975
- /**
23976
- * The binding is to a static text literal and not to an expression.
23977
- */
23978
- BindingFlags[BindingFlags["TextValue"] = 1] = "TextValue";
23979
- /**
23980
- * The binding belongs to the `<ng-template>` side of a `t.Template`.
23981
- */
23982
- BindingFlags[BindingFlags["BindingTargetsTemplate"] = 2] = "BindingTargetsTemplate";
23983
- /**
23984
- * The binding is on a structural directive.
23985
- */
23986
- BindingFlags[BindingFlags["IsStructuralTemplateAttribute"] = 4] = "IsStructuralTemplateAttribute";
23987
- /**
23988
- * The binding is on a `t.Template`.
23989
- */
23990
- BindingFlags[BindingFlags["OnNgTemplateElement"] = 8] = "OnNgTemplateElement";
23991
- })(BindingFlags || (BindingFlags = {}));
23992
- function ingestBinding(view, xref, name, value, type, unit, securityContext, sourceSpan, flags) {
23993
- if (value instanceof ASTWithSource) {
23994
- value = value.ast;
23995
24631
  }
23996
- if (flags & BindingFlags.OnNgTemplateElement && !(flags & BindingFlags.BindingTargetsTemplate) &&
23997
- type === 0 /* e.BindingType.Property */) {
23998
- // This binding only exists for later const extraction, and is not an actual binding to be
23999
- // created.
24000
- view.create.push(createExtractedAttributeOp(xref, BindingKind.Property, name, null));
24001
- return;
24632
+ // TODO: Perhaps we could do this in a phase? (It likely wouldn't change the slot indices.)
24633
+ if (bindings.some(b => b?.i18nMessage) !== null) {
24634
+ unit.create.push(createI18nAttributesOp(unit.job.allocateXrefId(), new SlotHandle(), op.xref));
24002
24635
  }
24003
- let expression;
24004
- // TODO: We could easily generate source maps for subexpressions in these cases, but
24005
- // TemplateDefinitionBuilder does not. Should we do so?
24006
- if (value instanceof Interpolation$1) {
24007
- expression = new Interpolation(value.strings, value.expressions.map(expr => convertAst(expr, view.job, null)));
24636
+ }
24637
+ /**
24638
+ * Helper to ingest an individual binding on a template, either an explicit `ng-template`, or an
24639
+ * implicit template created via structural directive.
24640
+ *
24641
+ * Bindings on templates are *extremely* tricky. I have tried to isolate all of the confusing edge
24642
+ * cases into this function, and to comment it well to document the behavior.
24643
+ *
24644
+ * Some of this behavior is intuitively incorrect, and we should consider changing it in the future.
24645
+ *
24646
+ * @param view The compilation unit for the view containing the template.
24647
+ * @param xref The xref of the template op.
24648
+ * @param type The binding type, according to the parser. This is fairly reasonable, e.g. both
24649
+ * dynamic and static attributes have e.BindingType.Attribute.
24650
+ * @param name The binding's name.
24651
+ * @param value The bindings's value, which will either be an input AST expression, or a string
24652
+ * literal. Note that the input AST expression may or may not be const -- it will only be a
24653
+ * string literal if the parser considered it a text binding.
24654
+ * @param unit If the binding has a unit (e.g. `px` for style bindings), then this is the unit.
24655
+ * @param securityContext The security context of the binding.
24656
+ * @param isStructuralTemplateAttribute Whether this binding actually applies to the structural
24657
+ * ng-template. For example, an `ngFor` would actually apply to the structural template. (Most
24658
+ * bindings on structural elements target the inner element, not the template.)
24659
+ * @param templateKind Whether this is an explicit `ng-template` or an implicit template created by
24660
+ * a structural directive. This should never be a block template.
24661
+ * @param i18nMessage The i18n metadata for the binding, if any.
24662
+ * @param sourceSpan The source span of the binding.
24663
+ * @returns An IR binding op, or null if the binding should be skipped.
24664
+ */
24665
+ function createTemplateBinding(view, xref, type, name, value, unit, securityContext, isStructuralTemplateAttribute, templateKind, i18nMessage, sourceSpan) {
24666
+ const isTextBinding = typeof value === 'string';
24667
+ // If this is a structural template, then several kinds of bindings should not result in an
24668
+ // update instruction.
24669
+ if (templateKind === TemplateKind.Structural) {
24670
+ if (!isStructuralTemplateAttribute &&
24671
+ (type === 0 /* e.BindingType.Property */ || type === 2 /* e.BindingType.Class */ ||
24672
+ type === 3 /* e.BindingType.Style */)) {
24673
+ // Because this binding doesn't really target the ng-template, it must be a binding on an
24674
+ // inner node of a structural template. We can't skip it entirely, because we still need it on
24675
+ // the ng-template's consts (e.g. for the purposes of directive matching). However, we should
24676
+ // not generate an update instruction for it.
24677
+ return createExtractedAttributeOp(xref, BindingKind.Property, name, null, null, i18nMessage, securityContext);
24678
+ }
24679
+ if (!isTextBinding && (type === 1 /* e.BindingType.Attribute */ || type === 4 /* e.BindingType.Animation */)) {
24680
+ // Again, this binding doesn't really target the ng-template; it actually targets the element
24681
+ // inside the structural template. In the case of non-text attribute or animation bindings,
24682
+ // the binding doesn't even show up on the ng-template const array, so we just skip it
24683
+ // entirely.
24684
+ return null;
24685
+ }
24008
24686
  }
24009
- else if (value instanceof AST) {
24010
- expression = convertAst(value, view.job, null);
24687
+ let bindingType = BINDING_KINDS.get(type);
24688
+ if (templateKind === TemplateKind.NgTemplate) {
24689
+ // We know we are dealing with bindings directly on an explicit ng-template.
24690
+ // Static attribute bindings should be collected into the const array as k/v pairs. Property
24691
+ // bindings should result in a `property` instruction, and `AttributeMarker.Bindings` const
24692
+ // entries.
24693
+ //
24694
+ // The difficulty is with dynamic attribute, style, and class bindings. These don't really make
24695
+ // sense on an `ng-template` and should probably be parser errors. However,
24696
+ // TemplateDefinitionBuilder generates `property` instructions for them, and so we do that as
24697
+ // well.
24698
+ //
24699
+ // Note that we do have a slight behavior difference with TemplateDefinitionBuilder: although
24700
+ // TDB emits `property` instructions for dynamic attributes, styles, and classes, only styles
24701
+ // and classes also get const collected into the `AttributeMarker.Bindings` field. Dynamic
24702
+ // attribute bindings are missing from the consts entirely. We choose to emit them into the
24703
+ // consts field anyway, to avoid creating special cases for something so arcane and nonsensical.
24704
+ if (type === 2 /* e.BindingType.Class */ || type === 3 /* e.BindingType.Style */ ||
24705
+ (type === 1 /* e.BindingType.Attribute */ && !isTextBinding)) {
24706
+ // TODO: These cases should be parse errors.
24707
+ bindingType = BindingKind.Property;
24708
+ }
24011
24709
  }
24012
- else {
24013
- expression = value;
24710
+ return createBindingOp(xref, bindingType, name, convertAstWithInterpolation(view.job, value, i18nMessage), unit, securityContext, isTextBinding, isStructuralTemplateAttribute, templateKind, i18nMessage, sourceSpan);
24711
+ }
24712
+ function makeListenerHandlerOps(unit, handler, handlerSpan) {
24713
+ handler = astOf(handler);
24714
+ const handlerOps = new Array();
24715
+ let handlerExprs = handler instanceof Chain ? handler.expressions : [handler];
24716
+ if (handlerExprs.length === 0) {
24717
+ throw new Error('Expected listener to have non-empty expression list.');
24014
24718
  }
24015
- const kind = BINDING_KINDS.get(type);
24016
- view.update.push(createBindingOp(xref, kind, name, expression, unit, securityContext, !!(flags & BindingFlags.TextValue), !!(flags & BindingFlags.IsStructuralTemplateAttribute), sourceSpan));
24719
+ const expressions = handlerExprs.map(expr => convertAst(expr, unit.job, handlerSpan));
24720
+ const returnExpr = expressions.pop();
24721
+ handlerOps.push(...expressions.map(e => createStatementOp(new ExpressionStatement(e, e.sourceSpan))));
24722
+ handlerOps.push(createStatementOp(new ReturnStatement(returnExpr, returnExpr.sourceSpan)));
24723
+ return handlerOps;
24724
+ }
24725
+ function astOf(ast) {
24726
+ return ast instanceof ASTWithSource ? ast.ast : ast;
24017
24727
  }
24018
24728
  /**
24019
24729
  * Process all of the local references on an element-like structure in the template AST and
@@ -24101,11 +24811,12 @@ function ingestControlFlowInsertionPoint(unit, xref, node) {
24101
24811
  // and they can be used in directive matching (in the case of `Template.templateAttrs`).
24102
24812
  if (root !== null) {
24103
24813
  for (const attr of root.attributes) {
24104
- ingestBinding(unit, xref, attr.name, literal(attr.value), 1 /* e.BindingType.Attribute */, null, SecurityContext.NONE, attr.sourceSpan, BindingFlags.TextValue);
24814
+ const securityContext = domSchema.securityContext(NG_TEMPLATE_TAG_NAME$1, attr.name, true);
24815
+ unit.update.push(createBindingOp(xref, BindingKind.Attribute, attr.name, literal(attr.value), null, securityContext, true, false, null, asMessage(attr.i18n), attr.sourceSpan));
24105
24816
  }
24106
24817
  const tagName = root instanceof Element$1 ? root.name : root.tagName;
24107
24818
  // Don't pass along `ng-template` tag name since it enables directive matching.
24108
- return tagName === 'ng-template' ? null : tagName;
24819
+ return tagName === NG_TEMPLATE_TAG_NAME$1 ? null : tagName;
24109
24820
  }
24110
24821
  return null;
24111
24822
  }
@@ -27184,7 +27895,7 @@ class TemplateDefinitionBuilder {
27184
27895
  else {
27185
27896
  // ... otherwise we need to activate post-processing
27186
27897
  // to replace ICU placeholders with proper values
27187
- const placeholder = wrapI18nPlaceholder(`${I18N_ICU_MAPPING_PREFIX}${key}`);
27898
+ const placeholder = wrapI18nPlaceholder(`${I18N_ICU_MAPPING_PREFIX$1}${key}`);
27188
27899
  params[key] = literal(placeholder);
27189
27900
  icuMapping[key] = literalArr(refs);
27190
27901
  }
@@ -29459,14 +30170,16 @@ function createBaseDirectiveTypeParams(meta) {
29459
30170
  function getInputsTypeExpression(meta) {
29460
30171
  return literalMap(Object.keys(meta.inputs).map(key => {
29461
30172
  const value = meta.inputs[key];
29462
- return {
29463
- key,
29464
- value: literalMap([
29465
- { key: 'alias', value: literal(value.bindingPropertyName), quoted: true },
29466
- { key: 'required', value: literal(value.required), quoted: true }
29467
- ]),
29468
- quoted: true
29469
- };
30173
+ const values = [
30174
+ { key: 'alias', value: literal(value.bindingPropertyName), quoted: true },
30175
+ { key: 'required', value: literal(value.required), quoted: true },
30176
+ ];
30177
+ // TODO(legacy-partial-output-inputs): Consider always emitting this information,
30178
+ // or leaving it as is.
30179
+ if (value.isSignal) {
30180
+ values.push({ key: 'isSignal', value: literal(value.isSignal), quoted: true });
30181
+ }
30182
+ return { key, value: literalMap(values), quoted: true };
29470
30183
  }));
29471
30184
  }
29472
30185
  /**
@@ -29532,6 +30245,7 @@ function createHostBindingsFunction(hostBindingsMetadata, typeSourceSpan, bindin
29532
30245
  }
29533
30246
  const hostJob = ingestHostBinding({
29534
30247
  componentName: name,
30248
+ componentSelector: selector,
29535
30249
  properties: bindings,
29536
30250
  events: eventBindings,
29537
30251
  attributes: hostBindingsMetadata.attributes,
@@ -29544,6 +30258,8 @@ function createHostBindingsFunction(hostBindingsMetadata, typeSourceSpan, bindin
29544
30258
  }
29545
30259
  return emitHostBindingFunction(hostJob);
29546
30260
  }
30261
+ let bindingId = 0;
30262
+ const getNextBindingId = () => `${bindingId++}`;
29547
30263
  const bindingContext = variable(CONTEXT_NAME);
29548
30264
  const styleBuilder = new StylingBuilder(bindingContext);
29549
30265
  const { styleAttr, classAttr } = hostBindingsMetadata.specialAttributes;
@@ -29596,7 +30312,7 @@ function createHostBindingsFunction(hostBindingsMetadata, typeSourceSpan, bindin
29596
30312
  for (const binding of allOtherBindings) {
29597
30313
  // resolve literal arrays and literal objects
29598
30314
  const value = binding.expression.visit(getValueConverter());
29599
- const bindingExpr = bindingFn(bindingContext, value);
30315
+ const bindingExpr = bindingFn(bindingContext, value, getNextBindingId);
29600
30316
  const { bindingName, instruction, isAttribute } = getBindingNameAndInstruction(binding);
29601
30317
  const securityContexts = bindingParser.calcPossibleSecurityContexts(selector, bindingName, isAttribute)
29602
30318
  .filter(context => context !== SecurityContext.NONE);
@@ -29675,10 +30391,12 @@ function createHostBindingsFunction(hostBindingsMetadata, typeSourceSpan, bindin
29675
30391
  // at the top of this method when all the input bindings were counted.
29676
30392
  totalHostVarsCount +=
29677
30393
  Math.max(call.allocateBindingSlots - MIN_STYLING_BINDING_SLOTS_REQUIRED, 0);
30394
+ const { params, stmts } = convertStylingCall(call, bindingContext, bindingFn, getNextBindingId);
30395
+ updateVariables.push(...stmts);
29678
30396
  updateInstructions.push({
29679
30397
  reference: instruction.reference,
29680
- paramsOrFn: convertStylingCall(call, bindingContext, bindingFn),
29681
- span: null
30398
+ paramsOrFn: params,
30399
+ span: null,
29682
30400
  });
29683
30401
  }
29684
30402
  });
@@ -29699,11 +30417,19 @@ function createHostBindingsFunction(hostBindingsMetadata, typeSourceSpan, bindin
29699
30417
  }
29700
30418
  return null;
29701
30419
  }
29702
- function bindingFn(implicit, value) {
29703
- return convertPropertyBinding(null, implicit, value, 'b');
30420
+ function bindingFn(implicit, value, getNextBindingIdFn) {
30421
+ return convertPropertyBinding(null, implicit, value, getNextBindingIdFn());
29704
30422
  }
29705
- function convertStylingCall(call, bindingContext, bindingFn) {
29706
- return call.params(value => bindingFn(bindingContext, value).currValExpr);
30423
+ function convertStylingCall(call, bindingContext, bindingFn, getNextBindingIdFn) {
30424
+ const stmts = [];
30425
+ const params = call.params(value => {
30426
+ const result = bindingFn(bindingContext, value, getNextBindingIdFn);
30427
+ if (Array.isArray(result.stmts) && result.stmts.length > 0) {
30428
+ stmts.push(...result.stmts);
30429
+ }
30430
+ return result.currValExpr;
30431
+ });
30432
+ return { params, stmts };
29707
30433
  }
29708
30434
  function getBindingNameAndInstruction(binding) {
29709
30435
  let bindingName = binding.name;
@@ -29830,6 +30556,18 @@ function compileStyles(styles, selector, hostSelector) {
29830
30556
  return shadowCss.shimCssText(style, selector, hostSelector);
29831
30557
  });
29832
30558
  }
30559
+ /**
30560
+ * Encapsulates a CSS stylesheet with emulated view encapsulation.
30561
+ * This allows a stylesheet to be used with an Angular component that
30562
+ * is using the `ViewEncapsulation.Emulated` mode.
30563
+ *
30564
+ * @param style The content of a CSS stylesheet.
30565
+ * @returns The encapsulated content for the style.
30566
+ */
30567
+ function encapsulateStyle(style) {
30568
+ const shadowCss = new ShadowCss();
30569
+ return shadowCss.shimCssText(style, CONTENT_ATTR, HOST_ATTR);
30570
+ }
29833
30571
  function createHostDirectivesType(meta) {
29834
30572
  if (!meta.hostDirectives?.length) {
29835
30573
  return NONE_TYPE;
@@ -30895,6 +31633,8 @@ function convertDirectiveFacadeToMetadata(facade) {
30895
31633
  bindingPropertyName: ann.alias || field,
30896
31634
  classPropertyName: field,
30897
31635
  required: ann.required || false,
31636
+ // TODO(signals): Support JIT signal inputs via decorator transform.
31637
+ isSignal: false,
30898
31638
  transformFunction: ann.transform != null ? new WrappedNodeExpr(ann.transform) : null,
30899
31639
  };
30900
31640
  }
@@ -30926,7 +31666,7 @@ function convertDeclareDirectiveFacadeToMetadata(declaration, typeSourceSpan) {
30926
31666
  type: wrapReference(declaration.type),
30927
31667
  typeSourceSpan,
30928
31668
  selector: declaration.selector ?? null,
30929
- inputs: declaration.inputs ? inputsMappingToInputMetadata(declaration.inputs) : {},
31669
+ inputs: declaration.inputs ? inputsPartialMetadataToInputMetadata(declaration.inputs) : {},
30930
31670
  outputs: declaration.outputs ?? {},
30931
31671
  host: convertHostDeclarationToMetadata(declaration.host),
30932
31672
  queries: (declaration.queries ?? []).map(convertQueryDeclarationToMetadata),
@@ -31189,28 +31929,51 @@ function isInput(value) {
31189
31929
  function isOutput(value) {
31190
31930
  return value.ngMetadataName === 'Output';
31191
31931
  }
31192
- function inputsMappingToInputMetadata(inputs) {
31193
- return Object.keys(inputs).reduce((result, key) => {
31194
- const value = inputs[key];
31195
- if (typeof value === 'string') {
31196
- result[key] = {
31197
- bindingPropertyName: value,
31198
- classPropertyName: value,
31199
- transformFunction: null,
31200
- required: false,
31201
- };
31932
+ function inputsPartialMetadataToInputMetadata(inputs) {
31933
+ return Object.keys(inputs).reduce((result, minifiedClassName) => {
31934
+ const value = inputs[minifiedClassName];
31935
+ // Handle legacy partial input output.
31936
+ if (typeof value === 'string' || Array.isArray(value)) {
31937
+ result[minifiedClassName] = parseLegacyInputPartialOutput(value);
31202
31938
  }
31203
31939
  else {
31204
- result[key] = {
31205
- bindingPropertyName: value[0],
31206
- classPropertyName: value[1],
31207
- transformFunction: value[2] ? new WrappedNodeExpr(value[2]) : null,
31208
- required: false,
31940
+ result[minifiedClassName] = {
31941
+ bindingPropertyName: value.publicName,
31942
+ classPropertyName: minifiedClassName,
31943
+ transformFunction: value.transformFunction !== null ?
31944
+ new WrappedNodeExpr(value.transformFunction) :
31945
+ null,
31946
+ required: value.isRequired,
31947
+ isSignal: value.isSignal,
31209
31948
  };
31210
31949
  }
31211
31950
  return result;
31212
31951
  }, {});
31213
31952
  }
31953
+ /**
31954
+ * Parses the legacy input partial output. For more details see `partial/directive.ts`.
31955
+ * TODO(legacy-partial-output-inputs): Remove in v18.
31956
+ */
31957
+ function parseLegacyInputPartialOutput(value) {
31958
+ if (typeof value === 'string') {
31959
+ return {
31960
+ bindingPropertyName: value,
31961
+ classPropertyName: value,
31962
+ transformFunction: null,
31963
+ required: false,
31964
+ // legacy partial output does not capture signal inputs.
31965
+ isSignal: false,
31966
+ };
31967
+ }
31968
+ return {
31969
+ bindingPropertyName: value[0],
31970
+ classPropertyName: value[1],
31971
+ transformFunction: value[2] ? new WrappedNodeExpr(value[2]) : null,
31972
+ required: false,
31973
+ // legacy partial output does not capture signal inputs.
31974
+ isSignal: false,
31975
+ };
31976
+ }
31214
31977
  function parseInputsArray(values) {
31215
31978
  return values.reduce((results, value) => {
31216
31979
  if (typeof value === 'string') {
@@ -31219,6 +31982,8 @@ function parseInputsArray(values) {
31219
31982
  bindingPropertyName,
31220
31983
  classPropertyName,
31221
31984
  required: false,
31985
+ // Signal inputs not supported for the inputs array.
31986
+ isSignal: false,
31222
31987
  transformFunction: null,
31223
31988
  };
31224
31989
  }
@@ -31227,6 +31992,8 @@ function parseInputsArray(values) {
31227
31992
  bindingPropertyName: value.alias || value.name,
31228
31993
  classPropertyName: value.name,
31229
31994
  required: value.required || false,
31995
+ // Signal inputs not supported for the inputs array.
31996
+ isSignal: false,
31230
31997
  transformFunction: value.transform != null ? new WrappedNodeExpr(value.transform) : null,
31231
31998
  };
31232
31999
  }
@@ -31279,7 +32046,7 @@ function publishFacade(global) {
31279
32046
  * @description
31280
32047
  * Entry point for all public APIs of the compiler package.
31281
32048
  */
31282
- const VERSION = new Version('17.1.0-next.2');
32049
+ const VERSION = new Version('17.1.0-next.4');
31283
32050
 
31284
32051
  class CompilerConfig {
31285
32052
  constructor({ defaultEncapsulation = ViewEncapsulation.Emulated, preserveWhitespaces, strictInjectionParameters } = {}) {
@@ -32841,11 +33608,11 @@ function compileClassDebugInfo(debugInfo) {
32841
33608
  *
32842
33609
  * Do not include any prerelease in these versions as they are ignored.
32843
33610
  */
32844
- const MINIMUM_PARTIAL_LINKER_VERSION$6 = '12.0.0';
33611
+ const MINIMUM_PARTIAL_LINKER_VERSION$5 = '12.0.0';
32845
33612
  function compileDeclareClassMetadata(metadata) {
32846
33613
  const definitionMap = new DefinitionMap();
32847
- definitionMap.set('minVersion', literal(MINIMUM_PARTIAL_LINKER_VERSION$6));
32848
- definitionMap.set('version', literal('17.1.0-next.2'));
33614
+ definitionMap.set('minVersion', literal(MINIMUM_PARTIAL_LINKER_VERSION$5));
33615
+ definitionMap.set('version', literal('17.1.0-next.4'));
32849
33616
  definitionMap.set('ngImport', importExpr(Identifiers.core));
32850
33617
  definitionMap.set('type', metadata.type);
32851
33618
  definitionMap.set('decorators', metadata.decorators);
@@ -32924,14 +33691,6 @@ function compileDependency(dep) {
32924
33691
  return depMeta.toLiteralMap();
32925
33692
  }
32926
33693
 
32927
- /**
32928
- * Every time we make a breaking change to the declaration interface or partial-linker behavior, we
32929
- * must update this constant to prevent old partial-linkers from incorrectly processing the
32930
- * declaration.
32931
- *
32932
- * Do not include any prerelease in these versions as they are ignored.
32933
- */
32934
- const MINIMUM_PARTIAL_LINKER_VERSION$5 = '16.1.0';
32935
33694
  /**
32936
33695
  * Compile a directive declaration defined by the `R3DirectiveMetadata`.
32937
33696
  */
@@ -32947,13 +33706,9 @@ function compileDeclareDirectiveFromMetadata(meta) {
32947
33706
  */
32948
33707
  function createDirectiveDefinitionMap(meta) {
32949
33708
  const definitionMap = new DefinitionMap();
32950
- const hasTransformFunctions = Object.values(meta.inputs).some(input => input.transformFunction !== null);
32951
- // Note: in order to allow consuming Angular libraries that have been compiled with 16.1+ in
32952
- // Angular 16.0, we only force a minimum version of 16.1 if input transform feature as introduced
32953
- // in 16.1 is actually used.
32954
- const minVersion = hasTransformFunctions ? MINIMUM_PARTIAL_LINKER_VERSION$5 : '14.0.0';
33709
+ const minVersion = getMinimumVersionForPartialOutput(meta);
32955
33710
  definitionMap.set('minVersion', literal(minVersion));
32956
- definitionMap.set('version', literal('17.1.0-next.2'));
33711
+ definitionMap.set('version', literal('17.1.0-next.4'));
32957
33712
  // e.g. `type: MyDirective`
32958
33713
  definitionMap.set('type', meta.type.value);
32959
33714
  if (meta.isStandalone) {
@@ -32966,7 +33721,8 @@ function createDirectiveDefinitionMap(meta) {
32966
33721
  if (meta.selector !== null) {
32967
33722
  definitionMap.set('selector', literal(meta.selector));
32968
33723
  }
32969
- definitionMap.set('inputs', conditionallyCreateDirectiveBindingLiteral(meta.inputs, true));
33724
+ definitionMap.set('inputs', needsNewInputPartialOutput(meta) ? createInputsPartialMetadata(meta.inputs) :
33725
+ legacyInputsPartialMetadata(meta.inputs));
32970
33726
  definitionMap.set('outputs', conditionallyCreateDirectiveBindingLiteral(meta.outputs));
32971
33727
  definitionMap.set('host', compileHostMetadata(meta.host));
32972
33728
  definitionMap.set('providers', meta.providers);
@@ -32991,6 +33747,44 @@ function createDirectiveDefinitionMap(meta) {
32991
33747
  definitionMap.set('ngImport', importExpr(Identifiers.core));
32992
33748
  return definitionMap;
32993
33749
  }
33750
+ /**
33751
+ * Determines the minimum linker version for the partial output
33752
+ * generated for this directive.
33753
+ *
33754
+ * Every time we make a breaking change to the declaration interface or partial-linker
33755
+ * behavior, we must update the minimum versions to prevent old partial-linkers from
33756
+ * incorrectly processing the declaration.
33757
+ *
33758
+ * NOTE: Do not include any prerelease in these versions as they are ignored.
33759
+ */
33760
+ function getMinimumVersionForPartialOutput(meta) {
33761
+ // We are starting with the oldest minimum version that can work for common
33762
+ // directive partial compilation output. As we discover usages of new features
33763
+ // that require a newer partial output emit, we bump the `minVersion`. Our goal
33764
+ // is to keep libraries as much compatible with older linker versions as possible.
33765
+ let minVersion = '14.0.0';
33766
+ // Note: in order to allow consuming Angular libraries that have been compiled with 16.1+ in
33767
+ // Angular 16.0, we only force a minimum version of 16.1 if input transform feature as introduced
33768
+ // in 16.1 is actually used.
33769
+ const hasTransformFunctions = Object.values(meta.inputs).some(input => input.transformFunction !== null);
33770
+ if (hasTransformFunctions) {
33771
+ minVersion = '16.1.0';
33772
+ }
33773
+ // If there are input flags and we need the new emit, use the actual minimum version,
33774
+ // where this was introduced. i.e. in 17.1.0
33775
+ // TODO(legacy-partial-output-inputs): Remove in v18.
33776
+ if (needsNewInputPartialOutput(meta)) {
33777
+ minVersion = '17.1.0';
33778
+ }
33779
+ return minVersion;
33780
+ }
33781
+ /**
33782
+ * Gets whether the given directive needs the new input partial output structure
33783
+ * that can hold additional metadata like `isRequired`, `isSignal` etc.
33784
+ */
33785
+ function needsNewInputPartialOutput(meta) {
33786
+ return Object.values(meta.inputs).some(input => input.isSignal);
33787
+ }
32994
33788
  /**
32995
33789
  * Compiles the metadata of a single query into its partial declaration form as declared
32996
33790
  * by `R3DeclareQueryMetadata`.
@@ -33064,6 +33858,74 @@ function createHostDirectives(hostDirectives) {
33064
33858
  // otherwise we can save some bytes by using a plain array, e.g. `[{directive: HostDir}]`.
33065
33859
  return literalArr(expressions);
33066
33860
  }
33861
+ /**
33862
+ * Generates partial output metadata for inputs of a directive.
33863
+ *
33864
+ * The generated structure is expected to match `R3DeclareDirectiveFacade['inputs']`.
33865
+ */
33866
+ function createInputsPartialMetadata(inputs) {
33867
+ const keys = Object.getOwnPropertyNames(inputs);
33868
+ if (keys.length === 0) {
33869
+ return null;
33870
+ }
33871
+ return literalMap(keys.map(declaredName => {
33872
+ const value = inputs[declaredName];
33873
+ return {
33874
+ key: declaredName,
33875
+ // put quotes around keys that contain potentially unsafe characters
33876
+ quoted: UNSAFE_OBJECT_KEY_NAME_REGEXP.test(declaredName),
33877
+ value: literalMap([
33878
+ { key: 'classPropertyName', quoted: false, value: asLiteral(value.classPropertyName) },
33879
+ { key: 'publicName', quoted: false, value: asLiteral(value.bindingPropertyName) },
33880
+ { key: 'isSignal', quoted: false, value: asLiteral(value.isSignal) },
33881
+ { key: 'isRequired', quoted: false, value: asLiteral(value.required) },
33882
+ { key: 'transformFunction', quoted: false, value: value.transformFunction ?? NULL_EXPR },
33883
+ ])
33884
+ };
33885
+ }));
33886
+ }
33887
+ /**
33888
+ * Pre v18 legacy partial output for inputs.
33889
+ *
33890
+ * Previously, inputs did not capture metadata like `isSignal` in the partial compilation output.
33891
+ * To enable capturing such metadata, we restructured how input metadata is communicated in the
33892
+ * partial output. This would make libraries incompatible with older Angular FW versions where the
33893
+ * linker would not know how to handle this new "format". For this reason, if we know this metadata
33894
+ * does not need to be captured- we fall back to the old format. This is what this function
33895
+ * generates.
33896
+ *
33897
+ * See:
33898
+ * https://github.com/angular/angular/blob/d4b423690210872b5c32a322a6090beda30b05a3/packages/core/src/compiler/compiler_facade_interface.ts#L197-L199
33899
+ */
33900
+ function legacyInputsPartialMetadata(inputs) {
33901
+ // TODO(legacy-partial-output-inputs): Remove function in v18.
33902
+ const keys = Object.getOwnPropertyNames(inputs);
33903
+ if (keys.length === 0) {
33904
+ return null;
33905
+ }
33906
+ return literalMap(keys.map(declaredName => {
33907
+ const value = inputs[declaredName];
33908
+ const publicName = value.bindingPropertyName;
33909
+ const differentDeclaringName = publicName !== declaredName;
33910
+ let result;
33911
+ if (differentDeclaringName || value.transformFunction !== null) {
33912
+ const values = [asLiteral(publicName), asLiteral(declaredName)];
33913
+ if (value.transformFunction !== null) {
33914
+ values.push(value.transformFunction);
33915
+ }
33916
+ result = literalArr(values);
33917
+ }
33918
+ else {
33919
+ result = asLiteral(publicName);
33920
+ }
33921
+ return {
33922
+ key: declaredName,
33923
+ // put quotes around keys that contain potentially unsafe characters
33924
+ quoted: UNSAFE_OBJECT_KEY_NAME_REGEXP.test(declaredName),
33925
+ value: result,
33926
+ };
33927
+ }));
33928
+ }
33067
33929
 
33068
33930
  /**
33069
33931
  * Compile a component declaration defined by the `R3ComponentMetadata`.
@@ -33230,7 +34092,7 @@ const MINIMUM_PARTIAL_LINKER_VERSION$4 = '12.0.0';
33230
34092
  function compileDeclareFactoryFunction(meta) {
33231
34093
  const definitionMap = new DefinitionMap();
33232
34094
  definitionMap.set('minVersion', literal(MINIMUM_PARTIAL_LINKER_VERSION$4));
33233
- definitionMap.set('version', literal('17.1.0-next.2'));
34095
+ definitionMap.set('version', literal('17.1.0-next.4'));
33234
34096
  definitionMap.set('ngImport', importExpr(Identifiers.core));
33235
34097
  definitionMap.set('type', meta.type.value);
33236
34098
  definitionMap.set('deps', compileDependencies(meta.deps));
@@ -33265,7 +34127,7 @@ function compileDeclareInjectableFromMetadata(meta) {
33265
34127
  function createInjectableDefinitionMap(meta) {
33266
34128
  const definitionMap = new DefinitionMap();
33267
34129
  definitionMap.set('minVersion', literal(MINIMUM_PARTIAL_LINKER_VERSION$3));
33268
- definitionMap.set('version', literal('17.1.0-next.2'));
34130
+ definitionMap.set('version', literal('17.1.0-next.4'));
33269
34131
  definitionMap.set('ngImport', importExpr(Identifiers.core));
33270
34132
  definitionMap.set('type', meta.type.value);
33271
34133
  // Only generate providedIn property if it has a non-null value
@@ -33316,7 +34178,7 @@ function compileDeclareInjectorFromMetadata(meta) {
33316
34178
  function createInjectorDefinitionMap(meta) {
33317
34179
  const definitionMap = new DefinitionMap();
33318
34180
  definitionMap.set('minVersion', literal(MINIMUM_PARTIAL_LINKER_VERSION$2));
33319
- definitionMap.set('version', literal('17.1.0-next.2'));
34181
+ definitionMap.set('version', literal('17.1.0-next.4'));
33320
34182
  definitionMap.set('ngImport', importExpr(Identifiers.core));
33321
34183
  definitionMap.set('type', meta.type.value);
33322
34184
  definitionMap.set('providers', meta.providers);
@@ -33349,7 +34211,7 @@ function createNgModuleDefinitionMap(meta) {
33349
34211
  throw new Error('Invalid path! Local compilation mode should not get into the partial compilation path');
33350
34212
  }
33351
34213
  definitionMap.set('minVersion', literal(MINIMUM_PARTIAL_LINKER_VERSION$1));
33352
- definitionMap.set('version', literal('17.1.0-next.2'));
34214
+ definitionMap.set('version', literal('17.1.0-next.4'));
33353
34215
  definitionMap.set('ngImport', importExpr(Identifiers.core));
33354
34216
  definitionMap.set('type', meta.type.value);
33355
34217
  // We only generate the keys in the metadata if the arrays contain values.
@@ -33400,7 +34262,7 @@ function compileDeclarePipeFromMetadata(meta) {
33400
34262
  function createPipeDefinitionMap(meta) {
33401
34263
  const definitionMap = new DefinitionMap();
33402
34264
  definitionMap.set('minVersion', literal(MINIMUM_PARTIAL_LINKER_VERSION));
33403
- definitionMap.set('version', literal('17.1.0-next.2'));
34265
+ definitionMap.set('version', literal('17.1.0-next.4'));
33404
34266
  definitionMap.set('ngImport', importExpr(Identifiers.core));
33405
34267
  // e.g. `type: MyPipe`
33406
34268
  definitionMap.set('type', meta.type.value);
@@ -33433,5 +34295,5 @@ publishFacade(_global);
33433
34295
 
33434
34296
  // This file is not used to build this module. It is only used during editing
33435
34297
 
33436
- 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 };
34298
+ 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 };
33437
34299
  //# sourceMappingURL=compiler.mjs.map