Fix build lags

This commit is contained in:
2026-10-02 14:48:38 +01:00
parent 17a5c3df01
commit 8fa5b64b48
38 changed files with 1390 additions and 1238 deletions
@@ -2,25 +2,23 @@ import { type Plugin } from "esbuild";
import type { PageFiles } from "../../../types";
import { log } from "../../../utils/log";
import grabArtifactsFromBundledResults from "../grab-artifacts-from-bundled-result";
import buildOnstartErrorHandler from "../build-on-start-error-handler";
import _ from "lodash";
import pagesSSRBundler from "../pages-ssr-bundler";
import enqueueSSRRebuild from "../pages-ssr-context-bundler";
import grabDirNames from "../../../utils/grab-dir-names";
import {
logBuildErrorsOnce,
resetBuildErrorLog,
} from "../../../utils/build-error-log";
import { cpSync, existsSync, mkdirSync, rmSync, writeFileSync } from "fs";
import { cpSync, existsSync, mkdirSync, rmSync } from "fs";
import fullRebuild from "../../server/full-rebuild";
import path from "path";
import cleanupLogsDirs from "../../cleanup-logs-dir";
import grabRelativePath from "../../../utils/grab-relative-path";
import cleanupArtifacts from "../../server/cleanup-artifacts";
const { BUNX_BUNDLER_ERROR_EXIT_FILE, BUNX_ERROR_LOGS_DIR, ROOT_DIR } =
grabDirNames();
const { BUNX_BUNDLER_ERROR_EXIT_FILE, BUNX_ERROR_LOGS_DIR } = grabDirNames();
let build_start = 0;
const MAX_BUILD_STARTS = 2;
type Params = {
entryToPage: Map<
@@ -29,7 +27,10 @@ type Params = {
tsx: string;
}
>;
post_build_fn?: (params: { artifacts: any[] }) => Promise<void> | void;
post_build_fn?: (params: {
artifacts: any[];
changed_pages?: string[];
}) => Promise<void> | void;
build_only?: boolean;
};
@@ -42,19 +43,7 @@ export default function esbuildCTXArtifactTracker({
name: "artifact-tracker",
setup(build) {
build.onStart(async () => {
global.BUNEXT_MAIN_CTX_BUILD_STARTS++;
build_start = performance.now();
const does_error_file_exist = existsSync(
BUNX_BUNDLER_ERROR_EXIT_FILE,
);
if (
global.BUNEXT_MAIN_CTX_BUILD_STARTS >= MAX_BUILD_STARTS &&
!does_error_file_exist
) {
await buildOnstartErrorHandler();
}
});
build.onEnd(async (result) => {
@@ -92,6 +81,12 @@ export default function esbuildCTXArtifactTracker({
entryToPage,
});
// Pages whose entry output hash changed since the previous
// build. With `splitting`, a change to any shared chunk also
// re-hashes every page entry that imports it, so this set
// covers all pages whose client graph actually changed.
const changed_pages: string[] = [];
if (artifacts?.[0] && artifacts.length > 0) {
for (let i = 0; i < artifacts.length; i++) {
const artifact = artifacts[i];
@@ -104,13 +99,22 @@ export default function esbuildCTXArtifactTracker({
artifact.local_path,
);
const prior =
global.BUNEXT_BUNDLER_CTX_MAP[relative_path];
if (
!prior?.path ||
prior.path !== artifact.path ||
prior.css_path !== artifact.css_path
) {
changed_pages.push(relative_path);
}
global.BUNEXT_BUNDLER_CTX_MAP[relative_path] =
_.merge(
global.BUNEXT_BUNDLER_CTX_MAP[
relative_path
],
{ ...artifact, local_path: relative_path },
);
_.merge(prior, {
...artifact,
local_path: relative_path,
});
}
}
}
@@ -129,17 +133,19 @@ export default function esbuildCTXArtifactTracker({
}
global.BUNEXT_BUILD_FAILED = false;
global.BUNEXT_MAIN_CTX_BUILD_STARTS = 0;
global.BUNEXT_BUNDLER_CTX_DISPOSED = false;
const does_error_file_exist = existsSync(
BUNX_BUNDLER_ERROR_EXIT_FILE,
);
// SSR must finish before HMR so server props are fresh
// SSR must finish before HMR so server props are fresh.
// The rebuild is single-flighted: concurrent hydration
// rebuilds join the in-flight SSR rebuild instead of
// starting overlapping ones.
if (build_only) {
try {
await pagesSSRBundler();
await enqueueSSRRebuild();
} catch (error) {
log.error(`SSR Bundler Error: ${error}`);
}
@@ -154,20 +160,26 @@ export default function esbuildCTXArtifactTracker({
await fullRebuild();
} else {
try {
await pagesSSRBundler();
await enqueueSSRRebuild();
} catch (error) {
log.error(`SSR Bundler Error: ${error}`);
}
if (artifacts?.[0] && artifacts.length > 0) {
try {
await post_build_fn?.({ artifacts });
await post_build_fn?.({
artifacts,
changed_pages,
});
} catch (error) {
log.error(`Post-build Error: ${error}`);
}
}
}
// Prune orphaned entry outputs from previous rebuilds.
await cleanupArtifacts({ paradigm: "hydration" });
global.BUNEXT_RECOMPILING = false;
global.BUNEXT_IS_SERVER_COMPONENT = false;
});
@@ -12,10 +12,8 @@ import {
import { log } from "../../../utils/log";
let build_start = 0;
let build_starts = 0;
const MAX_BUILD_STARTS = 2;
const { BUNX_TMP_DIR, ROOT_DIR } = grabDirNames();
const { BUNX_TMP_DIR } = grabDirNames();
type Params = {
entryToPage: Map<
@@ -24,35 +22,20 @@ type Params = {
tsx: string;
}
>;
post_build_fn?: (params: { artifacts: any[] }) => Promise<void> | void;
};
export default function ssrCTXArtifactTracker({
entryToPage,
post_build_fn,
}: Params) {
export default function ssrCTXArtifactTracker({ entryToPage }: Params) {
const artifactTracker: Plugin = {
name: "ssr-artifact-tracker",
setup(build) {
build.onStart(async () => {
build_starts++;
build_start = performance.now();
if (build_starts == MAX_BUILD_STARTS) {
global.BUNEXT_SSR_BUNDLER_CTX_DISPOSED = true;
await global.BUNEXT_SSR_BUNDLER_CTX?.dispose();
global.BUNEXT_SSR_BUNDLER_CTX = undefined;
}
});
build.onEnd(async (result) => {
if (result.errors.length > 0) {
global.BUNEXT_BUILD_FAILED = true;
global.BUNEXT_SSR_BUNDLER_CTX_DISPOSED = true;
try {
await global.BUNEXT_SSR_BUNDLER_CTX?.dispose();
} catch {}
global.BUNEXT_SSR_BUNDLER_CTX = undefined;
build_starts = 0;
logBuildErrorsOnce(result.errors, (line) =>
log.error(`SSR Build error: ${line}`),
);
@@ -83,13 +66,6 @@ export default function ssrCTXArtifactTracker({
};
}
}
// post_build_fn?.({ artifacts });
// const elapsed = (performance.now() - build_start).toFixed(
// 0,
// );
// log.success(`SSR [Built] in ${elapsed}ms`);
}
try {
@@ -103,6 +79,9 @@ export default function ssrCTXArtifactTracker({
);
} catch (error) {}
// const elapsed = (performance.now() - build_start).toFixed(0);
// log.success(`SSR [Built] in ${elapsed}ms`);
global.BUNEXT_SSR_BUNDLER_CTX_DISPOSED = false;
global.BUNEXT_BUILD_FAILED = false;
resetBuildErrorLog();
@@ -1,18 +1,20 @@
import type { Plugin } from "esbuild";
import path from "path";
import type { PageFiles } from "../../../types";
import { log } from "../../../utils/log";
type SSRVirtualEntry = PageFiles & { tsx: string };
type Params = {
entryToPage: Map<
string,
PageFiles & {
tsx: string;
}
>;
/**
* Lazily-resolved current virtual entry contents. The map is refreshed
* before every rebuild so edits to `.server.ts`/api files (whose virtual
* entries mirror their raw file content) are picked up by a plain
* `ctx.rebuild()` without recreating the esbuild context.
*/
getEntryToPage: () => Map<string, SSRVirtualEntry>;
};
export default function ssrVirtualFilesPlugin({ entryToPage }: Params) {
export default function ssrVirtualFilesPlugin({ getEntryToPage }: Params) {
const virtualPlugin: Plugin = {
name: "ssr-virtual-hydration",
setup(build) {
@@ -25,7 +27,7 @@ export default function ssrVirtualFilesPlugin({ entryToPage }: Params) {
});
build.onLoad({ filter: /.*/, namespace: "ssr-virtual" }, (args) => {
const target = entryToPage.get(args.path);
const target = getEntryToPage().get(args.path);
if (!target?.tsx) return null;
const contents = target.tsx;