Having used both, Node's package system is horrific compared to PHP's Composer. Composer's built-in composer.lock system is something NPM sorely needs by default.
It exists (npm shrinkwrap [1]), but it's not enabled by default, and it has some warts, such as non-deterministic sorting of entries.
There's a third-party tool [2] that is supposed to solve many of the issues, but I haven't tried it.
Of course, if you do this, there's a decent chance that if you try installing it again in 6 months, your shrinkwrapped version of at least one of the dependencies won't be available any more...
Biggest wart I've encountered is I've got a package that installs fsevents (OSX-only) as one of its optional dependencies. When I shrinkwrap and attempt to install on a Linux server, that optional dependency is treated as a required one, and it can't install. I have to manually zap it from the JSON. :-/
One wonders, why shouldn't it be 'npm lock'... I'm not that into plastic packaging, and not a native speaker, so shrinkwrap gives me as much clue about what the command does as blinktap, or pricklywarp.
Having used both, Node's package system is horrific compared to PHP's Composer. Composer's built-in composer.lock system is something NPM sorely needs by default.