2013-03-08 63 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