I had some confusions about package.json
and package-lock.json
and what is used when but now that I have been “enlightened” I’ll record my new knowledge in this article.
package.json and package-lock.json
package.json
lists, among other things, the dependencies you need for your JavaScript project (if you use npm). You’d edit this file manually. In contrast package-lock.json
is generated by npm.
Example package.json
:
{ "name": "my-project", "version": "1.0.0", "dependencies": { "express": "^4.17.1", "lodash": "^4.17.20" }, "devDependencies": { "nodemon": "^2.0.7" } }
Often, the versions listed in package.json
are given as ranges. npm uses SemVer, meaning a version scheme in three parts like a.b.c where a,b and c are numbers (also called “major.minor.patch”). “~a.b.c
” means a and b are fixed and the last part can be c or any greater number: “~4.17.1
” means “4.17.x for x>=1”. “^a.b.c” means a is fixed and minor and patch version is variable: “^4.17.20
” means “4.x.y for either (x=17 and y>=20) or x>18”.
In contrast, package-lock.json
contains the exact versions of the project’s dependencies (and their transitive dependencies and so on). When package-lock.json
is generated or updated, the version range in package.json
is resolved to the latest “allowed” version.
Generating and updating package-lock.json
How do you create the lock file? “npm install
” will do it.
How do you update the lock file? “npm update
” will do it, usually. Say package.json
states module A in version “^3.4.5
” and the existing state module A in version “3.4.20
“. Then you run “npm update A
” or “npm update
” and when there is a version “3.5.2
” of A out there, npm will update the lockfile to version “3.5.2
” of module A.
If package.json
and the lock file are out of sync (the version in package-lock.json
is out of the range specified in package.json
), “npm install
” will correct the package-lock.json
file.
Why committing the lock-file?
The general advice is to commit package-lock.json
to your repository. That way, every developer will be using the same versions: the ones listed in the lock-file (using “npm install
“).
How to upgrade dependencies?
“npm outdated
” shows outdated dependencies and “npm update <pkg> --save
” updates a package. Commit both files.
Another way is to use tools like dependabot or renovate which check for new versions. If a new version of a module is detected, these tools will create a branch using the new version. CI pipelines are run and pull/merge requests are created or even merged automatically.
CI pipelines
There is a special command for CI pipelines: “npm ci
“. It will fail if the lock file is missing or is out of sync with package.json. So the build will fail if “npm install
” would change the lock-file.
“npm ci
” ensures that your build is always based on a consistent set of dependencies, which is important for reproducibility and stability. It also helps avoid problems that can arise from using different versions of the same package across different stages of the pipeline.
Pinning dependencies
Pinning a dependency means using an exact version in package.json
.