package.json の browser field 入門編

# package.json の browser field 入門編

Nodeプロジェクトの`package.json`には、`browser`というフィールドを設定することができます。
`browser`フィールドは、そのプロジェクトをブラウザ等のJSで使ってもらうための仕組みです。
この仕様は、[browser field spec](https://github.com/defunctzombie/package-browser-field-spec)に定義されています。
入門編では、この役割と基本的な機能について紹介します。
[実践編](http://qiita.com/shinout/items/911e024368e2cb29fd3d)では、 bundlerごとの解釈の違いと、それを回避する方法を説明します。
# bundler とは
このbrowser fieldを理解するには、まずbundlerについて理解する必要があります。

[browser field spec](https://github.com/defunctzombie/package-browser-field-spec)には`bundler`という用語が定義されていて、そこには
> A tool which takes a plain javascript package and creates client usable files. It may include, but is not limited to: replacing modules or files with client versions (since the client may already provide the functionality), merging all the dependencies into a single file, etc.

これを翻訳すると
> plainなJSパッケージを受け取り、`client`が利用できるファイルを生成するツール。モジュールやファイルを`client`用に置き換えたり、依存関係のあるファイル群を1つのファイルに結合したりする機能を有していることが期待される(その他の機能もあってよい)。

となります。

`client`の定義もしてあるので、興味のある方は読んでみてください。
(上記でいう`plain`というのは、おそらく**プラットフォーム依存のない**という意味だと思っています)

なお、bundlerのひとつであるbrowserifyの概念についての良い説明が下記の記事にあります。

[Browserify: それはrequire()を使うための魔法の杖](http://qiita.com/cognitom/items/4c63969b5085c90639d4)


## 3大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)

(3大と定義したのは私の主観です。)

- シンプルで単機能の`browserify`
- タスクランナーやサーバー機能などを兼ねる`webpack`
- [React Native](https://facebook.github.io/react-native/)用の`React Native Packager`

などがあります。


# 本題
## browser field は "差し替え" の指定
提供したいモジュールのなかに`fs`などbrowserでは利用できないものがあると、bundlerはうまくclient用のファイルを生成できません。
`browser`フィールドは、それらを差し替えるのに使います。

まず、web等で利用可能なものに書き換えた別ファイルを生成します(e.g. `web.js`)。

その後、


```package.json
{
  "browser": "web.js"
}
```

と記述すると、bundlerはこのモジュールのエントリを`web.js`と解釈してくれて、client用のファイル生成がうまくいきます。
余談ですが、この`web.js`のようなUniversalなJSをいかに作るか、というのは近年のJSであり重要なスキルになってきていると思います(後述)。


## 依存モジュール / ファイル だけ差し替える
> You can simply prevent a module or file from being loaded into a bundle by specifying a value of false for any of the keys.
This is useful if you know certain codepaths will not be executed client side but find it awkward to split up or change the code structure.

楽に差し替えたいなら、依存モジュールやファイルをkey-valueの形で差し替えることができます。


```package.json
{
  "browser": {
    "./file1.js": "./file2.js",
    "fs": false
  }
}
```

たとえば上記のように記述すれば、

- `./file1.js`を呼び出しているところは代わりに`./file2.js`を呼び出すようになりあす。
- `fs`モジュールがplain objectになります。(plain objectになるのは仕様ではなく、各packagerの実装のようです)。

なお[実践編](http://qiita.com/shinout/items/911e024368e2cb29fd3d)ではこの仕様の解釈の違いについて記載します。


# 手元でbundleしない時代に
browser fieldが目指す未来は、Universal JS(と思います)。

かつてはライブラリ作者は`<script>`タグで利用する用途も考えて、web版も配布していました。

```web.js
window.XXXX = require('./index')
```

こんな風にしてglobalに定義したエントリを作成し、

```sh
browserify web.js > dist/bundle.js
```

こうやって配布するパターン。

現代のJavaScriptはこのパターンを推奨していません。代わりに、 **クライアント側でバンドルして利用する**のです。
そのためにiライブラリ作者は`browser`フィールドによって、`bundler`によってブラウザ等で動くライブラリにしておく必要があります。

# 余談 Universal JSを意識した設計に
ライブラリ作者はUniversalを考慮した設計にすることで、browserでもより多くのモジュールが利用できるようになるでしょう。

- Node.jsのツール系の処理と、提供したいロジックを分離する
- HTTP clientではfetchを利用する -> [Jxckさんの尽力によりUniversalに](https://github.com/github/fetch/pull/31)
- 変数requireしない仕組みで実装する
- プラットフォーム依存な処理はインターフェイスを決めて外部から注入

このようなことに注意しつつ、bundle可能なライブラリを作っていきたいですね。