mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-07-31 04:12:10 +03:00
fix(build): assemble static/server-files/chunks under distDir, not literal .next
CRITICAL white-screen bug from the build-output-isolation refactor: the standalone
server.js bakes distDir ("./.build/next") into its config and serves /_next/static
from <root>/.build/next/static — but assembleStandalone hard-coded the destination
to <outDir>/.next/static (+ sanitised/patched <outDir>/.next/{required-server-files,
server}). Result: the server's static dir was EMPTY → every JS/CSS chunk 404'd →
blank login page (health stayed 200, so it slipped past the health-only dry-run).
Mirror the distDir path (relative to projectRoot) for static, required-server-files
sanitization (was a silent no-op → 0 paths sanitised, now 11), and the Turbopack
chunk patch. Verified: booting the assembled bundle serves the webpack chunk 200.
Affects every consumer (npm/Docker/Electron/VPS).
This commit is contained in:
@@ -283,11 +283,13 @@ async function syncExtraModulesToDir(projectRoot, outDir, fsImpl, log) {
|
||||
* @param {string} outDir - assembled standalone output directory
|
||||
* @returns {number} number of path replacements made
|
||||
*/
|
||||
export function assemblePathSanitize(projectRoot, outDir) {
|
||||
export function assemblePathSanitize(projectRoot, outDir, distDir = ".next") {
|
||||
const buildRoot = projectRoot.replace(/\\/g, "/"); // normalise for regex safety
|
||||
const sanitizeTargets = [
|
||||
path.join(outDir, "server.js"),
|
||||
path.join(outDir, ".next", "required-server-files.json"),
|
||||
// required-server-files.json lives under the distDir (e.g. .build/next), not
|
||||
// a literal .next — the standalone preserves the configured distDir path.
|
||||
path.join(outDir, distDir, "required-server-files.json"),
|
||||
];
|
||||
|
||||
let sanitisedCount = 0;
|
||||
@@ -316,8 +318,8 @@ export function assemblePathSanitize(projectRoot, outDir) {
|
||||
* @param {string} outDir - assembled standalone output directory
|
||||
* @returns {{ patchedFiles: number, patchedMatches: number }}
|
||||
*/
|
||||
export function patchTurbopackChunks(outDir) {
|
||||
const serverOutput = path.join(outDir, ".next", "server");
|
||||
export function patchTurbopackChunks(outDir, distDir = ".next") {
|
||||
const serverOutput = path.join(outDir, distDir, "server");
|
||||
const HASH_RE = /(['"\\])([a-z@][a-z0-9@./_-]+?-[0-9a-f]{16}(?:\/[^'"\\]+)?)\1/g;
|
||||
let patchedFiles = 0;
|
||||
let patchedMatches = 0;
|
||||
@@ -392,6 +394,12 @@ export function assembleStandalone({
|
||||
if (!distDir) throw new Error("[assembleStandalone] distDir is required");
|
||||
if (!outDir) throw new Error("[assembleStandalone] outDir is required");
|
||||
|
||||
// The standalone bundle preserves the distDir path RELATIVE to projectRoot
|
||||
// (the server's baked config uses e.g. "./.build/next"), so output dest paths
|
||||
// for static / required-server-files / server chunks must use the relative
|
||||
// distDir appended to outDir — never the absolute build-machine distDir.
|
||||
const relDistDir = path.isAbsolute(distDir) ? path.relative(projectRoot, distDir) : distDir;
|
||||
|
||||
const standaloneDir = path.resolve(path.join(distDir, "standalone"));
|
||||
const resolvedOutDir = path.resolve(outDir);
|
||||
if (!fsSync.existsSync(standaloneDir)) {
|
||||
@@ -425,9 +433,13 @@ export function assembleStandalone({
|
||||
}
|
||||
}
|
||||
|
||||
// 2. Copy <distDir>/static -> resolvedOutDir/.next/static
|
||||
// 2. Copy <distDir>/static -> resolvedOutDir/<distDir>/static
|
||||
// CRITICAL: the standalone server.js is built with distDir baked into its config
|
||||
// (e.g. "./.build/next"), so it serves /_next/static from <outDir>/<distDir>/static,
|
||||
// NOT a literal <outDir>/.next/static. Copying to .next/static leaves the server's
|
||||
// static dir empty → every JS/CSS chunk 404s → blank page. Mirror the distDir path.
|
||||
const staticSrc = path.join(distDir, "static");
|
||||
const staticDest = path.join(resolvedOutDir, ".next", "static");
|
||||
const staticDest = path.join(resolvedOutDir, relDistDir, "static");
|
||||
if (fsSync.existsSync(staticSrc)) {
|
||||
fsSync.mkdirSync(path.dirname(staticDest), { recursive: true });
|
||||
fsSync.cpSync(staticSrc, staticDest, { recursive: true, force: true });
|
||||
@@ -442,7 +454,7 @@ export function assembleStandalone({
|
||||
|
||||
// 4. Optionally sanitize abs paths
|
||||
if (sanitizePaths) {
|
||||
const count = assemblePathSanitize(projectRoot, resolvedOutDir);
|
||||
const count = assemblePathSanitize(projectRoot, resolvedOutDir, relDistDir);
|
||||
if (count > 0) {
|
||||
console.log(`[assembleStandalone] Sanitised ${count} hardcoded path references`);
|
||||
}
|
||||
@@ -450,7 +462,7 @@ export function assembleStandalone({
|
||||
|
||||
// 5. Optionally patch Turbopack hashed chunks
|
||||
if (doPatchChunks) {
|
||||
const { patchedFiles, patchedMatches } = patchTurbopackChunks(resolvedOutDir);
|
||||
const { patchedFiles, patchedMatches } = patchTurbopackChunks(resolvedOutDir, relDistDir);
|
||||
if (patchedMatches > 0) {
|
||||
console.log(
|
||||
`[assembleStandalone] Hash-strip: patched ${patchedMatches} hashed require() in ${patchedFiles} server chunk file(s)`
|
||||
|
||||
@@ -20,7 +20,16 @@ test("assembleStandalone copies standalone + static + public + sidecars into out
|
||||
assembleStandalone({ distDir, outDir, projectRoot: tmp, sanitizePaths: false, copyNatives: false });
|
||||
|
||||
assert.ok(fs.existsSync(path.join(outDir, "server.js")), "server.js copied");
|
||||
assert.ok(fs.existsSync(path.join(outDir, ".next/static/x.js")), "static copied");
|
||||
// Static lands under the distDir path (.build/next/static), where the standalone
|
||||
// server.js — built with distDir baked into its config — serves /_next/static from.
|
||||
assert.ok(
|
||||
fs.existsSync(path.join(outDir, ".build/next/static/x.js")),
|
||||
"static copied under distDir"
|
||||
);
|
||||
assert.ok(
|
||||
!fs.existsSync(path.join(outDir, ".next/static/x.js")),
|
||||
"static is NOT placed under a literal .next (would 404 against distDir server)"
|
||||
);
|
||||
assert.ok(fs.existsSync(path.join(outDir, "public/logo.svg")), "public copied");
|
||||
fs.rmSync(tmp, { recursive: true, force: true });
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user