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.js CHANGED
@@ -35,6 +35,7 @@ __export(index_exports, {
35
35
  });
36
36
  module.exports = __toCommonJS(index_exports);
37
37
  var import_fs2 = __toESM(require("fs"));
38
+ var import_path2 = __toESM(require("path"));
38
39
 
39
40
  // src/langgraph.ts
40
41
  var import_langgraph = require("@langchain/langgraph");
@@ -652,9 +653,388 @@ ${report.fixed ? `✅ **已自动修复**
652
653
  import_fs.default.writeFileSync(reportPath, JSON.stringify(jsonReport, null, 2), "utf-8");
653
654
  console.log(`📄 JSON 报告已生成: ${reportPath}`);
654
655
  }
656
+ /**
657
+ * 生成多错误合并报告
658
+ */
659
+ static async generateMultiReport(reports, options = {}) {
660
+ const multiReport = {
661
+ timestamp: (/* @__PURE__ */ new Date()).toLocaleString("zh-CN"),
662
+ totalErrors: reports.length,
663
+ errors: reports
664
+ };
665
+ if (options.html) {
666
+ await this.generateMultiHTMLReport(multiReport);
667
+ }
668
+ if (options.markdown) {
669
+ await this.generateMultiMarkdownReport(multiReport);
670
+ }
671
+ if (options.json) {
672
+ await this.generateMultiJSONReport(multiReport);
673
+ }
674
+ }
675
+ /**
676
+ * 生成多错误 HTML 报告
677
+ */
678
+ static async generateMultiHTMLReport(report) {
679
+ const errorsHtml = report.errors.map(
680
+ (error, index) => `
681
+ <div class="error-item">
682
+ <div class="error-number">错误 ${index + 1} / ${report.totalErrors}</div>
683
+ <div class="error-box">
684
+ <span class="error-type">${error.error.type}</span>
685
+ <div class="error-message">${this.escapeHtml(
686
+ error.error.message
687
+ )}</div>
688
+ <div class="error-file">📂 ${error.error.file}</div>
689
+ ${error.error.stack ? `<div class="stack-trace">${this.escapeHtml(
690
+ error.error.stack
691
+ )}</div>` : ""}
692
+ </div>
693
+
694
+ <div class="section">
695
+ <div class="section-title">🔍 AI 分析</div>
696
+ <div class="analysis-box">
697
+ ${this.formatText(error.analysis)}
698
+ </div>
699
+ </div>
700
+
701
+ <div class="section">
702
+ <div class="section-title">💡 修复建议</div>
703
+ <div class="suggestion-box">
704
+ ${this.formatText(error.suggestion)}
705
+ </div>
706
+ </div>
707
+
708
+ <div class="section">
709
+ <div class="section-title">🔧 修复状态</div>
710
+ ${error.fixed ? `<div class="fixed-badge">✅ 已自动修复</div>
711
+ <div style="margin-top: 10px; color: #666;">修复文件: <code>${error.fixedFilePath}</code></div>` : `<div class="not-fixed-badge">⚠️ 未自动修复</div>
712
+ <div style="margin-top: 10px; color: #666;">请根据上述建议手动修复</div>`}
713
+ </div>
714
+ </div>
715
+ `
716
+ ).join("");
717
+ const html = `
718
+ <!DOCTYPE html>
719
+ <html lang="zh-CN">
720
+ <head>
721
+ <meta charset="UTF-8">
722
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
723
+ <title>AI 诊断报告 - ${report.totalErrors} 个错误</title>
724
+ <style>
725
+ * { margin: 0; padding: 0; box-sizing: border-box; }
726
+ body {
727
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
728
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
729
+ padding: 20px;
730
+ min-height: 100vh;
731
+ }
732
+ .container {
733
+ max-width: 1000px;
734
+ margin: 0 auto;
735
+ background: white;
736
+ border-radius: 12px;
737
+ box-shadow: 0 8px 32px rgba(0,0,0,0.2);
738
+ overflow: hidden;
739
+ }
740
+ .header {
741
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
742
+ color: white;
743
+ padding: 30px;
744
+ text-align: center;
745
+ }
746
+ .header h1 {
747
+ font-size: 32px;
748
+ margin-bottom: 10px;
749
+ display: flex;
750
+ align-items: center;
751
+ justify-content: center;
752
+ gap: 10px;
753
+ }
754
+ .header .time {
755
+ font-size: 14px;
756
+ opacity: 0.9;
757
+ }
758
+ .header .error-count {
759
+ font-size: 18px;
760
+ margin-top: 10px;
761
+ background: rgba(255,255,255,0.2);
762
+ padding: 8px 16px;
763
+ border-radius: 20px;
764
+ display: inline-block;
765
+ }
766
+ .content {
767
+ padding: 30px;
768
+ }
769
+ .error-item {
770
+ margin-bottom: 40px;
771
+ padding-bottom: 40px;
772
+ border-bottom: 2px dashed #e2e8f0;
773
+ }
774
+ .error-item:last-child {
775
+ border-bottom: none;
776
+ margin-bottom: 0;
777
+ padding-bottom: 0;
778
+ }
779
+ .error-number {
780
+ font-size: 16px;
781
+ font-weight: bold;
782
+ color: #667eea;
783
+ margin-bottom: 15px;
784
+ padding: 8px 16px;
785
+ background: #f0f9ff;
786
+ border-radius: 8px;
787
+ display: inline-block;
788
+ }
789
+ .section {
790
+ margin-bottom: 20px;
791
+ }
792
+ .section-title {
793
+ font-size: 18px;
794
+ font-weight: bold;
795
+ color: #333;
796
+ margin-bottom: 12px;
797
+ padding-bottom: 8px;
798
+ border-bottom: 2px solid #667eea;
799
+ display: flex;
800
+ align-items: center;
801
+ gap: 8px;
802
+ }
803
+ .error-box {
804
+ background: #fff5f5;
805
+ border-left: 4px solid #f56565;
806
+ padding: 20px;
807
+ border-radius: 8px;
808
+ margin-bottom: 20px;
809
+ }
810
+ .error-type {
811
+ display: inline-block;
812
+ background: #f56565;
813
+ color: white;
814
+ padding: 4px 12px;
815
+ border-radius: 12px;
816
+ font-size: 12px;
817
+ font-weight: bold;
818
+ margin-bottom: 10px;
819
+ }
820
+ .error-message {
821
+ font-size: 16px;
822
+ color: #c53030;
823
+ font-weight: 500;
824
+ margin-bottom: 10px;
825
+ line-height: 1.6;
826
+ }
827
+ .error-file {
828
+ font-size: 14px;
829
+ color: #666;
830
+ font-family: 'Courier New', monospace;
831
+ background: #f7fafc;
832
+ padding: 8px 12px;
833
+ border-radius: 4px;
834
+ display: inline-block;
835
+ }
836
+ .analysis-box {
837
+ background: #f0f9ff;
838
+ border-left: 4px solid #3b82f6;
839
+ padding: 20px;
840
+ border-radius: 8px;
841
+ line-height: 1.8;
842
+ color: #1e40af;
843
+ }
844
+ .suggestion-box {
845
+ background: #f0fdf4;
846
+ border-left: 4px solid #10b981;
847
+ padding: 20px;
848
+ border-radius: 8px;
849
+ line-height: 1.8;
850
+ color: #065f46;
851
+ }
852
+ .suggestion-box pre {
853
+ background: #1e293b;
854
+ color: #e2e8f0;
855
+ padding: 15px;
856
+ border-radius: 6px;
857
+ overflow-x: auto;
858
+ margin: 10px 0;
859
+ font-size: 14px;
860
+ line-height: 1.5;
861
+ }
862
+ .fixed-badge {
863
+ display: inline-flex;
864
+ align-items: center;
865
+ gap: 6px;
866
+ background: #10b981;
867
+ color: white;
868
+ padding: 8px 16px;
869
+ border-radius: 20px;
870
+ font-size: 14px;
871
+ font-weight: bold;
872
+ }
873
+ .not-fixed-badge {
874
+ display: inline-flex;
875
+ align-items: center;
876
+ gap: 6px;
877
+ background: #f59e0b;
878
+ color: white;
879
+ padding: 8px 16px;
880
+ border-radius: 20px;
881
+ font-size: 14px;
882
+ font-weight: bold;
883
+ }
884
+ .footer {
885
+ background: #f7fafc;
886
+ padding: 20px;
887
+ text-align: center;
888
+ color: #718096;
889
+ font-size: 14px;
890
+ border-top: 1px solid #e2e8f0;
891
+ }
892
+ .stack-trace {
893
+ background: #1e293b;
894
+ color: #e2e8f0;
895
+ padding: 15px;
896
+ border-radius: 6px;
897
+ overflow-x: auto;
898
+ font-family: 'Courier New', monospace;
899
+ font-size: 12px;
900
+ line-height: 1.5;
901
+ margin-top: 10px;
902
+ max-height: 300px;
903
+ overflow-y: auto;
904
+ }
905
+ </style>
906
+ </head>
907
+ <body>
908
+ <div class="container">
909
+ <div class="header">
910
+ <h1>🤖 AI 诊断报告</h1>
911
+ <div class="time">生成时间: ${report.timestamp}</div>
912
+ <div class="error-count">共发现 ${report.totalErrors} 个错误</div>
913
+ </div>
914
+
915
+ <div class="content">
916
+ ${errorsHtml}
917
+ </div>
918
+
919
+ <div class="footer">
920
+ <p>AI Diagnostic Plugin v1.0.7</p>
921
+ <p>Powered by LangGraph & OpenAI</p>
922
+ </div>
923
+ </div>
924
+ </body>
925
+ </html>
926
+ `.trim();
927
+ const reportsDir = import_path.default.resolve(process.cwd(), "ai-reports");
928
+ if (!import_fs.default.existsSync(reportsDir)) {
929
+ import_fs.default.mkdirSync(reportsDir, { recursive: true });
930
+ }
931
+ const reportPath = import_path.default.resolve(reportsDir, "diagnostic-report.html");
932
+ import_fs.default.writeFileSync(reportPath, html, "utf-8");
933
+ console.log(
934
+ `
935
+ 📄 诊断报告已生成: ${reportPath} (${report.totalErrors} 个错误)
936
+ `
937
+ );
938
+ }
939
+ /**
940
+ * 生成多错误 Markdown 报告
941
+ */
942
+ static async generateMultiMarkdownReport(report) {
943
+ const errorsMarkdown = report.errors.map(
944
+ (error, index) => `
945
+ ## 错误 ${index + 1} / ${report.totalErrors}
946
+
947
+ ### ❌ 错误信息
948
+
949
+ **类型**: \`${error.error.type}\`
950
+ **文件**: \`${error.error.file}\`
951
+ **消息**:
952
+ \`\`\`
953
+ ${error.error.message}
954
+ \`\`\`
955
+
956
+ ${error.error.stack ? `**堆栈跟踪**:
957
+ \`\`\`
958
+ ${error.error.stack}
959
+ \`\`\`
960
+ ` : ""}
961
+
962
+ ### 🔍 AI 分析
963
+
964
+ ${error.analysis}
965
+
966
+ ### 💡 修复建议
967
+
968
+ ${error.suggestion}
969
+
970
+ ### 🔧 修复状态
971
+
972
+ ${error.fixed ? `✅ **已自动修复**
973
+
974
+ 修复文件: \`${error.fixedFilePath}\`` : `⚠️ **未自动修复**
975
+
976
+ 请根据上述建议手动修复`}
977
+
978
+ ---
979
+ `
980
+ ).join("\n");
981
+ const markdown = `# 🤖 AI 诊断报告
982
+
983
+ **生成时间**: ${report.timestamp}
984
+ **错误总数**: ${report.totalErrors}
985
+
986
+ ---
987
+
988
+ ${errorsMarkdown}
989
+
990
+ *AI Diagnostic Plugin v1.0.7*
991
+ *Powered by LangGraph & OpenAI*
992
+ `;
993
+ const reportsDir = import_path.default.resolve(process.cwd(), "ai-reports");
994
+ if (!import_fs.default.existsSync(reportsDir)) {
995
+ import_fs.default.mkdirSync(reportsDir, { recursive: true });
996
+ }
997
+ const reportPath = import_path.default.resolve(reportsDir, "diagnostic-report.md");
998
+ import_fs.default.writeFileSync(reportPath, markdown, "utf-8");
999
+ console.log(`📄 Markdown 报告已生成: ${reportPath}`);
1000
+ }
1001
+ /**
1002
+ * 生成多错误 JSON 报告
1003
+ */
1004
+ static async generateMultiJSONReport(report) {
1005
+ const jsonReport = {
1006
+ version: "1.0.7",
1007
+ timestamp: report.timestamp,
1008
+ totalErrors: report.totalErrors,
1009
+ errors: report.errors.map((error) => ({
1010
+ error: {
1011
+ type: error.error.type,
1012
+ message: error.error.message,
1013
+ file: error.error.file,
1014
+ stack: error.error.stack || null
1015
+ },
1016
+ diagnosis: {
1017
+ analysis: error.analysis,
1018
+ suggestion: error.suggestion
1019
+ },
1020
+ fix: {
1021
+ applied: error.fixed,
1022
+ filePath: error.fixedFilePath || null
1023
+ }
1024
+ }))
1025
+ };
1026
+ const reportsDir = import_path.default.resolve(process.cwd(), "ai-reports");
1027
+ if (!import_fs.default.existsSync(reportsDir)) {
1028
+ import_fs.default.mkdirSync(reportsDir, { recursive: true });
1029
+ }
1030
+ const reportPath = import_path.default.resolve(reportsDir, "diagnostic-report.json");
1031
+ import_fs.default.writeFileSync(reportPath, JSON.stringify(jsonReport, null, 2), "utf-8");
1032
+ console.log(`📄 JSON 报告已生成: ${reportPath}`);
1033
+ }
655
1034
  };
656
1035
 
657
1036
  // src/index.ts
1037
+ var import_glob = require("glob");
658
1038
  function vitePluginAIDiagnostic(options = {}) {
659
1039
  const {
660
1040
  apiKey = process.env.OPENAI_API_KEY || "",
@@ -678,6 +1058,8 @@ function vitePluginAIDiagnostic(options = {}) {
678
1058
  let buildErrors = [];
679
1059
  let lastTransformFile = null;
680
1060
  let processedErrors = /* @__PURE__ */ new Set();
1061
+ let config;
1062
+ let diagnosticResults = [];
681
1063
  async function processError(error) {
682
1064
  const errorKey = `${error.file}:${error.message}`;
683
1065
  if (processedErrors.has(errorKey)) {
@@ -727,7 +1109,7 @@ function vitePluginAIDiagnostic(options = {}) {
727
1109
  fixed: !!(result.fixedCode && result.filePath),
728
1110
  fixedFilePath: result.filePath
729
1111
  };
730
- await DiagnosticReporter.generate(report, output);
1112
+ diagnosticResults.push(report);
731
1113
  } catch (err) {
732
1114
  console.error("❌ AI 诊断失败:", err.message);
733
1115
  }
@@ -736,7 +1118,8 @@ function vitePluginAIDiagnostic(options = {}) {
736
1118
  name: "vite-plugin-ai-diagnostic",
737
1119
  // 确保插件在其他插件之后执行,以捕获更多错误
738
1120
  enforce: "post",
739
- configResolved(config) {
1121
+ configResolved(resolvedConfig) {
1122
+ config = resolvedConfig;
740
1123
  console.log("\n🤖 AI 诊断助手已启动...");
741
1124
  console.log(`⚙️ 自动修复: ${autoFix ? "✅ 已启用" : "❌ 未启用"}`);
742
1125
  console.log(`📝 根目录: ${config.root}`);
@@ -745,10 +1128,123 @@ function vitePluginAIDiagnostic(options = {}) {
745
1128
  `
746
1129
  );
747
1130
  },
748
- buildStart() {
1131
+ async buildStart() {
749
1132
  buildErrors = [];
750
1133
  processedErrors.clear();
751
- console.log("🔍 [调试] buildStart 已执行");
1134
+ diagnosticResults = [];
1135
+ console.log("🔍 开始扫描源文件...");
1136
+ const srcDir = import_path2.default.join(config.root, "src");
1137
+ if (!import_fs2.default.existsSync(srcDir)) {
1138
+ console.log("⚠️ src 目录不存在,跳过文件扫描");
1139
+ return;
1140
+ }
1141
+ try {
1142
+ const files = await (0, import_glob.glob)("**/*.{vue,ts,tsx,js,jsx}", {
1143
+ cwd: srcDir,
1144
+ absolute: true,
1145
+ ignore: ["**/node_modules/**", "**/*.d.ts"]
1146
+ });
1147
+ console.log(`📂 找到 ${files.length} 个源文件`);
1148
+ for (const file of files) {
1149
+ try {
1150
+ const code = import_fs2.default.readFileSync(file, "utf-8");
1151
+ if (file.endsWith(".vue")) {
1152
+ if (!code.includes("<template>") && !code.includes("<script>")) {
1153
+ buildErrors.push({
1154
+ type: "syntax",
1155
+ message: "Vue 文件缺少 <template> 或 <script> 标签",
1156
+ file,
1157
+ code
1158
+ });
1159
+ }
1160
+ const templateMatch = code.match(/<template[^>]*>/);
1161
+ if (templateMatch && !code.includes("</template>")) {
1162
+ buildErrors.push({
1163
+ type: "syntax",
1164
+ message: "Vue 文件中 <template> 标签未闭合",
1165
+ file,
1166
+ code
1167
+ });
1168
+ }
1169
+ const scriptMatch = code.match(/<script[^>]*>/);
1170
+ if (scriptMatch && !code.includes("</script>")) {
1171
+ buildErrors.push({
1172
+ type: "syntax",
1173
+ message: "Vue 文件中 <script> 标签未闭合",
1174
+ file,
1175
+ code
1176
+ });
1177
+ }
1178
+ }
1179
+ if (file.endsWith(".ts") || file.endsWith(".tsx") || file.endsWith(".js") || file.endsWith(".jsx")) {
1180
+ const openBraces = (code.match(/\{/g) || []).length;
1181
+ const closeBraces = (code.match(/\}/g) || []).length;
1182
+ if (openBraces !== closeBraces) {
1183
+ buildErrors.push({
1184
+ type: "syntax",
1185
+ message: `括号不匹配:{ 有 ${openBraces} 个,} 有 ${closeBraces} 个`,
1186
+ file,
1187
+ code
1188
+ });
1189
+ }
1190
+ const openParens = (code.match(/\(/g) || []).length;
1191
+ const closeParens = (code.match(/\)/g) || []).length;
1192
+ if (openParens !== closeParens) {
1193
+ buildErrors.push({
1194
+ type: "syntax",
1195
+ message: `圆括号不匹配:( 有 ${openParens} 个,) 有 ${closeParens} 个`,
1196
+ file,
1197
+ code
1198
+ });
1199
+ }
1200
+ }
1201
+ const importRegex = /import\s+.*?\s+from\s+['"](.+?)['"]/g;
1202
+ let match;
1203
+ while ((match = importRegex.exec(code)) !== null) {
1204
+ const importPath = match[1];
1205
+ if (importPath.startsWith(".") || importPath.startsWith("/")) {
1206
+ const resolvedPath = import_path2.default.resolve(
1207
+ import_path2.default.dirname(file),
1208
+ importPath
1209
+ );
1210
+ const extensions = [".ts", ".tsx", ".js", ".jsx", ".vue", ""];
1211
+ let exists = false;
1212
+ for (const ext of extensions) {
1213
+ const fullPath = resolvedPath + ext;
1214
+ if (import_fs2.default.existsSync(fullPath)) {
1215
+ exists = true;
1216
+ break;
1217
+ }
1218
+ if (import_fs2.default.existsSync(resolvedPath) && import_fs2.default.statSync(resolvedPath).isDirectory()) {
1219
+ const indexPath = import_path2.default.join(resolvedPath, "index" + ext);
1220
+ if (import_fs2.default.existsSync(indexPath)) {
1221
+ exists = true;
1222
+ break;
1223
+ }
1224
+ }
1225
+ }
1226
+ if (!exists) {
1227
+ buildErrors.push({
1228
+ type: "module",
1229
+ message: `找不到模块: ${importPath}`,
1230
+ file,
1231
+ code
1232
+ });
1233
+ }
1234
+ }
1235
+ }
1236
+ } catch (err) {
1237
+ console.warn(`⚠️ 无法读取文件 ${file}: ${err.message}`);
1238
+ }
1239
+ }
1240
+ if (buildErrors.length > 0) {
1241
+ console.log(`⚠️ 扫描发现 ${buildErrors.length} 个潜在问题`);
1242
+ } else {
1243
+ console.log("✅ 文件扫描完成,未发现明显问题");
1244
+ }
1245
+ } catch (err) {
1246
+ console.error(`❌ 文件扫描失败: ${err.message}`);
1247
+ }
752
1248
  },
753
1249
  // 解析模块时捕获错误
754
1250
  async resolveId(source, importer, options2) {
@@ -843,6 +1339,17 @@ function vitePluginAIDiagnostic(options = {}) {
843
1339
  for (const error of buildErrors) {
844
1340
  await processError(error);
845
1341
  }
1342
+ if (diagnosticResults.length > 0) {
1343
+ console.log(
1344
+ `
1345
+ 📊 正在生成合并诊断报告 (${diagnosticResults.length} 个错误)...
1346
+ `
1347
+ );
1348
+ await DiagnosticReporter.generateMultiReport(
1349
+ diagnosticResults,
1350
+ output
1351
+ );
1352
+ }
846
1353
  } else {
847
1354
  console.log("✨ 构建完成,未检测到错误\n");
848
1355
  }