S.F. Page

Programming,Music,etc...

ES2015 ModuleのLoaderの件

ECMA Script 2015 Module (ESM)はまだ未確定の部分があるとのこと。

先日読んだ「ES Modules と Node.js について - from scratch」を引用するに、

そもそも ECMAScript 2015 自身で定義されたのは構文だけなので、構文はともかく、どうやってモジュールを取ってくるかという Loader の部分がまだ決まりきっていません。

https://whatwg.github.io/loader/

現時点はいくつも決めなきゃいけないポイントがあって

  • 参照解決処理
  • 取得処理
  • script タグでどう書くのか
  • メモ化処理(所謂caching)

の全てを決めて一旦ロードマップ上のMilestone 0 が達成されるような状況です。

yosuke-furukawa.hatenablog.com

これを読んでも、私の中では腑に落ちるような、落ちないような感じで、さらにいろんな文献を読んでみた。

まず読んだのが、https://whatwg.github.io/loader/1.1. Introductionである。 引用がてら翻訳してみると、

Modulesの開発を通じて、JavaScript Modulesは2つの領域に分割された。

オーサリング・フォーマット:インポートおよびエクスポートだけでなく、変数のバインディングと生存サイクルのセマンティクスを定義する。

JavaScript ローダー:JavaScriptモジュールのオンデマンド・非同期読み込みのパイプラインを提供する。

オーサリング・フォーマットは(Browserifyのような)プリコンパイルのサポートおよび(AMDのような)オンデマンド・非同期ローディングのために注意深く設計された。オーサリング・フォーマットは異なるプラットフォーム間(とりわけ重要なのはNode.jsとWebブラウザ)で動く可搬性があるモジュールを書くために必要な最低限の文法を定義する。

JavaScriptローダーはNode.jsやWebブラウザのようなホスト環境がオンデマンドにモジュールを取ってきて読み込むことを可能にする。JavaScriptローダーはフック可能なパイプラインを提供し、Browserify・ WebPack・jspmのようなフロントエンド・パッケージ・ソリューションがローディング・プロセスでのフックを可能にする。

このディビジョンは開発者がすべてのJavaScript環境で使用することができる単一のフォーマットを提供しかつ、各々の環境で別々のローディング・メカニズムを提供する。例えば Nodeローダーは独自のルックアップ・アルゴリズムでファイル・システムからモジュールをロードするだろうし、ブラウザローダーではブラウザが提供するパッケージ形式を使用してモジュールを取り込むだろう。

JavaScript自身はECMAScript 2015規格において、モジュールの構文とモジュール間のリンク・セマンティクスを定義する。 モジュールが要求されたとき、モジュールのローディングについての責務をホスト環境に委譲する。ローダーはホスト環境がJavaScriptコードをローディング・プロセスにおいて設計可能な方法を定義する。

主要目的はNodeとBrowser環境の間でできる限り首尾一貫したプロセスとすることである。例えば、JavaScriptプログラムが実行中に.coffeeファイルをJavaScriptに翻訳したい場合、ローダーは"translate"フックを定義する。これは若干の部分(具体的にいうと、ホストで定義されたストレージから特定のモジュールを得るプロセス)は環境間で異なるけれども、プログラムがローディング・プロセスに関与することを可能にする。

これを読むと、モジュールの構文はES2015で定義されたが、モジュールをロードする部分はホスト環境に委譲している。なのでホスト環境側でどのようにロードするかを決めないと実装できないのである。また、この文書を読むにimport/export文だけではなくて、JavaScript loader API(動的モジュール・ローダ)なるものの存在も確認した。これはAMDやrequire的なモジュールのロード方法を実現する。これも絶賛策定中である。

The WHATWG Blog — Adding JavaScript modules to the web platform」を読むと同じようなことが書いてあった。

  • ホスト環境によって異なるモジュールのロード方法を仕様に入れるには、クリアしなければならない難しい問題がいくつかあった。ES2015規格策定の締め切りが迫っていたので、妥協案としてsyntaxのみを規格に入れ、モジュールのロード部分(import x from "x"の"x"の部分)は HostResolveImportedModuleフックを用意し、ホスト環境に委譲した。

とある。このHostResolveImportedModuleというのはどのようなものかというと、規格より引用し翻訳すると

HostResolveImportedModuleはreferencingModuleモジュールレコードによって表わされるモジュールのコンテキスト中で発生する、ModuleSpecifier文字列、識別子に対応する具象モジュール・レコードサブクラスを提供する、定義された抽象操作の実装である。 HostResolveImportedModuleの実装は次の必要条件に従わなければならない。

  • 正常な返却値は、モジュールレコードの具象サブクラスのインスタンスでなければならない。
  • もしreferencingModule, 識別子のペアに対応するモジュールレコードが存在しないか、作成できなかった場合、例外がスローされなければならない。
  • 操作は正常に完了した場合、等冪でなければならない。引数として特定のreferencingModuleと識別子のペアが呼ばれるたびに、同じモジュールレコード・インスタンスを返さなければならない。

複数の異なるreferencingModuleと記述子のペアが同じモジュール・レコードインスタンスに登録される。実際のマッピング・セマンティックは定義された実装であるが、一般的には正規化されたプロセスは、マッピング・プロセスの一部として識別子へ適用される。一般的な正規化プロセスは、アルファベットの大文字・小文字変換、相対や省略パス記述子の補完・拡張などを含む。

ここに書いてあることは訳がまずいせいもありわかりにくいけど、つまりは

  • ホスト環境はこのフックを通じて、モジュール識別子(文字列)を受け取り、モジュールレコードのインスタンスを返さなければならない。

ということのようだ。

このポストには<script type="module">についても書いてあった。これはモジュールをロードするスクリプトを読み込んだり、インラインで書いたりするときに使う。通常の<script>タグではimport/exportは書けないようである。CORSやらdefferedやら細かい課題があるけど、もうすぐまとまるようではある。

つまり今の時点では

  • Blowser Loaderは<script type="module">でまとまりつつあり、微調整がもうちょっと必要。
  • Node.jsはまだ混とんとしている(commonJSとの相互運用部分)。

であるということのようなのである。

早くまとまってほしいねぇ。。

しかしまあcommonJSとの相互運用ってブラウザにも関係してくるような気がする。ブラウザはcommonJSローダーはもともとないけど、ブラウザのimportでcommonJSが読めれば、Browserifyなどは不要になるんじゃないかなと思うので。