From 5b484737bb83a47f4d0f9f942f39f2bbf7fee05d Mon Sep 17 00:00:00 2001 From: diegosouzapw Date: Wed, 3 Jun 2026 14:04:30 -0300 Subject: [PATCH] build(layer1): isolate Next output to .build/next; gitignore .build/ dist/ .next/ MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - next.config.mjs: distDir default ".next" → ".build/next" (NEXT_DIST_DIR override kept) - .gitignore: add /.build/, /dist/, /.next/; remove loose dist/ under #dependencies - scripts/build/paths.mjs: new shared module exporting ROOT, DIST_DIR, STANDALONE_DIR - scripts/build/build-next-isolated.mjs: default ".next" → ".build/next" in distDir, resetStandaloneOutput fallback, and pruneStandaloneArtifacts - scripts/build/prepublish.ts: NEXT_DIST default ".next" → ".build/next" - scripts/build/assembleStandalone.mjs: legacy syncStandalone* helpers updated to resolve distDir via NEXT_DIST_DIR || ".build/next" - tsconfig.json: Next.js auto-added .build/next/types includes (generated on build) Smoke: .build/next/standalone/server.js exists; BUNDLE_OK confirmed; no .next directory created. --- .gitignore | 8 +++++- next.config.mjs | 2 +- scripts/build/assembleStandalone.mjs | 16 ++++++----- scripts/build/build-next-isolated.mjs | 9 ++++-- scripts/build/paths.mjs | 41 +++++++++++++++++++++++++++ scripts/build/prepublish.ts | 8 +++--- tsconfig.json | 22 ++++++++++---- 7 files changed, 85 insertions(+), 21 deletions(-) create mode 100644 scripts/build/paths.mjs diff --git a/.gitignore b/.gitignore index f54c2e1463..a00b96ef47 100644 --- a/.gitignore +++ b/.gitignore @@ -31,7 +31,6 @@ docs/new-features/ # dependencies node_modules/ -dist/ *.map .DS_Store @@ -107,6 +106,13 @@ backup/ # 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/ +/.build/ +/dist/ +/.next/ + # Electron electron/dist-electron/ electron/node_modules/ diff --git a/next.config.mjs b/next.config.mjs index 632bf48b18..ce4252d2bf 100644 --- a/next.config.mjs +++ b/next.config.mjs @@ -4,7 +4,7 @@ import { dirname } from "node:path"; import { fileURLToPath } from "node:url"; const withNextIntl = createNextIntlPlugin("./src/i18n/request.ts"); -const distDir = process.env.NEXT_DIST_DIR || ".next"; +const distDir = process.env.NEXT_DIST_DIR || ".build/next"; const projectRoot = dirname(fileURLToPath(import.meta.url)); const scriptSrc = process.env.NODE_ENV === "development" diff --git a/scripts/build/assembleStandalone.mjs b/scripts/build/assembleStandalone.mjs index 27c7d1c4a0..afa4c09845 100644 --- a/scripts/build/assembleStandalone.mjs +++ b/scripts/build/assembleStandalone.mjs @@ -64,24 +64,25 @@ async function exists(targetPath) { /** * Copy native standalone assets (wreq-js rust/, better-sqlite3 build/). * - * The destination is derived as /.next/standalone/node_modules/... + * The destination is derived as //standalone/node_modules/... * for backward compatibility with existing callers and tests. * - * @param {string} rootDir - project root (node_modules are read from here; destination is .next/standalone inside rootDir) + * @param {string} rootDir - project root (node_modules are read from here) * @param {typeof fs} [fsImpl] - fs/promises implementation (injectable for tests) * @param {Console|{log:Function}} [log] - logger * @returns {Promise} true if any asset was copied */ export async function syncStandaloneNativeAssets(rootDir, fsImpl = fs, log = console) { - const standaloneRoot = path.join(rootDir, ".next", "standalone"); + const nextDistDir = process.env.NEXT_DIST_DIR || ".build/next"; + const standaloneRoot = path.join(rootDir, nextDistDir, "standalone"); return syncNativeAssetsToDir(rootDir, standaloneRoot, fsImpl, log); } /** - * Copy extra modules and sidecars into .next/standalone. + * Copy extra modules and sidecars into the Next.js standalone output. * - * The destination is derived as /.next/standalone/... - * for backward compatibility with existing callers and tests. + * The destination is derived as //standalone/... + * where distDir defaults to ".build/next" (overridable via NEXT_DIST_DIR). * * @param {string} rootDir - project root * @param {typeof fs} [fsImpl] - fs/promises implementation (injectable for tests) @@ -89,7 +90,8 @@ export async function syncStandaloneNativeAssets(rootDir, fsImpl = fs, log = con * @returns {Promise} true if any module was copied */ export async function syncStandaloneExtraModules(rootDir, fsImpl = fs, log = console) { - const standaloneRoot = path.join(rootDir, ".next", "standalone"); + const nextDistDir = process.env.NEXT_DIST_DIR || ".build/next"; + const standaloneRoot = path.join(rootDir, nextDistDir, "standalone"); return syncExtraModulesToDir(rootDir, standaloneRoot, fsImpl, log); } diff --git a/scripts/build/build-next-isolated.mjs b/scripts/build/build-next-isolated.mjs index 2acb8a294e..f7b1521f6c 100644 --- a/scripts/build/build-next-isolated.mjs +++ b/scripts/build/build-next-isolated.mjs @@ -19,7 +19,7 @@ import { */ const projectRoot = process.cwd(); -const distDir = path.resolve(process.env.NEXT_DIST_DIR || ".next"); +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) { @@ -121,7 +121,8 @@ export function resolveNextBuildEnv(baseEnv = process.env) { 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, ".next"); + 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; @@ -132,7 +133,9 @@ async function resetStandaloneOutput(rootDir = projectRoot, fsImpl = fs) { } export async function pruneStandaloneArtifacts(rootDir = projectRoot, fsImpl = fs) { - const standaloneRoot = path.join(rootDir, ".next", "standalone"); + 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) { diff --git a/scripts/build/paths.mjs b/scripts/build/paths.mjs new file mode 100644 index 0000000000..02f87a936f --- /dev/null +++ b/scripts/build/paths.mjs @@ -0,0 +1,41 @@ +#!/usr/bin/env node + +/** + * Shared build-path constants for OmniRoute build scripts. + * + * All build scripts that need to locate the Next.js distDir or the assembled + * standalone output dir should import from here so that the single source of + * truth (NEXT_DIST_DIR env + the ".build/next" default) is never scattered + * across multiple files. + * + * Layer 1 change: default distDir moved from ".next" to ".build/next". + * Consumers may still override via NEXT_DIST_DIR env var. + */ + +import path from "node:path"; +import { dirname } from "node:path"; +import { fileURLToPath } from "node:url"; + +const __dirname = dirname(fileURLToPath(import.meta.url)); + +/** + * Absolute path to the repository root. + * Derived from this file's location: scripts/build/paths.mjs → ../../ + */ +export const ROOT = path.resolve(__dirname, "..", ".."); + +/** + * Next.js distDir (where `next build` writes its output). + * Defaults to ".build/next"; overridable via NEXT_DIST_DIR env var. + * + * @type {string} - absolute path + */ +export const DIST_DIR = path.resolve(ROOT, process.env.NEXT_DIST_DIR || ".build/next"); + +/** + * Absolute path to the Next.js standalone output directory inside distDir. + * This is the raw output of `next build --output=standalone` before assembly. + * + * @type {string} - absolute path + */ +export const STANDALONE_DIR = path.join(DIST_DIR, "standalone"); diff --git a/scripts/build/prepublish.ts b/scripts/build/prepublish.ts index 2fbb77ee12..f8792cacde 100644 --- a/scripts/build/prepublish.ts +++ b/scripts/build/prepublish.ts @@ -3,7 +3,7 @@ /** * OmniRoute — Prepublish Build Script * - * Consumes the .next/standalone artifact produced by `npm run build` + * Consumes the .build/next/standalone artifact produced by `npm run build` * (build-next-isolated.mjs) and assembles the npm staging `app/` 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. @@ -119,12 +119,12 @@ if (existsSync(APP_DIR)) { // ── Step 2: Assert / trigger the Next.js standalone build ── // prepublish no longer runs its own `next build`. It consumes the -// .next/standalone artifact produced by `npm run build` (build-next-isolated.mjs). +// .build/next/standalone artifact produced by `npm run build` (build-next-isolated.mjs). // If the artifact is absent we invoke it exactly once. -const NEXT_DIST = process.env.NEXT_DIST_DIR || ".next"; +const NEXT_DIST = process.env.NEXT_DIST_DIR || ".build/next"; const standaloneServerJs = join(ROOT, NEXT_DIST, "standalone", "server.js"); if (!existsSync(standaloneServerJs)) { - console.log(" 🏗️ .next/standalone not found — running `npm run build` once..."); + console.log(" 🏗️ .build/next/standalone not found — running `npm run build` once..."); execFileSync(process.execPath, ["scripts/build/build-next-isolated.mjs"], { cwd: ROOT, stdio: "inherit", diff --git a/tsconfig.json b/tsconfig.json index 2a04942a4d..117cdf65d9 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,7 +1,11 @@ { "compilerOptions": { "target": "ES2022", - "lib": ["dom", "dom.iterable", "esnext"], + "lib": [ + "dom", + "dom.iterable", + "esnext" + ], "allowJs": true, "checkJs": false, "skipLibCheck": true, @@ -17,9 +21,15 @@ "incremental": true, "forceConsistentCasingInFileNames": true, "paths": { - "@/*": ["./src/*"], - "@omniroute/open-sse": ["./open-sse"], - "@omniroute/open-sse/*": ["./open-sse/*"] + "@/*": [ + "./src/*" + ], + "@omniroute/open-sse": [ + "./open-sse" + ], + "@omniroute/open-sse/*": [ + "./open-sse/*" + ] }, "plugins": [ { @@ -37,7 +47,9 @@ ".next/dev/types/**/*.ts", ".next/dev/dev/types/**/*.ts", ".next-playwright/types/**/*.ts", - ".next-playwright/dev/types/**/*.ts" + ".next-playwright/dev/types/**/*.ts", + ".build/next/types/**/*.ts", + ".build/next/dev/types/**/*.ts" ], "exclude": [ "node_modules",