NPMのパッケージとかモジュールシステム

パッケージとはディレクトリツリーのことだが、次の条件/制約がある。

  1. トップレベルに package.json を持つ。
  2. サブディレクトリで、package.json を持つものがあれば、それは別なパッケージなので除外する(サブツリーを切り落とす)。
  3. すぐ下のサブディレクトリ node_modules/ は除外する(サブツリーを切り落とす)。

ひとつのファイルシステム内にたくさんのパッケージを持てる。ZIPに固めたりしないところが昔とは違う。ディスク容量が増えて、gitのプロトコルでファイルツリーがそのまま転送されるようになって、アーカイブする動機がなくなったと思う。ディレクトリツリーは、そのまま転送する(コピーする)感覚になった。

CJS方式だと、ディレクトリをモジュールとみなすこと(folders as modules)ができる。Webサーバーと同じ index.* をフォルダーとみなす。これはあまり良くないと思う。ディレクトリ名と同名で、型を表す拡張子のファイルが好み;./foo/foo.md とか。これはこれでほんとに ./foo/foo.md が欲しいときに困るが。

パッケージのエントリーポイントと公開名〈公開ラベル〉は同じもの。package.json の exports プロパティはエクスポートテーブルを与える。エクスポートテーブルは、名前の変換表になる。main も名前の変換をするが、パッケージ名をファイル名にマップする。

パッケージの置かれているローカルディレクトリを仮にパッケージコンテナと呼ぶと、モジュール検索は、幾つかのパッケージコンテナ内を順番に検索となる。

  1. パッケージコンテナ(例えば ./node_modules/)内にパッケージ名と同名のディレクトリがあれば、それがパッケージ。
  2. パッケージ内の package.json を見て、モジュール名をファイル名にどうマップするかを決める。このとき見る package.json のプロパティは exports 、なければ main 。exports があれば main は見ない。
  3. exports は「モジュール名 → 実体であるファイル名」マッピングの記述。
  4. ./package.json#property(exports) は、パッケージ内の実体に対する公開/非公開の制御と名前の設定を行う。
  5. これは、ツリー構造の部分的な貼り合わせの技法。なかなか優れている。
  6. 名前に対して最終的にはプログラム・エンティティをロードするが; (1)パッケージ名からパッケージ実体(ディレクトリツリー)を探す、(2)モジュール名からファイルモジュール=モジュールファイルを探す (3)エンティティ名からエンティティ〈実体〉を探す、(4)現在の名前グラフに追加する。
  7. 名前からエンティティの検索と、見つかったエンティティの内部登録の際に、“名前→名前マッピング”と“名前→実体”マッピングが繰り返し使われる。
  8. TypeScriptだと、export {foo as pubName} とか import {pubName as myName} とかできる。ES2015+ ならどうかは知らん。
  9. TypeScriptのモジュールのエクスポートテーブルには、やはり値名空間、型名空間、名前空間名空間がある。
  10. mainがエントリーポイントという発想はどうも古すぎる。エクスポートテーブル、あるいはエクスポートリストだろうな。テーブルエントリ、リストアイテムに名前〈ラベル〉だけでなく型〈プロファイル〉が付いているとさらに新しい。指標とセオリーが使える。
  11. mainエントリーポイント → デフォルト・エクスポート・エントリ。同じ「エントリー」が使われているが、「入り口」と「項目」で違う意味。
  12. 名前構造をツリーだと思うのもダメで、名前グラフで考えないと。