gulpとBrowserifyでJSをビルドしてみた


# はじめに

[gulp](http://gulpjs.com/)と[Browserify](http://browserify.org/)を組み合わせてフロントエンドJSをビルドする機会があったので、一通りやってみたことをまとめてみた。

## gulpについて

Node.jsの[stream](https://github.com/substack/stream-handbook)を活用したビルドシステム、ということらしい。
Streamのおかげでビルドフローをとても直感的に記述できると感じた。
他のWebフロントエンドのビルドシステムは使ったことがないので比較はできないけど…。

## Browserifyについて

node.jsと同じように (npm、`require`などを使って) モジュール化されたフロントエンドJSが書けるツール。便利。
JSのモジュール化というと、最近は[webpack](http://webpack.github.io/)というのもあるらしい。

# 基本

```coffeescript:gulpfile.coffee
gulp = require 'gulp'
source = require 'vinyl-source-stream'
browserify = require 'browserify'

gulp.task 'build', ->
  browserify
      entries: ['./src/index.js']
    .bundle()
    .pipe source 'bundle.js'
    .pipe gulp.dest 'build'

gulp.task 'default', ['build']
```

* Browserify結果のReadable streamを`bundle()`で得る
* vinyl-source-streamで普通のstreamからgulpのstreamに
* `gulp.dest`でディレクトリに突っ込む

[gulp-browserify](https://github.com/deepak1556/gulp-browserify)というgulpでBrowserifyを扱うプラグインもあるが、gulp公式的には[非推奨らしく](https://github.com/gulpjs/plugins/issues/47)、browserifyを直接使ったほうが良さそう。

# ソースにCoffeeScriptを使う

```coffeescript:gulpfile.coffee
gulp = require 'gulp'
source = require 'vinyl-source-stream'
browserify = require 'browserify'

gulp.task 'build', ->
  browserify
      entries: ['./src/index.coffee']
      extensions: ['.coffee', '.js']
    .transform 'coffeeify'
    .bundle()
    .pipe source 'bundle.js'
    .pipe gulp.dest 'build'
```

* `.transform 'coffeeify'`で、[coffeeify](https://github.com/jnordberg/coffeeify)というCoffeeScriptサポートを追加するBrowserify用のプラグインを指定
  * 入力にCoffeeScriptが使えるようになる。
* [browserify-shim](https://github.com/thlorenz/browserify-shim)などの他のプラグインも同様に使える。

# Uglifyを使ってminify

```coffeescript:gulpfile.coffee
gulp = require 'gulp'
source = require 'vinyl-source-stream'
buffer = require 'vinyl-buffer'
browserify = require 'browserify'
uglify = require 'gulp-uglify'

gulp.task 'build', ->
  browserify
      entries: ['./src/index.coffee']
      extensions: ['.coffee', '.js']
    .transform 'coffeeify'
    .bundle()
    .pipe source 'bundle.js'
    .pipe buffer()
    .pipe uglify()
    .pipe gulp.dest 'build'
```

* さっきの出力結果に[gulp-uglify](https://github.com/terinjokes/gulp-uglify/)をpipe

これとは別に、Browserifyのプラグイン ([uglifyify](https://github.com/hughsk/uglifyify))を使ってuglifyする方法もあるようだ。

# Source maps を出力する

```coffeescript:gulpfile.coffee
gulp = require 'gulp'
source = require 'vinyl-source-stream'
buffer = require 'vinyl-buffer'
browserify = require 'browserify'
uglify = require 'gulp-uglify'
sourcemaps = require 'gulp-sourcemaps'

gulp.task 'build', ->
  browserify
      entries: ['./src/index.coffee']
      extensions: ['.coffee', '.js']
      debug: true
    .transform 'coffeeify'
    .bundle()
    .pipe source 'bundle.js'
    .pipe buffer()
    .pipe sourcemaps.init
      loadMaps: true
    .pipe uglify()
    .pipe sourcemaps.write()
    .pipe gulp.dest 'build'

gulp.task 'watch', ->
  gulp.watch path, ['compile']

gulp.task 'default', ['compile']
```

* 最初の`browserify`に`debug: true`を指定
  * これでBrowserifyの出力にはsource mapsがコメントとして含まれるようになる
* `sourcemaps.init`で`loadMaps: true`を指定
  * 使っているのは[gulp-sourcemaps](https://github.com/floridoo/gulp-sourcemaps)というプラグインで、gulp中の処理(uglifyなど)のsource mapを生成してくれる
  * `loadMaps: true`でBrowserifyの出力の中のsource mapを拾う
* `sourcemaps.write()`でsource mapsをコメントとして出力
  * 引数を指定すると別ファイルにもできる

## 追記

### watchifyで監視・インクリメンタルビルド

[watchify](https://github.com/substack/watchify)を使うと、ファイルを監視してインクリメンタルなbrowserifyビルドを行うことができる。
特に大きなプロジェクトになると全体を毎回ビルドし直すより圧倒的に速いので捗る。

gulpと組み合わせる方法の参考: https://github.com/gulpjs/gulp/blob/master/docs/recipes/fast-browserify-builds-with-watchify.md