@angular/core 18.1.0-next.3 → 18.1.0-next.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. package/esm2022/primitives/event-dispatch/src/eventcontract.mjs +2 -2
  2. package/esm2022/src/change_detection/change_detector_ref.mjs +3 -2
  3. package/esm2022/src/defer/instructions.mjs +2 -2
  4. package/esm2022/src/di/host_tag_name_token.mjs +4 -1
  5. package/esm2022/src/errors.mjs +1 -1
  6. package/esm2022/src/event_delegation_utils.mjs +3 -2
  7. package/esm2022/src/hydration/annotate.mjs +27 -16
  8. package/esm2022/src/hydration/error_handling.mjs +3 -1
  9. package/esm2022/src/hydration/event_replay.mjs +3 -2
  10. package/esm2022/src/hydration/i18n.mjs +102 -18
  11. package/esm2022/src/hydration/node_lookup_utils.mjs +12 -6
  12. package/esm2022/src/render3/collect_native_nodes.mjs +6 -1
  13. package/esm2022/src/render3/component_ref.mjs +1 -1
  14. package/esm2022/src/render3/instructions/i18n_icu_container_visitor.mjs +61 -51
  15. package/esm2022/src/render3/instructions/let_declaration.mjs +30 -7
  16. package/esm2022/src/render3/instructions/projection.mjs +14 -11
  17. package/esm2022/src/render3/instructions/shared.mjs +1 -1
  18. package/esm2022/src/render3/interfaces/node.mjs +2 -1
  19. package/esm2022/src/render3/node_assert.mjs +9 -8
  20. package/esm2022/src/render3/node_manipulation.mjs +10 -3
  21. package/esm2022/src/version.mjs +1 -1
  22. package/esm2022/testing/src/logger.mjs +3 -3
  23. package/fesm2022/core.mjs +289 -140
  24. package/fesm2022/core.mjs.map +1 -1
  25. package/fesm2022/primitives/event-dispatch.mjs +2 -2
  26. package/fesm2022/primitives/event-dispatch.mjs.map +1 -1
  27. package/fesm2022/primitives/signals.mjs +1 -1
  28. package/fesm2022/rxjs-interop.mjs +1 -1
  29. package/fesm2022/testing.mjs +1 -1
  30. package/index.d.ts +9 -3
  31. package/package.json +1 -1
  32. package/primitives/event-dispatch/index.d.ts +2 -2
  33. package/primitives/signals/index.d.ts +1 -1
  34. package/rxjs-interop/index.d.ts +1 -1
  35. package/schematics/migrations/after-render-phase/bundle.js +12 -12
  36. package/schematics/migrations/http-providers/bundle.js +15 -15
  37. package/schematics/migrations/invalid-two-way-bindings/bundle.js +168 -165
  38. package/schematics/migrations/invalid-two-way-bindings/bundle.js.map +3 -3
  39. package/schematics/ng-generate/control-flow-migration/bundle.js +176 -173
  40. package/schematics/ng-generate/control-flow-migration/bundle.js.map +3 -3
  41. package/schematics/ng-generate/standalone-migration/bundle.js +454 -456
  42. package/schematics/ng-generate/standalone-migration/bundle.js.map +3 -3
  43. package/testing/index.d.ts +1 -1
package/fesm2022/core.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @license Angular v18.1.0-next.3
2
+ * @license Angular v18.1.0-next.4
3
3
  * (c) 2010-2024 Google LLC. https://angular.io/
4
4
  * License: MIT
5
5
  */
@@ -5317,6 +5317,7 @@ function toTNodeTypeAsString(tNodeType) {
5317
5317
  tNodeType & 16 /* TNodeType.Projection */ && (text += '|Projection');
5318
5318
  tNodeType & 32 /* TNodeType.Icu */ && (text += '|IcuContainer');
5319
5319
  tNodeType & 64 /* TNodeType.Placeholder */ && (text += '|Placeholder');
5320
+ tNodeType & 128 /* TNodeType.LetDeclaration */ && (text += '|LetDeclaration');
5320
5321
  return text.length > 0 ? text.substring(1) : text;
5321
5322
  }
5322
5323
  /**
@@ -5392,13 +5393,14 @@ function assertTNodeType(tNode, expectedTypes, message) {
5392
5393
  }
5393
5394
  }
5394
5395
  function assertPureTNodeType(type) {
5395
- if (!(type === 2 /* TNodeType.Element */ || //
5396
- type === 1 /* TNodeType.Text */ || //
5397
- type === 4 /* TNodeType.Container */ || //
5398
- type === 8 /* TNodeType.ElementContainer */ || //
5399
- type === 32 /* TNodeType.Icu */ || //
5400
- type === 16 /* TNodeType.Projection */ || //
5401
- type === 64 /* TNodeType.Placeholder */)) {
5396
+ if (!(type === 2 /* TNodeType.Element */ ||
5397
+ type === 1 /* TNodeType.Text */ ||
5398
+ type === 4 /* TNodeType.Container */ ||
5399
+ type === 8 /* TNodeType.ElementContainer */ ||
5400
+ type === 32 /* TNodeType.Icu */ ||
5401
+ type === 16 /* TNodeType.Projection */ ||
5402
+ type === 64 /* TNodeType.Placeholder */ ||
5403
+ type === 128 /* TNodeType.LetDeclaration */)) {
5402
5404
  throwError(`Expected TNodeType to have only a single type selected, but got ${toTNodeTypeAsString(type)}.`);
5403
5405
  }
5404
5406
  }
@@ -6596,6 +6598,9 @@ function getDevModeNodeName(tNode) {
6596
6598
  else if (tNode.type & 4 /* TNodeType.Container */) {
6597
6599
  return 'an <ng-template>';
6598
6600
  }
6601
+ else if (tNode.type & 128 /* TNodeType.LetDeclaration */) {
6602
+ return 'an @let declaration';
6603
+ }
6599
6604
  else {
6600
6605
  return 'a node';
6601
6606
  }
@@ -10840,8 +10845,10 @@ function getParentRElement(tView, tNode, lView) {
10840
10845
  function getClosestRElement(tView, tNode, lView) {
10841
10846
  let parentTNode = tNode;
10842
10847
  // Skip over element and ICU containers as those are represented by a comment node and
10843
- // can't be used as a render parent.
10844
- while (parentTNode !== null && parentTNode.type & (8 /* TNodeType.ElementContainer */ | 32 /* TNodeType.Icu */)) {
10848
+ // can't be used as a render parent. Also skip let declarations since they don't have a
10849
+ // corresponding DOM node at all.
10850
+ while (parentTNode !== null &&
10851
+ parentTNode.type & (8 /* TNodeType.ElementContainer */ | 32 /* TNodeType.Icu */ | 128 /* TNodeType.LetDeclaration */)) {
10845
10852
  tNode = parentTNode;
10846
10853
  parentTNode = tNode.parent;
10847
10854
  }
@@ -11091,6 +11098,11 @@ function clearElementContents(rElement) {
11091
11098
  function applyNodes(renderer, action, tNode, lView, parentRElement, beforeNode, isProjection) {
11092
11099
  while (tNode != null) {
11093
11100
  ngDevMode && assertTNodeForLView(tNode, lView);
11101
+ // Let declarations don't have corresponding DOM nodes so we skip over them.
11102
+ if (tNode.type === 128 /* TNodeType.LetDeclaration */) {
11103
+ tNode = tNode.next;
11104
+ continue;
11105
+ }
11094
11106
  ngDevMode &&
11095
11107
  assertTNodeType(tNode, 3 /* TNodeType.AnyRNode */ | 12 /* TNodeType.AnyContainer */ | 16 /* TNodeType.Projection */ | 32 /* TNodeType.Icu */);
11096
11108
  const rawSlotValue = lView[tNode.index];
@@ -12941,6 +12953,11 @@ function removeLViewFromLContainer(lContainer, index) {
12941
12953
 
12942
12954
  function collectNativeNodes(tView, lView, tNode, result, isProjection = false) {
12943
12955
  while (tNode !== null) {
12956
+ // Let declarations don't have corresponding DOM nodes so we skip over them.
12957
+ if (tNode.type === 128 /* TNodeType.LetDeclaration */) {
12958
+ tNode = isProjection ? tNode.projectionNext : tNode.next;
12959
+ continue;
12960
+ }
12944
12961
  ngDevMode &&
12945
12962
  assertTNodeType(tNode, 3 /* TNodeType.AnyRNode */ | 12 /* TNodeType.AnyContainer */ | 16 /* TNodeType.Projection */ | 32 /* TNodeType.Icu */);
12946
12963
  const lNode = lView[tNode.index];
@@ -13882,6 +13899,8 @@ function getFriendlyStringFromTNodeType(tNodeType) {
13882
13899
  return 'projection';
13883
13900
  case 1 /* TNodeType.Text */:
13884
13901
  return 'text';
13902
+ case 128 /* TNodeType.LetDeclaration */:
13903
+ return '@let';
13885
13904
  default:
13886
13905
  // This should not happen as we cover all possible TNode types above.
13887
13906
  return '<unknown>';
@@ -14476,6 +14495,89 @@ function isRootTemplateMessage(subTemplateIndex) {
14476
14495
  return subTemplateIndex === -1;
14477
14496
  }
14478
14497
 
14498
+ function enterIcu(state, tIcu, lView) {
14499
+ state.index = 0;
14500
+ const currentCase = getCurrentICUCaseIndex(tIcu, lView);
14501
+ if (currentCase !== null) {
14502
+ ngDevMode && assertNumberInRange(currentCase, 0, tIcu.cases.length - 1);
14503
+ state.removes = tIcu.remove[currentCase];
14504
+ }
14505
+ else {
14506
+ state.removes = EMPTY_ARRAY;
14507
+ }
14508
+ }
14509
+ function icuContainerIteratorNext(state) {
14510
+ if (state.index < state.removes.length) {
14511
+ const removeOpCode = state.removes[state.index++];
14512
+ ngDevMode && assertNumber(removeOpCode, 'Expecting OpCode number');
14513
+ if (removeOpCode > 0) {
14514
+ const rNode = state.lView[removeOpCode];
14515
+ ngDevMode && assertDomNode(rNode);
14516
+ return rNode;
14517
+ }
14518
+ else {
14519
+ state.stack.push(state.index, state.removes);
14520
+ // ICUs are represented by negative indices
14521
+ const tIcuIndex = ~removeOpCode;
14522
+ const tIcu = state.lView[TVIEW].data[tIcuIndex];
14523
+ ngDevMode && assertTIcu(tIcu);
14524
+ enterIcu(state, tIcu, state.lView);
14525
+ return icuContainerIteratorNext(state);
14526
+ }
14527
+ }
14528
+ else {
14529
+ if (state.stack.length === 0) {
14530
+ return null;
14531
+ }
14532
+ else {
14533
+ state.removes = state.stack.pop();
14534
+ state.index = state.stack.pop();
14535
+ return icuContainerIteratorNext(state);
14536
+ }
14537
+ }
14538
+ }
14539
+ function loadIcuContainerVisitor() {
14540
+ const _state = {
14541
+ stack: [],
14542
+ index: -1,
14543
+ };
14544
+ /**
14545
+ * Retrieves a set of root nodes from `TIcu.remove`. Used by `TNodeType.ICUContainer`
14546
+ * to determine which root belong to the ICU.
14547
+ *
14548
+ * Example of usage.
14549
+ * ```
14550
+ * const nextRNode = icuContainerIteratorStart(tIcuContainerNode, lView);
14551
+ * let rNode: RNode|null;
14552
+ * while(rNode = nextRNode()) {
14553
+ * console.log(rNode);
14554
+ * }
14555
+ * ```
14556
+ *
14557
+ * @param tIcuContainerNode Current `TIcuContainerNode`
14558
+ * @param lView `LView` where the `RNode`s should be looked up.
14559
+ */
14560
+ function icuContainerIteratorStart(tIcuContainerNode, lView) {
14561
+ _state.lView = lView;
14562
+ while (_state.stack.length)
14563
+ _state.stack.pop();
14564
+ ngDevMode && assertTNodeForLView(tIcuContainerNode, lView);
14565
+ enterIcu(_state, tIcuContainerNode.value, lView);
14566
+ return icuContainerIteratorNext.bind(null, _state);
14567
+ }
14568
+ return icuContainerIteratorStart;
14569
+ }
14570
+ function createIcuIterator(tIcu, lView) {
14571
+ const state = {
14572
+ stack: [],
14573
+ index: -1,
14574
+ lView,
14575
+ };
14576
+ ngDevMode && assertTIcu(tIcu);
14577
+ enterIcu(state, tIcu, lView);
14578
+ return icuContainerIteratorNext.bind(null, state);
14579
+ }
14580
+
14479
14581
  /**
14480
14582
  * Regexp that extracts a reference node information from the compressed node location.
14481
14583
  * The reference node is represented as either:
@@ -14547,15 +14649,21 @@ function getNoOffsetIndex(tNode) {
14547
14649
  }
14548
14650
  /**
14549
14651
  * Check whether a given node exists, but is disconnected from the DOM.
14652
+ */
14653
+ function isDisconnectedNode(tNode, lView) {
14654
+ return (!(tNode.type & (16 /* TNodeType.Projection */ | 128 /* TNodeType.LetDeclaration */)) &&
14655
+ !!lView[tNode.index] &&
14656
+ isDisconnectedRNode(unwrapRNode(lView[tNode.index])));
14657
+ }
14658
+ /**
14659
+ * Check whether the given node exists, but is disconnected from the DOM.
14550
14660
  *
14551
14661
  * Note: we leverage the fact that we have this information available in the DOM emulation
14552
14662
  * layer (in Domino) for now. Longer-term solution should not rely on the DOM emulation and
14553
14663
  * only use internal data structures and state to compute this information.
14554
14664
  */
14555
- function isDisconnectedNode(tNode, lView) {
14556
- return (!(tNode.type & 16 /* TNodeType.Projection */) &&
14557
- !!lView[tNode.index] &&
14558
- !unwrapRNode(lView[tNode.index])?.isConnected);
14665
+ function isDisconnectedRNode(rNode) {
14666
+ return !!rNode && !rNode.isConnected;
14559
14667
  }
14560
14668
  /**
14561
14669
  * Locate a node in an i18n tree that corresponds to a given instruction index.
@@ -14819,7 +14927,7 @@ function calcPathForNode(tNode, lView, excludedParentNodes) {
14819
14927
  referenceNodeName = renderStringify(parentIndex - HEADER_OFFSET);
14820
14928
  }
14821
14929
  let rNode = unwrapRNode(lView[tNode.index]);
14822
- if (tNode.type & 12 /* TNodeType.AnyContainer */) {
14930
+ if (tNode.type & (12 /* TNodeType.AnyContainer */ | 32 /* TNodeType.Icu */)) {
14823
14931
  // For <ng-container> nodes, instead of serializing a reference
14824
14932
  // to the anchor comment node, serialize a location of the first
14825
14933
  // DOM element. Paired with the container size (serialized as a part
@@ -14951,30 +15059,104 @@ function trySerializeI18nBlock(lView, index, context) {
14951
15059
  if (!tI18n || !tI18n.ast) {
14952
15060
  return null;
14953
15061
  }
14954
- const caseQueue = [];
14955
- tI18n.ast.forEach((node) => serializeI18nBlock(lView, caseQueue, context, node));
14956
- return caseQueue.length > 0 ? caseQueue : null;
15062
+ const serializedI18nBlock = {
15063
+ caseQueue: [],
15064
+ disconnectedNodes: new Set(),
15065
+ disjointNodes: new Set(),
15066
+ };
15067
+ serializeI18nBlock(lView, serializedI18nBlock, context, tI18n.ast);
15068
+ return serializedI18nBlock.caseQueue.length === 0 &&
15069
+ serializedI18nBlock.disconnectedNodes.size === 0 &&
15070
+ serializedI18nBlock.disjointNodes.size === 0
15071
+ ? null
15072
+ : serializedI18nBlock;
15073
+ }
15074
+ function serializeI18nBlock(lView, serializedI18nBlock, context, nodes) {
15075
+ let prevRNode = null;
15076
+ for (const node of nodes) {
15077
+ const nextRNode = serializeI18nNode(lView, serializedI18nBlock, context, node);
15078
+ if (nextRNode) {
15079
+ if (isDisjointNode(prevRNode, nextRNode)) {
15080
+ serializedI18nBlock.disjointNodes.add(node.index - HEADER_OFFSET);
15081
+ }
15082
+ prevRNode = nextRNode;
15083
+ }
15084
+ }
15085
+ return prevRNode;
14957
15086
  }
14958
- function serializeI18nBlock(lView, caseQueue, context, node) {
15087
+ /**
15088
+ * Helper to determine whether the given nodes are "disjoint".
15089
+ *
15090
+ * The i18n hydration process walks through the DOM and i18n nodes
15091
+ * at the same time. It expects the sibling DOM node of the previous
15092
+ * i18n node to be the first node of the next i18n node.
15093
+ *
15094
+ * In cases of content projection, this won't always be the case. So
15095
+ * when we detect that, we mark the node as "disjoint", ensuring that
15096
+ * we will serialize the path to the node. This way, when we hydrate the
15097
+ * i18n node, we will be able to find the correct place to start.
15098
+ */
15099
+ function isDisjointNode(prevNode, nextNode) {
15100
+ return prevNode && prevNode.nextSibling !== nextNode;
15101
+ }
15102
+ /**
15103
+ * Process the given i18n node for serialization.
15104
+ * Returns the first RNode for the i18n node to begin hydration.
15105
+ */
15106
+ function serializeI18nNode(lView, serializedI18nBlock, context, node) {
15107
+ const maybeRNode = unwrapRNode(lView[node.index]);
15108
+ if (!maybeRNode || isDisconnectedRNode(maybeRNode)) {
15109
+ serializedI18nBlock.disconnectedNodes.add(node.index - HEADER_OFFSET);
15110
+ return null;
15111
+ }
15112
+ const rNode = maybeRNode;
14959
15113
  switch (node.kind) {
14960
- case 0 /* I18nNodeKind.TEXT */:
14961
- const rNode = unwrapRNode(lView[node.index]);
15114
+ case 0 /* I18nNodeKind.TEXT */: {
14962
15115
  processTextNodeBeforeSerialization(context, rNode);
14963
15116
  break;
15117
+ }
14964
15118
  case 1 /* I18nNodeKind.ELEMENT */:
14965
- case 2 /* I18nNodeKind.PLACEHOLDER */:
14966
- node.children.forEach((node) => serializeI18nBlock(lView, caseQueue, context, node));
15119
+ case 2 /* I18nNodeKind.PLACEHOLDER */: {
15120
+ serializeI18nBlock(lView, serializedI18nBlock, context, node.children);
14967
15121
  break;
14968
- case 3 /* I18nNodeKind.ICU */:
15122
+ }
15123
+ case 3 /* I18nNodeKind.ICU */: {
14969
15124
  const currentCase = lView[node.currentCaseLViewIndex];
14970
15125
  if (currentCase != null) {
14971
15126
  // i18n uses a negative value to signal a change to a new case, so we
14972
15127
  // need to invert it to get the proper value.
14973
15128
  const caseIdx = currentCase < 0 ? ~currentCase : currentCase;
14974
- caseQueue.push(caseIdx);
14975
- node.cases[caseIdx].forEach((node) => serializeI18nBlock(lView, caseQueue, context, node));
15129
+ serializedI18nBlock.caseQueue.push(caseIdx);
15130
+ serializeI18nBlock(lView, serializedI18nBlock, context, node.cases[caseIdx]);
14976
15131
  }
14977
15132
  break;
15133
+ }
15134
+ }
15135
+ return getFirstNativeNodeForI18nNode(lView, node);
15136
+ }
15137
+ /**
15138
+ * Helper function to get the first native node to begin hydrating
15139
+ * the given i18n node.
15140
+ */
15141
+ function getFirstNativeNodeForI18nNode(lView, node) {
15142
+ const tView = lView[TVIEW];
15143
+ const maybeTNode = tView.data[node.index];
15144
+ if (isTNodeShape(maybeTNode)) {
15145
+ // If the node is backed by an actual TNode, we can simply delegate.
15146
+ return getFirstNativeNode(lView, maybeTNode);
15147
+ }
15148
+ else if (node.kind === 3 /* I18nNodeKind.ICU */) {
15149
+ // A nested ICU container won't have an actual TNode. In that case, we can use
15150
+ // an iterator to find the first child.
15151
+ const icuIterator = createIcuIterator(maybeTNode, lView);
15152
+ let rNode = icuIterator();
15153
+ // If the ICU container has no nodes, then we use the ICU anchor as the node.
15154
+ return rNode ?? unwrapRNode(lView[node.index]);
15155
+ }
15156
+ else {
15157
+ // Otherwise, the node is a text or trivial element in an ICU container,
15158
+ // and we can just use the RNode directly.
15159
+ return unwrapRNode(lView[node.index]) ?? null;
14978
15160
  }
14979
15161
  }
14980
15162
  function setCurrentNode(state, node) {
@@ -15067,16 +15249,24 @@ function prepareI18nBlockForHydrationImpl(lView, index, parentTNode, subTemplate
15067
15249
  }
15068
15250
  function collectI18nNodesFromDom(context, state, nodeOrNodes) {
15069
15251
  if (Array.isArray(nodeOrNodes)) {
15252
+ let nextState = state;
15070
15253
  for (const node of nodeOrNodes) {
15071
- // If the node is being projected elsewhere, we need to temporarily
15072
- // branch the state to that location to continue hydration.
15073
- // Otherwise, we continue hydration from the current location.
15254
+ // Whenever a node doesn't directly follow the previous RNode, it
15255
+ // is given a path. We need to resume collecting nodes from that location
15256
+ // until and unless we find another disjoint node.
15074
15257
  const targetNode = tryLocateRNodeByPath(context.hydrationInfo, context.lView, node.index - HEADER_OFFSET);
15075
- const nextState = targetNode ? forkHydrationState(state, targetNode) : state;
15258
+ if (targetNode) {
15259
+ nextState = forkHydrationState(state, targetNode);
15260
+ }
15076
15261
  collectI18nNodesFromDom(context, nextState, node);
15077
15262
  }
15078
15263
  }
15079
15264
  else {
15265
+ if (context.disconnectedNodes.has(nodeOrNodes.index - HEADER_OFFSET)) {
15266
+ // i18n nodes can be considered disconnected if e.g. they were projected.
15267
+ // In that case, we have to make sure to skip over them.
15268
+ return;
15269
+ }
15080
15270
  switch (nodeOrNodes.kind) {
15081
15271
  case 0 /* I18nNodeKind.TEXT */: {
15082
15272
  // Claim a text node for hydration
@@ -17011,7 +17201,7 @@ function createRootComponent(componentView, rootComponentDef, rootDirectives, ho
17011
17201
  function setRootNodeAttributes(hostRenderer, componentDef, hostRNode, rootSelectorOrNode) {
17012
17202
  if (rootSelectorOrNode) {
17013
17203
  // The placeholder will be replaced with the actual version at build time.
17014
- setUpAttributes(hostRenderer, hostRNode, ['ng-version', '18.1.0-next.3']);
17204
+ setUpAttributes(hostRenderer, hostRNode, ['ng-version', '18.1.0-next.4']);
17015
17205
  }
17016
17206
  else {
17017
17207
  // If host element is created as a part of this function call (i.e. `rootSelectorOrNode`
@@ -20813,7 +21003,7 @@ function triggerResourceLoading(tDetails, lView, tNode) {
20813
21003
  if (failed) {
20814
21004
  tDetails.loadingState = DeferDependenciesLoadingState.FAILED;
20815
21005
  if (tDetails.errorTmplIndex === null) {
20816
- const templateLocation = getTemplateLocationDetails(lView);
21006
+ const templateLocation = ngDevMode ? getTemplateLocationDetails(lView) : '';
20817
21007
  const error = new RuntimeError(750 /* RuntimeErrorCode.DEFER_LOADING_FAILED */, ngDevMode &&
20818
21008
  'Loading dependencies for `@defer` block failed, ' +
20819
21009
  `but no \`@error\` block was configured${templateLocation}. ` +
@@ -25096,79 +25286,6 @@ function getCaseIndex(icuExpression, bindingValue) {
25096
25286
  return index === -1 ? null : index;
25097
25287
  }
25098
25288
 
25099
- function loadIcuContainerVisitor() {
25100
- const _stack = [];
25101
- let _index = -1;
25102
- let _lView;
25103
- let _removes;
25104
- /**
25105
- * Retrieves a set of root nodes from `TIcu.remove`. Used by `TNodeType.ICUContainer`
25106
- * to determine which root belong to the ICU.
25107
- *
25108
- * Example of usage.
25109
- * ```
25110
- * const nextRNode = icuContainerIteratorStart(tIcuContainerNode, lView);
25111
- * let rNode: RNode|null;
25112
- * while(rNode = nextRNode()) {
25113
- * console.log(rNode);
25114
- * }
25115
- * ```
25116
- *
25117
- * @param tIcuContainerNode Current `TIcuContainerNode`
25118
- * @param lView `LView` where the `RNode`s should be looked up.
25119
- */
25120
- function icuContainerIteratorStart(tIcuContainerNode, lView) {
25121
- _lView = lView;
25122
- while (_stack.length)
25123
- _stack.pop();
25124
- ngDevMode && assertTNodeForLView(tIcuContainerNode, lView);
25125
- enterIcu(tIcuContainerNode.value, lView);
25126
- return icuContainerIteratorNext;
25127
- }
25128
- function enterIcu(tIcu, lView) {
25129
- _index = 0;
25130
- const currentCase = getCurrentICUCaseIndex(tIcu, lView);
25131
- if (currentCase !== null) {
25132
- ngDevMode && assertNumberInRange(currentCase, 0, tIcu.cases.length - 1);
25133
- _removes = tIcu.remove[currentCase];
25134
- }
25135
- else {
25136
- _removes = EMPTY_ARRAY;
25137
- }
25138
- }
25139
- function icuContainerIteratorNext() {
25140
- if (_index < _removes.length) {
25141
- const removeOpCode = _removes[_index++];
25142
- ngDevMode && assertNumber(removeOpCode, 'Expecting OpCode number');
25143
- if (removeOpCode > 0) {
25144
- const rNode = _lView[removeOpCode];
25145
- ngDevMode && assertDomNode(rNode);
25146
- return rNode;
25147
- }
25148
- else {
25149
- _stack.push(_index, _removes);
25150
- // ICUs are represented by negative indices
25151
- const tIcuIndex = ~removeOpCode;
25152
- const tIcu = _lView[TVIEW].data[tIcuIndex];
25153
- ngDevMode && assertTIcu(tIcu);
25154
- enterIcu(tIcu, _lView);
25155
- return icuContainerIteratorNext();
25156
- }
25157
- }
25158
- else {
25159
- if (_stack.length === 0) {
25160
- return null;
25161
- }
25162
- else {
25163
- _removes = _stack.pop();
25164
- _index = _stack.pop();
25165
- return icuContainerIteratorNext();
25166
- }
25167
- }
25168
- }
25169
- return icuContainerIteratorStart;
25170
- }
25171
-
25172
25289
  /**
25173
25290
  * Converts `I18nCreateOpCodes` array into a human readable format.
25174
25291
  *
@@ -26621,17 +26738,20 @@ function ɵɵprojectionDef(projectionSlots) {
26621
26738
  const tails = projectionHeads.slice();
26622
26739
  let componentChild = componentNode.child;
26623
26740
  while (componentChild !== null) {
26624
- const slotIndex = projectionSlots
26625
- ? matchingProjectionSlotIndex(componentChild, projectionSlots)
26626
- : 0;
26627
- if (slotIndex !== null) {
26628
- if (tails[slotIndex]) {
26629
- tails[slotIndex].projectionNext = componentChild;
26630
- }
26631
- else {
26632
- projectionHeads[slotIndex] = componentChild;
26741
+ // Do not project let declarations so they don't occupy a slot.
26742
+ if (componentChild.type !== 128 /* TNodeType.LetDeclaration */) {
26743
+ const slotIndex = projectionSlots
26744
+ ? matchingProjectionSlotIndex(componentChild, projectionSlots)
26745
+ : 0;
26746
+ if (slotIndex !== null) {
26747
+ if (tails[slotIndex]) {
26748
+ tails[slotIndex].projectionNext = componentChild;
26749
+ }
26750
+ else {
26751
+ projectionHeads[slotIndex] = componentChild;
26752
+ }
26753
+ tails[slotIndex] = componentChild;
26633
26754
  }
26634
- tails[slotIndex] = componentChild;
26635
26755
  }
26636
26756
  componentChild = componentChild.next;
26637
26757
  }
@@ -28360,36 +28480,52 @@ function ɵɵtwoWayListener(eventName, listenerFn) {
28360
28480
  * Use of this source code is governed by an MIT-style license that can be
28361
28481
  * found in the LICENSE file at https://angular.io/license
28362
28482
  */
28483
+ /** Object that indicates the value of a `@let` declaration that hasn't been initialized yet. */
28484
+ const UNINITIALIZED_LET = {};
28363
28485
  /**
28364
- * Declares an `@let` at a specific data slot.
28486
+ * Declares an `@let` at a specific data slot. Returns itself to allow chaining.
28365
28487
  *
28366
28488
  * @param index Index at which to declare the `@let`.
28367
28489
  *
28368
28490
  * @codeGenApi
28369
28491
  */
28370
28492
  function ɵɵdeclareLet(index) {
28371
- // TODO(crisbeto): implement this
28493
+ const tView = getTView();
28494
+ const lView = getLView();
28495
+ const adjustedIndex = index + HEADER_OFFSET;
28496
+ const tNode = getOrCreateTNode(tView, adjustedIndex, 128 /* TNodeType.LetDeclaration */, null, null);
28497
+ setCurrentTNode(tNode, false);
28498
+ store(tView, lView, adjustedIndex, UNINITIALIZED_LET);
28372
28499
  return ɵɵdeclareLet;
28373
28500
  }
28374
28501
  /**
28375
28502
  * Instruction that stores the value of a `@let` declaration on the current view.
28503
+ * Returns the value to allow usage inside variable initializers.
28376
28504
  *
28377
28505
  * @codeGenApi
28378
28506
  */
28379
28507
  function ɵɵstoreLet(value) {
28380
- // TODO(crisbeto): implement this
28508
+ performanceMarkFeature('NgLet');
28509
+ const tView = getTView();
28510
+ const lView = getLView();
28511
+ const index = getSelectedIndex();
28512
+ store(tView, lView, index, value);
28381
28513
  return value;
28382
28514
  }
28383
28515
  /**
28384
- * Retrieves the value of a `@let` declaration defined within the same view.
28516
+ * Retrieves the value of a `@let` declaration defined in a parent view.
28385
28517
  *
28386
28518
  * @param index Index of the declaration within the view.
28387
28519
  *
28388
28520
  * @codeGenApi
28389
28521
  */
28390
28522
  function ɵɵreadContextLet(index) {
28391
- // TODO(crisbeto): implement this
28392
- return null;
28523
+ const contextLView = getContextLView();
28524
+ const value = load(contextLView, HEADER_OFFSET + index);
28525
+ if (value === UNINITIALIZED_LET) {
28526
+ throw new RuntimeError(314 /* RuntimeErrorCode.UNINITIALIZED_LET_ACCESS */, ngDevMode && 'Attempting to access a @let declaration whose value is not available yet');
28527
+ }
28528
+ return value;
28393
28529
  }
28394
28530
 
28395
28531
  /*
@@ -30845,7 +30981,7 @@ class Version {
30845
30981
  /**
30846
30982
  * @publicApi
30847
30983
  */
30848
- const VERSION = new Version('18.1.0-next.3');
30984
+ const VERSION = new Version('18.1.0-next.4');
30849
30985
 
30850
30986
  /*
30851
30987
  * This file exists to support compilation of @angular/core in Ivy mode.
@@ -34045,7 +34181,8 @@ function createViewRef(tNode, lView, isPipe) {
34045
34181
  const componentView = getComponentLViewByIndex(tNode.index, lView); // look down
34046
34182
  return new ViewRef$1(componentView, componentView);
34047
34183
  }
34048
- else if (tNode.type & (3 /* TNodeType.AnyRNode */ | 12 /* TNodeType.AnyContainer */ | 32 /* TNodeType.Icu */)) {
34184
+ else if (tNode.type &
34185
+ (3 /* TNodeType.AnyRNode */ | 12 /* TNodeType.AnyContainer */ | 32 /* TNodeType.Icu */ | 128 /* TNodeType.LetDeclaration */)) {
34049
34186
  // The LView represents the location where the injection is requested from.
34050
34187
  // We need to locate the containing LView (in case where the `lView` is an embedded view)
34051
34188
  const hostComponentView = lView[DECLARATION_COMPONENT_VIEW]; // look up
@@ -36660,7 +36797,8 @@ const initGlobalEventDelegation = (eventDelegation, injector) => {
36660
36797
  if (injector.get(IS_EVENT_REPLAY_ENABLED, EVENT_REPLAY_ENABLED_DEFAULT)) {
36661
36798
  return;
36662
36799
  }
36663
- eventDelegation.eventContract = new EventContract(new EventContractContainer(document.body));
36800
+ eventDelegation.eventContract = new EventContract(new EventContractContainer(document.body),
36801
+ /* useActionResolver= */ false);
36664
36802
  const dispatcher = new EventDispatcher(invokeRegisteredListeners);
36665
36803
  registerDispatcher(eventDelegation.eventContract, dispatcher);
36666
36804
  };
@@ -36754,7 +36892,8 @@ const initEventReplay = (eventDelegation, injector) => {
36754
36892
  // This is set in packages/platform-server/src/utils.ts
36755
36893
  const container = globalThis[CONTRACT_PROPERTY]?.[appId];
36756
36894
  const earlyJsactionData = getJsactionData(container);
36757
- const eventContract = (eventDelegation.eventContract = new EventContract(new EventContractContainer(earlyJsactionData.c)));
36895
+ const eventContract = (eventDelegation.eventContract = new EventContract(new EventContractContainer(earlyJsactionData.c),
36896
+ /* useActionResolver= */ false));
36758
36897
  for (const et of earlyJsactionData.et) {
36759
36898
  eventContract.addEvent(et);
36760
36899
  }
@@ -37053,15 +37192,18 @@ function serializeLContainer(lContainer, context) {
37053
37192
  function appendSerializedNodePath(ngh, tNode, lView, excludedParentNodes) {
37054
37193
  const noOffsetIndex = tNode.index - HEADER_OFFSET;
37055
37194
  ngh[NODES] ??= {};
37056
- ngh[NODES][noOffsetIndex] = calcPathForNode(tNode, lView, excludedParentNodes);
37195
+ // Ensure we don't calculate the path multiple times.
37196
+ ngh[NODES][noOffsetIndex] ??= calcPathForNode(tNode, lView, excludedParentNodes);
37057
37197
  }
37058
37198
  /**
37059
37199
  * Helper function to append information about a disconnected node.
37060
37200
  * This info is needed at runtime to avoid DOM lookups for this element
37061
37201
  * and instead, the element would be created from scratch.
37062
37202
  */
37063
- function appendDisconnectedNodeIndex(ngh, tNode) {
37064
- const noOffsetIndex = tNode.index - HEADER_OFFSET;
37203
+ function appendDisconnectedNodeIndex(ngh, tNodeOrNoOffsetIndex) {
37204
+ const noOffsetIndex = typeof tNodeOrNoOffsetIndex === 'number'
37205
+ ? tNodeOrNoOffsetIndex
37206
+ : tNodeOrNoOffsetIndex.index - HEADER_OFFSET;
37065
37207
  ngh[DISCONNECTED_NODES] ??= [];
37066
37208
  if (!ngh[DISCONNECTED_NODES].includes(noOffsetIndex)) {
37067
37209
  ngh[DISCONNECTED_NODES].push(noOffsetIndex);
@@ -37092,7 +37234,15 @@ function serializeLView(lView, context) {
37092
37234
  const i18nData = trySerializeI18nBlock(lView, i, context);
37093
37235
  if (i18nData) {
37094
37236
  ngh[I18N_DATA] ??= {};
37095
- ngh[I18N_DATA][noOffsetIndex] = i18nData;
37237
+ ngh[I18N_DATA][noOffsetIndex] = i18nData.caseQueue;
37238
+ for (const nodeNoOffsetIndex of i18nData.disconnectedNodes) {
37239
+ appendDisconnectedNodeIndex(ngh, nodeNoOffsetIndex);
37240
+ }
37241
+ for (const nodeNoOffsetIndex of i18nData.disjointNodes) {
37242
+ const tNode = tView.data[nodeNoOffsetIndex + HEADER_OFFSET];
37243
+ ngDevMode && assertTNode(tNode);
37244
+ appendSerializedNodePath(ngh, tNode, lView, i18nChildren);
37245
+ }
37096
37246
  continue;
37097
37247
  }
37098
37248
  // Skip processing of a given slot in the following cases:
@@ -37205,13 +37355,14 @@ function serializeLView(lView, context) {
37205
37355
  ngh[ELEMENT_CONTAINERS] ??= {};
37206
37356
  ngh[ELEMENT_CONTAINERS][noOffsetIndex] = calcNumRootNodes(tView, lView, tNode.child);
37207
37357
  }
37208
- else if (tNode.type & 16 /* TNodeType.Projection */) {
37209
- // Current TNode represents an `<ng-content>` slot, thus it has no
37210
- // DOM elements associated with it, so the **next sibling** node would
37211
- // not be able to find an anchor. In this case, use full path instead.
37358
+ else if (tNode.type & (16 /* TNodeType.Projection */ | 128 /* TNodeType.LetDeclaration */)) {
37359
+ // Current TNode represents an `<ng-content>` slot or `@let` declaration,
37360
+ // thus it has no DOM elements associated with it, so the **next sibling**
37361
+ // node would not be able to find an anchor. In this case, use full path instead.
37212
37362
  let nextTNode = tNode.next;
37213
- // Skip over all `<ng-content>` slots in a row.
37214
- while (nextTNode !== null && nextTNode.type & 16 /* TNodeType.Projection */) {
37363
+ // Skip over all `<ng-content>` slots and `@let` declarations in a row.
37364
+ while (nextTNode !== null &&
37365
+ nextTNode.type & (16 /* TNodeType.Projection */ | 128 /* TNodeType.LetDeclaration */)) {
37215
37366
  nextTNode = nextTNode.next;
37216
37367
  }
37217
37368
  if (nextTNode && !isInSkipHydrationBlock(nextTNode)) {
@@ -37219,11 +37370,9 @@ function serializeLView(lView, context) {
37219
37370
  appendSerializedNodePath(ngh, nextTNode, lView, i18nChildren);
37220
37371
  }
37221
37372
  }
37222
- else {
37223
- if (tNode.type & 1 /* TNodeType.Text */) {
37224
- const rNode = unwrapRNode(lView[i]);
37225
- processTextNodeBeforeSerialization(context, rNode);
37226
- }
37373
+ else if (tNode.type & 1 /* TNodeType.Text */) {
37374
+ const rNode = unwrapRNode(lView[i]);
37375
+ processTextNodeBeforeSerialization(context, rNode);
37227
37376
  }
37228
37377
  }
37229
37378
  }