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
リント対象のファイルです。
const str1 = "good";
const str2 = "bad";
ルールを定義します。
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 で確認できます。左側に解析したいコードを貼り付けると、右側にASTが表示されます。実際にコードを貼り付けてやってみてください。
ルールの処理をまとめると、
- ルールのメタデータを定義
- 必要なノードを見つけてそれを検査する
- 結果を返す
となります。
続いてプラグインのindex.js
とpackage.json
も作成します。
{
"name": "eslint-plugin-custom-rules",
"version": "1.0.0",
"private": true,
"main": "index.js"
}
eslint-plugin-custom-rules"
がプラグイン名です。
module.exports = {
rules: { "no-bad-literal": require("./no-bad-literal") },
};
no-bad-literal
がルール名です。
パッケージをプロジェクトにインストールします。
npm i -D ./src/lib/eslint-plugin-custom-rules
するとプロジェクトルートのpackage.json
はこの様になっているはずです。
{
// 一部抜粋
"devDependencies": {
"eslint": "^8.56.0",// ESLintのバージョンは状況により違うのでこの限りではありません。
"eslint-plugin-custom-rules": "file:src/lib/eslint-plugin-custom-rules",
},
}
次はESLintの設定ファイルを作成します。
// 今回の動作に必要な項目のみ記述しています。
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を使う方法もありますので、必要であればそちらも併せて情報を参照してください。
最後までお読みいただき、ありがとうございました。
参考情報
Discussion