Node.js: Get (absolute) root path of installed npm package

前端 未结 2 1369
佛祖请我去吃肉
佛祖请我去吃肉 2021-01-12 05:54

Task

I\'m looking for an universal way to get the (absolute) root path of an installed npm package in Node.js.

Problem

I know about require

相关标签:
2条回答
  • 2021-01-12 06:34

    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?).

    0 讨论(0)
  • 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());
            });
        };
    }
    
    0 讨论(0)
提交回复
热议问题