The way I see it, the contract between node and npm (and other package managers) is the resolution algorithm. Whereby the code may contain require statements like require('foo'). Then npm allows you to say, I depend on foo at version ^1.2.3. The guarantee npm provides then, is that a copy of foo compatible with the ^1.2.3 range will be found in some location where the resolution algorithm will find it. That location may differ between versions of npm, yarn, and will definitely be something totally out of this world under pnpm 😀
I believe all but the earliest versions of npm have tried to flatten the tree as much as possible, so deep dependencies are hoisted to the top as much as possible. However I never recommend relying on that because of the above and because it leads to other bad practices like failing to specify your dependencies in your package.json.
Generally yes, however it's not guaranteed.
The way I see it, the contract between node and npm (and other package managers) is the resolution algorithm. Whereby the code may contain require statements like
require('foo')
. Then npm allows you to say, I depend onfoo
at version^1.2.3
. The guarantee npm provides then, is that a copy offoo
compatible with the^1.2.3
range will be found in some location where the resolution algorithm will find it. That location may differ between versions of npm, yarn, and will definitely be something totally out of this world under pnpm 😀I believe all but the earliest versions of npm have tried to flatten the tree as much as possible, so deep dependencies are hoisted to the top as much as possible. However I never recommend relying on that because of the above and because it leads to other bad practices like failing to specify your dependencies in your package.json.
This is a great overview! Related talk: youtube.com/watch?v=4lUzKhq3C-M