Knip
Setting up Knip (unused files / exports / dependencies detector) so it keeps catching real dead code without drowning you in false positives.
Scope
Use when the change touches a Knip config or responds to Knip output. Guidance, not a procedure.
Rules
Config lives in knip.jsonc
Not knip.json, not knip.ts, not a package.json key. JSONC allows comments, and every ignore entry needs a written reason next to it — a bare package name in ignoreDependencies is unreviewable a month later.
{
"$schema": "https://unpkg.com/knip@6/schema-jsonc.json",
"entry": ["src/index.ts"],
"project": ["src/**/*.{ts,tsx}"]
}
Only use knip.ts when the config needs a compilers function, which cannot be expressed as data.
Never set ignoreExportsUsedInFile
An exported type/interface that only its own file's functions use is a local type wearing an export keyword. Knip's default (false) reports it — that is the finding you want. Setting it to true, or granularly to { "interface": true, "type": true }, deletes the check. Same for excluding the types issue type to make CI green.
The fix for such a finding is to remove the export keyword. If one export genuinely must stay public, tag that symbol (/** @public */ plus "tags": ["-public"]) rather than relaxing the global option.
shadcn/ui: ignore the generated directory
{
// Generated by shadcn/ui — not hand-authored, do not prune.
"ignore": ["src/components/ui/**"]
}
Those files are vendored and ship with unused variants; pruning them just loses the next shadcn add. Match the path to the project's components.json alias.
Consequence: ignore drops the files from the graph, so packages used only there (radix, class-variance-authority, clsx, tailwind-merge, lucide-react, …) now report as unused — add the ones the project has to ignoreDependencies. If that list gets long, use "entry": ["src/components/ui/**"] instead: exports go unreported and imports still count.
CSS-imported packages go in ignoreDependencies
Tailwind v4 is wired from CSS (@import "tailwindcss"), which Knip does not scan, so it reports a false positive.
{
"ignoreDependencies": [
// Imported from CSS, invisible to the module graph.
"tailwindcss",
"tw-animate-css"
]
}
Grep the stylesheets before deleting any dependency Knip flags.
Fix noise in this order
entry/project globs → plugin config → targeted ignores (ignoreDependencies, ignoreFiles, a tag) → ignore last. Never quiet a finding by widening top-level exclude; that kills the check repo-wide to hide one instance.
Completion Criterion
- Config is
knip.jsoncwith theschema-jsonc.json$schema(unlesscompilersforcesknip.ts). ignoreExportsUsedInFileis unset, andtypesis not excluded.- Every
ignore/ignoreFiles/ignoreDependenciesentry has a comment saying why. - shadcn's
components/ui/**is ignored or set asentry; if ignored, its exclusive dependencies are listed. - CSS-imported packages are in
ignoreDependencies, not removed frompackage.json. knipruns clean with no check disabled repo-wide.