claude-code/scripts/health-check.ts
weiqianpu cf1707bbb0 feat: 开源发布 — 完整 README、MIT 许可证、22+ 大模型厂商支持
- 重写 README.md:项目简介、快速开始、22+ 厂商列表、功能特性、架构说明、配置指南、FAQ、贡献指南
- 新增 MIT LICENSE
- 包含全部源码与文档
2026-04-02 02:34:24 +00:00

164 lines
5.3 KiB
TypeScript
Executable File
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/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);