Thanks to visit codestin.com
Credit goes to github.com

Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 9 additions & 11 deletions packages/next/src/client/components/errors/console-error.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,20 @@ const consoleTypeSym = Symbol.for('next.console.error.type')

// Represent non Error shape unhandled promise rejections or console.error errors.
// Those errors will be captured and displayed in Error Overlay.
type UnhandledError = Error & {
[digestSym]: 'NEXT_UNHANDLED_ERROR'
export type ConsoleError = Error & {
[digestSym]: 'NEXT_CONSOLE_ERROR'
[consoleTypeSym]: 'string' | 'error'
environmentName: string
}

export function createUnhandledError(
export function createConsoleError(
message: string | Error,
environmentName?: string | null
): UnhandledError {
): ConsoleError {
const error = (
typeof message === 'string' ? new Error(message) : message
) as UnhandledError
error[digestSym] = 'NEXT_UNHANDLED_ERROR'
) as ConsoleError
error[digestSym] = 'NEXT_CONSOLE_ERROR'
error[consoleTypeSym] = typeof message === 'string' ? 'string' : 'error'

if (environmentName && !error.environmentName) {
Expand All @@ -27,12 +27,10 @@ export function createUnhandledError(
return error
}

export const isUnhandledConsoleOrRejection = (
error: any
): error is UnhandledError => {
return error && error[digestSym] === 'NEXT_UNHANDLED_ERROR'
export const isConsoleError = (error: any): error is ConsoleError => {
return error && error[digestSym] === 'NEXT_CONSOLE_ERROR'
}

export const getUnhandledErrorType = (error: UnhandledError) => {
export const getConsoleErrorType = (error: ConsoleError) => {
return error[consoleTypeSym]
}
49 changes: 35 additions & 14 deletions packages/next/src/client/components/errors/use-error-handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { isNextRouterError } from '../is-next-router-error'
import { storeHydrationErrorStateFromConsoleArgs } from './hydration-error-info'
import { formatConsoleArgs, parseConsoleArgs } from '../../lib/console'
import isError from '../../../lib/is-error'
import { createUnhandledError } from './console-error'
import { createConsoleError } from './console-error'
import { enqueueConsecutiveDedupedError } from './enqueue-client-error'
import { getReactStitchedError } from '../errors/stitched-error'

Expand All @@ -18,21 +18,19 @@ const errorHandlers: Array<ErrorHandler> = []
const rejectionQueue: Array<Error> = []
const rejectionHandlers: Array<ErrorHandler> = []

export function handleClientError(
export function handleConsoleError(
originError: unknown,
consoleErrorArgs: any[],
capturedFromConsole: boolean = false
consoleErrorArgs: any[]
) {
let error: Error
if (!originError || !isError(originError)) {
// If it's not an error, format the args into an error
const formattedErrorMessage = formatConsoleArgs(consoleErrorArgs)
const { environmentName } = parseConsoleArgs(consoleErrorArgs)
error = createUnhandledError(formattedErrorMessage, environmentName)
const { environmentName } = parseConsoleArgs(consoleErrorArgs)
if (isError(originError)) {
error = createConsoleError(originError, environmentName)
} else {
error = capturedFromConsole
? createUnhandledError(originError)
: originError
error = createConsoleError(
formatConsoleArgs(consoleErrorArgs),
environmentName
)
}
error = getReactStitchedError(error)

Expand All @@ -49,6 +47,29 @@ export function handleClientError(
}
}

export function handleClientError(originError: unknown) {
let error: Error
if (isError(originError)) {
error = originError
} else {
// If it's not an error, format the args into an error
const formattedErrorMessage = originError + ''
error = new Error(formattedErrorMessage)
}
error = getReactStitchedError(error)

attachHydrationErrorState(error)

enqueueConsecutiveDedupedError(errorQueue, error)
for (const handler of errorHandlers) {
// Delayed the error being passed to React Dev Overlay,
// avoid the state being synchronously updated in the component.
queueMicroTask(() => {
handler(error)
})
}
}

export function useErrorHandler(
handleOnUnhandledError: ErrorHandler,
handleOnUnhandledRejection: ErrorHandler
Expand Down Expand Up @@ -85,7 +106,7 @@ function onUnhandledError(event: WindowEventMap['error']): void | boolean {
// When there's an error property present, we log the error to error overlay.
// Otherwise we don't do anything as it's not logging in the console either.
if (event.error) {
handleClientError(event.error, [])
handleClientError(event.error)
}
}

Expand All @@ -98,7 +119,7 @@ function onUnhandledRejection(ev: WindowEventMap['unhandledrejection']): void {

let error = reason
if (error && !isError(error)) {
error = createUnhandledError(error + '')
error = new Error(error + '')
}

rejectionQueue.push(error)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import isError from '../../../lib/is-error'
import { isNextRouterError } from '../is-next-router-error'
import { handleClientError } from '../errors/use-error-handler'
import { handleConsoleError } from '../errors/use-error-handler'
import { parseConsoleArgs } from '../../lib/console'

export const originConsoleError = globalThis.console.error
Expand Down Expand Up @@ -29,12 +29,11 @@ export function patchConsoleError() {

if (!isNextRouterError(maybeError)) {
if (process.env.NODE_ENV !== 'production') {
handleClientError(
handleConsoleError(
// replayed errors have their own complex format string that should be used,
// but if we pass the error directly, `handleClientError` will ignore it
maybeError,
args,
true
args
)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ function ReplaySsrOnlyErrors({
// TODO(veil): Produces wrong Owner Stack
// TODO(veil): Mark as recoverable error
// TODO(veil): console.error
handleClientError(ssrError, [])
handleClientError(ssrError)

// If it's missing root tags, we can't recover, make it blocking.
if (ssrError.digest === MISSING_ROOT_TAGS_ERROR) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ import { EnvironmentNameLabel } from '../environment-name-label/environment-name
import { useFocusTrap } from '../dev-tools-indicator/utils'
import { Fader } from '../../fader'

interface ErrorOverlayLayoutProps extends ErrorBaseProps {
export interface ErrorOverlayLayoutProps extends ErrorBaseProps {
errorMessage: ErrorMessageType
errorType: ErrorType
children?: React.ReactNode
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,12 +31,6 @@ export const ConsoleError: Story = {
},
}

export const UnhandledRuntimeError: Story = {
args: {
errorType: 'Unhandled Runtime Error',
},
}

export const MissingRequiredHTMLTag: Story = {
args: {
errorType: 'Missing Required HTML Tag',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ export type ErrorType =
| 'Build Error'
| 'Runtime Error'
| 'Console Error'
| 'Unhandled Runtime Error'
| 'Missing Required HTML Tag'

type ErrorTypeLabelProps = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,14 @@ import {
getHydrationWarningType,
} from '../../../errors/hydration-error-info'
import {
getUnhandledErrorType,
isUnhandledConsoleOrRejection,
isConsoleError,
getConsoleErrorType,
} from '../../../errors/console-error'
import { extractNextErrorCode } from '../../../../../lib/error-telemetry-utils'
import { ErrorOverlayLayout } from '../components/errors/error-overlay-layout/error-overlay-layout'
import {
ErrorOverlayLayout,
type ErrorOverlayLayoutProps,
} from '../components/errors/error-overlay-layout/error-overlay-layout'
import { NEXTJS_HYDRATION_ERROR_LINK } from '../../../is-hydration-error'
import type { ReadyRuntimeError } from '../../utils/get-error-by-type'
import type { ErrorBaseProps } from '../components/errors/error-overlay/error-overlay'
Expand All @@ -38,20 +41,16 @@ function ErrorDescription({
error: Error
hydrationWarning: string | null
}) {
const isUnhandledOrReplayError = isUnhandledConsoleOrRejection(error)
const unhandledErrorType = isUnhandledOrReplayError
? getUnhandledErrorType(error)
const unhandledErrorType = isConsoleError(error)
? getConsoleErrorType(error)
: null
const isConsoleErrorStringMessage = unhandledErrorType === 'string'
// If the error is:
// - hydration warning
// - captured console error or unhandled rejection
// skip displaying the error name
const title =
(isUnhandledOrReplayError && isConsoleErrorStringMessage) ||
hydrationWarning
? ''
: error.name + ': '
isConsoleErrorStringMessage || hydrationWarning ? '' : error.name + ': '

const environmentName =
'environmentName' in error ? error.environmentName : ''
Expand All @@ -75,6 +74,13 @@ function ErrorDescription({
)
}

function getErrorType(error: Error): ErrorOverlayLayoutProps['errorType'] {
if (isConsoleError(error)) {
return 'Console Error'
}
return 'Runtime Error'
}

export function Errors({
runtimeErrors,
debugInfo,
Expand Down Expand Up @@ -119,7 +125,7 @@ export function Errors({
const isServerError = ['server', 'edge-server'].includes(
getErrorSource(error) || ''
)
const isUnhandledError = isUnhandledConsoleOrRejection(error)
const errorType = getErrorType(error)
const errorDetails: HydrationErrorState = (error as any).details || {}
const notes = errorDetails.notes || ''
const [warningTemplate, serverContent, clientContent] =
Expand All @@ -145,13 +151,7 @@ export function Errors({
return (
<ErrorOverlayLayout
errorCode={errorCode}
errorType={
isServerError
? 'Runtime Error'
: isUnhandledError
? 'Console Error'
: 'Unhandled Runtime Error'
}
errorType={errorType}
errorMessage={
<ErrorDescription error={error} hydrationWarning={hydrationWarning} />
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ export function onCaughtError(
// Log and report the error with location but without modifying the error stack
originConsoleError('%o\n\n%s', err, errorLocation)

handleClientError(stitchedError, [])
handleClientError(stitchedError)
} else {
originConsoleError(err)
}
Expand Down
Loading
Loading