vite-plugin-ai-diagnostic 1.0.6 → 1.0.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -1,5 +1,6 @@
1
1
  // src/index.ts
2
2
  import fs2 from "fs";
3
+ import path2 from "path";
3
4
 
4
5
  // src/langgraph.ts
5
6
  import { StateGraph, END, START } from "@langchain/langgraph";
@@ -617,9 +618,388 @@ ${report.fixed ? `✅ **已自动修复**
617
618
  fs.writeFileSync(reportPath, JSON.stringify(jsonReport, null, 2), "utf-8");
618
619
  console.log(`📄 JSON 报告已生成: ${reportPath}`);
619
620
  }
621
+ /**
622
+ * 生成多错误合并报告
623
+ */
624
+ static async generateMultiReport(reports, options = {}) {
625
+ const multiReport = {
626
+ timestamp: (/* @__PURE__ */ new Date()).toLocaleString("zh-CN"),
627
+ totalErrors: reports.length,
628
+ errors: reports
629
+ };
630
+ if (options.html) {
631
+ await this.generateMultiHTMLReport(multiReport);
632
+ }
633
+ if (options.markdown) {
634
+ await this.generateMultiMarkdownReport(multiReport);
635
+ }
636
+ if (options.json) {
637
+ await this.generateMultiJSONReport(multiReport);
638
+ }
639
+ }
640
+ /**
641
+ * 生成多错误 HTML 报告
642
+ */
643
+ static async generateMultiHTMLReport(report) {
644
+ const errorsHtml = report.errors.map(
645
+ (error, index) => `
646
+ <div class="error-item">
647
+ <div class="error-number">错误 ${index + 1} / ${report.totalErrors}</div>
648
+ <div class="error-box">
649
+ <span class="error-type">${error.error.type}</span>
650
+ <div class="error-message">${this.escapeHtml(
651
+ error.error.message
652
+ )}</div>
653
+ <div class="error-file">📂 ${error.error.file}</div>
654
+ ${error.error.stack ? `<div class="stack-trace">${this.escapeHtml(
655
+ error.error.stack
656
+ )}</div>` : ""}
657
+ </div>
658
+
659
+ <div class="section">
660
+ <div class="section-title">🔍 AI 分析</div>
661
+ <div class="analysis-box">
662
+ ${this.formatText(error.analysis)}
663
+ </div>
664
+ </div>
665
+
666
+ <div class="section">
667
+ <div class="section-title">💡 修复建议</div>
668
+ <div class="suggestion-box">
669
+ ${this.formatText(error.suggestion)}
670
+ </div>
671
+ </div>
672
+
673
+ <div class="section">
674
+ <div class="section-title">🔧 修复状态</div>
675
+ ${error.fixed ? `<div class="fixed-badge">✅ 已自动修复</div>
676
+ <div style="margin-top: 10px; color: #666;">修复文件: <code>${error.fixedFilePath}</code></div>` : `<div class="not-fixed-badge">⚠️ 未自动修复</div>
677
+ <div style="margin-top: 10px; color: #666;">请根据上述建议手动修复</div>`}
678
+ </div>
679
+ </div>
680
+ `
681
+ ).join("");
682
+ const html = `
683
+ <!DOCTYPE html>
684
+ <html lang="zh-CN">
685
+ <head>
686
+ <meta charset="UTF-8">
687
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
688
+ <title>AI 诊断报告 - ${report.totalErrors} 个错误</title>
689
+ <style>
690
+ * { margin: 0; padding: 0; box-sizing: border-box; }
691
+ body {
692
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
693
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
694
+ padding: 20px;
695
+ min-height: 100vh;
696
+ }
697
+ .container {
698
+ max-width: 1000px;
699
+ margin: 0 auto;
700
+ background: white;
701
+ border-radius: 12px;
702
+ box-shadow: 0 8px 32px rgba(0,0,0,0.2);
703
+ overflow: hidden;
704
+ }
705
+ .header {
706
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
707
+ color: white;
708
+ padding: 30px;
709
+ text-align: center;
710
+ }
711
+ .header h1 {
712
+ font-size: 32px;
713
+ margin-bottom: 10px;
714
+ display: flex;
715
+ align-items: center;
716
+ justify-content: center;
717
+ gap: 10px;
718
+ }
719
+ .header .time {
720
+ font-size: 14px;
721
+ opacity: 0.9;
722
+ }
723
+ .header .error-count {
724
+ font-size: 18px;
725
+ margin-top: 10px;
726
+ background: rgba(255,255,255,0.2);
727
+ padding: 8px 16px;
728
+ border-radius: 20px;
729
+ display: inline-block;
730
+ }
731
+ .content {
732
+ padding: 30px;
733
+ }
734
+ .error-item {
735
+ margin-bottom: 40px;
736
+ padding-bottom: 40px;
737
+ border-bottom: 2px dashed #e2e8f0;
738
+ }
739
+ .error-item:last-child {
740
+ border-bottom: none;
741
+ margin-bottom: 0;
742
+ padding-bottom: 0;
743
+ }
744
+ .error-number {
745
+ font-size: 16px;
746
+ font-weight: bold;
747
+ color: #667eea;
748
+ margin-bottom: 15px;
749
+ padding: 8px 16px;
750
+ background: #f0f9ff;
751
+ border-radius: 8px;
752
+ display: inline-block;
753
+ }
754
+ .section {
755
+ margin-bottom: 20px;
756
+ }
757
+ .section-title {
758
+ font-size: 18px;
759
+ font-weight: bold;
760
+ color: #333;
761
+ margin-bottom: 12px;
762
+ padding-bottom: 8px;
763
+ border-bottom: 2px solid #667eea;
764
+ display: flex;
765
+ align-items: center;
766
+ gap: 8px;
767
+ }
768
+ .error-box {
769
+ background: #fff5f5;
770
+ border-left: 4px solid #f56565;
771
+ padding: 20px;
772
+ border-radius: 8px;
773
+ margin-bottom: 20px;
774
+ }
775
+ .error-type {
776
+ display: inline-block;
777
+ background: #f56565;
778
+ color: white;
779
+ padding: 4px 12px;
780
+ border-radius: 12px;
781
+ font-size: 12px;
782
+ font-weight: bold;
783
+ margin-bottom: 10px;
784
+ }
785
+ .error-message {
786
+ font-size: 16px;
787
+ color: #c53030;
788
+ font-weight: 500;
789
+ margin-bottom: 10px;
790
+ line-height: 1.6;
791
+ }
792
+ .error-file {
793
+ font-size: 14px;
794
+ color: #666;
795
+ font-family: 'Courier New', monospace;
796
+ background: #f7fafc;
797
+ padding: 8px 12px;
798
+ border-radius: 4px;
799
+ display: inline-block;
800
+ }
801
+ .analysis-box {
802
+ background: #f0f9ff;
803
+ border-left: 4px solid #3b82f6;
804
+ padding: 20px;
805
+ border-radius: 8px;
806
+ line-height: 1.8;
807
+ color: #1e40af;
808
+ }
809
+ .suggestion-box {
810
+ background: #f0fdf4;
811
+ border-left: 4px solid #10b981;
812
+ padding: 20px;
813
+ border-radius: 8px;
814
+ line-height: 1.8;
815
+ color: #065f46;
816
+ }
817
+ .suggestion-box pre {
818
+ background: #1e293b;
819
+ color: #e2e8f0;
820
+ padding: 15px;
821
+ border-radius: 6px;
822
+ overflow-x: auto;
823
+ margin: 10px 0;
824
+ font-size: 14px;
825
+ line-height: 1.5;
826
+ }
827
+ .fixed-badge {
828
+ display: inline-flex;
829
+ align-items: center;
830
+ gap: 6px;
831
+ background: #10b981;
832
+ color: white;
833
+ padding: 8px 16px;
834
+ border-radius: 20px;
835
+ font-size: 14px;
836
+ font-weight: bold;
837
+ }
838
+ .not-fixed-badge {
839
+ display: inline-flex;
840
+ align-items: center;
841
+ gap: 6px;
842
+ background: #f59e0b;
843
+ color: white;
844
+ padding: 8px 16px;
845
+ border-radius: 20px;
846
+ font-size: 14px;
847
+ font-weight: bold;
848
+ }
849
+ .footer {
850
+ background: #f7fafc;
851
+ padding: 20px;
852
+ text-align: center;
853
+ color: #718096;
854
+ font-size: 14px;
855
+ border-top: 1px solid #e2e8f0;
856
+ }
857
+ .stack-trace {
858
+ background: #1e293b;
859
+ color: #e2e8f0;
860
+ padding: 15px;
861
+ border-radius: 6px;
862
+ overflow-x: auto;
863
+ font-family: 'Courier New', monospace;
864
+ font-size: 12px;
865
+ line-height: 1.5;
866
+ margin-top: 10px;
867
+ max-height: 300px;
868
+ overflow-y: auto;
869
+ }
870
+ </style>
871
+ </head>
872
+ <body>
873
+ <div class="container">
874
+ <div class="header">
875
+ <h1>🤖 AI 诊断报告</h1>
876
+ <div class="time">生成时间: ${report.timestamp}</div>
877
+ <div class="error-count">共发现 ${report.totalErrors} 个错误</div>
878
+ </div>
879
+
880
+ <div class="content">
881
+ ${errorsHtml}
882
+ </div>
883
+
884
+ <div class="footer">
885
+ <p>AI Diagnostic Plugin v1.0.7</p>
886
+ <p>Powered by LangGraph & OpenAI</p>
887
+ </div>
888
+ </div>
889
+ </body>
890
+ </html>
891
+ `.trim();
892
+ const reportsDir = path.resolve(process.cwd(), "ai-reports");
893
+ if (!fs.existsSync(reportsDir)) {
894
+ fs.mkdirSync(reportsDir, { recursive: true });
895
+ }
896
+ const reportPath = path.resolve(reportsDir, "diagnostic-report.html");
897
+ fs.writeFileSync(reportPath, html, "utf-8");
898
+ console.log(
899
+ `
900
+ 📄 诊断报告已生成: ${reportPath} (${report.totalErrors} 个错误)
901
+ `
902
+ );
903
+ }
904
+ /**
905
+ * 生成多错误 Markdown 报告
906
+ */
907
+ static async generateMultiMarkdownReport(report) {
908
+ const errorsMarkdown = report.errors.map(
909
+ (error, index) => `
910
+ ## 错误 ${index + 1} / ${report.totalErrors}
911
+
912
+ ### ❌ 错误信息
913
+
914
+ **类型**: \`${error.error.type}\`
915
+ **文件**: \`${error.error.file}\`
916
+ **消息**:
917
+ \`\`\`
918
+ ${error.error.message}
919
+ \`\`\`
920
+
921
+ ${error.error.stack ? `**堆栈跟踪**:
922
+ \`\`\`
923
+ ${error.error.stack}
924
+ \`\`\`
925
+ ` : ""}
926
+
927
+ ### 🔍 AI 分析
928
+
929
+ ${error.analysis}
930
+
931
+ ### 💡 修复建议
932
+
933
+ ${error.suggestion}
934
+
935
+ ### 🔧 修复状态
936
+
937
+ ${error.fixed ? `✅ **已自动修复**
938
+
939
+ 修复文件: \`${error.fixedFilePath}\`` : `⚠️ **未自动修复**
940
+
941
+ 请根据上述建议手动修复`}
942
+
943
+ ---
944
+ `
945
+ ).join("\n");
946
+ const markdown = `# 🤖 AI 诊断报告
947
+
948
+ **生成时间**: ${report.timestamp}
949
+ **错误总数**: ${report.totalErrors}
950
+
951
+ ---
952
+
953
+ ${errorsMarkdown}
954
+
955
+ *AI Diagnostic Plugin v1.0.7*
956
+ *Powered by LangGraph & OpenAI*
957
+ `;
958
+ const reportsDir = path.resolve(process.cwd(), "ai-reports");
959
+ if (!fs.existsSync(reportsDir)) {
960
+ fs.mkdirSync(reportsDir, { recursive: true });
961
+ }
962
+ const reportPath = path.resolve(reportsDir, "diagnostic-report.md");
963
+ fs.writeFileSync(reportPath, markdown, "utf-8");
964
+ console.log(`📄 Markdown 报告已生成: ${reportPath}`);
965
+ }
966
+ /**
967
+ * 生成多错误 JSON 报告
968
+ */
969
+ static async generateMultiJSONReport(report) {
970
+ const jsonReport = {
971
+ version: "1.0.7",
972
+ timestamp: report.timestamp,
973
+ totalErrors: report.totalErrors,
974
+ errors: report.errors.map((error) => ({
975
+ error: {
976
+ type: error.error.type,
977
+ message: error.error.message,
978
+ file: error.error.file,
979
+ stack: error.error.stack || null
980
+ },
981
+ diagnosis: {
982
+ analysis: error.analysis,
983
+ suggestion: error.suggestion
984
+ },
985
+ fix: {
986
+ applied: error.fixed,
987
+ filePath: error.fixedFilePath || null
988
+ }
989
+ }))
990
+ };
991
+ const reportsDir = path.resolve(process.cwd(), "ai-reports");
992
+ if (!fs.existsSync(reportsDir)) {
993
+ fs.mkdirSync(reportsDir, { recursive: true });
994
+ }
995
+ const reportPath = path.resolve(reportsDir, "diagnostic-report.json");
996
+ fs.writeFileSync(reportPath, JSON.stringify(jsonReport, null, 2), "utf-8");
997
+ console.log(`📄 JSON 报告已生成: ${reportPath}`);
998
+ }
620
999
  };
621
1000
 
622
1001
  // src/index.ts
1002
+ import { glob } from "glob";
623
1003
  function vitePluginAIDiagnostic(options = {}) {
624
1004
  const {
625
1005
  apiKey = process.env.OPENAI_API_KEY || "",
@@ -643,6 +1023,8 @@ function vitePluginAIDiagnostic(options = {}) {
643
1023
  let buildErrors = [];
644
1024
  let lastTransformFile = null;
645
1025
  let processedErrors = /* @__PURE__ */ new Set();
1026
+ let config;
1027
+ let diagnosticResults = [];
646
1028
  async function processError(error) {
647
1029
  const errorKey = `${error.file}:${error.message}`;
648
1030
  if (processedErrors.has(errorKey)) {
@@ -692,7 +1074,7 @@ function vitePluginAIDiagnostic(options = {}) {
692
1074
  fixed: !!(result.fixedCode && result.filePath),
693
1075
  fixedFilePath: result.filePath
694
1076
  };
695
- await DiagnosticReporter.generate(report, output);
1077
+ diagnosticResults.push(report);
696
1078
  } catch (err) {
697
1079
  console.error("❌ AI 诊断失败:", err.message);
698
1080
  }
@@ -701,7 +1083,8 @@ function vitePluginAIDiagnostic(options = {}) {
701
1083
  name: "vite-plugin-ai-diagnostic",
702
1084
  // 确保插件在其他插件之后执行,以捕获更多错误
703
1085
  enforce: "post",
704
- configResolved(config) {
1086
+ configResolved(resolvedConfig) {
1087
+ config = resolvedConfig;
705
1088
  console.log("\n🤖 AI 诊断助手已启动...");
706
1089
  console.log(`⚙️ 自动修复: ${autoFix ? "✅ 已启用" : "❌ 未启用"}`);
707
1090
  console.log(`📝 根目录: ${config.root}`);
@@ -710,10 +1093,123 @@ function vitePluginAIDiagnostic(options = {}) {
710
1093
  `
711
1094
  );
712
1095
  },
713
- buildStart() {
1096
+ async buildStart() {
714
1097
  buildErrors = [];
715
1098
  processedErrors.clear();
716
- console.log("🔍 [调试] buildStart 已执行");
1099
+ diagnosticResults = [];
1100
+ console.log("🔍 开始扫描源文件...");
1101
+ const srcDir = path2.join(config.root, "src");
1102
+ if (!fs2.existsSync(srcDir)) {
1103
+ console.log("⚠️ src 目录不存在,跳过文件扫描");
1104
+ return;
1105
+ }
1106
+ try {
1107
+ const files = await glob("**/*.{vue,ts,tsx,js,jsx}", {
1108
+ cwd: srcDir,
1109
+ absolute: true,
1110
+ ignore: ["**/node_modules/**", "**/*.d.ts"]
1111
+ });
1112
+ console.log(`📂 找到 ${files.length} 个源文件`);
1113
+ for (const file of files) {
1114
+ try {
1115
+ const code = fs2.readFileSync(file, "utf-8");
1116
+ if (file.endsWith(".vue")) {
1117
+ if (!code.includes("<template>") && !code.includes("<script>")) {
1118
+ buildErrors.push({
1119
+ type: "syntax",
1120
+ message: "Vue 文件缺少 <template> 或 <script> 标签",
1121
+ file,
1122
+ code
1123
+ });
1124
+ }
1125
+ const templateMatch = code.match(/<template[^>]*>/);
1126
+ if (templateMatch && !code.includes("</template>")) {
1127
+ buildErrors.push({
1128
+ type: "syntax",
1129
+ message: "Vue 文件中 <template> 标签未闭合",
1130
+ file,
1131
+ code
1132
+ });
1133
+ }
1134
+ const scriptMatch = code.match(/<script[^>]*>/);
1135
+ if (scriptMatch && !code.includes("</script>")) {
1136
+ buildErrors.push({
1137
+ type: "syntax",
1138
+ message: "Vue 文件中 <script> 标签未闭合",
1139
+ file,
1140
+ code
1141
+ });
1142
+ }
1143
+ }
1144
+ if (file.endsWith(".ts") || file.endsWith(".tsx") || file.endsWith(".js") || file.endsWith(".jsx")) {
1145
+ const openBraces = (code.match(/\{/g) || []).length;
1146
+ const closeBraces = (code.match(/\}/g) || []).length;
1147
+ if (openBraces !== closeBraces) {
1148
+ buildErrors.push({
1149
+ type: "syntax",
1150
+ message: `括号不匹配:{ 有 ${openBraces} 个,} 有 ${closeBraces} 个`,
1151
+ file,
1152
+ code
1153
+ });
1154
+ }
1155
+ const openParens = (code.match(/\(/g) || []).length;
1156
+ const closeParens = (code.match(/\)/g) || []).length;
1157
+ if (openParens !== closeParens) {
1158
+ buildErrors.push({
1159
+ type: "syntax",
1160
+ message: `圆括号不匹配:( 有 ${openParens} 个,) 有 ${closeParens} 个`,
1161
+ file,
1162
+ code
1163
+ });
1164
+ }
1165
+ }
1166
+ const importRegex = /import\s+.*?\s+from\s+['"](.+?)['"]/g;
1167
+ let match;
1168
+ while ((match = importRegex.exec(code)) !== null) {
1169
+ const importPath = match[1];
1170
+ if (importPath.startsWith(".") || importPath.startsWith("/")) {
1171
+ const resolvedPath = path2.resolve(
1172
+ path2.dirname(file),
1173
+ importPath
1174
+ );
1175
+ const extensions = [".ts", ".tsx", ".js", ".jsx", ".vue", ""];
1176
+ let exists = false;
1177
+ for (const ext of extensions) {
1178
+ const fullPath = resolvedPath + ext;
1179
+ if (fs2.existsSync(fullPath)) {
1180
+ exists = true;
1181
+ break;
1182
+ }
1183
+ if (fs2.existsSync(resolvedPath) && fs2.statSync(resolvedPath).isDirectory()) {
1184
+ const indexPath = path2.join(resolvedPath, "index" + ext);
1185
+ if (fs2.existsSync(indexPath)) {
1186
+ exists = true;
1187
+ break;
1188
+ }
1189
+ }
1190
+ }
1191
+ if (!exists) {
1192
+ buildErrors.push({
1193
+ type: "module",
1194
+ message: `找不到模块: ${importPath}`,
1195
+ file,
1196
+ code
1197
+ });
1198
+ }
1199
+ }
1200
+ }
1201
+ } catch (err) {
1202
+ console.warn(`⚠️ 无法读取文件 ${file}: ${err.message}`);
1203
+ }
1204
+ }
1205
+ if (buildErrors.length > 0) {
1206
+ console.log(`⚠️ 扫描发现 ${buildErrors.length} 个潜在问题`);
1207
+ } else {
1208
+ console.log("✅ 文件扫描完成,未发现明显问题");
1209
+ }
1210
+ } catch (err) {
1211
+ console.error(`❌ 文件扫描失败: ${err.message}`);
1212
+ }
717
1213
  },
718
1214
  // 解析模块时捕获错误
719
1215
  async resolveId(source, importer, options2) {
@@ -808,6 +1304,17 @@ function vitePluginAIDiagnostic(options = {}) {
808
1304
  for (const error of buildErrors) {
809
1305
  await processError(error);
810
1306
  }
1307
+ if (diagnosticResults.length > 0) {
1308
+ console.log(
1309
+ `
1310
+ 📊 正在生成合并诊断报告 (${diagnosticResults.length} 个错误)...
1311
+ `
1312
+ );
1313
+ await DiagnosticReporter.generateMultiReport(
1314
+ diagnosticResults,
1315
+ output
1316
+ );
1317
+ }
811
1318
  } else {
812
1319
  console.log("✨ 构建完成,未检测到错误\n");
813
1320
  }