Hatena::ブログ(Diary)

naoyaのはてなダイアリー

May 04, 2013

昨今のWebアプリケーションのひな形その2 - Grunt

昨日の続き。

こういうアプリケーションのテンプレートを管理するのに便利な仕組みはないですかねーと言っていたら @teppeis さんや @omo2009 さんに GruntYeoman はどうかと教えてもらった。

Grunt はユースケースとしては JavaScript の連結や圧縮、SCSS/LESS なんかのメタ言語のコンパイルをするときに使うもの、つまり rake なんかと同じようなものと以前にチラ見した程度で知った気になっていたけども、ちょっと違っていた。Grunt は確かにタスクランナーではあるのだが、Node.js で実装している利点を十分に活かして、任意のファイルが更新されたのをトリガに一連のタスクを実行させたり、Grunt で Webサーバーを立ち上げて他のタスクと連携させたりといったことができるようになっている。プラグインの仕組みがあって、エコシステム的に結構活発に開発されているみたいだ。

Yeoman は、まだあまり調べられていないけど bower と Grunt を組み合わせて、各種ファイルのコンパイルやテストやそのランナーなどを含めて管理するインテグレーションキットみたいなものだと思う。

Grunt

Grunt の典型的なユースケースとしては、JavaScript ファイルの更新を検知してそれらを連結したり minify したりして指定したファイルとして吐き出す、というもの。

Gruntfile という、make/rake でいうところの Makefile/Rakefile みたいなものにタスクを定義する。そして grunt コマンドでタスクを実行する。Gruntfile は node.js よろしくなツールなので javascript で書くわけだけど、最近のバージョンでは coffee でいけるようになったので、Gruntfile.coffee として書く。

以下は

  • sass ファイルを更新すると css にコンパイルして連結
  • その css を minifty して app.min.css に出力
  • coffee ファイルを更新すると js にコンパイル & 連結
  • その js を uglify で圧縮して出力

ということをやっている。regarde というプラグインが、ファイル更新を監視するためのもの。

module.exports = (grunt) -> 
  grunt.initConfig
    sass:
      dist:
        files:
          'css/app.css': ['sass/base.sass', 'sass/styles.sass']
      
    cssmin:
      compress:
        files:
          'css/app.min.css': 'app.css'
          
    coffee:
      compile:
        files:
          'js/app.js': ['coffee/base.coffee', 'coffee/application.coffee']

    uglify:
      my_target:
        options:
          mangle: true
        files:
          'js/app.min.js': ['js/app.js']
          
    regarde:
      css:
        files: 'sass/*.sass'
        tasks: ['sass', 'cssmin']

      js:
        files: 'coffee/*.coffee'
        tasks: ['coffee', 'uglify']

  grunt.loadNpmTasks 'grunt-regarde'
  grunt.loadNpmTasks 'grunt-contrib-sass'  
  grunt.loadNpmTasks 'grunt-contrib-cssmin'
  grunt.loadNpmTasks 'grunt-contrib-coffee'
  grunt.loadNpmTasks 'grunt-contrib-uglify'
  
  grunt.registerTask 'default', [
    'sass',
    'cssmin',
    'coffee',
    'uglify',
    'regarde'
  ]

これで grunt を実行すると

% grunt
Running "sass:dist" (sass) task

Running "cssmin:compress" (cssmin) task
File css/app.min.css created.

Running "coffee:compile" (coffee) task
File js/app.js created.

Running "uglify:my_target" (uglify) task
File "js/app.min.js" created.

Running "regarde" task
Watching sass/*.sass
Watching coffee/*.coffee

こんな感じで各タスクが走り出す。regrde によって、sass や coffee を更新すると関連するファイルがすべて更新される。

と、ここまではファイル関してしてタスクを走らせるだけという感じだけど、そのほか各種プラグインを使うとファイルが更新されたらブラウザを自動でリロードするなんて連携ができたり、Jasmine や QUnit でテストを走らせたりといったことも可能になる。Grunt はこんな感じでタスクの実行系を中心として、メタ言語系なんかを実際にコンパイルするタスクの実装がプラグインとして用意されていて、かつリアルタイムに諸々を実行・管理できるタスクランナー・・・といったところだと思う。

で、そのプラグインの中に grunt-init というものがあって、これを使うと Grunt を使ったプロジェクトのひな形を生成できる、というものの様子。本来は grunt-init が目的のものなのかもしれないが、そこまではまだ手を動かせてない。

Grunt で livereload

てなわけで、昨日作ったひな形にブラウザの livereload の仕組みがあれば便利だなと思ったので Grunt 周りも設定しておいた。

path = require 'path'

module.exports = (grunt) ->
  grunt.initConfig
    livereload:
      port: 35729
                      
    regarde:
      views:
        files: 'views/*.*'
        tasks: ['livereload']

  grunt.loadNpmTasks 'grunt-regarde'
  grunt.loadNpmTasks 'grunt-contrib-livereload'

  grunt.registerTask 'default', [
    'livereload-start',
    'regarde'
  ]

これで Grunt を立ち上げると views 以下のファイルの更新に合わせて livereload が機能する。Google Chrome なら LiveReload 拡張 を入れると、35729 ポートで通信してよしなに面倒みてくれる。livereload の仕組みっぽいものは http://aligach.net/diary/20110925.html あたりを参照のこと。

こうなったら sass や slim や coffee のコンパイルも Sinatra 任せにせずに Grunt でやればいい気もする。その辺は必要に応じて追々対応するとする。

foreman

ところで Grunt を導入したのは良いのだけど grunt と Sinatra な Rack を両方立ち上げるのがめんどくさいので、ここは foreman の出番。Procfile に

application: bundle exec ruby app.rb
grunt: grunt

と書いて

% foreman start

で、両方が起動する。

f:id:naoya:20130504130542p:image

良い感じであります。

引き続き Grunt のライフチェンジングなプラグインがないかと、Yeoman 辺りを調べていこうかなと思います。こうやって開発環境周りのことを書いてるとまたT氏に「中年の危機」とかいって dis られるのでしょうが、気にしない。