読者です 読者をやめる 読者になる 読者になる

io.js用に書いたパッケージはnpmでどう扱うか問題

東京Node学園 15時限目に参加したときにふと思った疑問について。

Node.jsの場合

例えばNode.js v0.12系にしか存在しないAPIを使ったパッケージの場合、package.jsonenginesフィールドにこう書くことで、

{ "engines" : { "node" : ">=0.12" } }

適合しないNode.js v0.10でnpm installしたときに警告を出すことができた。逆に削除されたAPIを使っている場合はバージョンの上限も指定できる。また非推奨ながらengineStrictフィールドを使うと警告ではなくエラーにできる。

io.jsでの議論は

では、io.jsにあってNode.jsには無いAPIを使ったパッケージをnpmにあげた場合、想定していない環境でnpm installしたユーザーに何らかの警告を知らせることはできるのだろうか?というのが今回の疑問。

io.jsのissueを見てみるとまさに議論中だった。

結論は出ていないけど、議論の方向としてはenginesフィールドにio.jsやV8のバージョンを載せて判定するのは筋悪だよね、feature detectで対応していこう、という流れになっている(ように見える)。

コード例としては、パッケージ作者が内部でGeneratorを使ったコードfoo.es6.jsを書く場合、トランスパイラでfoo.es5.jsを生成しつつ、呼び出すところfoo.jsでこう書くとのこと。

module.exports =
    require('has-generators') ? require('./foo.es6') : require('./foo.es5')

マジすかw

  • 結局enginesフィールドにはなんて書くのか?
    • "node" : ">=0.12"って書くとしたら、Node.js v0.12からの全ての差分をパッケージ作者はfeature detectで書かないといけないのか?MUSTはムリ。
    • feature detectが任意だとしたら、そのパッケージをインストールするユーザーが動作環境を知る術が結局無いことになる。
  • 議論がES6対応という綺麗にfeature detectできる問題に限定されてる。CHANGELOGを見れば分かるように細かな差分は既に大量にあって、detectどころか、何が差分なのか把握すら難しい。
  • Node.jsでも走らせたいコードだったら最初からio.js専用APIとか使わないのでは?互換性を目指すよりも動作環境を明示できれば十分なのでは。

ということで、この方法は破綻するのではと思った(または議論をミスリードしているか)。

どうするか?

自分の意見としては、パッケージ作者が想定している、すなわちテストしている(もっと具体的に言えば.travis.ymlで指定している)環境をenginesフィールドに書くしかないと思っている。

{ "engines" : { "node" : ">=0.12", "iojs": ">=1.1" } }

Node.jsもサポートしたけりゃ自前でトランスパイルなりすればいいし、したくないならiojsだけ書いとけばいい。現状npm側はnode以外は対応してないけど、ここでiojsも対応して警告を出してもらえば良い。

参考に見てみたらkoaのpackage.jsonもこの書き方になってた。

懸念されるディストピア

当然ながら、この方法はかつてブラウザ業界が味わったUser-Agentの苦い記憶が思い出される。予想される最悪な未来として、

  1. io.jsがそこそこ流行る。
  2. 世の中のパッケージがみんな "iojs": ">=30.0" とか書いて最新機能を使いまくる。
  3. Node.jsがV8上げて最新機能に対応してもpackage.json更新してくれない警告出るままのパッケージが多すぎる。
  4. 怒ったJoyentがNode.jsのprocess.versionsを偽装してio.jsと偽るようになり、npmのチェックをすり抜けるようになる。
  5. ちゃんとfeature detectしよう!という啓蒙とともにライブラリModernodeizrが開発される。

半分冗談ですけど。

これに対する反論としては、たかだか警告で動かないわけじゃないんだし良いじゃないっていうのがひとつ。

もう一つは、仮にあるfeatureの対応がdetectできたからって、パッケージ作者が想定(テスト)してない(または多くのユーザーが使ってない)環境で動かせるぐらいNode.jsとio.jsって今後互換性が保たれるの?っていう点で現状のCHANGELOGと開発の流れを見る限り自分は懐疑的なので、実際動かないならしょうがないじゃない、と思う。

といったことをissueで議論するだけの気力と英語力が無かったのでとりあえずブログに書いた。

要するに、Node.jsとio.jsが和解してくれるのが一番幸せな未来ってことですよ!