Dead code detection + Git hooks in one setup. Works with: pnpm monorepo, SvelteKit, Hono, Vitest, Biome
pnpm add -Dw knip lefthookAdd scripts to root package.json:
{
"$schema": "https://unpkg.com/knip@5/schema.json"
}knip auto-detects: package.json exports/main, tsconfig paths, Vitest tests, etc.
Most single-package projects need zero config.
{
"$schema": "https://unpkg.com/knip@5/schema.json",
"workspaces": {
".": {
"entry": "scripts/*.ts"
},
"packages/*": {},
"apps/backend": {
"entry": "scripts/*.ts"
},
"apps/frontend": {}
}
}- knip reads
pnpm-workspace.yamlautomatically - Built-in plugins: SvelteKit (
+page.svelte,+server.ts), Vitest, Vite, etc. "packages/*": {}— empty = rely on plugin auto-detection- Add
"entry"only for non-standard entry points (e.g.scripts/)
{
// Files that are referenced but not directly imported (e.g. vitest setupFiles)
"ignoreFiles": ["**/vitest.setup.ts"],
// SvelteKit virtual modules (./$types) cause false "unresolved" reports
"ignoreIssues": {
"**/+server.ts": ["unresolved"],
"**/+page.server.ts": ["unresolved"],
"**/+layout.server.ts": ["unresolved"],
"**/vitest.config.ts": ["unresolved"]
}
}| Issue | Cause | Fix |
|---|---|---|
./$types unresolved |
SvelteKit virtual module | ignoreIssues on +server.ts etc. |
vitest.setup.ts unused file |
Referenced via setupFiles, not import |
ignoreFiles |
| Workspace deps flagged unused | .svelte-kit/ dir present |
Delete .svelte-kit/, rely on ignoreIssues instead |
--fix removes needed dep |
Root scripts/*.ts not registered as entry |
Add ".": { "entry": "scripts/*.ts" } |
| Duplicate export warning | export const X = Y where Y is already exported |
Add /** @alias */ JSDoc tag |
--fix removes export but leaves dead local code |
knip only strips export keyword |
Manually delete the now-unused function |
pre-commit:
parallel: true
jobs:
- name: biome
glob: "*.{ts,js,mjs,cjs}"
run: pnpm biome check --no-errors-on-unmatched {staged_files}
- name: knip
run: pnpm knip --no-progressbiome: runs on staged files only (fast)knip: analyzes entire project (dependency graph requires full scan)parallel: true: both run concurrently- Do NOT add typecheck/test here (too slow for pre-commit)
pre-commit:
parallel: true
jobs:
- name: lint
glob: "*.{ts,js,mjs,cjs,tsx,jsx}"
run: pnpm eslint --no-warn-ignored {staged_files}
- name: format
glob: "*.{ts,js,mjs,cjs,tsx,jsx,json,css,md}"
run: pnpm prettier --check {staged_files}
- name: knip
run: pnpm knip --no-progress# lefthook
lefthook-local.yml# Install git hooks
pnpm lefthook install
# Run knip standalone
pnpm knip
# Auto-fix (removes unused exports/deps)
pnpm knip --fix
# Test hooks manually
pnpm lefthook run pre-commit
# Verify no breakage
pnpm build && pnpm test && pnpm typecheck# GitHub Actions
- name: Dead code check
run: pnpm knip --no-progresspnpm knip # Detect issues
pnpm knip --fix # Auto-remove unused exports/deps
pnpm knip --include files # Only check unused files
pnpm knip --include exports # Only check unused exports
pnpm knip --debug # Verbose output for troubleshooting config
{ "scripts": { "knip": "knip", "knip:fix": "knip --fix" } }