72 lines
2.3 KiB
TypeScript
72 lines
2.3 KiB
TypeScript
import { log } from "../../utils/log";
|
|
import allPagesESBuildContextBundler from "../bundler/all-pages-esbuild-context-bundler";
|
|
import serverPostBuildFn from "./server-post-build-fn";
|
|
import { restartWatcher } from "./watcher-esbuild-ctx";
|
|
import { logBuildErrorsOnce } from "../../utils/build-error-log";
|
|
import disposeBundlerContexts from "../bundler/dispose-bundler-contexts";
|
|
import isDevelopment from "../../utils/is-development";
|
|
|
|
// Single-flight: error recovery paths (grab-page-component retries, the
|
|
// watcher, post-build HMR pushes) all call fullRebuild, and each rebuild
|
|
// triggers serverPostBuildFn which can re-enter error handling. Without this
|
|
// guard one broken page could fan out into cascading, overlapping rebuilds
|
|
// that leave the dev server unresponsive. Concurrent callers await the same
|
|
// in-flight rebuild instead of starting a new one.
|
|
let rebuild_in_progress: Promise<void> | undefined;
|
|
|
|
export default async function fullRebuild(params?: { msg?: string }) {
|
|
if (rebuild_in_progress) {
|
|
if (params?.msg) {
|
|
log.watch(`Rebuild already in progress, skipping: ${params.msg}`);
|
|
}
|
|
return rebuild_in_progress;
|
|
}
|
|
|
|
const run = doFullRebuild(params).finally(() => {
|
|
if (rebuild_in_progress === run) {
|
|
rebuild_in_progress = undefined;
|
|
}
|
|
});
|
|
|
|
rebuild_in_progress = run;
|
|
return run;
|
|
}
|
|
|
|
async function doFullRebuild(params?: { msg?: string }) {
|
|
try {
|
|
const { msg } = params || {};
|
|
|
|
global.BUNEXT_RECOMPILING = true;
|
|
|
|
if (msg) {
|
|
log.watch(msg);
|
|
}
|
|
|
|
global.BUNEXT_ROUTER.reload();
|
|
|
|
await disposeBundlerContexts();
|
|
|
|
await allPagesESBuildContextBundler({
|
|
post_build_fn: async (params) => {
|
|
await serverPostBuildFn(params);
|
|
},
|
|
});
|
|
|
|
// Production never rebuilds incrementally; don't keep esbuild's
|
|
// build state alive for the rest of the process.
|
|
if (!isDevelopment()) {
|
|
await disposeBundlerContexts();
|
|
}
|
|
} catch (error: any) {
|
|
logBuildErrorsOnce(
|
|
[{ text: error?.message || String(error) }],
|
|
(line) => log.error(line),
|
|
);
|
|
} finally {
|
|
global.BUNEXT_RECOMPILING = false;
|
|
global.BUNEXT_IS_SERVER_COMPONENT = false;
|
|
}
|
|
|
|
restartWatcher();
|
|
}
|