They also strategically avoid mentioning the payoff which is that your project is entirely self-contained. `node_modules` is at once your complete development kit, build tool, compiler, and collection of runtime dependencies.
Once you check out a node project with a package.json you can reasonably assume you can install it and run it anywhere. A docker image for a node project should just be "install node && npm build". You're side-stepping the entire dev ops nightmare. Find me another language with JavaScript's popularity and that level of simplicity. I'd go as far to argue that this is what happens to any massively popular language that has a functional package manager from day one.
Find me a mainstream language that's more complicated than this? "Checkout and build" works pretty much everywhere? I mean, you need the right version of mvn or pip or whatnot... just like you need the right version of npm.
If anything, node/npm deserves special mention for NOT having reliable repeatable builds. Part of this is that a lot of packages rely on native code. But most of it is that the ecosystem culture defaults to "always grab the latest versions of everything".
And npm only got package-lock.json a few years ago, not "from day one". Prior to package-lock.json, builds were wildly unpredictable - like, expect breaking changes week to week even if you don't change anything at all in your code.
If you want an "entirely self contained" payoff, languages that produce static binaries are pretty hard to beat. Node is not that.
Worse than that: lock files aren’t lock files the way that they are in every other language package manager that has lock files. In Cargo, Bundler, and Mix, you specify a pessimistic version (~> 2.1) and you may get 2.1.1 or 2.3.0. But that version is _always_ the same for every developer because the lock file locks the version and you explicitly upgrade after that.
I recently had a case where a developer joined us on a project and he got a different version of a package than I did because the lockfile didn’t constraint the dependencies and sub-dependencies and everything else. (For that you have to pass an explicit parameter like `--ci` or `--frozen-lockfile` depending on which of three different package managers you use.)
To put it simply to build a modern Java project you need the jdk + bazel + docker + maven + runtime classpath for a sub-set of npm's features (as far as I know you still can't include two versions of the same maven dependency).
For a Node project you just need node. npm is part of node, and yarn is optional. The repeatable builds was a real issue but that's been solved years ago.
That's not true for Java. Most require Maven and then it's mvn install (you need the JDK for maven, but that feels similar to saying you need node for npm, they effectively come together).
Npm's dev dependencies are analogous to Maven's plugins.
Bazel is an alternative to maven. Docker fulfills the same role as in the Node ecosystem. The runtime class path is an implementation detail that you need to concern yourself with if you wish to run your Java code without Maven (analogous to running your JavaScript code without npm).
The two forms of the same dependency is a limitation that stems from Java the language rather than its ecosystem.
FWIW, nearly every language ecosystem I know of is at the point of "download build tool, run build tool."
Heck, in javaland the paractice now is to put the build tool in the repo, as you will only update maven once in every decade git won't complain too much about the binary, or you can just update the version in the wrapper and put the binary on the git ignore. First time you run it, it will update the build tool.
Nowadays on javaland you should only need to have JDK and git/svn/hg/(wtv version control you use).
And with the JDK, you can always download the most recent that java is backwards compatible.
Once you check out a node project with a package.json you can reasonably assume you can install it and run it anywhere. A docker image for a node project should just be "install node && npm build". You're side-stepping the entire dev ops nightmare. Find me another language with JavaScript's popularity and that level of simplicity. I'd go as far to argue that this is what happens to any massively popular language that has a functional package manager from day one.