npm equivalent of yarn resolutions?

Npmpackage.jsonYarnpkg

Npm Problem Overview


Is there an npm equivalent of the yarn resolutions functionality? There is no mention of it in the npm package.json docs.

For example, I want to install [email protected] and one of its dependencies (@lerna/publish) at 3.3.2 as well. Currently doing that with yarn like so, but would prefer to use npm and not manually change package-lock.json or anything dodgy like that.

"devDependencies": {
  "lerna": "3.3.2",
},
"resolutions": {
  "@lerna/publish": "3.3.2"
}

Npm Solutions


Solution 1 - Npm

This does not seem to be supported by npm natively, however this package aims to add this functionality:

https://github.com/rogeriochaves/npm-force-resolutions

Solution 2 - Npm

[tag:npm] is due to support overrides, which is equivalent to yarn's resolutions.

For more information about the current RFC status:

https://github.com/npm/rfcs/blob/latest/accepted/0036-overrides.md

Solution 3 - Npm

Npm equivalent to yarn resolutions is overrides. After the RFC was accepted now there's an epic to watch the progress of implementation. https://github.com/npm/statusboard/issues/343

Edit: This was released in npm v8.3.0

Documentation: https://docs.npmjs.com/cli/v8/configuring-npm/package-json#overrides

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

Solution 4 - Npm

As far as I can tell, npm-force-resolutions does not work with [tag:npm] v7. The package-lock.json format changed in v7 and npm-force-resolutions no longer updates the relevant fields.

However, it is relatively easy to write a script to restrict your dependency tree to only 1 version of a package, e.g.

#!/usr/bin/env node

/* eslint-disable unicorn/prevent-abbreviations */
/* eslint-disable import/unambiguous */
/* eslint-disable import/no-commonjs */
/* eslint-disable node/shebang */

const fs = require('fs').promises;
const path = require('path');

const main = async (resolutions) => {
  const packageLockFilePath = path.resolve(__dirname, '../package-lock.json');

  for (const [name, version] of Object.entries(resolutions)) {
    const packageLock = JSON.parse(await fs.readFile(packageLockFilePath));

    const packagePaths = Object.keys(packageLock.packages);

    const deletePaths = [];

    for (const packagePath of packagePaths) {
      if (packagePath.endsWith('/' + name)) {
        if (packageLock.packages[packagePath].version !== version) {
          deletePaths.push(packagePath);
        }
      }
    }

    for (const packagePath of deletePaths) {
      for (const deletePath of deletePaths) {
        if (packagePath === deletePath || packagePath.startsWith(deletePath + '/')) {
          // eslint-disable-next-line fp/no-delete
          delete packageLock.packages[packagePath];
        }
      }
    }

    await fs.writeFile(
      packageLockFilePath,
      JSON.stringify(packageLock, null, '  '),
    );
  }
};

main(require('../package.json').resolutions);

This script simply removes all links to dependencies that do not satisfy resolutions defined in package.json.

To execute the script, just add it to package.json scripts and define resolutions field, e.g.

{
  "scripts": {
    "postinstall": "node bin/fix-package-lock.js"
  },
  "resolutions": {
    "webpack": "5.6.0"
  }
}

resolutions is simply a map of package names and the exact versions of those packages that should be kept in the dependency tree, i.e. the above configuration will remove all versions of [tag:webpack] that are not 5.6.0. As long you install [email protected] version as a dependency of the project you are working with, this will guarantee that all packages load the same version of webpack.

Solution 5 - Npm

In package.json add something like this to resolve any grapahql package (replace with your package of issue) to the specified version.

  "resolutions": {
    "graphql": "^15.8.0",
    "**/graphql": "^15.8.0"
  },

You can list the packages you have installed with npm list <package name> to see which package is required and where. Untested, but think its working on my machine.

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
QuestionadanilevView Question on Stackoverflow
Solution 1 - NpmJulienView Answer on Stackoverflow
Solution 2 - NpmGajusView Answer on Stackoverflow
Solution 3 - NpmfernandopasikView Answer on Stackoverflow
Solution 4 - NpmGajusView Answer on Stackoverflow
Solution 5 - NpmJeremyView Answer on Stackoverflow