Setiap runtime yang matang punya “laci perkakas” berisi alat-alat kecil yang dipakai berulang-ulang, dan di Node.js laci itu bernama node:util. Isinya beragam — dari pemrosesan argumen command line, konversi function callback menjadi promise, format string ala printf, sampai perapian output debugging. Kita tidak akan menghafal seluruh isinya; chapter ini membedah empat anggota yang paling sering membantu pekerjaan sehari-hari: parseArgs(), promisify(), format(), serta inspect() dan styleText().
Argumen CLI dengan parseArgs() #
Command line adalah antarmuka paling alami untuk alat berbasis Node.js, tetapi argumen mentah dari process.argv datang sebagai array string tanpa struktur: mana flag, mana nilai, mana argumen posisi. Function parseArgs() memilah semuanya berdasarkan skema yang kita definisikan sendiri. Berkas parse-args.mjs menerima satu nama pengguna, satu opsi bernilai, dan satu flag:
import { parseArgs } from "node:util";
const { values, positionals } = parseArgs({
allowPositionals: true,
options: {
prioritas: { type: "string", short: "p" },
ringkas: { type: "boolean", short: "r" },
},
});
console.log("positionals:", positionals);
console.log("prioritas :", values.prioritas);
console.log("ringkas :", values.ringkas);Output yang Dihasilkan:
positionals: [ 'siti' ]
prioritas : tinggi
ringkas : trueProgram di atas dijalankan dengan perintah node parse-args.mjs siti --prioritas=tinggi --ringkas. Mari kita telaah baris-barisnya:
- Baris 3-9 (
parseArgs({...})): Object konfigurasi mendefinisikan dua hal.allowPositionals: truemengizinkan argumen posisi — tanpa baris ini, argumen bebas sepertisitilangsung dianggap error. Propertyoptionsmemetakan nama opsi ke tipe dan singkatannya:prioritasbertipe"string"bisa ditulis pendek-p, sedangkanringkasbertipe"boolean"cukup ditulis--ringkastanpa nilai. - Baris 11 (
positionals): Hasil pemilahan berupa dua bagian. Arraypositionalsberisi argumen posisi dalam urutan penulisan, dan objectvaluesberisi pasangan opsi-nilai. - Baris 12-13: Nilai
values.prioritasberupa string hasil pemotongan tanda=, sedangkan flag boolean menghasilkantrue— bukan string"true"— sehingga siap dipakai langsung dalam kondisiif.
parseArgs() juga menjaga disiplin: opsi yang tidak terdaftar di options membuat error terlempar, sehingga salah ketik seperti --ringaks terdeteksi sejak awal. Perilaku allowPositionals sendiri bisa kita lihat langsung dengan snippet berikut:
import { parseArgs } from "node:util";
try {
parseArgs({ args: ["siti"], options: {} });
} catch (error) {
console.log(error.code);
console.log(error.message);
}Output yang Dihasilkan:
ERR_PARSE_ARGS_UNEXPECTED_POSITIONAL
Unexpected argument 'siti'. This command does not take positional argumentsCallback ke Promise dengan promisify() #
Banyak API Node.js lama — dan banyak library pihak ketiga — masih memakai gaya callback: hasil pengiriman lewat argument terakhir sebuah function, dengan konvensi parameter pertama callback berisi error (atau null bila sukses). Di sisi lain, kode modern lebih nyaman memakai async/await. Function promisify() menjembatani keduanya dengan membungkus function callback menjadi function yang mengembalikan promise. Berkas promisify.mjs mendefinisikan function callback sendiri lalu membungkusnya:
import { promisify } from "node:util";
function bacaKonfigurasi(berkas, callback) {
if (!berkas.endsWith(".json")) {
callback(new Error("ekstensi harus .json"));
return;
}
callback(null, { berkas, mode: "produksi" });
}
const bacaKonfigurasiAsync = promisify(bacaKonfigurasi);
console.log(await bacaKonfigurasiAsync("tugas.json"));
try {
await bacaKonfigurasiAsync("catatan.txt");
} catch (error) {
console.log("gagal:", error.message);
}Output yang Dihasilkan:
{ berkas: 'tugas.json', mode: 'produksi' }
gagal: ekstensi harus .jsonMari kita telaah baris-baris kode di atas:
- Baris 3-9 (
bacaKonfigurasi): Function sumber mengikuti konvensi callback Node.js — parameter terakhir adalah callback, dan callback dipanggil dengannew Error(...)saat gagal atau(null, hasil)saat sukses. Kondisi gagal ditangani lebih dulu, pola khas gaya callback. - Baris 11 (
promisify(bacaKonfigurasi)): Hasil pembungkusan adalah function baru dengan parameter yang sama minus callback. PanggilanbacaKonfigurasiAsync("tugas.json")mengembalikan promise, bukanundefined. - Baris 13 (
await ...): Nilai sukses tiba sebagai hasilawait— object konfigurasi — tanpa perlu menyentuh callback sama sekali. - Baris 15-19 (
try/catch): Error yang dikirim lewat parameter pertama callback berubah menjadi promise yang ditolak, jadi penanganannya memakaicatchbiasa — satu jalur error yang sama dengan seluruh kodeasync/awaitkita.
Pola yang sama bekerja untuk function bawaan runtime. Contoh klasiknya setTimeout(), yang dalam gaya callback dipanggil bersama callback dan durasi; setelah dibungkus, kita mendapat function delay() yang bisa di-await:
import { promisify } from "node:util";
const delay = promisify(setTimeout);
console.log("sebelum");
await delay(10);
console.log("sesudah");Output yang Dihasilkan:
sebelum
sesudahFormat String dengan format() #
Function format() merangkai string dengan penanda tempat (placeholder) ala printf — pola yang juga dipakai console.log secara internal. Tabel berikut merangkum penanda yang paling sering dipakai:
| Penanda | Diganti dengan | Contoh masukan | Hasil |
|---|---|---|---|
%s | String | "%s!", "Budi" | Budi! |
%d | Angka | "Beban %d%%", 85 | Beban 85% |
%j | JSON | "%j", {mode: "produksi"} | {"mode":"produksi"} |
%o | Hasil util.inspect() | "%o", objek | representasi object |
%% | Satu tanda persen | "100%%" bersama argumen lain | 100% |
Blok format-inspect.mjs di bawah sekaligus memakai format(), inspect(), dan styleText(); kita bedah ketiganya setelahnya:
import util from "node:util";
console.log(util.format("Halo, %s!", "Budi"));
console.log(util.format("Beban %d%%", 85));
console.log(util.format("Konfigurasi: %j", { mode: "produksi" }));
const dalam = { tugas: { rincian: { jumlah: 3 } } };
console.log(util.inspect(dalam));
console.log(util.inspect(dalam, { depth: 1 }));
console.log("status :", util.styleText("green", "berhasil"));
console.log("peringatan:", util.styleText("bold", "periksa berkas"));Output yang Dihasilkan:
Halo, Budi!
Beban 85%
Konfigurasi: {"mode":"produksi"}
{ tugas: { rincian: { jumlah: 3 } } }
{ tugas: { rincian: [Object] } }
status : berhasil
peringatan: periksa berkasBaris 3-5 memperlihatkan tiga penanda bekerja: %s untuk nama, %d untuk angka, dan %j untuk object dalam bentuk JSON — berguna saat menulis log satu baris. Penanda %% selalu berpasangan dengan argumen lain karena format() yang menerima satu argumen string saja dikembalikan apa adanya tanpa diproses.
Menata Output dengan inspect() dan styleText() #
console.log() memanggil util.inspect() di belakang layar untuk mengubah object menjadi teks, tetapi versi langsungnya memberi kita kendali penuh lewat options. Option depth membatasi kedalaman struktur yang ditampilkan: pada baris 9 berkas di atas, { depth: 1 } membuat level terdalam tampil sebagai [Object] — ringkasan yang mencegah log menjadi panjang tak berkesudahan saat struktur data bersarang dalam. Default-nya adalah depth: 2; nilai null membuka seluruh kedalaman. Option lain yang sering dipakai antara lain breakLength untuk mengatur kapan array panjang dipatahkan menjadi beberapa baris.
Sementara inspect() mengatur struktur, styleText() mengatur gaya visual. Function ini menerima nama gaya dan teks, lalu mengembalikan teks yang disisipi kode warna ANSI — misalnya "green" untuk hijau dan "bold" untuk cetak tebal; array seperti ["bold", "red"] menggabungkan beberapa gaya sekaligus. Ada dua hal penting dari perilakunya: variable lingkungan NO_COLOR mematikan seluruh gaya, dan gaya juga tidak diterapkan saat output ditujukan ke stream tanpa dukungan warna seperti pipa atau berkas. Karena contoh di atas dieksekusi dengan output yang ditangkap, blok hasilnya menampilkan teks polos — di terminal interaktif pembaca, kata berhasil tampil hijau. Perilaku otomatis inilah yang membuat styleText() aman dipakai di program CLI: pengguna yang mengalihkan output ke grep atau menyimpannya ke berkas mendapat teks bersih tanpa karakter aneh.
Empat perkakas ini baru sebagian kecil isi node:util, tetapi sudah cukup untuk membuat CLI yang rapi membaca argumen, menyambungkan API lama ke kode modern, dan menghasilkan log yang enak dibaca. Bab berikutnya berpindah ke pola yang menjadi jantung arsitektur Node.js sejak hari pertama: EventEmitter dari module node:events.
Referensi lanjutan untuk topik chapter ini: