[2023年]Next.js + eslint周りの設定
更新履歴
- 2024年3月31日 → npm scriptsについて追記しました。
- 2024年6月16日 → eslintの破壊的変更について追記しました。
- 2024年10月28日 → 「[2024年]Next.js(v15) + ESLint(v9 FlatConfig )周りの設定」の記事へのリンクを追加しました。
追記その2(2024年10月28日)
2024年版のESLint(v9 FlatConfig)に対応したバージョンの記事を書きました。
追記(2024年6月16日)
eslintのバージョンを上げたら、めちゃくちゃ設定ファイルに変更が入っています。
詳細は以下の記事が詳しいです。
これにそって、設定ファイルを修正する必要があります。
Flat Config Files
という設定方法に変わっていて、
書き方と読み込むライブラリが大幅に変わっています。
下は雰囲気をつかんでもらうためのサンプルです。(Next.jsのではないので注意!)
- 設定ファイル名は、eslint.config.{js,cjs,mjs} しかダメになった。
-
Flat Config Files
という書き方になった - ignoreの書き方が変わった(.eslintignoreは廃止)。
// Next.jsのではないので注意!
import js from '@eslint/js';
import tseslint from 'typescript-eslint';
import prettierConfig from 'eslint-config-prettier';
export default [
js.configs.recommended,
...tseslint.configs.recommended,
prettierConfig,
{
ignores: ['**/.*', 'wp-content/*', 'esbuild.config.js'],
},
{
rules: {
'no-const-assign': 'error',
'@typescript-eslint/no-unused-vars': 'warn',
},
},
];
あとは、.vscode/settings.json でflatConfigの設定が必要です。
{
"eslint.experimental.useFlatConfig": true
}
動機
リンター周りの設定で理解が曖昧な箇所があったので、
クリアにして諸々の設定を見直したい
と考えたからです。
リンターや、フォーマッターで他人の設定をコピペして設定している人も多いかと思います。
それだと応用が効かなかったり、古い設定をそのまま使用することになってしまいます。
方針
- 最小限の設定から足りないものを足していくスタイル
- 魔改造しない
- これはやっといた方がいいものだけに絞る
- 一個一個噛み砕いて、設定していく
成果物
こちらに成果物のリポジトリを用意しました。
記事を読むのは面倒な人はこちらからどうぞ。
commitは記事の順番にそっているので、記事の流れに沿って設定できます。
(1)Next.jsをインストール
npx create-next-app@latest --ts
上記コマンドを打てば、対話形式で設定できます。
appDirだけNoで後は全部Yesにしました。
自分で1から設定するのは大変なので、新規プロジェクトの場合はこちらのコマンドを使いましょう。不要なものは除いてあるので安心です。
"dependencies": {
"@types/node": "18.15.11",
"@types/react": "18.0.37",
"@types/react-dom": "18.0.11",
"autoprefixer": "10.4.14",
"eslint": "8.38.0",
"eslint-config-next": "13.3.0",
"next": "13.3.0",
"postcss": "8.4.22",
"react": "18.2.0",
"react-dom": "18.2.0",
"tailwindcss": "3.3.1",
"typescript": "5.0.4"
}
eslintでは初期からCore Web Vitalsに関する設定が入っています。設定されている
eslint-config-next は eslint-plugin-next の設定を読み込んで拡張しています。
// .eslintrcファイルは扱いやすくするために拡張子を.jsにして以下のように変更します。
/** @type {import('eslint').ESLint.ConfigData} */
module.exports = { extends: "next/core-web-vitals" }
これは、{"extends": "next"}
にCore Web Vitalsについてのルールを追加した拡張版になります
"next/core-web-vitals"
は以下を読み込んでいます。
これを紐解いていくと、
extends[0]
eslint-config-nextの設定を読み込んでいます
# extends[0]
require.resolve('.')
↓
/eslint-config-next/index.js
該当ファイル
next/recommendedに加えて、
react/recommended
react-hooks/recommended
も読み込んでいるので、これらのインストールは不要です。
extends[1]
eslint-plugin-nextのcore-web-vitalsの設定を読み込んでいます
# extends[1]
'plugin:@next/next/core-web-vitals'
↓
/eslint-plugin-next/src/index.ts
該当ファイルと読み込んでいる設定の箇所
補足
.eslintignoreファイルも作成しましょう
touch .eslintignore
node_modules はデフォルトでオフになっているようです。
lintから外したいファイルは設定しましょう。
# config
.eslintrc.js
prettier.config.js
next.config.js
tailwind.config.js
tsconfig.json
postcss.config.js
# build dir
build/
bin/
obj/
out/
.next/
参考
追記
ツイートしている方もおられますが、
fetchやlint結果のキャッシュが.next/
に残っていて開発の妨げになる時があるので、
以下を、npm scriptsに追記した方が良さそうです。
"scripts": {
"predev": "rm -fr .next",
"dev": "next dev",
windows環境だと動かないので、以下をインストールして
こちらの方が親切ですね。
"scripts": {
"predev": "rimraf .next",
"dev": "next dev",
(2)prettierをインストール
フォーマッターのprettierを入れましょう。最近はdemoのコードを書く時も入れてます。
自分でインデントや改行を編集するのは辛すぎるのと、他人のコードと差分が出てしまいやすいからです。
npm install -D prettier
eslintと設定が衝突する可能性があるので、以下もインストール
npm install -D eslint-config-prettier
.eslintrc.jsonを編集。
prettierは最後に設定しましょう。
/** @type {import('eslint').ESLint.ConfigData} */
module.exports = {
"extends": ["next/core-web-vitals" , "prettier"]
}
コマンドでフォーマットしてくれるように追加
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start",
"lint": "next lint",
"format": "prettier --write ."
},
以下は推奨されていないので、入れません。
eslint-plugin-prettier
設定ファイルを作成します。
touch prettier.config.js
この設定は度々宗教論争になりやすいです。(大体CTOとか偉い人が決めるのでしょうか?)
メンバーで合わせる事が重要なので、何が正解とかは無いですが、ここで強制することによって
(メンバーそれぞれの思惑はともかく笑)コードの一貫性を担保出来るのはいいですね。
文頭にあるコメントを入れると、プロパティの型の補完をエディタがしてくれて便利です。
/** @type {import('prettier').Config} */
module.exports = {
"semi": false, // セミコロン無し
"singleQuote": true, // シングルクォート使う
"printWidth": 90, // 折り返し
"tabWidth": 2, // スペースの単位
"trailingComma": "all", // ケツカンマつける
"jsxSingleQuote": true // JSXでシングルクォートを使用
}
.prettierignoreも作成します。
touch .prettierignore
node_modules/
はデフォルトで無視されています。
# Ignore artifacts:
build
coverage
yarn.lock
# dotfile
.env*
# markdown
*.md
# next.js
/.next/
/out/
# production
/build
# yarn
/.yarn/
参考
(3)IDEのeslintとprettierの拡張機能を入れる
私はVScodeを使用していますが、専用の拡張機能を入れています。
eslint
常時errorやwarningを出してくれます。
prettier
保存時に自動フォーマットが出来ます。
上記2つの拡張機能の設定をsetting.jsonに設定を加えます
editor.formatOnSave が保存時にフォーマット
editor.codeActionsOnSave.source.fixAll.eslint が保存時にlint --fix
editor.defaultFormatter でデフォルトのフォーマッターを決めています。
その下に言語ごとに設定を上書きできます。
{
"editor.formatOnSave": true,
"editor.codeActionsOnSave": {
"source.fixAll.eslint": true
},
"editor.defaultFormatter": "esbenp.prettier-vscode",
"[typescriptreact]": {},
"[typescript]": {},
"[jsonc]": {},
"[javascript]": {},
"[html]": {
"editor.defaultFormatter": "vscode.html-language-features",
"editor.formatOnSave": false
},
"[ejs]": {
"editor.formatOnSave": false
}
}
参考
プラグインのインストールの強要はあまりしたくないですが、上記2つは必須で入れといてほしい拡張機能です。なので、vscodeのrecommendationsに追加しましょう。
これを入れることで、メンバーに同じ拡張機能のインストールをしてもらうことが簡単に出来るようになります。
{
"recommendations": [
"dbaeumer.vscode-eslint",
"esbenp.prettier-vscode"
]
}
参考
(4)eslintで追加で必要なプラグインを設定する
eslint:recommended
現状だと、基本的なjavascriptのlintの設定が入っていません。
たとえば、constの再代入も通ってしまいます。
const x = 'abc'
x = 123 // pass !?
なので、eslintの推奨ルールを追加しましょう(eslintはインストール済みなので)
(eslint:allだと全部盛りなので画面が真っ赤になります)
/** @type {import('eslint').ESLint.ConfigData} */
module.exports = {
"extends": [
"eslint:recommended",
"next/core-web-vitals",
"prettier"
]
}
これで、以下の基本的なエラーも出るようになりました。
13:1 Error: 'x' is constant. no-const-assign
13:1 Error: 'x' is assigned a value but never used. no-unused-vars
19:3 Error: 'yy' is not defined. no-undef
typescript-eslint
現状、Javascriptのlintは入りましたが、Typescriptには対応していません。
eslintに対応させるにはこちらが必要になります。
npm i -D @typescript-eslint/parser @typescript-eslint/eslint-plugin
/** @type {import('eslint').ESLint.ConfigData} */
module.exports = {
"extends": [
"eslint:recommended",
"plugin:@typescript-eslint/recommended", // (A)
"plugin:@typescript-eslint/recommended-requiring-type-checking", // (B)
"next/core-web-vitals",
"prettier"
],
"parser": "@typescript-eslint/parser",
"parserOptions": {
"project": "./tsconfig.json"
},
"plugins": [
"@typescript-eslint"
],
"root": true
}
公式で推奨している 設定(A) を加えます。
なお、(A)の中では以下も読み込んでいて、
plugin:@typescript-eslint/recommended と機能がバッティングしている eslint:recommended の設定を切ってくれています。
型についてのルールを追加した 設定(B) も追加して、より厳密にします。
参考
(5)あるといいプラグインを追加する
prettier-plugin-tailwindcss
tailwindcssを使っている場合は、是非いれましょう。
cssのclassの並び替えを自動でやってくれます。
npm install -D prettier-plugin-tailwindcss
インストールするだけで自動ロードする環境としない環境があるようです(yarn, pnpm)。
その場合は明示的な設定が必要と書いてあるのでpluginsに追加しましょう。
他のプラグインを入れてカスタマイズ出来るみたいですが、デフォルトのままでも十分活躍してくれます。
2023/10/16 編集
pluginsの箇所で、require
使うと 新しいバージョンだとエラーになるのを修正
/** @type {import('prettier').Config} */
module.exports = {
+ plugins: ['prettier-plugin-tailwindcss'],
"semi": false,
"singleQuote": true,
"printWidth": 90,
"tabWidth": 2,
"trailingComma": "all",
"jsxSingleQuote": true
}
Tailwind CSS IntelliSense
拡張機能を入れれば、保存時に並び替え、classの補完、不正な値や重複に警告を出してくれます。
recommendationsにも追加しときましょう。
{
"recommendations": [
"dbaeumer.vscode-eslint",
"esbenp.prettier-vscode",
+ "bradlc.vscode-tailwindcss"
]
}
ちなみにvscodeのclass補完が遅い場合はこの設定で早くなりました
{
~
~
+ "editor.quickSuggestions": {
+ "strings": true
+ }
}
eslint-plugin-import
importとexportの部分のルールを設定して並び替えも行ってくれます。
importが増えてくると、ファイル上部がカオスなことになるので、こちらの設定もおすすめします。
npm install eslint-plugin-import --save-dev
モジュールの種類や、パスの種類で順番についてのルールを設定出来ます。
lint時や、エディタで設定しておけば保存時にも整形してくれます。
これは手動でやるとなかなか大変です。
設定の詳細は、公式リポジトリか参考記事をご確認ください。
/** @type {import('eslint').ESLint.ConfigData} */
module.exports = {
〜
plugins: ['import', '@typescript-eslint'],
〜
〜
rules: {
'import/order': [
'error',
{
groups: [
'builtin',
'external',
'internal',
['parent', 'sibling'],
'object',
'type',
'index',
],
'newlines-between': 'always',
pathGroupsExcludedImportTypes: ['builtin'],
pathGroups: [
{
pattern: '@/utils/**',
group: 'internal',
position: 'before',
},
{
pattern: '@/libs/**',
group: 'internal',
position: 'before',
},
{
pattern: '@/hooks/**',
group: 'internal',
position: 'before',
},
{
pattern: '@/components/**',
group: 'internal',
position: 'before',
},
{
pattern: '@/const/**',
group: 'internal',
position: 'before',
},
{
pattern: '@/types/**',
group: 'internal',
position: 'before',
},
],
alphabetize: {
order: 'asc',
},
},
],
},
}
参考
commit時にコードの検証をする
最後に、commit時にコードの検証と整形を自動で行う設定をします。
lint-staged
lint-stagedは、Gitでステージングされたファイルに対してリント(コードの品質とスタイルチェック)を実行するツールです。
huskyは
huskyは、Gitフックを簡単に設定し、特定のGitイベント(コミットやプッシュなど)が発生したときに自動的にタスクを実行できるようにするツールです。
処理の流れ
1. git commit
2. huskyがgitのイベントを検知
3. lint-staged が呼ばれ検証と整形を行う
設定
huskyの設定
こちらを参考にすすめます。
npm install --save-dev husky
husky のインストール
npm install --save-dev lint-staged
lint-staged のインストール
npx husky install
このコマンドでルートディレクトリに .huskyディレクトリが出来ます。
npm pkg set scripts.prepare="husky install"
package.jsonに husky installを追加します。
npm installが実行された場合、prepareスクリプトは自動的に実行されます。
npx husky add .husky/pre-commit "npx lint-staged"
Gitのpre-commitフックにlint-stagedを追加するためのHusky設定を行っています。
以下のファイルが自動で作成されて、commit時に lint-stagedが走ります。
#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"
npx lint-staged
lint-stagedの設定
通常のlint-stagedの設定ではnextjsでlintを設定するとエラーが起きてしまいます。
{
"lint-staged": {
"*": "your-cmd"
}
}
nextjs公式の設定の仕方があるので、そちらを参照して設定します。
touch .lintstagedrc.js
const path = require('path')
const buildEslintCommand = (filenames) =>
`next lint --fix --file ${filenames
.map((f) => path.relative(process.cwd(), f))
.join(' --file ')}`
module.exports = {
'*.{ts,tsx}': [buildEslintCommand],
}
さらにprettierの設定を追加します
const path = require('path')
const buildEslintCommand = (filenames) =>
`next lint --fix --file ${filenames
.map((f) => path.relative(process.cwd(), f))
.join(' --file ')}`
module.exports = {
'*.{ts,tsx}': [
buildEslintCommand,
+ "prettier --write"
]
}
上記の設定をすると
{.ts,.tsx}ファイルに対してnext lint
と prettier
をコミット時に行ってくれます。
これで完成!!と言いたい所ですが、まだ抜け穴があります。
型エラー(ts2322)などは拾ってくれません。
これはeslintのエラーではなく、tsのコンパイルエラーだからです。
このまま、push→ビルド→コケてエラーを知る、というのはよくあると思います。
なので、tscのコンパイルの検証の設定を入れます。
const path = require('path')
const buildEslintCommand = (filenames) =>
`next lint --fix --file ${filenames
.map((f) => path.relative(process.cwd(), f))
.join(' --file ')}`
module.exports = {
'*.{ts,tsx}': [
+ () => 'tsc --incremental false --noEmit',
buildEslintCommand,
'prettier --write',
],
}
-
--incremental
オプションは入っているとコンパイラは前回のビルドから変更されたファイルのみをコンパイルし、ビルド速度を大幅に向上させることができるオプションですが、入っているとルートディレクトリに キャッシュ用のtsconfig.tsbuildinfo
ファイルが出来てしまうので、切ります。 -
--noEmit
オプションは、コンパイラにコードの型チェックを実行するように指示しますが、ファイルの出力は行いません。 今回は検証だけ行いたいので、設定します。
これで、ts特有のエラーの時は、エラーを出るようになりました。
commit時にそれぞれの処理が走り、問題無ければ commitのコメント入力へ移動します。
SourcetreeのGUIからcommitを行うとエラーになってしまうので、
ターミナルのコマンドからcommitするようにしてください。
最後に
1から設定を見直して、とても勉強になりました。
この記事を読んで、開発環境の設定を見直すきっかけになれば幸いです。
Discussion