How do I override nested NPM dependency versions?
Asked Answered
O

12

667

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

─┬ [email protected]
 │ ├─┬ [email protected]
 │ │ ├─┬ [email protected]

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:

Operatic answered 4/4, 2013 at 8:30 Comment(4)
Just git clone or fork required module. You can also remove nested phantomjs manually.Fianna
grunt-contrib-jasmine is on 0.5.1, which uses [email protected], which uses [email protected] :)Genotype
npm plans to release overrides in the futureLeon
overrides are now a built-in feature. See my answer below.Hawaii
H
289

As of npm v8.3 (released with Node.js 16), 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/v9/configuring-npm/package-json#overrides

Hawaii answered 17/12, 2021 at 16:31 Comment(7)
@Operatic please consider marking this as the correct answer -- the ecosystem has changed since you asked the question and this is a high ranking Google resultKauffmann
Consider adding "engines": { "npm": ">=8.3.0" } to your package.json to indicate that a new npm version is requiredHaileyhailfellowwellmet
This tripped me up: if you use a monorepo with workspaces, you need to define overrides in the root package, not in the nested projects with the actual dependencies.Doubtful
I added this to package.json but not work with yarn install. I use yarn. But I added it that maybe anyone else possibly can use npm. I hope this is also works with npm.Tentmaker
Great advice from @leumasme! We expanded it with engine-strict=true inside .npmrc to not only get a warning, since the dependency that needs to be overriding breaks a lot for us.Tidwell
Note that there are issues with the implementation of the overrides property and it's reported to only work with the first npm install command: github.com/npm/cli/issues/5443Endomorph
Thanks for this answer. I wish that npm audit force also made fixes by using overrides - right now it just updates the package-lock.json, which suffers from all the same problems brought up in the comments to Viraj Singh's answer.Koralle
P
344

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

For using the latest version of dependency:

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

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.


Psychro answered 30/1, 2018 at 15:3 Comment(16)
This is nice, as long as npm install runs one time. In my case the edits are necessary as the nested dep is causing a fail.Purl
this will be removed anytime you run npm i instead of editing your package-lock.json and adding the child dependency to "dependencies" there, add the child dependency to your package.json "dependencies" sectionMalayoindonesian
Better yet, add it to your devDependencies, since you aren't really using that module on your code.Raspberry
I've created a library that does exactly that for you automatically: github.com/rogeriochaves/npm-force-resolutionsBlabbermouth
@Malayoindonesian I tried to add the new version (in my case cssnano/@next) to devDependencies, but my the package I want to change the dependency for (in my case css-loader), still uses cssnano/@latest. Could you please elaborate on how to force it to use another /@next instead of /@latest? Thank you.Matazzoni
@Matazzoni Does overriding dependencies via package-lock.json work in your case?Psychro
This doesn't work for me. I'm still getting the unwanted version of the child dependency.Unhelm
@Unhelm please provide information about your Node version (it works for me on Node 8.4.0) and check if the dependency is duplicated somewhere (e.g. if package A requires version 1 of package B, and package C requires version 1 of package B, and you want to override version of B, you must override it for both A and C).Psychro
What is the workaround for npm install undoing this?Frivolous
@Frivolous The last paragraph says that npm install should add missing dependencies, not ignore all your changes. Please make sure this works: remove package-lock.json, run npm install, edit package-lock.json the way it's described in the answer, then delete node_modules folder and run npm install once again. Will it work for you?Psychro
It works but then if I run npm install again then all the changes to package-lock.json get reverted and I get the bad version of the dep back.Frivolous
I run npm ci and this does not touch the package-lock.jsonProcter
how do i specify a git repo instead of version?Patroon
@DanielLizik E.g., "dependencies": { "graphql-relay": { "version": "0.6.0", "resolved": "[email protected]:LMBernardo/graphql-relay-js.git#6dbd6f3cb0fa901620a85c16c8522d240f4315b4" } }Communicant
This doesn't seem to work for peerDependencies :'(Ulland
Note that the option in pnpm is pnpm.overrides and overrides won't take effectAlethaalethea
H
289

As of npm v8.3 (released with Node.js 16), 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/v9/configuring-npm/package-json#overrides

Hawaii answered 17/12, 2021 at 16:31 Comment(7)
@Operatic please consider marking this as the correct answer -- the ecosystem has changed since you asked the question and this is a high ranking Google resultKauffmann
Consider adding "engines": { "npm": ">=8.3.0" } to your package.json to indicate that a new npm version is requiredHaileyhailfellowwellmet
This tripped me up: if you use a monorepo with workspaces, you need to define overrides in the root package, not in the nested projects with the actual dependencies.Doubtful
I added this to package.json but not work with yarn install. I use yarn. But I added it that maybe anyone else possibly can use npm. I hope this is also works with npm.Tentmaker
Great advice from @leumasme! We expanded it with engine-strict=true inside .npmrc to not only get a warning, since the dependency that needs to be overriding breaks a lot for us.Tidwell
Note that there are issues with the implementation of the overrides property and it's reported to only work with the first npm install command: github.com/npm/cli/issues/5443Endomorph
Thanks for this answer. I wish that npm audit force also made fixes by using overrides - right now it just updates the package-lock.json, which suffers from all the same problems brought up in the comments to Viraj Singh's answer.Koralle
I
287

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/)

Iridium answered 2/7, 2013 at 11:5 Comment(13)
Nice and simple! But you don't really need from. Why do you add it?Jinks
When I do this, only the grunt-contrib-connect dependency and its children are installed. All my other dependencies in package.json are not installed.Lynea
I had the same issue as @iDVB. I ended up editing the node_modules directory so that the full shrinkwrap dependency dump was exactly what I wanted, not just overrides. But still a kind of painful solution.Hendon
@Jinks this file is created by running npm shrinkwrap, the entries are not added by handAnnual
why at one line it says [email protected] and then on another line only connect --> connect@~2.7.3Requisite
Thank you! npm shrinkwrap wouldn't run because of "unmet peer dependencies" until I manually created a npm-shrinkwrap.json file per your template here and overrode "peerDependencies". Once I did that I could remove the .json file and then npm shrinkwrap ran fine. Yay!Elasmobranch
Agreed, its a pain to generate a full shrinkwrap file and edit only the sub or sub-sub dependencies with npm 2.x (node4) but with npm 3.x (node5) we can be minimalistic and only create a shrinkwrap file to address exactly what needs to be overridden. github.com/npm/npm/issues/7108Lierne
Unfortunately, as is mentioned in that bug, with npm4, the minimalistic approach no longer works. (When deleting node_modules, running an install with a minimal shrinkwrap seems to leave devDependencies intact though ignoring dependencies, but running another install removes the non-explicit items, so for now it is important to run npm shrinkwrap to get a full file, modify the portion in question, and then run npm install again)Television
use npm shrinkwrap to generate it, but be aware that this will make npm-shrinkwrap.json take precedence over any other existing or future package-lock.json filesRomelda
npm 6.4 will just overwrite the shrinkwrap file and use the outdated dependenciesLang
If you already have a package-lock.json (and you almost definitely do) then the shrinkwrap file you create will be incorporated into your package-lock.json the next time you install because you cannot have both.Hooten
This way you're basically ditching your package-lock.json. That is, your packages are going to be updated in accordance with the package.json file. And this is a hack, your override will vanish the next time npm has to change something.Adelaidaadelaide
using npm 6.13.1. I have a similar scenario. My project calls "A" that depends on "B" that depends on "C, 1.0.0" and I want to force B to use "C, 2.0.0". What do I have to do?Motley
R
104

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.

Reprise answered 17/7, 2020 at 14:27 Comment(7)
hint: use --save-dev flag for npm installVlad
this would not work if one wants to upgrade corresponding dependency only for one particular 3rd party dependencyThew
Note: this works only when you have package-lock.json enabled, which some devs might not have due to its inherent problems.Akmolinsk
Is there any built-in solution in latest versions of NPM as per year 2021? I would not like to depend on a third party library for this kind of things - manipulating dependency tree.Nitride
@DaniP. npm is poor's man dependency manager, so I doubt itReprise
For me the preinstall command broke in Circle CI, so i used this one instead: "preinstall": "npm install --package-lock-only --ignore-scripts && npx npm-force-resolutions" See https://mcmap.net/q/67956/-npm-force-resolutions-not-working-when-installing-a-new-packageErickericka
@keemor worth mentioning that's completely different library with similar name and purposeReprise
L
67

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://mcmap.net/q/67957/-how-do-i-override-nested-dependencies-with-yarn

Lissner answered 19/3, 2019 at 13:10 Comment(0)
L
10

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:

How to override a nested npm sub-dependency with a different package altogether (not just different package version number)?

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"
  }
Lita answered 30/11, 2021 at 23:11 Comment(2)
Thank you, this was really useful. I thought replacing a package would have been possible by overriding using a reference e.g. "bar": "$foo" as documented in the last example here but I couldn't get it to work. But your solution did the trick and I would give an extra point for the details.Nessie
npm uses "overrides" instead of "resolutions"Antineutrino
I
10

Based on the rest of the answers, I provide the same solution, but I display the package.json, as I struggled a little bit on where to place the override and how.

{
  "name": "my-app",
  "version": "snapshot",
  "scripts": {
    "ng": "ng",
    "build-dev": "ng build --configuration development",
  },
  "private": true,
  "dependencies": {
    "@angular/animations": "~14.2.9",
    "@angular/common": "~14.2.9"
  ...
  },
  "devDependencies": {
    "@angular-devkit/build-angular": "^14.2.8",
  ....
  },
  "overrides": {
    "loader-utils@>2.0.0 <3": "2.0.4",
    "loader-utils@>3.0.0 <4": "3.2.1"
  }
}

For November 2022 "loader-utils" security vulnerability, it was requested to

  • use the version 2.0.4, if you are in the 2.X
  • use the version 3.2.1, if you are in the 3.X

And to verify

  • add the package.json the override tag
  • delete the package-lock.json
  • run "npm install"
  • run "npm audit"
Innes answered 16/11, 2022 at 10:22 Comment(2)
Wouldn't this loader-utils@>3.0.0 <4 override a future 3.3 to 3.2.1?Connote
yes, for the time being 3.2.1 is the only safe one though. This was an example, it does not have to cover all the potential cases.Innes
B
3

@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.

Bushwa answered 13/9, 2021 at 20:6 Comment(3)
I used "preinstall": "npx force-resolutions" as suggested here github.com/rogeriochaves/npm-force-resolutions/issues/…Profanity
There's also a more speedier route via a bit of bashery: https://mcmap.net/q/67956/-npm-force-resolutions-not-working-when-installing-a-new-packageYachting
There is no answer by "Gus". Could you instead explicitly link to the answer you refer to when writing and then use @Lissner 's answer?Newby
S
2

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
Striker answered 14/4, 2020 at 9:24 Comment(1)
using npm 6 this does NOT work. npm i overwrites any change to the package lock fileSpiniferous
N
2

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"
  }
}
Nolpros answered 15/10, 2021 at 20:24 Comment(0)
G
0

I could not get the override technique to work for me, at least not to prevent the error. 1

I also couldn't use --force or --legacy-peer-deps as in npm install --force. I can run npm install --force locally but I don't know how to run that command on the server (I happen to use Google AppEngine, deployed using gcloud.cmd app deploy)

I was able to fix my issue with two steps:

  1. manually modify my package-lock.json to remove the offending peer dependency:
  2. call npm ci (instead of npm install, read about the difference here)

For the record here is my entire error message, similar to the one seen here and here

npm ERR! code ERESOLVE
npm ERR! ERESOLVE unable to resolve dependency tree
npm ERR!
npm ERR! While resolving: [email protected]
npm ERR! Found: [email protected]
npm ERR! node_modules/cypress
npm ERR!   dev cypress@"^9.1.0" from the root project
npm ERR!
npm ERR! Could not resolve dependency:
npm ERR! peer cypress@"^4.5.0" from [email protected]
npm ERR! node_modules/cypress-plugin-snapshots
npm ERR!   dev cypress-plugin-snapshots@"^1.4.4" from the root project
npm ERR!
npm ERR! Fix the upstream dependency conflict, or retry
npm ERR! this command with --force, or --legacy-peer-deps
npm ERR! to accept an incorrect (and potentially broken) dependency resolution.
npm ERR!
npm ERR! See C:\Users\there\AppData\Local\npm-cache\eresolve-report.txt for a full report.

npm ERR! A complete log of this run can be found in:
npm ERR!     C:\Users\there\AppData\Local\npm-cache\_logs\2024-01-05T07_42_59_544Z-debug.log

1 Maybe my overrides does not work because of the bug mentioned here and here. Those bugs say overrides only work on a "fresh" install, when there is no node_modules and no package-lock.json. I will double-check this...

Grange answered 5/1 at 7:59 Comment(0)
O
-9

Run this first

npm i -D @types/[email protected]

it will solve the issue

Offence answered 1/7, 2022 at 6:29 Comment(1)
No, it doesn't.Putandtake

© 2022 - 2024 — McMap. All rights reserved.