Node.js - check if module is installed without actually requiring it

node.jsModuleRequire

node.js Problem Overview


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?

node.js Solutions


Solution 1 - node.js

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.

Solution 2 - node.js

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

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
QuestionAndreyMView Question on Stackoverflow
Solution 1 - node.jsuser568109View Answer on Stackoverflow
Solution 2 - node.jsJan ŚwięckiView Answer on Stackoverflow