React18がリリースされたので、開発環境をゼロから構築してみる。【中編】

[前編](https://qiita.com/kztmk_media_pep/items/a00631a5e1bb1cd13597)からの続きです。

## なぜWebpack設定ファイルを分割するのか？

webpackは、「JavaScript」「CSS」「SASS」「画像」などを

* 1つ、または、複数のファイルにまとめる。
* 出力されたファイルを圧縮・難読化する。

などの機能があります。

設定ファイルが複数あれば、

開発時にはJavaScript、CSSはソースマップ付きで読みやすく、リリースビルド時には、圧縮しファイルサイズを小さくする。

開発時には、圧縮関連のプラグインを読み込まずコンパイル時間を短くする。

など、開発時、リリースビルド時で設定を柔軟に変更することができます。


### Webpack設定ファイルの分割

今回は、webpack.config.jsを

* 共用
* 開発版
* リリース版

の3つに分割し、開発時は、

共用版 + 開発版

リリースビルド時は、

共用版 + リリース版

を使うようにします。
 

コマンドラインで作成された「webpack.config.js」は以下の通りです。

それに加え、[前編](https://qiita.com/kztmk_media_pep/items/a00631a5e1bb1cd13597)で、リリースビルド用に

* リリースビルド時に、console関数を除去するためのプラグイン
* CSS圧縮プラグイン

を導入しました。

```javascript
// Generated using webpack-cli https://github.com/webpack/webpack-cli

const path = require("path");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");

const isProduction = process.env.NODE_ENV == "production";

const stylesHandler = MiniCssExtractPlugin.loader;

const config = {
  entry: "./src/index.js",
  output: {
    path: path.resolve(__dirname, "public"),
  },
  devServer: {
    open: true,
    host: "localhost",
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: "src/index.html",
    }),

    new MiniCssExtractPlugin(),

    // Add your plugins here
    // Learn more about plugins from https://webpack.js.org/configuration/plugins/
  ],
  module: {
    rules: [
      {
        test: /\.(ts|tsx)$/i,
        loader: "ts-loader",
        exclude: ["/node_modules/"],
      },
      {
        test: /\.css$/i,
        use: [stylesHandler, "css-loader"],
      },
      {
        test: /\.s[ac]ss$/i,
        use: [stylesHandler, "css-loader", "sass-loader"],
      },
      {
        test: /\.(eot|svg|ttf|woff|woff2|png|jpg|gif)$/i,
        type: "asset",
      },

      // Add your rules for custom modules here
      // Learn more about loaders from https://webpack.js.org/loaders/
    ],
  },
  resolve: {
    extensions: [".tsx", ".ts", ".js"],
  },
};

module.exports = () => {
  if (isProduction) {
    config.mode = "production";
  } else {
    config.mode = "development";
  }
  return config;
};
```

#### webpack.config.jsを分割する準備

まずは、上記ファルをコピーし

* webpack.config.common.js ---共用
* webpack.config.dev.js --- 開発用
* webpack.config.prod.js --- リリースビルド用

の3つのファイルを作成します。

#### 共用部分

開発時、リリースビルド時に共通して必要なものは、

* entryポイント
* output
* 出力先にindex.htmlファイルを作成するHtmlWebpackPlugin
* モジュール部分から、cssとs[ac]ss部分以外
* resolve

です。

モジュール部分の「css」「s[ac]ss」部分は、開発時とリリースビルド時で読み込むモジュールが違うため共通部分から削除し「webpack.config.dev.js」「webpack.config.prod.js」で使用します。


作成した「webpack.config.common.js」から不要な部分を削除したものです。
```javascript
const path = require("path");
const HtmlWebpackPlugin = require("html-webpack-plugin");

module.exports = {
  entry: "./src/index.js",
  output: {
    path: path.resolve(__dirname, "public"),
    assetModuleFilename: 'image/[name][ext][query]',
    clean: true,
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: "index.html",
    }),
  ],
  module: {
    rules: [
      {
        test: /\.(ts|tsx)$/i,
        loader: "ts-loader",
        exclude: ["/node_modules/"],
      },
      {
        test: /\.(eot|svg|ttf|woff|woff2|png|jpg|gif)$/i,
        type: "asset",
      },

      // Add your rules for custom modules here
      // Learn more about loaders from https://webpack.js.org/loaders/
    ],
  },
  resolve: {
    extensions: [".tsx", ".ts", ".js"],
  },
};

```
#### 開発用

開発用には、先ほどのcssモジュール部でのcss関連、開発用サーバーのdevServer関連が共通部分に追加されます。


devServerは通常の「--open」コマンドだと規定のブラウザが開くため、Google Chromeが開くようにします。

Google Chromeは、OSにより記述がことなるためOS判定部分も追加しています。

共通部分にあるものを削除し、必要な部分を追加したものが以下となります。

```javascript
const { merge } = require('webpack-merge');
const common = require('./webpack.config.common');
const os = require('os');

let devBrowser = 'Google Chrome';
switch (os.platform()) {
  case 'win32':
    devBrowser = 'chrome';
    break;
  case 'linux':
    devBrowser = 'google-chrome';
    break;
  default:
    break;
}

module.exports = merge(common, {
  mode: 'development',
  module: {
    rules: [
      {
        test: /\.css$/i,
        use: ['style-loader', 'css-loader'],
      },
      {
        test: /\.s[ac]ss$/i,
        use: ['style-loader', 'css-loader', 'sass-loader'],
      },
    ],
  },
  devtool: 'eval-source-map',
  devServer: {
    open: {
      app: {
        name: devBrowser,
      },
    },
    host: 'localhost',
    port: 3000,
    static: './public',
  },
});
```

#### リリースビルド用

リリースビルド用には、css関連を圧縮するモジュール、console関数を除去するプラグインをしようするようにしています。

```javascript
const { merge } = require('webpack-merge');
const common = require('./webpack.config.common');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const TerserPlugin = require('terser-webpack-plugin');
const CssMinimizerPlugin = require('css-minimizer-webpack-plugin');

module.exports = merge(common, {
  mode: 'production',
  plugins: [new MiniCssExtractPlugin(), new CssMinimizerPlugin()],
  module: {
    rules: [
      {
        test: /\.css$/i,
        use: [MiniCssExtractPlugin.loader, 'css-loader'],
      },
      {
        test: /\.s[ac]ss$/i,
        use: [MiniCssExtractPlugin.loader, 'css-loader', 'sass-loader'],
      },
    ],
  },
  resolve: {
    extensions: ['.tsx', '.ts', '.js'],
  },
  optimization: {
    minimize: true,
    minimizer: [
      new TerserPlugin({
        minify: TerserPlugin.uglifyJsMinify,
        terserOptions: {
          compress: {
            drop_console: true,
          },
        },
      }),
      new CssMinimizerPlugin(),
    ],
  },
});
```

#### テスト

webpackの設定ファイルをテストします。

開発用

```shell
npx webpack configtest ./webpack.config.dev.js
[webpack-cli] Validate '/Users/yaruo_react_redux/zero-start-react18/webpack.config.dev.js'.
[webpack-cli] There are no validation errors in the given webpack configuration.
```

リリースビルド用
```shell
npx webpack configtest ./webpack.config.prod.js
[webpack-cli] Validate '/Users/yaruo-react-redux/zero-start-react18/webpack.config.prod.js'.
[webpack-cli] There are no validation errors in the given webpack configuration.
```

#### スクリプト追加
package.jsonの「script」へ以下を追加します。

```json
 "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "build": "webpack --config webpack.prod.js",
    "build:dev": "webpack --config webpack.config.dev.js",
    "build:prod": "webpack --config webpack.config.prod.js",
    "start": "webpack serve --config webpack.config.dev.js"
  },
```

Google Chromeで開くかを確認します。

```shell
npm run start
```

以上で、webpack設定ファイルを分割することができました。


ここまでの内容は、以下から取得できます。

```shell
git clone -b 04_webpack-config-split https://github.com/yaruo-react-redux/yaruo-start-template-react18.git
```

また、詳しく解説しているものは、こちらです。

https://techbookfest.org/product/4616093891035136?productVariantID=4899714673672192


[←前編](https://qiita.com/kztmk_media_pep/items/a00631a5e1bb1cd13597)　　　　　[後編→](https://qiita.com/kztmk_media_pep/items/a9cc5186cdaa3771e2ab)