qat-cli 0.3.4 → 0.3.6

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.cjs CHANGED
@@ -2861,9 +2861,15 @@ ${errorDetails}`;
2861
2861
  /**
2862
2862
  * 压缩源码:保留签名和关键逻辑,剔除注释、空行、样式块
2863
2863
  * 目标:在保留准确性的前提下减少 token 消耗
2864
+ * @param code 源码内容
2865
+ * @param maxLength 最大长度
2866
+ * @param importPathRewrites import 路径重写映射(原路径→正确路径)
2864
2867
  */
2865
- compressSourceCode(code, maxLength = 3e3) {
2868
+ compressSourceCode(code, maxLength = 3e3, importPathRewrites) {
2866
2869
  let compressed = code;
2870
+ if (importPathRewrites && importPathRewrites.size > 0) {
2871
+ compressed = this.rewriteImportPaths(compressed, importPathRewrites);
2872
+ }
2867
2873
  compressed = compressed.replace(/<style[^>]*>[\s\S]*?<\/style>/gi, "");
2868
2874
  compressed = compressed.replace(/<template[^>]*>([\s\S]*?)<\/template>/gi, (_match, content) => {
2869
2875
  return `<template>${content.replace(/\s*(?:class|style)\s*=\s*["'][^"']*["']/gi, "")}</template>`;
@@ -2901,7 +2907,8 @@ ${errorDetails}`;
2901
2907
  }
2902
2908
  buildGenerateTestUserPrompt(req) {
2903
2909
  const importPath = this.computeTestImportPath(req.type, req.target);
2904
- let prompt = `\u4E3A${req.target}\u751F\u6210${req.type}\u6D4B\u8BD5\u3002import\u8DEF\u5F84:${importPath}
2910
+ let prompt = `\u4E3A${req.target}\u751F\u6210${req.type}\u6D4B\u8BD5\u3002
2911
+ \u3010\u5F3A\u5236\u3011import\u88AB\u6D4B\u6A21\u5757\u5FC5\u987B\u7528: ${importPath}
2905
2912
  `;
2906
2913
  if (req.analysis) {
2907
2914
  const parts = [];
@@ -2923,13 +2930,19 @@ ${errorDetails}`;
2923
2930
  if (req.analysis.computed?.length) {
2924
2931
  parts.push(`Computed:${req.analysis.computed.join(",")}`);
2925
2932
  }
2933
+ if (req.analysis.importSignatures?.length) {
2934
+ parts.push(`\u4F9D\u8D56\u7B7E\u540D:${req.analysis.importSignatures.map(
2935
+ (imp) => `${imp.source}{${Object.entries(imp.signatures).map(([k, v]) => `${k}:${v}`).join(",")}}`
2936
+ ).join(";")}`);
2937
+ }
2926
2938
  prompt += parts.join("\n") + "\n";
2927
2939
  }
2928
2940
  if (req.context) {
2941
+ const importPathRewrites = this.buildImportPathRewrites(req.type, req.target);
2929
2942
  prompt += `
2930
2943
  \u6E90\u7801:
2931
2944
  \`\`\`
2932
- ${this.compressSourceCode(req.context)}
2945
+ ${this.compressSourceCode(req.context, 3e3, importPathRewrites)}
2933
2946
  \`\`\`
2934
2947
  `;
2935
2948
  }
@@ -2964,6 +2977,58 @@ ${this.compressSourceCode(req.context)}
2964
2977
  }
2965
2978
  return `${prefix}${cleanPath}`;
2966
2979
  }
2980
+ /**
2981
+ * 构建 import 路径重写映射
2982
+ * 将源码中可能引用自身的各种写法,映射到测试文件中正确的导入路径
2983
+ */
2984
+ buildImportPathRewrites(testType, targetPath) {
2985
+ const rewrites = /* @__PURE__ */ new Map();
2986
+ if (!targetPath) return rewrites;
2987
+ const correctImportPath = this.computeTestImportPath(testType, targetPath);
2988
+ const variations = /* @__PURE__ */ new Set();
2989
+ variations.add(targetPath);
2990
+ variations.add(targetPath.replace(/^\.\//, ""));
2991
+ const withoutExt = targetPath.replace(/\.(ts|js|tsx|jsx)$/, "");
2992
+ variations.add(withoutExt);
2993
+ const srcDirMatch = targetPath.match(/^(?:\.\/)?(src\/.+)$/);
2994
+ if (srcDirMatch) {
2995
+ variations.add(`@/${srcDirMatch[1]}`);
2996
+ variations.add(`@/${srcDirMatch[1].replace(/\.(ts|js|tsx|jsx)$/, "")}`);
2997
+ variations.add(`#/${srcDirMatch[1]}`);
2998
+ variations.add(`#/${srcDirMatch[1].replace(/\.(ts|js|tsx|jsx)$/, "")}`);
2999
+ }
3000
+ const pathParts = targetPath.replace(/^\.\//, "").split("/");
3001
+ const fileName = pathParts[pathParts.length - 1];
3002
+ const fileNameNoExt = fileName.replace(/\.(ts|js|tsx|jsx|vue)$/, "");
3003
+ variations.add(`./${fileName}`);
3004
+ variations.add(`./${fileNameNoExt}`);
3005
+ for (let i = 1; i < pathParts.length; i++) {
3006
+ const relPath = "../".repeat(i) + pathParts.slice(i).join("/");
3007
+ variations.add(relPath);
3008
+ variations.add(relPath.replace(/\.(ts|js|tsx|jsx)$/, ""));
3009
+ }
3010
+ for (const variant of variations) {
3011
+ if (variant && variant !== correctImportPath) {
3012
+ rewrites.set(variant, correctImportPath);
3013
+ }
3014
+ }
3015
+ return rewrites;
3016
+ }
3017
+ /**
3018
+ * 重写源码中的 import 路径
3019
+ * 将 from 'oldPath' / from "oldPath" 替换为正确的路径
3020
+ */
3021
+ rewriteImportPaths(code, rewrites) {
3022
+ let result = code;
3023
+ for (const [oldPath, newPath] of rewrites) {
3024
+ const escaped = oldPath.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
3025
+ const singleQuoteRegex = new RegExp(`(from\\s+')${escaped}(')`, "g");
3026
+ const doubleQuoteRegex = new RegExp(`(from\\s+")${escaped}(")`, "g");
3027
+ result = result.replace(singleQuoteRegex, `$1${newPath}$2`);
3028
+ result = result.replace(doubleQuoteRegex, `$1${newPath}$2`);
3029
+ }
3030
+ return result;
3031
+ }
2967
3032
  parseGenerateTestResponse(content) {
2968
3033
  const codeBlockMatch = content.match(/```(?:typescript|ts|javascript|js)?\s*\n([\s\S]*?)```/);
2969
3034
  const code = codeBlockMatch ? codeBlockMatch[1].trim() : content.replace(/^(?:```[\s\S]*?\n)?/, "").replace(/\n?```$/, "").trim();
@@ -2986,12 +3051,14 @@ SUGGESTIONS:- \u5EFA\u8BAE(\u6BCF\u884C\u4E00\u4E2A)`;
2986
3051
  }
2987
3052
  buildReviewTestUserPrompt(req) {
2988
3053
  const importPath = this.computeTestImportPath(req.testType, req.target);
2989
- let prompt = `\u5BA1\u67E5\u6D4B\u8BD5\u4EE3\u7801\u3002\u88AB\u6D4B:${req.target} \u7C7B\u578B:${req.testType} import\u8DEF\u5F84:${importPath}
3054
+ let prompt = `\u5BA1\u67E5\u6D4B\u8BD5\u4EE3\u7801\u3002\u88AB\u6D4B:${req.target} \u7C7B\u578B:${req.testType}
3055
+ \u3010\u5F3A\u5236\u3011import\u88AB\u6D4B\u6A21\u5757\u5FC5\u987B\u7528: ${importPath}
2990
3056
  `;
3057
+ const importPathRewrites = this.buildImportPathRewrites(req.testType, req.target);
2991
3058
  prompt += `
2992
3059
  \u6E90\u7801:
2993
3060
  \`\`\`
2994
- ${this.compressSourceCode(req.sourceCode, 2e3)}
3061
+ ${this.compressSourceCode(req.sourceCode, 2e3, importPathRewrites)}
2995
3062
  \`\`\`
2996
3063
  `;
2997
3064
  prompt += `
@@ -3010,6 +3077,11 @@ ${req.testCode}
3010
3077
  if (req.analysis.emits?.length) {
3011
3078
  parts.push(`Emits:${req.analysis.emits.map((e) => `${e.name}(${e.params?.join(",") || ""})`).join(",")}`);
3012
3079
  }
3080
+ if (req.analysis.importSignatures?.length) {
3081
+ parts.push(`\u4F9D\u8D56:${req.analysis.importSignatures.map(
3082
+ (imp) => `${imp.source}{${Object.entries(imp.signatures).map(([k, v]) => `${k}:${v}`).join(",")}}`
3083
+ ).join(";")}`);
3084
+ }
3013
3085
  if (parts.length > 0) {
3014
3086
  prompt += `
3015
3087
  \u5206\u6790:${parts.join("|")}`;
@@ -3621,15 +3693,18 @@ var import_node_path10 = __toESM(require("path"), 1);
3621
3693
  function analyzeFile(filePath) {
3622
3694
  const absolutePath = import_node_path10.default.resolve(process.cwd(), filePath);
3623
3695
  if (!import_node_fs9.default.existsSync(absolutePath)) {
3624
- return { filePath, exports: [], apiCalls: [] };
3696
+ return { filePath, exports: [], imports: [], importSignatures: [], apiCalls: [] };
3625
3697
  }
3626
3698
  const content = import_node_fs9.default.readFileSync(absolutePath, "utf-8");
3627
3699
  const ext = import_node_path10.default.extname(filePath);
3628
3700
  const result = {
3629
3701
  filePath,
3630
3702
  exports: extractExports(content, ext),
3703
+ imports: extractImports(content),
3704
+ importSignatures: [],
3631
3705
  apiCalls: extractAPICalls(content, filePath)
3632
3706
  };
3707
+ result.importSignatures = analyzeImportSignatures(result.imports, import_node_path10.default.dirname(absolutePath));
3633
3708
  if (ext === ".vue") {
3634
3709
  result.vueAnalysis = analyzeVueComponent(content, import_node_path10.default.basename(filePath, ".vue"));
3635
3710
  const scriptMatch = content.match(/<script[^>]*>([\s\S]*?)<\/script>/);
@@ -4052,6 +4127,90 @@ function generateMockRoutesFromAPICalls(apiCalls) {
4052
4127
  }
4053
4128
  return routes;
4054
4129
  }
4130
+ function extractImports(content) {
4131
+ const imports = [];
4132
+ const namedImportRegex = /import\s*\{([^}]+)\}\s*from\s*['"]([^'"]+)['"]/g;
4133
+ let match;
4134
+ while ((match = namedImportRegex.exec(content)) !== null) {
4135
+ const names = match[1].split(",").map((n) => {
4136
+ const parts = n.trim().split(/\s+as\s+/);
4137
+ return parts[0].trim();
4138
+ }).filter(Boolean);
4139
+ imports.push({ names, source: match[2], isDefault: false, isNamespace: false });
4140
+ }
4141
+ const defaultImportRegex = /import\s+(\w+)\s+from\s*['"]([^'"]+)['"]/g;
4142
+ while ((match = defaultImportRegex.exec(content)) !== null) {
4143
+ const fullLine = content.slice(Math.max(0, match.index - 5), match.index + match[0].length);
4144
+ if (fullLine.includes("{")) continue;
4145
+ imports.push({ names: [match[1]], source: match[2], isDefault: true, isNamespace: false });
4146
+ }
4147
+ const namespaceImportRegex = /import\s*\*\s*as\s+(\w+)\s*from\s*['"]([^'"]+)['"]/g;
4148
+ while ((match = namespaceImportRegex.exec(content)) !== null) {
4149
+ imports.push({ names: [match[1]], source: match[2], isDefault: false, isNamespace: true });
4150
+ }
4151
+ return imports;
4152
+ }
4153
+ function analyzeImportSignatures(imports, baseDir) {
4154
+ const signatures = [];
4155
+ for (const imp of imports) {
4156
+ if (!imp.source.startsWith(".") && !imp.source.startsWith("@/") && !imp.source.startsWith("#/") && !imp.source.startsWith("~")) {
4157
+ continue;
4158
+ }
4159
+ const resolvedPath = resolveImportPath2(imp.source, baseDir);
4160
+ if (!resolvedPath || !import_node_fs9.default.existsSync(resolvedPath)) continue;
4161
+ try {
4162
+ const content = import_node_fs9.default.readFileSync(resolvedPath, "utf-8");
4163
+ const ext = import_node_path10.default.extname(resolvedPath);
4164
+ const exports2 = extractExports(content, ext);
4165
+ const sigMap = {};
4166
+ for (const name of imp.names) {
4167
+ const exp = exports2.find((e) => e.name === name);
4168
+ if (exp) {
4169
+ const params = exp.params.length > 0 ? `(${exp.params.join(", ")})` : "";
4170
+ const ret = exp.returnType ? `: ${exp.returnType}` : "";
4171
+ const async = exp.isAsync ? "async " : "";
4172
+ sigMap[name] = `${async}${exp.kind}${params}${ret}`;
4173
+ } else if (imp.isDefault) {
4174
+ const defaultExp = exports2.find((e) => e.kind === "default");
4175
+ if (defaultExp) {
4176
+ sigMap[name] = `default[${defaultExp.name || "anonymous"}]`;
4177
+ }
4178
+ } else if (imp.isNamespace) {
4179
+ sigMap[name] = `{${exports2.map((e) => e.name).join(", ")}}`;
4180
+ }
4181
+ }
4182
+ if (ext === ".vue") {
4183
+ const vueAnalysis = analyzeVueComponent(content, import_node_path10.default.basename(resolvedPath, ".vue"));
4184
+ if (vueAnalysis.props.length > 0) {
4185
+ sigMap["__props__"] = vueAnalysis.props.map((p) => `${p.name}:${p.type}${p.required ? "!" : "?"}`).join(",");
4186
+ }
4187
+ }
4188
+ if (Object.keys(sigMap).length > 0) {
4189
+ signatures.push({ source: imp.source, signatures: sigMap });
4190
+ }
4191
+ } catch {
4192
+ }
4193
+ }
4194
+ return signatures;
4195
+ }
4196
+ function resolveImportPath2(importSource, baseDir) {
4197
+ let resolved;
4198
+ if (importSource.startsWith("@/") || importSource.startsWith("#/") || importSource.startsWith("~")) {
4199
+ const relativePath = importSource.replace(/^[@#~]\//, "src/");
4200
+ resolved = import_node_path10.default.resolve(process.cwd(), relativePath);
4201
+ } else if (importSource.startsWith(".")) {
4202
+ resolved = import_node_path10.default.resolve(baseDir, importSource);
4203
+ } else {
4204
+ return null;
4205
+ }
4206
+ const extensions = [".ts", ".tsx", ".js", ".jsx", ".vue", "/index.ts", "/index.js"];
4207
+ if (import_node_fs9.default.existsSync(resolved)) return resolved;
4208
+ for (const ext of extensions) {
4209
+ const withExt = resolved + ext;
4210
+ if (import_node_fs9.default.existsSync(withExt)) return withExt;
4211
+ }
4212
+ return null;
4213
+ }
4055
4214
  // Annotate the CommonJS export names for ESM import in node:
4056
4215
  0 && (module.exports = {
4057
4216
  AI_PRESET_PROVIDERS,