diff --git a/src/functions/server/server-params-gen.ts b/src/functions/server/server-params-gen.ts index ce8a585..2da6dff 100644 --- a/src/functions/server/server-params-gen.ts +++ b/src/functions/server/server-params-gen.ts @@ -1,66 +1,19 @@ import path from "path"; -import type { RouterTypes, ServeOptions } from "bun"; +import type { ServeOptions } from "bun"; import grabAppPort from "../../utils/grab-app-port"; import grabDirNames from "../../utils/grab-dir-names"; import handleWebPages from "./web-pages/handle-web-pages"; import handleRoutes from "./handle-routes"; import isDevelopment from "../../utils/is-development"; -import type { Server } from "http"; type Params = { dev?: boolean; }; -// type ServerOptions = Omit & { -// routes: { [K: string]: RouterTypes.RouteValue }; -// fetch?: ( -// this: Server, -// request: Request, -// server: Server, -// ) => Response | Promise; -// }; - export default async function (params?: Params): Promise { const port = grabAppPort(); const { PUBLIC_DIR } = grabDirNames(); - // const opts: ServerOptions = { - // routes: { - // "/__hmr": { - // async GET(req) { - // if (!isDevelopment()) { - // return new Response(`Production Environment`); - // } - - // let controller: ReadableStreamDefaultController; - // const stream = new ReadableStream({ - // start(c) { - // controller = c; - // global.HMR_CONTROLLERS.add(c); - // }, - // cancel() { - // global.HMR_CONTROLLERS.delete(controller); - // }, - // }); - - // return new Response(stream, { - // headers: { - // "Content-Type": "text/event-stream", - // "Cache-Control": "no-cache", - // Connection: "keep-alive", - // }, - // }); - // }, - // }, - // "/api/*": {}, - // "/*": { - // async GET(req) { - // return await handleWebPages({ req }); - // }, - // }, - // }, - // }; - return { async fetch(req, server) { try { @@ -72,17 +25,11 @@ export default async function (params?: Params): Promise { ); const match = global.ROUTER.match(referer_url.pathname); - if (!match?.filePath) { - return new Response(`Unhandled Path.`); - } - - const target_map = global.BUNDLER_CTX_MAP?.find( - (m) => m.local_path == match.filePath, - ); - - if (!target_map?.entrypoint) { - return new Response(`Target Path has no map`); - } + const target_map = match?.filePath + ? global.BUNDLER_CTX_MAP?.find( + (m) => m.local_path == match.filePath, + ) + : undefined; let controller: ReadableStreamDefaultController; const stream = new ReadableStream({ diff --git a/src/functions/server/server-post-build-fn.ts b/src/functions/server/server-post-build-fn.ts index 672cfae..10c4491 100644 --- a/src/functions/server/server-post-build-fn.ts +++ b/src/functions/server/server-post-build-fn.ts @@ -16,17 +16,20 @@ export default async function serverPostBuildFn({ artifacts }: Params) { for (let i = 0; i < global.HMR_CONTROLLERS.length; i++) { const controller = global.HMR_CONTROLLERS[i]; - const target_artifact = artifacts.find( - (a) => controller.target_map.local_path == a.local_path, - ); - if (!target_artifact?.local_path) continue; + const target_artifact = artifacts.find( + (a) => controller.target_map?.local_path == a.local_path, + ); const final_artifact: Omit = { ..._.omit(controller, ["controller"]), target_map: target_artifact, }; + if (!target_artifact) { + delete final_artifact.target_map; + } + try { controller.controller.enqueue( `event: update\ndata: ${JSON.stringify(final_artifact)}\n\n`, diff --git a/src/functions/server/watcher.tsx b/src/functions/server/watcher.tsx index c4d4ed3..e84ad13 100644 --- a/src/functions/server/watcher.tsx +++ b/src/functions/server/watcher.tsx @@ -1,10 +1,15 @@ -import { watch } from "fs"; +import { watch, existsSync } from "fs"; +import path from "path"; import grabDirNames from "../../utils/grab-dir-names"; import serverParamsGen from "./server-params-gen"; -// import allPagesBundler from "../bundler/all-pages-bundler"; +import allPagesBundler from "../bundler/all-pages-bundler"; +import serverPostBuildFn from "./server-post-build-fn"; +import refreshRouter from "../../utils/refresh-router"; const { PAGES_DIR } = grabDirNames(); +const PAGE_FILE_RE = /\.(tsx?|jsx?)$/; + export default function watcher() { watch( PAGES_DIR, @@ -13,48 +18,39 @@ export default function watcher() { persistent: true, }, (event, filename) => { - // if (!filename) return; - // if (filename.match(/ /)) return; - // if (filename.match(/^node_modules\//)) return; - // if (filename.match(/\.bunext|\/?public\//)) return; - // if (!filename.match(/\.(tsx|ts|css|js|jsx)$/)) return; + if (!filename) return; + if (!PAGE_FILE_RE.test(filename)) return; - console.log("event", event); + // "change" events (file content modified) are already handled by + // esbuild's internal ctx.watch(). Only "rename" (create or delete) + // requires a full rebuild because entry points have changed. + if (event !== "rename") return; if (global.RECOMPILING) return; + const fullPath = path.join(PAGES_DIR, filename); + const action = existsSync(fullPath) ? "created" : "deleted"; + clearTimeout(global.WATCHER_TIMEOUT); global.WATCHER_TIMEOUT = setTimeout(async () => { try { global.RECOMPILING = true; - console.log(`File Changed. Rebuilding ...`); + console.log(`Page ${action}: ${filename}. Rebuilding ...`); - // await allPagesBundler(); + await global.BUNDLER_CTX?.dispose(); + global.BUNDLER_CTX = undefined; - // global.LAST_BUILD_TIME = Date.now(); - - // for (const controller of global.HMR_CONTROLLERS) { - // try { - // controller.enqueue( - // `event: update\ndata: ${global.LAST_BUILD_TIME}\n\n`, - // ); - // } catch { - // global.HMR_CONTROLLERS.delete(controller); - // } - // } + await allPagesBundler({ + watch: true, + post_build_fn: serverPostBuildFn, + }); } catch (error: any) { - console.log(error); + console.error(error); } finally { global.RECOMPILING = false; } }, 150); - - // if (filename.match(/\/pages\//)) { - // } else if (filename.match(/\.(js|ts|tsx|jsx)$/)) { - // clearTimeout(global.WATCHER_TIMEOUT); - // global.WATCHER_TIMEOUT = setTimeout(() => reloadServer(), 150); - // } }, ); diff --git a/src/functions/server/web-pages/grab-page-component.tsx b/src/functions/server/web-pages/grab-page-component.tsx index d36fe60..981d814 100644 --- a/src/functions/server/web-pages/grab-page-component.tsx +++ b/src/functions/server/web-pages/grab-page-component.tsx @@ -25,13 +25,19 @@ export default async function grabPageComponent({ const url = req?.url ? new URL(req.url) : undefined; const router = grabRouter(); - const { BUNX_ROOT_500_PRESET_COMPONENT, BUNX_ROOT_404_PRESET_COMPONENT, PAGES_DIR } = grabDirNames(); + const { + BUNX_ROOT_500_PRESET_COMPONENT, + BUNX_ROOT_404_PRESET_COMPONENT, + PAGES_DIR, + } = grabDirNames(); const routeParams = req ? await grabRouteParams({ req }) : undefined; try { const match = url ? router.match(url.pathname) : undefined; + console.log("match", match); + if (!match?.filePath && url?.pathname) { throw new NotFoundError(`Page ${url.pathname} not found`); } @@ -163,7 +169,9 @@ export default async function grabPageComponent({ justifyContent: "center", }} > - {is404 ? "404 Not Found" : "500 Internal Server Error"} + + {is404 ? "404 Not Found" : "500 Internal Server Error"} + ); 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 d14fd93..8eb04a1 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 @@ -1,6 +1,5 @@ import grabDirNames from "../../../utils/grab-dir-names"; import type { BundlerCTXMap, PageDistGenParams } from "../../../types"; -import grabConstants from "../../../utils/grab-constants"; const { BUNX_HYDRATION_SRC_DIR } = grabDirNames(); @@ -9,9 +8,6 @@ type Params = { }; export default async function ({ bundledMap }: Params) { - const { ClientRootElementIDName, ClientRootComponentWindowName } = - await grabConstants(); - let script = ""; // script += `import React from "react";\n`; diff --git a/src/types/index.ts b/src/types/index.ts index dfc46d6..dfa4fbf 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -215,5 +215,5 @@ export type BundlerCTXMap = { export type GlobalHMRControllerObject = { controller: ReadableStreamDefaultController; page_url: string; - target_map: BundlerCTXMap; + target_map?: BundlerCTXMap; }; diff --git a/src/utils/refresh-router.ts b/src/utils/refresh-router.ts new file mode 100644 index 0000000..b1d2c2a --- /dev/null +++ b/src/utils/refresh-router.ts @@ -0,0 +1,12 @@ +import grabDirNames from "./grab-dir-names"; + +export default function refreshRouter() { + const { PAGES_DIR } = grabDirNames(); + + const router = new Bun.FileSystemRouter({ + style: "nextjs", + dir: PAGES_DIR, + }); + + global.ROUTER = router; +}