gulp4 lastRun関数の挙動

## はじめに

gulp4には`lastRun`という関数が追加されました。`gulp.src`関数と組み合わせて使うことで、更新のあったファイルのみを後続のpipeに流すことができます。

[公式ドキュメント](https://gulpjs.com/docs/en/api/lastrun)

この関数の挙動を理解するために、テスト用のコードを書いて実験をしてみました。

## この記事の前提

### 想定する読者

この記事は、以下の方を想定しています。

- gulp3をすでに利用したことがある
- gulp4への移行を考えている

gulpのインストールやセットアップに関する内容は取り扱いません。

### 実行環境

この記事の実験用コードは以下のモジュールを利用しています。バージョンが異なる場合、この記事とは異なる結果になることがあります。

*▼package.json*

```json
"devDependencies": {
  "del": "^4.1.1",
  "gulp": "^4.0.1",
  "gulp-imagemin": "^5.0.3"
}
```

### 実験用コード

今回使用した実験用コードはこちらのリポジトリにアップロードしています。

https://github.com/MasatoMakino/behavior_of_gulp_lastrun

このコードには3つのgulpタスクが登録されています。

- gulp clean : 出力先フォルダーの中身をすべて削除する。
- gulp images : ソースフォルダー内の画像を圧縮する。
- gulp watch : ソースフォルダー内の画像の変更を監視し、gulp imagesタスクを実行する。

imagesタスクの内容は以下の通りです。

*▼gulpfile.js*

```js
const images = () => {
  const lastRunResult = lastRun(images);
  console.log(lastRunResult);
  return src("./src/img/**/*.jpg", { since: lastRunResult })
    .pipe(imagemin())
    .pipe(dest("./dist/img/"));
};
```

lastRunの結果をコンソール出力し、srcにはオプションでlastRunの結果を渡します。

## 実行結果

### 実験1. タスクを直接実行

`gulp images`で画像圧縮タスクを直接起動した場合のログは以下の通りです。

```console
$ gulp images
[12:21:50] Using gulpfile ~/Documents/projects/behavior_of_gulp_lastrun/gulpfile.js
[12:21:50] Starting 'images'...
undefined
[12:21:51] gulp-imagemin: Minified 1 image (saved 9.91 kB - 60.6%)
[12:21:51] Finished 'images' after 555 ms
Done in 2.56s.
```

`lastRun(images)`の実行結果は`undefined`になり、すべての画像が処理の対象になりました。

### 実験2. タスクをwatch経由で実行

`gulp watch`でファイルの更新を監視した場合のログは以下の通りです。

```console
$ gulp watch
[12:16:56] Using gulpfile ~/Documents/projects/behavior_of_gulp_lastrun/gulpfile.js
[12:16:56] Starting 'watch'...
[12:18:21] Starting 'images'...
undefined
[12:18:22] gulp-imagemin: Minified 0 images
[12:18:22] Finished 'images' after 791 ms
[12:19:03] Starting 'images'...
1556507901322
[12:19:03] gulp-imagemin: Minified 1 image (saved 9.46 kB - 61.1%)
[12:19:03] Finished 'images' after 56 ms
```

`watch`開始後1回目のタスク実行時、`lastRun(images)`の結果は`undefined`になり、すべての画像が処理の対象になりました。
2回目のタスク実行時、`lastRun(images)`の結果は`1556507901322`になり、変更した1ファイルのみが処理の対象になりました。`1556507901322`は前回実行時のタイムスタンプです。

### 実験3. watchタスクを実行中に出力先を削除

`gulp watch`を実行中に`gulp clean`で出力先のファイルをすべて削除しました。
そのあとsrcフォルダー内の画像ファイルを編集しました。結果は以下の通りです。

```console
[12:30:44] Starting 'images'...
1556508367927
[12:30:44] gulp-imagemin: Minified 1 image (saved 10.3 kB - 55.8%)
[12:30:44] Finished 'images' after 65 ms
```

`lastRun(images)`の処理は継続され、編集した1ファイルのみがdistフォルダーに出力されました。出力先のファイルが削除されたことには反応しません。

## まとめ

- タスクを直接実行した場合、lastRunは常に`undefined`を返し、すべてのファイルが処理対象になる。
- タスクを`watch`経由で実行した場合、lastRunは2回目以降から前回タスク実行時のタイムスタンプを返す。
- `watch`実行中に、出力先のファイルを操作されても反応しない。

呼び出し元がwatchでない場合、lastRunは無視されます。たとえばデプロイ用のseries/parallelタスク内に今回のimagesタスクを組み込んでも、すべてのファイルが処理対象になるので出力漏れを起こすことはありません。

また、lastRunは出力先のファイルを考慮しません。[gulp-changed](https://github.com/sindresorhus/gulp-changed)プラグインのように、[出力先ファイルの更新時間の確認](https://github.com/sindresorhus/gulp-changed/blob/master/index.js#L24)はしません。初回実行時は出力先に処理済みファイルがあったとしても、無視してすべてのファイルを処理して上書きします。

たとえば数百の画像ファイルの最適化処理などの、以下のようなタスクにはgulp-changedプラグインは有効です。

- 数十秒 ~ 数百秒の時間がかかる
- 処理済みファイルを再利用して部分的にスキップができる

gulp4においてもgulp-changedは有用なプラグインです。どのような性質の処理なのかを考慮して、lastRunと使い分けてください。

以上、ありがとうございました。