SIGN IN SIGN UP

fix(arborist): load transitive optional deps into linked actual tree (#9654)

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`, an installed transitive optional
dependency was missing from the actual tree built by `loadActual` when
scanning the filesystem (`forceActual: true`), the path `npm sbom` and
`npm query` use. On disk the dep is correct — extracted in `.store` and
symlinked as a store sibling of its consumer — but `npm sbom` omitted it
(e.g. `chokidar` → `fsevents`: 14 components vs the hoisted strategy's
15; `esbuild` → `@esbuild/darwin-arm64` likewise).

In `#findMissingEdges()`, the skip condition treated an edge as already
resolved when `!edge.missing`. An unresolved optional edge has no target
yet reports `missing === false` (`Edge.error` returns `null` for an
optional edge with no target), so it was skipped and the on-disk store
sibling was never walked or loaded.

This changes the check to walk any edge whose target is unresolved,
including optional ones. The walk only loads a package that actually
exists in an ancestor `node_modules`, so genuinely-uninstalled optionals
(impossible platform) stay absent, and behavior is unchanged for
required, missing, dummy, and hoisted-ancestor edges. The linked SBOM
now matches the hoisted strategy.

## References

Fixes #9627
M
Manzoor Wani committed
6a5bf269408b1d2822e5c4d5c126e2aff850c3c2
Parent: 803ba70
Committed by GitHub <noreply@github.com> on 6/25/2026, 5:38:29 PM