gruntで更新されたファイルを対象にタスクを動かす方法
JavaScript のビルドツール grunt が熱いということで使い始めました。
Sencha Touchの場合は senchaコマンドでビルドできるのですが、それ以外のプロジェクトでリリースビルドを作るのに、sassのコンパイル、cssのminify、jsの結合とminify、そしてリリースディレクトリへのコピーなどを一括で行うために使っています。
便利なんだけど、毎回全ビルドが走るのがどうにも非効率だなぁ、と感じていました。更新されたファイルだけ対象にしてくれればいいのに。というかantとかrakeとか他のビルドツールには普通あるでしょ!
同じような事を思っている人は当然いるわけで、gruntに提案されています。しかしまだ取り込まれていないようです。→ Provide conditional support for file inclusion
ここでも言われてますが、gruntのタスクに定義するファイルには filter オプションというのがあって、タスク実行時にsrcファイルを任意の関数でフィルタリングすることができます。これを使えば、更新されたファイルだけ対象することができそうです。
と、いうわけで作ってみた。dsuket/Gruntfile.js
3つのフィルタ関数を作りました。
- newer
- srcがdestよりも新しければ true を返す。
- expandNewer
- destにディレクトリを指定して、srcの各ファイルと比較する。
- newerAny
- srcの何れかがdescよりも新しければ true を返す。
grunt のカスタムフィルタ関数は、タスク実行前に srcファイルを引数に呼ばれます。true を返せばそのファイルを対象とし、falseならば対象としません。Custom Filter Function
フィルタ関数の引数には srcしか渡されないため、destファイルがわかりません。そのため、フィルタ関数を返す関数を作成し、その引数に dest ファイルを指定するようにしました。やや冗長ですが仕方ない。
悩ましいのが、newerAny。フィルタ関数は1つのsrcファイル毎に呼び出されるため、予め全てのsrcがわかりません。そこで第2引数にsrcの定義を取るようにしました。配列でも定義できますが、文字列で与えた場合、その変数の値を取ります。(あんまりいけてない)
大体使えるようになりましたが、まだ問題はいくつかあります。
テンプレートが使えない。
テンプレートの展開は grunt.initConfig() の中で行われるので、newerのターゲットでテンプレートを使っても展開されません。
watch で newerAny が更新されない
最初のsrcファイルのフィルタ実行時にしか判定しないため、watchで起動したときは更新されません。毎回チェックするようにすればいいんですが、今のところwatchはあまり使っていないのでペンディング。
newerAny で expand が使えない。
srcファイルの展開は実行時行われるため、定義時にsrcの一覧が取得できません。
と、まぁ幾つか細かい問題がありますが、このフィルタの最大の問題点は、uglify で失敗するということです。
grunt-contrib-uglify タスクが、srcファイルが無い場合エラーで止まる問題があります。filterの結果、更新ファイルがなければ src は空になるのですが、その場合以降のタスクが止まってしまいます。他のタスクはsrcが空でも動くようになっているので、grunt-contrib-uglifyのバグだと思います。一応修正版を github にアップしました。(pull requestも出しておいた)
https://github.com/dsuket/grunt-contrib-uglify
参考: