NodeJS 检测ES模块是否从节点中的命令行运行

laximzn5  于 2023-01-20  发布在  Node.js
关注(0)|答案(6)|浏览(112)

在Node中使用CommonJS模块时,可以使用require.main === module从命令行检测是否正在运行脚本。
在Node中使用ES模块(带有--experimental-modules标志)时,检测是否正在从命令行运行脚本的等效方法是什么?

dohp0rv5

dohp0rv51#

用途

if (import.meta.url === `file://${process.argv[1]}`) {
  // module was not imported but called directly
}

有关详细信息,请参见import.meta上的MDN文档。

更新日期:2021年9月27日

可能更健壮,但涉及额外的导入(通过Rich Harris

import {pathToFileURL} from 'url'

if (import.meta.url === pathToFileURL(process.argv[1]).href) {
  // module was not imported but called directly
}
os8fio9y

os8fio9y2#

目前还没有(它仍处于实验阶段!)尽管主流观点认为这样的检查无论如何都是一个不好的做法,您应该为库和可执行文件提供单独的脚本,there is an idea为此提供一个布尔值import.meta.main属性。

x8goxv8g

x8goxv8g3#

其他答案也很接近,但对于package.json文件中的bin属性所公开的相当典型的用例cli脚本来说,可能会漏掉一些。
这些脚本将在node_modules/.bin文件夹中进行符号链接。这些脚本可以通过npx调用,也可以作为package.json中的scripts-对象中定义的脚本调用。在这种情况下,process.argv[1]将是符号链接,而不是import.meta.url引用的实际文件
此外,我们需要将文件路径转换为实际的file://-url,否则它将无法在不同的平台上正常工作。

import { realpathSync } from "fs";
import { pathToFileURL } from "url";

function wasCalledAsScript() {
    // We use realpathSync to resolve symlinks, as cli scripts will often
    // be executed from symlinks in the `node_modules/.bin`-folder
    const realPath = realpathSync(process.argv[1]);

    // Convert the file-path to a file-url before comparing it
    const realPathAsUrl = pathToFileURL(realPath).href;
  
    return import.meta.url === realPathAsUrl;
}

if (wasCalledAsScript()) {
  // module was executed and not imported by another file.
}

我本来想把这个作为对公认答案的评论,但显然我不允许用一个新的帐户发表评论。

3ks5zfa0

3ks5zfa04#

module全局变量将在CommonJS中定义,但在ES模块中根本不存在。是的,这是不一致的,ES模块是 * 不 * 有module变量的东西。
可以通过查看typeof v是否为字符串(不是值!)'undefined'来检查未定义的变量。
这就变成了:

const inCommonJs = typeof module !== 'undefined';

console.log(`inCommonJs = ${inCommonJs}`);

如果我们将该代码放入.cjs.mjs文件中,我们将得到正确的答案:

$ node foo.mjs
inCommonJs = false
$ cp foo.mjs foo.cjs
$ node foo.cjs
inCommonJs = true
628mspwn

628mspwn5#

我喜欢import.meta.url ===file://${process.argv[1]}``,但是它在Windows的bash shell中不起作用。这是一个只检查基本名称的替代方法:

const runningAsScript = import.meta.url.endsWith(path.basename(process.argv[1]));
atmip9wb

atmip9wb6#

现在似乎有一种记录在案的方法可以做到这一点:

if (require.main === module) {
    console.log('executed directly');
    . . .
}

相关问题