@tanstack/redact 0.0.21 → 0.1.0

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 (253) 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 +69 -9
  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 +15 -4
  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 +1 -1
  152. package/dist/react/context.js +3 -3
  153. package/dist/react/context.js.map +1 -1
  154. package/dist/react/element.d.ts +6 -3
  155. package/dist/react/element.js +2 -2
  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 +143 -96
  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 +66 -4
  216. package/src/dom/features/context/stub.ts +6 -0
  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/element.ts +6 -2
  242. package/src/react/hooks.ts +12 -3
  243. package/src/react/index.ts +18 -0
  244. package/src/react/shared-internals.ts +3 -3
  245. package/src/react/view-transition.ts +41 -0
  246. package/src/server/bootstrap-script.ts +36 -5
  247. package/src/server/dispatcher.ts +35 -13
  248. package/src/server/escape.ts +15 -12
  249. package/src/server/renderToString.ts +7 -5
  250. package/src/server/resource-hints.ts +165 -0
  251. package/src/server/stream.ts +154 -62
  252. package/src/server/walk.ts +72 -11
  253. package/src/vite/index.ts +30 -13
@@ -0,0 +1,166 @@
1
+ import { FiberTag, type Fiber } from '../../../core'
2
+ import { REACT_ACTIVITY_TYPE } from '../../../react/activity'
3
+ import { onCommitRollback, queueMutation } from '../../commit'
4
+ import { retainedEffects as effects, inSuspensePrimary, setLayoutDisconnected, walkRetained as walk } from '../../retained-effects'
5
+ import {
6
+ attachRef,
7
+ childrenToArray,
8
+ detachRef,
9
+ enqueueEffect,
10
+ findRoot,
11
+ flushFiberCommits,
12
+ getCurrentRoot,
13
+ handleCommitError,
14
+ installCapability,
15
+ reconcileChildren,
16
+ registerRenderer,
17
+ registerTypeMatcher,
18
+ scheduleLifecycle,
19
+ scheduleUpdate,
20
+ } from '../../reconcile'
21
+
22
+ // Retain effect descriptions, not subscriptions, while a subtree is hidden.
23
+ // Weak keys keep this bookkeeping out of fibers that do not use Activity.
24
+ const disconnected = new WeakSet<Fiber>()
25
+ const hiddenDoms = new WeakSet<Fiber>()
26
+
27
+ function setMembership(set: WeakSet<Fiber>, fiber: Fiber, present: boolean): boolean {
28
+ const previous = set.has(fiber)
29
+ if (previous === present) return previous
30
+ onCommitRollback(() => { if (previous) set.add(fiber); else set.delete(fiber) })
31
+ if (present) set.add(fiber)
32
+ else set.delete(fiber)
33
+ return previous
34
+ }
35
+
36
+ function isHidden(fiber: Fiber): boolean {
37
+ if (!(fiber.root ?? findRoot(fiber))?.a) return false
38
+ for (let parent: Fiber | null = fiber; parent; parent = parent.parent) {
39
+ if (parent.tag === FiberTag.Activity && parent.pp?.mode === 'hidden') return true
40
+ }
41
+ return false
42
+ }
43
+
44
+ function report(fiber: Fiber, fn: () => void): void {
45
+ try { fn() } catch (error) { handleCommitError(fiber, error) }
46
+ }
47
+
48
+ function disconnect(fiber: Fiber): void {
49
+ if (disconnected.has(fiber)) return
50
+ setMembership(disconnected, fiber, true)
51
+ queueMutation(() => {
52
+ // Insertion effects remain connected, just as they do in React.
53
+ const insertion = new Set<() => void>()
54
+ for (const [hook, effect] of effects.get(fiber) ?? []) {
55
+ if (effect.t === 2 && hook.c) insertion.add(hook.c)
56
+ }
57
+ if (fiber.cu) {
58
+ const keep = fiber.cu.filter(cleanup => insertion.has(cleanup))
59
+ for (const cleanup of fiber.cu) {
60
+ if (!insertion.has(cleanup)) report(fiber, cleanup)
61
+ }
62
+ fiber.cu = keep.length ? keep : null
63
+ }
64
+ for (const [hook, effect] of effects.get(fiber) ?? []) {
65
+ if (effect.t !== 2) hook.c = null
66
+ }
67
+ detachRef(fiber.cr ?? fiber.ref)
68
+ fiber.cr = null
69
+ fiber.rc = null
70
+ const instance = fiber.sn?._fiber === fiber ? fiber.sn : null
71
+ if (!fiber.ld && instance?.componentWillUnmount) {
72
+ const props = instance.props, state = instance.state, committed = fiber.ms?.c
73
+ if (committed) { instance.props = committed.props; instance.state = committed.state }
74
+ try { report(fiber, () => instance.componentWillUnmount()) }
75
+ finally { instance.props = props; instance.state = state }
76
+ }
77
+ })
78
+ }
79
+
80
+ function prepare(fiber: Fiber): void {
81
+ if (!isHidden(fiber) || disconnected.has(fiber)) return
82
+ if (fiber.mp == null) setMembership(disconnected, fiber, true)
83
+ else disconnect(fiber)
84
+ }
85
+
86
+ function needsHiddenDom(fiber: Fiber): boolean {
87
+ for (let parent = fiber.parent; parent; parent = parent.parent) {
88
+ if (parent.tag === FiberTag.Activity && parent.pp?.mode === 'hidden') return true
89
+ if (parent.tag === FiberTag.Host) return false
90
+ // A portal starts a new DOM tree, but keeps the parent's Activity state.
91
+ if (parent.tag === FiberTag.Portal) return isHidden(parent)
92
+ }
93
+ return false
94
+ }
95
+
96
+ function syncDom(fiber: Fiber): void {
97
+ if (!(fiber.root ?? findRoot(fiber))?.a) return
98
+ if (!fiber.dom || (fiber.tag !== FiberTag.Host && fiber.tag !== FiberTag.Text)) return
99
+ if (needsHiddenDom(fiber)) {
100
+ setMembership(hiddenDoms, fiber, true)
101
+ queueMutation(() => {
102
+ if (fiber.tag === FiberTag.Text) fiber.dom!.nodeValue = ''
103
+ else (fiber.dom as HTMLElement).style.setProperty('display', 'none', 'important')
104
+ })
105
+ } else if (setMembership(hiddenDoms, fiber, false)) {
106
+ // A Suspense fallback can remain active after its Activity is revealed.
107
+ // Its preserved primary DOM must stay hidden until that boundary resumes.
108
+ for (let parent = fiber.parent; parent; parent = parent.parent) {
109
+ if (parent.tag === FiberTag.Suspense && parent.ms?.d?.some(([node]: [Node, string]) => node === fiber.dom)) return
110
+ }
111
+ const props = fiber.pp
112
+ queueMutation(() => {
113
+ if (fiber.tag === FiberTag.Text) fiber.dom!.nodeValue = props
114
+ else {
115
+ const display = props?.style?.display
116
+ ;(fiber.dom as HTMLElement).style.display = display == null || typeof display === 'boolean' ? '' : ('' + display).trim()
117
+ }
118
+ })
119
+ }
120
+ }
121
+
122
+ function reconnect(fiber: Fiber): void {
123
+ syncDom(fiber)
124
+ if (isHidden(fiber) || inSuspensePrimary(fiber) || !setMembership(disconnected, fiber, false)) return
125
+ setLayoutDisconnected(fiber, false)
126
+ const instance = fiber.sn?._fiber === fiber ? fiber.sn : null
127
+ if (fiber.tag === FiberTag.Host || fiber.tag === FiberTag.Resource) attachRef(fiber, fiber.dom)
128
+ else if (instance || fiber.tag === FiberTag.Fragment) attachRef(fiber, fiber.sn)
129
+ for (const effect of effects.get(fiber)?.values() ?? []) {
130
+ if (effect.t !== 2) enqueueEffect(fiber, effect)
131
+ }
132
+ if (instance?.componentDidMount) {
133
+ scheduleLifecycle(fiber, () => instance.componentDidMount())
134
+ }
135
+ flushFiberCommits(fiber)
136
+ }
137
+
138
+ function finish(fiber: Fiber): void {
139
+ if (fiber.tag === FiberTag.Suspense && (fiber.root ?? findRoot(fiber))?.a) walk(fiber, reconnect, true)
140
+ else syncDom(fiber)
141
+ }
142
+
143
+ function renderActivity(fiber: Fiber, domParent: Node, anchor: Node | null): void {
144
+ const root = fiber.root ?? findRoot(fiber)
145
+ if (root) root.a = true
146
+ const props = fiber.pp ?? {}
147
+ // Hidden content is absent from server HTML. Mount it after hydration so it
148
+ // cannot consume a following visible sibling's hydration cursor.
149
+ if (getCurrentRoot()?.h && props.mode === 'hidden' && !fiber.mp) {
150
+ fiber.mp = props
151
+ queueMicrotask(() => scheduleUpdate(fiber))
152
+ return
153
+ }
154
+ if (isHidden(fiber)) walk(fiber, prepare)
155
+ else walk(fiber, syncDom)
156
+ reconcileChildren(fiber, childrenToArray(props.children), domParent, anchor)
157
+ walk(fiber, reconnect, true)
158
+ fiber.mp = props
159
+ }
160
+
161
+ installCapability('isActivityHidden', isHidden)
162
+ installCapability('activityIsDisconnected', fiber => disconnected.has(fiber))
163
+ installCapability('prepareActivityFiber', prepare)
164
+ installCapability('syncActivity', finish)
165
+ registerTypeMatcher(type => type === REACT_ACTIVITY_TYPE ? FiberTag.Activity : null)
166
+ registerRenderer(FiberTag.Activity, renderActivity)
@@ -0,0 +1 @@
1
+ export * from './full'
@@ -0,0 +1,14 @@
1
+ import { FiberTag, type Fiber } from '../../../core'
2
+ import { REACT_ACTIVITY_TYPE } from '../../../react/activity'
3
+ import { childrenToArray, reconcileChildren, registerRenderer, registerTypeMatcher } from '../../reconcile'
4
+
5
+ // Explicit small-preset downgrade: hidden children unmount instead of being
6
+ // retained. Showing them again creates fresh state, DOM, effects, and refs.
7
+ function renderActivity(fiber: Fiber, parent: Node, anchor: Node | null): void {
8
+ const props = fiber.pp ?? {}
9
+ reconcileChildren(fiber, props.mode === 'hidden' ? [] : childrenToArray(props.children), parent, anchor)
10
+ fiber.mp = props
11
+ }
12
+
13
+ registerTypeMatcher(type => type === REACT_ACTIVITY_TYPE ? FiberTag.Activity : null)
14
+ registerRenderer(FiberTag.Activity, renderActivity)
@@ -1,4 +1,5 @@
1
1
  import { FiberTag, type Fiber, type ReactNode } from '../../../core'
2
+ import { checkpointCommit, rewindCommit, queueBeforeMutation, queueMutation } from '../../commit'
2
3
  import {
3
4
  registerRenderer,
4
5
  reconcileChildren,
@@ -8,17 +9,30 @@ import {
8
9
  isThenable,
9
10
  handleSuspended,
10
11
  handleErrorInRender,
12
+ readContext,
13
+ attachRef,
14
+ syncRefIfChanged,
15
+ isActivityHidden,
16
+ layoutIsDisconnected,
17
+ RenderErrorCapture,
18
+ handleCommitError,
11
19
  } from '../../reconcile'
12
20
 
13
21
  function renderClass(fiber: Fiber, domParent: Node, anchor: Node | null): void {
22
+ fiber.cx?.clear()
14
23
  const Ctor = fiber.type as any
15
24
  let instance = fiber.sn
16
25
  const props = fiber.pp ?? {}
17
26
  const isNew = !instance
27
+ const previous = fiber.ms?.c
28
+ let didRender = true
29
+ let captured = fiber.ms?.error instanceof RenderErrorCapture ? fiber.ms.error as RenderErrorCapture : undefined
30
+ if (captured) delete fiber.ms.error
18
31
 
19
32
  // Class contextType: read the current value of the subscribed context so
20
33
  // `this.context` reflects the nearest Provider. Evaluated every render.
21
- const ctxValue = Ctor.contextType ? Ctor.contextType._currentValue : undefined
34
+ const ctxValue = Ctor.contextType ? readContext(fiber, Ctor.contextType) : undefined
35
+ const contextChanged = instance && !Object.is(instance.context, ctxValue)
22
36
 
23
37
  if (isNew) {
24
38
  instance = new Ctor(props, ctxValue)
@@ -47,39 +61,31 @@ function renderClass(fiber: Fiber, domParent: Node, anchor: Node | null): void {
47
61
  if (d) instance.state = { ...instance.state, ...d }
48
62
  }
49
63
  } else {
50
- const prevProps = instance.props
51
- const prevState = instance.state
52
64
  // Refresh context on every render — Providers higher up may have changed.
53
65
  instance.context = ctxValue
54
66
  if (Ctor.getDerivedStateFromProps) {
55
67
  const d = Ctor.getDerivedStateFromProps(props, instance.state)
56
68
  if (d) instance.state = { ...instance.state, ...d }
57
69
  }
58
- if (instance.shouldComponentUpdate) {
70
+ if (!captured && instance.shouldComponentUpdate && !contextChanged) {
59
71
  if (!instance.shouldComponentUpdate(props, instance.state, instance.context)) {
60
- instance.props = props
61
- fiber.mp = props
62
- // Still need to render children with previous output
63
- if (fiber.ms?.r) {
64
- reconcileChildren(fiber, childrenToArray(fiber.ms.r), domParent, anchor)
65
- }
66
- return
72
+ didRender = false
67
73
  }
68
74
  }
69
75
  instance.props = props
70
- // New snapshot must win over any stale one from a previous render —
71
- // otherwise componentDidUpdate keeps seeing the original props and can
72
- // ping-pong setState forever.
73
- fiber.ms = { ...(fiber.ms ?? {}), p: prevProps, s: prevState }
76
+ }
77
+ if (captured && Ctor.getDerivedStateFromError) {
78
+ const update = Ctor.getDerivedStateFromError(captured.error)
79
+ if (update != null) instance.state = { ...instance.state, ...update }
74
80
  }
75
81
 
76
82
  let rendered: ReactNode
77
83
  try {
78
- rendered = instance.render()
84
+ rendered = captured && !Ctor.getDerivedStateFromError ? null : didRender ? instance.render() : fiber.ms?.r
79
85
  } catch (e: any) {
80
86
  if (isThenable(e)) {
81
87
  handleSuspended(fiber, e)
82
- rendered = null
88
+ return
83
89
  } else {
84
90
  handleErrorInRender(fiber, e)
85
91
  return
@@ -87,24 +93,62 @@ function renderClass(fiber: Fiber, domParent: Node, anchor: Node | null): void {
87
93
  }
88
94
  fiber.ms = { ...(fiber.ms ?? {}), r: rendered }
89
95
 
90
- reconcileChildren(fiber, childrenToArray(rendered), domParent, anchor)
96
+ const canCatch = Ctor.getDerivedStateFromError || instance.componentDidCatch
97
+ if (canCatch) fiber.ms.b = Ctor
98
+ if (canCatch && fiber.root) fiber.root.eb = true
99
+ const checkpoint = canCatch ? checkpointCommit(fiber) : -1
100
+ try {
101
+ reconcileChildren(fiber, childrenToArray(rendered), domParent, anchor)
102
+ } catch (error) {
103
+ if (!(error instanceof RenderErrorCapture) || error.boundary !== fiber) throw error
104
+ rewindCommit(checkpoint)
105
+ captured = error
106
+ didRender = true
107
+ if (Ctor.getDerivedStateFromError) {
108
+ const update = Ctor.getDerivedStateFromError(error.error)
109
+ if (update != null) instance.state = { ...instance.state, ...update }
110
+ try { rendered = instance.render() } catch (fallbackError) {
111
+ handleErrorInRender(fiber, fallbackError)
112
+ return
113
+ }
114
+ } else rendered = null
115
+ fiber.ms = { ...(fiber.ms ?? {}), r: rendered }
116
+ reconcileChildren(fiber, childrenToArray(rendered), domParent, anchor)
117
+ }
91
118
  fiber.mp = props
119
+ const state = instance.state
120
+ queueMutation(() => { fiber.ms.c = { props, state } })
121
+
122
+ if (isNew) attachRef(fiber, instance)
123
+ else syncRefIfChanged(fiber, instance)
92
124
 
93
125
  // Schedule lifecycle
94
126
  if (isNew) {
95
- if (instance.componentDidMount || instance.componentWillUnmount) {
96
- scheduleLifecycle(fiber, () => {
97
- // An abandoned render never mounted, so it has no unmount lifecycle.
98
- if (instance.componentWillUnmount) {
99
- fiber.cu ||= []
100
- fiber.cu.push(() => instance.componentWillUnmount())
127
+ if (instance.componentDidMount) {
128
+ scheduleLifecycle(fiber, () => instance.componentDidMount())
129
+ }
130
+ } else if (previous && didRender) {
131
+ let snapshot: any
132
+ if (instance.getSnapshotBeforeUpdate && !layoutIsDisconnected(fiber)) {
133
+ queueBeforeMutation(() => {
134
+ if (!fiber.um && !fiber.pd && !isActivityHidden(fiber) && !layoutIsDisconnected(fiber)) {
135
+ try { snapshot = instance.getSnapshotBeforeUpdate(previous.props, previous.state) }
136
+ catch (error) { handleCommitError(fiber, error) }
101
137
  }
102
- instance.componentDidMount?.()
103
138
  })
104
139
  }
105
- } else if (instance.componentDidUpdate) {
106
- const { p, s } = fiber.ms ?? {}
107
- scheduleLifecycle(fiber, () => instance.componentDidUpdate(p, s))
140
+ if (instance.componentDidUpdate) {
141
+ scheduleLifecycle(fiber, () => instance.componentDidUpdate(previous.props, previous.state, snapshot))
142
+ }
143
+ }
144
+ if (captured) {
145
+ const error = captured.error
146
+ const info = { componentStack: captured.stack }
147
+ scheduleLifecycle(fiber, () => {
148
+ try { fiber.root?.ce?.(error, { ...info, errorBoundary: instance }) }
149
+ catch (callbackError) { setTimeout(() => { throw callbackError }) }
150
+ instance.componentDidCatch?.(error, info)
151
+ })
108
152
  }
109
153
  }
110
154
 
@@ -6,9 +6,14 @@ import {
6
6
  installCapability,
7
7
  reconcileChildren,
8
8
  childrenToArray,
9
+ renderFiber,
10
+ getHostParent,
11
+ getAnchor,
9
12
  } from '../../reconcile'
10
13
 
11
- function realReadContext(fiber: Fiber, ctx: any): any {
14
+ let changingProviders = 0
15
+
16
+ function contextValue(fiber: Fiber, ctx: any): any {
12
17
  let p: Fiber | null = fiber.parent
13
18
  while (p) {
14
19
  if (p.tag === FiberTag.Provider && (p.type as any)._context === ctx) {
@@ -19,15 +24,71 @@ function realReadContext(fiber: Fiber, ctx: any): any {
19
24
  return ctx._currentValue
20
25
  }
21
26
 
27
+ function remember(fiber: Fiber, ctx: any, value: any): void {
28
+ ;(fiber.cx ??= new Map()).set(ctx, value)
29
+ }
30
+
31
+ function realReadContext(fiber: Fiber, ctx: any): any {
32
+ const value = contextValue(fiber, ctx)
33
+ remember(fiber, ctx, value)
34
+ // Initial suspended attempts are discarded. Keep their external context
35
+ // reads on the boundary so a provider can retry them before a promise settles.
36
+ let child = fiber
37
+ let p = fiber.parent
38
+ while (p) {
39
+ if (p.tag === FiberTag.Provider && p.type._context === ctx) break
40
+ if (p.tag === FiberTag.Suspense && p.ms?.f !== child) remember(p, ctx, value)
41
+ child = p
42
+ p = p.parent
43
+ }
44
+ return value
45
+ }
46
+
47
+ export function ownContextChanged(fiber: Fiber): boolean {
48
+ if (!changingProviders) return false
49
+ const reads = fiber.cx
50
+ if (reads) {
51
+ for (const [ctx, value] of reads) {
52
+ if (!Object.is(value, contextValue(fiber, ctx))) return true
53
+ }
54
+ }
55
+ return false
56
+ }
57
+
58
+ export function hasContextChanged(fiber: Fiber, includeFallback = true): boolean {
59
+ if (!changingProviders) return false
60
+ if (ownContextChanged(fiber)) return true
61
+ let child = fiber.child
62
+ while (child) {
63
+ if (hasContextChanged(child)) return true
64
+ child = child.sibling
65
+ }
66
+ return includeFallback && fiber.tag === FiberTag.Suspense && !!fiber.ms?.f && hasContextChanged(fiber.ms.f)
67
+ }
68
+
69
+ export function renderContextConsumers(fiber: Fiber): void {
70
+ if (!changingProviders) return
71
+ let child = fiber.child
72
+ while (child) {
73
+ if (child.tag === FiberTag.Suspense ? hasContextChanged(child) : ownContextChanged(child)) {
74
+ const domParent = getHostParent(child)
75
+ renderFiber(child, domParent, getAnchor(child, domParent))
76
+ } else {
77
+ renderContextConsumers(child)
78
+ }
79
+ child = child.sibling
80
+ }
81
+ }
82
+
22
83
  function renderProvider(fiber: Fiber, domParent: Node, anchor: Node | null): void {
23
84
  const ctx = (fiber.type as any)._context
24
85
  const props = fiber.pp ?? {}
25
- const prevValue = ctx._currentValue
26
- ctx._currentValue = props.value
86
+ const changed = fiber.mp && !Object.is(fiber.mp.value, props.value)
87
+ if (changed) changingProviders++
27
88
  try {
28
89
  reconcileChildren(fiber, childrenToArray(props.children), domParent, anchor)
29
90
  } finally {
30
- ctx._currentValue = prevValue
91
+ if (changed) changingProviders--
31
92
  }
32
93
  // Also store the value on the fiber so descendants rendering later (via updates)
33
94
  // can read through by walking up.
@@ -36,6 +97,7 @@ function renderProvider(fiber: Fiber, domParent: Node, anchor: Node | null): voi
36
97
  }
37
98
 
38
99
  function renderConsumer(fiber: Fiber, domParent: Node, anchor: Node | null): void {
100
+ fiber.cx?.clear()
39
101
  const ctx = (fiber.type as any)._context
40
102
  const props = fiber.pp ?? {}
41
103
  const children = props.children
@@ -7,6 +7,12 @@ import {
7
7
  childrenToArray,
8
8
  } from '../../reconcile'
9
9
 
10
+ export function hasContextChanged(_fiber: Fiber, _includeFallback = true): boolean {
11
+ return false
12
+ }
13
+ export const ownContextChanged = hasContextChanged
14
+ export function renderContextConsumers(_fiber: Fiber): void {}
15
+
10
16
  // Stub: Context feature disabled. Provider elements render as Fragments
11
17
  // (value is never propagated — descendants see only the Context's default).
12
18
  // Consumer elements still invoke their function-children with the default
@@ -1,5 +1,5 @@
1
1
  import { FiberTag, type Fiber, type ReactNode } from '../../../core'
2
- import { ReactSharedInternals, REACT_FORWARD_REF_TYPE } from '../../../react'
2
+ import { REACT_FORWARD_REF_TYPE } from '../../../react'
3
3
  import {
4
4
  registerRenderer,
5
5
  registerTypeMatcher,
@@ -8,42 +8,32 @@ import {
8
8
  isThenable,
9
9
  handleSuspended,
10
10
  handleErrorInRender,
11
+ getForceRerenderingFiber,
11
12
  } from '../../reconcile'
12
- import { makeDispatcher } from '../../dispatcher'
13
+ import { HOOK_BAILOUT, renderWithHooks } from '../../dispatcher'
13
14
 
14
15
  function renderForwardRef(fiber: Fiber, domParent: Node, anchor: Node | null): void {
16
+ const canBail = fiber === getForceRerenderingFiber() && fiber.pp === fiber.mp
17
+ fiber.cx?.clear()
15
18
  const props = fiber.pp ?? {}
16
19
  const render = (fiber.type as any).render
17
20
  const ref = fiber.ref ?? (props.ref ?? null)
18
21
 
19
- const prevDispatcher = ReactSharedInternals.H
20
- const prevFiber = ReactSharedInternals.F
21
- const prevHook = ReactSharedInternals.K
22
- const prevIndex = ReactSharedInternals.I
23
- ReactSharedInternals.H = makeDispatcher()
24
- ReactSharedInternals.F = fiber
25
- ReactSharedInternals.K = null
26
- ReactSharedInternals.I = 0
27
-
28
- let rendered: ReactNode
22
+ let rendered: ReactNode | typeof HOOK_BAILOUT
29
23
  try {
30
24
  const { ref: _omit, ...rest } = props
31
- rendered = render(rest, ref)
25
+ rendered = renderWithHooks(fiber, render, rest, ref, canBail)
32
26
  } catch (e: any) {
33
27
  if (isThenable(e)) {
34
28
  handleSuspended(fiber, e)
35
- rendered = null
29
+ return
36
30
  } else {
37
31
  handleErrorInRender(fiber, e)
38
32
  return
39
33
  }
40
- } finally {
41
- ReactSharedInternals.H = prevDispatcher
42
- ReactSharedInternals.F = prevFiber
43
- ReactSharedInternals.K = prevHook
44
- ReactSharedInternals.I = prevIndex
45
34
  }
46
35
 
36
+ if (rendered === HOOK_BAILOUT) return
47
37
  reconcileChildren(fiber, childrenToArray(rendered), domParent, anchor)
48
38
  fiber.mp = props
49
39
  }
@@ -0,0 +1 @@
1
+ export { createFragmentInstance, updateFragmentHost, refreshFragmentInstances } from '../../fragment-instance'
@@ -0,0 +1,3 @@
1
+ export function createFragmentInstance(_fiber: unknown): any { return null }
2
+ export function updateFragmentHost(_fiber: unknown, _adding: boolean): void {}
3
+ export function refreshFragmentInstances(_root: unknown): void {}