Skip to content

Commit

Permalink
Share the hydration error info extraction util (vercel#67639)
Browse files Browse the repository at this point in the history
### What

Share the util of extract hydration error info used for dev error
overlay into one shared util between pages router and app router
  • Loading branch information
huozhi committed Jul 10, 2024
1 parent 3cce038 commit 026f887
Show file tree
Hide file tree
Showing 3 changed files with 60 additions and 90 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import {
isHydrationError,
getDefaultHydrationErrorMessage,
} from '../../../is-hydration-error'
import {
hydrationErrorState,
getReactHydrationDiffSegments,
} from './hydration-error-info'

export function attachHydrationErrorState(error: Error) {
if (
isHydrationError(error) &&
!error.message.includes(
'https://nextjs.org/docs/messages/react-hydration-error'
)
) {
const reactHydrationDiffSegments = getReactHydrationDiffSegments(
error.message
)
let parsedHydrationErrorState: typeof hydrationErrorState = {}
if (reactHydrationDiffSegments) {
parsedHydrationErrorState = {
...(error as any).details,
...hydrationErrorState,
warning: hydrationErrorState.warning || [
getDefaultHydrationErrorMessage(),
],
notes: reactHydrationDiffSegments[0],
reactOutputComponentDiff: reactHydrationDiffSegments[1],
}
} else {
// If there's any extra information in the error message to display,
// append it to the error message details property
if (hydrationErrorState.warning) {
// The patched console.error found hydration errors logged by React
// Append the logged warning to the error message
parsedHydrationErrorState = {
...(error as any).details,
// It contains the warning, component stack, server and client tag names
...hydrationErrorState,
}
}
error.message +=
'\nSee more info here: https://nextjs.org/docs/messages/react-hydration-error'
}
;(error as any).details = parsedHydrationErrorState
}
}
Original file line number Diff line number Diff line change
@@ -1,13 +1,8 @@
import { useEffect } from 'react'
import {
hydrationErrorState,
getReactHydrationDiffSegments,
} from './hydration-error-info'

import { isNextRouterError } from '../../../is-next-router-error'
import {
isHydrationError,
getDefaultHydrationErrorMessage,
} from '../../../is-hydration-error'
import { isHydrationError } from '../../../is-hydration-error'
import { attachHydrationErrorState } from './attach-hydration-error-state'

export type ErrorHandler = (error: Error) => void

Expand All @@ -30,47 +25,10 @@ export function handleClientError(error: unknown) {
return
}

const isCausedByHydrationFailure = isHydrationError(error)
if (
isHydrationError(error) &&
!error.message.includes(
'https://nextjs.org/docs/messages/react-hydration-error'
)
) {
const reactHydrationDiffSegments = getReactHydrationDiffSegments(
error.message
)
let parsedHydrationErrorState: typeof hydrationErrorState = {}
if (reactHydrationDiffSegments) {
parsedHydrationErrorState = {
...(error as any).details,
...hydrationErrorState,
warning: hydrationErrorState.warning || [
getDefaultHydrationErrorMessage(),
],
notes: reactHydrationDiffSegments[0],
reactOutputComponentDiff: reactHydrationDiffSegments[1],
}
} else {
// If there's any extra information in the error message to display,
// append it to the error message details property
if (hydrationErrorState.warning) {
// The patched console.error found hydration errors logged by React
// Append the logged warning to the error message
parsedHydrationErrorState = {
...(error as any).details,
// It contains the warning, component stack, server and client tag names
...hydrationErrorState,
}
}
error.message +=
'\nSee more info here: https://nextjs.org/docs/messages/react-hydration-error'
}
;(error as any).details = parsedHydrationErrorState
}
attachHydrationErrorState(error)

// Only queue one hydration every time
if (isCausedByHydrationFailure) {
if (isHydrationError(error)) {
if (!hasHydrationError) {
errorQueue.push(error)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import * as Bus from './bus'
import { parseStack } from '../internal/helpers/parseStack'
import { parseComponentStack } from '../internal/helpers/parse-component-stack'
import {
getReactHydrationDiffSegments,
hydrationErrorState,
storeHydrationErrorStateFromConsoleArgs,
} from '../internal/helpers/hydration-error-info'
Expand All @@ -16,10 +15,7 @@ import {
ACTION_VERSION_INFO,
} from '../shared'
import type { VersionInfo } from '../../../../server/dev/parse-version-info'
import {
getDefaultHydrationErrorMessage,
isHydrationError,
} from '../../is-hydration-error'
import { attachHydrationErrorState } from '../internal/helpers/attach-hydration-error-state'

let isRegistered = false
let stackTraceLimit: number | undefined = undefined
Expand All @@ -30,43 +26,8 @@ function handleError(error: unknown) {
return
}

if (
isHydrationError(error) &&
!error.message.includes(
'https://nextjs.org/docs/messages/react-hydration-error'
)
) {
const reactHydrationDiffSegments = getReactHydrationDiffSegments(
error.message
)
let parsedHydrationErrorState: typeof hydrationErrorState = {}
if (reactHydrationDiffSegments) {
parsedHydrationErrorState = {
...(error as any).details,
warning: hydrationErrorState.warning || [
getDefaultHydrationErrorMessage(),
],
notes: reactHydrationDiffSegments[0],
reactOutputComponentDiff: reactHydrationDiffSegments[1],
}
} else {
// If there's any extra information in the error message to display,
// append it to the error message details property
if (hydrationErrorState.warning) {
// The patched console.error found hydration errors logged by React
// Append the logged warning to the error message
parsedHydrationErrorState = {
...(error as any).details,
// It contains the warning, component stack, server and client tag names
...hydrationErrorState,
}
}
error.message += `\nSee more info here: https://nextjs.org/docs/messages/react-hydration-error`
}
;(error as any).details = parsedHydrationErrorState
}
attachHydrationErrorState(error)

const e = error
const componentStackTrace =
(error as any)._componentStack || hydrationErrorState.componentStack
const componentStackFrames =
Expand All @@ -76,11 +37,14 @@ function handleError(error: unknown) {

// Skip ModuleBuildError and ModuleNotFoundError, as it will be sent through onBuildError callback.
// This is to avoid same error as different type showing up on client to cause flashing.
if (e.name !== 'ModuleBuildError' && e.name !== 'ModuleNotFoundError') {
if (
error.name !== 'ModuleBuildError' &&
error.name !== 'ModuleNotFoundError'
) {
Bus.emit({
type: ACTION_UNHANDLED_ERROR,
reason: error,
frames: parseStack(e.stack!),
frames: parseStack(error.stack!),
componentStackFrames,
})
}
Expand Down

0 comments on commit 026f887

Please sign in to comment.