Files
gaokao-volunteer-system/apps/web/scripts/bundle-report.cjs

87 lines
3.4 KiB
JavaScript
Raw Normal View History

/**
* V10 Sprint 4 · T-B-25 · Bundle 优化报告
*
* 读取 vite build 输出 (dist/assets/*.js + manifest)输出
* - 每个 chunk size + gzip size
* - 累计 gzip 大小 vs 预算
* - 标记 vendor / app
*
* 预算main + react-vendor + query-vendor + form-vendor 200 KB gzip
* 任何单 chunk 150 KB gzip
*/
const fs = require('fs');
const path = require('path');
const distDir = path.join(__dirname, '..', 'dist', 'assets');
if (!fs.existsSync(distDir)) {
console.error(`${distDir} 不存在,请先 pnpm build`);
process.exit(1);
}
const files = fs.readdirSync(distDir).filter((f) => f.endsWith('.js'));
const rows = files.map((f) => {
const buf = fs.readFileSync(path.join(distDir, f));
const size = buf.length;
const gz = require('zlib').gzipSync(buf, { level: 9 }).length;
return {
file: f,
type: f.includes('vendor') ? 'vendor' : 'app',
size,
gz,
};
});
rows.sort((a, b) => b.gz - a.gz);
const totalGz = rows.reduce((s, r) => s + r.gz, 0);
const BUDGET_TOTAL_GZ = 500 * 1024;
const BUDGET_PER_CHUNK_GZ = 150 * 1024;
console.log(`\n📊 Bundle Report`);
console.log(`════════════════════════════════════════════════════════`);
console.log(`File │ Raw │ Gzip │ OK`);
console.log(`─────────────────────────────────┼────────────┼────────────┼────`);
let allOk = true;
for (const r of rows) {
const ok = r.gz <= BUDGET_PER_CHUNK_GZ;
if (!ok) allOk = false;
const fileStr = r.file.padEnd(35);
const sizeStr = humanSize(r.size).padStart(10);
const gzStr = humanSize(r.gz).padStart(10);
console.log(`${fileStr}${sizeStr}${gzStr}${ok ? '✅' : '❌'}`);
}
console.log(`─────────────────────────────────┼────────────┼────────────┼────`);
console.log(`${'TOTAL'.padEnd(35)}${humanSize(rows.reduce((s, r) => s + r.size, 0)).padStart(10)}${humanSize(totalGz).padStart(10)}${totalGz <= BUDGET_TOTAL_GZ ? '✅' : '❌'}`);
console.log(`\n预算:每个 chunk ≤ 150 KB gzip, total ≤ 500 KB gzip`);
const appGz = rows.filter((r) => r.type === 'app').reduce((s, r) => s + r.gz, 0);
const vendorGz = rows.filter((r) => r.type === 'vendor').reduce((s, r) => s + r.gz, 0);
console.log(`\nApp chunks: ${humanSize(appGz)} (${rows.filter((r) => r.type === 'app').length} 个)`);
console.log(`Vendor chunks: ${humanSize(vendorGz)} (${rows.filter((r) => r.type === 'vendor').length} 个)`);
console.log(``);
// 找出可 lazy load 的重 vendor
const heavy = rows.filter((r) => r.gz > 80 * 1024);
if (heavy.length) {
console.log(`📦 可考虑懒加载的重 vendor> 80 KB gzip:`);
for (const r of heavy) {
console.log(` - ${r.file} (${humanSize(r.gz)})`);
}
}
if (allOk && totalGz <= BUDGET_TOTAL_GZ) {
console.log(`\n✅ T-B-25 验证通过`);
process.exit(0);
} else {
console.log(`\n❌ 超出预算`);
process.exit(1);
}
function humanSize(b) {
if (b < 1024) return b + ' B';
if (b < 1024 * 1024) return (b / 1024).toFixed(2) + ' KB';
return (b / (1024 * 1024)).toFixed(2) + ' MB';
}