SIGN IN SIGN UP

fix(arborist): remove stale .bin shims after uninstall under linked (#9632)

In continuation of our exploration of using `install-strategy=linked` in
the [Gutenberg
monorepo](https://github.com/WordPress/gutenberg/pull/75814), which
powers the WordPress Block Editor.

Under `install-strategy=linked`, `npm uninstall <pkg>` removed the
package's top-level symlink and its `.store` entry but left its shim in
`node_modules/.bin` behind as a dangling link. The leftover shim can
break tools that enumerate `node_modules/.bin`, shadow a later-installed
binary of the same name, and is not healed by a subsequent `npm
install`.

## Why

A linked reify builds the actual tree for the diff from the ideal tree
(`#buildLinkedActualForDiff`), so a removed dependency is never compared
against what is on disk and the diff emits no action to drop its bin
shim. The top-level symlink and store entry are already cleaned by the
post-reify sweep, but bin shims were not covered by it.

## How

`reify.js` `#cleanOrphanedStoreEntries` now also builds `binsByDir`:
while collecting the valid top-level links per `node_modules` directory,
it records the bin names each still-linked package provides from
`child.package.bin`. The new `#cleanStaleBinLinks` then removes any
`.bin` entry whose base name (after stripping a `.cmd`/`.ps1` suffix) is
not provided by a surviving package, or which is a dangling symlink.
Matching by name keeps the check cross-platform across POSIX symlink
shims and Windows `.cmd`/`.ps1` shim files, and reuses data already in
the ideal tree without adding a dependency. Shims for packages that
survive the uninstall are preserved.

## References

Fixes #9613
M
Manzoor Wani committed
981e2498589c83859b3c9e8b92a2cc67562dc06b
Parent: 6968015
Committed by GitHub <noreply@github.com> on 6/24/2026, 6:17:22 PM