wasm-pack adalah tool yang mengubah kode Rust jadi paket siap pakai untuk JavaScript: dia meng-compile Rust ke .wasm, menjalankan wasm-bindgen untuk membuat kode JavaScript perantaranya (glue code) dan file tipe TypeScript, mengecilkan .wasm dengan wasm-opt, lalu menulis package.json. Bayangin carica, oleh-oleh khas Dieng. Buahnya dimasak jadi manisan, dimasukkan ke botol dengan label dan cara penyajian, ditutup rapat, lalu dikemas dalam kardus berlabel toko supaya siap dipajang. Buahnya itu kode Rust, botol dan labelnya glue code JavaScript, tutup rapatnya wasm-opt, kardusnya package.json. wasm-pack adalah pabrik yang mengerjakan semuanya dengan satu perintah.
🧪 Tebakan cepat: fungsi
hargaInapyang di WAT cuma 47 byte ditulis ulang dalam Rust lalu di-build denganwasm-pack build. File.wasm-nya kira-kira berapa besar: 47 byte, 14 KB, atau 2 MB? Tebak dulu, jawabannya ada di bagian "Ukuran file".
Siapkan toolchain
Kamu butuh Rust (lewat rustup), target wasm32-unknown-unknown, dan wasm-pack:
rustup target add wasm32-unknown-unknown
curl https://wasm-bindgen.github.io/wasm-pack/installer/init.sh -sSf | sh
wasm-pack --version
# wasm-pack 0.15.0
- Cara lain memasang wasm-pack:
npm install -g wasm-pack(binary jadi) ataucargo install wasm-pack(compile dari source, lama). wasm32-unknown-unknownartinya: Wasm 32-bit, gak ada sistem operasi di bawahnya. Ini target untuk browser.- Pelajaran ini memakai rustc 1.99.0, wasm-pack 0.15.0, dan wasm-bindgen 0.2.129.
- Build pertama lebih lama: wasm-pack memasang
wasm-bindgen-clidengan versi yang sama persis dengan crate wasm-bindgen di proyekmu. Di laptop ini butuh sekitar 2 menit, build berikutnya kurang dari 1 detik.
Proyeknya: homestay-kabut
Buat proyeknya dengan cargo new --lib homestay-kabut, lalu ubah Cargo.toml jadi begini:
[package]
name = "homestay-kabut"
version = "0.1.0"
edition = "2024"
[lib]
crate-type = ["cdylib"]
[dependencies]
wasm-bindgen = "0.2"
Lalu src/lib.rs berisi dua fungsi sepanjang jalur ini, harga menginap dan grayscale foto kamar:
use wasm_bindgen::prelude::*;
/// Harga menginap di Homestay Kabut: malam × harga per malam (rupiah).
#[wasm_bindgen(js_name = hargaInap)]
pub fn harga_inap(malam: i32, harga_per_malam: i32) -> i32 {
malam * harga_per_malam
}
/// Foto kamar jadi abu-abu, langsung di array-nya (RGBA, 4 byte per piksel).
#[wasm_bindgen]
pub fn grayscale(piksel: &mut [u8]) {
for px in piksel.chunks_exact_mut(4) {
let abu = ((px[0] as u32 * 77 + px[1] as u32 * 150 + px[2] as u32 * 29) >> 8) as u8;
px[0] = abu;
px[1] = abu;
px[2] = abu;
}
}
crate-type = ["cdylib"]meminta Rust membuat library yang bisa dimuat bahasa lain. Tanpa ini gak ada.wasmyang keluar.#[wasm_bindgen]menandai fungsi yang di-export ke JavaScript.js_name = hargaInapmengganti namanya di sisi JS, karena gaya Rustharga_inapdan gaya JShargaInap.i32di Rust jadinumberdi JS, sama sepertii32di WAT.&mut [u8]jadiUint8Arraydi JS. Isinya disalin masuk ke memori Wasm, diubah, lalu disalin balik ke array aslinya.chunks_exact_mut(4)berjalan per 4 byte, yaitu per piksel. Rumusnya sama dengan versi JS dan WAT, jadi hasilnya sama byte demi byte.
Build dengan wasm-pack
$ wasm-pack build --target web
[INFO]: 🎯 Checking for the Wasm target...
[INFO]: 🌀 Compiling to Wasm...
Compiling homestay-kabut v0.1.0 (/Users/sri/homestay-kabut)
Finished `release` profile [optimized] target(s) in 0.48s
[INFO]: ⬇️ Installing wasm-bindgen...
[INFO]: Optimizing wasm binaries with `wasm-opt`...
[INFO]: Optional fields missing from Cargo.toml: 'description', 'repository', and 'license'. These are not necessary, but recommended
[INFO]: ✨ Done in 0.98s
[INFO]: 📦 Your wasm pkg is ready to publish at /Users/sri/homestay-kabut/pkg.
(Path folder di laptopmu tentu beda.) Hasilnya ada di folder pkg/:
| File | Ukuran | Isinya |
|---|---|---|
homestay_kabut_bg.wasm |
14.896 byte | kode Rust yang sudah jadi Wasm |
homestay_kabut.js |
5.311 byte | glue code: fungsi JS yang memanggil Wasm, dan init() |
homestay_kabut.d.ts |
1.649 byte | tipe TypeScript untuk hargaInap dan grayscale |
package.json |
280 byte | supaya folder ini bisa jadi paket npm |
Ini potongan glue code yang dibuat wasm-bindgen:
export function hargaInap(malam, harga_per_malam) {
const ret = wasm.hargaInap(malam, harga_per_malam);
return ret;
}
export function grayscale(piksel) {
var ptr0 = passArray8ToWasm0(piksel, wasm.__wbindgen_malloc);
var len0 = WASM_VECTOR_LEN;
wasm.grayscale(ptr0, len0, piksel);
}
hargaInapcuma meneruskan dua angka. Gak ada yang perlu disalin.grayscalememesan tempat di memori Wasm lewat__wbindgen_malloc, menyalinUint8Array-nya ke sana, lalu memanggil Wasm. Setelah selesai, Rust menyalin hasilnya balik ke array aslinya lewat import yang juga dibuat wasm-bindgen. Ongkos salin dari pelajaran sebelumnya tetap ada, cuma sekarang dikerjakan otomatis.
Selain build, wasm-pack juga punya wasm-pack test (menjalankan test di Node atau di browser headless), wasm-pack pack (membuat file .tgz paketnya), dan wasm-pack publish (mengunggahnya ke npm).
Pakai di browser
<script type="module">
import init, { hargaInap, grayscale } from './pkg/homestay_kabut.js';
await init(); // unduh dan instantiate homestay_kabut_bg.wasm
console.log(hargaInap(2, 350000)); // 700000
const px = new Uint8Array([200, 120, 40, 255]);
grayscale(px);
console.log(px); // Uint8Array(4) [135, 135, 135, 255]
</script>
init()mencarihomestay_kabut_bg.wasmdi sebelah file JS-nya, lalu memuatnya denganinstantiateStreaming(dan mundur keinstantiatekalau Content-Type-nya salah).awaitboleh langsung di<script type="module">.- Buka halamannya lewat server lokal. Dari
file://, browser gak mau mengambil file.wasm-nya.
target web, bundler, nodejs
File .wasm-nya sama persis di ketiga target (14.896 byte). Yang beda cuma glue code-nya:
| Target | Cara memuat .wasm |
Cara memakainya |
|---|---|---|
web |
init() mengambilnya dengan fetch |
import init, { hargaInap } from './pkg/homestay_kabut.js', lalu await init() |
bundler (default kalau --target gak ditulis) |
import * as wasm from "./homestay_kabut_bg.wasm", diurus bundler |
import { hargaInap } from 'homestay-kabut' |
nodejs |
require('fs').readFileSync lalu new WebAssembly.Module (CommonJS) |
const { hargaInap } = require('./pkg/homestay_kabut.js') |
Target bundler butuh bundler yang bisa meng-import .wasm langsung, seperti webpack 5 dengan experiments.asyncWebAssembly. Di Vite, pakai plugin seperti vite-plugin-wasm, atau pakai target web.
Ukuran file
| Build | Ukuran .wasm |
|---|---|
harga.wat lewat wat2wasm |
47 byte |
Rust tanpa wasm-bindgen (#[unsafe(no_mangle)] pub extern "C" fn), cargo build --release |
338 byte, 68 byte setelah wasm-strip |
wasm-pack --release, hargaInap saja |
13.588 byte |
wasm-pack --release, hargaInap + grayscale |
14.896 byte (6.762 byte setelah gzip) |
wasm-pack --dev, hargaInap + grayscale |
64.731 byte |
Jadi jawaban tebakannya: sekitar 14 KB. Perkaliannya sendiri tetap beberapa byte. Sisanya adalah perlengkapan wasm-bindgen yang selalu ikut: allocator memori (dlmalloc), tabel untuk menyimpan objek JS, dan penanganan panic. Ongkos itu dibayar sekali; menambah grayscale cuma menambah 1,3 KB. --dev lebih besar karena berisi info debug dan gak dikecilkan wasm-opt.
Ini juga alasan Rust populer untuk Wasm: Rust gak butuh garbage collector, jadi gak ada runtime GC yang ikut dikirim, binary-nya kecil, dan tool-nya (wasm-pack, wasm-bindgen, web-sys) matang.
📌 Status wasm-pack dan wasm-bindgen sejak 2025
- Juli 2025: blog Inside Rust mengumumkan "Sunsetting the rustwasm GitHub org". Working group Rust dan WebAssembly sudah diarsipkan sejak 2024, dan organisasi GitHub
rustwasmdiarsipkan September 2025. - wasm-bindgen (bersama web-sys dan js-sys) pindah ke organisasi baru,
wasm-bindgen, dengan pengelola tambahan yang baru. Proyek ini aktif; rilis terbarunya 0.2.129 (September 2026). - wasm-pack awalnya diserahkan ke pengelola lamanya, Jesper Håkansson (drager), dan rilis 0.14.0 (Januari 2026) keluar dari
drager/wasm-pack. Setelah itu repo-nya pindah lagi ke organisasiwasm-bindgen: rilis 0.15.0 (Mei 2026) keluar darigithub.com/wasm-bindgen/wasm-pack, dan dokumentasinya diwasm-bindgen.github.io/wasm-pack. - Repo lain di
rustwasm, seperti gloo dan twiggy, diarsipkan atau diserahkan ke pengelolanya masing-masing.
⚠️ Jebakan yang sering ketemu
- Lupa
crate-type = ["cdylib"]. wasm-pack berhenti denganError: crate-type must be cdylib to compile to WebAssembly. Add the following to your Cargo.toml file:lalu menyarankan[lib]dancrate-type = ["cdylib", "rlib"]. - Memanggil fungsi sebelum
await init(). Glue code-nya belum punya modul Wasm. Di Chrome:TypeError: Cannot read properties of undefined (reading 'hargaInap'). - Overflow beda antara
--devdan--release.hargaInap(7000, 350000)di build--releasediam-diam jadi-1844967296. Di build--dev, Rust memeriksa overflow dan panic, yang sampai di JS sebagaiRuntimeError: unreachable. Untuk angka besar, pakaii64atauchecked_mul. - Membuka halaman lewat
file://.init()butuhfetch, jadi halamannya harus dibuka lewat server.
TL;DR: wasm-pack mengubah crate Rust jadi paket untuk JavaScript: cargo build ke wasm32-unknown-unknown, wasm-bindgen membuat glue code JS dan .d.ts, wasm-opt mengecilkan .wasm, dan package.json ditulis. Syaratnya crate-type = ["cdylib"] dan dependency wasm-bindgen; fungsi yang di-export diberi #[wasm_bindgen] (js_name untuk mengganti namanya). wasm-pack build --target web dipakai dengan import init, { hargaInap } from './pkg/homestay_kabut.js' dan await init(); target bundler (default) untuk bundler, nodejs untuk require di Node. &mut [u8] disalin masuk dan keluar otomatis. Ukurannya: hargaInap + grayscale 14.896 byte di --release, 64.731 byte di --dev, sebagian besar perlengkapan wasm-bindgen. wasm-pack juga punya test, pack, dan publish. Sejak 2025 organisasi rustwasm diarsipkan; wasm-bindgen dan sekarang wasm-pack dirawat di organisasi wasm-bindgen.