I\'m looking for an universal way to get the (absolute) root path of an installed npm package in Node.js.
I know about require
Try this:
require.resolve('bootstrap-sass/package.json')
which returns:
path_to_my_project/node_modules/bootstrap-sass/package.json
You can now get rid of 'package.json' path suffix such as:
var path = require('path') // npm install path
var bootstrapPath = path.dirname(require.resolve('bootstrap-sass/package.json'))
Since it is mandatory for every package to contain package.json
file, this should always work (see What is a package?).
you don't need to look up the desired package's package.json file, or even refer to its location explicitly.
moreover, you shouldn't do it, as there is no sure way to know where will that npm package end up in npm's tree (which is why you turned to require.resolve
for help).
instead, you can query the npm API (or CLI) by using npm ls with the --parseable flag, which will:
Show parseable output instead of tree view.
for example:
$ npm ls my-dep -p
… will output something like this:
/Users/me/my-project/node_modules/my-dep
you should be aware that this command can output some irrelevant errors as well to stderr (e.g. about extraneous installations) — to work around this, activate the --silent
flag (see loglevel in the docs):
$ npm ls my-dep -ps
this command can be integrated into your code using a child process, in which case it's preferred to run the command without the --silent
flag to allow capturing any error.
if an error is catched, you can then decide whether its fatal or not (e.g. the aforementioned error about extraneous package should be ignored).
so usage of the CLI via a child process can look like this:
const exec = require('child_process').exec;
const packageName = 'my-dep';
exec(`npm ls ${packageName} --parseable`, (err, stdout, stderr) => {
if (!err) {
console.log(stdout.trim()); // -> /Users/me/my-project/node_modules/my-dep
}
});
this can then be used (along with some closure magic…) in an async flow, e.g.:
const exec = require('child_process').exec;
const async = require('async');
async.waterfall([
npmPackagePathResolver('my-dep'),
(packagePath, callback) => {
console.log(packagePath) // -> /Users/me/my-project/node_modules/my-dep
callback();
}
], (err, results) => console.log('all done!'));
function npmPackagePathResolver(packageName) {
return (callback) => {
exec(`npm ls ${packageName} --parseable`, (err, stdout, stderr) => {
callback(err, stdout.trim());
});
};
}