2014年あたりから、v4が出なくてやきもきしていましたが、待つこと3年(4年?)、2018年のはじめにv4.0.0
がリリースされました。2018年現在の視点で、v4をチェックしてみたいと思います。
タスクは関数に
以下、GitHubにある例からの抜粋です。(多少変形しています)
import {src, dest} from 'gulp'
import less from 'gulp-less'
import rename from 'gulp-rename'
import cleanCSS from 'gulp-clean-css'
export default () => src('src/style.css')
.pipe(less())
.pipe(cleanCSS())
.pipe(rename({basename: 'main', suffix: '.min'}))
.pipe(dest('somewhere/'))
今までgulp.task('default', function () {...})
とやっていた部分が、ただの関数をエクスポートする形になります。
メモ: 後方互換も維持されていて、
gulp.task()
も引き続き使えます。
JavaScriptの作法でエクスポート
複数のタスクがある場合は、それぞれをエクスポートする形です。series
やparallel
で、順番に実行したり並行実行を指定できます。
import {src, dest, series} from 'gulp'
export const styles = () => src(...).pipe(...) // 略
export const scripts = () => src(...).pipe(...) // 略
export default series(styles, scripts)
以前よりJavaScriptの文法に沿った書き方ができる分、だいぶ読みやすい!
タスクの中でgulpを使わなくてもいい
v3
でもそうでしたが、Promiseを返すものなら何でもOKです。v4
になって、というよりもasync/await
が使えるようになって、だいぶ書きやすさが増しました。
import {src, dest} from 'gulp'
import del from 'del'
export const clean = () => del(['assets'])
export const someAsyncFunc = async () => {...} // 略
export const other = () => src(...).pipe(...) // 略
インストール
インストールに際して、
$ npm install --save-dev gulp
だと、まだv3系がインストールされます。代わりに次のようにします。
$ npm install --save-dev gulp@next
CLIを使う
CLIの使い方はあまり変化ありませんが、npm@5.2.0
から、npx
コマンドが追加されたので、次のような呼び出しが可能です。
$ npx gulp
グローバルにgulp-cli
を入れなくて済みます。
メモ:
gulp-cli
は、gulp
の依存性として、ローカルのnode_modules
にインストールされています。
なお、上述の例のようにimport
/export
を使う場合、Nodeのネイティブ対応はまだなので、トランスパイルの必要があります。GitHubのドキュメントにbabelを使う方法が書かれていますので、そちらをどうぞ。
あるいは、babelが大げさに感じるならreify
でも動きました。
$ npm install --save-dev reify
$ npx gulp --require reify
CLIを使わない
gulp.task()
の呪縛から離れたので、gulpのスクリプトを直接「プログラマブルに」扱いたいケースは増えそうです。
// build.js
import {src, dest, series} from 'gulp'
const styles = src(...).pipe(...) // 略
const scripts = src(...).pipe(...) // 略
const build = series(styles, scripts)
build()
上記の例は、node build.js
すれば動きます。(実際には、babelかreifyが必要)
なお、gulpのタスクは基本的にはストリームなので、async
/await
では直接扱えません。gulp.series
やgulp.parallel
で制御するのが良いでしょう。どうしても使いたい場合はstream-to-promiseあたりで、Promiseの文脈に持ち込む手はあります。
// build2.js
import {src, dest} from 'gulp'
import toPromise from 'stream-to-promise'
const styles = src(...).pipe(...) // 略
const scripts = src(...).pipe(...) // 略
const main = async () => {
await toPromise(styles)
await toPromise(scripts)
}
main()
メモ:
gulp.start()
はディスコンになりました。
まとめ
gulp4になって、次の点が寄与してboilerplateから完全に解放されました。
- タスクがただの関数になったこと
- ES2015の文法
素のNodeのスクリプトを書くのともう変わらないので、タスクランナー嫌い(?)のひとも、改めてチェックしてみても良いかも。
雑感
gulpの最大の利点は(今も昔も)次の2点です。プラグインのエコシステムのおかげで、シンプルに書ける面は多分にありますが、それほど本質的ではありません。
- ファイルを
vinyl
ストリームで扱う - 非同期タスクの実行順序制御 (series, parallel)
この2点は、シェルスクリプトだけで実現するのは厄介なので、引き続きgulpを使う理由として残るように思います。特に前者について、複数ファイルの扱いが秀逸です。この点、シェルスクリプトではtar
で固めるしかなく、キレイに書く方法があまりありません。
メモ: 後者については、npm-run-allを使う手もあります。
最近は、シェルスクリプト(sh
/bash
)を触らない人も多いので、JavaScriptのプロジェクトについてはタスクもJavaScriptで書くというのは自然な気もします。個人的な指針としては、npm scriptsに書ききれなくなった時はgulp
の出番です。具体的には
- 複数のファイルを扱う
- 複数のツールを組み合わせる
といったシーンです。
WebPack, Parcelなどの包括的なツールの流行りで、元々のgulpの使い途だった、トランスパイル系の操作での需要は減っていますが、2018年も意外と(?)使いどころが残っているgulpでした。