3

I am learning about the differences between package.json and package-lock.json

I been experimenting on a package with only one dependency called chance

I first installed it via npm i chance@1.0.0 and the package.json has "chance": "^1.0.0" and package-lock.json has "version": "1.0.0".

Because I wanted to see the effect that the lock file has on the version, I went ahead and deleted package-lock.json and node_modules, I ran npm install, the version of chance stays the same in package.json, which is "chance": "^1.0.0". In the newly created lock file, the version of chance became "chance": {"version": "1.1.8",, so it updated itself.

I then deleted package-lock.json and node_modules again and ran npm update, the results seemed to be the same with the previous experiment – in package.json I have "^1.0.0" in package.json and "1.1.8" in package-lock.json

My questions are:

  1. in either case, with "^1.0.0" in package.json and "1.1.8" in package-lock.json, which version of the dependency am I actually using in my project, I guess it is 1.1.8 right? so by merely looking at the versions in package.json is not enough to determine the exact version of the dependencies used in a project?
  2. When does running npm install change the lock file? I know that if we delete the lock file, it will generate a new one with the newest versions in the allowable ranges from package.json. But are there any cases where npm install would change the lock file even if I didn't delete the lock file?
Joji
  • 4,703
  • 7
  • 41
  • 86
  • I would expect npm's behavior is that when you run `npm i chance@1.0.0`, your package.json is changed to have the exact version without `^`: `"chance": "1.0.0"`. Reality is different.. which results in that a consequent `npm i` will potentially change `package-lock` with any higher minor/patch version of that package. Even though you just asked to install the exact version of your dep package. – hastrb Mar 05 '23 at 14:58

1 Answers1

2

So, the answer is a bit complex. Essentially there are 2 things at play: The version of the package you want/need, and the version of the package that is installed.

When you are building a project, you probably don't care what specific version of a given dependency is. Most of the time you want the latest one, or the latest patch near a specific major version. The package.json is supposed to document what you, the developer, believe is required for your project to work. So, if you put in the package json "chance": "1.0.0", it would mean that only version 1.0.0 exactly is acceptable, and any other version is unacceptable. If you put "chance": "^1.0.0", it means any version compatible with 1.0.0 is acceptable. So 1.2 or 1.3 might also be fine, but 1.4 might introduce a change that breaks compatibility.

Once you decide what packages you want, by writing the package json, you run npm install. npm install can't always install exactly the versions you want. For example, imagine you want to install two packages: React v1.13 and momentJS v2.8. So you add these to your package json like this:

(Note: these version numbers and dependancies are not based on real React or Moment version numbers)

"momentJS" : "2.8",
"react" : "1.13"

then you run npm install. And you get an error: Package dependencies cannot be resolved. (or something like that). The problem is that React version 1.13 requires momentJS 2.9, but your package json specifies that you want version 2.8 exactly. You can't have both, so npm isn't able to resolve the conflict. A fix would be:

"momentJS" : "^2.8",
"react" : "1.13"

Now you are saying that you need a version of moment compatible with 2.8, and you are okay with npm adjusting that to satisfy other packages. Run npm install again and npm might install version 2.9, which satisfies both your requirement of "compatible with 1.8" and React, which wants 2.9. Now, the web app I'm currently working on has over 1,000 dependancies total, so npm absolutely needs to be able to adjust version numbers in order to get all of those packages to play nice.

Now there is often more than one way to solve a dependancy graph--more than one way to adjust all the version numbers to make every package happy. Your package lock file records what the current solution is and what actual packages are installed.

All the options for specifying package verions are here

Hope that helps!

Also: the second part of your question was "will npm change the lock file without me deleting it?" And the answer is: basically everytime you run npm install, npm changes the lock file. What npm does try to do is change the lock file as little as possible with each new install and keep most packages the same

Sam Spade
  • 1,107
  • 7
  • 20
  • Thanks so much for the thorough answer! I guess for the second question what I was really trying to ask is, what are the cases when merely running npm install would change the lock file? I remember I ran into some weird situations where the lock file changed despite the fact that I didn't touch the package.json file... – Joji Apr 28 '22 at 04:18
  • @Joji First, if I answered your question, I'd appreciate it if you could accept the answer. If you run npm install without changing the package json, the package lock shouldn't change, but you shouldn't rely on it not changing. Logic surrounding how precisely the package lock changes is not trivial. – Sam Spade Apr 28 '22 at 05:11