123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200 |
- .TH "PACKAGE\-LOCKS" "5" "August 2021" "" ""
- .SH "NAME"
- \fBpackage-locks\fR \- An explanation of npm lockfiles
- .SS Description
- .P
- Conceptually, the "input" to npm help \fBinstall\fP is a npm help package\.json, while its
- "output" is a fully\-formed \fBnode_modules\fP tree: a representation of the
- dependencies you declared\. In an ideal world, npm would work like a pure
- function: the same \fBpackage\.json\fP should produce the exact same \fBnode_modules\fP
- tree, any time\. In some cases, this is indeed true\. But in many others, npm is
- unable to do this\. There are multiple reasons for this:
- .RS 0
- .IP \(bu 2
- different versions of npm (or other package managers) may have been used to install a package, each using slightly different installation algorithms\.
- .IP \(bu 2
- a new version of a direct semver\-range package may have been published since the last time your packages were installed, and thus a newer version will be used\.
- .IP \(bu 2
- A dependency of one of your dependencies may have published a new version, which will update even if you used pinned dependency specifiers (\fB1\.2\.3\fP instead of \fB^1\.2\.3\fP)
- .IP \(bu 2
- The registry you installed from is no longer available, or allows mutation of versions (unlike the primary npm registry), and a different version of a package exists under the same version number now\.
- .RE
- .P
- As an example, consider package A:
- .P
- .RS 2
- .nf
- {
- "name": "A",
- "version": "0\.1\.0",
- "dependencies": {
- "B": "<0\.1\.0"
- }
- }
- .fi
- .RE
- .P
- package B:
- .P
- .RS 2
- .nf
- {
- "name": "B",
- "version": "0\.0\.1",
- "dependencies": {
- "C": "<0\.1\.0"
- }
- }
- .fi
- .RE
- .P
- and package C:
- .P
- .RS 2
- .nf
- {
- "name": "C",
- "version": "0\.0\.1"
- }
- .fi
- .RE
- .P
- If these are the only versions of A, B, and C available in the
- registry, then a normal \fBnpm install A\fP will install:
- .P
- .RS 2
- .nf
- A@0\.1\.0
- `\-\- B@0\.0\.1
- `\-\- C@0\.0\.1
- .fi
- .RE
- .P
- However, if B@0\.0\.2 is published, then a fresh \fBnpm install A\fP will
- install:
- .P
- .RS 2
- .nf
- A@0\.1\.0
- `\-\- B@0\.0\.2
- `\-\- C@0\.0\.1
- .fi
- .RE
- .P
- assuming the new version did not modify B's dependencies\. Of course,
- the new version of B could include a new version of C and any number
- of new dependencies\. If such changes are undesirable, the author of A
- could specify a dependency on B@0\.0\.1\|\. However, if A's author and B's
- author are not the same person, there's no way for A's author to say
- that he or she does not want to pull in newly published versions of C
- when B hasn't changed at all\.
- .P
- To prevent this potential issue, npm uses npm help package\-lock\.json or, if present, npm help npm\-shrinkwrap\.json\. These files are called package locks, or lockfiles\.
- .P
- Whenever you run \fBnpm install\fP, npm generates or updates your package lock,
- which will look something like this:
- .P
- .RS 2
- .nf
- {
- "name": "A",
- "version": "0\.1\.0",
- \.\.\.metadata fields\.\.\.
- "dependencies": {
- "B": {
- "version": "0\.0\.1",
- "resolved": "https://registry\.npmjs\.org/B/\-/B\-0\.0\.1\.tgz",
- "integrity": "sha512\-DeAdb33F+"
- "dependencies": {
- "C": {
- "version": "git://github\.com/org/C\.git#5c380ae319fc4efe9e7f2d9c78b0faa588fd99b4"
- }
- }
- }
- }
- }
- .fi
- .RE
- .P
- This file describes an \fIexact\fR, and more importantly \fIreproducible\fR
- \fBnode_modules\fP tree\. Once it's present, any future installation will base its
- work off this file, instead of recalculating dependency versions off
- npm help package\.json\.
- .P
- The presence of a package lock changes the installation behavior such that:
- .RS 0
- .IP 1. 3
- The module tree described by the package lock is reproduced\. This means
- reproducing the structure described in the file, using the specific files
- referenced in "resolved" if available, falling back to normal package resolution
- using "version" if one isn't\.
- .IP 2. 3
- The tree is walked and any missing dependencies are installed in the usual
- fashion\.
- .RE
- .P
- If \fBpreshrinkwrap\fP, \fBshrinkwrap\fP or \fBpostshrinkwrap\fP are in the \fBscripts\fP
- property of the \fBpackage\.json\fP, they will be executed in order\. \fBpreshrinkwrap\fP
- and \fBshrinkwrap\fP are executed before the shrinkwrap, \fBpostshrinkwrap\fP is
- executed afterwards\. These scripts run for both \fBpackage\-lock\.json\fP and
- \fBnpm\-shrinkwrap\.json\fP\|\. For example to run some postprocessing on the generated
- file:
- .P
- .RS 2
- .nf
- "scripts": {
- "postshrinkwrap": "json \-I \-e \\"this\.myMetadata = $MY_APP_METADATA\\""
- }
- .fi
- .RE
- .SS Using locked packages
- .P
- Using a locked package is no different than using any package without a package
- lock: any commands that update \fBnode_modules\fP and/or \fBpackage\.json\fP\|'s
- dependencies will automatically sync the existing lockfile\. This includes \fBnpm
- install\fP, \fBnpm rm\fP, \fBnpm update\fP, etc\. To prevent this update from happening,
- you can use the \fB\-\-no\-save\fP option to prevent saving altogether, or
- \fB\-\-no\-shrinkwrap\fP to allow \fBpackage\.json\fP to be updated while leaving
- \fBpackage\-lock\.json\fP or \fBnpm\-shrinkwrap\.json\fP intact\.
- .P
- It is highly recommended you commit the generated package lock to source
- control: this will allow anyone else on your team, your deployments, your
- CI/continuous integration, and anyone else who runs \fBnpm install\fP in your
- package source to get the exact same dependency tree that you were developing
- on\. Additionally, the diffs from these changes are human\-readable and will
- inform you of any changes npm has made to your \fBnode_modules\fP, so you can notice
- if any transitive dependencies were updated, hoisted, etc\.
- .SS Resolving lockfile conflicts
- .P
- Occasionally, two separate npm install will create package locks that cause
- merge conflicts in source control systems\. As of \fBnpm@5\.7\.0\fP, these conflicts
- can be resolved by manually fixing any \fBpackage\.json\fP conflicts, and then
- running \fBnpm install [\-\-package\-lock\-only]\fP again\. npm will automatically
- resolve any conflicts for you and write a merged package lock that includes all
- the dependencies from both branches in a reasonable tree\. If
- \fB\-\-package\-lock\-only\fP is provided, it will do this without also modifying your
- local \fBnode_modules/\fP\|\.
- .P
- To make this process seamless on git, consider installing
- \fBnpm\-merge\-driver\fP \fIhttps://npm\.im/npm\-merge\-driver\fR, which will teach git how
- to do this itself without any user interaction\. In short: \fB$ npx
- npm\-merge\-driver install \-g\fP will let you do this, and even works with
- pre\-\fBnpm@5\.7\.0\fP versions of npm 5, albeit a bit more noisily\. Note that if
- \fBpackage\.json\fP itself conflicts, you will have to resolve that by hand and run
- \fBnpm install\fP manually, even with the merge driver\.
- .SS See Also
- .RS 0
- .IP \(bu 2
- https://medium\.com/@sdboyer/so\-you\-want\-to\-write\-a\-package\-manager\-4ae9c17d9527
- .IP \(bu 2
- npm help package\.json
- .IP \(bu 2
- npm help package\-lock\.json
- .IP \(bu 2
- npm help shrinkwrap\.json
- .IP \(bu 2
- npm help shrinkwrap
- .RE
|