Track HiveGrid Cloud planning reference
This commit is contained in:
1 parent
b3d5ea6764
commit
e341c5938a
5784 files changed
+1104929
-3
No files matched your search
@@ -0,0 +1,137 @@
|
||||
#!/usr/bin/env node
|
||||
// Renders the markdown body for the Bundle-Budget PR comment.
|
||||
//
|
||||
// Inputs:
|
||||
// --current <path> JSON produced by `verify-bundle-size.mjs --json`
|
||||
// --baseline <path> Optional baseline JSON from main branch (for Δ column).
|
||||
// Missing/unreadable → Δ column shows "—".
|
||||
// --out <path> Write rendered markdown here (default: bundle-comment.md)
|
||||
//
|
||||
// Output marker: the comment starts with `<!-- bundle-budget-report -->` so
|
||||
// downstream upsert steps can identify and replace it without duplication.
|
||||
|
||||
import fs from "node:fs";
|
||||
|
||||
function parseArgs(argv) {
|
||||
const out = {};
|
||||
for (let i = 0; i < argv.length; i += 1) {
|
||||
const arg = argv[i];
|
||||
if (arg === "--current") out.current = argv[++i];
|
||||
else if (arg === "--baseline") out.baseline = argv[++i];
|
||||
else if (arg === "--out") out.out = argv[++i];
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
export function readReport(filePath) {
|
||||
if (!filePath) return null;
|
||||
try {
|
||||
if (!fs.existsSync(filePath)) return null;
|
||||
const raw = fs.readFileSync(filePath, "utf8");
|
||||
if (!raw.trim()) return null;
|
||||
return JSON.parse(raw);
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
const STATUS_BADGE = {
|
||||
ok: "[OK]",
|
||||
warning: "[WARN]",
|
||||
exceeds: "[EXCEED]",
|
||||
unbudgeted: "[?]",
|
||||
};
|
||||
|
||||
const KIND_LABEL = {
|
||||
route: "route",
|
||||
vendor: "vendor",
|
||||
};
|
||||
|
||||
export function formatKb(bytes) {
|
||||
if (typeof bytes !== "number") return "—";
|
||||
return `${(bytes / 1024).toFixed(1)} KB`;
|
||||
}
|
||||
|
||||
export function formatDelta(currentBytes, baselineBytes) {
|
||||
if (typeof baselineBytes !== "number") return "—";
|
||||
const diff = currentBytes - baselineBytes;
|
||||
const sign = diff >= 0 ? "+" : "";
|
||||
return `${sign}${(diff / 1024).toFixed(1)} KB`;
|
||||
}
|
||||
|
||||
export function indexBaseline(report) {
|
||||
const map = new Map();
|
||||
if (!report?.entries) return map;
|
||||
for (const e of report.entries) {
|
||||
map.set(e.key, e);
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
export function buildMarkdown(current, baseline) {
|
||||
const lines = [];
|
||||
lines.push("<!-- bundle-budget-report -->");
|
||||
lines.push("## Bundle Budget Report");
|
||||
lines.push("");
|
||||
if (!current || current.skipped) {
|
||||
lines.push("> No bundle artifacts measured (web build skipped or empty).");
|
||||
return lines.join("\n");
|
||||
}
|
||||
const totals = current.totals ?? { ok: 0, warning: 0, exceeds: 0, unbudgeted: 0 };
|
||||
lines.push(
|
||||
`**Summary:** ok=${totals.ok} - warning=${totals.warning} - exceeds=${totals.exceeds} - unbudgeted=${totals.unbudgeted}`,
|
||||
);
|
||||
lines.push("");
|
||||
if (totals.exceeds > 0) {
|
||||
lines.push(
|
||||
"> Some routes exceed their configured limit. This does **not** block the PR, but please check whether the growth is intentional.",
|
||||
);
|
||||
lines.push("");
|
||||
}
|
||||
|
||||
const baselineIndex = indexBaseline(baseline);
|
||||
const entries = [...(current.entries ?? [])];
|
||||
// sort: exceeds → warning → unbudgeted → ok, then biggest first
|
||||
const rank = { exceeds: 0, warning: 1, unbudgeted: 2, ok: 3 };
|
||||
entries.sort((a, b) => {
|
||||
if (rank[a.status] !== rank[b.status]) return rank[a.status] - rank[b.status];
|
||||
return b.gzipSize - a.gzipSize;
|
||||
});
|
||||
|
||||
lines.push("| Status | Kind | Route / Chunk | Size (gzip) | Limit | Δ from main |");
|
||||
lines.push("|---|---|---|---|---|---|");
|
||||
for (const e of entries) {
|
||||
const status = STATUS_BADGE[e.status] ?? e.status;
|
||||
const kind = KIND_LABEL[e.kind] ?? e.kind;
|
||||
const limit = typeof e.limitKb === "number" ? `${e.limitKb} KB` : "default";
|
||||
const base = baselineIndex.get(e.key);
|
||||
const delta = formatDelta(e.gzipSize, base?.gzipSize);
|
||||
lines.push(`| ${status} | ${kind} | \`${e.key}\` | ${formatKb(e.gzipSize)} | ${limit} | ${delta} |`);
|
||||
}
|
||||
lines.push("");
|
||||
lines.push("_Sizes are gzip-compressed. Budgets configured in `scripts/bundle-budgets.json`. Docs: `docs/architecture/bundle-budgets.md`._");
|
||||
return lines.join("\n");
|
||||
}
|
||||
|
||||
async function main() {
|
||||
const args = parseArgs(process.argv.slice(2));
|
||||
if (!args.current) {
|
||||
console.error("Usage: render-bundle-budget-comment.mjs --current <path> [--baseline <path>] [--out <path>]");
|
||||
process.exit(2);
|
||||
}
|
||||
const current = readReport(args.current);
|
||||
const baseline = readReport(args.baseline);
|
||||
const md = buildMarkdown(current, baseline);
|
||||
const outPath = args.out ?? "bundle-comment.md";
|
||||
fs.writeFileSync(outPath, `${md}\n`, "utf8");
|
||||
// Also surface on stdout for log visibility.
|
||||
process.stdout.write(`${md}\n`);
|
||||
}
|
||||
|
||||
const isMain = import.meta.url === `file://${process.argv[1]}`;
|
||||
if (isMain) {
|
||||
main().catch((err) => {
|
||||
console.error(err);
|
||||
process.exit(1);
|
||||
});
|
||||
}
|
||||
Reference in new issue
Block a user