Refactor HMR server-side watcher. Move to fs.watch instead of the inbuild esbuild.watch

This commit is contained in:
Benjamin Toby 2026-03-21 05:21:51 +01:00
parent 5fbcd0b8d6
commit 71f3598709
9 changed files with 38 additions and 14 deletions

View File

@ -81,6 +81,8 @@ export default async function allPagesBundler(params?: Params) {
const elapsed = (performance.now() - buildStart).toFixed(0);
log.success(`[Built] in ${elapsed}ms`);
global.RECOMPILING = false;
if (params?.exit_after_first_build) {
process.exit();
}
@ -115,6 +117,6 @@ export default async function allPagesBundler(params?: Params) {
if (params?.watch) {
global.BUNDLER_CTX = ctx;
global.BUNDLER_CTX.watch();
// global.BUNDLER_CTX.watch();
}
}

View File

@ -54,9 +54,9 @@ export default function grabClientHydrationScript({ page_local_path }: Params) {
txt += `const pageProps = window.__PAGE_PROPS__ || {};\n`;
if (does_root_exist) {
txt += `const component = <Root {...pageProps}><Page {...pageProps} /></Root>\n`;
txt += `const component = <Root suppressHydrationWarning={true} {...pageProps}><Page {...pageProps} /></Root>\n`;
} else {
txt += `const component = <Page {...pageProps} />\n`;
txt += `const component = <Page suppressHydrationWarning={true} {...pageProps} />\n`;
}
txt += `if (window.${ClientRootComponentWindowName}?.render) {\n`;

View File

@ -3,6 +3,7 @@ import type { BunextServerRouteConfig, BunxRouteParams } from "../../types";
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";
type Params = {
req: Request;
@ -11,6 +12,7 @@ type Params = {
export default async function ({ req, server }: Params): Promise<Response> {
const url = new URL(req.url);
const is_dev = isDevelopment();
const { MBInBytes, ServerDefaultRequestBodyLimitBytes } = grabConstants();
@ -37,7 +39,10 @@ export default async function ({ req, server }: Params): Promise<Response> {
const routeParams: BunxRouteParams = await grabRouteParams({ req });
const module = await import(match.filePath);
const now = Date.now();
const import_path = is_dev ? `${match.filePath}?t=${now}` : match.filePath;
const module = await import(import_path);
const config = module.config as BunextServerRouteConfig | undefined;
const contentLength = req.headers.get("content-length");
@ -70,5 +75,9 @@ export default async function ({ req, server }: Params): Promise<Response> {
server,
} as BunxRouteParams);
if (is_dev) {
res.headers.set("Cache-Control", "no-cache, no-store, must-revalidate");
}
return res;
}

View File

@ -16,7 +16,17 @@ export default function watcher() {
async (event, filename) => {
if (!filename) return;
if (event !== "rename") return;
if (event !== "rename") {
if (
filename.match(/\.(tsx?|jsx?|css)$/) &&
global.BUNDLER_CTX
) {
if (global.RECOMPILING) return;
global.RECOMPILING = true;
await global.BUNDLER_CTX.rebuild();
}
return;
}
if (!filename.match(/^pages\//)) return;
if (filename.match(/\/(--|\()/)) return;

View File

@ -16,9 +16,8 @@ export default async function grabPageBundledReactComponent({
try {
let tsx = ``;
const server_res_json = EJSON.stringify(server_res || {})?.replace(
/"/g,
'\\"',
const server_res_json = JSON.stringify(
EJSON.stringify(server_res || {}) ?? "{}",
);
if (root_file) {
@ -27,7 +26,7 @@ export default async function grabPageBundledReactComponent({
tsx += `import Page from "${file_path}"\n`;
tsx += `export default function Main() {\n\n`;
tsx += `const props = JSON.parse("${server_res_json}")\n\n`;
tsx += `const props = JSON.parse(${server_res_json})\n\n`;
tsx += ` return (\n`;
if (root_file) {
tsx += ` <Root suppressHydrationWarning={true} {...props}><Page {...props} /></Root>\n`;

View File

@ -27,6 +27,7 @@ export default async function grabPageComponent({
}: Params): Promise<GrabPageComponentRes> {
const url = req?.url ? new URL(req.url) : undefined;
const router = global.ROUTER;
const now = Date.now();
let routeParams: BunxRouteParams | undefined = undefined;
@ -77,7 +78,7 @@ export default async function grabPageComponent({
const { root_file } = grabRootFile();
const module: BunextPageModule = await import(file_path);
const module: BunextPageModule = await import(`${file_path}?t=${now}`);
if (debug) {
log.info(`module:`, module);
@ -106,7 +107,10 @@ export default async function grabPageComponent({
try {
if (routeParams) {
const serverData = await module["server"]?.(routeParams);
const serverData = await module["server"]?.({
...routeParams,
query: { ...routeParams.query, ...match?.query },
});
return {
...serverData,
...default_props,

View File

@ -16,7 +16,7 @@ export default async function ({ bundledMap }: Params) {
script += ` console.log(\`HMR Changes Detected. Updating ...\`);\n`;
script += ` try {\n`;
script += ` const data = JSON.parse(event.data);\n`;
// script += ` console.log("data", 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`;

View File

@ -221,7 +221,7 @@ export type BunextPageModuleServerReturn<
* Expiry time of the cache in seconds
*/
cacheExpiry?: number;
url: BunextPageModuleServerReturnURLObject;
url?: BunextPageModuleServerReturnURLObject;
};
export type BunextPageModuleServerReturnURLObject = URL & {};

View File

@ -42,7 +42,7 @@ function grabPageDirRecursively({ page_dir }: { page_dir: string }) {
continue;
}
if (page.match(/\(|\)|--/)) {
if (page.match(/\(|\)|--|\/api\//)) {
continue;
}