From 1470e2986e18581a0867d5147761e772691dab65 Mon Sep 17 00:00:00 2001 From: "Anders D. Johnson" Date: Wed, 7 Jun 2023 20:44:15 -0500 Subject: [PATCH 1/4] feat: add component stack to recoverable error Attaches any component stack from error info to the error object on a recoverable error. This will help understanding hydration mismatch errors in production logs. --- .../next/src/client/on-recoverable-error.ts | 25 ++++++++++++++++--- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/packages/next/src/client/on-recoverable-error.ts b/packages/next/src/client/on-recoverable-error.ts index a58f52ce29dfb..e6ddda3cf8822 100644 --- a/packages/next/src/client/on-recoverable-error.ts +++ b/packages/next/src/client/on-recoverable-error.ts @@ -1,6 +1,22 @@ import { NEXT_DYNAMIC_NO_SSR_CODE } from '../shared/lib/lazy-dynamic/no-ssr-error' -export default function onRecoverableError(err: any) { +/** + * @see [ReactInternalTypes]{@link https://github.com/facebook/react/blob/910045696bb5f693acb77890e6750c5e4659b420/packages/react-reconciler/src/ReactInternalTypes.js#L278-L281} + */ +interface ErrorInfo { + componentStack?: string +} + +export default function onRecoverableError(err: any, errorInfo: ErrorInfo) { + // Attach any component stack to the error, so it's accessible when passed thru `reportError` below, + // such as from custom `window.onerror` or `window` `error` event listeners in an app. + // Similar is done in `@next/react-dev-overlay`, but this supports production use cases as well. + // This is technically a mutation of an argument, which is generally inadvisable, but probably not problematic in this case. + // The alternative of constucting a new error could be more complicated. + if (errorInfo.componentStack) { + err._componentStack = errorInfo.componentStack + } + // Using default react onRecoverableError // x-ref: https://github.com/facebook/react/blob/d4bc16a7d69eb2ea38a88c8ac0b461d5f72cdcab/packages/react-dom/src/client/ReactDOMRoot.js#L83 const defaultOnRecoverableError = @@ -8,11 +24,12 @@ export default function onRecoverableError(err: any) { ? // In modern browsers, reportError will dispatch an error event, // emulating an uncaught JavaScript error. reportError - : (error: any) => { - window.console.error(error) + : (error: any, errorInfo: ErrorInfo) => { + window.console.error(error, errorInfo) } // Skip certain custom errors which are not expected to be reported on client if (err.digest === NEXT_DYNAMIC_NO_SSR_CODE) return - defaultOnRecoverableError(err) + + defaultOnRecoverableError(err, errorInfo) } From 5799cfbbd7036d1b8051f2d7be64253b69f01ec4 Mon Sep 17 00:00:00 2001 From: "Anders D. Johnson" Date: Sun, 18 Aug 2024 14:49:41 -0500 Subject: [PATCH 2/4] fix a typo in a comment --- packages/next/src/client/on-recoverable-error.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/next/src/client/on-recoverable-error.ts b/packages/next/src/client/on-recoverable-error.ts index e6ddda3cf8822..b4f90574248d3 100644 --- a/packages/next/src/client/on-recoverable-error.ts +++ b/packages/next/src/client/on-recoverable-error.ts @@ -12,7 +12,7 @@ export default function onRecoverableError(err: any, errorInfo: ErrorInfo) { // such as from custom `window.onerror` or `window` `error` event listeners in an app. // Similar is done in `@next/react-dev-overlay`, but this supports production use cases as well. // This is technically a mutation of an argument, which is generally inadvisable, but probably not problematic in this case. - // The alternative of constucting a new error could be more complicated. + // The alternative of constructing a new error could be more complicated. if (errorInfo.componentStack) { err._componentStack = errorInfo.componentStack } From 65752ca4244848b73e516ceaf25f30d18c23714e Mon Sep 17 00:00:00 2001 From: "Anders D. Johnson" Date: Sun, 18 Aug 2024 14:51:34 -0500 Subject: [PATCH 3/4] Adding optional chaining to safely handle nullish values, just in case. --- packages/next/src/client/on-recoverable-error.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/next/src/client/on-recoverable-error.ts b/packages/next/src/client/on-recoverable-error.ts index b4f90574248d3..780bf5e60526f 100644 --- a/packages/next/src/client/on-recoverable-error.ts +++ b/packages/next/src/client/on-recoverable-error.ts @@ -7,13 +7,13 @@ interface ErrorInfo { componentStack?: string } -export default function onRecoverableError(err: any, errorInfo: ErrorInfo) { +export default function onRecoverableError(err: any, errorInfo?: ErrorInfo) { // Attach any component stack to the error, so it's accessible when passed thru `reportError` below, // such as from custom `window.onerror` or `window` `error` event listeners in an app. // Similar is done in `@next/react-dev-overlay`, but this supports production use cases as well. // This is technically a mutation of an argument, which is generally inadvisable, but probably not problematic in this case. // The alternative of constructing a new error could be more complicated. - if (errorInfo.componentStack) { + if (errorInfo?.componentStack) { err._componentStack = errorInfo.componentStack } From d66638a4cbc95d5d06bee685c59552bbcc6accf7 Mon Sep 17 00:00:00 2001 From: "Anders D. Johnson" Date: Sun, 18 Aug 2024 14:54:04 -0500 Subject: [PATCH 4/4] Adding optional chaining to safely handle nullish values, just in case. --- packages/next/src/client/on-recoverable-error.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/next/src/client/on-recoverable-error.ts b/packages/next/src/client/on-recoverable-error.ts index 780bf5e60526f..a6c63fe93855c 100644 --- a/packages/next/src/client/on-recoverable-error.ts +++ b/packages/next/src/client/on-recoverable-error.ts @@ -24,7 +24,7 @@ export default function onRecoverableError(err: any, errorInfo?: ErrorInfo) { ? // In modern browsers, reportError will dispatch an error event, // emulating an uncaught JavaScript error. reportError - : (error: any, errorInfo: ErrorInfo) => { + : (error: any, errorInfo?: ErrorInfo) => { window.console.error(error, errorInfo) }