ai-git-tools 2.0.48 → 2.0.50

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.
@@ -1,170 +0,0 @@
1
- /**
2
- * 測試框架檢測模組
3
- * 自動偵測專案使用的測試框架(Jest / Vitest / Mocha)
4
- * 並發現所有相關測試檔案
5
- */
6
-
7
- import { existsSync, readdirSync, statSync, readFileSync } from 'fs';
8
- import { resolve, join } from 'path';
9
-
10
- /** 支援的框架識別字 */
11
- export const FRAMEWORKS = {
12
- JEST: 'jest',
13
- VITEST: 'vitest',
14
- MOCHA: 'mocha',
15
- };
16
-
17
- export class TestDetector {
18
- /**
19
- * @param {string} projectRoot - 專案根目錄(預設 process.cwd())
20
- */
21
- constructor(projectRoot = process.cwd()) {
22
- this.projectRoot = projectRoot;
23
- }
24
-
25
- /**
26
- * 偵測測試框架
27
- * 優先順序:設定檔 > package.json scripts > package.json devDependencies
28
- * @returns {string|null} 框架名稱或 null
29
- */
30
- detectFramework() {
31
- const root = this.projectRoot;
32
-
33
- // 1. 專用設定檔
34
- if (
35
- existsSync(join(root, 'vitest.config.ts')) ||
36
- existsSync(join(root, 'vitest.config.js')) ||
37
- existsSync(join(root, 'vitest.config.mjs'))
38
- ) {
39
- return FRAMEWORKS.VITEST;
40
- }
41
-
42
- if (
43
- existsSync(join(root, 'jest.config.js')) ||
44
- existsSync(join(root, 'jest.config.ts')) ||
45
- existsSync(join(root, 'jest.config.mjs')) ||
46
- existsSync(join(root, 'jest.config.json'))
47
- ) {
48
- return FRAMEWORKS.JEST;
49
- }
50
-
51
- if (
52
- existsSync(join(root, '.mocharc.js')) ||
53
- existsSync(join(root, '.mocharc.cjs')) ||
54
- existsSync(join(root, '.mocharc.json')) ||
55
- existsSync(join(root, '.mocharc.yml')) ||
56
- existsSync(join(root, '.mocharc.yaml'))
57
- ) {
58
- return FRAMEWORKS.MOCHA;
59
- }
60
-
61
- // 2. package.json
62
- const pkgPath = join(root, 'package.json');
63
- if (existsSync(pkgPath)) {
64
- try {
65
- const pkg = JSON.parse(readFileSync(pkgPath, 'utf-8'));
66
-
67
- // test script
68
- const testScript = pkg.scripts?.test || '';
69
- if (/\bvitest\b/.test(testScript)) return FRAMEWORKS.VITEST;
70
- if (/\bjest\b/.test(testScript)) return FRAMEWORKS.JEST;
71
- if (/\bmocha\b/.test(testScript)) return FRAMEWORKS.MOCHA;
72
-
73
- // devDependencies / dependencies
74
- const allDeps = {
75
- ...pkg.devDependencies,
76
- ...pkg.dependencies,
77
- };
78
- if (allDeps.vitest) return FRAMEWORKS.VITEST;
79
- if (allDeps.jest || allDeps['@jest/core']) return FRAMEWORKS.JEST;
80
- if (allDeps.mocha) return FRAMEWORKS.MOCHA;
81
- } catch (_) {
82
- // 解析失敗,繼續往下
83
- }
84
- }
85
-
86
- return null;
87
- }
88
-
89
- /**
90
- * 遞迴搜尋符合 patterns 的檔案
91
- * @param {string} dir
92
- * @param {RegExp[]} patterns
93
- * @param {string[]} excludes - 排除目錄名稱
94
- * @returns {string[]}
95
- */
96
- _glob(dir, patterns, excludes = ['node_modules', 'dist', '.next', 'coverage', '.git']) {
97
- const results = [];
98
-
99
- let entries;
100
- try {
101
- entries = readdirSync(dir);
102
- } catch (_) {
103
- return results;
104
- }
105
-
106
- for (const entry of entries) {
107
- if (excludes.includes(entry)) continue;
108
- const fullPath = join(dir, entry);
109
- let stat;
110
- try {
111
- stat = statSync(fullPath);
112
- } catch (_) {
113
- continue;
114
- }
115
-
116
- if (stat.isDirectory()) {
117
- results.push(...this._glob(fullPath, patterns, excludes));
118
- } else if (patterns.some((p) => p.test(entry))) {
119
- results.push(fullPath);
120
- }
121
- }
122
-
123
- return results;
124
- }
125
-
126
- /**
127
- * 依框架發現測試檔案
128
- * @param {string} framework
129
- * @param {Object} [config] - 可選:{ testPaths?: string[] }
130
- * @returns {string[]}
131
- */
132
- discoverTests(framework, config = {}) {
133
- // 若使用者明確指定路徑,直接回傳(轉為絕對路徑)
134
- if (config.testPaths && config.testPaths.length > 0) {
135
- return config.testPaths.map((p) => resolve(this.projectRoot, p));
136
- }
137
-
138
- const commonPatterns = [
139
- /\.test\.(js|ts|jsx|tsx|mjs|cjs)$/,
140
- /\.spec\.(js|ts|jsx|tsx|mjs|cjs)$/,
141
- ];
142
-
143
- switch (framework) {
144
- case FRAMEWORKS.JEST:
145
- case FRAMEWORKS.VITEST:
146
- return this._glob(this.projectRoot, commonPatterns);
147
-
148
- case FRAMEWORKS.MOCHA: {
149
- // Mocha 慣例:test/ 或 tests/ 目錄
150
- const mochaPatterns = [/\.(js|ts|mjs|cjs)$/];
151
- const dirs = ['test', 'tests', 'spec', 'specs'].map((d) =>
152
- join(this.projectRoot, d)
153
- );
154
- const results = [];
155
- for (const d of dirs) {
156
- if (existsSync(d)) {
157
- results.push(...this._glob(d, mochaPatterns));
158
- }
159
- }
160
- // 也搜尋根目錄的 .test. / .spec. 檔案
161
- results.push(...this._glob(this.projectRoot, commonPatterns));
162
- return [...new Set(results)];
163
- }
164
-
165
- default:
166
- // 未知框架,使用通用 patterns
167
- return this._glob(this.projectRoot, commonPatterns);
168
- }
169
- }
170
- }
@@ -1,90 +0,0 @@
1
- /**
2
- * Vitest 測試執行器
3
- * 使用 --reporter=json 取得結構化輸出
4
- */
5
-
6
- import { execSync } from 'child_process';
7
- import { ExecutorBase } from './executor-base.js';
8
-
9
- export class VitestExecutor extends ExecutorBase {
10
- constructor(options = {}) {
11
- super(options);
12
- this.framework = 'vitest';
13
- }
14
-
15
- /**
16
- * 執行 Vitest,回傳標準化 TestResults
17
- * @param {string[]} testPaths
18
- * @param {Object} options
19
- * @returns {Promise<import('./executor-base.js').TestResults>}
20
- */
21
- async run(testPaths = [], options = {}) {
22
- const pathArgs = testPaths.length > 0 ? testPaths.join(' ') : '';
23
- const cmd = `npx vitest run --reporter=json ${pathArgs}`;
24
-
25
- if (options.verbose) {
26
- console.log(` 執行命令:${cmd}`);
27
- }
28
-
29
- let output = '';
30
- try {
31
- output = execSync(cmd, {
32
- encoding: 'utf-8',
33
- timeout: this.timeout,
34
- stdio: ['pipe', 'pipe', 'pipe'],
35
- cwd: options.projectRoot || process.cwd(),
36
- });
37
- } catch (error) {
38
- output = error.stdout || error.output?.[1] || '';
39
- }
40
-
41
- return this.parseResults(output);
42
- }
43
-
44
- /**
45
- * 將 Vitest --reporter=json 輸出解析為標準 TestResults
46
- * @param {string} output
47
- * @returns {import('./executor-base.js').TestResults}
48
- */
49
- parseResults(output) {
50
- const result = this.emptyResults(this.framework);
51
- result.rawOutput = output;
52
-
53
- let json;
54
- try {
55
- const start = output.indexOf('{');
56
- if (start === -1) return result;
57
- json = JSON.parse(output.slice(start));
58
- } catch (_) {
59
- return result;
60
- }
61
-
62
- // Vitest JSON 結構
63
- const sum = (json.numPassedTests ?? 0) + (json.numFailedTests ?? 0) + (json.numPendingTests ?? 0);
64
- const numTotalTests = json.numTotalTests ?? sum;
65
- result.total = numTotalTests;
66
- result.passed = json.numPassedTests ?? 0;
67
- result.failed = json.numFailedTests ?? 0;
68
- result.skipped = json.numPendingTests ?? 0;
69
- result.duration = json.testResults?.reduce(
70
- (acc, s) => acc + (s.perfStats?.runtime ?? s.duration ?? 0),
71
- 0
72
- ) ?? 0;
73
-
74
- for (const suite of json.testResults ?? []) {
75
- const suiteName =
76
- suite.name?.replace(process.cwd(), '').replace(/^\//, '') ?? '';
77
- for (const t of suite.assertionResults ?? []) {
78
- result.tests.push({
79
- name: t.fullName ?? t.title,
80
- suite: suiteName,
81
- passed: t.status === 'passed',
82
- duration: t.duration ?? 0,
83
- error: t.failureMessages?.join('\n') || null,
84
- });
85
- }
86
- }
87
-
88
- return result;
89
- }
90
- }