PHPerが学ぶ〜GulpでSassを自動コンパイルする方法〜


##Gulpとは？
[Gulp](https://gulpjs.com/)はNode.jsをベースとしたビルドシステムヘルパーです。

![gulp-thumb.png](https://qiita-image-store.s3.amazonaws.com/0/140727/24e1e09a-edf4-5507-8f61-680692c35af2.png)


###タスクランナーとビルドシステムヘルパーの違い

タスクランナーの[Grunt](https://gruntjs.com/)とビルドシステムヘルパーのGulpの目的は同じです。
どちらも、いろいろな機能（プラグイン）を使い、目的とする成果物を求める一連の処理（タスク）を実行することです✌️
ただ、そのアプローチの仕方が両者で少々異なります。

- Gruntの場合

タスク＝プラグインといった感じに、
その単一のタスク（プラグイン）を実行させるのが主な役割です。
単一のタスクを組み合わせて実行することで、ビルドという大きい目標に辿り着くというアプローチになります。
要するに、それぞれのタスクはそれぞれで完結しているってこと😇

- Gulpの場合

プラグインはあくまでも入力に対して処理を行うためのものです。
GulpのタスクはGruntでいうところのビルドに近いもので、タスクの中でいろいろなプラグインを組み合わせることで、タスク＝ビルドを行うといったアプローチを取っています。
要するに、入力されたものに対して処理を実行して行き、１つの流れの中でビルドを行うアプローチってこと😇


###Gulpの特徴

Gulpの一番の特徴は**「ストリーミングビルドシステム」**です。
「ストリーミングビルドシステム」とは、ファイルの処理をストリームで行うことです。
この特徴によって複雑なタスクも細かくカスタマイズして書くことが可能になるわけです。

※ストリームがどのようなものかは、後述します。


##Gulpインストール

npmでのパッケージ管理の説明は省きます。
まずは、プロジェクトに移動して、`package.json`（プロジェクトの設定ファイル）を作成

```linux-config
$ cd プロジェクト
$ npm init
```

###インストール

ローカルにインストール（後で`npm scripts`に登録）

```linux-config
$ npm install gulp --save-dev
```

###Sassのプラグインインストール

Sassをコンパイルするためのプラグイン`gulp-sass`をインストール。

```linux-config
$ npm install gulp-sass --save-dev
```

##タスクを書く

###gulpfile.jsの作成

まずは、プロジェクトフォルダに`gulpfile.js`というファイルを作成。
このファイルがGulpの設定ファイルです。ここに実行するタスクを記述していきます。
※`gulpfile.js`は`package.json`と同じディレクトリに置いてください。

###Gulpの設定

`gulpfile.js`に以下を記述。

```javascript:gulpfile.js
// gulp core
var gulp = require('gulp');
// sass compiler
var sass = require('gulp-sass');

// task
gulp.task('sass', function () {
  gulp.src('src/style.scss')
    .pipe(sass())
    .pipe(gulp.dest('dist/'));
});
```
*説明*
- `require`でGulp本体とプラグインを呼び出す
※プラグインを追加して使用するたびに呼び出してください。
- `gulp.task('タスク名', 処理)`でタスクを定義
- `gulp.src('対象ファイル')`で読み込むファイル(.scss)を指定
- `pipe()`で実行する処理を書きます
- `gulp.dest('出力先')`で処理を行ったファイルの出力先を指定

`scr/style.scss`ファイルに対してSassをコンパイルするプラグインを実行 → 最後に指定したディレクトリに出力という流れです。
`pipe()`でそのタスクで実行する処理をつなげていく感じですね。😤

###ストリーム(stream)について
ざっくり言うと、**データの流れ**です。
データ（結果のオブジェクト）をバトンタッチのように次の処理へと流していくみたいな感じ😇
各処理を実行した結果（ストリーム）を`pipe()`によって次の処理に渡すことで、
複数の処理を簡潔にまとめることが可能というわけです。
それぞれの処理の間に新たに処理を追加したい、といった場合には、`pipe()`を増やすだけ！
タスクが何をするのか、直感的に書くことができるからわかりやすいですね！😇

###Gulpのメリット
GruntではタスクをJSON形式で宣言的に記述していくんですが、
宣言的な記述はタスクの内容がパッと見でわかりやすい反面、タスク同士を組み合わせるのが難しいです😭
タスクが増えていくと、それぞれの関連性などを把握しきれなくなるみたい💦
もうGrunt使うことないだろうけど、一応タスクランナーとの違いは覚えて損はないかな。。。


一方、Gulpでは処理したファイルをストリームによって次の処理に渡すことで、手続き的に記述することができます。
「アレをしてからコレをする」というタスクをわかりやすく、より簡潔に書くことができます。
GulpのAPIはたいていコールバック関数を取れるようになっているので、「タスク実行後に何かする」ってのも書けます👍✨

つまり、Gulpでは**タスク**と**処理を行う機能**が分離できるので、
タスクの持っている役割をより明確にすることができるメリットがあります！😎


###MiniMatch

`gulp.src('対象ファイル')`で読み込むファイルの指定パターンは以下があります。
プロジェクトのファイル構成によって変えてください。


|MiniMatchパターン|説明|
|:-----------------|:------------------|
|'src/style.scss'|特定のファイルを指定|
|'src/*.scss'|特定のファイル形式を指定その1(srcディレクトリ直下にある`.scss`ファイル)|
|'src/**/*.scss'|特定のファイルを指定その2(sassディレクトリ以下にあるすべての`.scss`ファイル)|
|['src//.scss','!src/sample//*.scss]'|特定ファイルの除外(src/sample以下にある`.scss`ファイルを除く、srcディレクトリ以下の`.scss`ファイル)|


###ファイルの変更を監視

`gulpfile.js`に以下を追加。

```javascript:gulpfile.js
//...省略
gulp.task('default', ['sass'], function () {
  gulp.watch('src/**/*.scss', ['sass']);
});
```
`gulp.watch()`でファイルの保存を監視し、
変更があれば`sass`のタスクが自動で実行されるタスクを追加しています。
`default`のタスクを作っている理由としては、`$ gulp`コマンドを実行するだけで`default`タスクを実行することができます。
よく使うタスクは`default`タスクにしましょう😎



###ビルド実行

```linux-config
$ gulp sass
```
グローバルにインストールしていれば、上記のようにgulpコマンドで実行できるんですが、
今回は`package.json`にコマンドを追加。


```javascript:package.json
"scripts": {
  "build": "gulp"
}

```

npmでビルド実行
`$ gulp タスク名`で実行したいタスクだけを指定できます。
指定しない場合は`default`タスクが実行されます。

```linux-config
$ npm run build
```

無事にコンパイルされたcssファイルが出力されたか確認してみてください😪


##現場で使える設定

現場で使える設定を以下にまとめてみました。参考になれば幸いです。
別途必要なものはカスタマイズしてみてください。


###プラグインインストール

```linux-config
$ npm install gulp-autoprefixer gulp-plumber gulp-notify gulp-clean-css browser-sync --save-dev
```


###Gulpの設定

サンプルです。

```javascript:gulpfile.js
// gulp core
var gulp = require('gulp');
// sass compiler
var sass = require('gulp-sass');
// auto prifix
var autoprefixer = require('gulp-autoprefixer');
// error handling
var plumber = require('gulp-plumber');
// notify
var notify  = require('gulp-notify');
// clean css
var cleanCSS = require('gulp-clean-css');
// server
var browser = require("browser-sync");

// task
gulp.task('server', function () {
  browser({
    server: {
      baseDir: "./"
    }
  });
});

gulp.task('sass', function () {
  gulp.src('src/style.scss')
    .pipe(plumber({
      errorHandler: notify.onError('Error: <%= error.message %>')
    }))
    .pipe(sass())
    .pipe(autoprefixer())
    .pipe(cleanCSS())
    .pipe(gulp.dest('dist/'))
    .pipe(browser.reload({stream:true}));
});

gulp.task('default', ['server', 'sass'], function () {
  gulp.watch('src/**/*.scss', ['sass']);
});
```

*説明*

- gulp-autoprefixer
CSSのベンダープレフィックス付与を自動化(※gulp-pleeeaseの方が良いかも😏)

- gulp-plumber
watch中にエラーが発生するとwatch自体が停止してしまうため、タスクが強制停止することを防止

- gulp-notify
コマンドラインではエラーに気づきにくいためエラーが発生したときにデスクトップに通知

- gulp-clean-css
CSSファイルの圧縮

- browser-sync
LiveReload環境構築　→　プロジェクトに`index.html`置いて[http://localhost:3000](http://localhost:3000)にアクセしてみてください。

他にもたくさんあるので、案件ごとに必要なプラグインを追加していきながらカスタマイズしていく感じかな。
単純にsassをビルドするような場合には、簡単で使いやすそうでした。
だけど、jsの依存関係も解消するとなると、結局webpackを使うことになるんですけどね😷💦