@angular/compiler 17.2.0-next.0 → 17.2.0-rc.0

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 (52) hide show
  1. package/esm2022/src/compiler_util/expression_converter.mjs +98 -25
  2. package/esm2022/src/constant_pool.mjs +14 -4
  3. package/esm2022/src/expression_parser/ast.mjs +2 -3
  4. package/esm2022/src/expression_parser/parser.mjs +8 -27
  5. package/esm2022/src/jit_compiler_facade.mjs +9 -2
  6. package/esm2022/src/render3/partial/class_metadata.mjs +1 -1
  7. package/esm2022/src/render3/partial/directive.mjs +1 -1
  8. package/esm2022/src/render3/partial/factory.mjs +1 -1
  9. package/esm2022/src/render3/partial/injectable.mjs +1 -1
  10. package/esm2022/src/render3/partial/injector.mjs +1 -1
  11. package/esm2022/src/render3/partial/ng_module.mjs +1 -1
  12. package/esm2022/src/render3/partial/pipe.mjs +1 -1
  13. package/esm2022/src/render3/r3_class_metadata_compiler.mjs +5 -3
  14. package/esm2022/src/render3/r3_control_flow.mjs +39 -7
  15. package/esm2022/src/render3/r3_identifiers.mjs +6 -1
  16. package/esm2022/src/render3/r3_template_transform.mjs +6 -6
  17. package/esm2022/src/render3/view/api.mjs +1 -1
  18. package/esm2022/src/render3/view/compiler.mjs +12 -12
  19. package/esm2022/src/render3/view/template.mjs +21 -10
  20. package/esm2022/src/render3/view/util.mjs +4 -2
  21. package/esm2022/src/template/pipeline/ir/src/enums.mjs +27 -11
  22. package/esm2022/src/template/pipeline/ir/src/expression.mjs +32 -1
  23. package/esm2022/src/template/pipeline/ir/src/ops/create.mjs +21 -3
  24. package/esm2022/src/template/pipeline/ir/src/ops/update.mjs +22 -1
  25. package/esm2022/src/template/pipeline/src/compilation.mjs +4 -3
  26. package/esm2022/src/template/pipeline/src/emit.mjs +4 -2
  27. package/esm2022/src/template/pipeline/src/ingest.mjs +58 -15
  28. package/esm2022/src/template/pipeline/src/instruction.mjs +14 -1
  29. package/esm2022/src/template/pipeline/src/phases/attribute_extraction.mjs +15 -1
  30. package/esm2022/src/template/pipeline/src/phases/binding_specialization.mjs +11 -1
  31. package/esm2022/src/template/pipeline/src/phases/chaining.mjs +3 -1
  32. package/esm2022/src/template/pipeline/src/phases/const_collection.mjs +12 -5
  33. package/esm2022/src/template/pipeline/src/phases/create_defer_deps_fns.mjs +9 -3
  34. package/esm2022/src/template/pipeline/src/phases/generate_variables.mjs +2 -1
  35. package/esm2022/src/template/pipeline/src/phases/naming.mjs +14 -2
  36. package/esm2022/src/template/pipeline/src/phases/next_context_merging.mjs +2 -2
  37. package/esm2022/src/template/pipeline/src/phases/ordering.mjs +14 -5
  38. package/esm2022/src/template/pipeline/src/phases/reify.mjs +9 -1
  39. package/esm2022/src/template/pipeline/src/phases/resolve_contexts.mjs +2 -1
  40. package/esm2022/src/template/pipeline/src/phases/resolve_dollar_event.mjs +6 -3
  41. package/esm2022/src/template/pipeline/src/phases/resolve_names.mjs +3 -2
  42. package/esm2022/src/template/pipeline/src/phases/save_restore_view.mjs +2 -2
  43. package/esm2022/src/template/pipeline/src/phases/temporary_variables.mjs +2 -2
  44. package/esm2022/src/template/pipeline/src/phases/transform_two_way_binding_set.mjs +79 -0
  45. package/esm2022/src/template/pipeline/src/phases/var_counting.mjs +4 -1
  46. package/esm2022/src/template/pipeline/src/phases/variable_optimization.mjs +3 -3
  47. package/esm2022/src/template_parser/binding_parser.mjs +45 -17
  48. package/esm2022/src/version.mjs +1 -1
  49. package/fesm2022/compiler.mjs +599 -161
  50. package/fesm2022/compiler.mjs.map +1 -1
  51. package/index.d.ts +49 -223
  52. package/package.json +2 -2
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @license Angular v17.2.0-next.0
2
+ * @license Angular v17.2.0-rc.0
3
3
  * (c) 2010-2022 Google LLC. https://angular.io/
4
4
  * License: MIT
5
5
  */
@@ -2236,6 +2236,13 @@ class ConstantPool {
2236
2236
  this.literals = new Map();
2237
2237
  this.literalFactories = new Map();
2238
2238
  this.sharedConstants = new Map();
2239
+ /**
2240
+ * Constant pool also tracks claimed names from {@link uniqueName}.
2241
+ * This is useful to avoid collisions if variables are intended to be
2242
+ * named a certain way- but may conflict. We wouldn't want to always suffix
2243
+ * them with unique numbers.
2244
+ */
2245
+ this._claimedNames = new Map();
2239
2246
  this.nextNameIndex = 0;
2240
2247
  }
2241
2248
  getConstLiteral(literal, forceShared) {
@@ -2358,14 +2365,17 @@ class ConstantPool {
2358
2365
  return { literalFactory, literalFactoryArguments };
2359
2366
  }
2360
2367
  /**
2361
- * Produce a unique name.
2368
+ * Produce a unique name in the context of this pool.
2362
2369
  *
2363
2370
  * The name might be unique among different prefixes if any of the prefixes end in
2364
2371
  * a digit so the prefix should be a constant string (not based on user input) and
2365
2372
  * must not end in a digit.
2366
2373
  */
2367
- uniqueName(prefix) {
2368
- return `${prefix}${this.nextNameIndex++}`;
2374
+ uniqueName(name, alwaysIncludeSuffix = true) {
2375
+ const count = this._claimedNames.get(name) ?? 0;
2376
+ const result = count === 0 && !alwaysIncludeSuffix ? `${name}` : `${name}${count}`;
2377
+ this._claimedNames.set(name, count + 1);
2378
+ return result;
2369
2379
  }
2370
2380
  freshName() {
2371
2381
  return this.uniqueName(CONSTANT_PREFIX);
@@ -2632,6 +2642,10 @@ class Identifiers {
2632
2642
  static { this.viewQuerySignal = { name: 'ɵɵviewQuerySignal', moduleName: CORE }; }
2633
2643
  static { this.contentQuerySignal = { name: 'ɵɵcontentQuerySignal', moduleName: CORE }; }
2634
2644
  static { this.queryAdvance = { name: 'ɵɵqueryAdvance', moduleName: CORE }; }
2645
+ // Two-way bindings
2646
+ static { this.twoWayProperty = { name: 'ɵɵtwoWayProperty', moduleName: CORE }; }
2647
+ static { this.twoWayBindingSet = { name: 'ɵɵtwoWayBindingSet', moduleName: CORE }; }
2648
+ static { this.twoWayListener = { name: 'ɵɵtwoWayListener', moduleName: CORE }; }
2635
2649
  static { this.NgOnChangesFeature = { name: 'ɵɵNgOnChangesFeature', moduleName: CORE }; }
2636
2650
  static { this.InheritDefinitionFeature = { name: 'ɵɵInheritDefinitionFeature', moduleName: CORE }; }
2637
2651
  static { this.CopyDefinitionFeature = { name: 'ɵɵCopyDefinitionFeature', moduleName: CORE }; }
@@ -2661,6 +2675,7 @@ class Identifiers {
2661
2675
  // type-checking
2662
2676
  static { this.InputSignalBrandWriteType = { name: 'ɵINPUT_SIGNAL_BRAND_WRITE_TYPE', moduleName: CORE }; }
2663
2677
  static { this.UnwrapDirectiveSignalInputs = { name: 'ɵUnwrapDirectiveSignalInputs', moduleName: CORE }; }
2678
+ static { this.unwrapWritableSignal = { name: 'ɵunwrapWritableSignal', moduleName: CORE }; }
2664
2679
  }
2665
2680
 
2666
2681
  const DASH_CASE_REGEXP = /-+([a-z0-9])/g;
@@ -4961,6 +4976,8 @@ const CHAINABLE_INSTRUCTIONS = new Set([
4961
4976
  Identifiers.textInterpolate8,
4962
4977
  Identifiers.textInterpolateV,
4963
4978
  Identifiers.templateCreate,
4979
+ Identifiers.twoWayProperty,
4980
+ Identifiers.twoWayListener,
4964
4981
  ]);
4965
4982
  /** Generates a call to a single instruction. */
4966
4983
  function invokeInstruction(span, reference, params) {
@@ -5138,7 +5155,7 @@ function getAttrsForDirectiveMatching(elOrTpl) {
5138
5155
  }
5139
5156
  });
5140
5157
  elOrTpl.inputs.forEach(i => {
5141
- if (i.type === 0 /* BindingType.Property */) {
5158
+ if (i.type === 0 /* BindingType.Property */ || i.type === 5 /* BindingType.TwoWay */) {
5142
5159
  attributesMap[i.name] = '';
5143
5160
  }
5144
5161
  });
@@ -6930,10 +6947,9 @@ var ParsedPropertyType;
6930
6947
  ParsedPropertyType[ParsedPropertyType["DEFAULT"] = 0] = "DEFAULT";
6931
6948
  ParsedPropertyType[ParsedPropertyType["LITERAL_ATTR"] = 1] = "LITERAL_ATTR";
6932
6949
  ParsedPropertyType[ParsedPropertyType["ANIMATION"] = 2] = "ANIMATION";
6950
+ ParsedPropertyType[ParsedPropertyType["TWO_WAY"] = 3] = "TWO_WAY";
6933
6951
  })(ParsedPropertyType || (ParsedPropertyType = {}));
6934
6952
  class ParsedEvent {
6935
- // Regular events have a target
6936
- // Animation events have a phase
6937
6953
  constructor(name, targetOrPhase, type, handler, sourceSpan, handlerSpan, keySpan) {
6938
6954
  this.name = name;
6939
6955
  this.targetOrPhase = targetOrPhase;
@@ -6977,32 +6993,10 @@ class EventHandlerVars {
6977
6993
  * used in an action binding (e.g. an event handler).
6978
6994
  */
6979
6995
  function convertActionBinding(localResolver, implicitReceiver, action, bindingId, baseSourceSpan, implicitReceiverAccesses, globals) {
6980
- if (!localResolver) {
6981
- localResolver = new DefaultLocalResolver(globals);
6982
- }
6983
- const actionWithoutBuiltins = convertPropertyBindingBuiltins({
6984
- createLiteralArrayConverter: (argCount) => {
6985
- // Note: no caching for literal arrays in actions.
6986
- return (args) => literalArr(args);
6987
- },
6988
- createLiteralMapConverter: (keys) => {
6989
- // Note: no caching for literal maps in actions.
6990
- return (values) => {
6991
- const entries = keys.map((k, i) => ({
6992
- key: k.key,
6993
- value: values[i],
6994
- quoted: k.quoted,
6995
- }));
6996
- return literalMap(entries);
6997
- };
6998
- },
6999
- createPipeConverter: (name) => {
7000
- throw new Error(`Illegal State: Actions are not allowed to contain pipes. Pipe: ${name}`);
7001
- }
7002
- }, action);
6996
+ localResolver ??= new DefaultLocalResolver(globals);
7003
6997
  const visitor = new _AstToIrVisitor(localResolver, implicitReceiver, bindingId, /* supportsInterpolation */ false, baseSourceSpan, implicitReceiverAccesses);
7004
6998
  const actionStmts = [];
7005
- flattenStatements(actionWithoutBuiltins.visit(visitor, _Mode.Statement), actionStmts);
6999
+ flattenStatements(convertActionBuiltins(action).visit(visitor, _Mode.Statement), actionStmts);
7006
7000
  prependTemporaryDecls(visitor.temporaryCount, bindingId, actionStmts);
7007
7001
  if (visitor.usesImplicitReceiver) {
7008
7002
  localResolver.notifyImplicitReceiverUse();
@@ -7017,6 +7011,77 @@ function convertActionBinding(localResolver, implicitReceiver, action, bindingId
7017
7011
  }
7018
7012
  return actionStmts;
7019
7013
  }
7014
+ function convertAssignmentActionBinding(localResolver, implicitReceiver, action, bindingId, baseSourceSpan, implicitReceiverAccesses, globals) {
7015
+ localResolver ??= new DefaultLocalResolver(globals);
7016
+ const visitor = new _AstToIrVisitor(localResolver, implicitReceiver, bindingId, /* supportsInterpolation */ false, baseSourceSpan, implicitReceiverAccesses);
7017
+ let convertedAction = convertActionBuiltins(action).visit(visitor, _Mode.Statement);
7018
+ // This should already have been asserted in the parser, but we verify it here just in case.
7019
+ if (!(convertedAction instanceof ExpressionStatement)) {
7020
+ throw new Error(`Illegal state: unsupported expression in two-way action binding.`);
7021
+ }
7022
+ // Converts `[(ngModel)]="name"` to `twoWayBindingSet(ctx.name, $event) || (ctx.name = $event)`.
7023
+ convertedAction = wrapAssignmentAction(convertedAction.expr).toStmt();
7024
+ const actionStmts = [];
7025
+ flattenStatements(convertedAction, actionStmts);
7026
+ prependTemporaryDecls(visitor.temporaryCount, bindingId, actionStmts);
7027
+ // Assignment events always return `$event`.
7028
+ actionStmts.push(new ReturnStatement(EventHandlerVars.event));
7029
+ implicitReceiverAccesses?.add(EventHandlerVars.event.name);
7030
+ if (visitor.usesImplicitReceiver) {
7031
+ localResolver.notifyImplicitReceiverUse();
7032
+ }
7033
+ return actionStmts;
7034
+ }
7035
+ function wrapAssignmentReadExpression(ast) {
7036
+ return new ExternalExpr(Identifiers.twoWayBindingSet)
7037
+ .callFn([ast, EventHandlerVars.event])
7038
+ .or(ast.set(EventHandlerVars.event));
7039
+ }
7040
+ function isReadExpression$1(value) {
7041
+ return value instanceof ReadPropExpr || value instanceof ReadKeyExpr;
7042
+ }
7043
+ function wrapAssignmentAction(ast) {
7044
+ // The only officially supported expressions inside of a two-way binding are read expressions.
7045
+ if (isReadExpression$1(ast)) {
7046
+ return wrapAssignmentReadExpression(ast);
7047
+ }
7048
+ // However, historically the expression parser was handling two-way events by appending `=$event`
7049
+ // to the raw string before attempting to parse it. This has led to bugs over the years (see
7050
+ // #37809) and to unintentionally supporting unassignable events in the two-way binding. The
7051
+ // logic below aims to emulate the old behavior while still supporting the new output format
7052
+ // which uses `twoWayBindingSet`. Note that the generated code doesn't necessarily make sense
7053
+ // based on what the user wrote, for example the event binding for `[(value)]="a ? b : c"`
7054
+ // would produce `ctx.a ? ctx.b : ctx.c = $event`. We aim to reproduce what the parser used
7055
+ // to generate before #54154.
7056
+ if (ast instanceof BinaryOperatorExpr && isReadExpression$1(ast.rhs)) {
7057
+ // `a && b` -> `ctx.a && twoWayBindingSet(ctx.b, $event) || (ctx.b = $event)`
7058
+ return new BinaryOperatorExpr(ast.operator, ast.lhs, wrapAssignmentReadExpression(ast.rhs));
7059
+ }
7060
+ // Note: this also supports nullish coalescing expressions which
7061
+ // would've been downleveled to ternary expressions by this point.
7062
+ if (ast instanceof ConditionalExpr && isReadExpression$1(ast.falseCase)) {
7063
+ // `a ? b : c` -> `ctx.a ? ctx.b : twoWayBindingSet(ctx.c, $event) || (ctx.c = $event)`
7064
+ return new ConditionalExpr(ast.condition, ast.trueCase, wrapAssignmentReadExpression(ast.falseCase));
7065
+ }
7066
+ // `!!a` -> `twoWayBindingSet(ctx.a, $event) || (ctx.a = $event)`
7067
+ // Note: previously we'd actually produce `!!(ctx.a = $event)`, but the wrapping
7068
+ // node doesn't affect the result so we don't need to carry it over.
7069
+ if (ast instanceof NotExpr) {
7070
+ let expr = ast.condition;
7071
+ while (true) {
7072
+ if (expr instanceof NotExpr) {
7073
+ expr = expr.condition;
7074
+ }
7075
+ else {
7076
+ if (isReadExpression$1(expr)) {
7077
+ return wrapAssignmentReadExpression(expr);
7078
+ }
7079
+ break;
7080
+ }
7081
+ }
7082
+ }
7083
+ throw new Error(`Illegal state: unsupported expression in two-way action binding.`);
7084
+ }
7020
7085
  function convertPropertyBindingBuiltins(converterFactory, ast) {
7021
7086
  return convertBuiltins(converterFactory, ast);
7022
7087
  }
@@ -7100,6 +7165,29 @@ function convertBuiltins(converterFactory, ast) {
7100
7165
  const visitor = new _BuiltinAstConverter(converterFactory);
7101
7166
  return ast.visit(visitor);
7102
7167
  }
7168
+ function convertActionBuiltins(action) {
7169
+ const converterFactory = {
7170
+ createLiteralArrayConverter: () => {
7171
+ // Note: no caching for literal arrays in actions.
7172
+ return (args) => literalArr(args);
7173
+ },
7174
+ createLiteralMapConverter: (keys) => {
7175
+ // Note: no caching for literal maps in actions.
7176
+ return (values) => {
7177
+ const entries = keys.map((k, i) => ({
7178
+ key: k.key,
7179
+ value: values[i],
7180
+ quoted: k.quoted,
7181
+ }));
7182
+ return literalMap(entries);
7183
+ };
7184
+ },
7185
+ createPipeConverter: (name) => {
7186
+ throw new Error(`Illegal State: Actions are not allowed to contain pipes. Pipe: ${name}`);
7187
+ }
7188
+ };
7189
+ return convertPropertyBindingBuiltins(converterFactory, action);
7190
+ }
7103
7191
  function temporaryName(bindingId, temporaryNumber) {
7104
7192
  return `tmp_${bindingId}_${temporaryNumber}`;
7105
7193
  }
@@ -8983,46 +9071,54 @@ var OpKind;
8983
9071
  * An update up for a repeater.
8984
9072
  */
8985
9073
  OpKind[OpKind["Repeater"] = 35] = "Repeater";
9074
+ /**
9075
+ * An operation to bind an expression to the property side of a two-way binding.
9076
+ */
9077
+ OpKind[OpKind["TwoWayProperty"] = 36] = "TwoWayProperty";
9078
+ /**
9079
+ * An operation declaring the event side of a two-way binding.
9080
+ */
9081
+ OpKind[OpKind["TwoWayListener"] = 37] = "TwoWayListener";
8986
9082
  /**
8987
9083
  * The start of an i18n block.
8988
9084
  */
8989
- OpKind[OpKind["I18nStart"] = 36] = "I18nStart";
9085
+ OpKind[OpKind["I18nStart"] = 38] = "I18nStart";
8990
9086
  /**
8991
9087
  * A self-closing i18n on a single element.
8992
9088
  */
8993
- OpKind[OpKind["I18n"] = 37] = "I18n";
9089
+ OpKind[OpKind["I18n"] = 39] = "I18n";
8994
9090
  /**
8995
9091
  * The end of an i18n block.
8996
9092
  */
8997
- OpKind[OpKind["I18nEnd"] = 38] = "I18nEnd";
9093
+ OpKind[OpKind["I18nEnd"] = 40] = "I18nEnd";
8998
9094
  /**
8999
9095
  * An expression in an i18n message.
9000
9096
  */
9001
- OpKind[OpKind["I18nExpression"] = 39] = "I18nExpression";
9097
+ OpKind[OpKind["I18nExpression"] = 41] = "I18nExpression";
9002
9098
  /**
9003
9099
  * An instruction that applies a set of i18n expressions.
9004
9100
  */
9005
- OpKind[OpKind["I18nApply"] = 40] = "I18nApply";
9101
+ OpKind[OpKind["I18nApply"] = 42] = "I18nApply";
9006
9102
  /**
9007
9103
  * An instruction to create an ICU expression.
9008
9104
  */
9009
- OpKind[OpKind["IcuStart"] = 41] = "IcuStart";
9105
+ OpKind[OpKind["IcuStart"] = 43] = "IcuStart";
9010
9106
  /**
9011
9107
  * An instruction to update an ICU expression.
9012
9108
  */
9013
- OpKind[OpKind["IcuEnd"] = 42] = "IcuEnd";
9109
+ OpKind[OpKind["IcuEnd"] = 44] = "IcuEnd";
9014
9110
  /**
9015
9111
  * An instruction representing a placeholder in an ICU expression.
9016
9112
  */
9017
- OpKind[OpKind["IcuPlaceholder"] = 43] = "IcuPlaceholder";
9113
+ OpKind[OpKind["IcuPlaceholder"] = 45] = "IcuPlaceholder";
9018
9114
  /**
9019
9115
  * An i18n context containing information needed to generate an i18n message.
9020
9116
  */
9021
- OpKind[OpKind["I18nContext"] = 44] = "I18nContext";
9117
+ OpKind[OpKind["I18nContext"] = 46] = "I18nContext";
9022
9118
  /**
9023
9119
  * A creation op that corresponds to i18n attributes on an element.
9024
9120
  */
9025
- OpKind[OpKind["I18nAttributes"] = 45] = "I18nAttributes";
9121
+ OpKind[OpKind["I18nAttributes"] = 47] = "I18nAttributes";
9026
9122
  })(OpKind || (OpKind = {}));
9027
9123
  /**
9028
9124
  * Distinguishes different kinds of IR expressions.
@@ -9134,6 +9230,10 @@ var ExpressionKind;
9134
9230
  * An expression that will be automatically extracted to the component const array.
9135
9231
  */
9136
9232
  ExpressionKind[ExpressionKind["ConstCollected"] = 25] = "ConstCollected";
9233
+ /**
9234
+ * Operation that sets the value of a two-way binding.
9235
+ */
9236
+ ExpressionKind[ExpressionKind["TwoWayBindingSet"] = 26] = "TwoWayBindingSet";
9137
9237
  })(ExpressionKind || (ExpressionKind = {}));
9138
9238
  var VariableFlags;
9139
9239
  (function (VariableFlags) {
@@ -9219,6 +9319,10 @@ var BindingKind;
9219
9319
  * Animation property bindings.
9220
9320
  */
9221
9321
  BindingKind[BindingKind["Animation"] = 6] = "Animation";
9322
+ /**
9323
+ * Property side of a two-way binding.
9324
+ */
9325
+ BindingKind[BindingKind["TwoWayProperty"] = 7] = "TwoWayProperty";
9222
9326
  })(BindingKind || (BindingKind = {}));
9223
9327
  /**
9224
9328
  * Enumeration of possible times i18n params can be resolved.
@@ -9485,6 +9589,27 @@ function createPropertyOp(target, name, expression, isAnimationTrigger, security
9485
9589
  ...NEW_OP,
9486
9590
  };
9487
9591
  }
9592
+ /**
9593
+ * Create a `TwoWayPropertyOp`.
9594
+ */
9595
+ function createTwoWayPropertyOp(target, name, expression, securityContext, isStructuralTemplateAttribute, templateKind, i18nContext, i18nMessage, sourceSpan) {
9596
+ return {
9597
+ kind: OpKind.TwoWayProperty,
9598
+ target,
9599
+ name,
9600
+ expression,
9601
+ securityContext,
9602
+ sanitizer: null,
9603
+ isStructuralTemplateAttribute,
9604
+ templateKind,
9605
+ i18nContext,
9606
+ i18nMessage,
9607
+ sourceSpan,
9608
+ ...TRAIT_DEPENDS_ON_SLOT_CONTEXT,
9609
+ ...TRAIT_CONSUMES_VARS,
9610
+ ...NEW_OP,
9611
+ };
9612
+ }
9488
9613
  /** Create a `StylePropOp`. */
9489
9614
  function createStylePropOp(xref, name, expression, unit, sourceSpan) {
9490
9615
  return {
@@ -9862,6 +9987,31 @@ class ResetViewExpr extends ExpressionBase {
9862
9987
  return new ResetViewExpr(this.expr.clone());
9863
9988
  }
9864
9989
  }
9990
+ class TwoWayBindingSetExpr extends ExpressionBase {
9991
+ constructor(target, value) {
9992
+ super();
9993
+ this.target = target;
9994
+ this.value = value;
9995
+ this.kind = ExpressionKind.TwoWayBindingSet;
9996
+ }
9997
+ visitExpression(visitor, context) {
9998
+ this.target.visitExpression(visitor, context);
9999
+ this.value.visitExpression(visitor, context);
10000
+ }
10001
+ isEquivalent(other) {
10002
+ return this.target.isEquivalent(other.target) && this.value.isEquivalent(other.value);
10003
+ }
10004
+ isConstant() {
10005
+ return false;
10006
+ }
10007
+ transformInternalExpressions(transform, flags) {
10008
+ this.target = transformExpressionsInExpression(this.target, transform, flags);
10009
+ this.value = transformExpressionsInExpression(this.value, transform, flags);
10010
+ }
10011
+ clone() {
10012
+ return new TwoWayBindingSetExpr(this.target, this.value);
10013
+ }
10014
+ }
9865
10015
  /**
9866
10016
  * Read of a variable declared as an `ir.VariableOp` and referenced through its `ir.XrefId`.
9867
10017
  */
@@ -10321,6 +10471,11 @@ function transformExpressionsInOp(op, transform, flags) {
10321
10471
  op.sanitizer =
10322
10472
  op.sanitizer && transformExpressionsInExpression(op.sanitizer, transform, flags);
10323
10473
  break;
10474
+ case OpKind.TwoWayProperty:
10475
+ op.expression = transformExpressionsInExpression(op.expression, transform, flags);
10476
+ op.sanitizer =
10477
+ op.sanitizer && transformExpressionsInExpression(op.sanitizer, transform, flags);
10478
+ break;
10324
10479
  case OpKind.I18nExpression:
10325
10480
  op.expression = transformExpressionsInExpression(op.expression, transform, flags);
10326
10481
  break;
@@ -10349,6 +10504,7 @@ function transformExpressionsInOp(op, transform, flags) {
10349
10504
  }
10350
10505
  break;
10351
10506
  case OpKind.Listener:
10507
+ case OpKind.TwoWayListener:
10352
10508
  for (const innerOp of op.handlerOps) {
10353
10509
  transformExpressionsInOp(innerOp, transform, flags | VisitorContextFlag.InChildOperation);
10354
10510
  }
@@ -10967,6 +11123,24 @@ function createListenerOp(target, targetSlot, name, tag, handlerOps, animationPh
10967
11123
  ...NEW_OP,
10968
11124
  };
10969
11125
  }
11126
+ /**
11127
+ * Create a `TwoWayListenerOp`.
11128
+ */
11129
+ function createTwoWayListenerOp(target, targetSlot, name, tag, handlerOps, sourceSpan) {
11130
+ const handlerList = new OpList();
11131
+ handlerList.push(handlerOps);
11132
+ return {
11133
+ kind: OpKind.TwoWayListener,
11134
+ target,
11135
+ targetSlot,
11136
+ tag,
11137
+ name,
11138
+ handlerOps: handlerList,
11139
+ handlerFnName: null,
11140
+ sourceSpan,
11141
+ ...NEW_OP,
11142
+ };
11143
+ }
10970
11144
  function createPipeOp(xref, slot, name) {
10971
11145
  return {
10972
11146
  kind: OpKind.Pipe,
@@ -11024,7 +11198,7 @@ function createExtractedAttributeOp(target, bindingKind, namespace, name, expres
11024
11198
  ...NEW_OP,
11025
11199
  };
11026
11200
  }
11027
- function createDeferOp(xref, main, mainSlot, metadata, sourceSpan) {
11201
+ function createDeferOp(xref, main, mainSlot, metadata, resolverFn, sourceSpan) {
11028
11202
  return {
11029
11203
  kind: OpKind.Defer,
11030
11204
  xref,
@@ -11043,7 +11217,7 @@ function createDeferOp(xref, main, mainSlot, metadata, sourceSpan) {
11043
11217
  errorView: null,
11044
11218
  errorSlot: null,
11045
11219
  metadata,
11046
- resolverFn: null,
11220
+ resolverFn,
11047
11221
  sourceSpan,
11048
11222
  ...NEW_OP,
11049
11223
  ...TRAIT_CONSUMES_SLOT,
@@ -11232,11 +11406,12 @@ class CompilationJob {
11232
11406
  * embedded views or host bindings.
11233
11407
  */
11234
11408
  class ComponentCompilationJob extends CompilationJob {
11235
- constructor(componentName, pool, compatibility, relativeContextFilePath, i18nUseExternalIds, deferBlocksMeta) {
11409
+ constructor(componentName, pool, compatibility, relativeContextFilePath, i18nUseExternalIds, deferBlocksMeta, allDeferrableDepsFn) {
11236
11410
  super(componentName, pool, compatibility);
11237
11411
  this.relativeContextFilePath = relativeContextFilePath;
11238
11412
  this.i18nUseExternalIds = i18nUseExternalIds;
11239
11413
  this.deferBlocksMeta = deferBlocksMeta;
11414
+ this.allDeferrableDepsFn = allDeferrableDepsFn;
11240
11415
  this.kind = CompilationJobKind.Tmpl;
11241
11416
  this.fnSuffix = 'Template';
11242
11417
  this.views = new Map();
@@ -11323,7 +11498,7 @@ class CompilationUnit {
11323
11498
  *ops() {
11324
11499
  for (const op of this.create) {
11325
11500
  yield op;
11326
- if (op.kind === OpKind.Listener) {
11501
+ if (op.kind === OpKind.Listener || op.kind === OpKind.TwoWayListener) {
11327
11502
  for (const listenerOp of op.handlerOps) {
11328
11503
  yield listenerOp;
11329
11504
  }
@@ -11569,6 +11744,11 @@ function extractAttributes(job) {
11569
11744
  /* i18nMessage */ null, op.securityContext), lookupElement$2(elements, op.target));
11570
11745
  }
11571
11746
  break;
11747
+ case OpKind.TwoWayProperty:
11748
+ OpList.insertBefore(createExtractedAttributeOp(op.target, BindingKind.TwoWayProperty, null, op.name, /* expression */ null,
11749
+ /* i18nContext */ null,
11750
+ /* i18nMessage */ null, op.securityContext), lookupElement$2(elements, op.target));
11751
+ break;
11572
11752
  case OpKind.StyleProp:
11573
11753
  case OpKind.ClassProp:
11574
11754
  // TODO: Can style or class bindings be i18n attributes?
@@ -11602,6 +11782,15 @@ function extractAttributes(job) {
11602
11782
  }
11603
11783
  }
11604
11784
  break;
11785
+ case OpKind.TwoWayListener:
11786
+ // Two-way listeners aren't supported in host bindings.
11787
+ if (job.kind !== CompilationJobKind.Host) {
11788
+ const extractedAttributeOp = createExtractedAttributeOp(op.target, BindingKind.Property, null, op.name, /* expression */ null,
11789
+ /* i18nContext */ null,
11790
+ /* i18nMessage */ null, SecurityContext.NONE);
11791
+ OpList.insertBefore(extractedAttributeOp, lookupElement$2(elements, op.target));
11792
+ }
11793
+ break;
11605
11794
  }
11606
11795
  }
11607
11796
  }
@@ -11690,6 +11879,15 @@ function specializeBindings(job) {
11690
11879
  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));
11691
11880
  }
11692
11881
  break;
11882
+ case BindingKind.TwoWayProperty:
11883
+ if (!(op.expression instanceof Expression)) {
11884
+ // We shouldn't be able to hit this code path since interpolations in two-way bindings
11885
+ // result in a parser error. We assert here so that downstream we can assume that
11886
+ // the value is always an expression.
11887
+ throw new Error(`Expected value of two-way property binding "${op.name}" to be an expression`);
11888
+ }
11889
+ OpList.replace(op, createTwoWayPropertyOp(op.target, op.name, op.expression, op.securityContext, op.isStructuralTemplateAttribute, op.templateKind, op.i18nContext, op.i18nMessage, op.sourceSpan));
11890
+ break;
11693
11891
  case BindingKind.I18n:
11694
11892
  case BindingKind.ClassName:
11695
11893
  case BindingKind.StyleProperty:
@@ -11726,6 +11924,8 @@ const CHAINABLE = new Set([
11726
11924
  Identifiers.syntheticHostListener,
11727
11925
  Identifiers.syntheticHostProperty,
11728
11926
  Identifiers.templateCreate,
11927
+ Identifiers.twoWayProperty,
11928
+ Identifiers.twoWayListener,
11729
11929
  ]);
11730
11930
  /**
11731
11931
  * Post-process a reified view compilation and convert sequential calls to chainable instructions
@@ -11999,7 +12199,7 @@ class ElementAttributes {
11999
12199
  return this.byKind.get(BindingKind.StyleProperty) ?? FLYWEIGHT_ARRAY;
12000
12200
  }
12001
12201
  get bindings() {
12002
- return this.byKind.get(BindingKind.Property) ?? FLYWEIGHT_ARRAY;
12202
+ return this.propertyBindings ?? FLYWEIGHT_ARRAY;
12003
12203
  }
12004
12204
  get template() {
12005
12205
  return this.byKind.get(BindingKind.Template) ?? FLYWEIGHT_ARRAY;
@@ -12011,6 +12211,7 @@ class ElementAttributes {
12011
12211
  this.compatibility = compatibility;
12012
12212
  this.known = new Map();
12013
12213
  this.byKind = new Map;
12214
+ this.propertyBindings = null;
12014
12215
  this.projectAs = null;
12015
12216
  }
12016
12217
  isKnown(kind, name, value) {
@@ -12060,10 +12261,16 @@ class ElementAttributes {
12060
12261
  }
12061
12262
  }
12062
12263
  arrayFor(kind) {
12063
- if (!this.byKind.has(kind)) {
12064
- this.byKind.set(kind, []);
12264
+ if (kind === BindingKind.Property || kind === BindingKind.TwoWayProperty) {
12265
+ this.propertyBindings ??= [];
12266
+ return this.propertyBindings;
12267
+ }
12268
+ else {
12269
+ if (!this.byKind.has(kind)) {
12270
+ this.byKind.set(kind, []);
12271
+ }
12272
+ return this.byKind.get(kind);
12065
12273
  }
12066
- return this.byKind.get(kind);
12067
12274
  }
12068
12275
  }
12069
12276
  /**
@@ -12159,11 +12366,16 @@ function createDeferDepsFns(job) {
12159
12366
  if (op.metadata.deps.length === 0) {
12160
12367
  continue;
12161
12368
  }
12369
+ if (op.resolverFn !== null) {
12370
+ continue;
12371
+ }
12162
12372
  const dependencies = [];
12163
12373
  for (const dep of op.metadata.deps) {
12164
12374
  if (dep.isDeferrable) {
12165
12375
  // Callback function, e.g. `m () => m.MyCmp;`.
12166
- const innerFn = arrowFn([new FnParam('m', DYNAMIC_TYPE)], variable('m').prop(dep.symbolName));
12376
+ const innerFn = arrowFn(
12377
+ // Default imports are always accessed through the `default` property.
12378
+ [new FnParam('m', DYNAMIC_TYPE)], variable('m').prop(dep.isDefaultImport ? 'default' : dep.symbolName));
12167
12379
  // Dynamic import, e.g. `import('./a').then(...)`.
12168
12380
  const importExpr = (new DynamicImportExpr(dep.importPath)).prop('then').callFn([innerFn]);
12169
12381
  dependencies.push(importExpr);
@@ -12177,7 +12389,8 @@ function createDeferDepsFns(job) {
12177
12389
  if (op.handle.slot === null) {
12178
12390
  throw new Error('AssertionError: slot must be assigned bfore extracting defer deps functions');
12179
12391
  }
12180
- op.resolverFn = job.pool.getSharedFunctionReference(depsFnExpr, `${job.componentName}_Defer_${op.handle.slot}_DepsFn`,
12392
+ const fullPathName = unit.fnName?.replace(`_Template`, ``);
12393
+ op.resolverFn = job.pool.getSharedFunctionReference(depsFnExpr, `${fullPathName}_Defer_${op.handle.slot}_DepsFn`,
12181
12394
  /* Don't use unique names for TDB compatibility */ false);
12182
12395
  }
12183
12396
  }
@@ -12989,6 +13202,7 @@ function recursivelyProcessView(view, parentScope) {
12989
13202
  }
12990
13203
  break;
12991
13204
  case OpKind.Listener:
13205
+ case OpKind.TwoWayListener:
12992
13206
  // Prepend variables to listener handler functions.
12993
13207
  op.handlerOps.prepend(generateVariablesInScopeForView(view, scope));
12994
13208
  break;
@@ -13652,15 +13866,12 @@ class Parser$1 {
13652
13866
  this._lexer = _lexer;
13653
13867
  this.errors = [];
13654
13868
  }
13655
- parseAction(input, isAssignmentEvent, location, absoluteOffset, interpolationConfig = DEFAULT_INTERPOLATION_CONFIG) {
13869
+ parseAction(input, location, absoluteOffset, interpolationConfig = DEFAULT_INTERPOLATION_CONFIG) {
13656
13870
  this._checkNoInterpolation(input, location, interpolationConfig);
13657
13871
  const sourceToLex = this._stripComments(input);
13658
13872
  const tokens = this._lexer.tokenize(sourceToLex);
13659
- let flags = 1 /* ParseFlags.Action */;
13660
- if (isAssignmentEvent) {
13661
- flags |= 2 /* ParseFlags.AssignmentEvent */;
13662
- }
13663
- const ast = new _ParseAST(input, location, absoluteOffset, tokens, flags, this.errors, 0).parseChain();
13873
+ const ast = new _ParseAST(input, location, absoluteOffset, tokens, 1 /* ParseFlags.Action */, this.errors, 0)
13874
+ .parseChain();
13664
13875
  return new ASTWithSource(ast, input, location, absoluteOffset, this.errors);
13665
13876
  }
13666
13877
  parseBinding(input, location, absoluteOffset, interpolationConfig = DEFAULT_INTERPOLATION_CONFIG) {
@@ -14137,7 +14348,7 @@ class _ParseAST {
14137
14348
  let result = this.parseExpression();
14138
14349
  if (this.consumeOptionalOperator('|')) {
14139
14350
  if (this.parseFlags & 1 /* ParseFlags.Action */) {
14140
- this.error('Cannot have a pipe in an action expression');
14351
+ this.error(`Cannot have a pipe in an action expression`);
14141
14352
  }
14142
14353
  do {
14143
14354
  const nameStart = this.inputIndex;
@@ -14479,7 +14690,7 @@ class _ParseAST {
14479
14690
  const nameSpan = this.sourceSpan(nameStart);
14480
14691
  let receiver;
14481
14692
  if (isSafe) {
14482
- if (this.consumeOptionalAssignment()) {
14693
+ if (this.consumeOptionalOperator('=')) {
14483
14694
  this.error('The \'?.\' operator cannot be used in the assignment');
14484
14695
  receiver = new EmptyExpr$1(this.span(start), this.sourceSpan(start));
14485
14696
  }
@@ -14488,7 +14699,7 @@ class _ParseAST {
14488
14699
  }
14489
14700
  }
14490
14701
  else {
14491
- if (this.consumeOptionalAssignment()) {
14702
+ if (this.consumeOptionalOperator('=')) {
14492
14703
  if (!(this.parseFlags & 1 /* ParseFlags.Action */)) {
14493
14704
  this.error('Bindings cannot contain assignments');
14494
14705
  return new EmptyExpr$1(this.span(start), this.sourceSpan(start));
@@ -14515,22 +14726,6 @@ class _ParseAST {
14515
14726
  return isSafe ? new SafeCall(span, sourceSpan, receiver, args, argumentSpan) :
14516
14727
  new Call(span, sourceSpan, receiver, args, argumentSpan);
14517
14728
  }
14518
- consumeOptionalAssignment() {
14519
- // When parsing assignment events (originating from two-way-binding aka banana-in-a-box syntax),
14520
- // it is valid for the primary expression to be terminated by the non-null operator. This
14521
- // primary expression is substituted as LHS of the assignment operator to achieve
14522
- // two-way-binding, such that the LHS could be the non-null operator. The grammar doesn't
14523
- // naturally allow for this syntax, so assignment events are parsed specially.
14524
- if ((this.parseFlags & 2 /* ParseFlags.AssignmentEvent */) && this.next.isOperator('!') &&
14525
- this.peek(1).isOperator('=')) {
14526
- // First skip over the ! operator.
14527
- this.advance();
14528
- // Then skip over the = operator, to fully consume the optional assignment operator.
14529
- this.advance();
14530
- return true;
14531
- }
14532
- return this.consumeOptionalOperator('=');
14533
- }
14534
14729
  parseCallArguments() {
14535
14730
  if (this.next.isCharacter($RPAREN))
14536
14731
  return [];
@@ -20608,7 +20803,10 @@ function nameFunctionsAndVariables(job) {
20608
20803
  }
20609
20804
  function addNamesToView(unit, baseName, state, compatibility) {
20610
20805
  if (unit.fnName === null) {
20611
- unit.fnName = sanitizeIdentifier(`${baseName}_${unit.job.fnSuffix}`);
20806
+ // Ensure unique names for view units. This is necessary because there might be multiple
20807
+ // components with same names in the context of the same pool. Only add the suffix
20808
+ // if really needed.
20809
+ unit.fnName = unit.job.pool.uniqueName(sanitizeIdentifier(`${baseName}_${unit.job.fnSuffix}`), /* alwaysIncludeSuffix */ false);
20612
20810
  }
20613
20811
  // Keep track of the names we assign to variables in the view. We'll need to propagate these
20614
20812
  // into reads of those variables afterwards.
@@ -20641,6 +20839,15 @@ function addNamesToView(unit, baseName, state, compatibility) {
20641
20839
  }
20642
20840
  op.handlerFnName = sanitizeIdentifier(op.handlerFnName);
20643
20841
  break;
20842
+ case OpKind.TwoWayListener:
20843
+ if (op.handlerFnName !== null) {
20844
+ break;
20845
+ }
20846
+ if (op.targetSlot.slot === null) {
20847
+ throw new Error(`Expected a slot to be assigned`);
20848
+ }
20849
+ op.handlerFnName = sanitizeIdentifier(`${unit.fnName}_${op.tag.replace('-', '_')}_${op.name}_${op.targetSlot.slot}_listener`);
20850
+ break;
20644
20851
  case OpKind.Variable:
20645
20852
  varNames.set(op.xref, getVariableName(unit, op.variable, state));
20646
20853
  break;
@@ -20755,7 +20962,7 @@ function stripImportant(name) {
20755
20962
  function mergeNextContextExpressions(job) {
20756
20963
  for (const unit of job.units) {
20757
20964
  for (const op of unit.create) {
20758
- if (op.kind === OpKind.Listener) {
20965
+ if (op.kind === OpKind.Listener || op.kind === OpKind.TwoWayListener) {
20759
20966
  mergeNextContextsInOps(op.handlerOps);
20760
20967
  }
20761
20968
  }
@@ -20896,6 +21103,14 @@ function kindWithInterpolationTest(kind, interpolation) {
20896
21103
  return op.kind === kind && interpolation === op.expression instanceof Interpolation;
20897
21104
  };
20898
21105
  }
21106
+ function basicListenerKindTest(op) {
21107
+ return (op.kind === OpKind.Listener && !(op.hostListener && op.isAnimationListener)) ||
21108
+ op.kind === OpKind.TwoWayListener;
21109
+ }
21110
+ function nonInterpolationPropertyKindTest(op) {
21111
+ return (op.kind === OpKind.Property || op.kind === OpKind.TwoWayProperty) &&
21112
+ !(op.expression instanceof Interpolation);
21113
+ }
20899
21114
  /**
20900
21115
  * Defines the groups based on `OpKind` that ops will be divided into, for the various create
20901
21116
  * op kinds. Ops will be collected into groups, then optionally transformed, before recombining
@@ -20903,7 +21118,7 @@ function kindWithInterpolationTest(kind, interpolation) {
20903
21118
  */
20904
21119
  const CREATE_ORDERING = [
20905
21120
  { test: op => op.kind === OpKind.Listener && op.hostListener && op.isAnimationListener },
20906
- { test: op => op.kind === OpKind.Listener && !(op.hostListener && op.isAnimationListener) },
21121
+ { test: basicListenerKindTest },
20907
21122
  ];
20908
21123
  /**
20909
21124
  * Defines the groups based on `OpKind` that ops will be divided into, for the various update
@@ -20916,7 +21131,7 @@ const UPDATE_ORDERING = [
20916
21131
  { test: kindTest(OpKind.ClassProp) },
20917
21132
  { test: kindWithInterpolationTest(OpKind.Attribute, true) },
20918
21133
  { test: kindWithInterpolationTest(OpKind.Property, true) },
20919
- { test: kindWithInterpolationTest(OpKind.Property, false) },
21134
+ { test: nonInterpolationPropertyKindTest },
20920
21135
  { test: kindWithInterpolationTest(OpKind.Attribute, false) },
20921
21136
  ];
20922
21137
  /**
@@ -20935,8 +21150,9 @@ const UPDATE_HOST_ORDERING = [
20935
21150
  * The set of all op kinds we handle in the reordering phase.
20936
21151
  */
20937
21152
  const handledOpKinds = new Set([
20938
- OpKind.Listener, OpKind.StyleMap, OpKind.ClassMap, OpKind.StyleProp,
20939
- OpKind.ClassProp, OpKind.Property, OpKind.HostProperty, OpKind.Attribute
21153
+ OpKind.Listener, OpKind.TwoWayListener, OpKind.StyleMap, OpKind.ClassMap,
21154
+ OpKind.StyleProp, OpKind.ClassProp, OpKind.Property, OpKind.TwoWayProperty,
21155
+ OpKind.HostProperty, OpKind.Attribute
20940
21156
  ]);
20941
21157
  /**
20942
21158
  * Many type of operations have ordering constraints that must be respected. For example, a
@@ -21402,6 +21618,12 @@ function listener(name, handlerFn, eventTargetResolver, syntheticHost, sourceSpa
21402
21618
  }
21403
21619
  return call(syntheticHost ? Identifiers.syntheticHostListener : Identifiers.listener, args, sourceSpan);
21404
21620
  }
21621
+ function twoWayBindingSet(target, value) {
21622
+ return importExpr(Identifiers.twoWayBindingSet).callFn([target, value]);
21623
+ }
21624
+ function twoWayListener(name, handlerFn, sourceSpan) {
21625
+ return call(Identifiers.twoWayListener, [literal(name), handlerFn], sourceSpan);
21626
+ }
21405
21627
  function pipe(slot, name) {
21406
21628
  return call(Identifiers.pipe, [
21407
21629
  literal(slot),
@@ -21562,6 +21784,13 @@ function property(name, expression, sanitizer, sourceSpan) {
21562
21784
  }
21563
21785
  return call(Identifiers.property, args, sourceSpan);
21564
21786
  }
21787
+ function twoWayProperty(name, expression, sanitizer, sourceSpan) {
21788
+ const args = [literal(name), expression];
21789
+ if (sanitizer !== null) {
21790
+ args.push(sanitizer);
21791
+ }
21792
+ return call(Identifiers.twoWayProperty, args, sourceSpan);
21793
+ }
21565
21794
  function attribute(name, expression, sanitizer, namespace) {
21566
21795
  const args = [literal(name), expression];
21567
21796
  if (sanitizer !== null || namespace !== null) {
@@ -22004,6 +22233,9 @@ function reifyCreateOperations(unit, ops) {
22004
22233
  }
22005
22234
  OpList.replace(op, listener(op.name, listenerFn, eventTargetResolver, op.hostListener && op.isAnimationListener, op.sourceSpan));
22006
22235
  break;
22236
+ case OpKind.TwoWayListener:
22237
+ OpList.replace(op, twoWayListener(op.name, reifyListenerHandler(unit, op.handlerFnName, op.handlerOps, true), op.sourceSpan));
22238
+ break;
22007
22239
  case OpKind.Variable:
22008
22240
  if (op.variable.name === null) {
22009
22241
  throw new Error(`AssertionError: unnamed variable ${op.xref}`);
@@ -22112,6 +22344,9 @@ function reifyUpdateOperations(_unit, ops) {
22112
22344
  OpList.replace(op, property(op.name, op.expression, op.sanitizer, op.sourceSpan));
22113
22345
  }
22114
22346
  break;
22347
+ case OpKind.TwoWayProperty:
22348
+ OpList.replace(op, twoWayProperty(op.name, op.expression, op.sanitizer, op.sourceSpan));
22349
+ break;
22115
22350
  case OpKind.StyleProp:
22116
22351
  if (op.expression instanceof Interpolation) {
22117
22352
  OpList.replace(op, stylePropInterpolate(op.name, op.expression.strings, op.expression.expressions, op.unit, op.sourceSpan));
@@ -22209,6 +22444,8 @@ function reifyIrExpression(expr) {
22209
22444
  return reference(expr.targetSlot.slot + 1 + expr.offset);
22210
22445
  case ExpressionKind.LexicalRead:
22211
22446
  throw new Error(`AssertionError: unresolved LexicalRead of ${expr.name}`);
22447
+ case ExpressionKind.TwoWayBindingSet:
22448
+ throw new Error(`AssertionError: unresolved TwoWayBindingSet`);
22212
22449
  case ExpressionKind.RestoreView:
22213
22450
  if (typeof expr.view === 'number') {
22214
22451
  throw new Error(`AssertionError: unresolved RestoreView`);
@@ -22369,6 +22606,7 @@ function processLexicalScope$1(view, ops) {
22369
22606
  }
22370
22607
  break;
22371
22608
  case OpKind.Listener:
22609
+ case OpKind.TwoWayListener:
22372
22610
  processLexicalScope$1(view, op.handlerOps);
22373
22611
  break;
22374
22612
  }
@@ -22404,10 +22642,13 @@ function resolveDollarEvent(job) {
22404
22642
  }
22405
22643
  function transformDollarEvent(unit, ops) {
22406
22644
  for (const op of ops) {
22407
- if (op.kind === OpKind.Listener) {
22645
+ if (op.kind === OpKind.Listener || op.kind === OpKind.TwoWayListener) {
22408
22646
  transformExpressionsInOp(op, (expr) => {
22409
22647
  if (expr instanceof LexicalReadExpr && expr.name === '$event') {
22410
- op.consumesDollarEvent = true;
22648
+ // Two-way listeners always consume `$event` so they omit this field.
22649
+ if (op.kind === OpKind.Listener) {
22650
+ op.consumesDollarEvent = true;
22651
+ }
22411
22652
  return new ReadVarExpr(expr.name);
22412
22653
  }
22413
22654
  return expr;
@@ -22782,6 +23023,7 @@ function processLexicalScope(unit, ops, savedView) {
22782
23023
  }
22783
23024
  break;
22784
23025
  case OpKind.Listener:
23026
+ case OpKind.TwoWayListener:
22785
23027
  // Listener functions have separate variable declarations, so process them as a separate
22786
23028
  // lexical scope.
22787
23029
  processLexicalScope(unit, op.handlerOps, savedView);
@@ -22792,7 +23034,7 @@ function processLexicalScope(unit, ops, savedView) {
22792
23034
  // scope. Also, look for `ir.RestoreViewExpr`s and match them with the snapshotted view context
22793
23035
  // variable.
22794
23036
  for (const op of ops) {
22795
- if (op.kind == OpKind.Listener) {
23037
+ if (op.kind == OpKind.Listener || op.kind === OpKind.TwoWayListener) {
22796
23038
  // Listeners were already processed above with their own scopes.
22797
23039
  continue;
22798
23040
  }
@@ -22941,6 +23183,75 @@ function getOnlySecurityContext(securityContext) {
22941
23183
  return securityContext;
22942
23184
  }
22943
23185
 
23186
+ /**
23187
+ * Transforms a `TwoWayBindingSet` expression into an expression that either
23188
+ * sets a value through the `twoWayBindingSet` instruction or falls back to setting
23189
+ * the value directly. E.g. the expression `TwoWayBindingSet(target, value)` becomes:
23190
+ * `ng.twoWayBindingSet(target, value) || (target = value)`.
23191
+ */
23192
+ function transformTwoWayBindingSet(job) {
23193
+ for (const unit of job.units) {
23194
+ for (const op of unit.create) {
23195
+ if (op.kind === OpKind.TwoWayListener) {
23196
+ transformExpressionsInOp(op, (expr) => {
23197
+ if (expr instanceof TwoWayBindingSetExpr) {
23198
+ return wrapAction(expr.target, expr.value);
23199
+ }
23200
+ return expr;
23201
+ }, VisitorContextFlag.InChildOperation);
23202
+ }
23203
+ }
23204
+ }
23205
+ }
23206
+ function wrapSetOperation(target, value) {
23207
+ return twoWayBindingSet(target, value).or(target.set(value));
23208
+ }
23209
+ function isReadExpression(value) {
23210
+ return value instanceof ReadPropExpr || value instanceof ReadKeyExpr;
23211
+ }
23212
+ function wrapAction(target, value) {
23213
+ // The only officially supported expressions inside of a two-way binding are read expressions.
23214
+ if (isReadExpression(target)) {
23215
+ return wrapSetOperation(target, value);
23216
+ }
23217
+ // However, historically the expression parser was handling two-way events by appending `=$event`
23218
+ // to the raw string before attempting to parse it. This has led to bugs over the years (see
23219
+ // #37809) and to unintentionally supporting unassignable events in the two-way binding. The
23220
+ // logic below aims to emulate the old behavior while still supporting the new output format
23221
+ // which uses `twoWayBindingSet`. Note that the generated code doesn't necessarily make sense
23222
+ // based on what the user wrote, for example the event binding for `[(value)]="a ? b : c"`
23223
+ // would produce `ctx.a ? ctx.b : ctx.c = $event`. We aim to reproduce what the parser used
23224
+ // to generate before #54154.
23225
+ if (target instanceof BinaryOperatorExpr && isReadExpression(target.rhs)) {
23226
+ // `a && b` -> `ctx.a && twoWayBindingSet(ctx.b, $event) || (ctx.b = $event)`
23227
+ return new BinaryOperatorExpr(target.operator, target.lhs, wrapSetOperation(target.rhs, value));
23228
+ }
23229
+ // Note: this also supports nullish coalescing expressions which
23230
+ // would've been downleveled to ternary expressions by this point.
23231
+ if (target instanceof ConditionalExpr && isReadExpression(target.falseCase)) {
23232
+ // `a ? b : c` -> `ctx.a ? ctx.b : twoWayBindingSet(ctx.c, $event) || (ctx.c = $event)`
23233
+ return new ConditionalExpr(target.condition, target.trueCase, wrapSetOperation(target.falseCase, value));
23234
+ }
23235
+ // `!!a` -> `twoWayBindingSet(ctx.a, $event) || (ctx.a = $event)`
23236
+ // Note: previously we'd actually produce `!!(ctx.a = $event)`, but the wrapping
23237
+ // node doesn't affect the result so we don't need to carry it over.
23238
+ if (target instanceof NotExpr) {
23239
+ let expr = target.condition;
23240
+ while (true) {
23241
+ if (expr instanceof NotExpr) {
23242
+ expr = expr.condition;
23243
+ }
23244
+ else {
23245
+ if (isReadExpression(expr)) {
23246
+ return wrapSetOperation(expr, value);
23247
+ }
23248
+ break;
23249
+ }
23250
+ }
23251
+ }
23252
+ throw new Error(`Unsupported expression in two-way action binding.`);
23253
+ }
23254
+
22944
23255
  /**
22945
23256
  * When inside of a listener, we may need access to one or more enclosing views. Therefore, each
22946
23257
  * view should save the current view, and each listener must have the ability to restore the
@@ -22956,7 +23267,7 @@ function saveAndRestoreView(job) {
22956
23267
  }, new GetCurrentViewExpr(), VariableFlags.None),
22957
23268
  ]);
22958
23269
  for (const op of unit.create) {
22959
- if (op.kind !== OpKind.Listener) {
23270
+ if (op.kind !== OpKind.Listener && op.kind !== OpKind.TwoWayListener) {
22960
23271
  continue;
22961
23272
  }
22962
23273
  // Embedded views always need the save/restore view operation.
@@ -23146,7 +23457,7 @@ function generateTemporaries(ops) {
23146
23457
  generatedStatements.push(...Array.from(new Set(defs.values()))
23147
23458
  .map(name => createStatementOp(new DeclareVarStmt(name))));
23148
23459
  opCount++;
23149
- if (op.kind === OpKind.Listener) {
23460
+ if (op.kind === OpKind.Listener || op.kind === OpKind.TwoWayListener) {
23150
23461
  op.handlerOps.prepend(generateTemporaries(op.handlerOps));
23151
23462
  }
23152
23463
  }
@@ -23394,6 +23705,9 @@ function varsUsedByOp(op) {
23394
23705
  slots += op.expression.expressions.length;
23395
23706
  }
23396
23707
  return slots;
23708
+ case OpKind.TwoWayProperty:
23709
+ // Two-way properties can only have expressions so they only need one variable slot.
23710
+ return 1;
23397
23711
  case OpKind.StyleProp:
23398
23712
  case OpKind.ClassProp:
23399
23713
  case OpKind.StyleMap:
@@ -23467,14 +23781,14 @@ function optimizeVariables(job) {
23467
23781
  inlineAlwaysInlineVariables(unit.create);
23468
23782
  inlineAlwaysInlineVariables(unit.update);
23469
23783
  for (const op of unit.create) {
23470
- if (op.kind === OpKind.Listener) {
23784
+ if (op.kind === OpKind.Listener || op.kind === OpKind.TwoWayListener) {
23471
23785
  inlineAlwaysInlineVariables(op.handlerOps);
23472
23786
  }
23473
23787
  }
23474
23788
  optimizeVariablesInOpList(unit.create, job.compatibility);
23475
23789
  optimizeVariablesInOpList(unit.update, job.compatibility);
23476
23790
  for (const op of unit.create) {
23477
- if (op.kind === OpKind.Listener) {
23791
+ if (op.kind === OpKind.Listener || op.kind === OpKind.TwoWayListener) {
23478
23792
  optimizeVariablesInOpList(op.handlerOps, job.compatibility);
23479
23793
  }
23480
23794
  }
@@ -23929,6 +24243,7 @@ const phases = [
23929
24243
  { kind: CompilationJobKind.Tmpl, fn: generateTrackVariables },
23930
24244
  { kind: CompilationJobKind.Both, fn: resolveNames },
23931
24245
  { kind: CompilationJobKind.Tmpl, fn: resolveDeferTargetNames },
24246
+ { kind: CompilationJobKind.Tmpl, fn: transformTwoWayBindingSet },
23932
24247
  { kind: CompilationJobKind.Tmpl, fn: optimizeTrackFns },
23933
24248
  { kind: CompilationJobKind.Both, fn: resolveContexts },
23934
24249
  { kind: CompilationJobKind.Both, fn: resolveSanitizers },
@@ -23937,7 +24252,6 @@ const phases = [
23937
24252
  { kind: CompilationJobKind.Both, fn: expandSafeReads },
23938
24253
  { kind: CompilationJobKind.Both, fn: generateTemporaryVariables },
23939
24254
  { kind: CompilationJobKind.Tmpl, fn: allocateSlots },
23940
- { kind: CompilationJobKind.Tmpl, fn: createDeferDepsFns },
23941
24255
  { kind: CompilationJobKind.Tmpl, fn: resolveI18nElementPlaceholders },
23942
24256
  { kind: CompilationJobKind.Tmpl, fn: resolveI18nExpressionPlaceholders },
23943
24257
  { kind: CompilationJobKind.Tmpl, fn: extractI18nMessages },
@@ -23950,6 +24264,7 @@ const phases = [
23950
24264
  { kind: CompilationJobKind.Tmpl, fn: generateAdvance },
23951
24265
  { kind: CompilationJobKind.Both, fn: optimizeVariables },
23952
24266
  { kind: CompilationJobKind.Both, fn: nameFunctionsAndVariables },
24267
+ { kind: CompilationJobKind.Tmpl, fn: createDeferDepsFns },
23953
24268
  { kind: CompilationJobKind.Tmpl, fn: mergeNextContextExpressions },
23954
24269
  { kind: CompilationJobKind.Tmpl, fn: generateNgContainerOps },
23955
24270
  { kind: CompilationJobKind.Tmpl, fn: collapseEmptyInstructions },
@@ -24075,8 +24390,8 @@ const NG_TEMPLATE_TAG_NAME$1 = 'ng-template';
24075
24390
  * representation.
24076
24391
  * TODO: Refactor more of the ingestion code into phases.
24077
24392
  */
24078
- function ingestComponent(componentName, template, constantPool, relativeContextFilePath, i18nUseExternalIds, deferBlocksMeta) {
24079
- const job = new ComponentCompilationJob(componentName, constantPool, compatibilityMode, relativeContextFilePath, i18nUseExternalIds, deferBlocksMeta);
24393
+ function ingestComponent(componentName, template, constantPool, relativeContextFilePath, i18nUseExternalIds, deferBlocksMeta, allDeferrableDepsFn) {
24394
+ const job = new ComponentCompilationJob(componentName, constantPool, compatibilityMode, relativeContextFilePath, i18nUseExternalIds, deferBlocksMeta, allDeferrableDepsFn);
24080
24395
  ingestNodes(job.root, template);
24081
24396
  return job;
24082
24397
  }
@@ -24134,7 +24449,7 @@ function ingestHostAttribute(job, name, value, securityContexts) {
24134
24449
  job.root.update.push(attrBinding);
24135
24450
  }
24136
24451
  function ingestHostEvent(job, event) {
24137
- const [phase, target] = event.type === 0 /* e.ParsedEventType.Regular */ ? [null, event.targetOrPhase] :
24452
+ const [phase, target] = event.type !== 1 /* e.ParsedEventType.Animation */ ? [null, event.targetOrPhase] :
24138
24453
  [event.targetOrPhase, null];
24139
24454
  const eventBinding = createListenerOp(job.root.xref, new SlotHandle(), event.name, null, makeListenerHandlerOps(job.root, event.handler, event.handlerSpan), phase, target, true, event.sourceSpan);
24140
24455
  job.root.create.push(eventBinding);
@@ -24400,7 +24715,7 @@ function ingestDeferBlock(unit, deferBlock) {
24400
24715
  const error = ingestDeferView(unit, 'Error', deferBlock.error?.i18n, deferBlock.error?.children, deferBlock.error?.sourceSpan);
24401
24716
  // Create the main defer op, and ops for all secondary views.
24402
24717
  const deferXref = unit.job.allocateXrefId();
24403
- const deferOp = createDeferOp(deferXref, main.xref, main.handle, blockMeta, deferBlock.sourceSpan);
24718
+ const deferOp = createDeferOp(deferXref, main.xref, main.handle, blockMeta, unit.job.allDeferrableDepsFn, deferBlock.sourceSpan);
24404
24719
  deferOp.placeholderView = placeholder?.xref ?? null;
24405
24720
  deferOp.placeholderSlot = placeholder?.handle ?? null;
24406
24721
  deferOp.loadingSlot = loading?.handle ?? null;
@@ -24725,6 +25040,7 @@ function convertAstWithInterpolation(job, value, i18nMeta, sourceSpan) {
24725
25040
  // TODO: Can we populate Template binding kinds in ingest?
24726
25041
  const BINDING_KINDS = new Map([
24727
25042
  [0 /* e.BindingType.Property */, BindingKind.Property],
25043
+ [5 /* e.BindingType.TwoWay */, BindingKind.TwoWayProperty],
24728
25044
  [1 /* e.BindingType.Attribute */, BindingKind.Attribute],
24729
25045
  [2 /* e.BindingType.Class */, BindingKind.ClassName],
24730
25046
  [3 /* e.BindingType.Style */, BindingKind.StyleProperty],
@@ -24768,12 +25084,20 @@ function asMessage(i18nMeta) {
24768
25084
  */
24769
25085
  function ingestElementBindings(unit, op, element) {
24770
25086
  let bindings = new Array();
25087
+ let i18nAttributeBindingNames = new Set();
24771
25088
  for (const attr of element.attributes) {
24772
25089
  // Attribute literal bindings, such as `attr.foo="bar"`.
24773
25090
  const securityContext = domSchema.securityContext(element.name, attr.name, true);
24774
25091
  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));
25092
+ if (attr.i18n) {
25093
+ i18nAttributeBindingNames.add(attr.name);
25094
+ }
24775
25095
  }
24776
25096
  for (const input of element.inputs) {
25097
+ if (i18nAttributeBindingNames.has(input.name)) {
25098
+ console.error(`On component ${unit.job.componentName}, the binding ${input
25099
+ .name} is both an i18n attribute and a property. You may want to remove the property binding. This will become a compilation error in future versions of Angular.`);
25100
+ }
24777
25101
  // All dynamic bindings (both attribute and property bindings).
24778
25102
  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));
24779
25103
  }
@@ -24783,7 +25107,12 @@ function ingestElementBindings(unit, op, element) {
24783
25107
  if (output.type === 1 /* e.ParsedEventType.Animation */ && output.phase === null) {
24784
25108
  throw Error('Animation listener should have a phase');
24785
25109
  }
24786
- 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));
25110
+ if (output.type === 2 /* e.ParsedEventType.TwoWay */) {
25111
+ unit.create.push(createTwoWayListenerOp(op.xref, op.handle, output.name, op.tag, makeTwoWayListenerHandlerOps(unit, output.handler, output.handlerSpan), output.sourceSpan));
25112
+ }
25113
+ else {
25114
+ 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));
25115
+ }
24787
25116
  }
24788
25117
  // If any of the bindings on this element have an i18n message, then an i18n attrs configuration
24789
25118
  // op is also required.
@@ -24822,7 +25151,12 @@ function ingestTemplateBindings(unit, op, template, templateKind) {
24822
25151
  throw Error('Animation listener should have a phase');
24823
25152
  }
24824
25153
  if (templateKind === TemplateKind.NgTemplate) {
24825
- 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));
25154
+ if (output.type === 2 /* e.ParsedEventType.TwoWay */) {
25155
+ unit.create.push(createTwoWayListenerOp(op.xref, op.handle, output.name, op.tag, makeTwoWayListenerHandlerOps(unit, output.handler, output.handlerSpan), output.sourceSpan));
25156
+ }
25157
+ else {
25158
+ 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));
25159
+ }
24826
25160
  }
24827
25161
  if (templateKind === TemplateKind.Structural &&
24828
25162
  output.type !== 1 /* e.ParsedEventType.Animation */) {
@@ -24869,14 +25203,19 @@ function createTemplateBinding(view, xref, type, name, value, unit, securityCont
24869
25203
  // If this is a structural template, then several kinds of bindings should not result in an
24870
25204
  // update instruction.
24871
25205
  if (templateKind === TemplateKind.Structural) {
24872
- if (!isStructuralTemplateAttribute &&
24873
- (type === 0 /* e.BindingType.Property */ || type === 2 /* e.BindingType.Class */ ||
24874
- type === 3 /* e.BindingType.Style */)) {
24875
- // Because this binding doesn't really target the ng-template, it must be a binding on an
24876
- // inner node of a structural template. We can't skip it entirely, because we still need it on
24877
- // the ng-template's consts (e.g. for the purposes of directive matching). However, we should
24878
- // not generate an update instruction for it.
24879
- return createExtractedAttributeOp(xref, BindingKind.Property, null, name, null, null, i18nMessage, securityContext);
25206
+ if (!isStructuralTemplateAttribute) {
25207
+ switch (type) {
25208
+ case 0 /* e.BindingType.Property */:
25209
+ case 2 /* e.BindingType.Class */:
25210
+ case 3 /* e.BindingType.Style */:
25211
+ // Because this binding doesn't really target the ng-template, it must be a binding on an
25212
+ // inner node of a structural template. We can't skip it entirely, because we still need
25213
+ // it on the ng-template's consts (e.g. for the purposes of directive matching). However,
25214
+ // we should not generate an update instruction for it.
25215
+ return createExtractedAttributeOp(xref, BindingKind.Property, null, name, null, null, i18nMessage, securityContext);
25216
+ case 5 /* e.BindingType.TwoWay */:
25217
+ return createExtractedAttributeOp(xref, BindingKind.TwoWayProperty, null, name, null, null, i18nMessage, securityContext);
25218
+ }
24880
25219
  }
24881
25220
  if (!isTextBinding && (type === 1 /* e.BindingType.Attribute */ || type === 4 /* e.BindingType.Animation */)) {
24882
25221
  // Again, this binding doesn't really target the ng-template; it actually targets the element
@@ -24924,6 +25263,25 @@ function makeListenerHandlerOps(unit, handler, handlerSpan) {
24924
25263
  handlerOps.push(createStatementOp(new ReturnStatement(returnExpr, returnExpr.sourceSpan)));
24925
25264
  return handlerOps;
24926
25265
  }
25266
+ function makeTwoWayListenerHandlerOps(unit, handler, handlerSpan) {
25267
+ handler = astOf(handler);
25268
+ const handlerOps = new Array();
25269
+ if (handler instanceof Chain) {
25270
+ if (handler.expressions.length === 1) {
25271
+ handler = handler.expressions[0];
25272
+ }
25273
+ else {
25274
+ // This is validated during parsing already, but we do it here just in case.
25275
+ throw new Error('Expected two-way listener to have a single expression.');
25276
+ }
25277
+ }
25278
+ const handlerExpr = convertAst(handler, unit.job, handlerSpan);
25279
+ const eventReference = new LexicalReadExpr('$event');
25280
+ const twoWaySetExpr = new TwoWayBindingSetExpr(handlerExpr, eventReference);
25281
+ handlerOps.push(createStatementOp(new ExpressionStatement(twoWaySetExpr)));
25282
+ handlerOps.push(createStatementOp(new ReturnStatement(eventReference)));
25283
+ return handlerOps;
25284
+ }
24927
25285
  function astOf(ast) {
24928
25286
  return ast instanceof ASTWithSource ? ast.ast : ast;
24929
25287
  }
@@ -25153,7 +25511,7 @@ class BindingParser {
25153
25511
  for (const propName of Object.keys(properties)) {
25154
25512
  const expression = properties[propName];
25155
25513
  if (typeof expression === 'string') {
25156
- this.parsePropertyBinding(propName, expression, true, sourceSpan, sourceSpan.start.offset, undefined, [],
25514
+ this.parsePropertyBinding(propName, expression, true, false, sourceSpan, sourceSpan.start.offset, undefined, [],
25157
25515
  // Use the `sourceSpan` for `keySpan`. This isn't really accurate, but neither is the
25158
25516
  // sourceSpan, as it represents the sourceSpan of the host itself rather than the
25159
25517
  // source of the host binding (which doesn't exist in the template). Regardless,
@@ -25249,7 +25607,7 @@ class BindingParser {
25249
25607
  else if (binding.value) {
25250
25608
  const srcSpan = isIvyAst ? bindingSpan : sourceSpan;
25251
25609
  const valueSpan = moveParseSourceSpan(sourceSpan, binding.value.ast.sourceSpan);
25252
- this._parsePropertyAst(key, binding.value, srcSpan, keySpan, valueSpan, targetMatchableAttrs, targetProps);
25610
+ this._parsePropertyAst(key, binding.value, false, srcSpan, keySpan, valueSpan, targetMatchableAttrs, targetProps);
25253
25611
  }
25254
25612
  else {
25255
25613
  targetMatchableAttrs.push([key, '' /* value */]);
@@ -25302,7 +25660,7 @@ class BindingParser {
25302
25660
  targetProps.push(new ParsedProperty(name, this._exprParser.wrapLiteralPrimitive(value, '', absoluteOffset), ParsedPropertyType.LITERAL_ATTR, sourceSpan, keySpan, valueSpan));
25303
25661
  }
25304
25662
  }
25305
- parsePropertyBinding(name, expression, isHost, sourceSpan, absoluteOffset, valueSpan, targetMatchableAttrs, targetProps, keySpan) {
25663
+ parsePropertyBinding(name, expression, isHost, isPartOfAssignmentBinding, sourceSpan, absoluteOffset, valueSpan, targetMatchableAttrs, targetProps, keySpan) {
25306
25664
  if (name.length === 0) {
25307
25665
  this._reportError(`Property name is missing in binding`, sourceSpan);
25308
25666
  }
@@ -25325,20 +25683,20 @@ class BindingParser {
25325
25683
  this._parseAnimation(name, expression, sourceSpan, absoluteOffset, keySpan, valueSpan, targetMatchableAttrs, targetProps);
25326
25684
  }
25327
25685
  else {
25328
- this._parsePropertyAst(name, this.parseBinding(expression, isHost, valueSpan || sourceSpan, absoluteOffset), sourceSpan, keySpan, valueSpan, targetMatchableAttrs, targetProps);
25686
+ this._parsePropertyAst(name, this.parseBinding(expression, isHost, valueSpan || sourceSpan, absoluteOffset), isPartOfAssignmentBinding, sourceSpan, keySpan, valueSpan, targetMatchableAttrs, targetProps);
25329
25687
  }
25330
25688
  }
25331
25689
  parsePropertyInterpolation(name, value, sourceSpan, valueSpan, targetMatchableAttrs, targetProps, keySpan, interpolatedTokens) {
25332
25690
  const expr = this.parseInterpolation(value, valueSpan || sourceSpan, interpolatedTokens);
25333
25691
  if (expr) {
25334
- this._parsePropertyAst(name, expr, sourceSpan, keySpan, valueSpan, targetMatchableAttrs, targetProps);
25692
+ this._parsePropertyAst(name, expr, false, sourceSpan, keySpan, valueSpan, targetMatchableAttrs, targetProps);
25335
25693
  return true;
25336
25694
  }
25337
25695
  return false;
25338
25696
  }
25339
- _parsePropertyAst(name, ast, sourceSpan, keySpan, valueSpan, targetMatchableAttrs, targetProps) {
25697
+ _parsePropertyAst(name, ast, isPartOfAssignmentBinding, sourceSpan, keySpan, valueSpan, targetMatchableAttrs, targetProps) {
25340
25698
  targetMatchableAttrs.push([name, ast.source]);
25341
- targetProps.push(new ParsedProperty(name, ast, ParsedPropertyType.DEFAULT, sourceSpan, keySpan, valueSpan));
25699
+ targetProps.push(new ParsedProperty(name, ast, isPartOfAssignmentBinding ? ParsedPropertyType.TWO_WAY : ParsedPropertyType.DEFAULT, sourceSpan, keySpan, valueSpan));
25342
25700
  }
25343
25701
  _parseAnimation(name, expression, sourceSpan, absoluteOffset, keySpan, valueSpan, targetMatchableAttrs, targetProps) {
25344
25702
  if (name.length === 0) {
@@ -25408,7 +25766,8 @@ class BindingParser {
25408
25766
  const mappedPropName = this._schemaRegistry.getMappedPropName(boundProp.name);
25409
25767
  boundPropertyName = mapPropertyName ? mappedPropName : boundProp.name;
25410
25768
  securityContexts = calcPossibleSecurityContexts(this._schemaRegistry, elementSelector, mappedPropName, false);
25411
- bindingType = 0 /* BindingType.Property */;
25769
+ bindingType =
25770
+ boundProp.type === ParsedPropertyType.TWO_WAY ? 5 /* BindingType.TwoWay */ : 0 /* BindingType.Property */;
25412
25771
  if (!skipValidation) {
25413
25772
  this._validatePropertyOrAttributeName(mappedPropName, boundProp.sourceSpan, false);
25414
25773
  }
@@ -25425,7 +25784,7 @@ class BindingParser {
25425
25784
  if (keySpan !== undefined) {
25426
25785
  keySpan = moveParseSourceSpan(keySpan, new AbsoluteSourceSpan(keySpan.start.offset + 1, keySpan.end.offset));
25427
25786
  }
25428
- this._parseAnimationEvent(name, expression, isAssignmentEvent, sourceSpan, handlerSpan, targetEvents, keySpan);
25787
+ this._parseAnimationEvent(name, expression, sourceSpan, handlerSpan, targetEvents, keySpan);
25429
25788
  }
25430
25789
  else {
25431
25790
  this._parseRegularEvent(name, expression, isAssignmentEvent, sourceSpan, handlerSpan, targetMatchableAttrs, targetEvents, keySpan);
@@ -25435,11 +25794,11 @@ class BindingParser {
25435
25794
  const prop = this._schemaRegistry.getMappedPropName(propName);
25436
25795
  return calcPossibleSecurityContexts(this._schemaRegistry, selector, prop, isAttribute);
25437
25796
  }
25438
- _parseAnimationEvent(name, expression, isAssignmentEvent, sourceSpan, handlerSpan, targetEvents, keySpan) {
25797
+ _parseAnimationEvent(name, expression, sourceSpan, handlerSpan, targetEvents, keySpan) {
25439
25798
  const matches = splitAtPeriod(name, [name, '']);
25440
25799
  const eventName = matches[0];
25441
25800
  const phase = matches[1].toLowerCase();
25442
- const ast = this._parseAction(expression, isAssignmentEvent, handlerSpan);
25801
+ const ast = this._parseAction(expression, handlerSpan);
25443
25802
  targetEvents.push(new ParsedEvent(eventName, phase, 1 /* ParsedEventType.Animation */, ast, sourceSpan, handlerSpan, keySpan));
25444
25803
  if (eventName.length === 0) {
25445
25804
  this._reportError(`Animation event name is missing in binding`, sourceSpan);
@@ -25456,17 +25815,24 @@ class BindingParser {
25456
25815
  _parseRegularEvent(name, expression, isAssignmentEvent, sourceSpan, handlerSpan, targetMatchableAttrs, targetEvents, keySpan) {
25457
25816
  // long format: 'target: eventName'
25458
25817
  const [target, eventName] = splitAtColon(name, [null, name]);
25459
- const ast = this._parseAction(expression, isAssignmentEvent, handlerSpan);
25818
+ const prevErrorCount = this.errors.length;
25819
+ const ast = this._parseAction(expression, handlerSpan);
25820
+ const isValid = this.errors.length === prevErrorCount;
25460
25821
  targetMatchableAttrs.push([name, ast.source]);
25461
- targetEvents.push(new ParsedEvent(eventName, target, 0 /* ParsedEventType.Regular */, ast, sourceSpan, handlerSpan, keySpan));
25822
+ // Don't try to validate assignment events if there were other
25823
+ // parsing errors to avoid adding more noise to the error logs.
25824
+ if (isAssignmentEvent && isValid && !this._isAllowedAssignmentEvent(ast)) {
25825
+ this._reportError('Unsupported expression in a two-way binding', sourceSpan);
25826
+ }
25827
+ targetEvents.push(new ParsedEvent(eventName, target, isAssignmentEvent ? 2 /* ParsedEventType.TwoWay */ : 0 /* ParsedEventType.Regular */, ast, sourceSpan, handlerSpan, keySpan));
25462
25828
  // Don't detect directives for event names for now,
25463
25829
  // so don't add the event name to the matchableAttrs
25464
25830
  }
25465
- _parseAction(value, isAssignmentEvent, sourceSpan) {
25831
+ _parseAction(value, sourceSpan) {
25466
25832
  const sourceInfo = (sourceSpan && sourceSpan.start || '(unknown').toString();
25467
25833
  const absoluteOffset = (sourceSpan && sourceSpan.start) ? sourceSpan.start.offset : 0;
25468
25834
  try {
25469
- const ast = this._exprParser.parseAction(value, isAssignmentEvent, sourceInfo, absoluteOffset, this._interpolationConfig);
25835
+ const ast = this._exprParser.parseAction(value, sourceInfo, absoluteOffset, this._interpolationConfig);
25470
25836
  if (ast) {
25471
25837
  this._reportExpressionParserErrors(ast.errors, sourceSpan);
25472
25838
  }
@@ -25501,6 +25867,26 @@ class BindingParser {
25501
25867
  this._reportError(report.msg, sourceSpan, ParseErrorLevel.ERROR);
25502
25868
  }
25503
25869
  }
25870
+ /**
25871
+ * Returns whether a parsed AST is allowed to be used within the event side of a two-way binding.
25872
+ * @param ast Parsed AST to be checked.
25873
+ */
25874
+ _isAllowedAssignmentEvent(ast) {
25875
+ if (ast instanceof ASTWithSource) {
25876
+ return this._isAllowedAssignmentEvent(ast.ast);
25877
+ }
25878
+ if (ast instanceof NonNullAssert) {
25879
+ return this._isAllowedAssignmentEvent(ast.expression);
25880
+ }
25881
+ if (ast instanceof PropertyRead || ast instanceof KeyedRead) {
25882
+ return true;
25883
+ }
25884
+ if (ast instanceof Binary) {
25885
+ return (ast.operation === '&&' || ast.operation === '||' || ast.operation === '??') &&
25886
+ (ast.right instanceof PropertyRead || ast.right instanceof KeyedRead);
25887
+ }
25888
+ return ast instanceof Conditional || ast instanceof PrefixNot;
25889
+ }
25504
25890
  }
25505
25891
  class PipeCollector extends RecursiveAstVisitor {
25506
25892
  constructor() {
@@ -25633,11 +26019,16 @@ const FOR_LOOP_EXPRESSION_PATTERN = /^\s*([0-9A-Za-z_$]*)\s+of\s+([\S\s]*)/;
25633
26019
  /** Pattern for the tracking expression in a for loop block. */
25634
26020
  const FOR_LOOP_TRACK_PATTERN = /^track\s+([\S\s]*)/;
25635
26021
  /** Pattern for the `as` expression in a conditional block. */
25636
- const CONDITIONAL_ALIAS_PATTERN = /^as\s+(.*)/;
26022
+ const CONDITIONAL_ALIAS_PATTERN = /^(as\s)+(.*)/;
25637
26023
  /** Pattern used to identify an `else if` block. */
25638
26024
  const ELSE_IF_PATTERN = /^else[^\S\r\n]+if/;
25639
26025
  /** Pattern used to identify a `let` parameter. */
25640
26026
  const FOR_LOOP_LET_PATTERN = /^let\s+([\S\s]*)/;
26027
+ /**
26028
+ * Pattern to group a string into leading whitespace, non whitespace, and trailing whitespace.
26029
+ * Useful for getting the variable name span when a span can contain leading and trailing space.
26030
+ */
26031
+ const CHARACTERS_IN_SURROUNDING_WHITESPACE_PATTERN = /(\s*)(\S+)(\s*)/;
25641
26032
  /** Names of variables that are allowed to be used in the `let` expression of a `for` loop. */
25642
26033
  const ALLOWED_FOR_LOOP_LET_VARIABLES = new Set(['$index', '$first', '$last', '$even', '$odd', '$count']);
25643
26034
  /**
@@ -25777,8 +26168,13 @@ function parseForLoopParameters(block, errors, bindingParser) {
25777
26168
  return null;
25778
26169
  }
25779
26170
  const [, itemName, rawExpression] = match;
26171
+ // `expressionParam.expression` contains the variable declaration and the expression of the
26172
+ // for...of statement, i.e. 'user of users' The variable of a ForOfStatement is _only_ the "const
26173
+ // user" part and does not include "of x".
26174
+ const variableName = expressionParam.expression.split(' ')[0];
26175
+ const variableSpan = new ParseSourceSpan(expressionParam.sourceSpan.start, expressionParam.sourceSpan.start.moveBy(variableName.length));
25780
26176
  const result = {
25781
- itemName: new Variable(itemName, '$implicit', expressionParam.sourceSpan, expressionParam.sourceSpan),
26177
+ itemName: new Variable(itemName, '$implicit', variableSpan, variableSpan),
25782
26178
  trackBy: null,
25783
26179
  expression: parseBlockParameterToBinding(expressionParam, bindingParser, rawExpression),
25784
26180
  context: {},
@@ -25786,7 +26182,8 @@ function parseForLoopParameters(block, errors, bindingParser) {
25786
26182
  for (const param of secondaryParams) {
25787
26183
  const letMatch = param.expression.match(FOR_LOOP_LET_PATTERN);
25788
26184
  if (letMatch !== null) {
25789
- parseLetParameter(param.sourceSpan, letMatch[1], param.sourceSpan, result.context, errors);
26185
+ const variablesSpan = new ParseSourceSpan(param.sourceSpan.start.moveBy(letMatch[0].length - letMatch[1].length), param.sourceSpan.end);
26186
+ parseLetParameter(param.sourceSpan, letMatch[1], variablesSpan, result.context, errors);
25790
26187
  continue;
25791
26188
  }
25792
26189
  const trackMatch = param.expression.match(FOR_LOOP_TRACK_PATTERN);
@@ -25817,6 +26214,7 @@ function parseForLoopParameters(block, errors, bindingParser) {
25817
26214
  /** Parses the `let` parameter of a `for` loop block. */
25818
26215
  function parseLetParameter(sourceSpan, expression, span, context, errors) {
25819
26216
  const parts = expression.split(',');
26217
+ let startSpan = span.start;
25820
26218
  for (const part of parts) {
25821
26219
  const expressionParts = part.split('=');
25822
26220
  const name = expressionParts.length === 2 ? expressionParts[0].trim() : '';
@@ -25831,8 +26229,26 @@ function parseLetParameter(sourceSpan, expression, span, context, errors) {
25831
26229
  errors.push(new ParseError(sourceSpan, `Duplicate "let" parameter variable "${variableName}"`));
25832
26230
  }
25833
26231
  else {
25834
- context[variableName] = new Variable(name, variableName, span, span);
26232
+ const [, keyLeadingWhitespace, keyName] = expressionParts[0].match(CHARACTERS_IN_SURROUNDING_WHITESPACE_PATTERN) ?? [];
26233
+ const keySpan = keyLeadingWhitespace !== undefined && expressionParts.length === 2 ?
26234
+ new ParseSourceSpan(
26235
+ /* strip leading spaces */
26236
+ startSpan.moveBy(keyLeadingWhitespace.length),
26237
+ /* advance to end of the variable name */
26238
+ startSpan.moveBy(keyLeadingWhitespace.length + keyName.length)) :
26239
+ span;
26240
+ let valueSpan = undefined;
26241
+ if (expressionParts.length === 2) {
26242
+ const [, valueLeadingWhitespace, implicit] = expressionParts[1].match(CHARACTERS_IN_SURROUNDING_WHITESPACE_PATTERN) ?? [];
26243
+ valueSpan = valueLeadingWhitespace !== undefined ?
26244
+ new ParseSourceSpan(startSpan.moveBy(expressionParts[0].length + 1 + valueLeadingWhitespace.length), startSpan.moveBy(expressionParts[0].length + 1 + valueLeadingWhitespace.length +
26245
+ implicit.length)) :
26246
+ undefined;
26247
+ }
26248
+ const sourceSpan = new ParseSourceSpan(keySpan.start, valueSpan?.end ?? keySpan.end);
26249
+ context[variableName] = new Variable(name, variableName, sourceSpan, keySpan, valueSpan);
25835
26250
  }
26251
+ startSpan = startSpan.moveBy(part.length + 1 /* add 1 to move past the comma */);
25836
26252
  }
25837
26253
  }
25838
26254
  /**
@@ -25944,8 +26360,10 @@ function parseConditionalBlockParameters(block, errors, bindingParser) {
25944
26360
  errors.push(new ParseError(param.sourceSpan, 'Conditional can only have one "as" expression'));
25945
26361
  }
25946
26362
  else {
25947
- const name = aliasMatch[1].trim();
25948
- expressionAlias = new Variable(name, name, param.sourceSpan, param.sourceSpan);
26363
+ const name = aliasMatch[2].trim();
26364
+ const variableStart = param.sourceSpan.start.moveBy(aliasMatch[1].length);
26365
+ const variableSpan = new ParseSourceSpan(variableStart, variableStart.moveBy(name.length));
26366
+ expressionAlias = new Variable(name, name, variableSpan, variableSpan);
25949
26367
  }
25950
26368
  }
25951
26369
  return { expression, expressionAlias };
@@ -26806,7 +27224,7 @@ class HtmlAstToIvyAst {
26806
27224
  if (bindParts[KW_BIND_IDX] != null) {
26807
27225
  const identifier = bindParts[IDENT_KW_IDX];
26808
27226
  const keySpan = createKeySpan(srcSpan, bindParts[KW_BIND_IDX], identifier);
26809
- this.bindingParser.parsePropertyBinding(identifier, value, false, srcSpan, absoluteOffset, attribute.valueSpan, matchableAttributes, parsedProperties, keySpan);
27227
+ this.bindingParser.parsePropertyBinding(identifier, value, false, false, srcSpan, absoluteOffset, attribute.valueSpan, matchableAttributes, parsedProperties, keySpan);
26810
27228
  }
26811
27229
  else if (bindParts[KW_LET_IDX]) {
26812
27230
  if (isTemplateElement) {
@@ -26833,7 +27251,7 @@ class HtmlAstToIvyAst {
26833
27251
  else if (bindParts[KW_BINDON_IDX]) {
26834
27252
  const identifier = bindParts[IDENT_KW_IDX];
26835
27253
  const keySpan = createKeySpan(srcSpan, bindParts[KW_BINDON_IDX], identifier);
26836
- this.bindingParser.parsePropertyBinding(identifier, value, false, srcSpan, absoluteOffset, attribute.valueSpan, matchableAttributes, parsedProperties, keySpan);
27254
+ this.bindingParser.parsePropertyBinding(identifier, value, false, true, srcSpan, absoluteOffset, attribute.valueSpan, matchableAttributes, parsedProperties, keySpan);
26837
27255
  this.parseAssignmentEvent(identifier, value, srcSpan, attribute.valueSpan, matchableAttributes, boundEvents, keySpan);
26838
27256
  }
26839
27257
  else if (bindParts[KW_AT_IDX]) {
@@ -26863,11 +27281,11 @@ class HtmlAstToIvyAst {
26863
27281
  const identifier = name.substring(delims.start.length, name.length - delims.end.length);
26864
27282
  const keySpan = createKeySpan(srcSpan, delims.start, identifier);
26865
27283
  if (delims.start === BINDING_DELIMS.BANANA_BOX.start) {
26866
- this.bindingParser.parsePropertyBinding(identifier, value, false, srcSpan, absoluteOffset, attribute.valueSpan, matchableAttributes, parsedProperties, keySpan);
27284
+ this.bindingParser.parsePropertyBinding(identifier, value, false, true, srcSpan, absoluteOffset, attribute.valueSpan, matchableAttributes, parsedProperties, keySpan);
26867
27285
  this.parseAssignmentEvent(identifier, value, srcSpan, attribute.valueSpan, matchableAttributes, boundEvents, keySpan);
26868
27286
  }
26869
27287
  else if (delims.start === BINDING_DELIMS.PROPERTY.start) {
26870
- this.bindingParser.parsePropertyBinding(identifier, value, false, srcSpan, absoluteOffset, attribute.valueSpan, matchableAttributes, parsedProperties, keySpan);
27288
+ this.bindingParser.parsePropertyBinding(identifier, value, false, false, srcSpan, absoluteOffset, attribute.valueSpan, matchableAttributes, parsedProperties, keySpan);
26871
27289
  }
26872
27290
  else {
26873
27291
  const events = [];
@@ -26909,7 +27327,7 @@ class HtmlAstToIvyAst {
26909
27327
  }
26910
27328
  parseAssignmentEvent(name, expression, sourceSpan, valueSpan, targetMatchableAttrs, boundEvents, keySpan) {
26911
27329
  const events = [];
26912
- this.bindingParser.parseEvent(`${name}Change`, `${expression} =$event`, /* isAssignmentEvent */ true, sourceSpan, valueSpan || sourceSpan, targetMatchableAttrs, events, keySpan);
27330
+ this.bindingParser.parseEvent(`${name}Change`, expression, /* isAssignmentEvent */ true, sourceSpan, valueSpan || sourceSpan, targetMatchableAttrs, events, keySpan);
26913
27331
  addEvents(events, boundEvents);
26914
27332
  }
26915
27333
  reportError(message, sourceSpan, level = ParseErrorLevel.ERROR) {
@@ -27743,7 +28161,9 @@ function prepareEventListenerParameters(eventAst, handlerName = null, scope = nu
27743
28161
  const implicitReceiverExpr = (scope === null || scope.bindingLevel === 0) ?
27744
28162
  variable(CONTEXT_NAME) :
27745
28163
  scope.getOrCreateSharedContextVar(0);
27746
- const bindingStatements = convertActionBinding(scope, implicitReceiverExpr, handler, 'b', eventAst.handlerSpan, implicitReceiverAccesses, EVENT_BINDING_SCOPE_GLOBALS);
28164
+ const bindingStatements = eventAst.type === 2 /* ParsedEventType.TwoWay */ ?
28165
+ convertAssignmentActionBinding(scope, implicitReceiverExpr, handler, 'b', eventAst.handlerSpan, implicitReceiverAccesses, EVENT_BINDING_SCOPE_GLOBALS) :
28166
+ convertActionBinding(scope, implicitReceiverExpr, handler, 'b', eventAst.handlerSpan, implicitReceiverAccesses, EVENT_BINDING_SCOPE_GLOBALS);
27747
28167
  const statements = [];
27748
28168
  const variableDeclarations = scope?.variableDeclarations();
27749
28169
  const restoreViewStatement = scope?.restoreViewStatement();
@@ -28265,7 +28685,8 @@ class TemplateDefinitionBuilder {
28265
28685
  element.inputs.forEach(input => {
28266
28686
  const stylingInputWasSet = stylingBuilder.registerBoundInput(input);
28267
28687
  if (!stylingInputWasSet) {
28268
- if (input.type === 0 /* BindingType.Property */ && input.i18n) {
28688
+ if ((input.type === 0 /* BindingType.Property */ || input.type === 5 /* BindingType.TwoWay */) &&
28689
+ input.i18n) {
28269
28690
  boundI18nAttrs.push(input);
28270
28691
  }
28271
28692
  else {
@@ -28310,7 +28731,7 @@ class TemplateDefinitionBuilder {
28310
28731
  // Generate Listeners (outputs)
28311
28732
  if (element.outputs.length > 0) {
28312
28733
  for (const outputAst of element.outputs) {
28313
- this.creationInstruction(outputAst.sourceSpan, Identifiers.listener, this.prepareListenerParameter(element.name, outputAst, elementIndex));
28734
+ this.creationInstruction(outputAst.sourceSpan, outputAst.type === 2 /* ParsedEventType.TwoWay */ ? Identifiers.twoWayListener : Identifiers.listener, this.prepareListenerParameter(element.name, outputAst, elementIndex));
28314
28735
  }
28315
28736
  }
28316
28737
  // Note: it's important to keep i18n/i18nStart instructions after i18nAttributes and
@@ -28353,6 +28774,7 @@ class TemplateDefinitionBuilder {
28353
28774
  this.allocateBindingSlots(value);
28354
28775
  propertyBindings.push({
28355
28776
  span: input.sourceSpan,
28777
+ reference: Identifiers.property,
28356
28778
  paramsOrFn: getBindingFunctionParams(() => hasValue ? this.convertPropertyBinding(value) : emptyValueBindInstruction, prepareSyntheticPropertyName(input.name))
28357
28779
  });
28358
28780
  }
@@ -28391,7 +28813,9 @@ class TemplateDefinitionBuilder {
28391
28813
  }
28392
28814
  }
28393
28815
  this.allocateBindingSlots(value);
28394
- if (inputType === 0 /* BindingType.Property */) {
28816
+ // Note: we don't separate two-way property bindings and regular ones,
28817
+ // because their assignment order needs to be maintained.
28818
+ if (inputType === 0 /* BindingType.Property */ || inputType === 5 /* BindingType.TwoWay */) {
28395
28819
  if (value instanceof Interpolation$1) {
28396
28820
  // prop="{{value}}" and friends
28397
28821
  this.interpolatedUpdateInstruction(getPropertyInterpolationExpression(value), elementIndex, attrName, input, value, params);
@@ -28401,6 +28825,7 @@ class TemplateDefinitionBuilder {
28401
28825
  // Collect all the properties so that we can chain into a single function at the end.
28402
28826
  propertyBindings.push({
28403
28827
  span: input.sourceSpan,
28828
+ reference: inputType === 5 /* BindingType.TwoWay */ ? Identifiers.twoWayProperty : Identifiers.property,
28404
28829
  paramsOrFn: getBindingFunctionParams(() => this.convertPropertyBinding(value), attrName, params)
28405
28830
  });
28406
28831
  }
@@ -28433,7 +28858,7 @@ class TemplateDefinitionBuilder {
28433
28858
  }
28434
28859
  });
28435
28860
  for (const propertyBinding of propertyBindings) {
28436
- this.updateInstructionWithAdvance(elementIndex, propertyBinding.span, Identifiers.property, propertyBinding.paramsOrFn);
28861
+ this.updateInstructionWithAdvance(elementIndex, propertyBinding.span, propertyBinding.reference, propertyBinding.paramsOrFn);
28437
28862
  }
28438
28863
  for (const attributeBinding of attributeBindings) {
28439
28864
  this.updateInstructionWithAdvance(elementIndex, attributeBinding.span, Identifiers.attribute, attributeBinding.paramsOrFn);
@@ -28466,7 +28891,9 @@ class TemplateDefinitionBuilder {
28466
28891
  }
28467
28892
  }
28468
28893
  const contextName = `${this.contextName}${contextNameSuffix}_${index}`;
28469
- const name = `${contextName}_Template`;
28894
+ // Note: For the unique name, we don't include an unique suffix, unless really needed.
28895
+ // This keeps the generated output more clean as most of the time, we don't expect conflicts.
28896
+ const name = this.constantPool.uniqueName(`${contextName}_Template`, /** alwaysIncludeSuffix */ false);
28470
28897
  // Create the template function
28471
28898
  const visitor = new TemplateDefinitionBuilder(this.constantPool, this._bindingScope, this.level + 1, contextName, this.i18n, index, name, this._namespace, this.fileBasedI18nSuffix, this.i18nUseExternalIds, this.deferBlocks, this.elementLocations, this.allDeferrableDepsFn, this._constants);
28472
28899
  // Nested templates must not be visited until after their parent templates have completed
@@ -28529,7 +28956,7 @@ class TemplateDefinitionBuilder {
28529
28956
  }
28530
28957
  // Generate listeners for directive output
28531
28958
  for (const outputAst of template.outputs) {
28532
- this.creationInstruction(outputAst.sourceSpan, Identifiers.listener, this.prepareListenerParameter('ng_template', outputAst, templateIndex));
28959
+ this.creationInstruction(outputAst.sourceSpan, outputAst.type === 2 /* ParsedEventType.TwoWay */ ? Identifiers.twoWayListener : Identifiers.listener, this.prepareListenerParameter('ng_template', outputAst, templateIndex));
28533
28960
  }
28534
28961
  }
28535
28962
  }
@@ -28783,7 +29210,9 @@ class TemplateDefinitionBuilder {
28783
29210
  for (const deferredDep of metadata.deps) {
28784
29211
  if (deferredDep.isDeferrable) {
28785
29212
  // Callback function, e.g. `m () => m.MyCmp;`.
28786
- const innerFn = arrowFn([new FnParam('m', DYNAMIC_TYPE)], variable('m').prop(deferredDep.symbolName));
29213
+ const innerFn = arrowFn([new FnParam('m', DYNAMIC_TYPE)],
29214
+ // Default imports are always accessed through the `default` property.
29215
+ variable('m').prop(deferredDep.isDefaultImport ? 'default' : deferredDep.symbolName));
28787
29216
  // Dynamic import, e.g. `import('./a').then(...)`.
28788
29217
  const importExpr = (new DynamicImportExpr(deferredDep.importPath)).prop('then').callFn([innerFn]);
28789
29218
  dependencyExp.push(importExpr);
@@ -30290,9 +30719,9 @@ function compileDirectiveFromMetadata(meta, constantPool, bindingParser) {
30290
30719
  function createDeferredDepsFunction(constantPool, name, deps) {
30291
30720
  // This defer block has deps for which we need to generate dynamic imports.
30292
30721
  const dependencyExp = [];
30293
- for (const [symbolName, importPath] of deps) {
30722
+ for (const [symbolName, { importPath, isDefaultImport }] of deps) {
30294
30723
  // Callback function, e.g. `m () => m.MyCmp;`.
30295
- const innerFn = arrowFn([new FnParam('m', DYNAMIC_TYPE)], variable('m').prop(symbolName));
30724
+ const innerFn = arrowFn([new FnParam('m', DYNAMIC_TYPE)], variable('m').prop(isDefaultImport ? 'default' : symbolName));
30296
30725
  // Dynamic import, e.g. `import('./a').then(...)`.
30297
30726
  const importExpr = (new DynamicImportExpr(importPath)).prop('then').callFn([innerFn]);
30298
30727
  dependencyExp.push(importExpr);
@@ -30321,16 +30750,16 @@ function compileComponentFromMetadata(meta, constantPool, bindingParser) {
30321
30750
  // e.g. `template: function MyComponent_Template(_ctx, _cm) {...}`
30322
30751
  const templateTypeName = meta.name;
30323
30752
  const templateName = templateTypeName ? `${templateTypeName}_Template` : null;
30753
+ let allDeferrableDepsFn = null;
30754
+ if (meta.deferBlocks.size > 0 && meta.deferrableTypes.size > 0 &&
30755
+ meta.deferBlockDepsEmitMode === 1 /* DeferBlockDepsEmitMode.PerComponent */) {
30756
+ const fnName = `${templateTypeName}_DeferFn`;
30757
+ allDeferrableDepsFn = createDeferredDepsFunction(constantPool, fnName, meta.deferrableTypes);
30758
+ }
30324
30759
  // Template compilation is currently conditional as we're in the process of rewriting it.
30325
- if (!USE_TEMPLATE_PIPELINE) {
30760
+ if (!USE_TEMPLATE_PIPELINE && !meta.useTemplatePipeline) {
30326
30761
  // This is the main path currently used in compilation, which compiles the template with the
30327
30762
  // legacy `TemplateDefinitionBuilder`.
30328
- let allDeferrableDepsFn = null;
30329
- if (meta.deferBlocks.size > 0 && meta.deferrableTypes.size > 0 &&
30330
- meta.deferBlockDepsEmitMode === 1 /* DeferBlockDepsEmitMode.PerComponent */) {
30331
- const fnName = `${templateTypeName}_DeferFn`;
30332
- allDeferrableDepsFn = createDeferredDepsFunction(constantPool, fnName, meta.deferrableTypes);
30333
- }
30334
30763
  const template = meta.template;
30335
30764
  const templateBuilder = new TemplateDefinitionBuilder(constantPool, BindingScope.createRootScope(), 0, templateTypeName, null, null, templateName, Identifiers.namespaceHTML, meta.relativeContextFilePath, meta.i18nUseExternalIds, meta.deferBlocks, new Map(), allDeferrableDepsFn);
30336
30765
  const templateFunctionExpression = templateBuilder.buildTemplateFunction(template.nodes, []);
@@ -30367,7 +30796,7 @@ function compileComponentFromMetadata(meta, constantPool, bindingParser) {
30367
30796
  else {
30368
30797
  // This path compiles the template using the prototype template pipeline. First the template is
30369
30798
  // ingested into IR:
30370
- const tpl = ingestComponent(meta.name, meta.template.nodes, constantPool, meta.relativeContextFilePath, meta.i18nUseExternalIds, meta.deferBlocks);
30799
+ const tpl = ingestComponent(meta.name, meta.template.nodes, constantPool, meta.relativeContextFilePath, meta.i18nUseExternalIds, meta.deferBlocks, allDeferrableDepsFn);
30371
30800
  // Then the IR is transformed to prepare it for cod egeneration.
30372
30801
  transform(tpl, CompilationJobKind.Tmpl);
30373
30802
  // Finally we emit the template function:
@@ -30562,7 +30991,7 @@ function createHostBindingsFunction(hostBindingsMetadata, typeSourceSpan, bindin
30562
30991
  const bindings = bindingParser.createBoundHostProperties(hostBindingsMetadata.properties, typeSourceSpan);
30563
30992
  // Calculate host event bindings
30564
30993
  const eventBindings = bindingParser.createDirectiveHostEventAsts(hostBindingsMetadata.listeners, typeSourceSpan);
30565
- if (USE_TEMPLATE_PIPELINE) {
30994
+ if (USE_TEMPLATE_PIPELINE || hostBindingsMetadata.useTemplatePipeline) {
30566
30995
  // The parser for host bindings treats class and style attributes specially -- they are
30567
30996
  // extracted into these separate fields. This is not the case for templates, so the compiler can
30568
30997
  // actually already handle these special attributes internally. Therefore, we just drop them
@@ -31746,6 +32175,7 @@ function extractScopedNodeEntities(rootScope) {
31746
32175
  class ResourceLoader {
31747
32176
  }
31748
32177
 
32178
+ const SHOULD_USE_TEMPLATE_PIPELINE_FOR_JIT = false;
31749
32179
  class CompilerFacadeImpl {
31750
32180
  constructor(jitEvaluator = new JitEvaluator()) {
31751
32181
  this.jitEvaluator = jitEvaluator;
@@ -31879,6 +32309,7 @@ class CompilerFacadeImpl {
31879
32309
  null,
31880
32310
  relativeContextFilePath: '',
31881
32311
  i18nUseExternalIds: true,
32312
+ useTemplatePipeline: SHOULD_USE_TEMPLATE_PIPELINE_FOR_JIT,
31882
32313
  };
31883
32314
  const jitExpressionSourceMap = `ng:///${facade.name}.js`;
31884
32315
  return this.compileComponentFromMeta(angularCoreEnv, jitExpressionSourceMap, meta);
@@ -32001,7 +32432,10 @@ function convertDirectiveFacadeToMetadata(facade) {
32001
32432
  typeSourceSpan: facade.typeSourceSpan,
32002
32433
  type: wrapReference(facade.type),
32003
32434
  deps: null,
32004
- host: extractHostBindings(facade.propMetadata, facade.typeSourceSpan, facade.host),
32435
+ host: {
32436
+ ...extractHostBindings(facade.propMetadata, facade.typeSourceSpan, facade.host),
32437
+ useTemplatePipeline: SHOULD_USE_TEMPLATE_PIPELINE_FOR_JIT,
32438
+ },
32005
32439
  inputs: { ...inputsFromMetadata, ...inputsFromType },
32006
32440
  outputs: { ...outputsFromMetadata, ...outputsFromType },
32007
32441
  queries: facade.queries.map(convertToR3QueryMetadata),
@@ -32044,6 +32478,7 @@ function convertHostDeclarationToMetadata(host = {}) {
32044
32478
  classAttr: host.classAttribute,
32045
32479
  styleAttr: host.styleAttribute,
32046
32480
  },
32481
+ useTemplatePipeline: SHOULD_USE_TEMPLATE_PIPELINE_FOR_JIT,
32047
32482
  };
32048
32483
  }
32049
32484
  function convertHostDirectivesToMetadata(metadata) {
@@ -32116,6 +32551,7 @@ function convertDeclareComponentFacadeToMetadata(decl, typeSourceSpan, sourceMap
32116
32551
  declarationListEmitMode: 2 /* DeclarationListEmitMode.ClosureResolved */,
32117
32552
  relativeContextFilePath: '',
32118
32553
  i18nUseExternalIds: true,
32554
+ useTemplatePipeline: SHOULD_USE_TEMPLATE_PIPELINE_FOR_JIT,
32119
32555
  };
32120
32556
  }
32121
32557
  function convertDeclarationFacadeToMetadata(declaration) {
@@ -32397,7 +32833,7 @@ function publishFacade(global) {
32397
32833
  * @description
32398
32834
  * Entry point for all public APIs of the compiler package.
32399
32835
  */
32400
- const VERSION = new Version('17.2.0-next.0');
32836
+ const VERSION = new Version('17.2.0-rc.0');
32401
32837
 
32402
32838
  class CompilerConfig {
32403
32839
  constructor({ defaultEncapsulation = ViewEncapsulation.Emulated, preserveWhitespaces, strictInjectionParameters } = {}) {
@@ -33898,9 +34334,11 @@ function compileComponentClassMetadata(metadata, deferrableTypes) {
33898
34334
  }
33899
34335
  const dynamicImports = [];
33900
34336
  const importedSymbols = [];
33901
- for (const [symbolName, importPath] of deferrableTypes) {
34337
+ for (const [symbolName, { importPath, isDefaultImport }] of deferrableTypes) {
33902
34338
  // e.g. `(m) => m.CmpA`
33903
- const innerFn = arrowFn([new FnParam('m', DYNAMIC_TYPE)], variable('m').prop(symbolName));
34339
+ const innerFn =
34340
+ // Default imports are always accessed through the `default` property.
34341
+ arrowFn([new FnParam('m', DYNAMIC_TYPE)], variable('m').prop(isDefaultImport ? 'default' : symbolName));
33904
34342
  // e.g. `import('./cmp-a').then(...)`
33905
34343
  const importExpr = (new DynamicImportExpr(importPath)).prop('then').callFn([innerFn]);
33906
34344
  dynamicImports.push(importExpr);
@@ -33963,7 +34401,7 @@ const MINIMUM_PARTIAL_LINKER_VERSION$5 = '12.0.0';
33963
34401
  function compileDeclareClassMetadata(metadata) {
33964
34402
  const definitionMap = new DefinitionMap();
33965
34403
  definitionMap.set('minVersion', literal(MINIMUM_PARTIAL_LINKER_VERSION$5));
33966
- definitionMap.set('version', literal('17.2.0-next.0'));
34404
+ definitionMap.set('version', literal('17.2.0-rc.0'));
33967
34405
  definitionMap.set('ngImport', importExpr(Identifiers.core));
33968
34406
  definitionMap.set('type', metadata.type);
33969
34407
  definitionMap.set('decorators', metadata.decorators);
@@ -34059,7 +34497,7 @@ function createDirectiveDefinitionMap(meta) {
34059
34497
  const definitionMap = new DefinitionMap();
34060
34498
  const minVersion = getMinimumVersionForPartialOutput(meta);
34061
34499
  definitionMap.set('minVersion', literal(minVersion));
34062
- definitionMap.set('version', literal('17.2.0-next.0'));
34500
+ definitionMap.set('version', literal('17.2.0-rc.0'));
34063
34501
  // e.g. `type: MyDirective`
34064
34502
  definitionMap.set('type', meta.type.value);
34065
34503
  if (meta.isStandalone) {
@@ -34451,7 +34889,7 @@ const MINIMUM_PARTIAL_LINKER_VERSION$4 = '12.0.0';
34451
34889
  function compileDeclareFactoryFunction(meta) {
34452
34890
  const definitionMap = new DefinitionMap();
34453
34891
  definitionMap.set('minVersion', literal(MINIMUM_PARTIAL_LINKER_VERSION$4));
34454
- definitionMap.set('version', literal('17.2.0-next.0'));
34892
+ definitionMap.set('version', literal('17.2.0-rc.0'));
34455
34893
  definitionMap.set('ngImport', importExpr(Identifiers.core));
34456
34894
  definitionMap.set('type', meta.type.value);
34457
34895
  definitionMap.set('deps', compileDependencies(meta.deps));
@@ -34486,7 +34924,7 @@ function compileDeclareInjectableFromMetadata(meta) {
34486
34924
  function createInjectableDefinitionMap(meta) {
34487
34925
  const definitionMap = new DefinitionMap();
34488
34926
  definitionMap.set('minVersion', literal(MINIMUM_PARTIAL_LINKER_VERSION$3));
34489
- definitionMap.set('version', literal('17.2.0-next.0'));
34927
+ definitionMap.set('version', literal('17.2.0-rc.0'));
34490
34928
  definitionMap.set('ngImport', importExpr(Identifiers.core));
34491
34929
  definitionMap.set('type', meta.type.value);
34492
34930
  // Only generate providedIn property if it has a non-null value
@@ -34537,7 +34975,7 @@ function compileDeclareInjectorFromMetadata(meta) {
34537
34975
  function createInjectorDefinitionMap(meta) {
34538
34976
  const definitionMap = new DefinitionMap();
34539
34977
  definitionMap.set('minVersion', literal(MINIMUM_PARTIAL_LINKER_VERSION$2));
34540
- definitionMap.set('version', literal('17.2.0-next.0'));
34978
+ definitionMap.set('version', literal('17.2.0-rc.0'));
34541
34979
  definitionMap.set('ngImport', importExpr(Identifiers.core));
34542
34980
  definitionMap.set('type', meta.type.value);
34543
34981
  definitionMap.set('providers', meta.providers);
@@ -34570,7 +35008,7 @@ function createNgModuleDefinitionMap(meta) {
34570
35008
  throw new Error('Invalid path! Local compilation mode should not get into the partial compilation path');
34571
35009
  }
34572
35010
  definitionMap.set('minVersion', literal(MINIMUM_PARTIAL_LINKER_VERSION$1));
34573
- definitionMap.set('version', literal('17.2.0-next.0'));
35011
+ definitionMap.set('version', literal('17.2.0-rc.0'));
34574
35012
  definitionMap.set('ngImport', importExpr(Identifiers.core));
34575
35013
  definitionMap.set('type', meta.type.value);
34576
35014
  // We only generate the keys in the metadata if the arrays contain values.
@@ -34621,7 +35059,7 @@ function compileDeclarePipeFromMetadata(meta) {
34621
35059
  function createPipeDefinitionMap(meta) {
34622
35060
  const definitionMap = new DefinitionMap();
34623
35061
  definitionMap.set('minVersion', literal(MINIMUM_PARTIAL_LINKER_VERSION));
34624
- definitionMap.set('version', literal('17.2.0-next.0'));
35062
+ definitionMap.set('version', literal('17.2.0-rc.0'));
34625
35063
  definitionMap.set('ngImport', importExpr(Identifiers.core));
34626
35064
  // e.g. `type: MyPipe`
34627
35065
  definitionMap.set('type', meta.type.value);
@@ -34654,5 +35092,5 @@ publishFacade(_global);
34654
35092
 
34655
35093
  // This file is not used to build this module. It is only used during editing
34656
35094
 
34657
- export { AST, ASTWithName, ASTWithSource, AbsoluteSourceSpan, ArrayType, ArrowFunctionExpr, AstMemoryEfficientTransformer, AstTransformer, Attribute, Binary, BinaryOperator, BinaryOperatorExpr, BindingPipe, Block, BlockParameter, BoundElementProperty, BuiltinType, BuiltinTypeName, CUSTOM_ELEMENTS_SCHEMA, Call, Chain, ChangeDetectionStrategy, CommaExpr, Comment, CompilerConfig, Conditional, ConditionalExpr, ConstantPool, CssSelector, DEFAULT_INTERPOLATION_CONFIG, DYNAMIC_TYPE, DeclareFunctionStmt, DeclareVarStmt, DomElementSchemaRegistry, DynamicImportExpr, EOF, Element, ElementSchemaRegistry, EmitterVisitorContext, EmptyExpr$1 as EmptyExpr, Expansion, ExpansionCase, Expression, ExpressionBinding, ExpressionStatement, ExpressionType, ExternalExpr, ExternalReference, FactoryTarget$1 as FactoryTarget, FunctionExpr, HtmlParser, HtmlTagDefinition, I18NHtmlParser, IfStmt, ImplicitReceiver, InstantiateExpr, Interpolation$1 as Interpolation, InterpolationConfig, InvokeFunctionExpr, JSDocComment, JitEvaluator, KeyedRead, KeyedWrite, LeadingComment, Lexer, LiteralArray, LiteralArrayExpr, LiteralExpr, LiteralMap, LiteralMapExpr, LiteralPrimitive, LocalizedString, MapType, MessageBundle, NONE_TYPE, NO_ERRORS_SCHEMA, NodeWithI18n, NonNullAssert, NotExpr, ParseError, ParseErrorLevel, ParseLocation, ParseSourceFile, ParseSourceSpan, ParseSpan, ParseTreeResult, ParsedEvent, ParsedProperty, ParsedPropertyType, ParsedVariable, Parser$1 as Parser, ParserError, PrefixNot, PropertyRead, PropertyWrite, R3BoundTarget, Identifiers as R3Identifiers, R3NgModuleMetadataKind, R3SelectorScopeMode, R3TargetBinder, R3TemplateDependencyKind, ReadKeyExpr, ReadPropExpr, ReadVarExpr, RecursiveAstVisitor, RecursiveVisitor, ResourceLoader, ReturnStatement, STRING_TYPE, SafeCall, SafeKeyedRead, SafePropertyRead, SelectorContext, SelectorListContext, SelectorMatcher, Serializer, SplitInterpolation, Statement, StmtModifier, TagContentType, TaggedTemplateExpr, TemplateBindingParseResult, TemplateLiteral, TemplateLiteralElement, Text, ThisReceiver, BoundAttribute as TmplAstBoundAttribute, BoundDeferredTrigger as TmplAstBoundDeferredTrigger, BoundEvent as TmplAstBoundEvent, BoundText as TmplAstBoundText, Content as TmplAstContent, DeferredBlock as TmplAstDeferredBlock, DeferredBlockError as TmplAstDeferredBlockError, DeferredBlockLoading as TmplAstDeferredBlockLoading, DeferredBlockPlaceholder as TmplAstDeferredBlockPlaceholder, DeferredTrigger as TmplAstDeferredTrigger, Element$1 as TmplAstElement, ForLoopBlock as TmplAstForLoopBlock, ForLoopBlockEmpty as TmplAstForLoopBlockEmpty, HoverDeferredTrigger as TmplAstHoverDeferredTrigger, Icu$1 as TmplAstIcu, IdleDeferredTrigger as TmplAstIdleDeferredTrigger, IfBlock as TmplAstIfBlock, IfBlockBranch as TmplAstIfBlockBranch, ImmediateDeferredTrigger as TmplAstImmediateDeferredTrigger, InteractionDeferredTrigger as TmplAstInteractionDeferredTrigger, RecursiveVisitor$1 as TmplAstRecursiveVisitor, Reference as TmplAstReference, SwitchBlock as TmplAstSwitchBlock, SwitchBlockCase as TmplAstSwitchBlockCase, Template as TmplAstTemplate, Text$3 as TmplAstText, TextAttribute as TmplAstTextAttribute, TimerDeferredTrigger as TmplAstTimerDeferredTrigger, UnknownBlock as TmplAstUnknownBlock, Variable as TmplAstVariable, ViewportDeferredTrigger as TmplAstViewportDeferredTrigger, Token, TokenType, TransplantedType, TreeError, Type, TypeModifier, TypeofExpr, Unary, UnaryOperator, UnaryOperatorExpr, VERSION, VariableBinding, Version, ViewEncapsulation, WrappedNodeExpr, WriteKeyExpr, WritePropExpr, WriteVarExpr, Xliff, Xliff2, Xmb, XmlParser, Xtb, _ParseAST, compileClassDebugInfo, compileClassMetadata, compileComponentClassMetadata, compileComponentFromMetadata, compileDeclareClassMetadata, compileDeclareComponentFromMetadata, compileDeclareDirectiveFromMetadata, compileDeclareFactoryFunction, compileDeclareInjectableFromMetadata, compileDeclareInjectorFromMetadata, compileDeclareNgModuleFromMetadata, compileDeclarePipeFromMetadata, compileDirectiveFromMetadata, compileFactoryFunction, compileInjectable, compileInjector, compileNgModule, compilePipeFromMetadata, computeMsgId, core, createCssSelectorFromNode, createInjectableType, createMayBeForwardRefExpression, devOnlyGuardedExpression, emitDistinctChangesOnlyDefaultValue, encapsulateStyle, getHtmlTagDefinition, getNsPrefix, getSafePropertyAccessString, identifierName, isIdentifier, isNgContainer, isNgContent, isNgTemplate, jsDocComment, leadingComment, literal, literalMap, makeBindingParser, mergeNsAndName, output_ast as outputAst, parseHostBindings, parseTemplate, preserveWhitespacesDefault, publishFacade, r3JitTypeSourceSpan, sanitizeIdentifier, splitNsName, verifyHostBindings, visitAll };
35095
+ export { AST, ASTWithName, ASTWithSource, AbsoluteSourceSpan, ArrayType, ArrowFunctionExpr, AstMemoryEfficientTransformer, AstTransformer, Attribute, Binary, BinaryOperator, BinaryOperatorExpr, BindingPipe, Block, BlockParameter, BoundElementProperty, BuiltinType, BuiltinTypeName, CUSTOM_ELEMENTS_SCHEMA, Call, Chain, ChangeDetectionStrategy, CommaExpr, Comment, CompilerConfig, Conditional, ConditionalExpr, ConstantPool, CssSelector, DEFAULT_INTERPOLATION_CONFIG, DYNAMIC_TYPE, DeclareFunctionStmt, DeclareVarStmt, DomElementSchemaRegistry, DynamicImportExpr, EOF, Element, ElementSchemaRegistry, EmitterVisitorContext, EmptyExpr$1 as EmptyExpr, Expansion, ExpansionCase, Expression, ExpressionBinding, ExpressionStatement, ExpressionType, ExternalExpr, ExternalReference, FactoryTarget$1 as FactoryTarget, FunctionExpr, HtmlParser, HtmlTagDefinition, I18NHtmlParser, IfStmt, ImplicitReceiver, InstantiateExpr, Interpolation$1 as Interpolation, InterpolationConfig, InvokeFunctionExpr, JSDocComment, JitEvaluator, KeyedRead, KeyedWrite, LeadingComment, Lexer, LiteralArray, LiteralArrayExpr, LiteralExpr, LiteralMap, LiteralMapExpr, LiteralPrimitive, LocalizedString, MapType, MessageBundle, NONE_TYPE, NO_ERRORS_SCHEMA, NodeWithI18n, NonNullAssert, NotExpr, ParseError, ParseErrorLevel, ParseLocation, ParseSourceFile, ParseSourceSpan, ParseSpan, ParseTreeResult, ParsedEvent, ParsedProperty, ParsedPropertyType, ParsedVariable, Parser$1 as Parser, ParserError, PrefixNot, PropertyRead, PropertyWrite, R3BoundTarget, Identifiers as R3Identifiers, R3NgModuleMetadataKind, R3SelectorScopeMode, R3TargetBinder, R3TemplateDependencyKind, ReadKeyExpr, ReadPropExpr, ReadVarExpr, RecursiveAstVisitor, RecursiveVisitor, ResourceLoader, ReturnStatement, STRING_TYPE, SafeCall, SafeKeyedRead, SafePropertyRead, SelectorContext, SelectorListContext, SelectorMatcher, Serializer, SplitInterpolation, Statement, StmtModifier, TagContentType, TaggedTemplateExpr, TemplateBindingParseResult, TemplateLiteral, TemplateLiteralElement, Text, ThisReceiver, BoundAttribute as TmplAstBoundAttribute, BoundDeferredTrigger as TmplAstBoundDeferredTrigger, BoundEvent as TmplAstBoundEvent, BoundText as TmplAstBoundText, Content as TmplAstContent, DeferredBlock as TmplAstDeferredBlock, DeferredBlockError as TmplAstDeferredBlockError, DeferredBlockLoading as TmplAstDeferredBlockLoading, DeferredBlockPlaceholder as TmplAstDeferredBlockPlaceholder, DeferredTrigger as TmplAstDeferredTrigger, Element$1 as TmplAstElement, ForLoopBlock as TmplAstForLoopBlock, ForLoopBlockEmpty as TmplAstForLoopBlockEmpty, HoverDeferredTrigger as TmplAstHoverDeferredTrigger, Icu$1 as TmplAstIcu, IdleDeferredTrigger as TmplAstIdleDeferredTrigger, IfBlock as TmplAstIfBlock, IfBlockBranch as TmplAstIfBlockBranch, ImmediateDeferredTrigger as TmplAstImmediateDeferredTrigger, InteractionDeferredTrigger as TmplAstInteractionDeferredTrigger, RecursiveVisitor$1 as TmplAstRecursiveVisitor, Reference as TmplAstReference, SwitchBlock as TmplAstSwitchBlock, SwitchBlockCase as TmplAstSwitchBlockCase, Template as TmplAstTemplate, Text$3 as TmplAstText, TextAttribute as TmplAstTextAttribute, TimerDeferredTrigger as TmplAstTimerDeferredTrigger, UnknownBlock as TmplAstUnknownBlock, Variable as TmplAstVariable, ViewportDeferredTrigger as TmplAstViewportDeferredTrigger, Token, TokenType, TransplantedType, TreeError, Type, TypeModifier, TypeofExpr, Unary, UnaryOperator, UnaryOperatorExpr, VERSION, VariableBinding, Version, ViewEncapsulation, WrappedNodeExpr, WriteKeyExpr, WritePropExpr, WriteVarExpr, Xliff, Xliff2, Xmb, XmlParser, Xtb, compileClassDebugInfo, compileClassMetadata, compileComponentClassMetadata, compileComponentFromMetadata, compileDeclareClassMetadata, compileDeclareComponentFromMetadata, compileDeclareDirectiveFromMetadata, compileDeclareFactoryFunction, compileDeclareInjectableFromMetadata, compileDeclareInjectorFromMetadata, compileDeclareNgModuleFromMetadata, compileDeclarePipeFromMetadata, compileDirectiveFromMetadata, compileFactoryFunction, compileInjectable, compileInjector, compileNgModule, compilePipeFromMetadata, computeMsgId, core, createCssSelectorFromNode, createInjectableType, createMayBeForwardRefExpression, devOnlyGuardedExpression, emitDistinctChangesOnlyDefaultValue, encapsulateStyle, getHtmlTagDefinition, getNsPrefix, getSafePropertyAccessString, identifierName, isIdentifier, isNgContainer, isNgContent, isNgTemplate, jsDocComment, leadingComment, literal, literalMap, makeBindingParser, mergeNsAndName, output_ast as outputAst, parseHostBindings, parseTemplate, preserveWhitespacesDefault, publishFacade, r3JitTypeSourceSpan, sanitizeIdentifier, splitNsName, verifyHostBindings, visitAll };
34658
35096
  //# sourceMappingURL=compiler.mjs.map