#!/usr/bin/env node /** * Build script for Codeman. * Extracted from the package.json one-liner for readability and debuggability. * * Steps: * 1. TypeScript compilation * 2. Copy static assets (web/public, templates) * 3. Build vendor xterm bundles * 4. Minify frontend assets (app.js, styles.css, mobile.css) * 5. Compress with gzip + brotli */ import { execSync } from 'child_process'; import { appendFileSync } from 'fs'; import { fileURLToPath } from 'url'; import { join } from 'path'; const ROOT = join(fileURLToPath(import.meta.url), '..', '..'); function run(label, cmd) { console.log(`\n[build] ${label}`); execSync(cmd, { stdio: 'inherit', cwd: ROOT, shell: true }); } // 1. TypeScript compilation run('tsc', 'tsc'); run('chmod dist/index.js', 'chmod +x dist/index.js'); // 2. Copy static assets run('prepare dirs', 'mkdir -p dist/web dist/templates dist/web/public/vendor'); run('copy web assets', 'cp -r src/web/public dist/web/'); run('copy template', 'cp src/templates/case-template.md dist/templates/'); // 3. Vendor xterm bundles run('xterm css', 'cp node_modules/xterm/css/xterm.css dist/web/public/vendor/'); run('xterm js', 'npx esbuild node_modules/xterm/lib/xterm.js --minify --outfile=dist/web/public/vendor/xterm.min.js'); run('xterm-addon-fit', 'npx esbuild node_modules/xterm-addon-fit/lib/xterm-addon-fit.js --minify --outfile=dist/web/public/vendor/xterm-addon-fit.min.js'); run('xterm-addon-webgl', 'cp node_modules/xterm-addon-webgl/lib/xterm-addon-webgl.js dist/web/public/vendor/xterm-addon-webgl.min.js'); run('xterm-addon-unicode11', 'npx esbuild node_modules/xterm-addon-unicode11/lib/xterm-addon-unicode11.js --minify --outfile=dist/web/public/vendor/xterm-addon-unicode11.min.js'); run('xterm-zerolag-input', 'npx esbuild packages/xterm-zerolag-input/src/zerolag-input-addon.ts --bundle --minify --format=iife --global-name=XtermZerolagInput --outfile=dist/web/public/vendor/xterm-zerolag-input.js'); // Append global aliases so app.js can use `new LocalEchoOverlay(terminal)` appendFileSync( join(ROOT, 'dist/web/public/vendor/xterm-zerolag-input.js'), '\n// Global aliases for browser usage\n' + 'if(typeof window!=="undefined"){' + 'window.ZerolagInputAddon=XtermZerolagInput.ZerolagInputAddon;' + 'window.LocalEchoOverlay=class extends XtermZerolagInput.ZerolagInputAddon{' + 'constructor(terminal){' + 'super({prompt:{type:"character",char:"\\u276f",offset:2}});' + 'this.activate(terminal);' + '}' + '};' + '}\n' ); // 4. Minify frontend assets run('minify app.js', 'npx esbuild dist/web/public/app.js --minify --drop:console --outfile=dist/web/public/app.js --allow-overwrite'); run('minify styles.css', 'npx esbuild dist/web/public/styles.css --minify --outfile=dist/web/public/styles.css --allow-overwrite'); run('minify mobile.css', 'npx esbuild dist/web/public/mobile.css --minify --outfile=dist/web/public/mobile.css --allow-overwrite'); // 5. Compress with gzip + brotli run( 'compress', `for f in dist/web/public/*.js dist/web/public/*.css dist/web/public/*.html dist/web/public/vendor/*.js dist/web/public/vendor/*.css; do` + ` [ -f "$f" ] && gzip -9 -k -f "$f" && { brotli -9 -k -f "$f" 2>/dev/null || true; }; done` ); console.log('\n✓ Build complete');