UNPKG

next

Version:

The React Framework

131 lines (130 loc) • 5.44 kB
import { useEffect } from 'react'; import { isNextRouterError } from '../../../../client/components/is-next-router-error'; import { formatConsoleArgs, parseConsoleArgs } from '../../../../client/lib/console'; import isError from '../../../../lib/is-error'; import { createConsoleError } from '../../../shared/console-error'; import { coerceError, setOwnerStackIfAvailable } from './stitched-error'; import { forwardUnhandledError, logUnhandledRejection } from '../forward-logs'; const queueMicroTask = globalThis.queueMicrotask || ((cb)=>Promise.resolve().then(cb)); const errorQueue = []; const errorHandlers = []; const rejectionQueue = []; const rejectionHandlers = []; export function handleConsoleError(originError, consoleErrorArgs) { let error; const { environmentName } = parseConsoleArgs(consoleErrorArgs); if (isError(originError)) { error = createConsoleError(originError, environmentName); } else { error = createConsoleError(formatConsoleArgs(consoleErrorArgs), environmentName); } setOwnerStackIfAvailable(error); if (process.env.__NEXT_EXPOSE_RUNTIME_ERRORS_TO_HMR) { const { dispatcher } = require('next/dist/compiled/next-devtools'); queueMicroTask(()=>dispatcher.onUnhandledError(error)); } else { errorQueue.push(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 handleClientError(error, metadata = undefined) { if (process.env.__NEXT_EXPOSE_RUNTIME_ERRORS_TO_HMR) { const { dispatcher } = require('next/dist/compiled/next-devtools'); const { takeRuntimeErrorMetadata } = require('./runtime-error-metadata'); const occurrence = metadata ?? takeRuntimeErrorMetadata(error); // The overlay queues events until its own root mounts. Do not depend on // HotReload committing: an initial application failure can prevent that. queueMicroTask(()=>dispatcher.onUnhandledError(error, occurrence)); } else { errorQueue.push(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, handleOnUnhandledRejection) { useEffect(()=>{ // Handle queued errors. errorQueue.forEach(handleOnUnhandledError); rejectionQueue.forEach(handleOnUnhandledRejection); // Listen to new errors. errorHandlers.push(handleOnUnhandledError); rejectionHandlers.push(handleOnUnhandledRejection); return ()=>{ // Remove listeners. errorHandlers.splice(errorHandlers.indexOf(handleOnUnhandledError), 1); rejectionHandlers.splice(rejectionHandlers.indexOf(handleOnUnhandledRejection), 1); // Reset error queues. errorQueue.splice(0, errorQueue.length); rejectionQueue.splice(0, rejectionQueue.length); }; }, [ handleOnUnhandledError, handleOnUnhandledRejection ]); } function onUnhandledError(event) { const thrownValue = event.error; if (isNextRouterError(thrownValue)) { event.preventDefault(); return false; } // 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 (thrownValue) { const error = coerceError(thrownValue); setOwnerStackIfAvailable(error); if (process.env.__NEXT_EXPOSE_RUNTIME_ERRORS_TO_HMR) { const { takeRuntimeErrorMetadata } = require('./runtime-error-metadata'); const { isRecoverableError } = require('../../../../client/react-client-callbacks/on-recoverable-error'); handleClientError(error, takeRuntimeErrorMetadata(error) ?? (isRecoverableError(error) ? undefined : { fatal: false })); } else { handleClientError(error); } forwardUnhandledError(error); } } function onUnhandledRejection(ev) { const reason = ev?.reason; if (isNextRouterError(reason)) { ev.preventDefault(); return; } const error = coerceError(reason); setOwnerStackIfAvailable(error); if (process.env.__NEXT_EXPOSE_RUNTIME_ERRORS_TO_HMR) { const { dispatcher } = require('next/dist/compiled/next-devtools'); dispatcher.onUnhandledRejection(error, { fatal: false }); } else { rejectionQueue.push(error); for (const handler of rejectionHandlers){ handler(error); } } logUnhandledRejection(reason); } export function handleGlobalErrors() { if (typeof window !== 'undefined') { try { // Increase the number of stack frames on the client Error.stackTraceLimit = 50; } catch {} window.addEventListener('error', onUnhandledError); window.addEventListener('unhandledrejection', onUnhandledRejection); } } //# sourceMappingURL=use-error-handler.js.map