mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-07-26 09:52:11 +03:00
- scripts/build/prepublish.ts: APP_DIR -> DIST_DIR; remove Step 1 and Step 2.5 hack blocks; fix MCP esbuild outfile (app/ -> dist/); update all log messages - scripts/build/build-next-isolated.mjs: remove legacy-app-snapshot entry from getTransientBuildPaths() (App-Router collision hack deleted) - scripts/build/assembleStandalone.mjs: fix standalone package.json after copy — removes "type":"module" so Next.js standalone server.js (CJS) loads correctly; also adds .build/next/ to allowed staging prefixes so server bundles are kept - scripts/build/pack-artifact-policy.ts: app/ -> dist/ in all PACK_ARTIFACT_* paths; add ".build/next/" to APP_STAGING_ALLOWED_PATH_PREFIXES (Layer 1 distDir change) - scripts/build/validate-pack-artifact.ts: dist/ check instead of app/ - scripts/build/postinstall.mjs: all app/ paths -> dist/ - scripts/build/postinstallSupport.mjs: hasStandaloneAppBundle checks dist/server.js - bin/cli/commands/serve.mjs: APP_DIR -> dist/ - package.json: files[] "app/" -> "dist/" - .gitignore: remove both /app and /app/ entries (no longer needed) Smoke: NO_APP_DIR_OK; DIST_OK; check:pack-artifact PASS; health 200 from dist/
243 lines
7.2 KiB
JavaScript
243 lines
7.2 KiB
JavaScript
#!/usr/bin/env node
|
|
|
|
import fs from "node:fs/promises";
|
|
import os from "node:os";
|
|
import path from "node:path";
|
|
import { spawn } from "node:child_process";
|
|
import { pathToFileURL } from "node:url";
|
|
import {
|
|
assembleStandalone,
|
|
syncStandaloneNativeAssets as _syncNativeAssets,
|
|
syncStandaloneExtraModules as _syncExtraModules,
|
|
} from "./assembleStandalone.mjs";
|
|
|
|
/**
|
|
* Layer 1: `app/` has been renamed to `dist/` and the App-Router collision is gone.
|
|
* The only transient paths remaining are `.tmp/wine32` (Wine prefix used by some
|
|
* older build tools) and `_tasks` (planning workspace).
|
|
*/
|
|
|
|
const projectRoot = process.cwd();
|
|
const distDir = path.resolve(process.env.NEXT_DIST_DIR || ".build/next");
|
|
const backupRoot = path.join(os.tmpdir(), `omniroute-build-isolated-${process.pid}-${Date.now()}`);
|
|
|
|
export function getTransientBuildPaths(rootDir = projectRoot, env = process.env) {
|
|
const paths = [
|
|
{
|
|
label: "local Wine prefix",
|
|
sourcePath: path.join(rootDir, ".tmp", "wine32"),
|
|
backupPath: path.join(backupRoot, "wine32"),
|
|
},
|
|
];
|
|
|
|
if (env.OMNIROUTE_BUILD_MOVE_TASKS === "1") {
|
|
paths.push({
|
|
label: "task planning workspace",
|
|
sourcePath: path.join(rootDir, "_tasks"),
|
|
backupPath: path.join(backupRoot, "_tasks"),
|
|
});
|
|
}
|
|
|
|
return paths;
|
|
}
|
|
|
|
async function exists(targetPath) {
|
|
try {
|
|
await fs.access(targetPath);
|
|
return true;
|
|
} catch {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
export async function movePath(sourcePath, destinationPath, fsImpl = fs) {
|
|
const mkdir = typeof fsImpl.mkdir === "function" ? fsImpl.mkdir.bind(fsImpl) : fs.mkdir.bind(fs);
|
|
await mkdir(path.dirname(destinationPath), { recursive: true });
|
|
|
|
try {
|
|
await fsImpl.rename(sourcePath, destinationPath);
|
|
} catch (error) {
|
|
if (error?.code !== "EXDEV") {
|
|
throw error;
|
|
}
|
|
|
|
console.warn(
|
|
`[build-next-isolated] EXDEV while moving ${sourcePath} -> ${destinationPath}; falling back to copy/remove`
|
|
);
|
|
await fsImpl.cp(sourcePath, destinationPath, {
|
|
recursive: true,
|
|
preserveTimestamps: true,
|
|
force: false,
|
|
errorOnExist: true,
|
|
});
|
|
await fsImpl.rm(sourcePath, { recursive: true, force: true });
|
|
}
|
|
}
|
|
|
|
function runNextBuild() {
|
|
return new Promise((resolve) => {
|
|
const nextBin = path.join(projectRoot, "node_modules", "next", "dist", "bin", "next");
|
|
const child = spawn(process.execPath, [nextBin, "build", resolveNextBuildBundlerFlag()], {
|
|
cwd: projectRoot,
|
|
stdio: "inherit",
|
|
env: resolveNextBuildEnv(process.env),
|
|
});
|
|
|
|
const forward = (signal) => {
|
|
if (!child.killed) child.kill(signal);
|
|
};
|
|
|
|
process.on("SIGINT", forward);
|
|
process.on("SIGTERM", forward);
|
|
|
|
child.on("exit", (code, signal) => {
|
|
process.off("SIGINT", forward);
|
|
process.off("SIGTERM", forward);
|
|
if (signal) {
|
|
resolve({ code: 1, signal });
|
|
return;
|
|
}
|
|
resolve({ code: code ?? 1, signal: null });
|
|
});
|
|
});
|
|
}
|
|
|
|
export function resolveNextBuildBundlerFlag(baseEnv = process.env) {
|
|
return baseEnv.OMNIROUTE_USE_TURBOPACK === "1" ? "--turbopack" : "--webpack";
|
|
}
|
|
|
|
export function resolveNextBuildEnv(baseEnv = process.env) {
|
|
return {
|
|
...baseEnv,
|
|
NEXT_PRIVATE_BUILD_WORKER: baseEnv.NEXT_PRIVATE_BUILD_WORKER || "0",
|
|
};
|
|
}
|
|
|
|
async function resetStandaloneOutput(rootDir = projectRoot, fsImpl = fs) {
|
|
// Use the module-level distDir so NEXT_DIST_DIR is respected
|
|
const resolvedDistDir =
|
|
rootDir === projectRoot ? distDir : path.join(rootDir, process.env.NEXT_DIST_DIR || ".build/next");
|
|
const standaloneRoot = path.join(resolvedDistDir, "standalone");
|
|
if (!(await exists(standaloneRoot))) return;
|
|
|
|
const staleStandaloneBackup = path.join(backupRoot, "standalone-stale");
|
|
|
|
await movePath(standaloneRoot, staleStandaloneBackup, fsImpl);
|
|
console.log("[build-next-isolated] Moved stale standalone output out of the build path");
|
|
}
|
|
|
|
export async function pruneStandaloneArtifacts(rootDir = projectRoot, fsImpl = fs) {
|
|
const resolvedDistDirForPrune =
|
|
rootDir === projectRoot ? distDir : path.join(rootDir, process.env.NEXT_DIST_DIR || ".build/next");
|
|
const standaloneRoot = path.join(resolvedDistDirForPrune, "standalone");
|
|
const pruneTargets = [path.join(standaloneRoot, "_tasks")];
|
|
|
|
for (const targetPath of pruneTargets) {
|
|
if (!(await exists(targetPath))) continue;
|
|
await fsImpl.rm(targetPath, { recursive: true, force: true });
|
|
console.log(
|
|
`[build-next-isolated] Pruned standalone artifact: ${path.relative(rootDir, targetPath)}`
|
|
);
|
|
}
|
|
}
|
|
|
|
export async function syncStandaloneNativeAssets(
|
|
rootDir = projectRoot,
|
|
fsImpl = fs,
|
|
log = console
|
|
) {
|
|
return _syncNativeAssets(rootDir, fsImpl, log);
|
|
}
|
|
|
|
export async function syncStandaloneExtraModules(
|
|
rootDir = projectRoot,
|
|
fsImpl = fs,
|
|
log = console
|
|
) {
|
|
return _syncExtraModules(rootDir, fsImpl, log);
|
|
}
|
|
|
|
export async function main() {
|
|
const movedPaths = [];
|
|
const transientBuildPaths = getTransientBuildPaths();
|
|
|
|
try {
|
|
for (const entry of transientBuildPaths) {
|
|
if (!(await exists(entry.sourcePath))) continue;
|
|
await movePath(entry.sourcePath, entry.backupPath);
|
|
movedPaths.push(entry);
|
|
}
|
|
|
|
await resetStandaloneOutput(projectRoot);
|
|
|
|
const result = await runNextBuild();
|
|
const standaloneDir = path.join(distDir, "standalone");
|
|
if (result.code === 0 && (await exists(standaloneDir))) {
|
|
try {
|
|
await fs.cp(
|
|
path.join(projectRoot, "docs"),
|
|
path.join(standaloneDir, "docs"),
|
|
{ recursive: true }
|
|
);
|
|
console.log("[build-next-isolated] Copied docs/ to standalone output");
|
|
} catch (docsCopyErr) {
|
|
console.warn("[build-next-isolated] Non-fatal error copying docs/:", docsCopyErr?.message);
|
|
}
|
|
|
|
try {
|
|
await pruneStandaloneArtifacts(projectRoot);
|
|
} catch (pruneErr) {
|
|
console.warn(
|
|
"[build-next-isolated] Non-fatal error pruning standalone artifacts:",
|
|
pruneErr
|
|
);
|
|
}
|
|
|
|
try {
|
|
console.log("[build-next-isolated] Assembling standalone bundle (static + public + natives + extras)...");
|
|
assembleStandalone({
|
|
distDir,
|
|
outDir: standaloneDir,
|
|
projectRoot,
|
|
copyNatives: true,
|
|
});
|
|
} catch (assembleErr) {
|
|
console.warn(
|
|
"[build-next-isolated] Non-fatal error assembling standalone:",
|
|
assembleErr
|
|
);
|
|
}
|
|
}
|
|
process.exitCode = result.code;
|
|
} catch (error) {
|
|
console.error("[build-next-isolated] Build failed:", error);
|
|
process.exitCode = 1;
|
|
} finally {
|
|
while (movedPaths.length > 0) {
|
|
const entry = movedPaths.pop();
|
|
if (!entry) continue;
|
|
try {
|
|
await movePath(entry.backupPath, entry.sourcePath);
|
|
} catch (restoreError) {
|
|
console.error(
|
|
`[build-next-isolated] Failed to restore ${entry.label} from ${entry.backupPath}:`,
|
|
restoreError
|
|
);
|
|
process.exitCode = 1;
|
|
}
|
|
}
|
|
|
|
try {
|
|
await fs.rm(backupRoot, { recursive: true, force: true });
|
|
} catch (cleanupError) {
|
|
console.warn("[build-next-isolated] Failed to clean temporary backup root:", cleanupError);
|
|
}
|
|
}
|
|
}
|
|
|
|
const entryScript = process.argv[1] ? pathToFileURL(process.argv[1]).href : null;
|
|
|
|
if (entryScript === import.meta.url) {
|
|
await main();
|
|
}
|