Error handling bugfix: pass #1
This commit is contained in:
+13
-1
@@ -79,11 +79,17 @@ export default async function genWebHTML({ component: Main, pageProps, bundledMa
|
||||
console.info = () => { };
|
||||
console.debug = () => { };
|
||||
let htmlBody;
|
||||
let render_error;
|
||||
let has_render_error = false;
|
||||
try {
|
||||
const stream = await renderToReadableStream(final_component, {
|
||||
onError(error) {
|
||||
if (error.message.includes('unique "key" prop'))
|
||||
if (error?.message?.includes('unique "key" prop'))
|
||||
return;
|
||||
if (!has_render_error) {
|
||||
has_render_error = true;
|
||||
render_error = error;
|
||||
}
|
||||
originalConsole.error(error);
|
||||
},
|
||||
});
|
||||
@@ -92,6 +98,12 @@ export default async function genWebHTML({ component: Main, pageProps, bundledMa
|
||||
finally {
|
||||
Object.assign(console, originalConsole);
|
||||
}
|
||||
// A component crash during SSR must surface as a proper error (handled
|
||||
// upstream as a 500 page). React only reports these to `onError`; without
|
||||
// rethrowing, the user gets a 200 with broken/blank markup.
|
||||
if (has_render_error) {
|
||||
throw render_error;
|
||||
}
|
||||
html += htmlBody;
|
||||
return html;
|
||||
}
|
||||
|
||||
@@ -4,6 +4,12 @@ type Params = {
|
||||
file_path?: string;
|
||||
debug?: boolean;
|
||||
retry?: boolean;
|
||||
/**
|
||||
* Skip the dev auto-rebuild/retry recovery path. Used by internal callers
|
||||
* (e.g. HMR post-build) where triggering yet another fullRebuild on top
|
||||
* of the rebuild that's already in progress would cascade.
|
||||
*/
|
||||
no_retry?: boolean;
|
||||
return_server_res_only?: boolean;
|
||||
skip_server_res?: boolean;
|
||||
is_hydration?: boolean;
|
||||
|
||||
+21
-8
@@ -10,6 +10,7 @@ import isDevelopment from "../../../utils/is-development";
|
||||
import { existsSync } from "fs";
|
||||
import grabDirNames from "../../../utils/grab-dir-names";
|
||||
import grabRelativePath from "../../../utils/grab-relative-path";
|
||||
import { logBuildErrorsOnce } from "../../../utils/build-error-log";
|
||||
const { BUNX_BUNDLER_ERROR_EXIT_FILE, ROOT_DIR } = grabDirNames();
|
||||
class NotFoundError extends Error {
|
||||
status = 404;
|
||||
@@ -19,7 +20,7 @@ class NotFoundError extends Error {
|
||||
}
|
||||
}
|
||||
export default async function grabPageComponent(params) {
|
||||
const { req, file_path: passed_file_path, debug, return_server_res_only, skip_server_res, is_hydration, } = params;
|
||||
const { req, file_path: passed_file_path, debug, return_server_res_only, skip_server_res, is_hydration, no_retry, } = params;
|
||||
const url = req?.url ? new URL(req.url) : undefined;
|
||||
const router = global.BUNEXT_ROUTER;
|
||||
const is_dev = isDevelopment();
|
||||
@@ -59,14 +60,20 @@ export default async function grabPageComponent(params) {
|
||||
}
|
||||
let retries = 0;
|
||||
const MAX_RETRIES = 2;
|
||||
while (retries < MAX_RETRIES) {
|
||||
// Only retry transient failures: if the last rebuild failed
|
||||
// (BUNEXT_BUILD_FAILED), the file is broken and more rebuilds
|
||||
// will just repeat the same error.
|
||||
while (retries < MAX_RETRIES &&
|
||||
!global.BUNEXT_BUILD_FAILED) {
|
||||
await fullRebuild({
|
||||
msg: `Retrying Bundle map for file \`${relative_file_path}\``,
|
||||
});
|
||||
await Bun.sleep(1000);
|
||||
bundledMap = global.BUNEXT_BUNDLER_CTX_MAP[relative_file_path];
|
||||
bundledMap =
|
||||
global.BUNEXT_BUNDLER_CTX_MAP[relative_file_path];
|
||||
if (bundledMap?.path)
|
||||
break;
|
||||
retries++;
|
||||
}
|
||||
if (!bundledMap?.path) {
|
||||
const errMsg = `No Bundled File Path for this request path!`;
|
||||
@@ -116,8 +123,11 @@ export default async function grabPageComponent(params) {
|
||||
const is404 = error instanceof NotFoundError ||
|
||||
error?.name === "NotFoundError" ||
|
||||
error?.status === 404;
|
||||
if (!params.retry && is_dev) {
|
||||
while (global.BUNEXT_REBUILD_RETRIES < 2) {
|
||||
if (!params.retry && !params.no_retry && is_dev) {
|
||||
// Skip the retry cascade when the last rebuild failed: the error
|
||||
// is persistent and more rebuilds would repeat it endlessly.
|
||||
while (!global.BUNEXT_BUILD_FAILED &&
|
||||
global.BUNEXT_REBUILD_RETRIES < 2) {
|
||||
global.BUNEXT_REBUILD_RETRIES =
|
||||
global.BUNEXT_REBUILD_RETRIES + 1;
|
||||
await fullRebuild();
|
||||
@@ -139,9 +149,12 @@ export default async function grabPageComponent(params) {
|
||||
global.BUNEXT_IS_404_PAGE = true;
|
||||
}
|
||||
else {
|
||||
log.error(`Error Grabbing Page Component: ${error.message}`);
|
||||
log.error(`Page: ${passed_file_path || url?.pathname}`);
|
||||
if (is_dev) {
|
||||
logBuildErrorsOnce([
|
||||
{
|
||||
text: `Error Grabbing Page Component: ${error.message}; Page: ${passed_file_path || url?.pathname}`,
|
||||
},
|
||||
], (line) => log.error(line));
|
||||
if (is_dev && !params.no_retry && !global.BUNEXT_BUILD_FAILED) {
|
||||
fullRebuild();
|
||||
}
|
||||
}
|
||||
|
||||
+6
-1
@@ -1,5 +1,6 @@
|
||||
import isDevelopment from "../../../utils/is-development";
|
||||
import { log } from "../../../utils/log";
|
||||
import { logBuildErrorsOnce } from "../../../utils/build-error-log";
|
||||
import getCache from "../../cache/get-cache";
|
||||
import generateWebPageResponseFromComponentReturn from "./generate-web-page-response-from-component-return";
|
||||
import grabPageComponent from "./grab-page-component";
|
||||
@@ -32,7 +33,11 @@ export default async function handleWebPages({ req, }) {
|
||||
});
|
||||
}
|
||||
catch (error) {
|
||||
log.error(`Error Handling Web Page: ${error.message}`, error);
|
||||
logBuildErrorsOnce([
|
||||
{
|
||||
text: `Error Handling Web Page: ${error?.message || String(error)}`,
|
||||
},
|
||||
], (line) => log.error(line));
|
||||
const componentRes = await grabPageErrorComponent({
|
||||
error,
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user