Metadata-Version: 2.4
Name: depsnap
Version: 0.1.0
Summary: Find unused dependencies — conservative, zero-config, zero dependencies. Flags only packages referenced nowhere, so every result is safe to remove.
Author: yyfjj
License: MIT
Project-URL: Homepage, https://github.com/jjdoor/depsnap-py
Project-URL: Repository, https://github.com/jjdoor/depsnap-py
Project-URL: Issues, https://github.com/jjdoor/depsnap-py/issues
Keywords: dependencies,unused,unused-dependencies,depcheck,package-json,cleanup,cli,ci
Classifier: Development Status :: 4 - Beta
Classifier: Environment :: Console
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Topic :: Software Development :: Quality Assurance
Classifier: Topic :: Utilities
Requires-Python: >=3.8
Description-Content-Type: text/markdown
License-File: LICENSE
Dynamic: license-file

# 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**.

```bash
pip install depsnap
depsnap
```

```
depsnap — 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](https://github.com/depcheck/depcheck)
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](https://knip.dev/). depsnap is the fast first pass.

## Usage

```bash
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 output
```

Try it on the bundled example after cloning:

```bash
python -m depsnap examples/demo        # → flags left-pad
python -m depsnap examples/demo --dev    # → also flags unused-dev-tool
```

## How it works

1. Reads `package.json` for your declared dependencies.
2. 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 **never** `package.json` or a lockfile, since those list every
   dependency by name and would mask everything.
3. For each dependency, checks whether its name (with import-aware word
   boundaries, so `lodash` ≠ `lodash-es`) appears anywhere in that corpus, in the
   npm `scripts`, or as one of its installed `node_modules` bin names.
4. 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:

```yaml
- 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, --help
```

## Scope

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 Node

```bash
npx depsnap
```

Same checks, same flags, same exit codes — [depsnap on npm](https://github.com/jjdoor/depsnap).

> Note: depsnap reads `package.json` and your source, so it works on any JS/TS
> project regardless of which language you install it from.

## License

MIT
