process.argv dan util.parseArgs() cukup untuk alat kecil, tetapi bayangkan git atau npm: keduanya punya puluhan subperintah, ratusan opsi, layar bantuan yang rapi, dan pesan error yang menjelaskan apa yang salah. Membangun semua itu dari nol di setiap program jelas tidak masuk akal. Package Commander — salah satu framework CLI paling populer di ekosistem Node.js — menutup celah ini: kita mendeklarasikan perintah, opsi, dan argumen, lalu Commander mengurus parsing, validasi, help, sampai pesan error. Pada chapter penutup section ini kita menyusun CLI lengkap dengannya.
Instalasi #
Commander adalah package npm biasa yang dipasang dengan satu perintah:
npm install commanderProgram Pertama #
Commander menyediakan object program siap pakai yang dikonfigurasi lewat rangkaian method berantai. Berkas sapa.mjs mendeklarasikan identitas program, satu argumen opsional, dan satu flag:
import { program } from "commander";
program
.name("sapa")
.description("Sapa seseorang dari terminal")
.version("1.2.0")
.argument("[nama]", "nama yang akan disapa", "Dunia")
.option("-s, --semangat", "tambahkan kata penyemangat")
.action((nama, opsi) => {
let pesan = `Halo, ${nama}!`;
if (opsi.semangat) {
pesan += " Semangat selalu.";
}
console.log(pesan);
});
program.parse();Output yang Dihasilkan:
Halo, Dunia!Mari kita telaah baris-per-baris kode di atas:
- Baris 1 (import):
programadalah instanceCommandyang sudah dibuatkan Commander — cukup untuk CLI satu perintah. Untuk CLI dengan subperintah, kita membuat instance sendiri lewatnew Command()seperti pada contoh kedua di bawah. - Baris 3-7 (identitas program):
.name(),.description(), dan.version()mengisi data yang nanti tampil di help;.argument("[nama]", ..., "Dunia")mendeklarasikan argumen opsional — kurung siku berarti boleh tidak diisi, dan parameter ketiga adalah nilai default-nya. - Baris 8 (
.option()): flag-s/--semangatdilengkapi deskripsi yang juga tampil di help; Commander mengumpulkan seluruh opsi ke satu object. - Baris 9-14 (
.action()): callback inilah tubuh program — parameter pertama menerima nilai argumen hasil parsing, parameter kedua object opsi. Tidak adaslice(2), tidak ada pemeriksaanstartsWith("--"): semua dikerjakan Commander. - Baris 16 (
program.parse()): baris ini membacaprocess.argv, memvalidasi masukan, lalu menjalankan action yang cocok.
Berjalan tanpa argumen, program memakai nilai default Dunia (output di atas). Dengan argumen dan flag, hasilnya berubah:
node sapa.mjs Siti --semangatOutput yang Dihasilkan:
Halo, Siti! Semangat selalu.Help dan Version Otomatis #
Deklarasi di atas otomatis menghasilkan dua flag bonus: -h/--help dan -V/--version — tanpa satu baris kode tambahan pun:
node sapa.mjs --helpOutput yang Dihasilkan:
Usage: sapa [options] [nama]
Sapa seseorang dari terminal
Arguments:
nama nama yang akan disapa (default: "Dunia")
Options:
-V, --version output the version number
-s, --semangat tambahkan kata penyemangat
-h, --help display help for commandLayar bantuan tersebut dirakit murni dari deklarasi kita: baris Usage mengikuti nama program dan argumennya, deskripsi program tercetak di bawahnya, lalu setiap argumen dan opsi tampil lengkap dengan keterangannya. Opsi versi bekerja dengan cara yang sama:
node sapa.mjs --versionOutput yang Dihasilkan:
1.2.0Subperintah ala git dan npm #
CLI sungguhan mengelompokkan fungsi ke dalam subperintah: git commit, npm install. Di Commander, setiap panggilan .command() menambahkan satu subperintah dengan deklarasi argumen dan opsi miliknya sendiri. Berkas tugas.mjs memiliki dua subperintah:
import { Command } from "commander";
const program = new Command();
program
.name("tugas")
.description("Alat pengelola daftar tugas")
.version("1.0.0");
program
.command("tambah")
.description("Tambah tugas baru")
.argument("<nama>", "nama tugas")
.option("-p, --prioritas <level>", "tingkat prioritas", "sedang")
.action((nama, opsi) => {
console.log(`tugas "${nama}" ditambahkan (prioritas: ${opsi.prioritas})`);
});
program
.command("daftar")
.description("Tampilkan semua tugas")
.option("-s, --selesai", "hanya tugas yang sudah selesai")
.action((opsi) => {
const label = opsi.selesai ? "selesai" : "semua";
console.log(`menampilkan ${label} tugas:`);
console.log("- belajar Node.js");
});
program.parse();Output yang Dihasilkan:
tugas "belajar-node" ditambahkan (prioritas: tinggi)Subperintah tambah berjalan dengan argumen wajib belajar-node dan opsi bernilai --prioritas tinggi. Perbedaan penting dari sapa.mjs: .argument("<nama>", ...) memakai kurung sudut tanpa nilai default, sehingga argumen ini wajib — dan Commander menolaknya sebelum action sempat berjalan bila luput:
node tugas.mjs tambah
echo kode: $?Output yang Dihasilkan:
error: missing required argument 'nama'
kode: 1Pesan error: missing required argument 'nama' ditulis ke stderr dan proses keluar dengan kode 1 — persis disiplin chapter Exit Code, kali ini tanpa kita tulis sendiri. Subperintah kedua menerima flag biasa:
node tugas.mjs daftar --selesaiOutput yang Dihasilkan:
menampilkan selesai tugas:
- belajar Node.jsBantuan program induk kini menyertakan daftar subperintah beserta ringkasannya, dan tiap subperintah punya layar bantuan sendiri lewat node tugas.mjs tambah --help:
node tugas.mjs --helpOutput yang Dihasilkan:
Usage: tugas [options] [command]
Alat pengelola daftar tugas
Options:
-V, --version output the version number
-h, --help display help for command
Commands:
tambah [options] <nama> Tambah tugas baru
daftar [options] Tampilkan semua tugas
help [command] display help for commandMenjalankan tugas.mjs tanpa subperintah apa pun membuat Commander mencetak bantuan yang sama ke stderr dan keluar dengan kode 1 — perilaku yang masuk akal untuk program tanpa aksi bawaan. Sebagai penutup, berikut rangkuman pola deklarasi yang kita pakai sepanjang chapter:
| Pola | Makna | Contoh pemakaian |
|---|---|---|
.argument("<nama>", ...) | argumen wajib | tugas.mjs tambah belajar-node |
.argument("[nama]", ..., default) | argumen opsional dengan nilai default | sapa.mjs → Dunia |
.option("-s, --selesai", ...) | flag boolean | tugas.mjs daftar --selesai |
.option("-p, --prioritas <level>", ..., default) | opsi bernilai dengan default | --prioritas tinggi |
Dengan Commander, seluruh bahan section ini menyatu: process.argv dibaca oleh program.parse(), opsi dan argumen tervalidasi otomatis, help tercetak mengikuti konvensi stream dan exit code yang benar. Kini kita bisa merilis alat CLI yang terasa profesional sejak commit pertama. Kemampuan berikutnya yang menanti bukan lagi antarmuka teks, melainkan jaringan: Section Networking mengajak kita membangun TCP server dan client pertama dengan module net.
Referensi lanjutan untuk topik chapter ini: