JavaScript
Node.js
gulp
gulpplugin

gulpプラグインの基本構造(プラグイン開発者向け)

More than 5 years have passed since last update.

プラグイン開発に関する詳細な情報は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)前に一回だけ呼び出される。通常は最後にまとめて行う処理を実装する。省略可能。



gulp plugin basics.png


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);

};