#!/usr/bin/env node /** * Production build: one Node bundle (all deps inlined) + minified single-file Web UI. * * Outputs: * dist/server.js — main app (Torznab, qBt, MCP, UI API) * dist/mcp.js — optional MCP-only process * dist/ui/index.html — Search/Queue UI (CSS+JS inlined) */ import * as esbuild from 'esbuild'; import fs from 'node:fs'; import path from 'node:path'; import { fileURLToPath } from 'node:url'; const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..'); const dist = path.join(root, 'dist'); const distUi = path.join(dist, 'ui'); fs.rmSync(dist, { recursive: true, force: true }); fs.mkdirSync(distUi, { recursive: true }); const nodeBundle = { bundle: true, platform: 'node', target: 'node20', format: 'cjs', minify: true, legalComments: 'none', sourcemap: false, // Keep Node builtins external; pull npm deps into the file. packages: 'bundle', }; async function bundleServer() { const result = await esbuild.build({ ...nodeBundle, entryPoints: [path.join(root, 'src/index.js')], outfile: path.join(dist, 'server.js'), metafile: true, logLevel: 'info', }); const out = result.metafile.outputs[Object.keys(result.metafile.outputs)[0]]; console.log(`server.js ${(out.bytes / 1024 / 1024).toFixed(2)} MB`); } async function bundleMcp() { const result = await esbuild.build({ ...nodeBundle, entryPoints: [path.join(root, 'src/mcp.js')], outfile: path.join(dist, 'mcp.js'), banner: { js: '#!/usr/bin/env node\n' }, metafile: true, logLevel: 'info', }); const out = result.metafile.outputs[Object.keys(result.metafile.outputs)[0]]; console.log(`mcp.js ${(out.bytes / 1024 / 1024).toFixed(2)} MB`); try { fs.chmodSync(path.join(dist, 'mcp.js'), 0o755); } catch { /* windows */ } } async function bundleUi() { const cssSrc = fs.readFileSync(path.join(root, 'ui/styles.css'), 'utf8'); const jsSrc = fs.readFileSync(path.join(root, 'ui/scripts.js'), 'utf8'); const htmlSrc = fs.readFileSync(path.join(root, 'ui/index.html'), 'utf8'); const css = await esbuild.transform(cssSrc, { loader: 'css', minify: true }); const js = await esbuild.transform(jsSrc, { loader: 'js', minify: true, target: ['es2020'], }); // Prefer a single HTML payload (no extra static requests) for the tiny UI. let html = htmlSrc .replace(/\s*/i, '') .replace(/\n`, ); const outHtml = path.join(distUi, 'index.html'); fs.writeFileSync(outHtml, html); console.log(`ui/index.html ${(Buffer.byteLength(html) / 1024).toFixed(1)} KB (inlined css+js)`); } await bundleServer(); await bundleMcp(); await bundleUi(); console.log('build ok → dist/');