node_modules Structure & Hoisting
Understand why npm flattens dependencies, the danger of phantom dependencies, and how pnpm fixes it with symlinks.
The problem with nested dependencies
In early versions of npm, node_modules was strictly nested. If your app installed express, and express needed accepts, npm placed accepts inside node_modules/express/node_modules/accepts.
This created massive duplication (downloading the same package 50 times) and caused Windows to frequently crash because the file paths exceeded the 260-character limit.
The Flattening (Hoisting) Solution
To fix this, npm 3 introduced **hoisting**. When you install a dependency, npm tries to 'flatten' the tree by lifting all sub-dependencies up to the root node_modules folder.
This solves the duplication and path length issues, but creates a dangerous new problem: **Phantom Dependencies**.
Phantom Dependencies
Because sub-dependencies like lodash are hoisted to the root node_modules, your code can accidentally import 'lodash' even though you never explicitly listed it in your package.json.
If the top-level package later drops its dependency on lodash, it gets removed from the root, and your app suddenly crashes in production with a Module not found error.
Remember this
- npm flattens
node_modulesto save space and avoid long file paths. - Hoisting creates 'Phantom Dependencies', allowing you to import packages you didn't explicitly install.
- pnpm uses symlinks to enforce strict dependency boundaries while remaining flat on disk.
Done with this concept?
Mark it complete to track your progress. No login needed.