Procházet zdrojové kódy

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.
xingyu4j před 3 týdny
rodič
revize
9ec98f0846

+ 6 - 1
packages/effects/common-ui/src/components/index.ts

@@ -21,6 +21,7 @@ export {
   VbenButtonGroup,
   VbenCheckbox,
   VbenCheckButtonGroup,
+  VbenCollapsibleParams,
   VbenContextMenu,
   VbenCountToAnimator,
   VbenFullScreen,
@@ -33,5 +34,9 @@ export {
   VbenSpinner,
 } 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';

+ 2 - 4
playground/src/adapter/component/index.ts

@@ -36,14 +36,13 @@ import type { Component, Ref } from 'vue';
 import type {
   ApiComponentSharedProps,
   BaseFormComponentType,
+  CollapsibleParamsProps,
   IconPickerProps,
 } from '@vben/common-ui';
 import type { Sortable } from '@vben/hooks';
 import type { TipTapProps } from '@vben/plugins/tiptap';
 import type { Recordable } from '@vben/types';
 
-import type { CollapsibleParamsProps } from '@vben-core/shadcn-ui';
-
 import {
   computed,
   defineAsyncComponent,
@@ -62,6 +61,7 @@ import {
   ApiComponent,
   globalShareState,
   IconPicker,
+  VbenCollapsibleParams,
   VCropper,
 } from '@vben/common-ui';
 import { useSortable } from '@vben/hooks';
@@ -70,8 +70,6 @@ import { $t } from '@vben/locales';
 import { VbenTiptap } from '@vben/plugins/tiptap';
 import { isEmpty } from '@vben/utils';
 
-import { VbenCollapsibleParams } from '@vben-core/shadcn-ui';
-
 import { message, Modal, notification } from 'antdv-next';
 
 import { upload_file } from '#/api/examples/upload';

+ 2 - 6
playground/src/views/examples/form/collapsible.vue

@@ -1,15 +1,11 @@
 <script lang="ts" setup>
 import type { RadioGroupProps } from 'antdv-next';
 
-import type { FormLayout } from '@vben/common-ui';
-
-import type { CollapsibleParamSchema } from '@vben-core/shadcn-ui';
+import type { CollapsibleParamSchema, FormLayout } from '@vben/common-ui';
 
 import { h, ref } from 'vue';
 
-import { Page } from '@vben/common-ui';
-
-import { VbenCollapsibleParams } from '@vben-core/shadcn-ui';
+import { Page, VbenCollapsibleParams } from '@vben/common-ui';
 
 import { Button, Card, message, RadioGroup } from 'antdv-next';
 

Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 411 - 185
pnpm-lock.yaml


+ 1 - 1
pnpm-workspace.yaml

@@ -115,7 +115,6 @@ catalog:
   czg: ^1.13.1
   dayjs: ^1.11.20
   defu: ^6.1.7
-  depcheck: ^1.4.7
   dotenv: ^17.4.2
   echarts: ^6.1.0
   element-plus: ^2.14.0
@@ -141,6 +140,7 @@ catalog:
   is-ci: ^4.1.0
   json-bigint: ^1.0.0
   jsonwebtoken: ^9.0.3
+  knip: ^6.14.1
   lefthook: ^2.1.8
   lodash.clonedeep: ^4.5.0
   lucide-vue-next: ^0.577.0

+ 1 - 1
scripts/vsh/package.json

@@ -25,7 +25,7 @@
     "@vben/node-utils": "workspace:*",
     "cac": "catalog:",
     "circular-dependency-scanner": "catalog:",
-    "depcheck": "catalog:",
+    "knip": "catalog:",
     "publint": "catalog:"
   }
 }

+ 93 - 140
scripts/vsh/src/check-dep/index.ts

@@ -1,162 +1,133 @@
 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 = {
-  // 需要忽略的依赖匹配
-  ignoreMatches: [
-    'vite',
-    'vitest',
-    'tsdown',
-    '@vben/tailwind-config',
-    '@vben/tsconfig',
-    '@vben/vite-config',
-    '@types/*',
+  ignore: ['dist/**', 'docs/**', 'node_modules/**', 'public/**'],
+  ignoreBinaries: [] as string[],
+  ignoreDependencies: [
+    '@iconify/json',
     '@vben-core/design',
-  ],
-  // 需要忽略的包
-  ignorePackages: [
-    '@vben/backend-mock',
     '@vben/commitlint-config',
     '@vben/eslint-config',
-    '@vben/node-utils',
-    '@vben/oxfmt-config',
-    '@vben/oxlint-config',
     '@vben/stylelint-config',
-    '@vben/tsconfig',
+    '@vben/tailwind-config',
     '@vben/vite-config',
-    '@vben/vsh',
+    '@vben/oxlint-config',
+    'playwright',
+    'rimraf',
+    'tailwindcss',
   ],
-  // 需要忽略的文件模式
-  ignorePatterns: ['dist', 'node_modules', 'public'],
+  ignoreWorkspaces: ['internal/lint-configs/*', 'scripts/*'],
 };
 
-interface DepcheckResult {
-  dependencies: string[];
-  devDependencies: string[];
-  missing: Record<string, string[]>;
-}
-
-interface DepcheckConfig {
-  ignoreMatches?: string[];
-  ignorePackages?: string[];
-  ignorePatterns?: string[];
+interface KnipDependency {
+  col: number;
+  line: number;
+  name: string;
+  pos: number;
 }
 
-interface PackageInfo {
-  dir: string;
-  packageJson: {
-    name: string;
-  };
+interface KnipFileIssue {
+  dependencies: KnipDependency[];
+  devDependencies: KnipDependency[];
+  file: string;
+  optionalPeerDependencies: KnipDependency[];
 }
 
-/**
- * 清理依赖检查结果
- * @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);
-    }
-  });
+interface KnipResult {
+  issues: KnipFileIssue[];
 }
 
 /**
  * 格式化依赖检查结果
- * @param pkgName - 包名
- * @param unused - 依赖检查结果
+ * @param result - 依赖检查结果
  */
-function formatDepcheckResult(pkgName: string, unused: DepcheckResult): void {
-  const hasIssues =
-    Object.keys(unused.missing).length > 0 ||
-    unused.dependencies.length > 0 ||
-    unused.devDependencies.length > 0;
+function formatResult(result: KnipResult): void {
+  let hasIssues = false;
 
-  if (!hasIssues) {
-    return;
-  }
+  for (const issue of result.issues) {
+    const hasDeps = issue.dependencies.length > 0;
+    const hasDevDeps = issue.devDependencies.length > 0;
+
+    if (!hasDeps && !hasDevDeps) {
+      continue;
+    }
 
-  console.log('\n📦 Package:', pkgName);
+    hasIssues = true;
+    console.log(`\n📦 ${issue.file}`);
 
-  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 (hasDeps) {
+      console.log('⚠️ Unused dependencies:');
+      for (const dep of issue.dependencies) {
+        console.log(`  - ${dep.name}`);
+      }
+    }
 
-  if (unused.dependencies.length > 0) {
-    console.log('⚠️ Unused dependencies:');
-    unused.dependencies.forEach((dep) => console.log(`  - ${dep}`));
+    if (hasDevDeps) {
+      console.log('⚠️ Unused devDependencies:');
+      for (const dep of issue.devDependencies) {
+        console.log(`  - ${dep.name}`);
+      }
+    }
   }
 
-  if (unused.devDependencies.length > 0) {
-    console.log('⚠️ Unused devDependencies:');
-    unused.devDependencies.forEach((dep) => console.log(`  - ${dep}`));
+  if (!hasIssues) {
+    console.log('\n✅ Dependency check completed, no issues found');
   }
 }
 
 /**
  * 运行依赖检查
- * @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 {
-    const finalConfig = {
-      ...DEFAULT_CONFIG,
-      ...config,
+    await writeFile(configFile, JSON.stringify(DEFAULT_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();
-
-    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');
+    if (execaError.exitCode === 1 && execaError.stdout) {
+      const result: KnipResult = JSON.parse(execaError.stdout);
+      formatResult(result);
+      return;
     }
-  } catch (error) {
+
     console.error(
       '❌ Dependency check failed:',
       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实例
  */
-function defineDepcheckCommand(cac: CAC): void {
+function defineCheckDepCommand(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);
+    .usage('Analyze project dependencies using knip')
+    .action(async () => {
+      await runKnipCheck();
     });
 }
 
-export { defineDepcheckCommand, type DepcheckConfig };
+export { defineCheckDepCommand };

+ 2 - 2
scripts/vsh/src/index.ts

@@ -4,7 +4,7 @@ import { cac } from 'cac';
 
 import { version } from '../package.json';
 import { defineCheckCircularCommand } from './check-circular';
-import { defineDepcheckCommand } from './check-dep';
+import { defineCheckDepCommand } from './check-dep';
 import { defineCodeWorkspaceCommand } from './code-workspace';
 import { defineLintCommand } from './lint';
 import { definePubLintCommand } from './publint';
@@ -30,7 +30,7 @@ async function main(): Promise<void> {
     definePubLintCommand(vsh);
     defineCodeWorkspaceCommand(vsh);
     defineCheckCircularCommand(vsh);
-    defineDepcheckCommand(vsh);
+    defineCheckDepCommand(vsh);
 
     // Set up CLI
     vsh.usage('vsh <command> [options]');

Některé soubory nejsou zobrazeny, neboť je v těchto rozdílových datech změněno mnoho souborů