Error handling bugfix: pass #1
This commit is contained in:
+3
-2
@@ -7,6 +7,7 @@ import handleFiles from "./handle-files";
|
||||
import handleBunextPublicAssets from "./handle-bunext-public-assets";
|
||||
import checkExcludedPatterns from "../../utils/check-excluded-patterns";
|
||||
import { AppData } from "../../data/app-data";
|
||||
import { log } from "../../utils/log";
|
||||
import fullRebuild from "./full-rebuild";
|
||||
const HMR_RETRY_COOLDOWN_MS = 5000;
|
||||
let lastHmrRetryTime = 0;
|
||||
@@ -67,12 +68,12 @@ export default async function bunextRequestHandler({ req: initial_req, server, }
|
||||
return response;
|
||||
}
|
||||
catch (error) {
|
||||
log.error(`Server Error: ${error?.message || error}\n${error?.stack || ""}`);
|
||||
if (is_dev) {
|
||||
return new Response(`Server Error: ${error.message}`, {
|
||||
return new Response(`Server Error: ${error?.message || error}`, {
|
||||
status: 500,
|
||||
});
|
||||
}
|
||||
console.error(`Server Error: ${error.message}`, error);
|
||||
return new Response("Internal Server Error", { status: 500 });
|
||||
}
|
||||
}
|
||||
|
||||
+26
-6
@@ -1,8 +1,31 @@
|
||||
import { log } from "../../utils/log";
|
||||
import allPagesESBuildContextBundler from "../bundler/all-pages-esbuild-context-bundler";
|
||||
import serverPostBuildFn from "./server-post-build-fn";
|
||||
import watcherEsbuildCTX from "./watcher-esbuild-ctx";
|
||||
import { restartWatcher } from "./watcher-esbuild-ctx";
|
||||
import { logBuildErrorsOnce } from "../../utils/build-error-log";
|
||||
// Single-flight: error recovery paths (grab-page-component retries, the
|
||||
// watcher, post-build HMR pushes) all call fullRebuild, and each rebuild
|
||||
// triggers serverPostBuildFn which can re-enter error handling. Without this
|
||||
// guard one broken page could fan out into cascading, overlapping rebuilds
|
||||
// that leave the dev server unresponsive. Concurrent callers await the same
|
||||
// in-flight rebuild instead of starting a new one.
|
||||
let rebuild_in_progress;
|
||||
export default async function fullRebuild(params) {
|
||||
if (rebuild_in_progress) {
|
||||
if (params?.msg) {
|
||||
log.watch(`Rebuild already in progress, skipping: ${params.msg}`);
|
||||
}
|
||||
return rebuild_in_progress;
|
||||
}
|
||||
const run = doFullRebuild(params).finally(() => {
|
||||
if (rebuild_in_progress === run) {
|
||||
rebuild_in_progress = undefined;
|
||||
}
|
||||
});
|
||||
rebuild_in_progress = run;
|
||||
return run;
|
||||
}
|
||||
async function doFullRebuild(params) {
|
||||
try {
|
||||
const { msg } = params || {};
|
||||
global.BUNEXT_RECOMPILING = true;
|
||||
@@ -24,14 +47,11 @@ export default async function fullRebuild(params) {
|
||||
});
|
||||
}
|
||||
catch (error) {
|
||||
log.error(error);
|
||||
logBuildErrorsOnce([{ text: error?.message || String(error) }], (line) => log.error(line));
|
||||
}
|
||||
finally {
|
||||
global.BUNEXT_RECOMPILING = false;
|
||||
global.BUNEXT_IS_SERVER_COMPONENT = false;
|
||||
}
|
||||
if (global.BUNEXT_PAGES_SRC_WATCHER) {
|
||||
global.BUNEXT_PAGES_SRC_WATCHER.close();
|
||||
watcherEsbuildCTX();
|
||||
}
|
||||
restartWatcher();
|
||||
}
|
||||
|
||||
+130
-85
@@ -2,103 +2,148 @@ import grabRouteParams from "../../utils/grab-route-params";
|
||||
import grabConstants from "../../utils/grab-constants";
|
||||
import grabRouter from "../../utils/grab-router";
|
||||
import isDevelopment from "../../utils/is-development";
|
||||
import { log } from "../../utils/log";
|
||||
import _ from "lodash";
|
||||
import path from "path";
|
||||
import grabDirNames from "../../utils/grab-dir-names";
|
||||
import grabRelativePath from "../../utils/grab-relative-path";
|
||||
const { ROOT_DIR } = grabDirNames();
|
||||
export default async function ({ req }) {
|
||||
const url = new URL(req.url);
|
||||
const is_dev = isDevelopment();
|
||||
const { MBInBytes, ServerDefaultRequestBodyLimitBytes } = grabConstants();
|
||||
const router = grabRouter();
|
||||
const match = router.match(url.pathname);
|
||||
if (!match?.filePath) {
|
||||
const errMsg = `Route ${url.pathname} not found`;
|
||||
try {
|
||||
const url = new URL(req.url);
|
||||
const is_dev = isDevelopment();
|
||||
const { MBInBytes, ServerDefaultRequestBodyLimitBytes } = grabConstants();
|
||||
const router = grabRouter();
|
||||
const match = router.match(url.pathname);
|
||||
if (!match?.filePath) {
|
||||
const errMsg = `Route ${url.pathname} not found`;
|
||||
return Response.json({
|
||||
success: false,
|
||||
msg: errMsg,
|
||||
}, {
|
||||
status: 404,
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
});
|
||||
}
|
||||
const routeParams = await grabRouteParams({
|
||||
req,
|
||||
query: match.query,
|
||||
});
|
||||
let module;
|
||||
const now = Date.now();
|
||||
const relative_match_path = grabRelativePath(match.filePath);
|
||||
if (is_dev &&
|
||||
global.BUNEXT_SSR_BUNDLER_CTX_MAP?.[relative_match_path]?.path) {
|
||||
const target_import = path.join(ROOT_DIR, global.BUNEXT_SSR_BUNDLER_CTX_MAP[relative_match_path].path);
|
||||
module = await import(`${target_import}?t=${now}`);
|
||||
}
|
||||
else {
|
||||
const import_path = is_dev
|
||||
? `${match.filePath}?t=${now}`
|
||||
: match.filePath;
|
||||
module = await import(import_path);
|
||||
}
|
||||
const config = module.config;
|
||||
const maxBodyBytes = config?.max_request_body_mb
|
||||
? config.max_request_body_mb * MBInBytes
|
||||
: ServerDefaultRequestBodyLimitBytes;
|
||||
const contentLength = req.headers.get("content-length");
|
||||
if (contentLength) {
|
||||
const size = parseInt(contentLength, 10);
|
||||
if (size > maxBodyBytes) {
|
||||
return Response.json({
|
||||
success: false,
|
||||
msg: "Request Body Too Large!",
|
||||
}, {
|
||||
status: 413,
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
else if (req.method !== "GET" && req.method !== "HEAD") {
|
||||
const body = await req.arrayBuffer();
|
||||
if (body.byteLength > maxBodyBytes) {
|
||||
return Response.json({
|
||||
success: false,
|
||||
msg: "Request Body Too Large!",
|
||||
}, {
|
||||
status: 413,
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
});
|
||||
}
|
||||
try {
|
||||
routeParams.body = JSON.parse(new TextDecoder().decode(body) || "{}");
|
||||
}
|
||||
catch (error) {
|
||||
log.error(`Invalid JSON body for ${url.pathname}: ${error?.message}`);
|
||||
return Response.json({
|
||||
success: false,
|
||||
msg: "Invalid JSON body",
|
||||
}, {
|
||||
status: 400,
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
const target_module = (module["default"] ||
|
||||
module["handler"]);
|
||||
let res;
|
||||
try {
|
||||
res = await target_module?.({
|
||||
...routeParams,
|
||||
});
|
||||
}
|
||||
catch (error) {
|
||||
log.error(`API Route Error (${url.pathname}): ${error?.message}\n${error?.stack || ""}`);
|
||||
return Response.json({
|
||||
success: false,
|
||||
msg: `Route handler error: ${error?.message || "Internal Server Error"}`,
|
||||
}, {
|
||||
status: 500,
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
});
|
||||
}
|
||||
if (res instanceof Response) {
|
||||
if (is_dev) {
|
||||
res.headers.set("Cache-Control", "no-cache, no-store, must-revalidate");
|
||||
}
|
||||
return res;
|
||||
}
|
||||
if (res) {
|
||||
let final_res = Response.json(_.omit(res, [
|
||||
"bunext_api_route_res_options",
|
||||
"bunext_api_route_res_transform_fn",
|
||||
]), {
|
||||
...(res.bunext_api_route_res_options || undefined),
|
||||
});
|
||||
if (res.bunext_api_route_res_transform_fn) {
|
||||
final_res = await res.bunext_api_route_res_transform_fn(final_res);
|
||||
}
|
||||
return final_res;
|
||||
}
|
||||
return Response.json({ err: `Route handler error` });
|
||||
}
|
||||
catch (error) {
|
||||
const url = new URL(req.url);
|
||||
log.error(`API Route Error (${url.pathname}): ${error?.message}\n${error?.stack || ""}`);
|
||||
return Response.json({
|
||||
success: false,
|
||||
msg: errMsg,
|
||||
msg: `Route handler error: ${error?.message || "Internal Server Error"}`,
|
||||
}, {
|
||||
status: 404,
|
||||
status: 500,
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
});
|
||||
}
|
||||
const routeParams = await grabRouteParams({
|
||||
req,
|
||||
query: match.query,
|
||||
});
|
||||
let module;
|
||||
const now = Date.now();
|
||||
const relative_match_path = grabRelativePath(match.filePath);
|
||||
if (is_dev &&
|
||||
global.BUNEXT_SSR_BUNDLER_CTX_MAP?.[relative_match_path]?.path) {
|
||||
const target_import = path.join(ROOT_DIR, global.BUNEXT_SSR_BUNDLER_CTX_MAP[relative_match_path].path);
|
||||
module = await import(`${target_import}?t=${now}`);
|
||||
}
|
||||
else {
|
||||
const import_path = is_dev
|
||||
? `${match.filePath}?t=${now}`
|
||||
: match.filePath;
|
||||
module = await import(import_path);
|
||||
}
|
||||
const config = module.config;
|
||||
const maxBodyBytes = config?.max_request_body_mb
|
||||
? config.max_request_body_mb * MBInBytes
|
||||
: ServerDefaultRequestBodyLimitBytes;
|
||||
const contentLength = req.headers.get("content-length");
|
||||
if (contentLength) {
|
||||
const size = parseInt(contentLength, 10);
|
||||
if (size > maxBodyBytes) {
|
||||
return Response.json({
|
||||
success: false,
|
||||
msg: "Request Body Too Large!",
|
||||
}, {
|
||||
status: 413,
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
else if (req.method !== "GET" && req.method !== "HEAD") {
|
||||
const body = await req.arrayBuffer();
|
||||
if (body.byteLength > maxBodyBytes) {
|
||||
return Response.json({
|
||||
success: false,
|
||||
msg: "Request Body Too Large!",
|
||||
}, {
|
||||
status: 413,
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
});
|
||||
}
|
||||
routeParams.body = JSON.parse(new TextDecoder().decode(body) || "{}");
|
||||
}
|
||||
const target_module = (module["default"] ||
|
||||
module["handler"]);
|
||||
const res = await target_module?.({
|
||||
...routeParams,
|
||||
});
|
||||
if (res instanceof Response) {
|
||||
if (is_dev) {
|
||||
res.headers.set("Cache-Control", "no-cache, no-store, must-revalidate");
|
||||
}
|
||||
return res;
|
||||
}
|
||||
if (res) {
|
||||
let final_res = Response.json(_.omit(res, [
|
||||
"bunext_api_route_res_options",
|
||||
"bunext_api_route_res_transform_fn",
|
||||
]), {
|
||||
...(res.bunext_api_route_res_options || undefined),
|
||||
});
|
||||
if (res.bunext_api_route_res_transform_fn) {
|
||||
final_res = await res.bunext_api_route_res_transform_fn(final_res);
|
||||
}
|
||||
return final_res;
|
||||
}
|
||||
return Response.json({ err: `Route handler error` });
|
||||
}
|
||||
|
||||
@@ -43,6 +43,9 @@ export default async function serverPostBuildFn(params) {
|
||||
req: mock_req,
|
||||
return_server_res_only: true,
|
||||
is_hydration: true,
|
||||
// This runs inside serverPostBuildFn (post-build of fullRebuild);
|
||||
// letting it trigger another rebuild/retry would cascade.
|
||||
no_retry: true,
|
||||
});
|
||||
if (page_component instanceof Response) {
|
||||
continue;
|
||||
|
||||
+7
-4
@@ -2,7 +2,7 @@ import _ from "lodash";
|
||||
import { log } from "../../utils/log";
|
||||
import serverParamsGen from "./server-params-gen";
|
||||
import isDevelopment from "../../utils/is-development";
|
||||
import watcherEsbuildCTX from "./watcher-esbuild-ctx";
|
||||
import { restartWatcher, watcherRestartAvailable, } from "./watcher-esbuild-ctx";
|
||||
export default async function startServer() {
|
||||
const serverParams = await serverParamsGen();
|
||||
const server = Bun.serve(serverParams);
|
||||
@@ -10,10 +10,13 @@ export default async function startServer() {
|
||||
global.BUNEXT_SERVER = server;
|
||||
log.server(`http://${server.hostname}:${server.port}`);
|
||||
if (is_dev) {
|
||||
// Watchdog: periodically re-create the FS watcher in case it went
|
||||
// stale and stopped receiving events. Skipped while a recent restart
|
||||
// (e.g. by fullRebuild) is still fresh so the watcher isn't churned
|
||||
// twice in a row.
|
||||
setInterval(() => {
|
||||
if (global.BUNEXT_PAGES_SRC_WATCHER) {
|
||||
global.BUNEXT_PAGES_SRC_WATCHER.close();
|
||||
watcherEsbuildCTX();
|
||||
if (watcherRestartAvailable()) {
|
||||
restartWatcher();
|
||||
}
|
||||
}, 5000);
|
||||
}
|
||||
|
||||
+11
@@ -1 +1,12 @@
|
||||
/**
|
||||
* Closes and re-creates the FS watcher. Deduped: callers that restart the
|
||||
* watcher around the same time (fullRebuild, reloadWatcher, watchdog) only
|
||||
* trigger a single re-creation.
|
||||
*/
|
||||
export declare function restartWatcher(): void;
|
||||
/**
|
||||
* True when the watcher was last (re)created long enough ago that the
|
||||
* watchdog should consider re-creating it again.
|
||||
*/
|
||||
export declare function watcherRestartAvailable(): boolean;
|
||||
export default function watcherEsbuildCTX(): Promise<void>;
|
||||
|
||||
+40
-6
@@ -6,7 +6,33 @@ import { AppData } from "../../data/app-data";
|
||||
import checkExcludedPatterns from "../../utils/check-excluded-patterns";
|
||||
import pagesSSRBundler from "../bundler/pages-ssr-bundler";
|
||||
import { log } from "../../utils/log";
|
||||
import { logBuildErrorsOnce } from "../../utils/build-error-log";
|
||||
const { ROOT_DIR, BUNX_BUNDLER_ERROR_EXIT_FILE } = grabDirNames();
|
||||
// Tracks the last watcher re-creation so the 5s watchdog in start-server
|
||||
// doesn't fight fullRebuild (which also restarts the watcher after a rebuild).
|
||||
let last_watcher_restart = 0;
|
||||
/**
|
||||
* Closes and re-creates the FS watcher. Deduped: callers that restart the
|
||||
* watcher around the same time (fullRebuild, reloadWatcher, watchdog) only
|
||||
* trigger a single re-creation.
|
||||
*/
|
||||
export function restartWatcher() {
|
||||
if (!global.BUNEXT_PAGES_SRC_WATCHER)
|
||||
return;
|
||||
if (Date.now() - last_watcher_restart < 1000)
|
||||
return;
|
||||
global.BUNEXT_PAGES_SRC_WATCHER.close();
|
||||
global.BUNEXT_PAGES_SRC_WATCHER = undefined;
|
||||
last_watcher_restart = Date.now();
|
||||
watcherEsbuildCTX();
|
||||
}
|
||||
/**
|
||||
* True when the watcher was last (re)created long enough ago that the
|
||||
* watchdog should consider re-creating it again.
|
||||
*/
|
||||
export function watcherRestartAvailable() {
|
||||
return Date.now() - last_watcher_restart > 6000;
|
||||
}
|
||||
export default async function watcherEsbuildCTX() {
|
||||
const pages_src_watcher = watch(ROOT_DIR, {
|
||||
recursive: true,
|
||||
@@ -85,9 +111,16 @@ export default async function watcherEsbuildCTX() {
|
||||
if (filename.match(/.*\.server\.tsx?/)) {
|
||||
global.BUNEXT_IS_SERVER_COMPONENT = true;
|
||||
}
|
||||
if (global.BUNEXT_BUNDLER_CTX) {
|
||||
if (global.BUNEXT_BUNDLER_CTX &&
|
||||
!global.BUNEXT_BUNDLER_CTX_DISPOSED) {
|
||||
await global.BUNEXT_BUNDLER_CTX.rebuild();
|
||||
}
|
||||
else if (global.BUNEXT_BUNDLER_CTX_DISPOSED) {
|
||||
await fullRebuild({
|
||||
msg: `Restarting Bundler ...`,
|
||||
});
|
||||
return;
|
||||
}
|
||||
if (filename.match(/(404|500)\.tsx?/)) {
|
||||
for (let i = global.BUNEXT_HMR_CONTROLLERS.length - 1; i >= 0; i--) {
|
||||
const controller = global.BUNEXT_HMR_CONTROLLERS[i];
|
||||
@@ -129,7 +162,11 @@ export default async function watcherEsbuildCTX() {
|
||||
});
|
||||
}
|
||||
catch (error) {
|
||||
log.error(`Watcher rebuild failed: ${error}`);
|
||||
logBuildErrorsOnce([
|
||||
{
|
||||
text: `Watcher rebuild failed: ${error?.message || String(error)}`,
|
||||
},
|
||||
], (line) => log.error(line));
|
||||
}
|
||||
finally {
|
||||
if (owns_recompile) {
|
||||
@@ -141,8 +178,5 @@ export default async function watcherEsbuildCTX() {
|
||||
global.BUNEXT_PAGES_SRC_WATCHER = pages_src_watcher;
|
||||
}
|
||||
function reloadWatcher() {
|
||||
if (global.BUNEXT_PAGES_SRC_WATCHER) {
|
||||
global.BUNEXT_PAGES_SRC_WATCHER.close();
|
||||
watcherEsbuildCTX();
|
||||
}
|
||||
restartWatcher();
|
||||
}
|
||||
|
||||
+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