Node.js(ES6で実装)におけるESLint・Prettierの設定を1からやってみた
はじめに
こんにちは、SHIFT の開発部門に所属しているKatayamaです。
開発をしていく中に静的解析・構文チェックが常に走る状態になっておらず、開発後に一気に静的解析・構文チェックを実行すると、中々悲惨な事になってしまいます・・・。というわけで今回は JavaScript の開発をする上では必須とも言える静的解析・構文チェックツールである ESLint・prettier の設定を 1 からやってみたので、それについて書いていこうと思います。
合わせて、ESLint・prettier の設定に合わせた VS Code の設定も行ってみたので、それについても書いています。
※なお、今回 ESLint の設定を行うプロジェクトは、Node.js で ES Modules を利用して実装しているものにります。Node.js で ES Modules を利用するための設定についてはNode.js で import・export(ES6 の構文)を使えるように webpack × Babel の設定をやってみたを参照ください。
ESLint を設定する
JavaScript の静的解析と言えばESLintという感じなので ESLint で静的解析のルール設定をしていく。
以下での ESLint の実行対象コードは以下。
// src/index.js
import express from "express";
const app = express();
app.get("/", async (req, res) => {
const reqTime = Date.now();
console.log(Array.from("foo"));
await new Promise((resolve) => {
setTimeout(() => {
resolve("sleep");
}, 500);
});
res.status(200).send({
msg: "hello world!",
elaptime: Date.now() - reqTime,
});
});
app.listen(3000, () => console.log("listening on port 3000!"));
ESLint を PJ に導入し、init で初期設定を構築
はじめの導入はGetting Started with ESLintを見れば簡単にできる。
yarn add --dev eslint
yarn run eslint --init
"yarn run eslint --init" を実行すると、CLI 上で質問が出てきて初期の構成を作ってくれる。今回は一旦以下のように設定した。
初期設定時の ".eslintrc.json" は以下のようになる。ここからルールを変更していく。
// .eslintrc.json
{
"env": {
"es2021": true,
"node": true
},
"extends": ["airbnb-base"],
"parserOptions": {
"ecmaVersion": 13,
"sourceType": "module"
},
"rules": {}
}
※ちなみに今の状態で ESLint を実行すると、
[root@localhost node-express]# yarn lint
yarn run v1.22.17
$ eslint src/*
/root/workspace/node-express/src/index.js
7:3 warning Unexpected console statement no-console
21:24 warning Unexpected console statement no-console
✖ 2 problems (0 errors, 2 warnings)
Done in 1.38s.
のようになる。
・参考:ESLint 最初の一歩
結論:ESLint の設定内容と prettier の設定内容
今回設定した ESLint・prettier の内容全体としては以下の通り。
// .eslintrc.json
{
"root": true,
"env": {
"es2021": true
},
"extends": [
"eslint:recommended",
"airbnb-base",
"plugin:prettier/recommended"
],
"rules": {
"no-console": "off"
},
"ignorePatterns": ["/node_modules/", "/dist/"]
}
// .prettierrc
{
"singleQuote": true,
"useTabs": true,
"semi": true,
"bracketSpacing": true,
"arrowParens": "always",
"printWidth": 80,
"trailingComma": "none"
}
## 一部省略している
[root@localhost node-express]# tree -I 'node_modules|.git' -a
.
├── .eslintrc.json
├── .prettierrc
├── .vscode
│ └── settings.json
├── babel.config.js
├── package.json
├── src
│ └── index.js
├── webpack.config.js
└── yarn.lock
以下の項で、上記の設定の詳細についてみていく。
root: true
ESLint を特定のプロジェクトに制限するための設定。
ESLint はデフォルトでは公式に書かれている通り、ルートディレクトリまでのすべての親フォルダにある設定ファイルを探す。これはすべてのプロジェクトを特定の規則に従わせたい場合に便利だが、意図しないルール適用になったりするので、それを防止するために "root: true" として、ESLint を特定のプロジェクトに限定する(ESLint は "root": true で親フォルダまで探しに行くのを Stop させられる)。
env
静的解析の前提条件(JavaScript のコードが、ブラウザで動くコードである・ES6 である、など)を設定する事ができるオプション。 これを設定する意味としては、例えばブラウザだと
window.onload = function () {
alert("hello world!");
};
みたいなコードを書けてしまうが、ESLint からすれば "window" や "alert" はどこにも定義されていないから no-undef(未定義だからダメだよ)に引っかかってしまう。これを回避するには、Specifying Globalsというオプションでグローバル変数に定義済みなので宣言不要で使用可能なもの一覧を設定する事ができるので、これを設定するしかない。
// .eslintrc.json
{
"globals": {
"window": false,
"alert": false
}
}
がしかし、こんな事をイチイチやっていると日が暮れるので ESLint 側でも環境ごと(ブラウザとか node, es6 など)に使われるグローバル変数のリストを既に用意しており、どのリストを適用させるのか?= env の設定という仕組みになっている。
※ESLint が持っている Global の設定一覧は、environments.jsで "import globals from "globals";" として読み込まれているものと思われ、その globalsを見てみると、globals.jsonに各環境ごとの設定が書かれている。
今回は Node.js で ES6 の構文を使う+最新の ES2021 も使うかもしれないので、"node", "es2021" と設定する(globals.jsonを見る限り、ES2021 を設定しておけば、下位の es6 等のルールも含まれているので)。
"env": {
"es2021": true,
"node": true
}
と言いたいところだが、この後見ていく extends の設定で『airbnb-base』について解説するが、この "airbnb-base" に "node" は組み込み済みなのでここで明示的に宣言せずで問題ない(分かりやすさを考えて宣言するのはありだが、同じ事を書いている事になるので冗長な気もしたり…)。
なので、結論としては "es2021" だけ記載するようにする。
"env": {
"es2021": true
},
・参考:Specifying Environments
・参考:Specifying Parser Options
・参考:🌟 環境設定をする
・参考:webpack の基本的な使い方
・参考:.eslintrc の env 設定ってなんぞや?
parserOptions
今回は extends の "airbnb-base" が諸々いい感じに設定してくれるので設定不要だが、知識として以下は知っておく必要がありそうなので書いておく。
・ES Moduleの機能(簡単に言ってしまえば import/export のやつ)を静的解析でチェックするには、後に出てくる "parserOptions" の設定で "sourceType": "module" とする必要がある。
・(同じく parserOptions について)ecmaVersion という設定項目があるが、これは env で "es6: true" のように設定している場合、特に設定は不要。理由は公式に、
と書かれているように、env に "es6: true" と設定すると、自動的に parserOptions の ecmaVersion が 6 に設定されるため(es2017, es2020, es2021 も同様)
"parserOptions": {
// "ecmaVersion": 12, ← env の方で設定しているなら不要
"sourceType": "module"
}
extends
eslint の設定(.eslintrc.json などに書く設定)で、他の設定内容を継承させるための設定をする部分。
普通のプログラムの継承と同じように、env とか rules とか parserOptions とか、eslint の設定項目の各項目に対する設定を、他のファイルから読み込んで継承させられる。
例えば、今回 extends にしてしようとしている "airbnb-base"(eslint-config-airbnb-base)では、index.jsの方に、rules フォルダ内のパスが書かれており、その rules フォルダ内には、es6.jsというのがあるが、この中身を継承するので、env の "es6: true", parserOptions の "ecmaVersion: 6", "sourceType: 'module'" や、rules の各項目が、自分の ESLint の設定ファイル(.eslintrc.json)に書かれているのと同じ扱いになる。
※airbnb-base の詳細については『airbnb-base の中身』の項を参照。
今回は、eslint の方の recommended になっているルール(eslint:recommended)と、airbnb-base、さらにフォーマッタは prettier を使いたいので plugin:prettier/recommended の 3 つを継承させる。
※prettier の使い方は今までの使われ方から含めて『prettier について』の項に詳細を書いている。
・eslint:recommended
eslint の推奨ルールなので設定に組み込む
・airbnb-base
JavaScript の静的解析ルールとして厳しいと言われており、これで書いておくと良さげらしいので設定に組み込む
・prettier(eslint-config-prettier)
コードのフォーマッターである prettier のルールにコードスタイルを統一するために設定に組み込む
以下でそれぞれについて少し詳細を見ていく。
・参考:Extending Configuration Files
・参考:Using a configuration from a plugin
eslint:recommended の中身
ルールの中身全体はRulesに書かれている。
ソースコードとしてはeslint-recommended.jsが "eslint:recommended" の定義をしているものと思われる。
※eslint:recommended を始めとする eslint-config-_系の比較表としてはeslint-config-_ の比較表が分かりやすいと思う。
airbnb-base の中身
ルールの中身全体はAirbnb JavaScript Style Guideに書かれている。
ソースコードとしては、eslint-config-airbnb-baseの rules フォルダ内のものと思われる(『extends』の項で説明した通り)。
※ちなみに、より具体的にルールを見ていくと、『関数の引数にそのまま値を代入』の方はエラーになるが、Express の middleware を定義の場合はエラーにならない。これは、https://github.com/airbnb/javascript/blob/master/packages/eslint-config-airbnb-base/rules/best-practices.js#L226 の設定のおかげ。
// 関数の引数にそのまま値を代入
const foo = (bar) => {
bar = "text";
return bar;
};
foo();
// expressのmiddlewareの例
app.use((req, res, next) => {
req.time = Date.now();
next();
});
prettier について
今回はフォーマットルールに沿っているか?も静的解析でチェックをしたい(ESLintのルールとして検証する構成にしたい)ので plugin:prettier/recommended を extends に入れる(※合わせて『おまけ』の『ESLintとprettier』の項も参照ください)。
どのように設定するかだが、まず大前提として prettier のルールと衝突する ESLint やその他のルールは off にする必要がある。これはeslint-plugin-prettier の Recommended Configurationにも書かれている。
という事で、手順にある通りeslint-config-prettierを依存に追加する。
yarn add --dev eslint-config-prettier
ちなみに、この eslint-config-prettier の中身は、index.jsを見ると分かるが、単にいくつかのルールを off にしているだけ。
続いて、eslint-plugin-prettier を依存に追加する。
yarn add --dev eslint-plugin-prettier prettier
あとは、eslint-plugin-prettier の Recommended Configurationに書かれている通り、extends にplugin:prettier/recommendedを追記するだけ。
この plugin:prettier/recommended は、公式にも書いてあるように、単に
{
"extends": ["prettier"],
"plugins": ["prettier"],
"rules": {
"prettier/prettier": "error",
"arrow-body-style": "off",
"prefer-arrow-callback": "off"
}
}
と同じ事を自分の ESLint の設定に追加してくれるだけで、ソースコードで言えばeslint-plugin-prettier.jsがそれ。
これで ESLint に prettier のフォーマッティングルールの設定を組み込んだ状態になり、以下のように eslint コマンド時に prettier のエラーも出力されるようになる。
[root@localhost node-express]# yarn lint
yarn run v1.22.17
$ eslint src/*
/root/workspace/node-express/src/index.js
1:21 error Replace `"express"` with `'express'` prettier/prettier
5:9 error Replace `"/"` with `'/'` prettier/prettier
7:3 warning Unexpected console statement no-console
7:26 error Replace `"foo"` with `'foo'` prettier/prettier
11:15 error Replace `"sleep"` with `'sleep'` prettier/prettier
16:10 error Replace `"hello·world!"` with `'hello·world!'` prettier/prettier
21:24 warning Unexpected console statement no-console
21:36 error Replace `"listening·on·port·3000!"` with `'listening·on·port·3000!'` prettier/prettier
✖ 8 problems (6 errors, 2 warnings)
6 errors and 0 warnings potentially fixable with the `--fix` option.
※prettier の設定や ESLint の設定全体は『結論:ESlint の設定内容と prettier の設定内容』の項を参照。
※ちなみに、eslint-plugin-prettier だと以下のように赤線が出る...。なおこれは、extends に "plugin:prettier/recommended" を記載し、.prettierrc に "{ "singleQuote": true }" を記載した時の状況。
しかし、基本的に VS Code であれば、設定でソースの保存時には自動でフォーマッティングを走らせることができるので、新規で自分がコードを書いている時に赤線が出る以外は、この赤線は表示されることはないと思われ、そこまで煩わしくもないと思う。
※この ESLint・prettier を使う時の VS Code の設定については、『VS Code の設定』の項を参照。
・参考:How To Set Up ESLint & Prettier In VS Code
・参考:Prettier と ESLint の組み合わせの公式推奨が変わり plugin が不要になった
VS Code の設定
今回は ESLint の中で prettier のフォーマットルールに則っているか?のチェックもしているので、 "source.fixAll.eslint" + Extentions の ESLintの追加で最低限の設定にはなると思われる(prettier は package.json に追加してありそれが動く事が前提)。
その場合の setting.json は以下のようになる。
// ./.vscode/setting.json
{
"editor.codeActionsOnSave": {
"source.fixAll.eslint": true
}
}
※ただ、この状態だと、
・ESLint が走るファイル以外(json など)で prettier のフォーマッティングができない
といった事があるので、"editor.formatOnSave"+Extentions の Prettierを入れるという事も一つの手に思える。この場合、ソースコードの保存時にどちらが先に走るのか?が分からないのと、思わぬ副作用があるかもしれないので注意は必要かもしれない。
上記の方法でやる場合、Prettier の Extentions を追加した上で、 "editor.formatOnSave": true を追記する。setting.json は以下のようになる("editor.defaultFormatter" を設定しているが、ESLint のフォーマットチェックと同じ prettier なので、この設定がフォーマッティングのルールに衝突する事はないと思っている)。
// 両方書いた場合
{
"editor.defaultFormatter": "esbenp.prettier-vscode",
"editor.formatOnSave": true,
"editor.codeActionsOnSave": {
"source.fixAll.eslint": true
}
}
※ESLint のルール設定をしていて気づいたが、VS Code の Extentions で ESLint を入れて、コマンドではなくその Extentions を介して "source.fixAll.eslint" でフォーマッティングを走らせている場合、 ".prettierrc" の設定内容を変えてソースコードを保存しても変更後のルールでフォーマッティングされなかった...。しょうがないので ".prettierrc" を変えたら再起動するとちゃんと "source.fixAll.eslint" でフォーマッティングが自動修正されるようになった。
まとめとして
今回は ESLint と prettier の設定を 1 からやってみて、開発をする上での環境を整備してみた。少しさらに理解を深めていく必要がある部分があるが、基本的な設定はできたのではないかと思っている。
※上記内容で理解に誤りがある等ありましたらご指摘いただけると幸いです。
おまけ
ESLintとprettier
今回はeslint-plugin-prettierを使った設定を行ったが、この設定ではIntegrating with Lintersに書かれている通り、以下のようなデメリットもある。
この、
・フォーマッターなのに赤い線がエディタに出てしまって気になってしまう(本来フォーマットの事は忘れるべきなのに)
・prettier を直接実行するより遅い
という内容から、prettier としては、基本的には ESLint と prettier は分けて使われるべきという考え方をしているのだろうと思われる。そのため eslint-plugin-prettier ではなく、eslint-config-prettier を使って、prettier と衝突するような ESLint のルールを off にしつつ、フォーマットの事で(ESLint が prettier のフォーマットルール違反を検知して)警告がでないような形でフォーマッティングする(普通に prettier のコマンドでフォーマッティングする)という考え方をしているのだろうと思われる。
また、gitを使っていればGit hooksにあるような設定で prettier を実行することも可能だろう。
※ちなみに、ESLint と prettier を分けて考える場合の設定方法としては、公式にあるようにeslint-config-prettierを依存に追加(yarn add --dev eslint-config-prettier)し、"prettier"を extends の末尾に追記する。あとは、prettier のフォーマッティングルールを ".prettierrc" に記載し、後は以下のようなコマンドを実行するだけ(以下はprettier 本体の package.jsonを参考に書いたもの)。
// package.json
"scripts": {
"fix": "run-s fix:eslint fix:prettier",
"fix:eslint": "eslint --fix",
"fix:prettier": "prettier --write",
}
_________________________________
お問合せはお気軽に
https://service.shiftinc.jp/contact/
SHIFTについて(コーポレートサイト)
https://www.shiftinc.jp/
SHIFTのサービスについて(サービスサイト)
https://service.shiftinc.jp/
SHIFTの導入事例
https://service.shiftinc.jp/case/
お役立ち資料はこちら
https://service.shiftinc.jp/resources/
SHIFTの採用情報はこちら
https://recruit.shiftinc.jp/career/