refactor: replace depcheck with knip for dependency checking
Migrate vsh check-dep from depcheck to knip, reducing 315 transitive dependencies. Config is inlined in DEFAULT_CONFIG, matching the check-circular pattern. Also re-export CollapsibleParams from @vben/common-ui.master^2
parent
f71094e878
commit
9ec98f0846
|
|
@ -21,6 +21,7 @@ export {
|
||||||
VbenButtonGroup,
|
VbenButtonGroup,
|
||||||
VbenCheckbox,
|
VbenCheckbox,
|
||||||
VbenCheckButtonGroup,
|
VbenCheckButtonGroup,
|
||||||
|
VbenCollapsibleParams,
|
||||||
VbenContextMenu,
|
VbenContextMenu,
|
||||||
VbenCountToAnimator,
|
VbenCountToAnimator,
|
||||||
VbenFullScreen,
|
VbenFullScreen,
|
||||||
|
|
@ -33,5 +34,9 @@ export {
|
||||||
VbenSpinner,
|
VbenSpinner,
|
||||||
} from '@vben-core/shadcn-ui';
|
} from '@vben-core/shadcn-ui';
|
||||||
|
|
||||||
export type { FlattenedItem } from '@vben-core/shadcn-ui';
|
export type {
|
||||||
|
CollapsibleParamSchema,
|
||||||
|
CollapsibleParamsProps,
|
||||||
|
FlattenedItem,
|
||||||
|
} from '@vben-core/shadcn-ui';
|
||||||
export { globalShareState } from '@vben-core/shared/global-state';
|
export { globalShareState } from '@vben-core/shared/global-state';
|
||||||
|
|
|
||||||
|
|
@ -36,14 +36,13 @@ import type { Component, Ref } from 'vue';
|
||||||
import type {
|
import type {
|
||||||
ApiComponentSharedProps,
|
ApiComponentSharedProps,
|
||||||
BaseFormComponentType,
|
BaseFormComponentType,
|
||||||
|
CollapsibleParamsProps,
|
||||||
IconPickerProps,
|
IconPickerProps,
|
||||||
} from '@vben/common-ui';
|
} from '@vben/common-ui';
|
||||||
import type { Sortable } from '@vben/hooks';
|
import type { Sortable } from '@vben/hooks';
|
||||||
import type { TipTapProps } from '@vben/plugins/tiptap';
|
import type { TipTapProps } from '@vben/plugins/tiptap';
|
||||||
import type { Recordable } from '@vben/types';
|
import type { Recordable } from '@vben/types';
|
||||||
|
|
||||||
import type { CollapsibleParamsProps } from '@vben-core/shadcn-ui';
|
|
||||||
|
|
||||||
import {
|
import {
|
||||||
computed,
|
computed,
|
||||||
defineAsyncComponent,
|
defineAsyncComponent,
|
||||||
|
|
@ -62,6 +61,7 @@ import {
|
||||||
ApiComponent,
|
ApiComponent,
|
||||||
globalShareState,
|
globalShareState,
|
||||||
IconPicker,
|
IconPicker,
|
||||||
|
VbenCollapsibleParams,
|
||||||
VCropper,
|
VCropper,
|
||||||
} from '@vben/common-ui';
|
} from '@vben/common-ui';
|
||||||
import { useSortable } from '@vben/hooks';
|
import { useSortable } from '@vben/hooks';
|
||||||
|
|
@ -70,8 +70,6 @@ import { $t } from '@vben/locales';
|
||||||
import { VbenTiptap } from '@vben/plugins/tiptap';
|
import { VbenTiptap } from '@vben/plugins/tiptap';
|
||||||
import { isEmpty } from '@vben/utils';
|
import { isEmpty } from '@vben/utils';
|
||||||
|
|
||||||
import { VbenCollapsibleParams } from '@vben-core/shadcn-ui';
|
|
||||||
|
|
||||||
import { message, Modal, notification } from 'antdv-next';
|
import { message, Modal, notification } from 'antdv-next';
|
||||||
|
|
||||||
import { upload_file } from '#/api/examples/upload';
|
import { upload_file } from '#/api/examples/upload';
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,11 @@
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import type { RadioGroupProps } from 'antdv-next';
|
import type { RadioGroupProps } from 'antdv-next';
|
||||||
|
|
||||||
import type { FormLayout } from '@vben/common-ui';
|
import type { CollapsibleParamSchema, FormLayout } from '@vben/common-ui';
|
||||||
|
|
||||||
import type { CollapsibleParamSchema } from '@vben-core/shadcn-ui';
|
|
||||||
|
|
||||||
import { h, ref } from 'vue';
|
import { h, ref } from 'vue';
|
||||||
|
|
||||||
import { Page } from '@vben/common-ui';
|
import { Page, VbenCollapsibleParams } from '@vben/common-ui';
|
||||||
|
|
||||||
import { VbenCollapsibleParams } from '@vben-core/shadcn-ui';
|
|
||||||
|
|
||||||
import { Button, Card, message, RadioGroup } from 'antdv-next';
|
import { Button, Card, message, RadioGroup } from 'antdv-next';
|
||||||
|
|
||||||
|
|
|
||||||
776
pnpm-lock.yaml
776
pnpm-lock.yaml
File diff suppressed because it is too large
Load Diff
|
|
@ -115,7 +115,6 @@ catalog:
|
||||||
czg: ^1.13.1
|
czg: ^1.13.1
|
||||||
dayjs: ^1.11.20
|
dayjs: ^1.11.20
|
||||||
defu: ^6.1.7
|
defu: ^6.1.7
|
||||||
depcheck: ^1.4.7
|
|
||||||
dotenv: ^17.4.2
|
dotenv: ^17.4.2
|
||||||
echarts: ^6.1.0
|
echarts: ^6.1.0
|
||||||
element-plus: ^2.14.0
|
element-plus: ^2.14.0
|
||||||
|
|
@ -141,6 +140,7 @@ catalog:
|
||||||
is-ci: ^4.1.0
|
is-ci: ^4.1.0
|
||||||
json-bigint: ^1.0.0
|
json-bigint: ^1.0.0
|
||||||
jsonwebtoken: ^9.0.3
|
jsonwebtoken: ^9.0.3
|
||||||
|
knip: ^6.14.1
|
||||||
lefthook: ^2.1.8
|
lefthook: ^2.1.8
|
||||||
lodash.clonedeep: ^4.5.0
|
lodash.clonedeep: ^4.5.0
|
||||||
lucide-vue-next: ^0.577.0
|
lucide-vue-next: ^0.577.0
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,7 @@
|
||||||
"@vben/node-utils": "workspace:*",
|
"@vben/node-utils": "workspace:*",
|
||||||
"cac": "catalog:",
|
"cac": "catalog:",
|
||||||
"circular-dependency-scanner": "catalog:",
|
"circular-dependency-scanner": "catalog:",
|
||||||
"depcheck": "catalog:",
|
"knip": "catalog:",
|
||||||
"publint": "catalog:"
|
"publint": "catalog:"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,162 +1,133 @@
|
||||||
import type { CAC } from 'cac';
|
import type { CAC } from 'cac';
|
||||||
|
|
||||||
import { getPackages } from '@vben/node-utils';
|
import { mkdtemp, rm, writeFile } from 'node:fs/promises';
|
||||||
|
import { createRequire } from 'node:module';
|
||||||
|
import { tmpdir } from 'node:os';
|
||||||
|
import { dirname, join } from 'node:path';
|
||||||
|
|
||||||
import depcheck from 'depcheck';
|
import { execa } from '@vben/node-utils';
|
||||||
|
|
||||||
|
const require = createRequire(import.meta.url);
|
||||||
|
const knipMain = require.resolve('knip');
|
||||||
|
const knipCli = join(dirname(knipMain), '..', 'bin', 'knip.js');
|
||||||
|
|
||||||
// 默认配置
|
|
||||||
const DEFAULT_CONFIG = {
|
const DEFAULT_CONFIG = {
|
||||||
// 需要忽略的依赖匹配
|
ignore: ['dist/**', 'docs/**', 'node_modules/**', 'public/**'],
|
||||||
ignoreMatches: [
|
ignoreBinaries: [] as string[],
|
||||||
'vite',
|
ignoreDependencies: [
|
||||||
'vitest',
|
'@iconify/json',
|
||||||
'tsdown',
|
|
||||||
'@vben/tailwind-config',
|
|
||||||
'@vben/tsconfig',
|
|
||||||
'@vben/vite-config',
|
|
||||||
'@types/*',
|
|
||||||
'@vben-core/design',
|
'@vben-core/design',
|
||||||
],
|
|
||||||
// 需要忽略的包
|
|
||||||
ignorePackages: [
|
|
||||||
'@vben/backend-mock',
|
|
||||||
'@vben/commitlint-config',
|
'@vben/commitlint-config',
|
||||||
'@vben/eslint-config',
|
'@vben/eslint-config',
|
||||||
'@vben/node-utils',
|
|
||||||
'@vben/oxfmt-config',
|
|
||||||
'@vben/oxlint-config',
|
|
||||||
'@vben/stylelint-config',
|
'@vben/stylelint-config',
|
||||||
'@vben/tsconfig',
|
'@vben/tailwind-config',
|
||||||
'@vben/vite-config',
|
'@vben/vite-config',
|
||||||
'@vben/vsh',
|
'@vben/oxlint-config',
|
||||||
|
'playwright',
|
||||||
|
'rimraf',
|
||||||
|
'tailwindcss',
|
||||||
],
|
],
|
||||||
// 需要忽略的文件模式
|
ignoreWorkspaces: ['internal/lint-configs/*', 'scripts/*'],
|
||||||
ignorePatterns: ['dist', 'node_modules', 'public'],
|
|
||||||
};
|
};
|
||||||
|
|
||||||
interface DepcheckResult {
|
interface KnipDependency {
|
||||||
dependencies: string[];
|
col: number;
|
||||||
devDependencies: string[];
|
line: number;
|
||||||
missing: Record<string, string[]>;
|
name: string;
|
||||||
|
pos: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface DepcheckConfig {
|
interface KnipFileIssue {
|
||||||
ignoreMatches?: string[];
|
dependencies: KnipDependency[];
|
||||||
ignorePackages?: string[];
|
devDependencies: KnipDependency[];
|
||||||
ignorePatterns?: string[];
|
file: string;
|
||||||
|
optionalPeerDependencies: KnipDependency[];
|
||||||
}
|
}
|
||||||
|
|
||||||
interface PackageInfo {
|
interface KnipResult {
|
||||||
dir: string;
|
issues: KnipFileIssue[];
|
||||||
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 result - 依赖检查结果
|
||||||
* @param unused - 依赖检查结果
|
|
||||||
*/
|
*/
|
||||||
function formatDepcheckResult(pkgName: string, unused: DepcheckResult): void {
|
function formatResult(result: KnipResult): void {
|
||||||
const hasIssues =
|
let hasIssues = false;
|
||||||
Object.keys(unused.missing).length > 0 ||
|
|
||||||
unused.dependencies.length > 0 ||
|
for (const issue of result.issues) {
|
||||||
unused.devDependencies.length > 0;
|
const hasDeps = issue.dependencies.length > 0;
|
||||||
|
const hasDevDeps = issue.devDependencies.length > 0;
|
||||||
|
|
||||||
|
if (!hasDeps && !hasDevDeps) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
hasIssues = true;
|
||||||
|
console.log(`\n📦 ${issue.file}`);
|
||||||
|
|
||||||
|
if (hasDeps) {
|
||||||
|
console.log('⚠️ Unused dependencies:');
|
||||||
|
for (const dep of issue.dependencies) {
|
||||||
|
console.log(` - ${dep.name}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hasDevDeps) {
|
||||||
|
console.log('⚠️ Unused devDependencies:');
|
||||||
|
for (const dep of issue.devDependencies) {
|
||||||
|
console.log(` - ${dep.name}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (!hasIssues) {
|
if (!hasIssues) {
|
||||||
return;
|
console.log('\n✅ Dependency check completed, no issues found');
|
||||||
}
|
|
||||||
|
|
||||||
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> {
|
async function runKnipCheck(): Promise<void> {
|
||||||
|
const cwd = process.cwd();
|
||||||
|
const tempDir = await mkdtemp(join(tmpdir(), 'vsh-check-dep-'));
|
||||||
|
const configFile = join(tempDir, 'knip.json');
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const finalConfig = {
|
await writeFile(configFile, JSON.stringify(DEFAULT_CONFIG));
|
||||||
...DEFAULT_CONFIG,
|
|
||||||
...config,
|
const args = [
|
||||||
|
knipCli,
|
||||||
|
'--config',
|
||||||
|
configFile,
|
||||||
|
'--include',
|
||||||
|
'dependencies',
|
||||||
|
'--reporter',
|
||||||
|
'json',
|
||||||
|
'--no-config-hints',
|
||||||
|
];
|
||||||
|
|
||||||
|
await execa(process.execPath, args, { cwd });
|
||||||
|
console.log('\n✅ Dependency check completed, no issues found');
|
||||||
|
} catch (error: unknown) {
|
||||||
|
const execaError = error as {
|
||||||
|
exitCode?: number;
|
||||||
|
stdout?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
const { packages } = await getPackages();
|
if (execaError.exitCode === 1 && execaError.stdout) {
|
||||||
|
const result: KnipResult = JSON.parse(execaError.stdout);
|
||||||
let hasIssues = false;
|
formatResult(result);
|
||||||
|
return;
|
||||||
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(
|
console.error(
|
||||||
'❌ Dependency check failed:',
|
'❌ Dependency check failed:',
|
||||||
error instanceof Error ? error.message : error,
|
error instanceof Error ? error.message : error,
|
||||||
);
|
);
|
||||||
|
} finally {
|
||||||
|
await rm(tempDir, { force: true, recursive: true });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -164,31 +135,13 @@ async function runDepcheck(config: DepcheckConfig = {}): Promise<void> {
|
||||||
* 定义依赖检查命令
|
* 定义依赖检查命令
|
||||||
* @param cac - CAC实例
|
* @param cac - CAC实例
|
||||||
*/
|
*/
|
||||||
function defineDepcheckCommand(cac: CAC): void {
|
function defineCheckDepCommand(cac: CAC): void {
|
||||||
cac
|
cac
|
||||||
.command('check-dep')
|
.command('check-dep')
|
||||||
.option(
|
.usage('Analyze project dependencies using knip')
|
||||||
'--ignore-packages <packages>',
|
.action(async () => {
|
||||||
'Packages to ignore, comma separated',
|
await runKnipCheck();
|
||||||
)
|
|
||||||
.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 };
|
export { defineCheckDepCommand };
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ import { cac } from 'cac';
|
||||||
|
|
||||||
import { version } from '../package.json';
|
import { version } from '../package.json';
|
||||||
import { defineCheckCircularCommand } from './check-circular';
|
import { defineCheckCircularCommand } from './check-circular';
|
||||||
import { defineDepcheckCommand } from './check-dep';
|
import { defineCheckDepCommand } from './check-dep';
|
||||||
import { defineCodeWorkspaceCommand } from './code-workspace';
|
import { defineCodeWorkspaceCommand } from './code-workspace';
|
||||||
import { defineLintCommand } from './lint';
|
import { defineLintCommand } from './lint';
|
||||||
import { definePubLintCommand } from './publint';
|
import { definePubLintCommand } from './publint';
|
||||||
|
|
@ -30,7 +30,7 @@ async function main(): Promise<void> {
|
||||||
definePubLintCommand(vsh);
|
definePubLintCommand(vsh);
|
||||||
defineCodeWorkspaceCommand(vsh);
|
defineCodeWorkspaceCommand(vsh);
|
||||||
defineCheckCircularCommand(vsh);
|
defineCheckCircularCommand(vsh);
|
||||||
defineDepcheckCommand(vsh);
|
defineCheckDepCommand(vsh);
|
||||||
|
|
||||||
// Set up CLI
|
// Set up CLI
|
||||||
vsh.usage('vsh <command> [options]');
|
vsh.usage('vsh <command> [options]');
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue