#!/usr/bin/env node /** * Bundle size gate — TASK-DEBT-015 * Fails the build if initial JS bundle exceeds 200KB gzipped. * Measures: index-*.js + vendor-react-*.js (critical path for first paint). */ import { readdirSync, readFileSync } from 'fs'; import { createGzip } from 'zlib'; import { dirname, join } from 'path'; import { fileURLToPath } from 'url'; const __dirname = dirname(fileURLToPath(import.meta.url)); const DIST = join(__dirname, '../dist_verification/assets'); const MAX_COMBINED_KB = 200; function gzipSize(bytes) { return new Promise((resolve, reject) => { const chunks = []; const gzip = createGzip(); gzip.on('data', (chunk) => chunks.push(chunk)); gzip.on('end', () => resolve(Buffer.concat(chunks).length)); gzip.on('error', reject); gzip.end(bytes); }); } async function main() { let dir; try { dir = readdirSync(DIST); } catch (e) { console.error('Run "npm run build" first. dist_verification/assets not found.'); process.exit(1); } const indexFile = dir.find((f) => f.startsWith('index-') && f.endsWith('.js')); const vendorReactFile = dir.find((f) => f.startsWith('vendor-react-') && f.endsWith('.js')); let totalGzip = 0; const results = []; for (const file of [indexFile, vendorReactFile]) { if (!file) continue; const path = join(DIST, file); const bytes = readFileSync(path); const sizeGzip = await gzipSize(bytes); totalGzip += sizeGzip; results.push({ file, kb: (sizeGzip / 1024).toFixed(1) }); } const totalKb = totalGzip / 1024; console.log('Bundle size (gzipped):'); results.forEach((r) => console.log(` ${r.file}: ${r.kb} kB`)); console.log(` Total (index + vendor-react): ${totalKb.toFixed(1)} kB`); if (totalKb > MAX_COMBINED_KB) { console.error(`\nFAIL: Initial bundle exceeds ${MAX_COMBINED_KB} kB (target: ORIGIN_PERFORMANCE_TARGETS §3.2)`); process.exit(1); } console.log(`\nPASS: Bundle under ${MAX_COMBINED_KB} kB limit.`); } main().catch((e) => { console.error(e); process.exit(1); });