node_modules Layouts

Flat, Nested and Symlinked node_modules

Three folder layouts can make one graph visible to Node.js 2,131 (5). npm 1 2,036 and 2 used the nested layout, where every package gets its own private node_modules; npm 3 (2015) switched to the flat, hoisted layout. pnpm 69,400 made the symlinked layout popular: a hidden store holds one folder per version, and symlinks re-create each package's declared dependencies and nothing else.

The same three packages in hoisted, nested and symlinked layouts
The same three packages in hoisted, nested and symlinked layouts

npm can still produce all three through its install-strategy setting (hoisted, nested, shallow and linked), so you can compare them on BookNest without changing tools.

Installing BookNest with three npm install strategiesShell
cd ~/v5-ch1 && mkdir layouts
for s in hoisted nested linked; do
  mkdir layouts/$s && cp booknest/package.json layouts/$s/ && cd layouts/$s
  npm install --install-strategy=$s --no-audit --no-fund >/dev/null 2>&1
  copies=$(find node_modules -type f -regextype egrep \
    -regex '.*node_modules/(@[^/]+/)?[^/]+/package.json' | wc -l)
  echo "$s: $copies copies, $(ls node_modules | wc -l) top-level, $(du -sh node_modules | cut -f1)"
  cd ~/v5-ch1
done
Output
hoisted: 82 copies, 79 top-level, 5.3M
nested: 98 copies, 2 top-level, 6.7M
linked: 80 copies, 2 top-level, 6.3M

Nested stores 98 copies of 80 packages, up to seven node_modules levels deep, the paths that once broke Windows' 260-character limit. linked, npm's version of pnpm's layout, keeps packages in node_modules/.store and looks larger than hoisted only because its 154 extra folders each take a disk block (the files total 2.6 MB in both). npm 11.18.0 made linked stable, so install-strategy=linked in .npmrc catches phantom dependencies without pnpm, though only pnpm's store shares files between projects (Global Store and Hard Links).