| | |
| |
|
| | import type { ErrorInfo } from 'react' |
| | import { isNextRouterError } from '../components/is-next-router-error' |
| | import { isBailoutToCSRError } from '../../shared/lib/lazy-dynamic/bailout-to-csr' |
| | import { reportGlobalError } from './report-global-error' |
| | import { ErrorBoundaryHandler } from '../components/error-boundary' |
| | import DefaultErrorBoundary from '../components/builtin/global-error' |
| |
|
| | const devToolErrorMod: typeof import('../../next-devtools/userspace/app/errors') = |
| | process.env.NODE_ENV !== 'production' |
| | ? (require('../../next-devtools/userspace/app/errors') as typeof import('../../next-devtools/userspace/app/errors')) |
| | : { |
| | decorateDevError: (error: unknown) => error as Error, |
| | handleClientError: () => {}, |
| | originConsoleError: console.error.bind(console), |
| | } |
| |
|
| | export function onCaughtError( |
| | thrownValue: unknown, |
| | errorInfo: ErrorInfo & { errorBoundary?: React.Component } |
| | ) { |
| | const errorBoundaryComponent = errorInfo.errorBoundary?.constructor |
| |
|
| | let isImplicitErrorBoundary |
| |
|
| | if (process.env.NODE_ENV !== 'production') { |
| | const { AppDevOverlayErrorBoundary } = |
| | require('../../next-devtools/userspace/app/app-dev-overlay-error-boundary') as typeof import('../../next-devtools/userspace/app/app-dev-overlay-error-boundary') |
| |
|
| | isImplicitErrorBoundary = |
| | errorBoundaryComponent === AppDevOverlayErrorBoundary |
| | } |
| |
|
| | isImplicitErrorBoundary = |
| | isImplicitErrorBoundary || |
| | (errorBoundaryComponent === ErrorBoundaryHandler && |
| | (errorInfo.errorBoundary! as InstanceType<typeof ErrorBoundaryHandler>) |
| | .props.errorComponent === DefaultErrorBoundary) |
| |
|
| | |
| | if (process.env.NODE_ENV !== 'production') { |
| | const { SEGMENT_EXPLORER_SIMULATED_ERROR_MESSAGE } = |
| | require('../../next-devtools/userspace/app/segment-explorer-node') as typeof import('../../next-devtools/userspace/app/segment-explorer-node') |
| | if ( |
| | thrownValue instanceof Error && |
| | thrownValue.message === SEGMENT_EXPLORER_SIMULATED_ERROR_MESSAGE |
| | ) { |
| | return |
| | } |
| | } |
| |
|
| | if (isImplicitErrorBoundary) { |
| | |
| | |
| | |
| | return onUncaughtError(thrownValue) |
| | } |
| |
|
| | |
| | if (isBailoutToCSRError(thrownValue) || isNextRouterError(thrownValue)) return |
| |
|
| | if (process.env.NODE_ENV !== 'production') { |
| | const errorBoundaryName = |
| | |
| | (errorBoundaryComponent as any)?.displayName || |
| | errorBoundaryComponent?.name || |
| | 'Unknown' |
| |
|
| | const componentThatErroredFrame = errorInfo?.componentStack?.split('\n')[1] |
| |
|
| | |
| | const matches = |
| | |
| | |
| | |
| | componentThatErroredFrame?.match(/\s+at (\w+)\s+|(\w+)@/) ?? [] |
| | const componentThatErroredName = matches[1] || matches[2] || 'Unknown' |
| |
|
| | |
| | const errorBoundaryMessage = `It was handled by the <${errorBoundaryName}> error boundary.` |
| | const componentErrorMessage = componentThatErroredName |
| | ? `The above error occurred in the <${componentThatErroredName}> component.` |
| | : `The above error occurred in one of your components.` |
| |
|
| | const errorLocation = `${componentErrorMessage} ${errorBoundaryMessage}` |
| | const error = devToolErrorMod.decorateDevError(thrownValue) |
| |
|
| | |
| | devToolErrorMod.originConsoleError('%o\n\n%s', thrownValue, errorLocation) |
| |
|
| | devToolErrorMod.handleClientError(error) |
| | } else { |
| | devToolErrorMod.originConsoleError(thrownValue) |
| | } |
| | } |
| |
|
| | export function onUncaughtError(thrownValue: unknown) { |
| | |
| | if (isBailoutToCSRError(thrownValue) || isNextRouterError(thrownValue)) return |
| |
|
| | if (process.env.NODE_ENV !== 'production') { |
| | const error = devToolErrorMod.decorateDevError(thrownValue) |
| |
|
| | |
| | reportGlobalError(error) |
| | } else { |
| | reportGlobalError(thrownValue) |
| | } |
| | } |
| |
|