ESLint - Prettier連携のやり方と仕組み

メモ代わりに関係ない情報も色々含めた。

# これまでのあらすじ
## ESLint is 何
JSのLinter。めっちゃカスタマイズできる。
`eslint --fix`コマンドでフォーマットもできる。

## Prettier is 何
JSのフォーマッター。`eslint --fix`よりも強いらしい。
具体例で言うと、「1行80字超えのコードを適切に改行してくれる」らしい。
やたらこの例を推してくる。他の例を知りたい。

## Prettier only ダメ is なぜ
### フォーマットしかできない
Linterのルールには2種類ある。

1. フォーマットに関するルール
2. コードの品質に関するルール

「後者はLinterの仕事。Prettier、ソウイウノ、ヤラナイ。」的なことが[公式](https://prettier.io/docs/en/comparison.html)で案内されている。

### カスタマイズ性に乏しい
ESLintほど充実していない。
よりカスタマイズできる版を提供しようっていう[フォークプロジェクト](https://github.com/arijs/prettier-miscellaneous)があるぐらい。
応援したい。でも開発止まってるっぽい。

## Why don't you ESLintとPrettierを併用？
Yes, I am.

## 併用 is どうやる
### prettierをかましてから`eslint --fix`をかます。
> Code ➡️ prettier ➡️ eslint --fix ➡️ Formatted Code ✨ 
[prettier/prettier-eslint](https://github.com/prettier/prettier-eslint)

公式リポジトリにそういうプロジェクトがあるぐらいなので、アリっぽい。
個人的にはそれで良いのか感がすごい。
Prettier流のフォーマットがどれだけ上書きされてるのか知りたい。
やはりPrettierの優位な点をリストアップした資料とかが欲しい。
「80字超え対処」しか強みがないならこれで良いと思う。

### ESlintをカスタマイズしてPrettierと連携させる。
[prettier/eslint-config-prettier](https://github.com/prettier/eslint-config-prettier)
[prettier/eslint-plugin-prettier](https://github.com/prettier/eslint-plugin-prettier)
この辺を組み込む。すると、`eslint --fix`をした時にprettier準拠でフォーマットがされることになる。
以下、この辺の仕組みの解説。

**※ 2020/12/27 追記**
（コメントにて @masato_makino さんから指摘頂いた内容。）
[prettierリポジトリにあるドキュメント](https://github.com/prettier/prettier/blob/master/docs/integrating-with-linters.md)が2020年6月頃に更新され、「eslint-plugin-prettierは非推奨。まあ特定の状況では便利かもね。」みたいなことが書かれるようになった。

以下、このドキュメントから読み取ったことを雑に羅列。

- リンターを使わなくても`prettier --check .`があるよ。
- 昔はprettier用のエディタプラグインがなくて、リンター向けのプラグインを使うしかなかったから、しょうがなくリンター経由でprettierしてたかもしれないけど、今時はどのエディタもprettier用のプラグインがあるからその必要はないよ。
- リンター経由だとエディタ上で赤線出まくったりしてだるいぞ。
- 直接prettierした方が速いぞ。
- 間接的なレイヤーを１個挟むことによって、何か余計なバグが生まれるリスクがあるぞ。
- prettierのフォーマットに気に入らない部分がある？じゃあprettier-eslintでprettier後のコードを`eslint --fix`して帳尻合わせてくれ。まあ重いけどね。

ということなので、eslint-plugin-prettierを使うより直接prettierコマンドを叩く方が推奨らしい。なお、eslint-config-prettierの使用は問題ない。
以下、その辺を考慮しながら必要な情報だけ取捨選択してもらいたい。
**（追記終）**

# 本題
## ESLintの設定について
### 概要
ソース内コメントや設定ファイルによって色々カスタマイズできる。
設定ファイルは、`package.json`内に`eslintConfig`フィールドを設けて記述するパターンと、`.eslintrc.{js|json|yaml}`ファイルを作るパターンがある。

### 設定の優先順位
高い順に

1. コメント
2. CLIコマンドのオプション
3. 設定ファイル
    * `.eslintrc.*`を置いたフォルダの子フォルダに更に`.eslintrc.*`を置いたりできる。
        * チェックするjsファイルについて、近い階層に置いてある`.eslintrc.*`の設定ほど優先して採用される。
        * `.eslintrc.*`内で`root: true`を指定すると、それより親フォルダの`.eslintc.*`は無視される。
    * `.eslintrc.*`と`eslintConfig`を記載した`package.json`の両方が同じ階層にある場合、`eslintConfig`は無視される。

上記のどれか１つしか採用されないわけではなく全部採用される。競合する設定について優先順位が考慮されるイメージ。
オブジェクト指向的にいえば、設定をどんどんオーバーライドしていってる感じ。

また、ホームディレクトリに`.eslintrc.*`があるとフォールバックとして働く。
上記の3つが全部無かった時のみここの設定が反映される。

### 設定できることの一例：ルールのON/OFF

```.eslintrc.js
module.exports = {
    "rules": {
        // ==を使おうが===を使おうが気にしない。
        "eqeqeq": "off",
        // if文などで{}を略したらwarning。
        "curly": "warn",
        // ダブルクォートを使う。シングルクォートを使ってたらLinterエラー。
        "quotes": ["error", "double"],
        // セミコロンは必須。無かったらLinterエラー。
        "semi": ["error", "always"]
    }
}
```

他には、環境（ECMAいくつか、nodeかbrowserか、jQuery使ってるか、etc...）、グローバル変数の設定など。

### 設定の共有
`eslint-config-*`という名前でnpmモジュールとして設定を共有できる。

例えば、`eslint-config-standard`は以下の記述で取り込むことができる。

```.eslintrc.json
{
    "extends": "standard"
}
```

ただし、記述するだけでなくnpmで自分で落としてこないといけない。
ESLintを入れた場所がプロジェクトローカルならプロジェクトローカルに、グローバルならグローバルに。同じレベルに落とさないと使えない。

複数の設定を取り込むこともできる。

```.eslintrc.json
{
    "extends": [
        "eslint:recommended",
        "standard"
    ]
}
```

後のやつが前の設定をどんどんオーバーライドしていく。
取り込んだ後、更に自分で`rules`とか設定しだすのもオーバーライドになる。

### 設定のプラグイン
`eslint-plugin-*`という名前でnpmモジュールとしてプラグインを作成できる。
ルールを増やしたりできる。共有設定を含めることもできる。

例えば、`eslint-plugin-react`は以下の記述で取り込むことができる。

```.eslintrc.json
{
    "plugins": ["react"]
}
```

プラグイン内のルールは以下のように指定する。

```.eslintrc.json
{
    "plugins": ["react"],
    "rules": {
        "react/no-set-state": "error"
    }
}
```

プラグイン内の共有設定は以下のように取り込む。

```.eslintrc.json
{
    "plugins": ["react"],
    "extends": ["plugin:react/recommended"]
}
```

## Prettier連携
### 方針
`eslint --fix`の仕様についてはちゃんと調べていないが、おそらく「ルール」の中には「どう直すか」も含まれている。
ということは、ESLintの組み込みルールを全部無効にし、Prettierのプラグインルールに差し替えまくればいいことになる。

上の方で、ルールには2種類あると書いた。

1. フォーマットに関するルール
2. コードの品質に関するルール

Prettierの担当範囲は前者である。
なので、前者に関するESLintの組み込みルールを全部オフにする。

### `eslint-config-prettier`の使い方
共有設定`prettier`を取り込むことで、Prettierと競合しそうなESLintの組み込みルールを全部OFFにしてくれる。
`react`プラグインの分をOFFにする共有設定`prettier/react`等、細かい追加分もある。

### `eslint-plugin-prettier`の使い方
これを取り込むことで、Prettierのルールが代わりに追加される。されるのだが...。
なんと、

```.eslintrc.json
{
    "plugins": ["prettier"],
    "rules": {
       "prettier/prettier": "error"
    }
}
```

**`prettier`というルール1つしか追加されない。
Prettierが扱う全ルールが、この1つにまとめられている。**

つまり、ルール毎にON/OFFを細かく設定できない。
全部offか、全部warningか、全部errorかしかない。

しかし、全くカスタマイズが効かないというわけではない。
例えば、ESLint組み込みの`indent`ルールは、ルールのON/OFFだけでなくインデント幅のルールも設定できる。

```.eslintrc.js
module.exports = {
    "rules": {
        // インデント幅が2でなければLinterエラー。
        "indent": ["error", 2]
    }
}
```

同じノリで、`prettier`ルールも**オブジェクトを使って色々ルールを設定できる**。

```.eslintrc.js
module.exports = {
    "plugins": ["prettier"],
    "rules": {
        // シングルクォートを使う。セミコロンは無し。
        "prettier/prettier": ["error", {"singleQuote": true, "semi": false}]
    }
}
```

無理やり感がすごい。
ちなみに`.prettierrc`ってファイルで設定する方法もあるらしい。

なお、このプラグインには共有設定`recommended`も含まれている。

```.eslintrc.json
{
    "extends": ["plugin:prettier/recommended"]
}
```

上記により、下記と同じ効果が得られる。

```.eslintrc.json
{
    "extends": ["prettier"],
    "plugins": ["prettier"],
    "rules": {
        "prettier/prettier": "error"
    }
}
```

逆にいうとそれだけ。

### 手順まとめ

```shell.sh
npm install --save-dev eslint
npm install --save-dev eslint-config-prettier
npm install --save-dev eslint-plugin-prettier
npm install --save-dev prettier
```

Prettier本体は雰囲気で入れてるけど、本当に必要なのかは未検証。

```.eslintrc.js
module.exports = {
    "extends": ["prettier"],
    "plugins": ["prettier"],
    "rules": {
        "prettier/prettier": [
            "error",
            {
                // 各種設定
            }
        ]
    }
}
```

他に取り込みたい共有設定があれば、`prettier`より前にextendする。（`prettier`でルールOFFにする方が優先順位が高いので。）

# 愚痴
[JavaScript Standard Style](https://standardjs.com/)に合わせたいのだが、Prettierに`spaceBeforeFunctionParen`オプション（関数名と引数名の間にスペースを空ける。ex. `function name (arg) { ... }`）が無いため合わせられない。

* フォークプロジェクト頑張ってほしい。
* というか普通にPrettierがカスタマイズ性上げてほしい。
* というかStandard Styleはこの仕様だけマイナーなのでやめてほしい。
* というかセミコロン無しのStyleでもっと良いの出てきてほしい。