@tanstack/redact 0.0.9 → 0.0.11
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.
- package/dist/dom/dispatcher.js +4 -0
- package/dist/dom/dispatcher.js.map +2 -2
- package/dist/dom/features/hydration/full.js +19 -8
- package/dist/dom/features/hydration/full.js.map +2 -2
- package/dist/dom/index.d.ts +30 -0
- package/dist/dom/index.js +26 -0
- package/dist/dom/index.js.map +2 -2
- package/dist/dom/reconcile.d.ts +1 -4
- package/dist/dom/reconcile.js +41 -35
- package/dist/dom/reconcile.js.map +2 -2
- package/dist/dom/root.js +79 -3
- package/dist/dom/root.js.map +2 -2
- package/dist/server/bootstrap-script.d.ts +4 -1
- package/dist/server/bootstrap-script.js.map +2 -2
- package/dist/server/stream.d.ts +1 -0
- package/dist/server/stream.js +72 -7
- package/dist/server/stream.js.map +2 -2
- package/dist/server/walk.js +21 -0
- package/dist/server/walk.js.map +2 -2
- package/dist/vite/index.js +2 -0
- package/dist/vite/index.js.map +2 -2
- package/package.json +1 -1
- package/src/dom/dispatcher.ts +10 -0
- package/src/dom/features/hydration/full.ts +25 -8
- package/src/dom/index.ts +17 -0
- package/src/dom/reconcile.ts +54 -54
- package/src/dom/root.ts +99 -3
- package/src/server/bootstrap-script.ts +4 -1
- package/src/server/stream.ts +88 -7
- package/src/server/walk.ts +30 -0
- package/src/vite/index.ts +2 -0
package/dist/dom/dispatcher.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/dom/dispatcher.ts"],
|
|
4
|
-
"sourcesContent": ["import type { Hook, Fiber, FiberRoot, Effect } from '../core'\nimport { ReactSharedInternals, REACT_CONTEXT_TYPE } from '../react'\nimport { scheduleUpdate, enqueueEffect, readContext } from './reconcile'\n\nfunction getCurrentFiber(): Fiber {\n const f = ReactSharedInternals.currentFiber\n if (!f) throw new Error('Hook called outside a function component render.')\n return f\n}\n\nfunction nextHook(): Hook {\n const fiber = getCurrentFiber()\n const idx = ReactSharedInternals.hookIndex++\n\n let prev = ReactSharedInternals.currentHook\n\n if (idx === 0) {\n if (fiber.hooks) {\n ReactSharedInternals.currentHook = fiber.hooks\n return fiber.hooks\n }\n const h: Hook = { state: undefined, queue: undefined, deps: undefined, cleanup: undefined, next: null }\n fiber.hooks = h\n ReactSharedInternals.currentHook = h\n return h\n }\n\n if (prev && prev.next) {\n ReactSharedInternals.currentHook = prev.next\n return prev.next\n }\n\n const h: Hook = { state: undefined, queue: undefined, deps: undefined, cleanup: undefined, next: null }\n if (prev) prev.next = h\n else fiber.hooks = h\n ReactSharedInternals.currentHook = h\n return h\n}\n\nfunction depsEqual(\n a: ReadonlyArray<unknown> | undefined,\n b: ReadonlyArray<unknown> | undefined,\n): boolean {\n if (a === b) return true\n if (!a || !b) return false\n if (a.length !== b.length) return false\n for (let i = 0; i < a.length; i++) {\n if (!Object.is(a[i], b[i])) return false\n }\n return true\n}\n\nexport function makeDispatcher() {\n return {\n useState<S>(initial: S | (() => S)) {\n return this.useReducer<S, S | ((p: S) => S)>(\n basicReducer as any,\n typeof initial === 'function' ? (initial as () => S)() : initial,\n )\n },\n\n useReducer<S, A>(reducer: (s: S, a: A) => S, initialArg: any, init?: (a: any) => S) {\n const hook = nextHook()\n const fiber = getCurrentFiber()\n if (hook.queue === undefined) {\n hook.state = init ? init(initialArg) : initialArg\n const queue: any = { reducer }\n const dispatch = (action: A) => {\n const currentState = hook.state as S\n const next = queue.reducer(currentState, action)\n if (!Object.is(next, currentState)) {\n hook.state = next\n scheduleUpdate(fiber)\n }\n }\n queue.dispatch = dispatch\n hook.queue = queue\n } else {\n hook.queue.reducer = reducer\n }\n return [hook.state, hook.queue.dispatch] as [S, (a: A) => void]\n },\n\n useEffect(create: () => any, deps?: ReadonlyArray<unknown>) {\n const hook = nextHook()\n const fiber = getCurrentFiber()\n const prevDeps = hook.deps\n if (prevDeps !== undefined && depsEqual(prevDeps, deps)) return\n hook.deps = deps\n const effect: Effect = {\n tag: 'effect',\n create: () => {\n // Run the prior cleanup INSIDE the effect run, not during the\n // dispatch/render phase. If render A \u2192 B \u2192 C all happen back-to-\n // back before the passive microtask drains, dispatch-time cleanup\n // only fires once (between A\u2192B) and effects B + C both run fresh,\n // leaving two side-effects (e.g. two plot SVGs) in the DOM. Doing\n // it here, at effect-run time, means every new create first tears\n // down whatever cleanup is currently live on the hook.\n if (hook.cleanup) {\n try { hook.cleanup() } catch {}\n // The prior cleanup was also pushed onto fiber.cleanups; remove\n // it so unmount doesn't double-call it.\n if (fiber.cleanups) {\n const i = fiber.cleanups.indexOf(hook.cleanup)\n if (i >= 0) fiber.cleanups.splice(i, 1)\n }\n hook.cleanup = null\n }\n const c = create()\n hook.cleanup = typeof c === 'function' ? c : null\n return hook.cleanup\n },\n destroy: undefined,\n deps,\n }\n enqueueEffect(fiber, effect)\n },\n\n useLayoutEffect(create: () => any, deps?: ReadonlyArray<unknown>) {\n const hook = nextHook()\n const fiber = getCurrentFiber()\n const prevDeps = hook.deps\n if (prevDeps !== undefined && depsEqual(prevDeps, deps)) return\n hook.deps = deps\n const effect: Effect = {\n tag: 'layout',\n create: () => {\n // Mirror useEffect: tear down the prior cleanup at run time so\n // coalesced renders don't leak side-effects.\n if (hook.cleanup) {\n try { hook.cleanup() } catch {}\n if (fiber.cleanups) {\n const i = fiber.cleanups.indexOf(hook.cleanup)\n if (i >= 0) fiber.cleanups.splice(i, 1)\n }\n hook.cleanup = null\n }\n const c = create()\n hook.cleanup = typeof c === 'function' ? c : null\n return hook.cleanup\n },\n destroy: undefined,\n deps,\n }\n enqueueEffect(fiber, effect)\n },\n\n useInsertionEffect(create: () => any, deps?: ReadonlyArray<unknown>) {\n return this.useLayoutEffect(create, deps)\n },\n\n useRef<T>(initial: T) {\n const hook = nextHook()\n if (hook.state === undefined) hook.state = { current: initial }\n return hook.state as { current: T }\n },\n\n useMemo<T>(factory: () => T, deps?: ReadonlyArray<unknown>) {\n const hook = nextHook()\n if (hook.deps !== undefined && depsEqual(hook.deps, deps)) {\n return hook.state as T\n }\n const value = factory()\n hook.state = value\n hook.deps = deps\n return value\n },\n\n useCallback<T extends Function>(fn: T, deps?: ReadonlyArray<unknown>): T {\n return this.useMemo(() => fn, deps) as T\n },\n\n useContext<T>(ctx: any): T {\n const fiber = getCurrentFiber()\n return readContext(fiber, ctx)\n },\n\n useImperativeHandle<T>(ref: any, factory: () => T, deps?: ReadonlyArray<unknown>) {\n const hook = nextHook()\n if (hook.deps !== undefined && depsEqual(hook.deps, deps)) return\n hook.deps = deps\n const value = factory()\n if (ref) {\n if (typeof ref === 'function') ref(value)\n else ref.current = value\n }\n },\n\n useDebugValue<T>(_value: T, _formatter?: (v: T) => any): void {\n // noop\n },\n\n useId(): string {\n const hook = nextHook()\n if (hook.state === undefined) {\n const fiber = getCurrentFiber()\n const root = findRootFromFiber(fiber)\n hook.state = (root?.identifierPrefix ?? ':r') + (idCounter++).toString(36)\n }\n return hook.state as string\n },\n\n useTransition(): [boolean, (fn: () => void) => void] {\n return [false, (fn: () => void) => fn()]\n },\n\n useDeferredValue<T>(v: T): T {\n return v\n },\n\n useSyncExternalStore<T>(\n subscribe: (cb: () => void) => () => void,\n getSnapshot: () => T,\n getServerSnapshot?: () => T,\n ): T {\n const fiber = getCurrentFiber()\n const hook = nextHook()\n\n // During hydration, use the server snapshot (if provided) so the tree\n // matches the SSR output. Components like TanStack Router's ClientOnly\n // rely on this: they render `false` on server, `true` on client \u2014 and\n // if we return `true` during hydration, client and server diverge and\n // the tree mounts fresh next to the SSR fallback DOM.\n const root = fiber.root ?? findRootFromFiber(fiber)\n const isHydrating = Boolean(root?.hydrating)\n const value =\n isHydrating && getServerSnapshot ? getServerSnapshot() : getSnapshot()\n hook.state = value\n\n if (hook.cleanup == null) {\n const forceUpdate = () => {\n let next: T\n try {\n next = getSnapshot()\n } catch {\n scheduleUpdate(fiber)\n return\n }\n if (!Object.is(hook.state, next)) {\n hook.state = next\n scheduleUpdate(fiber)\n }\n }\n const unsubscribe = subscribe(forceUpdate)\n hook.cleanup = unsubscribe\n // Register with fiber so unmountFiber runs it. Without this, the store\n // keeps holding forceUpdate and every store update schedules an already-\n // unmounted fiber \u2014 its rerender walks stale .parent pointers and mounts\n // zombie DOM into the old parent.\n if (typeof unsubscribe === 'function') {\n fiber.cleanups ||= []\n fiber.cleanups.push(unsubscribe)\n }\n\n // If we served the server snapshot, run a post-hydration check so\n // components like `useHydrated()` flip from false \u2192 true after the\n // initial render commits. Queued late so hydration finishes first.\n if (isHydrating && getServerSnapshot) {\n queueMicrotask(() => queueMicrotask(forceUpdate))\n }\n }\n return value\n },\n\n use<T>(resource: any): T {\n if (resource == null) throw new Error('use() received null or undefined')\n if (resource.$$typeof === REACT_CONTEXT_TYPE) {\n return readContext(getCurrentFiber(), resource)\n }\n if (typeof resource.then === 'function') {\n const thenable = resource\n switch (thenable.status) {\n case 'fulfilled':\n return thenable.value\n case 'rejected':\n throw thenable.reason\n default: {\n if (thenable.status === undefined) {\n thenable.status = 'pending'\n thenable.then(\n (v: any) => {\n if (thenable.status === 'pending') {\n thenable.status = 'fulfilled'\n thenable.value = v\n }\n },\n (e: any) => {\n if (thenable.status === 'pending') {\n thenable.status = 'rejected'\n thenable.reason = e\n }\n },\n )\n }\n throw thenable\n }\n }\n }\n throw new Error('use() expected a Promise or Context')\n },\n }\n}\n\nfunction basicReducer<S>(state: S, action: S | ((p: S) => S)): S {\n return typeof action === 'function' ? (action as (p: S) => S)(state) : action\n}\n\nlet idCounter = 0\n\nfunction findRootFromFiber(fiber: Fiber): FiberRoot | null {\n let f: Fiber | null = fiber\n while (f) {\n if (f.root) return f.root\n f = f.parent\n }\n return null\n}\n"],
|
|
5
|
-
"mappings": ";AACA,SAAS,sBAAsB,0BAA0B;AACzD,SAAS,gBAAgB,eAAe,mBAAmB;AAE3D,SAAS,kBAAyB;AAChC,QAAM,IAAI,qBAAqB;AAC/B,MAAI,CAAC,EAAG,OAAM,IAAI,MAAM,kDAAkD;AAC1E,SAAO;AACT;AAEA,SAAS,WAAiB;AACxB,QAAM,QAAQ,gBAAgB;AAC9B,QAAM,MAAM,qBAAqB;AAEjC,MAAI,OAAO,qBAAqB;AAEhC,MAAI,QAAQ,GAAG;AACb,QAAI,MAAM,OAAO;AACf,2BAAqB,cAAc,MAAM;AACzC,aAAO,MAAM;AAAA,IACf;AACA,UAAMA,KAAU,EAAE,OAAO,QAAW,OAAO,QAAW,MAAM,QAAW,SAAS,QAAW,MAAM,KAAK;AACtG,UAAM,QAAQA;AACd,yBAAqB,cAAcA;AACnC,WAAOA;AAAA,EACT;AAEA,MAAI,QAAQ,KAAK,MAAM;AACrB,yBAAqB,cAAc,KAAK;AACxC,WAAO,KAAK;AAAA,EACd;AAEA,QAAM,IAAU,EAAE,OAAO,QAAW,OAAO,QAAW,MAAM,QAAW,SAAS,QAAW,MAAM,KAAK;AACtG,MAAI,KAAM,MAAK,OAAO;AAAA,MACjB,OAAM,QAAQ;AACnB,uBAAqB,cAAc;AACnC,SAAO;AACT;AAEA,SAAS,UACP,GACA,GACS;AACT,MAAI,MAAM,EAAG,QAAO;AACpB,MAAI,CAAC,KAAK,CAAC,EAAG,QAAO;AACrB,MAAI,EAAE,WAAW,EAAE,OAAQ,QAAO;AAClC,WAAS,IAAI,GAAG,IAAI,EAAE,QAAQ,KAAK;AACjC,QAAI,CAAC,OAAO,GAAG,EAAE,CAAC,GAAG,EAAE,CAAC,CAAC,EAAG,QAAO;AAAA,EACrC;AACA,SAAO;AACT;
|
|
4
|
+
"sourcesContent": ["import type { Hook, Fiber, FiberRoot, Effect } from '../core'\nimport { ReactSharedInternals, REACT_CONTEXT_TYPE } from '../react'\nimport { scheduleUpdate, enqueueEffect, readContext } from './reconcile'\n\nfunction getCurrentFiber(): Fiber {\n const f = ReactSharedInternals.currentFiber\n if (!f) throw new Error('Hook called outside a function component render.')\n return f\n}\n\nfunction nextHook(): Hook {\n const fiber = getCurrentFiber()\n const idx = ReactSharedInternals.hookIndex++\n\n let prev = ReactSharedInternals.currentHook\n\n if (idx === 0) {\n if (fiber.hooks) {\n ReactSharedInternals.currentHook = fiber.hooks\n return fiber.hooks\n }\n const h: Hook = { state: undefined, queue: undefined, deps: undefined, cleanup: undefined, next: null }\n fiber.hooks = h\n ReactSharedInternals.currentHook = h\n return h\n }\n\n if (prev && prev.next) {\n ReactSharedInternals.currentHook = prev.next\n return prev.next\n }\n\n const h: Hook = { state: undefined, queue: undefined, deps: undefined, cleanup: undefined, next: null }\n if (prev) prev.next = h\n else fiber.hooks = h\n ReactSharedInternals.currentHook = h\n return h\n}\n\nfunction depsEqual(\n a: ReadonlyArray<unknown> | undefined,\n b: ReadonlyArray<unknown> | undefined,\n): boolean {\n if (a === b) return true\n if (!a || !b) return false\n if (a.length !== b.length) return false\n for (let i = 0; i < a.length; i++) {\n if (!Object.is(a[i], b[i])) return false\n }\n return true\n}\n\n// Singleton \u2014 every method reads render context via ReactSharedInternals,\n// and per-hook closures live on the hook itself, so nothing is render-local\n// to capture. Allocating a fresh wrapper + 17 method closures per function-\n// component render was pure GC pressure.\nconst DISPATCHER = makeDispatcherImpl()\n\nexport function makeDispatcher() {\n return DISPATCHER\n}\n\nfunction makeDispatcherImpl() {\n return {\n useState<S>(initial: S | (() => S)) {\n return this.useReducer<S, S | ((p: S) => S)>(\n basicReducer as any,\n typeof initial === 'function' ? (initial as () => S)() : initial,\n )\n },\n\n useReducer<S, A>(reducer: (s: S, a: A) => S, initialArg: any, init?: (a: any) => S) {\n const hook = nextHook()\n const fiber = getCurrentFiber()\n if (hook.queue === undefined) {\n hook.state = init ? init(initialArg) : initialArg\n const queue: any = { reducer }\n const dispatch = (action: A) => {\n const currentState = hook.state as S\n const next = queue.reducer(currentState, action)\n if (!Object.is(next, currentState)) {\n hook.state = next\n scheduleUpdate(fiber)\n }\n }\n queue.dispatch = dispatch\n hook.queue = queue\n } else {\n hook.queue.reducer = reducer\n }\n return [hook.state, hook.queue.dispatch] as [S, (a: A) => void]\n },\n\n useEffect(create: () => any, deps?: ReadonlyArray<unknown>) {\n const hook = nextHook()\n const fiber = getCurrentFiber()\n const prevDeps = hook.deps\n if (prevDeps !== undefined && depsEqual(prevDeps, deps)) return\n hook.deps = deps\n const effect: Effect = {\n tag: 'effect',\n create: () => {\n // Run the prior cleanup INSIDE the effect run, not during the\n // dispatch/render phase. If render A \u2192 B \u2192 C all happen back-to-\n // back before the passive microtask drains, dispatch-time cleanup\n // only fires once (between A\u2192B) and effects B + C both run fresh,\n // leaving two side-effects (e.g. two plot SVGs) in the DOM. Doing\n // it here, at effect-run time, means every new create first tears\n // down whatever cleanup is currently live on the hook.\n if (hook.cleanup) {\n try { hook.cleanup() } catch {}\n // The prior cleanup was also pushed onto fiber.cleanups; remove\n // it so unmount doesn't double-call it.\n if (fiber.cleanups) {\n const i = fiber.cleanups.indexOf(hook.cleanup)\n if (i >= 0) fiber.cleanups.splice(i, 1)\n }\n hook.cleanup = null\n }\n const c = create()\n hook.cleanup = typeof c === 'function' ? c : null\n return hook.cleanup\n },\n destroy: undefined,\n deps,\n }\n enqueueEffect(fiber, effect)\n },\n\n useLayoutEffect(create: () => any, deps?: ReadonlyArray<unknown>) {\n const hook = nextHook()\n const fiber = getCurrentFiber()\n const prevDeps = hook.deps\n if (prevDeps !== undefined && depsEqual(prevDeps, deps)) return\n hook.deps = deps\n const effect: Effect = {\n tag: 'layout',\n create: () => {\n // Mirror useEffect: tear down the prior cleanup at run time so\n // coalesced renders don't leak side-effects.\n if (hook.cleanup) {\n try { hook.cleanup() } catch {}\n if (fiber.cleanups) {\n const i = fiber.cleanups.indexOf(hook.cleanup)\n if (i >= 0) fiber.cleanups.splice(i, 1)\n }\n hook.cleanup = null\n }\n const c = create()\n hook.cleanup = typeof c === 'function' ? c : null\n return hook.cleanup\n },\n destroy: undefined,\n deps,\n }\n enqueueEffect(fiber, effect)\n },\n\n useInsertionEffect(create: () => any, deps?: ReadonlyArray<unknown>) {\n return this.useLayoutEffect(create, deps)\n },\n\n useRef<T>(initial: T) {\n const hook = nextHook()\n if (hook.state === undefined) hook.state = { current: initial }\n return hook.state as { current: T }\n },\n\n useMemo<T>(factory: () => T, deps?: ReadonlyArray<unknown>) {\n const hook = nextHook()\n if (hook.deps !== undefined && depsEqual(hook.deps, deps)) {\n return hook.state as T\n }\n const value = factory()\n hook.state = value\n hook.deps = deps\n return value\n },\n\n useCallback<T extends Function>(fn: T, deps?: ReadonlyArray<unknown>): T {\n return this.useMemo(() => fn, deps) as T\n },\n\n useContext<T>(ctx: any): T {\n const fiber = getCurrentFiber()\n return readContext(fiber, ctx)\n },\n\n useImperativeHandle<T>(ref: any, factory: () => T, deps?: ReadonlyArray<unknown>) {\n const hook = nextHook()\n if (hook.deps !== undefined && depsEqual(hook.deps, deps)) return\n hook.deps = deps\n const value = factory()\n if (ref) {\n if (typeof ref === 'function') ref(value)\n else ref.current = value\n }\n },\n\n useDebugValue<T>(_value: T, _formatter?: (v: T) => any): void {\n // noop\n },\n\n useId(): string {\n const hook = nextHook()\n if (hook.state === undefined) {\n const fiber = getCurrentFiber()\n const root = findRootFromFiber(fiber)\n hook.state = (root?.identifierPrefix ?? ':r') + (idCounter++).toString(36)\n }\n return hook.state as string\n },\n\n useTransition(): [boolean, (fn: () => void) => void] {\n return [false, (fn: () => void) => fn()]\n },\n\n useDeferredValue<T>(v: T): T {\n return v\n },\n\n useSyncExternalStore<T>(\n subscribe: (cb: () => void) => () => void,\n getSnapshot: () => T,\n getServerSnapshot?: () => T,\n ): T {\n const fiber = getCurrentFiber()\n const hook = nextHook()\n\n // During hydration, use the server snapshot (if provided) so the tree\n // matches the SSR output. Components like TanStack Router's ClientOnly\n // rely on this: they render `false` on server, `true` on client \u2014 and\n // if we return `true` during hydration, client and server diverge and\n // the tree mounts fresh next to the SSR fallback DOM.\n const root = fiber.root ?? findRootFromFiber(fiber)\n const isHydrating = Boolean(root?.hydrating)\n const value =\n isHydrating && getServerSnapshot ? getServerSnapshot() : getSnapshot()\n hook.state = value\n\n if (hook.cleanup == null) {\n const forceUpdate = () => {\n let next: T\n try {\n next = getSnapshot()\n } catch {\n scheduleUpdate(fiber)\n return\n }\n if (!Object.is(hook.state, next)) {\n hook.state = next\n scheduleUpdate(fiber)\n }\n }\n const unsubscribe = subscribe(forceUpdate)\n hook.cleanup = unsubscribe\n // Register with fiber so unmountFiber runs it. Without this, the store\n // keeps holding forceUpdate and every store update schedules an already-\n // unmounted fiber \u2014 its rerender walks stale .parent pointers and mounts\n // zombie DOM into the old parent.\n if (typeof unsubscribe === 'function') {\n fiber.cleanups ||= []\n fiber.cleanups.push(unsubscribe)\n }\n\n // If we served the server snapshot, run a post-hydration check so\n // components like `useHydrated()` flip from false \u2192 true after the\n // initial render commits. Queued late so hydration finishes first.\n if (isHydrating && getServerSnapshot) {\n queueMicrotask(() => queueMicrotask(forceUpdate))\n }\n }\n return value\n },\n\n use<T>(resource: any): T {\n if (resource == null) throw new Error('use() received null or undefined')\n if (resource.$$typeof === REACT_CONTEXT_TYPE) {\n return readContext(getCurrentFiber(), resource)\n }\n if (typeof resource.then === 'function') {\n const thenable = resource\n switch (thenable.status) {\n case 'fulfilled':\n return thenable.value\n case 'rejected':\n throw thenable.reason\n default: {\n if (thenable.status === undefined) {\n thenable.status = 'pending'\n thenable.then(\n (v: any) => {\n if (thenable.status === 'pending') {\n thenable.status = 'fulfilled'\n thenable.value = v\n }\n },\n (e: any) => {\n if (thenable.status === 'pending') {\n thenable.status = 'rejected'\n thenable.reason = e\n }\n },\n )\n }\n throw thenable\n }\n }\n }\n throw new Error('use() expected a Promise or Context')\n },\n }\n}\n\nfunction basicReducer<S>(state: S, action: S | ((p: S) => S)): S {\n return typeof action === 'function' ? (action as (p: S) => S)(state) : action\n}\n\nlet idCounter = 0\n\nfunction findRootFromFiber(fiber: Fiber): FiberRoot | null {\n let f: Fiber | null = fiber\n while (f) {\n if (f.root) return f.root\n f = f.parent\n }\n return null\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": ["h"]
|
|
7
7
|
}
|
|
@@ -7,18 +7,18 @@ var GUARD_WINDOW_MS = 3e3;
|
|
|
7
7
|
function installHydrationScrollGuard() {
|
|
8
8
|
if (typeof window === "undefined") return;
|
|
9
9
|
const w = window;
|
|
10
|
-
if (w.
|
|
11
|
-
w.
|
|
10
|
+
if (w.__redactScrollGuardInstalled) return;
|
|
11
|
+
w.__redactScrollGuardInstalled = true;
|
|
12
12
|
const guardStartedAt = performance.now();
|
|
13
13
|
let lastUserScrollAt = 0;
|
|
14
14
|
let programmatic = 0;
|
|
15
|
-
w.
|
|
15
|
+
w.__redactScrollLog = [];
|
|
16
16
|
window.addEventListener(
|
|
17
17
|
"scroll",
|
|
18
18
|
() => {
|
|
19
19
|
if (programmatic === 0) {
|
|
20
20
|
lastUserScrollAt = performance.now();
|
|
21
|
-
w.
|
|
21
|
+
w.__redactScrollLog.push({ t: Math.round(lastUserScrollAt), ev: "user-scroll", y: window.scrollY });
|
|
22
22
|
}
|
|
23
23
|
},
|
|
24
24
|
{ capture: true, passive: true }
|
|
@@ -29,7 +29,7 @@ function installHydrationScrollGuard() {
|
|
|
29
29
|
const inGuardWindow = now - guardStartedAt < GUARD_WINDOW_MS;
|
|
30
30
|
const userScrolledRecently = lastUserScrollAt > 0 && now - lastUserScrollAt < 1500;
|
|
31
31
|
if (inGuardWindow && userScrolledRecently) {
|
|
32
|
-
w.
|
|
32
|
+
w.__redactScrollLog.push({
|
|
33
33
|
t: Math.round(now),
|
|
34
34
|
ev: "suppressed",
|
|
35
35
|
args: JSON.stringify(args).slice(0, 80),
|
|
@@ -38,7 +38,7 @@ function installHydrationScrollGuard() {
|
|
|
38
38
|
});
|
|
39
39
|
return;
|
|
40
40
|
}
|
|
41
|
-
w.
|
|
41
|
+
w.__redactScrollLog.push({
|
|
42
42
|
t: Math.round(now),
|
|
43
43
|
ev: "allowed",
|
|
44
44
|
args: JSON.stringify(args).slice(0, 80),
|
|
@@ -115,6 +115,7 @@ var HEAD_KEY_ATTRS = {
|
|
|
115
115
|
style: [],
|
|
116
116
|
title: []
|
|
117
117
|
};
|
|
118
|
+
var DOCUMENT_HEAD_TAGS = /* @__PURE__ */ new Set(["base", "link", "meta", "script", "style", "title"]);
|
|
118
119
|
var CLAIMED = /* @__PURE__ */ new WeakSet();
|
|
119
120
|
function headAttrsMatch(el, props, keys) {
|
|
120
121
|
if (CLAIMED.has(el)) return false;
|
|
@@ -179,11 +180,17 @@ function adoptHostDom(fiber, parent) {
|
|
|
179
180
|
const cursor = hydrationCursors.get(hostParent);
|
|
180
181
|
if (!cursor) return false;
|
|
181
182
|
const tag = fiber.type.toLowerCase();
|
|
183
|
+
const documentHeadParent = getDocumentHeadParent(cursor.parent, tag);
|
|
182
184
|
const parentEl = cursor.parent;
|
|
183
185
|
const parentTag = parentEl.nodeType === 1 ? parentEl.tagName.toLowerCase() : "";
|
|
184
|
-
const isHeadish = parentTag === "head" || parentTag === "html";
|
|
186
|
+
const isHeadish = parentTag === "head" || parentTag === "html" || !!documentHeadParent;
|
|
185
187
|
let candidate;
|
|
186
|
-
if (
|
|
188
|
+
if (documentHeadParent) {
|
|
189
|
+
candidate = new HydrationCursor(documentHeadParent).takeMatchingHeadElement(
|
|
190
|
+
tag,
|
|
191
|
+
fiber.pendingProps ?? {}
|
|
192
|
+
);
|
|
193
|
+
} else if (isHeadish) {
|
|
187
194
|
candidate = cursor.takeMatchingHeadElement(tag, fiber.pendingProps ?? {});
|
|
188
195
|
} else {
|
|
189
196
|
candidate = cursor.takeHostNode();
|
|
@@ -208,6 +215,10 @@ function adoptHostDom(fiber, parent) {
|
|
|
208
215
|
hydrationCursors.set(fiber, new HydrationCursor(candidate));
|
|
209
216
|
return true;
|
|
210
217
|
}
|
|
218
|
+
function getDocumentHeadParent(parent, tag) {
|
|
219
|
+
if (parent.nodeType !== 9 || !DOCUMENT_HEAD_TAGS.has(tag)) return null;
|
|
220
|
+
return parent.head;
|
|
221
|
+
}
|
|
211
222
|
function adoptTextDom(fiber, parent, text) {
|
|
212
223
|
const cursor = hydrationCursors.get(findHostParent(parent));
|
|
213
224
|
if (!cursor) return false;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../src/dom/features/hydration/full.ts"],
|
|
4
|
-
"sourcesContent": ["import { FiberTag, type Fiber, type FiberRoot } from '../../../core'\nimport { setProp } from '../../dom'\nimport { findRoot } from '../../reconcile'\n\n// Re-export from event-replay so all hydration concerns live behind one\n// feature boundary \u2014 the plugin's stub swap strips drainReplayQueue too.\nexport { drainReplayQueue } from '../../event-replay'\n\nconst GUARD_WINDOW_MS = 3000\n\n/**\n * Preserve the user's scroll position across hydration. If the user scrolled\n * between SSR paint and hydrate (common in dev where JS takes seconds to\n * load), libraries that wire scroll-restoration into a `useLayoutEffect`\n * near the root (e.g. TanStack Router) will run during our synchronous\n * hydrate and call `window.scrollTo(savedFromLastVisit)` \u2014 overwriting the\n * user's fresh scroll. We install a short-lived wrapper around scrollTo that\n * suppresses programmatic calls when a user-initiated scroll happened\n * recently. Only runs in the hydration feature \u2014 the stub skips it.\n */\nexport function installHydrationScrollGuard(): void {\n if (typeof window === 'undefined') return\n const w = window as any\n if (w.__tdomScrollGuardInstalled) return\n w.__tdomScrollGuardInstalled = true\n const guardStartedAt = performance.now()\n let lastUserScrollAt = 0\n let programmatic = 0\n w.__tdomScrollLog = []\n window.addEventListener(\n 'scroll',\n () => {\n if (programmatic === 0) {\n lastUserScrollAt = performance.now()\n w.__tdomScrollLog.push({ t: Math.round(lastUserScrollAt), ev: 'user-scroll', y: window.scrollY })\n }\n },\n { capture: true, passive: true },\n )\n const origScrollTo = window.scrollTo.bind(window)\n window.scrollTo = function (this: any, ...args: any[]) {\n const now = performance.now()\n const inGuardWindow = now - guardStartedAt < GUARD_WINDOW_MS\n const userScrolledRecently = lastUserScrollAt > 0 && now - lastUserScrollAt < 1500\n if (inGuardWindow && userScrolledRecently) {\n w.__tdomScrollLog.push({\n t: Math.round(now),\n ev: 'suppressed',\n args: JSON.stringify(args).slice(0, 80),\n tSinceHydrate: Math.round(now - guardStartedAt),\n tSinceUserScroll: Math.round(now - lastUserScrollAt),\n })\n return\n }\n w.__tdomScrollLog.push({\n t: Math.round(now),\n ev: 'allowed',\n args: JSON.stringify(args).slice(0, 80),\n tSinceHydrate: Math.round(now - guardStartedAt),\n inGuard: inGuardWindow,\n userScrolled: userScrolledRecently,\n })\n programmatic++\n try {\n return (origScrollTo as any).apply(this, args)\n } finally {\n queueMicrotask(() => {\n programmatic = Math.max(0, programmatic - 1)\n })\n }\n }\n}\n\n/**\n * Hydration cursor: walks existing DOM children in document order so we can\n * adopt them during fiber tree construction. One cursor per host parent.\n *\n * `endBefore` scopes the cursor to a subrange \u2014 used by rehydrateBoundary()\n * so we only adopt DOM up to the closing `/$` marker for that boundary.\n */\nexport class HydrationCursor {\n next: ChildNode | null\n parent: Node\n endBefore: ChildNode | null\n constructor(parent: Node, start: ChildNode | null = null, endBefore: ChildNode | null = null) {\n this.parent = parent\n this.next = start ?? parent.firstChild\n this.endBefore = endBefore\n }\n takeHostNode(): ChildNode | null {\n while (this.next && this.next !== this.endBefore) {\n const n = this.next\n // Skip anything that isn't an element (1) or text (3):\n // comments (8), doctype (10), processing instructions (7), cdata (4).\n if (n.nodeType !== 1 && n.nodeType !== 3) {\n this.next = n.nextSibling\n continue\n }\n this.next = n.nextSibling\n return n\n }\n return null\n }\n /**\n * Position-insensitive lookup for head/html adoption. Scans forward past\n * non-matching nodes without removing them, matching by tag AND the key\n * attributes that identify head elements uniquely (rel/href for links,\n * name/property for meta, src for script). Non-matching nodes stay in\n * place so the SSR'd stylesheet/script order is preserved.\n */\n takeMatchingHeadElement(tag: string, props: Record<string, any>): ChildNode | null {\n const target = tag.toLowerCase()\n const keyAttrs = HEAD_KEY_ATTRS[target] ?? []\n let scan = this.parent.firstChild\n while (scan) {\n if (\n scan.nodeType === 1 &&\n (scan as Element).tagName.toLowerCase() === target &&\n headAttrsMatch(scan as Element, props, keyAttrs)\n ) {\n CLAIMED.add(scan)\n return scan\n }\n scan = scan.nextSibling\n }\n return null\n }\n remaining(): ChildNode[] {\n const out: ChildNode[] = []\n let n = this.next\n while (n && n !== this.endBefore) {\n out.push(n)\n n = n.nextSibling\n }\n return out\n }\n}\n\nconst hydrationCursors = new WeakMap<Fiber, HydrationCursor>()\n\n// Head elements that we match against server DOM by attribute signature.\nconst HEAD_KEY_ATTRS: Record<string, ReadonlyArray<string>> = {\n link: ['rel', 'href', 'sizes', 'type'],\n meta: ['name', 'property', 'charset', 'http-equiv'],\n script: ['src', 'type'],\n style: [],\n title: [],\n}\n\n// DOM elements already claimed by some fiber during this hydration pass.\nconst CLAIMED = new WeakSet<Node>()\n\nfunction headAttrsMatch(\n el: Element,\n props: Record<string, any>,\n keys: ReadonlyArray<string>,\n): boolean {\n if (CLAIMED.has(el)) return false\n if (keys.length === 0) return true\n for (const k of keys) {\n const propVal = props[k] ?? (k === 'http-equiv' ? props.httpEquiv : undefined)\n const elVal = el.getAttribute(k)\n // If neither defines it, skip this key; if one defines it, they must match.\n if (propVal == null && elVal == null) continue\n if (propVal == null || elVal == null) continue // tolerate missing on either side\n if (String(propVal) !== elVal) return false\n }\n // At least one matching signal must be present.\n return keys.some((k) => props[k] != null || el.hasAttribute(k))\n}\n\nexport function beginHydration(root: FiberRoot): void {\n root.hydrating = true\n hydrationCursors.set(root.current, new HydrationCursor(root.container))\n}\n\nexport function endHydration(root: FiberRoot): void {\n root.hydrating = false\n hydrationCursors.delete(root.current)\n}\n\n/**\n * Inspect the current cursor position for a streaming-suspense boundary\n * marker emitted by the server. Returns info + advances the cursor past the\n * marker pair (start comment + fallback/real content + end comment).\n */\nexport interface BoundaryInfo {\n kind: 'pending' | 'resolved'\n id: number\n startMark: Comment\n endMark: Comment\n}\n\nexport function tryConsumeBoundary(parent: Fiber): BoundaryInfo | null {\n const cursor = hydrationCursors.get(findHostParent(parent))\n if (!cursor) return null\n const peek = cursor.next\n if (!peek || peek.nodeType !== 8) return null\n const data = (peek as Comment).data\n const m = /^(\\$\\??)(\\d+)$/.exec(data)\n if (!m) return null\n const kind = m[1] === '$?' ? 'pending' : 'resolved'\n const id = Number(m[2])\n const startMark = peek as Comment\n // Advance past the start comment\n cursor.next = startMark.nextSibling\n // Locate end comment: closest <!--/$-->\n let endMark: Comment | null = null\n let scan = startMark.nextSibling\n while (scan) {\n if (scan.nodeType === 8 && (scan as Comment).data === '/$') {\n endMark = scan as Comment\n break\n }\n scan = scan.nextSibling\n }\n if (!endMark) return null\n return { kind, id, startMark, endMark }\n}\n\nexport function advanceCursorPast(parent: Fiber, node: Node): void {\n const cursor = hydrationCursors.get(findHostParent(parent))\n if (!cursor) return\n cursor.next = node.nextSibling\n}\n\nexport function getHydrationCursor(hostFiber: Fiber): HydrationCursor | undefined {\n return hydrationCursors.get(hostFiber)\n}\n\nexport function setHydrationCursor(hostFiber: Fiber, cursor: HydrationCursor): void {\n hydrationCursors.set(hostFiber, cursor)\n}\n\nexport function clearHydrationCursor(hostFiber: Fiber): void {\n hydrationCursors.delete(hostFiber)\n}\n\n/**\n * Try to adopt a DOM node for this host fiber. Returns true if adopted.\n * Attaches existing attrs/children via separate hydrate pass.\n */\nexport function adoptHostDom(fiber: Fiber, parent: Fiber): boolean {\n const hostParent = findHostParent(parent)\n const cursor = hydrationCursors.get(hostParent)\n if (!cursor) return false\n\n const tag = (fiber.type as string).toLowerCase()\n const parentEl = cursor.parent as Element\n const parentTag =\n parentEl.nodeType === 1 ? (parentEl as Element).tagName.toLowerCase() : ''\n const isHeadish = parentTag === 'head' || parentTag === 'html'\n\n let candidate: ChildNode | null\n if (isHeadish) {\n // Head/html children are position-insensitive \u2014 server may emit them in\n // a different order than the React tree (React 19 head hoisting, etc.).\n // Scan forward without removing non-matching nodes; match on attribute\n // signature so we don't adopt the wrong <link> and clobber its props.\n candidate = cursor.takeMatchingHeadElement(tag, fiber.pendingProps ?? {})\n } else {\n candidate = cursor.takeHostNode()\n }\n\n if (!candidate) {\n // Client expected a host here but the cursor is exhausted \u2014 server gave\n // fewer children than the client tree. Report the structural gap (React\n // fires `onRecoverableError` for this exact case) and let the reconciler\n // mount a fresh DOM for this fiber below.\n // Exception: <head> children are position-insensitive; a missing match\n // there means \"server didn't hoist this one yet\", which we silently mount.\n if (!isHeadish) onMismatch(fiber, null)\n return false\n }\n\n if (candidate.nodeType !== 1 || (candidate as Element).tagName.toLowerCase() !== tag) {\n // mismatch \u2014 log and re-render fresh from this point\n onMismatch(fiber, candidate)\n return false\n }\n fiber.dom = candidate\n // Apply props (attach events, sync IDL props). Don't re-set existing attrs.\n const props = fiber.pendingProps ?? {}\n const isSvg =\n tag === 'svg' ||\n ((candidate as Element).namespaceURI === 'http://www.w3.org/2000/svg' &&\n tag !== 'foreignobject')\n for (const k in props) {\n if (k === 'children') continue\n if (k[0] === 'o' && k[1] === 'n' && typeof props[k] === 'function') {\n setProp(candidate as Element, k, props[k], undefined, isSvg)\n }\n // Non-event props: trust the server HTML, skip\n }\n // Set up child cursor for this host's children\n hydrationCursors.set(fiber, new HydrationCursor(candidate))\n return true\n}\n\nexport function adoptTextDom(fiber: Fiber, parent: Fiber, text: string): boolean {\n const cursor = hydrationCursors.get(findHostParent(parent))\n if (!cursor) return false\n const candidate = cursor.takeHostNode()\n if (!candidate) return false\n if (candidate.nodeType === 3) {\n if ((candidate as Text).data !== text) {\n ;(candidate as Text).data = text\n }\n fiber.dom = candidate\n return true\n }\n onMismatch(fiber, candidate)\n return false\n}\n\nexport function findHostParent(fiber: Fiber): Fiber {\n let f: Fiber | null = fiber\n while (f) {\n // A fiber explicitly holding a cursor acts as a boundary for hydration\n // (e.g. Suspense with a scoped cursor during fallback/boundary hydration).\n if (hydrationCursors.has(f)) return f\n if (f.tag === FiberTag.Host || f.tag === FiberTag.Root || f.tag === FiberTag.Portal) {\n return f\n }\n f = f.parent\n }\n throw new Error('No host parent found')\n}\n\nfunction onMismatch(fiber: Fiber, actualNode: ChildNode | null): void {\n // For v1: log and exit hydration for this subtree. The normal reconciler\n // will create a fresh DOM node below.\n const root = findRoot(fiber)\n if (root?.onRecoverableError) {\n root.onRecoverableError(\n new Error(\n `Hydration mismatch: expected <${(fiber.type as string) ?? 'text'}> but found ${\n actualNode ? (actualNode.nodeType === 1 ? (actualNode as Element).tagName : 'text') : 'nothing'\n }.`,\n ),\n )\n }\n // Remove stale DOM if still there\n if (actualNode && actualNode.parentNode) actualNode.parentNode.removeChild(actualNode)\n}\n"],
|
|
5
|
-
"mappings": ";AAAA,SAAS,gBAA4C;AACrD,SAAS,eAAe;AACxB,SAAS,gBAAgB;AAIzB,SAAS,wBAAwB;AAEjC,IAAM,kBAAkB;AAYjB,SAAS,8BAAoC;AAClD,MAAI,OAAO,WAAW,YAAa;AACnC,QAAM,IAAI;AACV,MAAI,EAAE,
|
|
4
|
+
"sourcesContent": ["import { FiberTag, type Fiber, type FiberRoot } from '../../../core'\nimport { setProp } from '../../dom'\nimport { findRoot } from '../../reconcile'\n\n// Re-export from event-replay so all hydration concerns live behind one\n// feature boundary \u2014 the plugin's stub swap strips drainReplayQueue too.\nexport { drainReplayQueue } from '../../event-replay'\n\nconst GUARD_WINDOW_MS = 3000\n\n/**\n * Preserve the user's scroll position across hydration. If the user scrolled\n * between SSR paint and hydrate (common in dev where JS takes seconds to\n * load), libraries that wire scroll-restoration into a `useLayoutEffect`\n * near the root (e.g. TanStack Router) will run during our synchronous\n * hydrate and call `window.scrollTo(savedFromLastVisit)` \u2014 overwriting the\n * user's fresh scroll. We install a short-lived wrapper around scrollTo that\n * suppresses programmatic calls when a user-initiated scroll happened\n * recently. Only runs in the hydration feature \u2014 the stub skips it.\n */\nexport function installHydrationScrollGuard(): void {\n if (typeof window === 'undefined') return\n const w = window as any\n if (w.__redactScrollGuardInstalled) return\n w.__redactScrollGuardInstalled = true\n const guardStartedAt = performance.now()\n let lastUserScrollAt = 0\n let programmatic = 0\n w.__redactScrollLog = []\n window.addEventListener(\n 'scroll',\n () => {\n if (programmatic === 0) {\n lastUserScrollAt = performance.now()\n w.__redactScrollLog.push({ t: Math.round(lastUserScrollAt), ev: 'user-scroll', y: window.scrollY })\n }\n },\n { capture: true, passive: true },\n )\n const origScrollTo = window.scrollTo.bind(window)\n window.scrollTo = function (this: any, ...args: any[]) {\n const now = performance.now()\n const inGuardWindow = now - guardStartedAt < GUARD_WINDOW_MS\n const userScrolledRecently = lastUserScrollAt > 0 && now - lastUserScrollAt < 1500\n if (inGuardWindow && userScrolledRecently) {\n w.__redactScrollLog.push({\n t: Math.round(now),\n ev: 'suppressed',\n args: JSON.stringify(args).slice(0, 80),\n tSinceHydrate: Math.round(now - guardStartedAt),\n tSinceUserScroll: Math.round(now - lastUserScrollAt),\n })\n return\n }\n w.__redactScrollLog.push({\n t: Math.round(now),\n ev: 'allowed',\n args: JSON.stringify(args).slice(0, 80),\n tSinceHydrate: Math.round(now - guardStartedAt),\n inGuard: inGuardWindow,\n userScrolled: userScrolledRecently,\n })\n programmatic++\n try {\n return (origScrollTo as any).apply(this, args)\n } finally {\n queueMicrotask(() => {\n programmatic = Math.max(0, programmatic - 1)\n })\n }\n }\n}\n\n/**\n * Hydration cursor: walks existing DOM children in document order so we can\n * adopt them during fiber tree construction. One cursor per host parent.\n *\n * `endBefore` scopes the cursor to a subrange \u2014 used by rehydrateBoundary()\n * so we only adopt DOM up to the closing `/$` marker for that boundary.\n */\nexport class HydrationCursor {\n next: ChildNode | null\n parent: Node\n endBefore: ChildNode | null\n constructor(parent: Node, start: ChildNode | null = null, endBefore: ChildNode | null = null) {\n this.parent = parent\n this.next = start ?? parent.firstChild\n this.endBefore = endBefore\n }\n takeHostNode(): ChildNode | null {\n while (this.next && this.next !== this.endBefore) {\n const n = this.next\n // Skip anything that isn't an element (1) or text (3):\n // comments (8), doctype (10), processing instructions (7), cdata (4).\n if (n.nodeType !== 1 && n.nodeType !== 3) {\n this.next = n.nextSibling\n continue\n }\n this.next = n.nextSibling\n return n\n }\n return null\n }\n /**\n * Position-insensitive lookup for head/html adoption. Scans forward past\n * non-matching nodes without removing them, matching by tag AND the key\n * attributes that identify head elements uniquely (rel/href for links,\n * name/property for meta, src for script). Non-matching nodes stay in\n * place so the SSR'd stylesheet/script order is preserved.\n */\n takeMatchingHeadElement(tag: string, props: Record<string, any>): ChildNode | null {\n const target = tag.toLowerCase()\n const keyAttrs = HEAD_KEY_ATTRS[target] ?? []\n let scan = this.parent.firstChild\n while (scan) {\n if (\n scan.nodeType === 1 &&\n (scan as Element).tagName.toLowerCase() === target &&\n headAttrsMatch(scan as Element, props, keyAttrs)\n ) {\n CLAIMED.add(scan)\n return scan\n }\n scan = scan.nextSibling\n }\n return null\n }\n remaining(): ChildNode[] {\n const out: ChildNode[] = []\n let n = this.next\n while (n && n !== this.endBefore) {\n out.push(n)\n n = n.nextSibling\n }\n return out\n }\n}\n\nconst hydrationCursors = new WeakMap<Fiber, HydrationCursor>()\n\n// Head elements that we match against server DOM by attribute signature.\nconst HEAD_KEY_ATTRS: Record<string, ReadonlyArray<string>> = {\n link: ['rel', 'href', 'sizes', 'type'],\n meta: ['name', 'property', 'charset', 'http-equiv'],\n script: ['src', 'type'],\n style: [],\n title: [],\n}\n\nconst DOCUMENT_HEAD_TAGS = new Set(['base', 'link', 'meta', 'script', 'style', 'title'])\n\n// DOM elements already claimed by some fiber during this hydration pass.\nconst CLAIMED = new WeakSet<Node>()\n\nfunction headAttrsMatch(\n el: Element,\n props: Record<string, any>,\n keys: ReadonlyArray<string>,\n): boolean {\n if (CLAIMED.has(el)) return false\n if (keys.length === 0) return true\n for (const k of keys) {\n const propVal = props[k] ?? (k === 'http-equiv' ? props.httpEquiv : undefined)\n const elVal = el.getAttribute(k)\n // If neither defines it, skip this key; if one defines it, they must match.\n if (propVal == null && elVal == null) continue\n if (propVal == null || elVal == null) continue // tolerate missing on either side\n if (String(propVal) !== elVal) return false\n }\n // At least one matching signal must be present.\n return keys.some((k) => props[k] != null || el.hasAttribute(k))\n}\n\nexport function beginHydration(root: FiberRoot): void {\n root.hydrating = true\n hydrationCursors.set(root.current, new HydrationCursor(root.container))\n}\n\nexport function endHydration(root: FiberRoot): void {\n root.hydrating = false\n hydrationCursors.delete(root.current)\n}\n\n/**\n * Inspect the current cursor position for a streaming-suspense boundary\n * marker emitted by the server. Returns info + advances the cursor past the\n * marker pair (start comment + fallback/real content + end comment).\n */\nexport interface BoundaryInfo {\n kind: 'pending' | 'resolved'\n id: number\n startMark: Comment\n endMark: Comment\n}\n\nexport function tryConsumeBoundary(parent: Fiber): BoundaryInfo | null {\n const cursor = hydrationCursors.get(findHostParent(parent))\n if (!cursor) return null\n const peek = cursor.next\n if (!peek || peek.nodeType !== 8) return null\n const data = (peek as Comment).data\n const m = /^(\\$\\??)(\\d+)$/.exec(data)\n if (!m) return null\n const kind = m[1] === '$?' ? 'pending' : 'resolved'\n const id = Number(m[2])\n const startMark = peek as Comment\n // Advance past the start comment\n cursor.next = startMark.nextSibling\n // Locate end comment: closest <!--/$-->\n let endMark: Comment | null = null\n let scan = startMark.nextSibling\n while (scan) {\n if (scan.nodeType === 8 && (scan as Comment).data === '/$') {\n endMark = scan as Comment\n break\n }\n scan = scan.nextSibling\n }\n if (!endMark) return null\n return { kind, id, startMark, endMark }\n}\n\nexport function advanceCursorPast(parent: Fiber, node: Node): void {\n const cursor = hydrationCursors.get(findHostParent(parent))\n if (!cursor) return\n cursor.next = node.nextSibling\n}\n\nexport function getHydrationCursor(hostFiber: Fiber): HydrationCursor | undefined {\n return hydrationCursors.get(hostFiber)\n}\n\nexport function setHydrationCursor(hostFiber: Fiber, cursor: HydrationCursor): void {\n hydrationCursors.set(hostFiber, cursor)\n}\n\nexport function clearHydrationCursor(hostFiber: Fiber): void {\n hydrationCursors.delete(hostFiber)\n}\n\n/**\n * Try to adopt a DOM node for this host fiber. Returns true if adopted.\n * Attaches existing attrs/children via separate hydrate pass.\n */\nexport function adoptHostDom(fiber: Fiber, parent: Fiber): boolean {\n const hostParent = findHostParent(parent)\n const cursor = hydrationCursors.get(hostParent)\n if (!cursor) return false\n\n const tag = (fiber.type as string).toLowerCase()\n const documentHeadParent = getDocumentHeadParent(cursor.parent, tag)\n const parentEl = cursor.parent as Element\n const parentTag =\n parentEl.nodeType === 1 ? (parentEl as Element).tagName.toLowerCase() : ''\n const isHeadish = parentTag === 'head' || parentTag === 'html' || !!documentHeadParent\n\n let candidate: ChildNode | null\n if (documentHeadParent) {\n // React 19 can project <meta>/<title>/<link> from anywhere in the tree into\n // document.head. Redact does not have that projection yet, so when a\n // document-root hydration pass sees a top-level head element, adopt it\n // from <head> rather than trying to append it beside <html>.\n candidate = new HydrationCursor(documentHeadParent).takeMatchingHeadElement(\n tag,\n fiber.pendingProps ?? {},\n )\n } else if (isHeadish) {\n // Head/html children are position-insensitive \u2014 server may emit them in\n // a different order than the React tree (React 19 head hoisting, etc.).\n // Scan forward without removing non-matching nodes; match on attribute\n // signature so we don't adopt the wrong <link> and clobber its props.\n candidate = cursor.takeMatchingHeadElement(tag, fiber.pendingProps ?? {})\n } else {\n candidate = cursor.takeHostNode()\n }\n\n if (!candidate) {\n // Client expected a host here but the cursor is exhausted \u2014 server gave\n // fewer children than the client tree. Report the structural gap (React\n // fires `onRecoverableError` for this exact case) and let the reconciler\n // mount a fresh DOM for this fiber below.\n // Exception: <head> children are position-insensitive; a missing match\n // there means \"server didn't hoist this one yet\", which we silently mount.\n if (!isHeadish) onMismatch(fiber, null)\n return false\n }\n\n if (candidate.nodeType !== 1 || (candidate as Element).tagName.toLowerCase() !== tag) {\n // mismatch \u2014 log and re-render fresh from this point\n onMismatch(fiber, candidate)\n return false\n }\n fiber.dom = candidate\n // Apply props (attach events, sync IDL props). Don't re-set existing attrs.\n const props = fiber.pendingProps ?? {}\n const isSvg =\n tag === 'svg' ||\n ((candidate as Element).namespaceURI === 'http://www.w3.org/2000/svg' &&\n tag !== 'foreignobject')\n for (const k in props) {\n if (k === 'children') continue\n if (k[0] === 'o' && k[1] === 'n' && typeof props[k] === 'function') {\n setProp(candidate as Element, k, props[k], undefined, isSvg)\n }\n // Non-event props: trust the server HTML, skip\n }\n // Set up child cursor for this host's children\n hydrationCursors.set(fiber, new HydrationCursor(candidate))\n return true\n}\n\nfunction getDocumentHeadParent(parent: Node, tag: string): HTMLHeadElement | null {\n if (parent.nodeType !== 9 || !DOCUMENT_HEAD_TAGS.has(tag)) return null\n return (parent as Document).head\n}\n\nexport function adoptTextDom(fiber: Fiber, parent: Fiber, text: string): boolean {\n const cursor = hydrationCursors.get(findHostParent(parent))\n if (!cursor) return false\n const candidate = cursor.takeHostNode()\n if (!candidate) return false\n if (candidate.nodeType === 3) {\n if ((candidate as Text).data !== text) {\n ;(candidate as Text).data = text\n }\n fiber.dom = candidate\n return true\n }\n onMismatch(fiber, candidate)\n return false\n}\n\nexport function findHostParent(fiber: Fiber): Fiber {\n let f: Fiber | null = fiber\n while (f) {\n // A fiber explicitly holding a cursor acts as a boundary for hydration\n // (e.g. Suspense with a scoped cursor during fallback/boundary hydration).\n if (hydrationCursors.has(f)) return f\n if (f.tag === FiberTag.Host || f.tag === FiberTag.Root || f.tag === FiberTag.Portal) {\n return f\n }\n f = f.parent\n }\n throw new Error('No host parent found')\n}\n\nfunction onMismatch(fiber: Fiber, actualNode: ChildNode | null): void {\n // For v1: log and exit hydration for this subtree. The normal reconciler\n // will create a fresh DOM node below.\n const root = findRoot(fiber)\n if (root?.onRecoverableError) {\n root.onRecoverableError(\n new Error(\n `Hydration mismatch: expected <${(fiber.type as string) ?? 'text'}> but found ${\n actualNode ? (actualNode.nodeType === 1 ? (actualNode as Element).tagName : 'text') : 'nothing'\n }.`,\n ),\n )\n }\n // Remove stale DOM if still there\n if (actualNode && actualNode.parentNode) actualNode.parentNode.removeChild(actualNode)\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
package/dist/dom/index.d.ts
CHANGED
|
@@ -7,6 +7,21 @@ export declare function preload(_href: string, _opts?: any): void;
|
|
|
7
7
|
export declare function preinit(_href: string, _opts?: any): void;
|
|
8
8
|
export declare function preloadModule(_href: string, _opts?: any): void;
|
|
9
9
|
export declare function preinitModule(_href: string, _opts?: any): void;
|
|
10
|
+
export declare const __DOM_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE: {
|
|
11
|
+
d: {
|
|
12
|
+
f(): void;
|
|
13
|
+
r(): void;
|
|
14
|
+
D(): void;
|
|
15
|
+
C(): void;
|
|
16
|
+
L(): void;
|
|
17
|
+
m(): void;
|
|
18
|
+
X(): void;
|
|
19
|
+
S(): void;
|
|
20
|
+
M(): void;
|
|
21
|
+
};
|
|
22
|
+
p: number;
|
|
23
|
+
findDOMNode: any;
|
|
24
|
+
};
|
|
10
25
|
export declare const version = "19.2.3";
|
|
11
26
|
import { flushSync, batchedUpdates } from './root';
|
|
12
27
|
import { createPortal } from './portal';
|
|
@@ -20,6 +35,21 @@ declare const _default: {
|
|
|
20
35
|
preinit: typeof preinit;
|
|
21
36
|
preloadModule: typeof preloadModule;
|
|
22
37
|
preinitModule: typeof preinitModule;
|
|
38
|
+
__DOM_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE: {
|
|
39
|
+
d: {
|
|
40
|
+
f(): void;
|
|
41
|
+
r(): void;
|
|
42
|
+
D(): void;
|
|
43
|
+
C(): void;
|
|
44
|
+
L(): void;
|
|
45
|
+
m(): void;
|
|
46
|
+
X(): void;
|
|
47
|
+
S(): void;
|
|
48
|
+
M(): void;
|
|
49
|
+
};
|
|
50
|
+
p: number;
|
|
51
|
+
findDOMNode: any;
|
|
52
|
+
};
|
|
23
53
|
version: string;
|
|
24
54
|
};
|
|
25
55
|
export default _default;
|
package/dist/dom/index.js
CHANGED
|
@@ -16,6 +16,30 @@ function preloadModule(_href, _opts) {
|
|
|
16
16
|
}
|
|
17
17
|
function preinitModule(_href, _opts) {
|
|
18
18
|
}
|
|
19
|
+
var __DOM_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE = {
|
|
20
|
+
d: {
|
|
21
|
+
f() {
|
|
22
|
+
},
|
|
23
|
+
r() {
|
|
24
|
+
},
|
|
25
|
+
D() {
|
|
26
|
+
},
|
|
27
|
+
C() {
|
|
28
|
+
},
|
|
29
|
+
L() {
|
|
30
|
+
},
|
|
31
|
+
m() {
|
|
32
|
+
},
|
|
33
|
+
X() {
|
|
34
|
+
},
|
|
35
|
+
S() {
|
|
36
|
+
},
|
|
37
|
+
M() {
|
|
38
|
+
}
|
|
39
|
+
},
|
|
40
|
+
p: 0,
|
|
41
|
+
findDOMNode: null
|
|
42
|
+
};
|
|
19
43
|
var version = "19.2.3";
|
|
20
44
|
var dom_default = {
|
|
21
45
|
flushSync: flushSync2,
|
|
@@ -27,9 +51,11 @@ var dom_default = {
|
|
|
27
51
|
preinit,
|
|
28
52
|
preloadModule,
|
|
29
53
|
preinitModule,
|
|
54
|
+
__DOM_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE,
|
|
30
55
|
version: "19.2.3"
|
|
31
56
|
};
|
|
32
57
|
export {
|
|
58
|
+
__DOM_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE,
|
|
33
59
|
createPortal,
|
|
34
60
|
dom_default as default,
|
|
35
61
|
flushSync,
|
package/dist/dom/index.js.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/dom/index.ts"],
|
|
4
|
-
"sourcesContent": ["// Side-effect import: registers opt-in features (Portal, etc.) with the\n// reconciler. A vite plugin may alias individual feature modules to their\n// stub variants to strip them from the bundle.\nimport './features'\n\nexport { flushSync, batchedUpdates as unstable_batchedUpdates } from './root'\nexport { createPortal } from './portal'\n\n// Resource hints \u2014 stubs\nexport function preconnect(_href: string, _opts?: any): void {}\nexport function prefetchDNS(_href: string): void {}\nexport function preload(_href: string, _opts?: any): void {}\nexport function preinit(_href: string, _opts?: any): void {}\nexport function preloadModule(_href: string, _opts?: any): void {}\nexport function preinitModule(_href: string, _opts?: any): void {}\n\nexport const version = '19.2.3'\n\n// Required by React's default export consumers\nimport { flushSync, batchedUpdates } from './root'\nimport { createPortal } from './portal'\nexport default {\n flushSync,\n unstable_batchedUpdates: batchedUpdates,\n createPortal,\n preconnect,\n prefetchDNS,\n preload,\n preinit,\n preloadModule,\n preinitModule,\n version: '19.2.3',\n}\n"],
|
|
5
|
-
"mappings": ";AAGA,OAAO;AAEP,SAAS,WAA6B,sBAA+B;AACrE,SAAS,oBAAoB;
|
|
4
|
+
"sourcesContent": ["// Side-effect import: registers opt-in features (Portal, etc.) with the\n// reconciler. A vite plugin may alias individual feature modules to their\n// stub variants to strip them from the bundle.\nimport './features'\n\nexport { flushSync, batchedUpdates as unstable_batchedUpdates } from './root'\nexport { createPortal } from './portal'\n\n// Resource hints \u2014 stubs\nexport function preconnect(_href: string, _opts?: any): void {}\nexport function prefetchDNS(_href: string): void {}\nexport function preload(_href: string, _opts?: any): void {}\nexport function preinit(_href: string, _opts?: any): void {}\nexport function preloadModule(_href: string, _opts?: any): void {}\nexport function preinitModule(_href: string, _opts?: any): void {}\n\nexport const __DOM_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE = {\n d: {\n f() {},\n r() {},\n D() {},\n C() {},\n L() {},\n m() {},\n X() {},\n S() {},\n M() {},\n },\n p: 0,\n findDOMNode: null,\n}\n\nexport const version = '19.2.3'\n\n// Required by React's default export consumers\nimport { flushSync, batchedUpdates } from './root'\nimport { createPortal } from './portal'\nexport default {\n flushSync,\n unstable_batchedUpdates: batchedUpdates,\n createPortal,\n preconnect,\n prefetchDNS,\n preload,\n preinit,\n preloadModule,\n preinitModule,\n __DOM_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE,\n version: '19.2.3',\n}\n"],
|
|
5
|
+
"mappings": ";AAGA,OAAO;AAEP,SAAS,WAA6B,sBAA+B;AACrE,SAAS,oBAAoB;AA6B7B,SAAS,aAAAA,YAAW,kBAAAC,uBAAsB;AAC1C,SAAS,gBAAAC,qBAAoB;AA3BtB,SAAS,WAAW,OAAe,OAAmB;AAAC;AACvD,SAAS,YAAY,OAAqB;AAAC;AAC3C,SAAS,QAAQ,OAAe,OAAmB;AAAC;AACpD,SAAS,QAAQ,OAAe,OAAmB;AAAC;AACpD,SAAS,cAAc,OAAe,OAAmB;AAAC;AAC1D,SAAS,cAAc,OAAe,OAAmB;AAAC;AAE1D,IAAM,+DAA+D;AAAA,EAC1E,GAAG;AAAA,IACD,IAAI;AAAA,IAAC;AAAA,IACL,IAAI;AAAA,IAAC;AAAA,IACL,IAAI;AAAA,IAAC;AAAA,IACL,IAAI;AAAA,IAAC;AAAA,IACL,IAAI;AAAA,IAAC;AAAA,IACL,IAAI;AAAA,IAAC;AAAA,IACL,IAAI;AAAA,IAAC;AAAA,IACL,IAAI;AAAA,IAAC;AAAA,IACL,IAAI;AAAA,IAAC;AAAA,EACP;AAAA,EACA,GAAG;AAAA,EACH,aAAa;AACf;AAEO,IAAM,UAAU;AAKvB,IAAO,cAAQ;AAAA,EACb,WAAAF;AAAA,EACA,yBAAyBC;AAAA,EACzB,cAAAC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,SAAS;AACX;",
|
|
6
6
|
"names": ["flushSync", "batchedUpdates", "createPortal"]
|
|
7
7
|
}
|
package/dist/dom/reconcile.d.ts
CHANGED
|
@@ -4,10 +4,7 @@ export declare function flushSyncWork(fn: () => void): void;
|
|
|
4
4
|
export declare function batchedUpdates<T>(fn: () => T): T;
|
|
5
5
|
export declare function findRoot(fiber: Fiber): FiberRoot | null;
|
|
6
6
|
export declare function renderRoot(root: FiberRoot, children: ReactNode): void;
|
|
7
|
-
type
|
|
8
|
-
_text: string;
|
|
9
|
-
};
|
|
10
|
-
type NormalizedChild = ReactElement | TextChild | null;
|
|
7
|
+
type NormalizedChild = ReactElement | string | null;
|
|
11
8
|
export declare function childrenToArray(children: ReactNode): NormalizedChild[];
|
|
12
9
|
/**
|
|
13
10
|
* Reconcile a parent fiber's child list against new normalized children.
|
package/dist/dom/reconcile.js
CHANGED
|
@@ -141,7 +141,7 @@ function rerenderFiber(fiber, root) {
|
|
|
141
141
|
}
|
|
142
142
|
}
|
|
143
143
|
function isTextChild(child) {
|
|
144
|
-
return
|
|
144
|
+
return typeof child === "string";
|
|
145
145
|
}
|
|
146
146
|
function childrenToArray(children) {
|
|
147
147
|
const out = [];
|
|
@@ -150,9 +150,13 @@ function childrenToArray(children) {
|
|
|
150
150
|
}
|
|
151
151
|
function pushChildren(node, out) {
|
|
152
152
|
if (node == null || typeof node === "boolean") return;
|
|
153
|
-
if (typeof node === "string"
|
|
153
|
+
if (typeof node === "string") {
|
|
154
154
|
if (node === "") return;
|
|
155
|
-
out.push(
|
|
155
|
+
out.push(node);
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
158
|
+
if (typeof node === "number") {
|
|
159
|
+
out.push("" + node);
|
|
156
160
|
return;
|
|
157
161
|
}
|
|
158
162
|
if (Array.isArray(node)) {
|
|
@@ -192,7 +196,7 @@ function fiberFromChild(child, parent) {
|
|
|
192
196
|
if (!child) return createFiber(FiberTag.Fragment, null, null);
|
|
193
197
|
if (isTextChild(child)) {
|
|
194
198
|
const f2 = createFiber(FiberTag.Text, null, null);
|
|
195
|
-
f2.pendingProps = child
|
|
199
|
+
f2.pendingProps = child;
|
|
196
200
|
f2.parent = parent;
|
|
197
201
|
return f2;
|
|
198
202
|
}
|
|
@@ -222,44 +226,34 @@ function fiberFromChild(child, parent) {
|
|
|
222
226
|
function reconcileChildren(parent, newChildren, domParent, anchor) {
|
|
223
227
|
if (!currentRoot?.hydrating) {
|
|
224
228
|
let f = parent.child;
|
|
225
|
-
let i = 0;
|
|
226
229
|
let ok = true;
|
|
227
|
-
for (; i < newChildren.length; i++) {
|
|
230
|
+
for (let i = 0; i < newChildren.length; i++) {
|
|
228
231
|
const child = newChildren[i];
|
|
229
|
-
if (child == null) {
|
|
232
|
+
if (child == null || !f || f.key != null) {
|
|
230
233
|
ok = false;
|
|
231
234
|
break;
|
|
232
235
|
}
|
|
233
|
-
if (
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
236
|
+
if (typeof child === "string") {
|
|
237
|
+
if (f.tag !== FiberTag.Text) {
|
|
238
|
+
ok = false;
|
|
239
|
+
break;
|
|
240
|
+
}
|
|
241
|
+
f.pendingProps = child;
|
|
242
|
+
} else {
|
|
243
|
+
if (child.key != null) {
|
|
244
|
+
ok = false;
|
|
245
|
+
break;
|
|
246
|
+
}
|
|
247
|
+
if (f.type !== child.type) {
|
|
248
|
+
ok = false;
|
|
249
|
+
break;
|
|
250
|
+
}
|
|
251
|
+
f.pendingProps = child.props;
|
|
252
|
+
f.ref = child.ref ?? null;
|
|
248
253
|
}
|
|
249
254
|
f = f.sibling;
|
|
250
255
|
}
|
|
251
256
|
if (ok && f === null) {
|
|
252
|
-
let g = parent.child;
|
|
253
|
-
for (let j = 0; j < newChildren.length; j++) {
|
|
254
|
-
const child = newChildren[j];
|
|
255
|
-
if (isTextChild(child)) {
|
|
256
|
-
g.pendingProps = child._text;
|
|
257
|
-
} else {
|
|
258
|
-
g.pendingProps = child.props;
|
|
259
|
-
g.ref = child.ref ?? null;
|
|
260
|
-
}
|
|
261
|
-
g = g.sibling;
|
|
262
|
-
}
|
|
263
257
|
for (let r = parent.child; r; r = r.sibling) {
|
|
264
258
|
let a = anchor;
|
|
265
259
|
for (let s = r.sibling; s; s = s.sibling) {
|
|
@@ -325,7 +319,7 @@ function reconcileChildren(parent, newChildren, domParent, anchor) {
|
|
|
325
319
|
claimed.add(match);
|
|
326
320
|
fiber = match;
|
|
327
321
|
if (isTextChild(child)) {
|
|
328
|
-
fiber.pendingProps = child
|
|
322
|
+
fiber.pendingProps = child;
|
|
329
323
|
} else {
|
|
330
324
|
fiber.type = child.type;
|
|
331
325
|
fiber.pendingProps = child.props;
|
|
@@ -480,7 +474,7 @@ function renderText(fiber, domParent, anchor) {
|
|
|
480
474
|
fiber.dom = document.createTextNode(text);
|
|
481
475
|
insertInto(domParent, fiber.dom, anchor);
|
|
482
476
|
}
|
|
483
|
-
} else
|
|
477
|
+
} else {
|
|
484
478
|
;
|
|
485
479
|
fiber.dom.data = text;
|
|
486
480
|
}
|
|
@@ -719,12 +713,24 @@ function unmountAllChildren(parent, domParent) {
|
|
|
719
713
|
parent.child = null;
|
|
720
714
|
}
|
|
721
715
|
function insertInto(parent, node, anchor) {
|
|
716
|
+
const projectedHeadParent = getDocumentHeadInsertionParent(parent, node);
|
|
717
|
+
if (projectedHeadParent) {
|
|
718
|
+
projectedHeadParent.appendChild(node);
|
|
719
|
+
return;
|
|
720
|
+
}
|
|
722
721
|
if (anchor && anchor.parentNode === parent) {
|
|
723
722
|
parent.insertBefore(node, anchor);
|
|
724
723
|
} else {
|
|
725
724
|
parent.appendChild(node);
|
|
726
725
|
}
|
|
727
726
|
}
|
|
727
|
+
var DOCUMENT_HEAD_TAGS = /* @__PURE__ */ new Set(["base", "link", "meta", "script", "style", "title"]);
|
|
728
|
+
function getDocumentHeadInsertionParent(parent, node) {
|
|
729
|
+
if (parent.nodeType !== 9 || node.nodeType !== 1) return null;
|
|
730
|
+
const tag = node.tagName.toLowerCase();
|
|
731
|
+
if (!DOCUMENT_HEAD_TAGS.has(tag)) return null;
|
|
732
|
+
return parent.head;
|
|
733
|
+
}
|
|
728
734
|
function getHostParent(fiber) {
|
|
729
735
|
let p = fiber.parent;
|
|
730
736
|
while (p) {
|