Files
pnpm/.changeset
Zoltan Kochan 2f2bdaf37f fix(fs.hard-link-dir): fill the destination in place (#14242)
Copying a built package to its other hoisted locations staged a fresh copy
of the source in a temp directory and renamed it over the destination. The
destination directory that went away took its own node_modules with it —
under the hoisted node linker that is where the dependencies that could not
be hoisted any higher live, so those were silently deleted from every
location the copy landed on.

The copies of one build chunk run concurrently, so a sibling package could
also be staging its own copy inside that node_modules at the time. Its
staging directory was carried off with the replaced directory and its
commit then failed with ERR_PNPM_ENOENT, aborting the install:

  ENOENT: no such file or directory,
    rename '<dest>/node_modules/_tmp_<pid>_<hash>' -> '<dest>/node_modules/<pkg>'

Link the source into the destination entry by entry instead. A file the
destination already has is replaced through a temp sibling and a rename, so
a reader sees either the whole old file or the whole new one; a destination
entry whose kind does not match the source's is removed first, which also
keeps a symlinked directory from redirecting the writes out of the
destination; and a file that is already the source file is skipped. Nothing
removes the destination directory any more, so its node_modules and everyone
working inside it are left alone.

The rename retry that leaves the destination in place was private to
`@pnpm/fs.indexed-pkg-importer`, which needed it for the same reason. It now
lives in `@pnpm/fs.graceful-fs` and both call sites use it.

Closes pnpm/pnpm#12880
2026-08-27 21:37:29 +02:00
..