Files
Zoltan Kochan 473cccecef fix(prune): remove extraneous direct deps and honor workspace selection (#17154)
Fix three `pnpm prune` issues across pnpm v11 and pnpm v12:

1. Extraneous direct dependencies in `node_modules`:
   - In pnpm v11, `prune.handler` did not disable `optimisticRepeatInstall`, and `installDeps` could short-circuit via `canReportAlreadyUpToDate` before `headlessInstall` ran `prune()`. In addition, `pruneRemovedDependencies` skipped `prune()` when `currentLockfile` was absent (for example, when a project had zero dependencies). Disable `optimisticRepeatInstall` in `prune.handler`, prevent `canReportAlreadyUpToDate` when `pruneDirectDependencies` or `pruneStore` is set, and pass an empty `currentLockfile` to `prune()` when `pruneDirectDependencies` is enabled and no current lockfile exists.
   - In pnpm v12 (`pacquet`), `prune_stale_modules` only unlinked direct dependencies that were present in `current_lockfile` and removed from `wanted_lockfile`, never scanning `node_modules` on disk for extraneous entries when `prune` ran. Add `PruneTreeOptions::direct_dependencies` and `PruneExtraneousDirectDeps` to scan each selected importer's `node_modules` directory during `pnpm prune`, remove entries (and their bin shims and emptied scope directories) that are not wanted direct dependencies, excluded manifest links, or root public hoists, and force `commit_modules_state`'s virtual-store sweep during `prune`.

2. `pnpm --filter <pkg> prune` and `pnpm -r prune` in pnpm v11:
   - Separate `cliOptionsTypes` from `rcOptionsTypes` in `@pnpm/installing.commands` `prune` and add `'recursive': Boolean` so the CLI parser accepts `-r` / `--recursive` and `--filter`.
   - Do not pass `include` to `validateModules` when `pruneDirectDependencies` is set so `prune --prod` does not purge every workspace project's `node_modules` directory.
   - Do not auto-include the workspace root in `shouldInstallWorkspaceRoot` during filtered `prune`.
   - Scope `pruneDirectDependencies` to the selected `ctx.projects` and keep all dependency groups in `findOrphanDepPaths` / `getPkgsDepPathsOwnedOnlyByImporters` during a partial workspace `prune` so unselected workspace importers do not lose their devDependencies from the shared virtual store.

3. Workspace selection and `sharedWorkspaceLockfile: false` in pnpm v12:
   - Add `CliCommand::Prune(_)` to `CliCommand::recursive_by_default`.
   - Route `PrunePipeline` through `select_install_family_plan` (`PerProject`, `Shared`, `Single`) and pass `WorkspaceInstallSelection` (with `install_dirs` restricted to `selected_dirs`) into `Install::run_prune`.

Closes https://github.com/pnpm/tasks/issues/171
2026-10-20 17:07:56 +02:00

510 B

id, title
id title
prune pnpm prune

Removes unnecessary packages.

Options

--prod

Remove the packages specified in devDependencies.

--no-optional

Remove the packages specified in optionalDependencies.

--recursive, -r

Run pnpm prune in every package found in subdirectories, or in every workspace package when executed inside a workspace.

--filter <package_selector>

Restrict pnpm prune to the selected workspace projects. Read more about filtering.