Pre-commit hook dengan Husky dan lint-staged membantu menangkap masalah yang paling sering lolos ke pull request: format berantakan, error lint, dan perubahan kecil yang seharusnya bisa diperbaiki otomatis sebelum kode di-push. Dengan menjalankan pemeriksaan pada file yang berubah saja, feedback datang lebih cepat daripada menunggu pipeline CI.
Namun hook lokal bukan pengganti CI. Hook efektif untuk validasi cepat di mesin developer, sementara CI tetap diperlukan sebagai sumber kebenaran akhir karena hook bisa dilewati, lingkungan lokal bisa berbeda, dan beberapa pemeriksaan memang terlalu berat jika dijalankan di setiap commit. Artikel ini fokus pada setup yang praktis, cepat, dan cukup ketat untuk menjaga PR tetap bersih tanpa membuat developer frustrasi.
Masalah yang Diselesaikan oleh Pre-commit Hook
Tanpa pre-commit hook, masalah berikut sering muncul di pull request:
- Formatting tidak konsisten, misalnya spasi, kutip, trailing comma, atau urutan import.
- Error lint sederhana yang seharusnya bisa ditemukan sebelum kode dikirim.
- Perubahan kecil bercampur dengan noise, sehingga review menjadi lebih sulit.
- Feedback terlambat karena menunggu CI hanya untuk gagal pada aturan dasar.
Dengan Husky dan lint-staged, Anda bisa mengotomatisasi pengecekan saat git commit dijalankan. Husky bertugas memasang hook Git, sedangkan lint-staged menjalankan command hanya pada file yang sedang di-stage. Kombinasi ini cocok untuk menjaga kualitas dasar tanpa membebani seluruh repository pada setiap commit.
Kapan Hook Lokal Efektif, dan Kapan Tidak
Kapan efektif
- Untuk tugas cepat seperti Prettier, ESLint, atau validasi ringan pada file yang berubah.
- Pada tim yang ingin mengurangi PR dengan masalah kosmetik.
- Untuk monorepo atau proyek sedang-besar, selama command dibatasi hanya pada file staged.
Batasannya
- Bisa di-bypass, misalnya dengan
git commit --no-verify. - Lingkungan lokal tidak selalu sama dengan CI atau mesin anggota tim lain.
- Check berat seperti full test suite, build penuh, atau type check seluruh workspace sering terlalu lambat untuk pre-commit.
- Beberapa validasi membutuhkan konteks lintas file atau hasil build, sehingga lebih cocok dijalankan di pre-push atau CI.
Prinsip yang aman: gunakan pre-commit untuk fast feedback, dan gunakan CI untuk authoritative validation.
Arsitektur Singkat: Husky dan lint-staged Bekerja Bagaimana?
Husky menambahkan script hook Git, misalnya pre-commit. Saat Anda menjalankan git commit, Git akan memanggil hook tersebut.
lint-staged membaca daftar file yang sudah di-stage, lalu mengeksekusi command hanya untuk file yang relevan. Ini penting karena:
- lebih cepat daripada menjalankan lint ke seluruh codebase,
- lebih fokus pada perubahan yang benar-benar akan masuk ke commit,
- lebih mudah diterima tim karena tidak terasa berat.
Jika command mengubah file, lint-staged dapat menambahkan kembali hasil perubahan itu ke staging area, sehingga developer tidak perlu menjalankan langkah tambahan secara manual.
Instalasi Dasar di Proyek JavaScript/TypeScript
Berikut contoh setup umum pada proyek Node.js dengan npm. Jika Anda memakai pnpm atau yarn, sesuaikan perintah instalasinya, tetapi konsepnya tetap sama.
npm install -D husky lint-staged eslint prettier typescriptJika proyek Anda sudah punya ESLint, Prettier, atau TypeScript, tentu tidak perlu memasangnya lagi.
Selanjutnya aktifkan Husky:
npx husky initPerintah ini biasanya membuat folder .husky dan file hook awal. Setelah itu, pastikan ada script untuk menyiapkan Husky ketika dependency diinstal. Contoh di package.json:
{
"scripts": {
"prepare": "husky"
}
}Script prepare berguna agar hook tetap terpasang setelah npm install, termasuk saat clone proyek di mesin baru.
Konfigurasi Pre-commit Hook dengan lint-staged
Opsi 1: Simpan konfigurasi di package.json
Untuk setup sederhana, Anda bisa menaruh konfigurasi langsung di package.json:
{
"scripts": {
"prepare": "husky",
"lint": "eslint .",
"format": "prettier --write .",
"typecheck": "tsc --noEmit"
},
"lint-staged": {
"*.{js,jsx,ts,tsx}": [
"eslint --fix",
"prettier --write"
],
"*.{json,md,css,scss,yml,yaml}": [
"prettier --write"
]
}
}Lalu isi hook .husky/pre-commit agar menjalankan lint-staged:
npx lint-stagedKonfigurasi ini berarti:
- File JavaScript/TypeScript yang di-stage akan diperiksa dengan ESLint dan diformat dengan Prettier.
- File seperti JSON, Markdown, CSS, dan YAML hanya diformat dengan Prettier.
- Hanya file yang berubah yang diproses, bukan seluruh repository.
Opsi 2: Gunakan file terpisah
Jika konfigurasi mulai panjang, file terpisah lebih mudah dirawat. Contoh .lintstagedrc.json:
{
"*.{js,jsx,ts,tsx}": [
"eslint --fix",
"prettier --write"
],
"*.{json,md,css,scss,yml,yaml}": [
"prettier --write"
]
}Pendekatan ini lebih nyaman untuk tim karena file konfigurasi tidak bercampur dengan script aplikasi di package.json.
Integrasi dengan ESLint, Prettier, dan Type Check Ringan
ESLint untuk error dan auto-fix
ESLint cocok dijalankan di pre-commit karena banyak aturan bisa diperbaiki otomatis dengan --fix. Namun pastikan rule yang dipasang memang relevan. Jika terlalu banyak rule mahal atau terlalu ketat, commit akan terasa lambat.
Untuk proyek TypeScript, pastikan konfigurasi ESLint tidak memaksa analisis yang terlalu berat pada setiap commit jika memang tidak diperlukan. Beberapa setup lint berbasis type information bisa terasa lebih lambat dibanding lint biasa.
Prettier untuk konsistensi output
Prettier adalah kandidat ideal untuk pre-commit karena:
- cepat,
- deterministik,
- mengurangi debat gaya penulisan di review code.
Jalankan Prettier setelah ESLint jika Anda ingin hasil akhir file tetap rapi. Dalam banyak kasus, kombinasi eslint --fix lalu prettier --write memberi hasil yang konsisten.
Type check ringan: hati-hati memilih tempatnya
Type check penuh dengan tsc --noEmit sering berguna, tetapi tidak selalu ideal di pre-commit, terutama pada proyek besar. Ada beberapa strategi yang lebih realistis:
- Proyek kecil/menengah: jalankan
tsc --noEmitdi pre-commit jika waktunya masih masuk akal. - Proyek besar: pindahkan type check penuh ke pre-push atau CI.
- Fallback aman: tetap jalankan lint dan format di pre-commit, lalu jalankan type check penuh di CI.
Contoh jika Anda tetap ingin menambahkan type check ke pre-commit:
#!/usr/bin/env sh
npx lint-staged
npm run typecheckTetapi gunakan ini hanya jika durasinya tidak mengganggu workflow. Jika developer harus menunggu lama untuk commit kecil, mereka cenderung mencari cara untuk mem-bypass hook.
Strategi Menjalankan Check Hanya pada File yang Berubah
Inilah kekuatan utama lint-staged. Daripada menjalankan command seperti eslint . untuk seluruh repository, lint-staged meneruskan daftar file staged ke command yang sesuai. Hasilnya:
- commit lebih cepat,
- resource lebih hemat,
- perubahan kecil tidak memicu proses besar.
Contoh pola file yang umum:
{
"*.{js,jsx,ts,tsx}": [
"eslint --fix",
"prettier --write"
],
"*.{md,json,yml,yaml}": [
"prettier --write"
]
}Ada satu hal penting: tidak semua tool benar-benar cocok dijalankan per-file. Misalnya, type check TypeScript pada umumnya membutuhkan konteks proyek, bukan hanya satu file. Karena itu, strategi terbaik adalah:
- gunakan lint-staged untuk tool yang memang aman dijalankan pada file berubah,
- gunakan CI untuk validasi global yang bergantung pada keseluruhan proyek.
Contoh Setup yang Seimbang untuk Tim
Berikut contoh konfigurasi yang cukup aman untuk banyak tim frontend atau full-stack JavaScript/TypeScript:
{
"scripts": {
"prepare": "husky",
"lint": "eslint .",
"format": "prettier --write .",
"typecheck": "tsc --noEmit",
"test": "npm run test:unit"
},
"lint-staged": {
"*.{js,jsx,ts,tsx}": [
"eslint --fix",
"prettier --write"
],
"*.{json,md,css,scss,html,yml,yaml}": [
"prettier --write"
]
}
}Isi file .husky/pre-commit:
npx lint-stagedLalu di CI, tetap jalankan validasi penuh:
npm run lintnpm run typechecknpm test
Pola ini biasanya cukup efektif: lokal untuk feedback cepat, CI untuk verifikasi final.
Praktik Tim agar Hook Tetap Cepat dan Tidak Dibenci
1. Fokus pada check yang cepat
Masukkan hanya validasi yang memberi nilai tinggi dengan waktu rendah. Prettier dan ESLint dengan scope file staged biasanya pilihan terbaik. Hindari build penuh, integrasi test, atau script berat lain di pre-commit kecuali benar-benar cepat.
2. Jadikan CI sebagai fallback wajib
Jangan bergantung penuh pada hook lokal. Semua rule penting tetap harus dijalankan di CI karena:
- hook bisa dilewati,
- tidak semua developer menjalankan environment identik,
- beberapa validasi hanya benar jika dieksekusi penuh.
3. Sepakati kapan bypass boleh dilakukan
git commit --no-verify kadang dibutuhkan, misalnya saat hook rusak atau saat sedang menangani insiden. Tetapi bypass tidak boleh menjadi kebiasaan. Praktik yang umum:
- boleh bypass hanya untuk kondisi darurat,
- harus diperbaiki segera setelah itu,
- CI tetap memblokir merge jika validasi penting gagal.
4. Simpan konfigurasi sesederhana mungkin
Semakin rumit hook, semakin sulit di-debug. Prioritaskan command yang jelas, sedikit, dan dapat dijalankan manual saat troubleshooting.
5. Dokumentasikan cara kerjanya
Tambahkan penjelasan singkat di README atau dokumentasi internal:
- apa yang dijalankan saat commit,
- cara menginstal hook,
- cara menjalankan command yang sama secara manual,
- cara menangani error umum.
Kesalahan Umum yang Membuat Developer Frustrasi
Hook terlalu lambat
Ini penyebab paling umum. Jika satu commit kecil butuh waktu lama, developer akan kehilangan fokus. Solusinya:
- batasi ke file staged,
- hapus check berat dari pre-commit,
- pindahkan validasi global ke CI atau pre-push.
Menjalankan lint ke seluruh repo dari pre-commit
Kesalahan ini sering terjadi ketika hook berisi npm run lint yang sebenarnya memeriksa semua file. Secara teknis bisa jalan, tetapi manfaat lint-staged jadi hilang.
Urutan command tidak dipikirkan
Jika formatter dan linter sama-sama mengubah file, urutannya penting. Banyak tim memilih:
eslint --fixprettier --write
Tujuannya agar perbaikan lint diterapkan dulu, lalu format akhir dirapikan secara konsisten.
Hook gagal karena command tidak tersedia
Ini biasanya terjadi jika tool diinstal global di satu mesin tetapi tidak ada di mesin lain. Gunakan dependency proyek dan panggil lewat npx atau script npm agar konsisten antar developer.
File berubah setelah commit gagal dan staging jadi membingungkan
Karena lint-staged bisa memodifikasi file, beberapa developer bingung saat commit gagal lalu melihat file ikut berubah. Ini bukan bug. Artinya formatter atau auto-fix bekerja, tetapi ada error lain yang masih harus diselesaikan. Biasanya langkah aman adalah:
- baca output error,
- cek file yang diubah otomatis,
- stage ulang jika perlu,
- commit kembali.
Type check dipaksa di semua commit meski terlalu berat
Secara teori bagus, tetapi dalam praktik sering menjadi bottleneck. Jika type check penuh memakan waktu lama, pindahkan ke CI. Jangan memaksakan validasi ideal jika efek sampingnya membuat tim sering mem-bypass hook.
Tips Debugging Saat Hook Gagal
- Jalankan command yang sama secara manual, misalnya
npx lint-stagedataunpm run typecheck, untuk melihat error lebih jelas. - Periksa isi file hook di
.husky/pre-commitagar tidak ada command yang salah atau path yang tidak valid. - Pastikan dependency terinstal di
devDependencies, bukan mengandalkan instalasi global. - Cek file yang di-stage dengan
git diff --cached --name-onlyjika terasa ada file yang tidak ikut diproses. - Uji pola glob pada konfigurasi lint-staged. Kadang command tidak jalan karena ekstensi file tidak cocok dengan pattern.
Kapan Perlu Menambah Hook Lain selain Pre-commit?
Jika tim Anda ingin validasi sedikit lebih kuat tanpa membebani setiap commit, pertimbangkan pemisahan tanggung jawab:
- pre-commit: format dan lint file staged.
- pre-push: type check penuh atau unit test yang relatif cepat.
- CI: validasi lengkap, termasuk test suite penuh, build, dan pengecekan lint seluruh proyek.
Pemisahan ini sering lebih sehat daripada menumpuk semua proses ke satu hook.
Checklist Implementasi
- Pasang husky dan lint-staged sebagai dev dependency.
- Aktifkan Husky dan pastikan script
prepareada dipackage.json. - Buat hook
pre-commityang menjalankannpx lint-staged. - Konfigurasikan lint-staged untuk hanya memproses file staged.
- Tambahkan ESLint dengan
--fixpada file JavaScript/TypeScript. - Tambahkan Prettier untuk format otomatis pada file yang relevan.
- Evaluasi apakah type check cukup cepat untuk pre-commit; jika tidak, pindahkan ke pre-push atau CI.
- Pastikan CI tetap menjalankan lint, type check, dan test penuh.
- Dokumentasikan cara kerja hook dan cara troubleshooting di README.
- Sepakati kebijakan tim terkait
--no-verifyagar bypass tidak menjadi kebiasaan.
Jika diterapkan dengan scope yang tepat, pre-commit hook dengan Husky dan lint-staged memberi keuntungan nyata: PR lebih bersih, feedback lebih cepat, dan review bisa fokus pada logika bisnis, bukan masalah formatting atau lint yang seharusnya sudah selesai sebelum kode dikirim.
Komentar
0 komentar
Masuk ke akun kamu untuk ikut berkomentar.
Belum ada komentar
Jadilah yang pertama ikut berdiskusi!