package.json の browser field 実践編

# package.json の browser field 実践編
[package.json の browser field 入門編](http://qiita.com/shinout/items/4c9854b00977883e0668) では、`package.json`のbrowser fieldの役割と機能について紹介しました。

本編では、この機能の`bundler`ごとの実装の違いと、それを回避する方法を説明します。
ここで取り上げる実装の違いとはずばり**pathの解決方法**です。

- `./`から記述するかどうか
- `.js`を記述するかどうか
- `main`との対応関係

この3つの要素が絡んできます。

なお、パス解決の`resolver`を指定できる系もあるようですが、ここでは各々のbundlerがデフォルトで用意している`resolver`について論じています。
(なぜなら、resolverを外部が指定しなければ意図通りbundleされないというのは、利用者にとってはbundleされないのとほぼ同義です)

調査したbundlerは以下です。

- [browserify](https://github.com/substack/node-browserify)
- [webpack](http://webpack.github.io)
- [React Native Packager](https://github.com/facebook/react-native/tree/master/packager)


# パス記述の組み合わせ
組み合わせ的には下記があります

- `./`から記述するかどうか: 変換前と変換後のそれぞれに対して2通り = 4通り
- `.js`を記述するかどうか: 変換前と変換後のそれぞれに対して2通り = 4通り
- `main`の記述方法: 相対パス記述と.js記述のそれぞれで4通り、さらに`browser`フィールドに変換を指定しているか否かで2通り = 8通り

4 x 4 x 8 = 128通りものパターンがあります。
その全パターンについて、指定がうまくいくかどうかを表にしました。

## 表のよみかた
|main|from|to|browserify|webpack|rn-packager|
|---|---|---|---|---|---|
|file1|file1|file2|:ok:|:x:|:ok:|

1行は、`package.json`と、bundlerごとの解釈結果を示しています。

この行は

```package.json
{
  "main": "file1",
  "browser": {
    "file1": "file2"
  }
}
```

という意味です。

- `main` カラムは`main`フィールドの値
- `from` カラムは、`browser`フィールドの最初のキー
- `to` カラムは、`from`の値

この場合、`webpack`以外はうまくバンドルできたということを示しています。

## 全結果
|main|from|to|browserify|webpack|rn-packager|
|---|---|---|---|---|---|
|file1|file1|file2|:ok:|:x:|:ok:|
|file1|file1|./file2|:ok:|:x:|:ok:|
|file1|file1|file2.js|:ok:|:x:|:ok:|
|file1|file1|./file2.js|:ok:|:x:|:ok:|
|file1|./file1|file2|:ok:|:x:|:x:|
|file1|./file1|./file2|:ok:|:x:|:x:|
|file1|./file1|file2.js|:ok:|:x:|:x:|
|file1|./file1|./file2.js|:ok:|:x:|:x:|
|file1|file1.js|file2|:x:|:x:|:ok:|
|file1|file1.js|./file2|:x:|:ok:|:ok:|
|file1|file1.js|file2.js|:x:|:x:|:ok:|
|file1|file1.js|./file2.js|:x:|:ok:|:ok:|
|file1|./file1.js|file2|:x:|:x:|:x:|
|file1|./file1.js|./file2|:x:|:ok:|:x:|
|file1|./file1.js|file2.js|:x:|:x:|:x:|
|file1|./file1.js|./file2.js|:x:|:ok:|:x:|
|./file1|file1|file2|:x:|:x:|:x:|
|./file1|file1|./file2|:x:|:x:|:x:|
|./file1|file1|file2.js|:x:|:x:|:x:|
|./file1|file1|./file2.js|:x:|:x:|:x:|
|./file1|./file1|file2|:ok:|:x:|:ok:|
|./file1|./file1|./file2|:ok:|:x:|:ok:|
|./file1|./file1|file2.js|:ok:|:x:|:ok:|
|./file1|./file1|./file2.js|:ok:|:x:|:ok:|
|./file1|file1.js|file2|:x:|:x:|:x:|
|./file1|file1.js|./file2|:x:|:ok:|:x:|
|./file1|file1.js|file2.js|:x:|:x:|:x:|
|./file1|file1.js|./file2.js|:x:|:ok:|:x:|
|./file1|./file1.js|file2|:x:|:x:|:ok:|
|./file1|./file1.js|./file2|:x:|:ok:|:ok:|
|./file1|./file1.js|file2.js|:x:|:x:|:ok:|
|./file1|./file1.js|./file2.js|:x:|:ok:|:ok:|
|file1.js|file1|file2|:x:|:x:|:ok:|
|file1.js|file1|./file2|:x:|:x:|:ok:|
|file1.js|file1|file2.js|:x:|:x:|:ok:|
|file1.js|file1|./file2.js|:x:|:x:|:ok:|
|file1.js|./file1|file2|:x:|:x:|:x:|
|file1.js|./file1|./file2|:x:|:x:|:x:|
|file1.js|./file1|file2.js|:x:|:x:|:x:|
|file1.js|./file1|./file2.js|:x:|:x:|:x:|
|file1.js|file1.js|file2|:ok:|:x:|:ok:|
|file1.js|file1.js|./file2|:ok:|:ok:|:ok:|
|file1.js|file1.js|file2.js|:ok:|:x:|:ok:|
|file1.js|file1.js|./file2.js|:ok:|:ok:|:ok:|
|file1.js|./file1.js|file2|:ok:|:x:|:x:|
|file1.js|./file1.js|./file2|:ok:|:ok:|:x:|
|file1.js|./file1.js|file2.js|:ok:|:x:|:x:|
|file1.js|./file1.js|./file2.js|:ok:|:ok:|:x:|
|./file1.js|file1|file2|:x:|:x:|:x:|
|./file1.js|file1|./file2|:x:|:x:|:x:|
|./file1.js|file1|file2.js|:x:|:x:|:x:|
|./file1.js|file1|./file2.js|:x:|:x:|:x:|
|./file1.js|./file1|file2|:x:|:x:|:ok:|
|./file1.js|./file1|./file2|:x:|:x:|:ok:|
|./file1.js|./file1|file2.js|:x:|:x:|:ok:|
|./file1.js|./file1|./file2.js|:x:|:x:|:ok:|
|./file1.js|file1.js|file2|:x:|:x:|:x:|
|./file1.js|file1.js|./file2|:x:|:ok:|:x:|
|./file1.js|file1.js|file2.js|:x:|:x:|:x:|
|./file1.js|file1.js|./file2.js|:x:|:ok:|:x:|
|./file1.js|./file1.js|file2|:ok:|:x:|:ok:|
|./file1.js|./file1.js|./file2|:ok:|:ok:|:ok:|
|./file1.js|./file1.js|file2.js|:ok:|:x:|:ok:|
|./file1.js|./file1.js|./file2.js|:ok:|:ok:|:ok:|
|file3|file1|file2|:x:|:x:|:x:|
|file3|file1|./file2|:x:|:x:|:x:|
|file3|file1|file2.js|:x:|:x:|:x:|
|file3|file1|./file2.js|:x:|:x:|:x:|
|file3|./file1|file2|:x:|:x:|:ok:|
|file3|./file1|./file2|:ok:|:x:|:ok:|
|file3|./file1|file2.js|:x:|:x:|:ok:|
|file3|./file1|./file2.js|:ok:|:x:|:ok:|
|file3|file1.js|file2|:x:|:x:|:x:|
|file3|file1.js|./file2|:x:|:ok:|:x:|
|file3|file1.js|file2.js|:x:|:x:|:x:|
|file3|file1.js|./file2.js|:x:|:ok:|:x:|
|file3|./file1.js|file2|:x:|:x:|:ok:|
|file3|./file1.js|./file2|:x:|:ok:|:ok:|
|file3|./file1.js|file2.js|:x:|:x:|:ok:|
|file3|./file1.js|./file2.js|:ok:|:ok:|:ok:|
|./file3|file1|file2|:x:|:x:|:x:|
|./file3|file1|./file2|:x:|:x:|:x:|
|./file3|file1|file2.js|:x:|:x:|:x:|
|./file3|file1|./file2.js|:x:|:x:|:x:|
|./file3|./file1|file2|:x:|:x:|:ok:|
|./file3|./file1|./file2|:ok:|:x:|:ok:|
|./file3|./file1|file2.js|:x:|:x:|:ok:|
|./file3|./file1|./file2.js|:ok:|:x:|:ok:|
|./file3|file1.js|file2|:x:|:x:|:x:|
|./file3|file1.js|./file2|:x:|:ok:|:x:|
|./file3|file1.js|file2.js|:x:|:x:|:x:|
|./file3|file1.js|./file2.js|:x:|:ok:|:x:|
|./file3|./file1.js|file2|:x:|:x:|:ok:|
|./file3|./file1.js|./file2|:x:|:ok:|:ok:|
|./file3|./file1.js|file2.js|:x:|:x:|:ok:|
|./file3|./file1.js|./file2.js|:ok:|:ok:|:ok:|
|file3.js|file1|file2|:x:|:x:|:x:|
|file3.js|file1|./file2|:x:|:x:|:x:|
|file3.js|file1|file2.js|:x:|:x:|:x:|
|file3.js|file1|./file2.js|:x:|:x:|:x:|
|file3.js|./file1|file2|:x:|:x:|:ok:|
|file3.js|./file1|./file2|:ok:|:x:|:ok:|
|file3.js|./file1|file2.js|:x:|:x:|:ok:|
|file3.js|./file1|./file2.js|:ok:|:x:|:ok:|
|file3.js|file1.js|file2|:x:|:x:|:x:|
|file3.js|file1.js|./file2|:x:|:ok:|:x:|
|file3.js|file1.js|file2.js|:x:|:x:|:x:|
|file3.js|file1.js|./file2.js|:x:|:ok:|:x:|
|file3.js|./file1.js|file2|:x:|:x:|:ok:|
|file3.js|./file1.js|./file2|:x:|:ok:|:ok:|
|file3.js|./file1.js|file2.js|:x:|:x:|:ok:|
|file3.js|./file1.js|./file2.js|:ok:|:ok:|:ok:|
|./file3.js|file1|file2|:x:|:x:|:x:|
|./file3.js|file1|./file2|:x:|:x:|:x:|
|./file3.js|file1|file2.js|:x:|:x:|:x:|
|./file3.js|file1|./file2.js|:x:|:x:|:x:|
|./file3.js|./file1|file2|:x:|:x:|:ok:|
|./file3.js|./file1|./file2|:ok:|:x:|:ok:|
|./file3.js|./file1|file2.js|:x:|:x:|:ok:|
|./file3.js|./file1|./file2.js|:ok:|:x:|:ok:|
|./file3.js|file1.js|file2|:x:|:x:|:x:|
|./file3.js|file1.js|./file2|:x:|:ok:|:x:|
|./file3.js|file1.js|file2.js|:x:|:x:|:x:|
|./file3.js|file1.js|./file2.js|:x:|:ok:|:x:|
|./file3.js|./file1.js|file2|:x:|:x:|:ok:|
|./file3.js|./file1.js|./file2|:x:|:ok:|:ok:|
|./file3.js|./file1.js|file2.js|:x:|:x:|:ok:|
|./file3.js|./file1.js|./file2.js|:ok:|:ok:|:ok:|

## 3つとも成功したパターン
|main|from|to|browserify|webpack|rn-packager|
|---|---|---|---|---|---|
|file1.js|file1.js|./file2|:ok:|:ok:|:ok:|
|file1.js|file1.js|./file2.js|:ok:|:ok:|:ok:|
|./file1.js|./file1.js|./file2|:ok:|:ok:|:ok:|
|./file1.js|./file1.js|./file2.js|:ok:|:ok:|:ok:|
|file3|./file1.js|./file2.js|:ok:|:ok:|:ok:|
|./file3|./file1.js|./file2.js|:ok:|:ok:|:ok:|
|file3.js|./file1.js|./file2.js|:ok:|:ok:|:ok:|
|./file3.js|./file1.js|./file2.js|:ok:|:ok:|:ok:|


# 導き出された結論
- `to` は `./` で始める必要がある
- `from` は `.js` で終わる必要がある
- `from` は、それが`main`を置き換えるものであるなら、`main`と同じ値である必要がある
- `from` は、それが`main`を置き換えないものであるなら、どんなパターンでもよい
- `from` は、`main`と同じでない場合は`./`で始める必要がある

## 結論を覚えられなかった人へ
**全部`./`と`.js`をつけよう！mainもね！**

mainにだけ付け忘れるとうまくいかないパターンがあるという罠があったわけです。


# 実験レポジトリ

[shinout/bundler-comparison](https://github.com/shinout/bundler-comparison)

これをcloneして、自身の手で確認してみてください (要 Node v6)。

[Generatorを駆使して組み合わせのループをうまく実装している例](https://github.com/shinout/bundler-comparison/blob/master/src/pkg-json-generator.js)にもなっています。
ご参考に。 [昔書いたGeneratorの記事](http://qiita.com/shinout/items/1cb7bf5eded9370f983f) も見てみてください。