__dirname is not defined 是 esm 的正常行为,因 esm 标准未定义 __dirname 和 __filename,需用 import.meta.url 配合 fileurltopath 和 dirname 替代,或直接用 path.resolve(fileurltopath(import.meta.url), ...) 构建路径。

__dirname is not defined 是 ESM 的正常行为,不是 bug
你在 .mjs 文件里或 package.json 里写了 "type": "module",Node.js 就会按 ESM 规范加载——这时 __dirname 和 __filename 确实不存在。这不是 VSCode 的问题,也不是配置漏了,是 ESM 标准本身没定义这两个变量。CommonJS 里它们是 Node.js 运行时注入的“便利糖”,ESM 要求你显式获取路径,更规范也更可预测。
用 import.meta.url + fileURLToPath 替代 __dirname
这是 Node.js 官方推荐、跨版本兼容(v12.20+)的写法,必须成对使用:
-
import.meta.url是 ESM 下唯一可靠的当前模块 URL(形如file:///path/to/index.mjs) -
fileURLToPath()把它转成文件路径字符串(/path/to/index.mjs) -
dirname()再从该路径提取目录名(/path/to)
示例:
import { fileURLToPath } from 'url';
import { dirname } from 'path';
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
// ✅ 现在可以安全拼接路径
const configPath = path.join(__dirname, 'config.json');
VSCode 里别硬套 CommonJS 写法,小心调试器不识别
如果你在 launch.json 里配了 "program": "${workspaceFolder}/src/index.mjs",但代码里还写 __dirname,断点会直接跳过或报错。调试器能解析 ESM,但不会帮你“补全”不存在的变量。
- 确保所有路径逻辑都基于
import.meta.url衍生,不要混用process.cwd()或相对路径字符串 - 如果项目同时有
.cjs和.mjs,别把__dirname复制粘贴过去——类型系统不同,行为完全不同 - VSCode 的 IntelliSense 可能仍提示
__dirname存在,那是基于 JS 语言服务的默认假设,实际运行时无效
想彻底避开兼容问题?统一用 path.resolve() + import.meta.url
不需要模拟 __dirname,直接从模块 URL 构建绝对路径更干净:
path.resolve(fileURLToPath(import.meta.url), '..', 'data', 'users.json')- 比先算
__dirname再join少一步,也避免变量名污染(比如你不小心重声明了__dirname) - 在 Deno、Bun 等新运行时中也能平滑迁移,因为
import.meta.url是标准 API
真正容易被忽略的是:即使你只改了一处 __dirname,只要还有其他地方用 require('./util') 加载 CommonJS 模块,整个模块图就可能混合两种系统——此时路径计算逻辑必须各自独立,不能假设“都一样”。











