gulp

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

More than 3 years have passed since last update.

このドキュメントは、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を思い出してください)