SIGN IN SIGN UP

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