// After next build, the standalone server needs the static files beside it; Next leaves // that copy to us (Next docs, config/output.md). Also drops in the CLI's own modules, so // the published package can run from anywhere (vrek iss-ej4ahga). import { access, cp, mkdir, readdir, rm, stat, writeFile } from "node:fs/promises"; import path from "node:path"; const root = process.cwd(); const standalone = path.join(root, ".next", "standalone"); const exists = (file) => access(file).then(() => true, () => false); /** Bytes under a path, so the build can say what it dropped. */ async function size(target) { const info = await stat(target); if (!info.isDirectory()) return info.size; let total = 0; for (const entry of await readdir(target)) total += await size(path.join(target, entry)); return total; } if (!(await exists(standalone))) { console.error("No .next/standalone: is output: 'standalone' still set in next.config.ts?"); process.exit(1); } await mkdir(path.join(standalone, ".next"), { recursive: true }); await cp(path.join(root, ".next", "static"), path.join(standalone, ".next", "static"), { recursive: true }); if (await exists(path.join(root, "public"))) { await cp(path.join(root, "public"), path.join(standalone, "public"), { recursive: true }); } // Next's tracing sweeps in things the app never runs: the MediaMTX binary that happens to // sit in bin/, and sharp's platform binaries, which are only for next/image (unused here). // outputFileTracingExcludes didn't keep them out, so they go now, before publishing. const prune = ["bin", "node_modules/@img", "node_modules/sharp"]; let freed = 0; for (const name of prune) { const target = path.join(standalone, name); if (!(await exists(target))) continue; freed += await size(target); await rm(target, { recursive: true, force: true }); } // The compiled CLI modules are ES modules; without this Node would read them as CommonJS. await writeFile(path.join(root, "dist", "package.json"), '{\n "type": "module"\n}\n'); console.log( `Bundled the standalone server with its static files` + (freed ? `, and dropped ${(freed / 1024 / 1024).toFixed(0)} MB it doesn't need.` : "."), );