From c3e1341ff8460eb571496e9c0fbb44bda90c5ead Mon Sep 17 00:00:00 2001 From: Benjamin Toby Date: Sun, 22 Mar 2026 12:09:24 +0100 Subject: [PATCH] Update HMR. Make page reload when __root.tsx file is changed. --- dist/functions/bunext-init.d.ts | 1 + dist/functions/server/server-params-gen.d.ts | 3 +- dist/functions/server/server-post-build-fn.js | 11 +- dist/functions/server/start-server.d.ts | 2 +- dist/functions/server/watcher.js | 3 + .../grab-web-page-hydration-script.js | 11 +- .../web-pages/write-hmr-tsx-module.d.ts | 7 - .../server/web-pages/write-hmr-tsx-module.js | 106 --------------- dist/types/index.d.ts | 4 +- package.json | 2 +- src/functions/bunext-init.ts | 1 + src/functions/server/server-params-gen.ts | 5 +- src/functions/server/server-post-build-fn.ts | 14 +- src/functions/server/watcher.ts | 5 + .../grab-web-page-hydration-script.tsx | 13 +- .../server/web-pages/write-hmr-tsx-module.tsx | 126 ------------------ src/types/index.ts | 4 +- 17 files changed, 60 insertions(+), 258 deletions(-) delete mode 100644 dist/functions/server/web-pages/write-hmr-tsx-module.d.ts delete mode 100644 dist/functions/server/web-pages/write-hmr-tsx-module.js delete mode 100644 src/functions/server/web-pages/write-hmr-tsx-module.tsx diff --git a/dist/functions/bunext-init.d.ts b/dist/functions/bunext-init.d.ts index 775fbd1..6f60f91 100644 --- a/dist/functions/bunext-init.d.ts +++ b/dist/functions/bunext-init.d.ts @@ -22,5 +22,6 @@ declare global { var PAGES_SRC_WATCHER: FSWatcher | undefined; var CURRENT_VERSION: string | undefined; var PAGE_FILES: PageFiles[]; + var ROOT_FILE_UPDATED: boolean; } export default function bunextInit(): Promise; diff --git a/dist/functions/server/server-params-gen.d.ts b/dist/functions/server/server-params-gen.d.ts index 86b4965..dbd1ab1 100644 --- a/dist/functions/server/server-params-gen.d.ts +++ b/dist/functions/server/server-params-gen.d.ts @@ -1,2 +1 @@ -import type { ServeOptions } from "bun"; -export default function (): Promise; +export default function (): Promise>; diff --git a/dist/functions/server/server-post-build-fn.js b/dist/functions/server/server-post-build-fn.js index d27b83f..3af6a19 100644 --- a/dist/functions/server/server-post-build-fn.js +++ b/dist/functions/server/server-post-build-fn.js @@ -25,7 +25,16 @@ export default async function serverPostBuildFn({ artifacts }) { final_artifact.page_props = serverRes; } try { - controller.controller.enqueue(`event: update\ndata: ${JSON.stringify(final_artifact)}\n\n`); + let final_data = {}; + console.log("global.ROOT_FILE_UPDATED", global.ROOT_FILE_UPDATED); + if (global.ROOT_FILE_UPDATED) { + final_data = { reload: true }; + } + else { + final_data = final_artifact; + } + controller.controller.enqueue(`event: update\ndata: ${JSON.stringify(final_data)}\n\n`); + global.ROOT_FILE_UPDATED = false; } catch { global.HMR_CONTROLLERS.splice(i, 1); diff --git a/dist/functions/server/start-server.d.ts b/dist/functions/server/start-server.d.ts index d320c8a..95c729b 100644 --- a/dist/functions/server/start-server.d.ts +++ b/dist/functions/server/start-server.d.ts @@ -1 +1 @@ -export default function startServer(): Promise>; +export default function startServer(): Promise>; diff --git a/dist/functions/server/watcher.js b/dist/functions/server/watcher.js index 1d62149..9cf00c0 100644 --- a/dist/functions/server/watcher.js +++ b/dist/functions/server/watcher.js @@ -37,6 +37,9 @@ export default async function watcher() { if (global.RECOMPILING) return; global.RECOMPILING = true; + if (full_file_path.match(/\_\_root\.tsx?$/)) { + global.ROOT_FILE_UPDATED = true; + } await rewritePagesModule({ page_url: full_file_path }); await global.BUNDLER_CTX.rebuild(); } diff --git a/dist/functions/server/web-pages/grab-web-page-hydration-script.js b/dist/functions/server/web-pages/grab-web-page-hydration-script.js index 92ddc41..0329719 100644 --- a/dist/functions/server/web-pages/grab-web-page-hydration-script.js +++ b/dist/functions/server/web-pages/grab-web-page-hydration-script.js @@ -25,13 +25,18 @@ export default async function (params) { 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 += ` console.log("data", data);\n`; + script += ` if (data.reload) {\n`; + script += ` console.log(\`Root Changes Detected. Reloading Page ...\`);\n`; + script += ` window.location.reload();\n`; + script += ` return;\n`; + script += ` }\n`; + script += ` console.log(\`HMR Changes Detected. Updating ...\`);\n`; script += ` if (data.page_props) {\n`; - script += ` window.${ClientWindowPagePropsName} = data.page_props\n`; + script += ` window.${ClientWindowPagePropsName} = data.page_props;\n`; script += ` }\n`; script += ` const oldCSSLink = document.querySelector('link[rel="stylesheet"]');\n`; script += ` if (data.target_map.css_path) {\n`; diff --git a/dist/functions/server/web-pages/write-hmr-tsx-module.d.ts b/dist/functions/server/web-pages/write-hmr-tsx-module.d.ts deleted file mode 100644 index a3e16cb..0000000 --- a/dist/functions/server/web-pages/write-hmr-tsx-module.d.ts +++ /dev/null @@ -1,7 +0,0 @@ -import type { BundlerCTXMap } from "../../../types"; -type Params = { - tsx: string; - out_file: string; -}; -export default function writeHMRTsxModule({ tsx, out_file }: Params): Promise | undefined>; -export {}; diff --git a/dist/functions/server/web-pages/write-hmr-tsx-module.js b/dist/functions/server/web-pages/write-hmr-tsx-module.js deleted file mode 100644 index 23a0344..0000000 --- a/dist/functions/server/web-pages/write-hmr-tsx-module.js +++ /dev/null @@ -1,106 +0,0 @@ -import * as esbuild from "esbuild"; -import tailwindEsbuildPlugin from "./tailwind-esbuild-plugin"; -import path from "path"; -export default async function writeHMRTsxModule({ tsx, out_file }) { - try { - const build = await esbuild.build({ - stdin: { - contents: tsx, - resolveDir: process.cwd(), - loader: "tsx", - }, - bundle: true, - format: "esm", - target: "es2020", - platform: "browser", - external: [ - "react", - "react-dom", - "react/jsx-runtime", - "react-dom/client", - ], - minify: true, - jsx: "automatic", - outfile: out_file, - plugins: [tailwindEsbuildPlugin], - metafile: true, - }); - const artifacts = Object.entries(build.metafile.outputs) - .filter(([, meta]) => meta.entryPoint) - .map(([outputPath, meta]) => { - const cssPath = meta.cssBundle || undefined; - return { - path: outputPath, - hash: path.basename(outputPath, path.extname(outputPath)), - type: outputPath.endsWith(".css") - ? "text/css" - : "text/javascript", - css_path: cssPath, - }; - }); - return artifacts?.[0]; - } - catch (error) { - return undefined; - } -} -// import * as esbuild from "esbuild"; -// import path from "path"; -// import tailwindEsbuildPlugin from "./tailwind-esbuild-plugin"; -// const hmrExternalsPlugin: esbuild.Plugin = { -// name: "hmr-globals", -// setup(build) { -// const mapping: Record = { -// react: "__REACT__", -// "react-dom": "__REACT_DOM__", -// "react-dom/client": "__REACT_DOM_CLIENT__", -// "react/jsx-runtime": "__JSX_RUNTIME__", -// }; -// const filter = new RegExp( -// `^(${Object.keys(mapping) -// .map((k) => k.replace("/", "\\/")) -// .join("|")})$`, -// ); -// build.onResolve({ filter }, (args) => { -// return { path: args.path, namespace: "hmr-global" }; -// }); -// build.onLoad({ filter: /.*/, namespace: "hmr-global" }, (args) => { -// const globalName = mapping[args.path]; -// return { -// contents: `module.exports = window.${globalName};`, -// loader: "js", -// }; -// }); -// }, -// }; -// type Params = { -// tsx: string; -// file_path: string; -// out_file: string; -// }; -// export default async function writeHMRTsxModule({ -// tsx, -// file_path, -// out_file, -// }: Params) { -// try { -// await esbuild.build({ -// stdin: { -// contents: tsx, -// resolveDir: path.dirname(file_path), -// loader: "tsx", -// }, -// bundle: true, -// format: "esm", -// target: "es2020", -// platform: "browser", -// minify: true, -// jsx: "automatic", -// outfile: out_file, -// plugins: [hmrExternalsPlugin, tailwindEsbuildPlugin], -// }); -// return true; -// } catch (error) { -// return false; -// } -// } diff --git a/dist/types/index.d.ts b/dist/types/index.d.ts index 1b2c567..fb2e93d 100644 --- a/dist/types/index.d.ts +++ b/dist/types/index.d.ts @@ -1,4 +1,4 @@ -import type { MatchedRoute, ServeOptions, Server, WebSocketHandler } from "bun"; +import type { MatchedRoute, Server, WebSocketHandler } from "bun"; import type { FC, JSX, PropsWithChildren, ReactNode } from "react"; export type ServerProps = { params: Record; @@ -47,7 +47,7 @@ export type BunextConfig = { middleware?: (params: BunextConfigMiddlewareParams) => Promise | Response | Request | undefined; defaultCacheExpiry?: number; websocket?: WebSocketHandler; - serverOptions?: ServeOptions; + serverOptions?: Bun.Serve.Options; }; export type BunextConfigMiddlewareParams = { req: Request; diff --git a/package.json b/package.json index 90b792c..ccc6af1 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "@moduletrace/bunext", "module": "index.ts", "type": "module", - "version": "1.0.12", + "version": "1.0.13", "main": "dist/index.js", "types": "dist/index.d.ts", "exports": { diff --git a/src/functions/bunext-init.ts b/src/functions/bunext-init.ts index b9b6a90..b34979d 100644 --- a/src/functions/bunext-init.ts +++ b/src/functions/bunext-init.ts @@ -37,6 +37,7 @@ declare global { var PAGES_SRC_WATCHER: FSWatcher | undefined; var CURRENT_VERSION: string | undefined; var PAGE_FILES: PageFiles[]; + var ROOT_FILE_UPDATED: boolean; } export default async function bunextInit() { diff --git a/src/functions/server/server-params-gen.ts b/src/functions/server/server-params-gen.ts index 20b2f7e..56bee09 100644 --- a/src/functions/server/server-params-gen.ts +++ b/src/functions/server/server-params-gen.ts @@ -1,11 +1,10 @@ -import type { ServeOptions } from "bun"; import grabAppPort from "../../utils/grab-app-port"; import isDevelopment from "../../utils/is-development"; import bunextRequestHandler from "./bunext-req-handler"; import grabConfig from "../grab-config"; import _ from "lodash"; -export default async function (): Promise { +export default async function (): Promise> { const port = grabAppPort(); const development = isDevelopment(); @@ -20,5 +19,5 @@ export default async function (): Promise { development, websocket: config?.websocket, ..._.omit(config?.serverOptions || {}, ["fetch"]), - } as ServeOptions; + } as Bun.Serve.Options; } diff --git a/src/functions/server/server-post-build-fn.ts b/src/functions/server/server-post-build-fn.ts index e24c388..42f14fc 100644 --- a/src/functions/server/server-post-build-fn.ts +++ b/src/functions/server/server-post-build-fn.ts @@ -42,9 +42,21 @@ export default async function serverPostBuildFn({ artifacts }: Params) { } try { + let final_data: { [k: string]: any } = {}; + + console.log("global.ROOT_FILE_UPDATED", global.ROOT_FILE_UPDATED); + + if (global.ROOT_FILE_UPDATED) { + final_data = { reload: true }; + } else { + final_data = final_artifact; + } + controller.controller.enqueue( - `event: update\ndata: ${JSON.stringify(final_artifact)}\n\n`, + `event: update\ndata: ${JSON.stringify(final_data)}\n\n`, ); + + global.ROOT_FILE_UPDATED = false; } catch { global.HMR_CONTROLLERS.splice(i, 1); } diff --git a/src/functions/server/watcher.ts b/src/functions/server/watcher.ts index 9d811e8..f3a3cbd 100644 --- a/src/functions/server/watcher.ts +++ b/src/functions/server/watcher.ts @@ -48,6 +48,11 @@ export default async function watcher() { if (filename.match(target_files_match) && global.BUNDLER_CTX) { if (global.RECOMPILING) return; global.RECOMPILING = true; + + if (full_file_path.match(/\_\_root\.tsx?$/)) { + global.ROOT_FILE_UPDATED = true; + } + await rewritePagesModule({ page_url: full_file_path }); await global.BUNDLER_CTX.rebuild(); } diff --git a/src/functions/server/web-pages/grab-web-page-hydration-script.tsx b/src/functions/server/web-pages/grab-web-page-hydration-script.tsx index 4727345..0c98d7f 100644 --- a/src/functions/server/web-pages/grab-web-page-hydration-script.tsx +++ b/src/functions/server/web-pages/grab-web-page-hydration-script.tsx @@ -35,14 +35,21 @@ export default async function (params?: Params) { 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 += ` console.log("data", data);\n`; + + script += ` if (data.reload) {\n`; + script += ` console.log(\`Root Changes Detected. Reloading Page ...\`);\n`; + script += ` window.location.reload();\n`; + script += ` return;\n`; + script += ` }\n`; + + script += ` console.log(\`HMR Changes Detected. Updating ...\`);\n`; script += ` if (data.page_props) {\n`; - script += ` window.${ClientWindowPagePropsName} = data.page_props\n`; + script += ` window.${ClientWindowPagePropsName} = data.page_props;\n`; script += ` }\n`; script += ` const oldCSSLink = document.querySelector('link[rel="stylesheet"]');\n`; diff --git a/src/functions/server/web-pages/write-hmr-tsx-module.tsx b/src/functions/server/web-pages/write-hmr-tsx-module.tsx deleted file mode 100644 index 4a3bb94..0000000 --- a/src/functions/server/web-pages/write-hmr-tsx-module.tsx +++ /dev/null @@ -1,126 +0,0 @@ -import * as esbuild from "esbuild"; -import tailwindEsbuildPlugin from "./tailwind-esbuild-plugin"; -import type { BundlerCTXMap } from "../../../types"; -import path from "path"; - -type Params = { - tsx: string; - out_file: string; -}; - -export default async function writeHMRTsxModule({ tsx, out_file }: Params) { - try { - const build = await esbuild.build({ - stdin: { - contents: tsx, - resolveDir: process.cwd(), - loader: "tsx", - }, - bundle: true, - format: "esm", - target: "es2020", - platform: "browser", - external: [ - "react", - "react-dom", - "react/jsx-runtime", - "react-dom/client", - ], - minify: true, - jsx: "automatic", - outfile: out_file, - plugins: [tailwindEsbuildPlugin], - metafile: true, - }); - - const artifacts: ( - | Pick - | undefined - )[] = Object.entries(build.metafile!.outputs) - .filter(([, meta]) => meta.entryPoint) - .map(([outputPath, meta]) => { - const cssPath = meta.cssBundle || undefined; - - return { - path: outputPath, - hash: path.basename(outputPath, path.extname(outputPath)), - type: outputPath.endsWith(".css") - ? "text/css" - : "text/javascript", - css_path: cssPath, - }; - }); - - return artifacts?.[0]; - } catch (error) { - return undefined; - } -} - -// import * as esbuild from "esbuild"; -// import path from "path"; -// import tailwindEsbuildPlugin from "./tailwind-esbuild-plugin"; - -// const hmrExternalsPlugin: esbuild.Plugin = { -// name: "hmr-globals", -// setup(build) { -// const mapping: Record = { -// react: "__REACT__", -// "react-dom": "__REACT_DOM__", -// "react-dom/client": "__REACT_DOM_CLIENT__", -// "react/jsx-runtime": "__JSX_RUNTIME__", -// }; - -// const filter = new RegExp( -// `^(${Object.keys(mapping) -// .map((k) => k.replace("/", "\\/")) -// .join("|")})$`, -// ); - -// build.onResolve({ filter }, (args) => { -// return { path: args.path, namespace: "hmr-global" }; -// }); - -// build.onLoad({ filter: /.*/, namespace: "hmr-global" }, (args) => { -// const globalName = mapping[args.path]; -// return { -// contents: `module.exports = window.${globalName};`, -// loader: "js", -// }; -// }); -// }, -// }; - -// type Params = { -// tsx: string; -// file_path: string; -// out_file: string; -// }; - -// export default async function writeHMRTsxModule({ -// tsx, -// file_path, -// out_file, -// }: Params) { -// try { -// await esbuild.build({ -// stdin: { -// contents: tsx, -// resolveDir: path.dirname(file_path), -// loader: "tsx", -// }, -// bundle: true, -// format: "esm", -// target: "es2020", -// platform: "browser", -// minify: true, -// jsx: "automatic", -// outfile: out_file, -// plugins: [hmrExternalsPlugin, tailwindEsbuildPlugin], -// }); - -// return true; -// } catch (error) { -// return false; -// } -// } diff --git a/src/types/index.ts b/src/types/index.ts index 8919d77..82b2668 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -1,4 +1,4 @@ -import type { MatchedRoute, ServeOptions, Server, WebSocketHandler } from "bun"; +import type { MatchedRoute, Server, WebSocketHandler } from "bun"; import type { FC, JSX, PropsWithChildren, ReactNode } from "react"; export type ServerProps = { @@ -57,7 +57,7 @@ export type BunextConfig = { | undefined; defaultCacheExpiry?: number; websocket?: WebSocketHandler; - serverOptions?: ServeOptions; + serverOptions?: Bun.Serve.Options; }; export type BunextConfigMiddlewareParams = {