Fix Bundler Errors

This commit is contained in:
Benjamin Toby 2026-03-17 18:09:30 +01:00
parent 05154bb758
commit 9e94e20198
2 changed files with 27 additions and 81 deletions

View File

@ -11,7 +11,6 @@ import isDevelopment from "../../utils/is-development";
import type { BundlerCTXMap } from "../../types"; import type { BundlerCTXMap } from "../../types";
import { execSync } from "child_process"; import { execSync } from "child_process";
import grabConstants from "../../utils/grab-constants"; import grabConstants from "../../utils/grab-constants";
import rebuildBundler from "../server/rebuild-bundler";
const { HYDRATION_DST_DIR, PAGES_DIR, HYDRATION_DST_DIR_MAP_JSON_FILE } = const { HYDRATION_DST_DIR, PAGES_DIR, HYDRATION_DST_DIR_MAP_JSON_FILE } =
grabDirNames(); grabDirNames();
@ -41,15 +40,9 @@ type Params = {
export default async function allPagesBundler(params?: Params) { export default async function allPagesBundler(params?: Params) {
const pages = grabAllPages({ exclude_api: true }); const pages = grabAllPages({ exclude_api: true });
const { const { ClientRootElementIDName, ClientRootComponentWindowName } =
ClientRootElementIDName, await grabConstants();
ClientRootComponentWindowName,
MaxBundlerRebuilds,
} = await grabConstants();
// Use index-based keys so bracket paths (e.g. [[...catch_all]]) never
// appear in any path that esbuild's binary tracks internally. The actual
// file path is only ever used inside our JS plugin callbacks.
const virtualEntries: Record<string, string> = {}; const virtualEntries: Record<string, string> = {};
const dev = isDevelopment(); const dev = isDevelopment();
@ -60,16 +53,15 @@ export default async function allPagesBundler(params?: Params) {
const does_root_exist = existsSync(root_component_path); const does_root_exist = existsSync(root_component_path);
for (let i = 0; i < pages.length; i++) { for (const page of pages) {
const page = pages[i]; const key = page.local_path;
const virtualKey = `page-${i}`;
let txt = ``; let txt = ``;
txt += `import { hydrateRoot } from "react-dom/client";\n`; txt += `import { hydrateRoot } from "react-dom/client";\n`;
if (does_root_exist) { if (does_root_exist) {
txt += `import Root from "${root_component_path}";\n`; txt += `import Root from "${root_component_path}";\n`;
} }
txt += `import Page from "page-entry:${i}";\n\n`; txt += `import Page from "${page.local_path}";\n\n`;
txt += `const pageProps = window.__PAGE_PROPS__ || {};\n`; txt += `const pageProps = window.__PAGE_PROPS__ || {};\n`;
if (does_root_exist) { if (does_root_exist) {
@ -80,7 +72,7 @@ export default async function allPagesBundler(params?: Params) {
txt += `const root = hydrateRoot(document.getElementById("${ClientRootElementIDName}"), component);\n\n`; txt += `const root = hydrateRoot(document.getElementById("${ClientRootElementIDName}"), component);\n\n`;
txt += `window.${ClientRootComponentWindowName} = root;\n`; txt += `window.${ClientRootComponentWindowName} = root;\n`;
virtualEntries[virtualKey] = txt; virtualEntries[key] = txt;
} }
const virtualPlugin: esbuild.Plugin = { const virtualPlugin: esbuild.Plugin = {
@ -91,33 +83,11 @@ export default async function allPagesBundler(params?: Params) {
namespace: "virtual", namespace: "virtual",
})); }));
build.onResolve({ filter: /^page-entry:/ }, (args) => ({
path: args.path.replace("page-entry:", ""),
namespace: "page-entry",
}));
build.onLoad({ filter: /.*/, namespace: "virtual" }, (args) => ({ build.onLoad({ filter: /.*/, namespace: "virtual" }, (args) => ({
contents: virtualEntries[args.path], contents: virtualEntries[args.path],
loader: "tsx", loader: "tsx",
resolveDir: process.cwd(), resolveDir: process.cwd(),
})); }));
build.onLoad(
{ filter: /.*/, namespace: "page-entry" },
async (args) => {
const page = pages[parseInt(args.path)];
try {
return {
contents: await readFile(page.local_path, "utf-8"),
loader: "tsx" as const,
resolveDir: path.dirname(page.local_path),
watchFiles: [page.local_path],
};
} catch (e: any) {
return { errors: [{ text: e.message }] };
}
},
);
}, },
}; };
@ -128,38 +98,19 @@ export default async function allPagesBundler(params?: Params) {
console.time("build"); console.time("build");
}); });
build.onEnd(async (result) => { build.onEnd((result) => {
if (result.errors.length > 0) { if (result.errors.length > 0) return;
const messages = await esbuild.formatMessages(
result.errors,
{ kind: "error", color: true },
);
for (const msg of messages) {
process.stderr.write(msg);
}
global.BUNDLER_REBUILDS++;
if (global.BUNDLER_REBUILDS > MaxBundlerRebuilds) {
console.error(`Max Rebuilds all failed.`);
process.exit(1);
}
await rebuildBundler();
console.timeEnd("build");
return;
}
const artifacts: (BundlerCTXMap | undefined)[] = Object.entries( const artifacts: (BundlerCTXMap | undefined)[] = Object.entries(
result.metafile!.outputs, result.metafile!.outputs,
) )
.filter(([, meta]) => meta.entryPoint) .filter(([, meta]) => meta.entryPoint)
.map(([outputPath, meta]) => { .map(([outputPath, meta]) => {
const indexMatch = const target_page = pages.find((p) => {
meta.entryPoint?.match(/^virtual:page-(\d+)$/); return (
const target_page = indexMatch meta.entryPoint === `virtual:${p.local_path}`
? pages[parseInt(indexMatch[1])] );
: undefined; });
if (!target_page || !meta.entryPoint) { if (!target_page || !meta.entryPoint) {
return undefined; return undefined;
@ -196,7 +147,6 @@ export default async function allPagesBundler(params?: Params) {
// ); // );
global.BUNDLER_CTX_MAP = final_artifacts; global.BUNDLER_CTX_MAP = final_artifacts;
global.BUNDLER_REBUILDS = 0;
params?.post_build_fn?.({ artifacts: final_artifacts }); params?.post_build_fn?.({ artifacts: final_artifacts });
} }
@ -221,7 +171,7 @@ export default async function allPagesBundler(params?: Params) {
execSync(`rm -rf ${HYDRATION_DST_DIR}`); execSync(`rm -rf ${HYDRATION_DST_DIR}`);
const ctx = await esbuild.context({ const ctx = await esbuild.context({
entryPoints: Object.keys(virtualEntries).map((k) => `virtual:${k}`), // ["virtual:page-0", ...] entryPoints: Object.keys(virtualEntries).map((k) => `virtual:${k}`),
outdir: HYDRATION_DST_DIR, outdir: HYDRATION_DST_DIR,
bundle: true, bundle: true,
minify: !dev, minify: !dev,
@ -240,9 +190,7 @@ export default async function allPagesBundler(params?: Params) {
jsx: "automatic", jsx: "automatic",
}); });
await ctx.rebuild().catch((error: any) => { await ctx.rebuild();
console.error(`Build failed:`, error.message);
});
if (params?.watch) { if (params?.watch) {
global.BUNDLER_CTX = ctx; global.BUNDLER_CTX = ctx;

View File

@ -14,9 +14,10 @@ export default function watcher() {
recursive: true, recursive: true,
persistent: true, persistent: true,
}, },
(event, filename) => { async (event, filename) => {
if (!filename) return; if (!filename) return;
if (!PAGE_FILE_RE.test(filename)) return; const file_path = path.join(SRC_DIR, filename);
// if (!PAGE_FILE_RE.test(filename)) return;
// "change" events (file content modified) are already handled by // "change" events (file content modified) are already handled by
// esbuild's internal ctx.watch(). Only "rename" (create or delete) // esbuild's internal ctx.watch(). Only "rename" (create or delete)
@ -28,20 +29,17 @@ export default function watcher() {
const fullPath = path.join(SRC_DIR, filename); const fullPath = path.join(SRC_DIR, filename);
const action = existsSync(fullPath) ? "created" : "deleted"; const action = existsSync(fullPath) ? "created" : "deleted";
clearTimeout(global.WATCHER_TIMEOUT); try {
global.WATCHER_TIMEOUT = setTimeout(async () => { global.RECOMPILING = true;
try {
global.RECOMPILING = true;
console.log(`Page ${action}: ${filename}. Rebuilding ...`); console.log(`Page ${action}: ${filename}. Rebuilding ...`);
await rebuildBundler(); await rebuildBundler();
} catch (error: any) { } catch (error: any) {
console.error(error); console.error(error);
} finally { } finally {
global.RECOMPILING = false; global.RECOMPILING = false;
} }
}, 150);
}, },
); );
} }