load-grunt-configでのGruntのファイル分割をすべてCoffeeScriptで実現する


gruntfileって肥大化するよね
個人的な解決策としては、CoffeeScriptで書きつつload-grunt-config使ってファイル分割するって感じかなと思っています。
どうしてもファイル分割する気のない方は、load-grunt-tasksあたりでせめてタスクの読み込みの省略をするって感じになるんですかね?分割した方が勝手がよいと個人的に思っているので、わかりません。

load-grunt-configってなんですか?って人のための説明

load-grunt-configは、Gruntfileを分割できるようにするGruntのライブラリです。ちいさいプロジェクトではGruntfileは1ファイルで完璧です。でもプロジェクトが大きくなるとGruntfileのメンテナンスはどうにもできなくなります。そんなときにはこのload-grunt-configを使ってください。Thomas Boytの"More Maintainable Gruntfiles"にとってもインスパイアされました。

って書いてある通りです。ちいさなプロジェクトで、というかGruntにさせていることがすくないうちは、別にひとつのgruntfile.jsですべて書いて、動いてまーす!みたいな感じでよくて。でも、プロジェクトの拡大につれて、めっちゃ大変なことになるわけですね。メンテナンスめっちゃ大変だよねって。

特徴

  • タスクごとに設定ファイルを書けます。
  • Gruntのプラグインをオートロードします。
  • YAML,CSON,CoffeeScriptをサポートしています。もちろんJSのままでもよいです。
  • エイリアスファイルに簡単にタスク登録できます。
  • 特定の設定の無視やグループ化もできます。

便利だから使うけど何で書こうか。

上記で太字にした通り、サポートしている言語が複数あって、どれを使おうか迷いますね。YAML推し感が否めないんですけど。
でも、メンテナブル的な感じにしたいと思うと、記法をそろえたいというか……。
タイトルにもあるんですけど

すべてCoffeeScriptで書かせてくださいよ!!

ということでね。一応この方針の根拠としては以下の2つということで。

  • CoffeeScriptがスキ
  • 統一されていた方がメンテナンスのコストが低い(気がする)

実際に書いてみた。

どうやって説明しようかと思ったので、簡単なサンプルを用意してみました。
GitHub - grunt-simple-frontend

  • Jadeのコンパイル
  • Stylusのコンパイル
  • CoffeeScriptのコンパイル
  • サーバーの立ち上げ
  • ファイル監視
  • Live Reload
  • Gruntにかかる時間の測定(オマケ)

説明

git cloneしたらこんな感じのディレクトリ構成になると思います。

npm installするとnode_modulesがここに加わりますね。

gruntfile.coffee

gruntfile.coffee
module.exports = (grunt) ->
  'use strict'

  # time-gruntでかかる時間を測定
  require('time-grunt') grunt

  # load-grunt-configを読み込み
  require('load-grunt-config') grunt,
    init: true

    # ディレクトリへのショートカットを設定
    data: config:
      app: 'app'
      dest: 'app/dest'
      jade: 'app/jade'
      stylus: 'app/stylus'
      coffee: 'app/coffee'
      css: 'app/dest/css'
      js: 'app/dest/js'

メインのgruntfileへの記述はこれだけです。

aliases.coffee

aliases.coffee
module.exports = (grunt) ->
  'use strict'

  grunt.registerTask 'htmltask',[
    'jade'
  ]
  grunt.registerTask 'csstask',[
    'stylus'
  ]
  grunt.registerTask 'jstask',[
    'coffee'
  ]
  grunt.registerTask 'compile',[
    'htmltask'
    'csstask'
    'jstask'
  ]
  grunt.registerTask 'default',[
    'compile'
    'connect'
    'watch'
  ]

先にcsstaskとか定義しているのは、後々にautoprefixとかを加えること想定してるからです。とはいえタスク定義しすぎもメンテナブルから遠ざかるのでプロジェクトごとに熟考するべきですね。

分割されたgruntタスクたち

  • jade.coffee
jade.coffee
module.exports = (grunt) ->
  app:
    option:
      pretty: true # コンパイル後のHTMLを整形する
    files:[
      expand: true
      cwd: '<%= config.jade %>' # gruntfile.coffeeで定義したjadeディレクトリ
      src: '**/*.jade'
      dest: '<%= config.app %>' # appディレクトリ
      ext: '.html'
    ]

jadeディレクトリにあるjadeファイルをapp直下にコンパイルしてるだけです。


  • stylus.coffee
stylus.coffee
module.exports = (grunt) ->
  app:
    files:
      '<%= config.dest %>/style.css': '<%= config.stylus %>/style.styl'
      # stylus.stylをstyle.cssにコンパイルする

style.stylに他のstylusファイルをインポートしているんでコンパイルはstyle.stylだけ。


  • coffee.coffee
coffee.coffee
module.exports = (grunt) ->
  options:
    bare: true
    sourceMap: true
  app:
    files:[
      expand: true
      cwd:'<%= config.coffee %>'
      src: '**/*.coffee'
      dest:'<%= config.js %>'
      ext: '.js'
    ]

coffeeディレクトリからdest下にあるjsファイルにコンパイル。


  • connect.coffee
connect.coffee
module.exports = (grunt) ->
  server:
    options:
      port: 5040
      hostname: 'localhost'
      open: true # 立ち上げたらブラウザで開く
      base: 'app'

localhost:5040で立ち上がる。
baseがappにしてあるのでapp直下のindex.htmlがインデックスになる。


  • watch.coffee
watch.coffee
module.exports = (grunt) ->
  options:
    spawn: false
    livereload: true # Live Reload
  jade:
    files: ['<%= config.jade %>/**/*.jade']
    tasks: ['htmltask']
  stylus:
    files: ['<%= config.stylus %>/**/*.styl']
    tasks: ['csstask']
  coffee:
    files:  ['<%= config.coffee %>/**/*.coffee']
    tasks:  ['jstask']

Live Reloadはブラウザの拡張をインストールする必要があります。上手く言ってなかったら拡張のボタンを押せばだいたいなんとかなる(雑)


今回サンプルとして用意したのはこんな感じです。gruntに何か追加するときはあたらしくgrunt/にcoffeeファイルを作成してタスクを記述していけばよいです。
ファイル分割するようになってから、メンテナンスがかなりラクになった気がしています。
あくまで今回のはサンプルとして作ったものなのでおそらくひとつのファイルなんで問題ないんですけど、実際のプロジェクトだとすくなくともこの倍以上のボリュームになると思うので、積極的にファイル分割していきたいです。

オマケ

ちなみに、これを分割しないで、JavaScriptで書いた場合はこうなるよっていうやつ。
Gistにあげておきました。

Gist - gruntfile.js

なんとなく比較用に作った方がいいかなとか思って書いたんですけど、正直これ書くのめんどくさすぎてなんで書いたんだろうって後悔してる。

最後にっていうか今後のこと?

何度も言うんですが、load-grunt-configは積極的に使っていきたい。
でも、みなさんGrunt使ってますか?って考えたとき、何事もなくgulpに移行済の人が多いと勝手に思ってます。
どっちも使っている身ですが、メインのプロジェクトがGruntで、最初は別に困らないだろとか思ってたものの、最近はgulpの方がやっぱり使いやすいんだなあって実感しているところです。gulpでファイル分割する場合は、require-dir使ってますけど、他によい方法ありますかね?

また、gulpじゃなくない?的なお話も聞くことがありますけど、まあしばらく様子見てよって気分でいます。すごいエンジニアの人が完成形みたいなもの作ったら何事もなく移行しようと思います。がんばってください。

そんな状況でもGrunt使うならload-grunt-config使っていこうって言いたいだけなんですけども。

以上です。

参考

load-grunt-configをつかってGruntタスクを分割する
load-grunt-configでGruntfileを分割するときの注意点


前記事 vagrant-triggerを使ってVagrant+Chef環境で発生するsynced_folderの問題を解決した。