ESLint(あるいはTSLint)とPrettierを併用する

[追記:] TypeScriptでESLintを使う方法も書きました

tech-1natsu.hatenablog.com

TSLintではなくESLintを使いたい方はこちらもあとで読んでみてください。

なお

  • JS with ESLint
  • TS with TSLint

この構成をお求めの方は当記事(下記)です。


Prettier導入するにあたって色々絡み合うものがあるので調べたりしたことをまとめておく。

基本的にリントとフォーマッタのどちらか片方しか使わないという場面は、リリースを意識したプロジェクトではあまりないと思うので、実質この組み合わせは必須のようになってくる気がしている。

それで、Prettierとはなんぞや…JSのコードフォーマッタで、、というPrettierの概要についてはググればめっちゃ出てくるので省略。

この記事は導入したいけどなにをどうすればいいんやという人向け。

とりあえずESLint+Prettierについて書いて理解してもらったあとでTypeScripter向けにTSLint+Prettierのことも書いてあります。ちなみに導入についての話なのでESLintやPrettierのルールの解説はない。

prettier.io

そもそもESLintとPrettierどう違うんやみたいなのは公式にこういうページがある。

CAVEAT

この記事を参考にすべき対象者

この記事はプロジェクトフォルダ内でESLintとPrettierを併用しつつ、コミット時にも再確認してgitにESLintルールに従っていないコードやPrettierによる未整形のコードが紛れ込まないようにしたい人向けです。

つまり

  • プロジェクトフォルダがある
    • はい
  • プロジェクトに存在するコードの秩序を保ちたい
    • はい
  • ESLintでコードのルールチェックをしたい
    • はい
  • Prettierも使って整形済みのキレイなコードを保ちたい
    • はい
  • Gitに秩序が保たれていないコードが紛れ込むのを阻止したい
    • はい

こういう欲求がある人向けです。

一番まとまっている導入解説

ESLint導入環境にprettierを追加して運用する - Kenta Katoh's Blog

この方のブログが一番簡潔でわかりやすいのでまず読むとわかってくる。

なので読むとわかる。…読んで下さいで終わると雑すぎるので、以下から導入解説。

導入しましょう

導入するにあたってなんか色々プラグインがあってどれを入れればいいのか問題が発生すると思う。

上記ブログにも書いてあるけれど、Prettierの処理の取り扱いには2種類ある。

  1. .eslintrcにPrettierのルールを定義 → ESLintの処理時にPrettierのルールもやってもらう

  2. Prettierでフォーマット → フォーマット済みコードをeslint --fixに繋いで渡す → ESLintにはESLintの処理してもらう

ESlint+Prettierの導入は上記2つのどっちかのやりかたでできる。

【1の特徴】

  • 既存ESLint環境がある場合に導入しやすい
  • .eslintrcにPrettierの設定をできて一元管理感がある
    • Prettier使うことが明示されるのでわかりやすくてよい
  • eslint --fix したタイミングでPrettierも実行される
  • どうせPrettierするならESLintもするでしょう?という人によさげ

【2の特徴】

  • 処理を担うのは prettier-eslint もしくは prettier-eslint-cli というプラグイン
    • 前者はJSファイルにimportして使うライブラリタイプで、後者はcliコマンドでやりたい場合
  • prettier-eslintを使うとPrettierしてからeslint --fixしてくれる
    • PrettierとESLintの処理をそれぞれ分けておける
    • Prettierのオプションは.prettierrcで管理するか、エディタのIntegrationプラグイン上で設定する問題がある

どっちでもいいけど自分は1.の手法をとっている。というのも最初に書いたようにESLintとPrettierは併用が実質必須みたいになりつつある(ESLintのみだけならまだしもPrettierのみという場面はなさそう)。なのでESLintの設定と一緒にPrettierの設定も書かれていたほうが1枚に設定がまとまっていてわかりやすいからそうしている。

ちなみに2のやり方は実際に使ってないので当記事では省略します。

1.のやりかた

というわけで1.のやりかたでの導入。

ESLint + Prettierをまず入れる

github.com

github.com

yarn add -D eslint prettier

yarnで書いているけれど別にnpmでもよい。お好きなパッケージツールで。
これでESLintとPrettier本体が入った。

次にESLintのPrettier用プラグインとコンフィグを入れる

GitHub - prettier/eslint-plugin-prettier: ESLint plugin for Prettier formatting

  • eslint-plugin-prettier
    • ESLintのルールとしてPrettierを読み込んで個々に処理するやつ

GitHub - prettier/eslint-config-prettier: Turns off all rules that are unnecessary or might conflict with Prettier.

  • eslint-config-prettier
    • ESLintとPrettierの処理で重複する部分を無効化してくれるやつ
yarn add -D eslint-plugin-prettier eslint-config-prettier

とりあえず必要なものは入ったのであとは.eslintrcに設定を書いていく。

.eslintrc

以下.eslintrc.jsonの例

eslint-plugin-prettier のconfig

{
  "plugins": [
    "prettier"
  ],
  "rules": {
    "prettier/prettier": "error"
  }
}

次にeslint-config-prettierのconfig

{
  "extends": [
    "prettier"
  ]
}

合わせるとこう

{
  "extends": ["prettier"],
  "plugins": ["prettier"],
  "rules": {
    "prettier/prettier": "error"
  }
}

最小限できた。
実際にはこれに加えてESlintのRuleがめっちゃ書かれるか、AirbnbないしはxoないしはGoogleないしはStandardあたりのなにかしらのルール郡をextendsで利用することになる。

以下は僕が実際に使ったりするESLintの

GitHub - standard/eslint-config-standard: ESLint Config for JavaScript Standard Style

JavaScript Standardをベースにしている.eslintrc.json

{
  "extends": ["standard", "prettier"],
  "env": {
    "browser": true
  },
  "plugins": ["prettier"],
  "rules": {
    "prettier/prettier": [
      "error",
      {
        "singleQuote": true,
        "semi": false
      }
    ],
    "yoda": 0,
    "no-unused-vars": 1
  },
  "globals": {
    "$": false
  }
}

standard使う場合は以下も必要なのでやっている、がここでは本筋ではないので閑話休題ということで…

yarn add -D eslint-config-standard eslint-plugin-standard eslint-plugin-promise eslint-plugin-import eslint-plugin-node

エディタ側の設定

ここまででPrettierを使う準備はできた。ので、エディタの設定をする。

VS Codeでの例で書く。

marketplace.visualstudio.com

ESLintのプラグインを入れる。

⌘+,するかVS Codeの設定を開いて

"eslint.autoFixOnSave": true

すると保存時にeslint --fixされるのでPrettierもされる、という流れになる。

これで無事ESLint+Prettierが使えるようになった。

Prettierのプラグインはエディタに入れなくてもいいんですか?

prettier.io

各エディタにはPrettierのプラグインもある。これは必要なのかどうかという悩みが出る。

端的にいうとこれまで解説してきたやりかただけをやるのであれば必要ないということになる。

これまでの解説はESLintとPrettierを併用することを前提としたプロジェクト単位での話で、『2つを併用するとルール面でコンフリクトが起こるのでよしなにしたい』というのが根底にあるからいろいろプラグイン入れたりコンフィグ書いたりしている。

そもそもPrettierだけを単体で使うだけであれば、エディタにプラグインを入れてしまえばOKという事実がある。逆にいえば、プロジェクト単位ではないような…例えばgitにも上げないような実験的なJSを書くときもPrettierを使って書きたいのなら、エディタのPrettierプラグインを入れる必要がある。

Prettierプラグインの設定にもESLintのプラグイン同様に"onSave"のような設定があって、保存時にPrettierが走るようにできる。でもこれを設定しておくと、これまで解説してきたようなeslint --fixでPrettierも走るようなプロジェクトを触るときに2回Prettierが走るので、注意したほうがいいという問題はある。なので自分はPrettierのプラグインを使う場面では手動で実行させている。

エディタ依存になるのでgit hookでPre-commit時にPrettierしましょう

自分は上述のエディタ連携をしてファイル保存時に逐一PrettierとESLintが走り、エラーが出たら都度直すほうが好きなのでそういう使いかたをしている。でもOSSとか複数人で共有するプロジェクトの場合、全員がエディタプラグイン入れて連携させる必要が出てきてこれではよくない。誰かがエディタ連携しないままgitにプッシュするとESLint&Prettierの検閲がされていないコードがgitに紛れ込んでしまう。

そういうわけでgit commitすると自動でeslint --fixが走るように設定しておくと間違いがなくなって便利。

公式に解説があってやりかたは複数あるけどポピュラーっぽいHuskyとLint-stagedを使う方法で僕はやっている。

まずhuskylint-stagedを入れる。

yarn add -D lint-staged husky

package.jsonに以下を追加

{
  "husky": {
    "hooks": {
      "pre-commit": "lint-staged"
    }
  },
  "lint-staged": {
    "*.{js,jsx}": [
      "eslint --fix",
      "git add"
    ]
  }
}

これでgit commit時にESLintのFixが走るのでgitコミットされるコードは確実にlint&format済みのコードということになって秩序が生まれる。

TypeScriptでPrettierしたい

上記まででJS環境=ESLint+Prettierを併用できるようになった。TS環境=TSLint+Prettierではどうすればという解説。

流れは同じなので雑に書きます。

まず必要なライブラリを入れる

yarn add -D tslint prettier tslint-config-prettier tslint-plugin-prettier

tslint.jsonを書く

{
  "rulesDirectory": ["tslint-plugin-prettier"],
  "extends": ["tslint-config-standard", "tslint-config-prettier"],
  "rules": {
    "prettier": [
      true, {
        "singleQuote": true,
        "semi": false
      }
    ]
  }
}

こういう感じ。

エディタのTSLintプラグイン

marketplace.visualstudio.com

Pre-commit

ESLintのときと同じ。huskylint-stagedを入れる。

{
  "husky": {
    "hooks": {
      "pre-commit": "lint-staged"
    }
  },
  "lint-staged": {
    "*.{ts,tsx}": [
      "tslint --fix",
      "git add"
    ]
  }
}

lint-stagedを.ts対象にする

基本的にESLintとやりかたは同じで、tslint.jsonの書き方が若干ESLintと異なるくらいだと思う。


ちなみに

これは小ネタではあるけど、ESLintは.eslintrcを探すときルートディレクトリまでずっと遡って探索する。ので、仮にプロジェクトに.eslintrcがなければユーザーのローカルルートディレクトリまで遡る。 ルートにdotfilesを置く風潮なので、自前の.eslintrcをルートディレクトリにおいておけば、例えば雑にエディタでJSを書くときにはルートにある自前の.eslintrcが参照される。
エディタのプラグインに.eslintrcの場所を指定するオプションがあったりするけど、基本的に特殊な階層において管理していないのであれば、ああいう設定は不要だったりする。

長くなったけどESLint+Prettierを導入するにはこういう感じです。

.eslintrcでPrettierも管理して実行はeslint --fix駆動ということが理解できればそんなに難しくないけど付随する設定やプラグインが多すぎる。1個にまとめて欲しい気もする。