ESLint

発表資料: sonarish で俺がお前を叱る

自己紹介

  • mizchi
  • フリーランス
  • リクルートでは yosuke_furukawa の元でR&Dみたいなことをしている

飛び入りLT用(発表ないかも)


yosuke_furukawa 「コードメトリクスツール作って」


コンテキスト

  • リクルート社内に膨大なJS含むプロジェクトがある
  • JSを(僕や yosuke_furukawa ぐらいに)真っ当に書ける人が少ない
  • いろんなプロダクトを串刺しでスコアリングして可視化したい

eslint は?

  • プロジェクトごとに運用のされ方が違う || そもそもない
  • 「1件でも落とすと exit-status = 1」 なので攻めたルールを採用しにくい

どうせだったらOSSにしようぜ => はい


※ 画面は開発中のものです


sonarish (仮)

$ npm i -g sonarish-cli
$ cd your-repo
$ sonarish src

実行例

$ cd sonarish
$ sonarish --detail
--- complexity
score: 96.4/100
┌──────────────────────┬───────┬──────────┬───────┐
│ rule                 │ score │ priority │ count │
├──────────────────────┼───────┼──────────┼───────┤
│ no-shadow            │ -2.6  │ 2        │ 2     │
├──────────────────────┼───────┼──────────┼───────┤
│ mutation/no-mutation │ -0.9  │ 1        │ 1     │
└──────────────────────┴───────┴──────────┴───────┘
--- best-practice
score: 92.9/100
┌───────────────────────────────────┬───────┬──────────┬───────┐
│ rule                              │ score │ priority │ count │
├───────────────────────────────────┼───────┼──────────┼───────┤
│ import/no-extraneous-dependencies │ -2.5  │ 3        │ 4     │
├───────────────────────────────────┼───────┼──────────┼───────┤
│ no-console                        │ -2    │ 1        │ 22    │
├───────────────────────────────────┼───────┼──────────┼───────┤
│ no-unused-vars                    │ -1.8  │ 1        │ 18    │
├───────────────────────────────────┼───────┼──────────┼───────┤
│ compat/compat                     │ -0.6  │ 1        │ 2     │
└───────────────────────────────────┴───────┴──────────┴───────┘
--- meta-comments
score: 63.8/100
┌───────────────────────────────────┬───────┬──────────┬───────┐
│ rule                              │ score │ priority │ count │
├───────────────────────────────────┼───────┼──────────┼───────┤
│ no-warning-comments               │ -20.2 │ 4        │ 4     │
├───────────────────────────────────┼───────┼──────────┼───────┤
│ eslint-comments/no-use            │ -7.9  │ 1        │ 10    │
├───────────────────────────────────┼───────┼──────────┼───────┤
│ eslint-comments/no-unused-disable │ -7.9  │ 1        │ 10    │
└───────────────────────────────────┴───────┴──────────┴───────┘


babel/babel

--- complexity
score: 61.1/100
┌──────────────────────┬───────┬──────────┬───────┐
│ rule                 │ score │ priority │ count │
├──────────────────────┼───────┼──────────┼───────┤
│ no-param-reassign    │ -15.7 │ 3        │ 106   │
├──────────────────────┼───────┼──────────┼───────┤
│ no-shadow            │ -9.2  │ 2        │ 78    │
├──────────────────────┼───────┼──────────┼───────┤
│ max-lines            │ -5.5  │ 2        │ 28    │
├──────────────────────┼───────┼──────────┼───────┤
│ mutation/no-mutation │ -5.2  │ 1        │ 601   │
├──────────────────────┼───────┼──────────┼───────┤
│ no-unreachable       │ -2.9  │ 4        │ 2     │
└──────────────────────┴───────┴──────────┴───────┘
--- best-practice
score: 86.4/100
┌───────────────────────────────────┬───────┬──────────┬───────┐
│ rule                              │ score │ priority │ count │
├───────────────────────────────────┼───────┼──────────┼───────┤
│ prefer-arrow-callback             │ -4.8  │ 2        │ 503   │
├───────────────────────────────────┼───────┼──────────┼───────┤
│ import/no-extraneous-dependencies │ -3.6  │ 3        │ 25    │
├───────────────────────────────────┼───────┼──────────┼───────┤
│ promise/always-return             │ -2    │ 2        │ 17    │
├───────────────────────────────────┼───────┼──────────┼───────┤
│ no-console                        │ -1.2  │ 1        │ 25    │
├───────────────────────────────────┼───────┼──────────┼───────┤
│ eqeqeq                            │ -0.8  │ 1        │ 13    │
├───────────────────────────────────┼───────┼──────────┼───────┤
│ import/no-mutable-exports         │ -0.6  │ 2        │ 2     │
├───────────────────────────────────┼───────┼──────────┼───────┤
│ no-unused-vars                    │ -0.2  │ 1        │ 1     │
└───────────────────────────────────┴───────┴──────────┴───────┘
--- meta-comments
score: 34.1/100
┌───────────────────────────────────┬───────┬──────────┬───────┐
│ rule                              │ score │ priority │ count │
├───────────────────────────────────┼───────┼──────────┼───────┤
│ no-warning-comments               │ -49.4 │ 4        │ 75    │
├───────────────────────────────────┼───────┼──────────┼───────┤
│ eslint-comments/no-use            │ -9.2  │ 1        │ 42    │
├───────────────────────────────────┼───────┼──────────┼───────┤
│ eslint-comments/no-unused-disable │ -7.1  │ 1        │ 25    │
└───────────────────────────────────┴───────┴──────────┴───────┘


オプション

$ sonarish [--root <root-path>] [--detail] 'src/**' 'subdir/**'

sonarish の 設計

  • 空白のスタイリングなどには関与しない(prettierでやれ)
  • ルールが落ちることを前提に、エラーカウント/合計ファイル数 で減点
  • 複数のカテゴリごとにeslintのルールセットを作る
  • カテゴリ内の priority を設定

「攻めたルール」の例

  • object-rest-spread あれば代入なんていらねーよな!
  • クラス以外で this なんてつかわねーよな!
  • 二重 if / switch はダサいよな!
  • 複雑な論理オペレータのネストはダサいよな!
  • TODOコメントなんて残んねーよな!

ダサいを可視化する

  • いわゆる codesmell の検出
  • 自分でそのためのeslintルールを書いてる(既存のものにそういうルール集はあんまりない)

スコアリングルール

そのルールの (エラー数/総ファイル数) * (priority/合計priority)

(実際は細かい正規化とスレッショルドがいっぱいあって調整中)


TODO

  • 自分でスコアルールを書けるようにする
  • ソースの公開
  • eslint 以外のツール対応
    • package.json の health check
    • コピペ検出
    • etc...

わかったこと

  • no-unused-vars は結構効く
  • TODO コメントはめっちゃ残ってる
  • eslintをまともに運用してるように見えてもeslint-disableを数えると結構やばい