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 互換のコードが生成されたかどうかを確認する、という形でだいぶマシになるだろう。

参考

PHPアプリの依存packageをpearで管理する

いつもちゃんと pear で管理しろよと言っている wtnabe です。おはようございます。

以前 pfm を使って pear package を作れるようになってからずいぶん経つんですが、実はこれまで依存 package を管理していませんでした。自分の作っているものはスタート時点では依存 package がややこしくならないようにするためのライブラリだったのですが、だんだんとできることが増えくるにしたがって依存 package も増えてきました。

package.xml を書くのは面倒

そこで pear package で依存 package をどう記述するのかを調べたのですが、当然ながら XML という非人間的なフォーマットで定義されているので、とてもサッと書ける気がしません。どうしよう、YAML で書いて package.xml に merge するツールでも書こうか、とか思ったりもしていたのですが、面倒です。だいたいあんまり生産的な感じがしない。

しかし先日、ついに気がつきました。

pfm に依存 package を定義するインターフェイスがあるじゃん。

使ってなかったから見落としてただけでした。

じゃあ話は簡単、とっとと定義してしまおう。add と delete しかできない豪快仕様だけど、XML 手打ちよりは多少マシ。

で、依存 package を解決できるようになってハタと思いつきました。

これでアプリ(サイト)単位で利用してる package を管理できるじゃん。

複数サイトのメンテナンスは面倒

複数のアプリ(サイト)をメンテしていると環境の準備がだんだん煩雑になってきます。こっちはこれとこれに依存していて、こっちはあれとそれに依存していて、えーとじゃあこの機械には結局何と何が入っていればいいんだっけ?という状態になるわけです。作っているときは気にならないけど、久しぶりにいじらなきゃいけなくなったときにこれではかなり困ります。ドキュメントがあればまだマシですが、手作業で準備しなきゃいけないのは面倒です。常にオリジナルから全 copy というスタイルもあるようですが、portable じゃないのであまり好きになれません。

そこで思いついたのが

依存 package だけを定義した package があればいんじゃね?

という方法だったのですが、結論から言うとこれは無理でした。

ダミーファイルを用意して package を作る

だったら話は簡単、内容のないダミーのファイルを用意すればいいんです。具体的にはアプリ(サイト)を識別する名前で空のファイルを作ります。

<contents>
  <dir baseinstalldir="." name="/">
  <file baseinstalldir="."
        md5sum="d41d8cd98f00b204e9800998ecf8427e"
        name="APP_NAME"
        role="data" />
  </dir>
</contents>

最低一つはこうした形で中身が必要になります。

名前とインストール先

名前はライブラリをインストールしたいアプリケーションの名前でいいんじゃないかと思います。この際、拡張子なしでファイルを作ると pear directory の中の data/ というディレクトリに入ります。

以下のような感じ。

|-- data
 `-- app_name   アプリ名
   `-- APP_NAME ファイル

具体的な pfm での作業の様子

まずは空のファイルを作る

作業のディレクトリを掘って、空のファイルを用意します。

$ mkdir app_name
$ cd app_name
$ touch APP_NAME

pfm で package の基本情報を入力

具体的にどのように作業するのか、ログを晒していきます。

 $ rlwrap pfm .

rlwrap は必須じゃないですが、あるとないとで作業効率が全然違います。ぜひ入れておきましょう。

PEAR Package File Manager Command Line Tool

Please enter the location of your package [.]*: .

Creating a new package file ...

Enter the base install directory*: .

Enter the name of the package [app_name]*:

Channel or URI based package? [c] (c,u)*: u

Enter the package URI*: http://example.com

Enter a 1 line summary*: meta package

Enter a description* (2 blank lines to finish):
meta package for dependency packages


Enter the release version*: 0.1.0

Enter the API version [0.1.0]*:

Choose a release stability [alpha] (alpha,beta,stable)*: beta

Choose an API stability [beta] (alpha,beta,stable)*:

Enter any release notes* (2 blank lines to finish):
initial release


Enter the minimum PHP version [5]*:

Enter the minimum PEAR Installer version [1.4.0]*:

Please choose a license from one of the following options

    1) Apache
    2) BSD Style
    3) LGPL
    4) MIT
    5) PHP

Please choose an option: 2

How many maintainers?*: 1

What type of maintainer is #1? [lead] (lead,developer,contributor,helper)*:

Enter maintainer #1's name*: name

Enter maintainer #1's username*: name

Enter maintainer #1's email [name@php.net]*: name@example.com

ここまでで以下のような package の元データができます。

PEAR Package File Manager Command Line Tool

    1. Package name                 [app_name]
    2. Channel/URI                  [URI: http://example.com]
    3. Summary                      [meta package]
    4. Description                  [meta package for dependency packages]
    5. Maintainers
    6. Version                      [Release: 0.1.0 API: 0.1.0]
    7. Stability                    [Release: beta API: beta]
    8. License                      [BSD Style]
    9. Notes                        [initial release]
   10. Dependencies
   11. Tasks
   12. Regenerate contents
   13. Echo package file to stdout
   14. Save & Quit
   15. Quit without saving          (ctrl-c)

Please choose an option from the menu:

ここで 10 を選んで依存 package を定義していきます。

依存 package の定義

すると dependencies 用の menu が現れます。今回は試しに Pear にある Net_URL_Mapper を定義します。

Edit Dependencies

    1. Return to main menu

    2. Add new dependency
    3. Clear all dependencies

    4. Change PHP >= 5
    5. Change PEAR Installer >= 1.4.0

    Dependencies:


Please choose an option from the menu: 2

Dependency type [pkg] (pkg,ext,php,prog,os,sapi,zend)*:

Dependency name*: Net_URL_Mapper

Is the dependency (o)ptional or (r)equired [r] (o,r)*: r

Package type (c)hannel or (u)ri [c] (c,u)*: c

Dependency channel [pear.php.net]*:

Minimum version:

Maximum version:

Edit Dependencies

    1. Return to main menu

    2. Add new dependency
    3. Clear all dependencies

    4. Change PHP >= 5
    5. Change PEAR Installer >= 1.4.0

    Dependencies:

    Required Package dependency "Net_URL_Mapper" - pear.php.net

こんな感じで依存 package が定義できました。あとはメインメニューに戻って

   14. Save & Quit

を選びます。できあがった package.xml のうち dependencies の部分だけ抜き出すと以下のようになっています。

<dependencies>
 <required>
  <php>
   <min>5</min>
  </php>
  <pearinstaller>
   <min>1.4.0</min>
  </pearinstaller>
  <package>
   <name>Net_URL_Mapper</name>
   <channel>pear.php.net</channel>
  </package>
 </required>
</dependencies>

まぁ慣れれば手でも書けそうですけどね…。一度定義すればそんなに変更することはないですからね。慣れによる精度向上を望めない作業は直接の手書きよりも pfm のようなインターフェイスで制限を設けて作業した方がいいように思います。

URI package は拡張子抜きで

[2010-07-13 追記]

Manual :: package.xml 2.0 における依存性の指定

に書いてある通りなんですが、

<package>
  <name>Foo<name>
  <uri>http://www.example.com/Foo-1.3.0</uri>
</package>

こんな感じですね。恐らくほぼ間違いなく .tgz を省略することになると思います。

beta package の依存性を解決する

pear パッケージマネージャはデフォルトでは beta バージョンについては依存関係を解決できません。一つ一つ手で入れる必要があります。しかしちゃんと検証して使えることが分かっていれば beta でも自動で入ってくれた方が嬉しいです。これは

pear config-show | grep preferred_state

が stable ではなく beta になっていると beta も自動解決できるようになります。

package.xml の中で特定の依存性に関しては beta でも解決するとか、install コマンドのオプションとか、そういう方法がないのが痛いですねぇ、pear は。channel package だけは beta でもインストールを継続するための記法があるんですが、依存 package についても beta でもインストールできるようにするオプションというものがありません。この辺は正直気が効いてない印象ですね。

まぁ、pear は cpan などと違って自由がないのが特徴なんですけど。

Googleカレンダーってfeedでも読めたんだね

チョー今さらな話。

Google Calendar って feed で取得できたんだなぁ。

読むだけでも、先々の予定と今日の予定とか切り替えながら確実に確認するためには通常のカレンダー表示の方がいい。でもとりあえず先々どんな予定があるのかなーとチラ見できる程度でいいなら feed の方が扱いやすい。

そもそも自分は予定を作るのが好きじゃない、思いつきドリブンなタイプなので Google Calendar はいくつか Ruby とか OSS などのカレンダーを表示させてみておぉすげぇと言っておしまいだったんだけど、今後はちょこちょこ検索して feed を利用させてもらおうと思った。うむ。なかなか便利な感じだ。しかしほんといろんなカレンダーがあるな。多くの人がこれ入力してくれてるんだね。ありがたいことだ。

年中無休のはずじゃ

年中無休のはずのキャラバンサライ金沢保古本店。閉店の 20:00 前に行ったが店は閉まっていた。いったい何があったとゆーーーのだーーぁーあーx−あー。

もうコーヒー切れたんだよ。勘弁してよ。

About

例によって個人のなんちゃらです

Recent Posts

Categories

Tool 日々 Web Biz Net Apple MS ことば News Unix howto Food PHP Movie Edu Community Book Security Text TV Perl Ruby Music Pdoc 生き方 RDoc ViewCVS CVS Rsync Disk Mail FreeBSD Cygwin PDF Photo Zebedee Debian OSX Comic Cron Sysadmin Font Analog iCal Sunbird DNS Linux Wiki Emacs Thunderbird Sitecopy Terminal Drawing tDiary AppleScript Life Money Omni PukiWiki Xen XREA Zsh Screen CASL Firefox Fink zsh haXe Ecmascript PATH_INFO SQLite PEAR Lighttpd FastCGI Subversion au prototype.js jsUnit Apache Trac Template Java Rhino Mochikit Feed Bloglines CSS del.icio.us SBS qwikWeb gettext Ajax JSDoc Rails HTML CHM EPWING NDTP EB IE CLI ck ThinkPad Toy WSH RFC readline rlwrap ImageMagick epeg Frenzy sysprep Ubuntu MeCab DTP ERD DBMS eclipse Eclipse Awk RD Diigo XAMPP RubyGems PHPDoc iCab DOM YAML Camino Geekmonkey w3m Scheme Gauche Lisp JSAN Google VMware DSL SLAX Safari Markdown Textile IRC Jabber Fastladder MacPorts LLSpirit CPAN Mozilla Twitter OpenFL Rswatch ITS NTP GUI Pragger Yapra XML Mobile Git Study JSON VirtualBox Samba Pear Growl Mercurial Rack Capistrano Rake Win RSS Mechanize Sitemaps Android JavaScript Python RTM OOo iPod Yahoo Unicode Github iTunes God SBM friendfeed Friendfeed HokuUn Sinatra TDD Test Project Evernote iPad Geohash Location Map Search Simplenote Image WebKit RSpec Phone CSV WiMAX USB Chrome RubyKaigi RubyKaigi2011 Space CoffeeScript Nokogiri Hpricot Rubygems jQuery Node GTD CI UX Design VCS Kanazawa.rb Kindle Amazon Agile Vagrant Chef Windows Composer Dotenv PaaS Itamae SaaS Docker Swagger Grape WebAPI Microservices OmniAuth HTTP 分析基盤 CDN Terraform IaaS HCL Webpack Vue.js BigQuery Middleman CMS AWS PNG Laravel Selenium OAuth OpenAPI GitHub UML GCP TypeScript SQL Hanami Document SVG AsciiDoc Pandoc DocBook Develop Jekyll macOS Node.js Vite Heroku Transformer AI Data Cloud Wasm