Handle browser Errors.
This commit is contained in:
@@ -21,36 +21,13 @@ export default function grabClientHydrationScript({ page_local_path }: Params) {
|
||||
|
||||
const does_root_exist = existsSync(root_component_path);
|
||||
|
||||
// let txt = ``;
|
||||
// txt += `import { hydrateRoot } from "react-dom/client";\n`;
|
||||
// if (does_root_exist) {
|
||||
// txt += `import Root from "${root_component_path}";\n`;
|
||||
// }
|
||||
// txt += `import Page from "${page.local_path}";\n\n`;
|
||||
// txt += `const pageProps = window.__PAGE_PROPS__ || {};\n`;
|
||||
|
||||
// if (does_root_exist) {
|
||||
// txt += `const component = <Root {...pageProps}><Page {...pageProps} /></Root>\n`;
|
||||
// } else {
|
||||
// txt += `const component = <Page {...pageProps} />\n`;
|
||||
// }
|
||||
// txt += `const root = hydrateRoot(document.getElementById("${ClientRootElementIDName}"), component);\n\n`;
|
||||
// txt += `window.${ClientRootComponentWindowName} = root;\n`;
|
||||
|
||||
let txt = ``;
|
||||
// txt += `import * as React from "react";\n`;
|
||||
// txt += `import * as ReactDOM from "react-dom";\n`;
|
||||
// txt += `import * as ReactDOMClient from "react-dom/client";\n`;
|
||||
// txt += `import * as JSXRuntime from "react/jsx-runtime";\n`;
|
||||
|
||||
txt += `import { hydrateRoot, createElement } from "react-dom/client";\n`;
|
||||
if (does_root_exist) {
|
||||
txt += `import Root from "${root_component_path}";\n`;
|
||||
}
|
||||
txt += `import Page from "${page_local_path}";\n\n`;
|
||||
// txt += `window.__REACT__ = React;\n`;
|
||||
// txt += `window.__REACT_DOM__ = ReactDOM;\n`;
|
||||
// txt += `window.__REACT_DOM_CLIENT__ = ReactDOMClient;\n`;
|
||||
// txt += `window.__JSX_RUNTIME__ = JSXRuntime;\n\n`;
|
||||
txt += `const pageProps = window.__PAGE_PROPS__ || {};\n`;
|
||||
|
||||
if (does_root_exist) {
|
||||
@@ -62,7 +39,9 @@ export default function grabClientHydrationScript({ page_local_path }: Params) {
|
||||
txt += `if (window.${ClientRootComponentWindowName}?.render) {\n`;
|
||||
txt += ` window.${ClientRootComponentWindowName}.render(component);\n`;
|
||||
txt += `} else {\n`;
|
||||
txt += ` const root = hydrateRoot(document.getElementById("${ClientRootElementIDName}"), component);\n\n`;
|
||||
txt += ` const root = hydrateRoot(document.getElementById("${ClientRootElementIDName}"), component, { onRecoverableError: () => {\n\n`;
|
||||
txt += ` console.log(\`Hydration Error.\`)\n\n`;
|
||||
txt += ` } });\n\n`;
|
||||
txt += ` window.${ClientRootComponentWindowName} = root;\n`;
|
||||
|
||||
txt += ` window.__BUNEXT_RERENDER__ = (NewPage) => {\n`;
|
||||
@@ -71,14 +50,5 @@ export default function grabClientHydrationScript({ page_local_path }: Params) {
|
||||
txt += ` };\n`;
|
||||
txt += `}\n`;
|
||||
|
||||
// // HMR re-render helper
|
||||
// if (does_root_exist) {
|
||||
// txt += `window.__BUNEXT_RERENDER__ = (NewPage) => {\n`;
|
||||
// txt += ` const props = window.__PAGE_PROPS__ || {};\n`;
|
||||
// txt += ` root.render(<Root {...props}><NewPage {...props} /></Root>);\n`;
|
||||
// txt += `};\n`;
|
||||
// } else {
|
||||
// }
|
||||
|
||||
return txt;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
import handleWebPages from "./web-pages/handle-web-pages";
|
||||
import handleRoutes from "./handle-routes";
|
||||
import isDevelopment from "../../utils/is-development";
|
||||
import grabConstants from "../../utils/grab-constants";
|
||||
import { AppData } from "../../data/app-data";
|
||||
import handleHmr from "./handle-hmr";
|
||||
import handleHmrUpdate from "./handle-hmr-update";
|
||||
import handlePublic from "./handle-public";
|
||||
import handleFiles from "./handle-files";
|
||||
|
||||
type Params = {
|
||||
req: Request;
|
||||
};
|
||||
|
||||
export default async function bunextRequestHandler({
|
||||
req,
|
||||
}: Params): Promise<Response> {
|
||||
const is_dev = isDevelopment();
|
||||
|
||||
try {
|
||||
const url = new URL(req.url);
|
||||
|
||||
const { config } = grabConstants();
|
||||
|
||||
let response: Response | undefined = undefined;
|
||||
|
||||
if (config?.middleware) {
|
||||
const middleware_res = await config.middleware({
|
||||
req,
|
||||
url,
|
||||
});
|
||||
|
||||
if (typeof middleware_res == "object") {
|
||||
return middleware_res;
|
||||
}
|
||||
}
|
||||
|
||||
if (url.pathname == `/${AppData["ClientHMRPath"]}`) {
|
||||
response = await handleHmrUpdate({ req });
|
||||
} else if (url.pathname === "/__hmr" && is_dev) {
|
||||
response = await handleHmr({ req });
|
||||
} else if (url.pathname.startsWith("/api/")) {
|
||||
response = await handleRoutes({ req });
|
||||
} else if (url.pathname.startsWith("/public/")) {
|
||||
response = await handlePublic({ req });
|
||||
} else if (url.pathname.match(/\..*$/)) {
|
||||
response = await handleFiles({ req });
|
||||
} else {
|
||||
response = await handleWebPages({ req });
|
||||
}
|
||||
|
||||
if (!response) {
|
||||
throw new Error(`No Response generated`);
|
||||
}
|
||||
|
||||
if (is_dev) {
|
||||
response.headers.set(
|
||||
"Cache-Control",
|
||||
"no-cache, no-store, must-revalidate",
|
||||
);
|
||||
}
|
||||
|
||||
return response;
|
||||
} catch (error: any) {
|
||||
return new Response(`Server Error: ${error.message}`, {
|
||||
status: 500,
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,3 @@
|
||||
import type { Server } from "bun";
|
||||
import grabDirNames from "../../utils/grab-dir-names";
|
||||
import path from "path";
|
||||
import isDevelopment from "../../utils/is-development";
|
||||
@@ -8,10 +7,9 @@ const { PUBLIC_DIR } = grabDirNames();
|
||||
|
||||
type Params = {
|
||||
req: Request;
|
||||
server: Server;
|
||||
};
|
||||
|
||||
export default async function ({ req, server }: Params): Promise<Response> {
|
||||
export default async function ({ req }: Params): Promise<Response> {
|
||||
try {
|
||||
const is_dev = isDevelopment();
|
||||
const url = new URL(req.url);
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import type { Server } from "bun";
|
||||
import grabDirNames from "../../utils/grab-dir-names";
|
||||
import { AppData } from "../../data/app-data";
|
||||
import path from "path";
|
||||
@@ -6,14 +5,13 @@ import grabRootFile from "./web-pages/grab-root-file";
|
||||
import grabPageBundledReactComponent from "./web-pages/grab-page-bundled-react-component";
|
||||
import writeHMRTsxModule from "./web-pages/write-hmr-tsx-module";
|
||||
|
||||
const { PUBLIC_DIR, BUNX_HYDRATION_SRC_DIR } = grabDirNames();
|
||||
const { BUNX_HYDRATION_SRC_DIR } = grabDirNames();
|
||||
|
||||
type Params = {
|
||||
req: Request;
|
||||
server: Server;
|
||||
};
|
||||
|
||||
export default async function ({ req, server }: Params): Promise<Response> {
|
||||
export default async function ({ req }: Params): Promise<Response> {
|
||||
try {
|
||||
const url = new URL(req.url);
|
||||
|
||||
|
||||
@@ -6,10 +6,9 @@ import grabRouter from "../../utils/grab-router";
|
||||
|
||||
type Params = {
|
||||
req: Request;
|
||||
server: Server;
|
||||
};
|
||||
|
||||
export default async function ({ req, server }: Params): Promise<Response> {
|
||||
export default async function ({ req }: Params): Promise<Response> {
|
||||
const referer_url = new URL(req.headers.get("referer") || "");
|
||||
const match = global.ROUTER.match(referer_url.pathname);
|
||||
|
||||
|
||||
@@ -1,17 +1,15 @@
|
||||
import type { Server } from "bun";
|
||||
import grabDirNames from "../../utils/grab-dir-names";
|
||||
import path from "path";
|
||||
import isDevelopment from "../../utils/is-development";
|
||||
import { existsSync } from "fs";
|
||||
|
||||
const { PUBLIC_DIR, BUNX_HYDRATION_SRC_DIR } = grabDirNames();
|
||||
const { PUBLIC_DIR } = grabDirNames();
|
||||
|
||||
type Params = {
|
||||
req: Request;
|
||||
server: Server;
|
||||
};
|
||||
|
||||
export default async function ({ req, server }: Params): Promise<Response> {
|
||||
export default async function ({ req }: Params): Promise<Response> {
|
||||
try {
|
||||
const is_dev = isDevelopment();
|
||||
const url = new URL(req.url);
|
||||
|
||||
@@ -7,10 +7,9 @@ import isDevelopment from "../../utils/is-development";
|
||||
|
||||
type Params = {
|
||||
req: Request;
|
||||
server: Server;
|
||||
};
|
||||
|
||||
export default async function ({ req, server }: Params): Promise<Response> {
|
||||
export default async function ({ req }: Params): Promise<Response> {
|
||||
const url = new URL(req.url);
|
||||
const is_dev = isDevelopment();
|
||||
|
||||
@@ -72,7 +71,6 @@ export default async function ({ req, server }: Params): Promise<Response> {
|
||||
|
||||
const res: Response = await module["default"]({
|
||||
...routeParams,
|
||||
server,
|
||||
} as BunxRouteParams);
|
||||
|
||||
if (is_dev) {
|
||||
|
||||
@@ -1,14 +1,7 @@
|
||||
import type { ServeOptions } from "bun";
|
||||
import grabAppPort from "../../utils/grab-app-port";
|
||||
import handleWebPages from "./web-pages/handle-web-pages";
|
||||
import handleRoutes from "./handle-routes";
|
||||
import isDevelopment from "../../utils/is-development";
|
||||
import grabConstants from "../../utils/grab-constants";
|
||||
import { AppData } from "../../data/app-data";
|
||||
import handleHmr from "./handle-hmr";
|
||||
import handleHmrUpdate from "./handle-hmr-update";
|
||||
import handlePublic from "./handle-public";
|
||||
import handleFiles from "./handle-files";
|
||||
import bunextRequestHandler from "./bunext-req-handler";
|
||||
|
||||
type Params = {
|
||||
dev?: boolean;
|
||||
@@ -21,59 +14,10 @@ export default async function (params?: Params): Promise<ServeOptions> {
|
||||
|
||||
return {
|
||||
async fetch(req, server) {
|
||||
try {
|
||||
const url = new URL(req.url);
|
||||
|
||||
const { config } = grabConstants();
|
||||
|
||||
let response: Response | undefined = undefined;
|
||||
|
||||
if (config?.middleware) {
|
||||
const middleware_res = await config.middleware({
|
||||
req,
|
||||
url,
|
||||
server,
|
||||
});
|
||||
|
||||
if (typeof middleware_res == "object") {
|
||||
return middleware_res;
|
||||
}
|
||||
}
|
||||
|
||||
if (url.pathname == `/${AppData["ClientHMRPath"]}`) {
|
||||
response = await handleHmrUpdate({ req, server });
|
||||
} else if (url.pathname === "/__hmr" && is_dev) {
|
||||
response = await handleHmr({ req, server });
|
||||
} else if (url.pathname.startsWith("/api/")) {
|
||||
response = await handleRoutes({ req, server });
|
||||
} else if (url.pathname.startsWith("/public/")) {
|
||||
response = await handlePublic({ req, server });
|
||||
} else if (url.pathname.match(/\..*$/)) {
|
||||
response = await handleFiles({ req, server });
|
||||
} else {
|
||||
response = await handleWebPages({ req });
|
||||
}
|
||||
|
||||
if (!response) {
|
||||
throw new Error(`No Response generated`);
|
||||
}
|
||||
|
||||
if (is_dev) {
|
||||
response.headers.set(
|
||||
"Cache-Control",
|
||||
"no-cache, no-store, must-revalidate",
|
||||
);
|
||||
}
|
||||
|
||||
return response;
|
||||
} catch (error: any) {
|
||||
return new Response(`Server Error: ${error.message}`, {
|
||||
status: 500,
|
||||
});
|
||||
}
|
||||
return await bunextRequestHandler({ req });
|
||||
},
|
||||
port,
|
||||
idleTimeout: 0,
|
||||
// idleTimeout: 0,
|
||||
development: {
|
||||
hmr: true,
|
||||
},
|
||||
|
||||
@@ -62,7 +62,7 @@ export default async function genWebHTML({
|
||||
}
|
||||
|
||||
if (isDevelopment()) {
|
||||
html += `<script defer>\n${await grabWebPageHydrationScript({ bundledMap })}\n</script>\n`;
|
||||
html += `<script defer>\n${await grabWebPageHydrationScript()}\n</script>\n`;
|
||||
}
|
||||
|
||||
if (headHTML) {
|
||||
|
||||
@@ -5,31 +5,36 @@ type Params = {
|
||||
bundledMap?: BundlerCTXMap;
|
||||
};
|
||||
|
||||
export default async function ({ bundledMap }: Params) {
|
||||
export default async function (params?: Params) {
|
||||
let script = "";
|
||||
|
||||
script += `console.log(\`Development Environment\`);\n\n`;
|
||||
script += `const _ce = console.error.bind(console);\n`;
|
||||
script += `console.error = (...args) => {\n`;
|
||||
script += ` if (typeof args[0] === "string" && args[0].includes("hydrat")) return;\n`;
|
||||
script += ` _ce(...args);\n`;
|
||||
script += `};\n\n`;
|
||||
|
||||
script += `function __bunext_show_error(message, source, stack) {\n`;
|
||||
script += ` const existing = document.getElementById("__bunext_error_overlay");\n`;
|
||||
script += ` if (existing) existing.remove();\n`;
|
||||
script += ` const overlay = document.createElement("div");\n`;
|
||||
script += ` overlay.id = "__bunext_error_overlay";\n`;
|
||||
script += ` overlay.style.cssText = "position:fixed;inset:0;z-index:99999;background:#1a1a1a;color:#ff6b6b;font-family:monospace;font-size:14px;padding:24px;overflow:auto;";\n`;
|
||||
script += ` overlay.innerHTML = \`<div style="max-width:900px;margin:0 auto"><div style="font-size:18px;font-weight:bold;margin-bottom:12px;color:#ff4444">Runtime Error</div><div style="color:#fff;margin-bottom:16px">\${message}</div>\${source ? \`<div style="color:#888;margin-bottom:16px">\${source}</div>\` : ""}\${stack ? \`<pre style="background:#111;padding:16px;border-radius:6px;overflow:auto;color:#ffa07a;white-space:pre-wrap">\${stack}</pre>\` : ""}<button onclick="this.closest('#__bunext_error_overlay').remove()" style="margin-top:16px;padding:8px 16px;background:#333;color:#fff;border:none;border-radius:4px;cursor:pointer">Dismiss</button></div>\`;\n`;
|
||||
script += ` document.body.appendChild(overlay);\n`;
|
||||
script += `}\n\n`;
|
||||
script += `window.addEventListener("error", (e) => __bunext_show_error(e.message, e.filename ? e.filename + ":" + e.lineno + ":" + e.colno : "", e.error?.stack ?? ""));\n`;
|
||||
script += `window.addEventListener("unhandledrejection", (e) => __bunext_show_error(String(e.reason?.message ?? e.reason), "", e.reason?.stack ?? ""));\n\n`;
|
||||
|
||||
script += `const hmr = new EventSource("/__hmr");\n`;
|
||||
script += `window.addEventListener("beforeunload", () => hmr.close());\n`;
|
||||
script += `hmr.addEventListener("update", async (event) => {\n`;
|
||||
script += ` if (event?.data) {\n`;
|
||||
script += ` console.log(\`HMR Changes Detected. Updating ...\`);\n`;
|
||||
script += ` try {\n`;
|
||||
script += ` document.getElementById("__bunext_error_overlay")?.remove();\n`;
|
||||
script += ` const data = JSON.parse(event.data);\n`;
|
||||
// script += ` console.log("data", data);\n`;
|
||||
// script += ` const modulePath = \`/\${data.target_map.path}\`;\n\n`;
|
||||
|
||||
// script += ` const modulePath = \`/${AppData["ClientHMRPath"]}?href=\${window.location.href}&t=\${Date.now()}\`;\n\n`;
|
||||
// script += ` console.log("Fetching updated module ...", modulePath);\n\n`;
|
||||
// script += ` const newModule = await import(modulePath);\n\n`;
|
||||
// script += ` console.log("newModule", newModule);\n\n`;
|
||||
// script += ` if (window.__BUNEXT_RERENDER__ && newModule.default) {\n`;
|
||||
// script += ` window.__BUNEXT_RERENDER__(newModule.default);\n`;
|
||||
// script += ` console.log(\`HMR: Component updated in-place\`);\n`;
|
||||
// script += ` } else {\n`;
|
||||
// script += ` console.warn(\`HMR: No re-render helper found, falling back to reload\`);\n`;
|
||||
// // script += ` window.location.reload();\n`;
|
||||
// script += ` }\n\n`;
|
||||
|
||||
script += ` if (data.target_map.css_path) {\n`;
|
||||
script += ` const oldLink = document.querySelector('link[rel="stylesheet"]');\n`;
|
||||
@@ -60,75 +65,3 @@ export default async function ({ bundledMap }: Params) {
|
||||
|
||||
return script;
|
||||
}
|
||||
|
||||
// import grabDirNames from "../../../utils/grab-dir-names";
|
||||
// import type { BundlerCTXMap, PageDistGenParams } from "../../../types";
|
||||
|
||||
// const { BUNX_HYDRATION_SRC_DIR } = grabDirNames();
|
||||
|
||||
// type Params = {
|
||||
// bundledMap?: BundlerCTXMap;
|
||||
// };
|
||||
|
||||
// export default async function ({ bundledMap }: Params) {
|
||||
// let script = "";
|
||||
|
||||
// // script += `import React from "react";\n`;
|
||||
// // script += `import { hydrateRoot } from "react-dom/client";\n`;
|
||||
// // script += `import App from "${page_file}";\n`;
|
||||
|
||||
// // script += `declare global {\n`;
|
||||
// // script += ` interface Window {\n`;
|
||||
// // script += ` ${ClientWindowPagePropsName}: any;\n`;
|
||||
// // script += ` }\n`;
|
||||
// // script += `}\n`;
|
||||
|
||||
// // script += `let root: any = null;\n\n`;
|
||||
// // script += `const component = <App {...window.${ClientWindowPagePropsName}} />;\n\n`;
|
||||
// // script += `const container = document.getElementById("${ClientRootElementIDName}");\n\n`;
|
||||
// // script += `if (container) {\n`;
|
||||
// // script += ` root = hydrateRoot(container, component);\n`;
|
||||
// // script += `}\n\n`;
|
||||
// script += `console.log(\`Development Environment\`);\n`;
|
||||
// // script += `console.log(import.meta);\n`;
|
||||
|
||||
// // script += `if (import.meta.hot) {\n`;
|
||||
// // script += ` console.log(\`HMR active\`);\n`;
|
||||
// // script += ` import.meta.hot.dispose(() => {\n`;
|
||||
// // script += ` console.log("dispose");\n`;
|
||||
// // script += ` });\n`;
|
||||
// // script += `}\n`;
|
||||
|
||||
// script += `const hmr = new EventSource("/__hmr");\n`;
|
||||
// script += `hmr.addEventListener("update", async (event) => {\n`;
|
||||
// // script += ` console.log(\`HMR even received:\`, event);\n`;
|
||||
// script += ` if (event.data) {\n`;
|
||||
// script += ` console.log(\`HMR Changes Detected. Reloading ...\`);\n`;
|
||||
// // script += ` console.log("event", event);\n`;
|
||||
// // script += ` console.log("window.${ClientRootComponentWindowName}", window.${ClientRootComponentWindowName});\n\n`;
|
||||
// // script += ` const event_data = JSON.parse(event.data);\n\n`;
|
||||
// // script += ` const new_js_path = \`/\${event_data.target_map.path}\`;\n\n`;
|
||||
|
||||
// // script += ` console.log("event_data", event_data);\n\n`;
|
||||
// // script += ` console.log("new_js_path", new_js_path);\n\n`;
|
||||
|
||||
// // script += ` if (window.${ClientRootComponentWindowName}) {\n`;
|
||||
// // script += ` const new_component = await import(new_js_path);\n`;
|
||||
// // script += ` window.${ClientRootComponentWindowName}.render(new_component);\n`;
|
||||
// // script += ` }\n`;
|
||||
|
||||
// // script += ` import("${page_file}?t=" + event.data.update).then((module) => {\n`;
|
||||
// // script += ` root.render(module.default);\n`;
|
||||
// // script += ` })\n`;
|
||||
// // script += ` console.log("root", root);\n`;
|
||||
// // script += ` root.unmount();\n`;
|
||||
// // script += ` const container = document.getElementById("${ClientRootElementIDName}");\n\n`;
|
||||
// // script += ` root = hydrateRoot(container!, component);\n`;
|
||||
// // script += ` window.history.pushState({ page: 1 }, "New Page Title", \`\${window.location.pathname}?v=\${Date.now()}\`);\n`;
|
||||
// // script += ` root.render(component);\n`;
|
||||
// script += ` window.location.reload();\n`;
|
||||
// script += ` }\n`;
|
||||
// script += ` });\n`;
|
||||
|
||||
// return script;
|
||||
// }
|
||||
|
||||
+5
-1
@@ -1,2 +1,6 @@
|
||||
const bunext = {};
|
||||
import bunextRequestHandler from "./functions/server/bunext-req-handler";
|
||||
|
||||
const bunext = {
|
||||
bunextRequestHandler,
|
||||
};
|
||||
export default bunext;
|
||||
|
||||
@@ -57,7 +57,6 @@ export type BunextConfig = {
|
||||
export type BunextConfigMiddlewareParams = {
|
||||
req: Request;
|
||||
url: URL;
|
||||
server: Server;
|
||||
};
|
||||
|
||||
export type GetRouteReturn = {
|
||||
|
||||
Reference in New Issue
Block a user