为了使用ES6模块,我在运行Node应用程序时使用了——experimental-modules标志。

然而,当我使用这个标志时,元变量__dirname不可用。是否有另一种方法来获得与此模式兼容的存储在__dirname中的相同字符串?


当前回答

我使用这个选项,因为路径以file://开始,只需删除该部分。

const __filename = import.meta.url.slice(7);
const __dirname = import.meta.url.slice(7, import.meta.url.lastIndexOf("/"));

其他回答

我还在NPM上发布了一个名为cross-dirname的包(从es-dirname派生而来)。该包使用Node.js (ESM和CJS), Deno和GJS进行测试。

例子:

import dirname from 'cross-dirname'

console.log(dirname())

节点10.12 +…

假设你正在从一个模块中工作,这个解决方案应该可以工作,并且还提供了__filename支持

import path from 'node:path';
import { fileURLToPath } from 'node:url';

const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);

好的事情是,你也只需要两行代码就可以支持CommonJS模块的require()。为此,你可以补充:

import { createRequireFromPath } from 'module';
const require = createRequireFromPath(__filename); 

已经有人提议通过导入公开这些变量。meta,但现在,你需要一个hack的工作,我在这里发现:

// expose.js
module.exports = {__dirname};

// use.mjs
import expose from './expose.js';
const {__dirname} = expose;

您可以使用来自新的Error()的堆栈。这个错误不需要被抛出,也不会停止程序的执行。堆栈的第一行始终是错误及其消息,第二行是调用错误的文件。

由于这是一个方法(可能在util.js文件中),getDirname()调用的实际位置实际上是错误堆栈的第三行。

export const getDirname = () => {
    // get the stack
    const { stack } = new Error();
    // get the third line (the original invoker)
    const invokeFileLine = stack.split(`\n`)[2];
    // match the file URL from file://(.+)/ and get the first capturing group
    //     the (.+) is a greedy quantifier and will make the RegExp expand to the largest match
    const __dirname = invokeFileLine.match(/file:\/\/(.+)\//)[1];
    return __dirname;
};

我创建了这个模块es-dirname,它将返回当前脚本dirname。

import dirname from 'es-dirname'

console.log(dirname())

它既适用于CommonJs脚本,也适用于Windows和Linux上的ES模块。

打开一个问题,如果有一个错误,因为脚本一直在我的项目中工作,但在其他一些情况下可能会失败。因此,不要在生产环境中使用它。这是一个临时的解决方案,我相信Node.js团队在不久的将来会发布一个健壮的方法来实现它。