vue-vben-admin/scripts/vsh/src/check-circular/index.ts
2025-05-02 22:08:36 +08:00

171 lines
4.3 KiB
TypeScript
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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 };