Add bundle-size gate script for CI

This commit is contained in:
2026-04-15 16:20:55 +03:00
parent 3c17459b4e
commit 1b0f15b082
3 changed files with 93 additions and 1 deletions
+3
View File
@@ -40,6 +40,9 @@ jobs:
- name: Build both targets
run: pnpm build:both
- name: Bundle size gate
run: pnpm bundle-size
- name: Validate MF manifest
run: |
MANIFEST=$(find dist/remote -name "mf-manifest.json" | head -1)
+2 -1
View File
@@ -18,7 +18,8 @@
"test": "vitest run",
"test:coverage": "vitest run --coverage",
"lint": "eslint \"src/**/*.{ts,tsx}\" --max-warnings 0",
"typecheck": "tsc --noEmit"
"typecheck": "tsc --noEmit",
"bundle-size": "node scripts/ci/bundle-size-gate.mjs"
},
"dependencies": {
"@microsoft/signalr": "^10.0.0",
+88
View File
@@ -0,0 +1,88 @@
/**
* Bundle-size gate for CI.
*
* Finds all .js files in dist/remote/static/js/, measures their gzip size,
* reports per-file and total, and fails if total exceeds the budget.
*
* Budgets:
* - Total remote bundle: <= 2000 kB gzip
*/
import { readFileSync, readdirSync, statSync } from "node:fs";
import { join } from "node:path";
import { gzipSync } from "node:zlib";
const BUDGET_TOTAL_KB = 2000;
const JS_DIR = "dist/remote/static/js";
function collectJsFiles(dir) {
const results = [];
let entries;
try {
entries = readdirSync(dir);
} catch {
return results;
}
for (const entry of entries) {
const full = join(dir, entry);
const stat = statSync(full, { throwIfNoEntry: false });
if (!stat) continue;
if (stat.isDirectory()) {
results.push(...collectJsFiles(full));
} else if (entry.endsWith(".js")) {
results.push(full);
}
}
return results;
}
const files = collectJsFiles(JS_DIR);
if (files.length === 0) {
console.error(`No .js files found in ${JS_DIR}. Did the build run?`);
process.exit(1);
}
let totalGzip = 0;
const rows = [];
for (const file of files) {
const raw = readFileSync(file);
const gzipped = gzipSync(raw);
const gzipKB = gzipped.length / 1024;
totalGzip += gzipKB;
rows.push({ file: file.replace("dist/remote/", ""), rawKB: (raw.length / 1024).toFixed(1), gzipKB: gzipKB.toFixed(1) });
}
rows.sort((a, b) => parseFloat(b.gzipKB) - parseFloat(a.gzipKB));
console.log("\n--- Bundle Size Report ---\n");
console.log(
"File".padEnd(60) +
"Raw (kB)".padStart(12) +
"Gzip (kB)".padStart(12)
);
console.log("-".repeat(84));
for (const r of rows) {
console.log(
r.file.padEnd(60) +
r.rawKB.padStart(12) +
r.gzipKB.padStart(12)
);
}
console.log("-".repeat(84));
console.log(
"TOTAL".padEnd(60) +
"".padStart(12) +
totalGzip.toFixed(1).padStart(12)
);
console.log(`\nBudget: ${BUDGET_TOTAL_KB} kB gzip`);
if (totalGzip > BUDGET_TOTAL_KB) {
console.error(`\nFAILED: Total gzip size ${totalGzip.toFixed(1)} kB exceeds budget of ${BUDGET_TOTAL_KB} kB`);
process.exit(1);
} else {
console.log(`\nPASSED: ${totalGzip.toFixed(1)} kB / ${BUDGET_TOTAL_KB} kB budget`);
}