chore(ui): make knip trustworthy and enforce dead-code in CI (#32727)

knip was producing garbage locally and was never wired into CI, so nobody
trusted it. Two structural problems: it silently degrades when deps are
missing (a partial worktree install flagged all 436 test files as unused),
and its config had blind spots that surfaced as false positives.

Fixes so a knip run means something:

- Register every playwright config (serverRootPath + migration variants), not
  just the main one. serverRootPath.config.ts is invoked via --config in
  test_server_root_path.yml, which knip can't see; it was falsely flagged as
  an unused file
- Treat src/components/ui/** as entry points. These are shadcn design-system
  primitives, intentionally part of the palette before every one is consumed;
  knip was flagging not-yet-used ones (e.g. select.tsx) as dead files and
  their sub-exports as unused. Marking the directory as the design-system
  surface is the correct fix, not deleting components someone is about to use
- Declare @ant-design/icons as a direct dependency. It was imported in ~198
  files but only resolved via antd hoisting, so every one showed up as an
  "unlisted dependency"
- Add an explicit vitest plugin block so test-file classification no longer
  rides on auto-detection
- Stage severities via rules: gate the now-clean categories (files,
  dependencies, unlisted, unresolved) as errors and keep exports/types/
  duplicates as warnings, so CI enforces what's at zero today while the
  remaining findings ratchet down in follow-ups
- Run npm run knip in the frontend-lint CI job, which installs with npm ci so
  it never sees a partial tree

knip now exits 0 with the gated categories clean
This commit is contained in:
ryan-crabbe-berri 2026-07-10 11:50:27 -07:00 • committed by GitHub
parent bca3e88c5f
commit e2eee36438
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 31 additions and 2 deletions

View file

@ -112,3 +112,7 @@ jobs:
run: |
npx eslint . -f json -o "$RUNNER_TEMP/lint-report.json" || true
node scripts/check-lint-budgets.mjs "$RUNNER_TEMP/lint-report.json" eslint-budgets.json --check eslint-metrics.json
- name: Check for dead code (knip)
if: ${{ !cancelled() && steps.changed.outputs.has_files == 'true' }}
run: npm run knip

View file

@ -1,6 +1,6 @@
{
"$schema": "https://unpkg.com/knip@5/schema.json",
"entry": ["scripts/**/*.{ts,mjs}"],
"entry": ["scripts/**/*.{ts,mjs}", "src/components/ui/**/*.{ts,tsx}"],
"project": ["src/**/*.{ts,tsx}", "tests/**/*.{ts,tsx}", "scripts/**/*.{ts,mjs}", "e2e_tests/**/*.ts"],
"ignore": ["src/lib/http/schema.d.ts"],
"ignoreDependencies": [
@ -11,7 +11,30 @@
"tw-animate-css"
],
"playwright": {
"config": "e2e_tests/playwright.config.ts",
"config": [
"e2e_tests/playwright.config.ts",
"e2e_tests/serverRootPath.config.ts",
"e2e_tests/migration.serverRootPath.config.ts"
],
"entry": ["e2e_tests/**/*.spec.ts", "e2e_tests/**/*.setup.ts", "e2e_tests/globalSetup.ts"]
},
"vitest": {
"config": ["vitest.config.ts"]
},
"rules": {
"files": "error",
"dependencies": "error",
"devDependencies": "error",
"optionalPeerDependencies": "error",
"unlisted": "error",
"binaries": "error",
"unresolved": "error",
"exports": "warn",
"nsExports": "warn",
"types": "warn",
"nsTypes": "warn",
"enumMembers": "warn",
"classMembers": "warn",
"duplicates": "warn"
}
}

View file

@ -9,6 +9,7 @@
"version": "0.1.0",
"dependencies": {
"@ant-design/cssinjs": "1.24.0",
"@ant-design/icons": "5.6.1",
"@anthropic-ai/sdk": "0.92.0",
"@base-ui/react": "^1.6.0",
"@headlessui/tailwindcss": "0.2.2",

View file

@ -25,6 +25,7 @@
},
"dependencies": {
"@ant-design/cssinjs": "1.24.0",
"@ant-design/icons": "5.6.1",
"@anthropic-ai/sdk": "0.92.0",
"@base-ui/react": "^1.6.0",
"@headlessui/tailwindcss": "0.2.2",