ESMだのCJSだので詰まることが多すぎるので、いい加減情報をまとめる。
概要
ざっくりいうと、「modules
をどのように扱うか?」に係る2種の系統。
-
import
,export
するほうがESM -
require
,modules.export
するほうがCJS
ESM; ES Modules
import
の方。
ECMA Script2015(ES6)で策定されたモジュール仕様。
V8などは当然こちらに準拠している。
CJS; CommonJS
require
のほう
立ち上げは2009なので、ESMより古参。
ServerSideJavascriptというコンセプトを出発点とするため、Node.jsではデフォルトである一方、ブラウザでは非対応となっている。
趨勢
メインストリームはESMである。
ことフロントエンド開発においてはそもそもブラウザがCJSに非対応であるため、必然的にESM環境になる。
しかし、Node.js環境上においても、npm
の開発者であるIsaac氏自ら「CJSは時代遅れになりつつある」とコメントしている模様。
Breaking the CommonJS standardization impasse · Issue #5132 · nodejs/node-v0.x-archive · GitHub
こうした状況を反映してか、最近のサードパーティモジュールの中にはCJSに対応していない(ESMのみ提供)ものが増えつつある。
……とされていたが、Node.js v22からそうでも無くなった模様。
CJSからESMをrequire
で呼び出せるようになった。
Node.js — Node.js 22 is now available!
ただし、Pure ESM packageという観点からは、import
の使用を強く推奨されている。
Node.js + ESM + TypeScript
プロジェクト単位の手続き
一方、先述の通り、Node.jsにおいてはCJSをデフォルトとしている都合、Node.js環境を想定したプロジェクトをESM環境として扱うためにはボイラープレートな手続きが必要。
再掲・引用。
Pure ESM package · GitHub
- Add
"type": "module"
to your package.json.- Replace
"main": "index.js"
with"exports": "./index.js"
in your package.json.- Update the
"engines"
field in package.json to Node.js 18:"node": ">=18"
.- Remove
'use strict';
from all JavaScript files.- Replace all
require()
/module.export
withimport
/export
.- Use only full relative file paths for imports:
import x from '.';
→import x from './index.js';
.- If you have a TypeScript type definition (for example,
index.d.ts
), update it to use ESM imports/exports.- Use the
node:
protocol for Node.js built-in imports.
拡張子の明示と、TypeScriptでの都合
大きな違いとして、ESMでimport
を行う際は拡張子を明示するのが原則。
import modules from "./modules.js"
ここまではまだ良いとして、TypeScriptでESM環境を想定する場合に留意点がある。
それは、.ts
ファイルをimport
する場合でも、拡張子を.js
にする必要があるということ。
つまり、上記でimport
するファイルがmodules.ts
であろうとも、上のように.js
拡張子で書かないといけない。
これは、tsc
のコンパイル方針に起因する。
tsc
はimport
内の文字列の書き換えや読み替えを行わないという方針がTypeScript開発チームから示されている。
Design Meeting Notes, 11/22/2019 · Issue #35589 · microsoft/TypeScript
そのため、import
内にはトランスパイル後の.js
拡張子を記載する必要がある。
一応、このあたりはtsc
などがIDE上でサポートしてくれるのでエラーになったりすることはない。気持ちは悪い。
一応、tsconfig
の設定次第で.ts
を扱えるとされているようだが……なんだか情報が錯綜している。