UNPKG

next

Version:

The React Framework

170 lines (169 loc) 7.52 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); 0 && (module.exports = { installProcessErrorHandlers: null, isUnhandledRejectionListenerRegistered: null, registerUnhandledRejectionListener: null }); function _export(target, all) { for(var name in all)Object.defineProperty(target, name, { enumerable: true, get: all[name] }); } _export(exports, { installProcessErrorHandlers: function() { return installProcessErrorHandlers; }, isUnhandledRejectionListenerRegistered: function() { return isUnhandledRejectionListenerRegistered; }, registerUnhandledRejectionListener: function() { return registerUnhandledRejectionListener; } }); const _ispostpone = require("../lib/router-utils/is-postpone"); const _log = /*#__PURE__*/ _interop_require_wildcard(require("../../build/output/log")); function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function(nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); } function _interop_require_wildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = { __proto__: null }; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for(var key in obj){ if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; } let _global = globalThis; // The listener function is shared via globalThis so that multiple copies of // this module (e.g. in the pre-compiled server bundle and in a route module // bundle) still register and detect a single listener instance. const UNHANDLED_REJECTION_LISTENER_KEY = Symbol.for('next.unhandledRejectionListener'); function unhandledRejectionListener(reason) { if ((0, _ispostpone.isPostpone)(reason)) { // React postpones that are unhandled might end up logged here but they're // not really errors. They're just part of rendering. return; } // Immediately log the error. // TODO: Ideally, if we knew that this error was triggered by application // code, we would suppress it entirely without logging. We can't reliably // detect all of these, but when cacheComponents is enabled, we could suppress // at least some of them by waiting to log the error until after all in- // progress renders have completed. Then, only log errors for which there // was not a corresponding "rejectionHandled" event. _log.error('unhandledRejection:', reason); } function isUnhandledRejectionListenerRegistered() { const listener = _global[UNHANDLED_REJECTION_LISTENER_KEY]; return listener !== undefined && process.listeners('unhandledRejection').includes(listener); } function registerUnhandledRejectionListener() { if (isUnhandledRejectionListenerRegistered()) { return; } const listener = _global[UNHANDLED_REJECTION_LISTENER_KEY] ??= unhandledRejectionListener; process.on('unhandledRejection', listener); } function installProcessErrorHandlers(shouldRemoveUncaughtErrorAndRejectionListeners) { if (!_global.nextInitializedProcessErrorHandlers) { _global.nextInitializedProcessErrorHandlers = true; // The conventional wisdom of Node.js and other runtimes is to treat // unhandled errors as fatal and exit the process. // // But Next.js is not a generic JS runtime — it's a specialized runtime for // React Server Components. // // Many unhandled rejections are due to the late-awaiting pattern for // prefetching data. In Next.js it's OK to call an async function without // immediately awaiting it, to start the request as soon as possible // without blocking unncessarily on the result. These can end up // triggering an "unhandledRejection" if it later turns out that the // data is not needed to render the page. Example: // // const promise = fetchData() // const shouldShow = await checkCondition() // if (shouldShow) { // return <Component promise={promise} /> // } // // In this example, `fetchData` is called immediately to start the request // as soon as possible, but if `shouldShow` is false, then it will be // discarded without unwrapping its result. If it errors, it will trigger // an "unhandledRejection" event. // // Ideally, we would suppress these rejections completely without warning, // because we don't consider them real errors. (TODO: Currently we do warn.) // // But regardless of whether we do or don't warn, we definitely shouldn't // crash the entire process. // // Even a "legit" unhandled error unrelated to prefetching shouldn't // prevent the rest of the page from rendering. // // So, we're going to intentionally override the default error handling // behavior of the outer JS runtime to be more forgiving // Remove any existing "unhandledRejection" and "uncaughtException" handlers. // This is gated behind an experimental flag until we've considered the impact // in various deployment environments. It's possible this may always need to // be configurable. if (shouldRemoveUncaughtErrorAndRejectionListeners) { process.removeAllListeners('uncaughtException'); process.removeAllListeners('unhandledRejection'); } process.on('rejectionHandled', ()=>{ // TODO: See note in the unhandledRejection listener above. In the // future, we may use the "rejectionHandled" event to de-queue an error // from being logged. }); // Unhandled exceptions are errors triggered by non-async functions, so this // is unrelated to the late-awaiting pattern. However, for similar reasons, // we still shouldn't crash the process. Just log it. process.on('uncaughtException', (reason)=>{ if ((0, _ispostpone.isPostpone)(reason)) { return; } console.error(reason); }); } // Register the listener unconditionally, and not only during the guarded // initialization above: a previous registration may have been undone by the // `removeAllListeners` call of a later `installProcessErrorHandlers` call // (or by external code), and registering is a no-op if the listener is // still attached. registerUnhandledRejectionListener(); } //# sourceMappingURL=process-error-handlers.js.map