create-react-appで作成した雛形 + VSCodeにESLintとPrettierを導入する

前回、[DockerでReact + Swagger 環境を作ろう](https://qiita.com/h-yoshikawa44/items/962712623376fd40e1c5)という記事を書いたのですが、VSCode に ESLint 導入時に少し手間取ったので別記事にしました。
あわせてコード整形ツールである Prettier も導入したので、その手順を書いておきます。

※2021/03/22追記　`eslint-plugin-prettier`が非推奨になったりと情報が古くなったので、全体的に書き直しました。
※2021/08/22追記　`--template typescript`で TypeScript にした場合にも対応しました。

## ESLint
静的解析ツールです。
コーディングする上でのルールを決められるので、チーム開発するうえでコードのスタイルを統一することができます。

### セットアップの確認
https://eslint.org/

create-react-app で作成された雛形では、すでに ESLint に関するパッケージが導入されており、yarn.lock で`react-scripts`の依存関係となっているのを確認できます。
create-react-app を`--template typescript`としていても、同様です。

以下は`create-react-app@4.0.3`で作成した React プロジェクトの例です。

```
※一部抜粋
react-scripts@4.0.3:
  version "4.0.3"
  resolved "https://registry.yarnpkg.com/react-scripts/-/react-scripts-4.0.3.tgz#b1cafed7c3fa603e7628ba0f187787964cb5d345"
  integrity sha512-S5eO4vjUzUisvkIPB7jVsKtuH2HhWcASREYWHAQ1FP5HyCv3xgn+wpILAEWkmy+A+tTNbSZClhxjT3qz6g4L1A==
  dependencies:
    "@babel/core" "7.12.3"
    "@pmmmwh/react-refresh-webpack-plugin" "0.4.3"
    "@svgr/webpack" "5.5.0"
    "@typescript-eslint/eslint-plugin" "^4.5.0"
    "@typescript-eslint/parser" "^4.5.0"
    babel-eslint "^10.1.0"
    babel-jest "^26.6.0"
    babel-loader "8.1.0"
    babel-plugin-named-asset-import "^0.3.7"
    babel-preset-react-app "^10.0.0"
    bfj "^7.0.2"
    camelcase "^6.1.0"
    case-sensitive-paths-webpack-plugin "2.3.0"
    css-loader "4.3.0"
    dotenv "8.2.0"
    dotenv-expand "5.1.0"
    eslint "^7.11.0"
    eslint-config-react-app "^6.0.0"
    eslint-plugin-flowtype "^5.2.0"
    eslint-plugin-import "^2.22.1"
    eslint-plugin-jest "^24.1.0"
    eslint-plugin-jsx-a11y "^6.3.1"
    eslint-plugin-react "^7.21.5"
    eslint-plugin-react-hooks "^4.2.0"
    eslint-plugin-testing-library "^3.9.2"
    eslint-webpack-plugin "^2.5.2"
    file-loader "6.1.1"
    fs-extra "^9.0.1"
    html-webpack-plugin "4.5.0"
    identity-obj-proxy "3.0.0"
    jest "26.6.0"
    jest-circus "26.6.0"
    jest-resolve "26.6.0"
    jest-watch-typeahead "0.6.1"
    mini-css-extract-plugin "0.11.3"
    optimize-css-assets-webpack-plugin "5.0.4"
    pnp-webpack-plugin "1.6.4"
    postcss-flexbugs-fixes "4.2.1"
    postcss-loader "3.0.0"
    postcss-normalize "8.0.1"
    postcss-preset-env "6.7.0"
    postcss-safe-parser "5.0.2"
    prompts "2.4.0"
    react-app-polyfill "^2.0.0"
    react-dev-utils "^11.0.3"
    react-refresh "^0.8.3"
    resolve "1.18.1"
    resolve-url-loader "^3.1.2"
    sass-loader "^10.0.5"
    semver "7.3.2"
    style-loader "1.3.0"
    terser-webpack-plugin "4.2.3"
    ts-pnp "1.2.0"
    url-loader "4.1.1"
    webpack "4.44.2"
    webpack-dev-server "3.11.1"
    webpack-manifest-plugin "2.2.0"
    workbox-webpack-plugin "5.1.4"
  optionalDependencies:
    fsevents "^2.1.3"
```

設定に関しては`package.json`に記述があり、デフォルトではこの設定で ESLint が動作します。

```json
"eslintConfig": {
    "extends": [
      "react-app",
      "react-app/jest"
    ]
  },
```

最低限のセットアップはすでに行われているので、あとはそれをカスタマイズしていくだけです。

### CLI で動かしてみる
チェックを行いたいファイルパスを指定して実行します。

コマンド例

```bash
# JS
$ yarn run -s eslint './src/**/*.{js,jsx}'

# TS
$ yarn run -s eslint './src/**/*.{js,jsx,ts,tsx}'
```

自動整形までやりたい場合は`--fix`をつけて実行します。

コマンド例

```bash
# JS
$ yarn run -s eslint './src/**/*.{js,jsx}' --fix

# TS
$ yarn run -s eslint './src/**/*.{js,jsx,ts,tsx}' --fix
```

注意点として、自動整形は必ずしも全ての問題個所を直してくれるわけではありません。
自動整形で対応できない箇所は、自分たちで直す必要があります。

VSCode エディタ上で ESLint を動作させる際は、あまりコマンドを使用しないかもしれませんが、CI でのテスト時に使用したりということがあります。
使いやすいように`package.json`の scripts にコマンドを設定しておくとよいです。

TypeScript 向けの例

```json
"scripts": {
  .
  .
  .
  "lint:eslint": "eslint './src/**/*.{js,jsx,ts,tsx}'",
  "fix:eslint": "yarn lint:eslint --fix"
}
```

### VSCode 上で動かす
https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint

インストールして再起動。
特に問題なければこれだけでチェックが動作します。

ファイル保存時に自動整形を走らせたい場合は、VSCode の設定ファイルにその旨追記します。
デフォルトのフォーマッタは無効にして、ESLint によるフォーマットをかけるイメージです。

`.vscode/settings.json`の例

```json
{
  "editor.codeActionsOnSave": {
    "source.fixAll.eslint": true
  },
  "editor.formatOnSave": false,
}
```

### ESLint の設定ファイル
設定の定義の仕方としては、以下の4パターンがあります。

- `package.json`の eslintConfig に書く
- JavaScript ファイル（例　`.eslintrc.js`）に書く
- JSON ファイル（例　`.eslintrc.json`）に書く
- YAML ファイル（例　`.eslintrc.yml`）に書く

個人的な好みとしては2番目です。
別ファイルにしておいた方が、一度作成した設定を別プロジェクトで使いまわしたりしやすいです。

設定の記述方式は公式をご参照ください。

https://eslint.org/docs/user-guide/configuring

それと、ESLint のチェックから除外したいファイルがある場合は、`.eslintignore` ファイルを作って書いておくことで対応できます。

例

```
node_modules/
```

### ESLint の設定ファイルを対話式で作成
ESLint の設定ファイルは対話式で作成することも可能です。
まずこれで雛形を作成して、そこからカスタマイズしていくとよいです。

以下は回答の流れの例です。

```
$ yarn run -s eslint --init
```
ESLintをどのように使用しますか？

```
? How would you like to use ESLint?
  To check syntax only
  To check syntax and find problems
❯ To check syntax, find problems, and enforce code style
```
どんなモジュールを使用していますか？

```
? What type of modules does your project use?
❯ JavaScript modules (import/export)
  CommonJS (require/exports)
  None of these
```
どのフレームワークを使用していますか？

```
? Which framework does your project use?
❯ React
  Vue.js
  None of these
```
TypeScriptは使用していますか？

```
? Does your project use TypeScript? No / Yes
```
コードはどこで実行されますか？

```
? Where does your code run? (Press <space> to select, <a> to toggle all, <i> to invert selection)
❯ Browser
  Node
```
プロジェクトのスタイルをどのように定義しますか？

```
? How would you like to define a style for your project?
❯ Use a popular style guide
  Answer questions about your style
  Inspect your JavaScript file(s)
```
どのスタイルガイドをフォローしますか？

```
? Which style guide do you want to follow? (Use arrow keys)
❯ Airbnb (https://github.com/airbnb/javascript)
  Standard (https://github.com/standard/standard)
  Google (https://github.com/google/eslint-config-google)
```
設定ファイルのフォーマットはどれにしますか？

```
? What format do you want your config file to be in? (Use arrow keys)
❯ JavaScript
  YAML
  JSON
```
npm でこれらのパッケージをインストールしますか？
（yarn でインストールしたい場合は No を選択して、案内されたパッケージを手動でインストールする必要があります）

```
eslint-plugin-react@^7.14.3 eslint-config-airbnb@latest eslint@^5.16.0 || ^6.1.0 eslint-plugin-import@^2.18.2 eslint-plugin-jsx-a11y@^6.2.3 eslint-plugin-react-hooks@^1.7.0
? Would you like to install them now with npm? No / Yes
  No
```

この回答例だと、以下のような設定ファイルが作られます。

TypeScript を使わないと回答した場合

```js
module.exports = {
    "env": {
        "browser": true,
        "es2021": true
    },
    "extends": [
        "plugin:react/recommended",
        "airbnb"
    ],
    "parserOptions": {
        "ecmaFeatures": {
            "jsx": true
        },
        "ecmaVersion": 12,
        "sourceType": "module"
    },
    "plugins": [
        "react"
    ],
    "rules": {
    }
};
```

TypeScript を使うと回答した場合

```js
module.exports = {
    "env": {
        "browser": true,
        "es2021": true
    },
    "extends": [
        "plugin:react/recommended",
        "airbnb"
    ],
    "parser": "@typescript-eslint/parser",
    "parserOptions": {
        "ecmaFeatures": {
            "jsx": true
        },
        "ecmaVersion": 12,
        "sourceType": "module"
    },
    "plugins": [
        "react",
        "@typescript-eslint"
    ],
    "rules": {
    }
};
```

### スタイルガイドの導入
ESLint の設定ファイルを対話式で作成時の回答にもありましたが、企業などのスタイルガイドを準拠した ESLint の共有設定が存在します。
全く一からルール設定をするのは大変なので、この共有設定を適用して、適宜カスタマイズするとよいです。

以下は Airbnb の共有設定を使用する例です。

パッケージをインストール

```
$ yarn add -D eslint-config-airbnb
```

設定の extends に追加（`.eslintrc.js`での例）

```js
extends: [
  'plugin:react/recommended',
  'airbnb', // 追記
]
```

### トラブルシューティング
#### ルート階層に設定ファイルがない場合
VSCode の ESLint 拡張は、ワークスペースに追加したフォルダの直下に設定ファイル（`.eslintrc.js`、`package.json`など）がある前提で動作します。
直下にない場合は、VSCode の設定ファイルで別途指定する必要があります。

※例（app 直下に設定ファイルがある場合）

```json
{
  "eslint.workingDirectories": [
    "./app"
  ]
}
```

#### React のバージョン検知に問題がある場合
ESLint 実行時に
eslint-plugin-react の設定で React のバージョンが指定されていません
と表示されることがあります。

```
Warning: React version not specified in eslint-plugin-react settings. See https://github.com/yannickcr/eslint-plugin-react#configuration .
```
ESLint の設定ファイルに以下を追記することで、バージョンを自動検知させることが可能です。

`eslintrc.js`の例

```js
settings: {
  react: {
    version: 'detect'
  }
}
```

しかし、Docker コンテナの中に Node.js 環境があるが、ローカルの VSCode 上で ESLint 拡張を動かしている（ローカルに Node.js 環境がない）。などといった、環境によって次の Warning が出ることがあります。

```
Warning: React version was set to "detect" in eslint-plugin-react settings, but the "react" package is not installed. Assuming latest React version for linting.
```
上記のような環境においては、さすがにバージョン検知ができないようです。

この場合は ESLint の設定に、明示的にバージョンを指定することで対応できます。

`eslintrc.js`の例

```js
settings: {
  react: {
    version: '17.0.2'
  }
}
```

#### @typescript-eslint/typescript-estree のサポートバージョン外の場合
TypeScript のバージョンによっては、@typescript-eslint/typescript-estree が正式にサポートしているバージョンでないとの Warning が表示されることがあります。

```
=============
WARNING: You are currently running a version of TypeScript which is not officially supported by @typescript-eslint/typescript-estree.
You may find that it works just fine, or you may not.
SUPPORTED TYPESCRIPT VERSIONS: >=3.3.1 <4.2.0
YOUR TYPESCRIPT VERSION: 4.3.5
Please only submit bug reports when using the officially supported version.
=============
```

あくまで Warning のため、ESLint 自体は動作します。
気になる方は、バージョンを合わせる。
そのままのバージョンを使う方は、そのバージョンではバグレポート受付に対応していないことを理解したうえで使用しましょう。

## Prettier
https://prettier.io/

幅広いファイル形式に対応したコードフォーマッタです。  
ESLint とあわせて導入しておくと、さらなるコードの品質向上に繋がります。

`react-scripts`の依存関係に Prettier に関するものはないので、追加でライブラリをインストールしていく必要があります。

### 連携セットアップ
ライブラリインストール

```
$ yarn add -D prettier eslint-config-prettier
```

ESLint の設定ファイルに追記（`.eslintrc.js`の例）

```js
extends: [
  'plugin:react/recommended',
  'airbnb',
  'prettier', // 追記
],
```
ESLint と Prettier はルールが競合することがあるので、eslint-config-prettier を適用することで競合するルールを無効化して調整します。
その性質上、追加するのは extends の最後にしてください。

ちなみに eslint-config-prettier は、競合しているルールがないかチェックする CLI ツールを持っていたりします。

コマンド例

```bash
# JS
$ yarn run -s eslint-config-prettier './src/**/*.{js,jsx}'

# TS
$ yarn run -s eslint-config-prettier './src/**/*.{js,jsx,ts,tsx}'
```
問題なければ以下のような表示になります。

```
No rules that are unnecessary or conflict with Prettier were found.
```

### CLI で動かしてみる
チェックを行いたいファイルパスを指定して実行します。

コマンド例

```bash
# JS
$ yarn run -s prettier --check './{public,src}/**/*.{js,jsx,html,gql,graphql,json}'

# TS
$ yarn run -s prettier --check './{public,src}/**/*.{js,jsx,ts,tsx,html,gql,graphql,json}'
```

自動整形までやりたい場合は`--write`をつけて実行します。

コマンド例

```bash
# JS
$ yarn run -s prettier --check './{public,src}/**/*.{js,jsx,html,gql,graphql,json}' --write

# TS
$ yarn run -s prettier --check './{public,src}/**/*.{js,jsx,ts,tsx,html,gql,graphql,json}' --write
```

このコマンドも ESLint の時と同様に、`package.json`の scripts に追加しておくとよいです。

TypeScript 向けの例

```json
"scripts": {
  .
  .
  .
  "check:prettier": "prettier --check './{public,src}/**/*.{js,jsx,ts,tsx,html,gql,graphql,json}'",
  "fix:prettier": "yarn check:prettier --write",
}
```

ESLint とセットで1コマンドで実行したい場合は、下記のようにする手もあります。

TypeScript 向けの例

```json
"scripts": {
  .
  .
  .
  "lint-check": "yarn lint:eslint && yarn check:prettier",
  "lint:eslint": "eslint './src/**/*.{js,jsx,ts,tsx}'",
  "check:prettier": "prettier --check './{public,src}/**/*.{js,jsx,ts,tsx,html,gql,graphql,json}'",
  "fix": "yarn fix:eslint && yarn fix:prettier",
  "fix:eslint": "yarn lint:eslint --fix",
  "fix:prettier": "yarn check:prettier --write",
}
```

### VSCode 上で動かしてみる
https://marketplace.visualstudio.com/items?itemName=esbenp.prettier-vscode

インストールして必要に応じて再起動。

VSCode の設定ファイルに以下を追記（`.vscode/settings.json`の例）

```jsonc
{
  "editor.defaultFormatter": "esbenp.prettier-vscode",
  "[javascript]": {
    "editor.formatOnSave": true
  },
  "[javascriptreact]": {
    "editor.formatOnSave": true
  },
  "[json]": {
    "editor.formatOnSave": true
  },
  // TypeScript の場合は以下も
  "[typescript]": {
    "editor.formatOnSave": true
  },
  "[typescriptreact]": {
    "editor.formatOnSave": true
  },
}
```
VSCode のデフォルトのフォーマッタとして Prettier を設定。
加えて、上記3つのファイル形式において、保存時に自動整形が動作するようにしています。

### Prettier の設定ファイル
設定の定義の仕方としては、以下の6パターンがあります。

- `package.json`の prettier に書く
- `.prettierrc`ファイルに書く
- JavaScript ファイル（例　`.prettierrc.js`）に書く
- JSON ファイル（例　`.prettierrc.json`）に書く
- YAML ファイル（例　`.prettierrc.yml`）に書く
- TOML ファイル（例　`.prettierrc.toml`）に書く

個人的な好みとしては2番目です。
ESLint と同様に別ファイルにしておいた方が、一度作成した設定を別プロジェクトで使いまわしたりしやすいです。

設定の記述方式は公式をご参照ください。

https://prettier.io/docs/en/configuration.html

それと、Prettier のチェックから除外したいファイルがある場合は、`.prettierignore`ファイルを作って書いておくことで対応できます。

## 補足：glob を Windows にも対応させる（※2021/11/17追記）
上記では ESLint、Prettier のコマンドで、実行パスである glob 部分をシングルクォートで囲うようにしていました。
Mac や Linux といった Unix 環境であれば、これで問題ないのですが、Windows だとうまく動作しません。

Windows にも対応させる場合は、シングルクォートでなくダブルクォートで囲う必要があります。

```json
"scripts": {
  .
  .
  .
  "lint-check": "yarn lint:eslint && yarn check:prettier",
  "lint:eslint": "eslint \"./src/**/*.{js,jsx,ts,tsx}\"",
  "check:prettier": "prettier --check \"./{public,src}/**/*.{js,jsx,ts,tsx,html,gql,graphql,json}\"",
  "fix": "yarn fix:eslint && yarn fix:prettier",
  "fix:eslint": "yarn lint:eslint --fix",
  "fix:prettier": "yarn check:prettier --write",
}
```

## 参考リンクまとめ
- [ESLint](https://eslint.org/)
- [Prettier](https://prettier.io/)
- [GitHub - oukayuka/Riakuto-StartingReact-ja3.1 ※書籍](https://github.com/oukayuka/Riakuto-StartingReact-ja3.1)