diff --git a/dist/functions/server/web-pages/grab-page-bundled-react-component.js b/dist/functions/server/web-pages/grab-page-bundled-react-component.js index a70f66e..e9d2cb2 100644 --- a/dist/functions/server/web-pages/grab-page-bundled-react-component.js +++ b/dist/functions/server/web-pages/grab-page-bundled-react-component.js @@ -14,7 +14,11 @@ export default async function grabPageBundledReactComponent({ file_path, return_ // Loader.registry.delete(abs); const mod = await import(`${abs}?t=${Date.now()}`); const Main = mod.default; - return { component: Main }; + return { + component: Main, + page_module: mod.__bunext_page, + root_module: mod.__bunext_root, + }; } const { root_file_path } = grabRootFilePath(); let tsx = grabPageReactComponentString({ @@ -35,6 +39,8 @@ export default async function grabPageBundledReactComponent({ file_path, return_ return { component: Main, tsx, + page_module: mod.__bunext_page, + root_module: mod.__bunext_root, }; } catch (error) { diff --git a/dist/functions/server/web-pages/grab-page-modules.js b/dist/functions/server/web-pages/grab-page-modules.js index 00ac734..ee626a5 100644 --- a/dist/functions/server/web-pages/grab-page-modules.js +++ b/dist/functions/server/web-pages/grab-page-modules.js @@ -19,20 +19,25 @@ export default async function grabPageModules({ file_path, debug, url, query, ro ? 301 : serverRes.redirect.status_code || 302); } - const { root_file_path } = grabRootFilePath(); - const root_module = root_file_path - ? await import(`${root_file_path}?t=${now}`) - : undefined; - const module = await import(`${file_path}?t=${now}`); - if (debug) { - log.info(`module:`, module); - } - const { component } = (await grabPageBundledReactComponent({ + const { component, page_module, root_module: bundled_root_module, } = (await grabPageBundledReactComponent({ file_path, })) || {}; if (!component) { throw new Error(`Couldn't grab page component`); } + // Prefer the page/root exports re-exported by the SSR bundle. Importing + // the raw source with Bun's loader only cache-busts the page file itself: + // its transitive imports stay in Bun's module registry, so a component + // that failed to resolve once (e.g. imported before it was created) + // keeps failing until the server restarts. + const { root_file_path } = grabRootFilePath(); + const root_module = !root_file_path + ? undefined + : bundled_root_module || (await import(`${root_file_path}?t=${now}`)); + const module = page_module || (await import(`${file_path}?t=${now}`)); + if (debug) { + log.info(`module:`, module); + } if (debug) { log.info(`component:`, component); } diff --git a/dist/functions/server/web-pages/grab-page-react-component-string.js b/dist/functions/server/web-pages/grab-page-react-component-string.js index 28b88e9..41460b3 100644 --- a/dist/functions/server/web-pages/grab-page-react-component-string.js +++ b/dist/functions/server/web-pages/grab-page-react-component-string.js @@ -16,6 +16,12 @@ export default function grabPageReactComponentString({ file_path, root_file_path } tsx += ` )\n`; tsx += `}\n`; + // Expose the page/root's own exports (meta, Head, config, ...) through + // the bundle so the server never has to import the raw source files. + tsx += `export * as __bunext_page from "${file_path}"\n`; + if (root_file_path) { + tsx += `export * as __bunext_root from "${root_file_path}"\n`; + } return tsx; } catch (error) { diff --git a/dist/types/index.d.ts b/dist/types/index.d.ts index b7d7b30..f6fca71 100644 --- a/dist/types/index.d.ts +++ b/dist/types/index.d.ts @@ -318,6 +318,12 @@ export type GrabPageReactBundledComponentRes = { component?: FC; server_res?: BunextPageModuleServerReturn; tsx?: string; + /** + * The page's own exports (`meta`, `Head`, `config`, ...), re-exported + * from the SSR bundle so they share its fresh module graph. + */ + page_module?: BunextPageModule; + root_module?: BunextPageModule; }; export type PageFiles = { local_path: string; diff --git a/package.json b/package.json index e8f3d92..707bf87 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@moduletrace/bunext", - "version": "1.1.18", + "version": "1.1.19", "main": "dist/index.js", "module": "index.ts", "dependencies": { diff --git a/src/functions/server/web-pages/grab-page-bundled-react-component.tsx b/src/functions/server/web-pages/grab-page-bundled-react-component.tsx index e391ae7..c1fc3b8 100644 --- a/src/functions/server/web-pages/grab-page-bundled-react-component.tsx +++ b/src/functions/server/web-pages/grab-page-bundled-react-component.tsx @@ -32,7 +32,11 @@ export default async function grabPageBundledReactComponent({ const Main = mod.default as FC; - return { component: Main }; + return { + component: Main, + page_module: mod.__bunext_page, + root_module: mod.__bunext_root, + }; } const { root_file_path } = grabRootFilePath(); @@ -60,6 +64,8 @@ export default async function grabPageBundledReactComponent({ return { component: Main, tsx, + page_module: mod.__bunext_page, + root_module: mod.__bunext_root, }; } catch (error: any) { log.error( diff --git a/src/functions/server/web-pages/grab-page-modules.tsx b/src/functions/server/web-pages/grab-page-modules.tsx index 1474030..fe1e8f5 100644 --- a/src/functions/server/web-pages/grab-page-modules.tsx +++ b/src/functions/server/web-pages/grab-page-modules.tsx @@ -56,18 +56,11 @@ export default async function grabPageModules({ ); } - const { root_file_path } = grabRootFilePath(); - const root_module: BunextRootModule | undefined = root_file_path - ? await import(`${root_file_path}?t=${now}`) - : undefined; - - const module: BunextPageModule = await import(`${file_path}?t=${now}`); - - if (debug) { - log.info(`module:`, module); - } - - const { component } = + const { + component, + page_module, + root_module: bundled_root_module, + } = (await grabPageBundledReactComponent({ file_path, })) || {}; @@ -76,6 +69,23 @@ export default async function grabPageModules({ throw new Error(`Couldn't grab page component`); } + // Prefer the page/root exports re-exported by the SSR bundle. Importing + // the raw source with Bun's loader only cache-busts the page file itself: + // its transitive imports stay in Bun's module registry, so a component + // that failed to resolve once (e.g. imported before it was created) + // keeps failing until the server restarts. + const { root_file_path } = grabRootFilePath(); + const root_module: BunextRootModule | undefined = !root_file_path + ? undefined + : bundled_root_module || (await import(`${root_file_path}?t=${now}`)); + + const module: BunextPageModule = + page_module || (await import(`${file_path}?t=${now}`)); + + if (debug) { + log.info(`module:`, module); + } + if (debug) { log.info(`component:`, component); } diff --git a/src/functions/server/web-pages/grab-page-react-component-string.tsx b/src/functions/server/web-pages/grab-page-react-component-string.tsx index 88b1ce2..f887881 100644 --- a/src/functions/server/web-pages/grab-page-react-component-string.tsx +++ b/src/functions/server/web-pages/grab-page-react-component-string.tsx @@ -27,6 +27,13 @@ export default function grabPageReactComponentString({ tsx += ` )\n`; tsx += `}\n`; + // Expose the page/root's own exports (meta, Head, config, ...) through + // the bundle so the server never has to import the raw source files. + tsx += `export * as __bunext_page from "${file_path}"\n`; + if (root_file_path) { + tsx += `export * as __bunext_root from "${root_file_path}"\n`; + } + return tsx; } catch (error: any) { log.error(`grabPageReactComponentString Error: ${error.message}`); diff --git a/src/types/index.ts b/src/types/index.ts index 9dcc6ae..2ffffbe 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -354,6 +354,12 @@ export type GrabPageReactBundledComponentRes = { component?: FC; server_res?: BunextPageModuleServerReturn; tsx?: string; + /** + * The page's own exports (`meta`, `Head`, `config`, ...), re-exported + * from the SSR bundle so they share its fresh module graph. + */ + page_module?: BunextPageModule; + root_module?: BunextPageModule; }; export type PageFiles = {