フロントエンドのアプリケーションを構築していて、リンターとフォーマッターをどのように設定するのがベストなのか自分なりに調査しました。

結論

まず最初に、自分がベストだと思う設定方針を書いておきます。

開発時

  • 一般的なスタイルに関する問題
    • Prettier でフォーマットする
      • ファイル保存時に自動フォーマットする
  • 言語毎のコード構造に関する問題
    • JavaScript や TypeScript については ESLint で、CSS や SCSS については StyleLint でリント&フォーマットする
      • Prettier と競合するルールは無効化する
      • エディター上でリント結果のワーニングやエラーを表示する
      • ファイル保存時に自動フォーマットする

コミット時

  • pre-commit のタイミングでリントを実行して、エラーならコミットできないようにする
    • Prettier + ESLint + Stylelint を全て実行する

CI 時

  • CI でもリントを実行して、エラーならマージできないように設定する
    • Prettier + ESLint + Stylelint を全て実行する

リンターとフォーマッターは併用する?

ESLint や Stylelint もフォーマット機能が提供されているので、もはや Prettier はいらない子なのでは?
という疑問を誰もが持ったことがあると思います。
自分もよく分かってませんでしたので、公式サイトや人気記事にどう書いてあるのかを調べました。

Prettier側の記載

Prettier の公式サイトの、以下のページあたりに Linter との併用について記載がありました。

要約すると以下のようなことが書いてあります。

  • コードスタイルの問題には Prettier を使用し、コード構造に関する問題にはリンターを使用する
  • ただし、リンターに含まれる Prettier と競合するルールや不要なルールはオフにする

Prettier 側としては、立場上当然かもしれませんが、併用を推奨しているようです。

ESLint側の記載

ESLint の公式サイトの FAQ に、Prettier との併用について記載がありました。

要約すると以下のようなことが書いてあります。

  • ESLint は伝統的なコード構造に関するリント機能とスタイルチェックの両方を提供している
  • ESLint だけで完結してもいいし、コード構造に関する問題には ESLintを、コードスタイルの問題には Prettier を、というように両方使うのもOK

ESLint の立場としては、併用してもしなくてもいいよ、という感じのようです。
とはいえ、世の中では当たり前のように併用されているので、その辺調べた記事を探してみたところ、こちら にとても分かりやすくまとめていただいてました。

以下のようなことが書かれています。

  • ESLint ではフォーマットできないものでも、Prettier でフォーマットできるものがある(具体的にはmax-lenなど)
  • ESLint は設定したものだけがフォーマットされるが、Prettier はデフォルトでよしなにフォーマットしてくれる

確かに自分で使っていても、Prettier はインストールするだけで、 JavaScript / TypeScript だけでなく、 HTML / JSON / md など、いろんな形式のファイルを勝手に良い感じにフォーマットしてくれる点を便利に感じていたので、納得です。

結論としては、「ESLint だけでも成り立つけど、Prettier を併用する方がより良い」ということになりそうです。

Stylelint側の記載

Stylelint の公式サイトに、Prettier についての言及があります。

要約すると以下のようなことが書いてあります。

  • マシンがアルゴリズム的にコードを綺麗にできるもの
    • これは Prettier のような pretty printers で扱う問題で、競合するルールはオフにする必要がある
  • 人間がまずコードをフォーマットし、マシンが間違いを修正/警告するもの
    • Stylelint に内蔵されているルールとプラグインで多様なコード規約をサポートする

こちらは、Prettier との併用を推奨しているように受け取れます。
また、こちら をみると、Stylelint 自体の開発でも Prettier でのフォーマットを推奨してるみたいです。

結論として、「Stylelint は、Prettier と併用したほうがよい」ということで問題なさそうです。

併用時の競合ルール

リンターとフォーマッターを併用する場合、競合するルールの管理が問題になります。
両方に全く同じ設定を入れる、という方法もありますが、ダブルメンテになるし、メンテ忘れなどもリスクもあります。

そのようなことが起こらないように、Prettier で提供されているルールについて、リンター(ESLint や Stylelint)側で無効にすることが推奨されてます。
https://prettier.io/docs/en/integrating-with-linters.html

具体的な設定方法は後述しますが、eslint-config-prettierstylelint-config-prettierを導入するだけです。

フォーマット可能なルール

次に、Pretteir・ESLint・Stylelint でフォーマット可能なルールについて確認してみました。
なんとなく全体像が把握できた気がしてきます。

Prettier

https://prettier.io/docs/en/options.html

  • Print WidthTab WidthTabsSemicolonsQuotesTrailing CommasBracket SpacingBracket Lineなど非常にシンプルなルールばかり
  • 合計で22ルールだけ

ESLint

https://eslint.org/docs/rules/

  • 🔧 と記載があるものがフォーマット可能
    • 100ルールぐらいフォーマット可能
    • リントだけのものも含むと200ルール以上
  • no-else-returnno-extra-bindなどコード構造に関する問題でも、フォーマットできるものがある
  • max-lenmax-statements-per-lineなど、リントだけでフォーマットはしてくれないものがある
    • => max-lenのフォーマットには Prettier が必要
  • semiquotesなどは、Prettier と競合するルール
    • => eslint-config-prettierで無効にする必要がある
    • => 無効にしているルールは こちら で確認できる

Stylelint

https://stylelint.io/user-guide/rules/list

  • (Autofixable)と記載があるものがフォーマット可能
    • 90ルールぐらいフォーマット可能
    • リントだけのものも含むと200ルール弱
  • no-duplicate-at-import-rulesproperty-no-vendor-prefixなど、コード構造に関する問題でも、フォーマットできる
  • max-line-lengthcolor-no-invalid-hexなど、リントだけでフォーマットはしてくれないものがある
    • => max-line-lengthのフォーマットには Prettier が必要
  • selector-attribute-brackets-space-insidemax-empty-linesなどは、Prettier と競合するルール
    • => stylelint-config-prettierで無効にする必要がある
    • => 無効にしているルールは こちら で確認できる

設定方法

ここからは具体的な設定方法を記載しようと思います。

Prettierのインストール

  • インストール
    yarn add --dev prettier
  • .prettierrc.json作成
    singleQuote: true
    printWidth: 120

    => デフォルトから変更したい場合は、オプション を参照して設定します

これで Prettier の設定は一通り終わりです。
CLI で prettier を実行して意図通り動いていれば設定終了です。

# チェックだけ
yarn prettier --check .

# チェック+フォーマット
yarn prettier --check --write .

ESLintのインストール

  • インストール
    # インストール
    yarn add --dev eslint
  • 初期化
    yarn eslint --init

    => 「モジュールのインポート形式」「VueやReactを使うか」「TypeScriptを使うか」「コードスタイルに何を使うか」「設定ファイルの形式」など、質問に答えていくと、設定ファイル(.eslintrc.yml)が作成されます。
    自分の場合は以下のようになりました。

    env:
      browser: true
      es2021: true
    extends:
      - standard
    parser: '@typescript-eslint/parser'
    parserOptions:
      ecmaVersion: latest
      sourceType: module
    plugins:
      - '@typescript-eslint'
    rules: {}
  • 必要なルールセットを追加する
    • インストールが必要なものはyarn addで追加したうえで、設定ファイルを修正
      extends:
        - standard
        - eslint:recommended
        - plugin:@typescript-eslint/recommended
        - plugin:jest/recommended
  • リント対象外のファイルパスを指定
    • 設定ファイルに追加
      ignorePatterns:
          - '/dist'
  • Prettier と競合するルールを無効化
    • eslint-config-prettier を利用します
      これは Prettier と ESLint で競合するルールを ESLint 側でオフにする設定です
    • インストール
      yarn add --dev eslint-config-prettier
    • 設定ファイルに追加
      extends:
        - (省略)
        - eslint-config-prettier # 最後に追加

これで ESLint の設定は一通り終わりです。
CLI で eslint を実行して意図通り動いていれば設定終了です。

# リントだけ
yarn eslint .

# リント+フォーマット
yarn eslint . --fix

Stylelintのインストール

  • インストール
    yarn add --dev stylelint stylelint-config-standard
  • 設定ファイル(.stylelintrc.json)を作成
    {
        "extends": "stylelint-config-standard"
    }
  • 必要なプラグインやルールセットを追加する
    • ここでは scss を使ってる場合の設定を紹介しておきます
    • まずはプラグインやルールセットをインストール
      yarn add --dev stylelint-scss stylelint-config-standard-scss
    • 設定ファイルを修正
      {
        "plugins": ["stylelint-scss"],
        "extends": ["stylelint-config-standard", "stylelint-config-standard-scss"]
      }
  • リント対象外のファイルパスを指定
    • 設定ファイルに追加
      {
        "ignoreFiles": ["/dist/*"]
      }
  • Prettier と競合するルールを無効化
    • stylelint-config-prettier を利用します
      これは Prettier と Stylelint で競合するルールを Stylelint 側でオフにする設定です
    • インストール
      yarn add --dev eslint-config-prettier
    • 設定ファイルに追加
      {
        "extends": ["stylelint-config-standard", "stylelint-config-standard-scss", "stylelint-config-prettier"]
      }
    • [FYI] 競合ルールの確認
      • 既存のプロジェクトに stylelint-config-prettier を追加する場合、競合するルールを確認したくなると思います。
        その場合は、以下のコマンドで確認できます。
        yarn stylelint-config-prettier-check
        > Conflicting rule(s) detected in your stylelint configuration:
        >   indentation
        >   string-quotes

これで Stylelint の設定は一通り終わりです。
CLI で stylelint を実行して意図通り動いていれば設定終了です。

# リントだけ
yarn stylelint **/*.{css,scss}

# リント+フォーマット
yarn stylelint **/*.{css,scss} --fix

VSCodeの設定

ここまでで、CLI でリンターとフォーマッターを実行できるようになりました。
次は、VSCode 上でリントエラーを確認したり、ファイル保存時に自動フォーマットされるようにしたいと思います。

まずは、以下の三つの VSCode 拡張をインストールします。

これらをインストールするだけで、設定ファイルに従ってリントエラーがエディタ上で確認できるようになります。

さらに、保存時に自動フォーマットされるように、VSCodeの設定ファイル(.vscode/settings.json)を作成します。

{
  "editor.defaultFormatter": "esbenp.prettier-vscode", // デフォルトフォーマッターとして Prettier プラグインを利用
  "editor.formatOnSave": true, // ファイル保存時にデフォルトフォーマッターでファイルをフォーマットする
  "editor.codeActionsOnSave": { // ファイル保存時のコードアクション設定
    "source.fixAll.eslint": true, // ESLint プラグインでフォーマットする
    "source.fixAll.stylelint": true // Stylelint プラグインでフォーマットする
  }
}

これで設定完了です。
ちょっとリンターに怒られるようなコードを書いて、保存してフォーマットされているかどうか試してみてください。

コミット時にリント実行する設定

huskylint-staged を使ってコミット時にリンターを実行したいのですが、記事のボリュームが大きくなりすぎたので、別記事にしました。
https://rinoguchi.net/2021/12/husky-and-lint-staged.html

CIの設定

CI でリントを実行して、問題があればマージできないように設定したいと思いますが、設定方法はそれぞれの CI ツールで違うのでシェルスクリプトだけ軽く紹介しておきます。

コマンドラインで簡単に実行できるように、リントコマンドをpackage.jsonにスクリプトとして定義します。

"scripts": {
  // リント設定
  "lint": "yarn run lint:prettier && yarn run lint:es && yarn run lint:style ",
  "lint:prettier": "prettier --check --loglevel=warn '**/*.{js,ts,scss,md}'",
  "lint:es": "eslint '**/*.{js,ts}'",
  "lint:style": "stylelint '**/*.{css,scss}'",
  // フォーマット設定
  "format": "yarn run format:prettier && yarn run format:es && yarn run format:style",
  "format:prettier": "prettier --check --write --loglevel=warn '**/*.{js,ts,scss,md}'",
  "format:es": "eslint --fix '**/*.{js,ts}'",
  "format:style": "stylelint --fix '**/*.{css,scss}'"
},

あとは、ここで定義したスクリプトを CI ツールのシェルスクリプトで呼び出すだけです。

# リント
yarn lint
# フォーマット
yarn format

個人的には、CI でソースコードが変更されることに不安があるので、CI フォーマットの定義はしていませんが、参考までに載せてあります。

[FYI]現在は非推奨のライブラリ群

参考までですが、昔の記事とかを見ると出てくる、現在は非推奨になったライブラリたちを紹介しておきます。使わないように気をつけましょう。

最後に

ちょっと長くなりましたが、フロントエンドのアプリケーションでの、リンターとフォーマッターの併用について設定方針および設定方法がある程度理解できたと思います。
今後はこの方針に従って各リポジトリの初期設定をやっていこうと思います。