Skip to content

fix(arborist): repair wrong-but-existing symlink target in linked strategy#9644

Merged
owlstronaut merged 1 commit into
release/v11from
backport/v11/9628
Jun 24, 2026
Merged

fix(arborist): repair wrong-but-existing symlink target in linked strategy#9644
owlstronaut merged 1 commit into
release/v11from
backport/v11/9628

Conversation

@github-actions

Copy link
Copy Markdown
Contributor

Backport of #9628 to release/v11.

…ategy (#9628)

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

Under `install-strategy=linked`, if a top-level `node_modules/<dep>`
symlink points to a store key that exists on disk but is the **wrong
version**, re-running `npm install` does not repair it. npm reports
success and leaves the dependency resolving to the wrong version. This
is the state an interrupted update leaves behind: the new store key is
extracted but the symlink has not yet been repointed.

A symlink pointing at a **non-existent** target is already repaired on
reinstall; only a wrong-but-existing target slips through, because
cleanup validates the link name, not its target.

## Why

For linked installs, `#buildLinkedActualForDiff` synthesizes the
"actual" tree the diff compares against from the **ideal** children,
never reading the real on-disk symlink target. So a link whose on-disk
target is a valid-but-wrong store key looks identical to the ideal node,
the diff reports no change, and the symlink is left untouched. The
hoisted strategy is unaffected because it self-heals the analogous
corruption.

## How

In `#buildLinkedActualForDiff`, when an existing link's resolved on-disk
target differs from its ideal target, skip creating a synthetic actual
entry for it. With no actual entry to match, the diff treats the link as
an `ADD`, and `#reifyNode` removes the old symlink and recreates it
pointing at the correct store key. A new `#linkTargetMismatch` helper
compares the two resolved targets; it runs only after the existing
`existsSync` guards, so both paths are known to exist.

This repairs both the top-level symlink and wrong transitive/sibling
links inside the store, and leaves already-correct trees untouched (no
spurious relinking on an idempotent reinstall).

## References

Fixes #9611

(cherry picked from commit 0ffce98)
@owlstronaut owlstronaut merged commit 4e40b1c into release/v11 Jun 24, 2026
33 checks passed
@owlstronaut owlstronaut deleted the backport/v11/9628 branch June 24, 2026 18:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants