const { execSync } = require('child_process');
const fs = require('fs');
const path = require('path');
const ROOT = path.resolve(__dirname, '..');
const VENDOR = path.join(ROOT, 'web', 'static', 'vendor');
const FONTS_OUT = path.join(VENDOR, 'fonts');
const FONTS_SRC = path.join(ROOT, 'node_modules', '@kattebak', 'sterk', 'assets', 'fonts');
fs.mkdirSync(VENDOR, { recursive: true });
fs.mkdirSync(FONTS_OUT, { recursive: true });
const XTERM_PATCH = path.join(ROOT, 'patches', 'xterm-composition-helper.patch');
const XTERM_TARGET = path.join(ROOT, 'node_modules', '@xterm', 'xterm', 'src',
'browser', 'input', 'CompositionHelper.ts');
const XTERM_PKG = path.join(ROOT, 'node_modules', '@xterm', 'xterm');
if (fs.existsSync(XTERM_PKG)) {
console.log('[build] Applying xterm patch...');
try {
execSync(`patch --dry-run --forward --reject-file=- -p0 -i "${XTERM_PATCH}" "${XTERM_TARGET}"`, {
cwd: ROOT, stdio: 'pipe',
});
execSync(`patch --forward --reject-file=- -p0 -i "${XTERM_PATCH}" "${XTERM_TARGET}"`, {
cwd: ROOT, stdio: 'pipe',
});
console.log('[build] xterm patch applied.');
} catch (e) {
const out = (e.stdout?.toString() || '') + (e.stderr?.toString() || '');
if (out.includes('Reversed') || out.includes('previously applied')) {
console.log('[build] xterm patch already applied.');
} else {
console.error('[build] xterm patch failed:', out.trim());
process.exit(1);
}
}
console.log('[build] Bundling xterm...');
execSync([
'npx esbuild',
path.join(ROOT, 'web', 'src', 'xterm-entry.js'),
'--bundle',
'--format=iife',
'--minify',
'--sourcemap',
'--target=es2020',
`--outfile=${path.join(VENDOR, 'xterm.bundle.js')}`,
].join(' '), { cwd: ROOT, stdio: 'inherit' });
const xtermCssSrc = path.join(XTERM_PKG, 'css', 'xterm.css');
const xtermCssDest = path.join(VENDOR, 'xterm.css');
if (fs.existsSync(xtermCssSrc)) {
fs.copyFileSync(xtermCssSrc, xtermCssDest);
console.log('[build] xterm.css copied.');
} else {
console.warn(`[build] WARN: xterm.css not found at ${xtermCssSrc}`);
}
} else {
console.warn(`[build] WARN: @xterm/xterm not installed at ${XTERM_PKG}; skipping xterm bundle. Run \`npm install\` first.`);
}
if (fs.existsSync(FONTS_SRC)) {
for (const entry of fs.readdirSync(FONTS_SRC)) {
if (!entry.endsWith('.woff2') && entry !== 'LICENSES.txt') continue;
fs.copyFileSync(path.join(FONTS_SRC, entry), path.join(FONTS_OUT, entry));
}
const fonts = fs.readdirSync(FONTS_OUT).filter((f) => f.endsWith('.woff2'));
console.log(`[build] Copied ${fonts.length} woff2 fonts -> web/static/vendor/fonts/`);
} else {
console.warn(`[build] WARN: sterk fonts dir not found at ${FONTS_SRC}`);
}
console.log('[build] Bundling sterk...');
execSync([
'npx esbuild',
path.join(ROOT, 'web', 'src', 'sterk-entry.js'),
'--bundle',
'--format=iife',
'--minify',
'--sourcemap',
'--target=es2020',
`--define:import.meta.url='"http://sterk.invalid/"'`,
`--outfile=${path.join(VENDOR, 'sterk.bundle.js')}`,
].join(' '), { cwd: ROOT, stdio: 'inherit' });
console.log('[build] Done.');