index.ts
4.33 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
import type { CAC } from 'cac';
import { extname } from 'node:path';
import { getStagedFiles } from '@vben/node-utils';
import { circularDepsDetect } from 'circular-dependency-scanner';
// 默认配置
const DEFAULT_CONFIG = {
allowedExtensions: ['.cjs', '.js', '.jsx', '.mjs', '.ts', '.tsx', '.vue'],
ignoreDirs: [
'dist',
'.turbo',
'output',
'.cache',
'scripts',
'internal',
'packages/effects/request/src/',
'packages/@core/ui-kit/menu-ui/src/',
'packages/@core/ui-kit/popup-ui/src/',
],
threshold: 0, // 循环依赖的阈值
} as const;
// 类型定义
type CircularDependencyResult = string[];
interface CheckCircularConfig {
allowedExtensions?: string[];
ignoreDirs?: string[];
threshold?: number;
}
interface CommandOptions {
config?: CheckCircularConfig;
staged: boolean;
verbose: boolean;
}
// 缓存机制
const cache = new Map<string, CircularDependencyResult[]>();
/**
* 格式化循环依赖的输出
* @param circles - 循环依赖结果
*/
function formatCircles(circles: CircularDependencyResult[]): void {
if (circles.length === 0) {
console.log('✅ No circular dependencies found');
return;
}
console.log('⚠️ Circular dependencies found:');
circles.forEach((circle, index) => {
console.log(`\nCircular dependency #${index + 1}:`);
circle.forEach((file) => console.log(` → ${file}`));
});
}
/**
* 检查项目中的循环依赖
* @param options - 检查选项
* @param options.staged - 是否只检查暂存区文件
* @param options.verbose - 是否显示详细信息
* @param options.config - 自定义配置
* @returns Promise<void>
*/
async function checkCircular({
config = {},
staged,
verbose,
}: CommandOptions): Promise<void> {
try {
// 合并配置
const finalConfig = {
...DEFAULT_CONFIG,
...config,
};
// 生成忽略模式
const ignorePattern = `**/{${finalConfig.ignoreDirs.join(',')}}/**`;
// 检查缓存
const cacheKey = `${staged}-${process.cwd()}-${ignorePattern}`;
if (cache.has(cacheKey)) {
const cachedResults = cache.get(cacheKey);
if (cachedResults) {
verbose && formatCircles(cachedResults);
}
return;
}
// 检测循环依赖
const results = await circularDepsDetect({
absolute: staged,
cwd: process.cwd(),
ignore: [ignorePattern],
});
if (staged) {
let files = await getStagedFiles();
const allowedExtensions = new Set(finalConfig.allowedExtensions);
// 过滤文件列表
files = files.filter((file) => allowedExtensions.has(extname(file)));
const circularFiles: CircularDependencyResult[] = [];
for (const file of files) {
for (const result of results) {
const resultFiles = result.flat();
if (resultFiles.includes(file)) {
circularFiles.push(result);
}
}
}
// 更新缓存
cache.set(cacheKey, circularFiles);
verbose && formatCircles(circularFiles);
} else {
// 更新缓存
cache.set(cacheKey, results);
verbose && formatCircles(results);
}
// 如果发现循环依赖,只输出警告信息
if (results.length > 0) {
console.log(
'\n⚠️ Warning: Circular dependencies found, please check and fix',
);
}
} catch (error) {
console.error(
'❌ Error checking circular dependencies:',
error instanceof Error ? error.message : error,
);
}
}
/**
* 定义检查循环依赖的命令
* @param cac - CAC实例
*/
function defineCheckCircularCommand(cac: CAC): void {
cac
.command('check-circular')
.option('--staged', 'Only check staged files')
.option('--verbose', 'Show detailed information')
.option('--threshold <number>', 'Threshold for circular dependencies', {
default: 0,
})
.option('--ignore-dirs <dirs>', 'Directories to ignore, comma separated')
.usage('Analyze project circular dependencies')
.action(async ({ ignoreDirs, staged, threshold, verbose }) => {
const config: CheckCircularConfig = {
threshold: Number(threshold),
...(ignoreDirs && { ignoreDirs: ignoreDirs.split(',') }),
};
await checkCircular({
config,
staged,
verbose: verbose ?? true,
});
});
}
export { type CheckCircularConfig, defineCheckCircularCommand };