今更だけどまぁまぁ便利なのでgulp4を使う

今更だけどまぁまぁ便利なのでgulp4を使う

はい。
今更gulpって、、、って感じもあるし、webpackだのparcelだの、いやいや、そもそも最近はcliが充実してるので、設定ファイル自体書かないんだよってことがあるのは知ってるけど、gulp4結構便利だったので書いておく。

ちなみに今普通にgulp落としてきたら4以降が落ちてくるけど、3.9.1との明確な差別化のためにこの記事ではgulp4と呼びます。

モチベーション

gulpが便利なときがある

業務でプラグインみたいなやつ書いてて、webpackだけだと結構だるかった。
要件的には以下。

  • htmlとかcssとか扱いたいファイルが複数ある
  • ただコピーしたいのがちょいちょいある
  • 上書きしたいファイルが有る
  • develop用に生成されたファイルをちょくちょく確認したい

特に【上書きしたいファイルが有る】はwebpackだと面倒そうな気配が結構あったので、それが決め手だった。
個人的には未だにウェブサイト作るなら、正直gulpは必須だと思う。
npm scriptsで頑張ってもいいけど、gulpの手軽さには代え難いし、特にウェブサイトの制作だと最終的にWordPressのテーマにしちゃうことも多いと思うし、そういう場合だとNuxtとかは向かなそうな印象ある。

◾️ gulp@3.9.1に引きずられた記事が多い

gulpは@3.9.1の時代が長かったので、巷にはその時代の書き方は死ぬ程転がってる。
もちろんgulp4の書き方も山のように転がってるけど、案外3.9.1を引きずった書き方ばっかだったので、そうじゃない書き方の紹介があってもいいだろうと思った。
個人的には4系の書き方のほうが断然好き。gulp書いてる感でなくjs書いてるなーって感じがするから。

gulpfile.js

公式ドキュメント

まぁ、公式を読めって話だけど、往々にして人間は公式でなく日本語で書かれた記事を先にあたるので、こっちでも書いていく。
大雑把に何が違うかというと、大きいところではseriesparallelが入った。
今まで直列処理するのにrun-sequenceが必要だったけど、seriesが代替できるようになった。 並列処理の場合はその名の通りparallelを使う。 あとはmodule.exportsにタスクをぶら下げる事になったところも大きな変更点だと思う。

今までgulp.task()で文字列と処理を渡してたけど、その書き方は非推奨になった。今でもそのAPIは使用可能なこともあって、この書き方を紹介した記事が多い。ここでは紹介しないですが。

そういうわけで以下、具体的な書き方。

■ 基本形

const { src, dest } = requore('gulp');

const default = () => src(`path/to/src/files`).pipe(dest(`path/to/dest`));

module.exports = { default };

ばりシンプル🥰

ちなみにdefaultで外出ししたやつが gulp 実行時に呼ばれる。
以下のようにやるとgulp hogeで呼べるようになる。

const hoge = () => src(...).pipe(dest(...));

module.exports = { hoge };

各タスクの基本的な流れは3.9.1時代と変わらない。srcでファイルを指定してやって、pipeで様々な処理を行って最終的にdestの中で吐き出し先を指定してやるという感じ。
srcに複数渡したいときは今まで通り配列で渡せばオッケー👌globで渡せます。 3.9.1時代もそうだったけど、srcからの処理をきちんとreturnしてやる必要がある。

例)sassの場合

const { src, dest } = requore('gulp');
const sass = require('gulp-sass');
const sassGlob = require('gulp-sass-glob');
const sourcemaps = require('gulp-sourcemaps');
const please = require('gulp-pleeease');

// 僕は基本めんどいのでpleeeaseでsassの処理やってる
const styles = () =>
  src(`${DIR.SRC_ASSETS}sass/**/*.{sass,scss}`)
    .pipe(sourcemaps.init())
    .pipe(plumber())
    .pipe(sassGlob())
    .pipe(
      sass({
        outputStyle: ':expanded'
      }).on('error', sass.logError)
    )
    .pipe(
      please({
        sass: false,
        minifier: false,
        rem: false,
        pseudoElements: false,
        mqpacker: true
      })
    )
    .pipe(sourcemaps.write('./'))
    .pipe(dest(`${DIR.DEST_ASSETS}css`));

例にするには長々した記述のタスクだった。

parallelseries

並列処理と直列処理。
上記の感じで設定したタスクを渡すだけなのでスーパー簡単。

const { src, dest, series, parallel } = require('gulp');
const del = require('del');

// ejsなどの処理を書いておく

// parallelに書いたやつは並列で走る
const devTask = parallel(ejs, styles, scripts, imageMin);

const clean = dir =>  del([dir]);

// seriesで書いたやつは直列で走るので、これだと、clean.bind(null, DIR.DEST) => devTaskの順で走る。
const build = series(clean.bind(null, DIR.DEST), devTask);

watch

watchだけ結構癖があるというか、watchの場合は何も返さない。
ただ発火させるだけでいい。

watchもおんなじ感じで行ける。

const watchEjs = () => watch(`${DIR.SRC}**/*.ejs`, series(ejs, reload));
const watchSass = () =>
  watch(`${DIR.SRC_ASSETS}sass/**/*.{sass,scss}`, series(styles, reload));
const watchJs = () =>
  watch(`${DIR.SRC_ASSETS}js/**/*.js`, series(scripts, reload));

const watches = parallel(watchEjs, watchSass, watchJs);

const dev = series(build, devServer, watches);

module.exports = {
  default: dev
}

■ browserSync

こいつは最近公式に乗った気がするけど、gulp4で使う場合は引数でdoneを受け取って、処理後にそれを発火させる必要がある。

公式

const browserSync = require('browser-sync');
const devServer = done => {
  browserSync.init({
    server: {
      baseDir: DIR.DEST
    },
    ghostMode: {
      clicks: true,
      forms: true,
      scroll: false
    }
  });
  done();
};

const reload = done => {
  browserSync.reload();
  done();
};

これでwatchなんかの文脈で使ってもうまいことreloadしてくれる。

■ その他

gulp-ejs

こいつはgulp4から自分で拡張子の名前を変えることができなくなってやがる。
ので、gulp-renameを入れて別途拡張子の変更をしてやる必要がある。

www.npmjs.com

const { src, dest } = require('gulp');
const plumber = require('gulp-plumber');
const gulpEjs = require('gulp-ejs');
const gulpRename = require('gulp-rename');

const ejs = () =>
  src([`${DIR.SRC}**/*.ejs`, `!${DIR.SRC}_inc/**/*.ejs`])
    .pipe(plumber())
    .pipe(gulpEjs({ INC: Path.resolve(__dirname, `${DIR.SRC}_inc`) + '/' }))
    .pipe(gulpRename({ extname: '.html' }))
    .pipe(dest(DIR.DEST));

そんなとこですかね。

だいたいそんな感じでいけます。
関数ベースで組み立てられるので僕としては書いてて割と楽しい。
あと、結構スッキリ書けるようになったと思う。

今回の記述とってきたレポジトリはぼくのgithubに上がってるのでわかんないところあれば、参考にどうぞ

github.com