file: directory deps: npm symlinks, pnpm hardlinks (rename-saves desync)
With a local dependency "lib": "file:../lib", npm 10.9 creates node_modules/lib as a symlink to the source directory, so every change is visible at once. pnpm 10.34 symlinks node_modules/lib into .pnpm/lib@file+..+lib/node_modules/lib, which is a real directory whose files are hardlinks to the source files (same inode). In-place writes therefore propagate, but an editor or tool that saves by writing a temp file and renaming it breaks the link, and the pnpm install silently keeps the old content until you reinstall. Also, npm 10 treats an unmet peer as a hard ERESOLVE error (install aborts unless you pass --legacy-peer-deps or --force), while pnpm only prints a WARN. For reproducible tests, depend on npm pack tarballs instead.
Reproduced with pnpm 10.34.9 / Node 22.23.4: node_modules/lib -> .pnpm/lib@file+..+lib/node_modules/lib, and index.js there shares an inode with ../lib/index.js (link count 2). An in-place write (echo > file) shows up immediately (require gives v2). After a write-temp-then-mv save the inodes differ, each link count is 1, and require still returns v2. A plain re-run of 'pnpm install' (no --force) fixed it in 394ms and returned v3, so 'reinstall' only means running install again, not a clean install.
Follow-up on the peer sentence (npm 10.9.9). The scope matters: for a file: directory dep that npm symlinks, npm does not check that dep's peers at all. lib with peerDependencies is-number ^6, next to is-number 7.0.0 in the app, installed with rc=0 and 'added 2 packages', no error. The same tree with 'npm install --install-links' (copies instead of symlinking) fails with 'ERESOLVE unable to resolve dependency tree', rc=1, as does a registry conflict (react 17.0.2 + react-dom 18.2.0). pnpm 10.34.9 prints 'WARN Issues with peer dependencies found ... unmet peer is-number@^6.0.0: found 7.0.0' for the file: case, rc=0. So with linked local deps, npm is actually the more lenient of the two.