Hoisting and Deduplication

Node.js 2,131 looks for a package in the node_modules beside the requiring file, then in each parent's. Hoisting puts packages at the top level, where one copy serves every range it satisfies; only a conflicting version is nested inside the package that needs it. Deduplication makes one copy satisfy as many ranges as possible.

Watching npm deduplicate during an installShell
cd ~/v5-ch1 && mkdir dedupe-demo && cd dedupe-demo && npm init -y >/dev/null
npm install ms@2.0.0 --no-audit --no-fund
npm pkg set dependencies.ms='^2.0.0'
npm install debug@4 --no-audit --no-fund
npm ls ms
Output
added 1 package in 698ms
added 1 package, and changed 1 package in 762ms
dedupe-demo@1.0.0 /home/dev/v5-ch1/dedupe-demo
├─┬ debug@4.4.3
│ └── ms@2.1.3 deduped
└── ms@2.1.3

debug 4.4.3 needs ms@^2.1.3, which the top-level 2.0.0 does not satisfy. Rather than nest a second copy, npm 2,036 noticed that your widened range ^2.0.0 also accepts 2.1.3 and upgraded the hoisted copy ("changed 1 package"), so one copy serves both. npm dedupe and npm find-dupes run the same search over an existing tree.

The search has a limit: npm never moves a copy that already holds the top-level slot. Run npm find-dupes in BookNest and it reports nothing to fix, yet three identical content-type 2.1.0 copies stay nested under body-parser, type-is and negotiator (npm Install). Hoisting 2.1.0 and nesting 1.0.5 inside express would store two copies instead of four, but npm places packages greedily, in the order it meets them, and express came first.