Prettierの導入とESLintとの併用について

# 概要
Prettierの導入と、ESLintと併用する際の注意について調査した結果のメモ。
特に、「eslint-config-prettier」が必要になる経緯についてまとめた。

# Prettier導入

Prettierをインストールし、テスト実行する。

## Prettierのインストール

```bash
$ yarn add --dev prettier
```

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

## Prettierのテスト実行

整形するコードの例として、「create-next-app」利用時に生成される「pages/api/hello.js」を利用する。
このコードにはセミコロンがついていないため、Prettierの実行によりセミコロンが挿入されることを期待する。

```jsx
// pages/api/hello.js

// Next.js API route support: https://nextjs.org/docs/api-routes/introduction

export default (req, res) => {
  res.status(200).json({ name: 'John Doe' })
}
```

以下の通り、Prettierを実行。

```bash
$ yarn prettier pages/api/hello.js
```

実行結果が以下のように出力される。

```bash
yarn run v1.22.10

// Next.js API route support: https://nextjs.org/docs/api-routes/introduction

export default (req, res) => {
  res.status(200).json({ name: "John Doe" });
};
✨  Done in 0.69s.
```

結果を見るとセミコロンが挿入されている。
しかし、この状態ではファイルが書き換わっていない。

ファイルの修正を実行するには、「--write」オプションを付与してprettierを実行する。

```bash
$ yarn prettier --write pages/api/hello.js
```

git diffで確認すると以下のように差分が表示され、コードが自動で整形された。

```diff
 // Next.js API route support: https://nextjs.org/docs/api-routes/introduction

 export default (req, res) => {
-  res.status(200).json({ name: 'John Doe' })
-}
+  res.status(200).json({ name: "John Doe" });
+};
```

# ESLintとの併用

ESLintとPrettierを併用する際の注意点や併用によるメリットを調査した結果をまとめる。

https://eslint.org/

ESLintの導入については、以下の記事にまとめた。

https://qiita.com/yudwig/items/8a16f1ffaf985498239a

## 併用について

そもそも、ESLint単体でもコマンド「eslint --fix」によりコードの整形自体は可能。
しかし、現時点ではコード整形についてはPrettier等のツールのほうが強力なため、併用すべきといわれている。(当然、Prettierもこれを主張している。)

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

https://qiita.com/soarflat/items/06377f3b96964964a65d

## ESLintとPrettierのコード整形機能比較

併用のテストをする前に、試しにESLint, Prettierのコード整形をそれぞれテスト実行し、結果を比較する。

### 事前準備

ファイルは先程と同様のファイル「pages/api/hello.js」を以下の通り書き換えて利用する。

```diff
 // Next.js API route support: https://nextjs.org/docs/api-routes/introduction

 export default (req, res) => {
-  res.status(200).json({ name: 'John Doe' })
+  res.status(200).json({ name: 'John Doe', type: 'test type', id: 'test id', args: ['a', 'b', 'c', 'd', 'e', 'f'], status: 'available' });
 };
```

尚、ESLintの設定ファイル「.eslintrc.json」は以下とする。

```json
{
    "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によるコード整形により上記のコードがどのように修正されるか確認する。

ESLintを実行する。

```bash
$ yarn eslint pages/api/hello.js
```

以下の通り、「{, }」の前後に改行が無いためエラーが表示される。

```bash
  4:24   error  Expected a line break after this opening brace   object-curly-newline
  4:136  error  Expected a line break before this closing brace  object-curly-newline

✖ 2 problems (2 errors, 0 warnings)
  2 errors and 0 warnings potentially fixable with the `--fix` option.

error Command failed with exit code 1.
info Visit https://yarnpkg.com/en/docs/cli/run for documentation about this command.
```

ESLintのコード整形コマンドを実行する。

```bash
$ yarn eslint --fix pages/api/hello.js
```

以下のように整形される。

```diff
 // Next.js API route support: https://nextjs.org/docs/api-routes/introduction

 export default (req, res) => {
-  res.status(200).json({ name: 'John Doe' })
-}
+  res.status(200).json({
+    name: 'John Doe', type: 'test type', id: 'test id', args: ['a', 'b', 'c', 'd', 'e', 'f'], status: 'available',
+  });
+};
```

ESLintにより改行が追加され、コードの整形は実行されたが、オブジェクトの要素が横に並んだままとなっている。

### Prettierによるコード整形テスト

次に、先程と同様のコードに対してPrettierを実行する。

```bash
$ yarn prettier pages/api/hello.js
```

「--write」を付けていないため上書きはしていないが、以下のように整形後のコードが出力される。

```js
export default (req, res) => {
  res
    .status(200)
    .json({
      name: "John Doe",
      type: "test type",
      id: "test id",
      args: ["a", "b", "c", "d", "e", "f"],
      status: "available",
    });
};
```

先程の「ESLint」によるコード整形の結果と比較すると、縦配置のスッキリとした見た目に整形されている。

### 結果と併用時の問題点について

ここまででPrettierによるコード整形のほうが強力ということが確認できた。
ESLintとPrettierを併用することにより、ESLintによるエラーの指摘とPrettierによるコード整形を両方適用することが可能になる。
しかし、併用するには以下の問題がある。

Prettier実行のタイミングについて考えると、例えば、コードの保存時、コミット時、CI実行時などいくつか選択肢が存在する。
これらのタイミングでPrettierが実行されるまでの間、ESLintによってコードがエラーと判断されてしまうと、エディタ上で赤線が表示されたままになってしまい邪魔になる。

そこで、Prettier実行により整形される部分についてはESLintでエラーにならないように設定したい。
これを実現するのが「eslint-config-prettier」であり、利用することによりPrettierで整形されるような箇所についてはESLintがエラーを吐かないようESLintのルールを無効化することが可能になる。

## 「eslint-config-prettier」を利用したESLintとPrettierの併用について

「eslint-config-prettier」を導入し、先程挙げた問題点を解決する。

https://dev.classmethod.jp/articles/eslint-and-prettier/

### eslint-config-prettierのインストール

```bash
$ yarn add --dev eslint-config-prettier
```

### eslint-config-prettierの有効化

公式を参考に、ESLintの設定を変更する。

https://github.com/prettier/eslint-config-prettier

ESLintの設定ファイル「.eslintrc.json」に以下の通りextendsを追加する。
注意点として、他のルールを上書きするためextendsの最後に追加するよう公式で案内されている。

例:

```diff
     "extends": [
         "plugin:react/recommended",
-        "airbnb"
+        "airbnb",
+        "prettier"
     ],
     "parser": "@typescript-eslint/parser",
     "parserOptions": {
```

これにより、JSのコアルールや以下のプラグインで提供される一部のルールが無効化され、ESLintでエラーが出力されなくなる。

* @typescript-eslint/eslint-plugin
* eslint-plugin-babel
* eslint-plugin-flowtype
* eslint-plugin-react
* eslint-plugin-standard
* eslint-plugin-unicorn
* eslint-plugin-vue

### ESLintとPrettierの併用テスト

ESLintの設定を変更したので、併用のテストを実行する。
テストでは、ESLintではエラーにならず、Prettierによりコードが整形されるという状態が実現されたか確認する。

先程と同様、以下のファイル「pages/api/hello.js」を用意する。

```js
// Next.js API route support: https://nextjs.org/docs/api-routes/introduction

export default (req, res) => {
  res.status(200).json({name: 'John Doe', type: 'test type', id: 'test id', args: ['a', 'b', 'c', 'd', 'e', 'f'], status: 'available',});
};
```

ESLintによりエラーを確認する。

```bash
$ yarn eslint pages/api/hello.js
yarn run v1.22.10
✨  Done in 1.26s.
```

前回はESLintのエラーが表示されていたが、今回はエラーと判定されていないことが確認された。

次に、Prettierを実行する。

```bash
$ yarn prettier pages/api/hello.js
```

結果は以下のようなコードがコンソールに出力された。

```js
// Next.js API route support: https://nextjs.org/docs/api-routes/introduction

export default (req, res) => {
  res
    .status(200)
    .json({
      name: "John Doe",
      type: "test type",
      id: "test id",
      args: ["a", "b", "c", "d", "e", "f"],
      status: "available",
    });
};
```

Prettierが実行され、「eslint-config-prettier」導入前と同様の整形結果コードが出力されている。

このように、「eslint-config-prettier」を利用することで、ESLintによる指摘を回避しつつ、Prettierによるコード整形を行うことが可能になった。

### eslint-config-prettier導入についての補足

ESLintとPrettier併用について調べて出てくる各種記事では、現在非推奨のプラグイン「eslint-plugin-prettier」や現在利用されていないESLintの設定値について書かれていることが多かったので、ここで触れておく。

先程紹介した通り、eslint-config-prettier導入時のESLintの設定については、公式を参照するのが確実。

https://github.com/prettier/eslint-config-prettier

https://www.deepl.com/ja/translator

例えば、色々と記事を読んでいると、ESLintの設定においてextends配列に「prettier/react」を追加しているものが多く、少しハマった。
これが必要なのか色々と調べたところ、公式の以下の文章にたどり着いた。

> ℹ️ Note: You might find guides on the Internet saying you should also extend stuff like "prettier/react". Since version 8.0.0 of eslint-config-prettier, all you need to extend is "prettier"! That includes all plugins.

よって、eslint-config-prettierのバージョン8.0.0からextends配列に「prettier/react」を追加する必要は無いとのこと。

また、現在非推奨のプラグイン「exlint-plugin-prettier」について。

ESLintとPrettierを併用する際、少し前までESLintのプラグイン「exlint-plugin-prettier」によってPrettierを実行する構成が多かったらしいが、現在では公式がこれを推奨しなくなっている。
公式では代わりに
```sh
$ eslint --fix && prettier --write
```
のように、この記事で紹介してきた通り「eslint-config-prettier」を利用しつつESLintのプラグイン経由ではなく個別のコマンドで実行するようにとアナウンスしている。

理由については、プラグインでは実行が遅かったり不整合が発生する場合があるから、とのこと。今ではエディタ側もPrettierにうまく対応しつつあるので、プラグインは必要無くなったという経緯があるらしい。

https://github.com/prettier/prettier/blob/main/docs/integrating-with-linters.md