何をするのか
タイトル通りで、Next.jsでcommit
時にステージング(git add
)されているファイルにのみnext lint --fix
とprettier --write
を実行します。
ググれば日本語の記事でもやり方は沢山出てくるのですが、意外と落とし穴があったので今回はその詳細を書いていこうと思います。
※大前提として初めからeslintが導入されている11以降のバージョンを使用しましょう。
よくあるやり方
husky
とlint-staged
を入れて.husky/_/pre-commit
とpackage.json
で以下のように設定して、commit
する。
#!/bin/sh
. "$(dirname "$0")/_/husky.sh"
yarn lint-staged
{
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start",
"lint": "next lint --dir src",
"lint:fix": "next lint --fix --dir src",
"format": "prettier --write ./src/**/*",
"prepare": "husky install"
},
"dependencies": {
}
"devDependencies": {
},
"lint-staged": {
"*.(js|ts)?(x)": [
"yarn format",
"yarn lint:fix"
]
}
}
これの何が問題かというと、これだと該当する全てのファイルにリントとフォーマットが効いてしまいます。
今回はステージングされているファイルにのみ反映したいのです。
lint-staged
という名前ですが、そこら辺をよしなにやってくれたりはしないようです。
やり方
「よくあるやり方」の問題点ですが、.lintstagedrc.js
を使ってコマンドを動的に生成する事で解決します。
ただその前にそこまでの一通りの手順を記載いたします。
また今回はJavaScript Standard Style
を適用したいと思います。(ここはお好みで何でも良いです)
パッケージの導入
※全部一気にやってもいいですが、今回はわかりやすく分けてインストールしています。
まずNext.jsのプロジェクトが作成し終わったら、そのディレクトリで、
yarn add --dev eslint-config-standard eslint-plugin-promise eslint-plugin-import eslint-plugin-node
を実行して、JavaScript Standard Style
に必要なパッケージをインストールします。
次にコードフォーマッターのprettier
と、eslint
とprettier
のルールの競合を回避してくれるeslint-config-prettier
をインストールします。
yarn add --dev prettier eslint-config-prettier
そして最後にcommit
やpush
の前に特定の処理を挟んでくれるhusky
と、ステージングされているファイルの処理を行うためのlint-staged
をインストールします。
yarn add --dev husky lint-staged
パッケージの設定
次にeslint
とhusky
の設定を行います。
まずeslintrc.json
を以下のように編集します。
{
"extends": ["next/core-web-vitals", "standard", "prettier"]
}
このstandard
とprettier
はそれぞれJavaScript Standard Style
とeslint-config-prettier
の設定です。
またこの時の注意点ですがフォーマッターのルールはnext
より後に書きます。
次にhusky
の設定を行います。
以下のコマンドを実行してhusky
を初期化します。
npx husky-init && yarn
↑が実行されると、自動でルートディレクトリに.husky/_/pre-commit
が作成されるので、そのファイルを以下のように書き換えます。
#!/bin/sh
. "$(dirname "$0")/_/husky.sh"
yarn lint-staged
最後にlint-staged
の設定を行います。
ルートディレクトリに.lintstagedrc.js
を作成して、以下の内容を書き込みます。
module.exports = {
'*.(js|ts)?(x)': filenames => {
const rawFilePaths = filenames.map(file => {
const cwd = process.cwd()
const isMatchingPathFormat = file.includes(process.cwd())
const correctCwd = isMatchingPathFormat ? cwd : cwd.replace(/\\/g, '/')
const rawFilePath = file.replace(correctCwd, '.')
return rawFilePath
})
return [
`prettier --write ${rawFilePaths.join(' ')}`,
`next lint --fix --file ${rawFilePaths.join(' --file ')}`
]
}
}
以上でインストール&設定は完了です。
これでgit commit
すると、自動でステージングされているファイルにリントとフォーマットが効くようになりました。
.lintstagedrc.jsについて
まず.lintstagedrc.js
が何をやっているのかですが、return
で返される配列のコマンドをcommit
の前に実行してくれます。
つまり単純に
module.exports = {
'*.(js|ts)?(x)': filenames => [
'yarn format',
'yarn lint:fix'
]
}
のように書くと、ステージングされたファイルにjs
/jsx
/ts
/tsx
が存在すると、yarn format
とyarn lint
が実行されます。
ただしこれでは「よくあるやり方」で書いたように全てのファイルにリントとフォーマットが反映されてしまいます。
そのためステージングされているファイルにのみ反映するコマンドを引数filenames
を使用して生成する必要があります。
コード解説
このfilenames
ですが、こいつはステージングされているファイルの絶対パスを列挙した配列であり、中身は以下のようになっています。
[
'C:/Users/username/Desktop/my_project/.lintstagedrc.js',
'C:/Users/username/Desktop/my_project/next.config.js',
'C:/Users/username/Desktop/my_project/src/pages/members/index.tsx'
]
この配列をprocess.cwd
(ここではC:/Users/username/Desktop/my_project
が出力される)を使ってプロジェクトルートまでを.
に置換します。
const rawFilePaths = filenames.map(file => {
const cwd = process.cwd()
const isMatchingPathFormat = file.includes(process.cwd())
const correctCwd = isMatchingPathFormat ? cwd : cwd.replace(/\\/g, '/')
const rawFilePath = file.replace(correctCwd, '.')
return rawFilePath
})
[
'./.lintstagedrc.js',
'./next.config.js',
'./src/pages/members/index.tsx'
]
あとはこの配列を使ってお好みのコマンドが生成できます。
例えば上記のprettier --write ${rawFilePaths.join(' ')}
であれば、prettier --write ./.lintstagedrc.js ./next.config.js ./src/pages/members/index.tsx
となります。
注意点
※isMatchingPathFormat
とcorrectCwd
ですが、windowsの場合process.cwd()
の出力がC:\Users\username\Desktop\my_project
のようにスラッシュがバックスラッシュになってしまう事があるのでそれの調整をしています。