vendor prefix, minify, 開発/本番用ビルドの切り替え... CSSのあれこれをGulpにさせてみた

普段業務ではタスクランナーにGruntを使っているのですが、別件で簡単なアプリケーションを作ることになりました。

小さいプロジェクトにはGruntよりGulpがいいんじゃない？とか
今流行ってるし一回使ってみるかとか

そういう気分で試してみたので、メモを兼ねて紹介します。


## GruntとGulpの違い

**[Grunt](http://gruntjs.com/)**：「設定ファイル」に近い
箇条書きのようにつらつらと設定を書いていくため、タスクの流れを読みづらく、膨大になりやすいという欠点がある。（もちろんとても便利ですが）
（今のプロジェクトではcoffeeで書いてるのに700行くらいあって泣いてる）

**[Gulp](http://gulpjs.com/)**：処理を「プロセス」として記述
あれをこうしてここに入れる、と処理が一連のプロセスになるので、読みやすい。
あとなんか速いらしい。（曖昧）

大体このくらいの認識です。


## バージョンとか

* node: v0.10.29

下記をpackage.jsonに追加して```$ npm install```

```json:package.json
"devDependencies": {
  "gulp": "3.8.9",
  "gulp-pleeease": "1.0.4",
  "gulp-ruby-sass": "0.7.1",
  "minimist": "1.1.0"
}
```


## gulpにさせるタスク

簡単なアプリケーションなのでさっくり作りたい。楽したい。

作りながら必要そうなものを追加していったところ、vendor prefixどうしよう？と思い、

* [gulp-autoprefixerよりもいい感じ。gulp-pleeeaseを使ってcssを処理しよう](http://isee-web.net/?p=593)

こちらの記事を見て、ああなんか良さそう〜と思い[gulp-pleeease](https://github.com/danielhusar/gulp-pleeease)を使ってみました。

今回やるのは以下の２つ。

* vendor prefixの自動付加
* 開発時と本番時での圧縮の切り替え

（あとjsもちょっとやってるけどconcatしかまだしていないので省略）


## 1. vendor prefixの自動付加

### vendor prefixに関する悩み

ベンダープリフィックスを付加することで、CSS3で対応予定（正式には未対応）＆ブラウザで先行的に採用されている機能を使うことができます。

でも意外に早く使えるようになっているので、例えば[```border-radius```とか今はほぼprefixなしでも動きます](http://caniuse.com/#search=border-radius)。
去年入社直後に実装した時には「普通角丸にはprefixつけるよね」と教えてもらったのに…。

SCSSでせっせとprefixを書き加えたり、我流のmixinを使ってprefixをつけていましたが、それって結構無駄なんじゃ…？と、呆然としました。（```border-radius```にはいらないけど```display: flex```には必要…と、ものによって対応状況がまちまちなので、mixinからは「webkit」を消すことができない…）

~~というか現実問題、その頃書いた「今動いている」コードを今更直す機会なんて起こり得ない。~~

そんなブラウザの対応状況にいちいち、人力で対応してられません。
なら、ツールに任せちゃおう、というのが「Pleeease」。


### Pleeeaseって？

[Pleeease](http://pleeease.io/)は

> Pleeease is a CSS post-processor. It simplifies the use of preprocessors and helps keep stylesheets as small as possible for a better maintenability. 

ふんわり意訳すると

* SASSとかstylusとかのコードを簡潔にしてくれる
* スタイルシートをできるだけメンテナンスコストを低く運用する補助をしてくれる

まさに「運用」開発者向け。

色々機能はあるけどそれはサイトを見てもらうということで。

[gulp-pleeease](https://github.com/danielhusar/gulp-pleeease)は、[Pleeease](http://pleeease.io/)をGulpで使えるようにしたものです。


### vendor prefixは「ポスト」プロセッサーでつける

Pleeeaseでは、[Autoprefixser](https://github.com/postcss/autoprefixer)を使ってvendor prefixを付加しています。
これは、[Can I use...](http://caniuse.com/)で今必要なprefixを付加してくれる、CSSポストプロセッサー、らしい。
（SCSSはCSSにする「前に」書くものだから**プリ**プロセッサーと言うのに対して、CSSに対して「後から」処理を行うものを**ポスト**プロセッサーと言うっぽい。たぶん。）

```css:css
.hoge {
  display: flex;
}
```

と書いてAutoprefixerを通せば

```css:css
.hoge {
  display: -webkit-box;
  display: -webkit-flex;
  display: -ms-flexbox;
  display: flex;
}
```

と展開してくれる。（2014/11/10現在、```last 4 versions```指定時）

SCSSにせっせと書いていたprefixを、CSSに直接「必要なものだけ」書き加えてくれる。なにこれ超便利。


### 使い方

```js:gulpfile.js
gulp.task('css', function() {
  return gulp.src('assets/stylesheets/app.scss')
    .pipe(sass({ style: 'expanded' }))  // CSS展開のスタイル選択
    .pipe(please({
      autoprefixer: {"browsers": ["last 4 versions", "Android 2.3"]}
    }))
    .pipe(gulp.dest('build/assets/css/'));  // 出力先
});
```

対応バージョンはプロジェクトに応じて・時期に応じて切り替わるので、その都度対応すべきバージョンを書き換えるだけで、prefixを全て書き換えて出力してくれる、というのが最大の魅力。

詳しくは[Autoprefixer - Browsers](https://github.com/postcss/autoprefixer#browsers)を参照して下さい。


#### 検証

※ 2014/11/10現在の検証です。

[```display: flex```をCan I use...で見たとき](http://caniuse.com/#search=flex)、Android 4.4.4はSupportedになっており、Androidの２つ前のバージョン（4.3）ではprefixつきでSupportedになっていました。
最新〜４つ前までのバージョンまでを実際にビルドして出力を比較してみました。

ビルド前のソースは全て下記。

```scss:assets/stylesheets/app.scss
.hoge {
  display: flex;
}
```

* バージョンを各ブラウザの3つ前まで対応させたとき

```js:gulpfile.js（一部抜粋）
    .pipe(please({
      autoprefixer: {"browsers": ["last 4 versions"]}
    })
```

```css:build/assets/css/app.css（出力）
.hoge {
  display: -webkit-box;
  display: -webkit-flex;
  display: -ms-flexbox;
  display: flex;
}
```

* バージョンを2つ前まで対応させたとき（1つ前も同じだった）

```js:gulpfile.js（一部抜粋）
    .pipe(please({
      autoprefixer: {"browsers": ["last 2 versions"]}
    })
```

```css:build/assets/css/app.css（出力）
.hoge {
  display: -webkit-flex;
  display: -ms-flexbox;
  display: flex;
}
```

* バージョンを最新のものだけ対応させたとき

```js:gulpfile.js（一部抜粋）
    .pipe(please({
      autoprefixer: {"browsers": ["last 1 versions"]}
    })
```

```css:build/assets/css/app.css（出力）
.hoge {
  display: -webkit-flex;
  display: flex;
}
```

ちゃんと指定バージョンごとにprefixが切り替わってますね。
Android 4.4.4ではprefixは不要ですが、Safariではprefixが必要とのことで、```-webkit-```がついているものと思われます。


## 2. 開発時と本番時での圧縮の切り替え

スマホ向けサービス開発者にとっては、**ソースはなるべく小さく速く。**ということで、普段のプロジェクトでは、

* 開発時には（デバッグ用に）圧縮していないソース
* 本番リリース時には圧縮（改行やコメントを消して１行に）したソース

を利用しています。

簡単なアプリケーションと言えど、一応お勉強用にそちらも試してみました。


### gulp-pleeeaseで圧縮/非圧縮を切り替える

gulp-pleeeaseを使うと、デフォルトでは圧縮されてしまいますが、開発時にはちょっとつらい。
開発時には非圧縮のまま、でもvendor prefixはつけてほしいからpleeeeaseの処理をかませたい。

minifierの設定をtrue/false切り替えるだけ…なんですが。

色んなサイトで書いている方法でやってみても何か反映されない。
Pleeease公式では専用の設定ファイルを使うのでちょっと書き方が違うっぽい。
gulpとかpleeeaseのバージョンによるのかな？と思いつつ、試してみました。

結論としては

```js:gulpfile.js
gulp.task('css', function() {
  return gulp.src('assets/stylesheets/app.scss')
    .pipe(sass({ style: 'expanded' }))  // CSS展開のスタイル選択
    .pipe(please({
      minifier: false // 圧縮しない
    }))
    .pipe(gulp.dest('build/assets/css/'));  // 出力先
});
```

pleeeaseに渡すオブジェクトで```minifier: Boolean```を設定すれば圧縮・非圧縮が切り替わりました。```optimizers```って何だったんだろう…。


### コマンド入力でGulpに引数を渡したい

開発用ビルド（非圧縮）と本番用ビルド（圧縮）を切り替えたい。ので、コマンドライン入力でビルド環境を切り替えたい。Gulpではどうやるんだろう…と思って調べてみると、Gulpのレシピ（[Pass arguments from the command line](https://github.com/gulpjs/gulp/blob/master/docs/recipes/pass-arguments-from-cli.md)）に書いてあったのでそのまま拝借。

[minimist](https://www.npmjs.org/package/minimist)っていうのを使うといけるっぽい。ぽい。

```js:gulpfile.js
var minimist = require('minimist'); // CLI入力を解析

var knownOptions = {
  string: 'env',
  default: { env: process.env.NODE_ENV || 'development' } // NODE_ENVに指定がなければ開発モードをデフォルトにする
};

// コマンドラインの入力を解析
var options = minimist(process.argv.slice(2), knownOptions),
    isProduction = (options.env === 'production') ? true : false;

console.log('[build env]', options.env, '[is production]', isProduction);
```


```zsh
$ gulp
[build env] development [is production] false
...(略)
```

おお、いい感じ。

これを使い、先ほどのminifierの設定を切り替えて、

```zsh
$ gulp --env production   // production build
$ gulp   // development build
```

で、圧縮・非圧縮を切り替えることができました。

---


## CSSのあれこれをGulpにさせてみた

完成ソースを晒してみます。
もっとこうすればいいよ、とかあればご教授いただけると幸いです。


```js:gulpfile.js
/**
 * requires
 */
var gulp = require('gulp'),
    minimist = require('minimist'), // CLI入力を解析
    please = require('gulp-pleeease'),
    sass = require('gulp-ruby-sass'); // output styleを選べるので、gulp-sassではなくこちらを採用

/**
 * settings
 */
var knownOptions = {
  string: 'env',
  default: { env: process.env.NODE_ENV || 'development' } // NODE_ENVに指定がなければ開発モードをデフォルトにする
};
// コマンドラインの入力を解析
var options = minimist(process.argv.slice(2), knownOptions),
    isProduction = (options.env === 'production') ? true : false;

console.log('[build env]', options.env, '[is production]', isProduction);

/**
 * tasks
 */
gulp.task('css', function() {
  return gulp.src('assets/stylesheets/app.scss')
    .pipe(sass({ style: 'expanded' }))  // CSS展開のスタイル選択
    .pipe(please({
      autoprefixer: {"browsers": ["last 4 versions", "Android 2.3"]},  // ベンダープレフィックス対応バージョンの指定
      minifier: isProduction // ビルド環境に応じて圧縮
    }))
    .pipe(gulp.dest('build/assets/css/'));  // 出力先
});

gulp.task('watch', function() {
  gulp.watch('assets/stylesheets/**/*.scss', ['css']);
});

gulp.task('default', ['css']);
```


### 使い方

#### 本番用ビルド（圧縮）
```zsh
$ gulp --env production
```

#### 開発用ビルド（非圧縮）
```zsh
$ gulp
```

#### 開発中に変更を監視して開発用ビルド
```zsh
$ gulp watch
```

## 参考リンク

* [gulp-autoprefixerよりもいい感じ。gulp-pleeeaseを使ってcssを処理しよう](http://isee-web.net/?p=593)
* [Can I use...](http://caniuse.com/)
* [gulp-pleeease](https://github.com/danielhusar/gulp-pleeease)
* [Pleeease](http://pleeease.io/)
* [Autoprefixser](https://github.com/postcss/autoprefixer)
* Gulp Document/recipes: "[Pass arguments from the command line](https://github.com/gulpjs/gulp/blob/master/docs/recipes/pass-arguments-from-cli.md)"
* [minimist](https://www.npmjs.org/package/minimist)