ESLintのローカルルールで独自のコーディング規約を実装する

この記事はドワンゴ Advent Calendar 2023 の 22 日目の記事です。

ドワンゴ教育事業Webフロントエンドチームの berlysia です。1

この記事では、オンライン学習サービスN予備校の開発を支えるコーディング規約の考え方に少し触れ、その運用を支えるESLintの活用を紹介します。

この記事の内容は、他社様主催イベントにて発表した内容の再構成です。

speakerdeck.com

コーディング規約は実行可能にしたい

教育事業のWebフロントチームには、細かいコーディング規約が書かれた文書は存在しません。コーディング規約はESLintをはじめとする各種ツールによって、自動で検出・修正可能な形になっています。

文書という形でなく、各種ツールのコンフィグファイルとそのコメントとしてまとまっている、と言い換えてもよいかもしれません。ドキュメンテーションは重要な仕事ですが、それ以上に日々高頻度に行われる仕事は自動化されているべきで、レビューではもっと高次のことに集中してもらいたいためです。

チームの中に慣習として存在する規約が「発見」され、ツールの設定に反映されることもしばしばあります。新規メンバーが参加するタイミングは、こうした慣習を発見する良い機会です。ここ3年間でWebフロントチームのメンバー数が5倍になったこともあり、コードの内外で様々な慣習が発見されています。

「気にしない」を選択肢に入れる

ダブルクオート・シングルクオートのレベルでは、決めの問題しかないので決めてしまっていますが、決めの問題というには違いが大きいものは、気にしないと決めることもあります。

たとえば、TypeScriptの型定義をType AliasとInterfaceのどちらで書くか、関数の定義を関数宣言や関数式で書くかアロー関数で書くか、などが気にしないことになっています。それぞれの話題を掘り下げることはこの記事の目的ではないので省略しますが、どちらかに統一しようとするともう一方が好ましい場合が出てきます。

Linterのエラーを無視するのは、極力完全にその意味をわかっていてそれが例外的かつ合理的な場合に限りたい2ので、disableディレクティブを書くことに慣れないためにも、そもそも指摘をしないことにしています。

世の中にないけど欲しいルールは自分で書く

Linterのルールを設定するのは、やり方によっては重たくも軽くもある仕事です。私たちの場合はもともと細かいことを決めずにやってきていたので、ひとまずrecommendedのものをいれてみて、肌に合わないものや検知したいものがあれば都度調整を入れて、設定を育てています3

詳細なコーディング規約を持っているチームでは、それをLinterで表現できるようにするのは一大事になるでしょう。recommendedのようなまとまったルールに乗ってしまうような判断で省略したり、いくらかは私たちのように「気にしない」こともあわせて選択できるかと思いますが、コードベースの状況に応じては妥協できないルールもあるはずです。

私たちN予備校のWebフロントチームでも、自動検知・自動修正したいことが見つかったとき、既存のコアルールやプラグインのルールでは要求を満たせないことがあります。このようなときに、自分たちで検知対象やその修正結果を制御する方法として、必要に応じてプロジェクト固有のルールを実装しています。

現在この形で運用されているルールは、実を言うと「インポートパスを自動的にもっとも短い表現にするルール」のひとつだけです。いざとなれば自前で書くことを選択肢に入れていると、新しい規約が生まれそうになったときに「これは静的に書き換えできるだろうか」「人間にやらせるにも難しい判定をしようとしていないだろうか」という判断が挟まるためか、結果的に規約を増やさずに済んでいます。

自動修正の必要がなく検知だけできればよいものは、モジュール参照周りはno-restricted-importsimport/no-restricted-pathsを活用していることが多いです。構文レベルではno-restricted-syntaxを使うとかなり自由度高く記述を禁止できます。この範囲を超えたり、この範囲では管理しづらいものは、自前でルールを書くことで無理なく対応できます。

以前の記事ESLintを活用した漸進的リファクタリングのすすめでは独自のプラグインを作成していましたが、目の前の実装に密接なルールはそのプロジェクトの中で閉じられると扱いやすいです。

ユーザーがルールを実装できるのはESLintだけ

ESLintは言わずと知れた定番Linterです。昨今のJavaScript周りの開発環境にはGoやRustといった言語で書かれたツールが増えていますが、独自のルールをプラグインとして提供したり、プロジェクト固有のルールを書く仕組みは、2023年末現在、ESLintにしか存在しません。

ESLintでプロジェクト固有のルールを読み込む

作成したルールをESLintで実際に使うための方法を紹介します。ルールの実装方法自体は公式ドキュメントに詳細に書かれているほか、TypeScript向けのルールを書きたい場合はtypescript-eslintのドキュメントも助けになります。

2023年末現在、ESLintの設定ファイルは新しい記法に置き換わっていくことが発表されていて、フラットコンフィグという愛称のもとで v8.21.0 以降ですでに利用可能になっています。私たちのコードベースでも移行を始めており、移行時の試行錯誤は記事にもなっています。

新卒エンジニアがESLintのFlat Config移行と格闘した話 - ドワンゴ教育サービス開発者ブログ

ESLintの公式ブログにも記載があるように、フラットコンフィグの世界ではルールを設定ファイルにimportしてきて、それをプラグインの構造に少し合わせてやるだけで簡単に動作します。次に示すのは、ESLintの公式ブログに記載されている設定ファイルの例です。

import myrule from "./custom-rules/myrule.js";

export default [
    {
        files: ["**/*.js"],
        plugins: {
            custom: {
                rules: {
                    myrule
                }
            }
        }
        rules: {
            "custom/myrule": "error"
        }
    }
];

従来形式の設定ファイル下で読み込む

従来形式の設定ファイルでも、ここまで簡単ではありませんが、独自のルールを利用する方法があります。実用的なものとしては、特定のディレクトリ配下にルールを配置してこれを読み込むようなプラグインを利用すると簡単です。次のようなものが候補になります。

  • eslint-plugin-local-rules
    • eslint-local-rules.jseslint-local-rules/index.js をこのプラグインのルール一覧として読み込んでくれる
  • eslint-plugin-rulesdir
    • ルールが配置されているディレクトリを文字列で与えると、このプラグインのルール一覧として読みこんでくれる
  • eslint-plugin-local
    • .eslintplugin.js.eslintplugin/index.js という名前のファイルをプラグインとして読み込んでくれる
      • ルールだけでなくプラグインとして読み込めるので、手の込んだことをしたい場合はこれが便利

繰り返しになりますが、上述のようにフラットコンフィグの世界では、これらの道具がなくても独自のルールを定義・利用できます。

まとめ

N予備校Webフロントエンドチームのコーディング規約は、ESLintを中心としたツールによって実行可能な形にするよう努めています。必要に応じて独自のルールを実装することで、コミュニティルールでは実現が難しいような自動修正を叶えています。

JavaScriptが書ける開発者なら誰でも簡単にルールを作成できるのは、ESLintの大きな魅力です。フラットコンフィグの世界では、プロジェクト固有のルールを実装して利用するのも簡単になりました。

コミュニティルールに直接実装を入れるには一般化が難しいようなものでも、プロジェクト固有の制約を利用すれば簡単に表現できたりします。手に馴染んだコードベースでルールの実装に慣れてみると、コミュニティルールにも貢献できるようになるかもしれません。

年末年始の期間にでも、フラットコンフィグ化や独自ルールの実装にぜひ挑戦してみてください。

We are hiring!

株式会社ドワンゴの教育事業では、一緒に未来の当たり前の教育をつくるメンバーを募集しています。

自分たちの仕事をガイドするものは、自分たちの意思で選び決めるのが私たち流です。

カジュアル面談も行っています。お気軽にご連絡ください。

カジュアル面談応募フォームはこちら

www.nnn.ed.nico

開発チームの取り組み、教育事業の今後については、他の記事や採用資料をご覧ください。

speakerdeck.com


  1. 気付けば古株になりました。
  2. disable系のディレクティブを書く場合は、ESLintもTypeScriptも説明コメントを必須にしているリポジトリがほとんどです。
  3. 筆者は自前のESLintコンフィグを育てるのにハマっています。https://github.com/berlysia/eslint-config