gulpfile スタイルガイド - v0.5.0

  • 159
    いいね
  • 0
    コメント
この記事は最終更新日から1年以上が経過しています。

このドキュメントは、gulpfileの再利用性/メンテナンス性を高めることを目的とした、非公式なスタイルガイドです。

更新情報
  • 2014/10/05 - バージョン番号つけました。
  • 2014/10/04 - 「タスク辞書」あらため「タスクの共有」に。以前の内容はgulpfilesとして別ドキュメントに。

はじめに

gulpfilegulp.jsのタスクを定義するファイルです。スクリプトファイルなので自由に書ける反面、プロジェクトが大きくなると、書式を統一が問題になります。そんな時、このスタイルガイドが役に立つでしょう。対象となるのは次のようなケースです。

  • gulp.js に慣れて来て、再利用性のあるタスクを書きたい
  • チームで運用する必要があり、書式を統一したい

スタイルガイドとして、下記を参考にしています。また、ここで述べない JavaScript / CoffeeScript の言語としての記述スタイルについても、こちらを参照してください。

チェックリスト

スタイルガイドに全て目を通す前に、簡単に診断できるチェックリストを挙げておきます。後半への見出しも兼ねます。

項目 チェックすること
a.レイアウト インデントあたりスペース2つ、1行あたり最大79字、UTF-8
b.記述言語 JavaScript か CoffeeScript
c.プラグイン ブラックリスト入りはNG、インラインプラグインは適宜
d.繰り返し ファイルならgulp-foreach、配列ならmerge-stream
e.非同期処理 関数がプロミスを返す場合はプロミス、それ以外はコールバック
f.設定ファイル package.jsonにカスタムフィールド、多い場合は別ファイル
g.ファイル分割 1ファイルあたり50行程度まで、それ以上は目的別に分割
タスク名はファイル名を接頭辞にしてハイフン区切り
タスクファイルはtask/またはtasks/ディレクトリに配置
require-dirで読み込み

コードレイアウト

タブかスペースか

スペースのみを使います。1インデントあたり、2スペースとします。タブとスペースを混ぜてはいけません。

1ラインの最大文字数

すべての行は、最大79文字までに制限します。モニターがどんなに大きくても、これを越えてはいけません。

エンコーディング

UTF-8です。

モジュールの読み込み

順序変更 / コピー&ペーストしやすいように、一行ずつvarを使います。

var gulp       = require('gulp');
var browserify = require('browserify');
var source     = require('vinyl-source-stream');

以下は「悪い」例です。同じこと(モジュールの読み込み)をしているにも関わらず、行ごとの表記がことなるのは望ましくありません。JavaScriptの省略記法は、初学者を混乱させるので避けます。

var gulp       = require('gulp')
   ,browserify = require('browserify')
   ,source     = require('vinyl-source-stream');

記述言語

gulpfileは、下記のいずれかで記述します。

  • JavaScript
  • CoffeeScript

JavaScriptが「共通言語」

外部に公開する場合、プレゼンテーションで表示する場合は、JavaScriptを基本とします。また、フォーラムなどへの質問に際してもCoffeeScriptを主題としない限り、JavaScriptで投稿するべきです。

CoffeeScript

共通言語としてJavaScriptが推奨されながら、CoffeeScriptを使う理由は、「設定ファイル」としての見やすさです。gulpfileの記述にあたっての利点を下記に列挙します。

  • 括弧の省略
  • カンマの省略
  • 文字列挿入 (String Interpolation)

その他のAltJS言語

gulpfileは、その他のLiveScript他の言語で記述することも可能ですが、これはチームメンバーで合意が出来ている場合に限定されます。将来参加するメンバーについても想定に入れておくべきでしょう。

プラグインについて

ブラックリスト入りしているプラグインを避ける

プラグインは、公式ディレクトリで検索可能です。ただし、ブラックリストに入っていると表示されません。実際に使用する前に、ここで確認しておきましょう。gulp.jsのバージョン4以降は下記のコマンドでもチェックが可能になります。

$ gulp --verify

どうしても「ブラックリスト入り」を利用する必要がある場合は、その旨をgulpfileにコメントとして明記します。

インラインプラグイン

gulpfile内で、関数としてその場だけで使う「インラインプラグイン」を実装するケースがあります。

  • 再利用性が低い
  • プラグインが公開されていない
  • JavaScript APIを使う必要がある

などの場合です。下記はDuoを使う一例ですが、別関数とすることでタスク内の見通しが良くなっています。

gulp.task('script', function() {
  gulp.src('main.js')
    .pipe(duo())
    .pipe(gulp.dest('dist'))
})

function duo() {
  return map(function(file, cb) {
    Duo(file.base)
      .src(file.contents.toString());
      .run(function(err, src) {
        if (err) return cb(err);
        file.contents = new Buffer(src);
        cb(null, file);
      });
  });
}

スクリプト

繰り返し(1)

ファイルについての繰り返しには、gulp-foreachを使います。

var foreach = require('gulp-foreach');

gulp.task('zip', function(){
  return gulp.src('recipe/*')
    .pipe(foreach(function(stream, file){
      return gulp.src(
           path.basename(file.path) + '/**/*',
           { cwd: 'recipe/' }
        )
        .pipe(zip(name + '.zip'));
    }))
    .pipe(gulp.dest('download/'));
});

繰り返し(2)

配列内の繰り返しについては、merge-streamを使います。

var merge = require('merge-stream');

var data = [{ name: 'a' }, { name: 'b' }, { name: 'c' }];

gulp.task('page', function(){
  return merge(data.map(function(entry){
    return gulp.src('template.html')
      .pipe(consolidate(entry))
      .pipe(gulp.dest('dist/'));
  }));
});

※CoffeeScriptの場合は、for文の方が読みやすいでしょう。

プロミス (promise)

タスク内で非同期関数が使われる場合で、関数がプロミス(promise)を返す場合は、関数の結果をタスク内でreturnします。コールバックと、プロミスの両方に対応する関数の場合、プロミスを使うことを優先します。

gulp.task('async', function(){
  return iHaveAPromise();
});

独自のプロミス

タスク内で独自のプロミスを生成することは、可読性を下げます。特にチーム内のデザイナーにとって、次のようなコードは理解しにくいものとなります。次項のコールバックを使う方が望ましいでしょう。

gulp.task('async', function(){
  var deferred = Q.defer();

  setTimeout(function() {
    deferred.resolve();
  }, 1000);

  return deferred.promise;
});

そんなとき「then」

プロミスはthenを使って次の処理につなげられる点が便利ですが、タスク内での利用は必ずしも推奨されません。thenでタスク内の処理を長々と書くよりも、

  • タスクは十分に小さいか (特定の目的にフォーカスしているか)
  • タスクの外側でコントロールすべきではないか

を確認すべきです。連続するタスクをコントロールするために、次の方法が用意されています。

  • gulp.js v3.x: run-sequenceが利用可能です。
  • gulp.js v4.x: 標準APIに、gulp.parallelgulp.seriesが追加されました。

コールバック

タスク内で非同期関数が使われる場合で、プロミスに対応していない場合、引数としてコールバック関数を受け取り、非同期処理が完了時点でコールバック関数を呼び出します。コールバック関数であることを明示するため、callbackないしcbを一貫して使うものとします。

gulp.task('async', function(callback){
  setTimeout(function() {
    callback();
  }, 1000);
});

設定ファイル

gulpfileを複数プロジェクトで共有して、固有の値だけを変更したい場合があります。その場合は、package.jsonにカスタムフィールドを追加して記述します。設定の数が大量にある場合は、config.jsonといった名称で別ファイルに記述することが望ましいでしょう。

下記は、パッケージ名をフォントの名称にしている例です。

var meta = require('./package.json');

gulp.task('icon', function(){
  gulp.src('icon/*.svg')
    .pipe(iconfont({ fontName: meta.name })
    .pipe(gulp.dest('dist'));
});

ファイルの分割

通常のスクリプトと同様に、長過ぎるgulpfileはメンテナンス性が下がります。目的別に分割する必要があります。

gulpfileは1ファイルあたり、50行程度に留めるべきです。何行以下という制限は特に設けませんが、ひとつのファイルには、ひとつの目的のタスクのみを集約します。例えば、JavaScriptを扱うタスクと、CSSを扱うタスクは別のファイルにするべきです。

プロジェクトが大きくなると、担当者ごとに管理するgulp.jsのタスクが分かれて行きます。その際、他のメンバーが担当するタスクを不用意に変更する必要がないよう、タスクのモジュール性を維持するのは、良い戦略です。

以下、便宜的に次のように呼ぶこととします。

  • ルートファイル(root file): プロジェクトディレクトリ直下のgulpfile
  • タスクファイル(task file): タスクや目的別に分けられたgulpfile

タスクの再利用性

プロジェクトごとにタスクの組み合わせや設定が変わっても、タスクファイルはプロジェクト間で流用可能なケースが多々あります。下記の方針を立てることは、再利用性を高めるために重要です。

  • 目的ごとにタスクファイルを分ける
  • タスクファイル外のタスクに依存しない
  • シンプルに留める

1タスクごとに1タスクファイルとする必要はありません。例えばタスクファイルcoffee.jsに、

  • coffee
  • coffee-client
  • coffee-server
  • coffee-lint

といった複数のタスクを定義して構いません。これらのタスクは、必要とするモジュールも、利用されるシーンも重なっており、1ファイルにまとめるのが合理的です。

ルートファイルに含めるべきタスク

プロジェクト間で共有しにくいタスクがあります。次の3つについては、ルートファイルに含めます。

  • default
  • clean
  • watch

また、以下のケースも、ルートファイルに書くと良いでしょう。

  • 横断的に実行すべきタスク (他のgulpfileに依存するタスク)
  • アドホックなタスク

タスクファイルの置き場所

プロジェクトのルートにgulpfileが散乱するのは、望ましくありません。taskないし、tasksディレクトリにまとめます。下記は配置の例です。

  • gulpfile.js (ルートファイル)
  • task/
    • bower.js (タスクファイル)
    • css.js (タスクファイル)
    • coffee.js (タスクファイル)
    • icon.js (タスクファイル)

タスクファイルの読み込み

タスクファイルを読み込むには、require-dirモジュールを利用します。ルートファイルの先頭に下記を追加します。

var requireDir = require('require-dir');
var dir        = requireDir('./task');

タスク名は、ファイルを越えてgulp.jsのプロセス内で共有されます。そのため、この記述だけで、他のファイルに書かれたタスク('css'とか'script'とか)を呼び出すことが出来るようになります。

タスクファイルの記述

タスクファイルも、通常のgulpfileと同様に記述します。module.exports = yourFunctionの形式で書く必要はありません。

タスク名は、ファイル名と同一か、ファイル名を接頭辞とします。

対象
ファイル名 coffee.js
タスク名 coffee, coffee-client, coffee-serverなど

タスクファイルの共有

再利用を促進するには、タスクファイルをGitHubに置くのが有効です。(Dotfilesを思い出してください)