Trovella Wiki

Dead Code Detection

Knip configuration reference -- per-workspace entry points, exclusions, and why dead code detection matters for AI-generated codebases.

Knip is a dead code detection tool for JavaScript/TypeScript monorepos. It analyzes entry points, import graphs, and configuration files to find unused files, unused exports, unused dependencies, and unused types across the entire workspace graph. Unlike ESLint's no-unused-vars (which checks within a single file), Knip traces cross-package usage to determine whether an exported function is actually imported anywhere.

Why Dead Code Detection Matters Here

Dead code detection is especially important with AI coding agents. Agents tend to create new code instead of discovering and reusing existing patterns. When two Claude Code sessions independently solve similar problems, the result is two implementations where only one is actually used. Over time, this accumulates dead exports, orphaned files, and phantom dependencies.

Running Locally

pnpm lint:dead-code

This expands to knip, which reads configuration from knip.json at the monorepo root.

Configuration

Per-Workspace Entry Points

Knip needs to know the entry points for each package so it can trace the import graph. The configuration defines these per workspace.

apps/web -- Next.js entry points:

Entry PatternWhy
src/app/**/page.tsxNext.js page routes
src/app/**/layout.tsxNext.js layout components
src/app/**/route.tsNext.js API route handlers
source.config.tsFumadocs MDX source configuration
vitest.config.tsTest configuration
src/**/__tests__/**/*.{ts,tsx}Test files

Ignored dependencies in apps/web: tailwindcss (consumed by PostCSS, not imported), postcss (same), @vitejs/plugin-react (Vitest plugin reference), fumadocs-mdx (build plugin).

packages/db -- Drizzle ORM package:

EntryWhy
src/index.tsMain barrel export
drizzle.config.tsDrizzle Kit configuration
vitest.config.tsTest configuration

Ignored dependency: drizzle-seed (used by seed scripts that Knip cannot trace).

packages/mcp -- MCP tool server (multiple entry points):

EntryWhy
src/index.tsBarrel export
src/server.tsMCP server entry point
src/auth.tsAuth helper (imported by server)
src/resolve-org.tsOrganization resolver

packages/api -- tRPC routers. Ignored dependency: pino (used by logger context, not directly imported).

packages/auth -- Better Auth. Ignored dependency: drizzle-orm (used by Better Auth adapter config, not directly imported).

packages/logger -- Pino logger. Ignored dependency: pino-pretty (used via Pino transport config, not a direct import).

packages/* -- catch-all for remaining packages. Standard src/index.ts entry point.

tools/test-audit -- CLI tool with src/cli.ts entry point. Ignored dependencies: Stryker mutator packages (loaded dynamically by Stryker framework).

Global Exclusions

Certain packages and directories are excluded entirely from Knip analysis because they produce false positives:

ExcludedWhy
packages/config-eslint/**Tooling config, not an importable package
packages/config-typescript/**Tooling config, not an importable package
packages/design-tokens/**Consumed via Tailwind CSS config, not TypeScript imports
apps/web/src/components/ui/**shadcn/ui generated components (some may not be used yet)
scripts/**Standalone scripts, not part of the application graph

Global Ignored Dependencies

DependencyWhy
@repo/*Internal workspace packages (Knip handles these via workspace graph)
@vitest/coverage-v8Vitest coverage plugin (loaded by Vitest, not imported)
@modelcontextprotocol/sdkMCP SDK (used by transport, Knip cannot trace)
@vvago/valeVale prose linter (CLI tool, not imported)
markdown-link-checkCLI tool, not imported
vitestTest runner (loaded by config, not imported in source)

Configuration Flags

{
  "vitest": false,
  "ignoreExportsUsedInFile": true,
  "exclude": ["unlisted"]
}
  • vitest: false -- disables Knip's built-in Vitest plugin (entry points are defined manually for accuracy)
  • ignoreExportsUsedInFile -- does not flag exports that are also used within the same file
  • exclude: ["unlisted"] -- suppresses "unlisted dependency" findings (handled by other tools)

Barrel File Convention

One src/index.ts barrel file per package is the standard public API surface. Barrel files inside feature directories within apps/web/ are prohibited because:

  1. They cause circular import issues with Next.js App Router
  2. They make Knip's analysis less accurate -- everything re-exported from a barrel appears "used" even if nothing downstream imports it

CI Integration

Knip runs as pnpm lint:dead-code in the pnpm ci:check pipeline, after dependency-cruiser and before jscpd. A non-zero exit code blocks the PR from merging.

Common Findings

FindingTypical CauseFix
Unused exportAI agent created a helper that was later replacedRemove the export, or remove the file if all exports are unused
Unused dependencyPackage.json lists a dep that is no longer importedRemove from package.json, run pnpm install
Unused fileAI agent created a utility file but never imported itDelete the file

On this page