設定ファイル不要！webpack 4 でReactをビルドしてみた

[React #1 Advent Calendar 2017](https://qiita.com/advent-calendar/2017/react) 2日目の記事です。
もう2018年になって一月経ちましたが空いていたので埋めます。

webpack 4 ~~のbeta版が2018/01/25 JSTにプレリリースされました。
おそらく2月か3月中には正式リリースされるでしょう。~~
(追記 2/25)
正式リリースされました:raised_hands: 
正式版でもこの記事の内容に問題が無いことを確認しました。
https://github.com/webpack/webpack/releases/tag/v4.0.0

今回のwebpackは変更点が多く、速度改善もされているようです。
また設定ファイルも不要となり、webpack.config.js書くのが嫌な方には朗報です。
この記事では設定ファイル無しでビルドするところまで簡単な例を用いて紹介いたします。

# webpack 4について
今回はReactのビルドに関する記事なのでwebpack4の変更点をすべて載せる予定はありません。
それらについては以下の記事が詳しいので拝見してください。

- [Release v4.0.0-beta.0 · webpack/webpack](https://github.com/webpack/webpack/releases/tag/v4.0.0-beta.0)
- [次のリリースであるwebpack 4の主な変更点まとめ](http://abouthiroppy.hatenablog.jp/entry/2017/12/25/100004)
- [webpack 4 beta — try it today!](https://medium.com/webpack/webpack-4-beta-try-it-today-6b1d27d7d7e2)
- [Webpack 4 tutorial: All You Need to Know, from 0 Conf to Production Mode](https://www.valentinog.com/blog/webpack-4-tutorial/)
- [Release v4.0.0 · webpack/webpack](https://github.com/webpack/webpack/releases/tag/v4.0.0)


# 拙者が試したプロジェクトの周辺環境
- babel-preset-env
- babel-preset-react
- flow
- webpack 3.8

# これまでのwebpackの内容
`js`と`jsx`ファイルをミニファイして一つにバンドルしているだけです。

```js
const path = require('path');
const webpack = require('webpack');

module.exports = {
  entry: {
    'index': [
      path.resolve(__dirname, 'src/index.js')
    ]
  },
  output: {
    filename: '[name].js',
    path: path.resolve(__dirname, 'public'),
    publicPath: '/',
  },
  plugins: [
    new webpack.DefinePlugin({
      'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV)
    }),
    new webpack.optimize.UglifyJsPlugin(),
  ],
  module: {
    rules: [
      {
        test: /\.(js|jsx)$/,
        use: 'babel-loader',
      },
    ]
  },
  resolve: {
    extensions: ['.js', '.jsx'],
  },
};

```
# webpack 4への移行

## 準備
webpack 4とwebpack-cliをインストールします。
webpackをCLIで実行するのにこれまではwebpackだけで実行できたのですが、
webpack 4からはwebpack-cliに分離しました。

```bash
npm install --save-dev webpack webpack-cli
```

## 設定ファイルを使ったビルド
設定ファイルありバージョンを先に紹介します。
上記で紹介したwebpack.config.jsではwebpack4を使ったビルドは通りません。

上記の設定のままwebpackを実行すると、
`TypeError: webpack.optimize.UglifyJsPlugin is not a constructor
`とエラーが出ました。

これは`mode`という新しいオプションが関係します。
`mode`には`production`と`development`があります。
`production`は最適化オプションである`webpack.optimization`のプラグインが有効になります。
そのため、今まで指定していた`webpack.optimize.UglifyJsPlugin`は不要となります。
`development`では最適化はされず、`eval-source-maps`が有効になります。
またwatchで動かす場合は`development`を指定しましょう。
`mode`を指定せずにwebpackを実行した場合、どちらかを指定するように警告が出ます。
なので、`mode`も設定しましょう。

修正したwebpack.config.jsは以下の通りです。

```js
const path = require('path');
const webpack = require('webpack');

module.exports = {
  mode: 'production', // 追加
  entry: {
    'index': [
      path.resolve(__dirname, 'src/index.js')
    ]
  },
  output: {
    filename: '[name].bundle.js',
    path: path.resolve(__dirname, 'public'),
    publicPath: '/',
  },
  plugins: [
    new webpack.DefinePlugin({
      'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV)
    })
    // webpack.optimize.UglifyJsPluginを削除
  ],
  module: {
    rules: [
      {
        test: /\.(js|jsx)$/,
        use: 'babel-loader',
      },
    ]
  },
  resolve: {
    extensions: ['.js', '.jsx'],
  },
};
```

## 設定ファイルを使わないビルド
今回の目玉でもあるでしょう設定ファイルwebpack.configを使わないビルドについて紹介します。

### module-bind
まず何も考えずにオプションを付けずにwebpackを実行してみると、以下のようなエラーが出ました。

```
Module parse failed: Unexpected token (15:6)
You may need an appropriate loader to handle this file type.
|   render() {
|     return (
|       <div>
```

これはbabelが利いておらず、JSX文法を解析できていないためです。
webpack.config.jsでは以下のように指定していた部分です。

```js
module: {
  rules: [
    {
      test: /\.(js|jsx)$/,
      use: 'babel-loader',
    },
  ]
},
```

webpack.config内で指定していた`babel-loader`を指定するには`--module-bind`オプションを使います。
`--module-bind 拡張子=ローダー名`で使うことができます。

```
webpack --module-bind 'js=babel-loader' --module-bind 'jsx=babel-loader'
```

_※jsとjsxを一つにまとめて`js,jsx=babel-loader`ということができたら良いのですが、うまくいきませんでした。もし知っている方がいればコメントお願い致します。_


### resolve-extensions
上記`--module-bind`オプションをつけて実行すると別のエラーがでました。

```
Module not found: Error: Can't resolve './Foo' in '/path/to/src'
```

これはimport先のモジュールが読み込めていないエラーです。
以下のように拡張子を省いている場合、ファイル解決ができません。

```js
import Foo from 'components/Foo'
```

これをwebpack.config.jsでは以下で設定することで解決していました。

```js
resolve: {
  extensions: ['.js', '.jsx'],
},
```

CLIにもこれと同じことができる`resolve-extensions`というオプションがあります。
`--resolve-extensions .js,.jsx`のように拡張子を指定します。
先程の`module-bind`と組み合わせると以下のようになります。

```
webpack --module-bind 'js=babel-loader' --module-bind 'jsx=babel-loader' --resolve-extensions .js,.jsx 
```

### mode
このままでは警告が出ます。というのも前述した`mode`を指定していないためです。
`mode`オプションをつけると警告は消えます。以下のようになります。

```
webpack --module-bind 'js=babel-loader' --module-bind 'jsx=babel-loader' --resolve-extensions .js,.jsx --mode production
```

### entry
エントリポイントを指定することもできます。
`--entry src`のように指定するとsrcディレクトリ内にあるindex.jsをエントリポイントとします。
`--entry src/entry.js`のようにファイルを指定することもできます。

### output
出力先を指定することもできます。
`--output public`とするとpublicディレクトリにエントリポイントのファイルと同じファイル名のファイルができます。
`--output public/index.bundle.js`のようにファイル名を指定することもできます。
また、`-o public/index.bundle.js`と`--output`を`-o`とすることもできます。

### 最終形
上記で紹介したオプションをすべて反映させて最終的には以下のようにすることで、
前述したwebpack.config.jsを使ったビルドと同じことができます。

```
webpack --module-bind 'js=babel-loader' --module-bind 'jsx=babel-loader' --resolve-extensions .js,.jsx --mode production --entry ./src --output public/index.bundle.js
```

## 他オプション
`webpack --help` でオプションの一覧が見れます。
各オプションについては別の記事で紹介するかもしれません。

# 所感
webpackでもparcelのように設定ファイル無しでビルドすることができました。しかし数多くのオプションを指定しなければいけません。
webpack.config.jsを用いる方がわかりやすいかどうか好みが別れるとこかと思いますが、個人的には選択肢が増えて良いかと思います。
もっと複雑な構成になると設定ファイル無しでどこまでできるかまだ検証できていませんが、これからはできる限り設定ファイル無しで開発できたらいいなと考えています。
また、今回のwebpackはビルド速度の改善も大幅にされています。今回試した感じではParcelより早いかもと感じました。
[Keep webpack Fast: A Field Guide for Better Build Performance](https://slack.engineering/keep-webpack-fast-a-field-guide-for-better-build-performance-f56a5995e8f1)がビルド速度改善に関して詳しく書かれています。

# 参考
- [Release v4.0.0-beta.0 · webpack/webpack](https://github.com/webpack/webpack/releases/tag/v4.0.0-beta.0)
- [次のリリースであるwebpack 4の主な変更点まとめ](http://abouthiroppy.hatenablog.jp/entry/2017/12/25/100004)
- [webpack 4 beta — try it today!](https://medium.com/webpack/webpack-4-beta-try-it-today-6b1d27d7d7e2)
- [Webpack 4 tutorial: All You Need to Know, from 0 Conf to Production Mode](https://www.valentinog.com/blog/webpack-4-tutorial/)
- [Keep webpack Fast: A Field Guide for Better Build Performance](https://slack.engineering/keep-webpack-fast-a-field-guide-for-better-build-performance-f56a5995e8f1)
- [Release v4.0.0 · webpack/webpack](https://github.com/webpack/webpack/releases/tag/v4.0.0)


最後までお読み頂きありがとうございました。
不備や質問がありましたらコメントお願い致します。