使用--experimental-modules标志时,节点中__dirname的替代方法

Amy*_*eum 38 node.js ecmascript-6 es6-modules

--experimental-modules在运行节点应用程序时使用该标志以使用ES6模块.

但是,当我使用此标志时,元变量__dirname不可用.是否有另一种方法可以获得__dirname与此模式兼容的相同字符串?

GOT*_*O 0 41

在Node.js 10中,有一种不需要创建多个文件的替代方案:

import { dirname } from 'path';
import { fileURLToPath } from 'url';

const __dirname = dirname(fileURLToPath(import.meta.url));
Run Code Online (Sandbox Code Playgroud)

  • @DanDascalescu `__dirname` 不是进程目录路径,它是当前模块目录的路径。 (15认同)
  • 这个解决方案相对于 [`path.dirname(process.argv[1])`](/sf/answers/3767888561/) 有什么优势? (3认同)
  • 这在 Linux 下对我不起作用。还有其他人遇到过同样的问题吗? (3认同)
  • 在 Windows 上(未在其他平台上测试过),此解决方案会在路径中产生一个前导“\”字符,这会破坏很多内容。我可以修剪它来解决问题,但我不确定该解决方案是否跨平台兼容。 (3认同)
  • 也许它不需要创建多个文件,但它确实需要在每个想要使用“__dirname”的模块中复制这三行代码。那太丑了。对于“--experimental-modules”真的没有更好的解决方案吗? (3认同)
  • 如果不喜欢不必要的依赖项,也不关心像 Bob 这样的旧版操作系统... `import.meta.url.split('/').slice(2,-1).join('/') `。这不适用于处理不受信任的内容(例如 zip 文件)...甚至不要尝试 (3认同)
  • 很酷,但是如何设置 WebStrom IDE 来理解这个语法呢? (2认同)
  • 我还注意到 Windows 添加了一个前导 \ 它看起来像 `path.dirname(process.argv[1])` _is_ 给我当前模块目录的路径。 (2认同)

rob*_*lep 19

有关于公开这些变量的建议import.meta,但是现在,你需要一个我在这里找到的hacky解决方法:

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

// use.mjs
import expose from './expose.js';
const {__dirname} = expose;
Run Code Online (Sandbox Code Playgroud)

  • 大括号步骤不是必需的,除此之外,真的不推荐使用这种方法,因为如果`./expose.js` 在另一个目录中,它将给出该目录的值`__dirname`,而不是当前脚本的值......请参阅我的回答 (2认同)

Mik*_*ant 18

对于节点 10.12 +...

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

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

const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
Run Code Online (Sandbox Code Playgroud)

好消息是,您离支持 CommonJS 模块的 require() 也只有两行代码了。为此,您将添加:

import { createRequireFromPath } from 'module';
const require = createRequireFromPath(__filename); 
Run Code Online (Sandbox Code Playgroud)

  • 没有 `createRequireFromPath` - 它是 `createRequire` 并接受“import.meta.url”作为参数 (2认同)
  • 与上面的答案不同,当您需要 `__dirname` 时,这适用于 Windows,如果您正在调整一堆已经使用 `path.join(__dirname, ...)` 的代码,则可能会这样做。 (2认同)

Vac*_*tny 14

2021年最规范的方式

import { URL } from 'url'; // in Browser, the URL in native accessible on window

const __filename = new URL('', import.meta.url).pathname;
// Will contain trailing slash
const __dirname = new URL('.', import.meta.url).pathname;
Run Code Online (Sandbox Code Playgroud)

忘记join从当前文件创建路径,只需使用URL

const pathToAdjacentFooFile = new URL('./foo.txt', import.meta.url).pathname;
const pathToUpperBarFile = new URL('../bar.json', import.mera.url).pathname;
Run Code Online (Sandbox Code Playgroud)

  • 在 Windows 中不起作用 - 导致类似“/C:/...”的结果 - 更好地使用 fileUrlToPath:https://nodejs.org/api/url.html#url_url_fileurltopath_url (38认同)
  • 甚至不需要导入“URL”,因为它[可作为全局](https://nodejs.org/api/url.html#class-url)。 (8认同)

Gui*_*nto 11

在大多数情况下,使用 Node.js 原生的东西(带有 ES 模块),而不是外部资源,在大多数情况下__filename__dirname使用完全没有必要。大多数(如果不是全部)用于读取(流)的本机方法都支持new URL+ import.meta.url正如官方文档本身所建议的那样

正如您在方法的描述中所见,该path参数显示了支持的格式,其中包括<URL>, 示例:

方法 路径参数支持
fs.readFile(path[, options], callback) <string>, <Buffer>, <URL>,<integer>
fs.readFileSync(path[, options]) <string>, <Buffer>, <URL>,<integer>
fs.readdir(path[, options], callback) <string>, <Buffer>,<URL>
fs.readdirSync(path[, options]) <string>, <Buffer>, <URL>,<integer>
fsPromises.readdir(path[, options]) <string>, <Buffer>,<URL>
fsPromises.readFile(path[, options]) <string>, <Buffer>, <URL>,<FileHandle>

所以有了new URL('<path or file>', import.meta.url)它就解决了,你不需要处理字符串和创建稍后连接的变量。

例子:

查看如何在不需要或任何解决方法的情况下读取与脚本相同级别的文件__filename

import { readFileSync } from 'fs';

const output = readFileSync(new URL('./foo.txt', import.meta.url));

console.log(output.toString());
Run Code Online (Sandbox Code Playgroud)

列出脚本目录中的所有文件:

import { readdirSync } from 'fs';

readdirSync(new URL('./', import.meta.url)).forEach((dirContent) => {
  console.log(dirContent);
});
Run Code Online (Sandbox Code Playgroud)

注意:在示例中,我使用同步函数只是为了更容易复制和执行。

如果打算制作一个依赖第三方的“自己的日志”(或类似的东西),那么手动完成一些事情是值得的,但在语言和 Node.js 中这是没有必要的,ESMODULES完全有可能没有依赖于__filename两者之一__dirname,因为使用new URLwith 的本机资源已经解决了它。


请注意,如果您有兴趣在require战略时期使用类似的东西并且需要来自主脚本的绝对路径,您可以使用module.createRequire(filename)(仅限 Node.js v12.2.0 +)结合以import.meta.url在当前脚本级别以外的级别加载脚本,因为这已经有助于避免需要__dirname,使用import.meta.urlwith的示例module.createRequire

import { createRequire } from 'module';

const require = createRequire(import.meta.url);

// foo-bar.js is a CommonJS module.
const fooBar = require('./foo-bar');

fooBar();
Run Code Online (Sandbox Code Playgroud)

来源foo-bar.js

module.exports = () => {
    console.log('hello world!');
};
Run Code Online (Sandbox Code Playgroud)

这类似于在没有“ECMAScript 模块”的情况下使用:

const fooBar = require('./foo-bar');
Run Code Online (Sandbox Code Playgroud)

  • @jacobq 这与接受的答案不同,相反,它说事实上文件名和目录名在大多数情况下不是必需的,因为所有本机 NodeJS API 都识别“URL”类。我本打算以具体方式指出有关使用“fileURLToPath”的一些问题,但在我看来,答案中的解释足以理解我们不需要文件名和目录名。感谢您的评论。 (2认同)
  • 这是一个非常灰色的区域。使用 URL 并受节点支持的原因是为了尽可能严格地遵守标准化 ESM 规范。话虽这么说,你不能取消节点中的 possix 样式文件路径或 windows 样式文件路径。特别是在使用 CLI 或打印文件树(这些是我需要它们的用例)时,我确信还有更多。Node 必须能够同时使用两者。这是拥有支持 ESM 的服务器端 JS RTE 的唯一方法。关闭:文件路径名在节点中是不可或缺的,使用它们没有任何缺点。 (2认同)

per*_*507 9

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

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


// do not use the following code which is bad for CJK characters
const __filename = new URL('', import.meta.url).pathname;
Run Code Online (Sandbox Code Playgroud)


小智 8

从节点v21.2.020.11.0开始

import.meta.dirname可以替代__dirname

import.meta.filename可以替代__filename

const __dirname = import.meta.dirname;
const __filename = import.meta.filename;
Run Code Online (Sandbox Code Playgroud)


vde*_*nne 7

我制作了这个模块es-dirname,它将返回当前脚本目录名。

import dirname from 'es-dirname'

console.log(dirname())
Run Code Online (Sandbox Code Playgroud)

它适用于WindowsLinux上的CommonJs脚本和ES 模块

如果脚本在我的项目中运行到目前为止出现错误,则在那里打开一个问题,但在其他一些情况下它可能会失败。因此不要在生产环境中使用它。这是一个临时解决方案,因为我相信 Node.js 团队将在不久的将来发布一种强大的方法来做到这一点。


Iva*_*ele 7

由于其他答案虽然有用,但不涵盖跨平台情况(Windows POSIX)和/或除__dirnameor之外的路径解析__filename,并且在各处重复此类代码有点冗长:

import { dirname, join } from 'path'
import { fileURLToPath } from 'url'

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

const somePath = join(__dirname, '../some-dir-or-some-file')
Run Code Online (Sandbox Code Playgroud)

我刚刚发布了一个名为esm-path的 NPM 包来帮助完成这种重复性任务,希望它对其他人也有用。

它已记录,但这里如何使用它:

import { getAbsolutePath } from 'esm-path'

const currentDirectoryPath = getAbsolutePath(import.meta.url)
console.log(currentDirectoryPath)

const parentDirectoryPath = getAbsolutePath(import.meta.url, '..')
console.log(parentDirectoryPath)

// Adapt the relative path to your case
const packageJsonFilePath = getAbsolutePath(import.meta.url, '../package.json')
console.log(packageJsonFilePath)

// Adapt the relative path to your case
const packageJsonFilePath = getAbsolutePath(import.meta.url, '..' , 'package.json')
console.log(packageJsonFilePath)
Run Code Online (Sandbox Code Playgroud)


Ban*_*cid 6

我用了:

import path from 'path';

const __dirname = path.resolve(path.dirname(decodeURI(new URL(import.meta.url).pathname)));
Run Code Online (Sandbox Code Playgroud)

decodeURI 很重要:测试系统路径中的已用空间和其他内容。

path.resolve() 处理相对网址。


小智 5

import path from 'path';
const __dirname = path.join(path.dirname(decodeURI(new URL(import.meta.url).pathname))).replace(/^\\([A-Z]:\\)/, "$1");
Run Code Online (Sandbox Code Playgroud)

此代码也适用于 Windows。(替换在其他平台上是安全的,因为path.join仅在 Windows 上返回反斜杠分隔符)

  • 我为您的答案找到了一个简短的修复,可以在 Windows 上工作(已确认工作): `[现有代码].replace(/^\\([AZ]:\\)/, "$1");` 添加为编辑建议。 (2认同)

Ron*_*ton 5

这有效:

import path from 'node:path';
import url from 'node:url';

const DIR_NAME = path.dirname(url.fileURLToPath(import.meta.url));
Run Code Online (Sandbox Code Playgroud)