@tanstack/redact 0.0.14 → 0.0.15

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 (90) hide show
  1. package/dist/core/internal.d.ts +23 -39
  2. package/dist/core/internal.js +7 -23
  3. package/dist/core/internal.js.map +3 -3
  4. package/dist/dom/dispatcher.d.ts +3 -1
  5. package/dist/dom/dispatcher.js +104 -97
  6. package/dist/dom/dispatcher.js.map +2 -2
  7. package/dist/dom/dom.js +1 -1
  8. package/dist/dom/dom.js.map +2 -2
  9. package/dist/dom/event-replay.js +7 -29
  10. package/dist/dom/event-replay.js.map +2 -2
  11. package/dist/dom/features/class/full.js +16 -16
  12. package/dist/dom/features/class/full.js.map +2 -2
  13. package/dist/dom/features/class/stub.js +7 -7
  14. package/dist/dom/features/class/stub.js.map +2 -2
  15. package/dist/dom/features/context/full.js +7 -7
  16. package/dist/dom/features/context/full.js.map +2 -2
  17. package/dist/dom/features/context/stub.js +3 -3
  18. package/dist/dom/features/context/stub.js.map +2 -2
  19. package/dist/dom/features/forward-ref/full.js +11 -11
  20. package/dist/dom/features/forward-ref/full.js.map +2 -2
  21. package/dist/dom/features/hydration/full.d.ts +26 -14
  22. package/dist/dom/features/hydration/full.js +412 -92
  23. package/dist/dom/features/hydration/full.js.map +2 -2
  24. package/dist/dom/features/hydration/stub.d.ts +13 -12
  25. package/dist/dom/features/hydration/stub.js +23 -8
  26. package/dist/dom/features/hydration/stub.js.map +2 -2
  27. package/dist/dom/features/lazy/full.js +3 -43
  28. package/dist/dom/features/lazy/full.js.map +2 -2
  29. package/dist/dom/features/lazy/stub.js +1 -1
  30. package/dist/dom/features/lazy/stub.js.map +2 -2
  31. package/dist/dom/features/memo/full.js +3 -3
  32. package/dist/dom/features/memo/full.js.map +2 -2
  33. package/dist/dom/features/memo/stub.js +1 -1
  34. package/dist/dom/features/memo/stub.js.map +2 -2
  35. package/dist/dom/features/portal/full.js +2 -2
  36. package/dist/dom/features/portal/full.js.map +2 -2
  37. package/dist/dom/features/suspense/full.js +151 -109
  38. package/dist/dom/features/suspense/full.js.map +2 -2
  39. package/dist/dom/reconcile.d.ts +3 -1
  40. package/dist/dom/reconcile.js +141 -129
  41. package/dist/dom/reconcile.js.map +2 -2
  42. package/dist/dom/root-internal.d.ts +9 -0
  43. package/dist/dom/root-internal.js +31 -0
  44. package/dist/dom/root-internal.js.map +7 -0
  45. package/dist/dom/root.d.ts +1 -1
  46. package/dist/dom/root.js +4 -136
  47. package/dist/dom/root.js.map +2 -2
  48. package/dist/dom/test-utils.js +1 -1
  49. package/dist/dom/test-utils.js.map +2 -2
  50. package/dist/react/children.js +4 -1
  51. package/dist/react/children.js.map +2 -2
  52. package/dist/react/class.js +8 -2
  53. package/dist/react/class.js.map +2 -2
  54. package/dist/react/context.js +4 -1
  55. package/dist/react/context.js.map +2 -2
  56. package/dist/react/index.js +1 -1
  57. package/dist/react/index.js.map +2 -2
  58. package/dist/react/shared-internals.d.ts +4 -7
  59. package/dist/react/shared-internals.js +4 -7
  60. package/dist/react/shared-internals.js.map +2 -2
  61. package/dist/server/stream.d.ts +5 -1
  62. package/dist/server/stream.js.map +2 -2
  63. package/package.json +1 -1
  64. package/src/core/internal.ts +30 -50
  65. package/src/dom/dispatcher.ts +114 -105
  66. package/src/dom/dom.ts +1 -1
  67. package/src/dom/event-replay.ts +14 -30
  68. package/src/dom/features/class/full.ts +16 -16
  69. package/src/dom/features/class/stub.ts +7 -7
  70. package/src/dom/features/context/full.ts +7 -7
  71. package/src/dom/features/context/stub.ts +3 -3
  72. package/src/dom/features/forward-ref/full.ts +11 -11
  73. package/src/dom/features/hydration/full.ts +516 -104
  74. package/src/dom/features/hydration/stub.ts +28 -14
  75. package/src/dom/features/lazy/full.ts +3 -56
  76. package/src/dom/features/lazy/stub.ts +1 -1
  77. package/src/dom/features/memo/full.ts +3 -3
  78. package/src/dom/features/memo/stub.ts +1 -1
  79. package/src/dom/features/portal/full.ts +2 -2
  80. package/src/dom/features/suspense/full.ts +176 -129
  81. package/src/dom/reconcile.ts +164 -170
  82. package/src/dom/root-internal.ts +40 -0
  83. package/src/dom/root.ts +5 -165
  84. package/src/dom/test-utils.ts +1 -1
  85. package/src/react/children.ts +4 -1
  86. package/src/react/class.ts +8 -2
  87. package/src/react/context.ts +4 -1
  88. package/src/react/index.ts +1 -1
  89. package/src/react/shared-internals.ts +8 -14
  90. package/src/server/stream.ts +9 -4
@@ -1,6 +1,5 @@
1
1
  import {
2
2
  FiberTag,
3
- FiberFlag,
4
3
  createFiber,
5
4
  REACT_ELEMENT_TYPE,
6
5
  REACT_LEGACY_ELEMENT_TYPE,
@@ -23,13 +22,11 @@ import { makeDispatcher } from './dispatcher'
23
22
  import {
24
23
  adoptHostDom,
25
24
  adoptTextDom,
26
- tryConsumeBoundary,
27
- advanceCursorPast,
28
25
  setHydrationCursor,
29
26
  getHydrationCursor,
30
27
  clearHydrationCursor,
31
- HydrationCursor,
32
28
  findHostParent as findHydrationHost,
29
+ abortHydration,
33
30
  } from './features/hydration'
34
31
 
35
32
  // ---------------------------------------------------------------------------
@@ -52,19 +49,19 @@ const pendingRoots = new Set<FiberRoot>()
52
49
  let forceRerenderingFiber: Fiber | null = null
53
50
 
54
51
  export function scheduleUpdate(fiber: Fiber): void {
55
- // Drop updates scheduled on already-unmounted fibers. Subscribers (router,
52
+ // Drop updates scheduled on already-um fibers. Subscribers (router,
56
53
  // query, any external store) can fire after unmount if their cleanup was
57
54
  // missed, and letting those reach rerenderFiber mounts zombie DOM into the
58
55
  // old .parent's DOM (which stays reachable via the stale pointer).
59
- if (fiber.unmounted) return
56
+ if (fiber.um) return
60
57
  const root = findRoot(fiber)
61
58
  if (!root) return
62
- root.pending.add(fiber)
63
- fiber.dirty = true
59
+ root.p.add(fiber)
60
+ fiber.dy = true
64
61
  pendingRoots.add(root)
65
62
  if (isBatching) return
66
- if (!root.scheduled) {
67
- root.scheduled = true
63
+ if (!root.s) {
64
+ root.s = true
68
65
  queueMicrotask(flushPending)
69
66
  }
70
67
  }
@@ -98,23 +95,26 @@ function flushPending(): void {
98
95
  let guard = 0
99
96
  while (pendingRoots.size > 0) {
100
97
  if (++guard > 50) {
101
- throw new Error('flushPending exceeded 50 iterations — suspected infinite update loop.')
98
+ if (process.env.NODE_ENV !== 'production') {
99
+ throw new Error('flushPending exceeded 50 iterations — suspected infinite update loop.')
100
+ }
101
+ throw new Error()
102
102
  }
103
103
  const roots = [...pendingRoots]
104
104
  pendingRoots.clear()
105
105
  for (const root of roots) {
106
- root.scheduled = false
106
+ root.s = false
107
107
  // Render each pending fiber from shallowest first so an ancestor's
108
108
  // cascade reaches descendants before we try to render them directly.
109
- // Descendants rendered via cascade still have `dirty=true` (only
109
+ // Descendants rendered via cascade still have `dy=true` (only
110
110
  // rerenderFiber clears it); when we later reach them in this loop,
111
- // rerenderFiber's own `if (!dirty) return` is our short-circuit. We
112
- // previously filtered descendants of dirty ancestors here, but that
111
+ // rerenderFiber's own `if (!dy) return` is our short-circuit. We
112
+ // previously filtered descendants of dy ancestors here, but that
113
113
  // loses updates whenever an ancestor's render doesn't actually reach
114
114
  // the descendant — e.g. React.memo bailing on equal props. Keep all
115
- // dirty fibers and let rerenderFiber de-dupe via its dirty check.
116
- const pending = [...root.pending]
117
- root.pending.clear()
115
+ // dy fibers and let rerenderFiber de-dupe via its dy check.
116
+ const pending = [...root.p]
117
+ root.p.clear()
118
118
  pending.sort((a, b) => fiberDepth(a) - fiberDepth(b))
119
119
  for (const fiber of pending) {
120
120
  rerenderFiber(fiber, root)
@@ -127,6 +127,12 @@ function flushPending(): void {
127
127
  }
128
128
  }
129
129
 
130
+ export function discardPendingWork(root: FiberRoot): void {
131
+ root.p.clear()
132
+ root.s = false
133
+ pendingRoots.delete(root)
134
+ }
135
+
130
136
  function fiberDepth(fiber: Fiber): number {
131
137
  let d = 0
132
138
  let p: Fiber | null = fiber.parent
@@ -151,13 +157,13 @@ export function findRoot(fiber: Fiber): FiberRoot | null {
151
157
  // ---------------------------------------------------------------------------
152
158
 
153
159
  export function renderRoot(root: FiberRoot, children: ReactNode): void {
154
- const rootFiber = root.current
155
- rootFiber.pendingProps = { children }
160
+ const rootFiber = root.r
161
+ rootFiber.pp = { children }
156
162
  currentRoot = root
157
163
  try {
158
- reconcileChildren(rootFiber, childrenToArray(children), root.container as Node, null)
159
- rootFiber.memoizedProps = rootFiber.pendingProps
160
- rootFiber.dirty = false
164
+ reconcileChildren(rootFiber, childrenToArray(children), root.c as Node, null)
165
+ rootFiber.mp = rootFiber.pp
166
+ rootFiber.dy = false
161
167
  } finally {
162
168
  currentRoot = null
163
169
  }
@@ -165,29 +171,29 @@ export function renderRoot(root: FiberRoot, children: ReactNode): void {
165
171
  }
166
172
 
167
173
  function rerenderFiber(fiber: Fiber, root: FiberRoot): void {
168
- if (!fiber.dirty) return
169
- // Skip fibers that were unmounted between scheduling and flush. Without this,
174
+ if (!fiber.dy) return
175
+ // Skip fibers that were um between scheduling and flush. Without this,
170
176
  // the flush loop re-enters a zombie fiber whose .parent is still set; its
171
177
  // render mounts fresh DOM into the old parent's still-attached DOM (since
172
178
  // unmountFiber only clears fiber.child, not fiber.parent). Visible as route
173
179
  // content from a previous location staying on screen after nav, because a
174
- // pending rerender on the old route's LibraryLandingPage (unmounted during
180
+ // pending rerender on the old route's LibraryLandingPage (um during
175
181
  // Outlet's shallow-first render) still fires from root.pending.
176
- if (fiber.unmounted) return
182
+ if (fiber.um) return
177
183
  // Clear BEFORE rendering so a scheduleUpdate() triggered mid-render (e.g.
178
- // error boundary catching a descendant throw) marks us dirty for the next
184
+ // error boundary catching a descendant throw) marks us dy for the next
179
185
  // flush iteration instead of being wiped out when render() completes.
180
- fiber.dirty = false
186
+ fiber.dy = false
181
187
  currentRoot = root
182
188
  // If this rerender is resuming a hydration that was deferred by a suspension,
183
189
  // re-activate hydration mode for its duration so descendants adopt DOM
184
190
  // instead of re-creating it.
185
191
  const resumeHydration =
186
- fiber.memoizedState && (fiber.memoizedState as any)._pendingHydration === true
187
- const prevHydrating = root.hydrating
192
+ fiber.ms && (fiber.ms as any).p === true
193
+ const prevHydrating = root.h
188
194
  if (resumeHydration) {
189
- delete (fiber.memoizedState as any)._pendingHydration
190
- root.hydrating = true
195
+ delete (fiber.ms as any).p
196
+ root.h = true
191
197
  }
192
198
  const prevForcing = forceRerenderingFiber
193
199
  forceRerenderingFiber = fiber
@@ -196,7 +202,7 @@ function rerenderFiber(fiber: Fiber, root: FiberRoot): void {
196
202
  } finally {
197
203
  forceRerenderingFiber = prevForcing
198
204
  if (resumeHydration) {
199
- root.hydrating = prevHydrating
205
+ root.h = prevHydrating
200
206
  // Deferred hydration completed — detach the preserved cursor so future
201
207
  // updates (post-hydration state changes) don't try to adopt stale DOM.
202
208
  clearHydrationCursor(fiber)
@@ -269,7 +275,7 @@ function pushChildren(node: ReactNode, out: NormalizedChild[]): void {
269
275
  }
270
276
 
271
277
  function isIterable(obj: any): boolean {
272
- return obj != null && typeof obj !== 'string' && typeof obj[Symbol.iterator] === 'function'
278
+ return obj != null && typeof obj[Symbol.iterator] == 'function'
273
279
  }
274
280
 
275
281
  function getKeyOf(child: NormalizedChild, index: number): string {
@@ -297,7 +303,7 @@ function fiberFromChild(child: NormalizedChild, parent: Fiber): Fiber {
297
303
  if (!child) return createFiber(FiberTag.Fragment, null, null)
298
304
  if (isTextChild(child)) {
299
305
  const f = createFiber(FiberTag.Text, null, null)
300
- f.pendingProps = child
306
+ f.pp = child
301
307
  f.parent = parent
302
308
  return f
303
309
  }
@@ -317,13 +323,13 @@ function fiberFromChild(child: NormalizedChild, parent: Fiber): Fiber {
317
323
  if (matched !== null) break
318
324
  }
319
325
  if (matched !== null) tag = matched
320
- else if (typeof type === 'function') {
326
+ else if (typeof type == 'function') {
321
327
  tag = type.prototype && type.prototype.isReactComponent ? FiberTag.Class : FiberTag.Function
322
328
  }
323
329
  }
324
330
  const f = createFiber(tag, type, child.key ?? null)
325
331
  f.ref = (child as any).ref ?? null
326
- f.pendingProps = child.props
332
+ f.pp = child.props
327
333
  f.parent = parent
328
334
  return f
329
335
  }
@@ -346,9 +352,9 @@ export function reconcileChildren(
346
352
  // Fast path: unkeyed positional steady-state. Walk the existing sibling
347
353
  // chain and newChildren in lockstep, validating AND committing in one pass.
348
354
  // On any divergence we fall back to the slow path, which rebuilds the
349
- // sibling chain anyway — partial pendingProps writes are idempotent.
355
+ // sibling chain anyway — partial pp writes are idempotent.
350
356
  // Skips the Map / Set / existing-array allocation entirely.
351
- if (!currentRoot?.hydrating) {
357
+ if (!currentRoot?.h) {
352
358
  let f: Fiber | null = parent.child
353
359
  let ok = true
354
360
  for (let i = 0; i < newChildren.length; i++) {
@@ -356,11 +362,11 @@ export function reconcileChildren(
356
362
  if (child == null || !f || f.key != null) { ok = false; break }
357
363
  if (typeof child === 'string') {
358
364
  if (f.tag !== FiberTag.Text) { ok = false; break }
359
- f.pendingProps = child
365
+ f.pp = child
360
366
  } else {
361
367
  if ((child as ReactElement).key != null) { ok = false; break }
362
368
  if (f.type !== (child as ReactElement).type) { ok = false; break }
363
- f.pendingProps = (child as ReactElement).props
369
+ f.pp = (child as ReactElement).props
364
370
  f.ref = (child as any).ref ?? null
365
371
  }
366
372
  f = f.sibling
@@ -395,7 +401,7 @@ export function reconcileChildren(
395
401
  // budget > 0: more new than old remain → treat slot as an INSERTION: keep
396
402
  // the old cursor and create a fresh fiber for new[i].
397
403
  // budget < 0: more old than new remain → treat slot as a DELETION: advance
398
- // the old cursor past the mismatched fiber (it'll be unmounted
404
+ // the old cursor past the mismatched fiber (it'll be um
399
405
  // in the unclaimed pass) and retry.
400
406
  // budget == 0: equal remaining → treat as REPLACE by preferring delete
401
407
  // until budget flips positive or we hit a match.
@@ -453,7 +459,7 @@ export function reconcileChildren(
453
459
  break
454
460
  }
455
461
  // Deletion (or replace-as-delete-first): advance past cand. It remains
456
- // unclaimed and will be unmounted at the end.
462
+ // unclaimed and will be um at the end.
457
463
  existingIdx++
458
464
  budget++
459
465
  }
@@ -467,10 +473,10 @@ export function reconcileChildren(
467
473
  claimed.add(match)
468
474
  fiber = match
469
475
  if (isTextChild(child!)) {
470
- fiber.pendingProps = child
476
+ fiber.pp = child
471
477
  } else {
472
478
  fiber.type = (child as ReactElement).type
473
- fiber.pendingProps = (child as ReactElement).props
479
+ fiber.pp = (child as ReactElement).props
474
480
  fiber.ref = (child as any).ref ?? null
475
481
  }
476
482
  } else {
@@ -490,7 +496,7 @@ export function reconcileChildren(
490
496
  // forward with the correct per-child anchor. During hydration the cursor
491
497
  // walks DOM forward and each renderFiber adopts the next existing node,
492
498
  // so per-child anchors are moot — fall back to the parent's anchor.
493
- const hydrating = !!currentRoot?.hydrating
499
+ const hydrating = !!currentRoot?.h
494
500
  for (let f: Fiber | null = parent.child; f; f = f.sibling) {
495
501
  let a = anchor
496
502
  if (!hydrating) {
@@ -542,10 +548,8 @@ export function reconcileChildren(
542
548
  // on each re-render causes stylesheet flash and re-download. Head element
543
549
  // ordering is semantically fluid; the browser doesn't care about exact
544
550
  // order within <head>.
545
- const parentIsHead =
546
- (domParent as Element).nodeType === 1 &&
547
- (domParent as Element).tagName.toLowerCase() === 'head'
548
- if (structurallyChanged && !currentRoot?.hydrating && !parentIsHead) {
551
+ const parentIsHead = (domParent as Element).nodeName === 'HEAD'
552
+ if (structurallyChanged && !currentRoot?.h && !parentIsHead) {
549
553
  placeChildrenInOrder(parent, domParent, anchor)
550
554
  }
551
555
  }
@@ -710,11 +714,11 @@ export function renderFiber(fiber: Fiber, domParent: Node, anchor: Node | null):
710
714
  }
711
715
 
712
716
  function renderText(fiber: Fiber, domParent: Node, anchor: Node | null): void {
713
- const text = fiber.pendingProps as string
717
+ const text = fiber.pp as string
714
718
  // Identity-unchanged fast path: skip the native Text.data write entirely.
715
- if (fiber.dom && fiber.memoizedProps === text) return
719
+ if (fiber.dom && fiber.mp === text) return
716
720
  if (!fiber.dom) {
717
- const hydrated = currentRoot?.hydrating ? adoptTextDom(fiber, fiber.parent!, text) : false
721
+ const hydrated = currentRoot?.h ? adoptTextDom(fiber, fiber.parent!, text) : false
718
722
  if (!hydrated) {
719
723
  fiber.dom = document.createTextNode(text)
720
724
  insertInto(domParent, fiber.dom, anchor)
@@ -724,13 +728,13 @@ function renderText(fiber: Fiber, domParent: Node, anchor: Node | null): void {
724
728
  // hydration — `.data !== text` here is guaranteed, so write directly.
725
729
  ;(fiber.dom as Text).data = text
726
730
  }
727
- fiber.memoizedProps = text
728
- // dirty cleared at rerender start; leaving true lets mid-render schedule persist
731
+ fiber.mp = text
732
+ // dy cleared at rerender start; leaving true lets mid-render schedule persist
729
733
  }
730
734
 
731
735
  function renderHost(fiber: Fiber, domParent: Node, anchor: Node | null): void {
732
- const props = fiber.pendingProps ?? {}
733
- const prev = fiber.memoizedProps ?? {}
736
+ const props = fiber.pp ?? {}
737
+ const prev = fiber.mp ?? {}
734
738
  const type = fiber.type as string
735
739
  const isSvg = type === 'svg' || (domParent as Element).namespaceURI === 'http://www.w3.org/2000/svg'
736
740
 
@@ -744,7 +748,7 @@ function renderHost(fiber: Fiber, domParent: Node, anchor: Node | null): void {
744
748
  : undefined
745
749
 
746
750
  if (!fiber.dom) {
747
- const hydrated = currentRoot?.hydrating ? adoptHostDom(fiber, fiber.parent!) : false
751
+ const hydrated = currentRoot?.h ? adoptHostDom(fiber, fiber.parent!) : false
748
752
  if (!hydrated) {
749
753
  fiber.dom = createHostNode(type, isSvg)
750
754
  // Two passes so form-control attributes (notably <input type>) are in
@@ -806,23 +810,22 @@ function renderHost(fiber: Fiber, domParent: Node, anchor: Node | null): void {
806
810
  // structural mismatch (server produced more than client wants). Report.
807
811
  // <head>/<html> are position-insensitive — leftover here is normal
808
812
  // (Vite dev-style injections, SSR-only scripts, etc.).
809
- if (currentRoot?.hydrating) {
813
+ if (currentRoot?.h) {
810
814
  const parentTag = (fiber.type as string).toLowerCase()
811
- if (parentTag !== 'head' && parentTag !== 'html') {
815
+ const hasOpaqueHydrationChildren =
816
+ props.dangerouslySetInnerHTML != null ||
817
+ (parentTag === 'textarea' && (props.value != null || props.defaultValue != null))
818
+ if (parentTag !== 'head' && parentTag !== 'html' && !hasOpaqueHydrationChildren) {
812
819
  const cursor = getHydrationCursor(fiber)
813
820
  if (cursor) {
814
- const leftover = cursor.remaining().filter(
815
- (n) => n.nodeType === 1 || n.nodeType === 3,
816
- )
817
- if (leftover.length > 0 && currentRoot.onRecoverableError) {
818
- currentRoot.onRecoverableError(
819
- new Error(
820
- `Hydration mismatch: server rendered ${leftover.length} extra ` +
821
- `${leftover.length === 1 ? 'node' : 'nodes'} inside <${parentTag}> ` +
822
- `that the client tree did not.`,
823
- ),
821
+ if (cursor.has()) {
822
+ const error = new Error(
823
+ process.env.NODE_ENV !== 'production'
824
+ ? `Hydration mismatch: server rendered extra nodes inside <${parentTag}>.`
825
+ : 'Hydration mismatch.',
824
826
  )
825
- for (const n of leftover) n.parentNode?.removeChild(n)
827
+ if (currentRoot.re) currentRoot.re(error)
828
+ abortHydration(error, fiber)
826
829
  }
827
830
  }
828
831
  }
@@ -841,63 +844,28 @@ function renderHost(fiber: Fiber, domParent: Node, anchor: Node | null): void {
841
844
  }
842
845
  }
843
846
 
844
- fiber.memoizedProps = props
845
- // dirty cleared at rerender start; leaving true lets mid-render schedule persist
847
+ fiber.mp = props
848
+ // dy cleared at rerender start; leaving true lets mid-render schedule persist
846
849
  }
847
850
 
848
851
  function renderFunction(fiber: Fiber, domParent: Node, anchor: Node | null): void {
849
852
  const prevDispatcher = ReactSharedInternals.H
850
- const prevFiber = ReactSharedInternals.currentFiber
851
- const prevHook = ReactSharedInternals.currentHook
852
- const prevIndex = ReactSharedInternals.hookIndex
853
+ const prevFiber = ReactSharedInternals.F
854
+ const prevHook = ReactSharedInternals.K
855
+ const prevIndex = ReactSharedInternals.I
853
856
 
854
857
  ReactSharedInternals.H = makeDispatcher()
855
- ReactSharedInternals.currentFiber = fiber
856
- ReactSharedInternals.currentHook = null
857
- ReactSharedInternals.hookIndex = 0
858
+ ReactSharedInternals.F = fiber
859
+ ReactSharedInternals.K = null
860
+ ReactSharedInternals.I = 0
858
861
 
859
862
  let rendered: ReactNode
860
863
  let deferredForHydration = false
861
864
  try {
862
- rendered = (fiber.type as Function)(fiber.pendingProps ?? {})
865
+ rendered = (fiber.type as Function)(fiber.pp ?? {})
863
866
  } catch (e: any) {
864
867
  if (isThenable(e)) {
865
- if (currentRoot?.hydrating) {
866
- // Suspension during initial hydration. Leave the existing DOM alone
867
- // and preserve the in-scope hydration cursor on THIS fiber so it
868
- // survives the synchronous endHydration() that fires when the initial
869
- // hydrateRoot() call returns. When the promise settles, the fiber
870
- // re-renders (see rerenderFiber) with hydration re-activated and its
871
- // descendants adopt DOM instead of creating new nodes.
872
- const hostParent = findHydrationHost(fiber)
873
- const inheritedCursor = getHydrationCursor(hostParent)
874
- if (inheritedCursor) {
875
- setHydrationCursor(fiber, inheritedCursor)
876
- }
877
- fiber.memoizedState = {
878
- ...(fiber.memoizedState ?? {}),
879
- _pendingHydration: true,
880
- }
881
- // Mirror renderLazy's guard: mark the nearest Suspense ancestor as
882
- // awaiting hydration-resume, so any re-render of that Suspense (e.g.
883
- // rehydrateBoundary fired by $RC, or an unrelated state update from a
884
- // sibling) doesn't re-enter `tryChildren`, re-throw, and flip Suspense
885
- // into its suspended+pending path — which would unmount our deferred
886
- // subtree and remount a fallback on top of the SSR content. By
887
- // pinning the Suspense to a "hydration-suspended" no-op until our
888
- // resume fires, the deferred re-render owns the adoption pass.
889
- let sus: Fiber | null = fiber.parent
890
- while (sus && sus.tag !== FiberTag.Suspense) sus = sus.parent
891
- if (sus && sus.memoizedState) {
892
- ;(sus.memoizedState as any)._awaitingLazyHydration = true
893
- }
894
- const clearAwait = () => {
895
- if (sus && sus.memoizedState) {
896
- ;(sus.memoizedState as any)._awaitingLazyHydration = false
897
- }
898
- scheduleUpdate(fiber)
899
- }
900
- e.then(clearAwait, clearAwait)
868
+ if (deferHydration(fiber, e)) {
901
869
  deferredForHydration = true
902
870
  } else {
903
871
  CAPABILITIES.handleSuspended(fiber, e)
@@ -909,16 +877,16 @@ function renderFunction(fiber: Fiber, domParent: Node, anchor: Node | null): voi
909
877
  }
910
878
  } finally {
911
879
  ReactSharedInternals.H = prevDispatcher
912
- ReactSharedInternals.currentFiber = prevFiber
913
- ReactSharedInternals.currentHook = prevHook
914
- ReactSharedInternals.hookIndex = prevIndex
880
+ ReactSharedInternals.F = prevFiber
881
+ ReactSharedInternals.K = prevHook
882
+ ReactSharedInternals.I = prevIndex
915
883
  }
916
884
 
917
885
  if (deferredForHydration) return
918
886
 
919
887
  reconcileChildren(fiber, childrenToArray(rendered), domParent, anchor)
920
- fiber.memoizedProps = fiber.pendingProps
921
- // dirty cleared at rerender start; leaving true lets mid-render schedule persist
888
+ fiber.mp = fiber.pp
889
+ // dy cleared at rerender start; leaving true lets mid-render schedule persist
922
890
  }
923
891
 
924
892
  function hasAncestorHydrationCursor(_fiber: Fiber): boolean {
@@ -927,11 +895,32 @@ function hasAncestorHydrationCursor(_fiber: Fiber): boolean {
927
895
  return false
928
896
  }
929
897
 
898
+ export function deferHydration(fiber: Fiber, thenable: Promise<any>): boolean {
899
+ if (!currentRoot?.h) return false
900
+ const hostParent = findHydrationHost(fiber)
901
+ const inheritedCursor = getHydrationCursor(hostParent)
902
+ if (inheritedCursor) setHydrationCursor(fiber, inheritedCursor)
903
+ ;((fiber.ms ??= {}) as any).p = true
904
+ let sus: Fiber | null = fiber.parent
905
+ while (sus && sus.tag !== FiberTag.Suspense) sus = sus.parent
906
+ if (sus && sus.ms) {
907
+ ;(sus.ms as any).a = true
908
+ }
909
+ const clearAwait = () => {
910
+ if (sus && sus.ms) {
911
+ ;(sus.ms as any).a = false
912
+ }
913
+ scheduleUpdate(fiber)
914
+ }
915
+ thenable.then(clearAwait, clearAwait)
916
+ return true
917
+ }
918
+
930
919
  function renderFragment(fiber: Fiber, domParent: Node, anchor: Node | null): void {
931
- const props = fiber.pendingProps ?? {}
920
+ const props = fiber.pp ?? {}
932
921
  reconcileChildren(fiber, childrenToArray(props.children), domParent, anchor)
933
- fiber.memoizedProps = props
934
- // dirty cleared at rerender start; leaving true lets mid-render schedule persist
922
+ fiber.mp = props
923
+ // dy cleared at rerender start; leaving true lets mid-render schedule persist
935
924
  }
936
925
 
937
926
  // ---------------------------------------------------------------------------
@@ -981,12 +970,15 @@ export function handleSuspended(fiber: Fiber, thenable: Promise<any>): void {
981
970
  }
982
971
 
983
972
  export function handleErrorInRender(fiber: Fiber, err: any): void {
973
+ if (currentRoot?.h) {
974
+ abortHydration(err, fiber)
975
+ }
984
976
  // Bubble to nearest class boundary with getDerivedStateFromError / componentDidCatch
985
977
  let f: Fiber | null = fiber.parent
986
978
  while (f) {
987
979
  if (f.tag === FiberTag.Class) {
988
980
  const Ctor = f.type as any
989
- const instance = f.stateNode
981
+ const instance = f.sn
990
982
  if (Ctor.getDerivedStateFromError) {
991
983
  const update = Ctor.getDerivedStateFromError(err)
992
984
  instance.state = { ...instance.state, ...update }
@@ -1002,12 +994,12 @@ export function handleErrorInRender(fiber: Fiber, err: any): void {
1002
994
  f = f.parent
1003
995
  }
1004
996
  // No boundary — report to root
1005
- if (currentRoot?.onUncaughtError) currentRoot.onUncaughtError(err)
997
+ if (currentRoot?.ue) currentRoot.ue(err)
1006
998
  else throw err
1007
999
  }
1008
1000
 
1009
1001
  export function isThenable(x: any): x is Promise<any> {
1010
- return x != null && typeof x.then === 'function'
1002
+ return x != null && typeof x.then == 'function'
1011
1003
  }
1012
1004
 
1013
1005
  // ---------------------------------------------------------------------------
@@ -1015,7 +1007,7 @@ export function isThenable(x: any): x is Promise<any> {
1015
1007
  // ---------------------------------------------------------------------------
1016
1008
 
1017
1009
  export function unmountFiber(fiber: Fiber, domParent: Node): void {
1018
- fiber.unmounted = true
1010
+ fiber.um = true
1019
1011
  // Recurse first
1020
1012
  let c = fiber.child
1021
1013
  while (c) {
@@ -1025,27 +1017,27 @@ export function unmountFiber(fiber: Fiber, domParent: Node): void {
1025
1017
  }
1026
1018
  fiber.child = null
1027
1019
 
1028
- // Run cleanups (effects + layout effects)
1029
- if (fiber.cleanups) {
1030
- for (const cleanup of fiber.cleanups) {
1020
+ // Run cu (fx + layout fx)
1021
+ if (fiber.cu) {
1022
+ for (const cleanup of fiber.cu) {
1031
1023
  try {
1032
1024
  cleanup()
1033
1025
  } catch (e) {
1034
- if (currentRoot?.onRecoverableError) currentRoot.onRecoverableError(e)
1026
+ if (currentRoot?.re) currentRoot.re(e)
1035
1027
  }
1036
1028
  }
1037
- fiber.cleanups = null
1029
+ fiber.cu = null
1038
1030
  }
1039
1031
 
1040
- if (fiber.tag === FiberTag.Class && fiber.stateNode?.componentWillUnmount) {
1032
+ if (fiber.tag === FiberTag.Class && fiber.sn?.componentWillUnmount) {
1041
1033
  try {
1042
- fiber.stateNode.componentWillUnmount()
1034
+ fiber.sn.componentWillUnmount()
1043
1035
  } catch (e) {
1044
- if (currentRoot?.onRecoverableError) currentRoot.onRecoverableError(e)
1036
+ if (currentRoot?.re) currentRoot.re(e)
1045
1037
  }
1046
- fiber.stateNode._fiber = null
1047
- fiber.stateNode._enqueueUpdate = null
1048
- fiber.stateNode._forceUpdate = null
1038
+ fiber.sn._fiber = null
1039
+ fiber.sn._enqueueUpdate = null
1040
+ fiber.sn._forceUpdate = null
1049
1041
  }
1050
1042
 
1051
1043
  // Detach ref
@@ -1105,7 +1097,7 @@ function getHostParent(fiber: Fiber): Node {
1105
1097
  while (p) {
1106
1098
  if (p.tag === FiberTag.Host) return p.dom!
1107
1099
  if (p.tag === FiberTag.Root)
1108
- return (p.stateNode as Node) || (p.dom as Node) || (p.root?.container as Node)
1100
+ return (p.sn as Node) || (p.dom as Node) || (p.root?.c as Node)
1109
1101
  if (p.tag === FiberTag.Portal) {
1110
1102
  // Portal renders its children into the `container` prop, not into any
1111
1103
  // DOM element the portal fiber "owns". Read the container from the
@@ -1113,12 +1105,15 @@ function getHostParent(fiber: Fiber): Node {
1113
1105
  // (e.g. a Floating-UI-positioned popper in a Radix Portal) finds its
1114
1106
  // host parent — otherwise getHostParent returns undefined and the
1115
1107
  // next renderHost crashes reading `.namespaceURI` on undefined.
1116
- const props = (p.pendingProps ?? p.memoizedProps) as { container?: Element } | null
1117
- return (props?.container as Node) || (p.stateNode as Node) || (p.dom as Node) || (p.root?.container as Node)
1108
+ const props = (p.pp ?? p.mp) as { container?: Element } | null
1109
+ return (props?.container as Node) || (p.sn as Node) || (p.dom as Node) || (p.root?.c as Node)
1118
1110
  }
1119
1111
  p = p.parent
1120
1112
  }
1121
- throw new Error('No host parent found.')
1113
+ if (process.env.NODE_ENV !== 'production') {
1114
+ throw new Error('No host parent found.')
1115
+ }
1116
+ throw new Error()
1122
1117
  }
1123
1118
 
1124
1119
  function getAnchor(fiber: Fiber): Node | null {
@@ -1174,17 +1169,17 @@ function defaultReadContext(_fiber: Fiber, ctx: any): any {
1174
1169
  // ---------------------------------------------------------------------------
1175
1170
 
1176
1171
  function attachRef(fiber: Fiber, value: any): void {
1177
- const ref = fiber.ref ?? (fiber.pendingProps?.ref ?? null)
1172
+ const ref = fiber.ref ?? (fiber.pp?.ref ?? null)
1178
1173
  if (!ref) return
1179
- if (typeof ref === 'function') {
1174
+ if (typeof ref == 'function') {
1180
1175
  // Match React's commit-phase semantics: callback refs run after render
1181
1176
  // (during the layout/commit phase), not during render. Calling them
1182
1177
  // synchronously here breaks libraries that assert no event handlers run
1183
1178
  // during render (e.g. base-ui's useStableCallback trampoline).
1184
1179
  scheduleLifecycle(fiber, () => {
1185
1180
  const cleanup = ref(value)
1186
- fiber.cleanups ||= []
1187
- fiber.cleanups.push(typeof cleanup === 'function' ? cleanup : () => ref(null))
1181
+ fiber.cu ||= []
1182
+ fiber.cu.push(typeof cleanup == 'function' ? cleanup : () => ref(null))
1188
1183
  })
1189
1184
  } else {
1190
1185
  ref.current = value
@@ -1192,13 +1187,13 @@ function attachRef(fiber: Fiber, value: any): void {
1192
1187
  }
1193
1188
 
1194
1189
  function syncRefIfChanged(fiber: Fiber, value: any): void {
1195
- const ref = fiber.ref ?? (fiber.pendingProps?.ref ?? null)
1190
+ const ref = fiber.ref ?? (fiber.pp?.ref ?? null)
1196
1191
  if (!ref) return
1197
1192
  if (typeof ref === 'object' && ref.current !== value) ref.current = value
1198
1193
  }
1199
1194
 
1200
1195
  function detachRef(ref: any): void {
1201
- // Function refs are handled via fiber.cleanups (queued in attachRef during
1196
+ // Function refs are handled via fiber.cu (queued in attachRef during
1202
1197
  // the commit phase): the cleanup either invokes the user-returned cleanup
1203
1198
  // fn or calls ref(null). Calling ref(null) here would double-fire it.
1204
1199
  if (ref && typeof ref === 'object') {
@@ -1210,56 +1205,55 @@ function detachRef(ref: any): void {
1210
1205
  // Effects
1211
1206
  // ---------------------------------------------------------------------------
1212
1207
 
1213
- const pendingEffects: Array<{ fiber: Fiber; effect: Effect }> = []
1214
- const pendingLayoutEffects: Array<{ fiber: Fiber; effect: Effect }> = []
1215
- const pendingLifecycles: Array<{ fiber: Fiber; fn: () => void }> = []
1208
+ const pendingEffects: Array<[Fiber, Effect]> = []
1209
+ const pendingLayoutEffects: Array<[Fiber, Effect]> = []
1210
+ const pendingLifecycles: Array<() => void> = []
1216
1211
 
1217
1212
  export function enqueueEffect(fiber: Fiber, effect: Effect): void {
1218
- if (effect.tag === 'layout' || effect.tag === 'insertion') {
1219
- pendingLayoutEffects.push({ fiber, effect })
1213
+ if (effect.t) {
1214
+ pendingLayoutEffects.push([fiber, effect])
1220
1215
  } else {
1221
- pendingEffects.push({ fiber, effect })
1216
+ pendingEffects.push([fiber, effect])
1222
1217
  }
1223
1218
  }
1224
1219
 
1225
- export function scheduleLifecycle(fiber: Fiber, fn: () => void): void {
1226
- pendingLifecycles.push({ fiber, fn })
1220
+ export function scheduleLifecycle(_fiber: Fiber, fn: () => void): void {
1221
+ pendingLifecycles.push(fn)
1227
1222
  }
1228
1223
 
1229
1224
  export function runEffects(root: FiberRoot): void {
1230
- // Layout effects synchronously
1225
+ // Layout fx synchronously
1231
1226
  while (pendingLayoutEffects.length) {
1232
- const { fiber, effect } = pendingLayoutEffects.shift()!
1227
+ const [fiber, effect] = pendingLayoutEffects.shift()!
1233
1228
  runEffect(fiber, effect, root)
1234
1229
  }
1235
1230
  // Then lifecycles
1236
1231
  while (pendingLifecycles.length) {
1237
- const { fn } = pendingLifecycles.shift()!
1232
+ const fn = pendingLifecycles.shift()!
1238
1233
  try {
1239
1234
  fn()
1240
1235
  } catch (e) {
1241
- if (root.onCaughtError) root.onCaughtError(e)
1236
+ if (root.ce) root.ce(e)
1242
1237
  }
1243
1238
  }
1244
- // Passive effects on microtask
1239
+ // Passive fx on microtask
1245
1240
  if (pendingEffects.length) {
1246
1241
  const batch = pendingEffects.splice(0)
1247
1242
  queueMicrotask(() => {
1248
- for (const { fiber, effect } of batch) runEffect(fiber, effect, root)
1243
+ for (const [fiber, effect] of batch) runEffect(fiber, effect, root)
1249
1244
  })
1250
1245
  }
1251
1246
  }
1252
1247
 
1253
1248
  function runEffect(fiber: Fiber, effect: Effect, root: FiberRoot): void {
1254
1249
  try {
1255
- const cleanup = effect.create()
1256
- effect.destroy = typeof cleanup === 'function' ? cleanup : undefined
1257
- if (effect.destroy) {
1258
- fiber.cleanups ||= []
1259
- fiber.cleanups.push(effect.destroy)
1250
+ const cleanup = effect.c()
1251
+ if (typeof cleanup == 'function') {
1252
+ fiber.cu ||= []
1253
+ fiber.cu.push(cleanup)
1260
1254
  }
1261
1255
  } catch (e) {
1262
- if (root.onCaughtError) root.onCaughtError(e)
1256
+ if (root.ce) root.ce(e)
1263
1257
  }
1264
1258
  }
1265
1259