"create-react-app"を使用せずにReactプロジェクトを作成する

# はじめに
この記事は、個人開発の際に得た知識を、いつでも見返せるように保存する目的で使用しているため、言葉足らずかつ不適切である箇所が多々あるかと思います。気付いた箇所がありましたら、ご一報いただけると幸いです。

`create-react-app`を用いてReactアプリケーションを作成する場合、細かい設定を気にせず開発をスタートできるのは非常にありがたい機能です。しかし、それら設定が隠匿されているおかげ、裏で動作している機能を理解する機会を失っているのも事実です。そのため、`create-react-app`を使用せずにReactアプリケーションを構築していきます。

# 前提
Node.jsをすでにインストール済みであると仮定します。

# 内容
適当なディレクトリを作成し、その中で開発を行います。名前はお好きなもので結構です。その後、作成したディレクトリに移動してください。

## Nodeプロジェクトを作成する
Nodeプロジェクトを作り、`package.json`ファイルを生成します。以下のコマンドを実行してください。
```shell
$ npm init -y
```

## Babel関連のインストール
Babelは後に作成するJavaScriptを、後方互換のあるかたちに変換するためのコンパイラです。例えばES6に準拠したJavaScriptをES5までをサポートするブラウザで使用する場合、BabelはES6 JavaScriptをES5 JavaScriptに変換してくれます。

Reactは、HTMLに似たJSXシンタックを使用しアプリケーションを構築します。Babelはそれらを変換する目的でも使用されます。

以下のコマンドを実行し、Babelとそれに付随するパッケージをインストールします。
``` shell
npm install --save-dev @babel/core babel-loader @babel/cli @babel/preset-env @babel/preset-react
```
以下がインストールしたパッケージの詳細です。
- `@babel/core`: Babelのメインパッケージ
- `babel-loader`: Webpackとの橋渡し？
- `@babel/cli`: Babelをコマンドラインから使用可能にする。
- `@babel/preset-env`: ES6に準拠したJavaScriptを古いバージョンのJavaScriptに変換するパッケージのまとまり。
- `@babel/preset-react`: React JSXシンタックを異なるバージョンのJavaScriptに変換するパッケージ。

## Webpack関連のインストール
Webpackは複数に分かれて作成された、ファイルをバンドルし、一つのJavaScriptファイルを生成するパッケージです。バンドルできるファイルはJavaScriptのみではなく、CSSやJSONなど多岐にわたります。

以下のコマンドを実行し、Webpackとそれに付随するパッケージをおインストールします。
```shell
npm install --save-dev webpack webpack-cli webpack-dev-server
```
以下がインストールしたパッケージの詳細です。
- `webpack`: Webpackのメインパッケージ
- `webpack-cli`: Webpackをコマンドラインから使用可能にする。
- `webpack-dev-server`: 開発環境にて簡易的にサーバーとして機能する。

## HtmlWebpackPluginのインストール
HtmlWebpackPluginはメインとなるHTMLファイルの作成を手助けするパッケージです。Webpackは複数のファイルを単一のJavaScriptに変換してくれますが、実行毎に異なるファイル名が付与される場合、メインとなるHTMLはどれがバンドルされたJavsScriptなのかを認識する術を持ちません。その際に使用するのが HtmlWebpackPluginです。Webpackが全てのファイルをバンドルした際、HtmlWebpackPluginにメインのHTMLの元となるものを渡してあげることによって、自動的に新しいHTMLを生成してくれます。

以下のコマンドを実行し、HtmlWebpackPluginをインストールします。
```shell
npm install --save-dev html-webpack-plugin
```

## React関連のインストール
ReactとReactDOMをインストールします。以下のコマンドを実行してください。
```shell
npm install react react-dom 
```

## HTML, JavaScriptを書く
メインのHTMLと、Reactを使用したJavaScriptを作成します。まず、HTMLですが、`public`ディレクトリを作成し、その中に`index.html`ファイルを追加します。
```html:public/index.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Reactアプリケーション</title>
</head>
<body>
    <div id="root"></div>
</body>
</html>
```
次に`src`ディレクトリを作成し、その中に`App.js`ファイルを追加します。
```javascript:src/App.js
import React from "react";

export default function App() {
    return (
        <h1>
            Hello world!
            React without create-react-app.
        </h1>
    )
}
```
ルートディレクトリに戻り、作成するアプリケーションのエントリーポイントとなる`index.js`を作成します。
```javascript:index.js
import React from "react"
import ReactDOM from "react-dom"
import App from './src/App.js'

ReactDOM.render(
  <React.StrictMode>
    <App />
  </React.StrictMode>,
  document.getElementById('root')
)
```
`public/index.html`内の`<div id="root"><div>`に`App.js`で定義したアプリケーションを挿入しました。

## Babelの設定
ルートディレクトリ内で、`.babelrc`を作成し、以下を追加します。
```text:.babel.rc
{
    "presets": ["@babel/preset-env","@babel/preset-react"]
}
```
これは、Babelに以前にインストールした`@babel/preset-env`と`@babel/preset-react`を使用することを伝えます。

## Webpackの設定
ルートディレクトにて、`webpack.config.js`を作成します。内容が複雑になりがちなので、はじめに設定する項目を列挙します。
- __entry__: どのファイルを起点にバンドルを開始するか。今回の場合は`index.js`
- __mode__: バンドルするファイルに影響するプロパティ？
- __output__: Webpackがバンドルしたファイルをどこにどんな名前で吐き出すかを指定。
- __target__: サーバー側とブラウザ側どちらにコンパイルさせるかの設定。
- __devServer__: 開発サーバー(webpack-dev-server)の設定。
  - port: 使用するポート番号を指定する
  - static: 静的ファイル関連
- __resolve__: インポートする際に指定した拡張子を削除できる。
- __module__: 異なる種類のファイルをバンドルする際に、それぞれどのように操作するかを指定する。
- __plugins__: Webpack内で使用するプラグインを指定する。今回は`HTMLWebpackPlugin`を利用。
```javascript:webpack.config.js
const HtmlWebpackPlugin = require('html-webpack-plugin');
const path = require('path');

module.exports = {
  entry: './index.js',
  mode: 'development',
  output: {
    path: path.resolve(__dirname, './dist'),
    filename: 'index_bundle.js',
  },
  target: 'web',
  devServer: {
    port: '3000',
    static: {
      directory: path.join(__dirname, 'public')
    },
    open: true,
    hot: true,
    liveReload: true,
  },
  resolve: {
    extensions: ['.js', '.jsx', '.json'],
  },
  module: {
    rules: [
      {
        test: /\.(js|jsx)$/, 
        exclude: /node_modules/, 
        use: 'babel-loader', 
      },
    ],
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: path.join(__dirname, 'public', 'index.html')
    })
  ]
};

```

## ESlintの設定
ESlintは、JavaScriptやTypeScriptなどの静的解析ツールです。構文エラーや独自コーディングルールなどを定義することができます。

まずはじめに、初期設定ファイル`.eslintrc.js`を生成します。
```bash
$ npx eslint --init
> You can also run this command directly using 'npm init @eslint/config'.
> Need to install the following packages:
> @eslint/create-config@0.4.6
> Ok to proceed? (y) y
> ✔ How would you like to use ESLint? · problems
> ✔ What type of modules does your project use? · esm
> ✔ Which framework does your project use? · react
> ✔ Does your project use TypeScript? · No / Yes
> ✔ Where does your code run? · browser
> ✔ What format do you want your config file to be in? · JavaScript
> The config that you have selected requires the following dependencies:

> @typescript-eslint/eslint-plugin@latest eslint-plugin-react@latest @typescript-eslint/parser@latest
> ✔ Would you like to install them now? · No / Yes
> ✔ Which package manager do you want to use? · npm
> Installing @typescript-eslint/eslint-plugin@latest, eslint-plugin-react@latest, @typescript-eslint/parser@latest

> added 97 packages, and audited 816 packages in 7s

> 168 packages are looking for funding
>   run `npm fund` for details

> found 0 vulnerabilities
> Successfully created .eslintrc.js file in /Users/roishi/Desktop/Products/TimeCodeCoincident/frontend
```
作成後の中身は以下のような感じになっています。

```javascript:.eslintrc.js
module.exports = {
    "env": {
        "browser": true,
        "es2021": true
    },
    "extends": [
        "eslint:recommended",
        "plugin:@typescript-eslint/recommended",
        "plugin:react/recommended"
    ],
    "overrides": [
        {
            "env": {
                "node": true
            },
            "files": [
                ".eslintrc.{js,cjs}"
            ],
            "parserOptions": {
                "sourceType": "script"
            }
        }
    ],
    "parser": "@typescript-eslint/parser",
    "parserOptions": {
        "ecmaVersion": "latest",
        "sourceType": "module"
    },
    "plugins": [
        "@typescript-eslint",
        "react"
    ],
    "rules": {
    }
}
```

## Prettierの設定
Prettierは、JavaScript, TypeScript, JSX, CSSなどさまざまな言語に適応可能なコード整形ツールです。決められたコードスタイルを適応することができ、メンテナンス性を向上させます。

まずはインストールからです。
```bash
npm install --save-dev prettier eslint-config-prettier
```

ESlintに設定を追記する必要があります。
```javascript:.eslintrc.js
    "extends": [
        "eslint:recommended",
        "plugin:@typescript-eslint/recommended",
        "plugin:react/recommended",
        "prettier" <- 追加
    ],
```

Prettierの設定ファイル`.prettierrc`を作成します。
```shell:.prettierrc
{
  "singleQuote": true,
  "printWidth": 80,
  "tabWidth": 2
}
```
指定可能なオプションの詳細は[公式ドキュメント](https://prettier.io/docs/en/options.html)で確認できます。

## package.jsonに新しいコマンドの定義
ルートディレクトリにて、一番はじめに生成された`package.json`に以下を追加します。
```json:package.json
"scripts": {
    "start": "webpack-dev-server .",
    "build": "webpack ."
  }
```

## `npm run start`で実行
package.jsonにて定義したコマンドを用いて、アプリケーションを実行しましょう。うまくいくと以下のように表示されるはずです。
![スクリーンショット 2023-11-26 3.53.46.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3626697/ba025688-af05-4965-858a-e1bf89faa16d.png)

指定したポート番号によっては以下のようなエラーに遭遇する可能性があります。
```
Sanma 0:00:00 ~/Desktop/hoge $ npm run start

> dev@1.0.0 start
> webpacl-dev-server .

/Users/Sanma/Desktop/hoge/node_modules/webpack-dev-server/lib/Server.js:2557
    throw error;

Error: listen EADDRINUSE: address already in use :::5000
```
その際は、使用するポート番号を変更してください。

# 終わりに
`create-react-app`を使用することなく、Reactアプリケーションを作成することができました。思ったより複雑なことが一つのコマンドで実行されており、隠匿された機能の多さに正直圧倒されました。とはいえ、アプリケーション開発の大枠を掴むことができたので理解が大きく前進したと言えるでしょう。
以上です。お読みいただきありがとうございました。