【今更解説】gulp4始める時の、詳細メモ

`pug`や`sass`などをコンパイルする必要があるのに、
`prepros`とか`koala`とかのGUIツールで頑張っていたプロジェクトに対して、
`gulp`を入れる作業をしたのでその記録です。

出来上がった`gulpfile.js`だけを見ても
**「文字ブワーって書いてあるだけで、何やってるのか分からない」** と思うんです。
`gulp`タスクを書きながら、書き方を覚える感じで見てもらえると良いかと思います。

#最初のシンプルな構成で動作チェック

###.gitignoreに下記を追記する

```
.env
node_modules/
```

###npm initする。

```
$ cd project
$ npm init
```

```
package name: (project) 
version: (1.0.0) 1.0.0.
Invalid version: "1.0.0."
version: (1.0.0) 1.0.0
description: static html of project.
entry point: (index.js) 
test command: 
git repository: git@bitbucket.org:xxx/project.git
keywords: project
author: nishi
license: (ISC) 
Is this ok? (yes) yes
```

###package.jsonに追記する
lincenseの下に追記します。

```
"browserslist": [
    "last 2 version"
  ],
  "scripts": {
    "start": "gulp",
    "dev": "npm start",
    "build": "gulp build"
  },
  "devDependencies": {
    "@babel/core": "^7.1.2",
    "@babel/preset-env": "^7.1.0",
    "babel-loader": "^8.0.4",
    "browser-sync": "^2.26.7",
    "del": "^3.0.0",
    "eslint": "^5.8.0",
    "eslint-config-airbnb": "^17.1.0",
    "eslint-loader": "^2.1.1",
    "eslint-plugin-import": "^2.14.0",
    "eslint-plugin-jsx-a11y": "^6.1.2",
    "eslint-plugin-react": "^7.11.1",
    "gulp": "^4.0.2",
    "gulp-autoprefixer": "^6.0.0",
    "gulp-cssmin": "^0.2.0",
    "gulp-eslint": "^5.0.0",
    "gulp-imagemin": "^6.0.0",
    "gulp-plumber": "^1.2.0",
    "gulp-pug": "^4.0.1",
    "gulp-sass": "^4.0.2",
    "gulp-sass-glob": "^1.0.9",
    "gulp-watch": "^5.0.1",
    "imagemin-mozjpeg": "^8.0.0",
    "imagemin-pngquant": "^8.0.0",
    "run-sequence": "^2.2.1",
    "webpack": "^4.23.1",
    "webpack-stream": "^5.1.1"
  },
  "dependencies": {
    "normalize.css": "^8.0.0"
  }
```

###パッケージをインストールします。

```terminal
$ npm install
```
deprecated（古い）の警告が出ているのですが

```terminal
npm WARN deprecated gulp-util@2.2.20: gulp-util is deprecated - replace it, following the guidelines at https://medium.com/gulpjs/gulp-util-ca3b1f9f9ac5
npm WARN deprecated fsevents@1.2.13: fsevents 1 will break on node v14+ and could be using insecure binaries. Upgrade to fsevents 2.
npm WARN deprecated graceful-fs@2.0.3: please upgrade to graceful-fs 4 for compatibility with current and future versions of Node.js
npm WARN deprecated request@2.88.2: request has been deprecated, see https://github.com/request/request/issues/3142
npm WARN deprecated core-js@2.6.11: core-js@<3 is no longer maintained and not recommended for usage due to the number of issues. Please, upgrade your dependencies to the actual version of core-js@3.
```
そもそも`gulp`が依存してるファイルが既に古いので、あまり気にしないで下さい。

---

###テスト
ちょっとココまでで、動くのか試します。

```
$ npm run dev
No gulpfile found
```
動作確認のために、最初にシンプルな「gulpfile.js」を作ります。
**★「モジュール読み込み」**
**★「変数」**
**★「タスク単体」**
**★「まとめて実行タスク」**
の４つの場所に分けて置いて下さい。

gulpfile.js

```javascript
// モジュールの読み込み
const gulp = require('gulp');
const del = require('del');

// 変数
const options = {
　　BUILD_PATH: './dist',
};

//　タスク単体
gulp.task('clean', () => del([
  options.BUILD_PATH,
]));

// まとめて実行されるタスク
gulp.task('default', gulp.task(
   'clean'
));
```
エラーが出ないか試す。

```terminal
$ npm run dev
[20:53:03] Using gulpfile ~/checkmed-static-html/gulpfile.js
[20:53:03] Starting 'default'...
[20:53:03] Finished 'default' after 4.36 ms
```
一応、タスクなしの状態で `gulp`は動作しました。多分コレで動きそう。
動かなかった場合は、ここまでの記述を見直して下さい。

---
###cleanのタスクを書き換える

**(※注： 下記は、gulpを導入する前の環境への対応が混ざっています)**
まず最初に「`Prepros`」で書き出していた出力ファイルを消します。

「（旧環境では）プロジェクト直下にhtmlを書き出す」というやり方をしていましたが、
（htmlを書いたり消したりする上では）安全じゃない気がします。

下記「htmlファイルを消す」を設定する意味は、他のディレクトリにも影響しそうになるので、
`node_modules`が影響を受けないように、一生懸命、例外を指定していきました。
**（つまり、真似しなくて良い部分です）**

gulpfile.js

```javascript
const gulp = require('gulp');
const del = require('del');

const options = {
    BUILD_PATH: './dist',
    ALL_HTML: './*.html',
    ASSETS: './assets',
    SAMPLE: './sample',
    EXCLUDE_NODE_MODULES :'!node_modules/*',
    EXCLUDE_NODE_MODULES_HTML :'!node_modules/**/*.html',
};

gulp.task('clean', () => del([
  options.BUILD_PATH,
  options.ALL_HTML,
  options.ASSETS,
  options.SAMPLE,
  options.EXCLUDE_NODE_MODULES,
  options.EXCLUDE_NODE_MODULES_HTML,
]));

gulp.task('default', gulp.task(
   'clean'
));
```
###結論： 上記の書き方は良くないです。
はい、書いてみた結果思ったのですが、、、これはめんどくさいです。
いつ何時、必要なファイルを消すのか分かりません。想定外の場所に置いたら消えるので危ないです。
むしろ運用ルールを変えたほうが良いという結果に至りました。

###dist以下に書き出す設定にする
一般的にベストプラクティスとされている「dist」以下に書き出す設定しました。

```javascript
const options = {
    BUILD_PATH: './dist'
};

gulp.task('clean', () => del([
  options.BUILD_PATH,
]));

gulp.task('default', gulp.task(
   'clean'
));
```
タスクもスッキリしました。

###scss → cssのタスクを書く。
gulpfile.jsの先頭

```javascript
const sass = require('gulp-sass');
const sassGlob = require('gulp-sass-glob');
const autoprefixer = require('gulp-autoprefixer');
const browserSync = require('browser-sync').create();
```

最近は`AutoPrefixer`の設定にブラウザバージョンを直接書いてはいけないらしいです。
下図ではコメントアウトしております。

**（旧環境ではそのように指定されていただけですが）**
また、`npm run dev`（ブラウザプレビュー）の実行中、
**「プロジェクト直下をwatchする」**のも変というか、

`/src`フォルダまでプレビューに含まれちゃうので、
ブラウザプレビューする場合は`/public`以下に書き出して、そこを見るようにします。

```javascript
const options = {
    SRC_PATH: './src',
    AUTOPREFIXER: {
      //browsers: ['last 2 version', 'iOS >= 8.1', 'Android >= 4.4'],  //←【NG】
      cascade: false,
    },
    PUBLIC_PATH:'./public',
};
```

下のソースコードの`browserSync`は、まだ`init`してないので動きません。
（あとで動かしますのでコメントアウトしておいてください）

```javascript
gulp.task('scss', () => gulp.src(`${options.SRC_PATH}/assets/scss/**/*.scss`)
  .pipe(sassGlob())
  .pipe(sass({ outputStyle: 'expanded' }).on('error', sass.logError))
  .pipe(autoprefixer(options.AUTOPREFIXER))
  .pipe(gulp.dest(`${options.PUBLIC_PATH}/assets/css`))
  //.pipe(browserSync.stream())
);
```

```javascript
gulp.task('default', gulp.series(
   'clean',
   'scss',
));
```

`node-sass`が古かった（※`node-sass`の関連付けがおかしかった）ので、
もしも `node-sass` 関連のエラーが出る場合はエラーの指示通りに下記を打ち込む。

```
$ npm rebuild node-sass
```
そもそも、ローカルにsassがない場合は、`npm install -g node-sass`を実行する。

---

###テスト
ココで、試しに動かしてみる。

```
$ npm run dev
```
（上の方で書きましたが）`autoprefixer`の警告で「そこにブラウザバージョンをかくな」というエラーがでたので
ブラウザに関する記述は、`gulpfile.js`ではなく `package.json` に追記しました。

package.json

```javascript
"browserslist": [
    "last 2 version",
    "ie 11",
    "iOS >= 8.1",
    "Android >= 4.4"
  ],
```

###normarize.cssを読み込む
`normarize.css`を`node_modules`から呼び出す設定に関しては
`package.json`の依存ファイル(`dependencies`)に、パッケージ名を書いておき

```javascript
"dependencies": {
    "normalize.css": "^8.0.0"
  }
```
あとは`/src/assets/scss/custom.scss`の先頭に

```scss
@import "/node_modules/normalize.css/normalize.css";
```
これでOKです

---

###pug → htmlのタスクを書く。
`pug`の中でも`include`する部品はコンパイルから除外したいので、
除外設定をいれます。

（※こんなめんどくさい事になるなら`include`されるものは
名前の先頭に必ず”_”(アンダーバー)をつけるなどの命名ルールにすれば良かったです。
**読んでいる方は、こんな面倒な設定をしないでください。**
次回からルールを見直すようにしますが、これも過去の遺産に対しての対策です）

gulpfile.js

```javascript
#追記
const plumber = require('gulp-plumber');
const pug = require('gulp-pug');
```
`pretty: true`は、「きれいなインデントで出力する」オプションです。

```javascript
#追記
const options = {
    PUG: {
      pretty: true,
    },
};
```

（↓`/assets/tmpl/`に入っているのは、旧環境で使われていたpugの共通パーツです。）

```javascript
#追記
gulp.task('pug', () => gulp.src([
  `${options.SRC_PATH}/**/*.pug`,
  `!${options.SRC_PATH}/assets/tmpl/_*.pug`,
  `!${options.SRC_PATH}/assets/tmpl/elements/*.pug`,
  `!${options.SRC_PATH}/assets/tmpl/elements/**/*.pug`
  ])
  .pipe(plumber())
  .pipe(pug(options.PUG))
  .pipe(gulp.dest(`${options.PUBLIC_PATH}`))
);
```

```javascript
gulp.task('default', gulp.series(
   'clean',
   'scss',
   'pug',
));
```
---

動くか試します。

```
$ npm run dev
```

###エラー対応
ここで少しエラーあったのでメモしておきます：
絶対パスでincludeを書いている場合、どこを基準に絶対パスになってるのか
確認してください。絶対パスで指定すると、ルートがどこか分からない場合はエラーになります。

---

###画像を圧縮する

画像やフォントに関しては、
`npm run dev` と `npm run build` でやることが違います。

####npm run devは複製する
image → copy:img（/srcフォルダから→/publicフォルダへ）
font  → copy:font（/srcフォルダから→/publicフォルダへ）

####npm run buildは圧縮する
copy（/publicフォルダから → /distフォルダへ）
image-min（/distフォルダのimgを圧縮する）

gulpfile.js

```javascript
#追記
const imagemin = require('gulp-imagemin');
const pngquant = require('imagemin-pngquant');
const mozjpeg = require('imagemin-mozjpeg');
```

```javascript
#追記
const options = {
    MINIFY_IMG: true,
    IMAGEMIN: [
      pngquant('65-80'),
      mozjpeg({
        quality: 85,
        progressive: true,
      }),
      imagemin.svgo(),
      imagemin.optipng(),
      imagemin.gifsicle(),
    ],
};
```

```javascript
#追記
gulp.task("copy:img", () => gulp.src(
  `${options.SRC_PATH}/assets/img/**/*`
  )
  .pipe(gulp.dest(`${options.PUBLIC_PATH}/assets/img/`))
);

gulp.task("copy:fonts", () => gulp.src(
  `${options.SRC_PATH}/assets/fonts/*`
  )
  .pipe(gulp.dest(`${options.PUBLIC_PATH}/assets/fonts/`))
);

gulp.task('minify:img', () => options.MINIFY_IMG && gulp.src(
  `${options.BUILD_PATH}/assets/img/**/*`)
  .pipe(imagemin(options.IMAGEMIN, {
    verbose: true,
  }))
  .pipe(gulp.dest(`${options.BUILD_PATH}/assets/img`))
);
```
defaultとbuildのタスクも整頓しなおす。

```javascript
gulp.task('default', gulp.series(
   'scss',
   'pug',
   'copy:img',
   'copy:fonts',
));

gulp.task('clean', () => del([
  options.BUILD_PATH,
]));

gulp.task('copy', () => gulp
  .src(`${options.PUBLIC_PATH}/**`, {
    base: options.PUBLIC_PATH,
  })
  .pipe(gulp.dest(options.BUILD_PATH))
);

gulp.task('build', gulp.series(
  'clean',
  'copy',
  'minify:img',
));
```
コレで画像はとりあえず良い感じになるのですが、
まだココ問題がありまして、

###画像コピーと画像圧縮が一連の作業になっていない。
`npm run dev`でコピーをして、`npm run build`で圧縮をかけています。
必ず `npm run dev` をやった後に `npm run build` しないといけないので

いきなり `npm run build` した場合、変なファイルができてしまいトラブルになりそう。

（説明が複雑になるので割愛しますが、build単体で実行してもファイルがコピーされて圧縮されるように、
buildのタスクは、devのタスクを最初に全部やるように、重複して書いたほうが良いと思います）

ブラウザ確認に関して、イメージの更新に関してもホットモジュールリロードしたいので、
下記のタスクを用意します。`browserSync`でリロードする予定です。

```javascript
#追記
gulp.task('img:reload', (done) => {
  browserSync.reload();
  done();
});
```
後述しますが、上記をを動かすための`gulp.watch()`はまだ書いてないです。

---

###分割されているjsをまとめる。

Javascriptをまとめます。`webpack`を使っています。
（ちなみに`typescript`には非対応です。使いたい方は自分で追加して下さい。）

gulpfile.js

```javascript
#追記
const webpack = require('webpack-stream');
const webpackConfig = require('./webpack.config');
```

```javascript
#追記
const options = {
    MINIFY_JS: true,
}
```

```javascript
gulp.task('js', () => gulp.src(`${options.SRC_PATH}/assets/js/**/*.js`)
  .pipe(plumber())
  .pipe(webpack(Object.assign({}, webpackConfig, { mode: 'development' })))
  .pipe(gulp.dest(`${options.PUBLIC_PATH}/assets/js`)));

gulp.task('minify:js', () => options.MINIFY_JS && gulp.src(`${options.BUILD_PATH}/assets/js/**/*.js`)
  .pipe(plumber())
  .pipe(webpack(Object.assign({}, webpackConfig, { mode: 'production' })))
  .pipe(gulp.dest(`${options.BUILD_PATH}/assets/js`)));
```

webpack.config.js

```javascript
module.exports = {
  entry: {
    custom: './src/assets/js/custom.js',
  },
  output: {
    filename: '[name].js',
  },
  resolve: {
    extensions: ['.js', '.css'],
  },
  module: {
    rules: [
      {
        test: /\.js$/,
        loader: ['babel-loader'],
        exclude: /node_modules/,
      },
    ],
  },
};
```

###テスト
```
$ npm run dev
```
問題なく動きそうです。

---

###
タスクを書き直します。
gulpfile.js

```javascript
gulp.task('default', gulp.series(
   'scss',
   'pug',
   'js',
   'copy:img',
   'copy:fonts',
));
```

```javascript
gulp.task('build', gulp.series(
  'clean',
  'copy',
  'minify:img',
  'minify:js',
));
```
`browserSync`用のリロードタスクも用意しておきます。

```javascript
gulp.task('js:reload', (done) => {
  browserSync.reload();
  done();
});
```
先にリロードタスクが書かれてますが、`gulp.watch()`はまだ書いてないです。

---

###ファイルの変更監視・ブラウザ起動

`gulp.watch`でファイルの変更を監視します。

```javascript
＃追記
const browserSync = require('browser-sync').create();
```

```javascript
#追記
const options = {
  BROWSERSYNC: {
    server: './public',
    port: 3000,
    ghostMode: false,
    notify: false,
  },
}
```

```javascript
#追記
gulp.task('watch', () => {
  gulp.watch([`${options.SRC_PATH}/assets/img/**/*.*`], gulp.series('copy:img', 'img:reload'));
  gulp.watch([`${options.SRC_PATH}/**/*.pug`], gulp.series('pug', 'html:reload'));
  gulp.watch([`${options.SRC_PATH}/js/**/*.js`], gulp.series('js', 'js:reload'));
  gulp.watch([`${options.SRC_PATH}/scss/**/*.scss`], gulp.task('scss'));
  browserSync.init(options.BROWSERSYNC);
});
```

今まで他のタスクでコメントアウトしていた`browserSync`のコメントアウトを解除する。

```javascript
//.pipe(browserSync.stream())
↓
.pipe(browserSync.stream())
```

タスクの`default`も書き換えます。

```javascript
gulp.task('default', gulp.series(
   'scss',
   'pug',
   'js',
   'copy:img',
   'copy:fonts',
   'watch',
));
```

###テスト
動かしてみる。

```
$ npm run dev
```

エラー：htmlのリロードを書いてなかったので追加する。

```javascript
#追記
gulp.task('html:reload', (done) => {
  browserSync.reload();
  done();
});
```
で、修正して再度`npm run dev`したら、`browserSync`は動いてるのですが、
JSでエラーが発生してしまいました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/216132/1f204a73-88a5-3691-db0e-c8f504889d93.png)

###エラーの原因（と考えたもの）
・scssじゃなくて「元からcssだったもの」がコピーされてない。
・SmoothscrollってjQueryの使用デフォじゃないっけ？いや、そんな事ないか。
　jQueryプラグインは読み込んでいるが、中身でエラーになっている。
・faviconやsitemanifestなど「画像でもhtmlでもないもの」を扱ってなかった。
　しかし、いちいちコピータスクを書くのは面倒。ここのエラーバグつぶしが必要

---

上記エラーへの対応をしていきます。

###変換しないものは/publicにいれる。

`.gitignore` を修正しました。`/public`の中身は全部無視するのではなく、
出力されるものだけは無視するように設定しました。
（この辺は好みがあると思います。全部srcに入れて管理しておきたい場合は、こまめにcopyタスクを書いてください）

.gitignore

```
.history
.DS_Store
.env
node_modules/
dist/
public/*.html
public/assets/
src/**/*.html
src/assets/tmpl/*.html
src/assets/tmpl/elements/*.html
```

`/public`直下にいろいろいれておきます。

置くファイル

```
/public/assets/css/base.css
/public/site.manifest
```

**（過去の遺産対策なので、気にしないでください）**
ここでいう`base.css`は、以前から使っている過去の遺産的なもの、というか
他のプロジェクトと共通CSSとなっているCSSなので、
併用した時悪影響がないか、確認用に置いているだけです。基本的に編集しません。

`jQuery`のバージョンを、最新ではなく、
gulpで置き換える前の旧環境で公開されているものに合わせます。`3.5.1` → `3.3.1`

バージョンを合わせる理由は、
`gulp`に置き換えた事でバグった、というような指摘を受けないためです。

他もプラグインも`node_modules`として入れておきます。
下記は私が使ったプラグインの例です。

```
$ npm uninstall jquery
$ npm install --save jquery@3.3.
$ npm install --save smooth-scroll
$ npm install --save luxy.js
$ npm install --save lodash.js
$ npm install --save swiper
$ npm install --save jquery-inview
$ npm install --save lightcase
$ npm install --save-dev node-sass-package-importer
```

`lodash`とか`luxy`は、名前がよく似たnpmパッケージがあるようです。注意して下さい。
`node_modules`から`css`を読み込むのに、プラグインを利用します。

`node-sass-package-importer`
参考リンク：　https://qiita.com/irok/items/08a4a015c24a7a83510f

`scss`のタスクを書き換えました。

```javascript
gulp.task('scss', () => gulp.src(`${options.SRC_PATH}/assets/scss/**/*.scss`)
  .pipe(sassGlob())
  .pipe(sass({ 
    outputStyle: 'expanded',
    importer: packageImporter({
      extensions: ['.scss', '.css']
    }),
   }).on('error', sass.logError))
  .pipe(autoprefixer(options.AUTOPREFIXER))
  .pipe(gulp.dest(`${options.PUBLIC_PATH}/assets/css`))
  .pipe(browserSync.stream())
);
```

###custom.jsを修正する。

JSのモジュール読み込みは、`require()`じゃなくて`import`にします。
custom.js

```javascript
import $ from 'jquery';
global.jQuery = $;
import './_smooth-scroll-init.js';
import './_luxy-init.js';
import './_header.js';
import './_usage.js';
import './_usage_sp.js';
import './_pagetop.js';
import './_inview-init.js';
import './_accuracy.js';
import './_lightcase.min.js';
import './_movie.js';
import './_localhost.js';
import './_news-localhost.js';
import './_faq.js';
```
各モジュールごとに、使うプラグインをimportしています。
jquery使用が前提の場合は、jqueryをimportします。下図はその例です。
`$`（jQuery）が使えるようにする

```javascript
import $ from 'jquery';
$(function() {
　〜〜以下略〜〜
```
`jquery-inview`を使えるようにする。

```javascript
import $ from 'jquery'
import isInView from 'jquery-inview'
$(() => {
  $('.inView').css('visibility', 'hidden')
  $('.inView').on('inview', function (event, isInView) {
    if (isInView) {
      const delay = $(this).data('delay') ? $(this).data('delay') : 0
      $(this).css('visibility', 'visible')
      if (delay > 0) {
        $(this).css('animation-delay', `${delay}s`)
      }
      $(this).addClass(`animated ${$(this).data('animate')}`)
      $(this).off('inview')
    }
  })
})
```

いろいろな修正を加えて、完成したgulpfile.js

```javascript
const gulp = require("gulp");
const del = require("del");

const sass = require("gulp-sass");
const sassGlob = require("gulp-sass-glob");
const autoprefixer = require("gulp-autoprefixer");

const plumber = require("gulp-plumber");
const pug = require("gulp-pug");
const path = require("path");

const imagemin = require("gulp-imagemin");
const pngquant = require("imagemin-pngquant");
const mozjpeg = require("imagemin-mozjpeg");

const webpack = require("webpack-stream");
const webpackConfig = require("./webpack.config");

const browserSync = require("browser-sync").create();

const packageImporter = require("node-sass-package-importer");
const prettify = require("gulp-prettify");
const replace = require("gulp-replace");
const rename = require("gulp-rename");
//ファイルリビジョン
const rev = require("gulp-rev");
const revReplace = require("gulp-rev-replace");

//開発環境と本番環境で分岐を作る
const gulpif = require("gulp-if");
const minimist = require("minimist");

//タグの改行を強制的につける
const htmlbeautify = require("gulp-html-beautify");
const beautifyOptions = [{
  wrap_attributes: "force",
}, ];
const envOption = {
  string: "env",
  default: {
    env: process.env.NODE_ENV || "development",
  },
};
const node_process = minimist(process.argv.slice(2), envOption);

const isProd = node_process.env === "prod" ? true : false;
const isDev = node_process.env === "dev" ? true : false;

const options = {
  BUILD_PATH: "./dist/",
  SRC_PATH: "./src",
  AUTOPREFIXER: {
    cascade: false,
  },
  PUBLIC_PATH: "./public",
  BROWSERSYNC: {
    server: "./public",
    port: 3000,
    ghostMode: false,
    notify: false,
    directory: true,
  },
  MINIFY_IMG: true,
  IMAGEMIN: [
    pngquant("65-80"),
    mozjpeg({
      quality: 85,
      progressive: true,
    }),
    imagemin.svgo(),
    imagemin.optipng(),
    imagemin.gifsicle(),
  ],
  MINIFY_JS: true,
  PUG_SRC_PROD: [
    "./src/**/*.pug",
    "!./src/*.pug",
    "!./src/Block/_*.pug"],
  PUG_SRC_DEV: [
    "./src/**/*.pug",
    "!./src/Layouts/*.pug",
    "!./src/Block/*.pug",
    "!./src/Block/**/*.pug",
  ],
};
const pug_src =
  node_process.env === "prod" ? options.PUG_SRC_PROD : options.PUG_SRC_DEV;

gulp.task("rev", () => {
  return gulp
    .src(`${options.BUILD_PATH}/assets/**/*.+(js|css)`)
    .pipe(rev())
    .pipe(gulp.dest(`${options.BUILD_PATH}/assets`))
    .pipe(rev.manifest())
    .pipe(gulp.dest(`${options.BUILD_PATH}/assets`));
});

gulp.task("rev:replace", () => {
  var manifest = gulp.src(`${options.BUILD_PATH}/assets/rev-manifest.json`);
  return gulp
    .src(`${options.BUILD_PATH}/**/*.+(html|css|js)`)
    .pipe(
      revReplace({
        manifest: manifest,
      })
    )
    .pipe(gulp.dest(`${options.BUILD_PATH}/`));
});

gulp.task("hash", gulp.series("rev", "rev:replace"));

gulp.task("html2twig", () => {
  return gulp
  .src(`${options.BUILD_PATH}/**/*.html`)
  .pipe(replace(/src="\/assets\/img/g, 'src="assets/img'))
  .pipe(replace(/assets\/img\/.*?.(png|jpg|svg|gif)/g, "{{ asset('$&') }}"))
  .pipe(gulp.dest(`${options.BUILD_PATH}/`))
});

gulp.task("rename", (cb) => {
  return gulp
    .src(`${options.BUILD_PATH}/**/*.html`)
    .pipe(rename({
      extname: ".twig"
    }))
    .pipe(gulp.dest(`${options.BUILD_PATH}/`));
  cb();
});

gulp.task("htmlbeautify", () =>
  gulp
  .src(`${options.BUILD_PATH}/**/*.html`)
  .pipe(htmlbeautify(beautifyOptions))
  .pipe(gulp.dest(`${options.BUILD_PATH}/`))
);

gulp.task("scss", () => {
  return gulp
  .src(`${options.SRC_PATH}/assets/scss/**/*.scss`, {
    sourcemaps: true,
  })
  .pipe(sassGlob())
  .pipe(
    gulpif(
      isDev,
      sass({
        outputStyle: "expanded",
        importer: packageImporter({
          extensions: [".scss", ".css"],
        }),
      }).on("error", sass.logError)
    )
  )
  .pipe(
    gulpif(
      isProd,
      sass({
        outputStyle: "compressed",
        importer: packageImporter({
          extensions: [".scss", ".css"],
        }),
      }).on("error", sass.logError)
    )
  )
  .pipe(autoprefixer(options.AUTOPREFIXER))
  .pipe(
    gulp.dest(`${options.PUBLIC_PATH}/assets/css`, {
      sourcemaps: ".",
    })
  )
  .pipe(browserSync.stream())
  });

gulp.task("pug", () => {
  return gulp
  .src(pug_src)
  .pipe(plumber())
  .pipe(
    pug({
      pretty: true,
      basedir: path.resolve(__dirname, `${options.SRC_PATH}/`),
    })
  )
  .pipe(
    gulpif(
      isDev,
      prettify({
        indent_size: 2,
        indent_char: " ",
        indent_with_tabs: false,
      })
    )
  )
  .pipe(
    gulpif(
      isProd,
      prettify({
        indent_size: 4,
        indent_char: " ",
        indent_with_tabs: false,
      })
    )
  )
  .pipe(gulp.dest(`${options.PUBLIC_PATH}/`))
});

gulp.task("html:reload", (done) => {
  browserSync.reload();
  done();
});

gulp.task("copy:img", () => {
  return gulp
  .src(`${options.SRC_PATH}/assets/img/**/*`)
  .pipe(gulp.dest(`${options.PUBLIC_PATH}/assets/img/`))
});

gulp.task("copy:icon", () => {
  return gulp
  .src(`${options.SRC_PATH}/assets/icon/**/*`)
  .pipe(gulp.dest(`${options.PUBLIC_PATH}/assets/icon/`))
});

gulp.task("copy:fonts", () => {
  return gulp
  .src(`${options.SRC_PATH}/assets/fonts/*`)
  .pipe(gulp.dest(`${options.PUBLIC_PATH}/assets/fonts/`))
});

gulp.task("copy:functionjs", () => {
  return gulp
  .src(`${options.SRC_PATH}/assets/js/function.js`)
  .pipe(gulp.dest(`${options.PUBLIC_PATH}/assets/js/`))
});

gulp.task("copy:icon", () => {
  return gulp
  .src(`${options.SRC_PATH}/assets/icon/**/*`)
  .pipe(gulp.dest(`${options.PUBLIC_PATH}/assets/icon/`))
});

gulp.task("copy:webmanifest", () => {
  return gulp
  .src(`${options.SRC_PATH}/*.webmanifest`)
  .pipe(gulp.dest(`${options.PUBLIC_PATH}/`))
});

gulp.task("img:reload", (done) => {
  browserSync.reload();
  done();
});

gulp.task("minify:img", () => {
  return options.MINIFY_IMG && gulp
  .src(`${options.BUILD_PATH}/assets/img/**/*`)
  .pipe(
    imagemin(options.IMAGEMIN, {
      verbose: true,
    })
  )
  .pipe(gulp.dest(`${options.BUILD_PATH}/assets/img`))
});

gulp.task("js", () => {
  return gulp
  .src(`${options.SRC_PATH}/assets/js/**/*.js`)
  .pipe(plumber())
  .pipe(
    webpack(
      Object.assign({}, webpackConfig, {
        mode: "development",
      })
    )
  )
  .pipe(gulp.dest(`${options.PUBLIC_PATH}/assets/js`))
});

gulp.task("js:reload", (done) => {
  browserSync.reload();
  done();
});

gulp.task( "minify:js", () => {
  return options.MINIFY_JS && gulp
  .src(`${options.BUILD_PATH}/assets/js/**/*.js`)
  .pipe(plumber())
  .pipe(
    webpack(
      Object.assign({}, webpackConfig, {
        mode: "production",
      })
    )
  )
  .pipe(gulp.dest(`${options.BUILD_PATH}/assets/js`))
});

gulp.task("watch", () => {
  gulp.watch(
    [`${options.SRC_PATH}/assets/img/**/*.*`],
    gulp.series("copy:img", "img:reload")
  );
  gulp.watch(
    [`${options.SRC_PATH}/**/*.pug`],
    gulp.series("pug", "html:reload")
  );
  gulp.watch(
    [`${options.SRC_PATH}/assets/js/**/*.js`],
    gulp.series("js", "js:reload")
  );
  gulp.watch([`${options.SRC_PATH}/assets/scss/**/*.scss`], gulp.task("scss"));
  browserSync.init(options.BROWSERSYNC);
});

gulp.task("clean", () => {
  return del([options.BUILD_PATH])
});
gulp.task("clean:html", () => {
  return del([`${options.PUBLIC_PATH}/**/*.html`])
});

gulp.task("copy", () => {
  return gulp
  .src(`${options.PUBLIC_PATH}/**`, {
    base: options.PUBLIC_PATH,
  })
  .pipe(gulp.dest(options.BUILD_PATH))
});

gulp.task("default",
  gulp.series(
    "clean:html",
    "scss",
    "pug",
    "js",
    "copy:img",
    "copy:icon",
    "copy:fonts",
    "copy:functionjs",
    "copy:webmanifest",
    "copy:icon",
    "watch"
  )
);

gulp.task(
  "build",
  gulp.series(
    "clean:html",
    "clean",
    "scss",
    "pug",
    "js",
    "copy",
    "minify:img",
    "minify:js"
  )
);
```