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