gulpで普段使っている(使っていた)モジュールをまとめてみました。
本当はHTML/CSS/JSや関連ツールのモジュールも載せたかったのですが、長くなりそうなのでユーティリティとして使っているものだけを先に記事にしました。
一応、ここで紹介するgulpプラグインに関してはブラックリストに入っていない事を確認済みです。
ちなみに、Qiita内だと下記の記事がまとまっていて大変参考になると思います。
サンプルのGitHub
https://github.com/yuichiroharai/gulp-sample
ここで使用しているサンプルは全てGitHubにて公開しています。
実際に実行してみるとイメージが掴みやすいかと思います。
gulp-plumber
エラーをハンドリングしてプロセスが落ちるのを防ぎます。
var gulp = require("gulp");
var plumber = require("gulp-plumber");
var uglify = require("gulp-uglify"); // JSの圧縮
gulp.task("gulp-plumber", null, function() {
// 文法エラーのあるJSファイルを読み込む
return gulp.src("./src/txt/mistake_js.txt")
// 何らかの処理の前に挟んでおく
.pipe(plumber())
.pipe(uglify())
.pipe(gulp.dest("./dest/js/"));
});
恐らく最初に覚えるgulpモジュールのひとつ。
gulpは基本的にエラーが発生するとプロセスごと落ちてしまいます。
特にファイル監視などのタスクはプロセスを継続する必要があるので、エラーで落としたくない場合に使います。
何はともあれ、とりあえず挟んでおくと良いです。
gulp-notify
デスクトップ通知を行います。
var gulp = require("gulp");
var plumber = require("gulp-plumber");
var notify = require("gulp-notify");
var uglify = require("gulp-uglify"); // JSの圧縮
gulp.task("gulp-notify", null, function() {
// 文法エラーのあるJSファイルを読み込む
return gulp.src("./src/txt/mistake_js.txt")
// 何らかの処理の前に挟んでおく
.pipe(plumber({errorHandler: notify.onError('<%= error.message %>')}))
.pipe(uglify())
.pipe(gulp.dest("./dest/js/"));
});
gulp-plumber
と組み合わせる事で、エラーが起きた時にデスクトップ通知を出す事ができます。
※エラーが大量発生した場合は通知が止まらなくなる場合もあるので要注意です(笑)。
参考
require-dir
ディレクトリ以下のJSファイルをまとめて読み込みます。
var gulp = require("gulp");
var requireDir = require("require-dir");
// ディレクトリ以下を再帰的に検索
requireDir("./gulp/task", { recurse: true });
// 別ファイルに定義したタスクをデフォルトに設定
gulp.task("default", ["compile", "watch"]);
var gulp = require("gulp");
// タスクを定義
gulp.task("compile", function() {
// ...
});
var gulp = require("gulp");
// タスクを定義
gulp.task("watch", function() {
// ...
});
require-dir
を使うとディレクトリ内のJSファイルを自動で読み込んでくれます。
上記サンプルのようにgulpのタスクをファイル分割できるので非常に便利です。
読み込まれる側でmodule.exports
したり、読み込む側で個別にrequire
する必要がないのも簡単で良いですね。
オプションにrecurse:true
を渡すとディレクトリ以下を再帰的に辿った全てのファイルが対象になります。
参考
run-sequence
タスクを同期処理で順に実行できます。
var gulp = require("gulp");
var runSequence= require("run-sequence");
gulp.task("run-sequence", null, function(callback) {
runSequence(
"first",
["second-1", "second-2", "second-3"],
"third",
// タスク完了を知らせる
callback
);
});
// 以下、各タスクを定義
gulp.task("second", ["first"])
のような形でタスクの依存関係を利用して同期処理を実現する場合、secondを実行すると必ず先にfirstが実行されてしまいます。
それぞれを独立したタスクとして定義しつつも、別のタスクの中で同期処理で実行したい場合に使っています。
gulp-rename
ストリームのファイルパスを変更します。
var gulp = require("gulp");
var rename = require("gulp-rename");
var ejs = require("gulp-ejs"); // EJSをHTMLにレンダリング
gulp.task("gulp-rename", null, function() {
// 文字列指定
gulp.src("./src/ejs/index.ejs")
.pipe(ejs({text: "Hellow World."}))
.pipe(rename("index.html"))
.pipe(gulp.dest("./dest/01/")); // ./dest/01/index.html
// オブジェクト指定
gulp.src("./src/ejs/*.ejs")
.pipe(ejs({text: "Hellow World."}))
.pipe(rename({
dirname: "02",
prefix: "pre-",
suffix: "-suf",
extname: ".html"
}))
.pipe(gulp.dest("./dest/")); // ./dest/02/pre-*-suf.html
// 関数指定
gulp.src("./src/ejs/*.ejs")
.pipe(ejs({text: "Hellow World."}))
.pipe(rename(function(path){
path.dirname = "03";
path.basename = "pre-" + path.basename + "-suf";
path.extname = '.html';
}))
.pipe(gulp.dest("./dest/")); // ./dest/03/pre-*-suf.html
});
文字列指定は1つのファイルに対して、オブジェクト/関数指定は複数ファイルに対して使うのが基本かと思います。
オブジェクト指定だけでも柔軟に変更できるので、たいていは関数を書かなくても事足りています。
gulp-if
ストリームの処理を条件によって分岐します。
var gulp = require("gulp");
var gulpIf = require("gulp-if");
var uglify = require('gulp-uglify'); // JSの圧縮
var beautify = require('gulp-beautify'); // JSの整形
function transformJS(src, dest, isMinify) {
return gulp.src(src)
.pipe(gulpIf(isMinify,
// trueの場合は圧縮する
uglify(),
// falseの場合は整形だけする
beautify()
))
.pipe(gulp.dest(dest));
}
// 以下、タスクを定義
主にサンプルのように処理を関数化して使いまわす時に利用しています。
真偽値以外にも関数、正規表現などを渡して判定できるようなので、ストリームの中身に応じて分岐させる事も可能です。
※関係ないですけど予約語なのでvar if
と書けないのが残念です(笑)。
gulp-ignore
ストリームの処理を条件によって中断します。
var gulp = require("gulp");
var ignore = require("gulp-ignore");
var minifyCSS = require("gulp-minify-css"); // CSSの圧縮
gulp.task("gulp-ignore", null, function() {
return gulp.src("./src/**/*.*")
// excludeは関数がtrueを返したら除外
.pipe(ignore.exclude(function(file){
// 拡張子が.cssじゃなかったらtrueを返して除外
return file.path.search(/.css$/) == -1;
}))
// .cssファイルだけを圧縮
.pipe(minifyCSS())
.pipe(gulp.dest("./dest/"));
});
最初からgulp-src
にCSSファイルだけを渡せば良いのですが、あくまでサンプルとしてこうしています。
過去に使っていたgulpプラグインがストリームにファイルを追加するものだったので、そういった場合に追加されたファイルを除外するのに使ってました。
最近は使っていないですが、gulp-if
と合わせて覚えておくと良いと思います。
gulp-if
と同じ作者のプラグインですので、こちらも真偽値、関数、正規表現などで判定できます。
gulp-filter
ストリームの中身をグロブパターンによって抽出したファイルだけにします。
var gulp = require("gulp");
var filter = require("gulp-filter");
var uglify = require("gulp-uglify"); // JSの圧縮
var minifyCSS = require("gulp-minify-css"); // CSSの圧縮
// 後で復活させるためにフィルターを変数に格納しrestoreオプションをtrueに
var jsFilter = filter("**/*.js", {restore: true});
var cssFilter = filter("**/*.css", {restore: true});
gulp.task("gulp-filter", null, function() {
return gulp.src(["./src/**/*.js", "./src/**/*.css"])
// JSを残して圧縮
.pipe(jsFilter)
.pipe(uglify())
// JSを復活
.pipe(jsFilter.restore)
// CSSだけを残して圧縮
.pipe(cssFilter)
.pipe(minifyCSS())
// CSSを復活
.pipe(cssFilter.restore)
// JSとCSSを出力
.pipe(gulp.dest("./dest/"));
});
サンプルでは先にJSファイルだけを抽出して処理を実行し、その後でCSSファイルだけを同様に処理しています。
一度除外したファイルをもう一度復活させられるのがこのプラグインの特徴ですね。
これによって、通常ならストリームを複数に分けるような処理も1本にする事ができます。
gulp-if
でも同じような事はできるので結局は使っていませんが、参考までに載せてみました。
child_process.exec
コマンドを実行します。
var gulp = require("gulp");
var exec = require("child_process").exec;
gulp.task("child_process-exec", function(callback) {
// ここにコマンドを書く
var cmd = "node ./js/index.js";
// 作業ディレクトリを指定してコマンドを実行
exec(cmd, { cwd:"./src/" }, function (error, stdout, stderr) {
if (error) console.log("error: " + error);
if (stdout) console.log("stdout: " + stdout);
if (stderr) console.log("stderr: " + stderr);
// タスク完了を知らせる
callback();
});
});
利用しているツールによってはgulpプラグインが無い場合もあります。
また、プラグインがあっても開発が止まってて古かったり、バージョンアップで使い方が大きく変わったりして仕様を追いかけるのが面倒な事もあります。
そんなわけで、プラグインに頼らずにツールのコマンドを直接叩きたい場合に使っています。
※ちなみにnpmインストールは不要です。
【おまけ】gulp-exec
ストリーム中のファイル毎にコマンドを実行できるプラグインがあるようです。
まだ試していませんが、一応紹介しておきます。
fs
ファイルやディレクトリの操作を行います。
var gulp = require("gulp");
var fs = require("fs");
// JSONファイルを読み込んで、オブジェクトにパース
var obj = JSON.parse(fs.readFileSync("./src/json/config.json", { encoding:"utf8" }));
gulp.task("fs", function(callback) {
// オブジェクトを整形して出力
console.log(JSON.stringify(obj, null , "\t"));
callback();
});
fs
モジュールはnode.jsの標準機能ですが、その中のfs.readFileSync
で簡単にファイルの同期読み込みができるので重宝しています。
gulpのプラグインはオブジェクトでオプションを渡せるものが多いので、JSONで外部ファイル化しておくと便利です。
また、後で説明するファイル内に挿入/置換するテキストとして読み込むのもオススメです。
※こちらもnpmインストールは不必要です。
strip-json-comments
JSON文字列からJavaScriptコメントを除去します。
var gulp = require("gulp");
var fs = require("fs");
var stripJsonComments = require("strip-json-comments");
// JSONファイルを読み込んで、コメントを除去してからオブジェクトにパース
var obj = JSON.parse(stripJsonComments(fs.readFileSync("./src/json/config_comment.json", { encoding:"utf8" })));
gulp.task("strip-json-comments", function(callback) {
// オブジェクトを整形して出力
console.log(JSON.stringify(obj, null , "\t"));
callback();
});
JSONはXMLと違ってコメントを記述できませんが、このモジュールがあればJavaScriptと同じ形式のコメントを書いてもパースする直前で取り除けるのでとても便利です。
fs.readFileSync
とセットで使っています。
gulp-strip-json-comments
ストリーム中のJSONテキストからJavaScriptコメントを除去します。
var gulp = require("gulp");
var stripJsonComments = require("gulp-strip-json-comments");
gulp.task("gulp-strip-json-comments", function() {
return gulp.src("./src/json/*.json")
.pipe(stripJsonComments())
.pipe(gulp.dest("./dest/json/"));
});
上記のstrip-json-comments
のgulpプラグイン版です。
ストリーム内のファイルに対してコメントが取り除けます。
gulp-replace
ストリーム中のテキストを正規表現で置換します。
var gulp = require("gulp");
var replace = require("gulp-replace");
gulp.task("gulp-replace", null, function() {
return gulp.src("./src/js/*.js")
/* /// で始まるコメント行だけを削除 */
.pipe(replace(/^\/\/\/[^\n]*$\n?/gm, ""))
.pipe(gulp.dest("./dest/js"));
});
サンプルのように特定の行を削除するのに使ったりしています。
正規表現を使いたい時はこれで事足りるかと思います。
gulp-header / gulp-footer
ストリーム中のファイルの最初/最後にテキストを挿入します。
var gulp = require("gulp");
var header = require("gulp-header");
gulp.task("gulp-header", null, function() {
return gulp.src("./src/js/*.js")
.pipe(header("// Copyright (c) <%= year %> Yuichiroh Arai.\n\n", { year:(new Date()).getFullYear() } ))
.pipe(gulp.dest("./dest/js"));
});
主にライセンス表記を追加するのに使ったりしています。
サンプルのようにEJSのようなテンプレートタグも使えるので便利です。
fs.readFileSync
で読み込んだファイルを使ってテキストを挿入するのもオススメです。
gulp-footer
も使い方は同じです。
gulp-concat
ストリーム中のファイルを結合します。
var gulp = require("gulp");
var concat = require("gulp-concat");
gulp.task("gulp-concat", null, function() {
return gulp.src(["reset.css", "clearfix.css", "index.css"], { cwd:"./src/css/" })
.pipe(concat("index.css", { newLine: "\n\n" }))
.pipe(gulp.dest("./dest/css/"));
});
多くの場合は順番指定が必要だと思いますので、gulp.src
に配列で渡すのがポイントです。
サンプルのようにオプションで作業ディレクトリを変えるとファイルの記述が楽になりますね。
結合するファイルが多い場合は、JSONファイルでリストを用意してfs.readFileSync
で読み込む使い方も便利です。
event-stream.merge
ストリームのマージを行います。
var gulp = require("gulp");
var eventStream= require("event-stream");
gulp.task("event-stream", null, function() {
return eventStream.merge(
gulp.src("src/js/*.js")
.pipe(gulp.dest("dest/js/")),
gulp.src("src/css/*.css")
.pipe(gulp.dest("dest/css/"))
);
});
並行する複数のストリームを1つにまとめられます。
こうする事で、並列で処理されているストリームが全て終わった後にタスクが完了します。
gulp-typescript
ではストリームがJSファイルと型定義(.d.ts)ファイルに分岐するので、再度まとめるのに使っています。
del
ファイル/ディレクトリを削除します。
var gulp = require("gulp");
var del = require("del");
gulp.task("del", function(callback) {
// 作業ディレクトリの外側は指定しない
del("./dest/**/*", { force:false }, function (error, paths) {
if (error) {
console.log("error: " + error);
} else {
console.log("----- deleted -----\n" + paths.join("\n"));
}
// タスク完了を知らせる
callback();
});
});
サンプルではやっていませんが、gulpの作業ディレクトリの外側を対象にしたい場合は、オプションでforce:true
を指定する必要があります。
当然ながら、間違って大事なファイルを削除しないように気をつけてください。
こういった理由もあって、以前はプロジェクトフォルダにgulp
フォルダを作ってそこを作業ディレクトリにしていたのですが、最近はプロジェクトフォルダ直下を作業ディレクトリにするようにしています。
そんなわけで今回は以上になります。
今後も増えてきたら追記していきます。
また、HTML/CSS/JSや関連ツールのモジュールについては別の機会にまとめれればと。
ではでは。