gulpでよく使うパッケージのメモ

gulpで使うプラグインは毎回大体同じなので、よく使うもののメモを残しておく。カテゴリ分けはだいぶ適当。サンプルでは断りなくgulp-load-pluginsを使っているので`$.`ではじまるものは`gulp-`のパッケージを読み込んだものになっています。

基本的にgulpのプラグインを作ったりしたことがあるわけではないので、vinylの説明なんかはちょっと怪しいかもしれない。

## ファイル

* path: パスの取得など
* slash: Windowsのパスの`\`を`/`に変換する
* path-exists: パスの存在チェック
* require-dir
* del
* gulp-rename
* gulp-concat: ファイルの結合
* gulp-useref: HTMLで指定されたファイルを結合

### require-dir

ディレクトリ内のファイルを読み込むヘルパー。何に使うのかというと`gulpfile.js`を分割して管理されているようなものに使われています。

```js
var requireDir = require('require-dir');
requireDir('gulp/tasks', {recurse: true});
```

`recurse: true`はサブフォルダも読み込ませたいときに必要。

実際にファイル分割するときのディレクトリ構成については[gulp-starter](https://github.com/greypants/gulp-starter)で雰囲気がつかめるとおもう。

### del

ファイルを削除します。ビルド前にディレクトリ内のファイルを削除するのに使われています。

```js
gulp.task('clean', function() {
  return del(['dist\**\*']);
});
```

複数のファイルをglobによるパターンマッチングで除外設定をすることもできて、例えば`.gitkeep`などの`.git`から始まるファイルを削除したくないときには以下のようになます。

```js
gulp.task('clean:dist', function() {
  return del(['dist/**/*', '!dist/.git{,/**}'], { dot: true });
});
```

### gulp-rename

ファイルのリネームをします。ただ単純にリネームをするというよりも、prefixやsuffixをつけたりといった使い方をすることが多い。

```js
gulp.task('css', function() {
  return gulp.src( 'src/style.css')
  .pipe($.minifyCss())
  .pipe($.rename({ suffix: '.min' }))
  .pipe(gulp.dest('dist'));
});
```

これで出力されるファイルのファイル名が`style.min.css`になります。

## HTML

* gulp-posthtml
* gulp-inline-source
* gulp-cheerio

### gulp-cheerio

jQueryライクにHTMLを扱うことができます。

```js
gulp.task('move', function() {
  if(watch) {
    return gulp.src(src)
      .pipe($.cheerio(function(cheerio, file) {
        cheerio('meta[http-equiv="refresh"]').first().empty();
      }))
      .pipe(gulp.dest(dist));
  } else {
    return gulp.src(src)
      .pipe(gulp.dest(dist));
  }
});
```

監視時に不要な`meta[http-equiv="refresh"]`を削除するときなどに使ったり、importするファイルの変更するときなどに使えるとおもう。実際に使った記憶はちょっとない。

昔使っていたときは使用しているcheerioのバージョンが古いせいか、日本語が数値参照になっていたのでビルドに使うのであれば注意が必要かもしれない。

## JavaScrpt

* browserify, watchify: [gulpでBrowserifyを使う](http://qiita.com/hbsnow/items/176a2213a3c144651e9a)を参照
* webpack-stream: [gulpでWebPackを使う](http://qiita.com/hbsnow/items/f9418e61133b1a0f1717)を参照
* gulp-uglify: JavaScrptのminify
* gulp-eslint: Lint

## CSS

* gulp-postcss: PostCSS
* critical
* gulp-sass-inheritance: gulp-cacheｄでimportしているファイルを遡って変換する

最近はPostCSSを使っていて自分でgulpfileを書くようなときにSCSSもLESSを使うことはまずない。minifyなんかもPostCSSでCSSWringやcssnanoを使えばいいんじゃないかとおもう。[PostCSSについては別記事](http://qiita.com/hbsnow/items/3193f7576cbec0a5765e)に書いてます。

### critical

Critical-path CSSを作成します。

```js
gulp.task('critical', function(cb) {
  critical.generate({
    base: paths.dist,
    src: 'index.html',
    dest: 'styles/critical.css',
    dimensions: [{
      width: 320,
      height: 480,
    }, {
      width: 1300,
      height: 900
    }],
    minify: true
  }, function(err, output) {
    if (err) {
      console.log(err);
    }

    cb();
  });
});
```

すべてのHTMLファイルを一つ一つ検証していくわけにもいかないので、サンプルではトップページを対象にしていますが、実際に対象にするページをどこにするかはサイトによって変わってくるとおもいます。

対象にするCSSファイルを指定することもできますが、すでにHTMLに記述してある場合には省略することができるので省略しています。widthとheightはviewportの設定になるので変更が必要であれば変更してください。

## vinyl

browserifyを使うときに使う。gulpのdataイベントでうけとれるのはvinylオブジェクトらしいので、ストリームにあるpathとかの取得には素直にgulp-tapを使うのがいいんじゃないかと思う。

* vinyl-source-stream
* vinyl-transform
* vinyl-buffer

### vinyl-source-stream

vinylのstreamに変換します。

```js
var stream = require('vinyl-source-stream');
var browserify = require('browserify');
var src = 'src/main.js';

browserify(src)
  .bundle()
  .pipe(source(src))
  .pipe(gulp.dest('dist'));
```

### vinyl-transform

vinyl-source-streamと同じくvinylのstreamに変換します。ファイル名を引数に取れるので複数のファイルに対応できます。

```js
var transform = require('vinyl-transform');
var browserify = require('browserify');

gulp.task('js', function() {
  return gulp.src('**/*.js')
  .pipe(transform(function(filename) {
    return browserify(filename, {
    debug: true
    }).bundle();
  }))
  .pipe(gulp.dest('dist'));
});
```

browserifyでファイルを複数指定するようなことはないとおもうけど、フォルダ構成をそのまま維持させたたいとかなら便利なのかもしれない。

### vinyl-buffer

vinylのstreamをbufferに変換します。vinyl-source-streamやvinyl-transformはstreamに変換するので`.pipe`で別の処理を加えるときにはこれをつかってbufferに変換する必要があります。

例えば次のような記述はエラーになります。

```js
browserify(src)
  .bundle()
  .pipe(source(src))
  .pipe($.uglify())
  .pipe(gulp.dest(dist));
```

これはstreamを渡しているせいで、これを動作させるには次のようにvinyl-bufferを使うといい。

```js
var buffer = require('vinyl-buffer');

browserify(src)
  .bundle()
  .pipe(source(src))
  .pipe(buffer())
  .pipe($.uglify())
  .pipe(gulp.dest(dist));
```

## タスク

* merge-stream: ストリームをマージする。マージしたものは並列処理される
* run-sequence
* lazypipe

### run-sequence

タスクの実行順序を指定します。並列/直列に処理順序を指定できるので便利。

```js
gulp.task('build', function(cb) {
  runSequence('clean', ['js', 'less'], cb);
});
```

### lazypipe

別々のタスクで同じ流れの処理があるとき、それぞれのタスクで同じ記述を避けたいときに使う。使いそうであまり使った記憶がない。

```js
var lessMapTasks = lazypipe()
  .pipe($.sourcemaps.init())
  .pipe($.less())
  .pipe($.sourcemaps.write());

gulp.task('build:less', function() {
  return gulp.src(src)
    .pipe(lessMapTasks())
    .pipe(gulp.dest(dist));
});
```

## ストリームの選別

* gulp-if
* gulp-filter
* gulp-cached
* gulp-changed

### gulp-if

ストリームにある特定ファイルだけを処理したいときによく使う。

```js
gulp.task('components', () => {
  return gulp.src(paths.src + '/components/**/*')
    .pipe($.if('*.js', $.uglify()))
    .pipe($.if('*.css', $.minifyCss()))
    .pipe($.if('*.html', $.minifyHtml()))
    .pipe(gulp.dest(paths.build + '/components'));
});
```

###gulp-filter

フィルターの作成。同じ条件のgulp-ifが連続する場合には素直にこちらを使ったほうがいい。ただし、記述には若干の注意が必要。

```js
var filter = $.filter('**/*.css');
gulp.task('components', () => {
  return gulp.src(paths.src + '/components/**/*')
    .pipe(filter)
    .pipe($.uglify())
    .pipe(gulp.dest(paths.build + '/components'));
});
```

gulp-ifの感覚で`$.filter('*.css')`と書いてしまうと、マッチしてほしいファイルも素通ししてしまう。フィルターを元に戻したい場合には`restore`を`true`にして`filter.restore`を書けばいい。フィルター内で`dest`したいときには`passthrough`を`false`にする。

### gulp-cached, gulp-changed

この二つの違いは前者がストリームをメモリにキャッシュして、後者はファイル比較をしているということだと思う、たぶん。内容が異なるものだけを以降のストリームに流せるので`**/*`のように大量のファイルを扱っているタスクには必須。

基本的にはgulp-cachedを使うことが多くなるとは思うんだけど、かといってgulp-changedを使わないというわけでもない。

```js
gulp.task('components:jade', function(cb) {
  gulp.src(paths.src + '/components/**/*.jade')
    .pipe($.cached('components-jade'))
    .pipe($.plumber({
      errorHandler: function(err) {
        console.log(err);
        this.emit('end');
      }
    }))
    .pipe($.jade())
    .pipe(gulp.dest(paths.build + '/components'))
    .on('end', function() {
      gulp.src(paths.build + '/components/components.html')
        .pipe($.changed(paths.build + '/components/import.html'))
        .pipe($.rename({
          basename: 'import'
        }))
        .pipe(gulp.dest(paths.build + '/components'))
        .on('end', cb);
    });
});
```

例えばこれはPolymerのコンポーネントで使っているJadeのタスクなんだけど、こういうただリネームした同じファイルを出力すときにはgulp-changedを使うことになると思う。gulp-changedのサンプルのタスクでは画像用のタスクになっていることが多いかな。

## その他

* gulp-plumber
* gulp-size: ファイルサイズの表示
* gulp-load-plugins
* gulp-sourcemaps: ソースマップの出力
* gulp-tap
* minimist
* gulp-webserver

勝手にリロードされるとウザいだけなのでbrowser-syncは使っていません。同時にいくつもブラウザで確認するようなことなんてほとんどないので。

### gulp-plumber

watch時のエラーで監視が止まらないようにします。

```js
gulp.task('jade', function() {
  return gulp.src(paths.less.src + 'main.less')
    .pipe($.plumber({
      errorHandler: function(err) {
        console.log(err);
        this.emit('end');
      }
    }))
    .pipe($.jade())
    .pipe(gulp.dest(paths.less.dist));
});

gulp.task('watch', ['jade'], function() {
  gulp.watch(paths.src + '**/*.jade', ['jade']);
});
```

### gulp-load-plugins

`gulp-`や`gulp.`などのプレフィックスがつたパッケージをまとめてロードします。このページのすべてのサンプルで使用しています。

```js
var $ = require('gulp-load-plugins')();
```

### gulp-tap

結構色々と使える場面はありそうだけれども、自分が使うときはストリームのにあるファイルパスを取得したいとき。どういうときにファイルパスが必要になるかというと

- components
    - foo-button
        - foo-button.css
        - foo-button.jade
        - foo-button.js

例えばこういったディレクトリ構成で、`/components/foo-button/foo-button.css`に変更があったとき`include`している`foo-button.jade`の変換も必要になってくる。

```js
gulp.task('components:css', function() {
  return gulp.src(paths.src + '/components/**/*.css')
    .pipe($.postcss())
    .pipe($.tap(function(file) {
      // CSSの変更があった場合、それをincludeしているJadeも変換する
      var csspath = path.parse(file.relative);
      var jadedir = slash(csspath.dir);
      varjadepath = jadedir + '/' + csspath.name + '.jade';

      gulp.src(paths.src + '/components/' + jadepath)
        .pipe($.jade())
        .pipe(gulp.dest(paths.build + '/components/' + jadedir));
    }))
    .pipe(gulp.dest(paths.build + '/components'));
});
```

ファイルの内容の書き換えとかもできそうではあるけど、ちょっと具体例はあんまり思い浮かばないかな。そういうことをしなくても別の手段で解決できることが多そう。

### minimist

コマンドの解析。自分はgulpでオプションを作っても忘れるので使うことはまずない。

```js
var argv = require('minimist')(process.argv.slice(2));
var release = !!argv.release;

console.log('[RELEASE]', release);
```

`gulp --rerease`で`release`がtrueになります。

### gulp-webserver

サーバをたてられることに加えてライブロードも可能。

```js
gulp.task('serve', function() {
  return gulp.src('build')
    .pipe($.webserver({
      livereload: true,
      directoryListing: true,
      open: true
    }));
```

出力先のフォルダを監視しておけば、ビルド終了後にブラウザを再読み込みしてくれます。

## 参照文献

* [gulp-starter](https://github.com/greypants/gulp-starter)
* [gulp + browserify, the gulp-y way](https://medium.com/@sogko/gulp-browserify-the-gulp-y-way-bb359b3f9623)