Node.jsでCommonJSとESMの挙動を調べる

nodejsでコードを書いているとimoprtやrequireの使い分けがよくわからなかったので、使い分けを理解したかった。

以下の公式ドキュメントが参考になる。

Interoperability with CommonJS Modules: ECMAScript modules | Node.js v18.0.0 Documentation

imoprt statements

  • importはESMとCJSをインポートできる。
    • ただし、CJSはdynamic importでのみ対応している

package.jsonのtype=moduleの指定のありなし

  • なし
    • js拡張子はcjs扱い
    • esmのファイルはmjs拡張子にする
  • あり
    • js拡張子はesm扱い
    • cjsのファイルはcjs拡張子にする

ESMからCJSを読む

  • できる
    • `import hoge from "cjsmodule.js"
    • cjsってモジュール内でmodule.exportsにセットしたオブジェクト(とか関数)の参照を返す仕組みだから、hogeにその参照が渡ってくる
      • オブジェクト参照ならオブジェクトだし、関数参照なら関数

CJSからESMを読む

  • require("esm module")はできない
  • importは制限付きというかdynamic importのみできる
    • const hoge = await import("esm.mjs") みたいな。拡張子は省略できない。
  • export defaultされてたモジュールは、defaultで参照できる
    • const { default as hoge } = await import("esm.mjs")
  • defaultしかないモジュールでも{ default : xxxx } という形でcjs側ではimportされる
// myEsmModule.mjs
export const HOGE = "hoge";
export default  {
  fuga : "fuga"
}

// cjs
async function main() {
  const myModule = await import("./expEsm.mjs");
  console.log(myModule);
  // { HOGE: 'hoge', default: { fuga: 'fuga' } }
}
main();

import - JavaScript | MDN

package.jsonは上位ディレクトリを確認し一番近いファイルのtypeが有効

  • package.jsontype : moduleだけ追記してディレクトリに配置したら、その階層はjs拡張子でもESMとして処理される。
package.json // type:module指定なし
hoge.js // cjs
module1
  - package.json // type:module指定あり
  - fuga.js // esm