LoginSignup
212

More than 5 years have passed since last update.

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

Last updated at Posted at 2014-02-08

プラグイン開発に関する詳細な情報は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);

};

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
212