これは、なにをしたくて書いたもの?
JavaScriptでのリンターといえばESLint、フォーマッターといえばPrettierだと思います。
ESLintにはコードスタイルに関するルールも含まれており、これがPrettierと競合することがあります。これをなんとかする場合は
eslint-config-prettierというものを使えばよいということを知ったので、試してみることにします。
ESLint
ESLintは、JavaScriptのリンターです。
Find and fix problems in your JavaScript code - ESLint - Pluggable JavaScript Linter
様々なルールがあり、コードの問題を見つけてくれます。
Rules Reference - ESLint - Pluggable JavaScript Linter
さらに、コードスタイルに関するルールも含まれています。
Formatters Reference - ESLint - Pluggable JavaScript Linter
TypeScriptで使うには、TypeScript ESLintというプラグインを使います。
Prettier
Prettierは、コードフォーマッターです。
Prettier · Opinionated Code Formatter
ドキュメントを見ると、JavaScript以外にもいろいろ対応しているようです。
- JavaScript (including experimental features)
- JSX
- Angular
- Vue
- Flow
- TypeScript
- CSS, Less, and SCSS
- HTML
- Ember/Handlebars
- JSON
- GraphQL
- Markdown, including GFM and MDX v1
- YAML
ESLintとPrettier
リンターにはコードの問題を探すだけではなく、コードスタイルに関するルールが含まれます。
こうなると、Prettierとリンターのコードスタイルに関するルールが競合する可能性があります。
これをどうしたらいいかは、Prettier側のドキュメントに回答があります。
Integrating with Linters · Prettier
リンターがESLintの場合は、eslint-config-prettierを使い、競合するESLint側のルールを無効にします。
今回は、こちらを試してみたいと思います。
環境
今回の環境は、こちら。
$ node --version v18.17.1 $ npm --version 9.6.7
TypeScript × Node.jsプロジェクトをセットアップする
今回は、TypeScriptとNode.jsでプロジェクトを作成したいと思います。
プロジェクトの初期化と、TypeScriptおよびNode.jsの型定義のインストール。
$ npm init -y $ npm i -D typescript $ npm i -D @types/node@v18
TypeScriptの設定ファイルの作成。
$ npx tsc --init
これを編集して、今回はこんな感じにしました。
tsconfig.json
{ "compilerOptions": { "target": "esnext", "module": "commonjs", "moduleResolution": "node", "lib": ["esnext"], "baseUrl": "./src", "outDir": "dist", "strict": true, "forceConsistentCasingInFileNames": true, "noFallthroughCasesInSwitch": true, "noImplicitOverride": true, "noImplicitReturns": true, "noPropertyAccessFromIndexSignature": true, "esModuleInterop": true }, "include": [ "src" ] }
この時点で、package.json
の依存関係はこんな感じです。
"devDependencies": { "@types/node": "^18.17.6", "typescript": "^5.1.6" }
次に、ESLintのセットアップをします。
Getting Started with ESLint - ESLint - Pluggable JavaScript Linter
$ npm init @eslint/config
コードの問題だけではなく、スタイルに関するルールも含めるように指定。
✔ How would you like to use ESLint? · style ✔ What type of modules does your project use? · commonjs ✔ Which framework does your project use? · none ✔ Does your project use TypeScript? · No / Yes ✔ Where does your code run? · node ✔ How would you like to define a style for your project? · guide ✔ Which style guide do you want to follow? · standard-with-typescript ✔ What format do you want your config file to be in? · JavaScript Checking peerDependencies of eslint-config-standard-with-typescript@latest The config that you've selected requires the following dependencies: eslint-config-standard-with-typescript@latest @typescript-eslint/eslint-plugin@^6.1.0 eslint@^8.0.1 eslint-plugin-import@^2.25.2 eslint-plugin-n@^15.0.0 || ^16.0.0 eslint-plugin-promise@^6.0.0 typescript@* ✔ Would you like to install them now? · No / Yes ✔ Which package manager do you want to use? · npm Installing eslint-config-standard-with-typescript@latest, @typescript-eslint/eslint-plugin@^6.1.0, eslint@^8.0.1, eslint-plugin-import@^2.25.2, eslint-plugin-n@^15.0.0 || ^16.0.0 , eslint-plugin-promise@^6.0.0, typescript@*
フォーマットのルールは、今回は標準的なものを使いました。
生成された設定ファイルはこちら。
.eslintrc.js
module.exports = { "env": { "commonjs": true, "es2021": true, "node": true }, "extends": "standard-with-typescript", "overrides": [ { "env": { "node": true }, "files": [ ".eslintrc.{js,cjs}" ], "parserOptions": { "sourceType": "script" } } ], "parserOptions": { "ecmaVersion": "latest" }, "rules": { } }
package.json
の依存関係はこうなりました。
"devDependencies": { "@types/node": "^18.17.6", "@typescript-eslint/eslint-plugin": "^6.4.0", "@typescript-eslint/parser": "^6.4.0", "eslint": "^8.47.0", "eslint-config-standard-with-typescript": "^38.0.0", "eslint-plugin-import": "^2.28.1", "eslint-plugin-n": "^16.0.1", "eslint-plugin-promise": "^6.1.1", "typescript": "^5.1.6" }
ちなみに、このままESLintを実行すると、以下のようなエラーに悩まされます。
Error: Error while loading rule '@typescript-eslint/dot-notation': You have used a rule which requires parserServices to be generated. You must therefore provide a value for the "parserOptions.project" property for @typescript-eslint/parser.
これを回避するには、.eslintrc.js
のparserOptions
にproject
を追加してtsconfig.json
へのパスを指定します。
"parserOptions": { "ecmaVersion": "latest", "project": "tsconfig.json" },
最後に、Prettierをインストールします。
$ npm i -D prettier
空の設定ファイルを作成。
$ echo {} > .prettierrc.json
package.json
の依存関係はこちら。
"devDependencies": { "@types/node": "^18.17.6", "@typescript-eslint/eslint-plugin": "^6.4.0", "@typescript-eslint/parser": "^6.4.0", "eslint": "^8.47.0", "eslint-config-standard-with-typescript": "^38.0.0", "eslint-plugin-import": "^2.28.1", "eslint-plugin-n": "^16.0.1", "eslint-plugin-promise": "^6.1.1", "prettier": "^3.0.2", "typescript": "^5.1.6" }
最後に、package.json
のscripts
にESLintとPrettierを実行できるように構成。
"scripts": { "lint": "eslint src --ext .js,.ts", "format": "prettier --write src", "test": "echo \"Error: no test specified\" && exit 1" },
これで準備完了です。
ESLintとPrettierで矛盾するルールを作る
まずは、こんなファイルを作ってみます。
src/index.ts
console.log("Hello World!!");
この時点で競合します。
フォーマット。
$ npm run format > eslint-with-prettier@1.0.0 format > prettier --write src src/index.ts 198ms
リント。
$ npm run lint > eslint-with-prettier@1.0.0 lint > eslint src --ext .js,.ts path/to/src/index.ts 1:13 error Strings must use singlequote @typescript-eslint/quotes 1:29 error Extra semicolon @typescript-eslint/semi ✖ 2 problems (2 errors, 0 warnings) 2 errors and 0 warnings potentially fixable with the `--fix` option.
ESLintで設定ファイルを作る時に選んだ、標準ルール(standard-with-typescript
)と競合しているようです。
eslint-config-prettierを導入する
ではどうしたらいいのかですが、eslint-config-prettierを導入します。
インストール。
$ npm i -D eslint-config-prettier
この時のpackage.json
の依存関係。
"devDependencies": { "@types/node": "^18.17.6", "@typescript-eslint/eslint-plugin": "^6.4.0", "@typescript-eslint/parser": "^6.4.0", "eslint": "^8.47.0", "eslint-config-prettier": "^9.0.0", "eslint-config-standard-with-typescript": "^38.0.0", "eslint-plugin-import": "^2.28.1", "eslint-plugin-n": "^16.0.1", "eslint-plugin-promise": "^6.1.1", "prettier": "^3.0.2", "typescript": "^5.1.6" }
インストール方法を見ると、ESLintの設定ファイルのextends
内の最後に追加するようです。
eslint-config-prettier / Installation
というわけで、このように変更します。
"extends": [ "standard-with-typescript", "prettier" ],
これで、ESLintがNGとならなくなりました。
$ npm run lint > eslint-with-prettier@1.0.0 lint > eslint src --ext .js,.ts
prettier
を最後に置くのは、設定を継承するextends
内で最も優先されるようにするためのようです。
ちなみに今回は使いませんでしたが、以下のようにrules
内に直接コードスタイルのルールを書くこともできます。
"rules": { "indent": [ "error", 4 ], "linebreak-style": [ "error", "unix" ], "quotes": [ "error", "double" ], "semi": [ "error", "always" ] }
ここに書いた場合は、eslint-config-prettierを使っても競合するルールを無効化することはできないみたいです。
でも、そういう場合はそもそもESLintで競合するルールを定義するなという話ですね…。
まとめ
ESLintのコードスタイルルールとPrettierを合わせて使う時には、eslint-config-prettierを使うと競合するルールをオフにできるということ
でした。
まあ、そもそもESLintの設定ファイルを作成する時にはコードスタイルルールを含めないようにできますし、デフォルトでは
構文とコードの問題に関するチェックになっているので、そもそも競合するルールを含めないように構成するのがよいんでしょうね。
? How would you like to use ESLint? … To check syntax only ▸ To check syntax and find problems To check syntax, find problems, and enforce code style
ひとまず、こういう回避方法があると覚えておくとよさそうです。