Node.js 检测是通过require调用还是直接通过命令行调用

Node.js 检测是通过require调用还是直接通过命令行调用,node.js,require,Node.js,Require,如何检测是否使用SH:节点路径到文件或JS:require('path-to-file')调用了Node.JS文件 这是Node.JS,相当于我在Perl中的上一个问题: 请参阅此处的文档:还有另一种略短的方法(未在提及的文档中列出) var runningAsScript=!模块。父模块 我在中概述了关于这一切如何在引擎盖下工作的更多细节。我被解释中使用的术语弄糊涂了。所以我必须做一些快速测试 我发现这些方法产生了相同的结果: var isCLI = !module.parent; var i

如何检测是否使用SH:
节点路径到文件
或JS:
require('path-to-file')
调用了Node.JS文件

这是Node.JS,相当于我在Perl中的上一个问题:


请参阅此处的文档:

还有另一种略短的方法(未在提及的文档中列出)

var runningAsScript=!模块。父模块


我在中概述了关于这一切如何在引擎盖下工作的更多细节。

我被解释中使用的术语弄糊涂了。所以我必须做一些快速测试

我发现这些方法产生了相同的结果:

var isCLI = !module.parent;
var isCLI = require.main === module;
对于其他困惑的人(直接回答问题):


我总是试图回忆如何编写这个该死的代码片段,所以我决定为它创建一个简单的模块。我花了一点时间才让它工作起来,因为访问调用方的模块信息并不简单,但看到如何做到这一点很有趣

因此,我们的想法是调用一个模块,并询问它调用方模块是否是主模块。我们必须找出调用函数的模块。我的第一种方法是接受答案的一种变体:

module.exports = function () {
    return require.main === 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;
};
将其另存为
is main module.js
,现在您可以执行以下操作:

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

if (isMainModule()) {
    console.info("called directly");
} else {
    console.info("required as a module");
}

这更容易记住。

如果您使用的是ES6模块,请尝试以下操作:

if (process.mainModule.filename === __filename) {
  console.log('running as main module')
}

首先,让我们更好地定义问题。我的假设是,您真正需要的是脚本是否拥有
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");
}
if(
进程类型==='object'&&process&&process.argv
&& (
(
模块类型==='object'&&module
&& (
!module.parent
||require.main==模块
||(process.mainModule&&process.mainModule.filename====\uu文件名)
||(文件名===“[stdin]”&&&&&&uu目录名===”)
)
)
|| (
文档类型==“对象”
&&(功能(){
var scripts=document.getElementsByTagName(“脚本”);
尝试{//以防我们处于没有路径的特殊环境中
var normalize=需要(“路径”)。normalize;
对于(变量i=0,len=scripts.length | 0;i
在以下所有情况下,它在所有脚本中都能正常工作,并且从不抛出任何错误†:

  • 需要脚本(e.x.
    require('./main.js')
  • 直接调用脚本(e.x.
    nodejs cli.js
  • 预加载另一个脚本(e.x.
    nodejs-r main.js cli.js
  • 进入节点CLI的管道(e.x.
    cat CLI.js | nodejs
  • 带预加载的管道(e.x.
    cat cli.js | nodejs-r main.js
  • 在workers中(e.x.
    新Worker('./Worker.js')
  • eval
    ed Worker中(e.x.
    new Worker('if()…',{eval:true})
  • ES6模块内部(e.x.
    nodejs——实验模块cli-ES6.js
  • 带预加载的模块(e.x.
    nodejs——实验模块-r main-es6.js cli-es6.js
  • 管道式ES6模块(e.x.
    cat cli-ES6.js | nodejs——实验模块
  • 管道+预加载模块(e.x.
    cat cli-es6.js | nodejs——实验模块-r main-es6.js
  • 在浏览器中(在这种情况下,CLI为false,因为没有
    process.argv
  • 在混合浏览器+服务器环境中(例如ElectronJS,在这种情况下,内联脚本和通过
    标记加载的所有模块都被视为CLI)
is不起作用的唯一情况是预加载顶级脚本(e.x.
nodejs-r cli.js cli.js
)。通过管道(e.x.
cat cli.js | nodejs-r cli.js
)无法解决此问题,因为它会执行两次脚本(一次作为必需模块,一次作为顶级)。我不相信有任何可能的修复方法,因为无法从预加载的脚本中知道主脚本是什么


†理论上,错误可能是从对象的getter内部抛出的(例如,如果有人疯狂地执行
object.defineProperty(globalThis,“process”,{get(){throw 0}});
),但是,在任何环境中,对于代码段中使用的属性,在默认情况下都不会发生这种情况。

对于使用ES模块(和节点10.12+)的用户,可以使用
import.meta.url

从“url”导入{fileURLToPath}
const isRunningDirectlyViaCLI=process.argv[1]==fileURLToPath(import.meta.url)
require.main
module.parent
\uuu dirname
/
\uu filename
这样的东西

注意:如果使用ESLint,它可能会阻塞此语法,在这种情况下,您需要将
ecmaVersion
设置为
11
2020

更多
if (process.mainModule.filename === __filename) {
  console.log('running as main module')
}
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");
}