cordelia.dev

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. 1. ルールのメタデータを定義
  2. 2. 必要なノードを見つけてそれを検査する
  3. 3. 結果を返す


となります。

続いてプラグインのindex.jspackage.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