Files
2026-08-16 18:24:52 +07:00

85 lines
2.9 KiB
JavaScript

// 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');