// Bundles the compiled API (dist/, from `nest build`) into a self-contained .output/ that // docker/api.Dockerfile copies as-is, so building the server image installs nothing: // .output/dist/main.js, migrate.js, seed/seed.js all JS dependencies inlined // .output/node_modules/ native packages, Linux (musl, x64) builds only // The tsc output is bundled rather than src/ because esbuild does not emit the decorator // metadata Nest's dependency injection relies on. import { build } from "esbuild"; import { cpSync, existsSync, readFileSync, realpathSync, rmSync, writeFileSync } from "node:fs"; import path from "node:path"; import { fileURLToPath } from "node:url"; const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), ".."); const out = path.join(root, ".output"); // the docker/api.Dockerfile base image (node:*-alpine) const TARGET = { os: "linux", cpu: "x64", libc: "musl" }; // loaded at runtime from .output/node_modules (platform-specific binaries) const NATIVE = ["sharp", "@node-rs/argon2"]; // optional packages Nest and pg require inside try/catch; not installed, so left unresolved const OPTIONAL = [ "@nestjs/microservices", "@nestjs/microservices/*", "@nestjs/websockets", "@nestjs/websockets/*", "class-transformer", "class-transformer/*", "class-validator", "pg-native", ]; rmSync(out, { recursive: true, force: true }); await build({ absWorkingDir: root, entryPoints: { main: "dist/main.js", migrate: "dist/migrate.js", "seed/seed": "dist/seed/seed.js", }, outdir: path.join(out, "dist"), bundle: true, splitting: true, chunkNames: "chunks/[name]-[hash]", platform: "node", format: "esm", target: "node24", external: [...NATIVE, ...OPTIONAL], // Nest uses class names (logs, throttler keys) keepNames: true, // CommonJS dependencies call require() for Node built-ins (aliased: bundled code imports createRequire too) banner: { js: 'import { createRequire as __bundleCreateRequire } from "node:module"; const require = __bundleCreateRequire(import.meta.url);', }, legalComments: "none", logLevel: "warning", }); writeFileSync( path.join(out, "package.json"), JSON.stringify({ private: true, type: "module" }, null, 2) + "\n", ); /** Node's lookup of `name` from `fromDir`; follows pnpm's symlinks to the real package folder */ function findPackage(name, fromDir) { for (let dir = fromDir; ; dir = path.dirname(dir)) { const candidate = path.join(dir, "node_modules", name); if (existsSync(path.join(candidate, "package.json"))) return realpathSync(candidate); if (path.dirname(dir) === dir) return null; } } /** package.json os/cpu/libc fields, e.g. ["linux"] or ["!win32"] */ const allows = (list, value) => !list || ((list.includes(value) || list.every((v) => v.startsWith("!"))) && !list.includes(`!${value}`)); const copied = new Map(); function copyPackage(name, fromDir) { const dir = findPackage(name, fromDir); if (!dir) return; // optional dependency for another platform, not installed const pkg = JSON.parse(readFileSync(path.join(dir, "package.json"), "utf8")); if (!allows(pkg.os, TARGET.os) || !allows(pkg.cpu, TARGET.cpu) || !allows(pkg.libc, TARGET.libc)) return; if (copied.has(name)) { if (copied.get(name) !== pkg.version) throw new Error(`${name}: needs both ${copied.get(name)} and ${pkg.version}`); return; } copied.set(name, pkg.version); cpSync(dir, path.join(out, "node_modules", name), { recursive: true, dereference: true }); for (const dep of Object.keys({ ...pkg.dependencies, ...pkg.optionalDependencies })) copyPackage(dep, dir); } for (const name of NATIVE) copyPackage(name, root); for (const name of NATIVE) { if (!copied.has(name)) throw new Error(`${name} is not installed`); } // sharp and argon2 load their binary from a per-platform package; fail here, not in production for (const name of [ "@img/sharp-linuxmusl-x64", "@img/sharp-libvips-linuxmusl-x64", "@node-rs/argon2-linux-x64-musl", ]) { if (!copied.has(name)) { throw new Error( `${name} is missing; run \`pnpm install\` (pnpm-workspace.yaml supportedArchitectures adds it)`, ); } } console.log( `bundled into .output (native: ${[...copied].map(([n, v]) => `${n}@${v}`).join(", ")})`, );