fix(arborist): expose store node_modules via NODE_PATH for linked-strategy install scripts (#9551)
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`, a package whose install script invokes
a separate helper bin to locate a native binding fails (e.g.
`unrs-resolver` via `napi-postinstall`, and the wider `napi-postinstall`
family). The helper bin is reached via a `.bin` symlink, so Node
resolves it to its own realpath in its own store dir, from which it
can't `require.resolve` the host's binding — so it declares it missing
and re-downloads (fatal on a restricted registry/proxy). Hoisted works
because the binding is hoisted onto the helper's resolution path.
## How
When running a store node's lifecycle script, set `NODE_PATH` to the
host package's store `node_modules` so a script-invoked bin can resolve
the host's sibling deps:
```js
if (isInStore) {
const storeNodeModules = resolve(path, ...name.split('/').map(() => '..'))
env.NODE_PATH = [storeNodeModules, process.env.NODE_PATH].filter(Boolean).join(delimiter)
}
```
`NODE_PATH` is fallback-only (can't shadow working resolution), added
only to the script's env (never `process.env`), and scoped to the
store-local `node_modules` — install-time only, runtime isolation
untouched.
## References
Fixes #9549 M
Manzoor Wani committed
afce42408b4643d01ce0c9303effe8f94cc02d64
Parent: 8bbd70d
Committed by GitHub <noreply@github.com>
on 6/18/2026, 9:16:18 PM