51

I need to check whether "mocha" is installed, before running it. I came up with the following code:

try {
    var mocha = require("mocha");
} catch(e) {
    console.error(e.message);
    console.error("Mocha is probably not found. Try running `npm install mocha`.");
    process.exit(e.code);
}

I dont like the idea to catch an exception. Is there a better way?

AndreyM
  • 1,333
  • 2
  • 11
  • 23
  • 2
    I've answered but now I've noticed the "globally" word. By "globally" do you mean a module installed with `-g` option in `npm`? I.e. `npm install -g mocha`? Edit: AFAIK `require` will not find modules installed with `-g` option. – Jan Święcki Mar 08 '13 at 21:45

2 Answers2

85

You should use require.resolve() instead of require(). require will load the library if found, but require.resolve() will not, it will return the file name of the module.

See the documentation for require.resolve

try {
    console.log(require.resolve("mocha"));
} catch(e) {
    console.error("Mocha is not found");
    process.exit(e.code);
}

require.resolve() does throw error if module is not found so you have to handle it.

user568109
  • 43,824
  • 15
  • 87
  • 118
2

module.paths stores array of search paths for require. Search paths are relative to the current module from where require is called. So:

var fs = require("fs");

// checks if module is available to load
var isModuleAvailableSync = function(moduleName)
{
    var ret = false; // return value, boolean
    var dirSeparator = require("path").sep

    // scan each module.paths. If there exists
    // node_modules/moduleName then
    // return true. Otherwise return false.
    module.paths.forEach(function(nodeModulesPath)
    {
        if(fs.existsSync(nodeModulesPath + dirSeparator + moduleName) === true)
        {
            ret = true;
            return false; // break forEach
        }
    });

    return ret;
}

And asynchronous version:

// asynchronous version, calls callback(true) on success
// or callback(false) on failure.
var isModuleAvailable = function(moduleName, callback)
{
    var counter = 0;
    var dirSeparator = require("path").sep

    module.paths.forEach(function(nodeModulesPath)
    {
        var path = nodeModulesPath + dirSeparator + moduleName;
        fs.exists(path, function(exists)
        {
            if(exists)
            {
                callback(true);
            }
            else
            {
                counter++;

                if(counter === module.paths.length)
                {
                    callback(false);
                }
            }
        });
    });
};

Usage:

if( isModuleAvailableSync("mocha") === true )
{
    console.log("yay!");
}

Or:

isModuleAvailable("colors", function(exists)
{
    if(exists)
    {
        console.log("yay!");
    }
    else
    {
        console.log("nay:(");
    }
});

Edit: Note:

  • module.paths is not in the API
  • Documentation states that you can add paths that will be scanned by require but I couldn't make it work (I'm on Windows XP).
Jan Święcki
  • 1,384
  • 1
  • 12
  • 25
  • @AndreyM, just curious to know what you didn't like about this solution and why you marked the accepted answer as one that uses a try/catch hack to determine if a module exists? Thank you. – jmort253 Aug 24 '14 at 06:30
  • I'm assuming because module.paths is not in the API, you didn't go with this. I can see this not being future proof if Node gets changed in some manner, whereas the try/catch strategy, despite being ugly, would be more reliable. Still, this is an awesome answer and solves this without try/catch! +1 :) – jmort253 Aug 24 '14 at 06:35