ふと思い立って npm install -g npm@3
した。3.1.2 は VERY BETA とのこと(ニュアンスがよくわからん。アルファとの対比なのか、アルファ相当なのか) ので、そのうち出るでしょう。
node_modulesのフラットな展開
npm@3 だとnode_modulesの依存がパッケージ間のバージョンが衝突しない限り、トップレベルのnode_modulesにフラットに展開される。これによって同一のモジュールを依存ツリー間でモジュールが重複した時にnode_modules以下のサイズが膨らむのを抑えることができる。
衝突した場合はそのモジュールの子のnode_modulesに格納されて衝突は回避される。
気になるrequireの仕様
とはいえ、requireの仕様が変わってるわけじゃないので、トップレベルから子だけじゃなく孫モジュールも直接requireできてしまう。手元でたとえば a -> b な依存のときに、今までは require('b')
できなかったのができるようになる。
これはある時点でrequireできたモジュールが何の依存だったのかあとでわからないという、将来的に禍根になる可能性があるケースがあるのではないか。怖い。
壊れるケース
気にしないといけないのが一点。
子と孫で同じバージョンの場合はrequireキャッシュに引っかかって同一参照になる。依存自身からみた場合に子が孫をシングルトンだと想定してグローバルオプションをセットしている場合npm3にしたことで壊れる。
ちょうどmarkedが判りやすく壊れそうだったので、例に出す。
gfm-marked/gfm-marked.js
var marked = require('marked');
marked.setOptions({
renderer: new marked.Renderer(),
gfm: true
});
module.exports = marked
not-gfm-marked/not-gfm-marked.js
var marked = require('marked');
marked.setOptions({
renderer: new marked.Renderer(),
gfm: false
});
module.exports = marked
index.js
var notGfm = require('not-gfm-marked');
var gfm = require('gfm-marked');
notGfm('```aaa```'); // fenced code block はgfmでないと存在しないが、参照が同一な為あとで読み込んだsetOptionsで有効になってしまう。
まあ意図しないとなかなか起こせないとは思うんだけど、ハマった時の根は深そう。他に気づいた点としてはbrowserifyが対応してなかったので、browserify使っている人も対応待ちになるはず。
そういえば es modules と commonjs の関係ってどうなるか話進んでるんだろうか。