From 321c8ebb89052dfa9e7ea3e1b6b6e00edb9cfe0c Mon Sep 17 00:00:00 2001 From: Benjamin Toby Date: Tue, 24 Mar 2026 22:03:55 +0100 Subject: [PATCH] Refactor Bundler. Fix bugs. --- dist/functions/bundler/all-pages-bundler.js | 1 - ...l-pages-esbuild-context-bundler-files.d.ts | 7 ++ ...all-pages-esbuild-context-bundler-files.js | 58 ++++++++++++ .../all-pages-esbuild-context-bundler.js | 90 +++++-------------- .../grab-artifacts-from-bundled-result.d.ts | 4 +- .../grab-artifacts-from-bundled-result.js | 14 ++- .../bundler/grab-client-hydration-script.d.ts | 2 +- .../bundler/grab-client-hydration-script.js | 16 ++++ .../plugins/esbuild-ctx-artifact-tracker.d.ts | 12 +++ .../plugins/esbuild-ctx-artifact-tracker.js | 57 ++++++++++++ .../bundler/plugins/virtual-files-plugin.d.ts | 9 ++ .../bundler/plugins/virtual-files-plugin.js | 28 ++++++ dist/functions/server/server-post-build-fn.js | 2 +- dist/functions/server/watcher-esbuild-ctx.js | 15 +++- .../server/web-pages/grab-page-component.js | 64 ++----------- .../web-pages/grab-page-error-component.d.ts | 3 +- .../web-pages/grab-page-error-component.js | 49 ++++++---- .../server/web-pages/grab-page-modules.d.ts | 16 ++++ .../server/web-pages/grab-page-modules.js | 69 ++++++++++++++ dist/types/index.d.ts | 1 - dist/utils/grab-all-pages.js | 3 +- package.json | 2 +- .../all-pages-esbuild-context-bundler.ts | 1 + .../bundler/grab-client-hydration-script.tsx | 22 +++++ src/functions/server/watcher-esbuild-ctx.ts | 9 -- 25 files changed, 387 insertions(+), 167 deletions(-) create mode 100644 dist/functions/bundler/all-pages-esbuild-context-bundler-files.d.ts create mode 100644 dist/functions/bundler/all-pages-esbuild-context-bundler-files.js create mode 100644 dist/functions/bundler/plugins/esbuild-ctx-artifact-tracker.d.ts create mode 100644 dist/functions/bundler/plugins/esbuild-ctx-artifact-tracker.js create mode 100644 dist/functions/bundler/plugins/virtual-files-plugin.d.ts create mode 100644 dist/functions/bundler/plugins/virtual-files-plugin.js create mode 100644 dist/functions/server/web-pages/grab-page-modules.d.ts create mode 100644 dist/functions/server/web-pages/grab-page-modules.js diff --git a/dist/functions/bundler/all-pages-bundler.js b/dist/functions/bundler/all-pages-bundler.js index 6c79d55..4817e35 100644 --- a/dist/functions/bundler/all-pages-bundler.js +++ b/dist/functions/bundler/all-pages-bundler.js @@ -64,7 +64,6 @@ export default async function allPagesBundler(params) { if (build_starts == MAX_BUILD_STARTS) { const error_msg = `Build Failed. Please check all your components and imports.`; log.error(error_msg); - process.exit(1); } }); // build.onEnd((result) => { diff --git a/dist/functions/bundler/all-pages-esbuild-context-bundler-files.d.ts b/dist/functions/bundler/all-pages-esbuild-context-bundler-files.d.ts new file mode 100644 index 0000000..0fc436d --- /dev/null +++ b/dist/functions/bundler/all-pages-esbuild-context-bundler-files.d.ts @@ -0,0 +1,7 @@ +type Params = { + post_build_fn?: (params: { + artifacts: any[]; + }) => Promise | void; +}; +export default function allPagesESBuildContextBundlerFiles(params?: Params): Promise; +export {}; diff --git a/dist/functions/bundler/all-pages-esbuild-context-bundler-files.js b/dist/functions/bundler/all-pages-esbuild-context-bundler-files.js new file mode 100644 index 0000000..040272f --- /dev/null +++ b/dist/functions/bundler/all-pages-esbuild-context-bundler-files.js @@ -0,0 +1,58 @@ +import * as esbuild from "esbuild"; +import grabAllPages from "../../utils/grab-all-pages"; +import grabDirNames from "../../utils/grab-dir-names"; +import isDevelopment from "../../utils/is-development"; +import tailwindEsbuildPlugin from "../server/web-pages/tailwind-esbuild-plugin"; +import grabClientHydrationScript from "./grab-client-hydration-script"; +import path from "path"; +import esbuildCTXArtifactTracker from "./plugins/esbuild-ctx-artifact-tracker"; +const { HYDRATION_DST_DIR, BUNX_HYDRATION_SRC_DIR } = grabDirNames(); +export default async function allPagesESBuildContextBundlerFiles(params) { + const pages = grabAllPages({ exclude_api: true }); + global.PAGE_FILES = pages; + const dev = isDevelopment(); + const entryToPage = new Map(); + for (const page of pages) { + const tsx = await grabClientHydrationScript({ + page_local_path: page.local_path, + }); + if (!tsx) + continue; + const entryFile = path.join(BUNX_HYDRATION_SRC_DIR, `${page.url_path}.tsx`); + await Bun.write(entryFile, tsx, { createPath: true }); + entryToPage.set(entryFile, { ...page, tsx }); + } + const entryPoints = [...entryToPage.keys()]; + const ctx = await esbuild.context({ + entryPoints, + outdir: HYDRATION_DST_DIR, + bundle: true, + minify: !dev, + format: "esm", + target: "es2020", + platform: "browser", + define: { + "process.env.NODE_ENV": JSON.stringify(dev ? "development" : "production"), + }, + entryNames: "[dir]/[hash]", + metafile: true, + plugins: [ + tailwindEsbuildPlugin, + esbuildCTXArtifactTracker({ + entryToPage, + post_build_fn: params?.post_build_fn, + }), + ], + jsx: "automatic", + splitting: true, + logLevel: "silent", + external: [ + "react", + "react-dom", + "react-dom/client", + "react/jsx-runtime", + ], + }); + await ctx.rebuild(); + global.BUNDLER_CTX = ctx; +} diff --git a/dist/functions/bundler/all-pages-esbuild-context-bundler.js b/dist/functions/bundler/all-pages-esbuild-context-bundler.js index 5ba3bb8..8b86927 100644 --- a/dist/functions/bundler/all-pages-esbuild-context-bundler.js +++ b/dist/functions/bundler/all-pages-esbuild-context-bundler.js @@ -2,81 +2,30 @@ import * as esbuild from "esbuild"; import grabAllPages from "../../utils/grab-all-pages"; import grabDirNames from "../../utils/grab-dir-names"; import isDevelopment from "../../utils/is-development"; -import { log } from "../../utils/log"; import tailwindEsbuildPlugin from "../server/web-pages/tailwind-esbuild-plugin"; import grabClientHydrationScript from "./grab-client-hydration-script"; -import grabArtifactsFromBundledResults from "./grab-artifacts-from-bundled-result"; -import { writeFileSync } from "fs"; import path from "path"; -const { HYDRATION_DST_DIR, HYDRATION_DST_DIR_MAP_JSON_FILE, BUNX_HYDRATION_SRC_DIR, } = grabDirNames(); -let build_starts = 0; -const MAX_BUILD_STARTS = 10; +import virtualFilesPlugin from "./plugins/virtual-files-plugin"; +import esbuildCTXArtifactTracker from "./plugins/esbuild-ctx-artifact-tracker"; +const { HYDRATION_DST_DIR, BUNX_HYDRATION_SRC_DIR } = grabDirNames(); export default async function allPagesESBuildContextBundler(params) { + // return await allPagesESBuildContextBundlerFiles(params); const pages = grabAllPages({ exclude_api: true }); global.PAGE_FILES = pages; const dev = isDevelopment(); const entryToPage = new Map(); for (const page of pages) { - const txt = await grabClientHydrationScript({ + const tsx = await grabClientHydrationScript({ page_local_path: page.local_path, }); - if (!txt) + if (!tsx) continue; const entryFile = path.join(BUNX_HYDRATION_SRC_DIR, `${page.url_path}.tsx`); - await Bun.write(entryFile, txt, { createPath: true }); - entryToPage.set(path.resolve(entryFile), page); + // await Bun.write(entryFile, txt, { createPath: true }); + entryToPage.set(entryFile, { ...page, tsx }); } - let buildStart = 0; - const artifactTracker = { - name: "artifact-tracker", - setup(build) { - build.onStart(() => { - build_starts++; - buildStart = performance.now(); - if (build_starts == MAX_BUILD_STARTS) { - const error_msg = `Build Failed. Please check all your components and imports.`; - log.error(error_msg); - process.exit(1); - } - }); - build.onEnd((result) => { - if (result.errors.length > 0) { - for (const error of result.errors) { - const loc = error.location; - const location = loc - ? ` ${loc.file}:${loc.line}:${loc.column}` - : ""; - log.error(`[Build]${location} ${error.text}`); - } - return; - } - const artifacts = grabArtifactsFromBundledResults({ - result, - entryToPage, - }); - if (artifacts?.[0] && artifacts.length > 0) { - for (let i = 0; i < artifacts.length; i++) { - const artifact = artifacts[i]; - if (artifact?.local_path && global.BUNDLER_CTX_MAP) { - global.BUNDLER_CTX_MAP[artifact.local_path] = - artifact; - } - } - params?.post_build_fn?.({ artifacts }); - // writeFileSync( - // HYDRATION_DST_DIR_MAP_JSON_FILE, - // JSON.stringify(artifacts, null, 4), - // ); - } - const elapsed = (performance.now() - buildStart).toFixed(0); - log.success(`[Built] in ${elapsed}ms`); - global.RECOMPILING = false; - build_starts = 0; - }); - }, - }; - const entryPoints = [...entryToPage.keys()]; - const ctx = await esbuild.context({ + const entryPoints = [...entryToPage.keys()].map((e) => `hydration-virtual:${e}`); + global.BUNDLER_CTX = await esbuild.context({ entryPoints, outdir: HYDRATION_DST_DIR, bundle: true, @@ -89,10 +38,21 @@ export default async function allPagesESBuildContextBundler(params) { }, entryNames: "[dir]/[hash]", metafile: true, - plugins: [tailwindEsbuildPlugin, artifactTracker], + plugins: [ + tailwindEsbuildPlugin, + virtualFilesPlugin({ + entryToPage, + }), + esbuildCTXArtifactTracker({ + entryToPage, + post_build_fn: params?.post_build_fn, + }), + ], jsx: "automatic", splitting: true, + logLevel: "silent", // logLevel: "silent", + // logLevel: dev ? "error" : "silent", external: [ "react", "react-dom", @@ -100,9 +60,5 @@ export default async function allPagesESBuildContextBundler(params) { "react/jsx-runtime", ], }); - await ctx.rebuild(); - // if (params?.watch) { - // await ctx.watch(); - // } - global.BUNDLER_CTX = ctx; + await global.BUNDLER_CTX.rebuild(); } diff --git a/dist/functions/bundler/grab-artifacts-from-bundled-result.d.ts b/dist/functions/bundler/grab-artifacts-from-bundled-result.d.ts index fbc1b73..243e0d5 100644 --- a/dist/functions/bundler/grab-artifacts-from-bundled-result.d.ts +++ b/dist/functions/bundler/grab-artifacts-from-bundled-result.d.ts @@ -2,7 +2,9 @@ import * as esbuild from "esbuild"; import type { BundlerCTXMap, PageFiles } from "../../types"; type Params = { result: esbuild.BuildResult; - entryToPage: Map; + entryToPage: Map; }; export default function grabArtifactsFromBundledResults({ result, entryToPage, }: Params): BundlerCTXMap[] | undefined; export {}; diff --git a/dist/functions/bundler/grab-artifacts-from-bundled-result.js b/dist/functions/bundler/grab-artifacts-from-bundled-result.js index f5d7171..278dfa1 100644 --- a/dist/functions/bundler/grab-artifacts-from-bundled-result.js +++ b/dist/functions/bundler/grab-artifacts-from-bundled-result.js @@ -1,6 +1,7 @@ import path from "path"; import * as esbuild from "esbuild"; import grabDirNames from "../../utils/grab-dir-names"; +import { log } from "../../utils/log"; const { ROOT_DIR } = grabDirNames(); export default function grabArtifactsFromBundledResults({ result, entryToPage, }) { if (result.errors.length > 0) @@ -8,24 +9,29 @@ export default function grabArtifactsFromBundledResults({ result, entryToPage, } const artifacts = Object.entries(result.metafile.outputs) .filter(([, meta]) => meta.entryPoint) .map(([outputPath, meta]) => { - const entrypoint = path.join(ROOT_DIR, meta.entryPoint || ""); + const entrypoint = meta.entryPoint?.match(/^hydration-virtual:/) + ? meta.entryPoint?.replace(/^hydration-virtual:/, "") + : meta.entryPoint + ? path.join(ROOT_DIR, meta.entryPoint) + : ""; + // const entrypoint = path.join(ROOT_DIR, meta.entryPoint || ""); + // console.log("entrypoint", entrypoint); const target_page = entryToPage.get(entrypoint); if (!target_page || !meta.entryPoint) { return undefined; } - const { file_name, local_path, url_path, transformed_path } = target_page; + const { file_name, local_path, url_path } = target_page; return { path: outputPath, hash: path.basename(outputPath, path.extname(outputPath)), type: outputPath.endsWith(".css") ? "text/css" : "text/javascript", - entrypoint, + entrypoint: meta.entryPoint, css_path: meta.cssBundle, file_name, local_path, url_path, - transformed_path, }; }); if (artifacts.length > 0) { diff --git a/dist/functions/bundler/grab-client-hydration-script.d.ts b/dist/functions/bundler/grab-client-hydration-script.d.ts index 321be36..ef27964 100644 --- a/dist/functions/bundler/grab-client-hydration-script.d.ts +++ b/dist/functions/bundler/grab-client-hydration-script.d.ts @@ -1,5 +1,5 @@ type Params = { page_local_path: string; }; -export default function grabClientHydrationScript({ page_local_path, }: Params): Promise; +export default function grabClientHydrationScript({ page_local_path, }: Params): Promise; export {}; diff --git a/dist/functions/bundler/grab-client-hydration-script.js b/dist/functions/bundler/grab-client-hydration-script.js index a7cc607..11b1f06 100644 --- a/dist/functions/bundler/grab-client-hydration-script.js +++ b/dist/functions/bundler/grab-client-hydration-script.js @@ -13,6 +13,22 @@ export default async function grabClientHydrationScript({ page_local_path, }) { // const target_root_path = root_file_path // ? pagePathTransform({ page_path: root_file_path }) // : undefined; + if (!existsSync(page_local_path)) { + return undefined; + } + if (root_file_path) { + if (!existsSync(root_file_path)) { + return undefined; + } + const root_content = await Bun.file(root_file_path).text(); + if (!root_content.match(/^export default/m)) { + return undefined; + } + } + const page_content = await Bun.file(page_local_path).text(); + if (!page_content.match(/^export default/m)) { + return undefined; + } let txt = ``; txt += `import { hydrateRoot } from "react-dom/client";\n`; if (root_file_path) { diff --git a/dist/functions/bundler/plugins/esbuild-ctx-artifact-tracker.d.ts b/dist/functions/bundler/plugins/esbuild-ctx-artifact-tracker.d.ts new file mode 100644 index 0000000..420ef05 --- /dev/null +++ b/dist/functions/bundler/plugins/esbuild-ctx-artifact-tracker.d.ts @@ -0,0 +1,12 @@ +import type { Plugin } from "esbuild"; +import type { PageFiles } from "../../../types"; +type Params = { + entryToPage: Map; + post_build_fn?: (params: { + artifacts: any[]; + }) => Promise | void; +}; +export default function esbuildCTXArtifactTracker({ entryToPage, post_build_fn, }: Params): Plugin; +export {}; diff --git a/dist/functions/bundler/plugins/esbuild-ctx-artifact-tracker.js b/dist/functions/bundler/plugins/esbuild-ctx-artifact-tracker.js new file mode 100644 index 0000000..ca9b161 --- /dev/null +++ b/dist/functions/bundler/plugins/esbuild-ctx-artifact-tracker.js @@ -0,0 +1,57 @@ +import { log } from "../../../utils/log"; +import grabArtifactsFromBundledResults from "../grab-artifacts-from-bundled-result"; +let buildStart = 0; +let build_starts = 0; +const MAX_BUILD_STARTS = 10; +export default function esbuildCTXArtifactTracker({ entryToPage, post_build_fn, }) { + const artifactTracker = { + name: "artifact-tracker", + setup(build) { + build.onStart(() => { + build_starts++; + buildStart = performance.now(); + if (build_starts == MAX_BUILD_STARTS) { + const error_msg = `Build Failed. Please check all your components and imports.`; + log.error(error_msg); + global.RECOMPILING = false; + } + }); + build.onEnd((result) => { + if (result.errors.length > 0) { + // for (const error of result.errors) { + // const loc = error.location; + // const location = loc + // ? ` ${loc.file}:${loc.line}:${loc.column}` + // : ""; + // log.error(`[Build]${location} ${error.text}`); + // } + return; + } + const artifacts = grabArtifactsFromBundledResults({ + result, + entryToPage, + }); + // console.log("artifacts", artifacts); + if (artifacts?.[0] && artifacts.length > 0) { + for (let i = 0; i < artifacts.length; i++) { + const artifact = artifacts[i]; + if (artifact?.local_path && global.BUNDLER_CTX_MAP) { + global.BUNDLER_CTX_MAP[artifact.local_path] = + artifact; + } + } + post_build_fn?.({ artifacts }); + // writeFileSync( + // HYDRATION_DST_DIR_MAP_JSON_FILE, + // JSON.stringify(artifacts, null, 4), + // ); + } + const elapsed = (performance.now() - buildStart).toFixed(0); + log.success(`[Built] in ${elapsed}ms`); + global.RECOMPILING = false; + build_starts = 0; + }); + }, + }; + return artifactTracker; +} diff --git a/dist/functions/bundler/plugins/virtual-files-plugin.d.ts b/dist/functions/bundler/plugins/virtual-files-plugin.d.ts new file mode 100644 index 0000000..9e884d2 --- /dev/null +++ b/dist/functions/bundler/plugins/virtual-files-plugin.d.ts @@ -0,0 +1,9 @@ +import type { Plugin } from "esbuild"; +import type { PageFiles } from "../../../types"; +type Params = { + entryToPage: Map; +}; +export default function virtualFilesPlugin({ entryToPage }: Params): Plugin; +export {}; diff --git a/dist/functions/bundler/plugins/virtual-files-plugin.js b/dist/functions/bundler/plugins/virtual-files-plugin.js new file mode 100644 index 0000000..bea6ddc --- /dev/null +++ b/dist/functions/bundler/plugins/virtual-files-plugin.js @@ -0,0 +1,28 @@ +import path from "path"; +import { log } from "../../../utils/log"; +export default function virtualFilesPlugin({ entryToPage }) { + const virtualPlugin = { + name: "virtual-hydration", + setup(build) { + build.onResolve({ filter: /^hydration-virtual:/ }, (args) => { + const final_path = args.path.replace(/hydration-virtual:/, ""); + return { + path: final_path, + namespace: "hydration-virtual", + }; + }); + build.onLoad({ filter: /.*/, namespace: "hydration-virtual" }, (args) => { + const target = entryToPage.get(args.path); + if (!target?.tsx) + return null; + const contents = target.tsx; + return { + contents: contents || "", + loader: "tsx", + resolveDir: path.dirname(target.local_path), + }; + }); + }, + }; + return virtualPlugin; +} diff --git a/dist/functions/server/server-post-build-fn.js b/dist/functions/server/server-post-build-fn.js index 23eb074..14196cf 100644 --- a/dist/functions/server/server-post-build-fn.js +++ b/dist/functions/server/server-post-build-fn.js @@ -9,7 +9,7 @@ export default async function serverPostBuildFn() { } for (let i = global.HMR_CONTROLLERS.length - 1; i >= 0; i--) { const controller = global.HMR_CONTROLLERS[i]; - if (!controller.target_map?.local_path) { + if (!controller?.target_map?.local_path) { continue; } const target_artifact = global.BUNDLER_CTX_MAP[controller.target_map.local_path]; diff --git a/dist/functions/server/watcher-esbuild-ctx.js b/dist/functions/server/watcher-esbuild-ctx.js index 8e17075..1e6b40f 100644 --- a/dist/functions/server/watcher-esbuild-ctx.js +++ b/dist/functions/server/watcher-esbuild-ctx.js @@ -13,6 +13,9 @@ export default async function watcherEsbuildCTX() { }, async (event, filename) => { if (!filename) return; + if (filename.match(/^\.\w+/)) { + return; + } const full_file_path = path.join(ROOT_DIR, filename); if (full_file_path.match(/\/styles$/)) { global.RECOMPILING = true; @@ -38,6 +41,12 @@ export default async function watcherEsbuildCTX() { return; global.RECOMPILING = true; await global.BUNDLER_CTX?.rebuild(); + if (filename.match(/(404|500)\.tsx?/)) { + for (let i = global.HMR_CONTROLLERS.length - 1; i >= 0; i--) { + const controller = global.HMR_CONTROLLERS[i]; + controller?.controller?.enqueue(`event: update\ndata: ${JSON.stringify({ reload: true })}\n\n`); + } + } } return; } @@ -69,16 +78,14 @@ async function fullRebuild(params) { global.ROUTER.reload(); await global.BUNDLER_CTX?.dispose(); global.BUNDLER_CTX = undefined; - await allPagesESBuildContextBundler({ + global.BUNDLER_CTX_MAP = {}; + allPagesESBuildContextBundler({ post_build_fn: serverPostBuildFn, }); } catch (error) { log.error(error); } - finally { - global.RECOMPILING = false; - } if (global.PAGES_SRC_WATCHER) { global.PAGES_SRC_WATCHER.close(); watcherEsbuildCTX(); diff --git a/dist/functions/server/web-pages/grab-page-component.js b/dist/functions/server/web-pages/grab-page-component.js index 0e80471..58d0173 100644 --- a/dist/functions/server/web-pages/grab-page-component.js +++ b/dist/functions/server/web-pages/grab-page-component.js @@ -6,12 +6,12 @@ import { log } from "../../../utils/log"; import grabRootFilePath from "./grab-root-file-path"; import grabPageServerRes from "./grab-page-server-res"; import grabPageServerPath from "./grab-page-server-path"; +import grabPageModules from "./grab-page-modules"; class NotFoundError extends Error { } export default async function grabPageComponent({ req, file_path: passed_file_path, debug, }) { const url = req?.url ? new URL(req.url) : undefined; const router = global.ROUTER; - const now = Date.now(); let routeParams = undefined; try { routeParams = req ? await grabRouteParams({ req }) : undefined; @@ -45,63 +45,16 @@ export default async function grabPageComponent({ req, file_path: passed_file_pa if (debug) { log.info(`bundledMap:`, bundledMap); } - const { root_file_path } = grabRootFilePath(); - const root_module = root_file_path - ? await import(`${root_file_path}?t=${now}`) - : undefined; - const { server_file_path: root_server_file_path } = root_file_path - ? grabPageServerPath({ file_path: root_file_path }) - : {}; - const root_server_module = root_server_file_path - ? await import(`${root_server_file_path}?t=${now}`) - : undefined; - const root_server_fn = root_server_module?.default || root_server_module?.server; - const rootServerRes = root_server_fn - ? await grabPageServerRes({ - server_function: root_server_fn, - url, - query: match?.query, - routeParams, - }) - : undefined; - if (debug) { - log.info(`rootServerRes:`, rootServerRes); - } - const module = await import(`${file_path}?t=${now}`); - const { server_file_path } = grabPageServerPath({ file_path }); - const server_module = server_file_path - ? await import(`${server_file_path}?t=${now}`) - : undefined; - if (debug) { - log.info(`module:`, module); - } - const server_fn = server_module?.default || server_module?.server; - const serverRes = server_fn - ? await grabPageServerRes({ - server_function: server_fn, - url, - query: match?.query, - routeParams, - }) - : undefined; - if (debug) { - log.info(`serverRes:`, serverRes); - } - const mergedServerRes = _.merge(rootServerRes || {}, serverRes || {}); - const { component } = (await grabPageBundledReactComponent({ + const { component, module, serverRes, root_module } = await grabPageModules({ file_path, - root_file_path, - server_res: mergedServerRes, - })) || {}; - if (!component) { - throw new Error(`Couldn't grab page component`); - } - if (debug) { - log.info(`component:`, component); - } + debug, + query: match?.query, + routeParams, + url, + }); return { component, - serverRes: mergedServerRes, + serverRes, routeParams, module, bundledMap, @@ -114,6 +67,7 @@ export default async function grabPageComponent({ req, file_path: passed_file_pa error, routeParams, is404: error instanceof NotFoundError, + url, }); } } diff --git a/dist/functions/server/web-pages/grab-page-error-component.d.ts b/dist/functions/server/web-pages/grab-page-error-component.d.ts index ac44258..26083a6 100644 --- a/dist/functions/server/web-pages/grab-page-error-component.d.ts +++ b/dist/functions/server/web-pages/grab-page-error-component.d.ts @@ -3,6 +3,7 @@ type Params = { error?: any; routeParams?: BunxRouteParams; is404?: boolean; + url?: URL; }; -export default function grabPageErrorComponent({ error, routeParams, is404, }: Params): Promise; +export default function grabPageErrorComponent({ error, routeParams, is404, url, }: Params): Promise; export {}; diff --git a/dist/functions/server/web-pages/grab-page-error-component.js b/dist/functions/server/web-pages/grab-page-error-component.js index e9b5abf..0d9147c 100644 --- a/dist/functions/server/web-pages/grab-page-error-component.js +++ b/dist/functions/server/web-pages/grab-page-error-component.js @@ -1,31 +1,47 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime"; import grabDirNames from "../../../utils/grab-dir-names"; -export default async function grabPageErrorComponent({ error, routeParams, is404, }) { +import grabPageModules from "./grab-page-modules"; +import _ from "lodash"; +export default async function grabPageErrorComponent({ error, routeParams, is404, url, }) { const router = global.ROUTER; const { BUNX_ROOT_500_PRESET_COMPONENT, BUNX_ROOT_404_PRESET_COMPONENT } = grabDirNames(); const errorRoute = is404 ? "/404" : "/500"; const presetComponent = is404 ? BUNX_ROOT_404_PRESET_COMPONENT : BUNX_ROOT_500_PRESET_COMPONENT; + const default_server_res = { + responseOptions: { + status: is404 ? 404 : 500, + }, + }; try { const match = router.match(errorRoute); - const filePath = match?.filePath || presetComponent; - const bundledMap = match?.filePath - ? global.BUNDLER_CTX_MAP?.[match.filePath] - : undefined; - const module = await import(filePath); - const Component = module.default; - const component = _jsx(Component, { children: _jsx("span", { children: error.message }) }); + if (!match?.filePath) { + const default_module = await import(presetComponent); + const Component = default_module.default; + const default_jsx = (_jsx(Component, { children: _jsx("span", { children: error.message }) })); + return { + component: default_jsx, + module: default_module, + routeParams, + serverRes: default_server_res, + }; + } + const file_path = match.filePath; + const bundledMap = global.BUNDLER_CTX_MAP?.[file_path]; + const { component, module, serverRes, root_module } = await grabPageModules({ + file_path: file_path, + query: match?.query, + routeParams, + url, + }); return { component, routeParams, module, bundledMap, - serverRes: { - responseOptions: { - status: is404 ? 404 : 500, - }, - }, + serverRes: _.merge(serverRes, default_server_res), + root_module, }; } catch { @@ -41,12 +57,7 @@ export default async function grabPageErrorComponent({ error, routeParams, is404 component: _jsx(DefaultNotFound, {}), routeParams, module: { default: DefaultNotFound }, - bundledMap: undefined, - serverRes: { - responseOptions: { - status: is404 ? 404 : 500, - }, - }, + serverRes: default_server_res, }; } } diff --git a/dist/functions/server/web-pages/grab-page-modules.d.ts b/dist/functions/server/web-pages/grab-page-modules.d.ts new file mode 100644 index 0000000..08164df --- /dev/null +++ b/dist/functions/server/web-pages/grab-page-modules.d.ts @@ -0,0 +1,16 @@ +import type { BunextPageModule, BunextPageModuleServerReturn, BunxRouteParams } from "../../../types"; +import type { JSX } from "react"; +type Params = { + file_path: string; + debug?: boolean; + url?: URL; + query?: any; + routeParams?: BunxRouteParams; +}; +export default function grabPageModules({ file_path, debug, url, query, routeParams, }: Params): Promise<{ + component: JSX.Element; + serverRes: BunextPageModuleServerReturn; + module: BunextPageModule; + root_module: BunextPageModule | undefined; +}>; +export {}; diff --git a/dist/functions/server/web-pages/grab-page-modules.js b/dist/functions/server/web-pages/grab-page-modules.js new file mode 100644 index 0000000..0ff6574 --- /dev/null +++ b/dist/functions/server/web-pages/grab-page-modules.js @@ -0,0 +1,69 @@ +import grabPageBundledReactComponent from "./grab-page-bundled-react-component"; +import _ from "lodash"; +import { log } from "../../../utils/log"; +import grabRootFilePath from "./grab-root-file-path"; +import grabPageServerRes from "./grab-page-server-res"; +import grabPageServerPath from "./grab-page-server-path"; +export default async function grabPageModules({ file_path, debug, url, query, routeParams, }) { + const now = Date.now(); + const { root_file_path } = grabRootFilePath(); + const root_module = root_file_path + ? await import(`${root_file_path}?t=${now}`) + : undefined; + const { server_file_path: root_server_file_path } = root_file_path + ? grabPageServerPath({ file_path: root_file_path }) + : {}; + const root_server_module = root_server_file_path + ? await import(`${root_server_file_path}?t=${now}`) + : undefined; + const root_server_fn = root_server_module?.default || root_server_module?.server; + const rootServerRes = root_server_fn + ? await grabPageServerRes({ + server_function: root_server_fn, + url, + query, + routeParams, + }) + : undefined; + if (debug) { + log.info(`rootServerRes:`, rootServerRes); + } + const module = await import(`${file_path}?t=${now}`); + const { server_file_path } = grabPageServerPath({ file_path }); + const server_module = server_file_path + ? await import(`${server_file_path}?t=${now}`) + : undefined; + if (debug) { + log.info(`module:`, module); + } + const server_fn = server_module?.default || server_module?.server; + const serverRes = server_fn + ? await grabPageServerRes({ + server_function: server_fn, + url, + query, + routeParams, + }) + : undefined; + if (debug) { + log.info(`serverRes:`, serverRes); + } + const mergedServerRes = _.merge(rootServerRes || {}, serverRes || {}); + const { component } = (await grabPageBundledReactComponent({ + file_path, + root_file_path, + server_res: mergedServerRes, + })) || {}; + if (!component) { + throw new Error(`Couldn't grab page component`); + } + if (debug) { + log.info(`component:`, component); + } + return { + component, + serverRes: mergedServerRes, + module, + root_module, + }; +} diff --git a/dist/types/index.d.ts b/dist/types/index.d.ts index 829b500..c28618a 100644 --- a/dist/types/index.d.ts +++ b/dist/types/index.d.ts @@ -250,7 +250,6 @@ export type GrabPageReactBundledComponentRes = { }; export type PageFiles = { local_path: string; - transformed_path: string; url_path: string; file_name: string; }; diff --git a/dist/utils/grab-all-pages.js b/dist/utils/grab-all-pages.js index 0d546af..50491d5 100644 --- a/dist/utils/grab-all-pages.js +++ b/dist/utils/grab-all-pages.js @@ -67,10 +67,9 @@ function grabPageFileObject({ file_path, }) { let file_name = url_path.split("/").pop(); if (!file_name) return; - const transformed_path = pagePathTransform({ page_path: file_path }); + // const transformed_path = pagePathTransform({ page_path: file_path }); return { local_path: file_path, - transformed_path, url_path, file_name, }; diff --git a/package.json b/package.json index cd2d44e..615537a 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "@moduletrace/bunext", "module": "index.ts", "type": "module", - "version": "1.0.32", + "version": "1.0.33", "main": "dist/index.js", "types": "dist/index.d.ts", "exports": { diff --git a/src/functions/bundler/all-pages-esbuild-context-bundler.ts b/src/functions/bundler/all-pages-esbuild-context-bundler.ts index 2690337..81f0d8b 100644 --- a/src/functions/bundler/all-pages-esbuild-context-bundler.ts +++ b/src/functions/bundler/all-pages-esbuild-context-bundler.ts @@ -30,6 +30,7 @@ export default async function allPagesESBuildContextBundler(params?: Params) { const tsx = await grabClientHydrationScript({ page_local_path: page.local_path, }); + if (!tsx) continue; const entryFile = path.join( diff --git a/src/functions/bundler/grab-client-hydration-script.tsx b/src/functions/bundler/grab-client-hydration-script.tsx index 22f254b..d896add 100644 --- a/src/functions/bundler/grab-client-hydration-script.tsx +++ b/src/functions/bundler/grab-client-hydration-script.tsx @@ -28,6 +28,28 @@ export default async function grabClientHydrationScript({ // ? pagePathTransform({ page_path: root_file_path }) // : undefined; + if (!existsSync(page_local_path)) { + return undefined; + } + + if (root_file_path) { + if (!existsSync(root_file_path)) { + return undefined; + } + + const root_content = await Bun.file(root_file_path).text(); + + if (!root_content.match(/^export default/m)) { + return undefined; + } + } + + const page_content = await Bun.file(page_local_path).text(); + + if (!page_content.match(/^export default/m)) { + return undefined; + } + let txt = ``; txt += `import { hydrateRoot } from "react-dom/client";\n`; diff --git a/src/functions/server/watcher-esbuild-ctx.ts b/src/functions/server/watcher-esbuild-ctx.ts index 27eddee..3593e3c 100644 --- a/src/functions/server/watcher-esbuild-ctx.ts +++ b/src/functions/server/watcher-esbuild-ctx.ts @@ -52,8 +52,6 @@ export default async function watcherEsbuildCTX() { if (global.RECOMPILING) return; global.RECOMPILING = true; - log.info(`Rebuilding CTX ...`); - await global.BUNDLER_CTX?.rebuild(); if (filename.match(/(404|500)\.tsx?/)) { @@ -107,28 +105,21 @@ async function fullRebuild(params?: { msg?: string }) { log.watch(msg); } - log.info(`Reloading Router ...`); - global.ROUTER.reload(); - log.info(`Disposing Bundler CTX ...`); await global.BUNDLER_CTX?.dispose(); global.BUNDLER_CTX = undefined; global.BUNDLER_CTX_MAP = {}; - log.info(`Rebuilding Modules ...`); allPagesESBuildContextBundler({ post_build_fn: serverPostBuildFn, }); } catch (error: any) { log.error(error); - } finally { - global.RECOMPILING = false; } if (global.PAGES_SRC_WATCHER) { - log.info(`Restarting watcher ...`); global.PAGES_SRC_WATCHER.close(); watcherEsbuildCTX(); }