Browserify x Babelでproductionコードを作るために分かっていなかったこと

ようやく本格的に Node.js + ES2015 ベースで JS がそこそこ快適に書けるようになってきました。

が、FAQ はちゃんと読もう!

babel/babelify: Browserify transform for Babel

思いっきり

Why aren't files in node_modules being transformed?

This is the default browserify behavior.

って書いてある!

結論

  • browserify transform は指定した順番に適用されるので、babelify と組み合わせている場合、まず babelify を通さないと例えば import などでいきなりコケる
  • brfs は babel と相性がよくないので brfs-babel を使う
  • とにかく browserify -g babelify -t babelify しろ

前提

Node.js でテストコードを書き、テストを高速に回すために適宜 Sinon などで stub を使う比較的モダンな開発が分かっていることとする。

課題

  • 依存する npm package のコードをすべて把握するのはなかなか難しい
  • 普段のテストは Node.js で動かすが production コードのターゲット環境が Node.js でない(例えばブラウザ)場合は、当たり前だが最終的にターゲット上でどう動くかは完全には分からない

対策

ブラウザなど Browserify を使うことで Node.js で開発したコードをターゲット上で動かすことができる場合、

  • browserify -g babelify -t babelify を指定することで依存パッケージまるごと Babel を通すことができる。これだけでも依存パッケージの組み合わせで悩むことはだいぶ減るはず(-g は global transform の意味)
  • eslint を使う
    • ブラウザなら eslint-plugin-compat を、Google Apps Script (で DI しないなら)eslint-plugin-googleappsscript 辺りを追加

辺りが有効な対策になりそう。

実際に困ったこと

Browserify は基本的には Node.js, CommonJS の世界のライブラリをブラウザの世界に連れてくることが仕事である。ただし、エンドポイントとなるコードから呼び出されているコードすべてに対してあらゆる transform を適用することはデフォルトの動作となっていない。確かに、IO にタッチせずロジックや加工を担う、Node.js で通常通り動くライブラリがすでにあって、これをブラウザの世界に連れてくる、というだけなら require の部分さえ解決すればだいたいはうまくいく。現実的な選択と言ってよいだろう。

これは言い換えると

  • require 対象が ES2015
  • ターゲット環境が ES5

な組み合わせで、browserify -t babelify を利用して ES5 ready なコードを生成したい場合、デフォルトでは build 済みのコードはすべてが ES 5 ready になるわけではない。

自分で書いている ES2015 なメインのコードは ES5 ready になってくれるだろう。ただし、require 先の ES2015 ready なコードは、 require の部分が解決されただけで bundle され、結果として ES2015 ready なコードが混ざったものができあがってしまう。

これを ES2015 ready な環境(例えば Node.js や最新のブラウザ)でテストしても問題は発見できない。あくまで ES 5 な環境でテストする必要がある。

とは言え、必ず ES5 環境で動かしてテストしなければいけないとなるとテストの実行コストが大きくなってしまう。ということで現実的な選択肢としては eslint で正しく ES5 互換のコードが生成されたかどうかを確認する、という形でだいぶマシになるだろう。

参考

More