@angular/compiler 17.0.6 → 17.0.7

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 (35) hide show
  1. package/esm2022/src/render3/partial/class_metadata.mjs +1 -1
  2. package/esm2022/src/render3/partial/directive.mjs +1 -1
  3. package/esm2022/src/render3/partial/factory.mjs +1 -1
  4. package/esm2022/src/render3/partial/injectable.mjs +1 -1
  5. package/esm2022/src/render3/partial/injector.mjs +1 -1
  6. package/esm2022/src/render3/partial/ng_module.mjs +1 -1
  7. package/esm2022/src/render3/partial/pipe.mjs +1 -1
  8. package/esm2022/src/render3/view/compiler.mjs +2 -1
  9. package/esm2022/src/template/pipeline/ir/src/enums.mjs +9 -17
  10. package/esm2022/src/template/pipeline/ir/src/expression.mjs +12 -20
  11. package/esm2022/src/template/pipeline/ir/src/ops/create.mjs +14 -6
  12. package/esm2022/src/template/pipeline/ir/src/ops/host.mjs +4 -2
  13. package/esm2022/src/template/pipeline/ir/src/ops/update.mjs +15 -9
  14. package/esm2022/src/template/pipeline/src/emit.mjs +4 -4
  15. package/esm2022/src/template/pipeline/src/ingest.mjs +240 -155
  16. package/esm2022/src/template/pipeline/src/instruction.mjs +14 -14
  17. package/esm2022/src/template/pipeline/src/phases/assign_i18n_slot_dependencies.mjs +40 -56
  18. package/esm2022/src/template/pipeline/src/phases/attribute_extraction.mjs +15 -8
  19. package/esm2022/src/template/pipeline/src/phases/binding_specialization.mjs +4 -4
  20. package/esm2022/src/template/pipeline/src/phases/const_collection.mjs +13 -4
  21. package/esm2022/src/template/pipeline/src/phases/create_i18n_contexts.mjs +23 -1
  22. package/esm2022/src/template/pipeline/src/phases/generate_variables.mjs +7 -1
  23. package/esm2022/src/template/pipeline/src/phases/i18n_const_collection.mjs +10 -6
  24. package/esm2022/src/template/pipeline/src/phases/parse_extracted_styles.mjs +4 -3
  25. package/esm2022/src/template/pipeline/src/phases/propagate_i18n_blocks.mjs +27 -12
  26. package/esm2022/src/template/pipeline/src/phases/reify.mjs +12 -15
  27. package/esm2022/src/template/pipeline/src/phases/resolve_i18n_element_placeholders.mjs +60 -14
  28. package/esm2022/src/template/pipeline/src/phases/resolve_sanitizers.mjs +78 -14
  29. package/esm2022/src/template/pipeline/src/phases/slot_allocation.mjs +3 -1
  30. package/esm2022/src/template/pipeline/src/phases/var_counting.mjs +3 -1
  31. package/esm2022/src/version.mjs +1 -1
  32. package/fesm2022/compiler.mjs +590 -351
  33. package/fesm2022/compiler.mjs.map +1 -1
  34. package/index.d.ts +1 -1
  35. package/package.json +2 -2
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @license Angular v17.0.6
2
+ * @license Angular v17.0.7
3
3
  * (c) 2010-2022 Google LLC. https://angular.io/
4
4
  * License: MIT
5
5
  */
@@ -9064,23 +9064,27 @@ var ExpressionKind;
9064
9064
  * An expression representing a sanitizer function.
9065
9065
  */
9066
9066
  ExpressionKind[ExpressionKind["SanitizerExpr"] = 20] = "SanitizerExpr";
9067
+ /**
9068
+ * An expression representing a function to create trusted values.
9069
+ */
9070
+ ExpressionKind[ExpressionKind["TrustedValueFnExpr"] = 21] = "TrustedValueFnExpr";
9067
9071
  /**
9068
9072
  * An expression that will cause a literal slot index to be emitted.
9069
9073
  */
9070
- ExpressionKind[ExpressionKind["SlotLiteralExpr"] = 21] = "SlotLiteralExpr";
9074
+ ExpressionKind[ExpressionKind["SlotLiteralExpr"] = 22] = "SlotLiteralExpr";
9071
9075
  /**
9072
9076
  * A test expression for a conditional op.
9073
9077
  */
9074
- ExpressionKind[ExpressionKind["ConditionalCase"] = 22] = "ConditionalCase";
9078
+ ExpressionKind[ExpressionKind["ConditionalCase"] = 23] = "ConditionalCase";
9075
9079
  /**
9076
9080
  * A variable for use inside a repeater, providing one of the ambiently-available context
9077
9081
  * properties ($even, $first, etc.).
9078
9082
  */
9079
- ExpressionKind[ExpressionKind["DerivedRepeaterVar"] = 23] = "DerivedRepeaterVar";
9083
+ ExpressionKind[ExpressionKind["DerivedRepeaterVar"] = 24] = "DerivedRepeaterVar";
9080
9084
  /**
9081
9085
  * An expression that will be automatically extracted to the component const array.
9082
9086
  */
9083
- ExpressionKind[ExpressionKind["ConstCollected"] = 24] = "ConstCollected";
9087
+ ExpressionKind[ExpressionKind["ConstCollected"] = 25] = "ConstCollected";
9084
9088
  })(ExpressionKind || (ExpressionKind = {}));
9085
9089
  var VariableFlags;
9086
9090
  (function (VariableFlags) {
@@ -9124,18 +9128,6 @@ var CompatibilityMode;
9124
9128
  CompatibilityMode[CompatibilityMode["Normal"] = 0] = "Normal";
9125
9129
  CompatibilityMode[CompatibilityMode["TemplateDefinitionBuilder"] = 1] = "TemplateDefinitionBuilder";
9126
9130
  })(CompatibilityMode || (CompatibilityMode = {}));
9127
- /**
9128
- * Represents functions used to sanitize different pieces of a template.
9129
- */
9130
- var SanitizerFn;
9131
- (function (SanitizerFn) {
9132
- SanitizerFn[SanitizerFn["Html"] = 0] = "Html";
9133
- SanitizerFn[SanitizerFn["Script"] = 1] = "Script";
9134
- SanitizerFn[SanitizerFn["Style"] = 2] = "Style";
9135
- SanitizerFn[SanitizerFn["Url"] = 3] = "Url";
9136
- SanitizerFn[SanitizerFn["ResourceUrl"] = 4] = "ResourceUrl";
9137
- SanitizerFn[SanitizerFn["IframeAttribute"] = 5] = "IframeAttribute";
9138
- })(SanitizerFn || (SanitizerFn = {}));
9139
9131
  /**
9140
9132
  * Enumeration of the different kinds of `@defer` secondary blocks.
9141
9133
  */
@@ -9414,7 +9406,7 @@ class Interpolation {
9414
9406
  /**
9415
9407
  * Create a `BindingOp`, not yet transformed into a particular type of binding.
9416
9408
  */
9417
- function createBindingOp(target, kind, name, expression, unit, securityContext, isTextAttribute, isStructuralTemplate, i18nContext, sourceSpan) {
9409
+ function createBindingOp(target, kind, name, expression, unit, securityContext, isTextAttribute, isStructuralTemplateAttribute, templateKind, i18nMessage, sourceSpan) {
9418
9410
  return {
9419
9411
  kind: OpKind.Binding,
9420
9412
  bindingKind: kind,
@@ -9424,8 +9416,10 @@ function createBindingOp(target, kind, name, expression, unit, securityContext,
9424
9416
  unit,
9425
9417
  securityContext,
9426
9418
  isTextAttribute,
9427
- isStructuralTemplate: isStructuralTemplate,
9428
- i18nContext,
9419
+ isStructuralTemplateAttribute,
9420
+ templateKind,
9421
+ i18nContext: null,
9422
+ i18nMessage,
9429
9423
  sourceSpan,
9430
9424
  ...NEW_OP,
9431
9425
  };
@@ -9433,7 +9427,7 @@ function createBindingOp(target, kind, name, expression, unit, securityContext,
9433
9427
  /**
9434
9428
  * Create a `PropertyOp`.
9435
9429
  */
9436
- function createPropertyOp(target, name, expression, isAnimationTrigger, securityContext, isStructuralTemplate, i18nContext, sourceSpan) {
9430
+ function createPropertyOp(target, name, expression, isAnimationTrigger, securityContext, isStructuralTemplateAttribute, templateKind, i18nContext, i18nMessage, sourceSpan) {
9437
9431
  return {
9438
9432
  kind: OpKind.Property,
9439
9433
  target,
@@ -9442,8 +9436,10 @@ function createPropertyOp(target, name, expression, isAnimationTrigger, security
9442
9436
  isAnimationTrigger,
9443
9437
  securityContext,
9444
9438
  sanitizer: null,
9445
- isStructuralTemplate,
9439
+ isStructuralTemplateAttribute,
9440
+ templateKind,
9446
9441
  i18nContext,
9442
+ i18nMessage,
9447
9443
  sourceSpan,
9448
9444
  ...TRAIT_DEPENDS_ON_SLOT_CONTEXT,
9449
9445
  ...TRAIT_CONSUMES_VARS,
@@ -9508,7 +9504,7 @@ function createClassMapOp(xref, expression, sourceSpan) {
9508
9504
  /**
9509
9505
  * Create an `AttributeOp`.
9510
9506
  */
9511
- function createAttributeOp(target, name, expression, securityContext, isTextAttribute, isStructuralTemplate, i18nContext, sourceSpan) {
9507
+ function createAttributeOp(target, name, expression, securityContext, isTextAttribute, isStructuralTemplateAttribute, templateKind, i18nMessage, sourceSpan) {
9512
9508
  return {
9513
9509
  kind: OpKind.Attribute,
9514
9510
  target,
@@ -9517,8 +9513,10 @@ function createAttributeOp(target, name, expression, securityContext, isTextAttr
9517
9513
  securityContext,
9518
9514
  sanitizer: null,
9519
9515
  isTextAttribute,
9520
- isStructuralTemplate,
9521
- i18nContext,
9516
+ isStructuralTemplateAttribute,
9517
+ templateKind,
9518
+ i18nContext: null,
9519
+ i18nMessage,
9522
9520
  sourceSpan,
9523
9521
  ...TRAIT_DEPENDS_ON_SLOT_CONTEXT,
9524
9522
  ...TRAIT_CONSUMES_VARS,
@@ -10152,24 +10150,6 @@ class ReadTemporaryExpr extends ExpressionBase {
10152
10150
  return r;
10153
10151
  }
10154
10152
  }
10155
- class SanitizerExpr extends ExpressionBase {
10156
- constructor(fn) {
10157
- super();
10158
- this.fn = fn;
10159
- this.kind = ExpressionKind.SanitizerExpr;
10160
- }
10161
- visitExpression(visitor, context) { }
10162
- isEquivalent(e) {
10163
- return e instanceof SanitizerExpr && e.fn === this.fn;
10164
- }
10165
- isConstant() {
10166
- return true;
10167
- }
10168
- clone() {
10169
- return new SanitizerExpr(this.fn);
10170
- }
10171
- transformInternalExpressions() { }
10172
- }
10173
10153
  class SlotLiteralExpr extends ExpressionBase {
10174
10154
  constructor(slot) {
10175
10155
  super();
@@ -10300,7 +10280,6 @@ function transformExpressionsInOp(op, transform, flags) {
10300
10280
  case OpKind.ClassProp:
10301
10281
  case OpKind.ClassMap:
10302
10282
  case OpKind.Binding:
10303
- case OpKind.HostProperty:
10304
10283
  if (op.expression instanceof Interpolation) {
10305
10284
  transformExpressionsInInterpolation(op.expression, transform, flags);
10306
10285
  }
@@ -10309,6 +10288,7 @@ function transformExpressionsInOp(op, transform, flags) {
10309
10288
  }
10310
10289
  break;
10311
10290
  case OpKind.Property:
10291
+ case OpKind.HostProperty:
10312
10292
  case OpKind.Attribute:
10313
10293
  if (op.expression instanceof Interpolation) {
10314
10294
  transformExpressionsInInterpolation(op.expression, transform, flags);
@@ -10354,6 +10334,8 @@ function transformExpressionsInOp(op, transform, flags) {
10354
10334
  case OpKind.ExtractedAttribute:
10355
10335
  op.expression =
10356
10336
  op.expression && transformExpressionsInExpression(op.expression, transform, flags);
10337
+ op.trustedValueFn = op.trustedValueFn &&
10338
+ transformExpressionsInExpression(op.trustedValueFn, transform, flags);
10357
10339
  break;
10358
10340
  case OpKind.RepeaterCreate:
10359
10341
  op.track = transformExpressionsInExpression(op.track, transform, flags);
@@ -10485,6 +10467,14 @@ function transformExpressionsInExpression(expr, transform, flags) {
10485
10467
  else if (expr instanceof NotExpr) {
10486
10468
  expr.condition = transformExpressionsInExpression(expr.condition, transform, flags);
10487
10469
  }
10470
+ else if (expr instanceof TaggedTemplateExpr) {
10471
+ expr.tag = transformExpressionsInExpression(expr.tag, transform, flags);
10472
+ expr.template.expressions =
10473
+ expr.template.expressions.map(e => transformExpressionsInExpression(e, transform, flags));
10474
+ }
10475
+ else if (expr instanceof WrappedNodeExpr) {
10476
+ // TODO: Do we need to transform any TS nodes nested inside of this expression?
10477
+ }
10488
10478
  else if (expr instanceof ReadVarExpr || expr instanceof ExternalExpr ||
10489
10479
  expr instanceof LiteralExpr) {
10490
10480
  // No action for these types.
@@ -10846,7 +10836,7 @@ function createTemplateOp(xref, templateKind, tag, functionNameSuffix, namespace
10846
10836
  ...NEW_OP,
10847
10837
  };
10848
10838
  }
10849
- function createRepeaterCreateOp(primaryView, emptyView, tag, track, varNames, sourceSpan) {
10839
+ function createRepeaterCreateOp(primaryView, emptyView, tag, track, varNames, i18nPlaceholder, emptyI18nPlaceholder, sourceSpan) {
10850
10840
  return {
10851
10841
  kind: OpKind.RepeaterCreate,
10852
10842
  attributes: null,
@@ -10864,6 +10854,8 @@ function createRepeaterCreateOp(primaryView, emptyView, tag, track, varNames, so
10864
10854
  vars: null,
10865
10855
  varNames,
10866
10856
  usesComponentInstance: false,
10857
+ i18nPlaceholder,
10858
+ emptyI18nPlaceholder,
10867
10859
  sourceSpan,
10868
10860
  ...TRAIT_CONSUMES_SLOT,
10869
10861
  ...NEW_OP,
@@ -10912,7 +10904,9 @@ function createTextOp(xref, initialValue, sourceSpan) {
10912
10904
  /**
10913
10905
  * Create a `ListenerOp`. Host bindings reuse all the listener logic.
10914
10906
  */
10915
- function createListenerOp(target, targetSlot, name, tag, animationPhase, hostListener, sourceSpan) {
10907
+ function createListenerOp(target, targetSlot, name, tag, handlerOps, animationPhase, eventTarget, hostListener, sourceSpan) {
10908
+ const handlerList = new OpList();
10909
+ handlerList.push(handlerOps);
10916
10910
  return {
10917
10911
  kind: OpKind.Listener,
10918
10912
  target,
@@ -10920,11 +10914,12 @@ function createListenerOp(target, targetSlot, name, tag, animationPhase, hostLis
10920
10914
  tag,
10921
10915
  hostListener,
10922
10916
  name,
10923
- handlerOps: new OpList(),
10917
+ handlerOps: handlerList,
10924
10918
  handlerFnName: null,
10925
10919
  consumesDollarEvent: false,
10926
10920
  isAnimationListener: animationPhase !== null,
10927
- animationPhase: animationPhase,
10921
+ animationPhase,
10922
+ eventTarget,
10928
10923
  sourceSpan,
10929
10924
  ...NEW_OP,
10930
10925
  };
@@ -10971,7 +10966,7 @@ function createProjectionOp(xref, selector, i18nPlaceholder, attributes, sourceS
10971
10966
  /**
10972
10967
  * Create an `ExtractedAttributeOp`.
10973
10968
  */
10974
- function createExtractedAttributeOp(target, bindingKind, name, expression, i18nContext) {
10969
+ function createExtractedAttributeOp(target, bindingKind, name, expression, i18nContext, i18nMessage, securityContext) {
10975
10970
  return {
10976
10971
  kind: OpKind.ExtractedAttribute,
10977
10972
  target,
@@ -10979,6 +10974,9 @@ function createExtractedAttributeOp(target, bindingKind, name, expression, i18nC
10979
10974
  name,
10980
10975
  expression,
10981
10976
  i18nContext,
10977
+ i18nMessage,
10978
+ securityContext,
10979
+ trustedValueFn: null,
10982
10980
  ...NEW_OP,
10983
10981
  };
10984
10982
  }
@@ -11121,13 +11119,15 @@ function literalOrArrayLiteral$1(value) {
11121
11119
  return literal(value, INFERRED_TYPE);
11122
11120
  }
11123
11121
 
11124
- function createHostPropertyOp(name, expression, isAnimationTrigger, i18nContext, sourceSpan) {
11122
+ function createHostPropertyOp(name, expression, isAnimationTrigger, i18nContext, securityContext, sourceSpan) {
11125
11123
  return {
11126
11124
  kind: OpKind.HostProperty,
11127
11125
  name,
11128
11126
  expression,
11129
11127
  isAnimationTrigger,
11130
11128
  i18nContext,
11129
+ securityContext,
11130
+ sanitizer: null,
11131
11131
  sourceSpan,
11132
11132
  ...TRAIT_CONSUMES_VARS,
11133
11133
  ...NEW_OP,
@@ -11407,68 +11407,52 @@ function needsApplication(i18nContexts, op) {
11407
11407
  * after the last update instruction that depends on that slot.
11408
11408
  */
11409
11409
  function assignI18nSlotDependencies(job) {
11410
- const i18nLastSlotConsumers = new Map();
11411
- let lastSlotConsumer = null;
11412
- let currentI18nOp = null;
11413
11410
  for (const unit of job.units) {
11414
- // Record the last consumed slot before each i18n end instruction.
11415
- for (const op of unit.create) {
11416
- if (hasConsumesSlotTrait(op)) {
11417
- lastSlotConsumer = op.xref;
11411
+ // The first update op.
11412
+ let updateOp = unit.update.head;
11413
+ // I18n expressions currently being moved during the iteration.
11414
+ let i18nExpressionsInProgress = [];
11415
+ // Non-null while we are iterating through an i18nStart/i18nEnd pair
11416
+ let state = null;
11417
+ for (const createOp of unit.create) {
11418
+ if (createOp.kind === OpKind.I18nStart) {
11419
+ state = {
11420
+ blockXref: createOp.xref,
11421
+ lastSlotConsumer: createOp.xref,
11422
+ };
11418
11423
  }
11419
- switch (op.kind) {
11420
- case OpKind.I18nStart:
11421
- currentI18nOp = op;
11422
- break;
11423
- case OpKind.I18nEnd:
11424
- if (currentI18nOp === null) {
11425
- throw new Error('AssertionError: Expected an active I18n block while calculating last slot consumers');
11424
+ else if (createOp.kind === OpKind.I18nEnd) {
11425
+ for (const op of i18nExpressionsInProgress) {
11426
+ op.target = state.lastSlotConsumer;
11427
+ OpList.insertBefore(op, updateOp);
11428
+ }
11429
+ i18nExpressionsInProgress.length = 0;
11430
+ state = null;
11431
+ }
11432
+ if (hasConsumesSlotTrait(createOp)) {
11433
+ if (state !== null) {
11434
+ state.lastSlotConsumer = createOp.xref;
11435
+ }
11436
+ while (true) {
11437
+ if (updateOp.next === null) {
11438
+ break;
11426
11439
  }
11427
- if (lastSlotConsumer === null) {
11428
- throw new Error('AssertionError: Expected a last slot consumer while calculating last slot consumers');
11440
+ if (state !== null && updateOp.kind === OpKind.I18nExpression &&
11441
+ updateOp.usage === I18nExpressionFor.I18nText &&
11442
+ updateOp.i18nOwner === state.blockXref) {
11443
+ const opToRemove = updateOp;
11444
+ updateOp = updateOp.next;
11445
+ OpList.remove(opToRemove);
11446
+ i18nExpressionsInProgress.push(opToRemove);
11447
+ continue;
11429
11448
  }
11430
- i18nLastSlotConsumers.set(currentI18nOp.xref, lastSlotConsumer);
11431
- currentI18nOp = null;
11432
- break;
11433
- }
11434
- }
11435
- // Expresions that are currently being moved.
11436
- let opsToMove = [];
11437
- // Previously we found the last slot-consuming create mode op in the i18n block. That op will be
11438
- // the new target for any moved i18n expresion inside the i18n block, and that op's slot is
11439
- // stored here.
11440
- let moveAfterTarget = null;
11441
- // This is the last target in the create IR that we saw during iteration. Eventally, it will be
11442
- // equal to moveAfterTarget. But wait! We need to find the *last* such op whose target is equal
11443
- // to `moveAfterTarget`.
11444
- let previousTarget = null;
11445
- for (const op of unit.update) {
11446
- if (hasDependsOnSlotContextTrait(op)) {
11447
- // We've found an op that depends on another slot other than the one that we want to move
11448
- // the expressions to, after previously having seen the one we want to move to.
11449
- if (moveAfterTarget !== null && previousTarget === moveAfterTarget &&
11450
- op.target !== previousTarget) {
11451
- OpList.insertBefore(opsToMove, op);
11452
- moveAfterTarget = null;
11453
- opsToMove = [];
11454
- }
11455
- previousTarget = op.target;
11456
- }
11457
- if (op.kind === OpKind.I18nExpression && op.usage === I18nExpressionFor.I18nText) {
11458
- // This is an I18nExpressionOps that is used for text (not attributes).
11459
- OpList.remove(op);
11460
- opsToMove.push(op);
11461
- const target = i18nLastSlotConsumers.get(op.i18nOwner);
11462
- if (target === undefined) {
11463
- throw new Error('AssertionError: Expected to find a last slot consumer for an I18nExpressionOp');
11449
+ if (hasDependsOnSlotContextTrait(updateOp) && updateOp.target !== createOp.xref) {
11450
+ break;
11451
+ }
11452
+ updateOp = updateOp.next;
11464
11453
  }
11465
- op.target = target;
11466
- moveAfterTarget = op.target;
11467
11454
  }
11468
11455
  }
11469
- if (moveAfterTarget !== null) {
11470
- unit.update.push(opsToMove);
11471
- }
11472
11456
  }
11473
11457
  }
11474
11458
 
@@ -11501,19 +11485,21 @@ function extractAttributes(job) {
11501
11485
  case OpKind.Property:
11502
11486
  if (!op.isAnimationTrigger) {
11503
11487
  let bindingKind;
11504
- if (op.i18nContext !== null) {
11488
+ if (op.i18nMessage !== null && op.templateKind === null) {
11505
11489
  // If the binding has an i18n context, it is an i18n attribute, and should have that
11506
11490
  // kind in the consts array.
11507
11491
  bindingKind = BindingKind.I18n;
11508
11492
  }
11509
- else if (op.isStructuralTemplate) {
11510
- // TODO: How do i18n attributes on templates work?!
11493
+ else if (op.isStructuralTemplateAttribute) {
11511
11494
  bindingKind = BindingKind.Template;
11512
11495
  }
11513
11496
  else {
11514
11497
  bindingKind = BindingKind.Property;
11515
11498
  }
11516
- OpList.insertBefore(createExtractedAttributeOp(op.target, bindingKind, op.name, null, null), lookupElement$2(elements, op.target));
11499
+ OpList.insertBefore(
11500
+ // Deliberaly null i18nMessage value
11501
+ createExtractedAttributeOp(op.target, bindingKind, op.name, /* expression */ null, /* i18nContext */ null,
11502
+ /* i18nMessage */ null, op.securityContext), lookupElement$2(elements, op.target));
11517
11503
  }
11518
11504
  break;
11519
11505
  case OpKind.StyleProp:
@@ -11524,12 +11510,16 @@ function extractAttributes(job) {
11524
11510
  // mode.
11525
11511
  if (unit.job.compatibility === CompatibilityMode.TemplateDefinitionBuilder &&
11526
11512
  op.expression instanceof EmptyExpr) {
11527
- OpList.insertBefore(createExtractedAttributeOp(op.target, BindingKind.Property, op.name, null, null), lookupElement$2(elements, op.target));
11513
+ OpList.insertBefore(createExtractedAttributeOp(op.target, BindingKind.Property, op.name, /* expression */ null,
11514
+ /* i18nContext */ null,
11515
+ /* i18nMessage */ null, SecurityContext.STYLE), lookupElement$2(elements, op.target));
11528
11516
  }
11529
11517
  break;
11530
11518
  case OpKind.Listener:
11531
11519
  if (!op.isAnimationListener) {
11532
- const extractedAttributeOp = createExtractedAttributeOp(op.target, BindingKind.Property, op.name, null, null);
11520
+ const extractedAttributeOp = createExtractedAttributeOp(op.target, BindingKind.Property, op.name, /* expression */ null,
11521
+ /* i18nContext */ null,
11522
+ /* i18nMessage */ null, SecurityContext.NONE);
11533
11523
  if (job.kind === CompilationJobKind.Host) {
11534
11524
  // This attribute will apply to the enclosing host binding compilation unit, so order
11535
11525
  // doesn't matter.
@@ -11578,7 +11568,7 @@ function extractAttributeOp(unit, op, elements) {
11578
11568
  }
11579
11569
  }
11580
11570
  if (extractable) {
11581
- const extractedAttributeOp = createExtractedAttributeOp(op.target, op.isStructuralTemplate ? BindingKind.Template : BindingKind.Attribute, op.name, op.expression, op.i18nContext);
11571
+ const extractedAttributeOp = createExtractedAttributeOp(op.target, op.isStructuralTemplateAttribute ? BindingKind.Template : BindingKind.Attribute, op.name, op.expression, op.i18nContext, op.i18nMessage, op.securityContext);
11582
11572
  if (unit.job.kind === CompilationJobKind.Host) {
11583
11573
  // This attribute will apply to the enclosing host binding compilation unit, so order doesn't
11584
11574
  // matter.
@@ -11625,16 +11615,16 @@ function specializeBindings(job) {
11625
11615
  target.nonBindable = true;
11626
11616
  }
11627
11617
  else {
11628
- OpList.replace(op, createAttributeOp(op.target, op.name, op.expression, op.securityContext, op.isTextAttribute, op.isStructuralTemplate, op.i18nContext, op.sourceSpan));
11618
+ OpList.replace(op, createAttributeOp(op.target, op.name, op.expression, op.securityContext, op.isTextAttribute, op.isStructuralTemplateAttribute, op.templateKind, op.i18nMessage, op.sourceSpan));
11629
11619
  }
11630
11620
  break;
11631
11621
  case BindingKind.Property:
11632
11622
  case BindingKind.Animation:
11633
11623
  if (job.kind === CompilationJobKind.Host) {
11634
- OpList.replace(op, createHostPropertyOp(op.name, op.expression, op.bindingKind === BindingKind.Animation, op.i18nContext, op.sourceSpan));
11624
+ OpList.replace(op, createHostPropertyOp(op.name, op.expression, op.bindingKind === BindingKind.Animation, op.i18nContext, op.securityContext, op.sourceSpan));
11635
11625
  }
11636
11626
  else {
11637
- OpList.replace(op, createPropertyOp(op.target, op.name, op.expression, op.bindingKind === BindingKind.Animation, op.securityContext, op.isStructuralTemplate, op.i18nContext, op.sourceSpan));
11627
+ OpList.replace(op, createPropertyOp(op.target, op.name, op.expression, op.bindingKind === BindingKind.Animation, op.securityContext, op.isStructuralTemplateAttribute, op.templateKind, op.i18nContext, op.i18nMessage, op.sourceSpan));
11638
11628
  }
11639
11629
  break;
11640
11630
  case BindingKind.I18n:
@@ -11871,7 +11861,7 @@ function collectElementConsts(job) {
11871
11861
  if (op.kind === OpKind.ExtractedAttribute) {
11872
11862
  const attributes = allElementAttributes.get(op.target) || new ElementAttributes();
11873
11863
  allElementAttributes.set(op.target, attributes);
11874
- attributes.add(op.bindingKind, op.name, op.expression);
11864
+ attributes.add(op.bindingKind, op.name, op.expression, op.trustedValueFn);
11875
11865
  OpList.remove(op);
11876
11866
  }
11877
11867
  }
@@ -11937,11 +11927,12 @@ class ElementAttributes {
11937
11927
  get i18n() {
11938
11928
  return this.byKind.get(BindingKind.I18n) ?? FLYWEIGHT_ARRAY;
11939
11929
  }
11940
- add(kind, name, value) {
11930
+ add(kind, name, value, trustedValueFn) {
11941
11931
  if (this.known.has(name)) {
11942
11932
  return;
11943
11933
  }
11944
11934
  this.known.add(name);
11935
+ // TODO: Can this be its own phase
11945
11936
  if (name === 'ngProjectAs') {
11946
11937
  if (value === null || !(value instanceof LiteralExpr) || (value.value == null) ||
11947
11938
  (typeof value.value?.toString() !== 'string')) {
@@ -11957,7 +11948,15 @@ class ElementAttributes {
11957
11948
  if (value === null) {
11958
11949
  throw Error('Attribute, i18n attribute, & style element attributes must have a value');
11959
11950
  }
11960
- array.push(value);
11951
+ if (trustedValueFn !== null) {
11952
+ if (!isStringLiteral(value)) {
11953
+ throw Error('AssertionError: extracted attribute value should be string literal');
11954
+ }
11955
+ array.push(taggedTemplate(trustedValueFn, new TemplateLiteral([new TemplateLiteralElement(value.value)], []), undefined, value.sourceSpan));
11956
+ }
11957
+ else {
11958
+ array.push(value);
11959
+ }
11961
11960
  }
11962
11961
  }
11963
11962
  arrayFor(kind) {
@@ -12101,6 +12100,9 @@ function createI18nContexts(job) {
12101
12100
  const rootContexts = new Map();
12102
12101
  let currentI18nOp = null;
12103
12102
  let xref;
12103
+ // We use the message instead of the message ID, because placeholder values might differ even
12104
+ // when IDs are the same.
12105
+ const messageToContext = new Map();
12104
12106
  for (const unit of job.units) {
12105
12107
  for (const op of unit.create) {
12106
12108
  switch (op.kind) {
@@ -12138,6 +12140,25 @@ function createI18nContexts(job) {
12138
12140
  break;
12139
12141
  }
12140
12142
  }
12143
+ for (const op of unit.ops()) {
12144
+ switch (op.kind) {
12145
+ case OpKind.Binding:
12146
+ case OpKind.Property:
12147
+ case OpKind.Attribute:
12148
+ case OpKind.ExtractedAttribute:
12149
+ if (!op.i18nMessage) {
12150
+ continue;
12151
+ }
12152
+ if (!messageToContext.has(op.i18nMessage)) {
12153
+ // create the context
12154
+ const i18nContext = job.allocateXrefId();
12155
+ unit.create.push(createI18nContextOp(I18nContextKind.Attr, i18nContext, null, op.i18nMessage, null));
12156
+ messageToContext.set(op.i18nMessage, i18nContext);
12157
+ }
12158
+ op.i18nContext = messageToContext.get(op.i18nMessage);
12159
+ break;
12160
+ }
12161
+ }
12141
12162
  }
12142
12163
  // Assign contexts to child i18n blocks, now that all root i18n blocks have their context
12143
12164
  // assigned.
@@ -12812,9 +12833,15 @@ function recursivelyProcessView(view, parentScope) {
12812
12833
  for (const op of view.create) {
12813
12834
  switch (op.kind) {
12814
12835
  case OpKind.Template:
12836
+ // Descend into child embedded views.
12837
+ recursivelyProcessView(view.job.views.get(op.xref), scope);
12838
+ break;
12815
12839
  case OpKind.RepeaterCreate:
12816
12840
  // Descend into child embedded views.
12817
12841
  recursivelyProcessView(view.job.views.get(op.xref), scope);
12842
+ if (op.emptyView) {
12843
+ recursivelyProcessView(view.job.views.get(op.emptyView), scope);
12844
+ }
12818
12845
  break;
12819
12846
  case OpKind.Listener:
12820
12847
  // Prepend variables to listener handler functions.
@@ -19954,7 +19981,7 @@ const ESCAPE = '\uFFFD';
19954
19981
  function collectI18nConsts(job) {
19955
19982
  const fileBasedI18nSuffix = job.relativeContextFilePath.replace(/[^A-Za-z0-9]/g, '_').toUpperCase() + '_';
19956
19983
  // Step One: Build up various lookup maps we need to collect all the consts.
19957
- // Context Xref -> Extracted Attribute Op
19984
+ // Context Xref -> Extracted Attribute Ops
19958
19985
  const extractedAttributesByI18nContext = new Map();
19959
19986
  // Element/ElementStart Xref -> I18n Attributes config op
19960
19987
  const i18nAttributesByElement = new Map();
@@ -19965,7 +19992,9 @@ function collectI18nConsts(job) {
19965
19992
  for (const unit of job.units) {
19966
19993
  for (const op of unit.ops()) {
19967
19994
  if (op.kind === OpKind.ExtractedAttribute && op.i18nContext !== null) {
19968
- extractedAttributesByI18nContext.set(op.i18nContext, op);
19995
+ const attributes = extractedAttributesByI18nContext.get(op.i18nContext) ?? [];
19996
+ attributes.push(op);
19997
+ extractedAttributesByI18nContext.set(op.i18nContext, attributes);
19969
19998
  }
19970
19999
  else if (op.kind === OpKind.I18nAttributes) {
19971
20000
  i18nAttributesByElement.set(op.target, op);
@@ -20010,9 +20039,11 @@ function collectI18nConsts(job) {
20010
20039
  i18nValuesByContext.set(op.i18nContext, mainVar);
20011
20040
  // This i18n message may correspond to an individual extracted attribute. If so, The
20012
20041
  // value of that attribute is updated to read the extracted i18n variable.
20013
- const attributeForMessage = extractedAttributesByI18nContext.get(op.i18nContext);
20014
- if (attributeForMessage !== undefined) {
20015
- attributeForMessage.expression = mainVar;
20042
+ const attributesForMessage = extractedAttributesByI18nContext.get(op.i18nContext);
20043
+ if (attributesForMessage !== undefined) {
20044
+ for (const attr of attributesForMessage) {
20045
+ attr.expression = mainVar.clone();
20046
+ }
20016
20047
  }
20017
20048
  }
20018
20049
  }
@@ -20800,14 +20831,14 @@ function parseExtractedStyles(job) {
20800
20831
  if (op.name === 'style') {
20801
20832
  const parsedStyles = parse(op.expression.value);
20802
20833
  for (let i = 0; i < parsedStyles.length - 1; i += 2) {
20803
- OpList.insertBefore(createExtractedAttributeOp(op.target, BindingKind.StyleProperty, parsedStyles[i], literal(parsedStyles[i + 1]), null), op);
20834
+ OpList.insertBefore(createExtractedAttributeOp(op.target, BindingKind.StyleProperty, parsedStyles[i], literal(parsedStyles[i + 1]), null, null, SecurityContext.STYLE), op);
20804
20835
  }
20805
20836
  OpList.remove(op);
20806
20837
  }
20807
20838
  else if (op.name === 'class') {
20808
20839
  const parsedClasses = op.expression.value.trim().split(/\s+/g);
20809
20840
  for (const parsedClass of parsedClasses) {
20810
- OpList.insertBefore(createExtractedAttributeOp(op.target, BindingKind.ClassName, parsedClass, null, null), op);
20841
+ OpList.insertBefore(createExtractedAttributeOp(op.target, BindingKind.ClassName, parsedClass, null, null, null, SecurityContext.NONE), op);
20811
20842
  }
20812
20843
  OpList.remove(op);
20813
20844
  }
@@ -20973,22 +21004,37 @@ function propagateI18nBlocksToTemplates(unit, subTemplateIndex) {
20973
21004
  i18nBlock = null;
20974
21005
  break;
20975
21006
  case OpKind.Template:
20976
- const templateView = unit.job.views.get(op.xref);
20977
- // We found an <ng-template> inside an i18n block; increment the sub-template counter and
20978
- // wrap the template's view in a child i18n block.
20979
- if (op.i18nPlaceholder !== undefined) {
20980
- if (i18nBlock === null) {
20981
- throw Error('Expected template with i18n placeholder to be in an i18n block.');
20982
- }
20983
- subTemplateIndex++;
20984
- wrapTemplateWithI18n(templateView, i18nBlock);
21007
+ subTemplateIndex = propagateI18nBlocksForView(unit.job.views.get(op.xref), i18nBlock, op.i18nPlaceholder, subTemplateIndex);
21008
+ break;
21009
+ case OpKind.RepeaterCreate:
21010
+ // Propagate i18n blocks to the @for template.
21011
+ const forView = unit.job.views.get(op.xref);
21012
+ subTemplateIndex = propagateI18nBlocksForView(unit.job.views.get(op.xref), i18nBlock, op.i18nPlaceholder, subTemplateIndex);
21013
+ // Then if there's an @empty template, propagate the i18n blocks for it as well.
21014
+ if (op.emptyView !== null) {
21015
+ subTemplateIndex = propagateI18nBlocksForView(unit.job.views.get(op.emptyView), i18nBlock, op.emptyI18nPlaceholder, subTemplateIndex);
20985
21016
  }
20986
- // Continue traversing inside the template's view.
20987
- subTemplateIndex = propagateI18nBlocksToTemplates(templateView, subTemplateIndex);
21017
+ break;
20988
21018
  }
20989
21019
  }
20990
21020
  return subTemplateIndex;
20991
21021
  }
21022
+ /**
21023
+ * Propagate i18n blocks for a view.
21024
+ */
21025
+ function propagateI18nBlocksForView(view, i18nBlock, i18nPlaceholder, subTemplateIndex) {
21026
+ // We found an <ng-template> inside an i18n block; increment the sub-template counter and
21027
+ // wrap the template's view in a child i18n block.
21028
+ if (i18nPlaceholder !== undefined) {
21029
+ if (i18nBlock === null) {
21030
+ throw Error('Expected template with i18n placeholder to be in an i18n block.');
21031
+ }
21032
+ subTemplateIndex++;
21033
+ wrapTemplateWithI18n(view, i18nBlock);
21034
+ }
21035
+ // Continue traversing inside the template's view.
21036
+ return propagateI18nBlocksToTemplates(view, subTemplateIndex);
21037
+ }
20992
21038
  /**
20993
21039
  * Wraps a template view with i18n start and end ops.
20994
21040
  */
@@ -21154,17 +21200,13 @@ function disableBindings() {
21154
21200
  function enableBindings() {
21155
21201
  return call(Identifiers.enableBindings, [], null);
21156
21202
  }
21157
- function listener(name, handlerFn, sourceSpan) {
21158
- return call(Identifiers.listener, [
21159
- literal(name),
21160
- handlerFn,
21161
- ], sourceSpan);
21162
- }
21163
- function syntheticHostListener(name, handlerFn, sourceSpan) {
21164
- return call(Identifiers.syntheticHostListener, [
21165
- literal(name),
21166
- handlerFn,
21167
- ], sourceSpan);
21203
+ function listener(name, handlerFn, eventTargetResolver, syntheticHost, sourceSpan) {
21204
+ const args = [literal(name), handlerFn];
21205
+ if (eventTargetResolver !== null) {
21206
+ args.push(literal(false)); // `useCapture` flag, defaults to `false`
21207
+ args.push(importExpr(eventTargetResolver));
21208
+ }
21209
+ return call(syntheticHost ? Identifiers.syntheticHostListener : Identifiers.listener, args, sourceSpan);
21168
21210
  }
21169
21211
  function pipe(slot, name) {
21170
21212
  return call(Identifiers.pipe, [
@@ -21425,8 +21467,12 @@ function classMapInterpolate(strings, expressions, sourceSpan) {
21425
21467
  const interpolationArgs = collateInterpolationArgs(strings, expressions);
21426
21468
  return callVariadicInstruction(CLASS_MAP_INTERPOLATE_CONFIG, [], interpolationArgs, [], sourceSpan);
21427
21469
  }
21428
- function hostProperty(name, expression, sourceSpan) {
21429
- return call(Identifiers.hostProperty, [literal(name), expression], sourceSpan);
21470
+ function hostProperty(name, expression, sanitizer, sourceSpan) {
21471
+ const args = [literal(name), expression];
21472
+ if (sanitizer !== null) {
21473
+ args.push(sanitizer);
21474
+ }
21475
+ return call(Identifiers.hostProperty, args, sourceSpan);
21430
21476
  }
21431
21477
  function syntheticHostProperty(name, expression, sourceSpan) {
21432
21478
  return call(Identifiers.syntheticHostProperty, [literal(name), expression], sourceSpan);
@@ -21644,14 +21690,12 @@ function callVariadicInstruction(config, baseArgs, interpolationArgs, extraArgs,
21644
21690
  }
21645
21691
 
21646
21692
  /**
21647
- * Map of sanitizers to their identifier.
21693
+ * Map of target resolvers for event listeners.
21648
21694
  */
21649
- const sanitizerIdentifierMap = new Map([
21650
- [SanitizerFn.Html, Identifiers.sanitizeHtml],
21651
- [SanitizerFn.IframeAttribute, Identifiers.validateIframeAttribute],
21652
- [SanitizerFn.ResourceUrl, Identifiers.sanitizeResourceUrl],
21653
- [SanitizerFn.Script, Identifiers.sanitizeScript],
21654
- [SanitizerFn.Style, Identifiers.sanitizeStyle], [SanitizerFn.Url, Identifiers.sanitizeUrl]
21695
+ const GLOBAL_TARGET_RESOLVERS$1 = new Map([
21696
+ ['window', Identifiers.resolveWindow],
21697
+ ['document', Identifiers.resolveDocument],
21698
+ ['body', Identifiers.resolveBody],
21655
21699
  ]);
21656
21700
  /**
21657
21701
  * Compiles semantic operations across all views and generates output `o.Statement`s with actual
@@ -21728,10 +21772,11 @@ function reifyCreateOperations(unit, ops) {
21728
21772
  break;
21729
21773
  case OpKind.Listener:
21730
21774
  const listenerFn = reifyListenerHandler(unit, op.handlerFnName, op.handlerOps, op.consumesDollarEvent);
21731
- const reified = op.hostListener && op.isAnimationListener ?
21732
- syntheticHostListener(op.name, listenerFn, op.sourceSpan) :
21733
- listener(op.name, listenerFn, op.sourceSpan);
21734
- OpList.replace(op, reified);
21775
+ const eventTargetResolver = op.eventTarget ? GLOBAL_TARGET_RESOLVERS$1.get(op.eventTarget) : null;
21776
+ if (eventTargetResolver === undefined) {
21777
+ throw new Error(`AssertionError: unknown event target ${op.eventTarget}`);
21778
+ }
21779
+ OpList.replace(op, listener(op.name, listenerFn, eventTargetResolver, op.hostListener && op.isAnimationListener, op.sourceSpan));
21735
21780
  break;
21736
21781
  case OpKind.Variable:
21737
21782
  if (op.variable.name === null) {
@@ -21894,7 +21939,7 @@ function reifyUpdateOperations(_unit, ops) {
21894
21939
  OpList.replace(op, syntheticHostProperty(op.name, op.expression, op.sourceSpan));
21895
21940
  }
21896
21941
  else {
21897
- OpList.replace(op, hostProperty(op.name, op.expression, op.sourceSpan));
21942
+ OpList.replace(op, hostProperty(op.name, op.expression, op.sanitizer, op.sourceSpan));
21898
21943
  }
21899
21944
  }
21900
21945
  break;
@@ -21973,8 +22018,6 @@ function reifyIrExpression(expr) {
21973
22018
  return pipeBind(expr.targetSlot.slot, expr.varOffset, expr.args);
21974
22019
  case ExpressionKind.PipeBindingVariadic:
21975
22020
  return pipeBindV(expr.targetSlot.slot, expr.varOffset, expr.args);
21976
- case ExpressionKind.SanitizerExpr:
21977
- return importExpr(sanitizerIdentifierMap.get(expr.fn));
21978
22021
  case ExpressionKind.SlotLiteralExpr:
21979
22022
  return literal(expr.slot.slot);
21980
22023
  default:
@@ -22267,10 +22310,11 @@ function resolvePlaceholdersForView(job, unit, i18nContexts, elements, pendingSt
22267
22310
  }
22268
22311
  break;
22269
22312
  case OpKind.Template:
22313
+ const view = job.views.get(op.xref);
22270
22314
  if (op.i18nPlaceholder === undefined) {
22271
22315
  // If there is no i18n placeholder, just recurse into the view in case it contains i18n
22272
22316
  // blocks.
22273
- resolvePlaceholdersForView(job, job.views.get(op.xref), i18nContexts, elements);
22317
+ resolvePlaceholdersForView(job, view, i18nContexts, elements);
22274
22318
  }
22275
22319
  else {
22276
22320
  if (currentOps === null) {
@@ -22281,14 +22325,59 @@ function resolvePlaceholdersForView(job, unit, i18nContexts, elements, pendingSt
22281
22325
  // the current template as a pending structural directive to be recorded when we find
22282
22326
  // the element, content, or template it belongs to. This allows us to create combined
22283
22327
  // values that represent, e.g. the start of a template and element at the same time.
22284
- resolvePlaceholdersForView(job, job.views.get(op.xref), i18nContexts, elements, op);
22328
+ resolvePlaceholdersForView(job, view, i18nContexts, elements, op);
22285
22329
  }
22286
22330
  else {
22287
22331
  // If this is some other kind of template, we can record its start, recurse into its
22288
22332
  // view, and then record its end.
22289
- recordTemplateStart(job, op, currentOps.i18nContext, currentOps.i18nBlock, pendingStructuralDirective);
22290
- resolvePlaceholdersForView(job, job.views.get(op.xref), i18nContexts, elements);
22291
- recordTemplateClose(job, op, currentOps.i18nContext, currentOps.i18nBlock, pendingStructuralDirective);
22333
+ recordTemplateStart(job, view, op.handle.slot, op.i18nPlaceholder, currentOps.i18nContext, currentOps.i18nBlock, pendingStructuralDirective);
22334
+ resolvePlaceholdersForView(job, view, i18nContexts, elements);
22335
+ recordTemplateClose(job, view, op.handle.slot, op.i18nPlaceholder, currentOps.i18nContext, currentOps.i18nBlock, pendingStructuralDirective);
22336
+ pendingStructuralDirective = undefined;
22337
+ }
22338
+ }
22339
+ break;
22340
+ case OpKind.RepeaterCreate:
22341
+ if (pendingStructuralDirective !== undefined) {
22342
+ throw Error('AssertionError: Unexpected structural directive associated with @for block');
22343
+ }
22344
+ // RepeaterCreate has 3 slots: the first is for the op itself, the second is for the @for
22345
+ // template and the (optional) third is for the @empty template.
22346
+ const forSlot = op.handle.slot + 1;
22347
+ const forView = job.views.get(op.xref);
22348
+ // First record all of the placeholders for the @for template.
22349
+ if (op.i18nPlaceholder === undefined) {
22350
+ // If there is no i18n placeholder, just recurse into the view in case it contains i18n
22351
+ // blocks.
22352
+ resolvePlaceholdersForView(job, forView, i18nContexts, elements);
22353
+ }
22354
+ else {
22355
+ if (currentOps === null) {
22356
+ throw Error('i18n tag placeholder should only occur inside an i18n block');
22357
+ }
22358
+ recordTemplateStart(job, forView, forSlot, op.i18nPlaceholder, currentOps.i18nContext, currentOps.i18nBlock, pendingStructuralDirective);
22359
+ resolvePlaceholdersForView(job, forView, i18nContexts, elements);
22360
+ recordTemplateClose(job, forView, forSlot, op.i18nPlaceholder, currentOps.i18nContext, currentOps.i18nBlock, pendingStructuralDirective);
22361
+ pendingStructuralDirective = undefined;
22362
+ }
22363
+ // Then if there's an @empty template, add its placeholders as well.
22364
+ if (op.emptyView !== null) {
22365
+ // RepeaterCreate has 3 slots: the first is for the op itself, the second is for the @for
22366
+ // template and the (optional) third is for the @empty template.
22367
+ const emptySlot = op.handle.slot + 2;
22368
+ const emptyView = job.views.get(op.emptyView);
22369
+ if (op.emptyI18nPlaceholder === undefined) {
22370
+ // If there is no i18n placeholder, just recurse into the view in case it contains i18n
22371
+ // blocks.
22372
+ resolvePlaceholdersForView(job, emptyView, i18nContexts, elements);
22373
+ }
22374
+ else {
22375
+ if (currentOps === null) {
22376
+ throw Error('i18n tag placeholder should only occur inside an i18n block');
22377
+ }
22378
+ recordTemplateStart(job, emptyView, emptySlot, op.emptyI18nPlaceholder, currentOps.i18nContext, currentOps.i18nBlock, pendingStructuralDirective);
22379
+ resolvePlaceholdersForView(job, emptyView, i18nContexts, elements);
22380
+ recordTemplateClose(job, emptyView, emptySlot, op.emptyI18nPlaceholder, currentOps.i18nContext, currentOps.i18nBlock, pendingStructuralDirective);
22292
22381
  pendingStructuralDirective = undefined;
22293
22382
  }
22294
22383
  }
@@ -22336,8 +22425,8 @@ function recordElementClose(op, i18nContext, i18nBlock, structuralDirective) {
22336
22425
  /**
22337
22426
  * Records an i18n param value for the start of a template.
22338
22427
  */
22339
- function recordTemplateStart(job, op, i18nContext, i18nBlock, structuralDirective) {
22340
- let { startName, closeName } = op.i18nPlaceholder;
22428
+ function recordTemplateStart(job, view, slot, i18nPlaceholder, i18nContext, i18nBlock, structuralDirective) {
22429
+ let { startName, closeName } = i18nPlaceholder;
22341
22430
  let flags = I18nParamValueFlags.TemplateTag | I18nParamValueFlags.OpenTag;
22342
22431
  // For self-closing tags, there is no close tag placeholder. Instead, the start tag
22343
22432
  // placeholder accounts for the start and close of the element.
@@ -22352,20 +22441,20 @@ function recordTemplateStart(job, op, i18nContext, i18nBlock, structuralDirectiv
22352
22441
  }
22353
22442
  // Record the start of the template. For the sub-template index, pass the index for the template's
22354
22443
  // view, rather than the current i18n block's index.
22355
- addParam(i18nContext.params, startName, op.handle.slot, getSubTemplateIndexForTemplateTag(job, i18nBlock, op), flags);
22444
+ addParam(i18nContext.params, startName, slot, getSubTemplateIndexForTemplateTag(job, i18nBlock, view), flags);
22356
22445
  }
22357
22446
  /**
22358
22447
  * Records an i18n param value for the closing of a template.
22359
22448
  */
22360
- function recordTemplateClose(job, op, i18nContext, i18nBlock, structuralDirective) {
22361
- const { startName, closeName } = op.i18nPlaceholder;
22449
+ function recordTemplateClose(job, view, slot, i18nPlaceholder, i18nContext, i18nBlock, structuralDirective) {
22450
+ const { startName, closeName } = i18nPlaceholder;
22362
22451
  const flags = I18nParamValueFlags.TemplateTag | I18nParamValueFlags.CloseTag;
22363
22452
  // Self-closing tags don't have a closing tag placeholder, instead the template's closing is
22364
22453
  // recorded via an additional flag on the template start value.
22365
22454
  if (closeName) {
22366
22455
  // Record the closing of the template. For the sub-template index, pass the index for the
22367
22456
  // template's view, rather than the current i18n block's index.
22368
- addParam(i18nContext.params, closeName, op.handle.slot, getSubTemplateIndexForTemplateTag(job, i18nBlock, op), flags);
22457
+ addParam(i18nContext.params, closeName, slot, getSubTemplateIndexForTemplateTag(job, i18nBlock, view), flags);
22369
22458
  // If the template is associated with a structural directive, record the structural directive's
22370
22459
  // closing after. Since this template must be in the structural directive's view, we can just
22371
22460
  // directly use the current i18n block's sub-template index.
@@ -22378,8 +22467,8 @@ function recordTemplateClose(job, op, i18nContext, i18nBlock, structuralDirectiv
22378
22467
  * Get the subTemplateIndex for the given template op. For template ops, use the subTemplateIndex of
22379
22468
  * the child i18n block inside the template.
22380
22469
  */
22381
- function getSubTemplateIndexForTemplateTag(job, i18nOp, op) {
22382
- for (const childOp of job.views.get(op.xref).create) {
22470
+ function getSubTemplateIndexForTemplateTag(job, i18nOp, view) {
22471
+ for (const childOp of view.create) {
22383
22472
  if (childOp.kind === OpKind.I18nStart) {
22384
22473
  return childOp.subTemplateIndex;
22385
22474
  }
@@ -22596,12 +22685,20 @@ function processLexicalScope(unit, ops, savedView) {
22596
22685
  }
22597
22686
 
22598
22687
  /**
22599
- * Mapping of security contexts to sanitizer function for that context.
22688
+ * Map of security contexts to their sanitizer function.
22600
22689
  */
22601
- const sanitizers = new Map([
22602
- [SecurityContext.HTML, SanitizerFn.Html], [SecurityContext.SCRIPT, SanitizerFn.Script],
22603
- [SecurityContext.STYLE, SanitizerFn.Style], [SecurityContext.URL, SanitizerFn.Url],
22604
- [SecurityContext.RESOURCE_URL, SanitizerFn.ResourceUrl]
22690
+ const sanitizerFns = new Map([
22691
+ [SecurityContext.HTML, Identifiers.sanitizeHtml],
22692
+ [SecurityContext.RESOURCE_URL, Identifiers.sanitizeResourceUrl],
22693
+ [SecurityContext.SCRIPT, Identifiers.sanitizeScript],
22694
+ [SecurityContext.STYLE, Identifiers.sanitizeStyle], [SecurityContext.URL, Identifiers.sanitizeUrl]
22695
+ ]);
22696
+ /**
22697
+ * Map of security contexts to their trusted value function.
22698
+ */
22699
+ const trustedValueFns = new Map([
22700
+ [SecurityContext.HTML, Identifiers.trustConstantHtml],
22701
+ [SecurityContext.RESOURCE_URL, Identifiers.trustConstantResourceUrl],
22605
22702
  ]);
22606
22703
  /**
22607
22704
  * Resolves sanitization functions for ops that need them.
@@ -22609,24 +22706,61 @@ const sanitizers = new Map([
22609
22706
  function resolveSanitizers(job) {
22610
22707
  for (const unit of job.units) {
22611
22708
  const elements = createOpXrefMap(unit);
22612
- let sanitizerFn;
22709
+ // For normal element bindings we create trusted values for security sensitive constant
22710
+ // attributes. However, for host bindings we skip this step (this matches what
22711
+ // TemplateDefinitionBuilder does).
22712
+ // TODO: Is the TDB behavior correct here?
22713
+ if (job.kind !== CompilationJobKind.Host) {
22714
+ for (const op of unit.create) {
22715
+ if (op.kind === OpKind.ExtractedAttribute) {
22716
+ const trustedValueFn = trustedValueFns.get(getOnlySecurityContext(op.securityContext)) ?? null;
22717
+ op.trustedValueFn = trustedValueFn !== null ? importExpr(trustedValueFn) : null;
22718
+ }
22719
+ }
22720
+ }
22613
22721
  for (const op of unit.update) {
22614
22722
  switch (op.kind) {
22615
22723
  case OpKind.Property:
22616
22724
  case OpKind.Attribute:
22617
- sanitizerFn = sanitizers.get(op.securityContext) || null;
22618
- op.sanitizer = sanitizerFn ? new SanitizerExpr(sanitizerFn) : null;
22725
+ case OpKind.HostProperty:
22726
+ let sanitizerFn = null;
22727
+ if (Array.isArray(op.securityContext) && op.securityContext.length === 2 &&
22728
+ op.securityContext.indexOf(SecurityContext.URL) > -1 &&
22729
+ op.securityContext.indexOf(SecurityContext.RESOURCE_URL) > -1) {
22730
+ // When the host element isn't known, some URL attributes (such as "src" and "href") may
22731
+ // be part of multiple different security contexts. In this case we use special
22732
+ // sanitization function and select the actual sanitizer at runtime based on a tag name
22733
+ // that is provided while invoking sanitization function.
22734
+ sanitizerFn = Identifiers.sanitizeUrlOrResourceUrl;
22735
+ }
22736
+ else {
22737
+ sanitizerFn = sanitizerFns.get(getOnlySecurityContext(op.securityContext)) ?? null;
22738
+ }
22739
+ op.sanitizer = sanitizerFn !== null ? importExpr(sanitizerFn) : null;
22619
22740
  // If there was no sanitization function found based on the security context of an
22620
22741
  // attribute/property, check whether this attribute/property is one of the
22621
22742
  // security-sensitive <iframe> attributes (and that the current element is actually an
22622
22743
  // <iframe>).
22623
22744
  if (op.sanitizer === null) {
22624
- const ownerOp = elements.get(op.target);
22625
- if (ownerOp === undefined || !isElementOrContainerOp(ownerOp)) {
22626
- throw Error('Property should have an element-like owner');
22745
+ let isIframe = false;
22746
+ if (job.kind === CompilationJobKind.Host || op.kind === OpKind.HostProperty) {
22747
+ // Note: for host bindings defined on a directive, we do not try to find all
22748
+ // possible places where it can be matched, so we can not determine whether
22749
+ // the host element is an <iframe>. In this case, we just assume it is and append a
22750
+ // validation function, which is invoked at runtime and would have access to the
22751
+ // underlying DOM element to check if it's an <iframe> and if so - run extra checks.
22752
+ isIframe = true;
22753
+ }
22754
+ else {
22755
+ // For a normal binding we can just check if the element its on is an iframe.
22756
+ const ownerOp = elements.get(op.target);
22757
+ if (ownerOp === undefined || !isElementOrContainerOp(ownerOp)) {
22758
+ throw Error('Property should have an element-like owner');
22759
+ }
22760
+ isIframe = isIframeElement$1(ownerOp);
22627
22761
  }
22628
- if (isIframeElement$1(ownerOp) && isIframeSecuritySensitiveAttr(op.name)) {
22629
- op.sanitizer = new SanitizerExpr(SanitizerFn.IframeAttribute);
22762
+ if (isIframe && isIframeSecuritySensitiveAttr(op.name)) {
22763
+ op.sanitizer = importExpr(Identifiers.validateIframeAttribute);
22630
22764
  }
22631
22765
  }
22632
22766
  break;
@@ -22640,6 +22774,22 @@ function resolveSanitizers(job) {
22640
22774
  function isIframeElement$1(op) {
22641
22775
  return op.kind === OpKind.ElementStart && op.tag?.toLowerCase() === 'iframe';
22642
22776
  }
22777
+ /**
22778
+ * Asserts that there is only a single security context and returns it.
22779
+ */
22780
+ function getOnlySecurityContext(securityContext) {
22781
+ if (Array.isArray(securityContext)) {
22782
+ if (securityContext.length > 1) {
22783
+ // TODO: What should we do here? TDB just took the first one, but this feels like something we
22784
+ // would want to know about and create a special case for like we did for Url/ResourceUrl. My
22785
+ // guess is that, outside of the Url/ResourceUrl case, this never actually happens. If there
22786
+ // do turn out to be other cases, throwing an error until we can address it feels safer.
22787
+ throw Error(`AssertionError: Ambiguous security context`);
22788
+ }
22789
+ return securityContext[0] || SecurityContext.NONE;
22790
+ }
22791
+ return securityContext;
22792
+ }
22643
22793
 
22644
22794
  /**
22645
22795
  * When inside of a listener, we may need access to one or more enclosing views. Therefore, each
@@ -22743,6 +22893,8 @@ function allocateSlots(job) {
22743
22893
  // operation itself, so it can be emitted later.
22744
22894
  const childView = job.views.get(op.xref);
22745
22895
  op.decls = childView.decls;
22896
+ // TODO: currently we handle the decls for the RepeaterCreate empty template in the reify
22897
+ // phase. We should handle that here instead.
22746
22898
  }
22747
22899
  }
22748
22900
  }
@@ -23066,6 +23218,8 @@ function countVariables(job) {
23066
23218
  }
23067
23219
  const childView = job.views.get(op.xref);
23068
23220
  op.vars = childView.vars;
23221
+ // TODO: currently we handle the vars for the RepeaterCreate empty template in the reify
23222
+ // phase. We should handle that here instead.
23069
23223
  }
23070
23224
  }
23071
23225
  }
@@ -23587,10 +23741,10 @@ const phases = [
23587
23741
  { kind: CompilationJobKind.Tmpl, fn: emitNamespaceChanges },
23588
23742
  { kind: CompilationJobKind.Tmpl, fn: propagateI18nBlocks },
23589
23743
  { kind: CompilationJobKind.Tmpl, fn: wrapI18nIcus },
23590
- { kind: CompilationJobKind.Tmpl, fn: createI18nContexts },
23591
23744
  { kind: CompilationJobKind.Both, fn: specializeStyleBindings },
23592
23745
  { kind: CompilationJobKind.Both, fn: specializeBindings },
23593
23746
  { kind: CompilationJobKind.Both, fn: extractAttributes },
23747
+ { kind: CompilationJobKind.Tmpl, fn: createI18nContexts },
23594
23748
  { kind: CompilationJobKind.Both, fn: parseExtractedStyles },
23595
23749
  { kind: CompilationJobKind.Tmpl, fn: removeEmptyBindings },
23596
23750
  { kind: CompilationJobKind.Both, fn: collapseSingletonInterpolations },
@@ -23608,7 +23762,7 @@ const phases = [
23608
23762
  { kind: CompilationJobKind.Tmpl, fn: generateProjectionDefs },
23609
23763
  { kind: CompilationJobKind.Tmpl, fn: generateVariables },
23610
23764
  { kind: CompilationJobKind.Tmpl, fn: saveAndRestoreView },
23611
- { kind: CompilationJobKind.Tmpl, fn: deleteAnyCasts },
23765
+ { kind: CompilationJobKind.Both, fn: deleteAnyCasts },
23612
23766
  { kind: CompilationJobKind.Both, fn: resolveDollarEvent },
23613
23767
  { kind: CompilationJobKind.Tmpl, fn: generateRepeaterDerivedVars },
23614
23768
  { kind: CompilationJobKind.Tmpl, fn: generateTrackVariables },
@@ -23616,7 +23770,7 @@ const phases = [
23616
23770
  { kind: CompilationJobKind.Tmpl, fn: resolveDeferTargetNames },
23617
23771
  { kind: CompilationJobKind.Tmpl, fn: optimizeTrackFns },
23618
23772
  { kind: CompilationJobKind.Both, fn: resolveContexts },
23619
- { kind: CompilationJobKind.Tmpl, fn: resolveSanitizers },
23773
+ { kind: CompilationJobKind.Both, fn: resolveSanitizers },
23620
23774
  { kind: CompilationJobKind.Tmpl, fn: liftLocalRefs },
23621
23775
  { kind: CompilationJobKind.Both, fn: generateNullishCoalesceExpressions },
23622
23776
  { kind: CompilationJobKind.Both, fn: expandSafeReads },
@@ -23752,6 +23906,10 @@ function emitHostBindingFunction(job) {
23752
23906
  }
23753
23907
 
23754
23908
  const compatibilityMode = CompatibilityMode.TemplateDefinitionBuilder;
23909
+ // Schema containing DOM elements and their properties.
23910
+ const domSchema = new DomElementSchemaRegistry();
23911
+ // Tag name of the `ng-template` element.
23912
+ const NG_TEMPLATE_TAG_NAME$1 = 'ng-template';
23755
23913
  /**
23756
23914
  * Process a template AST and convert it into a `ComponentCompilation` in the intermediate
23757
23915
  * representation.
@@ -23769,10 +23927,24 @@ function ingestComponent(componentName, template, constantPool, relativeContextF
23769
23927
  function ingestHostBinding(input, bindingParser, constantPool) {
23770
23928
  const job = new HostBindingCompilationJob(input.componentName, constantPool, compatibilityMode);
23771
23929
  for (const property of input.properties ?? []) {
23772
- ingestHostProperty(job, property, false);
23930
+ let bindingKind = BindingKind.Property;
23931
+ // TODO: this should really be handled in the parser.
23932
+ if (property.name.startsWith('attr.')) {
23933
+ property.name = property.name.substring('attr.'.length);
23934
+ bindingKind = BindingKind.Attribute;
23935
+ }
23936
+ if (property.isAnimation) {
23937
+ bindingKind = BindingKind.Animation;
23938
+ }
23939
+ const securityContexts = bindingParser
23940
+ .calcPossibleSecurityContexts(input.componentSelector, property.name, bindingKind === BindingKind.Attribute)
23941
+ .filter(context => context !== SecurityContext.NONE);
23942
+ ingestHostProperty(job, property, bindingKind, false, securityContexts);
23773
23943
  }
23774
23944
  for (const [name, expr] of Object.entries(input.attributes) ?? []) {
23775
- ingestHostAttribute(job, name, expr);
23945
+ const securityContexts = bindingParser.calcPossibleSecurityContexts(input.componentSelector, name, true)
23946
+ .filter(context => context !== SecurityContext.NONE);
23947
+ ingestHostAttribute(job, name, expr, securityContexts);
23776
23948
  }
23777
23949
  for (const event of input.events ?? []) {
23778
23950
  ingestHostEvent(job, event);
@@ -23781,7 +23953,7 @@ function ingestHostBinding(input, bindingParser, constantPool) {
23781
23953
  }
23782
23954
  // TODO: We should refactor the parser to use the same types and structures for host bindings as
23783
23955
  // with ordinary components. This would allow us to share a lot more ingestion code.
23784
- function ingestHostProperty(job, property, isTextAttribute) {
23956
+ function ingestHostProperty(job, property, bindingKind, isTextAttribute, securityContexts) {
23785
23957
  let expression;
23786
23958
  const ast = property.expression.ast;
23787
23959
  if (ast instanceof Interpolation$1) {
@@ -23790,26 +23962,18 @@ function ingestHostProperty(job, property, isTextAttribute) {
23790
23962
  else {
23791
23963
  expression = convertAst(ast, job, property.sourceSpan);
23792
23964
  }
23793
- let bindingKind = BindingKind.Property;
23794
- // TODO: this should really be handled in the parser.
23795
- if (property.name.startsWith('attr.')) {
23796
- property.name = property.name.substring('attr.'.length);
23797
- bindingKind = BindingKind.Attribute;
23798
- }
23799
- if (property.isAnimation) {
23800
- bindingKind = BindingKind.Animation;
23801
- }
23802
- job.root.update.push(createBindingOp(job.root.xref, bindingKind, property.name, expression, null, SecurityContext
23803
- .NONE /* TODO: what should we pass as security context? Passing NONE for now. */, isTextAttribute, false, /* TODO: How do Host bindings handle i18n attrs? */ null, property.sourceSpan));
23965
+ 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));
23804
23966
  }
23805
- function ingestHostAttribute(job, name, value) {
23806
- const attrBinding = createBindingOp(job.root.xref, BindingKind.Attribute, name, value, null, SecurityContext.NONE, true, false,
23967
+ function ingestHostAttribute(job, name, value, securityContexts) {
23968
+ const attrBinding = createBindingOp(job.root.xref, BindingKind.Attribute, name, value, null, securityContexts, true, false, null,
23807
23969
  /* TODO */ null,
23808
23970
  /* TODO: host attribute source spans */ null);
23809
23971
  job.root.update.push(attrBinding);
23810
23972
  }
23811
23973
  function ingestHostEvent(job, event) {
23812
- const eventBinding = createListenerOp(job.root.xref, new SlotHandle(), event.name, null, event.targetOrPhase, true, event.sourceSpan);
23974
+ const [phase, target] = event.type === 0 /* e.ParsedEventType.Regular */ ? [null, event.targetOrPhase] :
23975
+ [event.targetOrPhase, null];
23976
+ const eventBinding = createListenerOp(job.root.xref, new SlotHandle(), event.name, null, [], phase, target, true, event.sourceSpan);
23813
23977
  // TODO: Can this be a chain?
23814
23978
  eventBinding.handlerOps.push(createStatementOp(new ReturnStatement(convertAst(event.handler.ast, job, event.sourceSpan), event.handlerSpan)));
23815
23979
  job.root.create.push(eventBinding);
@@ -23866,8 +24030,14 @@ function ingestElement(unit, element) {
23866
24030
  const [namespaceKey, elementName] = splitNsName(element.name);
23867
24031
  const startOp = createElementStartOp(elementName, id, namespaceForKey(namespaceKey), element.i18n instanceof TagPlaceholder ? element.i18n : undefined, element.startSourceSpan);
23868
24032
  unit.create.push(startOp);
23869
- ingestBindings(unit, startOp, element);
24033
+ ingestElementBindings(unit, startOp, element);
23870
24034
  ingestReferences(startOp, element);
24035
+ // Start i18n, if needed, goes after the element create and bindings, but before the nodes
24036
+ let i18nBlockId = null;
24037
+ if (element.i18n instanceof Message) {
24038
+ i18nBlockId = unit.job.allocateXrefId();
24039
+ unit.create.push(createI18nStartOp(i18nBlockId, element.i18n));
24040
+ }
23871
24041
  ingestNodes(unit, element.children);
23872
24042
  // The source span for the end op is typically the element closing tag. However, if no closing tag
23873
24043
  // exists, such as in `<input>`, we use the start source span instead. Usually the start and end
@@ -23877,9 +24047,7 @@ function ingestElement(unit, element) {
23877
24047
  const endOp = createElementEndOp(id, element.endSourceSpan ?? element.startSourceSpan);
23878
24048
  unit.create.push(endOp);
23879
24049
  // If there is an i18n message associated with this element, insert i18n start and end ops.
23880
- if (element.i18n instanceof Message) {
23881
- const i18nBlockId = unit.job.allocateXrefId();
23882
- OpList.insertAfter(createI18nStartOp(i18nBlockId, element.i18n), startOp);
24050
+ if (i18nBlockId !== null) {
23883
24051
  OpList.insertBefore(createI18nEndOp(i18nBlockId), endOp);
23884
24052
  }
23885
24053
  }
@@ -23905,7 +24073,7 @@ function ingestTemplate(unit, tmpl) {
23905
24073
  const templateKind = isPlainTemplate(tmpl) ? TemplateKind.NgTemplate : TemplateKind.Structural;
23906
24074
  const templateOp = createTemplateOp(childView.xref, templateKind, tagNameWithoutNamespace, functionNameSuffix, namespace, i18nPlaceholder, tmpl.startSourceSpan);
23907
24075
  unit.create.push(templateOp);
23908
- ingestBindings(unit, templateOp, tmpl);
24076
+ ingestTemplateBindings(unit, templateOp, tmpl, templateKind);
23909
24077
  ingestReferences(templateOp, tmpl);
23910
24078
  ingestNodes(childView, tmpl.children);
23911
24079
  for (const { name, value } of tmpl.variables) {
@@ -23921,7 +24089,7 @@ function ingestTemplate(unit, tmpl) {
23921
24089
  }
23922
24090
  }
23923
24091
  /**
23924
- * Ingest a literal text node from the AST into the given `ViewCompilation`.
24092
+ * Ingest a content node from the AST into the given `ViewCompilation`.
23925
24093
  */
23926
24094
  function ingestContent(unit, content) {
23927
24095
  if (content.i18n !== undefined && !(content.i18n instanceof TagPlaceholder)) {
@@ -23930,7 +24098,8 @@ function ingestContent(unit, content) {
23930
24098
  const attrs = content.attributes.flatMap(a => [a.name, a.value]);
23931
24099
  const op = createProjectionOp(unit.job.allocateXrefId(), content.selector, content.i18n, attrs, content.sourceSpan);
23932
24100
  for (const attr of content.attributes) {
23933
- ingestBinding(unit, op.xref, attr.name, literal(attr.value), 1 /* e.BindingType.Attribute */, null, SecurityContext.NONE, attr.sourceSpan, BindingFlags.TextValue, attr.i18n);
24101
+ const securityContext = domSchema.securityContext(content.name, attr.name, true);
24102
+ unit.update.push(createBindingOp(op.xref, BindingKind.Attribute, attr.name, literal(attr.value), null, securityContext, true, false, null, asMessage(attr.i18n), attr.sourceSpan));
23934
24103
  }
23935
24104
  unit.create.push(op);
23936
24105
  }
@@ -24045,13 +24214,16 @@ function ingestSwitchBlock(unit, switchBlock) {
24045
24214
  const conditional = createConditionalOp(firstXref, firstSlotHandle, convertAst(switchBlock.expression, unit.job, null), conditions, switchBlock.sourceSpan);
24046
24215
  unit.update.push(conditional);
24047
24216
  }
24048
- function ingestDeferView(unit, suffix, children, sourceSpan) {
24217
+ function ingestDeferView(unit, suffix, i18nMeta, children, sourceSpan) {
24218
+ if (i18nMeta !== undefined && !(i18nMeta instanceof BlockPlaceholder)) {
24219
+ throw Error('Unhandled i18n metadata type for defer block');
24220
+ }
24049
24221
  if (children === undefined) {
24050
24222
  return null;
24051
24223
  }
24052
24224
  const secondaryView = unit.job.allocateView(unit.xref);
24053
24225
  ingestNodes(secondaryView, children);
24054
- const templateOp = createTemplateOp(secondaryView.xref, TemplateKind.Block, null, `Defer${suffix}`, Namespace.HTML, undefined, sourceSpan);
24226
+ const templateOp = createTemplateOp(secondaryView.xref, TemplateKind.Block, null, `Defer${suffix}`, Namespace.HTML, i18nMeta, sourceSpan);
24055
24227
  unit.create.push(templateOp);
24056
24228
  return templateOp;
24057
24229
  }
@@ -24061,10 +24233,10 @@ function ingestDeferBlock(unit, deferBlock) {
24061
24233
  throw new Error(`AssertionError: unable to find metadata for deferred block`);
24062
24234
  }
24063
24235
  // Generate the defer main view and all secondary views.
24064
- const main = ingestDeferView(unit, '', deferBlock.children, deferBlock.sourceSpan);
24065
- const loading = ingestDeferView(unit, 'Loading', deferBlock.loading?.children, deferBlock.loading?.sourceSpan);
24066
- const placeholder = ingestDeferView(unit, 'Placeholder', deferBlock.placeholder?.children, deferBlock.placeholder?.sourceSpan);
24067
- const error = ingestDeferView(unit, 'Error', deferBlock.error?.children, deferBlock.error?.sourceSpan);
24236
+ const main = ingestDeferView(unit, '', deferBlock.i18n, deferBlock.children, deferBlock.sourceSpan);
24237
+ const loading = ingestDeferView(unit, 'Loading', deferBlock.loading?.i18n, deferBlock.loading?.children, deferBlock.loading?.sourceSpan);
24238
+ const placeholder = ingestDeferView(unit, 'Placeholder', deferBlock.placeholder?.i18n, deferBlock.placeholder?.children, deferBlock.placeholder?.sourceSpan);
24239
+ const error = ingestDeferView(unit, 'Error', deferBlock.error?.i18n, deferBlock.error?.children, deferBlock.error?.sourceSpan);
24068
24240
  // Create the main defer op, and ops for all secondary views.
24069
24241
  const deferXref = unit.job.allocateXrefId();
24070
24242
  const deferOp = createDeferOp(deferXref, main.xref, main.handle, blockMeta, deferBlock.sourceSpan);
@@ -24198,8 +24370,17 @@ function ingestForBlock(unit, forBlock) {
24198
24370
  $odd: forBlock.contextVariables.$odd.name,
24199
24371
  $implicit: forBlock.item.name,
24200
24372
  };
24373
+ if (forBlock.i18n !== undefined && !(forBlock.i18n instanceof BlockPlaceholder)) {
24374
+ throw Error('AssertionError: Unhandled i18n metadata type or @for');
24375
+ }
24376
+ if (forBlock.empty?.i18n !== undefined &&
24377
+ !(forBlock.empty.i18n instanceof BlockPlaceholder)) {
24378
+ throw Error('AssertionError: Unhandled i18n metadata type or @empty');
24379
+ }
24380
+ const i18nPlaceholder = forBlock.i18n;
24381
+ const emptyI18nPlaceholder = forBlock.empty?.i18n;
24201
24382
  const tagName = ingestControlFlowInsertionPoint(unit, repeaterView.xref, forBlock);
24202
- const repeaterCreate = createRepeaterCreateOp(repeaterView.xref, emptyView?.xref ?? null, tagName, track, varNames, forBlock.sourceSpan);
24383
+ const repeaterCreate = createRepeaterCreateOp(repeaterView.xref, emptyView?.xref ?? null, tagName, track, varNames, i18nPlaceholder, emptyI18nPlaceholder, forBlock.sourceSpan);
24203
24384
  unit.create.push(repeaterCreate);
24204
24385
  const expression = convertAst(forBlock.expression, unit.job, convertSourceSpan(forBlock.expression.span, forBlock.sourceSpan));
24205
24386
  const repeater = createRepeaterOp(repeaterCreate.xref, repeaterCreate.handle, expression, forBlock.sourceSpan);
@@ -24317,6 +24498,27 @@ function convertAst(ast, job, baseSourceSpan) {
24317
24498
  throw new Error(`Unhandled expression type "${ast.constructor.name}" in file "${baseSourceSpan?.start.file.url}"`);
24318
24499
  }
24319
24500
  }
24501
+ function convertAstWithInterpolation(job, value, i18nMeta) {
24502
+ let expression;
24503
+ if (value instanceof Interpolation$1) {
24504
+ expression = new Interpolation(value.strings, value.expressions.map(e => convertAst(e, job, null)), Object.keys(asMessage(i18nMeta)?.placeholders ?? {}));
24505
+ }
24506
+ else if (value instanceof AST) {
24507
+ expression = convertAst(value, job, null);
24508
+ }
24509
+ else {
24510
+ expression = literal(value);
24511
+ }
24512
+ return expression;
24513
+ }
24514
+ // TODO: Can we populate Template binding kinds in ingest?
24515
+ const BINDING_KINDS = new Map([
24516
+ [0 /* e.BindingType.Property */, BindingKind.Property],
24517
+ [1 /* e.BindingType.Attribute */, BindingKind.Attribute],
24518
+ [2 /* e.BindingType.Class */, BindingKind.ClassName],
24519
+ [3 /* e.BindingType.Style */, BindingKind.StyleProperty],
24520
+ [4 /* e.BindingType.Animation */, BindingKind.Animation],
24521
+ ]);
24320
24522
  /**
24321
24523
  * Checks whether the given template is a plain ng-template (as opposed to another kind of template
24322
24524
  * such as a structural directive template or control flow template). This is checked based on the
@@ -24335,149 +24537,184 @@ function convertAst(ast, job, baseSourceSpan) {
24335
24537
  * | `<ng-template *ngIf>` (structural) | null |
24336
24538
  */
24337
24539
  function isPlainTemplate(tmpl) {
24338
- return splitNsName(tmpl.tagName ?? '')[1] === 'ng-template';
24540
+ return splitNsName(tmpl.tagName ?? '')[1] === NG_TEMPLATE_TAG_NAME$1;
24339
24541
  }
24340
24542
  /**
24341
- * Process all of the bindings on an element-like structure in the template AST and convert them
24342
- * to their IR representation.
24543
+ * Ensures that the i18nMeta, if provided, is an i18n.Message.
24343
24544
  */
24344
- function ingestBindings(unit, op, element) {
24345
- let flags = BindingFlags.None;
24346
- let hasI18nAttributes = false;
24347
- if (element instanceof Template) {
24348
- flags |= BindingFlags.OnNgTemplateElement;
24349
- if (element instanceof Template && isPlainTemplate(element)) {
24350
- flags |= BindingFlags.BindingTargetsTemplate;
24351
- }
24352
- const templateAttrFlags = flags | BindingFlags.BindingTargetsTemplate | BindingFlags.IsStructuralTemplateAttribute;
24353
- for (const attr of element.templateAttrs) {
24354
- if (attr instanceof TextAttribute) {
24355
- ingestBinding(unit, op.xref, attr.name, literal(attr.value), 1 /* e.BindingType.Attribute */, null, SecurityContext.NONE, attr.sourceSpan, templateAttrFlags | BindingFlags.TextValue, attr.i18n);
24356
- hasI18nAttributes ||= attr.i18n !== undefined;
24357
- }
24358
- else {
24359
- ingestBinding(unit, op.xref, attr.name, attr.value, attr.type, attr.unit, attr.securityContext, attr.sourceSpan, templateAttrFlags, attr.i18n);
24360
- hasI18nAttributes ||= attr.i18n !== undefined;
24361
- }
24362
- }
24545
+ function asMessage(i18nMeta) {
24546
+ if (i18nMeta == null) {
24547
+ return null;
24548
+ }
24549
+ if (!(i18nMeta instanceof Message)) {
24550
+ throw Error(`Expected i18n meta to be a Message, but got: ${i18nMeta.constructor.name}`);
24363
24551
  }
24552
+ return i18nMeta;
24553
+ }
24554
+ /**
24555
+ * Process all of the bindings on an element in the template AST and convert them to their IR
24556
+ * representation.
24557
+ */
24558
+ function ingestElementBindings(unit, op, element) {
24559
+ let bindings = new Array();
24364
24560
  for (const attr of element.attributes) {
24365
- // This is only attribute TextLiteral bindings, such as `attr.foo="bar"`. This can never be
24366
- // `[attr.foo]="bar"` or `attr.foo="{{bar}}"`, both of which will be handled as inputs with
24367
- // `BindingType.Attribute`.
24368
- ingestBinding(unit, op.xref, attr.name, literal(attr.value), 1 /* e.BindingType.Attribute */, null, SecurityContext.NONE, attr.sourceSpan, flags | BindingFlags.TextValue, attr.i18n);
24369
- hasI18nAttributes ||= attr.i18n !== undefined;
24561
+ // Attribute literal bindings, such as `attr.foo="bar"`.
24562
+ const securityContext = domSchema.securityContext(element.name, attr.name, true);
24563
+ bindings.push(createBindingOp(op.xref, BindingKind.Attribute, attr.name, convertAstWithInterpolation(unit.job, attr.value, attr.i18n), null, securityContext, true, false, null, asMessage(attr.i18n), attr.sourceSpan));
24370
24564
  }
24371
24565
  for (const input of element.inputs) {
24372
- ingestBinding(unit, op.xref, input.name, input.value, input.type, input.unit, input.securityContext, input.sourceSpan, flags, input.i18n);
24373
- hasI18nAttributes ||= input.i18n !== undefined;
24566
+ // All dynamic bindings (both attribute and property bindings).
24567
+ bindings.push(createBindingOp(op.xref, BINDING_KINDS.get(input.type), input.name, convertAstWithInterpolation(unit.job, astOf(input.value), input.i18n), input.unit, input.securityContext, false, false, null, asMessage(input.i18n) ?? null, input.sourceSpan));
24374
24568
  }
24569
+ unit.create.push(bindings.filter((b) => b?.kind === OpKind.ExtractedAttribute));
24570
+ unit.update.push(bindings.filter((b) => b?.kind === OpKind.Binding));
24375
24571
  for (const output of element.outputs) {
24376
- let listenerOp;
24377
- if (output.type === 1 /* e.ParsedEventType.Animation */) {
24378
- if (output.phase === null) {
24379
- throw Error('Animation listener should have a phase');
24380
- }
24572
+ if (output.type === 1 /* e.ParsedEventType.Animation */ && output.phase === null) {
24573
+ throw Error('Animation listener should have a phase');
24381
24574
  }
24382
- if (element instanceof Template && !isPlainTemplate(element)) {
24383
- unit.create.push(createExtractedAttributeOp(op.xref, BindingKind.Property, output.name, null, null));
24384
- continue;
24385
- }
24386
- listenerOp = createListenerOp(op.xref, op.handle, output.name, op.tag, output.phase, false, output.sourceSpan);
24387
- // if output.handler is a chain, then push each statement from the chain separately, and
24388
- // return the last one?
24389
- let handlerExprs;
24390
- let handler = output.handler;
24391
- if (handler instanceof ASTWithSource) {
24392
- handler = handler.ast;
24393
- }
24394
- if (handler instanceof Chain) {
24395
- handlerExprs = handler.expressions;
24575
+ 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));
24576
+ }
24577
+ // If any of the bindings on this element have an i18n message, then an i18n attrs configuration
24578
+ // op is also required.
24579
+ if (bindings.some(b => b?.i18nMessage) !== null) {
24580
+ unit.create.push(createI18nAttributesOp(unit.job.allocateXrefId(), new SlotHandle(), op.xref));
24581
+ }
24582
+ }
24583
+ /**
24584
+ * Process all of the bindings on a template in the template AST and convert them to their IR
24585
+ * representation.
24586
+ */
24587
+ function ingestTemplateBindings(unit, op, template, templateKind) {
24588
+ let bindings = new Array();
24589
+ for (const attr of template.templateAttrs) {
24590
+ if (attr instanceof TextAttribute) {
24591
+ const securityContext = domSchema.securityContext(NG_TEMPLATE_TAG_NAME$1, attr.name, true);
24592
+ bindings.push(createTemplateBinding(unit, op.xref, 1 /* e.BindingType.Attribute */, attr.name, attr.value, null, securityContext, true, templateKind, asMessage(attr.i18n), attr.sourceSpan));
24396
24593
  }
24397
24594
  else {
24398
- handlerExprs = [handler];
24595
+ bindings.push(createTemplateBinding(unit, op.xref, attr.type, attr.name, astOf(attr.value), attr.unit, attr.securityContext, true, templateKind, asMessage(attr.i18n), attr.sourceSpan));
24596
+ }
24597
+ }
24598
+ for (const attr of template.attributes) {
24599
+ // Attribute literal bindings, such as `attr.foo="bar"`.
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, false, templateKind, asMessage(attr.i18n), attr.sourceSpan));
24602
+ }
24603
+ for (const input of template.inputs) {
24604
+ // Dynamic bindings (both attribute and property bindings).
24605
+ bindings.push(createTemplateBinding(unit, op.xref, input.type, input.name, astOf(input.value), input.unit, input.securityContext, false, templateKind, asMessage(input.i18n), input.sourceSpan));
24606
+ }
24607
+ unit.create.push(bindings.filter((b) => b?.kind === OpKind.ExtractedAttribute));
24608
+ unit.update.push(bindings.filter((b) => b?.kind === OpKind.Binding));
24609
+ for (const output of template.outputs) {
24610
+ if (output.type === 1 /* e.ParsedEventType.Animation */ && output.phase === null) {
24611
+ throw Error('Animation listener should have a phase');
24399
24612
  }
24400
- if (handlerExprs.length === 0) {
24401
- throw new Error('Expected listener to have non-empty expression list.');
24613
+ if (templateKind === TemplateKind.NgTemplate) {
24614
+ unit.create.push(createListenerOp(op.xref, op.handle, output.name, op.tag, makeListenerHandlerOps(unit, output.handler, output.handlerSpan), output.phase, output.target, false, output.sourceSpan));
24402
24615
  }
24403
- const expressions = handlerExprs.map(expr => convertAst(expr, unit.job, output.handlerSpan));
24404
- const returnExpr = expressions.pop();
24405
- for (const expr of expressions) {
24406
- const stmtOp = createStatementOp(new ExpressionStatement(expr, expr.sourceSpan));
24407
- listenerOp.handlerOps.push(stmtOp);
24616
+ if (templateKind === TemplateKind.Structural &&
24617
+ output.type !== 1 /* e.ParsedEventType.Animation */) {
24618
+ // Animation bindings are excluded from the structural template's const array.
24619
+ const securityContext = domSchema.securityContext(NG_TEMPLATE_TAG_NAME$1, output.name, false);
24620
+ unit.create.push(createExtractedAttributeOp(op.xref, BindingKind.Property, output.name, null, null, null, securityContext));
24408
24621
  }
24409
- listenerOp.handlerOps.push(createStatementOp(new ReturnStatement(returnExpr, returnExpr.sourceSpan)));
24410
- unit.create.push(listenerOp);
24411
24622
  }
24412
24623
  // TODO: Perhaps we could do this in a phase? (It likely wouldn't change the slot indices.)
24413
- if (hasI18nAttributes) {
24624
+ if (bindings.some(b => b?.i18nMessage) !== null) {
24414
24625
  unit.create.push(createI18nAttributesOp(unit.job.allocateXrefId(), new SlotHandle(), op.xref));
24415
24626
  }
24416
24627
  }
24417
- const BINDING_KINDS = new Map([
24418
- [0 /* e.BindingType.Property */, BindingKind.Property],
24419
- [1 /* e.BindingType.Attribute */, BindingKind.Attribute],
24420
- [2 /* e.BindingType.Class */, BindingKind.ClassName],
24421
- [3 /* e.BindingType.Style */, BindingKind.StyleProperty],
24422
- [4 /* e.BindingType.Animation */, BindingKind.Animation],
24423
- ]);
24424
- var BindingFlags;
24425
- (function (BindingFlags) {
24426
- BindingFlags[BindingFlags["None"] = 0] = "None";
24427
- /**
24428
- * The binding is to a static text literal and not to an expression.
24429
- */
24430
- BindingFlags[BindingFlags["TextValue"] = 1] = "TextValue";
24431
- /**
24432
- * The binding belongs to the `<ng-template>` side of a `t.Template`.
24433
- */
24434
- BindingFlags[BindingFlags["BindingTargetsTemplate"] = 2] = "BindingTargetsTemplate";
24435
- /**
24436
- * The binding is on a structural directive.
24437
- */
24438
- BindingFlags[BindingFlags["IsStructuralTemplateAttribute"] = 4] = "IsStructuralTemplateAttribute";
24439
- /**
24440
- * The binding is on a `t.Template`.
24441
- */
24442
- BindingFlags[BindingFlags["OnNgTemplateElement"] = 8] = "OnNgTemplateElement";
24443
- })(BindingFlags || (BindingFlags = {}));
24444
- function ingestBinding(view, xref, name, value, type, unit, securityContext, sourceSpan, flags, i18nMeta) {
24445
- if (value instanceof ASTWithSource) {
24446
- value = value.ast;
24447
- }
24448
- let i18nContext = null;
24449
- if (i18nMeta !== undefined) {
24450
- if (!(i18nMeta instanceof Message)) {
24451
- throw Error(`Unhandled i18n metadata type for binding: ${i18nMeta.constructor.name}`);
24628
+ /**
24629
+ * Helper to ingest an individual binding on a template, either an explicit `ng-template`, or an
24630
+ * implicit template created via structural directive.
24631
+ *
24632
+ * Bindings on templates are *extremely* tricky. I have tried to isolate all of the confusing edge
24633
+ * cases into this function, and to comment it well to document the behavior.
24634
+ *
24635
+ * Some of this behavior is intuitively incorrect, and we should consider changing it in the future.
24636
+ *
24637
+ * @param view The compilation unit for the view containing the template.
24638
+ * @param xref The xref of the template op.
24639
+ * @param type The binding type, according to the parser. This is fairly reasonable, e.g. both
24640
+ * dynamic and static attributes have e.BindingType.Attribute.
24641
+ * @param name The binding's name.
24642
+ * @param value The bindings's value, which will either be an input AST expression, or a string
24643
+ * literal. Note that the input AST expression may or may not be const -- it will only be a
24644
+ * string literal if the parser considered it a text binding.
24645
+ * @param unit If the binding has a unit (e.g. `px` for style bindings), then this is the unit.
24646
+ * @param securityContext The security context of the binding.
24647
+ * @param isStructuralTemplateAttribute Whether this binding actually applies to the structural
24648
+ * ng-template. For example, an `ngFor` would actually apply to the structural template. (Most
24649
+ * bindings on structural elements target the inner element, not the template.)
24650
+ * @param templateKind Whether this is an explicit `ng-template` or an implicit template created by
24651
+ * a structural directive. This should never be a block template.
24652
+ * @param i18nMessage The i18n metadata for the binding, if any.
24653
+ * @param sourceSpan The source span of the binding.
24654
+ * @returns An IR binding op, or null if the binding should be skipped.
24655
+ */
24656
+ function createTemplateBinding(view, xref, type, name, value, unit, securityContext, isStructuralTemplateAttribute, templateKind, i18nMessage, sourceSpan) {
24657
+ const isTextBinding = typeof value === 'string';
24658
+ // If this is a structural template, then several kinds of bindings should not result in an
24659
+ // update instruction.
24660
+ if (templateKind === TemplateKind.Structural) {
24661
+ if (!isStructuralTemplateAttribute &&
24662
+ (type === 0 /* e.BindingType.Property */ || type === 2 /* e.BindingType.Class */ ||
24663
+ type === 3 /* e.BindingType.Style */)) {
24664
+ // Because this binding doesn't really target the ng-template, it must be a binding on an
24665
+ // inner node of a structural template. We can't skip it entirely, because we still need it on
24666
+ // the ng-template's consts (e.g. for the purposes of directive matching). However, we should
24667
+ // not generate an update instruction for it.
24668
+ return createExtractedAttributeOp(xref, BindingKind.Property, name, null, null, i18nMessage, securityContext);
24669
+ }
24670
+ if (!isTextBinding && (type === 1 /* e.BindingType.Attribute */ || type === 4 /* e.BindingType.Animation */)) {
24671
+ // Again, this binding doesn't really target the ng-template; it actually targets the element
24672
+ // inside the structural template. In the case of non-text attribute or animation bindings,
24673
+ // the binding doesn't even show up on the ng-template const array, so we just skip it
24674
+ // entirely.
24675
+ return null;
24452
24676
  }
24453
- i18nContext = view.job.allocateXrefId();
24454
- view.create.push(createI18nContextOp(I18nContextKind.Attr, i18nContext, null, i18nMeta, null));
24455
- }
24456
- if (flags & BindingFlags.OnNgTemplateElement && !(flags & BindingFlags.BindingTargetsTemplate) &&
24457
- type === 0 /* e.BindingType.Property */) {
24458
- // This binding only exists for later const extraction, and is not an actual binding to be
24459
- // created.
24460
- view.create.push(createExtractedAttributeOp(xref, BindingKind.Property, name, null, i18nContext));
24461
- return;
24462
24677
  }
24463
- let expression;
24464
- // TODO: We could easily generate source maps for subexpressions in these cases, but
24465
- // TemplateDefinitionBuilder does not. Should we do so?
24466
- if (value instanceof Interpolation$1) {
24467
- let i18nPlaceholders = [];
24468
- if (i18nMeta !== undefined) {
24469
- i18nPlaceholders = Object.keys(i18nMeta.placeholders);
24678
+ let bindingType = BINDING_KINDS.get(type);
24679
+ if (templateKind === TemplateKind.NgTemplate) {
24680
+ // We know we are dealing with bindings directly on an explicit ng-template.
24681
+ // Static attribute bindings should be collected into the const array as k/v pairs. Property
24682
+ // bindings should result in a `property` instruction, and `AttributeMarker.Bindings` const
24683
+ // entries.
24684
+ //
24685
+ // The difficulty is with dynamic attribute, style, and class bindings. These don't really make
24686
+ // sense on an `ng-template` and should probably be parser errors. However,
24687
+ // TemplateDefinitionBuilder generates `property` instructions for them, and so we do that as
24688
+ // well.
24689
+ //
24690
+ // Note that we do have a slight behavior difference with TemplateDefinitionBuilder: although
24691
+ // TDB emits `property` instructions for dynamic attributes, styles, and classes, only styles
24692
+ // and classes also get const collected into the `AttributeMarker.Bindings` field. Dynamic
24693
+ // attribute bindings are missing from the consts entirely. We choose to emit them into the
24694
+ // consts field anyway, to avoid creating special cases for something so arcane and nonsensical.
24695
+ if (type === 2 /* e.BindingType.Class */ || type === 3 /* e.BindingType.Style */ ||
24696
+ (type === 1 /* e.BindingType.Attribute */ && !isTextBinding)) {
24697
+ // TODO: These cases should be parse errors.
24698
+ bindingType = BindingKind.Property;
24470
24699
  }
24471
- expression = new Interpolation(value.strings, value.expressions.map(expr => convertAst(expr, view.job, null)), i18nPlaceholders);
24472
- }
24473
- else if (value instanceof AST) {
24474
- expression = convertAst(value, view.job, null);
24475
24700
  }
24476
- else {
24477
- expression = value;
24701
+ return createBindingOp(xref, bindingType, name, convertAstWithInterpolation(view.job, value, i18nMessage), unit, securityContext, isTextBinding, isStructuralTemplateAttribute, templateKind, i18nMessage, sourceSpan);
24702
+ }
24703
+ function makeListenerHandlerOps(unit, handler, handlerSpan) {
24704
+ handler = astOf(handler);
24705
+ const handlerOps = new Array();
24706
+ let handlerExprs = handler instanceof Chain ? handler.expressions : [handler];
24707
+ if (handlerExprs.length === 0) {
24708
+ throw new Error('Expected listener to have non-empty expression list.');
24478
24709
  }
24479
- const kind = BINDING_KINDS.get(type);
24480
- view.update.push(createBindingOp(xref, kind, name, expression, unit, securityContext, !!(flags & BindingFlags.TextValue), !!(flags & BindingFlags.IsStructuralTemplateAttribute), i18nContext, sourceSpan));
24710
+ const expressions = handlerExprs.map(expr => convertAst(expr, unit.job, handlerSpan));
24711
+ const returnExpr = expressions.pop();
24712
+ handlerOps.push(...expressions.map(e => createStatementOp(new ExpressionStatement(e, e.sourceSpan))));
24713
+ handlerOps.push(createStatementOp(new ReturnStatement(returnExpr, returnExpr.sourceSpan)));
24714
+ return handlerOps;
24715
+ }
24716
+ function astOf(ast) {
24717
+ return ast instanceof ASTWithSource ? ast.ast : ast;
24481
24718
  }
24482
24719
  /**
24483
24720
  * Process all of the local references on an element-like structure in the template AST and
@@ -24565,11 +24802,12 @@ function ingestControlFlowInsertionPoint(unit, xref, node) {
24565
24802
  // and they can be used in directive matching (in the case of `Template.templateAttrs`).
24566
24803
  if (root !== null) {
24567
24804
  for (const attr of root.attributes) {
24568
- ingestBinding(unit, xref, attr.name, literal(attr.value), 1 /* e.BindingType.Attribute */, null, SecurityContext.NONE, attr.sourceSpan, BindingFlags.TextValue, attr.i18n);
24805
+ const securityContext = domSchema.securityContext(NG_TEMPLATE_TAG_NAME$1, attr.name, true);
24806
+ unit.update.push(createBindingOp(xref, BindingKind.Attribute, attr.name, literal(attr.value), null, securityContext, true, false, null, asMessage(attr.i18n), attr.sourceSpan));
24569
24807
  }
24570
24808
  const tagName = root instanceof Element$1 ? root.name : root.tagName;
24571
24809
  // Don't pass along `ng-template` tag name since it enables directive matching.
24572
- return tagName === 'ng-template' ? null : tagName;
24810
+ return tagName === NG_TEMPLATE_TAG_NAME$1 ? null : tagName;
24573
24811
  }
24574
24812
  return null;
24575
24813
  }
@@ -29996,6 +30234,7 @@ function createHostBindingsFunction(hostBindingsMetadata, typeSourceSpan, bindin
29996
30234
  }
29997
30235
  const hostJob = ingestHostBinding({
29998
30236
  componentName: name,
30237
+ componentSelector: selector,
29999
30238
  properties: bindings,
30000
30239
  events: eventBindings,
30001
30240
  attributes: hostBindingsMetadata.attributes,
@@ -31755,7 +31994,7 @@ function publishFacade(global) {
31755
31994
  * @description
31756
31995
  * Entry point for all public APIs of the compiler package.
31757
31996
  */
31758
- const VERSION = new Version('17.0.6');
31997
+ const VERSION = new Version('17.0.7');
31759
31998
 
31760
31999
  class CompilerConfig {
31761
32000
  constructor({ defaultEncapsulation = ViewEncapsulation.Emulated, preserveWhitespaces, strictInjectionParameters } = {}) {
@@ -33321,7 +33560,7 @@ const MINIMUM_PARTIAL_LINKER_VERSION$6 = '12.0.0';
33321
33560
  function compileDeclareClassMetadata(metadata) {
33322
33561
  const definitionMap = new DefinitionMap();
33323
33562
  definitionMap.set('minVersion', literal(MINIMUM_PARTIAL_LINKER_VERSION$6));
33324
- definitionMap.set('version', literal('17.0.6'));
33563
+ definitionMap.set('version', literal('17.0.7'));
33325
33564
  definitionMap.set('ngImport', importExpr(Identifiers.core));
33326
33565
  definitionMap.set('type', metadata.type);
33327
33566
  definitionMap.set('decorators', metadata.decorators);
@@ -33429,7 +33668,7 @@ function createDirectiveDefinitionMap(meta) {
33429
33668
  // in 16.1 is actually used.
33430
33669
  const minVersion = hasTransformFunctions ? MINIMUM_PARTIAL_LINKER_VERSION$5 : '14.0.0';
33431
33670
  definitionMap.set('minVersion', literal(minVersion));
33432
- definitionMap.set('version', literal('17.0.6'));
33671
+ definitionMap.set('version', literal('17.0.7'));
33433
33672
  // e.g. `type: MyDirective`
33434
33673
  definitionMap.set('type', meta.type.value);
33435
33674
  if (meta.isStandalone) {
@@ -33706,7 +33945,7 @@ const MINIMUM_PARTIAL_LINKER_VERSION$4 = '12.0.0';
33706
33945
  function compileDeclareFactoryFunction(meta) {
33707
33946
  const definitionMap = new DefinitionMap();
33708
33947
  definitionMap.set('minVersion', literal(MINIMUM_PARTIAL_LINKER_VERSION$4));
33709
- definitionMap.set('version', literal('17.0.6'));
33948
+ definitionMap.set('version', literal('17.0.7'));
33710
33949
  definitionMap.set('ngImport', importExpr(Identifiers.core));
33711
33950
  definitionMap.set('type', meta.type.value);
33712
33951
  definitionMap.set('deps', compileDependencies(meta.deps));
@@ -33741,7 +33980,7 @@ function compileDeclareInjectableFromMetadata(meta) {
33741
33980
  function createInjectableDefinitionMap(meta) {
33742
33981
  const definitionMap = new DefinitionMap();
33743
33982
  definitionMap.set('minVersion', literal(MINIMUM_PARTIAL_LINKER_VERSION$3));
33744
- definitionMap.set('version', literal('17.0.6'));
33983
+ definitionMap.set('version', literal('17.0.7'));
33745
33984
  definitionMap.set('ngImport', importExpr(Identifiers.core));
33746
33985
  definitionMap.set('type', meta.type.value);
33747
33986
  // Only generate providedIn property if it has a non-null value
@@ -33792,7 +34031,7 @@ function compileDeclareInjectorFromMetadata(meta) {
33792
34031
  function createInjectorDefinitionMap(meta) {
33793
34032
  const definitionMap = new DefinitionMap();
33794
34033
  definitionMap.set('minVersion', literal(MINIMUM_PARTIAL_LINKER_VERSION$2));
33795
- definitionMap.set('version', literal('17.0.6'));
34034
+ definitionMap.set('version', literal('17.0.7'));
33796
34035
  definitionMap.set('ngImport', importExpr(Identifiers.core));
33797
34036
  definitionMap.set('type', meta.type.value);
33798
34037
  definitionMap.set('providers', meta.providers);
@@ -33825,7 +34064,7 @@ function createNgModuleDefinitionMap(meta) {
33825
34064
  throw new Error('Invalid path! Local compilation mode should not get into the partial compilation path');
33826
34065
  }
33827
34066
  definitionMap.set('minVersion', literal(MINIMUM_PARTIAL_LINKER_VERSION$1));
33828
- definitionMap.set('version', literal('17.0.6'));
34067
+ definitionMap.set('version', literal('17.0.7'));
33829
34068
  definitionMap.set('ngImport', importExpr(Identifiers.core));
33830
34069
  definitionMap.set('type', meta.type.value);
33831
34070
  // We only generate the keys in the metadata if the arrays contain values.
@@ -33876,7 +34115,7 @@ function compileDeclarePipeFromMetadata(meta) {
33876
34115
  function createPipeDefinitionMap(meta) {
33877
34116
  const definitionMap = new DefinitionMap();
33878
34117
  definitionMap.set('minVersion', literal(MINIMUM_PARTIAL_LINKER_VERSION));
33879
- definitionMap.set('version', literal('17.0.6'));
34118
+ definitionMap.set('version', literal('17.0.7'));
33880
34119
  definitionMap.set('ngImport', importExpr(Identifiers.core));
33881
34120
  // e.g. `type: MyPipe`
33882
34121
  definitionMap.set('type', meta.type.value);