自分用ダッシュボード、My Todo NoteのWebpack設定


この記事は下記のシリーズの記事の 1 つです。

関連シリーズ:自分用の Dashboard サービスを作成する

注意:Webpack の設定構築、学習は時間がかかります

Webpack 設定を自分で書く場合、設定やデバッグにかなり時間をとられます。目的が React の使い方を学ぶことなら、自身で作成せずに CreateReactApp を使用して設定済みの環境をベースに開発するか、この記事で紹介するブランチのものをそのまま使用することをお勧めします。

My-Todo-Note では Webpack 設定を自分で書く予定なので記事にしていますが、これはわたしが Webpack 設定と慣れ親しんでおきたい、設定を色々実験したいのが理由であり、CreateReactApp と比較して特段良いことはありません。多分。

開発用リポジトリ

My-Todo-Note は次のリポジトリで管理しています。

この記事の最終結果は次のブランチで確認できます。

それでは、長く恐ろしい開発環境の構築を始めます。

package.json の作成

まずは package.json の準備。手動で作成しても良いです。

yarn init -y

React と使用予定のライブラリの追加

必要になった段階で随時追加すれば良いため、まずは思いついたもののみ追加します。

yarn add react react-dom react-router-dom axios classnames dotenv formik yup firebase

TypeScript 用に型定義のライブラリを追加。

yarn add -D @types/react @types/react-dom @types/classnames @types/react-router-dom

Webpack の追加

Webpack と関連ライブラリを追加します。やや手探りに進めているため必要なものは随時追加し、必要のないものは随時削除します。

Webpack 周りは何が最低限必要かを把握するのに毎回苦労し、設定含めスムーズに終わったことがありません。CreateReactApp の凄さを感じる部分です。

Webpack 本体

yarn add -D webpack webpack-cli webpack-dev-server webpack-merge dotenv-webpack html-webpack-plugin clean-webpack-plugin

JavaScript、TypeScript 関連

yarn add -D @babel/cli @babel/core @babel/preset-env @babel/preset-typescript @babel/preset-react @babel/plugin-transform-async-to-generator babel-loader typescript core-js regenerator-runtime fork-ts-checker-webpack-plugin @typescript-eslint/eslint-plugin @typescript-eslint/parser

css 関連

yarn add -D sass sass-loader sass-resources-loader css-loader style-loader

Webpack の設定ファイルの追加

開発用 と 本番用(build 設定)、共通設定の 3 ファイル作成します。build が必要になった段階で production を作る想定で、ひとまず開発ができるところを目指します。

エントリーファイルの追加

public/index.html を作成します

<!DOCTYPE html>
<html lang="jp">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>My ToDo Note</title>
  </head>
  <body>
    <div id="root"></div>
  </body>
</html>

webpack 設定ファイルの追加

webpack.dev.js、webpack.prod.js、webpack.common.js を追加します。

現在のファイル構成

.
├── LICENSE
├── package.json
├── public
   └── index.html
├── webpack.common.js
├── webpack.dev.js
├── webpack.prod.js
└── yarn.lock

webpack.common.js の修正

色々と調べながら、下記のように作成しました。これも修正の必要性を感じた段階でブラッシュアップします。ひとまず動けば良いと考えています。

css は通常の css、scss、css-modules の 3 種を扱えるように設定しました。*.module.scss でファイルを作成すれば css-module となります。共通の style は src/styles/shared/*.scss に設定します。

const path = require("path");
const { CleanWebpackPlugin } = require("clean-webpack-plugin");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const ForkTsCheckerWebpackPlugin = require("fork-ts-checker-webpack-plugin");
const Dotenv = require("dotenv-webpack");

module.exports = {
  mode: "development",
  entry: {
    app: "./src/index.tsx",
  },
  plugins: [
    new CleanWebpackPlugin(),
    new HtmlWebpackPlugin({
      template: "./public/index.html",
    }),
    new ForkTsCheckerWebpackPlugin(),
    new Dotenv(),
  ],
  output: {
    filename: "[name].bundle.js",
    path: path.resolve(__dirname, "dist"),
  },
  module: {
    rules: [
      {
        test: /\.(j|t)sx?$/,
        loader: "babel-loader",
        exclude: /node_modules/,
      },
      {
        test: /\.css$/i,
        use: ["style-loader", "css-loader"],
      },
      {
        test: /\.scss$/,
        exclude: /\.module\.scss$/,
        use: [
          {
            loader: "style-loader",
          },
          {
            loader: "css-loader",
            options: {
              importLoaders: 1,
              modules: {
                compileType: "icss",
              },
            },
          },
          {
            loader: "sass-loader",
          },
        ],
      },
      {
        test: /\.module\.scss$/,
        use: [
          {
            loader: "style-loader",
          },
          {
            loader: "css-loader",
            options: {
              importLoaders: 1,
              modules: {
                compileType: "module",
              },
            },
          },
          {
            loader: "sass-loader",
          },
          {
            loader: "sass-resources-loader",
            options: {
              resources: "src/styles/shared/*.scss",
            },
          },
        ],
      },
    ],
  },
  resolve: {
    extensions: [".ts", ".tsx", ".js", ".jsx"],
  },
};

webpack.dev.js の編集

開発時の dev-server 設定を書きます。

const { merge } = require("webpack-merge");
const common = require("./webpack.common.js");

module.exports = merge(common, {
  mode: "development",
  devtool: "inline-source-map",
  devServer: {
    contentBase: "./dist",
    hot: true,
    open: true,
    historyApiFallback: true,
  },
});

webpack.prod.js の編集

const { merge } = require("webpack-merge");
const common = require("./webpack.common.js");

module.exports = merge(common, {
  mode: "production",
});

babel 用設定ファイルの追加

package.json と同じ階層に babel.config.json を作成します。

{
  "presets": [
    [
      "@babel/preset-env",
      {
        "useBuiltIns": "usage",
        "corejs": "3"
      }
    ],
    "@babel/preset-typescript",
    "@babel/preset-react"
  ],
  "plugins": ["@babel/plugin-transform-async-to-generator"]
}

tsconfig.json の追加

同様に tsconfig.json を作成し、TypeScript 用の設定ファイルを作成してざっくりとルール追加します。

{
  "compilerOptions": {
    "outDir": "./dist/",
    "noImplicitAny": true,
    "module": "es6",
    "target": "es5",
    "jsx": "react",
    "allowJs": true,
    "sourceMap": true,
    "allowSyntheticDefaultImports": true,
    "moduleResolution": "node",
    "esModuleInterop": true
  },
  "exclude": ["node_modules"]
}

package.json に script を追加

  "scripts": {
    "start": "webpack serve --config webpack.dev.js "
  }

src/index.tsx の追加

ここまでの設定で Webpack が上手く動作するかを確認します。index.tsx ファイルを作成し、React で Hello World!!!を表示させます。

import "core-js/stable";
import "regenerator-runtime/runtime";
import React from "react";
import ReactDOM from "react-dom";

ReactDOM.render(<div>Hello World!!!</div>, document.getElementById("root"));
yarn start

エラーなく dev-server が起動した場合、localhost:8080 で Hello World が表示されます。

"Hello World"

css のチェック

次に css 関連が正しく動作しているかを確認します。scss を import できるよう型定義ファイルを作成しておきます。

  • styles.d.ts
declare module "*.scss" {
  interface IClassNames {
    [className: string]: string;
  }
  const classNames: IClassNames;
  export = classNames;
}

index.tsx を次のように修正します。

import "core-js/stable";
import "regenerator-runtime/runtime";
import React from "react";
import ReactDOM from "react-dom";
import { App } from "./App";
import "./styles/index.scss";

ReactDOM.render(<App />, document.getElementById("root"));

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

  • src/App.tsx
  • src/styles/index.scss
  • src/styles/shared/colors.scss
  • src/styles/app.module.scss

App.tsx

import React from "react";
import styles from "./styles/app.module.scss";

export const App = () => {
  return <div className={styles.appTxt}>App!!!</div>;
};

colors.scss

$blue: blue;

app.module.scss

.appTxt {
  color: $blue;
}

実際の画面

app

スタイルが正しく変更されていれば OK です。

ESLint、Prettier の追加

これらも後で設定を詰めていけば良いので、ざっくりと設定します。

yarn add -D eslint eslint-plugin-react eslint-plugin-react-hooks prettier

新規で設定ファイルを作成

Webpack との連動は後回しで、vscode でシンタックスが出る程度で良しとします。

  • .eslintrc.json
  • .prettierrc.json
// .eslintrc.json
{
  "parser": "@typescript-eslint/parser",
  "plugins": ["@typescript-eslint"],
  "extends": [
    "eslint:recommended",
    "plugin:react/recommended",
    "plugin:react-hooks/recommended",
    "plugin:@typescript-eslint/recommended"
  ],
  "parserOptions": {
    "ecmaVersion": 6,
    "sourceType": "module",
    "babelOptions": {
      "configFile": "./babel.config.json"
    }
  },
  "env": {
    "browser": true,
    "commonjs": true
  },
  "rules": {
    "react/prop-types": "off"
  }
}
// .prettierrc.json
{
  "arrowParens": "always",
  "bracketSpacing": true,
  "embeddedLanguageFormatting": "auto",
  "htmlWhitespaceSensitivity": "css",
  "insertPragma": false,
  "jsxBracketSameLine": false,
  "jsxSingleQuote": false,
  "printWidth": 80,
  "proseWrap": "preserve",
  "quoteProps": "as-needed",
  "requirePragma": false,
  "semi": true,
  "singleQuote": false,
  "tabWidth": 2,
  "trailingComma": "es5",
  "useTabs": false
}

lint-staged の追加

lint-staged を使用すると、git にファイルを commit する度に eslint と prettier を自動で動作させる設定を追加できます。

npx mrm lint-staged

package.json を修正し、対象ファイルを変更します

  "lint-staged": {
    "src/*.{js,ts,tsx}": "eslint --fix",
    "src/*.{js,ts,tsx,css,scss,md}": "prettier --write"
  }

ignore 系ファイルの追加

eslint や prettier、git などの ignore ファイルを追加します。node_modules を前提に、他は必要なものを適時追加します。

最終:ファイル構成

ここまでの作業で次のようになります。

.
├── LICENSE
├── package.json
├── public
   └── index.html
├── src
   ├── App.tsx
   ├── index.tsx
   └── styles
       ├── app.module.scss
       ├── index.scss
       └── shared
           └── color.scss
├── styles.d.ts
├── tsconfig.json
├── babel.config.json
├── webpack.common.js
├── webpack.dev.js
├── webpack.prod.js
└── yarn.lock

最低限の開発環境は整いました。次回はトップページとルーティングを作成します。