Skip to content

Instantly share code, notes, and snippets.

@kuy
Created October 7, 2016 16:11
Show Gist options
  • Save kuy/1a47a349888d41821c66d2ad88fd524f to your computer and use it in GitHub Desktop.
Save kuy/1a47a349888d41821c66d2ad88fd524f to your computer and use it in GitHub Desktop.
Node.js CommonJS と ECMAScript Modules の interop について

import と require() の違い

CommonJSのモジュール内でrequire()を使ってESMを読み込むこと、 ESMのモジュール内からimportを使ってCommonJSを読み込むことは可能だけど、 CommonJSの中からimportを使うことはできない。 ESMの中であれば例えば import { require } from 'nodejs' とかやれば可能になる。

  • CommonJSのモジュール内で require() を使ってESMを読み込むことは可能
  • ESMのモジュール内から import を使ってCommonJSを読み込むことは可能
  • ESMのモジュール内から require() を使うことは不可能ではない
    • import {require} from 'nodejs' みたいなのが必要
  • CommonJSのモジュール内では import を使えない

import() について

import ステートメントと import() 関数はまったく違うもの。 import ステートメントは構文解析の段階で処理されるが、 import() 関数は require() 関数と同じように実行時に呼び出される。 ただし、import() 関数は require() 関数と異なり、常に Promise を返す。 Promise なので await import('hoge') みたいにやれば同期的に読み込むこともできる。

CommonJS か ESM の判定について

  1. unambiguous grammar
  2. package.jsonにメタデータを記述して、ESMであることを明記する
  3. ESM向けに*.mjsという新しい拡張子を使う

2番と3番は @yosuke-furukawa さんによる解説がある。 ES Modules と Node.js について

1番は多くのエッジケースがあって実装が難しい。

現時点では *.mjs のアプローチが実現可能性が高い選択肢とのこと。

冪等性ついて(idempotency concerns)

複数回 require('foo') を呼んだとき、同じモジュールのインスタンスが返却されるか、という問題。 あるモジュールを読み込んだとき、他のモジュールの機能をモンキーパッチで置き換えたりするテクニックあ Node.jsのエコシステムではよく使われている。

あるアプリがCommonJSのモジュールAとモジュールBに依存していて、モジュールAはモジュールBを 拡張するためにモジュールBへの依存があった場合、アプリからモジュールAを読み込んでから モジュールBを読み込んで使ったときと、モジュールAでモジュールBを読み込んで使った場合で 異なる結果になってしまう。

ESMにおいてはこのようなモジュール間のモンキーパッチは難しくなっている。 実行前にimportは読み込み対象をリンクしているし、importは冪等性のために必要。 名前付きimportを使っている場合はモンキーパッチが難しくなる。

これらの冪等性のルールはモックとかテストとかで問題お起こす。 でも解決方法はいくつもあるので、インターセプトは可能である。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment