| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163 |
- #!/usr/bin/env bun
- /**
- * 代码健康度检查脚本
- *
- * 汇总项目各维度指标,输出健康度报告:
- * - 代码规模(文件数、代码行数)
- * - Lint 问题数(Biome)
- * - 测试结果(Bun test)
- * - 冗余代码(Knip)
- * - 构建状态
- */
- import { $ } from "bun";
- const DIVIDER = "─".repeat(60);
- interface Metric {
- label: string;
- value: string | number;
- status: "ok" | "warn" | "error" | "info";
- }
- const metrics: Metric[] = [];
- function add(label: string, value: string | number, status: Metric["status"] = "info") {
- metrics.push({ label, value, status });
- }
- function icon(status: Metric["status"]): string {
- switch (status) {
- case "ok":
- return "[OK]";
- case "warn":
- return "[!!]";
- case "error":
- return "[XX]";
- case "info":
- return "[--]";
- }
- }
- // ---------------------------------------------------------------------------
- // 1. 代码规模
- // ---------------------------------------------------------------------------
- async function checkCodeSize() {
- const tsFiles = await $`find src -name '*.ts' -o -name '*.tsx' | grep -v node_modules`.text();
- const fileCount = tsFiles.trim().split("\n").filter(Boolean).length;
- add("TypeScript 文件数", fileCount, "info");
- const loc = await $`find src -name '*.ts' -o -name '*.tsx' | grep -v node_modules | xargs wc -l | tail -1`.text();
- const totalLines = loc.trim().split(/\s+/)[0] ?? "?";
- add("总代码行数 (src/)", totalLines, "info");
- }
- // ---------------------------------------------------------------------------
- // 2. Lint 检查
- // ---------------------------------------------------------------------------
- async function checkLint() {
- try {
- const result = await $`bunx biome check src/ 2>&1`.quiet().nothrow().text();
- const errorMatch = result.match(/Found (\d+) errors?/);
- const warnMatch = result.match(/Found (\d+) warnings?/);
- const errors = errorMatch ? Number.parseInt(errorMatch[1]) : 0;
- const warnings = warnMatch ? Number.parseInt(warnMatch[1]) : 0;
- add("Lint 错误", errors, errors === 0 ? "ok" : errors < 100 ? "warn" : "info");
- add("Lint 警告", warnings, warnings === 0 ? "ok" : "info");
- } catch {
- add("Lint 检查", "执行失败", "error");
- }
- }
- // ---------------------------------------------------------------------------
- // 3. 测试
- // ---------------------------------------------------------------------------
- async function checkTests() {
- try {
- const result = await $`bun test 2>&1`.quiet().nothrow().text();
- const passMatch = result.match(/(\d+) pass/);
- const failMatch = result.match(/(\d+) fail/);
- const pass = passMatch ? Number.parseInt(passMatch[1]) : 0;
- const fail = failMatch ? Number.parseInt(failMatch[1]) : 0;
- add("测试通过", pass, pass > 0 ? "ok" : "warn");
- add("测试失败", fail, fail === 0 ? "ok" : "error");
- } catch {
- add("测试", "执行失败", "error");
- }
- }
- // ---------------------------------------------------------------------------
- // 4. 冗余代码
- // ---------------------------------------------------------------------------
- async function checkUnused() {
- try {
- const result = await $`bunx knip-bun 2>&1`.quiet().nothrow().text();
- const unusedFiles = result.match(/Unused files \((\d+)\)/);
- const unusedExports = result.match(/Unused exports \((\d+)\)/);
- const unusedDeps = result.match(/Unused dependencies \((\d+)\)/);
- add("未使用文件", unusedFiles?.[1] ?? "0", "info");
- add("未使用导出", unusedExports?.[1] ?? "0", "info");
- add("未使用依赖", unusedDeps?.[1] ?? "0", unusedDeps && Number(unusedDeps[1]) > 0 ? "warn" : "ok");
- } catch {
- add("冗余代码检查", "执行失败", "error");
- }
- }
- // ---------------------------------------------------------------------------
- // 5. 构建
- // ---------------------------------------------------------------------------
- async function checkBuild() {
- try {
- const result = await $`bun run build 2>&1`.quiet().nothrow();
- if (result.exitCode === 0) {
- // 获取产物大小
- const stat = Bun.file("dist/cli.js");
- const mb = (stat.size / 1024 / 1024).toFixed(1);
- const size = `${mb} MB`;
- add("构建状态", "成功", "ok");
- add("产物大小 (dist/cli.js)", size, "info");
- } else {
- add("构建状态", "失败", "error");
- }
- } catch {
- add("构建", "执行失败", "error");
- }
- }
- // ---------------------------------------------------------------------------
- // Run
- // ---------------------------------------------------------------------------
- console.log("");
- console.log(DIVIDER);
- console.log(" 代码健康度检查报告");
- console.log(` ${new Date().toLocaleString("zh-CN")}`);
- console.log(DIVIDER);
- await checkCodeSize();
- await checkLint();
- await checkTests();
- await checkUnused();
- await checkBuild();
- console.log("");
- for (const m of metrics) {
- const tag = icon(m.status);
- console.log(` ${tag} ${m.label.padEnd(20)} ${m.value}`);
- }
- const errorCount = metrics.filter((m) => m.status === "error").length;
- const warnCount = metrics.filter((m) => m.status === "warn").length;
- console.log("");
- console.log(DIVIDER);
- if (errorCount > 0) {
- console.log(` 结果: ${errorCount} 个错误, ${warnCount} 个警告`);
- } else if (warnCount > 0) {
- console.log(` 结果: 无错误, ${warnCount} 个警告`);
- } else {
- console.log(" 结果: 全部通过");
- }
- console.log(DIVIDER);
- console.log("");
- process.exit(errorCount > 0 ? 1 : 0);
|