How to lock transitive dependency versions in NPM package-lock.json?

I’m trying to ensure consistent builds across different environments. I’ve already set save-exact=true in my .npmrc file which works great for direct dependencies, but I’m still having issues with transitive dependencies.

The problem is that when I run npm install, the nested dependencies listed in package-lock.json aren’t truly locked down. This means different team members might end up with slightly different versions of sub-packages, which can cause unexpected issues.

I’m aware that npm ci exists, but it’s not practical for regular development since it wipes out the entire node_modules folder and reinstalls everything from scratch, which takes way too long.

Is there any method to make NPM respect the exact versions specified in the lockfile for all dependencies, including the nested ones? I want npm install to be completely deterministic.

Had this same issue last year. The problem was npm’s shrinkwrap behavior mixing with version range resolution. Even with package-lock.json, npm install can grab newer patch versions when your dependencies use ranges like ^1.2.0 instead of exact versions. I fixed it with a pre-commit hook that checks if the lockfile changed unexpectedly. Also started using npm shrinkwrap on top of the lockfile for critical projects - shrinkwrap wins and locks versions tighter. Check if any dependencies have their own npm-shrinkwrap.json files too, since those can mess with your lockfile. Run npm ls regularly to catch these issues before they break production builds.

Your lockfile should already handle this when it’s working right. If team members are getting different versions, something’s wrong. First, check if everyone’s running the same NPM version. Different NPM versions can create slightly different lockfiles from the same package.json. Also watch out for these issues: someone runs npm install <package> without the lockfile around, or the lockfile gets corrupted somehow. Always commit your lockfile and make sure teammates aren’t accidentally updating it. Try adding package-lock-only=true to your npmrc during development. This forces NPM to only install what’s in the lockfile and throws errors instead of trying to “fix” discrepancies.

Easiest fix? Delete node_modules and package-lock every time you pull from git. Takes 30 seconds with decent internet. I set up a bash alias for rm -rf node_modules package-lock.json && npm install so I don’t have to type it out. Way more reliable than fighting npm’s inconsistencies across machines.

This happens because npm install does some dependency resolution even with a lockfile. Usually, it’s platform-specific or optional dependencies that resolve differently on different machines. Setting engine-strict=true in your npmrc enforces consistency. You can also use npm install --frozen-lockfile during development - it’s faster than npm ci since it doesn’t wipe node_modules first, but still enforces strict lockfile compliance. If that flag errors about lockfile mismatches, someone changed dependencies without updating the lockfile. For team consistency, add a postinstall script that validates your lockfile hasn’t drifted. Make npm fail fast when versions don’t match instead of letting it silently resolve different versions.

Been through this nightmare countless times. Manual fixes work but they’re tedious and fall apart when you’re juggling multiple environments.

I automated the whole thing instead. Built a workflow that watches package.json and package-lock.json files - when something changes, it validates versions across environments and keeps everything in sync.

It handles version checks, lockfile validation, and rollbacks when things break. Runs before deployments and catches version drift early. Beats manual checks or hoping everyone follows protocol.

Used Latenode since it plays nice with NPM registries and Git repos. The workflow triggers on file changes, validates lockfile integrity, and alerts the team about discrepancies. 10 minutes to set up, saves hours of debugging.

Check it out: https://latenode.com