cakeを使ってcoffeeファイルが変更されたら自動で再コンパイルさせる

node.jsのドキュメントを眺めていたらfs.watchとfs.watchFileというのを見つけた。それぞれファイル名やディレクトリ名を渡すと、それが更新されるとコールバックを起動してくれる関数みたい。

前回の記事で、cakeを使ってビルドするtaskを作ったので、ファイルを監視して、それが変更されたら自動で再コンパイルするtaskを追加してみる。

前回の記事のCakefileの末尾に次のコードを追加する

task 'watch', 'Watch source files and build changes', ->
  util.log "Watching for changes in #{srcCoffeeDir}"

  fs.watch srcCoffeeDir, (event, filename) ->
    util.log "Saw change in #{srcCoffeeDir}/#{filename}"
    invoke 'build'  # build taskを起動する

このタスクを起動するには

$ cake watch
22 Jan 23:24:53 - Watching for changes in src/coffee

ターミナルをもう一つ立ち上げるなどして、src/coffee内のファイルを変更すると

22 Jan 23:25:08 - Saw change in src/coffee/undefined
22 Jan 23:25:08 - Building src/js/main.js
22 Jan 23:25:08 - Appending 3 files to src/coffee/main.coffee
22 Jan 23:25:08 - [1] intro.coffee
22 Jan 23:25:08 - [2] main.coffee
22 Jan 23:25:08 - [3] outro.coffee
22 Jan 23:25:08 - Compiled src/js/main.js

とbuild taskが起動されて、ログが見れる。ここで、一番上がSaw change in src/coffee/undefiedになっているのが問題で、コールバックのfilenameが正しく渡されるのはWindowsとLinux環境だけで、OSXだとundefinedが渡される。今後解決するらしいけど。

なので、ここではfs.watchではなく、fs.watchFileを使うことにした。以下のように変更

task 'watch', 'Watch source files and build changes', ->
  util.log "Watching for changes in #{srcCoffeeDir}"

  for file in coffeeFiles then do (file) ->
    fs.watchFile "#{srcCoffeeDir}/#{file}.coffee"
                , (curr, prev) ->
      if +curr.mtime isnt +prev.mtime
        util.log "Saw change in #{srcCoffeeDir/#{file}.coffee"
        invoke 'build'

これで問題なくログにどのファイルの変更を検知して再コンパイルを行なっているのかが表示される。