w3resource

Npm-package-locks


The npm-package-locks is an explanation of npm lockfiles.

Description

Conceptually, the package.json is the input to npm install, while the output for npm install is the a fully formed node_modules tree: which is a representation of the dependencies that you declared. In an ideal situation, npm will work like a pure function: what this means is that the same package.json file should produce the exact same node_modules tree, anytime. In some cases, this is true, but in many other cases, npm will be unable to do this. There are multiple reasons why this could happen:

  • You may have used different versions of npm (or other package managers) to install a package, with each version using slightly different installation algorithms.
  • A new version of a direct semver-range package might have been published since the last time your packages were installed, and thus a newer version is used.
  • A dependency for one of your dependencies may have published a new version, which will be updated even if you used pinned dependency specifiers (1.2.3 instead of ^1.2.3)
  • The registry that you installed from is no longer available, or it allows mutation of versions (unlike the primary npm registry), and a different version of a package exists under that same version number now.

Here is an example, consider the package A:

{
  "name": "A",
  "version": "0.1.0",
  "dependencies": {
    "B": "<0.1.0"
  }
}```
Package B:
```{
  "name": "B",
  "version": "0.0.1",
  "dependencies": {
    "C": "<0.1.0"
  }
}```

and package C:
```{
  "name": "C",
  "version": "0.0.1"
}```
If the above versions are the only versions of A, B, and C available in the registry, then a normal npm install A installs:  
```[email protected]
`-- [email protected]
    `-- [email protected]```
However, if [email protected] has been published, running a fresh npm install A will install:
```[email protected]
`-- [email protected]
    `-- [email protected]

This is with the assumption that the new version did not modify B's dependencies. Of course, the new version of B might include a new version of C as well as new dependencies. If the such changes are not desirable, the author of A might specify a dependency on [email protected]. However, if the authors of A and B are not the same person, there is no way for A's author to say that he or she does not want to pull in the newly published versions of C when B has not changed at all.

In order to prevent this potential issue, npm will use the package.json or the npm-shrinkwrap.json (if present). Both files are called package locks, or lockfiles.

When you run npm install, npm will generate or update your package lock, which looks something like this:

{
  "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"
        }
      }
    }
  }
}

This file will describe an exact, and more importantly a reproducible node_modules tree. Once it is present, any future installation will base its work on this file, instead of recalculating dependency versions on package.json.

The presence of a package lock will change the installation behavior such that:

  1. The module tree described by the package lock will be reproduced. This will mean reproducing the structure described in the file, using the specific files that are referenced in "resolved" if available, falling back to normal package resolution using "version" if there is none.
  2. The tree will be walked and any missing dependencies will be installed in the usual fashion.

If you have preshrinkwrap, shrinkwrap or postshrinkwrap in the scripts property of the package.json, they are executed in this order. preshrinkwrap and shrinkwrap will be executed before the shrinkwrap, postshrinkwrap is executed afterwards. These scripts will run for both package-lock.json and npm-shrinkwrap.json. For instance, if you want to run some postprocessing on the generated file:

"scripts": {
  "postshrinkwrap": "json -I -e \"this.myMetadata = $MY_APP_METADATA\""
}

Using locked packages

Using a locked package is not different from using any package without a package lock: any commands that will update node_modules and/or package.json's dependencies will automatically syncs the existing lockfile. This includes command like npm install, npm rm, npm update, etc. For you to prevent this update from happening, you can use the --no-save option to ensure nothing is saved, or --no-shrinkwrap to allow package.json to be updated while the package-lock.json or npm-shrinkwrap.json are left intact.

Resolving lockfile conflicts

Occasionally, two separate npm install command will create package locks that can cause merge conflicts in source control systems. As of [email protected], you can resolve these conflicts by manually fixing any package.json conflicts, and then running this command npm install [--package-lock-only] again. npm automatically resolves any conflicts for you and writes a merged package lock that includes all the dependencies from both branches in a reasonable tree. If you provide --package-lock-only, it does this without also modifying your local node_modules/.

If you want to make this process seamless on git, you should consider installing npm-merge-driver, which teaches git how to do this itself without any user interaction. Actually: $ npx npm-merge-driver install -g lets you do this, and it even works with [email protected] versions of npm 5. It should be noted that, when package.json itself conflicts, you will need to resolve that by hand and then run npm install manually, even if you are using the merge driver.

Previous: Package-lock.json
Next: Npm-shrinkwrap.json



Follow us on Facebook and Twitter for latest update.