Topik ini membahas ClojureScript beserta shadow-cljs sebagai build tool-nya. ClojureScript adalah compiler yang menerjemahkan Clojure menjadi JavaScript, sehingga bahasa yang sama dapat digunakan di browser dan di server, sedangkan shadow-cljs mengurus langkah build, integrasi dengan npm, dan hot reload selama pengembangan. Pembahasan mencakup konfigurasi proyek, kode aplikasi browser, penggunaan package npm, serta alur watch yang mengompilasi ulang secara otomatis. Karena langkah eksekusinya bergantung pada unduhan npm yang berat, seluruh blok kode pada topik ini dipaparkan sebagai pola, bukan dijalankan di lingkungan uji.
Konfigurasi proyek dengan shadow-cljs.edn#
Proyek shadow-cljs berpusat pada satu berkas shadow-cljs.edn berformat edn. Kunci :source-paths mendaftar direktori yang memuat kode ClojureScript, sedangkan kunci :builds mendefinisikan setiap build yang dapat dijalankan. Satu build bernama :app dengan target :browser sudah cukup untuk aplikasi browser sederhana, dan konfigurasi berikut adalah bentuk minimalnya.
{:source-paths ["src"]
:builds
{:app {:target :browser
:output-dir "public/js"
:asset-path "/js"
:modules {:utama {:init-fn aplikasi.utama/init}}}}}Kunci :target :browser meminta keluaran berupa berkas JavaScript untuk dimuat halaman web. Kunci :output-dir menentukan folder tempat hasil compile ditulis, yaitu public/js, sedangkan :asset-path menyatakan alamat URL yang sesuai pada server statis, yaitu /js. Kunci :modules memecah kode menjadi berkas yang dapat dimuat browser, dan :init-fn menunjuk fungsi yang dipanggil begitu modul selesai dimuat. Dengan konfigurasi di atas, build :app menghasilkan berkas public/js/utama.js yang dimuat oleh halaman HTML-nya.
Kode aplikasi browser#
Kode ClojureScript ditulis dengan sintaks Clojure yang sama dan disimpan di dalam :source-paths. Namespace aplikasi.utama berada di berkas src/aplikasi/utama.cljs, dan fungsi init yang dirujuk oleh :init-fn memodifikasi elemen halaman melalui interop JavaScript. Namespace js menyediakan akses langsung ke API browser, sehingga js/document adalah objek document yang sama seperti pada JavaScript murni.
; tidak-dijalankan
(ns aplikasi.utama)
(def judul-aktif (atom "katalog anggota"))
(defn init []
(let [elemen (js/document.getElementById "app")]
(set! (.-textContent elemen)
(str "sistem perpustakaan: " @judul-aktif))))Fungsi init mengambil elemen dengan id app, lalu mengganti teksnya dengan isi atom judul-aktif. Atom digunakan dengan cara yang sama seperti pada server, yaitu dibaca dengan @ dan diganti dengan swap!, sehingga state antarmuka tetap mengikuti model nilai Clojure. Halaman HTML yang memuat hasil compile cukup berisi wadah kosong dan satu tag script yang menunjuk ke berkas keluaran.
<!DOCTYPE html>
<html lang="id">
<head>
<meta charset="utf-8"/>
<title>Katalog Perpustakaan</title>
</head>
<body>
<div id="app"></div>
<script src="/js/utama.js"></script>
</body>
</html>Setelah build dijalankan, membuka halaman tersebut di browser memperlihatkan teks “sistem perpustakaan: katalog anggota” di dalam wadah app. Tampilan itu berasal sepenuhnya dari berkas JavaScript hasil compile, tanpa satu baris JavaScript yang ditulis manual.
Integrasi dengan npm#
shadow-cljs mengelola proyek sebagai proyek npm, sehingga berkas package.json menjadi bagian konfigurasinya. Dependensi build tool ditempatkan pada devDependencies, dan rentang ^3.0.0 meminta versi 3.x mana pun yang kompatibel. npm mengunci versi persis yang terpasang pada berkas package-lock.json, sehingga anggota tim dan mesin build memperoleh versi yang sama meskipun rentangnya longgar.
{
"name": "aplikasi-perpustakaan",
"version": "1.0.0",
"private": true,
"devDependencies": {
"shadow-cljs": "^3.0.0"
}
}Paket npm yang terpasang dapat digunakan dari ClojureScript dengan menuliskan namanya sebagai string pada bentuk :require. Rujukan berupa nama string membuat compiler menautkan modul JavaScript milik paket tersebut ke dalam keluaran build, dan nilai yang diimpor dapat dipanggil seperti fungsi Clojure biasa. Inilah pembeda utama shadow-cljs, yaitu ekosistem npm utuh dapat dimanfaatkan tanpa konfigurasi bundler tambahan.
Watch dan hot reload#
Alur kerja pengembangan berjalan melalui mode watch. Perintah watch memantau berkas di :source-paths, mengompilasi ulang setiap perubahan, lalu mengirim pemberitahuan reload ke browser yang terbuka. Langkah-langkahnya membutuhkan unduhan paket npm berukuran besar, sehingga blok berikut hanya dipaparkan sebagai pola dan tidak dijalankan di lingkungan uji.
# tidak-dijalankan
# pasang dependensi dev npm sekali di awal
npm install
# jalankan watch untuk build :app dan aktifkan hot reload
npx shadow-cljs watch appSaat watch berjalan, terminal menampilkan kemajuan kompilasi hingga muncul pesan bahwa build :app selesai, sedangkan browser menerima pembaruan tanpa dimuat ulang. Setiap kali utama.cljs disimpan, compiler mengompilasi ulang berkas itu, berkas utama.js diperbarui, dan fungsi yang terdaftar pada konfigurasi reload dipanggil ulang sehingga perubahan terlihat seketika. Untuk keluaran produksi, perintah npx shadow-cljs release app mengompilasi build yang sama dengan pengoptimalan penuh tanpa perangkat reload.
Target browser dan npm-module#
Konfigurasi :target menentukan bentuk keluaran build. Target :browser menghasilkan berkas JavaScript yang dimuat langsung oleh halaman web, sebagaimana contoh sepanjang topik ini. Target :npm-module menghasilkan modul JavaScript yang dikonsumsi oleh alat lain, misalnya aplikasi yang dibangun dengan pengelola modul sendiri, sehingga kode ClojureScript berperan sebagai pustaka di dalam proyek JavaScript yang lebih besar. Kedua target menggunakan kode sumber yang sama dan hanya berbeda pada cara keluarannya dibungkus.
Pilihan target mengikuti arsitektur proyek. Aplikasi browser mandiri cukup dengan :browser beserta :modules-nya, sedangkan integrasi bertingkat dengan tooling JavaScript menuntut :npm-module. Apabila keduanya dibutuhkan sekaligus, lebih dari satu build dapat didefinisikan pada :builds dan dijalankan dengan nama masing-masing.
Kesalahan umum#
Menjalankan npx shadow-cljs watch app sebelum npm install. Build tool belum terpasang dan perintah berakhir dengan pesan bahwa modul tidak ditemukan. Perbaikannya dengan menjalankan npm install terlebih dahulu, lalu mengulangi perintah watch-nya.
Menyamakan :asset-path dengan :output-dir. Kunci pertama adalah alamat URL yang dilihat browser, sedangkan yang kedua adalah folder pada sistem berkas, dan keduanya hanya cocok apabila server statis menyajikan folder tersebut pada akar alamatnya. Perbaikannya dengan menyelaraskan penyajian berkas statis dengan nilai :asset-path yang dideklarasikan.
Menunggu hot reload untuk kode yang tidak diregistrasi. Hanya fungsi yang terdaftar pada konfigurasi reload atau yang dipanggil melaluinya yang dijalankan ulang, sehingga perubahan terasa tidak berpengaruh. Perbaikannya dengan memastikan :init-fn merujuk fungsi yang benar dan memanggil ulang fungsi tersebut setelah state diganti.
Ringkasan#
- shadow-cljs adalah build tool ClojureScript yang mengurus kompilasi ke JavaScript, integrasi npm, dan hot reload melalui satu berkas
shadow-cljs.edn. - Build dengan
:target :browsermenghasilkan berkas di:output-diryang dimuat halaman HTML, dan:init-fnmenentukan fungsi yang dijalankan saat modul dimuat. - Paket npm digunakan dari ClojureScript melalui
:requiredengan nama string, dan npm mengunci versi terpasang padapackage-lock.json. - Perintah
npx shadow-cljs watch appmengompilasi ulang perubahan dan memperbarui browser secara otomatis, sedangkanreleasemenghasilkan keluaran produksi yang teroptimal.
Referensi#
- shadow-cljs User’s Guide — Dokumentasi resmi shadow-cljs untuk konfigurasi build, npm, dan hot reload.
- ClojureScript — Situs resmi ClojureScript beserta penjelasan compiler dan ekosistemnya.
- ClojureScript Quick Start — Panduan resmi kompilasi ClojureScript pertama tanpa alat tambahan.