エンジニアの皆さん、こんにちは。現場でコードの海を渡り歩いていると、「このプロジェクト特有のルールを自動チェックしたいが、わざわざnpmパッケージを作って公開するほどでもない…」という場面に必ず直面しますよね。
これまで、そうした「プロジェクト内ローカルルール」を実装するには `eslint-plugin-local-rules` のようなサードパーティの依存関係に頼るのが常識でした。しかし、ESLint Flat Config (v9系〜) の時代になり、構成は劇的にシンプルになりました。わざわざ外部ライブラリを挟む必要はありません。
今日は、プロジェクトルートのJSファイルを直接プラグインとして読み込み、チームの「暗黙の了解」を強固なルールへと昇華させる、最もクリーンで実用的なアーキテクチャを伝授します。
—
なぜ「プラグインの自作」がアーキテクチャ的に正しいのか
多くの現場では、ESLintのルール違反をコードレビューで指摘し、修正を依頼するという「人間による再帰的なプロセス」が行われています。これはコストの塊です。
もし、プロジェクト固有のドメイン知識(例:「特定のコンポーネント内ではこの関数を呼んではいけない」など)をLintルールとして定義できれば、開発者の思考コストはゼロになり、CIは自動的に品質を担保してくれます。
今回紹介する手法は、単にJSファイルを読み込むだけですが、これにより「ルールをテストする」というエンジニアリングの基本が、Jestなどの既存テスト環境で驚くほど簡単に実行できるようになります。
—
実装ステップ:プロジェクト内プラグインの構築
1. ディレクトリ構造の設計
`eslint-rules/` というディレクトリをルートに作り、そこにルールを格納します。
/my-project
├── eslint.config.js # ESLintの設定ファイル
├── eslint-rules/ # ローカルルール用のディレクトリ
│ └── no-console-log.js # 今回作る独自のルール
└── package.json
2. ルールの実装 (`eslint-rules/no-console-log.js`)
ここでは、「`console.log` を禁止する」というシンプルなルールを定義します。ESLintのAST(抽象構文木)を操作する基本形です。
// eslint-rules/no-console-log.js
module.exports = {
rules: {
“no-console-log”: {
create(context) {
return {
CallExpression(node) {
// console.log が呼ばれたらエラーを報告
if (
node.callee.type === ‘MemberExpression’ &&
node.callee.object.name === ‘console’ &&
node.callee.property.name === ‘log’
) {
context.report({
node,
message: “本番コードへのconsole.logは禁止です。削除してください。”
});
}
}
};
}
}
}
};
—
ESLint Flat Configへの統合
ここが今回のハイライトです。`eslint.config.js` で、先ほどのJSファイルを `plugins` オブジェクトとして読み込みます。
// eslint.config.js
const localRules = require(“./eslint-rules/no-console-log”);
module.exports = [
{
plugins: {
// 任意の名前空間 ‘local’ を指定し、ルールセットを登録
local: localRules
},
rules: {
// ‘プラグイン名/ルール名’ で指定可能
“local/no-console-log”: “error”
}
}
];
これだけで、ESLintは `eslint-rules/no-console-log.js` をプロジェクト内の正規のプラグインとして認識します。
—
なぜこの手法が最強なのか
1. 依存関係の排除
`eslint-plugin-local-rules` のような外部ツールをインストールする必要がありません。依存関係が減れば、将来的なESLintのメジャーアップデート時にも壊れる可能性が低くなります。
2. テストの書きやすさ
これが最大の実利です。ルール自体が単なるJSモジュールなので、JestやVitestを使って以下のように簡単にテストできます。
// eslint-rules/no-console-log.test.js
const { RuleTester } = require(“eslint”);
const rule = require(“./no-console-log”).rules[“no-console-log”];
const ruleTester = new RuleTester({ languageOptions: { ecmaVersion: 2020 } });
ruleTester.run(“no-console-log”, rule, {
valid: [“console.info(‘hello’)”], // 通過するケース
invalid: [{ code: “console.log(‘debug’)”, errors: 1 }] // 失敗するケース
});
ルールを作ってテストを回すサイクルが、通常のコンポーネント開発と同じ感覚で行えるため、チームの規律を守るための「テスト駆動型Lint」が実現します。
—
現場のエンジニアへ:明日から使えるアクションプラン
1. 「いつも指摘していること」をリスト化する: コードレビューで3回以上指摘したことは、全てLintルール化の候補です。
2. まずは1つだけ作ってみる: 最初から巨大なルールセットを作らず、今回の `no-console-log` のような小さなルールから始めてください。
3. チームに共有する: これをリポジトリにコミットすれば、全員の環境でルールが自動的に適用されます。「レビュー指摘の自動化」こそが、開発効率を爆上げする最強の武器です。
複雑な設定から解放され、より本質的なロジックの実装に集中しましょう。皆さんのコーディングライフが、より洗練されたものになることを願っています!