Node.jsのERR_UNSUPPORTED_DIR_IMPORTの直し方

​​Node.js​ES Modules,JavaScript,npm,トラブルシュート

Node.js で Error [ERR_UNSUPPORTED_DIR_IMPORT]: Directory import … is not supported resolving ES modules と出て止まるのは、ES モジュールの import にファイルではなくフォルダーを指定したときです。自分のプロジェクトのフォルダーなら、import './lib' を import './lib/index.js' と書き換えるとエラーは消えます。npm のパッケージの中のフォルダーなら、パッケージの exports で入口を決める方法もあります。

require('./lib') で書いていた CommonJS のコードを、そのまま import に書き換えると、このエラーになります。私は検証環境で ./lib のフォルダーを import してエラーを起こし、出たメッセージと、書き換えたあとに消えることを確かめました。

補足

検証環境は Ubuntu 24.04、Node.js 24.22.1(npm 11.19.1)です。エラーの全文と、直したあとの主な結果は、Node.js 22.23.4 でも確かめました。Windows と macOS での動き、TypeScript のファイル(.ts)を直接実行した場合、webpack や Vite などのツールを通した場合は試していません。

フォルダー名だけで import したときのエラー

検証環境で、"type": "module" を書いた package.json のプロジェクトを作りました。src/lib/index.js には、あいさつの文字列を返す関数 greet を export してあります。

js
export function greet(name) {
  return `hello, ${name}`;
}

フォルダー名の ./lib だけを書いて、src/index.js からこの関数を import します。

js
import { greet } from './lib';

console.log(greet('neko'));

これを node src/index.js で実行したときの出力です。終了コードは1で、パスは例示用の値に置き換えています。

text
$ node src/index.js
node:internal/modules/esm/resolve:260
    throw new ERR_UNSUPPORTED_DIR_IMPORT(path, basePath, String(resolved));
          ^

Error [ERR_UNSUPPORTED_DIR_IMPORT]: Directory import '/path/to/app/src/lib' is not supported resolving ES modules imported from /path/to/app/src/index.js
    at finalizeResolution (node:internal/modules/esm/resolve:260:11)
    at moduleResolve (node:internal/modules/esm/resolve:879:10)
    at defaultResolve (node:internal/modules/esm/resolve:1006:11)
    at #cachedDefaultResolve (node:internal/modules/esm/loader:705:20)
    at #resolveAndMaybeBlockOnLoaderThread (node:internal/modules/esm/loader:725:38)
    at ModuleLoader.resolveSync (node:internal/modules/esm/loader:763:56)
    at #resolve (node:internal/modules/esm/loader:687:17)
    at ModuleLoader.getOrCreateModuleJob (node:internal/modules/esm/loader:607:35)
    at ModuleJob.syncLink (node:internal/modules/esm/module_job:276:33)
    at ModuleJob.link (node:internal/modules/esm/module_job:381:17) {
  code: 'ERR_UNSUPPORTED_DIR_IMPORT',
  url: 'file:///path/to/app/src/lib'
}

Node.js v24.22.1

Directory import '…/src/lib' is not supported は、「…/src/lib はフォルダーなので、ES モジュールとしては読み込めない」という意味です。後ろの imported from …/src/index.js が、その import を書いたファイルです。lib の中に index.js があっても、Node.js はそれを選んでくれません。ES モジュールの解決アルゴリズムの説明でも、解決した先がフォルダーなら Unsupported Directory Import のエラーにする、と決められています。

Node.js 22.23.4 でも、Error [ERR_UNSUPPORTED_DIR_IMPORT]: Directory import … の行は同じでした(最初の行に出る resolve の行番号と、スタックの行番号・関数名は少し違います)。'./lib/' と末尾にスラッシュを付けても、Directory import '/path/to/app/src/lib/' is not supported … と、同じエラーで止まりました。

ポイント

フォルダーに package.json を置いて "main": "index.js" と書いても、ES モジュールの import './lib' は読めません。src/lib/package.json にこれを書いて試したところ、同じ ERR_UNSUPPORTED_DIR_IMPORT で止まりました。

自分のフォルダーなら index.js まで書く

import の行を、フォルダーの中のファイル名と拡張子まで書いた形にします。

js
import { greet } from './lib/index.js';

console.log(greet('neko'));
text
$ node src/index.js
hello, neko

エラーは出ず、関数の戻り値が表示されました。Node.js 22.23.4 でも同じでした。

拡張子の .js まで書くのが大事です。'./lib/index' と .js を省くと、今度は Error [ERR_MODULE_NOT_FOUND]: Cannot find module '/path/to/app/src/lib/index' imported from /path/to/app/src/index.js という別のエラーになりました(パスは例示用の値に置き換えています)。このエラーについては、別の記事「Node.jsのERR_MODULE_NOT_FOUNDの直し方」で扱っています。

import のたびに index.js を書きたくないときは、package.json の "imports" に # で始まる名前を決めておく方法もあります。"imports": { "#lib": "./src/lib/index.js" } と書き、import { greet } from '#lib'; とすると、Node.js 24.22.1 と 22.23.4 のどちらでも hello, neko と表示されました。この設定を足したあとも、await import('./lib') と書いた読み込みはエラーのままでした。'#lib' という名前で書いた行だけが対象になるので、直す行が数か所なら、index.js まで書き足すだけで十分です。

npm パッケージの中のフォルダーを import したとき

パッケージの中のフォルダーを import 'パッケージ名/フォルダー名' の形で読んだときも、同じエラーになります。検証用に、exports を書いていない neko-utils というパッケージを作りました。format/index.js に、文字列を大文字にする関数 shout が1つあります。これをローカルのフォルダーから npm install でプロジェクトに入れ、次のように読みました。

js
import { shout } from 'neko-utils/format';

console.log(shout('neko'));

次のように出ました(パスは例示用の値に置き換え、Did you mean の行より後ろは省いています)。

text
$ node src/index-pkg.js
node:internal/modules/run_main:107
    triggerUncaughtException(
    ^

Error [ERR_UNSUPPORTED_DIR_IMPORT]: Directory import '/path/to/app/node_modules/neko-utils/format' is not supported resolving ES modules imported from /path/to/app/src/index-pkg.js
Did you mean to import "neko-utils/format/index.js"?

今度は、メッセージの下に Did you mean to import "neko-utils/format/index.js"? と、書き換え先の候補が出ました。Node.js 22.23.4 でも同じ候補が出ました。候補のとおり 'neko-utils/format/index.js' と書き換えると、NEKO と表示されました。

パッケージを作る側なら、package.json の "exports" にサブパスを書く方法で、フォルダー名のまま読める入口を作れます。公式のエラー一覧にある ERR_UNSUPPORTED_DIR_IMPORT の説明でも、フォルダーを import する代わりに exports で名前を決める方法が示されています。neko-utils の package.json を次のようにして、入れ直しました。

json
{
  "name": "neko-utils",
  "version": "1.0.1",
  "type": "module",
  "exports": {
    ".": "./index.js",
    "./format": "./format/index.js"
  }
}

使う側の import { shout } from 'neko-utils/format'; を書き換えないまま実行すると、Node.js 24.22.1 と 22.23.4 のどちらでも NEKO と表示されました。

書き換え先の候補が出るときと出ないとき

Did you mean の行は、いつも出るわけではありませんでした。検証環境で試した結果は次のとおりです。

書き方 候補の行
ES モジュールのファイルで import { greet } from './lib' 出ない
ES モジュールのファイルで await import('./lib') 出ない
CommonJS のファイルで import('./lib') 出る("./lib/index.js")
ES モジュールのファイルで import … from 'neko-utils/format' 出る("neko-utils/format/index.js")

CommonJS のファイルで試すときは、package.json を "type": "commonjs" にした別のフォルダーに、次の1行だけの dynamic.js を用意しました。

js
import('./lib').then((m) => console.log(m.greet('neko')));

node dynamic.js を実行したときの出力から、エラーの2行だけを抜き出すと次のとおりです(パスは例示用の値に置き換えています)。

text
Error [ERR_UNSUPPORTED_DIR_IMPORT]: Directory import '/path/to/cjs-app/lib' is not supported resolving ES modules imported from /path/to/cjs-app/dynamic.js
Did you mean to import "./lib/index.js"?

候補の行が出なくても、直し方は同じです。Directory import '…' に出ているフォルダーの中を見て、読みたいファイルの名前を import に書きます。

フォルダーの中を探す仕組みは require にしかない

同じ CommonJS のフォルダーで、require('./lib') と書いた index.js を実行すると、lib/index.js が読まれて hello, neko と表示されました。require でフォルダーを読むときの説明によると、require() はフォルダーの package.json の main を見て、なければ index.js や index.node を探します。

ES モジュールの import には、この探し方がありません。同じ説明の中で、import('./some-library') で読み込むと ERR_UNSUPPORTED_DIR_IMPORT になると書かれています。フォルダーを読むこの仕組みは「Legacy(古い仕組み)」とされていて、代わりに exports や imports を使うよう勧められています。require('./lib') を import に書き換えるときは、'./lib/index.js' まで書くか、パッケージの入口なら exports で決めておく、と覚えておくと迷いません。

​Posted by 86