How do I override nested NPM dependency versions?

node.jsNpm

node.js Problem Overview


I would like to use the grunt-contrib-jasmine NPM package. It has various dependencies. Part of the dependency graph looks like this:

─┬ grunt-contrib-jasmine@0.4.1
 │ ├─┬ grunt-lib-phantomjs@0.2.0
 │ │ ├─┬ phantomjs@1.8.2-2

Unfortunately, there's a bug in this version phantomjs which prevents it from installing correctly on Mac OS X. This is fixed in the latest version.

How can I get grunt-lib-phantomjs to use a newer version of phantomjs?

Some additional context:

node.js Solutions


Solution 1 - node.js

You can use npm shrinkwrap functionality, in order to override any dependency or sub-dependency.

I've just done this in a grunt project of ours. We needed a newer version of connect, since 2.7.3. was causing trouble for us. So I created a file named npm-shrinkwrap.json:

{
  "dependencies": {
    "grunt-contrib-connect": {
      "version": "0.3.0",
      "from": "[email protected]",
      "dependencies": {
        "connect": {
          "version": "2.8.1",
          "from": "connect@~2.7.3"
        }
      }
    }
  }
}

npm should automatically pick it up while doing the install for the project.

(See: https://nodejs.org/en/blog/npm/managing-node-js-dependencies-with-shrinkwrap/)

Solution 2 - node.js

As of npm cli v8.3.0 (2021-12-09) this can be solved using the overrides field of package.json. As described in StriplingWarrior's answer

For example, the project has typescript version 4.6.2 as direct development dependency and awesome-typescript-loader that uses old version 2.7 of typescript. Here is how you can tell npm to use version 4.6.2 of typescript for awesome-typescript-loader:

{
  "name": "myproject",
  "version": "0.0.0",
  "scripts": ...
  "dependencies": ...
  "devDependencies": {
    "typescript": "~4.6.2",
    "awesome-typescript-loader": "^5.2.1",
    ...
  },
  "overrides": {
    "awesome-typescript-loader": {
      "typescript": "$typescript"
    }
  }
}

If you don't use typescript as direct development dependency, then you have to write 4.6.2 instead of $typescript in overrides section:

{
  "name": "myproject",
  "version": "0.0.0",
  "scripts": ...
  "dependencies": ...
  "devDependencies": {
    "awesome-typescript-loader": "^5.2.1",
    ...
  },
  "overrides": {
    "awesome-typescript-loader": {
      "typescript": "~4.6.2"
    }
  }
}

Same overrides can be used for both dependencies and devDependencies.


If you're using npm version >5 but <8.3.0: edit your package-lock.json: remove the library from "requires" section and add it under "dependencies".

For example, you want deglob package to use glob package version 3.2.11 instead of its current one. You open package-lock.json and see:

"deglob": {
  "version": "2.1.0",
  "resolved": "https://registry.npmjs.org/deglob/-/deglob-2.1.0.tgz",
  "integrity": "sha1-TUSr4W7zLHebSXK9FBqAMlApoUo=",
  "requires": {
    "find-root": "1.1.0",
    "glob": "7.1.2",
    "ignore": "3.3.5",
    "pkg-config": "1.1.1",
    "run-parallel": "1.1.6",
    "uniq": "1.0.1"
  }
},

Remove "glob": "7.1.2", from "requires", add "dependencies" with proper version:

"deglob": {
  "version": "2.1.0",
  "resolved": "https://registry.npmjs.org/deglob/-/deglob-2.1.0.tgz",
  "integrity": "sha1-TUSr4W7zLHebSXK9FBqAMlApoUo=",
  "requires": {
    "find-root": "1.1.0",
    "ignore": "3.3.5",
    "pkg-config": "1.1.1",
    "run-parallel": "1.1.6",
    "uniq": "1.0.1"
  },
  "dependencies": {
    "glob": {
      "version": "3.2.11"
    }
  }
},

Now remove your node_modules folder, run npm ci (or npm install for old version of node/npm) and it will add missing parts to the "dependencies" section.


Solution 3 - node.js

As of NPM v8.3, the correct way to deal with this is via the overrides section of your package.json file.

> If you need to make specific changes to dependencies of your > dependencies, for example replacing the version of a dependency with a > known security issue, replacing an existing dependency with a fork, or > making sure that the same version of a package is used everywhere, > then you may add an override. > > Overrides provide a way to replace a package in your dependency tree > with another version, or another package entirely. These changes can > be scoped as specific or as vague as desired. > > To make sure the package foo is always installed as version 1.0.0 no > matter what version your dependencies rely on: > > { > "overrides": { > "foo": "1.0.0" > } > }

There are a variety of other, more nuanced configurations allowing you to only override a package when it's a dependency of a particular package hierarchy. For more details, check out https://docs.npmjs.com/cli/v8/configuring-npm/package-json#overrides

Solution 4 - node.js

The only solution that worked for me (node 12.x, npm 6.x) was using npm-force-resolutions developed by @Rogerio Chaves.

First, install it by:

npm install npm-force-resolutions --save-dev

You can add --ignore-scripts if some broken transitive dependency scripts are blocking you from installing anything.

Then in package.json define what dependency should be overridden (you must set exact version number):

"resolutions": {
  "your-dependency-name": "1.23.4"
}

and in "scripts" section add new preinstall entry:

"preinstall": "npm-force-resolutions",

Now, npm install will apply changes and force your-dependency-name to be at version 1.23.4 for all dependencies.

Solution 5 - node.js

For those using yarn.

I tried using npm shrinkwrap until I discovered the yarn cli ignored my npm-shrinkwrap.json file.

Yarn has https://yarnpkg.com/lang/en/docs/selective-version-resolutions/ for this. Neat.

Check out this answer too: https://stackoverflow.com/a/41082766/3051080

Solution 6 - node.js

Nested replacement with an entirely different package

Most of the strategies outlined in the other answers here work well if you are just interested in overriding the package's version number, but in our case, we needed to find a way to override a nested npm sub-dependency with a different package altogether. For details on why you would ever want to do this, please refer to the following question:

https://stackoverflow.com/questions/69548370/how-to-override-a-nested-npm-sub-dependency-with-a-different-package-altogether/69591894

Specify the tarball directly

For nested replacement of a package with an entirely different package using the npm-force-resolutions strategy that others have mentioned, you just need to provide a link to the tarball where you would normally specify the overriding version number.

As an example, for the case of replacing the vulnerable package, ansi-html, with the fixed fork of this package, ansi-html-community, your resolutions section of package.json should look like this:

"resolutions": {
    "ansi-html": "https://registry.npmjs.org/ansi-html-community/-/ansi-html-community-0.0.8.tgz"
}

To find the link to the tarball, use the following command, modifying your registry as necessary:

npm view ansi-html-community dist.tarball --registry=https://registry.npmjs.org/

Also, note that for npm-force-resolutions to work when you run npm install, you will need a preinstall entry under the scripts section of package.json:

  "scripts": {
    "preinstall": "npx npm-force-resolutions"
  }

Solution 7 - node.js

I had an issue where one of the nested dependency had an npm audit vulnerability, but I still wanted to maintain the parent dependency version. the npm shrinkwrap solution didn't work for me, so what I did to override the nested dependency version:

  1. Remove the nested dependency under the 'requires' section in package-lock.json
  2. Add the updated dependency under DevDependencies in package.json, so that modules that require it will still be able to access it.
  3. npm i

Solution 8 - node.js

I was about to go down the npm-force-resolutions route but it seems that simply including the dependency in my own package.json fixed the problem for me.

I believe this worked in my case because the original dependency allows for patch versions of the dependency in question that I wanted to update. Thus by manually including a newer version it still fulfilled the dependency of the original dependency and will use the one I've manually added.

Example

Problem

I need to update plyr to version 3.6.9 from 3.6.8

Mine

package.json

{
  "dependencies": {
    "react-plyr": "^3.2.0"
  }
}

React Plyr

package.json

{
  "dependencies": {
    "plyr": "^3.6.8"
  }
}

> Notice for the plyr dependency it starts with ^ this means it can accept any minor patches. You can learn more about that here: > > <https://docs.npmjs.com/about-semantic-versioning#using-semantic-versioning-to-specify-update-types-your-package-can-accept>

Updating Mine

This updates the plyr dependency from my package.json.

package.json

{
  "dependencies": {
    "plyr": "^3.6.9",
    "react-plyr": "^3.2.0"
  }
}

Solution 9 - node.js

@user11153 's answer worked for me locally, but when trying to do a clean install (aka deleting node_modules), I would get:

npm-force-resolutions: command not found

I had to update the preinstall script to be:

"preinstall": "npm i npm-force-resolutions && npm-force-resolutions"

Which ensures that npm-force-resolutions package is installed before attempting to run it.

That being said, if you're able to use yarn instead, I would do that and then use @Gus 's answer.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestiongeorgebrockView Question on Stackoverflow
Solution 1 - node.jstuxpiperView Answer on Stackoverflow
Solution 2 - node.jsizogfifView Answer on Stackoverflow
Solution 3 - node.jsStriplingWarriorView Answer on Stackoverflow
Solution 4 - node.jsuser11153View Answer on Stackoverflow
Solution 5 - node.jsGusView Answer on Stackoverflow
Solution 6 - node.jsJustin DehortyView Answer on Stackoverflow
Solution 7 - node.jsEthan Yang View Answer on Stackoverflow
Solution 8 - node.jsCTS_AEView Answer on Stackoverflow
Solution 9 - node.jsEmilio VenegasView Answer on Stackoverflow