Formatting adalah tata letak kode: indentasi, spasi, baris kosong, tanda kutip, titik koma, panjang baris. Ia gak mengubah apa yang dikerjakan program, tapi menentukan seberapa cepat orang membacanya dan seberapa bersih diff (daftar baris yang berubah) saat review. Bayangin tiga kasir Toko Kaos Raka yang menulis nota dengan tangan: Sari menulis Rp75.000, Budi 75rb, Dewi 75.000,-. Semuanya benar, tapi satu angka yang salah tenggelam di antara perbedaan gaya itu. Begitu toko memakai mesin struk, semua nota formatnya sama, dan mata Raka langsung jatuh ke angka yang beda. Formatter adalah mesin struk itu untuk kode.
🧪 Tebakan cepat: PR #16 berjudul "Rapikan format kasir.js", dan
git diff --statbilang 14 baris berubah. Berapa baris yang mengubah perilaku kasir: nol, satu, atau empat belas? Tebak dulu, jawabannya ada di bagian "Satu baris di antara 14".
PR #16: "cuma format"
Deskripsi PR: "Rapikan format kasir.js, gak ada perubahan logika." Editor penulisnya diatur 4 spasi dan kutip ganda, dan waktu file disimpan, editornya menulis ulang semuanya. Potongan diff-nya (- baris lama, + baris baru):
function addShipping(amount) {
- if (amount < FREE_SHIPPING_MIN) return amount + SHIPPING_FEE;
- return amount;
+ if (amount <= FREE_SHIPPING_MIN) return amount + SHIPPING_FEE;
+ return amount;
}
function formatRupiah(amount) {
- return 'Rp ' + amount.toLocaleString('id-ID');
+ return "Rp " + amount.toLocaleString("id-ID");
}
Begitu terus dari atas sampai bawah: 1 file changed, 14 insertions(+), 14 deletions(-). Tiap baris di dalam fungsi berubah karena indentasinya, tiap teks karena tanda kutipnya. Reviewer yang melihat 28 baris merah-hijau dengan pola sama biasanya menyimpulkan "cuma format", lalu approve.
Satu baris di antara 14
Format file PR itu dengan aturan tim (caranya di bagian berikut), lalu bandingkan lagi dengan main:
function addShipping(amount) {
- if (amount < FREE_SHIPPING_MIN) return amount + SHIPPING_FEE;
+ if (amount <= FREE_SHIPPING_MIN) return amount + SHIPPING_FEE;
return amount;
}
Tinggal satu baris: < jadi <=. Itu jawaban tebakannya. Keempat keranjang test tetap OK, karena gak ada yang totalnya pas Rp 200.000. Keranjang E, 5 Topi Raka (5 × 40.000), yang membongkarnya:
OK Total: Rp 220.500
OK Total: Rp 245.000
OK Total: Rp 90.000
OK Total: Rp 204.000
BEDA Total: Rp 215.000, harusnya Total: Rp 200.000
Belanja pas Rp 200.000 dulu gratis ongkir, sekarang kena Rp 15.000. Disengaja atau salah ketik, ia harus kelihatan saat review, dan perubahan aturan ongkir layak punya PR sendiri dengan test. Format yang gak seragam bukan cuma jelek: ia menyembunyikan perubahan yang nyata.
Prettier: format diputuskan alat
Prettier adalah formatter: ia membaca kode jadi struktur (pohon sintaks), membuang tata letak lamanya, lalu mencetak ulang dengan aturannya sendiri. Ia sengaja opinionated: opsinya sedikit, supaya gak ada yang perlu diperdebatkan. Tim Raka cuma menulis satu keputusan di .prettierrc: { "singleQuote": true }. Sisanya bawaan: indentasi 2 spasi, titik koma, lebar baris 80 karakter (printWidth). Kode berantakan ini (baris let diawali tab):
const cartD = [{ name: "Kaos Polos Hitam", price: 75000, qty: 1 }, { name: "Kaos Sablon Kopi", price: 95000, qty: 1 }, { name: "Topi Raka", price: 40000, qty: 1 }]
function calculateTotal(cart,voucherCode){
let total=calculateSubtotal(cart)
total = applyVoucher(total,voucherCode);
total=addShipping( total )
return "Total: "+formatRupiah(total)}
Setelah npx prettier --write kasir.js, persis seperti yang dicetak Prettier 3:
const cartD = [
{ name: 'Kaos Polos Hitam', price: 75000, qty: 1 },
{ name: 'Kaos Sablon Kopi', price: 95000, qty: 1 },
{ name: 'Topi Raka', price: 40000, qty: 1 },
];
function calculateTotal(cart, voucherCode) {
let total = calculateSubtotal(cart);
total = applyVoucher(total, voucherCode);
total = addShipping(total);
return 'Total: ' + formatRupiah(total);
}
- Tab, 4 spasi dan 2 spasi jadi 2 spasi; spasi di sekitar
=,+dan setelah koma diseragamkan; titik koma ditambah; kutip ganda jadi tunggal. - Baris
cartD163 karakter, lewat dari 80, jadi dipecah per barang, dengan koma setelah barang terakhir. - Dua baris kosong jadi satu. Tapi baris kosong sebelum
returnbertahan: Prettier merapatkan baris kosong, gak menambah. Letaknya keputusanmu. Pakai seperti paragraf, satu baris kosong di antara fase yang berbeda (periksa, hitung, simpan), supaya pembaca bisa memindai strukturnya sekilas; jangan di tiap baris.
Prettier gak bisa mengubah <= balik jadi <: ia cuma mencetak ulang struktur yang sama, arti kode gak ia sentuh. Di CI (Continuous Integration, pemeriksaan otomatis tiap push), npx prettier --check . gagal selama ada file yang belum rapi:
Checking formatting...
[warn] kasir.js
[warn] Code style issues found in the above file. Run Prettier with --write to fix.
ESLint: yang bukan soal tampilan
ESLint adalah linter: ia gak mengurus tata letak, tapi mencari pola yang kemungkinan besar bug. Potongan kasir-lama.js dari lesson sebelumnya, diperiksa dengan js.configs.recommended plus aturan eqeqeq di eslint.config.js:
export function applyVoucher(t, v) {
if (v == 'HEMAT10') {
t = t - t * 0.1;
}
return t;
}
function hitungDiskonLama(t) {
return t * 0.15;
}
$ npx eslint kasir-lama.js
/home/raka/toko-raka/kasir-lama.js
2:9 error Expected '===' and instead saw '==' eqeqeq
8:10 error 'hitungDiskonLama' is defined but never used no-unused-vars
✖ 2 problems (2 errors, 0 warnings)
2:9artinya baris 2, kolom 9.eqeqeqmenolak==, yang diam-diam mengubah tipe sebelum membandingkan ('' == 0itutrue).no-unused-varsmenemukan Dead Code: fungsi yang gak pernah dipakai.- Prettier gak akan menemukan keduanya. Sejak versi 8.53 (2023) ESLint juga berhenti merawat aturan format bawaannya. Pembagiannya: Prettier untuk tampilan, ESLint untuk pola bug.
.editorconfig: kesepakatan untuk editor
Prettier jalan saat file disimpan atau di-commit. .editorconfig bekerja lebih awal: ia memberi tahu editor cara menulis selagi kamu mengetik, di editor apa pun (JetBrains membacanya bawaan, VS Code lewat ekstensi EditorConfig):
root = true
[*]
indent_style = space
indent_size = 2
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true
root = true berhenti mencari ke folder di atasnya, [*] berarti semua file. Tombol Tab menulis 2 spasi, spasi di ujung baris dibuang saat disimpan, file diakhiri baris baru, dan akhir baris sama di Windows dan Mac. Prettier ikut membaca indent_style, indent_size, max_line_length dan end_of_line dari sini, jadi "2 spasi" cukup ditulis sekali.
Kesepakatan tim, dijaga otomatis
.editorconfig,.prettierrcdaneslint.config.jsdi-commit ke repo: aturan format jadi kesepakatan tertulis, bukan kebiasaan tiap orang.- Di project lama, format seluruh repo dalam satu commit tersendiri ("format only, gak ada perubahan logika"). Hash-nya bisa dicatat di
.git-blame-ignore-revssupayagit blamemelewatinya. - CI menjalankan
prettier --check .daneslint .; banyak tim juga memasang pre-commit hook (misalnya husky dan lint-staged).
Hasilnya, debat kutip tunggal atau ganda di PR berhenti karena alat yang memutuskan, dan diff cuma berisi perubahan yang berarti.
Latihan: formatCode
formatCode(src) adalah formatter mini:
- Pecah per baris dengan
src.split('\n'). - Per baris, buang spasi di ujung dengan
.replace(/\s+$/, ''), lalu hitung lebar indentasi di awal (tab 4 kolom, spasi 1 kolom) dan tulis ulang sebagai' '.repeat(Math.round(kolom / 4)). Baris yang isinya kosong jadi''. - Rapatkan baris kosong beruntun jadi satu, buang baris kosong di awal dan akhir, lalu gabung dengan
join('\n').
Output dua console.log:
function a() {
return 1;
}
function a() {}
function b() {}
Di src1, satu tab jadi 2 spasi, dua tab jadi 4 spasi, dan spasi di ujung { dan 1; hilang. Di src2, tiga baris kosong di antara dua fungsi tinggal satu.
⚠️ Jebakan yang sering ketemu
- Format dan logika dalam satu PR. 14 baris format menyembunyikan satu
<=. Format di commit sendiri, logika di PR berikutnya. - Tiap orang memakai setelan editornya sendiri. Tanpa konfigurasi di repo, tiap simpan file menghasilkan diff format baru.
- Mengira Prettier menangkap bug.
==dan fungsi yang gak dipakai urusan ESLint, dan<=yang salah urusan test dan review. - Di latihan, cuma
trimEnd(). Pemeriksanya mencari.replace(atau.trim(, jadi pakai.replace(/\s+$/, ''). Tab dihitung 4 kolom, jadi satu tab jadi 2 spasi.
TL;DR: Formatting adalah tata letak kode; ia gak mengubah perilaku, tapi format yang gak seragam bikin diff penuh noise. PR #16 "cuma format" mengubah 14 baris, dan setelah diformat dengan aturan tim tinggal satu: < jadi <=, yang lolos keempat keranjang tapi bikin belanja pas Rp 200.000 jadi Rp 215.000. Prettier adalah formatter opinionated yang mencetak ulang kode tanpa mengubah artinya (2 spasi, titik koma, printWidth 80, singleQuote dari .prettierrc); prettier --check di CI menolak file yang belum rapi. Baris kosong di antara fase tetap keputusanmu, seperti paragraf. ESLint mencari pola bug (eqeqeq, no-unused-vars), bukan tampilan. .editorconfig mengatur editor saat mengetik. Semua config di-commit sebagai kesepakatan tim, format legacy dikerjakan di satu commit tersendiri, dan debat gaya di PR berhenti. Di latihan, formatCode menyeragamkan indentasi (tab dan 4 spasi jadi 2 spasi), membuang spasi di ujung baris dengan .replace, merapatkan baris kosong, dan mencetak function a() { sampai }, lalu function a() {}, baris kosong, function b() {}.