|
| 1 | +# MoonBit Tooling |
| 2 | + |
| 3 | +MoonBit is a programming language that provides first-class support for |
| 4 | +WebAssembly . This guide demonstrates how to build WebAssembly components using |
| 5 | +MoonBit, leveraging WIT (WebAssembly Interface Types) for interface definitions |
| 6 | +and the `wit-bindgen` toolchain for code generation. |
| 7 | + |
| 8 | +This tutorial walks through building a component that implements the |
| 9 | +[`adder` world][adder-wit] defined in the `docs:adder` package. The component |
| 10 | +will export an `add` interface containing an `add` function that sums two |
| 11 | +numbers. |
| 12 | + |
| 13 | +[adder-wit]: https://github.com/bytecodealliance/component-docs/tree/main/component-model/examples/tutorial/wit/adder/world.wit |
| 14 | + |
| 15 | +## 1. Install the Tools |
| 16 | + |
| 17 | +### Installing MoonBit |
| 18 | + |
| 19 | +First, install the MoonBit compiler and toolchain. Follow the installation |
| 20 | +instructions from the |
| 21 | +[MoonBit download page](https://www.moonbitlang.com/download). |
| 22 | + |
| 23 | +Verify your MoonBit installation (below are the versions at the time of |
| 24 | +writing): |
| 25 | + |
| 26 | +``` |
| 27 | +$ moon version |
| 28 | +moon 0.1.20250801 (edae1ae 2025-08-01) ~/.moon/bin/moon |
| 29 | +moonc v0.6.24+012953835 ~/.moon/bin/moonc |
| 30 | +moonrun 0.1.20250801 (edae1ae 2025-08-01) ~/.moon/bin/moonrun |
| 31 | +``` |
| 32 | + |
| 33 | +### Installing `wit-bindgen` |
| 34 | + |
| 35 | +Install the `wit-bindgen` CLI tool, which generates MoonBit bindings from WIT |
| 36 | +files: |
| 37 | + |
| 38 | +``` |
| 39 | +$ cargo install wit-bindgen-cli |
| 40 | +``` |
| 41 | + |
| 42 | +### Installing `wasm-tools` |
| 43 | + |
| 44 | +Install `wasm-tools` for working with WebAssembly components: |
| 45 | + |
| 46 | +``` |
| 47 | +$ cargo install wasm-tools |
| 48 | +``` |
| 49 | + |
| 50 | +Verify the installations (below are the versions at the time of writing): |
| 51 | + |
| 52 | +``` |
| 53 | +$ wit-bindgen --version |
| 54 | +wit-bindgen-cli 0.44.0 (bdb4df54b 2025-08-14) |
| 55 | +$ wasm-tools --version |
| 56 | +wasm-tools 1.237.0 |
| 57 | +``` |
| 58 | + |
| 59 | +## 2. Define the Interface (WIT) |
| 60 | + |
| 61 | +Before generating the MoonBit project, you need to define the component |
| 62 | +interface using WIT. Create a directory for your project and define the WIT |
| 63 | +file: |
| 64 | + |
| 65 | +```console |
| 66 | +$ mkdir moonbit-adder && cd moonbit-adder |
| 67 | +$ mkdir wit |
| 68 | +``` |
| 69 | + |
| 70 | +Create `wit/world.wit` with the following content: |
| 71 | + |
| 72 | +```wit |
| 73 | + |
| 74 | +
|
| 75 | +interface add { |
| 76 | + add: func(x: u32, y: u32) -> u32; |
| 77 | +} |
| 78 | +
|
| 79 | +world adder { |
| 80 | + export add; |
| 81 | +} |
| 82 | +``` |
| 83 | + |
| 84 | +This WIT definition: |
| 85 | + |
| 86 | +- Declares a package `docs:adder` with version `0.1.0` |
| 87 | +- Defines an `add` interface with a single function that takes two `u32` |
| 88 | + parameters and returns a `u32` |
| 89 | +- Creates an `adder` world that exports the `add` interface |
| 90 | + |
| 91 | +## 3. Generate MoonBit Project Structure |
| 92 | + |
| 93 | +Use `wit-bindgen` to generate the MoonBit project structure and bindings: |
| 94 | + |
| 95 | +```console |
| 96 | +$ wit-bindgen moonbit wit/world.wit --out-dir . \ |
| 97 | + --derive-eq \ |
| 98 | + --derive-show \ |
| 99 | + --derive-error |
| 100 | +``` |
| 101 | + |
| 102 | +This command generates the following directory structure: |
| 103 | + |
| 104 | +``` |
| 105 | +. |
| 106 | +├── ffi |
| 107 | +│ ├── moon.pkg.json |
| 108 | +│ └── top.mbt |
| 109 | +├── gen |
| 110 | +│ ├── ffi.mbt |
| 111 | +│ ├── gen_interface_docs_adder_add_export.mbt |
| 112 | +│ ├── interface |
| 113 | +│ │ └── docs |
| 114 | +│ │ └── adder |
| 115 | +│ │ └── add |
| 116 | +│ │ ├── moon.pkg.json |
| 117 | +│ │ ├── stub.mbt |
| 118 | +│ │ └── top.mbt |
| 119 | +│ ├── moon.pkg.json |
| 120 | +│ ├── world |
| 121 | +│ │ └── adder |
| 122 | +│ │ ├── moon.pkg.json |
| 123 | +│ │ └── stub.mbt |
| 124 | +│ └── world_adder_export.mbt |
| 125 | +├── moon.mod.json |
| 126 | +├── wit |
| 127 | +│ └── world.wit |
| 128 | +└── world |
| 129 | + └── adder |
| 130 | + ├── ffi_import.mbt |
| 131 | + ├── import.mbt |
| 132 | + ├── moon.pkg.json |
| 133 | + └── top.mbt |
| 134 | +``` |
| 135 | + |
| 136 | +The generated files include: |
| 137 | + |
| 138 | +- `moon.mod.json`: MoonBit module configuration |
| 139 | +- `gen/`: Generated export bindings |
| 140 | + - `interface/`: Generated export interface bindings |
| 141 | + - `world/`: Generated export world bindings |
| 142 | + - `stub.mbt`: Main implementation file |
| 143 | +- `interface/`: Generated import interface bindings |
| 144 | +- `world/`: Generated import world bindings |
| 145 | + |
| 146 | +## 4. Examine the Generated Code |
| 147 | + |
| 148 | +The `wit-bindgen` tool generates MoonBit bindings that handle the WebAssembly |
| 149 | +component interface. Let's examine the generated |
| 150 | +`gen/interface/docs/adder/add/stub.mbt`: |
| 151 | + |
| 152 | +```moonbit |
| 153 | +// Generated by `wit-bindgen` 0.44.0. |
| 154 | +
|
| 155 | +pub fn add(_x : UInt, _y : UInt) -> UInt { |
| 156 | + ... |
| 157 | +} |
| 158 | +``` |
| 159 | + |
| 160 | +The `...` is the placeholder syntax in MoonBit. When executing |
| 161 | +`moon check --target wasm`, 'unfinished code' warnings will appear. |
| 162 | + |
| 163 | +## 5. Implement the Component Logic |
| 164 | + |
| 165 | +Now implement the `add` function in `src/lib.mbt`: |
| 166 | + |
| 167 | +```moonbit |
| 168 | +// Generated by `wit-bindgen` 0.44.0. |
| 169 | +
|
| 170 | +///| |
| 171 | +pub fn add(x : UInt, y : UInt) -> UInt { |
| 172 | + x + y |
| 173 | +} |
| 174 | +``` |
| 175 | + |
| 176 | +## 6. Configure the Build |
| 177 | + |
| 178 | +Ensure your `gen/moon.pkg.json` is properly configured for WebAssembly target: |
| 179 | + |
| 180 | +```json |
| 181 | +{ |
| 182 | + // link configuration for Wasm backend |
| 183 | + "link": { |
| 184 | + "wasm": { |
| 185 | + "exports": [ |
| 186 | + // Export for cabi_realloc |
| 187 | + "cabi_realloc:cabi_realloc", |
| 188 | + // Export per the interface definition |
| 189 | + "wasmExportAdd:docs:adder/[email protected]#add" |
| 190 | + ], |
| 191 | + "export-memory-name": "memory", |
| 192 | + "heap-start-address": 16 |
| 193 | + } |
| 194 | + }, |
| 195 | + "import": [ |
| 196 | + { |
| 197 | + "path": "docs/adder/ffi", |
| 198 | + "alias": "ffi" |
| 199 | + }, |
| 200 | + { |
| 201 | + "path": "docs/adder/gen/interface/docs/adder/add", |
| 202 | + "alias": "add" |
| 203 | + } |
| 204 | + ] |
| 205 | +} |
| 206 | +``` |
| 207 | + |
| 208 | +## 7. Build the WebAssembly Component |
| 209 | + |
| 210 | +Build the MoonBit code to WebAssembly: |
| 211 | + |
| 212 | +```console |
| 213 | +$ moon build --target wasm |
| 214 | +``` |
| 215 | + |
| 216 | +This generates a WebAssembly module. To create a proper WebAssembly component, |
| 217 | +use `wasm-tools`: |
| 218 | + |
| 219 | +```console |
| 220 | +$ wasm-tools component embed wit target/wasm/release/build/gen/gen.wasm \ |
| 221 | + --encoding utf16 \ |
| 222 | + --output adder.wasm |
| 223 | +$ wasm-tools component new adder.wasm --output adder.component.wasm |
| 224 | +``` |
| 225 | + |
| 226 | +You can verify the component's interface using `wasm-tools`: |
| 227 | + |
| 228 | +```console |
| 229 | +$ wasm-tools component wit adder.component.wasm |
| 230 | +``` |
| 231 | + |
| 232 | +Expected output for both commands: |
| 233 | + |
| 234 | +```wit |
| 235 | +package root:component; |
| 236 | +
|
| 237 | +world root { |
| 238 | + export docs:adder/[email protected]; |
| 239 | +} |
| 240 | + |
| 241 | + interface add { |
| 242 | + add: func(x: u32, y: u32) -> u32; |
| 243 | + } |
| 244 | +} |
| 245 | +``` |
| 246 | + |
| 247 | +## 8. Testing the Component |
| 248 | + |
| 249 | +### Using the Example Host |
| 250 | + |
| 251 | +To test your component, use the [`example-host`][example-host] provided in this |
| 252 | +repository: |
| 253 | + |
| 254 | +```console |
| 255 | +$ git clone https://github.com/bytecodealliance/component-docs.git |
| 256 | +$ cd component-docs/component-model/examples/example-host |
| 257 | +$ cp /path/to/adder.component.wasm . |
| 258 | +$ cargo run --release -- 5 3 adder.component.wasm |
| 259 | +``` |
| 260 | + |
| 261 | +Expected output: |
| 262 | + |
| 263 | +``` |
| 264 | +5 + 3 = 8 |
| 265 | +``` |
| 266 | + |
| 267 | +[example-host]: https://github.com/bytecodealliance/component-docs/blob/main/component-model/examples/example-host/README.md |
| 268 | + |
| 269 | +### Using Wasmtime |
| 270 | + |
| 271 | +You can also test the component directly with `wasmtime`: |
| 272 | + |
| 273 | +```console |
| 274 | +$ wasmtime run --invoke 'add(10, 20)' adder.component.wasm |
| 275 | +30 |
| 276 | +``` |
| 277 | + |
| 278 | +## 9. Configurations |
| 279 | + |
| 280 | +### --derive-eq --derive-show |
| 281 | + |
| 282 | +These two options will add `derive(Eq)` and / or `derive(Show)` for all the |
| 283 | +generated types. |
| 284 | + |
| 285 | +### --derive-error |
| 286 | + |
| 287 | +This option will generate variants / enums whose names containing 'Error' as |
| 288 | +[suberrors](https://docs.moonbitlang.com/en/latest/language/error-handling.html#error-types). |
| 289 | +This allows you to integrate the MoonBit's error handling easier. |
| 290 | + |
| 291 | +For example, for the following interface: |
| 292 | + |
| 293 | +```wit |
| 294 | + |
| 295 | +
|
| 296 | +interface add { |
| 297 | + record error { |
| 298 | + message : string |
| 299 | + } |
| 300 | + add: func(x: u32, y: u32) -> result<u32, error>; |
| 301 | +} |
| 302 | +
|
| 303 | +world adder { |
| 304 | + import add; |
| 305 | +} |
| 306 | +``` |
| 307 | + |
| 308 | +Will generate the following type: |
| 309 | + |
| 310 | +```moonbit |
| 311 | +pub(all) suberror ComputationError { |
| 312 | + Overflow |
| 313 | +} derive(Show, Eq) |
| 314 | +``` |
| 315 | + |
| 316 | +and the following function: |
| 317 | + |
| 318 | +```moonbit |
| 319 | +pub fn add(x : UInt, y : UInt) -> Result[UInt, ComputationError] |
| 320 | +``` |
| 321 | + |
| 322 | +which you may use it as: |
| 323 | + |
| 324 | +```moonbit |
| 325 | +fn init { |
| 326 | + let a = add(1, 2).unwrap_or_error() catch { Overflow => ... } |
| 327 | +
|
| 328 | +} |
| 329 | +``` |
| 330 | + |
| 331 | +### --ignore-stub |
| 332 | + |
| 333 | +It happens when you would like to regenerate the project due to the updated |
| 334 | +interface, but you don't want the `stub` file to be touched. You may use |
| 335 | +`--ignore-stub` option to avoid such modifications. |
| 336 | + |
| 337 | +### --project-name |
| 338 | + |
| 339 | +By default, the project name is generated per the name defined in the MoonBit |
| 340 | +file. You may use this option to specify the name of the project. It can also be |
| 341 | +used if you are generating the project as part of a larger project. |
| 342 | + |
| 343 | +### --gen-dir |
| 344 | + |
| 345 | +By default, the exportation parts are generated under `gen`. You may use this |
| 346 | +option to specify another directory. |
| 347 | + |
| 348 | +## 10. References and Further Reading |
| 349 | + |
| 350 | +- [MoonBit Official Website](https://www.moonbitlang.com/) |
| 351 | +- [MoonBit Language Documentation](https://docs.moonbitlang.com/) |
| 352 | +- [WebAssembly Component Model](https://component-model.bytecodealliance.org/) |
| 353 | +- [WIT Format Specification](https://component-model.bytecodealliance.org/design/wit.html) |
| 354 | +- [`wit-bindgen` Documentation](https://github.com/bytecodealliance/wit-bindgen) |
| 355 | +- [WebAssembly Tools](https://github.com/bytecodealliance/wasm-tools) |
| 356 | +- [Wasmtime Runtime](https://wasmtime.dev/) |
| 357 | +- [Component Model Examples](https://github.com/bytecodealliance/component-docs/tree/main/component-model/examples) |
0 commit comments