Git でコミットする前にコミット対象ファイルを整形したいことがあります。そして少々手間なことに、ファイルの種別によって実行環境やらなにやら個別に用意する必要があります。これをスクリプトによって適切に振り分けて解決したくなります。ざっくばらんに言えば lint-staged がやっていることをより自由度の高い形で実行したい、といった感じです。
okonet/lint-staged: 🚫💩 — Run linters on git staged files
Git でコミットする予定の変更されたファイルにのみコマンドを実行するのは次のコードでできます。
// gitのコマンドを実行
const gitStatusStdOut = childProcess.execSync('git status -s').toString();
// git status -sで次の様な文字列が標準出力されます
// A addTest.txt
// M app/Library/PDF/AppTcpdf.php
// M database/seeders/EventOrderSeeder.php
// M package.json
// これらの内 A, M が今回変更されて色々処理すべきファイルです。これを正規表現で抜き出します
const filePaths = gitStatusStdOut
.split("\n")
.map(line => line.match(/^\s*[AM] +(.*)$/)?.[1])
.filter(v => !!v)
;
// 対象のファイル一覧を表示。このファイルパスを元に色々できます。
console.log(filePaths)
// 以下は lint を docker 経由でかける例です。素の lint-staged では Windows の絶対パスが使われるので、docker に渡すのが手間です。
// package.json の中からこのスクリプト用のデータセット app-lint-staged を抜き出す
const p = JSON.parse(fs.readFileSync(path.join(__dirname, '..', 'package.json')).toString())
if (!p['app-lint-staged']) {
return;
}
Object.keys(p['app-lint-staged']).forEach(regexStr => {
// app-lint-staged中の設定に従ってファイルを振り分けて処理
// ex.
// "app-lint-staged": {
// ".*\\.php": [
// "docker-compose run --rm app ./vendor/bin/php-cs-fixer fix -vvv --config ./.php-cs-fixer.php",
// "git add"
// ]
// },
const regex = new RegExp(regexStr);
filePaths.forEach(filePath => {
if (!regex.test(filePath)) {
return;
}
p['app-lint-staged'][regexStr].forEach(cmdStr => {
console.log(`${cmdStr} "${filePath}"`)
// package.json 内で定義されたコマンドの末尾にファイルパスを追記してコマンドを実行
const res = childProcess.execSync(`${cmdStr} "${filePath}"`);
console.log(res.toString())
})
})
})
こんな感じで特定のファイルにのみコマンドを実行できます。後は husky なり simple-git-hooks なりで適宜自動実行するようにすれば、自由で快適な Git フックが作れます。
typicode/husky: Modern native Git hooks made easy 🐶 woof!
toplenboren/simple-git-hooks: A simple git hooks manager for small projects