What is a package?
From npm's perspective:
A package is a file or directory that is described by a
package.json
file.
Most commonly you'll be working with a directory-based package and package.json
, also referred to as the "package file", will be at the root of that directory. For example, if you bootstrap a package with Create React App, the directory contents look like this:
$ tree -L 1 my-react-app
my-react-app
├── README.md
├── node_modules
├── package-lock.json
├── package.json
├── public
└── src
Package file
The package file contains metadata describing the package and its dependencies. The package file for the above CRA-based package is:
{
"name": "my-react-app",
"version": "0.1.0",
"private": true,
"dependencies": {
"@testing-library/jest-dom": "^5.17.0",
"@testing-library/react": "^13.4.0",
"@testing-library/user-event": "^13.5.0",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-scripts": "5.0.1",
"web-vitals": "^2.1.4"
},
// ...
}
We'll cover many of these properties throughout these docs, but for now note the package is named my-react-app
.
Lock file
As well as the package file there is usually a "lock file", package-lock.json
1. Where the package file includes only direct dependencies and generally has ranges for their versions, the lock file includes the whole dependency tree with explicit versions for every package. For the above package file, the lock file would be:
{
"name": "my-react-app",
"version": "0.1.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "my-react-app",
"version": "0.1.0",
"dependencies": {
"@testing-library/jest-dom": "^5.17.0",
"@testing-library/react": "^13.4.0",
"@testing-library/user-event": "^13.5.0",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-scripts": "5.0.1",
"web-vitals": "^2.1.4"
}
},
"node_modules/@aashutoshrathi/word-wrap": {
// ...
}
}
}
The packages
object contains information about all of the packages in the dependency tree, including the base package (which has the special key ""
in the packages
object), and their dependencies.
The lock file is almost always substantially larger than the package file, because it has to contain a lot of explicit information about the whole dependency tree (in the above example package-lock.json
is 675kb, covering over 1,500 packages, whereas package.json
is 0.8kb).
v3 lock files are compatible with npm v7 and above. If you have a pre-v3 lock file, you can upgrade it to v3 (and halve the storage space compared to v2 lock files, which effectively contain v1 and v3) with:
$ npm install --lockfile-version=3 --package-lock-only
Don't add package-lock.json
to your .gitignore
. Keeping a consistent dependency tree is important, especially if you don't want to deal with hard-to-debug issues between different environments, and package.json
alone does not contain enough information to ensure this.
Scoped packages
A scoped package is way of grouping related packages. The scope is usually shown as part of the package name: @{scope}/{name}
.
For example, the Angular framework's packages are all published to the public npm registry under the angular
scope:
@angular/cli
;@angular/core
;@angular/router
;- ... etc.
This can be slightly confusing because packages are often shown as {name}@{version}
, so when there's a scope involved you'll see multiple @
signs: @{scope}/{name}@{version}
(e.g. @angular/[email protected]
).
Footnotes
-
There is an alternative,
npm-shrinkwrap.json
, but here we'll focus mainly on thepackage-lock.json
. ↩