pnpm why package: find out why a dependency is installed
- tool
- pnpm
- versions
- pnpm 8+, pnpm 9
- os
- any
pnpm why is-number shows every path in the dependency graph that leads to is-number: which direct dependency of your project pulls it in and through which intermediate packages, with the installed versions. Use it to find where an unexpected or vulnerable package comes from before you update or override it. It reads the installed graph, so run pnpm install first.
Example
echo '{ "name": "app", "version": "1.0.0", "private": true }' > package.json
P="npx --yes pnpm@9"
$P add --silent is-odd@3.0.1 is-even@1.0.0
$P why is-numberOutput
Legend: production dependency, optional only, dev only app@1.0.0 /tmp/work (PRIVATE) dependencies: is-even 1.0.0 └─┬ is-odd 0.1.2 └── is-number 3.0.0 is-odd 3.0.1 └── is-number 6.0.0
is-number is not a direct dependency: it is installed twice, in two versions, because is-odd@3.0.1 asks for one version and is-even (through is-odd@0.1.2) asks for another. pnpm prints the tree from your direct dependencies down to the package, grouped by dependency type (dependencies, devDependencies). The example runs pnpm through npx pnpm@9; with pnpm installed the command is pnpm why is-number.
Options
| Command | What it does |
|---|---|
| pnpm why lodash | Paths to lodash in the current project |
| pnpm why -r lodash | Every package of the workspace |
| pnpm why 'babel-*' | Every package matching a pattern |
| pnpm why lodash --prod / --dev | Only production or only dev dependencies |
| pnpm why lodash --depth 1 | Limit how deep the tree is printed |
| pnpm why lodash --json | Machine-readable output |
| pnpm list --depth Infinity | The whole installed tree instead of the paths to one package |
What to do with the answer
- Two versions of one package, as above, are normal when ranges do not overlap. To force one version everywhere, add it to
pnpm.overridesinpackage.jsonand runpnpm install; test afterwards, because the package that asked for the other version may break. - A vulnerable transitive package: update the direct dependency at the top of the chain first (
pnpm update -L name, see pnpm update --latest); use an override only if no fixed version of the parent exists. - A package you did not expect at all usually comes from an optional or peer dependency of something else; the chain shows which one.
Pitfalls
pnpm whyreadsnode_modulesand the lockfile of the current project. In a workspace, a package installed for another package is only shown with-ror from that package's folder.- Peer dependencies are listed under the package that resolves them, so the same version can appear in several chains.
- Other package managers have the same command with different output: npm explain and yarn why.
How to confirm it worked
After an override or an update, run pnpm why package again: the chains should show only the version you want. pnpm list package --depth Infinity gives the same information as a flat tree.
FAQ
How do I see why a package is installed in every workspace package?
Run pnpm why -r package from the workspace root. Without -r, pnpm why only looks at the package in the current folder.
Why does pnpm why print nothing?
The package is not in the dependency graph of the current project: it is not installed, it was removed, or it belongs to another workspace package. Check the name, run pnpm install, or add -r.
How do I get the result as JSON?
Add --json, for example pnpm why lodash --json. It prints the same chains as structured data that scripts can read.