Package.json Vs Package-lock.json Vs Npm-shrinkwrap.json
Table of Contents:
- Package.json
- Difference between tilde (~) and caret (^) in package.json
- Package-lock.json
- Why is package-lock.json created?
- What is the purpose or use of package-lock.json?
- Why should we commit package-lock.json with our project source code?
- Npm-shrinkwrap.json
- convert an existing “ package-lock.json“ to an “npm-shrinkwrap.json “ .
Package.json
I am sure if you are using npm (Node Package Manager) then you already know about the versioning file i.e. package.json.
- It is used to install different open source and other available packages (that is, pre-packaged code modules) in a Node.js project. So I am going to explain the use of it in depth.
- It is used for more than dependencies – like defining project properties, description, author & license information, scripts, etc.
- It records the minimum version you app needs. If you update the versions of a particular package, the change is not going to be reflected here.
Example:
{
"name": "generator-venv",
"version": "2.0.1",
"description": "a generator to create vue environment ",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [
"generator",
"vue",
"router",
"vuex",
"components",
"yeoman-generator",
"typescript",
"webpack",
"babel"
],
"author": "Hossam Hilal",
"website": "https://github.com/hossamhilal/",
"repository": {
"type": "git",
"url": "git+https://github.com/hossamhilal/generator-venv"
},
"license": "ISC",
"dependencies": {
"chalk": "^3.0.0",
"grunt": "^1.0.4",
"lodash": "^4.17.15",
"mkdirp": "^1.0.3",
"npm": "^6.13.7",
"yeoman-generator": "^4.5.0",
"yosay": "^2.0.2"
},
"devDependencies": {},
"files": [
"app"
]
}
So as you can see in the picture above after every dependency listed under package.json there’s a number something like ^3.0.0
which is the version of that package but before the version, there is ^
. So ^ this little guy can be a total destroyer for your project.
^ sign before the version tells npm that if someone clones the project and runs npm install in the directory then install the latest minor version of the package in his node_modules.
So lets say I am having express with ^3.0.0
in package.json and then express team releases version 3.5.2
and now when someone clone my repo and runs npm install
in that directory they will get the version 3.5.2 (You can also put ~
instead of ^
it will update to latest patch version)
Difference between tilde (~) and caret (^) in package.json
If you have updated your npm to the latest version and tried installing the package using npm install moment — save you will see the package moment.js get saved in packages.json with a caret(^)prefix and in the previous version it was saved with tilde(~)prefix. You might think what is the difference between these symbols.
The tilde prefix simply indicates that the tilde (~) symbol will match the most recent patch version or the most recent minor version i.e. the middle number. For example, ~1.2.3 will match all 1.2.x versions but it will not match 1.3.0 or 1.3.x versions.
The caret indicates the first number i.e. the most recent major version. An example is in 1.x.x release the caret will update you and it will match with 1.3.0 but not 2.0.0.
However, this can be a huge issue if package developers break any of the functions on the minor version as it can make your application break down.
So npm later released a new file called package-lock.json to avoid such scenarios
If you have updated your npm to the latest version and tried installing the package using npm install moment — save you will see the package moment.js get saved in packages.json with a caret(^)prefix and in the previous version it was saved with tilde(~)prefix.
You might think what is the difference between these symbols.The tilde prefix simply indicates that the tilde (~) symbol will match the most recent patch version or the most recent minor version i.e. the middle number. For example, ~1.2.3 will match all 1.2.x versions but it will not match 1.3.0 or 1.3.x versions.The caret indicates the first number i.e. the most recent major version.
An example is in 1.x.x release the caret will update you and it will match with 1.3.0 but not 2.0.0.
Read More about Package.json : https://docs.npmjs.com/files/package.json
Package-lock.json
Before we get into the details, if you wish to follow along with your own project there is one thing to check first. If you are not using the current version of npm or it is lower than 5.0.0 you’ll need to update it to 5.x.x or current stable version. Once done, install the packages by running the command:
npm i
OR
npm install
Once this activity is done, you can see that a new file named “package-lock.json” is automatically created. If you open and read the content of this file you will find dependencies like package.json but with more details.
Below is an example structure of package-lock.json for dependency “babel” like that
{
"name": "generator-venv",
"version": "2.1.24",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
"@babel/code-frame": {
"version": "7.8.3",
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.8.3.tgz",
"integrity": "sha512-a9gxpmdXtZEInkCSHUJDLHZVBgb1QS0jhss4cPP93EW7s+uC5bikET2twEF3KV+7rDblJcmNvTR7VJejqd2C2g==",
"requires": {
"@babel/highlight": "^7.8.3"
}
},
"@babel/highlight": {
"version": "7.8.3",
"resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.8.3.tgz",
"integrity": "sha512-PX4y5xQUvy0fnEVHrYOarRPXVWafSjTW9T0Hab8gVIawpl2Sj0ORyrygANq+KjcNlSSTw0YCLSNA8OyZ1I4yEg==",
"requires": {
"chalk": "^2.0.0",
"esutils": "^2.0.2",
"js-tokens": "^4.0.0"
},
"dependencies": {
"chalk": {
"version": "2.4.2",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
"integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
"requires": {
"ansi-styles": "^3.2.1",
"escape-string-regexp": "^1.0.5",
"supports-color": "^5.3.0"
}
}
}
}
}
You can simply ignore this file continue with your work but have you ever wondered about these three points –
- Why is package-lock.json created?
- What is the purpose or use of package-lock.json?
- Why should we commit package-lock.json with our project source code?
Let me offer my understanding and answers to these questions.
Why is package-lock.json created?
When you install any package in your project by executing the command
npm i <package-name> — save
it will install the exact latest version of that package in your project and save the dependency in package.json with a carat (*^*) sign. Like, if the current version of a package is 5.2.3 then the installed version will be 5.2.3 and the saved dependency will be ^5.2.3. Carat (^) means it will support any higher version with major version 5 like 5.3.1 and so on. Here, package-lock.json is created for locking the dependency with the installed version.
The package-lock.json
- is an extremely important file that is there to save you from a lot of boom boom bam bam 🔥 in your repositories.
- will simply avoid this general behavior of installing updated minor version so when someone clones your repo and run npm install in their machine. NPM will look into package-lock.json and install exact versions of the package as the owner has installed so it will ignore the
^
and~
from package.json. - is solely used to lock dependencies to a specific version number.
- records the exact version of each installed package which allows you to re-install them. Future installs will be able to build an identical dependency tree.
- Also, it contains some other meta information which saves time of fetching that data from npm while you do npm install.
- Describe a single representation of a dependency tree such that teammates, deployments, and continuous integration are guaranteed to install exactly the same dependencies.
- Provide a facility for users to “time-travel” to previous states of
**node_modules**
without having to commit the directory itself. - To facilitate greater visibility of tree changes through readable source control diffs.
- And optimize the installation process by allowing npm to skip repeated metadata resolutions for previously-installed packages.
What is the purpose or use of package-lock.json?
To avoid differences in installed dependencies on different environments and to generate the same results on every environment we should use the package-lock.json file to install dependencies.
Ideally, this file should be on your source control with the package.json file so when you or any other user will clone the project and run the command “npm i”, it will install the exact same version saved in package-lock.json file and you will able to generate the same results as you developed with that particular package.
Why should we commit package-lock.json with our project source code?
During deployment, when you again run “npm i” with the same package.json file without the package-lock.json, the installed package might have a higher version now from what you had intended.
Now, what if you wanted to have that particular version for your dependency during deployment which you used at the time of development. This is the need of creating a package-lock.json file and keeping it with the source code. This file is created with the details of the specific version installed in your project.
Keep locking your dependencies!!
Read More about Package.json : here
Npm-shrinkwrap.json
A publishable lockfile
**npm-shrinkwrap.json**
is a file created by npm-shrinkwrap. It is identical to **package-lock.json**
, with one major caveat: Unlike **package-lock.json**
, **npm-shrinkwrap.json**
may be included when publishing a package.
The recommended use-case for **npm-shrinkwrap.json**
is applications deployed through the publishing process on the registry: for example, daemons and command-line tools intended as global installs or **devDependencies**
. It’s strongly discouraged for library authors to publish this file, since that would prevent end users from having control over transitive dependency updates.
Additionally, if both **package-lock.json**
and **npm-shrinkwrap.json**
are present in a package root, **package-lock.json**
will be ignored in favor of this file.
package-lock.json
is never published to npm, whereasnpm-shrinkwrap
is by defaultpackage-lock.json
files that are not in the top-level package are ignored, but shrinkwrap files belonging to dependencies are respectednpm-shrinkwrap.json
is backwards-compatible with npm versions 2, 3, and 4, whereaspackage-lock.json
is only recognized by npm 5+
You can convert an existing “ package-lock.json
“ to an “npm-shrinkwrap.json “
by running:
npm shrinkwrap
Thus:
- If you are not publishing your package to npm, the choice between these two files is of little consequence. You may wish to use
package-lock.json
because it is the default and its name is clearer to npm beginners; alternatively, you may wish to usenpm-shrinkwrap.json
for backwards compatibility with npm 2-4 if it is difficult for you to ensure everyone on your development team is on npm 5+. (Note that npm 5 was released on 25th May 2017; backwards compatibility will become less and less important the further we get from that date, as most people will eventually upgrade.) - If you are publishing your package to npm, you have a choice between:
- using a
package-lock.json
to record exactly which versions of dependencies you installed, but allowing people installing your package to use any version of the dependencies that is compatible with the version ranges dictated by yourpackage.json
, or - using an
npm-shrinkwrap.json
to guarantee that everyone who installs your package gets exactly the same version of all dependencies
- The official view described (very tersely) in the docs is that option 1 should be used for libraries (presumably in order to reduce the amount of package duplication caused when lots of a package’s dependencies all depend on slightly different versions of the same secondary dependency), but that option 2 might be reasonable for executables that are going to be installed globally.