gulp.distをCopyWebpackPluginで再現する

近年ただのモジュールバンドラであるはずのwebpackをgulpのように扱う傾向があるように感じます。
その流行に乗っかってgulpをwbpackに完全replaceしてみたのですが、htmlやimageを `/dist` などにコピーするという単純な処理に手間取ったので、解決法をここに記しておこうと思います。

## ディレクトリ構成
```
root
    webpack.config.js
    - dist
    - src
        index.html
        - under
            - index.html
        - assets
            - images
                - test.png
```

## 目標
* これらのファイルを構造を維持しつつ、distへコピーする.
* webpack-dev-serverを使いながらでも、ファイルが更新されるようにする.
* watch中にファイルが新しく追加されてもコピーされるようにする.

## ライブラリのインストール
```bash
$ yarn add -D webpack webpack-cli webpack-dev-server copy-webpack-plugin write-file-webpack-plugin
```

`webpack` 、 `webpack-cli` 、 `webpack-dev-server` の説明は割愛します。
`copy-webpack-plugin` が今回の主役です。名前の通り、webpackでの単純なファイルコピーを実現します.

また、 `webpack-dev-server` を使うなら `write-file-webpack-plugin` もあると楽ちんです。
通常　`webpack-dev-server` の起動時、更新されたファイルはメモリ上でのみ展開されるのですが、 `write-file-webpack-plugin` を使うことによって、実ファイルが更新されるようになるのです。


## webpack.config.js
```js
const CopyWebpackPlugin = require('copy-webpack-plugin')
const path = require('path');
const webpack = require('webpack');
const WriteFilePlugin = require('write-file-webpack-plugin');


module.exports = {
  mode: process.env.NODE_ENV || "development",
  watch: false,
  watchOptions: {
    ignored: /node_modules/
  },
  devtool: 'source-map',
  output: {
    path: path.join(__dirname, './dist/assets'),
    publicPath: '/assets',
    filename: 'js/[name].js'
  },
  plugins: [
    new CopyWebpackPlugin(
      [
        {
          from: '',
          to: '../',
          ignore: [
            '!*.html'
          ]
        },
      ],
      { context: 'src' }
    ),
    new CopyWebpackPlugin(
      [
        {
          from: '',
          to: 'images/',
        },
      ],
      { context: 'src/assets/images' }
    ),
    new WriteFilePlugin(),
  ],
  devServer: {
    contentBase: path.resolve(__dirname, 'dist'),
    port: 8080,
  },
};

```

こうなります。

これだけの目的なら `output` のpathはそうじゃなくてよくね？と思われそうですが、この `webpack.config.js` はjsやcssをコンパイルする想定で書いています。

今回は見やすいようにjsやcssのloaderについての記述は省いています。


## CopyWebpackPluginの設定
まずやりがちなのが、fromに `src/**/*.html` とか書いちゃうことですが、そのように書くとコピーされた時 `dist/src/index.html` のように `from` のパスをすべて保ったまま吐き出されてしまいます。
最悪です。

さらに注意したいのが、 `from` に `**/*.html` のようにファイル形式を指定してしまうと、watc中に新規でファイルを追加した時にwebpackの監視対象から外れてしまいます。
その対策として、 `from` は `''` とだけ書き、 `context `と `to` で何を対象とし、どこにコピーするかを記述する必要があります。

あと `to` についての補足ですが、 `to` は `output`のpathの情報を引き継ぎます。

これらを踏まえた上で、画像とhtmlのコピーを例にとって設定方法を説明していきます。

## 例1: 画像のコピー
source: `src/assets/images/**/*`
dist: `dist/assets/images/**/*`

まず contextを設定しましょう。contextとは、fromの基準をどのパスとするかの設定です。
画像は `dist/assets/images` に入る想定です。
そしてfromが `''` ということは、contextは `src/assets/images` となりますね。

そして、toは output.pathの `dist/assets` を基準パスとした時、 `images/` になりますね。

つまり画像をコピーするための設定は

```js
new CopyWebpackPlugin(
  [
    {
      from: '',
      to: 'images/',
    },
  ],
  { context: 'src/assets/images' }
)
```
となります。

## 例2: htmlのコピー
source: `src/**/*.html`
dist: `dist/**/*.html`

まずconextを考えましょう. fromが `''` ということは、 contextは `src` となります。
そして、to は output.pathの `dist/assets` を基準パスとした時、 `../` になります。

ここまではいいのですが、このままだとどこにもファイル形式についての指定がないので、 `src` の中身が そのまま `dist` へ コピーされてしまいます。

そうではなく、htmlのみをコピーしたいので、 fromではないどこかにファイル形式の指定をする必要があります。

そこで、 `ignore` の出番です。
`ignore` は無視するファイルを glob形式で記述できるのですが、ここで html以外のような指定をすると、htmlだけがコピーされるようになります。

それらを合わせた結果、このような設定になります。

```js
new CopyWebpackPlugin(
  [
    {
      from: '',
      to: '../',
      ignore: [
        '!*.html'
      ]
    },
  ],
  { context: 'src' }
)
```

## 〆
このように、少し工夫した設定をすることで、無事gulp.distをCopyWebpackPluginで再現することができました。
果たしてこんなに頑張ってまでgulpから脱却する必要があるのかはいささか疑問ですが、それはそれとしましょう。