VSCode上でvueファイルに対してESLintが快適に動作する設定

2020/04/22 追記：VSCodeやESLint機能拡張のアップデートで設定が変わったので、新しく記事を書きました

https://qiita.com/fukasawah/items/03fa205353ac03b06b7e

-----

今どきのWebフロントエンドの開発環境はさっぱりわからないので、もう少し良いやり方があるかもしれません。参考までに。


tl;dr
---------

VSCodeの拡張機能「Vetur」と「ESLint」を入れます。

ワークスペース設定（`.vscode/settings.json`）に以下を書いてバージョン管理します。

vue-cliを使っていない場合、後述の環境設定も参考にしてください。


``` .vscode/settings.json
{
  "eslint.autoFixOnSave": true,
  "eslint.validate": [
    {
      "language": "vue",
      "autoFix": true
    }
  ],

  "vetur.format.defaultFormatter.js": "none",
}
```

以下を参考にしました

- https://vuejs.github.io/vetur/linting-error.html#linting

### 理由

ESLintを使う理由は、ESLintの`--fix`がフォーマッタのような役割になっているので、Lintとフォーマッタを兼ねてるような良さを感じます。
また`npm run lint:fix`で独立して行えるようにしておくと、エディタに縛られなくなり良いです。

また、VSCodeはかなりカスタマイズが出来てしまうので、それを抑制するためワークスペース設定に入れてしまい、バージョン管理しておくのが良いと思います。


蛇足
---------

### VSCodeの設定の説明


- .vueファイルでeslintが行われるようになります
（`eslint.validate`による）
- .vueファイルの保存時にファイルに対して`eslint --fix`が行われます（`eslint.autoFixOnSave`と`eslint.validate`の`autoFix`による。両方必要。）
- .vueファイル内のJavaScriptに対してVeturのフォーマッタを無効にします（`vetur.format.defaultFormatter.js`による）


### 環境設定

vue-cliは色々入れてくるため、何が必要なのかよくわからないので、Vueファイルに対してESLintが効くだけのミニマムなプロジェクトを作り、そのpackage.jsonと.eslintrc.jsを抜粋します。

``` package.json

  "scripts": {
    "lint": "eslint --ext .js,.vue src",
    "lint:fix": "eslint --fix --ext .js,.vue src"
  },
  "devDependencies": {
    "babel-core": "^6.26.3",
    "babel-eslint": "^10.0.1",
    "babel-helper-vue-jsx-merge-props": "^2.0.3",
    "babel-plugin-istanbul": "^5.1.0",
    "babel-plugin-syntax-jsx": "^6.18.0",
    "babel-plugin-transform-runtime": "^6.23.0",
    "babel-plugin-transform-vue-jsx": "^3.7.0",
    "eslint": "^5.12.1",
    "eslint-config-standard": "^12.0.0",
    "eslint-plugin-import": "^2.14.0",
    "eslint-plugin-node": "^8.0.1",
    "eslint-plugin-promise": "^4.0.1",
    "eslint-plugin-standard": "^4.0.0",
    "eslint-plugin-vue": "^5.1.0"
  },
```

`.eslintrc.js`は以下の通りです。
ただ、これはvue-cliで作成されたものを流用していて、よくわからず使っていました。。

``` .eslintrc.js
module.exports = {
  root: true,
  parserOptions: {
    parser: 'babel-eslint'
  },
  env: {
    browser: true,
  },
  extends: [
    'plugin:vue/essential', 
    'standard'
  ],
  plugins: [
    'vue'
  ],
  rules: {
    'generator-star-spacing': 'off',
    'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off'
  }
}
```

多分、Vuejs的な要点は以下の部分です

- `'plugin:vue/essential'`で[VueのスタイルガイドでEssentialなもの](https://jp.vuejs.org/v2/style-guide/index.html#%E5%84%AA%E5%85%88%E5%BA%A6-A-%E5%BF%85%E9%A0%88)を適用している
- `plugins: ['vue']`でvueファイルを解釈できるようにしている



### 自動保存時にフォーマットをかける設定をしている場合（`editor.formatOnSave: true`）

ここの要約は「`"vetur.format.defaultFormatter.js": "none"`を書き足せばよい」です。

個人設定等で`editor.formatOnSave: true`を使っている事が多いかもしれません。

しかし、これはESLintのautofixとは**別で動く**のでVeturのフォーマッタの動作と競合して変な動きになります。
遭遇した例では、セミコロンレスにしたいのに既存のフォーマッタ(prettier)でセミコロンがつき、その後にESLintによるfixがかかり、セミコロンを除去しきれずESLintで怒られる、といった感じです。

現状は、formatOnSaveの動作をいじる仕組みが提供されていないので[ESLint側も対応できない模様で、ワークアラウンドが紹介されています](https://github.com/Microsoft/vscode-eslint/issues/380#issuecomment-355981051)。

なので、これに倣って、vueファイルに対してのみ`editor.formatOnSave`を無効にしてみます。

``` .vscode/settings.json
{
  "[vue]": {
    "editor.formatOnSave": false
  },
}
```

これで確かに競合するような動きはなくなります。

しかし、これだと「vueファイル内はHTMLとCSSがあるんだから、そっちは既存のフォーマッタをかけたい」という要望をカバーできません。そもそも、手癖でフォーマットのショートカットを入力した場合は、この設定をいれても既存のフォーマッタが動作してしまいます。

この解決策としては、`editor.formatOnSave`で動くフォーマッタが`vetur.format.defaultFormatter.js`の設定によるので、この値を`none`にしておくとVueファイル内のJS部分だけ既存のフォーマッタを無効にできます。


### 気になる点

`"vetur.format.defaultFormatter.js":"prettier-eslint"`があるのでnpmでprettier-eslintをいれてあれば、これで良いのでは？と思ったが、自分の手元では`eslint --fix`がかからずダメ。これが出来ればVSCodeの拡張機能のESLintが不要になって良い感じな気がするんですが。。

``` json
{
  "editor.formatOnSave": true,
  "vetur.format.defaultFormatter.js": "prettier-eslint"
}
```

ここで力尽きる。