|
@@ -20,6 +20,8 @@ import {
|
|
|
} from './node'
|
|
} from './node'
|
|
|
import { remove } from '../block'
|
|
import { remove } from '../block'
|
|
|
|
|
|
|
|
|
|
+const START_TAG_RE = /^<([^\s/>]+)/
|
|
|
|
|
+
|
|
|
export let isHydratingEnabled = false
|
|
export let isHydratingEnabled = false
|
|
|
|
|
|
|
|
export function setIsHydratingEnabled(value: boolean): void {
|
|
export function setIsHydratingEnabled(value: boolean): void {
|
|
@@ -321,21 +323,7 @@ export function locateHydrationBoundaryClose(
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
function handleMismatch(node: Node, template: string): Node {
|
|
function handleMismatch(node: Node, template: string): Node {
|
|
|
- if (!isMismatchAllowed(node.parentElement!, MismatchTypes.CHILDREN)) {
|
|
|
|
|
- ;(__DEV__ || __FEATURE_PROD_HYDRATION_MISMATCH_DETAILS__) &&
|
|
|
|
|
- warn(
|
|
|
|
|
- `Hydration node mismatch:\n- rendered on server:`,
|
|
|
|
|
- node,
|
|
|
|
|
- node.nodeType === 3
|
|
|
|
|
- ? `(text)`
|
|
|
|
|
- : isComment(node, '[[')
|
|
|
|
|
- ? `(start of block node)`
|
|
|
|
|
- : ``,
|
|
|
|
|
- `\n- expected on client:`,
|
|
|
|
|
- template,
|
|
|
|
|
- )
|
|
|
|
|
- logMismatchError()
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ warnHydrationNodeMismatch(node, template)
|
|
|
|
|
|
|
|
// fragment
|
|
// fragment
|
|
|
if (isComment(node, '[')) {
|
|
if (isComment(node, '[')) {
|
|
@@ -373,6 +361,54 @@ function handleMismatch(node: Node, template: string): Node {
|
|
|
return newNode
|
|
return newNode
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+export function validateHydrationTarget(node: Node, template: string): void {
|
|
|
|
|
+ let expectedType: number
|
|
|
|
|
+ if (template[0] !== '<') {
|
|
|
|
|
+ expectedType = 3
|
|
|
|
|
+ } else if (template[1] === '!') {
|
|
|
|
|
+ expectedType = 8
|
|
|
|
|
+ } else {
|
|
|
|
|
+ expectedType = 1
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if (node.nodeType !== expectedType) {
|
|
|
|
|
+ warnHydrationNodeMismatch(node, template)
|
|
|
|
|
+ return
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if (expectedType !== 1) {
|
|
|
|
|
+ return
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ const match = START_TAG_RE.exec(template)
|
|
|
|
|
+ const expectedTag = match && match[1]
|
|
|
|
|
+
|
|
|
|
|
+ if (
|
|
|
|
|
+ expectedTag &&
|
|
|
|
|
+ (node as Element).tagName.toLowerCase() !== expectedTag.toLowerCase()
|
|
|
|
|
+ ) {
|
|
|
|
|
+ warnHydrationNodeMismatch(node, template)
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+function warnHydrationNodeMismatch(node: Node, expected: unknown): void {
|
|
|
|
|
+ if (!isMismatchAllowed(node.parentElement!, MismatchTypes.CHILDREN)) {
|
|
|
|
|
+ ;(__DEV__ || __FEATURE_PROD_HYDRATION_MISMATCH_DETAILS__) &&
|
|
|
|
|
+ warn(
|
|
|
|
|
+ `Hydration node mismatch:\n- rendered on server:`,
|
|
|
|
|
+ node,
|
|
|
|
|
+ node.nodeType === 3
|
|
|
|
|
+ ? `(text)`
|
|
|
|
|
+ : isComment(node, '[[')
|
|
|
|
|
+ ? `(start of block node)`
|
|
|
|
|
+ : ``,
|
|
|
|
|
+ `\n- expected on client:`,
|
|
|
|
|
+ expected,
|
|
|
|
|
+ )
|
|
|
|
|
+ logMismatchError()
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
let hasLoggedMismatchError = false
|
|
let hasLoggedMismatchError = false
|
|
|
export const logMismatchError = (): void => {
|
|
export const logMismatchError = (): void => {
|
|
|
if (__TEST__ || hasLoggedMismatchError) {
|
|
if (__TEST__ || hasLoggedMismatchError) {
|