gulpでSassのコンパイルやlivereloadなどを試してみた

公開

※過去記事のメンテナンスは通常やっていないのですが(当時の記録を残す意味でも)、最近閲覧してくださる方も多いので、研究も兼ねて2014年7月23日に少し追記等を行いました。

※2015年2月に書き直したgulpfileを、「My gulpfile.coffee (Early 2015) でSassのコンパイルとか」で紹介しています。

gulp-ruby-sassの記述方式の変更について追記しました。


2012年にGruntを知ってから便利に使わせてもらっていますが、最近話題になっているgulp.js(以下gulp)を試してみました。今のところGruntには大きな不満はないのですが、万が一に備えて代替手段を用意しておく方が良いと考えていること、そしてGetting started with gulp -- Mark Goodyear — Front-end developer and designer(以下Getting started with gulp)にnode.jsのStreamを学ぶのに良いとあったためです。

今回のgulpの試用では、SassのコンパイルとLivereloadの実現、そして静的サーバーを立てることを目標としました。主にGetting started with gulpを参考にしましたが、Gruntで得た知見を反映するため、適宜gulpプラグインを探して代用しました。

以下に今回gulpを試用して分かったことやTipsををまとめます。
ターミナルでgulpを実行している様子

CoffeeScriptでgulpfileを記述したい

※2014年7月23日追記:v3.7.0で標準対応したようです。プロジェクトディレクトリにcoffee-scriptをインストールし、gulpfile.coffeeを用意するのみでOKです(npm install --save-dev coffee-script)。

Gruntは{}[]が多いですが、gulpは()が多くて記述ミスをしそうなので、GruntのようにCoffeeScriptで記述できないかなと考えました。調べたところ、Using coffee-script for gulpfileにCoffeeScriptでgulpfileを作成する方法が書かれていました。

2通りある内、1.のコマンドラインでgulp --require coffee-script/registerとする方法を選びましたが、毎回長いコマンドを打つのは当然手間なので、.zshrcでaliasを設定しました。これにより、gulpとするだけでgulpfile.coffeeが利用されるようになりました。

~/.zshrcや~/.bashrc
alias gulp="gulp --require coffee-script/register"

なお、CoffeeScript利用の際は、プロジェクトディレクトリにcoffee-scriptをインストールして下さい(npm install --save-dev coffee-script)。

※後に掲載するサンプルファイルでは、どの環境でも手軽に試すことができるよう、2.の方法を採用しています。

プラグインをまとめてロードしたい

本格導入した際は、今回の目標の他にもJavaScriptのminify化や画像の最適化などのタスク実現のため、多数のプラグインを導入することが予想されます。15のプラグインがあれば、requireを15回書くことになります。

Gruntのload-grunt-tasksのように、プラグインgulp-*を一括で読み込んでくれるものがないか探したところ、gulp-load-tasksが見つかりました。

※2014年7月23日追記:v0.2.0でgulp-load-pluginにリネームされたようです。以下taskspluginsと置き換えるのが妥当かと思います。Web Starter Kit - Google Developersでは、$ = require('gulp-load-plugins')();と、$に格納していますね。

利用の際は、npm install --save-dev gulp-load-pluginsでインストールした後、gulpfileの最初の方に下記のように記述をします。

gulpLoadTasks = require 'gulp-load-tasks'
tasks = gulpLoadTasks()

tasksの中に各タスクが読み込まれており、tasks.autoprefixerとするとautoprefixerが、tasks.jshintとするとjshintが使えるようになります。gulp-ruby-sassのようにハイフンが含まれるプラグインは、tasks['ruby-sass']で利用できました。詳しくは、サンプルのgulpfileでご確認下さい。

※2014年7月23日追記:gulp-ruby-sassは、plugins.rubySassとキャメルケースにしないと動きませんでした。

Sassプラグインの選択

gulp-sassgulp-ruby-sassの2種類がありますが、ひとまずmore stable and feature-richなgulp-ruby-sassを選択し、npm install --save-dev gulp-ruby-sassでインストールしました。

gulp-sassはSassをCで実装したLibsassを使うため、コンパイルが非常に高速でした(4倍以上速かったです)。Libsassに興味のある方は、libsass and SassC - ぶろぐを参考にしてみて下さい。

gulp-ruby-sassの記述方式の変更について、本記事の末尾に追記しました。

watchタスクが止まる? (2014年7月23日追記)

「Sassでコンパイルエラーを出してしまった時にwatchタスクが止まる(こける)ので、gulp-plumberを入れましょう」という話を見かけました。watchタスクが止まるか試してみたのですが、コンパイルエラーを出しても画面にエラーメッセージが表示されるだけで、エラーを修正すれば継続してコンパイルされました。そういうわけで、gulp-plumberは今のところ採用していません。

gulp-autoprefixerについて

ワイルドカードの利用でエラーが発生

次のような設定でSassをコンパイルしAutoprefixerを通そうとすると、なぜかエラーとなりました。*.scssではなくbasic.scssのように明示的に指定するとエラーは発生しませんでした。

gulp.task 'styles', ->
    gulp.src 'htdocs/_scss/*.scss'
        .pipe tasks['ruby-sass']
            style: 'expanded'
            sourcemap: true
        .pipe tasks.autoprefixer 'last 2 version', 'ie 9', 'ie 8', 'ie 7', 'Firefox ESR'
        .pipe gulp.dest 'htdocs/css'
        .pipe tasks.connect.reload()

Source Mapsについて

Source Maps(ソースマップ)がずれる問題は発生しませんでした。gulp-autoprefixerでは何も設定しなくても良いのでしょうか。(Autoprefixerさえ対応していれば良いので、そうなのかもしれません。この話題に関しては、Source Mapsに対応したAutoprefixerもご覧下さい。)

静的サーバーとLiveReload

Getting started with gulpとは異なり、両方を実現するgulp-connectを導入しました(npm install --save-dev gulp-connect)。設定も上記ページに詳しく書いてあり、迷うことは無いと思います。

本プラグインは、Gruntのgrunt-contrib-connectと違い、標準でLiveReloadスニペットをHTMLにインジェクションしてくれます。よって、ブラウザにLiveReloadの拡張を入れたりする必要はありません。拡張等なしでiPhoneでもLiveReloadされます。(Gruntも少し記述を加えるだけで実現できます。)
表示しているページのHTMLをデベロッパーツールで表示している画面。LiveReloadスニペットが自動挿入されている。

※2014年7月23日追記:connect 3をベースにしたgulp-webserverに発展しているようなので移行しました。connect.reload()と書かなくても、よしなにリロードしてくれるようです。

まとめ

Gruntに慣れていれば、ドキュメントを少し読むだけでgulpに移行できることが分かりました。gulpプラグインも色々と揃っているようです。実行速度はgulpの方が若干速いようです。必要なタスクを簡潔に書くことができ、それらがサーッと実行される感じです。

現在Yeomanを利用してオリジナルのジェネレーターを作成しスキャフォールディングできるようにしているので(generator-skyward)、gulpやGrunt 0.5の発展を見て、将来移行するかどうかを検討してみたいと思います。

サンプルファイル

hideki-a/gulp-sampleに、サンプルファイル一式を用意しました。本記事の内容を気軽にお試しいただけます。

※2015年2月16日更新。

(2015年2月16日追記)gulp-ruby-sassの記述方式の変更

gulp-ruby-sass 1.0よりVinyl Adapterに似たパスの記述方法になったようです。詳細については、rubySass 1.0, plugin as a vinyl stream source #116や、rw1.0ブランチのreadme.meをご覧ください。

具体的な記述方法は、以下の通りとなります。(js2coffeeで、gulpfile.jsに変換できます。)gulp.srcにパスを書くのではなく、rubySass('[Sassファイルへのパス]')みたいにするところがポイントです。gulp-ruby-sassのソースなどを見ると、glob file source...つまり**/*.scssのような記述は現在できませんが、Sassファイルがあるディレクトリの指定(rubySass('htdocs/_scss/')とか)でもOKなので、あまり大きな問題にはならないかと。

gulpfile.coffee

# Path Settings
BASEPATH = './htdocs/'
PATHS =
    STYLES:
        SRC: BASEPATH + '_scss/'
        DEST: BASEPATH + 'common/css/'

# Load Modules
gulp = require 'gulp'
plugins = require('gulp-load-plugins')()

# Tasks
gulp.task 'styles', ->
    plugins.rubySass(PATHS.STYLES.SRC,
            style: 'expanded'
            precision: 3
            cacheLocation: '.tmp/sass-cache'
        )
        .on 'error', (err) ->
            console.error 'Sass Compile Error!', err.message
            return
        .pipe plugins.autoprefixer
            browsers: AUTOPREFIXER_BROWSERS
        .pipe gulp.dest(PATHS.STYLES.DEST)