Can I find a package's peer dependencies on www.npmjs.com

Viewed 361

I have a project that has around 50 packages installed through NPM. After several months of neglect I now need to update most packages, resulting in several mismatching peer dependencies. In order to find the correct combination of package versions I want to see the peer dependencies of all versions of a certain package.

Where on www.npmjs.com can I find a package's peer dependencies?

A package's page shows "dependencies" and "dependents", but I believe these are normal dependencies, not "peer dependencies".

1 Answers

The www.npmjs.com website doesn't expose peer dependencies information. However, this metadata resides at the npm registry endpoint: https://registry.npmjs.org/.

Via your command line you can access it by utilizing the npm view command. For instance:

npm view <pkg_name> peerDependencies

Note: You'll need to substitute the <pkg_name> part with a real package name.

The aforementioned command will list peerDependencies for the latest version of the given package.


I want to see the peer dependencies of all versions of a certain package.

To achieve this you can:

  1. Run the following command to obtain all versions for a given package

    npm view babel-loader versions --json
    

    Note Here we check for babel-loader but this could be any valid package name

    This will print:

    [
      "4.0.0",
      "4.1.0",
      "4.2.0",
      "4.3.0",
      "5.0.0",
      ...
    ]
    
  2. Then run the following command to obtain the peer dependencies for each version previously listed:

    npm view babel-loader@4.0.0 peerDependencies --json
                         ^^^^^^
    

    prints:

    {
      "babel-core": "^4.0.0",
      "webpack": "^1.4.5"
    }
    

    Repeat again - similar to before but change the @<version> suffix, i.e. @4.1.0 in the example below.

    npm view babel-loader@4.1.0 peerDependencies --json
                         ^^^^^^
    

    This prints:

    {
      "babel-core": "^4.7.0",
      "webpack": "^1.4.5"
    }
    

    and so on...


Automate the task:

You may want to consider automating the steps above by creating a node.js script as follows. This utilizes the nodejs execSync() method to shell out the necessary npm commands, however you could change it to utilize the exec() method if you want it to run asynchronously:

script.js

const fs = require('fs');
const sh = require('child_process').execSync;

const PKG_NAME = 'babel-loader'; // <-- Change the package name.

const destFilePath = PKG_NAME.replace(/\//g, '_') + '-peer-deps.json';

const versions = JSON.parse(sh('npm view ' + PKG_NAME + ' versions --json').toString());

const data = versions.map(function(semver) {
  const pkgVersion = PKG_NAME + '@' + semver;
  console.log('> Fetching peerDependencies info for: ' + pkgVersion);

  const peerDeps = sh('npm view ' + pkgVersion +
      ' peerDependencies --json').toString().replace(/[\r\n]/, '');

  return {
    name: pkgVersion,
    peerDependencies: peerDeps ? JSON.parse(peerDeps) : null
  }
});

fs.writeFileSync(destFilePath, JSON.stringify(data, null, 2));

console.log('\nDone !');

Then run the following command to invoke the node.js script:

node ./path/to/script.js

Note: You'll need to redefine the ./path/to/ part as necessary.

Given the value of babel-loader currently assigned to the PKG_NAME variable in script.js you'll see something like the following logged to your console:

> Fetching peerDependencies info for: babel-loader@4.0.0
> Fetching peerDependencies info for: babel-loader@4.1.0
> Fetching peerDependencies info for: babel-loader@4.2.0
...

Upon completion it will write a .json file to disk named babel-loader-peer-deps.json, which includes the following content:

babel-loader-peer-deps.json

[
  {
    "name": "babel-loader@4.0.0",
    "peerDependencies": {
      "babel-core": "^4.0.0",
      "webpack": "^1.4.5"
    }
  },
  {
    "name": "babel-loader@4.1.0",
    "peerDependencies": {
      "babel-core": "^4.7.0",
      "webpack": "^1.4.5"
    }
  },
  {
    "name": "babel-loader@4.2.0",
    "peerDependencies": {
      "babel-core": "^4.7.0",
      "webpack": "^1.4.5"
    }
  },
  ...
]

EDIT: Reducing the number of https GET requests

If you're wanting to reduce the number of https GET requests to just one then I suggest utilizing the nodejs builtin https.get() to fetch JSON data from the https://registry.npmjs.org/ endpoint.

This example gist below will be much faster.

get-peer-deps.js

const fs = require('fs');
const path = require('path');
const https = require('https');

const pkgName = process.argv[2];
const myName = path.basename(__filename);

// Check the package name has been provided
if (!pkgName) {
  console.log('\x1b[40;37m%s\x1b[0m \x1b[40;31m%s\x1b[0m', myName,
      'ERR!', 'Missing package name argument');
  process.exit(1);
}

const fileName = pkgName.replace(/\//g, '_') + '-peer-deps.json';
const destFilePath = path.join(path.dirname(__filename), fileName);
const endPoint = 'https://registry.npmjs.org/' + encodeURIComponent(pkgName);

// Request JSON from npm registry endpoint.
https.get(endPoint, function(resuest) {
  console.log('> Fetching peerDependencies info for: %s...', pkgName);
  var response = '';

  resuest.on('data', function(chunk) {
    response += chunk;
  });

  resuest.on('end', function() {
    processJsonResponse(response);
  });

}).on('error', function(err) {
  console.error(err);
});

/**
 * Processes the JSON response to extract the necessary metadata and saves
 * resultant JSOn to disk.
 * @param {String} data - The JSON response from the npm registry.
 */
function processJsonResponse(data) {
  const versions = JSON.parse(data).versions;
  const semvers = Object.keys(versions);

  const peerDepsInfo = semvers.map(function(semver) {
    const current = versions[semver];
    return {
      name: current.name + '@' + current.version,
      peerDependencies: current.peerDependencies || null
    };
  });

  fs.writeFile(destFilePath, JSON.stringify(peerDepsInfo, null, 2), function(err) {
    if(err) {
      return console.log(err);
    }
    console.log('> Done. Saved result to:\n  %s', destFilePath);
  });
}

Usage:

Then run the following command via your command line tool to invoke get-peer-deps.js:

node ./path/to/get-peer-deps.js babel-loader
                                ^^^^^^^^^^^^

Note: When invoking get-peer-deps.js it's necessary to provide the package name as an argument. In the example above we pass in babel-loader. This can be replaced with whichever valid package name you prefer. For example in the next example we pass in @angular/forms:

node ./path/to/get-peer-deps.js @angular/forms
                                ^^^^^^^^^^^^^^

The resultant .json file will be formatted as described previously and will be save to the same directory where get-peer-deps.js resides.

Related