package.json の browser field 入門編

  • 36
    いいね
  • 0
    コメント
この記事は最終更新日から1年以上が経過しています。

package.json の browser field 入門編

Nodeプロジェクトのpackage.jsonには、browserというフィールドを設定することができます。
browserフィールドは、そのプロジェクトをブラウザ等のJSで使ってもらうための仕組みです。
この仕様は、browser field specに定義されています。
入門編では、この役割と基本的な機能について紹介します。
実践編では、 bundlerごとの解釈の違いと、それを回避する方法を説明します。

bundler とは

このbrowser fieldを理解するには、まずbundlerについて理解する必要があります。

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()を使うための魔法の杖

3大bundler

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

  • シンプルで単機能のbrowserify
  • タスクランナーやサーバー機能などを兼ねるwebpack
  • 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の実装のようです)。

なお実践編ではこの仕様の解釈の違いについて記載します。

手元でbundleしない時代に

browser fieldが目指す未来は、Universal JS(と思います)。

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

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

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

browserify web.js > dist/bundle.js

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

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

余談 Universal JSを意識した設計に

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

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

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