Обнаружить, если вызывается через require или напрямую из командной строки

299

Как я могу определить , был ли мой файл Node.js вызывается с помощью SH: node path-to-fileили JS: require('path-to-file')?

Это Node.JS, эквивалентный моему предыдущему вопросу в Perl: Как я могу запустить свой Perl-скрипт, только если он не был загружен с require?

Брайан Филд
источник
4
Возможный дубликат node.js, эквивалентный Python, если __name__ == '__main__'
GingerPlusPlus

Ответы:

472
if (require.main === module) {
    console.log('called directly');
} else {
    console.log('required as a module');
}

См. Документацию для этого здесь: https://nodejs.org/docs/latest/api/modules.html#modules_accessing_the_main_module

nicolaskruchten
источник
3
Есть ли способ обойти это? У меня есть код (который я не контролирую), который делает это, но мне нужно обязательно () его и действовать так, как если бы он был вызван напрямую. По сути, мне нужно обмануть то, что использует этот тест, чтобы думать, что он был вызван напрямую.
Кевин
2
@Kevin Я не знаю, как это сделать require(), но вы можете сделать это, либо импортировав файл, затем запустив evalего, либо запустивrequire('child_process').exec('node the_file.js')
MalcolmOcean
При использовании модулей ES с Node.js вы можете использовать es-mainпакет, чтобы проверить, был ли модуль запущен напрямую.
Тим Шауб
91

Есть другой, немного более короткий путь (не обозначенный в упомянутых документах).

var runningAsScript = !module.parent;

Я обрисовал в общих чертах больше деталей о том, как это все работает под капотом в этом сообщении в блоге .

Торстен Лоренц
источник
+1, мне это нравится больше, но я буду колебаться, прежде чем переключать принятые ответы. :)
Брайан Филд
8
Как я уже указывал, официально задокументированный способ описан @nicolaskruchten. Это всего лишь альтернатива, не нужно менять принятый ответ. Оба работают.
Торстен Лоренц
10
Я должен был использовать это, а не документированный способ - документированный способ работает, например. node script.jsно нет cat script.js | node. Этот способ работает для обоих.
Тим Мэлоун
9

Меня немного смутила терминология, использованная в объяснениях. Поэтому мне пришлось сделать пару быстрых тестов.

Я обнаружил, что они дают одинаковые результаты:

var isCLI = !module.parent;
var isCLI = require.main === module;

А для других сбитых с толку людей (и ответить на вопрос напрямую):

var isCLI = require.main === module;
var wasRequired = !isCLI;
боб
источник
5

Как и в Python, я всегда пытаюсь вспомнить, как написать этот проклятый фрагмент кода. Поэтому я решил создать для него простой модуль. Мне потребовалось немного времени для разработки, так как доступ к информации модуля вызывающего абонента не прост, но было интересно посмотреть, как это можно сделать.

Таким образом, идея состоит в том, чтобы вызвать модуль и спросить его, является ли модуль вызывающего абонента основным. Мы должны выяснить модуль функции звонящего. Мой первый подход был вариантом принятого ответа:

module.exports = function () {
    return require.main === module.parent;
};

Но это не гарантирует работу. module.parentуказывает на модуль, который загрузил нас в память, а не тот, который нам звонит. Если это был модуль вызывающего, который загрузил этот вспомогательный модуль в память, это нормально. Но если это не так, мы беспомощны. Поэтому нам нужно попробовать что-то еще. Моим решением было сгенерировать трассировку стека и получить оттуда имя модуля вызывающего:

module.exports = function () {
    // generate a stack trace
    const stack = (new Error()).stack;
    // the third line refers to our caller
    const stackLine = stack.split("\n")[2];
    // extract the module name from that line
    const callerModuleName = /\((.*):\d+:\d+\)$/.exec(stackLine)[1];

    return require.main.filename === callerModuleName;
};

Теперь мы можем сделать:

if (require("./is-main-module")()) {  // notice the `()` at the end
    // do something
} else {
    // do something else
}

Или более читабельно:

const isMainModule = require("./is-main-module");

if (isMainModule()) {
    // do something
} else {
    // do something else
}

Невозможно забыть :-)

Лусио Пайва
источник
2
Очень круто. Мне нравится, когда общие фрагменты кода сокращены до одного имени. Небольшая корректировка:return require.main /*this is undefined if we started node interactively*/ && require.main.filename === callerModuleName;
masterxilo
4

Попробуйте это, если вы используете модули ES6:

if (process.mainModule.filename === __filename) {
  console.log('running as main module')
}
Кэбот
источник
2
дерьмо, мой process.mainModuleестьundefined
datdinhquoc
1
GHOSHHHH, мне нужно проверить это в моем файле
.mjs