1- I want my dependencies to define a range for the libraries that they work with, so we don't have 20 different versions of some common library because our dependencies are hyper specific
2- I want every install of my project- be it on a dev machine or deploy machine- to have the exact same versions of dependencies, including transitive ones. I don't want to deal with bugs caused by surprise version changes
3- if I upgrade a dependency or remove it, I want the transitive dependencies managed automatically. I don't want orphaned transitive dependencies. In fact, I don't even want to think about them at all other than know that they work and aren't adding bloat or security risks.
You need a package manager with more than a passing thought for handling lock files. For the longest time, npm wasn't it. I'd argue that it still isn't, because "npm install" should NOT be the command used for both "set up a project for the first time" and "add a new package". In the first case, I want a reproducible, deterministic result of a known state. In the second case, I want to modify the dependency graph, producing a new state.