@tanstack/redact 0.0.17 → 0.0.19
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/core/internal.d.ts +1 -1
- package/dist/core/internal.js.map +1 -1
- package/dist/dom/dispatcher.js +3 -1
- package/dist/dom/dispatcher.js.map +2 -2
- package/dist/dom/features/hydration/full.d.ts +1 -0
- package/dist/dom/features/hydration/full.js +118 -114
- package/dist/dom/features/hydration/full.js.map +2 -2
- package/dist/dom/features/hydration/stub.d.ts +1 -0
- package/dist/dom/features/hydration/stub.js +4 -0
- package/dist/dom/features/hydration/stub.js.map +2 -2
- package/dist/dom/reconcile.js +9 -3
- package/dist/dom/reconcile.js.map +2 -2
- package/dist/dom/root-internal.js +1 -1
- package/dist/dom/root-internal.js.map +2 -2
- package/package.json +5 -5
- package/src/core/internal.ts +1 -1
- package/src/dom/dispatcher.ts +3 -3
- package/src/dom/features/hydration/full.ts +168 -121
- package/src/dom/features/hydration/stub.ts +4 -0
- package/src/dom/reconcile.ts +13 -2
- package/src/dom/root-internal.ts +1 -1
|
@@ -120,14 +120,7 @@ export class HydrationCursor {
|
|
|
120
120
|
has(): boolean {
|
|
121
121
|
let n = this.n
|
|
122
122
|
while (n && n !== this.e) {
|
|
123
|
-
if (n.nodeType === 1)
|
|
124
|
-
if ((n as Element).tagName === 'SCRIPT') {
|
|
125
|
-
n = n.nextSibling
|
|
126
|
-
continue
|
|
127
|
-
}
|
|
128
|
-
return true
|
|
129
|
-
}
|
|
130
|
-
if (n.nodeType === 3 && (n as Text).data.trim() !== '') return true
|
|
123
|
+
if (n.nodeType === 1 || n.nodeType === 3) return true
|
|
131
124
|
n = n.nextSibling
|
|
132
125
|
}
|
|
133
126
|
return false
|
|
@@ -171,17 +164,12 @@ export function hydrateRootImpl(
|
|
|
171
164
|
const target = container as any as Element | Document
|
|
172
165
|
const isDocument = (container as Node).nodeType === 9
|
|
173
166
|
const body = isDocument ? (target as Document).body : null
|
|
174
|
-
const root = createFiberRoot(target,
|
|
175
|
-
...options,
|
|
176
|
-
identifierPrefix: options.identifierPrefix ?? ':R',
|
|
177
|
-
})
|
|
167
|
+
const root = createFiberRoot(target, options)
|
|
178
168
|
|
|
179
169
|
installHydrationScrollGuard()
|
|
180
170
|
|
|
181
171
|
const normalizedInitialChildren =
|
|
182
172
|
isDocument ? normalizeDocumentChildren(initialChildren) : initialChildren
|
|
183
|
-
let documentBodyFallback = false
|
|
184
|
-
|
|
185
173
|
let hydrationError: unknown = null
|
|
186
174
|
beginHydration(root)
|
|
187
175
|
try {
|
|
@@ -193,36 +181,7 @@ export function hydrateRootImpl(
|
|
|
193
181
|
}
|
|
194
182
|
endHydration(root)
|
|
195
183
|
|
|
196
|
-
if (hydrationError)
|
|
197
|
-
if (!isHydrationBailout(hydrationError)) {
|
|
198
|
-
throw hydrationError
|
|
199
|
-
}
|
|
200
|
-
let recoveryContainer: Element | Document = target
|
|
201
|
-
let recoveryChildren = normalizedInitialChildren
|
|
202
|
-
const hostRecovery = getRecoverableHostChildren(hydrationError)
|
|
203
|
-
if (hostRecovery) {
|
|
204
|
-
recoveryContainer = hostRecovery[0]
|
|
205
|
-
recoveryChildren = hostRecovery[1]
|
|
206
|
-
} else if (body) {
|
|
207
|
-
const bodyChildren = getRecoverableDocumentBodyChildren(hydrationError)
|
|
208
|
-
if (bodyChildren != null) {
|
|
209
|
-
documentBodyFallback = true
|
|
210
|
-
recoveryContainer = body
|
|
211
|
-
recoveryChildren = bodyChildren
|
|
212
|
-
}
|
|
213
|
-
}
|
|
214
|
-
resetAfterHydrationFailure(root, recoveryContainer)
|
|
215
|
-
try {
|
|
216
|
-
root.i = options.identifierPrefix ?? ':r'
|
|
217
|
-
root.ic = 0
|
|
218
|
-
flushSyncWork(() => {
|
|
219
|
-
renderRoot(root, recoveryChildren)
|
|
220
|
-
})
|
|
221
|
-
} catch (clientError) {
|
|
222
|
-
resetAfterHydrationFailure(root, recoveryContainer)
|
|
223
|
-
throw clientError
|
|
224
|
-
}
|
|
225
|
-
}
|
|
184
|
+
if (hydrationError && !recoverHydration(root, hydrationError)) throw hydrationError
|
|
226
185
|
drainReplayQueue()
|
|
227
186
|
|
|
228
187
|
return {
|
|
@@ -231,7 +190,7 @@ export function hydrateRootImpl(
|
|
|
231
190
|
const normalized = isDocument ? normalizeDocumentChildren(children) : children
|
|
232
191
|
renderRoot(
|
|
233
192
|
root,
|
|
234
|
-
|
|
193
|
+
root.c === body ? getStaticDocumentBodyChildren(normalized) ?? normalized : normalized,
|
|
235
194
|
)
|
|
236
195
|
})
|
|
237
196
|
},
|
|
@@ -246,8 +205,9 @@ export function hydrateRootImpl(
|
|
|
246
205
|
// Head elements that we match against server DOM by attribute signature.
|
|
247
206
|
const HEAD_KEY_ATTRS: Record<string, ReadonlyArray<string>> = {
|
|
248
207
|
link: ['rel', 'href', 'sizes', 'type'],
|
|
249
|
-
meta: ['name', 'property', '
|
|
208
|
+
meta: ['name', 'property', 'charSet', 'httpEquiv'],
|
|
250
209
|
script: ['src', 'type'],
|
|
210
|
+
style: ['id', 'media', 'nonce', 'title'],
|
|
251
211
|
}
|
|
252
212
|
|
|
253
213
|
const DOCUMENT_HEAD_TAGS = new Set(['base', 'link', 'meta', 'script', 'style', 'title'])
|
|
@@ -262,21 +222,17 @@ function headAttrsMatch(
|
|
|
262
222
|
): boolean {
|
|
263
223
|
if (CLAIMED.has(el)) return false
|
|
264
224
|
if (!keys) return true
|
|
265
|
-
let matched = false
|
|
266
225
|
for (const k of keys) {
|
|
267
|
-
const propVal = props[k]
|
|
268
|
-
const elVal = el.getAttribute(k)
|
|
269
|
-
// If neither defines it, skip this key; if one defines it, they must match.
|
|
226
|
+
const propVal = props[k]
|
|
227
|
+
const elVal = el.getAttribute(attributeName(k))
|
|
270
228
|
if (propVal == null && elVal == null) continue
|
|
271
|
-
|
|
272
|
-
if (propVal == null || elVal == null) continue // tolerate missing on either side
|
|
273
|
-
if (String(propVal) !== elVal) return false
|
|
229
|
+
if (propVal == null || elVal == null || String(propVal) !== elVal) return false
|
|
274
230
|
}
|
|
275
|
-
|
|
276
|
-
return matched
|
|
231
|
+
return true
|
|
277
232
|
}
|
|
278
233
|
|
|
279
234
|
export function beginHydration(root: FiberRoot): void {
|
|
235
|
+
root.ic = 0
|
|
280
236
|
root.h = true
|
|
281
237
|
hydrationCursors.set(root.r, new HydrationCursor(root.c))
|
|
282
238
|
}
|
|
@@ -400,13 +356,24 @@ export function adoptHostDom(fiber: Fiber, parent: Fiber): boolean {
|
|
|
400
356
|
tag === 'svg' ||
|
|
401
357
|
((candidate as Element).namespaceURI === 'http://www.w3.org/2000/svg' &&
|
|
402
358
|
tag !== 'foreignobject')
|
|
359
|
+
const syncHydrationProps =
|
|
360
|
+
!props.suppressHydrationWarning &&
|
|
361
|
+
canRecoverHydrationPropMismatch(candidate, tag)
|
|
403
362
|
validateHydrationProps(fiber, candidate as Element, props, tag, isSvg)
|
|
404
363
|
for (const k in props) {
|
|
405
|
-
if (
|
|
406
|
-
|
|
364
|
+
if (
|
|
365
|
+
k === 'children' ||
|
|
366
|
+
k === 'key' ||
|
|
367
|
+
k === 'ref' ||
|
|
368
|
+
k === 'suppressHydrationWarning' ||
|
|
369
|
+
k === 'suppressContentEditableWarning'
|
|
370
|
+
) continue
|
|
371
|
+
if (
|
|
372
|
+
syncHydrationProps ||
|
|
373
|
+
(k[0] === 'o' && k[1] === 'n' && typeof props[k] == 'function')
|
|
374
|
+
) {
|
|
407
375
|
setProp(candidate as Element, k, props[k], undefined, isSvg)
|
|
408
376
|
}
|
|
409
|
-
// Non-event props: trust the server HTML, skip
|
|
410
377
|
}
|
|
411
378
|
// Set up child cursor for this host's children
|
|
412
379
|
hydrationCursors.set(fiber, new HydrationCursor(candidate))
|
|
@@ -417,16 +384,24 @@ export function adoptTextDom(fiber: Fiber, parent: Fiber, text: string): boolean
|
|
|
417
384
|
const cursor = hydrationCursors.get(findHostParent(parent))
|
|
418
385
|
if (!cursor) return false
|
|
419
386
|
const candidate = cursor.take()
|
|
420
|
-
if (!candidate)
|
|
387
|
+
if (!candidate) {
|
|
388
|
+
onMismatch(fiber, null)
|
|
389
|
+
return false
|
|
390
|
+
}
|
|
421
391
|
if (candidate.nodeType === 3) {
|
|
422
392
|
if ((candidate as Text).data !== text) {
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
new Error(
|
|
427
|
-
|
|
393
|
+
const recovered = failHydration(
|
|
394
|
+
fiber,
|
|
395
|
+
process.env.NODE_ENV !== 'production'
|
|
396
|
+
? new Error(
|
|
397
|
+
`Hydration text mismatch: expected "${text}" but found "${(candidate as Text).data}".`,
|
|
398
|
+
)
|
|
399
|
+
: undefined,
|
|
400
|
+
canRecoverHydrationPropMismatch(candidate),
|
|
401
|
+
)
|
|
402
|
+
if (recovered) {
|
|
403
|
+
;(candidate as Text).data = text
|
|
428
404
|
}
|
|
429
|
-
failHydration(fiber)
|
|
430
405
|
}
|
|
431
406
|
fiber.dom = candidate
|
|
432
407
|
return true
|
|
@@ -452,28 +427,41 @@ export function findHostParent(fiber: Fiber): Fiber {
|
|
|
452
427
|
throw new Error()
|
|
453
428
|
}
|
|
454
429
|
|
|
455
|
-
function onMismatch(fiber: Fiber, actualNode: ChildNode | null):
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
new Error(
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
failHydration(fiber)
|
|
430
|
+
function onMismatch(fiber: Fiber, actualNode: ChildNode | null): void {
|
|
431
|
+
failHydration(
|
|
432
|
+
fiber,
|
|
433
|
+
process.env.NODE_ENV !== 'production'
|
|
434
|
+
? new Error(
|
|
435
|
+
`Hydration mismatch: expected <${(fiber.type as string) ?? 'text'}> but found ${
|
|
436
|
+
actualNode ? (actualNode.nodeType === 1 ? (actualNode as Element).tagName : 'text') : 'nothing'
|
|
437
|
+
}.`,
|
|
438
|
+
)
|
|
439
|
+
: undefined,
|
|
440
|
+
)
|
|
467
441
|
}
|
|
468
442
|
|
|
469
|
-
function failHydration(
|
|
443
|
+
function failHydration(
|
|
444
|
+
fiber: Fiber,
|
|
445
|
+
error: Error = new Error(PROD_HYDRATION_ERROR),
|
|
446
|
+
recoverInPlace = false,
|
|
447
|
+
): boolean {
|
|
470
448
|
const root = findRoot(fiber)
|
|
471
449
|
if (root?.re) {
|
|
472
450
|
root.re(error)
|
|
473
451
|
}
|
|
452
|
+
if (recoverInPlace) return true
|
|
474
453
|
abortHydration(error, findHostRecoveryParent(fiber) ?? fiber)
|
|
475
454
|
}
|
|
476
455
|
|
|
456
|
+
function canRecoverHydrationPropMismatch(node: Node, tag?: string): boolean {
|
|
457
|
+
return (
|
|
458
|
+
tag === 'html' ||
|
|
459
|
+
tag === 'head' ||
|
|
460
|
+
tag === 'body' ||
|
|
461
|
+
!!node.ownerDocument?.head?.contains(node)
|
|
462
|
+
)
|
|
463
|
+
}
|
|
464
|
+
|
|
477
465
|
function findHostRecoveryParent(fiber: Fiber): Fiber | null {
|
|
478
466
|
if (fiber.tag === FiberTag.Text) {
|
|
479
467
|
let directHost = fiber.parent
|
|
@@ -523,6 +511,33 @@ function isSafeHostRecoveryElement(fiber: Fiber): boolean {
|
|
|
523
511
|
return tag !== 'html' && tag !== 'head' && tag !== 'body'
|
|
524
512
|
}
|
|
525
513
|
|
|
514
|
+
export function recoverHydration(root: FiberRoot, error: unknown): boolean {
|
|
515
|
+
if (!isHydrationBailout(error)) return false
|
|
516
|
+
|
|
517
|
+
let container = root.c as Element | Document
|
|
518
|
+
let children = root.r.pp?.children ?? null
|
|
519
|
+
const hostRecovery = getRecoverableHostChildren(error)
|
|
520
|
+
if (hostRecovery) {
|
|
521
|
+
container = hostRecovery[0]
|
|
522
|
+
children = hostRecovery[1]
|
|
523
|
+
} else if (container.nodeType === 9) {
|
|
524
|
+
const bodyChildren = getRecoverableDocumentBodyChildren(error)
|
|
525
|
+
if (bodyChildren != null) {
|
|
526
|
+
container = (container as Document).body
|
|
527
|
+
children = bodyChildren
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
resetAfterHydrationFailure(root, container)
|
|
532
|
+
try {
|
|
533
|
+
flushSyncWork(() => renderRoot(root, children))
|
|
534
|
+
} catch (clientError) {
|
|
535
|
+
resetAfterHydrationFailure(root, container)
|
|
536
|
+
throw clientError
|
|
537
|
+
}
|
|
538
|
+
return true
|
|
539
|
+
}
|
|
540
|
+
|
|
526
541
|
function resetAfterHydrationFailure(
|
|
527
542
|
root: FiberRoot,
|
|
528
543
|
container: Element | Document,
|
|
@@ -678,6 +693,7 @@ function validateHydrationProps(
|
|
|
678
693
|
): void {
|
|
679
694
|
if (props.suppressHydrationWarning) return
|
|
680
695
|
|
|
696
|
+
const recoverInPlace = canRecoverHydrationPropMismatch(el, tag)
|
|
681
697
|
let expected: Element | undefined
|
|
682
698
|
|
|
683
699
|
for (const k in props) {
|
|
@@ -692,16 +708,20 @@ function validateHydrationProps(
|
|
|
692
708
|
) continue
|
|
693
709
|
|
|
694
710
|
if (k === 'dangerouslySetInnerHTML') {
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
711
|
+
let html = '' + (value?.__html ?? '')
|
|
712
|
+
if (tag !== 'script' && tag !== 'style') {
|
|
713
|
+
const probe = document.createElement('div')
|
|
714
|
+
probe.innerHTML = html
|
|
715
|
+
html = probe.innerHTML
|
|
716
|
+
}
|
|
717
|
+
if ((el as HTMLElement).innerHTML !== html) {
|
|
718
|
+
failHydration(
|
|
719
|
+
fiber,
|
|
720
|
+
process.env.NODE_ENV !== 'production'
|
|
721
|
+
? new Error(`Hydration HTML mismatch inside <${tag}>.`)
|
|
722
|
+
: undefined,
|
|
723
|
+
recoverInPlace,
|
|
724
|
+
)
|
|
705
725
|
}
|
|
706
726
|
continue
|
|
707
727
|
}
|
|
@@ -711,10 +731,13 @@ function validateHydrationProps(
|
|
|
711
731
|
if (tag === 'input' || tag === 'textarea') {
|
|
712
732
|
if (k === 'value' || props.value == null) {
|
|
713
733
|
if (value != null && (el as HTMLInputElement).value !== '' + value) {
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
734
|
+
failHydration(
|
|
735
|
+
fiber,
|
|
736
|
+
process.env.NODE_ENV !== 'production'
|
|
737
|
+
? new Error(`Hydration ${tag} value mismatch on <${tag}>.`)
|
|
738
|
+
: undefined,
|
|
739
|
+
recoverInPlace,
|
|
740
|
+
)
|
|
718
741
|
}
|
|
719
742
|
}
|
|
720
743
|
continue
|
|
@@ -724,10 +747,13 @@ function validateHydrationProps(
|
|
|
724
747
|
if (tag === 'input' && (k === 'checked' || k === 'defaultChecked')) {
|
|
725
748
|
if (k === 'checked' || props.checked == null) {
|
|
726
749
|
if (value != null && (el as HTMLInputElement).checked !== !!value) {
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
750
|
+
failHydration(
|
|
751
|
+
fiber,
|
|
752
|
+
process.env.NODE_ENV !== 'production'
|
|
753
|
+
? new Error(`Hydration checked mismatch on <input>.`)
|
|
754
|
+
: undefined,
|
|
755
|
+
recoverInPlace,
|
|
756
|
+
)
|
|
731
757
|
}
|
|
732
758
|
}
|
|
733
759
|
continue
|
|
@@ -735,10 +761,13 @@ function validateHydrationProps(
|
|
|
735
761
|
|
|
736
762
|
if (tag === 'option' && k === 'selected') {
|
|
737
763
|
if (value != null && (el as HTMLOptionElement).selected !== !!value) {
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
764
|
+
failHydration(
|
|
765
|
+
fiber,
|
|
766
|
+
process.env.NODE_ENV !== 'production'
|
|
767
|
+
? new Error(`Hydration selected mismatch on <option>.`)
|
|
768
|
+
: undefined,
|
|
769
|
+
recoverInPlace,
|
|
770
|
+
)
|
|
742
771
|
}
|
|
743
772
|
continue
|
|
744
773
|
}
|
|
@@ -746,14 +775,19 @@ function validateHydrationProps(
|
|
|
746
775
|
if (k === 'style') {
|
|
747
776
|
expected ??= createHostNode(tag, isSvg)
|
|
748
777
|
setProp(expected, k, value, undefined, isSvg)
|
|
749
|
-
if (
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
778
|
+
if (
|
|
779
|
+
!hydrationStylesMatch(
|
|
780
|
+
(el as HTMLElement).style,
|
|
781
|
+
(expected as HTMLElement).style,
|
|
782
|
+
)
|
|
783
|
+
) {
|
|
784
|
+
failHydration(
|
|
785
|
+
fiber,
|
|
786
|
+
process.env.NODE_ENV !== 'production'
|
|
787
|
+
? new Error(`Hydration style mismatch on <${tag}>.`)
|
|
788
|
+
: undefined,
|
|
789
|
+
recoverInPlace,
|
|
790
|
+
)
|
|
757
791
|
}
|
|
758
792
|
continue
|
|
759
793
|
}
|
|
@@ -771,21 +805,34 @@ function validateHydrationProps(
|
|
|
771
805
|
}
|
|
772
806
|
const actualValue = el.getAttribute(attr)
|
|
773
807
|
if (expectedValue !== actualValue) {
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
808
|
+
failHydration(
|
|
809
|
+
fiber,
|
|
810
|
+
process.env.NODE_ENV !== 'production'
|
|
811
|
+
? new Error(
|
|
812
|
+
`Hydration attribute mismatch on <${tag}> for "${attr}": ` +
|
|
813
|
+
`expected ${formatHydrationValue(expectedValue)} but found ${formatHydrationValue(actualValue)}.`,
|
|
814
|
+
)
|
|
815
|
+
: undefined,
|
|
816
|
+
recoverInPlace,
|
|
817
|
+
)
|
|
818
|
+
}
|
|
819
|
+
}
|
|
820
|
+
}
|
|
821
|
+
|
|
822
|
+
function hydrationStylesMatch(
|
|
823
|
+
actual: CSSStyleDeclaration,
|
|
824
|
+
expected: CSSStyleDeclaration,
|
|
825
|
+
): boolean {
|
|
826
|
+
for (let index = 0; index < expected.length; index++) {
|
|
827
|
+
const property = expected.item(index)
|
|
828
|
+
if (
|
|
829
|
+
actual.getPropertyValue(property) !== expected.getPropertyValue(property) ||
|
|
830
|
+
actual.getPropertyPriority(property) !== expected.getPropertyPriority(property)
|
|
831
|
+
) {
|
|
832
|
+
return false
|
|
787
833
|
}
|
|
788
834
|
}
|
|
835
|
+
return true
|
|
789
836
|
}
|
|
790
837
|
|
|
791
838
|
function formatHydrationValue(value: string | null): string {
|
|
@@ -74,6 +74,10 @@ export function isHydrationBailout(_error: unknown): _error is HydrationBailoutE
|
|
|
74
74
|
return false
|
|
75
75
|
}
|
|
76
76
|
|
|
77
|
+
export function recoverHydration(_root: FiberRoot, _error: unknown): boolean {
|
|
78
|
+
return false
|
|
79
|
+
}
|
|
80
|
+
|
|
77
81
|
export function abortHydration(cause: unknown, fiber: Fiber | null = null): never {
|
|
78
82
|
const error = (cause instanceof Error ? cause : new Error('Hydration mismatch.')) as HydrationBailoutError
|
|
79
83
|
;(error as any).f = fiber
|
package/src/dom/reconcile.ts
CHANGED
|
@@ -27,6 +27,7 @@ import {
|
|
|
27
27
|
clearHydrationCursor,
|
|
28
28
|
findHostParent as findHydrationHost,
|
|
29
29
|
abortHydration,
|
|
30
|
+
recoverHydration,
|
|
30
31
|
} from './features/hydration'
|
|
31
32
|
|
|
32
33
|
// ---------------------------------------------------------------------------
|
|
@@ -117,7 +118,12 @@ function flushPending(): void {
|
|
|
117
118
|
root.p.clear()
|
|
118
119
|
pending.sort((a, b) => fiberDepth(a) - fiberDepth(b))
|
|
119
120
|
for (const fiber of pending) {
|
|
120
|
-
|
|
121
|
+
try {
|
|
122
|
+
rerenderFiber(fiber, root)
|
|
123
|
+
} catch (error) {
|
|
124
|
+
if (!recoverHydration(root, error)) throw error
|
|
125
|
+
break
|
|
126
|
+
}
|
|
121
127
|
}
|
|
122
128
|
runEffects(root)
|
|
123
129
|
}
|
|
@@ -815,7 +821,12 @@ function renderHost(fiber: Fiber, domParent: Node, anchor: Node | null): void {
|
|
|
815
821
|
const hasOpaqueHydrationChildren =
|
|
816
822
|
props.dangerouslySetInnerHTML != null ||
|
|
817
823
|
(parentTag === 'textarea' && (props.value != null || props.defaultValue != null))
|
|
818
|
-
if (
|
|
824
|
+
if (
|
|
825
|
+
parentTag !== 'head' &&
|
|
826
|
+
parentTag !== 'html' &&
|
|
827
|
+
parentTag !== 'body' &&
|
|
828
|
+
!hasOpaqueHydrationChildren
|
|
829
|
+
) {
|
|
819
830
|
const cursor = getHydrationCursor(fiber)
|
|
820
831
|
if (cursor) {
|
|
821
832
|
if (cursor.has()) {
|
package/src/dom/root-internal.ts
CHANGED