Prettier や TypeScript を使うようになって、3年前に構築してぼちぼち保守してきた自分用の共有設定 eslint-config-mysticatea をそのまま使えないことが増えてきました。また、1年ほど前に Glob ベースで設定できるようになっている ので、この際がっつり書き直すことにしました。
ついでに、ESLint 共有設定を作る際のプラクティスのようなものがまとまっていないと感じたので、まとめてみます。改めて整理してみると、本当にバッドノウハウの塊だなぁ...。互換性に縛られずリライトしたいですね...。
新しい共有設定の紹介
新しい設定は @mysticatea/eslint-plugin にあります (eslint-config-mysticatea ではない)。
$ npm install --save-dev eslint @mysticatea/eslint-plugin
extends:
- plugin:@mysticatea/es2018 # es5, es2015, es2016, es2017, es2019 もある。目的に応じて。
中身は以下のようになっています。
- バグっぽいコードを見つけるために ESLint コアルールと eslint-plugin-eslint-comments のルールを有効にしています。
- 空白や改行については eslint-plugin-prettier 経由で Prettier を使っています。
-
.ts
ファイルでは typescript-eslint-parser と eslint-plugin-typescript のルールも有効にしています。 -
.vue
ファイルでは vue-eslint-parser と eslint-plugin-vue のルールも有効にしています。 -
test
ディレクトリの中は mocha のグローバル変数を有効にしています。 -
scripts
ディレクトリの中は Node.js 環境であるとします (eslint-plugin-node のルールも有効にして、.js
ファイルはスクリプト、.mjs
ファイルはモジュールとして扱う)。 -
.eslintrc.js
やwebpack.config.js
等の一部ファイルは Node.js 環境であるとします。
また、開発目的に応じて追加の設定を行うことができます。
extends:
- plugin:@mysticatea/es2018
- plugin:@mysticatea/+browser # ブラウザ向けのグローバル変数を定義する。
# ただし name, event, top 等、よく使うような名前は定義しない。
extends:
- plugin:@mysticatea/es2018
- plugin:@mysticatea/+node # 全体を Node.js 環境とする。
extends:
- plugin:@mysticatea/es2018
- plugin:@mysticatea/+eslint-plugin # eslint-plugin 開発向けの設定を追加する。
# eslint-plugin-node と eslint-plugin-eslint-plugin の
# ルールを追加。
extends:
- plugin:@mysticatea/es2018
- plugin:@mysticatea/+modules # ES modules のオプションと追加のルールを有効にする。
- plugin:@mysticatea/+browser
Glob-based な設定によって、いつも使っているディレクトリ構造について1行で設定できるようになりました。.vue
.ts
に関しても追加の設定なしで検証かけられて楽になりました。
共有設定のプラクティス
1. 共有設定機能を使わない
共有設定機能、つまり eslint-config-xxx
のことです。この機能は ESLint のプラグイン機構が設計されるより前に作られたため、プラグインと共に使おうとすると不都合が多いです。
例えば、プラグインのパッケージは
eslint
がrequire("eslint-plugin-xxx")
で読み込める位置にインストールする必要がありますが、共有設定のdependencies
にプラグインを追加しても期待通りにインストールできない場合が多いです。人気のある eslint-config-airbnb の Usage に複雑なインストール手順が書かれているのはそのため。
そこで、プラグイン機構 (eslint-plugin-xxx
) を共有設定機能の代わりに使うのがおすすめです。
プラグインは、以下のように configs
プロパティで共有設定を公開することができます。現在も多くのプラグインでは、これを利用して plugin:node/recommended
のような推奨設定を提供していますね。
module.exports = {
configs: {
fuga: {
// ここに設定を書く
}
},
rules: {},
}
extends:
- plugin:hoge/fuga
2. 他のプラグインを使うときはルールを再定義する
次の例は良くない例です。
module.exports = {
configs: {
fuga: {
plugins: ["node", "hoge"],
rules: {
"node/no-unsupported-features": "error",
},
},
},
rules: {},
}
eslint-plugin-node を使う設定が書かれているので、この設定を使う場合は、前項の eslint-config-airbnb のようにインストール手順で eslint-plugin-node もインストールするよう要請する必要があります。
代わりに、自身の中にルールを再定義するのがおすすめです。
const nodePlugin = require("eslint-plugin-node")
module.exports = {
configs: {
fuga: {
plugins: ["hoge"],
rules: {
"hoge/node/no-unsupported-features": "error",
},
},
},
rules: {
"node/no-unsupported-features": nodePlugin.rules["no-unsupported-features"],
},
}
こうすることで eslint-plugin-node プラグインを読み込む主体が ESLint から eslint-plugin-hoge
に変わるので、dependencies
に記述した他のプラグインを確実に利用できます。そのため、eslint-plugin-hoge
の利用者は eslint-plugin-hoge
(と eslint
本体) だけをインストールすればそれを使えるようになります。
これは、共有設定とプラグインに関する議論の中で ESLint の作者が提案した方法です (eslint/eslint#3458)。いわゆる菱形継承問題があって自動的に処理するのが難しいため、このような提案になっています。バッドノウハウ。
3. 基本設定と環境別の設定に分離する
JavaScript は様々な用途で使われています。Web サイト、Web アプリに、Node.js アプリ (ESLint もそのうちの1つ)、Electron アプリ、Windows Script Host (WSH) や AppleScript 等のオートメーションツール、などなど。用途によって必要な設定は変わってきます。また、同じ環境でも使用するフレームワーク・ライブラリによって有用な検証ルールを提供してくれていたりします。
そこで、共有設定を作る場合には環境によらない基本的な設定と、環境ごとに特化した設定を分けておくと良いです。
私の場合は ECMAScript 言語仕様の範疇にある検証ルールを plugin:@mysticatea/es2018
のような設定にまとめ、環境ごとに plugin:@mysticatea/+browser
, plugin:@mysticatea/+node
, plugin:@mysticatea/+eslint-plugin
のような追加設定を書いています。
eslint-config-airbnb では基本設定と React 用の追加設定とに分かれていますね。
4. テストを書く
作成した共有設定を保守していくために CI テストを書くと良いです。
-
eslint.CLIEngine
オブジェクトを作ってプラグインを読み込み、正常動作することを確認する。 - eslint-find-rules のようなツールを使って足りないルールを確認する。
などすると良いでしょう。
- プラグインを
CLIEngine
から読み込むためにはnode_modules/eslint-plugin-xxx
なディレクトリが必要なので、mysticatea/eslint-plugin/.eslintrc.js#L10-L19 のようにシンボリックリンクを作成してやると良いと思います。バッドノウハウ。- mysticatea/eslint-plugin/tests/lib/configs ではいろいろテストを書いたので、これを元に ConfigTester API を公開できたらいいな: eslint/eslint#10289
5. セルフホストする
プラグイン自身のコードの静的検証にそのプラグインを使えると格好いいです。
mysticatea/eslint-plugin/.eslintrc.js#L10-L19 のように、.eslintrc.js
ファイルでシンボリック リンクを作ると自分自身を使えたりします。