| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194 |
- import type { CAC } from 'cac';
- import { getPackages } from '@vben/node-utils';
- import depcheck from 'depcheck';
- // 默认配置
- const DEFAULT_CONFIG = {
- // 需要忽略的依赖匹配
- ignoreMatches: [
- 'vite',
- 'vitest',
- 'unbuild',
- '@vben/tsconfig',
- '@vben/vite-config',
- '@vben/tailwind-config',
- '@types/*',
- '@vben-core/design',
- ],
- // 需要忽略的包
- ignorePackages: [
- '@vben/backend-mock',
- '@vben/commitlint-config',
- '@vben/eslint-config',
- '@vben/node-utils',
- '@vben/prettier-config',
- '@vben/stylelint-config',
- '@vben/tailwind-config',
- '@vben/tsconfig',
- '@vben/vite-config',
- '@vben/vsh',
- ],
- // 需要忽略的文件模式
- ignorePatterns: ['dist', 'node_modules', 'public'],
- };
- interface DepcheckResult {
- dependencies: string[];
- devDependencies: string[];
- missing: Record<string, string[]>;
- }
- interface DepcheckConfig {
- ignoreMatches?: string[];
- ignorePackages?: string[];
- ignorePatterns?: string[];
- }
- interface PackageInfo {
- dir: string;
- packageJson: {
- name: string;
- };
- }
- /**
- * 清理依赖检查结果
- * @param unused - 依赖检查结果
- */
- function cleanDepcheckResult(unused: DepcheckResult): void {
- // 删除file:前缀的依赖提示,该依赖是本地依赖
- Reflect.deleteProperty(unused.missing, 'file:');
- // 清理路径依赖
- Object.keys(unused.missing).forEach((key) => {
- unused.missing[key] = (unused.missing[key] || []).filter(
- (item: string) => !item.startsWith('/'),
- );
- if (unused.missing[key].length === 0) {
- Reflect.deleteProperty(unused.missing, key);
- }
- });
- }
- /**
- * 格式化依赖检查结果
- * @param pkgName - 包名
- * @param unused - 依赖检查结果
- */
- function formatDepcheckResult(pkgName: string, unused: DepcheckResult): void {
- const hasIssues =
- Object.keys(unused.missing).length > 0 ||
- unused.dependencies.length > 0 ||
- unused.devDependencies.length > 0;
- if (!hasIssues) {
- return;
- }
- console.log('\n📦 Package:', pkgName);
- if (Object.keys(unused.missing).length > 0) {
- console.log('❌ Missing dependencies:');
- Object.entries(unused.missing).forEach(([dep, files]) => {
- console.log(` - ${dep}:`);
- files.forEach((file) => console.log(` → ${file}`));
- });
- }
- if (unused.dependencies.length > 0) {
- console.log('⚠️ Unused dependencies:');
- unused.dependencies.forEach((dep) => console.log(` - ${dep}`));
- }
- if (unused.devDependencies.length > 0) {
- console.log('⚠️ Unused devDependencies:');
- unused.devDependencies.forEach((dep) => console.log(` - ${dep}`));
- }
- }
- /**
- * 运行依赖检查
- * @param config - 配置选项
- */
- async function runDepcheck(config: DepcheckConfig = {}): Promise<void> {
- try {
- const finalConfig = {
- ...DEFAULT_CONFIG,
- ...config,
- };
- const { packages } = await getPackages();
- let hasIssues = false;
- await Promise.all(
- packages.map(async (pkg: PackageInfo) => {
- // 跳过需要忽略的包
- if (finalConfig.ignorePackages.includes(pkg.packageJson.name)) {
- return;
- }
- const unused = await depcheck(pkg.dir, {
- ignoreMatches: finalConfig.ignoreMatches,
- ignorePatterns: finalConfig.ignorePatterns,
- });
- cleanDepcheckResult(unused);
- const pkgHasIssues =
- Object.keys(unused.missing).length > 0 ||
- unused.dependencies.length > 0 ||
- unused.devDependencies.length > 0;
- if (pkgHasIssues) {
- hasIssues = true;
- formatDepcheckResult(pkg.packageJson.name, unused);
- }
- }),
- );
- if (!hasIssues) {
- console.log('\n✅ Dependency check completed, no issues found');
- }
- } catch (error) {
- console.error(
- '❌ Dependency check failed:',
- error instanceof Error ? error.message : error,
- );
- }
- }
- /**
- * 定义依赖检查命令
- * @param cac - CAC实例
- */
- function defineDepcheckCommand(cac: CAC): void {
- cac
- .command('check-dep')
- .option(
- '--ignore-packages <packages>',
- 'Packages to ignore, comma separated',
- )
- .option(
- '--ignore-matches <matches>',
- 'Dependency patterns to ignore, comma separated',
- )
- .option(
- '--ignore-patterns <patterns>',
- 'File patterns to ignore, comma separated',
- )
- .usage('Analyze project dependencies')
- .action(async ({ ignoreMatches, ignorePackages, ignorePatterns }) => {
- const config: DepcheckConfig = {
- ...(ignorePackages && { ignorePackages: ignorePackages.split(',') }),
- ...(ignoreMatches && { ignoreMatches: ignoreMatches.split(',') }),
- ...(ignorePatterns && { ignorePatterns: ignorePatterns.split(',') }),
- };
- await runDepcheck(config);
- });
- }
- export { defineDepcheckCommand, type DepcheckConfig };
|