はじめに
こんにちは。フロントエンドなんもわからん浦丸です。
最近体重がMAXから-6kgまで落ちて嬉しいです。
みなさん、ESLintを活用していますか?ESLintはコードの静的解析ツールの一つです。1
ESLintを使用することで、コーディング段階での潜在的なバグの原因やコーディング既約に沿ったコーディングができ、生産性の向上が狙えます。弊社ではようやく活用できるようになってきました。
今回は、実装の背景と実際の実装方法を紹介したいと思います。
モチベーション
ESLint Configを作成するまでの背景を、弊社の状況を合わせつつ説明しようかなと思います。興味ないよという方は、実装の節まで飛ばしていただいて構いません。
レビュー工数削減
まず、ESLint Configを作成しようとした動機として一番大きいものが、「レビュー工数削減」です。
弊社はプロダクト担当制という仕組みで開発しており、プロダクト毎に1~2人のフロントエンドエンジニアがアサインされています。2人いるプロダクトならまだ良いのですが、1人しかいないプロダクトもあるため、その人しかコードの理解ができない、いわゆるコードの属人化を防ぎたかったのです。
実際、古くからいた方が退職されたときに引継いだプロダクトがあるのですが、理解及ばず読解に時間を費やしてしまったことがあります。当時はフロントエンドエンジニアが1名しか在籍しておらず、レビューできなかったのもしょうがないところがありますが…。しかし今は、既存社員5名+新卒2名と、レビューするためにリソースがあると判断したためこのようにしようと考えました。
さて、早速レビューをしようとなったわけですが、レビューにかかる工数が無視できなくなります。人が増えたとはいえ開発したいことは山ほどあり、開発したいのにレビューもしなきゃいけないといった状態になってしまいました。どうにかしてこれを減らせないか考えていたところ、レビューで起きる指摘の内容の大半が、コーディング規約に則っていないことや知識として持ってないとできないことであると気付きました。そしてこれはESLintで縛ることができます。例えば
- 変数はローワーキャメルケース、定数はアッパースネークケース
- Vueのcomponent名は複数単語で命名する2
- buttonタグにはtype属性を設定する
- 未使用変数の削除
のような問題です。「buttonタグにはtype属性を設定する」を例に挙げると、form内にbuttonを2つ以上おきたい場合は、buttonに適切なtypeを設定する必要があります。これは、typeを指定しないbuttonタグはデフォルトでtype=”submit”が付くようになっているためです3。typeの指定忘れが直ちに問題になることは少ないと考えていますが、潜在的なバグの原因になることがあるためできれば実装段階で気を付けたいことではあります。
これらは、レビューで発見、もしくはコーダーが知っていれば防げる内容ではありますが、どうしても人力では限界があるでしょう。機能開発やバグ修正は常に発生しますし、その都度確認していてはレビュー工数がどうしてもかさんでしまいます。先程上げたようなコーディング規約違反の検知やアンチパターンの排除をESLintに任せてしまうことで、レビュー工数を減らしつつロジック部分のレビューに注力できればいいという思惑がありました。
他プロダクトで出たESLintのルール追加・修正を一括で行いたい
といっても、レビュー工数だけを削減したいならプロダクト毎にESLintの設定を書けば良いでしょう。しかし、全員がすべてのプロダクトを担当するわけではないため、他プロダクトで出たrule設定漏れや改善などを、毎回自分のプロダクトまで反映させるのは少々手間です。
弊社のフロントエンドで使用している技術スタックは、プロダクトによってまちまちですが以下のようになっています。割合としてはWordPress+JavaScriptが一番多く、VueとReact+TypeScriptが同じくらいです。
- WordPress+JavaScript
- React+TypeScript
- Vue
社内で共通の設定を作るには、これらのプロダクトをカバーするような設定を作ってあげる必要があります。分類すると以下のようになります。
- JavaScriptのコアなルール
- TypeScript向けのルール
- React向けのルール
- Vue向けのルール
これらは、共通化したESLint Configをnpmパッケージとして公開して管理することで解決します。こうすることで、ライブラリ側はruleの調整だけを行えば良く、使用側では必要なconfigだけをeslint.config.jsに記述すれば良くなります(ライブラリが更新されたらnpm iする必要がありますが…)。
また、自作ruleを実装したときに、簡単に共有して組み込めるのも良いポイントだと思っています。
実装
ESLint v9 (flat config対応)
node v18以上
今回ESLint Configを作成するにあたって使用したライブラリは以下の通りです。これらを組み合わせて、JavaScript, TypeScript, React, Vueそれぞれのconfigを作成します。
- base
- typescript
- react
- vue
base
baseにはJavascriptの基本的なルールセットを詰め込みます。ライブラリの選定意図はよさそうなやつを適当に突っ込んだだけです。以下eslintのrulesを抜粋したものです。
export default {
rules: {
// 省略
// 重複したキーを禁止する
"no-dupe-keys": "error", // recommended
// switch文での重複したcaseを禁止する
"no-duplicate-case": "error", // recommended
// 省略
}
}
各種ライブラリには、ライブラリ作成者が推奨しているルールセットがあります。それを拡張しても良かったのですが、ルール理解のついでにドキュメントを読んで追加するかどうかを判断したため、このようになっています。
このeslintのrule以外にも、eslint-plugin-unicornなどのruleセットを作成しておきます。
作成したrulesはconfigでimportしましょう。そうすることで、作ったruleをまとめて使用することができます。ただ、任意のファイルだけ抜きたいが場合があるでしょう。そのときは、新しくconfigファイルを作成して、必要なruleのみをimportすれば問題ありません。以下が作成したconfigになります。
import globals from "globals";
import compatRules from "../rules/compat.js";
import eslintRules from "../rules/eslint.js";
import importRules from "../rules/import.js";
import promiseRules from "../rules/promise.js";
import unicornRules from "../rules/unicorn.js";
export default [
{ ignores: ["**/*.d.ts", "**/dist"] },
{
settings: {
"import/resolver": {
typescript: {},
},
},
languageOptions: {
ecmaVersion: "latest",
sourceType: "module",
globals: { ...globals.browser, ...globals.builtin, ...globals.node },
},
},
eslintRules,
importRules,
unicornRules,
compatRules,
promiseRules,
];
おわりに
今回は社内で使う共通のESLint Configを作った話をしました。
この社内ライブラリを作成したことで、レビュー時の本質的でないレビューを減らすことや、VSCode拡張と合わせて開発時にエラーやコーディング規約にそぐわないコードに気づくことができたため、開発生産性や工数削減に一役買えたのかなと思います。
記事中で触れてはいませんが、ESLintの他にStylelint用のconfigも作成していて、こちらも同様の効果が得られており導入してよかったかなと思っています。
ということで、let’s enjoy review life !!!