ESLint Flat Config メモ
![pirosikick](https://res.cloudinary.com/zenn/image/fetch/s--ud-YokQP--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_70/https://storage.googleapis.com/zenn-user-upload/avatar/318cfcd2da.jpeg)
何回読んでもわからんので、メモりながら整理する。
![pirosikick](https://res.cloudinary.com/zenn/image/fetch/s--ud-YokQP--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_70/https://storage.googleapis.com/zenn-user-upload/avatar/318cfcd2da.jpeg)
設定ファイル
以下の3通り。
- eslint.config.js
- eslint.config.mjs
- eslint.config.cjs
プロジェクトのルートディレクトリに置く必要あり。
以下のようにexport default
で配列を返し、その中にオブジェクトで設定を並べていく。
export default [
{
rules: {
semi: "error",
"prefer-const": "error"
}
}
];
package.jsonのtypeフィールドが"module"
の場合は、eslint.config.jsは以下のようなCommonJSフォーマットである必要がある。
module.exports = [
{
rules: {
semi: "error",
"prefer-const": "error"
}
}
];
![pirosikick](https://res.cloudinary.com/zenn/image/fetch/s--ud-YokQP--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_70/https://storage.googleapis.com/zenn-user-upload/avatar/318cfcd2da.jpeg)
Configuration Objects
export default
で返す配列に含まれるオブジェクト(以降、「設定オブジェクト」)が持てるプロパティ一覧。
files
globパターンの配列。設定オブジェクトに記述された設定を反映したいファイルを指定。
filesがない場合、その設定オブジェクトに記述された設定は他の設定オブジェクトで指定されたすべてのファイルに対して反映される。
ignores
globパターンの配列。設定オブジェクトに記述された設定を反映したくないファイルを指定。
languageOptions
割愛。ecmaVersion、parserとかがある。
parserには文字列でなく(例えば、"@typescript-eslint/parser"
)、パーサー本体(parse関数 or parserForESLint関数があるオブジェクト)を渡す。
linterOptions
静的解析のプロセスに関する設定。
- noInlineConfig: falseにするとインラインの設定を無効にできる
- reportUnusedDisableDirectives: 不要なenable/disableの指定があった場合に教えてくれる
processor
プロセッサーの指定。プロセッサー本体か文字列(pluginName/processorName
)
plugins
Key-Value形式で有効にするプラグインを記述。
filesが指定されている場合は、そのファイルのみに有効。逆に言うとfilesが無い場合は、すべてのファイルに反映。
rules
ルールの指定。
files、ignoresが指定されている場合は、そのファイルのみに有効。逆に言うとfilesが無い場合は、すべてのファイルに反映。
settings
すべてのルールから参照できる設定。eslint-plugin-importとかでよく使うやつ。
モヤモヤポイント
- 評価順やマージされるのか否かが気になった。その辺はこの後に出てくるのだろう。
- プロセッサーってなんやねん
![pirosikick](https://res.cloudinary.com/zenn/image/fetch/s--ud-YokQP--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_70/https://storage.googleapis.com/zenn-user-upload/avatar/318cfcd2da.jpeg)
Specifying files and ignores
要点のみ、抜き出し。
- files, ignoresはminimatchのsyntaxで、eslint.config.jsからの相対パスで評価される。
- ESLintはデフォルトで
**/*.js
、**/*.cjs
、**/*.mjs
にマッチする - 前述の通りだが、filesがない場合は他の設定オブジェクトで指定されたファイル、他の設定オブジェクトにもfilesがない場合はデフォルトのファイルに対して設定が適応される
- Non-global ignores = filesと併用して指定される
ignores
- Non-global ignoresは、ファイル名にしかマッチしない。
"dir-to-exclude/"
みたいなディレクトリ指定してもマッチしないので注意。代わりに、"dir-to-exclude/**"
みたいな風に指定すべし
ignores
Globalなfilesなしでignoresを指定する場合の話。
- 追加した設定はデフォルトの
["**/node_modules/", ".git/"]
の後に追加される。 - 特定のディレクトリを無視する場合は、
"dir/*"
- 除外するには先頭に
!
を付ける。例えば、"!node_moduels/mylibrary/"
みたいな感じ - Globalな
ignores
は、ディレクトリにもマッチできる。
![pirosikick](https://res.cloudinary.com/zenn/image/fetch/s--ud-YokQP--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_70/https://storage.googleapis.com/zenn-user-upload/avatar/318cfcd2da.jpeg)
Cascading configuration objects
ファイルが1つ以上の設定オブジェクトにマッチする場合、設定はマージされる。
以下の設定では、"tests/**/*.js"
は1つ目の設定オブジェクトにもマッチするので、MY_CUSTOM_GLOBAL、it、describeの3つのグローバル変数を許可することになる。
export default [
{
files: ["**/*.js"],
languageOptions: {
globals: {
MY_CUSTOM_GLOBAL: "readonly"
}
}
},
{
files: ["tests/**/*.js"],
languageOptions: {
globals: {
it: "readonly",
describe: "readonly"
}
}
}
];
![pirosikick](https://res.cloudinary.com/zenn/image/fetch/s--ud-YokQP--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_70/https://storage.googleapis.com/zenn-user-upload/avatar/318cfcd2da.jpeg)
Configuring linter options
linterOptions
の設定項目に関する詳細。特に気になる点はないので、割愛。
ただ、この中にある2つの設定は結構便利そうなので、覚えていて損はなさそう。
![pirosikick](https://res.cloudinary.com/zenn/image/fetch/s--ud-YokQP--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_70/https://storage.googleapis.com/zenn-user-upload/avatar/318cfcd2da.jpeg)
Configuring language options
ESLintが対象のJSファイルをどう評価するかの設定。
Configuring the JavaScript version
ecmaVersion
で、ECMAScriptのバージョンを指定。有効なグローバル変数と文法を決定するのに利用。
指定できるのは、6
などのバージョン番号、2022
などの西暦かlatest
(ESLintがサポートする最も新しいバージョンを指定)。
export default [
{
files: ["**/*.js"],
languageOptions: {
ecmaVersion: 5
}
}
];
Configuring the JavaScript source type
JSファイルが、以下の3つのどれか指定する。
- ES Module =
"module"
- CommonJS =
"commonjs"
- Script =
"script"
デフォルトでは*.js
, *.mjs
を"module"
、*.cjs
を"commonjs"
として評価する。
export default [
{
files: ["**/*.js"],
languageOptions: {
sourceType: "script"
}
}
];
Configuring a custom parser and its options
パーサーとしてbabelやtypescript-eslintを使いたい場合に設定。
import babelParser from "@babel/eslint-parser";
export default [
{
files: ["**/*.js", "**/*.mjs"],
languageOptions: {
parser: babelParser
}
}
];
parserOptionsを使えば、パーサーにダイレクトにオプションを渡せる。
import babelParser from "@babel/eslint-parser";
export default [
{
files: ["**/*.js", "**/*.mjs"],
languageOptions: {
parser: babelParser,
parserOptions: {
requireConfigFile: false,
babelOptions: {
babelrc: false,
configFile: false,
// your babel options
presets: ["@babel/preset-env"],
}
}
}
}
];
Configuring global variables
グローバル変数に関する設定。書き込み可か読み取り専用か指定できる。
export default [
{
files: ["**/*.js"],
languageOptions: {
globals: {
var1: "writable",
var2: "readonly"
}
}
}
];
"off"
を指定すると、グローバル変数を無効にできる。
export default [
{
languageOptions: {
globals: {
Promise: "off"
}
}
}
];
Jestで使うグローバル変数を一括で定義するenv.jest = true
みたいなやつは、Flat Configで廃止。globalsを使って指定してね、とのこと。
import globals from "globals";
export default [
{
languageOptions: {
globals: {
...globals.browser
}
}
}
];
![pirosikick](https://res.cloudinary.com/zenn/image/fetch/s--ud-YokQP--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_70/https://storage.googleapis.com/zenn-user-upload/avatar/318cfcd2da.jpeg)
Using plugins in your configuration
Using plugin rules
以下、eslint-plugin-jsdocを設定する例。旧設定と違うのは
-
プラグイン名/ルール名
のプラグイン名
の部分に、任意の文字列を指定できるようになったこと。 - プラグイン本体を渡すようになったこと
import jsdoc from "eslint-plugin-jsdoc";
export default [
{
files: ["**/*.js"],
plugins: {
jsd: jsdoc
},
rules: {
"jsd/require-description": "error",
"jsd/check-values": "error"
}
}
];
Using configurations included in plugins
プラグインが提供している設定を使用する場合は、設定オブジェクトの配列の中に入れればよい。
import jsdoc from "eslint-plugin-jsdoc";
export default [
// configuration included in plugin
jsdoc.configs["flat/recommended"],
// other configuration objects...
{
files: ["**/*.js"],
plugins: {
jsdoc: jsdoc
},
rules: {
"jsdoc/require-description": "warn",
}
}
];
Using processors
プロセッサーは、例えばmarkdown中のコードブロックなど、テキストをコード群に変換するために使う。
以下、eslint-plugin-markdownの例。
import markdown from "eslint-plugin-markdown";
export default [
{
files: ["**/*.md"],
plugins: {
markdown
},
processor: "markdown/markdown",
settings: {
sharedData: "Hello"
}
}
];
プロセッサーは、各コードに0.js
、1.js
みたいな名前を付けるので、プロセッサーが処理したテキストファイル内のJSのコードに対して設定オブジェクトを反映したい場合、files: ["**/*.md/*.js"]
みたいに記述するとよい。
import markdown from "eslint-plugin-markdown";
export default [
{
files: ["**/*.md"],
plugins: {
markdown
},
processor: "markdown/markdown",
settings: {
sharedData: "Hello"
}
},
// applies only to code blocks
{
files: ["**/*.md/*.js"],
rules: {
strict: "off"
}
}
];
疑問点
- processorやpluginって新旧で互換性あるんだっけ?なんかあるっぽい気がするけど。
![pirosikick](https://res.cloudinary.com/zenn/image/fetch/s--ud-YokQP--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_70/https://storage.googleapis.com/zenn-user-upload/avatar/318cfcd2da.jpeg)
Configuring rules
rules
について。設定できる値(error
とかwarn
とか)については今までと大体同じっぽいので割愛。
Rule configuration cascade
同じルールが複数の設定オブジェクトで指定されている場合は、後勝ち。
export default [
{
rules: {
semi: ["error", "never"]
}
},
{
rules: {
semi: ["warn", "always"]. // こっちが適応される
}
}
];
ルールのseverty(warnとかerrorとか)のみ上書きして変更できる。以下の場合、semiは["warn", "never"]
を指定した状態になる。
export default [
{
rules: {
semi: ["error", "never"]
}
},
{
rules: {
semi: "warn"
}
}
];
![pirosikick](https://res.cloudinary.com/zenn/image/fetch/s--ud-YokQP--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_70/https://storage.googleapis.com/zenn-user-upload/avatar/318cfcd2da.jpeg)
Configuring shared settings
settings
について。今で通りなので割愛。
![pirosikick](https://res.cloudinary.com/zenn/image/fetch/s--ud-YokQP--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_70/https://storage.googleapis.com/zenn-user-upload/avatar/318cfcd2da.jpeg)
Using predefined configurations
ESLintが提供しているルールセットは以下の2つがある。@eslint/js
パッケージにあるので使ってね。
- js.configs.recommended
- js.configs.all
import js from "@eslint/js";
export default [
js.configs.recommended,
{
rules: {
semi: ["warn", "always"]
}
}
];
特定のファイルに効かせたい場合は、以下のように設定オブジェクトに展開すればいい。
import js from "@eslint/js";
export default [
{
files: ["**/src/safe/*.js"],
...js.configs.recommended
}
];
![pirosikick](https://res.cloudinary.com/zenn/image/fetch/s--ud-YokQP--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_70/https://storage.googleapis.com/zenn-user-upload/avatar/318cfcd2da.jpeg)
Configuration File Resolution
ESLintは、cwdをeslint.config.js -> eslint.config.mjs -> eslint.configの順で探す。探しても無ければ親ディレクトリで同じように探す。
この挙動を変えたい場合は、ESLINT_USE_FLAT_CONFIG=true と --config で指定する。
ESLINT_USE_FLAT_CONFIG=true npx eslint --config some-other-file.js **/*.js
![pirosikick](https://res.cloudinary.com/zenn/image/fetch/s--ud-YokQP--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_70/https://storage.googleapis.com/zenn-user-upload/avatar/318cfcd2da.jpeg)
typescript-eslintのconfigって何やってるの?
// @ts-check
import eslint from '@eslint/js';
import tseslint from 'typescript-eslint';
export default tseslint.config(
eslint.configs.recommended,
...tseslint.configs.recommended,
);
上記コードがおすすめされているが、tseslint.config
は何をやっているのか気になって仕方ないので調べる。
また、...tseslint.configs.recommended
も気になるので調べる。この書き方をしているということはrecommended
は設定オブジェクトの配列なわけで、何が入るのか気になる。
tseslint.config
ソースコードはこちら。
Utility function to make it easy to strictly type your "Flat" config file
なるほど、config関数を通してFlat Configを型チェックするのが目的なのか〜。// @ts-check
を付けることで、VS Code上で簡単にチェックできる、と。
加えて、本来設定オブジェクトにはないextends
を使えるように拡張されているみたい。
以下のような設定が、
export default [
{
...eslint.configs.recommended,
files: ['** /*.ts'],
},
...tseslint.configs.recommended.map(conf => ({
...conf,
files: ['** /*.ts'],
})),
{
files: ['** /*.ts'],
rules: {
'@typescript-eslint/array-type': 'error',
'@typescript-eslint/consistent-type-imports': 'error',
},
},
]
extends
があることによって以下のようにシンプルに記述できる。
export default tseslint.config({
files: ['** /*.ts'],
extends: [
eslint.configs.recommended,
tseslint.configs.recommended,
],
rules: {
'@typescript-eslint/array-type': 'error',
'@typescript-eslint/consistent-type-imports': 'error',
},
})
でも、これはtypescript-eslint
の役割として正しいのか?というと疑問。
![pirosikick](https://res.cloudinary.com/zenn/image/fetch/s--ud-YokQP--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_70/https://storage.googleapis.com/zenn-user-upload/avatar/318cfcd2da.jpeg)
tseslint.configs.recommendedの中身を見る。
recommended
ソースはこちら。
- 関数として定義しparser, pluginを引数で渡すようになっており、parserとpluginを疎結合にしている。
- 返り値の配列には、このファイルで定義されているrules以外に、baseConfig, eslintRecommendedConfigがある。
baseConfigは以下のような感じで、parserとpluginの設定を共通化するためのものと思われる。
eslintRecommendedのソースはこちら。
以下がだいぶ怪しい。。。てめえの責任で使ってね?を読み込むやつをGetting Startedに載せるんかーい。
import config from '@typescript-eslint/eslint-plugin/use-at-your-own-risk/eslint-recommended-raw';
eslintのrecommendedの中で役割が被っているものを無効にするのが目的っぽい。
![pirosikick](https://res.cloudinary.com/zenn/image/fetch/s--ud-YokQP--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_70/https://storage.googleapis.com/zenn-user-upload/avatar/318cfcd2da.jpeg)
@eslint/eslintrcのFlatCompatって何やってんの?
旧フォーマットを頑張ってFlat Configにしているという感じ。当たり前だけども。
ちゃんと読めば理解できそうだけど、なるべく使わない方がいい気がした。