如何检测我的Node.JS文件是否使用SH调用:node path-to-file
或JS:require('path-to-file')
?
这是与我之前在Perl中的问题相同的Node.JS:How can I run my Perl script only if it wasn't loaded with require?
答案 0 :(得分:388)
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
答案 1 :(得分:76)
答案 2 :(得分:4)
我对解释中使用的术语感到有些困惑。所以我不得不做几次快速测试。
我发现这些产生了相同的结果:
var isCLI = !module.parent;
var isCLI = require.main === module;
对于其他困惑的人(并直接回答问题):
var isCLI = require.main === module;
var wasRequired = !isCLI;
答案 3 :(得分:2)
就像在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
}
不可能忘记: - )
答案 4 :(得分:2)
如果使用的是ES6模块,请尝试以下操作:
if (process.mainModule.filename === __filename) {
console.log('running as main module')
}
答案 5 :(得分:2)
对于使用 ES 模块(和 Node 10.12+)的用户,您可以使用 import.meta.url
:
import { fileURLToPath } from 'url'
const isRunningDirectlyViaCLI = process.argv[1] === fileURLToPath(import.meta.url)
诸如 require.main
、module.parent
和 __dirname
/__filename
aren’t available in ESM 之类的东西。
注意:如果使用 ESLint,它可能会阻塞这种语法,在这种情况下,您需要 update to ESLint ^7.2.0
并将 ecmaVersion
变为 11
(2020
)。
答案 6 :(得分:1)
首先,让我们更好地定义问题。我的假设是,您真正要寻找的是您的脚本是否拥有 process.argv
(即您的脚本是否负责处理 process.argv
)。考虑到这一假设,下面的代码和测试是准确的。
module.parent
工作得很好,但有充分的理由不推荐使用(一个模块可能有多个父级,在这种情况下 module.parent
仅代表第一个父级),因此使用以下面向未来的条件来涵盖所有情况:
if (
typeof process === 'object' && process && process.argv
&& (
(
typeof module === 'object' && module
&& (
!module.parent
|| require.main === module
|| (process.mainModule && process.mainModule.filename === __filename)
|| (__filename === "[stdin]" && __dirname === ".")
)
)
|| (
typeof document === "object"
&& (function() {
var scripts = document.getElementsByTagName("script");
try { // in case we are in a special environment without path
var normalize = require("path").normalize;
for (var i=0,len=scripts.length|0; i < len; i=i+1|0)
if (normalize(scripts[i].src.replace(/^file:/i,"")) === __filename)
return true;
} catch(e) {}
})()
)
)
) {
// this module is top-level and invoked directly by the CLI
console.log("Invoked from CLI");
} else {
console.log("Not invoked from CLI");
}
它在以下所有情况下的所有脚本中都能正常工作,并且从不抛出任何错误†:
require('./main.js')
)nodejs cli.js
)nodejs -r main.js cli.js
)cat cli.js | nodejs
)cat cli.js | nodejs -r main.js
)new Worker('./worker.js')
)eval
教育工作者(例如new Worker('if (<test for CLI>) ...', {eval: true})
)nodejs --experimental-modules cli-es6.js
)nodejs --experimental-modules -r main-es6.js cli-es6.js
)cat cli-es6.js | nodejs --experimental-modules
)cat cli-es6.js | nodejs --experimental-modules -r main-es6.js
)process.argv
)<script>
标签加载的所有模块都被视为 CLI) is 不起作用的唯一情况是您预加载顶级脚本(例如 nodejs -r cli.js cli.js
)。这个问题不能通过管道(例如 cat cli.js | nodejs -r cli.js
)来解决,因为它会执行脚本两次(一次作为必需的模块,一次作为顶层)。我不相信有任何可能的解决方法,因为无法从预加载的脚本中知道主脚本是什么。
† 从理论上讲,错误可能会从对象的 getter 内部抛出(例如,如果有人疯狂到做 Object.defineProperty(globalThis, "process", { get(){throw 0} });
),但是这在默认情况下永远不会发生在任何环境中的代码片段中使用的属性。