From b2e92e5792a2e1c6fde8ab7597d22a5688d4df56 Mon Sep 17 00:00:00 2001 From: Benjamin Toby Date: Sat, 18 Apr 2026 11:17:13 +0100 Subject: [PATCH] Updates --- dist/commands/dev/index.js | 12 ++++++- .../all-pages-esbuild-context-bundler.js | 3 +- dist/functions/bundler/pages-ssr-bundler.js | 6 +++- .../plugins/ssr-virtual-files-plugin.js | 3 ++ dist/functions/server/bunext-req-handler.js | 2 +- dist/functions/server/handle-routes.d.ts | 2 +- dist/functions/server/handle-routes.js | 2 +- .../grab-page-combined-server-res.js | 1 + .../server/web-pages/grab-page-component.js | 27 ++++++++++++--- .../web-pages/grab-page-server-res.d.ts | 3 +- .../server/web-pages/grab-page-server-res.js | 18 ++++------ dist/types/index.d.ts | 4 ++- package.json | 2 +- src/commands/dev/index.ts | 17 +++++++++- .../all-pages-esbuild-context-bundler.ts | 4 ++- src/functions/bundler/pages-ssr-bundler.ts | 5 ++- .../plugins/ssr-virtual-files-plugin.ts | 4 +++ src/functions/server/bunext-req-handler.ts | 2 +- src/functions/server/handle-routes.ts | 4 +-- .../grab-page-combined-server-res.ts | 1 + .../server/web-pages/grab-page-component.tsx | 33 ++++++++++++++++--- .../server/web-pages/grab-page-server-res.tsx | 19 +++++------ src/types/index.ts | 4 ++- src/utils/grab-all-pages.ts | 1 + 24 files changed, 134 insertions(+), 45 deletions(-) diff --git a/dist/commands/dev/index.js b/dist/commands/dev/index.js index 613a576..6c545ea 100644 --- a/dist/commands/dev/index.js +++ b/dist/commands/dev/index.js @@ -2,6 +2,8 @@ import { Command } from "commander"; import path from "path"; import grabDirNames from "../../utils/grab-dir-names"; import writeErrorFile from "../../functions/write-error-file"; +let retries = 0; +let timeout; export default function () { return new Command("dev") .description("Run development server") @@ -10,11 +12,15 @@ export default function () { }); } async function dev() { + clearTimeout(timeout); + if (retries == 1) { + process.exit(1); + } const dev_spawn_file = path.resolve(__dirname, "dev-spawn.ts"); const spawn_options = { cmd: ["bun", dev_spawn_file], stdio: ["inherit", "inherit", "inherit"], - onExit(subprocess, exitCode, signalCode, error) { + async onExit(subprocess, exitCode, signalCode, error) { writeErrorFile({ exitCode, error }); }, env: { @@ -23,6 +29,10 @@ async function dev() { }, }; let dev_process = Bun.spawn(spawn_options); + retries++; + timeout = setTimeout(() => { + retries = 0; + }, 5000); const exited = await dev_process.exited; if (exited) { return await dev(); diff --git a/dist/functions/bundler/all-pages-esbuild-context-bundler.js b/dist/functions/bundler/all-pages-esbuild-context-bundler.js index 6d934c0..500c7ec 100644 --- a/dist/functions/bundler/all-pages-esbuild-context-bundler.js +++ b/dist/functions/bundler/all-pages-esbuild-context-bundler.js @@ -20,8 +20,9 @@ export default async function allPagesESBuildContextBundler(params) { const tsx = await grabClientHydrationScript({ page_local_path: page.local_path, }); - if (!tsx) + if (!tsx) { continue; + } const entryFile = path.join(BUNX_HYDRATION_SRC_DIR, `${page.url_path}.tsx`); // await Bun.write(entryFile, txt, { createPath: true }); entryToPage.set(entryFile, { ...page, tsx }); diff --git a/dist/functions/bundler/pages-ssr-bundler.js b/dist/functions/bundler/pages-ssr-bundler.js index 4db0b30..3104d72 100644 --- a/dist/functions/bundler/pages-ssr-bundler.js +++ b/dist/functions/bundler/pages-ssr-bundler.js @@ -16,7 +16,9 @@ export default async function pagesSSRBundler(params) { for (const page of pages) { if (page.local_path.match(/\/pages\/api\//)) { const ts = await Bun.file(page.local_path).text(); - entryToPage.set(page.local_path, { ...page, tsx: ts }); + if (ts.match(/(export default)|(export \w+ handler)/)) { + entryToPage.set(page.local_path, { ...page, tsx: ts }); + } continue; } const tsx = grabPageReactComponentString({ @@ -25,6 +27,8 @@ export default async function pagesSSRBundler(params) { }); if (!tsx) continue; + if (!tsx.match(/export default/)) + continue; entryToPage.set(page.local_path, { ...page, tsx }); } const entryPoints = [...entryToPage.keys()].map((e) => `ssr-virtual:${e}`); diff --git a/dist/functions/bundler/plugins/ssr-virtual-files-plugin.js b/dist/functions/bundler/plugins/ssr-virtual-files-plugin.js index ad7df5d..fce2b9d 100644 --- a/dist/functions/bundler/plugins/ssr-virtual-files-plugin.js +++ b/dist/functions/bundler/plugins/ssr-virtual-files-plugin.js @@ -16,6 +16,9 @@ export default function ssrVirtualFilesPlugin({ entryToPage }) { if (!target?.tsx) return null; const contents = target.tsx; + if (!contents.match(/export/)) { + return null; + } return { contents: contents || "", loader: "tsx", diff --git a/dist/functions/server/bunext-req-handler.js b/dist/functions/server/bunext-req-handler.js index f77e61c..8d59aef 100644 --- a/dist/functions/server/bunext-req-handler.js +++ b/dist/functions/server/bunext-req-handler.js @@ -34,7 +34,7 @@ export default async function bunextRequestHandler({ req: initial_req, server, } return new Response("Modules Rebuilt"); } if (url.pathname === "/__hmr" && is_dev) { - response = await handleHmr({ req }); + return handleHmr({ req }); } else if (url.pathname.startsWith("/.bunext")) { response = await handleBunextPublicAssets({ req }); diff --git a/dist/functions/server/handle-routes.d.ts b/dist/functions/server/handle-routes.d.ts index 2dd8f87..9130e3e 100644 --- a/dist/functions/server/handle-routes.d.ts +++ b/dist/functions/server/handle-routes.d.ts @@ -1,5 +1,5 @@ type Params = { req: Request; }; -export default function ({ req }: Params): Promise; +export default function ({ req }: Params): Promise; export {}; diff --git a/dist/functions/server/handle-routes.js b/dist/functions/server/handle-routes.js index 181198d..bf3c2d0 100644 --- a/dist/functions/server/handle-routes.js +++ b/dist/functions/server/handle-routes.js @@ -81,7 +81,7 @@ export default async function ({ req }) { } return final_res; } - return undefined; + return Response.json({ err: `Route handler error` }); } // const relative_path = match.filePath.replace(API_DIR, ""); // const relative_module_js_file = relative_path.replace(/\.tsx?$/, ".js"); diff --git a/dist/functions/server/web-pages/grab-page-combined-server-res.js b/dist/functions/server/web-pages/grab-page-combined-server-res.js index 57099b8..e518b4f 100644 --- a/dist/functions/server/web-pages/grab-page-combined-server-res.js +++ b/dist/functions/server/web-pages/grab-page-combined-server-res.js @@ -32,6 +32,7 @@ export default async function grabPageCombinedServerRes({ file_path, debug, url, url, query, routeParams, + props: rootServerRes?.props || null, }); const mergedServerRes = _.merge(rootServerRes || {}, serverRes || {}); return { serverRes: mergedServerRes }; diff --git a/dist/functions/server/web-pages/grab-page-component.js b/dist/functions/server/web-pages/grab-page-component.js index 59e5f80..4710dec 100644 --- a/dist/functions/server/web-pages/grab-page-component.js +++ b/dist/functions/server/web-pages/grab-page-component.js @@ -7,6 +7,9 @@ import grabPageCombinedServerRes from "./grab-page-combined-server-res"; import fullRebuild from "../full-rebuild"; import serverPostBuildFn from "../server-post-build-fn"; import isDevelopment from "../../../utils/is-development"; +import { existsSync } from "fs"; +import grabDirNames from "../../../utils/grab-dir-names"; +const { BUNX_BUNDLER_ERROR_EXIT_FILE } = grabDirNames(); class NotFoundError extends Error { status = 404; constructor(message) { @@ -24,6 +27,7 @@ export default async function grabPageComponent(params) { url.protocol = forwarded_proto; } let routeParams = undefined; + const does_error_file_exist = existsSync(BUNX_BUNDLER_ERROR_EXIT_FILE); try { routeParams = req ? await grabRouteParams({ req }) : undefined; let url_path = url ? url.pathname : undefined; @@ -46,11 +50,26 @@ export default async function grabPageComponent(params) { // log.error(errMsg); throw new Error(errMsg); } - const bundledMap = global.BUNDLER_CTX_MAP[file_path]; + let bundledMap = global.BUNDLER_CTX_MAP[file_path]; if (!bundledMap?.path) { - const errMsg = `No Bundled File Path for this request path!`; - log.error(errMsg); - throw new Error(errMsg); + if (does_error_file_exist) { + throw new Error(`Application Error. Please Check your components. ${match?.filePath} likely exists but has no exported module.`); + } + let retries = 0; + const MAX_RETRIES = 2; + while (retries < MAX_RETRIES) { + await fullRebuild({ + msg: `Retrying Bundle map for file \`${file_path}\``, + }); + bundledMap = global.BUNDLER_CTX_MAP[file_path]; + if (bundledMap?.path) + break; + } + if (!bundledMap?.path) { + const errMsg = `No Bundled File Path for this request path!`; + log.error(errMsg); + throw new Error(errMsg); + } } if (req && !is_hydration) { global.BUNDLER_CTX_MAP[file_path].req = req; diff --git a/dist/functions/server/web-pages/grab-page-server-res.d.ts b/dist/functions/server/web-pages/grab-page-server-res.d.ts index 059c9fd..1c0dde2 100644 --- a/dist/functions/server/web-pages/grab-page-server-res.d.ts +++ b/dist/functions/server/web-pages/grab-page-server-res.d.ts @@ -4,6 +4,7 @@ type Params = { server_function?: BunextPageServerFn; query?: Record; routeParams?: BunxRouteParams; + props?: Record | null; }; -export default function grabPageServerRes({ url, query, routeParams, server_function, }: Params): Promise; +export default function grabPageServerRes({ url, query, routeParams, server_function, props, }: Params): Promise; export {}; diff --git a/dist/functions/server/web-pages/grab-page-server-res.js b/dist/functions/server/web-pages/grab-page-server-res.js index b0254f4..c3530c0 100644 --- a/dist/functions/server/web-pages/grab-page-server-res.js +++ b/dist/functions/server/web-pages/grab-page-server-res.js @@ -1,6 +1,6 @@ import _ from "lodash"; import { log } from "../../../utils/log"; -export default async function grabPageServerRes({ url, query, routeParams, server_function, }) { +export default async function grabPageServerRes({ url, query, routeParams, server_function, props, }) { const default_props = { url: url ? { @@ -22,26 +22,22 @@ export default async function grabPageServerRes({ url, query, routeParams, serve : null, query, }; + const init_props = props || null; try { if (routeParams && server_function) { const serverData = await server_function({ ...routeParams, query: { ...routeParams.query, ...query }, + props: init_props, }); - return { - ...serverData, - ...default_props, - }; + return _.merge(default_props, serverData); } - return { - ...default_props, - }; + return _.merge(default_props); } catch (error) { log.error(`Page ${url?.pathname} Server Error => ${error.message}\n`, error); - return { - ...default_props, + return _.merge(default_props, { error: error.message, - }; + }); } } diff --git a/dist/types/index.d.ts b/dist/types/index.d.ts index 2e7d47d..4f02ed6 100644 --- a/dist/types/index.d.ts +++ b/dist/types/index.d.ts @@ -190,7 +190,9 @@ export type BunextPageServerFn = (ctx: Omit) => Promise>; +}> = (ctx: Omit & { + props?: any; +}) => Promise>; export type BunextRouteConfig = { /** * Whether to cache the current page diff --git a/package.json b/package.json index 2f0b189..fa98eb0 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@moduletrace/bunext", - "version": "1.0.82", + "version": "1.0.83", "main": "dist/index.js", "module": "index.ts", "dependencies": { diff --git a/src/commands/dev/index.ts b/src/commands/dev/index.ts index 7b8f49a..97cff74 100644 --- a/src/commands/dev/index.ts +++ b/src/commands/dev/index.ts @@ -4,6 +4,9 @@ import type { BunSpawnOptions } from "../../types"; import grabDirNames from "../../utils/grab-dir-names"; import writeErrorFile from "../../functions/write-error-file"; +let retries = 0; +let timeout: any; + export default function () { return new Command("dev") .description("Run development server") @@ -13,12 +16,18 @@ export default function () { } async function dev() { + clearTimeout(timeout); + + if (retries == 1) { + process.exit(1); + } + const dev_spawn_file = path.resolve(__dirname, "dev-spawn.ts"); const spawn_options: BunSpawnOptions = { cmd: ["bun", dev_spawn_file], stdio: ["inherit", "inherit", "inherit"], - onExit(subprocess, exitCode, signalCode, error) { + async onExit(subprocess, exitCode, signalCode, error) { writeErrorFile({ exitCode, error }); }, env: { @@ -29,6 +38,12 @@ async function dev() { let dev_process = Bun.spawn(spawn_options); + retries++; + + timeout = setTimeout(() => { + retries = 0; + }, 5000); + const exited = await dev_process.exited; if (exited) { return await dev(); diff --git a/src/functions/bundler/all-pages-esbuild-context-bundler.ts b/src/functions/bundler/all-pages-esbuild-context-bundler.ts index 7e90369..e0c9fc5 100644 --- a/src/functions/bundler/all-pages-esbuild-context-bundler.ts +++ b/src/functions/bundler/all-pages-esbuild-context-bundler.ts @@ -41,7 +41,9 @@ export default async function allPagesESBuildContextBundler(params?: Params) { page_local_path: page.local_path, }); - if (!tsx) continue; + if (!tsx) { + continue; + } const entryFile = path.join( BUNX_HYDRATION_SRC_DIR, diff --git a/src/functions/bundler/pages-ssr-bundler.ts b/src/functions/bundler/pages-ssr-bundler.ts index 7078707..6092528 100644 --- a/src/functions/bundler/pages-ssr-bundler.ts +++ b/src/functions/bundler/pages-ssr-bundler.ts @@ -25,7 +25,9 @@ export default async function pagesSSRBundler(params?: Params) { for (const page of pages) { if (page.local_path.match(/\/pages\/api\//)) { const ts = await Bun.file(page.local_path).text(); - entryToPage.set(page.local_path, { ...page, tsx: ts }); + if (ts.match(/(export default)|(export \w+ handler)/)) { + entryToPage.set(page.local_path, { ...page, tsx: ts }); + } continue; } @@ -35,6 +37,7 @@ export default async function pagesSSRBundler(params?: Params) { }); if (!tsx) continue; + if (!tsx.match(/export default/)) continue; entryToPage.set(page.local_path, { ...page, tsx }); } diff --git a/src/functions/bundler/plugins/ssr-virtual-files-plugin.ts b/src/functions/bundler/plugins/ssr-virtual-files-plugin.ts index a46e6bb..3be216f 100644 --- a/src/functions/bundler/plugins/ssr-virtual-files-plugin.ts +++ b/src/functions/bundler/plugins/ssr-virtual-files-plugin.ts @@ -30,6 +30,10 @@ export default function ssrVirtualFilesPlugin({ entryToPage }: Params) { const contents = target.tsx; + if (!contents.match(/export/)) { + return null; + } + return { contents: contents || "", loader: "tsx", diff --git a/src/functions/server/bunext-req-handler.ts b/src/functions/server/bunext-req-handler.ts index 80389a4..692ea2e 100644 --- a/src/functions/server/bunext-req-handler.ts +++ b/src/functions/server/bunext-req-handler.ts @@ -50,7 +50,7 @@ export default async function bunextRequestHandler({ } if (url.pathname === "/__hmr" && is_dev) { - response = await handleHmr({ req }); + return handleHmr({ req }); } else if (url.pathname.startsWith("/.bunext")) { response = await handleBunextPublicAssets({ req }); } else if (url.pathname.startsWith("/api/")) { diff --git a/src/functions/server/handle-routes.ts b/src/functions/server/handle-routes.ts index a2c85c9..69186bb 100644 --- a/src/functions/server/handle-routes.ts +++ b/src/functions/server/handle-routes.ts @@ -17,7 +17,7 @@ type Params = { req: Request; }; -export default async function ({ req }: Params): Promise { +export default async function ({ req }: Params): Promise { const url = new URL(req.url); const is_dev = isDevelopment(); @@ -128,7 +128,7 @@ export default async function ({ req }: Params): Promise { return final_res; } - return undefined; + return Response.json({ err: `Route handler error` }); } // const relative_path = match.filePath.replace(API_DIR, ""); diff --git a/src/functions/server/web-pages/grab-page-combined-server-res.ts b/src/functions/server/web-pages/grab-page-combined-server-res.ts index 433be53..e8f57a4 100644 --- a/src/functions/server/web-pages/grab-page-combined-server-res.ts +++ b/src/functions/server/web-pages/grab-page-combined-server-res.ts @@ -62,6 +62,7 @@ export default async function grabPageCombinedServerRes({ url, query, routeParams, + props: rootServerRes?.props || null, }); const mergedServerRes = _.merge(rootServerRes || {}, serverRes || {}); diff --git a/src/functions/server/web-pages/grab-page-component.tsx b/src/functions/server/web-pages/grab-page-component.tsx index 765a215..ce775e4 100644 --- a/src/functions/server/web-pages/grab-page-component.tsx +++ b/src/functions/server/web-pages/grab-page-component.tsx @@ -8,6 +8,10 @@ import grabPageCombinedServerRes from "./grab-page-combined-server-res"; import fullRebuild from "../full-rebuild"; import serverPostBuildFn from "../server-post-build-fn"; import isDevelopment from "../../../utils/is-development"; +import { existsSync } from "fs"; +import grabDirNames from "../../../utils/grab-dir-names"; + +const { BUNX_BUNDLER_ERROR_EXIT_FILE } = grabDirNames(); class NotFoundError extends Error { status = 404; @@ -52,6 +56,8 @@ export default async function grabPageComponent( let routeParams: BunxRouteParams | undefined = undefined; + const does_error_file_exist = existsSync(BUNX_BUNDLER_ERROR_EXIT_FILE); + try { routeParams = req ? await grabRouteParams({ req }) : undefined; @@ -83,12 +89,31 @@ export default async function grabPageComponent( throw new Error(errMsg); } - const bundledMap = global.BUNDLER_CTX_MAP[file_path]; + let bundledMap = global.BUNDLER_CTX_MAP[file_path]; if (!bundledMap?.path) { - const errMsg = `No Bundled File Path for this request path!`; - log.error(errMsg); - throw new Error(errMsg); + if (does_error_file_exist) { + throw new Error( + `Application Error. Please Check your components. ${match?.filePath} likely exists but has no exported module.`, + ); + } + + let retries = 0; + const MAX_RETRIES = 2; + + while (retries < MAX_RETRIES) { + await fullRebuild({ + msg: `Retrying Bundle map for file \`${file_path}\``, + }); + bundledMap = global.BUNDLER_CTX_MAP[file_path]; + if (bundledMap?.path) break; + } + + if (!bundledMap?.path) { + const errMsg = `No Bundled File Path for this request path!`; + log.error(errMsg); + throw new Error(errMsg); + } } if (req && !is_hydration) { diff --git a/src/functions/server/web-pages/grab-page-server-res.tsx b/src/functions/server/web-pages/grab-page-server-res.tsx index 627edfc..316822e 100644 --- a/src/functions/server/web-pages/grab-page-server-res.tsx +++ b/src/functions/server/web-pages/grab-page-server-res.tsx @@ -13,6 +13,7 @@ type Params = { server_function?: BunextPageServerFn; query?: Record; routeParams?: BunxRouteParams; + props?: Record | null; }; export default async function grabPageServerRes({ @@ -20,6 +21,7 @@ export default async function grabPageServerRes({ query, routeParams, server_function, + props, }: Params): Promise { const default_props: BunextPageModuleServerReturn = { url: url @@ -43,31 +45,28 @@ export default async function grabPageServerRes({ query, }; + const init_props = props || null; + try { if (routeParams && server_function) { const serverData = await server_function({ ...routeParams, query: { ...routeParams.query, ...query }, + props: init_props, }); - return { - ...serverData, - ...default_props, - }; + return _.merge(default_props, serverData); } - return { - ...default_props, - }; + return _.merge(default_props); } catch (error: any) { log.error( `Page ${url?.pathname} Server Error => ${error.message}\n`, error, ); - return { - ...default_props, + return _.merge(default_props, { error: error.message, - }; + }); } } diff --git a/src/types/index.ts b/src/types/index.ts index 5a2e709..34e338c 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -223,7 +223,9 @@ export type BunextPageModuleMetaTwitter = { export type BunextPageServerFn< T extends { [k: string]: any } = { [k: string]: any }, > = ( - ctx: Omit, + ctx: Omit & { + props?: any; + }, ) => Promise>; export type BunextRouteConfig = { diff --git a/src/utils/grab-all-pages.ts b/src/utils/grab-all-pages.ts index 15310cb..8d1398e 100644 --- a/src/utils/grab-all-pages.ts +++ b/src/utils/grab-all-pages.ts @@ -64,6 +64,7 @@ function grabPageDirRecursively({ page_dir }: { page_dir: string }) { const new_page_files = grabPageDirRecursively({ page_dir: full_page_path, }); + pages_files.push(...new_page_files); } else if (page.match(/\.(ts|js)x?$/)) { const pages_file = grabPageFileObject({