// release/esbuild.config.mjs - Bundle the HOT-Step CPP server for portable distribution // // Usage: node release/esbuild.config.mjs // // Produces: release/staging/server/server.mjs // - All TypeScript/JS source bundled into a single ESM file // - better-sqlite3 marked external (native addon, can't be bundled) // - ffmpeg-static excluded (replaced by config-based path resolution) // - model-registry.json loaded via fs.readFileSync (not bundled inline) import esbuild from 'esbuild'; import path from 'path'; import { fileURLToPath } from 'url'; import { builtinModules } from 'module'; const __dirname = path.dirname(fileURLToPath(import.meta.url)); const projectRoot = path.resolve(__dirname, '..'); // Build complete list of Node.js built-in modules (with and without node: prefix) const nodeExternals = [ ...builtinModules, ...builtinModules.map(m => `node:${m}`), ]; const result = await esbuild.build({ entryPoints: [path.join(projectRoot, 'server/src/index.ts')], bundle: true, platform: 'node', target: 'node22', format: 'esm', outfile: path.join(projectRoot, 'release/staging/server/server.mjs'), // External: native addon + all Node.js built-in modules // Node builtins must be external because esbuild's ESM CJS-compat shim // can't handle dynamic require("node:events") from Express and other CJS deps. external: ['better-sqlite3', ...nodeExternals], // Plugin: redirect browser polyfill packages (with trailing slash) to Node.js // built-ins. e.g. require('process/') -> require('process') plugins: [{ name: 'node-polyfill-redirect', setup(build) { // Match requires like 'process/', 'string_decoder/', 'events/', etc. build.onResolve({ filter: /^(process|string_decoder|events|buffer|stream|util|path)\/$/ }, (args) => { const builtin = args.path.replace(/\/$/, ''); return { path: builtin, external: true }; }); }, }], // Don't minify - keep readable for debugging production issues minify: false, sourcemap: false, // Tree-shake unused exports treeShaking: true, // CJS interop banner: provide require(), __filename, __dirname for ESM bundle. // Express and other CJS dependencies need require() to work inside the ESM wrapper. banner: { js: [ '// HOT-Step CPP Server - bundled build', '// Generated by release/esbuild.config.mjs', '', '// CJS interop: provide require() for ESM bundle', 'import { createRequire as __bundleCreateRequire } from "module";', 'const require = __bundleCreateRequire(import.meta.url);', '', ].join('\n'), }, // Log build stats logLevel: 'info', metafile: true, }); // Print bundle size summary const outputs = result.metafile?.outputs || {}; for (const [file, info] of Object.entries(outputs)) { const sizeKB = (info.bytes / 1024).toFixed(1); console.log(` ${file}: ${sizeKB} KB`); } console.log('\n\u2705 Server bundle complete');