eslintの設定からprettierとの併用までの流れ

## はじめに

今までeslintやprettierの恩恵を受けながら具体的な設定や使い分けを理解していなかったので、改めて調べてみました。

### 対象
- 設定がよくわからないままeslint/prettierの恩恵を受けている
- eslintだけでなくprettierも導入したい

実際のコードは[こちら](https://github.com/shoichiimamura/react-webpack)で確認できます。

### 前提
```
$ yarn eslint -v
v5.6.1

$ yarn prettier -v
1.14.3
```

### eslintとprettierをなぜ使うのか？
詳しく[この記事](https://qiita.com/soarflat/items/06377f3b96964964a65d#%E4%BD%95%E6%95%85prettier%E3%82%92%E5%88%A9%E7%94%A8eslint%E3%81%A8%E4%BD%B5%E7%94%A8%E3%81%99%E3%82%8B%E3%81%AE%E3%81%8B)に載っている。
まとめると、eslintは構文チェックとコードフォーマットの機能はあるが、コードフォーマットに関してはprettierの方が優れている。そのためeslintは構文チェッカーとして、prettierはコードフォーマッターとして役割分担して使うことで両者のいいとこ取りができる。

問題は上記のようにeslintにもコードフォーマットの機能があるため、prettierとフォーマットルールが競合する可能性があることで、それぞれただ設定するのではなく、競合しないような設定にすることを注意する必要がある。

## eslintの設定
### eslintのインストール

```
$ yarn add -D eslint
```

eslintの初期設定。質問に答えていくことで設定ファイルができる。
もちろん自分で直接、設定ファイルを作ってもいい。

```
$ yarn eslint --init
? How would you like to configure ESLint? Answer questions about your style
? Which version of ECMAScript do you use? ES2018
? Are you using ES6 modules? Yes
? Where will your code run? Browser
? Do you use CommonJS? Yes
? Do you use JSX? Yes
? Do you use React? No
? What style of indentation do you use? Spaces
? What quotes do you use for strings? Single
? What line endings do you use? Unix
? Do you require semicolons? Yes
? What format do you want your config file to be in? JavaScript
'The config that you've selected requires the following dependencies:

eslint-plugin-react@latest
Successfully created .eslintrc.js file
   Done in 41.01s.
```

```js
// .eslintrc.js
module.exports = {
    "env": {
        "browser": true,
        "commonjs": true,
        "es6": true
    },
    "extends": "eslint:recommended",
    "parserOptions": {
        "ecmaFeatures": {
            "jsx": true
        },
        "ecmaVersion": 2018,
        "sourceType": "module"
    },
    "rules": {
        "indent": [
            "error",
            4
        ],
        "linebreak-style": [
            "error",
            "unix"
        ],
        "quotes": [
            "error",
            "single"
        ],
        "semi": [
            "error",
            "always"
        ]
    }
};
```

### eslintの設定プロパティ
**parserOptions**
https://eslint.org/docs/user-guide/configuring#specifying-parser-options
JavaScriptのパーサーに対するプロパティ。パーサー自体は、`parser`プロパティで指定できる。
ECMAScriptのバージョンやJSXの利用有無などを指定することができる。
ちなみにReactの使うJSXはJSXオプションを`true`にするだけでなく、[eslint-plugin-react](https://github.com/yannickcr/eslint-plugin-react)も利用する必要がある。
> Please note that supporting JSX syntax is not the same as supporting React.
> React applies specific semantics to JSX syntax that ESLint doesn’t recognize. 
> We recommend using eslint-plugin-react if you are using React and want React semantics. 

```js
module.exports = {
    ...
    "parserOptions": {
        "ecmaFeatures": {
            "jsx": true
        },
        "ecmaVersion": 2018,
        "sourceType": "module"
    },
    ...
};
```
**plugins**
https://eslint.org/docs/user-guide/configuring#configuring-plugins
外部が提供するプラグインをインストールした上で、指定することで利用可能になる。あとで設定する。

**globals**
https://eslint.org/docs/user-guide/configuring#specifying-globals
[no-undef](https://eslint.org/docs/rules/no-undef)ルールが有効化されていると、そのファイルでは使われているが、定義されていない変数は警告が出る。
そのためグローバル変数もそのエラー対象になってしまうので、globalsプロパティに指定してあげると警告が出なくなる。
また上書き可能の場合は`true`、読み取り専用の場合は`false`をつけてあげることで指定できる。

```js
module.exports = {
    ...
    "globals": {
        "require": false,
        "module": false
    }
};
```

**extends**
https://eslint.org/docs/user-guide/configuring#extending-configuration-files
extendsに追加することでrulesを拡張することができる。
具体的には、以下の振る舞いで拡張される。

- ルールの追加
- 同じルールの重複するオプションだけ上書き
- 同じルールのオプション全てを上書き

`eslint:recommended`はeslintの推奨するルールが追加される。また、各種プラグインもextend用のルールを用意しているので、それも指定することで使うことができる。
これもあとで設定を追加する。

**rules**
https://eslint.org/docs/user-guide/configuring#configuring-rules
eslintがlintとして適用するルールを追加したり、上書きしたりできる。必ずしも記述されているものが全てというわけではない。
それぞれのルールに対して、以下のオプションを指定して有効化どうかを設定できる。

- off or 0 : ルールの無効化
- warn or 1 : 警告を出すだけ
- error or 2 : エラーをだし、Exit Code 1を返す

```js
module.exports = {
    ...
    "rules": {
        "indent": [
            "error",
            4
        ],
        "linebreak-style": [
            "error",
            "unix"
        ],
        "quotes": [
            "error",
            "single"
        ],
        "semi": [
            "error",
            "always"
        ]
    }
};
```

### Reactとflowに対する設定

**babel-eslintパーサーを使う**
eslintの標準パーサーだと、babelがトランスパイルするes6以上のJavaScriptやflowによる型宣言に対応できないことがある。
それに対応したパーサーを使う。
https://eslint.org/docs/user-guide/configuring#specifying-parser

```
$ yarn add -D babel-eslint
```

```js
// .eslintrc.js
module.exports = {
    "parser": "babel-eslint",
    ...
};
```

**React, flowのrulesを追加する**

reactとflowを使っている場合。
以下のプラグインがないとparseに失敗する。

```
$ yarn add -D  eslint-plugin-react eslint-plugin-flowtype
```

また上記プラグインはReact, flow用のruleも利用できるようになっているので、extendsで追加する。

```js
// .eslintrc.js
module.exports = {
    "parser": "babel-eslint",
    "env": {
        "browser": true,
        "commonjs": true,
        "es6": true
    },
    "extends": [
      "eslint:recommended",
      "plugin:react/recommended", // 追加
      "plugin:flowtype/recommended" // 追加
    ],
    ...
    "plugins": [
        "react", // 追加
        "flowtype" // 追加
    ],
    ...
};
```

### webpackでビルド時にeslintをかける
webpackでeslintのチェックをできるようにする。そうすることで毎回eslintのチェックコマンドを実行しなくてもビルドする際に、チェックされるので手間が省ける。

```
$ yarn add -D eslint-loader
```
```js
const path = require('path');

module.exports = {
  ...
  module: {
    rules: [
      {
        test: /\.js$/,
        use: [
          {
            loader: 'babel-loader',
            options: {
              "presets": [
                '@babel/preset-env',
                '@babel/preset-react',
                '@babel/preset-flow'
              ]
            }
          },
          {
            loader: 'eslint-loader' // 追加
          }
        ],
        exclude: [/node_module/]
      }
    ]
  },
  ...
};
```

## prettierの設定

### prettierの導入
```
$ yarn add -D prettier
```

### eslintとのルールの競合の解消
**競合するルールを調査し、競合するeslintのルールを無効化する**
[eslint-config-prettier](https://github.com/prettier/eslint-config-prettier)で競合するeslintのルールを無効化することができる。

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

[README](https://github.com/prettier/eslint-config-prettier#installation)にあるように、他のextendsされたルールを上書いて無効化するために、extendsの配列の最後に追加する。
exntendsは配列の左から順番に適用されるので、最後にprettier関連のルールを追加しないと他のルールを上書きできないため。

```js
module.exports = {
    ...
    "extends": [
      "eslint:recommended",
      "plugin:react/recommended",
      "plugin:flowtype/recommended",
      "prettier", // 追加（prettier関連のルールは最後に追加する, eslintの競合ルールを無効化）
      "prettier/flowtype", // 追加（eslint-plugin-flowtypeを無効化）
      "prettier/react" // 追加（eslint-plugin-reactを無効化）
    ],
    ...
};
```

また、`eslint-config-prettier-check`にeslintrcの内容を渡すことで、競合しているルールをチェックできる。

```
$ ./node_modules/.bin/eslint --print-config . | ./node_modules/.bin/eslint-config-prettier-check
The following rules are unnecessary or might conflict with Prettier:

- flowtype/boolean-style
- flowtype/generic-spacing
- flowtype/space-after-type-colon
- flowtype/space-before-generic-bracket
- flowtype/space-before-type-colon
- flowtype/union-intersection-spacing
- indent
- no-extra-semi
- no-mixed-spaces-and-tabs
- semi

The following rules are enabled but cannot be automatically checked. See:
https://github.com/prettier/eslint-config-prettier#special-rules

- no-unexpected-multiline
- quotes
```

**prettierのルールをeslintrcで指定できるようにする**
次に、[eslint-plugin-prettier](https://github.com/prettier/eslint-plugin-prettier)で、prettierのルールをeslintのルールとして追加することが可能になる。

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

同様にextendsにはprettier関連のルールが最後になるようにする。
> https://github.com/prettier/eslint-plugin-prettier#installation
> Then you need to add plugin:prettier/recommended as the last extension in your .eslintrc.json:

```js
module.exports = {
    ...
    "extends": [
      "eslint:recommended",
      "plugin:react/recommended",
      "plugin:flowtype/recommended",
      "plugin:prettier/recommended", // 追加
      "prettier",
      "prettier/flowtype",
      "prettier/react"
    ],
    ...
    "plugins": [
        "react",
        "flowtype",
        "prettier" // 追加
    ],
    "rules": {
        "prettier/prettier", // 追加
        ...
    }
};
```

改めて競合するルールを調べてみる。

```
$ ./node_modules/.bin/eslint --print-config . | ./node_modules/.bin/eslint-config-prettier-check
The following rules are unnecessary or might conflict with Prettier:

- indent
- semi

The following rules are enabled but cannot be automatically checked. See:
https://github.com/prettier/eslint-config-prettier#special-rules

- quotes
```

だいぶ減ったが、それでも残った不要なルールの削除と自動でチェックされないルールの設定をする。

```js
module.exports = {
    ...
    "rules": {
        "prettier/prettier": ['error', { "singleQuote": true }],
        "linebreak-style": [
            "error",
            "unix"
        ]
    }
};
```

これで競合も解消された！

```
$ ./node_modules/.bin/eslint --print-config . | ./node_modules/.bin/eslint-config-prettier-check
No rules that are unnecessary or conflict with Prettier were found.
```

## 参考
- https://qiita.com/soarflat/items/06377f3b96964964a65d
- https://tech-1natsu.hatenablog.com/entry/2018/01/07/154941
- https://www.kken.io/posts/prettier-eslint/