depsnap
v0.1.0
Published
Find unused dependencies — conservative, zero-config, zero dependencies. Flags only packages referenced nowhere, so every result is safe to remove.
Downloads
148
Maintainers
Readme
depsnap
Find the dependencies you can safely delete. Your package.json collects
cruft — a package you stopped importing six refactors ago is still installed,
still audited, still slowing npm install. depsnap finds the ones referenced
nowhere in your code, config, or scripts. Conservative by design, zero
config, zero dependencies.
npx depsnapdepsnap — 1 unused dependency in demo-app (of 3 checked)
● left-pad dependencies no import or script reference
Verify before removing — depsnap only flags names it found nowhere · 1 @types/* skipped.Why another one
depcheck was the go-to for years, but it was archived in 2025
with a long tail of false-positive bugs — TypeScript type-only imports, subpath
imports, peer deps, @types/* stubs all getting wrongly flagged as "unused".
knip is the powerful successor, but it's a ~150-plugin tool built for big
codebases; sometimes you just want a five-second answer to "what's safe to
delete?"
depsnap takes the opposite bet from depcheck: instead of trying to prove a
dependency is used (and getting it wrong on edge cases), it only flags a
package when its name appears nowhere — no import, no require, no dynamic
import(), no string in a config file, no CLI in an npm script. Every result
is high-confidence removable.
That means it handles the cases depcheck botched, for free:
| Case | depcheck | depsnap |
|------|----------|---------|
| import type { X } from 'pkg' | often flagged unused | seen as used |
| import 'pkg/subpath' | sometimes missed | seen as used |
| await import('pkg') | sometimes missed | seen as used |
| @types/* stubs | flagged unused | skipped by default |
| CLI used only in an npm script (tsc) | flagged unused | seen as used (reads node_modules bins) |
| peer dependencies | flagged unused | not checked |
The trade-off is honest: depsnap won't catch a dependency you import but never actually call — that needs full reachability analysis. For that, reach for knip. depsnap is the fast first pass.
Usage
depsnap # check ./package.json against this project
depsnap path/to/pkg # check a project in another directory
depsnap --dev # also check devDependencies
depsnap --format json # machine-readable outputTry it on the bundled example after cloning:
node bin/cli.js examples/demo # → flags left-pad
node bin/cli.js examples/demo --dev # → also flags unused-dev-toolHow it works
- Reads
package.jsonfor your declared dependencies. - Walks your project (skipping
node_modules,dist,.git, etc.) and builds a corpus of every.js/.ts/.jsx/.tsx/.vue/.svelte/.json/.yaml/...and dotfile config — but neverpackage.jsonor a lockfile, since those list every dependency by name and would mask everything. - For each dependency, checks whether its name (with import-aware word
boundaries, so
lodash≠lodash-es) appears anywhere in that corpus, in the npmscripts, or as one of its installednode_modulesbin names. - Reports the ones that appear nowhere.
In CI
depsnap exits non-zero when it finds unused dependencies, so it can keep cruft
from creeping back in:
- run: npx depsnap --dev| Exit code | Meaning |
|-----------|---------|
| 0 | no unused dependencies |
| 1 | unused dependencies found |
| 2 | error (no package.json, bad arguments) |
Options
--dev also check devDependencies (default: dependencies only)
--include-types also check @types/* packages (default: skipped)
--format text|json output format (default: text)
-v, --version
-h, --helpScope
Single-package projects. Workspaces/monorepos aren't resolved across packages yet
(run it per-package). It reads package.json dependencies/devDependencies;
peerDependencies and optionalDependencies are intentionally left alone.
Also available for Python
pip install depsnap
depsnapSame checks, same flags, same exit codes — depsnap-py.
License
MIT
