Unfortunately with NPM this is still awkward, because as soon as you try to shrinkwrap your project, it doesn't just pin the version numbers, it also pins the full source location. That's in direct conflict with (and apparently takes precedence over) using one of the local caching proxies that would otherwise be a useful practical solution to this problem, and the situation gets even more complicated if you have people building at multiple locations and want each to have their own proxy.
Yarn seems to have a better approach to this. It has a few problems of its own, notably surprising, quiet updates to the lock file when using the default options (IMHO a mistake since the big selling point of Yarn is its deterministic, reproducible behaviour) but at least you can do something resembling a local cache combined with something resembling version pinning, which is the ante for any sane build system.
NPM has its own lockfiles now, similar to yarn.
> It has a few problems of its own, notably surprising, quiet updates to the lock file when using the default options
This I've never noticed. I'm genuinely curious how it would happen. The only way I imagine it might happen is if you do a fresh install with no cache and some of the packages have moved or changed on the registry. Do you happen to have more details?
Edit: Also thanks for the tip about npm's new locking mechanism. Apparently that arrived with NPM 5. I just checked, and we have developer machines here that were last updated well under a year ago and are still on NPM 3, and that had itself been installed along with something like the third new major version of Node in not much over a year. I don't know how anyone is supposed to do development intelligently while the most fundamental tools for things like dependency management are bumping a major version and radically changing how they do even the most basic and essential things literally every few months. :-(