I was having the same confusion with my own repo.
So I'm writing this my own sake as much as yours, in the hopes I remember it for next time I come looking for a solution.
The update on this npm issue clarified everything for me:
- If you have a
package.json
and you run npm i
we generate a
package-lock.json
from it.
- If you run
npm i
against that package.json
and package-lock.json
, the latter will never be updated, even if the package.json
would be
happy with newer versions.
- If you manually edit your
package.json
to have different ranges and
run npm i
and those ranges aren't compatible with your package- lock.json
then the latter will be updated with version that are
compatible with your package.json
. Further runs of npm i
will be as
with 2 above.
And here is an explanation on what was happening with your case:
I have a project where I removed some no-longer needed libraries from
my package.json file, removed node_modules, ran npm install, and
everything continued to work as expected.
In this case it was doing step 2 from above. You have a working package-lock.json
in place with all the correct dependency versions. So it reinstalled based on package-lock.json
. It did not edit package-lock.json
even if your package.json
says it would be happy with newer versions.
Unfortunately if I follow the same steps above after having deleted
package-lock.json, my build suddenly breaks. A new package-lock.json
is generated but I now get hundreds of deprecation warnings from some
dependencies and even though the project continues to build those same
dependencies will break in unexpected places.
In this case it is doing step 1 from above. No package-lock.json
was found, so it recreated your project from package.json
, updating to newer versions as your package.json
would allow. It then generated a new package-lock.json
based on that install. Unfortunately for you, those updated versions broke your project.
If I revert to the previous package-lock.json and again run the same
steps (remove node_modules, run npm install), the warnings once again
disappear and the build is once more in a safe state.
This is essentially doing the same thing as before. It is doing step 2 from above. Since you once again have a working package-lock.json
with all the correct package versions it will reinstall based on package-lock.json
. It does not edit package-lock.json
even if your package.json
says it would be happy with newer versions. This is why it worked again, because none of the packages were upgraded to the problematic version.
My understanding is that package-lock.json will update every time we
run npm install with the most recent package versions, respecting the
caret^ and tilde~ restrictions. Does this update differ if we have an
existing package-lock.json vs a fresh install?
Yes this will differ as described in the steps above. If you have an existing package-lock.json
it will respect and install those exact package versions. If you're doing a fresh install it will update to newer (and potentially breaking) versions as are allowed in package.json
.
This is why package-lock.json
should be committed to version control and never deleted. The npm docs also recommend that.
There might be times where you are tempted to delete package-lock.json
to solve conflicts. That is a bad idea, as explained in detail in this question. Instead, you can manually make changes to package.json
and then run npm install
. That is like doing step 3 above. Alternatively you can do npm update {dependency}
or npm install {dependency}@2.1.3
which has the same effect.