vue-element-adminにPrettierを導入する

vue-element-adminにPrettierを導入して、ESLintと競合しないようにするメモです。

## 環境
- MacOS Big Sur
- Node.js 14.15.1
- npm 6.14.8

## やりたいこと
1. 保存時にPrettierで整形できるようにする
2. PrettierとESLintの整形が競合しないようにする

## 方針
現在推奨されているのは、eslint-config-prettierでESLintのフォーマットに関するルールをオフにして、ESLintとPrettierを個別に実行する方法のようなので、その方針でやります。

参考: [Prettier と ESLint の組み合わせの公式推奨が変わり plugin が不要になった](https://blog.ojisan.io/prettier-eslint-cli)

## 手順
1. Prettierの導入
2. Prettierの設定をESLintに寄せる
3. Prettierとの競合をチェックして、ESLintのフォーマットルールをオフにする

### 1. Prettierの導入
```bash:terminal
npm install --save-dev prettier
```

インストールしたら、VSCodeのPrettierの拡張機能をインストールして、setting.jsonに以下を追記します。

```json:setting.json
{
    "[javascript]": {
        "editor.defaultFormatter": "esbenp.prettier-vscode",
        "editor.tabSize": 2
    },
    "[vue]": {
        "editor.defaultFormatter": "esbenp.prettier-vscode",
        "editor.tabSize": 2
    },
    "[css]":{
        "editor.defaultFormatter": "esbenp.prettier-vscode"
    },
}
```
これでファイル保存時にPrettierでフォーマット出来るようになると思います。

### 2. Prettierの設定をvue-element-adminのESLintの設定に寄せる
既に存在するファイルに合わせるために、ひとまずPrettierの設定をvue-element-adminのESLintの設定に寄せることにします。

まず、プロジェクトルートに.prettierrc.jsを作成して、以下を追記します。

```js:.prettierrc.js
module.exports = {
  singleQuote: true,
  semi: false,
  arrowParens: 'always',
  trailingComma: 'none',
  printWidth: 100
}
```
上の4つの設定はvue-element-adminのESLintに寄せるための設定です。printWidthに関しては、デフォルトでは80なのですが、80だと（特にVueのテンプレートが）縦に長くなって読みづらかったので100にしました。

この設定を

- src/store/modules/user.js
- src/views/login/index.vue

の２つで試してみると、JSファイルに関しては問題がなかったのですが、VueファイルはESLintの整形結果とPrettierの整形結果が異なっているところがありました。

例えば、

```vue

<!-- prettier後 -->
<el-button
	:loading="loading"
	type="primary"
	style="width: 100%; margin-bottom: 30px"
	@click.native.prevent="handleLogin"
	>Login</el-button
>
```

```vue
<!-- ESLint後 -->
<el-button
	:loading="loading"
	type="primary"
	style="width: 100%; margin-bottom: 30px"
	@click.native.prevent="handleLogin"
>Login</el-button>
```
のようなところがありました。これはprettierが長い行を折り返したときに変になっていて、以下のようにすると解決します。

```vue

<!-- こうするとPrettierとESLintの整形結果が一致する -->
<el-button
  :loading="loading"
  type="primary"
  style="width: 100%; margin-bottom: 30px"
  @click.native.prevent="handleLogin"
>
  Login
</el-button>
```
他の競合箇所は次の章で直すことにします。

### 3. Prettierとの競合をチェックして、ESLintのフォーマットルールをオフにする

ESLintにはフォーマットをさせない方針なので、ESLintからフォーマットに関するルールを削除します。eslint-config-prettierをextendすればフォーマットのルールをオフに出来るのですが、eslintrcのextendsよりrulesが優先されるようなので、結局rulesを消す必要があります。

まずはeslint-config-prettierを導入します。[eslint-config-prettier](https://github.com/prettier/eslint-config-prettier)

```bash:terminal
npm install --save-dev eslint-config-prettier
```

```javascript:.eslintrc.js
module.exports = {
  extends: ['plugin:vue/recommended', 'eslint:recommended', 'prettier'], // prettierを追記
}
```

次にPrettierとの競合をチェックして、フォーマットに関するルールを消します。以下のコマンドでPrettierとの競合をチェックすることが出来ます(eslint >= 7.0が必要なようなのでアップデートします)。

```bash:terminal
npx eslint-config-prettier src/views/login/index.vue
```

```bash:実行結果
❯ npx eslint-config-prettier src/views/login/index.vue 
npx: installed 1 in 1.424s
The following rules are unnecessary or might conflict with Prettier:

- array-bracket-spacing
- arrow-spacing
- block-spacing
- brace-style
- comma-dangle
- comma-spacing
- comma-style
- dot-location
- eol-last
- generator-star-spacing
- indent
- jsx-quotes
- key-spacing
- keyword-spacing
- new-parens
- no-extra-parens
- no-extra-semi
- no-floating-decimal
- no-mixed-spaces-and-tabs
- no-multi-spaces
- no-multiple-empty-lines
- no-spaced-func
- no-trailing-spaces
- no-whitespace-before-property
- object-curly-spacing
- operator-linebreak
- padded-blocks
- semi
- semi-spacing
- space-before-blocks
- space-before-function-paren
- space-in-parens
- space-infix-ops
- space-unary-ops
- template-curly-spacing
- vue/html-closing-bracket-newline
- vue/html-closing-bracket-spacing
- vue/html-end-tags
- vue/html-indent
- vue/html-quotes
- vue/max-attributes-per-line
- vue/mustache-interpolation-spacing
- vue/no-multi-spaces
- vue/no-spaces-around-equal-signs-in-attribute
- wrap-iife
- yield-star-spacing

The following rules are enabled with config that might conflict with Prettier. See:
https://github.com/prettier/eslint-config-prettier#special-rules

- curly
- vue/html-self-closing

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

- no-unexpected-multiline
- quotes
```

1番上のグループは不要そうなので、該当するルールがeslintrcにあれば消します。vueに関するルールは残しておきました（max-attributes-per-lineを消すと縦に長くなってしまったため）。

該当するルールを消してもう一度チェックすると以下のようになりました。

```bash
❯ npx eslint-config-prettier src/views/login/index.vue
The following rules are unnecessary or might conflict with Prettier:

- vue/html-closing-bracket-newline
- vue/html-closing-bracket-spacing
- vue/html-end-tags
- vue/html-indent
- vue/html-quotes
- vue/max-attributes-per-line
- vue/mustache-interpolation-spacing
- vue/no-multi-spaces
- vue/no-spaces-around-equal-signs-in-attribute

The following rules are enabled with config that might conflict with Prettier. See:
https://github.com/prettier/eslint-config-prettier#special-rules

- curly
- vue/html-self-closing

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

- no-unexpected-multiline
- quotes
```

とりあえずこれで良しとします。

あとはbrタグが競合していたので、rulesに以下を追記して直しました。[参考](https://qiita.com/yoshinbo/items/3436face1a1b02f2542f)

```javascript:.eslintrc.js
module.exports = {
  rules: {
    "vue/html-self-closing": [2, {
      "html": {
        "void": 'always',
      }
    }],
}
```

これでprettierとESLintの競合が（ほぼ）起きなくなりました。あとはコミット前にprettierを実行するようにpackage.jsonに追記します。

```javascript:package.json
{
  "lint-staged": {
    "src/**/*.{js,vue}": [
      "prettier --write", // 追記
      "eslint --fix",
      "git add"
    ]
  }
}
```
以上です。ESLintからフォーマットに関するルールを完全に消そうと思うと、extendsしているルールを見る必要がありそうですが、とりあえずこれで良しとします。

[Vue.jsスタイルガイドとeslint-plugin-vue検証ルールのマッピング](https://qiita.com/ota-meshi/items/85bf17494c8ea0be05d7)
[ESLintのRulesのドキュメント](https://eslint.org/docs/rules/)

## 感想
Prettierはガチガチに整形してくれるイメージがありましたが、整形結果が一意では無いことを知りました。Prettierを使うとVueのスタイルガイドに添えないところが少し出てくるのかなぁと思いました。

## 参考

[Prettier と ESLint の組み合わせの公式推奨が変わり plugin が不要になった](https://blog.ojisan.io/prettier-eslint-cli)