It’s c and c/v2, not c@1.0.0, c@1.0.5, c@1.0.10, c@1.1.3, c@2.0.0, c@2.3.1, ... Each necessary because packages in the middle have decided to pin versions or add upper bounds to work around bugs. That’s a huge difference.
FWIW, I've just pulled up a pretty large project I work on using NPM, and almost all of the duplicate dependencies had different major versions. Most of the ones that had the same major version were 0.x dependencies with different minor versions.
So I'm still not convinced that Go's approach is materially different here - certainly in terms of the practical output, NPM does a good job of ensuring that the fewest number of different versions will get installed for each dependency.
You forgot the part where npm people release new major versions for very little reason all the time, because there’s nothing stopping them. Go authors on the other hand are generally really reluctant to change to a new path. Go to a relatively large go codebase and count the v2s. Then do v3.
Coming back to a midsized JavaScript codebase after a few months and trying to upgrade to new major versions of things have always been a shitshow.
So I'm still not convinced that Go's approach is materially different here - certainly in terms of the practical output, NPM does a good job of ensuring that the fewest number of different versions will get installed for each dependency.