|
1 | 1 | // This file is only used in app router due to the specific error state handling. |
2 | 2 |
|
3 | | -import type { HydrationOptions } from 'react-dom/client' |
| 3 | +import type { ErrorInfo } from 'react' |
4 | 4 | import { getReactStitchedError } from '../components/errors/stitched-error' |
5 | 5 | import { handleClientError } from '../components/errors/use-error-handler' |
6 | 6 | import { isNextRouterError } from '../components/is-next-router-error' |
7 | 7 | import { isBailoutToCSRError } from '../../shared/lib/lazy-dynamic/bailout-to-csr' |
8 | 8 | import { reportGlobalError } from './report-global-error' |
9 | 9 | import { originConsoleError } from '../components/globals/intercept-console-error' |
| 10 | +import { AppDevOverlayErrorBoundary } from '../components/react-dev-overlay/app/app-dev-overlay-error-boundary' |
| 11 | +import { |
| 12 | + ErrorBoundaryHandler, |
| 13 | + GlobalError as DefaultErrorBoundary, |
| 14 | +} from '../components/error-boundary' |
| 15 | + |
| 16 | +export function onCaughtError( |
| 17 | + err: unknown, |
| 18 | + errorInfo: ErrorInfo & { errorBoundary?: React.Component } |
| 19 | +) { |
| 20 | + const errorBoundaryComponent = errorInfo.errorBoundary?.constructor |
| 21 | + |
| 22 | + const isImplicitErrorBoundary = |
| 23 | + (process.env.NODE_ENV !== 'production' && |
| 24 | + errorBoundaryComponent === AppDevOverlayErrorBoundary) || |
| 25 | + (errorBoundaryComponent === ErrorBoundaryHandler && |
| 26 | + (errorInfo.errorBoundary! as InstanceType<typeof ErrorBoundaryHandler>) |
| 27 | + .props.errorComponent === DefaultErrorBoundary) |
| 28 | + if (isImplicitErrorBoundary) { |
| 29 | + // We don't consider errors caught unless they're caught by an explicit error |
| 30 | + // boundary. The built-in ones are considered implicit. |
| 31 | + // This mimics how the same app would behave without Next.js. |
| 32 | + return onUncaughtError(err, errorInfo) |
| 33 | + } |
10 | 34 |
|
11 | | -export const onCaughtError: HydrationOptions['onCaughtError'] = ( |
12 | | - err, |
13 | | - errorInfo |
14 | | -) => { |
15 | 35 | // Skip certain custom errors which are not expected to be reported on client |
16 | 36 | if (isBailoutToCSRError(err) || isNextRouterError(err)) return |
17 | 37 |
|
18 | 38 | if (process.env.NODE_ENV !== 'production') { |
19 | | - const errorBoundaryComponent = errorInfo?.errorBoundary?.constructor |
20 | 39 | const errorBoundaryName = |
21 | 40 | // read react component displayName |
22 | 41 | (errorBoundaryComponent as any)?.displayName || |
@@ -57,35 +76,19 @@ export const onCaughtError: HydrationOptions['onCaughtError'] = ( |
57 | 76 | } |
58 | 77 | } |
59 | 78 |
|
60 | | -export const onUncaughtError: HydrationOptions['onUncaughtError'] = ( |
61 | | - err, |
62 | | - errorInfo |
63 | | -) => { |
| 79 | +export function onUncaughtError(err: unknown, errorInfo: React.ErrorInfo) { |
64 | 80 | // Skip certain custom errors which are not expected to be reported on client |
65 | 81 | if (isBailoutToCSRError(err) || isNextRouterError(err)) return |
66 | 82 |
|
67 | 83 | if (process.env.NODE_ENV !== 'production') { |
68 | | - const componentThatErroredFrame = errorInfo?.componentStack?.split('\n')[1] |
69 | | - |
70 | | - // Match chrome or safari stack trace |
71 | | - const matches = |
72 | | - componentThatErroredFrame?.match(/\s+at (\w+)\s+|(\w+)@/) ?? [] |
73 | | - const componentThatErroredName = matches[1] || matches[2] || 'Unknown' |
74 | | - |
75 | | - // Create error location with errored component and error boundary, to match the behavior of default React onCaughtError handler. |
76 | | - const errorLocation = componentThatErroredName |
77 | | - ? `The above error occurred in the <${componentThatErroredName}> component.` |
78 | | - : `The above error occurred in one of your components.` |
79 | | - |
80 | 84 | const stitchedError = getReactStitchedError(err) |
81 | 85 | // TODO: change to passing down errorInfo later |
82 | 86 | // In development mode, pass along the component stack to the error |
83 | 87 | if (errorInfo.componentStack) { |
84 | 88 | ;(stitchedError as any)._componentStack = errorInfo.componentStack |
85 | 89 | } |
86 | 90 |
|
87 | | - // Log and report the error with location but without modifying the error stack |
88 | | - originConsoleError('%o\n\n%s', err, errorLocation) |
| 91 | + // TODO: Add an adendum to the overlay telling people about custom error boundaries. |
89 | 92 | reportGlobalError(stitchedError) |
90 | 93 | } else { |
91 | 94 | reportGlobalError(err) |
|
0 commit comments