mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-07-26 09:52:11 +03:00
build(layer1): rename standalone output app/ -> dist/; delete both App-Router move hacks
- 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/
This commit is contained in:
9
.gitignore
vendored
9
.gitignore
vendored
@@ -101,14 +101,10 @@ app.__qa_backup/
|
||||
.app-build-backup-*/
|
||||
backup/
|
||||
|
||||
# Production standalone build (created by scripts/prepublish.mjs)
|
||||
# Conflicts with Next.js App Router detection in dev (root app/ shadows src/app/)
|
||||
# npm publish still includes it via package.json "files" field
|
||||
/app/
|
||||
|
||||
# Build intermediates (.build/) and shippable standalone (dist/).
|
||||
# These are fully reproducible from source; never committed.
|
||||
# Layer 1: Next.js now writes to .build/next (was .next); assembled bundle → dist/
|
||||
# (Previously /app/ was the standalone output; renamed to /dist/ in Layer 1.)
|
||||
/.build/
|
||||
/dist/
|
||||
/.next/
|
||||
@@ -126,9 +122,6 @@ vscode-extension/
|
||||
*.sqlite-wal
|
||||
*.sqlite-journal
|
||||
|
||||
# Compiled npm-package build artifact (not source, should not be in git)
|
||||
/app
|
||||
|
||||
# IDEA
|
||||
.idea/
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ import { isTermux } from "../../../scripts/build/postinstallSupport.mjs";
|
||||
|
||||
const __dirname = dirname(fileURLToPath(import.meta.url));
|
||||
const ROOT = join(__dirname, "..", "..", "..");
|
||||
const APP_DIR = join(ROOT, "app");
|
||||
const APP_DIR = join(ROOT, "dist");
|
||||
|
||||
function parsePort(value, fallback) {
|
||||
const parsed = parseInt(String(value), 10);
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
},
|
||||
"files": [
|
||||
"bin/",
|
||||
"app/",
|
||||
"dist/",
|
||||
"src/lib/cli-helper/",
|
||||
"@omniroute/",
|
||||
"open-sse/mcp-server/index.ts",
|
||||
|
||||
@@ -406,6 +406,25 @@ export function assembleStandalone({
|
||||
fsSync.cpSync(standaloneDir, resolvedOutDir, { recursive: true });
|
||||
}
|
||||
|
||||
// 1.5. Fix package.json in outDir: Next.js standalone's server.js is CommonJS (uses require()),
|
||||
// but the root package.json (which Next copies into the standalone) has "type":"module".
|
||||
// Remove "type" from the outDir's package.json so Node.js treats .js files as CJS in this dir.
|
||||
// Without this, `node server.js` (or `import("./server.js")`) fails with
|
||||
// ReferenceError: require is not defined in ES module scope
|
||||
const outDirPkgJson = path.join(resolvedOutDir, "package.json");
|
||||
if (fsSync.existsSync(outDirPkgJson)) {
|
||||
try {
|
||||
const pkg = JSON.parse(fsSync.readFileSync(outDirPkgJson, "utf8"));
|
||||
if (pkg.type === "module") {
|
||||
delete pkg.type;
|
||||
fsSync.writeFileSync(outDirPkgJson, JSON.stringify(pkg, null, 2) + "\n");
|
||||
console.log("[assembleStandalone] Removed 'type':'module' from standalone package.json (server.js is CJS)");
|
||||
}
|
||||
} catch (err) {
|
||||
console.warn(`[assembleStandalone] Could not patch standalone package.json: ${err.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
// 2. Copy <distDir>/static -> resolvedOutDir/.next/static
|
||||
const staticSrc = path.join(distDir, "static");
|
||||
const staticDest = path.join(resolvedOutDir, ".next", "static");
|
||||
|
||||
@@ -12,10 +12,9 @@ import {
|
||||
} from "./assembleStandalone.mjs";
|
||||
|
||||
/**
|
||||
* This repository contains a legacy `app/` snapshot (packaging/runtime artifacts)
|
||||
* alongside the active Next.js source in `src/app/`. Next.js route discovery scans
|
||||
* both and fails the build on legacy files. We temporarily move the legacy folder
|
||||
* out of the project root during `next build`, then restore it in all outcomes.
|
||||
* 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();
|
||||
@@ -24,11 +23,6 @@ const backupRoot = path.join(os.tmpdir(), `omniroute-build-isolated-${process.pi
|
||||
|
||||
export function getTransientBuildPaths(rootDir = projectRoot, env = process.env) {
|
||||
const paths = [
|
||||
{
|
||||
label: "legacy app snapshot",
|
||||
sourcePath: path.join(rootDir, "app"),
|
||||
backupPath: path.join(backupRoot, "app"),
|
||||
},
|
||||
{
|
||||
label: "local Wine prefix",
|
||||
sourcePath: path.join(rootDir, ".tmp", "wine32"),
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
/**
|
||||
* Shared policy for OmniRoute npm publish artifact hygiene.
|
||||
*
|
||||
* The package currently publishes the standalone runtime under app/.
|
||||
* The package publishes the standalone runtime under dist/ (Layer 1: renamed from app/).
|
||||
* This policy keeps local backups, QA scratch files, and development-only
|
||||
* directories out of the staged app/ tree and out of the final tarball.
|
||||
* directories out of the staged dist/ tree and out of the final tarball.
|
||||
*/
|
||||
|
||||
const STAGING_FORBIDDEN_DIRECTORIES = [
|
||||
@@ -40,6 +40,9 @@ export const APP_STAGING_ALLOWED_EXACT_PATHS: string[] = [
|
||||
];
|
||||
|
||||
export const APP_STAGING_ALLOWED_PATH_PREFIXES: string[] = [
|
||||
// Layer 1: Next.js distDir changed from ".next" to ".build/next"; the server
|
||||
// bundle now lives under .build/next/ inside the standalone output.
|
||||
".build/next/",
|
||||
".next/",
|
||||
"data/",
|
||||
"node_modules/",
|
||||
@@ -51,11 +54,11 @@ export const APP_STAGING_ALLOWED_PATH_PREFIXES: string[] = [
|
||||
];
|
||||
|
||||
export const PACK_ARTIFACT_ALLOWED_EXACT_PATHS: string[] = APP_STAGING_ALLOWED_EXACT_PATHS.map(
|
||||
(filePath: string) => `app/${filePath}`
|
||||
(filePath: string) => `dist/${filePath}`
|
||||
);
|
||||
|
||||
export const PACK_ARTIFACT_ALLOWED_PATH_PREFIXES: string[] = APP_STAGING_ALLOWED_PATH_PREFIXES.map(
|
||||
(directoryPath: string) => `app/${directoryPath}`
|
||||
(directoryPath: string) => `dist/${directoryPath}`
|
||||
);
|
||||
|
||||
export const PACK_ARTIFACT_ROOT_ALLOWED_EXACT_PATHS: string[] = [
|
||||
@@ -100,12 +103,12 @@ export const PACK_ARTIFACT_ROOT_ALLOWED_PATH_PREFIXES: string[] = [
|
||||
];
|
||||
|
||||
export const PACK_ARTIFACT_REQUIRED_PATHS: string[] = [
|
||||
"app/open-sse/services/compression/engines/rtk/filters/generic-output.json",
|
||||
"app/open-sse/services/compression/rules/en/filler.json",
|
||||
"app/server.js",
|
||||
"app/server-ws.mjs",
|
||||
"app/responses-ws-proxy.mjs",
|
||||
"app/peer-stamp.mjs",
|
||||
"dist/open-sse/services/compression/engines/rtk/filters/generic-output.json",
|
||||
"dist/open-sse/services/compression/rules/en/filler.json",
|
||||
"dist/server.js",
|
||||
"dist/server-ws.mjs",
|
||||
"dist/responses-ws-proxy.mjs",
|
||||
"dist/peer-stamp.mjs",
|
||||
"bin/cli/program.mjs",
|
||||
"bin/mcp-server.mjs",
|
||||
"bin/nodeRuntimeSupport.mjs",
|
||||
|
||||
@@ -5,12 +5,12 @@
|
||||
*
|
||||
* The npm package ships with a Next.js standalone build that includes
|
||||
* native modules compiled for the build platform (Linux x64) inside
|
||||
* app/node_modules/. However, npm also installs these as top-level
|
||||
* dist/node_modules/. However, npm also installs these as top-level
|
||||
* dependencies (in the root node_modules/), correctly compiled for
|
||||
* the user's platform.
|
||||
*
|
||||
* This script copies the correctly-built native binaries from the root
|
||||
* into the standalone app directory — no rebuild or build tools needed.
|
||||
* into the standalone dist directory — no rebuild or build tools needed.
|
||||
*
|
||||
* Modules repaired:
|
||||
* - better-sqlite3 (SQLite bindings)
|
||||
@@ -35,7 +35,7 @@ const ROOT = join(__dirname, "..", "..");
|
||||
|
||||
const appBinary = join(
|
||||
ROOT,
|
||||
"app",
|
||||
"dist",
|
||||
"node_modules",
|
||||
"better-sqlite3",
|
||||
"build",
|
||||
@@ -52,7 +52,7 @@ const rootBinary = join(
|
||||
);
|
||||
|
||||
async function fixBetterSqliteBinary() {
|
||||
if (!existsSync(join(ROOT, "app", "node_modules", "better-sqlite3"))) {
|
||||
if (!existsSync(join(ROOT, "dist", "node_modules", "better-sqlite3"))) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -92,14 +92,14 @@ async function fixBetterSqliteBinary() {
|
||||
const { execSync } = await import("node:child_process");
|
||||
const preGypBin = join(
|
||||
ROOT,
|
||||
"app",
|
||||
"dist",
|
||||
"node_modules",
|
||||
".bin",
|
||||
process.platform === "win32" ? "node-pre-gyp.cmd" : "node-pre-gyp"
|
||||
);
|
||||
const preGypFallback = join(
|
||||
ROOT,
|
||||
"app",
|
||||
"dist",
|
||||
"node_modules",
|
||||
"@mapbox",
|
||||
"node-pre-gyp",
|
||||
@@ -110,7 +110,7 @@ async function fixBetterSqliteBinary() {
|
||||
|
||||
if (existsSync(preGypCmd)) {
|
||||
execSync(`"${process.execPath}" "${preGypCmd}" install --fallback-to-build=false`, {
|
||||
cwd: join(ROOT, "app", "node_modules", "better-sqlite3"),
|
||||
cwd: join(ROOT, "dist", "node_modules", "better-sqlite3"),
|
||||
stdio: "inherit",
|
||||
timeout: 60_000,
|
||||
});
|
||||
@@ -147,7 +147,7 @@ async function fixBetterSqliteBinary() {
|
||||
}
|
||||
|
||||
execSync(rebuildCmd, {
|
||||
cwd: join(ROOT, "app"),
|
||||
cwd: join(ROOT, "dist"),
|
||||
stdio: "inherit",
|
||||
timeout: isAndroid ? 600_000 : 300_000, // ARM compilation is slower
|
||||
env,
|
||||
@@ -171,23 +171,23 @@ async function fixBetterSqliteBinary() {
|
||||
console.warn(" Manual fix options:");
|
||||
if (process.platform === "win32") {
|
||||
console.warn(" Option A (easiest — no build tools needed):");
|
||||
console.warn(` cd "${join(ROOT, "app", "node_modules", "better-sqlite3")}"`);
|
||||
console.warn(` cd "${join(ROOT, "dist", "node_modules", "better-sqlite3")}"`);
|
||||
console.warn(" npx @mapbox/node-pre-gyp install --fallback-to-build=false");
|
||||
console.warn(" Option B (requires Build Tools for Visual Studio):");
|
||||
console.warn(` cd "${join(ROOT, "app")}" && npm rebuild better-sqlite3`);
|
||||
console.warn(` cd "${join(ROOT, "dist")}" && npm rebuild better-sqlite3`);
|
||||
console.warn(" Install from: https://visualstudio.microsoft.com/visual-cpp-build-tools/");
|
||||
console.warn(" Also ensure Python is installed: https://python.org");
|
||||
} else if (process.platform === "darwin") {
|
||||
console.warn(` cd ${join(ROOT, "app")} && npm rebuild better-sqlite3`);
|
||||
console.warn(` cd ${join(ROOT, "dist")} && npm rebuild better-sqlite3`);
|
||||
console.warn(" If build tools are missing: xcode-select --install");
|
||||
} else {
|
||||
console.warn(` cd ${join(ROOT, "app")} && npm rebuild better-sqlite3`);
|
||||
console.warn(` cd ${join(ROOT, "dist")} && npm rebuild better-sqlite3`);
|
||||
}
|
||||
console.warn("");
|
||||
}
|
||||
|
||||
/**
|
||||
* Fix wreq-js native binary for the standalone app directory.
|
||||
* Fix wreq-js native binary for the standalone dist directory.
|
||||
*
|
||||
* wreq-js ships platform-specific .node binaries under rust/.
|
||||
* The standalone build may only contain Linux binaries from the CI.
|
||||
@@ -206,10 +206,10 @@ async function fixWreqJsBinary() {
|
||||
return;
|
||||
}
|
||||
|
||||
const appWreqDir = join(ROOT, "app", "node_modules", "wreq-js", "rust");
|
||||
const appWreqDir = join(ROOT, "dist", "node_modules", "wreq-js", "rust");
|
||||
const rootWreqDir = join(ROOT, "node_modules", "wreq-js", "rust");
|
||||
|
||||
if (!existsSync(join(ROOT, "app", "node_modules", "wreq-js"))) {
|
||||
if (!existsSync(join(ROOT, "dist", "node_modules", "wreq-js"))) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -262,12 +262,12 @@ async function fixWreqJsBinary() {
|
||||
}
|
||||
}
|
||||
|
||||
// Strategy 3: Rebuild wreq-js inside app/
|
||||
// Strategy 3: Rebuild wreq-js inside dist/
|
||||
console.log(" 📥 Attempting npm rebuild wreq-js...");
|
||||
try {
|
||||
const { execSync } = await import("node:child_process");
|
||||
execSync("npm rebuild wreq-js", {
|
||||
cwd: join(ROOT, "app"),
|
||||
cwd: join(ROOT, "dist"),
|
||||
stdio: "inherit",
|
||||
timeout: 120_000,
|
||||
});
|
||||
@@ -284,7 +284,7 @@ async function fixWreqJsBinary() {
|
||||
`\n ⚠️ Could not fix wreq-js native module for ${process.platform}-${process.arch}.`
|
||||
);
|
||||
console.warn(" OAuth-based providers (Codex, Cursor, etc.) may not work.");
|
||||
console.warn(` Manual fix: cd ${join(ROOT, "app")} && npm install wreq-js --no-save\n`);
|
||||
console.warn(` Manual fix: cd ${join(ROOT, "dist")} && npm install wreq-js --no-save\n`);
|
||||
}
|
||||
|
||||
async function ensureSwcHelpers() {
|
||||
@@ -292,7 +292,7 @@ async function ensureSwcHelpers() {
|
||||
return;
|
||||
}
|
||||
|
||||
const swcHelpersApp = join(ROOT, "app", "node_modules", "@swc", "helpers");
|
||||
const swcHelpersApp = join(ROOT, "dist", "node_modules", "@swc", "helpers");
|
||||
const swcHelpersRoot = join(ROOT, "node_modules", "@swc", "helpers");
|
||||
|
||||
if (existsSync(swcHelpersApp)) {
|
||||
@@ -302,13 +302,13 @@ async function ensureSwcHelpers() {
|
||||
if (existsSync(swcHelpersRoot)) {
|
||||
try {
|
||||
const { cpSync } = await import("node:fs");
|
||||
mkdirSync(join(ROOT, "app", "node_modules", "@swc"), { recursive: true });
|
||||
mkdirSync(join(ROOT, "dist", "node_modules", "@swc"), { recursive: true });
|
||||
cpSync(swcHelpersRoot, swcHelpersApp, { recursive: true });
|
||||
console.log(" ✅ @swc/helpers copied to standalone app/node_modules.\n");
|
||||
console.log(" ✅ @swc/helpers copied to standalone dist/node_modules.\n");
|
||||
} catch (err) {
|
||||
console.warn(` ⚠️ Could not copy @swc/helpers: ${err.message}`);
|
||||
console.warn(
|
||||
" Try manually: cp -r node_modules/@swc/helpers app/node_modules/@swc/helpers\n"
|
||||
" Try manually: cp -r node_modules/@swc/helpers dist/node_modules/@swc/helpers\n"
|
||||
);
|
||||
}
|
||||
return;
|
||||
|
||||
@@ -4,15 +4,16 @@ import { existsSync } from "node:fs";
|
||||
import { join } from "node:path";
|
||||
|
||||
/**
|
||||
* Detect whether the current install tree contains the published standalone app bundle.
|
||||
* Source checkouts should not create `app/` during postinstall because Next.js would
|
||||
* mis-detect it as a competing App Router root and serve 404s for the real `src/app` routes.
|
||||
* Detect whether the current install tree contains the published standalone bundle.
|
||||
* Checks for dist/server.js (Layer 1: renamed from app/server.js).
|
||||
* Source checkouts will not have dist/ so postinstall skips platform-specific
|
||||
* native repairs (which only apply to the shipped pre-built bundle).
|
||||
*
|
||||
* @param {string} rootDir
|
||||
* @returns {boolean}
|
||||
*/
|
||||
export function hasStandaloneAppBundle(rootDir) {
|
||||
return existsSync(join(rootDir, "app", "server.js"));
|
||||
return existsSync(join(rootDir, "dist", "server.js"));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
* OmniRoute — Prepublish Build Script
|
||||
*
|
||||
* Consumes the .build/next/standalone artifact produced by `npm run build`
|
||||
* (build-next-isolated.mjs) and assembles the npm staging `app/` directory.
|
||||
* (build-next-isolated.mjs) and assembles the npm staging `dist/` directory.
|
||||
* Does NOT run a second `next build` — the caller must run `npm run build` first,
|
||||
* or this script will invoke it exactly once if the artifact is absent.
|
||||
*
|
||||
@@ -39,7 +39,7 @@ const __dirname = dirname(__filename);
|
||||
const ROOT = join(__dirname, "..", "..");
|
||||
const NPX_BIN = process.platform === "win32" ? "npx.cmd" : "npx";
|
||||
|
||||
const APP_DIR = join(ROOT, "app");
|
||||
const DIST_DIR = join(ROOT, "dist");
|
||||
|
||||
function walkFiles(dir: string, rootDir: string = dir, files: string[] = []): string[] {
|
||||
let entries: string[] = [];
|
||||
@@ -110,11 +110,10 @@ function removeEmptyDirectories(dir: string): boolean {
|
||||
|
||||
console.log("🔨 OmniRoute — Building for npm publish...\n");
|
||||
|
||||
// ── Step 1: Clean previous app/ directory ──────────────────
|
||||
// KEEP for now (deleted in Layer 1 rename hack removal).
|
||||
if (existsSync(APP_DIR)) {
|
||||
console.log(" 🧹 Cleaning previous app/ directory...");
|
||||
rmSync(APP_DIR, { recursive: true, force: true });
|
||||
// ── Step 1: Clean previous dist/ directory ─────────────────
|
||||
if (existsSync(DIST_DIR)) {
|
||||
console.log(" 🧹 Cleaning previous dist/ directory...");
|
||||
rmSync(DIST_DIR, { recursive: true, force: true });
|
||||
}
|
||||
|
||||
// ── Step 2: Assert / trigger the Next.js standalone build ──
|
||||
@@ -137,33 +136,23 @@ if (!existsSync(standaloneServerJs)) {
|
||||
}
|
||||
console.log(" ✅ Standalone artifact present:", standaloneServerJs);
|
||||
|
||||
// ── Step 2.5: Remove app/ before assembly ──────────────────
|
||||
// CRITICAL: The postinstall script may create app/node_modules/@swc/helpers/,
|
||||
// which causes Next.js 16 to interpret app/ as an App Router directory
|
||||
// (competing with src/app/). We keep this guard in case postinstall re-runs
|
||||
// between the clean above and here. (Removed in Layer 1.)
|
||||
if (existsSync(APP_DIR)) {
|
||||
console.log(" 🧹 Removing app/ created by postinstall (App Router conflict fix)...");
|
||||
rmSync(APP_DIR, { recursive: true, force: true });
|
||||
}
|
||||
|
||||
// ── Step 3–7: Assemble standalone into app/ ────────────────
|
||||
// ── Step 3–7: Assemble standalone into dist/ ───────────────
|
||||
// All shared copy/sync/sanitize/chunk-patch operations are delegated to
|
||||
// assembleStandalone. npm-UNIQUE steps (MITM, MCP, CLI, sidecars) follow.
|
||||
console.log(" 📋 Assembling standalone bundle into app/...");
|
||||
console.log(" 📋 Assembling standalone bundle into dist/...");
|
||||
assembleStandalone({
|
||||
distDir: join(ROOT, NEXT_DIST),
|
||||
outDir: APP_DIR,
|
||||
outDir: DIST_DIR,
|
||||
projectRoot: ROOT,
|
||||
sanitizePaths: true,
|
||||
patchTurbopackChunks: true,
|
||||
copyNatives: true,
|
||||
});
|
||||
console.log(" ✅ Standalone bundle assembled to app/");
|
||||
console.log(" ✅ Standalone bundle assembled to dist/");
|
||||
|
||||
// ── Step 8: Compile + copy MITM cert utilities ─────────────
|
||||
const mitmSrc = join(ROOT, "src", "mitm");
|
||||
const mitmDest = join(APP_DIR, "src", "mitm");
|
||||
const mitmDest = join(DIST_DIR, "src", "mitm");
|
||||
if (existsSync(mitmSrc)) {
|
||||
console.log(" 🔨 Compiling MITM utilities (TypeScript → JavaScript)...");
|
||||
mkdirSync(mitmDest, { recursive: true });
|
||||
@@ -206,7 +195,7 @@ if (existsSync(mitmSrc)) {
|
||||
if (existsSync(mitmServerSrc)) {
|
||||
cpSync(mitmServerSrc, join(mitmDest, "server.cjs"));
|
||||
}
|
||||
console.log(" ✅ MITM utilities compiled to app/src/mitm/");
|
||||
console.log(" ✅ MITM utilities compiled to dist/src/mitm/");
|
||||
} catch (err: any) {
|
||||
console.warn(" ⚠️ MITM compile warning (non-fatal):", err.message);
|
||||
// Fallback: copy source files so at least they are present
|
||||
@@ -221,7 +210,7 @@ if (existsSync(mitmSrc)) {
|
||||
|
||||
// ── Step 8.5: Bundle MCP server ────────────────────────────
|
||||
const mcpSrcFile = join(ROOT, "open-sse", "mcp-server", "server.ts");
|
||||
const mcpDestDir = join(APP_DIR, "open-sse", "mcp-server");
|
||||
const mcpDestDir = join(DIST_DIR, "open-sse", "mcp-server");
|
||||
const mcpDestFile = join(mcpDestDir, "server.js");
|
||||
|
||||
if (existsSync(mcpSrcFile)) {
|
||||
@@ -237,11 +226,11 @@ if (existsSync(mcpSrcFile)) {
|
||||
"--platform=node",
|
||||
"--packages=external",
|
||||
"--format=esm",
|
||||
"--outfile=app/open-sse/mcp-server/server.js",
|
||||
"--outfile=dist/open-sse/mcp-server/server.js",
|
||||
],
|
||||
{ cwd: ROOT, stdio: "inherit" }
|
||||
);
|
||||
console.log(" ✅ MCP Server bundled to app/open-sse/mcp-server/server.js");
|
||||
console.log(" ✅ MCP Server bundled to dist/open-sse/mcp-server/server.js");
|
||||
} catch (err: any) {
|
||||
console.warn(" ⚠️ MCP Server bundle error:", err.message);
|
||||
}
|
||||
@@ -276,7 +265,7 @@ if (existsSync(cliSrcFile)) {
|
||||
|
||||
// ── Step 9: Copy shared utilities needed at runtime ────────
|
||||
const sharedApiKey = join(ROOT, "src", "shared", "utils", "apiKey.js");
|
||||
const sharedApiKeyDest = join(APP_DIR, "src", "shared", "utils");
|
||||
const sharedApiKeyDest = join(DIST_DIR, "src", "shared", "utils");
|
||||
if (existsSync(sharedApiKey)) {
|
||||
console.log(" 📋 Copying shared utilities...");
|
||||
mkdirSync(sharedApiKeyDest, { recursive: true });
|
||||
@@ -286,19 +275,19 @@ if (existsSync(sharedApiKey)) {
|
||||
// ── Step 9.5: Copy minimal runtime sidecars required outside .next ─────────
|
||||
const envExampleSrc = join(ROOT, ".env.example");
|
||||
if (existsSync(envExampleSrc)) {
|
||||
cpSync(envExampleSrc, join(APP_DIR, ".env.example"));
|
||||
cpSync(envExampleSrc, join(DIST_DIR, ".env.example"));
|
||||
}
|
||||
|
||||
const openapiSpecSrc = join(ROOT, "docs", "openapi.yaml");
|
||||
if (existsSync(openapiSpecSrc)) {
|
||||
const docsDest = join(APP_DIR, "docs");
|
||||
const docsDest = join(DIST_DIR, "docs");
|
||||
mkdirSync(docsDest, { recursive: true });
|
||||
cpSync(openapiSpecSrc, join(docsDest, "openapi.yaml"));
|
||||
}
|
||||
|
||||
const docsMarkdownSrc = join(ROOT, "docs");
|
||||
if (existsSync(docsMarkdownSrc)) {
|
||||
const docsDest = join(APP_DIR, "docs");
|
||||
const docsDest = join(DIST_DIR, "docs");
|
||||
mkdirSync(docsDest, { recursive: true });
|
||||
const mdFiles = readdirSync(docsMarkdownSrc).filter(
|
||||
(f) => f.endsWith(".md") || f.endsWith(".mdx")
|
||||
@@ -313,26 +302,26 @@ if (existsSync(docsMarkdownSrc)) {
|
||||
|
||||
const syncEnvSrc = join(ROOT, "scripts", "sync-env.mjs");
|
||||
if (existsSync(syncEnvSrc)) {
|
||||
const scriptsDest = join(APP_DIR, "scripts");
|
||||
const scriptsDest = join(DIST_DIR, "scripts");
|
||||
mkdirSync(scriptsDest, { recursive: true });
|
||||
cpSync(syncEnvSrc, join(scriptsDest, "sync-env.mjs"));
|
||||
}
|
||||
|
||||
const migrationsSrc = join(ROOT, "src", "lib", "db", "migrations");
|
||||
if (existsSync(migrationsSrc)) {
|
||||
const migrationsDest = join(APP_DIR, "src", "lib", "db", "migrations");
|
||||
mkdirSync(join(APP_DIR, "src", "lib", "db"), { recursive: true });
|
||||
const migrationsDest = join(DIST_DIR, "src", "lib", "db", "migrations");
|
||||
mkdirSync(join(DIST_DIR, "src", "lib", "db"), { recursive: true });
|
||||
cpSync(migrationsSrc, migrationsDest, { recursive: true, force: true });
|
||||
}
|
||||
|
||||
const runtimeAssetDirs = [
|
||||
{
|
||||
source: join(ROOT, "open-sse", "services", "compression", "engines", "rtk", "filters"),
|
||||
destination: join(APP_DIR, "open-sse", "services", "compression", "engines", "rtk", "filters"),
|
||||
destination: join(DIST_DIR, "open-sse", "services", "compression", "engines", "rtk", "filters"),
|
||||
},
|
||||
{
|
||||
source: join(ROOT, "open-sse", "services", "compression", "rules"),
|
||||
destination: join(APP_DIR, "open-sse", "services", "compression", "rules"),
|
||||
destination: join(DIST_DIR, "open-sse", "services", "compression", "rules"),
|
||||
},
|
||||
];
|
||||
for (const assetDir of runtimeAssetDirs) {
|
||||
@@ -343,66 +332,66 @@ for (const assetDir of runtimeAssetDirs) {
|
||||
}
|
||||
|
||||
// ── Step 10: Ensure data/ directory exists ──────────────────
|
||||
mkdirSync(join(APP_DIR, "data"), { recursive: true });
|
||||
mkdirSync(join(DIST_DIR, "data"), { recursive: true });
|
||||
|
||||
// ── Step 10.5: Copy @swc/helpers into standalone ───────────
|
||||
// Next.js standalone tracer sometimes omits @swc/helpers from app/node_modules/,
|
||||
// Next.js standalone tracer sometimes omits @swc/helpers from dist/node_modules/,
|
||||
// causing MODULE_NOT_FOUND at runtime. Always copy it explicitly.
|
||||
const swcHelpersSrc = join(ROOT, "node_modules", "@swc", "helpers");
|
||||
const swcHelpersDst = join(APP_DIR, "node_modules", "@swc", "helpers");
|
||||
const swcHelpersDst = join(DIST_DIR, "node_modules", "@swc", "helpers");
|
||||
if (existsSync(swcHelpersSrc) && !existsSync(swcHelpersDst)) {
|
||||
console.log(" 📋 Copying @swc/helpers to standalone app/node_modules...");
|
||||
mkdirSync(join(APP_DIR, "node_modules", "@swc"), { recursive: true });
|
||||
console.log(" 📋 Copying @swc/helpers to standalone dist/node_modules...");
|
||||
mkdirSync(join(DIST_DIR, "node_modules", "@swc"), { recursive: true });
|
||||
cpSync(swcHelpersSrc, swcHelpersDst, { recursive: true });
|
||||
console.log(" ✅ @swc/helpers included in standalone build.");
|
||||
}
|
||||
|
||||
// ── Step 10.6: Remove development-only residue from staged app/ ─────────────
|
||||
// ── Step 10.6: Remove development-only residue from staged dist/ ────────────
|
||||
for (const relativePath of APP_STAGING_REMOVAL_PATHS) {
|
||||
const targetPath = join(APP_DIR, relativePath);
|
||||
const targetPath = join(DIST_DIR, relativePath);
|
||||
if (existsSync(targetPath)) {
|
||||
console.log(` 🧹 Removing app/${relativePath} (not needed in npm package)...`);
|
||||
console.log(` 🧹 Removing dist/${relativePath} (not needed in npm package)...`);
|
||||
rmSync(targetPath, { recursive: true, force: true });
|
||||
console.log(` ✅ app/${relativePath} removed.`);
|
||||
console.log(` ✅ dist/${relativePath} removed.`);
|
||||
}
|
||||
}
|
||||
|
||||
// ── Step 10.7: Prune any staged app/ file outside the allowed runtime set ───
|
||||
const stagedFiles = walkFiles(APP_DIR);
|
||||
// ── Step 10.7: Prune any staged dist/ file outside the allowed runtime set ──
|
||||
const stagedFiles = walkFiles(DIST_DIR);
|
||||
const unexpectedStagedFiles = findUnexpectedArtifactPaths(stagedFiles, {
|
||||
exactPaths: APP_STAGING_ALLOWED_EXACT_PATHS,
|
||||
prefixPaths: APP_STAGING_ALLOWED_PATH_PREFIXES,
|
||||
});
|
||||
|
||||
if (unexpectedStagedFiles.length > 0) {
|
||||
console.log(" 🧹 Pruning unexpected files from staged app/...");
|
||||
console.log(" 🧹 Pruning unexpected files from staged dist/...");
|
||||
unexpectedStagedFiles.forEach((unexpectedPath: string) => {
|
||||
rmSync(join(APP_DIR, unexpectedPath), { force: true });
|
||||
console.log(` ✅ Removed app/${unexpectedPath}`);
|
||||
rmSync(join(DIST_DIR, unexpectedPath), { force: true });
|
||||
console.log(` ✅ Removed dist/${unexpectedPath}`);
|
||||
});
|
||||
removeEmptyDirectories(APP_DIR);
|
||||
removeEmptyDirectories(DIST_DIR);
|
||||
}
|
||||
|
||||
const remainingUnexpectedFiles = findUnexpectedArtifactPaths(walkFiles(APP_DIR), {
|
||||
const remainingUnexpectedFiles = findUnexpectedArtifactPaths(walkFiles(DIST_DIR), {
|
||||
exactPaths: APP_STAGING_ALLOWED_EXACT_PATHS,
|
||||
prefixPaths: APP_STAGING_ALLOWED_PATH_PREFIXES,
|
||||
});
|
||||
|
||||
if (remainingUnexpectedFiles.length > 0) {
|
||||
console.error("\n ❌ Staged app/ still contains unexpected publish artifacts:");
|
||||
remainingUnexpectedFiles.forEach((violation: string) => console.error(` - app/${violation}`));
|
||||
console.error("\n ❌ Staged dist/ still contains unexpected publish artifacts:");
|
||||
remainingUnexpectedFiles.forEach((violation: string) => console.error(` - dist/${violation}`));
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
// ── Done ───────────────────────────────────────────────────
|
||||
const appPkg = join(APP_DIR, "package.json");
|
||||
if (existsSync(appPkg)) {
|
||||
const pkg = JSON.parse(readFileSync(appPkg, "utf8"));
|
||||
const distPkg = join(DIST_DIR, "package.json");
|
||||
if (existsSync(distPkg)) {
|
||||
JSON.parse(readFileSync(distPkg, "utf8"));
|
||||
console.log(`\n ✅ Build complete!`);
|
||||
console.log(` App directory: app/`);
|
||||
console.log(` Server entry: app/server.js`);
|
||||
console.log(` Dist directory: dist/`);
|
||||
console.log(` Server entry: dist/server.js`);
|
||||
} else {
|
||||
console.log(`\n ✅ Build complete! (app/ ready for publish)`);
|
||||
console.log(`\n ✅ Build complete! (dist/ ready for publish)`);
|
||||
}
|
||||
|
||||
console.log("");
|
||||
|
||||
@@ -30,12 +30,12 @@ function runNpm(args: string[], stdio: "inherit" | "pipe" = "pipe"): string {
|
||||
|
||||
function ensureAppStagingReady(): void {
|
||||
const missingAppRequiredPaths = PACK_ARTIFACT_REQUIRED_PATHS.filter((requiredPath) =>
|
||||
requiredPath.startsWith("app/")
|
||||
requiredPath.startsWith("dist/")
|
||||
).filter((requiredPath) => !existsSync(join(ROOT, requiredPath)));
|
||||
|
||||
if (missingAppRequiredPaths.length === 0) return;
|
||||
|
||||
console.log("📦 app/ staging is missing required runtime files; running npm run build:cli...");
|
||||
console.log("📦 dist/ staging is missing required runtime files; running npm run build:cli...");
|
||||
runNpm(["run", "build:cli"], "inherit");
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user