@tanstack/redact 0.0.20 → 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 -21
  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 -76
  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 +192 -47
  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 +686 -222
  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 -21
  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 -82
  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 +187 -50
  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 +691 -244
  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
@@ -7,10 +7,12 @@ import {
7
7
  type ReactNode,
8
8
  } from '../../../core'
9
9
  import { attributeName, STRING_BOOLEAN_ATTRS } from '../../../core/attributes'
10
- import { createHostNode, setProp } from '../../dom'
10
+ import { createHostNode, setProp, syncTextareaValue } from '../../dom'
11
+ import { queueMutation, queueProp, queueText } from '../../commit'
11
12
  import { drainReplayQueue } from '../../event-replay'
12
- import { discardPendingWork, findRoot, flushSyncWork, renderRoot } from '../../reconcile'
13
+ import { discardPendingWork, discardPendingEffects, findRoot, flushSyncWork, renderRoot, scheduleRootRender, recoverRootError, unmountFiber } from '../../reconcile'
13
14
  import { attachRootFiber, createFiberRoot } from '../../root-internal'
15
+ import { componentStack } from '../../error-info'
14
16
 
15
17
  // Re-export from event-replay so all hydration concerns live behind one
16
18
  // feature boundary — the plugin's stub swap strips drainReplayQueue too.
@@ -146,9 +148,9 @@ export function abortHydration(cause: unknown, fiber: Fiber | null = null): neve
146
148
 
147
149
  interface HydrateRootOptions {
148
150
  identifierPrefix?: string
149
- onRecoverableError?: (error: unknown) => void
150
- onCaughtError?: (error: unknown) => void
151
- onUncaughtError?: (error: unknown) => void
151
+ onRecoverableError?: import('../../../core').RecoverableErrorHandler
152
+ onCaughtError?: import('../../../core').RecoverableErrorHandler
153
+ onUncaughtError?: import('../../../core').RecoverableErrorHandler
152
154
  }
153
155
 
154
156
  interface HydratedRoot {
@@ -186,7 +188,7 @@ export function hydrateRootImpl(
186
188
 
187
189
  return {
188
190
  render(children) {
189
- flushSyncWork(() => {
191
+ scheduleRootRender(root, () => {
190
192
  const normalized = isDocument ? normalizeDocumentChildren(children) : children
191
193
  renderRoot(
192
194
  root,
@@ -224,13 +226,19 @@ function headAttrsMatch(
224
226
  if (!keys) return true
225
227
  for (const k of keys) {
226
228
  const propVal = props[k]
227
- const elVal = el.getAttribute(attributeName(k))
229
+ const elVal = hydrationAttribute(el, attributeName(k))
228
230
  if (propVal == null && elVal == null) continue
229
231
  if (propVal == null || elVal == null || String(propVal) !== elVal) return false
230
232
  }
231
233
  return true
232
234
  }
233
235
 
236
+ function hydrationAttribute(el: Element, name: string): string | null {
237
+ return name === 'nonce' && 'nonce' in el && el.hasAttribute(name)
238
+ ? (el as HTMLElement).nonce ?? ''
239
+ : el.getAttribute(name)
240
+ }
241
+
234
242
  export function beginHydration(root: FiberRoot): void {
235
243
  root.ic = 0
236
244
  root.h = true
@@ -247,7 +255,7 @@ export function endHydration(root: FiberRoot): void {
247
255
  * marker emitted by the server. Returns info + advances the cursor past the
248
256
  * marker pair (start comment + fallback/real content + end comment).
249
257
  */
250
- export type BoundaryInfo = [0 | 1, number, Comment, Comment]
258
+ export type BoundaryInfo = [0 | 1 | 2 | 3, number, Comment, Comment]
251
259
 
252
260
  export function tryConsumeBoundary(parent: Fiber): BoundaryInfo | null {
253
261
  const cursor = hydrationCursors.get(findHostParent(parent))
@@ -255,9 +263,9 @@ export function tryConsumeBoundary(parent: Fiber): BoundaryInfo | null {
255
263
  const peek = cursor.n
256
264
  if (!peek || peek.nodeType !== 8) return null
257
265
  const data = (peek as Comment).data
258
- const m = /^(\$\??)(\d+)$/.exec(data)
266
+ const m = /^(\$[?!E]?)(\d+)$/.exec(data)
259
267
  if (!m) return null
260
- const kind = m[1] === '$?' ? 1 : 0
268
+ const kind = m[1] === '$?' ? 1 : m[1] === '$!' ? 2 : m[1] === '$E' ? 3 : 0
261
269
  const id = Number(m[2])
262
270
  const startMark = peek as Comment
263
271
  // Advance past the start comment
@@ -265,8 +273,11 @@ export function tryConsumeBoundary(parent: Fiber): BoundaryInfo | null {
265
273
  // Locate end comment: closest <!--/$-->
266
274
  let endMark: Comment | null = null
267
275
  let scan = startMark.nextSibling
276
+ let depth = 0
268
277
  while (scan) {
278
+ if (scan.nodeType === 8 && /^\$[?!E]?\d+$/.test((scan as Comment).data)) depth++
269
279
  if (scan.nodeType === 8 && (scan as Comment).data === '/$') {
280
+ if (depth) { depth--; scan = scan.nextSibling; continue }
270
281
  endMark = scan as Comment
271
282
  break
272
283
  }
@@ -360,26 +371,43 @@ export function adoptHostDom(fiber: Fiber, parent: Fiber): boolean {
360
371
  !props.suppressHydrationWarning &&
361
372
  canRecoverHydrationPropMismatch(candidate, tag)
362
373
  validateHydrationProps(fiber, candidate as Element, props, tag, isSvg)
374
+ // Form state can change between server paint and hydration. Initialize reset
375
+ // defaults without treating the live value or selection as a mismatch.
376
+ if (tag === 'input' || tag === 'textarea') queueHydrationFormState(candidate as Element, props, tag)
363
377
  for (const k in props) {
364
378
  if (
365
- k === 'children' ||
366
- k === 'key' ||
367
- k === 'ref' ||
368
- k === 'suppressHydrationWarning' ||
369
- k === 'suppressContentEditableWarning'
370
- ) continue
371
- if (
372
- syncHydrationProps ||
379
+ (syncHydrationProps &&
380
+ k !== 'children' && k !== 'key' && k !== 'ref' &&
381
+ (k !== 'nonce' || hydrationAttribute(candidate as Element, k) !== props[k]) &&
382
+ k !== 'suppressHydrationWarning' && k !== 'suppressContentEditableWarning' &&
383
+ !isHydrationFormProp(tag, k)) ||
373
384
  (k[0] === 'o' && k[1] === 'n' && typeof props[k] == 'function')
374
- ) {
375
- setProp(candidate as Element, k, props[k], undefined, isSvg)
376
- }
385
+ ) queueProp(candidate as Element, k, props[k], undefined, isSvg)
377
386
  }
378
387
  // Set up child cursor for this host's children
379
388
  hydrationCursors.set(fiber, new HydrationCursor(candidate))
380
389
  return true
381
390
  }
382
391
 
392
+ function queueHydrationFormState(el: Element, props: any, tag: string): void {
393
+ queueMutation(() => {
394
+ if (tag === 'input') {
395
+ const input = el as HTMLInputElement
396
+ if (props.type != null) input.type = props.type
397
+ const value = props.value ?? props.defaultValue
398
+ if (value != null && (props.value != null || (input.type !== 'submit' && input.type !== 'reset'))) {
399
+ input.defaultValue = '' + value
400
+ }
401
+ // Mark checked as dirty before changing its default, including clean SSR
402
+ // inputs whose client default differs. This also preserves radio groups.
403
+ input.checked = input.checked
404
+ input.defaultChecked = !!(props.checked ?? props.defaultChecked)
405
+ } else {
406
+ syncTextareaValue(el as HTMLTextAreaElement, props, true)
407
+ }
408
+ })
409
+ }
410
+
383
411
  export function adoptTextDom(fiber: Fiber, parent: Fiber, text: string): boolean {
384
412
  const cursor = hydrationCursors.get(findHostParent(parent))
385
413
  if (!cursor) return false
@@ -400,7 +428,7 @@ export function adoptTextDom(fiber: Fiber, parent: Fiber, text: string): boolean
400
428
  canRecoverHydrationPropMismatch(candidate),
401
429
  )
402
430
  if (recovered) {
403
- ;(candidate as Text).data = text
431
+ queueText(candidate as Text, text)
404
432
  }
405
433
  }
406
434
  fiber.dom = candidate
@@ -447,7 +475,7 @@ function failHydration(
447
475
  ): boolean {
448
476
  const root = findRoot(fiber)
449
477
  if (root?.re) {
450
- root.re(error)
478
+ root.re(error, { componentStack: componentStack(fiber) })
451
479
  }
452
480
  if (recoverInPlace) return true
453
481
  abortHydration(error, findHostRecoveryParent(fiber) ?? fiber)
@@ -533,6 +561,7 @@ export function recoverHydration(root: FiberRoot, error: unknown): boolean {
533
561
  flushSyncWork(() => renderRoot(root, children))
534
562
  } catch (clientError) {
535
563
  resetAfterHydrationFailure(root, container)
564
+ if (recoverRootError(clientError)) return true
536
565
  throw clientError
537
566
  }
538
567
  return true
@@ -542,25 +571,30 @@ function resetAfterHydrationFailure(
542
571
  root: FiberRoot,
543
572
  container: Element | Document,
544
573
  ): void {
574
+ // Retire committed subscriptions and portals without removing the document shell.
575
+ unmountFiber(root.r, container, false)
545
576
  discardPendingWork(root)
577
+ discardPendingEffects(root)
546
578
  clearHydrationContainer(container)
547
579
  attachRootFiber(root, container)
548
580
  root.h = false
549
581
  }
550
582
 
551
583
  function clearHydrationContainer(container: Element | Document): void {
552
- if (container.nodeType === 9) {
553
- let node = container.firstChild
554
- while (node) {
555
- const next = node.nextSibling
556
- if (node.nodeType !== 10 /* DOCUMENT_TYPE_NODE */) {
557
- container.removeChild(node)
584
+ queueMutation(() => {
585
+ if (container.nodeType === 9) {
586
+ let node = container.firstChild
587
+ while (node) {
588
+ const next = node.nextSibling
589
+ if (node.nodeType !== 10 /* DOCUMENT_TYPE_NODE */) {
590
+ container.removeChild(node)
591
+ }
592
+ node = next
558
593
  }
559
- node = next
594
+ return
560
595
  }
561
- return
562
- }
563
- ;(container as Element).textContent = ''
596
+ ;(container as Element).textContent = ''
597
+ })
564
598
  }
565
599
 
566
600
  function getRecoverableHostChildren(
@@ -684,6 +718,14 @@ function createHostElement(type: string, props: Record<string, unknown>): ReactE
684
718
  }
685
719
  }
686
720
 
721
+ function isHydrationFormProp(tag: string, key: string): boolean {
722
+ return (
723
+ ((tag === 'input' || tag === 'textarea' || tag === 'select') && (key === 'value' || key === 'defaultValue')) ||
724
+ (tag === 'input' && (key === 'checked' || key === 'defaultChecked')) ||
725
+ (tag === 'option' && key === 'selected')
726
+ )
727
+ }
728
+
687
729
  function validateHydrationProps(
688
730
  fiber: Fiber,
689
731
  el: Element,
@@ -708,13 +750,14 @@ function validateHydrationProps(
708
750
  ) continue
709
751
 
710
752
  if (k === 'dangerouslySetInnerHTML') {
711
- let html = '' + (value?.__html ?? '')
753
+ let html = value?.__html
754
+ if (html == null) continue
712
755
  if (tag !== 'script' && tag !== 'style') {
713
- const probe = document.createElement('div')
756
+ const probe = createHostNode(tag, isSvg)
714
757
  probe.innerHTML = html
715
758
  html = probe.innerHTML
716
759
  }
717
- if ((el as HTMLElement).innerHTML !== html) {
760
+ if ((el as HTMLElement).innerHTML !== '' + html) {
718
761
  failHydration(
719
762
  fiber,
720
763
  process.env.NODE_ENV !== 'production'
@@ -726,51 +769,7 @@ function validateHydrationProps(
726
769
  continue
727
770
  }
728
771
 
729
- if (k === 'value' || k === 'defaultValue') {
730
- if (tag === 'select') continue
731
- if (tag === 'input' || tag === 'textarea') {
732
- if (k === 'value' || props.value == null) {
733
- if (value != null && (el as HTMLInputElement).value !== '' + value) {
734
- failHydration(
735
- fiber,
736
- process.env.NODE_ENV !== 'production'
737
- ? new Error(`Hydration ${tag} value mismatch on <${tag}>.`)
738
- : undefined,
739
- recoverInPlace,
740
- )
741
- }
742
- }
743
- continue
744
- }
745
- }
746
-
747
- if (tag === 'input' && (k === 'checked' || k === 'defaultChecked')) {
748
- if (k === 'checked' || props.checked == null) {
749
- if (value != null && (el as HTMLInputElement).checked !== !!value) {
750
- failHydration(
751
- fiber,
752
- process.env.NODE_ENV !== 'production'
753
- ? new Error(`Hydration checked mismatch on <input>.`)
754
- : undefined,
755
- recoverInPlace,
756
- )
757
- }
758
- }
759
- continue
760
- }
761
-
762
- if (tag === 'option' && k === 'selected') {
763
- if (value != null && (el as HTMLOptionElement).selected !== !!value) {
764
- failHydration(
765
- fiber,
766
- process.env.NODE_ENV !== 'production'
767
- ? new Error(`Hydration selected mismatch on <option>.`)
768
- : undefined,
769
- recoverInPlace,
770
- )
771
- }
772
- continue
773
- }
772
+ if (isHydrationFormProp(tag, k)) continue
774
773
 
775
774
  if (k === 'style') {
776
775
  expected ??= createHostNode(tag, isSvg)
@@ -802,12 +801,15 @@ function validateHydrationProps(
802
801
  } else if (k === 'muted' && !isSvg && (tag === 'video' || tag === 'audio')) {
803
802
  // The muted property controls playback; defaultMuted reflects the SSR attribute.
804
803
  expectedValue = value ? '' : null
804
+ } else if (stringifiedBoolean && k.length > 5) {
805
+ // These attributes use setAttribute string coercion, not DOM property normalization.
806
+ expectedValue = '' + value
805
807
  } else {
806
808
  expected ??= createHostNode(tag, isSvg)
807
809
  setProp(expected, k, value, undefined, isSvg)
808
810
  expectedValue = expected.getAttribute(attr)
809
811
  }
810
- const actualValue = el.getAttribute(attr)
812
+ const actualValue = hydrationAttribute(el, attr)
811
813
  if (expectedValue !== actualValue) {
812
814
  failHydration(
813
815
  fiber,
@@ -24,7 +24,7 @@ export class HydrationCursor {
24
24
  }
25
25
  }
26
26
 
27
- export type BoundaryInfo = [0 | 1, number, Comment, Comment]
27
+ export type BoundaryInfo = [0 | 1 | 2 | 3, number, Comment, Comment]
28
28
 
29
29
  export function beginHydration(_root: FiberRoot): void {
30
30
  throw new Error(
@@ -9,3 +9,4 @@ import './memo'
9
9
  import './forward-ref'
10
10
  import './lazy'
11
11
  import './class'
12
+ import './activity'
@@ -10,6 +10,7 @@ import {
10
10
  renderFiber,
11
11
  getForceRerenderingFiber,
12
12
  } from '../../reconcile'
13
+ import { ownContextChanged, renderContextConsumers } from '../context'
13
14
 
14
15
  function shallowEqual(a: any, b: any): boolean {
15
16
  if (a === b) return true
@@ -33,10 +34,10 @@ function renderMemo(fiber: Fiber, domParent: Node, anchor: Node | null): void {
33
34
  // useSyncExternalStore notification), props haven't changed by definition —
34
35
  // bailing would swallow the state change and the subscriber never re-runs.
35
36
  // rerenderFiber tags the fiber so we skip the gate here.
36
- const bypassMemo = fiber === getForceRerenderingFiber()
37
+ const bypassMemo = fiber.dy || fiber === getForceRerenderingFiber()
37
38
  const eq = !bypassMemo && prev && (compare ? compare(prev, props) : shallowEqual(prev, props))
38
- if (eq) {
39
- // Re-render children with previous output (already in tree)
39
+ if (eq && !ownContextChanged(fiber)) {
40
+ renderContextConsumers(fiber)
40
41
  return
41
42
  }
42
43