@tanstack/redact 0.0.21 → 0.1.1

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 (254) hide show
  1. package/dist/core/attributes.js +2 -1
  2. package/dist/core/attributes.js.map +2 -2
  3. package/dist/core/browser.d.ts +9 -0
  4. package/dist/core/browser.js +35 -0
  5. package/dist/core/browser.js.map +7 -0
  6. package/dist/core/index.d.ts +2 -2
  7. package/dist/core/index.js +3 -3
  8. package/dist/core/internal.d.ts +31 -8
  9. package/dist/core/internal.js +3 -1
  10. package/dist/core/internal.js.map +2 -2
  11. package/dist/core/resource-hints.d.ts +7 -0
  12. package/dist/core/resource-hints.js +19 -0
  13. package/dist/core/resource-hints.js.map +7 -0
  14. package/dist/core/types.js +1 -1
  15. package/dist/dom/_all.d.ts +11 -16
  16. package/dist/dom/_all.js +9 -20
  17. package/dist/dom/_all.js.map +2 -2
  18. package/dist/dom/client.d.ts +4 -4
  19. package/dist/dom/client.js +4 -4
  20. package/dist/dom/commit.d.ts +23 -0
  21. package/dist/dom/commit.js +191 -0
  22. package/dist/dom/commit.js.map +7 -0
  23. package/dist/dom/dispatcher.d.ts +4 -22
  24. package/dist/dom/dispatcher.js +184 -126
  25. package/dist/dom/dispatcher.js.map +3 -3
  26. package/dist/dom/dom.d.ts +2 -0
  27. package/dist/dom/dom.js +72 -52
  28. package/dist/dom/dom.js.map +2 -2
  29. package/dist/dom/error-info.d.ts +2 -0
  30. package/dist/dom/error-info.js +16 -0
  31. package/dist/dom/error-info.js.map +7 -0
  32. package/dist/dom/event-replay.js +1 -1
  33. package/dist/dom/features/activity/full.d.ts +1 -0
  34. package/dist/dom/features/activity/full.js +162 -0
  35. package/dist/dom/features/activity/full.js.map +7 -0
  36. package/dist/dom/features/activity/index.d.ts +1 -0
  37. package/dist/dom/features/activity/index.js +3 -0
  38. package/dist/dom/features/activity/index.js.map +7 -0
  39. package/dist/dom/features/activity/stub.d.ts +1 -0
  40. package/dist/dom/features/activity/stub.js +12 -0
  41. package/dist/dom/features/activity/stub.js.map +7 -0
  42. package/dist/dom/features/class/full.js +87 -27
  43. package/dist/dom/features/class/full.js.map +2 -2
  44. package/dist/dom/features/class/index.d.ts +1 -1
  45. package/dist/dom/features/class/index.js +2 -2
  46. package/dist/dom/features/class/stub.js +3 -3
  47. package/dist/dom/features/context/full.d.ts +4 -1
  48. package/dist/dom/features/context/full.js +72 -15
  49. package/dist/dom/features/context/full.js.map +2 -2
  50. package/dist/dom/features/context/index.d.ts +1 -1
  51. package/dist/dom/features/context/index.js +2 -2
  52. package/dist/dom/features/context/stub.d.ts +4 -1
  53. package/dist/dom/features/context/stub.js +17 -8
  54. package/dist/dom/features/context/stub.js.map +2 -2
  55. package/dist/dom/features/forward-ref/full.js +12 -21
  56. package/dist/dom/features/forward-ref/full.js.map +2 -2
  57. package/dist/dom/features/forward-ref/index.d.ts +1 -1
  58. package/dist/dom/features/forward-ref/index.js +2 -2
  59. package/dist/dom/features/forward-ref/stub.js +4 -4
  60. package/dist/dom/features/fragment-refs/index.d.ts +1 -0
  61. package/dist/dom/features/fragment-refs/index.js +8 -0
  62. package/dist/dom/features/fragment-refs/index.js.map +7 -0
  63. package/dist/dom/features/fragment-refs/stub.d.ts +3 -0
  64. package/dist/dom/features/fragment-refs/stub.js +14 -0
  65. package/dist/dom/features/fragment-refs/stub.js.map +7 -0
  66. package/dist/dom/features/hydration/full.d.ts +11 -6
  67. package/dist/dom/features/hydration/full.js +76 -77
  68. package/dist/dom/features/hydration/full.js.map +2 -2
  69. package/dist/dom/features/hydration/index.d.ts +1 -1
  70. package/dist/dom/features/hydration/index.js +2 -2
  71. package/dist/dom/features/hydration/stub.d.ts +7 -2
  72. package/dist/dom/features/hydration/stub.js +1 -1
  73. package/dist/dom/features/hydration/stub.js.map +1 -1
  74. package/dist/dom/features/index.d.ts +8 -7
  75. package/dist/dom/features/index.js +9 -8
  76. package/dist/dom/features/index.js.map +2 -2
  77. package/dist/dom/features/lazy/full.js +4 -4
  78. package/dist/dom/features/lazy/index.d.ts +1 -1
  79. package/dist/dom/features/lazy/index.js +2 -2
  80. package/dist/dom/features/lazy/stub.js +4 -4
  81. package/dist/dom/features/memo/full.js +8 -6
  82. package/dist/dom/features/memo/full.js.map +2 -2
  83. package/dist/dom/features/memo/index.d.ts +1 -1
  84. package/dist/dom/features/memo/index.js +2 -2
  85. package/dist/dom/features/memo/stub.js +4 -4
  86. package/dist/dom/features/portal/full.js +4 -4
  87. package/dist/dom/features/portal/index.d.ts +1 -1
  88. package/dist/dom/features/portal/index.js +2 -2
  89. package/dist/dom/features/portal/stub.js +4 -4
  90. package/dist/dom/features/suspense/full.js +197 -52
  91. package/dist/dom/features/suspense/full.js.map +2 -2
  92. package/dist/dom/features/suspense/index.d.ts +1 -1
  93. package/dist/dom/features/suspense/index.js +2 -2
  94. package/dist/dom/features/suspense/stub.js +4 -4
  95. package/dist/dom/features/view-transition/full.d.ts +6 -0
  96. package/dist/dom/features/view-transition/full.js +280 -0
  97. package/dist/dom/features/view-transition/full.js.map +7 -0
  98. package/dist/dom/features/view-transition/index.d.ts +1 -0
  99. package/dist/dom/features/view-transition/index.js +3 -0
  100. package/dist/dom/features/view-transition/index.js.map +7 -0
  101. package/dist/dom/features/view-transition/resources.d.ts +11 -0
  102. package/dist/dom/features/view-transition/resources.js +108 -0
  103. package/dist/dom/features/view-transition/resources.js.map +7 -0
  104. package/dist/dom/features/view-transition/stub.d.ts +6 -0
  105. package/dist/dom/features/view-transition/stub.js +22 -0
  106. package/dist/dom/features/view-transition/stub.js.map +7 -0
  107. package/dist/dom/features/view-transition/visual.d.ts +14 -0
  108. package/dist/dom/features/view-transition/visual.js +356 -0
  109. package/dist/dom/features/view-transition/visual.js.map +7 -0
  110. package/dist/dom/fragment-instance.d.ts +40 -0
  111. package/dist/dom/fragment-instance.js +324 -0
  112. package/dist/dom/fragment-instance.js.map +7 -0
  113. package/dist/dom/index.d.ts +11 -11
  114. package/dist/dom/index.js +11 -18
  115. package/dist/dom/index.js.map +3 -3
  116. package/dist/dom/portal.d.ts +2 -2
  117. package/dist/dom/portal.js +2 -2
  118. package/dist/dom/portal.js.map +1 -1
  119. package/dist/dom/reconcile.d.ts +32 -3
  120. package/dist/dom/reconcile.js +689 -221
  121. package/dist/dom/reconcile.js.map +2 -2
  122. package/dist/dom/resource-hints.d.ts +6 -0
  123. package/dist/dom/resource-hints.js +115 -0
  124. package/dist/dom/resource-hints.js.map +7 -0
  125. package/dist/dom/resource-store.d.ts +13 -0
  126. package/dist/dom/resource-store.js +43 -0
  127. package/dist/dom/resource-store.js.map +7 -0
  128. package/dist/dom/resources.d.ts +1 -0
  129. package/dist/dom/resources.js +44 -0
  130. package/dist/dom/resources.js.map +7 -0
  131. package/dist/dom/retained-effects.d.ts +6 -0
  132. package/dist/dom/retained-effects.js +49 -0
  133. package/dist/dom/retained-effects.js.map +7 -0
  134. package/dist/dom/root-internal.d.ts +4 -4
  135. package/dist/dom/root-internal.js +2 -2
  136. package/dist/dom/root-internal.js.map +2 -2
  137. package/dist/dom/root.d.ts +5 -5
  138. package/dist/dom/root.js +11 -11
  139. package/dist/dom/root.js.map +2 -2
  140. package/dist/dom/test-utils.js +1 -1
  141. package/dist/react/activity.d.ts +7 -0
  142. package/dist/react/activity.js +8 -0
  143. package/dist/react/activity.js.map +7 -0
  144. package/dist/react/children.d.ts +1 -1
  145. package/dist/react/children.js +3 -3
  146. package/dist/react/children.js.map +1 -1
  147. package/dist/react/class.d.ts +1 -1
  148. package/dist/react/class.js +3 -3
  149. package/dist/react/class.js.map +1 -1
  150. package/dist/react/compiler-runtime.js +2 -2
  151. package/dist/react/context.d.ts +7 -2
  152. package/dist/react/context.js +3 -11
  153. package/dist/react/context.js.map +3 -3
  154. package/dist/react/element.d.ts +6 -3
  155. package/dist/react/element.js +7 -11
  156. package/dist/react/element.js.map +2 -2
  157. package/dist/react/hooks.d.ts +26 -7
  158. package/dist/react/hooks.js +13 -3
  159. package/dist/react/hooks.js.map +2 -2
  160. package/dist/react/index.d.ts +39 -24
  161. package/dist/react/index.js +39 -19
  162. package/dist/react/index.js.map +3 -3
  163. package/dist/react/jsx-runtime.d.ts +2 -2
  164. package/dist/react/jsx-runtime.js +3 -3
  165. package/dist/react/memo.d.ts +1 -1
  166. package/dist/react/memo.js +1 -1
  167. package/dist/react/portal.js +1 -1
  168. package/dist/react/shared-internals.d.ts +15 -5
  169. package/dist/react/shared-internals.js +2 -3
  170. package/dist/react/shared-internals.js.map +2 -2
  171. package/dist/react/suspense.js +1 -1
  172. package/dist/react/view-transition.d.ts +32 -0
  173. package/dist/react/view-transition.js +13 -0
  174. package/dist/react/view-transition.js.map +7 -0
  175. package/dist/scheduler/index.js +1 -1
  176. package/dist/server/bootstrap-script.d.ts +1 -23
  177. package/dist/server/bootstrap-script.js +7 -6
  178. package/dist/server/bootstrap-script.js.map +2 -2
  179. package/dist/server/dispatcher.d.ts +28 -6
  180. package/dist/server/dispatcher.js +33 -15
  181. package/dist/server/dispatcher.js.map +2 -2
  182. package/dist/server/escape.js +16 -15
  183. package/dist/server/escape.js.map +2 -2
  184. package/dist/server/index.d.ts +6 -6
  185. package/dist/server/index.js +7 -7
  186. package/dist/server/renderToString.d.ts +1 -1
  187. package/dist/server/renderToString.js +12 -8
  188. package/dist/server/renderToString.js.map +2 -2
  189. package/dist/server/resource-hints.d.ts +22 -0
  190. package/dist/server/resource-hints.js +148 -0
  191. package/dist/server/resource-hints.js.map +7 -0
  192. package/dist/server/stream.d.ts +15 -2
  193. package/dist/server/stream.js +155 -63
  194. package/dist/server/stream.js.map +2 -2
  195. package/dist/server/walk.d.ts +15 -2
  196. package/dist/server/walk.js +150 -110
  197. package/dist/server/walk.js.map +2 -2
  198. package/dist/vite/index.d.ts +6 -0
  199. package/dist/vite/index.js +22 -6
  200. package/dist/vite/index.js.map +2 -2
  201. package/package.json +1 -1
  202. package/src/core/attributes.ts +1 -0
  203. package/src/core/browser.ts +33 -0
  204. package/src/core/internal.ts +24 -6
  205. package/src/core/resource-hints.ts +36 -0
  206. package/src/dom/_all.ts +1 -7
  207. package/src/dom/commit.ts +186 -0
  208. package/src/dom/dispatcher.ts +190 -134
  209. package/src/dom/dom.ts +70 -54
  210. package/src/dom/error-info.ts +15 -0
  211. package/src/dom/features/activity/full.ts +166 -0
  212. package/src/dom/features/activity/index.ts +1 -0
  213. package/src/dom/features/activity/stub.ts +14 -0
  214. package/src/dom/features/class/full.ts +72 -28
  215. package/src/dom/features/context/full.ts +70 -13
  216. package/src/dom/features/context/stub.ts +10 -5
  217. package/src/dom/features/forward-ref/full.ts +9 -19
  218. package/src/dom/features/fragment-refs/index.ts +1 -0
  219. package/src/dom/features/fragment-refs/stub.ts +3 -0
  220. package/src/dom/features/hydration/full.ts +84 -85
  221. package/src/dom/features/hydration/stub.ts +1 -1
  222. package/src/dom/features/index.ts +1 -0
  223. package/src/dom/features/memo/full.ts +4 -3
  224. package/src/dom/features/suspense/full.ts +192 -55
  225. package/src/dom/features/view-transition/full.ts +288 -0
  226. package/src/dom/features/view-transition/index.ts +1 -0
  227. package/src/dom/features/view-transition/resources.ts +107 -0
  228. package/src/dom/features/view-transition/stub.ts +7 -0
  229. package/src/dom/features/view-transition/visual.ts +406 -0
  230. package/src/dom/fragment-instance.ts +337 -0
  231. package/src/dom/index.ts +6 -7
  232. package/src/dom/portal.ts +1 -1
  233. package/src/dom/reconcile.ts +695 -242
  234. package/src/dom/resource-hints.ts +115 -0
  235. package/src/dom/resource-store.ts +52 -0
  236. package/src/dom/resources.ts +39 -0
  237. package/src/dom/retained-effects.ts +38 -0
  238. package/src/dom/root-internal.ts +4 -4
  239. package/src/dom/root.ts +12 -14
  240. package/src/react/activity.ts +10 -0
  241. package/src/react/context.ts +4 -11
  242. package/src/react/element.ts +11 -12
  243. package/src/react/hooks.ts +12 -3
  244. package/src/react/index.ts +18 -0
  245. package/src/react/shared-internals.ts +3 -3
  246. package/src/react/view-transition.ts +41 -0
  247. package/src/server/bootstrap-script.ts +36 -5
  248. package/src/server/dispatcher.ts +35 -13
  249. package/src/server/escape.ts +15 -12
  250. package/src/server/renderToString.ts +7 -5
  251. package/src/server/resource-hints.ts +165 -0
  252. package/src/server/stream.ts +154 -62
  253. package/src/server/walk.ts +79 -26
  254. package/src/vite/index.ts +30 -13
@@ -0,0 +1,186 @@
1
+ import { FiberTag, type Fiber, type Hook } from '../core'
2
+ import { setProp } from './dom'
3
+
4
+ export interface CommitPlan {
5
+ q: any[][] // Before-mutation, mutation, effect, rollback and failure queues.
6
+ c: number[][] // Queue lengths saved at each rollback checkpoint.
7
+ b: number // Next before-mutation callback, including reentrant commits.
8
+ m: boolean // Mutation phase has started, preventing duplicate commits.
9
+ }
10
+
11
+ export let currentCommit: CommitPlan | null = null
12
+ let activeCommit: CommitPlan | null = null
13
+ let reusableCommit: CommitPlan | undefined
14
+ let checkpointHook: (() => void) | undefined
15
+
16
+ function createCommit(): CommitPlan {
17
+ return { q: [[], [], [], [], []], c: [], b: 0, m: false }
18
+ }
19
+
20
+ export function prepareCommit(render: () => void, plan = createCommit()): CommitPlan {
21
+ const previous = currentCommit
22
+ currentCommit = plan
23
+ try { render() } catch (error) {
24
+ currentCommit = null
25
+ const failed = plan.q[4]!
26
+ for (let i = 0; i < failed.length; i++) {
27
+ const work = failed[i]
28
+ if (typeof work === 'function') work()
29
+ else {
30
+ const children = failed[++i] as Fiber[] | undefined
31
+ work.child = children?.[0] ?? null
32
+ if (children) for (let j = 0; j < children.length; j++) children[j]!.sibling = children[j + 1] ?? null
33
+ }
34
+ }
35
+ throw error
36
+ } finally { currentCommit = previous }
37
+ return plan
38
+ }
39
+
40
+ export function queueMutation(work: () => void): void {
41
+ if (!currentCommit) { work(); return }
42
+ currentCommit.q[1]!.push(work)
43
+ }
44
+
45
+ export function queueText(node: Text, value: string): void {
46
+ if (currentCommit) currentCommit.q[1]!.push(value, node)
47
+ else node.data = value
48
+ }
49
+
50
+ export function queueProp(node: Element, key: string, value: any, previous: any, svg: boolean): void {
51
+ if (currentCommit) currentCommit.q[1]!.push(node, key, value, previous, svg)
52
+ else setProp(node, key, value, previous, svg)
53
+ }
54
+
55
+ export function queueBeforeMutation(work: () => void): void {
56
+ if (currentCommit) currentCommit.q[0]!.push(work)
57
+ else work()
58
+ }
59
+
60
+ export function queueCommitEffects(work: () => void): void {
61
+ const plan = currentCommit || activeCommit
62
+ if (plan) plan.q[2]!.push(work)
63
+ else work()
64
+ }
65
+
66
+ export function onCommitRollback(work: () => void): void {
67
+ currentCommit?.q[3]!.push(work)
68
+ }
69
+
70
+ export function onCommitFailure(work: () => void): void {
71
+ if (currentCommit) currentCommit.q[4]!.push(work)
72
+ else work()
73
+ }
74
+
75
+ // Structural reconciliation already builds the previous child list. Keep
76
+ // that list for a failed preparation without allocating per-fiber closures.
77
+ export function rememberChildList(parent: Fiber, children?: Fiber[]): void {
78
+ currentCommit?.q[4]!.push(parent, children)
79
+ }
80
+
81
+ export function setCommitCheckpointHook(hook: () => void): void {
82
+ checkpointHook = hook
83
+ }
84
+
85
+ export function checkpointCommit(fiber?: Fiber): number {
86
+ if (!currentCommit) return -1
87
+ const checkpoint = currentCommit.c.length
88
+ currentCommit.c.push(currentCommit.q.map(queue => queue.length))
89
+ if (fiber) rememberSubtree(fiber)
90
+ checkpointHook?.()
91
+ return checkpoint
92
+ }
93
+
94
+ function rememberSubtree(fiber: Fiber): void {
95
+ const saved = { ...fiber }
96
+ // Memo and lazy preserve their wrapper tag around the same class instance.
97
+ const instance = fiber.sn?._fiber === fiber ? fiber.sn : null
98
+ // Hook has four fields. One flat snapshot preserves slot and dispatch
99
+ // identity without a tuple and object clone for every hook.
100
+ const hooks: any[] | null = fiber.hooks && []
101
+ if (hooks) for (const hook of fiber.hooks!) {
102
+ const queue = hook.q
103
+ hooks.push(hook, hook.s, queue, hook.d, hook.c, queue && typeof queue === 'object' ? { ...queue } : null)
104
+ }
105
+ const memoized = fiber.ms && (instance || fiber.tag === FiberTag.Suspense || fiber.tag === FiberTag.Fragment)
106
+ ? { ...fiber.ms } : null
107
+ if (fiber.cx) saved.cx = new Map(fiber.cx)
108
+ if (fiber.cu) saved.cu = fiber.cu.slice()
109
+ const props = instance?.props, state = instance?.state, context = instance?.context
110
+ onCommitRollback(() => {
111
+ Object.assign(fiber, saved)
112
+ if (memoized) {
113
+ for (const key in fiber.ms) if (!(key in memoized)) delete fiber.ms[key]
114
+ Object.assign(fiber.ms, memoized)
115
+ }
116
+ if (hooks) {
117
+ fiber.hooks!.length = hooks.length / 6
118
+ for (let index = 0; index < hooks.length; index += 6) {
119
+ const hook: Hook = fiber.hooks![index / 6] = hooks[index]
120
+ hook.s = hooks[index + 1]
121
+ hook.q = hooks[index + 2]
122
+ hook.d = hooks[index + 3]
123
+ hook.c = hooks[index + 4]
124
+ const queue = hooks[index + 5]
125
+ if (queue) Object.assign(hook.q, queue)
126
+ }
127
+ }
128
+ if (instance) { instance.props = props; instance.state = state; instance.context = context }
129
+ })
130
+ for (let child = fiber.child; child; child = child.sibling) rememberSubtree(child)
131
+ if (fiber.tag === FiberTag.Suspense && fiber.ms?.f) rememberSubtree(fiber.ms.f)
132
+ }
133
+
134
+ export function rewindCommit(checkpoint: number): void {
135
+ if (!currentCommit || checkpoint < 0) return
136
+ const lengths = currentCommit.c[checkpoint]!
137
+ const undo = currentCommit.q[3]!
138
+ for (let index = undo.length - 1; index >= lengths[3]!; index--) undo[index]!()
139
+ currentCommit.q.forEach((queue, index) => { queue.length = lengths[index]! })
140
+ currentCommit.c.length = checkpoint
141
+ }
142
+
143
+ export function commitBeforeMutation(plan: CommitPlan): void {
144
+ const queue = plan.q[0]!
145
+ while (plan.b < queue.length) queue[plan.b++]!()
146
+ }
147
+
148
+ export function commitMutation(plan: CommitPlan): void {
149
+ if (plan.m) return
150
+ commitBeforeMutation(plan)
151
+ if (plan.m) return
152
+ plan.m = true
153
+ const previous = activeCommit
154
+ activeCommit = plan
155
+ try {
156
+ const mutations = plan.q[1]!
157
+ for (let index = 0; index < mutations.length; index++) {
158
+ const work = mutations[index]
159
+ if (typeof work === 'function') work()
160
+ else if (typeof work === 'string') mutations[++index].data = work
161
+ else setProp(work, mutations[++index], mutations[++index], mutations[++index], mutations[++index])
162
+ }
163
+ for (const work of plan.q[2]!) work()
164
+ } finally {
165
+ activeCommit = previous
166
+ for (const queue of plan.q) queue.length = 0
167
+ plan.c.length = 0
168
+ }
169
+ }
170
+
171
+ export function commitSynchronously(render: () => void): void {
172
+ if (currentCommit) { render(); return }
173
+ const plan = reusableCommit || createCommit()
174
+ reusableCommit = undefined
175
+ plan.b = 0
176
+ plan.m = false
177
+ try { commitMutation(prepareCommit(render, plan)) }
178
+ finally {
179
+ // Failed preparation must not leave work for a later synchronous root.
180
+ if (!plan.m) {
181
+ for (const queue of plan.q) queue.length = 0
182
+ plan.c.length = 0
183
+ }
184
+ reusableCommit = plan
185
+ }
186
+ }
@@ -1,6 +1,9 @@
1
- import type { Hook, Fiber, FiberRoot, Effect } from '../core'
2
- import { ReactSharedInternals, REACT_CONTEXT_TYPE } from '../react'
3
- import { scheduleUpdate, enqueueEffect, readContext } from './reconcile'
1
+ import type { Hook, Fiber, FiberRoot, Effect, ReactNode } from '../core'
2
+ import { ReactSharedInternals, REACT_CONTEXT_TYPE, startTransition } from '../react'
3
+ import { scheduleUpdate, enqueueEffect, readContext, rememberActivityEffect, handleCommitError } from './reconcile'
4
+ import { REACT_RECOVERABLE_TYPE } from '../core/browser'
5
+ import { queueCommitEffects } from './commit'
6
+ import { deferTransitionPassive } from './features/view-transition'
4
7
 
5
8
  function getCurrentFiber(): Fiber {
6
9
  const f = ReactSharedInternals.F
@@ -14,32 +17,9 @@ function getCurrentFiber(): Fiber {
14
17
  }
15
18
 
16
19
  function nextHook(): Hook {
17
- const fiber = getCurrentFiber()
18
- const idx = ReactSharedInternals.I++
19
-
20
- let prev = ReactSharedInternals.K
21
-
22
- if (idx === 0) {
23
- if (fiber.hooks) {
24
- ReactSharedInternals.K = fiber.hooks
25
- return fiber.hooks
26
- }
27
- const h: Hook = { s: undefined, q: undefined, d: undefined, c: undefined, n: null }
28
- fiber.hooks = h
29
- ReactSharedInternals.K = h
30
- return h
31
- }
32
-
33
- if (prev && prev.n) {
34
- ReactSharedInternals.K = prev.n
35
- return prev.n
36
- }
37
-
38
- const h: Hook = { s: undefined, q: undefined, d: undefined, c: undefined, n: null }
39
- if (prev) prev.n = h
40
- else fiber.hooks = h
41
- ReactSharedInternals.K = h
42
- return h
20
+ const hooks = getCurrentFiber().hooks ||= []
21
+ const index = ReactSharedInternals.I++
22
+ return hooks[index] ||= { s: undefined, q: undefined, d: undefined, c: undefined }
43
23
  }
44
24
 
45
25
  function depsEqual(
@@ -57,110 +37,185 @@ function depsEqual(
57
37
 
58
38
  type BasicStateAction<S> = S | ((p: S) => S)
59
39
 
40
+ function initializeReducer(hook: Hook, fiber: Fiber, initialArg: any, init?: (arg: any) => any): void {
41
+ hook.s = init ? init(initialArg) : initialArg
42
+ hook.c = null
43
+ hook.q = (action: any) => {
44
+ if (fiber.um || fiber.pd) return
45
+ ;(hook.c ||= []).push(action)
46
+ scheduleUpdate(fiber)
47
+ }
48
+ }
49
+
50
+ // Reducer slots reuse the effect-cleanup field for pending actions. Zero marks
51
+ // eager state work, null means no update. Replacing arrays lets retries restore
52
+ // pending actions without copying or shifting them.
53
+ function reducePending(hook: Hook, reducer: (state: any, action: any) => any, previous = hook.s): void {
54
+ const actions = hook.c
55
+ if (actions == null) return
56
+ hook.c = null
57
+ let state = hook.s
58
+ if (actions) for (const action of actions) state = reducer(state, action)
59
+ hookFlags |= Object.is(state, previous) ? 2 : 3
60
+ hook.s = state
61
+ }
62
+
63
+ let hookEffects: any[] | undefined
64
+ // Bit 1 records a changed value; bit 2 records a processed reducer queue.
65
+ let hookFlags = 0
66
+ export const HOOK_BAILOUT = Symbol()
67
+
68
+ function resetHookEffects(effects: any[]): void {
69
+ for (let index = 0; index < effects.length; index += 4) effects[index].d = effects[index + 1]
70
+ }
71
+
72
+ export function renderWithHooks(
73
+ fiber: Fiber,
74
+ render: (props: any, ref?: any) => ReactNode,
75
+ props: any,
76
+ ref?: any,
77
+ canBail = false,
78
+ ): ReactNode | typeof HOOK_BAILOUT {
79
+ const previousDispatcher = ReactSharedInternals.H, previousFiber = ReactSharedInternals.F, previousIndex = ReactSharedInternals.I
80
+ const previousEffects = hookEffects, previousFlags = hookFlags
81
+ hookEffects = undefined
82
+ hookFlags = 0
83
+ ReactSharedInternals.H = DISPATCHER
84
+ ReactSharedInternals.F = fiber
85
+ try {
86
+ let rendered: ReactNode, attempts = 0
87
+ do {
88
+ if (++attempts > 25) throw new Error(process.env.NODE_ENV !== 'production' ? 'Too many re-renders.' : '')
89
+ if (hookEffects) {
90
+ resetHookEffects(hookEffects)
91
+ hookEffects = undefined
92
+ }
93
+ ReactSharedInternals.I = 0
94
+ fiber.dy = false
95
+ rendered = render(props, ref)
96
+ } while (fiber.dy)
97
+ if (canBail && hookFlags === 2) {
98
+ // React accepts the rendered dependencies on a reducer bailout, but
99
+ // keeps the previous committed effect descriptions until real work.
100
+ hookEffects = undefined
101
+ return HOOK_BAILOUT
102
+ }
103
+ if (hookEffects) {
104
+ const effects: any[] = hookEffects
105
+ for (let index = 0; index < effects.length; index += 4) {
106
+ const hook = effects[index], effect = effects[index + 2]
107
+ rememberActivityEffect(fiber, hook, effect)
108
+ if (effects[index + 3]) enqueueEffect(fiber, effect)
109
+ }
110
+ hookEffects = undefined
111
+ }
112
+ return rendered
113
+ } finally {
114
+ if (hookEffects) resetHookEffects(hookEffects)
115
+ hookEffects = previousEffects
116
+ hookFlags = previousFlags
117
+ ReactSharedInternals.H = previousDispatcher
118
+ ReactSharedInternals.F = previousFiber
119
+ ReactSharedInternals.I = previousIndex
120
+ }
121
+ }
122
+
123
+ export function cleanupEffect(hook: Hook, fiber: Fiber): void {
124
+ if (!hook.c) return
125
+ const cleanup = hook.c
126
+ hook.c = null
127
+ if (fiber.cu) {
128
+ const index = fiber.cu.indexOf(cleanup)
129
+ if (index >= 0) fiber.cu.splice(index, 1)
130
+ }
131
+ try { cleanup() } catch (error) { handleCommitError(fiber, error) }
132
+ }
133
+
134
+ function useEffectImpl(type: Effect['t'], create: () => any, deps?: ReadonlyArray<unknown>): void {
135
+ const hook = nextHook()
136
+ const fiber = getCurrentFiber()
137
+ const changed = hook.d === undefined || !depsEqual(hook.d, deps)
138
+ if (!changed && !fiber.root?.a && !(type === 1 && fiber.root?.sp)) return
139
+ const previous = hook.d
140
+ hook.d = deps
141
+ const effect: Effect = {
142
+ t: type,
143
+ c: () => {
144
+ cleanupEffect(hook, fiber)
145
+ const cleanup = create()
146
+ // Each effect owns its cleanup even when hooks return the same function.
147
+ hook.c = typeof cleanup === 'function' ? () => {
148
+ if (type === 0) {
149
+ const dispose = () => {
150
+ try { cleanup() } catch (error) { handleCommitError(fiber, error) }
151
+ }
152
+ if (!deferTransitionPassive(dispose)) queueCommitEffects(dispose)
153
+ } else cleanup()
154
+ } : null
155
+ return hook.c
156
+ },
157
+ }
158
+ if (type === 1) effect.d = () => cleanupEffect(hook, fiber)
159
+ ;(hookEffects ||= []).push(hook, previous, effect, changed)
160
+ }
161
+
60
162
  // Singleton — every method reads render context via ReactSharedInternals,
61
163
  // and per-hook closures live on the hook itself, so nothing is render-local
62
164
  // to capture. Allocating a fresh wrapper + 17 method closures per function-
63
165
  // component render was pure GC pressure.
64
166
  const DISPATCHER = makeDispatcherImpl()
65
167
 
66
- export function makeDispatcher() {
67
- return DISPATCHER
68
- }
69
-
70
168
  function makeDispatcherImpl() {
71
169
  return {
72
170
  useState<S>(initial: S | (() => S)): [S, (a: BasicStateAction<S>) => void] {
73
- return this.useReducer(
74
- basicReducer as (state: S, action: BasicStateAction<S>) => S,
75
- typeof initial == 'function' ? (initial as () => S)() : initial,
76
- )
77
- },
78
-
79
- useReducer<S, A>(reducer: (s: S, a: A) => S, initialArg: any, init?: (a: any) => S) {
80
- const hook = nextHook()
81
- const fiber = getCurrentFiber()
171
+ const hook = nextHook(), fiber = getCurrentFiber()
82
172
  if (hook.q === undefined) {
83
- hook.s = init ? init(initialArg) : initialArg
84
- const queue: any = { r: reducer }
85
- const dispatch = (action: A) => {
86
- const currentState = hook.s as S
87
- const next = queue.r(currentState, action)
88
- if (!Object.is(next, currentState)) {
173
+ hook.s = hook.d = typeof initial === 'function' ? (initial as () => S)() : initial
174
+ hook.c = null
175
+ hook.q = (action: BasicStateAction<S>) => {
176
+ if (fiber.um || fiber.pd) return
177
+ if (ReactSharedInternals.F !== fiber && !hook.c) {
178
+ let next: S
179
+ try {
180
+ next = basicReducer(hook.s, action)
181
+ } catch {
182
+ hook.c = [action]
183
+ scheduleUpdate(fiber)
184
+ return
185
+ }
186
+ if (Object.is(next, hook.s)) return
89
187
  hook.s = next
188
+ hook.c = 0
90
189
  scheduleUpdate(fiber)
190
+ return
91
191
  }
192
+ ;(hook.c ||= []).push(action)
193
+ scheduleUpdate(fiber)
92
194
  }
93
- queue.d = dispatch
94
- hook.q = queue
95
- } else {
96
- hook.q.r = reducer
97
195
  }
98
- return [hook.s, hook.q.d] as [S, (a: A) => void]
196
+ reducePending(hook, basicReducer, hook.d)
197
+ hook.d = hook.s
198
+ return [hook.s, hook.q]
99
199
  },
100
200
 
101
- useEffect(create: () => any, deps?: ReadonlyArray<unknown>) {
201
+ useReducer<S, A>(reducer: (s: S, a: A) => S, initialArg: any, init?: (a: any) => S) {
102
202
  const hook = nextHook()
103
- const fiber = getCurrentFiber()
104
- const prevDeps = hook.d
105
- if (prevDeps !== undefined && depsEqual(prevDeps, deps)) return
106
- hook.d = deps
107
- const effect: Effect = {
108
- t: 0,
109
- c: () => {
110
- // Run the prior cleanup INSIDE the effect run, not during the
111
- // dispatch/render phase. If render A → B → C all happen back-to-
112
- // back before the passive microtask drains, dispatch-time cleanup
113
- // only fires once (between A→B) and fx B + C both run fresh,
114
- // leaving two side-fx (e.g. two plot SVGs) in the DOM. Doing
115
- // it here, at effect-run time, means every new create first tears
116
- // down whatever cleanup is currently live on the hook.
117
- if (hook.c) {
118
- try { hook.c() } catch {}
119
- // The prior cleanup was also pushed onto fiber.cu; remove
120
- // it so unmount doesn't double-call it.
121
- if (fiber.cu) {
122
- const i = fiber.cu.indexOf(hook.c)
123
- if (i >= 0) fiber.cu.splice(i, 1)
124
- }
125
- hook.c = null
126
- }
127
- const c = create()
128
- hook.c = typeof c == 'function' ? c : null
129
- return hook.c
130
- },
131
- }
132
- enqueueEffect(fiber, effect)
203
+ if (hook.q === undefined) initializeReducer(hook, getCurrentFiber(), initialArg, init)
204
+ reducePending(hook, reducer)
205
+ hook.d = reducer
206
+ return [hook.s, hook.q] as [S, (a: A) => void]
207
+ },
208
+
209
+ useEffect(create: () => any, deps?: ReadonlyArray<unknown>) {
210
+ useEffectImpl(0, create, deps)
133
211
  },
134
212
 
135
213
  useLayoutEffect(create: () => any, deps?: ReadonlyArray<unknown>) {
136
- const hook = nextHook()
137
- const fiber = getCurrentFiber()
138
- const prevDeps = hook.d
139
- if (prevDeps !== undefined && depsEqual(prevDeps, deps)) return
140
- hook.d = deps
141
- const effect: Effect = {
142
- t: 1,
143
- c: () => {
144
- // Mirror useEffect: tear down the prior cleanup at run time so
145
- // coalesced renders don't leak side-fx.
146
- if (hook.c) {
147
- try { hook.c() } catch {}
148
- if (fiber.cu) {
149
- const i = fiber.cu.indexOf(hook.c)
150
- if (i >= 0) fiber.cu.splice(i, 1)
151
- }
152
- hook.c = null
153
- }
154
- const c = create()
155
- hook.c = typeof c == 'function' ? c : null
156
- return hook.c
157
- },
158
- }
159
- enqueueEffect(fiber, effect)
214
+ useEffectImpl(1, create, deps)
160
215
  },
161
216
 
162
217
  useInsertionEffect(create: () => any, deps?: ReadonlyArray<unknown>) {
163
- return this.useLayoutEffect(create, deps)
218
+ useEffectImpl(2, create, deps)
164
219
  },
165
220
 
166
221
  useRef<T>(initial: T) {
@@ -190,14 +245,16 @@ function makeDispatcherImpl() {
190
245
  },
191
246
 
192
247
  useImperativeHandle<T>(ref: any, factory: () => T, deps?: ReadonlyArray<unknown>) {
193
- const hook = nextHook()
194
- if (hook.d !== undefined && depsEqual(hook.d, deps)) return
195
- hook.d = deps
196
- const value = factory()
197
- if (ref) {
198
- if (typeof ref == 'function') ref(value)
199
- else ref.current = value
200
- }
248
+ useEffectImpl(1, () => {
249
+ if (!ref) return
250
+ const value = factory()
251
+ if (typeof ref === 'function') {
252
+ const cleanup = ref(value)
253
+ return typeof cleanup === 'function' ? cleanup : () => ref(null)
254
+ }
255
+ ref.current = value
256
+ return () => { ref.current = null }
257
+ }, deps == null ? undefined : [...deps, ref])
201
258
  },
202
259
 
203
260
  useDebugValue<T>(_value: T, _formatter?: (v: T) => any): void {
@@ -217,13 +274,19 @@ function makeDispatcherImpl() {
217
274
  },
218
275
 
219
276
  useTransition(): [boolean, (fn: () => void) => void] {
220
- return [false, (fn: () => void) => fn()]
277
+ return [false, startTransition]
221
278
  },
222
279
 
223
280
  useDeferredValue<T>(v: T): T {
224
281
  return v
225
282
  },
226
283
 
284
+ useCacheRefresh(): () => void {
285
+ // There is no client cache to invalidate. Preserve hook identity and
286
+ // leave state and scheduling alone, as with the uncached client cache().
287
+ return this.useCallback(() => {}, [])
288
+ },
289
+
227
290
  useSyncExternalStore<T>(
228
291
  subscribe: (cb: () => void) => () => void,
229
292
  getSnapshot: () => T,
@@ -251,26 +314,19 @@ function makeDispatcherImpl() {
251
314
  const isHydrating = Boolean(root?.h)
252
315
  const value =
253
316
  isHydrating && getServerSnapshot ? getServerSnapshot() : getSnapshot()
317
+ if (!Object.is(hook.s, value)) hookFlags |= 1
254
318
  hook.s = value
255
319
 
256
320
  const deps = [subscribe]
257
- if (hook.d === undefined || !depsEqual(hook.d, deps)) {
321
+ const changed = hook.d === undefined || !depsEqual(hook.d, deps)
322
+ if (changed || fiber.root?.a) {
323
+ const previous = hook.d
258
324
  hook.d = deps
259
325
  const effect: Effect = {
260
326
  t: 1,
327
+ s: true,
261
328
  c: () => {
262
- if (hook.c) {
263
- try {
264
- hook.c()
265
- } catch {
266
- // ignore cleanup failures
267
- }
268
- if (fiber.cu) {
269
- const i = fiber.cu.indexOf(hook.c)
270
- if (i >= 0) fiber.cu.splice(i, 1)
271
- }
272
- hook.c = null
273
- }
329
+ cleanupEffect(hook, fiber)
274
330
 
275
331
  let unsubscribed = false
276
332
  const cleanup = () => {
@@ -293,7 +349,6 @@ function makeDispatcherImpl() {
293
349
  }
294
350
 
295
351
  if (!Object.is(hook.s, next)) {
296
- hook.s = next
297
352
  scheduleUpdate(fiber)
298
353
  }
299
354
  }
@@ -311,12 +366,13 @@ function makeDispatcherImpl() {
311
366
  return cleanup
312
367
  },
313
368
  }
314
- enqueueEffect(fiber, effect)
369
+ ;(hookEffects ||= []).push(hook, previous, effect, changed)
315
370
  }
316
371
  return value
317
372
  },
318
373
 
319
374
  use<T>(resource: any): T {
375
+ if (resource?.$$typeof === REACT_RECOVERABLE_TYPE) return undefined as T
320
376
  if (resource == null) {
321
377
  if (process.env.NODE_ENV !== 'production') {
322
378
  throw new Error('use() received null or undefined')
@@ -372,7 +428,7 @@ let idCounter = 0
372
428
  function findRootFromFiber(fiber: Fiber): FiberRoot | null {
373
429
  let f: Fiber | null = fiber
374
430
  while (f) {
375
- if (f.root) return f.root
431
+ if (f.root) return fiber.root = f.root
376
432
  f = f.parent
377
433
  }
378
434
  return null