ESLintカスタムルールの作り方
JavaScript
Node.js
ESLint
はじめに
こんにちは!Cordeliaです。今回はESLintのカスタムルールの作り方についてご紹介します。プロジェクトのローカルパッケージとして作成し、それをプラグインとしてESLintに認識させる方法を取ります。参考になれば嬉しいです。
注意点
- ESLintの詳細は説明しません。
- TypeScriptは使いません。
ゴール
プロジェクトを作り、その中でESLintに警告を出してもらいます。
以下のコードにおいて`bad`という文字列リテラルが定義されていたら警告を出す簡単なルールを作成します。
// OK
const str1 = "good";
// NG
const str2 = "bad";最終的なプロジェクト構成
myapp/
├── node_modules
├── src/
│  ├── lib/
│  │  └── eslint-plugin-custom-rules/
│  │    ├── no-bad-literal.js
│  │    ├── index.js
│  │    └── package.json
│  └── app.js
├── .eslintrc.js
├── package-lock.json
└── package.json作成
まずはプロジェクトを作成します。
mkdir myapp
cd $_
npm init -y
npm i -D eslint
リント対象のファイルです。app.js
const str1 = "good";
const str2 = "bad";
ルールを定義します。no-bad-literal.js
module.exports = {
 meta: {
  type: "problem",
  docs: {
   description: "文字列リテラルの'bad'が使われていたら警告を出す",
  },
 },
 create(context) {
  return {
   Literal(node) {
    if (node.value && typeof node.value === "string") {
     if (node.value.includes("bad")) {
      context.report({
       node,
       message: "文字列リテラルに'bad'は使えません。",
      });
     }
    }
   },
  };
 },
};metaはこのルールのメタデータを定義し、create()ではソースコードを検証してその結果を返します。ここではリテラルの存在チェックをした後に、それがbadという文字列なら警告メッセージを表示するという処理をしています。
ESLintはソースコードをASTという形で解析しています。ASTとは、抽象木構文(Abstract Syntax Tree)のことで、ソースコードを扱いやすいよう加工したデータ構造のことです。加工後のデータはJSONデータになっていて上のコードで言えばLiteralがそれに当たり、これをノードと言います。ソースコード中のリテラル情報はこのLiteralが持っているんですね。
ノードはそれ自身がメソッドになっていて、引数に自身が保持するデータを受け取ります。これを検査する訳です。
以下は const str2 = "bad"; をASTに変換したものです。
これは [AST Explorer](https://astexplorer.net/) で確認できます。左側に解析したいコードを貼り付けると、右側にASTが表示されます。実際にコードを貼り付けてやってみてください。
ルールの処理をまとめると、
- 1. ルールのメタデータを定義
- 2. 必要なノードを見つけてそれを検査する
- 3. 結果を返す
となります。
続いてプラグインのindex.jsとpackage.jsonも作成します。
eslint-plugin-custom-rules/package.json
{
 "name": "eslint-plugin-custom-rules",
 "version": "1.0.0",
 "private": true,
 "main": "index.js"
}eslint-plugin-custom-rulesがプラグイン名です。eslint-plugin-custom-rules/index.js
module.exports = {
 rules: { "no-bad-literal": require("./no-bad-literal") },
};no-bad-literalがルール名です。
パッケージをプロジェクトにインストールします。
npm i -D ./src/lib/eslint-plugin-custom-rules
するとプロジェクトルートのpackage.jsonはこの様になっているはずです。
package.json
{
 // 一部抜粋
 "devDependencies": {
  "eslint": "^8.56.0",// ESLintのバージョンは状況により違うのでこの限りではありません。
  "eslint-plugin-custom-rules": "file:src/lib/eslint-plugin-custom-rules",
 },
}
次はESLintの設定ファイルを作成します。.eslintrc.js
// 今回の動作に必要な項目のみ記述しています。
module.exports = {
 env: {
  es2021: true,
 },
 plugins: ["custom-rules"],
 rules: {
  "custom-rules/no-bad-literal": "warn",
 },
};plugins: ["custom-rules"]は先ほどインストールしたeslint-plugin-custom-rulesからeslint-pluginの部分を省略した名前を記述し、"custom-rules/no-bad-literal"はcustom-rulesというパッケージのno-bad-literalというルールを使う、という意味です。
それではESLintを実行してみます。
npx eslint ./src/app.js/Users/cordelia/Projects/myapp/src/app.js
 2:14 warning 'bad'という文字列リテラルは使えません。 custom-rules/no-bad-literal
✖ 1 problem (0 errors, 1 warning)
この様な結果が表示されれば成功です🎉
おわりに
本来であればルールの動作確認の為にテストを作成しますが、今回は割愛しました。テストの作成についてや、その他詳しい情報は公式ドキュメントを参照してください。またカスタムルールは`eslint-plugin-local-rules`などのpackageを使う方法や、最新のESLintではFlat configを使う方法もありますので、必要であればそちらも併せて情報を参照してください。
最後までお読みいただき、ありがとうございました。
参考情報
https://eslint.org/docs/latest/extend/custom-rule-tutorial
上のチュートリアルはESLint最新のFlat configに伴うものですが、ルール作成の流れは掴めると思います。
https://ronvalstar.nl/custom-local-eslint-rules
https://zenn.dev/kazuwombat/articles/2a870356528783