// optimize anchor cache lookup
;(Comment.prototype as any).$fe = undefined
;(Node.prototype as any).$idx = undefined
+ ;(Node.prototype as any).$auc = undefined
;(Node.prototype as any).$children = undefined
isOptimized = true
}
let node: Node | null
if (insertionAnchor !== undefined) {
const hydrationState = getHydrationState(insertionParent!)!
- const {
- prevDynamicCount,
- logicalChildren,
- appendAnchor,
- insertionAnchors,
- } = hydrationState
+ const { prevDynamicCount, logicalChildren, appendAnchor } = hydrationState
// prepend
if (insertionAnchor === 0) {
node = logicalChildren[prevDynamicCount]
}
// insert
else if (insertionAnchor instanceof Node) {
- const seen =
- (insertionAnchors && insertionAnchors.get(insertionAnchor)) || 0
- node = seen
- ? logicalChildren[(insertionAnchor as ChildItem).$idx + seen]
- : insertionAnchor
-
- hydrationState.insertionAnchors = (
- hydrationState.insertionAnchors || new Map()
- ).set(insertionAnchor, seen + 1)
+ const usedCount = (insertionAnchor as ChildItem).$auc
+ if (usedCount !== undefined) {
+ node =
+ logicalChildren[(insertionAnchor as ChildItem).$idx + usedCount + 1]
+ ;(insertionAnchor as ChildItem).$auc = usedCount + 1
+ } else {
+ node = insertionAnchor
+ hydrationState.insertionAnchorCount++
+ ;(insertionAnchor as ChildItem).$auc = 0
+ }
}
// append
else {
export function __nthChild(node: Node, i: number): Node {
const hydrationState = getHydrationState(node as ParentNode)
if (hydrationState) {
- const { prevDynamicCount, insertionAnchors, logicalChildren } =
+ const { prevDynamicCount, insertionAnchorCount, logicalChildren } =
hydrationState
// prevDynamicCount tracks how many dynamic nodes have been processed
// so far (prepend/insert/append).
// anchor node itself and do NOT consume the next child in `logicalChildren`,
// yet prevDynamicCount is still incremented. This overcounts the base
// offset by 1 per unique anchor that has appeared.
- // insertionAnchors.size equals the number of unique anchors seen, so we
+ // insertionAnchorCount equals the number of unique anchors seen, so we
// subtract it to neutralize those "first-use doesn't consume" cases:
- // base = prevDynamicCount - insertionAnchors.size
+ // base = prevDynamicCount - insertionAnchorCount
// Then index from this base: logicalChildren[base + i].
- const size = insertionAnchors ? insertionAnchors.size : 0
- return logicalChildren[prevDynamicCount - size + i]
+ return logicalChildren[prevDynamicCount - insertionAnchorCount + i]
}
return node.childNodes[i]
}
export function __next(node: Node): Node {
const hydrationState = getHydrationState(node.parentNode!)
if (hydrationState) {
- const { logicalChildren, insertionAnchors } = hydrationState
- const seenCount = (insertionAnchors && insertionAnchors.get(node)) || 0
- // If node is used as an anchor, the first hydration uses node itself,
- // but seenCount increases, so here needs -1
- const insertedNodesCount = seenCount === 0 ? 0 : seenCount - 1
- return logicalChildren[(node as ChildItem).$idx + insertedNodesCount + 1]
+ const { logicalChildren } = hydrationState
+ return logicalChildren[
+ (node as ChildItem).$idx + ((node as ChildItem).$auc || 0) + 1
+ ]
}
return node.nextSibling!
}
import { isHydrating } from './dom/hydration'
-export type ChildItem = ChildNode & { $idx: number }
+export type ChildItem = ChildNode & { $idx: number; $auc?: number }
export type InsertionParent = ParentNode & { $children?: ChildItem[] }
type HydrationState = {
logicalChildren: ChildItem[]
prevDynamicCount: number
- insertionAnchors: Map<Node, number> | null
+ insertionAnchorCount: number
appendAnchor: Node | null
}
export let insertionParent: InsertionParent | undefined
hydrationStateCache.set(parent, {
logicalChildren,
prevDynamicCount: 0,
- insertionAnchors: null,
+ insertionAnchorCount: 0,
appendAnchor: null,
})
}