qat-cli 0.3.3 → 0.3.5
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/README.md +1 -1
- package/dist/cli.js +744 -552
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +414 -144
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +48 -3
- package/dist/index.d.ts +48 -3
- package/dist/index.js +414 -144
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -2524,6 +2524,15 @@ var NoopAIProvider = class {
|
|
|
2524
2524
|
};
|
|
2525
2525
|
|
|
2526
2526
|
// src/ai/openai-provider.ts
|
|
2527
|
+
import chalk2 from "chalk";
|
|
2528
|
+
function isDebug() {
|
|
2529
|
+
return process.env.QAT_DEBUG === "true";
|
|
2530
|
+
}
|
|
2531
|
+
function debugLog(tag, ...args) {
|
|
2532
|
+
if (!isDebug()) return;
|
|
2533
|
+
const timestamp = (/* @__PURE__ */ new Date()).toISOString().slice(11, 19);
|
|
2534
|
+
console.error(chalk2.gray(`[DEBUG ${timestamp}] [${tag}]`), ...args);
|
|
2535
|
+
}
|
|
2527
2536
|
var OpenAICompatibleProvider = class {
|
|
2528
2537
|
constructor(config) {
|
|
2529
2538
|
this.capabilities = {
|
|
@@ -2537,25 +2546,20 @@ var OpenAICompatibleProvider = class {
|
|
|
2537
2546
|
this.baseUrl = config.baseUrl || this.getDefaultBaseUrl(config.provider);
|
|
2538
2547
|
}
|
|
2539
2548
|
async generateTest(req) {
|
|
2549
|
+
debugLog("GENERATE", `type=${req.type} target=${req.target}`);
|
|
2540
2550
|
const systemPrompt = this.buildGenerateTestSystemPrompt(req);
|
|
2541
2551
|
const userPrompt = this.buildGenerateTestUserPrompt(req);
|
|
2542
2552
|
const content = await this.chat(systemPrompt, userPrompt);
|
|
2543
|
-
|
|
2553
|
+
const result = this.parseGenerateTestResponse(content);
|
|
2554
|
+
debugLog("GENERATE", `done code=${result.code.length}chars confidence=${result.confidence}`);
|
|
2555
|
+
return result;
|
|
2544
2556
|
}
|
|
2545
2557
|
async analyzeResult(req) {
|
|
2546
|
-
const systemPrompt = `\
|
|
2547
|
-
\u8F93\u51FA\
|
|
2548
|
-
|
|
2549
|
-
|
|
2550
|
-
const
|
|
2551
|
-
const failed = r.suites.flatMap((s) => s.tests.filter((t) => t.status === "failed"));
|
|
2552
|
-
return `\u7C7B\u578B: ${r.type}, \u72B6\u6001: ${r.status}, \u8017\u65F6: ${r.duration}ms, \u5931\u8D25\u7528\u4F8B: ${failed.length}`;
|
|
2553
|
-
}).join("\n");
|
|
2554
|
-
const errorDetails = req.errorLogs?.join("\n") || req.testResults.flatMap((r) => r.suites.flatMap((s) => s.tests.filter((t) => t.status === "failed" && t.error))).map((t) => `[${t.name}] ${t.error?.message}`).join("\n") || "\u65E0\u9519\u8BEF\u8BE6\u60C5";
|
|
2555
|
-
const userPrompt = `\u6D4B\u8BD5\u7ED3\u679C:
|
|
2556
|
-
${resultSummary}
|
|
2557
|
-
|
|
2558
|
-
\u9519\u8BEF\u8BE6\u60C5:
|
|
2558
|
+
const systemPrompt = `\u6D4B\u8BD5\u5206\u6790\u4E13\u5BB6\u3002\u627E\u95EE\u9898\u6839\u56E0\uFF0C\u7ED9\u53EF\u64CD\u4F5C\u5EFA\u8BAE\u3002
|
|
2559
|
+
\u8F93\u51FA:1.\u6458\u8981(1-3\u53E5) 2.\u5EFA\u8BAE\u5217\u8868`;
|
|
2560
|
+
const failed = req.testResults.flatMap((r) => r.suites.flatMap((s) => s.tests.filter((t) => t.status === "failed")));
|
|
2561
|
+
const errorDetails = req.errorLogs?.join("\n") || failed.map((t) => `[${t.name}] ${t.error?.message}`).join("\n") || "\u65E0";
|
|
2562
|
+
const userPrompt = `\u5931\u8D25:${failed.length}
|
|
2559
2563
|
${errorDetails}`;
|
|
2560
2564
|
const content = await this.chat(systemPrompt, userPrompt);
|
|
2561
2565
|
return {
|
|
@@ -2565,23 +2569,22 @@ ${errorDetails}`;
|
|
|
2565
2569
|
};
|
|
2566
2570
|
}
|
|
2567
2571
|
async suggestFix(error) {
|
|
2568
|
-
const systemPrompt = `\
|
|
2569
|
-
\
|
|
2570
|
-
|
|
2571
|
-
|
|
2572
|
-
|
|
2573
|
-
const userPrompt = `\u9519\u8BEF\u4FE1\u606F: ${error.message}
|
|
2574
|
-
${error.stack ? `\u5806\u6808: ${error.stack}` : ""}
|
|
2575
|
-
${error.expected ? `\u671F\u671B\u503C: ${error.expected}` : ""}
|
|
2576
|
-
${error.actual ? `\u5B9E\u9645\u503C: ${error.actual}` : ""}`;
|
|
2572
|
+
const systemPrompt = `\u4EE3\u7801\u4FEE\u590D\u4E13\u5BB6\u3002\u7ED9\u51FA:1.\u95EE\u9898\u5B9A\u4F4D 2.\u4FEE\u590D\u65B9\u6848 3.\u793A\u4F8B\u4EE3\u7801`;
|
|
2573
|
+
const userPrompt = `\u9519\u8BEF:${error.message}${error.stack ? `
|
|
2574
|
+
\u5806\u6808:${error.stack}` : ""}${error.expected ? `
|
|
2575
|
+
\u671F\u671B:${error.expected}` : ""}${error.actual ? `
|
|
2576
|
+
\u5B9E\u9645:${error.actual}` : ""}`;
|
|
2577
2577
|
const content = await this.chat(systemPrompt, userPrompt);
|
|
2578
2578
|
return content.split("\n").filter((l) => l.trim().startsWith("-") || l.trim().startsWith("\u2022") || l.trim().match(/^\d+\./)).map((l) => l.replace(/^[-•\d.]+\s*/, "").trim()).filter(Boolean);
|
|
2579
2579
|
}
|
|
2580
2580
|
async reviewTest(req) {
|
|
2581
|
+
debugLog("REVIEW", `target=${req.target} type=${req.testType}`);
|
|
2581
2582
|
const systemPrompt = this.buildReviewTestSystemPrompt(req);
|
|
2582
2583
|
const userPrompt = this.buildReviewTestUserPrompt(req);
|
|
2583
2584
|
const content = await this.chat(systemPrompt, userPrompt);
|
|
2584
|
-
|
|
2585
|
+
const result = this.parseReviewTestResponse(content);
|
|
2586
|
+
debugLog("REVIEW", `approved=${result.approved} score=${result.score} feedback=${result.feedback}`);
|
|
2587
|
+
return result;
|
|
2585
2588
|
}
|
|
2586
2589
|
// ─── 内部方法 ──────────────────────────────────────────────
|
|
2587
2590
|
/**
|
|
@@ -2645,8 +2648,9 @@ ${error.actual ? `\u5B9E\u9645\u503C: ${error.actual}` : ""}`;
|
|
|
2645
2648
|
return { ok: false, message: `\u8FDE\u63A5\u5931\u8D25: ${error instanceof Error ? error.message : String(error)}`, latencyMs };
|
|
2646
2649
|
}
|
|
2647
2650
|
}
|
|
2648
|
-
async chat(systemPrompt, userPrompt) {
|
|
2651
|
+
async chat(systemPrompt, userPrompt, retries = 2) {
|
|
2649
2652
|
const url = `${this.baseUrl}/chat/completions`;
|
|
2653
|
+
const useStream = isDebug();
|
|
2650
2654
|
const body = {
|
|
2651
2655
|
model: this.model,
|
|
2652
2656
|
messages: [
|
|
@@ -2656,101 +2660,202 @@ ${error.actual ? `\u5B9E\u9645\u503C: ${error.actual}` : ""}`;
|
|
|
2656
2660
|
temperature: 0.3,
|
|
2657
2661
|
max_tokens: 4096
|
|
2658
2662
|
};
|
|
2663
|
+
if (useStream) {
|
|
2664
|
+
body.stream = true;
|
|
2665
|
+
}
|
|
2659
2666
|
const headers = {
|
|
2660
2667
|
"Content-Type": "application/json"
|
|
2661
2668
|
};
|
|
2662
2669
|
if (this.apiKey) {
|
|
2663
2670
|
headers["Authorization"] = `Bearer ${this.apiKey}`;
|
|
2664
2671
|
}
|
|
2665
|
-
|
|
2666
|
-
|
|
2667
|
-
|
|
2668
|
-
|
|
2669
|
-
|
|
2670
|
-
|
|
2671
|
-
|
|
2672
|
-
|
|
2673
|
-
|
|
2674
|
-
|
|
2672
|
+
debugLog("REQUEST", `POST ${url}`);
|
|
2673
|
+
debugLog("REQUEST", `model=${this.model} stream=${useStream}`);
|
|
2674
|
+
debugLog("SYSTEM", systemPrompt.length > 500 ? `${systemPrompt.slice(0, 500)}...` : systemPrompt);
|
|
2675
|
+
debugLog("USER", userPrompt.length > 1e3 ? `${userPrompt.slice(0, 1e3)}...` : userPrompt);
|
|
2676
|
+
let lastError = null;
|
|
2677
|
+
for (let attempt = 0; attempt <= retries; attempt++) {
|
|
2678
|
+
try {
|
|
2679
|
+
if (attempt > 0) {
|
|
2680
|
+
debugLog("RETRY", `\u7B2C${attempt}\u6B21\u91CD\u8BD5...`);
|
|
2681
|
+
}
|
|
2682
|
+
const response = await fetch(url, {
|
|
2683
|
+
method: "POST",
|
|
2684
|
+
headers,
|
|
2685
|
+
body: JSON.stringify(body),
|
|
2686
|
+
signal: AbortSignal.timeout(12e4)
|
|
2687
|
+
// 120s timeout
|
|
2688
|
+
});
|
|
2689
|
+
if (!response.ok) {
|
|
2690
|
+
const text = await response.text().catch(() => "");
|
|
2691
|
+
if ((response.status === 429 || response.status >= 500) && attempt < retries) {
|
|
2692
|
+
const delay = Math.min(1e3 * Math.pow(2, attempt), 8e3);
|
|
2693
|
+
debugLog("RETRY", `HTTP ${response.status}, ${delay}ms\u540E\u91CD\u8BD5`);
|
|
2694
|
+
await new Promise((r) => setTimeout(r, delay));
|
|
2695
|
+
lastError = new Error(`AI API \u8BF7\u6C42\u5931\u8D25 (${response.status}): ${text.slice(0, 200)}`);
|
|
2696
|
+
continue;
|
|
2697
|
+
}
|
|
2698
|
+
throw new Error(`AI API \u8BF7\u6C42\u5931\u8D25 (${response.status}): ${text.slice(0, 500)}`);
|
|
2699
|
+
}
|
|
2700
|
+
if (useStream && response.body) {
|
|
2701
|
+
const content = await this.readStream(response.body);
|
|
2702
|
+
debugLog("RESPONSE", content.length > 500 ? `${content.slice(0, 500)}...` : content);
|
|
2703
|
+
return content;
|
|
2704
|
+
}
|
|
2705
|
+
const data = await response.json();
|
|
2706
|
+
if (!data.choices?.[0]?.message?.content) {
|
|
2707
|
+
throw new Error("AI API \u8FD4\u56DE\u7A7A\u54CD\u5E94");
|
|
2708
|
+
}
|
|
2709
|
+
debugLog("RESPONSE", `tokens: prompt=${data.usage?.prompt_tokens} completion=${data.usage?.completion_tokens} total=${data.usage?.total_tokens}`);
|
|
2710
|
+
debugLog("RESPONSE", data.choices[0].message.content.length > 500 ? `${data.choices[0].message.content.slice(0, 500)}...` : data.choices[0].message.content);
|
|
2711
|
+
return data.choices[0].message.content;
|
|
2712
|
+
} catch (error) {
|
|
2713
|
+
if (error instanceof Error && error.name === "TimeoutError" && attempt < retries) {
|
|
2714
|
+
debugLog("TIMEOUT", `\u7B2C${attempt}\u6B21\u8D85\u65F6\uFF0C\u91CD\u8BD5\u4E2D...`);
|
|
2715
|
+
lastError = error;
|
|
2716
|
+
continue;
|
|
2717
|
+
}
|
|
2718
|
+
throw error;
|
|
2719
|
+
}
|
|
2675
2720
|
}
|
|
2676
|
-
|
|
2677
|
-
|
|
2678
|
-
|
|
2721
|
+
throw lastError || new Error("AI API \u8BF7\u6C42\u5931\u8D25");
|
|
2722
|
+
}
|
|
2723
|
+
/**
|
|
2724
|
+
* 读取 SSE 流式响应,实时输出内容
|
|
2725
|
+
*/
|
|
2726
|
+
async readStream(body) {
|
|
2727
|
+
const chunks = [];
|
|
2728
|
+
const decoder = new TextDecoder();
|
|
2729
|
+
const reader = body.getReader();
|
|
2730
|
+
let buffer = "";
|
|
2731
|
+
let lineCount = 0;
|
|
2732
|
+
try {
|
|
2733
|
+
while (true) {
|
|
2734
|
+
const { done, value } = await reader.read();
|
|
2735
|
+
if (done) break;
|
|
2736
|
+
buffer += decoder.decode(value, { stream: true });
|
|
2737
|
+
const lines = buffer.split("\n");
|
|
2738
|
+
buffer = lines.pop() || "";
|
|
2739
|
+
for (const line of lines) {
|
|
2740
|
+
const trimmed = line.trim();
|
|
2741
|
+
if (!trimmed || trimmed === "data: [DONE]") continue;
|
|
2742
|
+
if (!trimmed.startsWith("data: ")) continue;
|
|
2743
|
+
try {
|
|
2744
|
+
const json = JSON.parse(trimmed.slice(6));
|
|
2745
|
+
const delta = json.choices?.[0]?.delta?.content;
|
|
2746
|
+
if (delta) {
|
|
2747
|
+
chunks.push(delta);
|
|
2748
|
+
lineCount++;
|
|
2749
|
+
process.stderr.write(chalk2.gray(delta));
|
|
2750
|
+
if (lineCount % 20 === 0) {
|
|
2751
|
+
process.stderr.write("\n");
|
|
2752
|
+
}
|
|
2753
|
+
}
|
|
2754
|
+
if (json.choices?.[0]?.finish_reason === "stop") {
|
|
2755
|
+
debugLog("STREAM", "\u5B8C\u6210");
|
|
2756
|
+
}
|
|
2757
|
+
} catch {
|
|
2758
|
+
}
|
|
2759
|
+
}
|
|
2760
|
+
}
|
|
2761
|
+
} finally {
|
|
2762
|
+
reader.releaseLock();
|
|
2763
|
+
}
|
|
2764
|
+
if (chunks.length > 0) {
|
|
2765
|
+
process.stderr.write("\n");
|
|
2766
|
+
}
|
|
2767
|
+
return chunks.join("");
|
|
2768
|
+
}
|
|
2769
|
+
/**
|
|
2770
|
+
* 压缩源码:保留签名和关键逻辑,剔除注释、空行、样式块
|
|
2771
|
+
* 目标:在保留准确性的前提下减少 token 消耗
|
|
2772
|
+
* @param code 源码内容
|
|
2773
|
+
* @param maxLength 最大长度
|
|
2774
|
+
* @param importPathRewrites import 路径重写映射(原路径→正确路径)
|
|
2775
|
+
*/
|
|
2776
|
+
compressSourceCode(code, maxLength = 3e3, importPathRewrites) {
|
|
2777
|
+
let compressed = code;
|
|
2778
|
+
if (importPathRewrites && importPathRewrites.size > 0) {
|
|
2779
|
+
compressed = this.rewriteImportPaths(compressed, importPathRewrites);
|
|
2679
2780
|
}
|
|
2680
|
-
|
|
2781
|
+
compressed = compressed.replace(/<style[^>]*>[\s\S]*?<\/style>/gi, "");
|
|
2782
|
+
compressed = compressed.replace(/<template[^>]*>([\s\S]*?)<\/template>/gi, (_match, content) => {
|
|
2783
|
+
return `<template>${content.replace(/\s*(?:class|style)\s*=\s*["'][^"']*["']/gi, "")}</template>`;
|
|
2784
|
+
});
|
|
2785
|
+
compressed = compressed.replace(/\/\*[\s\S]*?\*\//g, "");
|
|
2786
|
+
compressed = compressed.replace(/(^|[^:])(\/\/.*$)/gm, "$1");
|
|
2787
|
+
compressed = compressed.replace(/\n\s*\n\s*\n/g, "\n\n");
|
|
2788
|
+
compressed = compressed.split("\n").map((line) => line.trimEnd()).join("\n").trim();
|
|
2789
|
+
if (compressed.length > maxLength) {
|
|
2790
|
+
const scriptMatch = compressed.match(/<script[^>]*>([\s\S]*?)<\/script>/i);
|
|
2791
|
+
const templateMatch = compressed.match(/<template[^>]*>([\s\S]*?)<\/template>/i);
|
|
2792
|
+
if (scriptMatch) {
|
|
2793
|
+
let scriptPart = scriptMatch[1].trim();
|
|
2794
|
+
scriptPart = compressFunctionBodies(scriptPart, maxLength * 0.7);
|
|
2795
|
+
const templatePart = templateMatch ? `<template>${templateMatch[1].replace(/\s+/g, " ").trim().slice(0, 300)}...</template>` : "";
|
|
2796
|
+
compressed = `${templatePart}
|
|
2797
|
+
<script${scriptMatch[0].match(/<script[^>]*>/)?.[0]?.slice(7) || ">"}>${scriptPart}</script>`;
|
|
2798
|
+
} else {
|
|
2799
|
+
compressed = compressFunctionBodies(compressed, maxLength);
|
|
2800
|
+
}
|
|
2801
|
+
}
|
|
2802
|
+
return compressed;
|
|
2681
2803
|
}
|
|
2682
2804
|
buildGenerateTestSystemPrompt(req) {
|
|
2683
2805
|
const typeMap = {
|
|
2684
|
-
unit: "\u5355\u5143\u6D4B\u8BD5
|
|
2685
|
-
component: "\u7EC4\u4EF6\u6D4B\u8BD5
|
|
2686
|
-
e2e: "E2E\
|
|
2687
|
-
api: "API\
|
|
2688
|
-
visual: "\u89C6\u89C9\u56DE\u5F52\u6D4B\u8BD5
|
|
2689
|
-
performance: "\u6027\u80FD\u6D4B\u8BD5
|
|
2806
|
+
unit: "\u5355\u5143\u6D4B\u8BD5(Vitest)",
|
|
2807
|
+
component: "\u7EC4\u4EF6\u6D4B\u8BD5(Vitest+@vue/test-utils)",
|
|
2808
|
+
e2e: "E2E\u6D4B\u8BD5(Playwright)",
|
|
2809
|
+
api: "API\u6D4B\u8BD5(Vitest+fetch)",
|
|
2810
|
+
visual: "\u89C6\u89C9\u56DE\u5F52\u6D4B\u8BD5(Playwright)",
|
|
2811
|
+
performance: "\u6027\u80FD\u6D4B\u8BD5(Playwright)"
|
|
2690
2812
|
};
|
|
2691
|
-
return `\u4F60\u662F\
|
|
2692
|
-
\
|
|
2693
|
-
1. \u53EA\u8F93\u51FA\u6D4B\u8BD5\u4EE3\u7801\uFF0C\u4E0D\u8981\u591A\u4F59\u7684\u89E3\u91CA
|
|
2694
|
-
2. \u4EE3\u7801\u5FC5\u987B\u53EF\u76F4\u63A5\u8FD0\u884C\uFF0C\u5305\u542B\u6240\u6709\u5FC5\u8981\u7684 import
|
|
2695
|
-
3. \u6D4B\u8BD5\u7528\u4F8B\u8986\u76D6\uFF1A\u6B63\u5E38\u8DEF\u5F84\u3001\u8FB9\u754C\u6761\u4EF6\u3001\u9519\u8BEF\u5904\u7406
|
|
2696
|
-
4. \u4F7F\u7528\u4E2D\u6587\u63CF\u8FF0 it/test \u5757\u540D\u79F0
|
|
2697
|
-
5. Vue \u7EC4\u4EF6\u6D4B\u8BD5\u4F7F\u7528 @vue/test-utils \u7684 mount
|
|
2698
|
-
6. \u5982\u679C\u6709 props/emits \u4FE1\u606F\uFF0C\u52A1\u5FC5\u9488\u5BF9\u6BCF\u4E2A prop \u548C emit \u751F\u6210\u6D4B\u8BD5`;
|
|
2813
|
+
return `\u4F60\u662F\u524D\u7AEF\u6D4B\u8BD5\u5DE5\u7A0B\u5E08\uFF0C\u7F16\u5199${typeMap[req.type] || req.type}\u3002
|
|
2814
|
+
\u89C4\u5219:1.\u53EA\u8F93\u51FA\u6D4B\u8BD5\u4EE3\u7801 2.\u5305\u542B\u6240\u6709import 3.\u8986\u76D6\u6B63\u5E38/\u8FB9\u754C/\u9519\u8BEF 4.it\u540D\u7528\u4E2D\u6587 5.Vue\u7528mount 6.\u5FC5\u6D4B\u6BCF\u4E2Aprop\u548Cemit`;
|
|
2699
2815
|
}
|
|
2700
2816
|
buildGenerateTestUserPrompt(req) {
|
|
2701
2817
|
const importPath = this.computeTestImportPath(req.type, req.target);
|
|
2702
|
-
let prompt = `\
|
|
2703
|
-
\
|
|
2704
|
-
\u6D4B\u8BD5\u6587\u4EF6\u5C06\u653E\u5728: ${this.getTestOutputDir(req.type)}/
|
|
2705
|
-
\u6B63\u786E\u7684 import \u8DEF\u5F84: ${importPath}
|
|
2706
|
-
|
|
2707
|
-
\u91CD\u8981\uFF1Aimport \u8BED\u53E5\u4E2D\u5FC5\u987B\u4F7F\u7528\u4E0A\u8FF0\u6B63\u786E\u7684\u76F8\u5BF9\u8DEF\u5F84 ${importPath}\uFF0C\u4E0D\u8981\u4F7F\u7528 ${req.target} \u6216\u5176\u4ED6\u8DEF\u5F84\uFF01
|
|
2818
|
+
let prompt = `\u4E3A${req.target}\u751F\u6210${req.type}\u6D4B\u8BD5\u3002
|
|
2819
|
+
\u3010\u5F3A\u5236\u3011import\u88AB\u6D4B\u6A21\u5757\u5FC5\u987B\u7528: ${importPath}
|
|
2708
2820
|
`;
|
|
2709
2821
|
if (req.analysis) {
|
|
2710
|
-
|
|
2822
|
+
const parts = [];
|
|
2711
2823
|
if (req.analysis.exports?.length > 0) {
|
|
2712
|
-
|
|
2713
|
-
${
|
|
2714
|
-
|
|
2715
|
-
|
|
2716
|
-
return ` - ${asyncFlag}${e.name}${params} [${e.kind}]`;
|
|
2717
|
-
}).join("\n")}
|
|
2718
|
-
`;
|
|
2824
|
+
parts.push(`\u5BFC\u51FA:${req.analysis.exports.map((e) => {
|
|
2825
|
+
const p = e.params?.length ? `(${e.params.join(",")})` : "";
|
|
2826
|
+
return `${e.isAsync ? "async " : ""}${e.name}${p}[${e.kind}]`;
|
|
2827
|
+
}).join(",")}`);
|
|
2719
2828
|
}
|
|
2720
2829
|
if (req.analysis.props?.length) {
|
|
2721
|
-
|
|
2722
|
-
${req.analysis.props.map(
|
|
2723
|
-
(p) => ` - ${p.name}: ${p.type}${p.required ? " (\u5FC5\u586B)" : " (\u53EF\u9009)"}`
|
|
2724
|
-
).join("\n")}
|
|
2725
|
-
`;
|
|
2830
|
+
parts.push(`Props:${req.analysis.props.map((p) => `${p.name}:${p.type}${p.required ? "!" : "?"}`).join(",")}`);
|
|
2726
2831
|
}
|
|
2727
2832
|
if (req.analysis.emits?.length) {
|
|
2728
|
-
|
|
2729
|
-
${req.analysis.emits.map(
|
|
2730
|
-
(e) => ` - ${e.name}${e.params?.length ? `(${e.params.join(", ")})` : ""}`
|
|
2731
|
-
).join("\n")}
|
|
2732
|
-
`;
|
|
2833
|
+
parts.push(`Emits:${req.analysis.emits.map((e) => `${e.name}(${e.params?.join(",") || ""})`).join(",")}`);
|
|
2733
2834
|
}
|
|
2734
2835
|
if (req.analysis.methods?.length) {
|
|
2735
|
-
|
|
2736
|
-
`;
|
|
2836
|
+
parts.push(`Methods:${req.analysis.methods.join(",")}`);
|
|
2737
2837
|
}
|
|
2738
2838
|
if (req.analysis.computed?.length) {
|
|
2739
|
-
|
|
2740
|
-
|
|
2839
|
+
parts.push(`Computed:${req.analysis.computed.join(",")}`);
|
|
2840
|
+
}
|
|
2841
|
+
if (req.analysis.importSignatures?.length) {
|
|
2842
|
+
parts.push(`\u4F9D\u8D56\u7B7E\u540D:${req.analysis.importSignatures.map(
|
|
2843
|
+
(imp) => `${imp.source}{${Object.entries(imp.signatures).map(([k, v]) => `${k}:${v}`).join(",")}}`
|
|
2844
|
+
).join(";")}`);
|
|
2741
2845
|
}
|
|
2846
|
+
prompt += parts.join("\n") + "\n";
|
|
2742
2847
|
}
|
|
2743
2848
|
if (req.context) {
|
|
2849
|
+
const importPathRewrites = this.buildImportPathRewrites(req.type, req.target);
|
|
2744
2850
|
prompt += `
|
|
2745
|
-
\u6E90\u7801
|
|
2746
|
-
\`\`\`
|
|
2747
|
-
${req.context}
|
|
2851
|
+
\u6E90\u7801:
|
|
2852
|
+
\`\`\`
|
|
2853
|
+
${this.compressSourceCode(req.context, 3e3, importPathRewrites)}
|
|
2748
2854
|
\`\`\`
|
|
2749
2855
|
`;
|
|
2750
2856
|
}
|
|
2751
2857
|
if (req.framework) {
|
|
2752
|
-
prompt +=
|
|
2753
|
-
\u6846\u67B6: ${req.framework}`;
|
|
2858
|
+
prompt += `\u6846\u67B6:${req.framework}`;
|
|
2754
2859
|
}
|
|
2755
2860
|
return prompt;
|
|
2756
2861
|
}
|
|
@@ -2781,18 +2886,54 @@ ${req.context}
|
|
|
2781
2886
|
return `${prefix}${cleanPath}`;
|
|
2782
2887
|
}
|
|
2783
2888
|
/**
|
|
2784
|
-
*
|
|
2889
|
+
* 构建 import 路径重写映射
|
|
2890
|
+
* 将源码中可能引用自身的各种写法,映射到测试文件中正确的导入路径
|
|
2785
2891
|
*/
|
|
2786
|
-
|
|
2787
|
-
const
|
|
2788
|
-
|
|
2789
|
-
|
|
2790
|
-
|
|
2791
|
-
|
|
2792
|
-
|
|
2793
|
-
|
|
2794
|
-
|
|
2795
|
-
|
|
2892
|
+
buildImportPathRewrites(testType, targetPath) {
|
|
2893
|
+
const rewrites = /* @__PURE__ */ new Map();
|
|
2894
|
+
if (!targetPath) return rewrites;
|
|
2895
|
+
const correctImportPath = this.computeTestImportPath(testType, targetPath);
|
|
2896
|
+
const variations = /* @__PURE__ */ new Set();
|
|
2897
|
+
variations.add(targetPath);
|
|
2898
|
+
variations.add(targetPath.replace(/^\.\//, ""));
|
|
2899
|
+
const withoutExt = targetPath.replace(/\.(ts|js|tsx|jsx)$/, "");
|
|
2900
|
+
variations.add(withoutExt);
|
|
2901
|
+
const srcDirMatch = targetPath.match(/^(?:\.\/)?(src\/.+)$/);
|
|
2902
|
+
if (srcDirMatch) {
|
|
2903
|
+
variations.add(`@/${srcDirMatch[1]}`);
|
|
2904
|
+
variations.add(`@/${srcDirMatch[1].replace(/\.(ts|js|tsx|jsx)$/, "")}`);
|
|
2905
|
+
}
|
|
2906
|
+
const pathParts = targetPath.replace(/^\.\//, "").split("/");
|
|
2907
|
+
const fileName = pathParts[pathParts.length - 1];
|
|
2908
|
+
const fileNameNoExt = fileName.replace(/\.(ts|js|tsx|jsx|vue)$/, "");
|
|
2909
|
+
variations.add(`./${fileName}`);
|
|
2910
|
+
variations.add(`./${fileNameNoExt}`);
|
|
2911
|
+
for (let i = 1; i < pathParts.length; i++) {
|
|
2912
|
+
const relPath = "../".repeat(i) + pathParts.slice(i).join("/");
|
|
2913
|
+
variations.add(relPath);
|
|
2914
|
+
variations.add(relPath.replace(/\.(ts|js|tsx|jsx)$/, ""));
|
|
2915
|
+
}
|
|
2916
|
+
for (const variant of variations) {
|
|
2917
|
+
if (variant && variant !== correctImportPath) {
|
|
2918
|
+
rewrites.set(variant, correctImportPath);
|
|
2919
|
+
}
|
|
2920
|
+
}
|
|
2921
|
+
return rewrites;
|
|
2922
|
+
}
|
|
2923
|
+
/**
|
|
2924
|
+
* 重写源码中的 import 路径
|
|
2925
|
+
* 将 from 'oldPath' / from "oldPath" 替换为正确的路径
|
|
2926
|
+
*/
|
|
2927
|
+
rewriteImportPaths(code, rewrites) {
|
|
2928
|
+
let result = code;
|
|
2929
|
+
for (const [oldPath, newPath] of rewrites) {
|
|
2930
|
+
const escaped = oldPath.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
2931
|
+
const singleQuoteRegex = new RegExp(`(from\\s+')${escaped}(')`, "g");
|
|
2932
|
+
const doubleQuoteRegex = new RegExp(`(from\\s+")${escaped}(")`, "g");
|
|
2933
|
+
result = result.replace(singleQuoteRegex, `$1${newPath}$2`);
|
|
2934
|
+
result = result.replace(doubleQuoteRegex, `$1${newPath}$2`);
|
|
2935
|
+
}
|
|
2936
|
+
return result;
|
|
2796
2937
|
}
|
|
2797
2938
|
parseGenerateTestResponse(content) {
|
|
2798
2939
|
const codeBlockMatch = content.match(/```(?:typescript|ts|javascript|js)?\s*\n([\s\S]*?)```/);
|
|
@@ -2805,69 +2946,56 @@ ${req.context}
|
|
|
2805
2946
|
};
|
|
2806
2947
|
}
|
|
2807
2948
|
buildReviewTestSystemPrompt(_req) {
|
|
2808
|
-
return `\u4F60\u662F\
|
|
2809
|
-
|
|
2810
|
-
\
|
|
2811
|
-
|
|
2812
|
-
|
|
2813
|
-
|
|
2814
|
-
|
|
2815
|
-
|
|
2816
|
-
|
|
2817
|
-
\u8F93\u51FA\u683C\u5F0F\uFF08\u4E25\u683C\u9075\u5B88\uFF09\uFF1A
|
|
2818
|
-
APPROVED: true \u6216 false
|
|
2819
|
-
SCORE: 0.0 \u5230 1.0 \u4E4B\u95F4\u7684\u8BC4\u5206
|
|
2820
|
-
FEEDBACK: \u4E00\u53E5\u8BDD\u5BA1\u8BA1\u610F\u89C1
|
|
2821
|
-
ISSUES: \u95EE\u9898\u5217\u8868\uFF08\u6BCF\u884C\u4E00\u4E2A\uFF0C\u683C\u5F0F "- \u95EE\u9898\u63CF\u8FF0"\uFF09
|
|
2822
|
-
SUGGESTIONS: \u6539\u8FDB\u5EFA\u8BAE\u5217\u8868\uFF08\u6BCF\u884C\u4E00\u4E2A\uFF0C\u683C\u5F0F "- \u5EFA\u8BAE\u63CF\u8FF0"\uFF09`;
|
|
2949
|
+
return `\u4F60\u662F\u6D4B\u8BD5\u5BA1\u8BA1\u4E13\u5BB6\u3002\u5BA1\u67E5\u6D4B\u8BD5\u4EE3\u7801\u662F\u5426\u51C6\u786E\u3002
|
|
2950
|
+
\u6807\u51C6:1.\u7C7B\u578B\u5339\u914D 2.\u8986\u76D6\u6838\u5FC3\u5BFC\u51FA/props/emits 3.\u65AD\u8A00\u6709\u6548(\u975E\u6C38\u771F) 4.import\u6B63\u786E 5.\u53EF\u8FD0\u884C
|
|
2951
|
+
\u8F93\u51FA:
|
|
2952
|
+
APPROVED:true\u6216false
|
|
2953
|
+
SCORE:0.0-1.0
|
|
2954
|
+
FEEDBACK:\u4E00\u53E5\u8BDD
|
|
2955
|
+
ISSUES:- \u95EE\u9898(\u6BCF\u884C\u4E00\u4E2A)
|
|
2956
|
+
SUGGESTIONS:- \u5EFA\u8BAE(\u6BCF\u884C\u4E00\u4E2A)`;
|
|
2823
2957
|
}
|
|
2824
2958
|
buildReviewTestUserPrompt(req) {
|
|
2825
2959
|
const importPath = this.computeTestImportPath(req.testType, req.target);
|
|
2826
|
-
let prompt = `\
|
|
2827
|
-
|
|
2828
|
-
|
|
2829
|
-
|
|
2830
|
-
|
|
2831
|
-
|
|
2832
|
-
|
|
2833
|
-
|
|
2834
|
-
|
|
2960
|
+
let prompt = `\u5BA1\u67E5\u6D4B\u8BD5\u4EE3\u7801\u3002\u88AB\u6D4B:${req.target} \u7C7B\u578B:${req.testType}
|
|
2961
|
+
\u3010\u5F3A\u5236\u3011import\u88AB\u6D4B\u6A21\u5757\u5FC5\u987B\u7528: ${importPath}
|
|
2962
|
+
`;
|
|
2963
|
+
const importPathRewrites = this.buildImportPathRewrites(req.testType, req.target);
|
|
2964
|
+
prompt += `
|
|
2965
|
+
\u6E90\u7801:
|
|
2966
|
+
\`\`\`
|
|
2967
|
+
${this.compressSourceCode(req.sourceCode, 2e3, importPathRewrites)}
|
|
2968
|
+
\`\`\`
|
|
2969
|
+
`;
|
|
2970
|
+
prompt += `
|
|
2971
|
+
\u6D4B\u8BD5\u4EE3\u7801:
|
|
2835
2972
|
\`\`\`
|
|
2836
|
-
|
|
2837
|
-
--- \u751F\u6210\u7684\u6D4B\u8BD5\u4EE3\u7801 ---
|
|
2838
|
-
\`\`\`typescript
|
|
2839
2973
|
${req.testCode}
|
|
2840
2974
|
\`\`\``;
|
|
2841
2975
|
if (req.analysis) {
|
|
2842
|
-
|
|
2976
|
+
const parts = [];
|
|
2843
2977
|
if (req.analysis.exports?.length > 0) {
|
|
2844
|
-
|
|
2845
|
-
\u5BFC\u51FA\u9879:
|
|
2846
|
-
${req.analysis.exports.map((e) => {
|
|
2847
|
-
const params = e.params?.length ? `(${e.params.join(", ")})` : "";
|
|
2848
|
-
const asyncFlag = e.isAsync ? "async " : "";
|
|
2849
|
-
return ` - ${asyncFlag}${e.name}${params} [${e.kind}]`;
|
|
2850
|
-
}).join("\n")}`;
|
|
2978
|
+
parts.push(`\u5BFC\u51FA:${req.analysis.exports.map((e) => `${e.isAsync ? "async " : ""}${e.name}(${e.params?.join(",") || ""})[${e.kind}]`).join(",")}`);
|
|
2851
2979
|
}
|
|
2852
2980
|
if (req.analysis.props?.length) {
|
|
2853
|
-
|
|
2854
|
-
Props:
|
|
2855
|
-
${req.analysis.props.map(
|
|
2856
|
-
(p) => ` - ${p.name}: ${p.type}${p.required ? " (\u5FC5\u586B)" : " (\u53EF\u9009)"}`
|
|
2857
|
-
).join("\n")}`;
|
|
2981
|
+
parts.push(`Props:${req.analysis.props.map((p) => `${p.name}:${p.type}${p.required ? "!" : "?"}`).join(",")}`);
|
|
2858
2982
|
}
|
|
2859
2983
|
if (req.analysis.emits?.length) {
|
|
2984
|
+
parts.push(`Emits:${req.analysis.emits.map((e) => `${e.name}(${e.params?.join(",") || ""})`).join(",")}`);
|
|
2985
|
+
}
|
|
2986
|
+
if (req.analysis.importSignatures?.length) {
|
|
2987
|
+
parts.push(`\u4F9D\u8D56:${req.analysis.importSignatures.map(
|
|
2988
|
+
(imp) => `${imp.source}{${Object.entries(imp.signatures).map(([k, v]) => `${k}:${v}`).join(",")}}`
|
|
2989
|
+
).join(";")}`);
|
|
2990
|
+
}
|
|
2991
|
+
if (parts.length > 0) {
|
|
2860
2992
|
prompt += `
|
|
2861
|
-
|
|
2862
|
-
${req.analysis.emits.map(
|
|
2863
|
-
(e) => ` - ${e.name}${e.params?.length ? `(${e.params.join(", ")})` : ""}`
|
|
2864
|
-
).join("\n")}`;
|
|
2993
|
+
\u5206\u6790:${parts.join("|")}`;
|
|
2865
2994
|
}
|
|
2866
2995
|
}
|
|
2867
2996
|
if (req.generationDescription) {
|
|
2868
2997
|
prompt += `
|
|
2869
|
-
|
|
2870
|
-
\u751F\u6210\u8005\u8BF4\u660E: ${req.generationDescription}`;
|
|
2998
|
+
\u8BF4\u660E:${req.generationDescription}`;
|
|
2871
2999
|
}
|
|
2872
3000
|
return prompt;
|
|
2873
3001
|
}
|
|
@@ -2927,6 +3055,61 @@ ${req.analysis.emits.map(
|
|
|
2927
3055
|
return urlMap[provider] || "https://api.openai.com/v1";
|
|
2928
3056
|
}
|
|
2929
3057
|
};
|
|
3058
|
+
function compressFunctionBodies(code, maxLength) {
|
|
3059
|
+
const lines = code.split("\n");
|
|
3060
|
+
const result = [];
|
|
3061
|
+
let depth = 0;
|
|
3062
|
+
let fnDepth = 0;
|
|
3063
|
+
let inFunction = false;
|
|
3064
|
+
let braceBalance = 0;
|
|
3065
|
+
let capturedLines = 0;
|
|
3066
|
+
for (const line of lines) {
|
|
3067
|
+
const trimmed = line.trim();
|
|
3068
|
+
for (const ch of trimmed) {
|
|
3069
|
+
if (ch === "{") {
|
|
3070
|
+
braceBalance++;
|
|
3071
|
+
depth++;
|
|
3072
|
+
}
|
|
3073
|
+
if (ch === "}") {
|
|
3074
|
+
braceBalance--;
|
|
3075
|
+
depth = Math.max(0, depth - 1);
|
|
3076
|
+
}
|
|
3077
|
+
}
|
|
3078
|
+
const isFnSignature = /^(export\s+)?(async\s+)?function\s|=>\s*\{|=>\s*$/m.test(trimmed) || /^(const|let|var)\s+\w+\s*=\s*(async\s+)?(\([^)]*\)|[^=])\s*=>/.test(trimmed);
|
|
3079
|
+
if (isFnSignature && !inFunction) {
|
|
3080
|
+
inFunction = true;
|
|
3081
|
+
fnDepth = depth;
|
|
3082
|
+
result.push(line);
|
|
3083
|
+
capturedLines++;
|
|
3084
|
+
continue;
|
|
3085
|
+
}
|
|
3086
|
+
if (inFunction) {
|
|
3087
|
+
const isReturn = trimmed.startsWith("return ");
|
|
3088
|
+
const isBranch = /^(if|else|switch|case|try|catch|finally|for|while)/.test(trimmed);
|
|
3089
|
+
const isClosing = trimmed === "}" && depth <= fnDepth - 1;
|
|
3090
|
+
if (isClosing) {
|
|
3091
|
+
result.push(line);
|
|
3092
|
+
inFunction = false;
|
|
3093
|
+
capturedLines++;
|
|
3094
|
+
} else if (isReturn || isBranch) {
|
|
3095
|
+
result.push(trimmed.length > 120 ? `${trimmed.slice(0, 120)}...` : line);
|
|
3096
|
+
capturedLines++;
|
|
3097
|
+
} else if (trimmed.startsWith("//") || trimmed === "") {
|
|
3098
|
+
} else if (capturedLines < maxLength / 30) {
|
|
3099
|
+
result.push(trimmed.length > 100 ? `${trimmed.slice(0, 100)}...` : line);
|
|
3100
|
+
capturedLines++;
|
|
3101
|
+
}
|
|
3102
|
+
} else {
|
|
3103
|
+
result.push(line);
|
|
3104
|
+
capturedLines++;
|
|
3105
|
+
}
|
|
3106
|
+
if (result.join("\n").length > maxLength) {
|
|
3107
|
+
result.push("// ... (truncated)");
|
|
3108
|
+
break;
|
|
3109
|
+
}
|
|
3110
|
+
}
|
|
3111
|
+
return result.join("\n");
|
|
3112
|
+
}
|
|
2930
3113
|
|
|
2931
3114
|
// src/ai/provider.ts
|
|
2932
3115
|
var providerRegistry = /* @__PURE__ */ new Map();
|
|
@@ -3416,15 +3599,18 @@ import path9 from "path";
|
|
|
3416
3599
|
function analyzeFile(filePath) {
|
|
3417
3600
|
const absolutePath = path9.resolve(process.cwd(), filePath);
|
|
3418
3601
|
if (!fs9.existsSync(absolutePath)) {
|
|
3419
|
-
return { filePath, exports: [], apiCalls: [] };
|
|
3602
|
+
return { filePath, exports: [], imports: [], importSignatures: [], apiCalls: [] };
|
|
3420
3603
|
}
|
|
3421
3604
|
const content = fs9.readFileSync(absolutePath, "utf-8");
|
|
3422
3605
|
const ext = path9.extname(filePath);
|
|
3423
3606
|
const result = {
|
|
3424
3607
|
filePath,
|
|
3425
3608
|
exports: extractExports(content, ext),
|
|
3609
|
+
imports: extractImports(content),
|
|
3610
|
+
importSignatures: [],
|
|
3426
3611
|
apiCalls: extractAPICalls(content, filePath)
|
|
3427
3612
|
};
|
|
3613
|
+
result.importSignatures = analyzeImportSignatures(result.imports, path9.dirname(absolutePath));
|
|
3428
3614
|
if (ext === ".vue") {
|
|
3429
3615
|
result.vueAnalysis = analyzeVueComponent(content, path9.basename(filePath, ".vue"));
|
|
3430
3616
|
const scriptMatch = content.match(/<script[^>]*>([\s\S]*?)<\/script>/);
|
|
@@ -3847,6 +4033,90 @@ function generateMockRoutesFromAPICalls(apiCalls) {
|
|
|
3847
4033
|
}
|
|
3848
4034
|
return routes;
|
|
3849
4035
|
}
|
|
4036
|
+
function extractImports(content) {
|
|
4037
|
+
const imports = [];
|
|
4038
|
+
const namedImportRegex = /import\s*\{([^}]+)\}\s*from\s*['"]([^'"]+)['"]/g;
|
|
4039
|
+
let match;
|
|
4040
|
+
while ((match = namedImportRegex.exec(content)) !== null) {
|
|
4041
|
+
const names = match[1].split(",").map((n) => {
|
|
4042
|
+
const parts = n.trim().split(/\s+as\s+/);
|
|
4043
|
+
return parts[0].trim();
|
|
4044
|
+
}).filter(Boolean);
|
|
4045
|
+
imports.push({ names, source: match[2], isDefault: false, isNamespace: false });
|
|
4046
|
+
}
|
|
4047
|
+
const defaultImportRegex = /import\s+(\w+)\s+from\s*['"]([^'"]+)['"]/g;
|
|
4048
|
+
while ((match = defaultImportRegex.exec(content)) !== null) {
|
|
4049
|
+
const fullLine = content.slice(Math.max(0, match.index - 5), match.index + match[0].length);
|
|
4050
|
+
if (fullLine.includes("{")) continue;
|
|
4051
|
+
imports.push({ names: [match[1]], source: match[2], isDefault: true, isNamespace: false });
|
|
4052
|
+
}
|
|
4053
|
+
const namespaceImportRegex = /import\s*\*\s*as\s+(\w+)\s*from\s*['"]([^'"]+)['"]/g;
|
|
4054
|
+
while ((match = namespaceImportRegex.exec(content)) !== null) {
|
|
4055
|
+
imports.push({ names: [match[1]], source: match[2], isDefault: false, isNamespace: true });
|
|
4056
|
+
}
|
|
4057
|
+
return imports;
|
|
4058
|
+
}
|
|
4059
|
+
function analyzeImportSignatures(imports, baseDir) {
|
|
4060
|
+
const signatures = [];
|
|
4061
|
+
for (const imp of imports) {
|
|
4062
|
+
if (!imp.source.startsWith(".") && !imp.source.startsWith("@/") && !imp.source.startsWith("~")) {
|
|
4063
|
+
continue;
|
|
4064
|
+
}
|
|
4065
|
+
const resolvedPath = resolveImportPath2(imp.source, baseDir);
|
|
4066
|
+
if (!resolvedPath || !fs9.existsSync(resolvedPath)) continue;
|
|
4067
|
+
try {
|
|
4068
|
+
const content = fs9.readFileSync(resolvedPath, "utf-8");
|
|
4069
|
+
const ext = path9.extname(resolvedPath);
|
|
4070
|
+
const exports = extractExports(content, ext);
|
|
4071
|
+
const sigMap = {};
|
|
4072
|
+
for (const name of imp.names) {
|
|
4073
|
+
const exp = exports.find((e) => e.name === name);
|
|
4074
|
+
if (exp) {
|
|
4075
|
+
const params = exp.params.length > 0 ? `(${exp.params.join(", ")})` : "";
|
|
4076
|
+
const ret = exp.returnType ? `: ${exp.returnType}` : "";
|
|
4077
|
+
const async = exp.isAsync ? "async " : "";
|
|
4078
|
+
sigMap[name] = `${async}${exp.kind}${params}${ret}`;
|
|
4079
|
+
} else if (imp.isDefault) {
|
|
4080
|
+
const defaultExp = exports.find((e) => e.kind === "default");
|
|
4081
|
+
if (defaultExp) {
|
|
4082
|
+
sigMap[name] = `default[${defaultExp.name || "anonymous"}]`;
|
|
4083
|
+
}
|
|
4084
|
+
} else if (imp.isNamespace) {
|
|
4085
|
+
sigMap[name] = `{${exports.map((e) => e.name).join(", ")}}`;
|
|
4086
|
+
}
|
|
4087
|
+
}
|
|
4088
|
+
if (ext === ".vue") {
|
|
4089
|
+
const vueAnalysis = analyzeVueComponent(content, path9.basename(resolvedPath, ".vue"));
|
|
4090
|
+
if (vueAnalysis.props.length > 0) {
|
|
4091
|
+
sigMap["__props__"] = vueAnalysis.props.map((p) => `${p.name}:${p.type}${p.required ? "!" : "?"}`).join(",");
|
|
4092
|
+
}
|
|
4093
|
+
}
|
|
4094
|
+
if (Object.keys(sigMap).length > 0) {
|
|
4095
|
+
signatures.push({ source: imp.source, signatures: sigMap });
|
|
4096
|
+
}
|
|
4097
|
+
} catch {
|
|
4098
|
+
}
|
|
4099
|
+
}
|
|
4100
|
+
return signatures;
|
|
4101
|
+
}
|
|
4102
|
+
function resolveImportPath2(importSource, baseDir) {
|
|
4103
|
+
let resolved;
|
|
4104
|
+
if (importSource.startsWith("@/") || importSource.startsWith("~")) {
|
|
4105
|
+
const relativePath = importSource.replace(/^[@~]\//, "src/");
|
|
4106
|
+
resolved = path9.resolve(process.cwd(), relativePath);
|
|
4107
|
+
} else if (importSource.startsWith(".")) {
|
|
4108
|
+
resolved = path9.resolve(baseDir, importSource);
|
|
4109
|
+
} else {
|
|
4110
|
+
return null;
|
|
4111
|
+
}
|
|
4112
|
+
const extensions = [".ts", ".tsx", ".js", ".jsx", ".vue", "/index.ts", "/index.js"];
|
|
4113
|
+
if (fs9.existsSync(resolved)) return resolved;
|
|
4114
|
+
for (const ext of extensions) {
|
|
4115
|
+
const withExt = resolved + ext;
|
|
4116
|
+
if (fs9.existsSync(withExt)) return withExt;
|
|
4117
|
+
}
|
|
4118
|
+
return null;
|
|
4119
|
+
}
|
|
3850
4120
|
export {
|
|
3851
4121
|
AI_PRESET_PROVIDERS,
|
|
3852
4122
|
DEFAULT_CONFIG,
|