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

Мне не нравится идея поймать исключение. Есть ли способ лучше?


person AndreyM    schedule 08.03.2013    source источник
comment
Я ответил, но теперь я заметил глобальное слово. Под глобально вы подразумеваете модуль, установленный с опцией -g в npm? т.е. npm install -g mocha? Изменить: AFAIK require не найдет модули, установленные с опцией -g.   -  person Jan Święcki    schedule 09.03.2013


Ответы (2)


Вы должны использовать require.resolve() вместо require(). require загрузит библиотеку, если она найдена, а require.resolve() нет, она вернет имя файла модуля.

См. документацию для require.resolve.

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

require.resolve() выдает ошибку, если модуль не найден, поэтому вам нужно с этим справиться.

person user568109    schedule 08.03.2013
comment
require.resolve по-прежнему выдает ошибку — этого я и пытаюсь избежать — ловить исключение. - person AndreyM; 09.03.2013
comment
Принятие ответа, поскольку другие решения никоим образом не чище. - person AndreyM; 09.03.2013
comment
да, но будет ли это также искать глобально установленные модули? Я хочу проверить, установлен ли модуль локально. - person Alexander Mills; 12.04.2016
comment
@AlexMills Лучший способ сделать это — использовать npm ls, где global по умолчанию имеет значение false. - person user568109; 12.04.2016

module.paths хранит массив путей поиска для require. Пути поиска относятся к текущему модулю, из которого вызывается 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!");
}

Or:

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

Изменить: Примечание:

  • module.paths отсутствует в API
  • В документации указано, что вы можете добавлять пути, которые будут сканироваться require, но я не смог это работает (у меня Windows XP).
person Jan Święcki    schedule 08.03.2013
comment
@AndreyM, просто любопытно узнать, что вам не понравилось в этом решении и почему вы пометили принятый ответ как тот, который использует взлом try/catch, чтобы определить, существует ли модуль? Спасибо. - person jmort253; 24.08.2014
comment
Я предполагаю, что, поскольку module.paths отсутствует в API, вы этого не сделали. Я вижу, что это не будет доказательством будущего, если Node каким-то образом изменится, тогда как стратегия try/catch, несмотря на то, что она уродлива, была бы более надежной. Тем не менее, это отличный ответ, и он решает эту проблему без попытки/поймать! +1 :) - person jmort253; 24.08.2014