| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270 |
- #!/usr/bin/env node
- const { execSync } = require("child_process");
- const fs = require("fs");
- const path = require("path");
- console.log("🔍 TypeScript 错误分析工具 v2\n");
- console.log("=".repeat(80) + "\n");
- /**
- * 运行 TypeScript 编译检查
- */
- function getTscErrors() {
- console.log("📊 正在运行 TypeScript 检查...\n");
- try {
- const result = execSync("npx tsc --noEmit -p tsconfig.app.json 2>&1", {
- cwd: path.resolve(__dirname, ".."),
- encoding: "utf-8",
- env: { ...process.env, NODE_NO_WARNINGS: "1" },
- });
- if (!result || result.trim().length === 0) {
- console.log("✅ 没有类型错误!");
- return "";
- }
- return result;
- } catch (error) {
- return error.stdout || error.stderr || error.message || "";
- }
- }
- /**
- * 保存原始输出
- */
- function saveRawOutput(output) {
- const rawPath = path.join(__dirname, "..", "tsc-raw-output.txt");
- fs.writeFileSync(rawPath, output, "utf-8");
- console.log(`📝 原始输出已保存: ${path.basename(rawPath)}\n`);
- }
- /**
- * 解析错误信息
- * 支持格式: src/file.tsx(1,22): error TS2307: Error message
- */
- function parseErrors(errorOutput) {
- const lines = errorOutput.split("\n");
- const errorsByType = new Map();
- let parsedCount = 0;
- console.log(`📄 总行数: ${lines.length}\n`);
- console.log("🔍 开始解析错误...\n");
- for (let i = 0; i < lines.length; i++) {
- const line = lines[i].trim();
- // 匹配格式: src/file.tsx(1,22): error TS2307: Error message
- const errorMatch = line.match(
- /^(.+?)\((\d+),(\d+)\):\s*error\s+(TS\d+):\s*(.+)$/
- );
- if (errorMatch) {
- parsedCount++;
- const [, filePath, lineNum, colNum, errorCode, errorMessage] = errorMatch;
- const errorObj = {
- filePath: filePath.trim(),
- lineNum: parseInt(lineNum),
- colNum: parseInt(colNum),
- errorCode,
- errorMessage: errorMessage.trim(),
- fullError: line,
- };
- // 按错误代码分组
- if (!errorsByType.has(errorCode)) {
- errorsByType.set(errorCode, {
- code: errorCode,
- count: 0,
- samples: [],
- description: errorMessage.trim(),
- });
- }
- const errorType = errorsByType.get(errorCode);
- errorType.count++;
- // 只保存前 2 个样本
- if (errorType.samples.length < 2) {
- errorType.samples.push(errorObj);
- }
- // 每 100 个输出进度
- if (parsedCount % 100 === 0) {
- process.stdout.write(`\r 已解析 ${parsedCount} 个错误...`);
- }
- }
- }
- if (parsedCount > 0) {
- process.stdout.write(`\r 已解析 ${parsedCount} 个错误...完成\n\n`);
- }
- return errorsByType;
- }
- /**
- * 生成 Markdown 报告
- */
- function generateMarkdownReport(errorsByType) {
- let report = "";
- report += "# TypeScript 错误分析报告\n\n";
- report += `生成时间: ${new Date().toLocaleString("zh-CN")}\n\n`;
- report += "=".repeat(80) + "\n\n";
- // 统计
- const totalErrors = Array.from(errorsByType.values()).reduce(
- (sum, e) => sum + e.count,
- 0
- );
- report += "## 📊 错误统计\n\n";
- report += `- **总错误数**: ${totalErrors}\n`;
- report += `- **错误类型**: ${errorsByType.size} 种\n\n`;
- // 排序
- const sortedErrors = Array.from(errorsByType.values()).sort(
- (a, b) => b.count - a.count
- );
- report += "### 错误分布\n\n";
- report += "| 排名 | 错误代码 | 数量 | 占比 | 描述 |\n";
- report += "|------|---------|------|------|------|\n";
- sortedErrors.forEach((error, index) => {
- const percentage = ((error.count / totalErrors) * 100).toFixed(1);
- const desc =
- error.description.substring(0, 60) +
- (error.description.length > 60 ? "..." : "");
- report += `| ${index + 1} | ${error.code} | ${error.count} | ${percentage}% | ${desc} |\n`;
- });
- report += "\n" + "=".repeat(80) + "\n\n";
- // 详细样本
- report += "## 📋 错误详细样本 (每种类型 2 个)\n\n";
- sortedErrors.forEach((error, index) => {
- report += `### ${index + 1}. ${error.code} - ${error.count} 个错误\n\n`;
- report += `**完整描述**: ${error.description}\n\n`;
- if (error.samples.length > 0) {
- report += "**样本:**\n\n";
- error.samples.forEach((sample, sampleIndex) => {
- report += `#### 样本 ${sampleIndex + 1}\n\n`;
- report += "```\n";
- report += `${sample.fullError}\n`;
- report += "```\n\n";
- });
- }
- report += "---\n\n";
- });
- return report;
- }
- /**
- * 生成 JSON 报告
- */
- function generateJsonReport(errorsByType) {
- const sortedErrors = Array.from(errorsByType.values()).sort(
- (a, b) => b.count - a.count
- );
- return JSON.stringify(
- {
- generatedAt: new Date().toISOString(),
- summary: {
- totalErrors: sortedErrors.reduce((sum, e) => sum + e.count, 0),
- errorTypes: errorsByType.size,
- },
- errors: sortedErrors.map((error) => ({
- code: error.code,
- count: error.count,
- description: error.description,
- percentage: (
- (error.count / sortedErrors.reduce((sum, e) => sum + e.count, 0)) *
- 100
- ).toFixed(2),
- samples: error.samples,
- })),
- },
- null,
- 2
- );
- }
- /**
- * 主函数
- */
- function main() {
- // 获取错误
- const errorOutput = getTscErrors();
- if (!errorOutput || errorOutput.trim().length === 0) {
- console.log("✅ 没有 TypeScript 错误!\n");
- return;
- }
- // 保存原始输出
- saveRawOutput(errorOutput);
- // 解析错误
- const errorsByType = parseErrors(errorOutput);
- if (errorsByType.size === 0) {
- console.log("⚠️ 无法解析错误信息\n");
- console.log("💡 请检查 tsc-raw-output.txt 查看原始输出\n");
- return;
- }
- // 生成报告
- console.log("📝 正在生成报告...\n");
- const mdReport = generateMarkdownReport(errorsByType);
- const jsonReport = generateJsonReport(errorsByType);
- // 保存文件
- const mdPath = path.join(__dirname, "..", "tsc-errors-report.md");
- const jsonPath = path.join(__dirname, "..", "tsc-errors-report.json");
- fs.writeFileSync(mdPath, mdReport, "utf-8");
- fs.writeFileSync(jsonPath, jsonReport, "utf-8");
- // 输出摘要
- const sortedErrors = Array.from(errorsByType.values()).sort(
- (a, b) => b.count - a.count
- );
- const totalErrors = sortedErrors.reduce((sum, e) => sum + e.count, 0);
- console.log("=".repeat(80));
- console.log("✅ 分析完成");
- console.log("=".repeat(80));
- console.log(`\n📊 总错误数: ${totalErrors}`);
- console.log(`📊 错误类型: ${errorsByType.size} 种\n`);
- console.log("Top 10 错误类型:\n");
- sortedErrors.slice(0, 10).forEach((error, index) => {
- const percentage = ((error.count / totalErrors) * 100).toFixed(1);
- const countStr = String(error.count).padStart(4);
- const pctStr = String(percentage).padStart(5);
- console.log(
- ` ${(index + 1).toString().padStart(2)}. ${error.code.padEnd(8)} ${countStr} 个 (${pctStr}%)`
- );
- });
- console.log("\n" + "=".repeat(80));
- console.log("📄 报告已生成:\n");
- console.log(` - tsc-errors-report.md`);
- console.log(` - tsc-errors-report.json`);
- console.log("=".repeat(80) + "\n");
- console.log("💡 下一步:\n");
- console.log(" cat ../tsc-errors-report.md\n");
- console.log(" 把报告内容发给 AI,获取修复方案\n");
- }
- main();
|