かもメモ

自分の落ちた落とし穴に何度も落ちる人のメモ帳

Gulp 4 gulp.seriesとgulp.parallel

gulp v4 で追加されたgulp.seriesgulp.parallelを試してみた

  • gulp.series(...tasks) ... 順番に実行する
  • gulp.parallel(...tasks) ... 並列に実行をする

GitHubのサンプル

gulp.task('one', function(done) {
  // do stuff
  done();
});

gulp.task('two', function(done) {
  // do stuff
  done();
});

gulp.task('default', gulp.series('one', 'two', function(done) {
  // do more stuff
  done();
}));

呼び出されるタスク(関数)の引数にdoneがあり、タスクの最後で実行されています。

fn
The fn is passed a single argument, callback, which is a function that must be called when work in the fn is complete. Instead of calling the callback function, async completion can be signalled by:

  • Returning a Stream or EventEmitter
  • Returning a Child Process
  • Returning a Promise
  • Returning an Observable

Once async completion is signalled, if another run is queued, it will be executed.
出典: gulp/API.md at master · gulpjs/gulp · GitHub

これはタスクの完了を通知するコールバック関数で、タスクランナーでよくあるreturn gulp.src()...をしない場合はdone()の実行を忘れると次のようなタスクの終了がわからないというエラーが表示されます。

[00:00:00] The following tasks did not complete: mytask
[00:00:00] Did you forget to signal async completion?

gulp.series()で実行されるタスクは、この終了の合図があると次のタスクに進むので、gulp.series()内にgulp.parallel()があると並行処理されるタスク全てが完了するまで待機されます。

サンプル

"use strict";
const gulp = require('gulp');

// TASK
function task1(done) {
  console.log('task 1');
  setTimeout(function() {
    console.log('task 1 >> 1100ms');
    done();
  }, 1100);
}

function task2(done) {
  console.log('task 2');
  setTimeout(function() {
    console.log('task 2 >> 1000ms');
    done();
  }, 1000);
}

function task3(done) {
  console.log('task 3');
  done();
}

// Default
gulp.task('default', gulp.series(
  gulp.parallel(task1, task2),
  task3,
  function(done) {
    console.log('default');
    done();
  }
));

👇 実行結果

$ npx gulp
[00:10:20] Using gulpfile ~/example/gulpfile.js
[00:10:20] Starting 'default'...
[00:10:20] Starting 'task1'...
[00:10:20] Starting 'task2'...
task 1
task 2
task 2 >> 1000ms
[00:10:21] Finished 'task2' after 1 s
task 1 >> 1100ms
[00:10:21] Finished 'task1' after 1.1 s
[00:10:21] Starting 'task3'...
task 3
[00:10:21] Finished 'task3' after 305 μs
[00:10:21] Starting '<anonymous>'...
default
[00:10:21] Finished '<anonymous>' after 278 μs
[00:10:21] Finished 'default' after 1.11 s

このように、gulp.seriesで順番を指定するとタスク内にsetTimeoutのような処理があってもdoneが実行されるまで、次の処理は実行されません。

以前はrun-sequenceなどを使って順番を決めていましたが、gulp v4からはgulp.seriesgulp.parallelを使えば少しタイプ数は増えますが複雑なタスクの管理がgulpだけでできそうです!


[参考]

Gulp v4 移行メモ

Gulp v4になってv3の書き方だとエラーになるケースがあったので移行したときのメモ

gulp.series と gulp.parallel

gulp v4で追加されたメソッド

added gulp.series and gulp.parallel methods for composing tasks. Everything must use these now.

gulp v3まではrun-sequenceで処理の順番を作っていた部分をgulp.seriesgulp.parallelを使って書き換えることができます。

run-sequenceを使ってたとき

gulp.task('build', function(cb) {
  runSequence(['pug', 'css', 'js'], 'server', cb);
});

👇gulp v4な書き方

gulp.task('build', gulp.series(
  gulp.parallel('pug', 'css', 'js'),
  'server'
));

処理の順番が複雑になるとrun-sequenceの方が見通しが良いように思いますが、モジュールをインストールしなくて済む分、hugeになるnode_moduleのサイズが少し小さくなくなるかもです。(gulp4になってその分サイズが大きくなってる可能性もありそうなので...)

gulp.series, gulp.parallelで実際に実行されるタスクはgulp.task()ではなく、通常の関数形式でも問題ないようです。

var task = function(done) {
  // TASK
  done();
};
gulp.task('build', gulp.series( task ));

この場合は実行される関数名を'"で囲ってgulp.series("task")のようにすると動作しませんでした。
また、ただの関数なので、コマンドラインから

$ gulp task

のように直接関数を実行することはできません。

gulp.task

gulp.taskの第二引数の依存タスクが廃止されたようです

  • v3: gulp.task(name [, deps] [, fn])
  • v4: gulp.task([name,] fn)

gulpjs/gulp API - gulp.task

gulp v3の次のようなのクリプトはgulp v4で実行するとエラーになってしまいます。

gulp.task('default', ['server'], function() {
  // task
});

👇gulp v4な書き方

gulp.task('default', gulp.series( 'server', function() {
  // task
} ));

nameが必須ではなくなっているので、次のような書き方もできるみたいです

gulp.task(function mytask(done) {
  // task
  done();
});

gulp.watch

gulp.watchでのタスクの指定方法が変更になっていました

  • v3: gulp.watch(glob [, opts], tasks)
  • v4: gulp.watch(globs [, opts] [, fn])

gulpjs/gulp API - gulp.watch

gulp v3では配列でタスク名を指定していたのを、v4では関数で指定します。

gulp.watch(['*.js'], ['task']);

👇gulp v4

gulp.watch(['*.js'], gulp.task('task'));
// or
gulp.watch(['*.js'], gulp.series('task'));
// or
gulp.watch(['*.js'], gulp.parallel('task'));
// or
gulp.watch(['*.js'], function() {
  // task
});

gulp v3 から gulp v4 に移行する際は、とりあえずgulp.taskgulp.watchの書き方を見直せば問題なく移行できるのかなと思いました。
babelとかを利用する場合はES2015的なgulpfileの書き方もできるようです。
参考: https://github.com/gulpjs/gulp/tree/4.0

 
技術情報のアップデートが出来てなくて今更ながらgulp4について調べてたら、「gulpはもう辛い。これからはnpm-script」とか言われてたみたいで情報交換が難しいちほーの端っこで個人で仕事やってると能動的に情報取りに行って置いていかれないようにしてないとあっという間に追いつけてない間に更に色々進んでしまうのは辛み。
情報のキャッチアップは社内勉強会があるような会社組織の中の方に身を置いてた時の方が断然良かったな〜って実感しました。


[参考]

Webデザイナーの仕事を楽にする! gulpではじめるWeb制作ワークフロー入門

Webデザイナーの仕事を楽にする! gulpではじめるWeb制作ワークフロー入門

👆amazon primeにあったので観てたけど面白かったです。
おバカなギャグがありつつ、こんなチームで仕事するの楽しそうだなーっておもいながら観てました。

Gulp ディレクトリ構造を保ったままリネームしたい

ニッチ過ぎて一体どこに需要があるのか謎すぎるのですが、gulpでディレクトリ構造を保ったままリネームして出力する方法を試していたのでメモです。
stylusで試してますがpugやsass、jsファイルでも同じかと思います。

renameしない場合、パスの/**/の部分のディレクトリは出力される

// gulpfile.js
gulp.task('stylus', function() {
  let files = './stylus/**/*.styl',
      destPath = './webroot/css';
  return gulp.src( files )
    .pipe( plumber() )
    .pipe( stylus() )
    .pipe( gulp.dest( destPath ) );
});

次のようなファイル構成だったとき

/stylus
  |- main.styl
  |- /page
       |- unique.styl

👇 gulpでコンパイル

/webroot
  |- /css
      |- main.css
      |- /page
           |- unique.css

gulp.src()に渡されるパスの**に該当する部分のディレクトリがそのまま出力されます。

gulp-rename でリネームする場合

引数にリネームされるファイル名をそのまま入れるとディレクトリ構造は維持されない

// gulpfile.js
const rename = require('gulp-rename');
gulp.task('stylus', function() {
  let files = './stylus/**/*.styl',
      destPath = './webroot/css';
  return gulp.src( files )
    .pipe( plumber() )
    .pipe( stylus() )
    .pipe( rename( 'page.css' ) )
    .pipe( gulp.dest( destPath ) );
});

👇 gulpでコンパイル

/webroot
  |- /css
      |- page.css

gulp.dest で指定されたパス直下にリネームに渡したファイル名でそのまま出力されます。(stylusは2ファイルあるけど同一ファイル名で上書きされるので最終的にcssは1ファイルのみ出力されます)

function か hash を使ってリネームするとディレクトリ構造が維持される

rename via function

const rename = require('gulp-rename');
gulp.task('stylus', function() {
  let files = './stylus/**/*.styl',
      destPath = './webroot/css';
  return gulp.src( files )
    .pipe( plumber() )
    .pipe( stylus() )
    .pipe( rename(function(path) {
      path.basename = 'page';
      console.log( path.dirname ); // ディレクトリ名が入っている
    }) )
    .pipe( gulp.dest( destPath ) );
});

rename via hash

const rename = require('gulp-rename');
gulp.task('stylus', function() {
  let files = './stylus/**/*.styl',
      destPath = './webroot/css';
  return gulp.src( files )
    .pipe( plumber() )
    .pipe( stylus() )
    .pipe( rename({
      basename: 'page'
    }) )
    .pipe( gulp.dest( destPath ) );
});

👇 gulpでコンパイル

/webroot
  |- /css
      |- page.css
      |- /page
           |- page.css

gulp.src()に渡されるパスの**に該当する部分のディレクトリ名がfunctionならpath.dirname、hashならdirnameにデフォルトで入っているので、ディレクトリも構造を維持したまま出力されます。

dirname is the relative path from the base directory set by gulp.src to the filename.
gulp.src() uses glob-stream which sets the base to the parent of the first directory glob (*, **, [], or extglob). dirname is the remaining directories or ./ if none. glob-stream versions >= 3.1.0 (used by gulp >= 3.2.2) accept a base option, which can be used to explicitly set the base.
[出典] gulp-rename - npm

 
gulpでディレクトリ構造を維持したままリネームして出力したい場合はgulp-renameでfunctionかhashを使う方法でリネームさせればOKなようです
gulp.src()の第2引数に渡せるbaseオプションの使いみちがいまいち理解できてないので、もっと便利にリネームして出力する方法があるのかもしれません。


Atomic Design ~堅牢で使いやすいUIを効率良く設計する

Atomic Design ~堅牢で使いやすいUIを効率良く設計する