( hydrationInfo: DehydratedView, tView: TView, lView: LView<unknown>, tNode: TNode, )
| 116 | * @returns an RNode that represents a given tNode |
| 117 | */ |
| 118 | export function locateNextRNode<T extends RNode>( |
| 119 | hydrationInfo: DehydratedView, |
| 120 | tView: TView, |
| 121 | lView: LView<unknown>, |
| 122 | tNode: TNode, |
| 123 | ): T | null { |
| 124 | const noOffsetIndex = getNoOffsetIndex(tNode); |
| 125 | let native = locateI18nRNodeByIndex(hydrationInfo, noOffsetIndex); |
| 126 | |
| 127 | if (native === undefined) { |
| 128 | const nodes = hydrationInfo.data[NODES]; |
| 129 | if (nodes?.[noOffsetIndex]) { |
| 130 | // We know the exact location of the node. |
| 131 | native = locateRNodeByPath(nodes[noOffsetIndex], lView); |
| 132 | } else if (tView.firstChild === tNode) { |
| 133 | // We create a first node in this view, so we use a reference |
| 134 | // to the first child in this DOM segment. |
| 135 | native = hydrationInfo.firstChild; |
| 136 | } else { |
| 137 | // Locate a node based on a previous sibling or a parent node. |
| 138 | const previousTNodeParent = tNode.prev === null; |
| 139 | const previousTNode = (tNode.prev ?? tNode.parent)!; |
| 140 | ngDevMode && |
| 141 | assertDefined( |
| 142 | previousTNode, |
| 143 | 'Unexpected state: current TNode does not have a connection ' + |
| 144 | 'to the previous node or a parent node.', |
| 145 | ); |
| 146 | if (isFirstElementInNgContainer(tNode)) { |
| 147 | const noOffsetParentIndex = getNoOffsetIndex(tNode.parent!); |
| 148 | native = getSegmentHead(hydrationInfo, noOffsetParentIndex); |
| 149 | } else { |
| 150 | let previousRElement = getNativeByTNode(previousTNode, lView); |
| 151 | if (previousTNodeParent) { |
| 152 | native = (previousRElement as RElement).firstChild; |
| 153 | } else { |
| 154 | // If the previous node is an element, but it also has container info, |
| 155 | // this means that we are processing a node like `<div #vcrTarget>`, which is |
| 156 | // represented in the DOM as `<div></div>...<!--container-->`. |
| 157 | // In this case, there are nodes *after* this element and we need to skip |
| 158 | // all of them to reach an element that we are looking for. |
| 159 | const noOffsetPrevSiblingIndex = getNoOffsetIndex(previousTNode); |
| 160 | const segmentHead = getSegmentHead(hydrationInfo, noOffsetPrevSiblingIndex); |
| 161 | if (previousTNode.type === TNodeType.Element && segmentHead) { |
| 162 | const numRootNodesToSkip = calcSerializedContainerSize( |
| 163 | hydrationInfo, |
| 164 | noOffsetPrevSiblingIndex, |
| 165 | ); |
| 166 | // `+1` stands for an anchor comment node after all the views in this container. |
| 167 | const nodesToSkip = numRootNodesToSkip + 1; |
| 168 | // First node after this segment. |
| 169 | native = siblingAfter(nodesToSkip, segmentHead); |
| 170 | } else { |
| 171 | native = previousRElement.nextSibling; |
| 172 | } |
| 173 | } |
| 174 | } |
| 175 | } |
no test coverage detected
searching dependent graphs…