できない.dev

Node.js の ESM で「__dirname is not defined」が解決できない

ES Modules では CommonJS の __dirname / __filename が定義されないためです。
import.meta.url から導出するか、Node 20.11 以降なら import.meta.dirname を使います。

公開: 更新:

実行例あり(2026-08-02 に実環境で検証)

要約

ReferenceError: __dirname is not defined は、ファイルが ES Modules として実行され、CommonJS のグローバル __dirname / __filename が ESM には存在しないために起きます。import.meta.url から自前で算出するか、Node.js 20.11 以降なら import.meta.dirname を使えば解決します。

よくある原因

  1. ESM で実行されている: package.json"type": "module" がある、または拡張子が .mjs のため、CommonJS のグローバルが無い。
  2. __dirname は CJS 専用: __dirname / __filename / require は CommonJS ラッパーが注入する変数で、ESM スコープには注入されない。
  3. コピペ由来: CommonJS 前提のブログ記事のコードをそのまま ESM プロジェクトに持ち込んでいる。

解決策

1. import.meta.url から導出する(全バージョン対応)

import { fileURLToPath } from "node:url";
import { dirname } from "node:path";
 
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);

この手法は Node.js の公式 ESM ドキュメント(新しいタブで開く)で案内されている定番です。

2. import.meta.dirname を使う(Node 20.11+ / 21.2+)

新しめの Node ならワンライナーで済みます。

const dir = import.meta.dirname;   // __dirname 相当
const file = import.meta.filename; // __filename 相当

3. パス結合に使う

import { join } from "node:path";
const configPath = join(import.meta.dirname, "config.json");

import.meta.dirname は Node 20.11.0 / 21.2.0 で追加されたため、それ未満を含むサポート対象がある場合は解決策 1 を採用してください。

実行例

実際に上記の手順を node:20(v20.20.2)環境で動かすと、.mjs ファイルで __dirname を参照した時点で ReferenceError: __dirname is not defined in ES module scope が発生し終了コード 1 となる一方、解決策 1・2 はいずれも終了コード 0 で期待どおりのパスを返す。

$ node --version
v20.20.2
$ node bad.mjs
file:///tmp/tmp.CnLx4B5Nap/bad.mjs:1
console.log("__dirname =", __dirname);
                           ^
 
ReferenceError: __dirname is not defined in ES module scope
    at file:///tmp/tmp.CnLx4B5Nap/bad.mjs:1:28
    at ModuleJob.run (node:internal/modules/esm/module_job:325:25)
    at async ModuleLoader.import (node:internal/modules/esm/loader:606:24)
    at async asyncRunEntryPointWithESMLoader (node:internal/modules/run_main:117:5)
 
Node.js v20.20.2
終了コード: 1
$ node fix1.mjs
__dirname = /tmp/tmp.CnLx4B5Nap
終了コード: 0
$ node fix2.mjs
import.meta.dirname = /tmp/tmp.CnLx4B5Nap
import.meta.filename = /tmp/tmp.CnLx4B5Nap/fix2.mjs
終了コード: 0

— 2026-08-02 時点の出力

検証環境

検証日
実行環境
node:20

この記事の「実行例」は、上記の環境で実際にコマンドを実行して得られた出力をそのまま掲載しています。 再現手順はリポジトリの検証スクリプトとして管理し、定期的に再実行して出力を更新しています。

この記事は役立ちましたか?