Vue.jsでスプレット演算子を使うとerrorになったのでWebpackを3から4に変えて対応

VueCLIで作ったアプリで、Vuexのヘルパー```mapState```をスプレット演算子にして使おうとしたらエラーになったので試行錯誤した上、webpackのversionを3から4に変えてbabelのversionをアップグレードしたりエラー処理をした手順を書きます。

```
"vue": "^2.5.22",
"vuex": "^3.1.0"
```

## 実行したかったこと

``` javascript:hoge.vue
import { mapState } from 'vuex';

export default {
  computed: {
    ...mapState(['admin']),
    currentRouteName() {
      return this.$route.name;
    },
  },
}
```

このように書くと、「スプレット演算子使えませんよ」と怒られる。

``` terminal:error内容
Syntax Error: Unexpected token (74:4)

  72 | export default {
  73 |   computed: {
> 74 |     ...mapState(['admin']),
     |     ^
  75 |     currentRouteName() {
  76 |       return this.$route.name;
  77 |     },
```

「ま、スプレット演算子使えるようにVueCLIで設定したファイルをいじれば大丈夫でしょ。」
と安易な気持ちで直そうと思ったら意外にハマったのでその直し方を共有します。

## まず始めにやったこと(直せなかった)

ググっていったらやはりスプレット演算子できなくて困ってる人がいました。

- [スプレッド演算子の導入](https://teratail.com/questions/111235)
- [Vue-loader: Unexpected token error for using spread operator on my vue component
](https://forum.vuejs.org/t/vue-loader-unexpected-token-error-for-using-spread-operator-on-my-vue-component/10241/6)
- [Error spread operator when using Bublé](https://github.com/vuejs/vue-hackernews-2.0/issues/87)

### 対応策

対応策を2つ見つけました。
が、どれもエラーは直らず。。

- plugin「transform-object-rest-spread」を使う
- bubleを使って対応

``` javascript:webpack.base.conf.js
"plugins": ["transform-object-rest-spread"]
```

``` javascript:vue-loader.conf.js
module.exports = {
  ...
  buble: {
    objectAssign: 'Object.assign',
  },
}
```

## 次にしたこと(エラーを解消)

次に考えたのはWebpackのversionをBabelのversionを上げてスプレット演算子を使えるようにするしかないかなって思いました。
なので下記のことをしました。

### 実行したこと

- webpackを3から4にする
- babelを最新「@」に変更
- `Tapable.plugin is deprecated. Use new API on .hooks instead`エラー解消
- `Error: Cannot find module 'babel-core'`エラー解消
- `Module build failed (from ./node_modules/eslint-loader/index.js):
TypeError: Cannot read property 'eslint' of undefined`エラー解消


### webpackを3から4にする

これはそんな難しいことじゃないですね。
それぞれを最新にしました。

``` javascript:package.json
"webpack": "^4.29.6",
"webpack-cli": "^3.2.3",
"webpack-dev-server": "^3.2.1",
```

### babelを最新「@」に変更

これはなかなか自力でできないのでググりました。

- [最新版で学ぶwebpack 4入門 – Babel 7でES2018環境の構築(React, Vue, Three.js, jQueryのサンプル付き)](https://ics.media/entry/16028#webpack-babel-vue)
- [Webpack 4：babel-loader のアップデートで起きたエラーを解決](https://qiita.com/oreo3@github/items/b383c57949e0f117cbea)

これらを参考にパッケージを最新にしたり```webpack.base.conf.js```を書き換えました

``` javascript:package.json
// add
"@babel/core": "^7.3.4",
"@babel/preset-env": "^7.3.4",

// remove
"babel-core": "^6.22.1",
"babel-loader": "^7.1.1",
"babel-plugin-syntax-jsx": "^6.18.0",
"babel-plugin-transform-runtime": "^6.22.0",
"babel-plugin-transform-vue-jsx": "^3.5.0",
"babel-preset-env": "^1.3.2",
"babel-preset-stage-2": "^6.22.0",
```

``` javascript:webpack.base.conf.js
{
  test: /\.js$/,
  exclude: /node_modules/,
  loader: 'babel-loader',
  query: {
    presets: [
      [
        "@babel/preset-env",
        {
          "useBuiltIns": "usage"
        }
      ]
    ],
  },
  include: [resolve('src'), resolve('test'), resolve('node_modules/webpack-dev-server/client')]
},
```

### `Tapable.plugin is deprecated. Use new API on .hooks instead`エラー解消

`yarn dev`をすると上記のエラーで怒られたのでそれを直します。

- [Tapable.plugin is deprecated. Use new API on `.hooks` instead](https://github.com/webpack/webpack/issues/6568)

ここに書いてあるように`html-webpack-plugin`と`extract-text-webpack-plugin`をupgradeすると書いてあるのでそれを実行

```
I'm still getting:

DeprecationWarning: Tapable.plugin is deprecated. Use new API on `.hooks` instead
DeprecationWarning: Tapable.apply is deprecated. Call apply on the plugin directly instead
with:

"html-webpack-plugin": "^3.0.4"
"extract-text-webpack-plugin": "^4.0.0-beta.0"
same with or without "extract-text-webpack-plugin": "^4.0.0-beta.0"
```

``` javascript:package.json
"html-webpack-plugin": "^3.2.0",
"extract-text-webpack-plugin": "4.0.0-beta.0",
```

### `Error: Cannot find module 'babel-core'`エラー解消

上記のエラーを解消するために`babel-loader`を最新にして、ついでに`css-loader`も最新にしました。

``` javascript:package.json
"css-loader": "^2.1.1",
"babel-loader": "^8.0.5",
```

### `Module build failed (from ./node_modules/eslint-loader/index.js):TypeError: Cannot read property 'eslint' of undefined`エラー解消

これが時間かかりました。
まずは`es-lint`を最新にしました。

``` javascript:package.json
// upgrade
"eslint": "^5.15.1",
"eslint-config-airbnb-base": "^13.1.0",
"eslint-friendly-formatter": "^4.0.1",
"eslint-loader": "^2.1.2",
"eslint-plugin-import": "^2.16.0",
"eslint-plugin-vue": "^5.2.2",
"eslint-import-resolver-webpack": "^0.11.0",
```

で、エラーの内容が`eslint-loader`が機能していないということなので、`es-loader`が書かれているところを見ると

``` javascript:webpack.base.conf.js
const createLintingRule = () => ({
  test: /\.(js|vue)$/,
  loader: 'eslint-loader',
  enforce: 'pre',
  include: [resolve('src'), resolve('test')],
  options: {
    formatter: require('eslint-friendly-formatter'),
    emitWarning: !config.dev.showEslintErrorsInOverlay
  }
});
・
・
・
module: {
    rules: [
      ...(config.dev.useEslint ? [createLintingRule()] : []),
```

js、vueファイルがcompileされる前にlintチェックをする処理なんですね。
で、その処理がWebpack4になってから書くところが変わったとのことで。

- [Webpack 4 issue with eslint](https://github.com/webpack/webpack/issues/6556)
- [LoaderOptionsPlugin](https://webpack.js.org/plugins/loader-options-plugin/)

`webpack.config.js`で`LoaderOptionsPlugin`pluginを読み込んでそこで定義しました。

``` javascript:webpack.dev.conf.js
new webpack.LoaderOptionsPlugin({ options: {
    test: /\.(js|vue)$/,
    loader: 'eslint-loader',
    enforce: 'pre',
    options: {
      formatter: require('eslint-friendly-formatter'),
      emitWarning: !config.dev.showEslintErrorsInOverlay
    }
  }
}),
```

``` javascript:これは消しました。
include: [resolve('src'), resolve('test')],
```

これでlocalを立ち上げることができました。

``` termninal
$ yarn dev
```

無事に`...mapState(['admin'])`も機能してリファクタリングできました。
3時間くらい要しました。。
※webpack.config,jsのproduction対応もしないと`yarn build`でerrorになります。