npm (node package manager)の菱形依存問題とdedupeと社内モジュールとJSX

npmで色々コードを書いていると、以下のような依存関係ができてしまうことがある*1

a
+-- b <-- depends on c@1.0.x
|   `-- c@1.0.3
`-- d <-- depends on c@~1.0.9
    `-- c@1.0.10

このように「bが依存するc」と「dが依存するc」が異なるものになってしまうと、(2回cがロードされる分)プログラムが無駄に大きなものになってしまったり、(bとdがお互いで生成したcを使う場合に)片方で生成されたcのインスタンスをもう片方で「instanceof c」してもfalseが返ってきてしまう、などの問題が発生する。

この問題を解決するために、npmはsemantic versioningという枠組みとnpm dedupeというコマンドを提供していて、両者を使うことで、依存構造を以下のような形に変換して問題を回避することができる(npmのモジュール探索は、子から始めて見つかるまでツリーをルート方向に上って行くため、この例では、bとdは隣接するcを参照するようになる)。

a
+-- b
+-- d
`-- c@1.0.10

ただ、npm dedupeコマンドには、npmに登録されたモジュールはdedupeできるが、GitHub(あるいはGitHub Enterprise)にあるモジュールをpackage.jsonから参照している場合はdedupeできないという限界があって、この点が、社内で大量にモジュールを書いていたりすると問題になりがちだった。

以上が前置き。

で、この問題についてはuser/repo dedupe issue · Issue #4213 · npm/npm · GitHubによると修正が存在する(がpull-requestにはまだ至っていない)ようなので、困ってる人は是非お願いコメントをつけると良いのではないか、というのが1点。

第2の点としては、最近JSXのライブラリをNPMで公開できるようにしたんだけど、JSXにおいてこの問題をどう解決するか。こちらについては、version 0.9.76より--add-search-pathオプションで指定したパスをnode_modules/よりも優先して探索するようにJSXコンパイラの機能を拡張した。よって、冒頭で示したようなグラフがある場合、

jsx --add-search-path node_modules/d/node_modules/c a.jsx

のようにコンパイルオプションを設定することで、bが参照するcについてもd/cが参照されるようになる*2

*1:グラフはいずれもnpm dedupeのマニュアルより引用

*2:ただし、--add-search-patchはimportするファイルがそのディレクトリに存在する必要があるので、この場合はcのpackage.jsonには"main":"c"と書かれている必要がある。このような(npmとしても直指定でも使える)dual-lifeなJSXパッケージの作り方についても整理が必要かなぁ