2013-03-08 65 views
32

我需要在运行之前检查“mocha”是否已安装。我想出了以下代码:Node.js - 检查是否安装了模块而没有实际需要它

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); 
} 

我不喜欢这个想法来捕捉异常。有没有更好的办法?

+2

我已经回答,但现在我已经注意到了“全球化”一词。对于“全局”,你是指在'npm'中安装了'-g'选项的模块?即'npm install -g mocha'?编辑:AFAIK'require'不会找到使用'-g'选项安装的模块。 – 2013-03-08 21:45:06

回答

55

您应该使用require.resolve()代替require()require如果找到会加载库,但require.resolve()不会,它会返回模块的文件名。

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(),如果找不到模块,所以你必须处理它抛出的错误。

+9

'require.resolve'仍然会抛出一个错误 - 这正是我想要避免的 - 捕获一个例外。 – AndreyM 2013-03-08 21:04:19

+3

接受答案,因为其他解决方案不以任何方式更清洁。 – AndreyM 2013-03-08 22:32:22

+0

是的,但是这也会寻找全球安装的模块吗?我想检查一个模块是否安装在本地。 – 2016-04-12 05:44:56

0

我认为你可以在这里制作一个技巧。就node.js可以使用shell命令而言,使用“npm list --global”列出所有已安装的模块,并检查是否需要提供。

var sys = require('sys') 
var exec = require('child_process').exec; 
var child; 

// execute command 
child = exec("npm list --global", function (error, stdout, stderr) { 
    // TODO: search in stdout 
    if (error !== null) { 
     console.log('exec error: ' + error); 
    } 
}); 
+2

我不认为它是一个更清洁的解决方案。 – AndreyM 2013-03-08 20:47:48

+0

至少你可以放心,在所需的库中的东西不会抛出异常 - 导致断言该模块未安装 – Stormherz 2013-03-08 21:10:18

+1

摩卡可能安装在本地,这是好的 – AndreyM 2013-03-08 21:16:42

2

module.pathsrequire存储搜索路径数组。搜索路径与调用require的当前模块有关。所以:

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; 
} 

和异步版本:

// 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); 
       } 
      } 
     }); 
    }); 
}; 

用法:

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

或者:

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

编辑:注:

  • module.paths不在API
  • 文档states,您可以添加将由require扫描路径,但我不能让它工作(我在Windows XP)。
+0

@AndreyM,只是想知道你不喜欢这个解决方案,以及为什么你将接受的答案标记为使用try/catch hack来确定模块是否存在?谢谢。 – jmort253 2014-08-24 06:30:37

+0

我假设,因为module.paths不在API中,你没有去与此。如果Node以某种方式改变,我可以看到这不是未来的证明,而try/catch策略尽管很丑,但会更可靠。尽管如此,这是一个非常棒的答案,并且在没有try/catch的情况下解决这个问题! +1 :) – jmort253 2014-08-24 06:35:47