// Assemble a self-contained server into dist/ from the Next standalone output. // `next build` with output:"standalone" produces: // .next/standalone/ (server.js + traced node_modules + .next/server) // .next/static/ (NOT inside standalone — must be copied) // We copy everything into dist/ so the published package ships one folder that // `node dist/server.js` can run. import { cp, mkdir, readdir, rm, writeFile } from "fs/promises"; import { existsSync } from "fs"; const STANDALONE = ".next/standalone"; const STATIC = ".next/static"; const PUBLIC = "public"; const OUT = "dist"; if (!existsSync(STANDALONE)) { console.error( `✗ ${STANDALONE} not found. Did "next build" run with output:"standalone"?` ); process.exit(1); } await rm(OUT, { recursive: true, force: true }); await cp(STANDALONE, OUT, { recursive: true }); await cp(STATIC, `${OUT}/.next/static`, { recursive: true }); if (existsSync(PUBLIC)) { await cp(PUBLIC, `${OUT}/public`, { recursive: true }); } // Turbopack represents serverExternalPackages with generated package aliases // under .next/node_modules. The standalone tree uses absolute symlinks for // those aliases, which npm omits from tarballs. Replace better-sqlite3 aliases // with portable shims and let npm install the real native dependency for the // target platform from the package root. const NEXT_NODE_MODULES = `${OUT}/.next/node_modules`; if (existsSync(NEXT_NODE_MODULES)) { for (const entry of await readdir(NEXT_NODE_MODULES)) { if (!entry.startsWith("better-sqlite3-")) continue; const alias = `${NEXT_NODE_MODULES}/${entry}`; await rm(alias, { recursive: true, force: true }); await mkdir(alias, { recursive: true }); await writeFile( `${alias}/package.json`, `${JSON.stringify( { name: entry, private: true, main: "index.js" }, null, 2 )}\n` ); await writeFile( `${alias}/index.js`, 'module.exports = require("better-sqlite3");\n' ); } } await rm(`${OUT}/node_modules/better-sqlite3`, { recursive: true, force: true, }); // Next copies the whole project root into the standalone bundle. Prune it down // to just the runtime essentials (drops src/, configs, and local notes so // they never get published). const KEEP = new Set([ "server.js", ".next", "node_modules", "package.json", "public", ]); for (const entry of await readdir(OUT)) { if (!KEEP.has(entry)) { await rm(`${OUT}/${entry}`, { recursive: true, force: true }); } } // Strip source maps (*.map): debug-only artifacts the published runtime never // needs. Removing them keeps the package lean and avoids shipping a stack-trace // → source mapping. Safe to delete — source maps are never required at runtime. let strippedMaps = 0; for (const entry of await readdir(OUT, { recursive: true })) { if (entry.endsWith(".map")) { await rm(`${OUT}/${entry}`, { force: true }); strippedMaps++; } } console.log( `✓ Assembled standalone server into ${OUT}/ ` + `(${strippedMaps} source map(s) stripped; run: node ${OUT}/server.js)` );