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
を分割して管理されているようなものに使われています。
var requireDir = require('require-dir');
requireDir('gulp/tasks', {recurse: true});
recurse: true
はサブフォルダも読み込ませたいときに必要。
実際にファイル分割するときのディレクトリ構成についてはgulp-starterで雰囲気がつかめるとおもう。
del
ファイルを削除します。ビルド前にディレクトリ内のファイルを削除するのに使われています。
gulp.task('clean', function() {
return del(['dist\**\*']);
});
複数のファイルをglobによるパターンマッチングで除外設定をすることもできて、例えば.gitkeep
などの.git
から始まるファイルを削除したくないときには以下のようになます。
gulp.task('clean:dist', function() {
return del(['dist/**/*', '!dist/.git{,/**}'], { dot: true });
});
gulp-rename
ファイルのリネームをします。ただ単純にリネームをするというよりも、prefixやsuffixをつけたりといった使い方をすることが多い。
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を扱うことができます。
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を使うを参照
- webpack-stream: gulpでWebPackを使うを参照
- gulp-uglify: JavaScrptのminify
- gulp-eslint: Lint
CSS
- gulp-postcss: PostCSS
- critical
- gulp-sass-inheritance: gulp-cachedでimportしているファイルを遡って変換する
最近はPostCSSを使っていて自分でgulpfileを書くようなときにSCSSもLESSを使うことはまずない。minifyなんかもPostCSSでCSSWringやcssnanoを使えばいいんじゃないかとおもう。PostCSSについては別記事に書いてます。
critical
Critical-path CSSを作成します。
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に変換します。
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に変換します。ファイル名を引数に取れるので複数のファイルに対応できます。
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に変換する必要があります。
例えば次のような記述はエラーになります。
browserify(src)
.bundle()
.pipe(source(src))
.pipe($.uglify())
.pipe(gulp.dest(dist));
これはstreamを渡しているせいで、これを動作させるには次のようにvinyl-bufferを使うといい。
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
タスクの実行順序を指定します。並列/直列に処理順序を指定できるので便利。
gulp.task('build', function(cb) {
runSequence('clean', ['js', 'less'], cb);
});
lazypipe
別々のタスクで同じ流れの処理があるとき、それぞれのタスクで同じ記述を避けたいときに使う。使いそうであまり使った記憶がない。
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
ストリームにある特定ファイルだけを処理したいときによく使う。
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が連続する場合には素直にこちらを使ったほうがいい。ただし、記述には若干の注意が必要。
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を使わないというわけでもない。
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時のエラーで監視が止まらないようにします。
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.
などのプレフィックスがつたパッケージをまとめてロードします。このページのすべてのサンプルで使用しています。
var $ = require('gulp-load-plugins')();
gulp-tap
結構色々と使える場面はありそうだけれども、自分が使うときはストリームのにあるファイルパスを取得したいとき。どういうときにファイルパスが必要になるかというと
- components
- foo-button
- foo-button.css
- foo-button.jade
- foo-button.js
- foo-button
例えばこういったディレクトリ構成で、/components/foo-button/foo-button.css
に変更があったときinclude
しているfoo-button.jade
の変換も必要になってくる。
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でオプションを作っても忘れるので使うことはまずない。
var argv = require('minimist')(process.argv.slice(2));
var release = !!argv.release;
console.log('[RELEASE]', release);
gulp --rerease
でrelease
がtrueになります。
gulp-webserver
サーバをたてられることに加えてライブロードも可能。
gulp.task('serve', function() {
return gulp.src('build')
.pipe($.webserver({
livereload: true,
directoryListing: true,
open: true
}));
出力先のフォルダを監視しておけば、ビルド終了後にブラウザを再読み込みしてくれます。