プラグイン開発に関する詳細な情報はgulpの公式ドキュメントを参照のこと。また、開発したプラグインを公開する場合はgulp公式によるプラグイン開発ガイドラインは必読(MUST readと書かれている)。README規約も見ておくとよい。
(2014/2/9追記:プラグイン開発ガイドラインの日本語訳を公開)
なお、gulpプラグインはnode.jsパッケージの一種なので、公開する際の手順やpackage.jsonの記述方法などはnode.jsのドキュメントを参照のこと。
プラグインの基本構造
- gulpプラグインの実態(i.e. pipeに渡すもの)はnode.jsのStream.Transformのサブクラス
- Stream.Transformとは、ストリームからの入力を受け取り、加工し、ストリームに出力するもの
- Stream.Transformのサブクラスを直接作って実装しても良いが、gulpプラグインではStream.Transformラッパーであるthrough2を利用するのが一般的
- through2がストリームの入出力部分を面倒見てくれるので「加工」の実装に注力するだけ
- through2のオブジェクトを生成する時に「加工」用の関数を2つ渡す
- transformFunction:入力ストリームからファイルを受け取る度に呼び出される。通常はファイル毎に行う処理を実装する。省略不可。
- flushFunction:出力ストリームへの書き込み(flush)前に一回だけ呼び出される。通常は最後にまとめて行う処理を実装する。省略可能。
transform関数
- 関数抜ける前に必ずcallbackを呼び出すこと(callback呼ばないとプラグイン呼び出し側に'end'イベントが発生しない)
- 出力ストリームへの書き出しはthrough2.push(file)を呼び出す(複数ファイル書き出ししてもいい)
- transformに渡されるfileはnull / buffer /streamの3種類ある
- null:入力ストリームからデータを受け取らなかった場合
- buffer:入力ストリームのデータを1ファイル分バッファリングしてからtransformに渡される場合
- stream:入力ストリームのデータが順次transformに渡される場合
flush関数
- 関数抜ける前に必ずcallbackを呼び出すこと(callback呼ばないとプラグイン呼び出し側に'end'イベントが発生しない)
- 出力ストリームへの書き出しはthrough2.push(file)を呼び出す(複数ファイル書き出ししてもいい)
サンプルプラグイン
サンプルとして、入力ストリームから2つのJSONファイルを受け取り、diffをとってその結果を出力するプラグインを作ってみる。
- diffの処理自体はnode.jsパッケージのJsonDiffPatchを利用(出力の仕様などはJsonDiffPatchに従う)
- プラグインに渡す引数は「出力ファイル名」のみ
var gutil = require('gulp-util');
var through = require('through2');
var jsonDiff = require('jsondiffpatch');
module.exports = function (outputFileName) {
var file1, file2; // diff対象のファイル
/*
transform関数:ファイル毎に呼び出される
*/
function transform(file, encoding, callback) {
// ファイルがnullの場合
if (file.isNull()) {
// 次のプラグインに処理を渡すためにthis.push(file)しておく
this.push(file);
// callback()は必ず実行
return callback();
}
// ファイルがstreamの場合(このサンプルプラグインはstreamに対応しない)
if (file.isStream()) {
// emit('error')を使って、プラグイン呼び出し側に'error'イベントを発生させる
this.emit('error', new gutil.PluginError('gulp-diff', 'Streaming not supported'));
// callback()は必ず実行
return callback();
}
// fileをローカル変数に保持しておく
if (!file1) {
file1 = file;
}
else if (!file2) {
file2 = file;
}
else {
// 入力ファイルが3つ以上指定された場合はエラーとする
this.emit('error', new gutil.PluginError('gulp-diff', 'File too much'));
}
// callback()は必ず実行
callback();
}
/*
flush関数:flush前に呼ばれる
*/
function flush(callback) {
// ファイルが2つ指定されなかった場合
if (!file1 || !file2) {
// エラーとする
this.emit('error', new gutil.PluginError('gulp-diff', 'File too less'));
}
// ファイルの内容(file.contents)をJSONオブジェクトに変換
var json1 = JSON.parse(file1.contents.toString('utf8'));
var json2 = JSON.parse(file2.contents.toString('utf8'));
// diff実行
var diff = jsonDiff.diff(json1, json2);
// 出力ファイルを生成(新規ファイル生成にはgulp-utilのFileを利用する)
var output = new gutil.File({
cwd: file1.cwd,
base: file1.base,
path: file1.base + outputFileName,
});
// ファイルのコンテンツにはnode.jsのBufferを使う
output.contents = new Buffer(JSON.stringify(diff));
// ファイルを出力ストリームに流す
this.push(output);
// callback()は必ず実行
callback();
}
/*
through2オブジェクトを生成してreturn
*/
return through.obj(transform, flush);
};