📚

マナリンクのソースコード整形(Prettier, ESLint, StyleLint)設定-2021年5月版

2021/05/14に公開

弊社のオンライン家庭教師サービス「マナリンク」で設定しているソースコード整形周りの設定を公開していきます。

以前マナリンクのフロントエンド開発環境【基礎編/TypeScript,ESLint,Jest,Sentry】 にて、「以前は僕もPrettierを愛用していましたが、最近のプロジェクトではESLintだけで事足りることも多く、存在意義がよくわからなくなってきています。このへんは分かったら記事化します。」と書きました。そのあと結局PrettierとESLintを併用する形にしたので、設定や目的などをまとめてみます。

husky, lint-stagedの設定

まずは、package.jsonにおけるhuskyとlint-stagedの設定を示します。

  "lint-staged": {
    "*.vue": [
      "prettier --write",
      "eslint --fix",
      "stylelint --fix"
    ],
    "*.{js,ts}": [
      "prettier --write",
      "eslint --fix"
    ],
    "*.{css,scss}": [
      "prettier --write",
      "stylelint --fix"
    ]
  },
  "husky": {
    "hooks": {
      "pre-commit": "lint-staged"
    }
  },

prettier-eslintprettier-stylelintは使わず、愚直に連続でコマンド実行する形にしています。
Nuxtプロジェクトのため、基本的なファイルはVue、JS、TS、CSS、SCSSです。
※huskyは現在はv4を使っていますが、v6にアップデートする差分をPull Requestするところまでは進めています。husky v6はいくつか設定が変わっていますが、migration toolが提供されているのでこれを実行すると割とスムーズです。

prettier-eslintやprettier-stylelintを使わない理由

以下のPrettier公式ドキュメントのNotesをご覧ください。
https://prettier.io/docs/en/integrating-with-linters.html#notes

雑に和訳すると実行速度が遅くなるからと書かれています。よほど差分が大きくない限り、prettier-eslintを使ってもprettierを直接実行してもほとんど速度が変わらないな、と体感的には思うのですが、公式が言うので従ったほうがいいかなと思っています。

それに、依存する(package.jsonに載る)ライブラリはできる限り少ないほうが良いと思います。使わなくても同等以上の速度で動作するのであれば、使わなくていいのかなと思います。

prettierの設定

続いてprettierrc.jsを貼ります。こちらのファイルを参照してPrettierが実行されます。

.prettierrc.js
module.exports = {
  printWidth: 120,
  semi: false,
  singleQuote: true,
  bracketSpacing: true,
  jsxBracketSameLine: false,
  arrowParens: 'avoid',
}

semi: false

現状マナリンクでは、セミコロンはfalseに設定しています。しかし以下のAirbnbやGoogle Style Guideを読む限りは特定のケースで行末にセミコロンがあるとみなす(ASI)ことに起因するバグを防ぐために、予めセミコロンを付けておくことを推奨しています。

https://github.com/airbnb/javascript
it uses a set of rules called Automatic Semicolon Insertion to determine whether or not it should regard that line break as the end of a statement
ASI contains a few eccentric behaviors, though, and your code will break if JavaScript misinterprets your line break.

https://google.github.io/styleguide/jsguide.html
Every statement must be terminated with a semicolon. Relying on automatic semicolon insertion is forbidden.

そのため、Next.jsで開発しているマナリンクTeachersやFirebaseリポジトリではすでにセミコロンを付けるようにしています。


ちなみにNext.jsそのもののリポジトリを見ると普通にsemi: falseだったりするので、実際問題は実害としてASIがバグを誘発することは少ないのかもしれません。
https://github.com/vercel/next.js/blob/canary/.prettierrc.json

bracketSpacing: true

BracketSpacingは個人的な見やすさでtrueにしましたが、よくネット上で見るコードは空白が空いていないことも多いので、ここも好みかもしれません。

ESLintの設定

eslintrc.js
module.exports = {
  extends: [
    '@nuxtjs/eslint-config-typescript',
    'plugin:import/typescript',
    'plugin:vuejs-accessibility/recommended',
    'plugin:vue/recommended',
    'prettier',
  ],
  plugins: ['vuejs-accessibility'],
  rules: {

    // 中略

    'vue/component-name-in-template-casing': ['error', 'kebab-case'],

    // Vue3対策
    'vue/no-deprecated-slot-attribute': 2,
    'vue/no-deprecated-scope-attribute': 2,
    'vue/no-deprecated-slot-scope-attribute': 2,
    'vue/no-deprecated-filter': 2,
    'vue/no-deprecated-v-bind-sync': 2,
    'vue/no-deprecated-v-on-number-modifiers': 2,
    'vue/no-deprecated-events-api': 2,
    'vue/no-deprecated-functional-template': 2,
    'vue/no-deprecated-html-element-is': 2,
    'vue/no-deprecated-vue-config-keycodes': 2,
    'vue/no-deprecated-dollar-listeners-api': 2,
    'vue/no-deprecated-v-on-native-modifier': 2,
    'vue/no-deprecated-dollar-scopedslots-api': 2,
  },
}

vuejs-accessibilityがすぐれものです。ありがちなものですとaltが指定されていないimgタグは避けようとか、divなどにonClickを当てるなら@keyupなども紐付けるようにしてね、とかがあり、詳しくなくてもa11yを適用できるようになってます。

あとはtemplate内部にコンポーネントがあったらケバブケースに統一するとか、Vue3で廃止予定の機能は使わないようにするとかです。

stylelintの設定

stylelintはstylelint-config-standardを使いつつ、そのままだと利用が厳しいルールをOFFにしています。けっこう厳し目のルールが多い印象で、既存のソースコードがかなり怒られてしまいます。

{
  "extends": "stylelint-config-standard",
  "rules": {
    "at-rule-no-unknown": null,
    "selector-pseudo-element-no-unknown": null
  }
}

prettierとLinterはどちらが先に実行されるべきか?

prettierとlinterを分けて実行するとなると、どちらを先に実行するのが良いか、という命題が生まれます。

私は現在prettierを先に、linterを後に実行しています。理論的には、eslint-config-prettierを入れているためどちらが先でも問題がないはず・・・です。

しかしこの記事のように、prettierを最後に実行するように指定する意見も見受けられます(悲しいことに、この記事ではなぜその順番が望ましいのかの回答が得られませんでした)。

逆にこの記事では、npm scriptsのfixとしてprettierを実行してからeslintしています。

prettierを実行した結果としてlint違反になるコードが生まれることはないでしょうが、lintを実行した結果としてprettier違反になるコードが生成されることは全く無くはない気がしています。
ということは、lintを先に実行したほうがより良いといえるのでしょうか。

何かしら明確な答えが得られましたら追記しようと思います。

Prettierを使う意味

ESLintのルールを見ても、いくつかソースコードの見た目を良くするためのルールが見受けられます。たとえば行末の余分なスペースを検知するなどです。しかしPrettierを使うのはなぜなのでしょうか。

それについて当時の私自身に説明するとしたら以下のように話します:

Prettierはソースコードの見た目を良くして可読性を上げるために実行する。Linterはソースコードの機能面は変えずに、様々な観点でより望ましい実装方法を提示したり、Fixしてくれる。Linterの機能の中にPrettierが果たせる機能が含まれることもあるが、eslint-config-prettier(source)などを使うことで、競合するルールに関するLintをOFFにして共存する。それではESLintだけでPrettierの機能を完全に代替できるのでは?という疑問が出てくるのは自然であるが、Lintのルールには純粋に整形しか目的としないような、たとえば関数にある程度の個数の引数があれば改行してきれいに見せてくれるような整形は含まれない。したがってPrettierに整形を任せるほうがいい


以上です。

マナリンク Tech Blog

Discussion