Type-safe MoonBit bindings for Web Platform APIs, automatically generated from WebIDL specifications.
This library provides MoonBit FFI bindings for browser APIs including:
- DOM - Document Object Model manipulation
- HTML - HTML elements and attributes
- Canvas 2D - Graphics and drawing operations
- Events - User interaction and event handling
- Fetch - HTTP requests and responses
- URL - URL parsing and manipulation
- XMLHttpRequest - Legacy HTTP requests
- SVG - Scalable Vector Graphics
- CSSOM - CSS Object Model
All bindings are automatically generated from official WebIDL specifications, ensuring type safety and API completeness.
Add this package to your MoonBit project:
moon add bikallem/webapiA simple counter application demonstrating DOM manipulation and event handling:
///|
fn main {
// Create mutable counter state
let mut count = 0
// Create count display element
let count_display : @webapi.HTMLDivElement = @webapi.document
.create_element("div")
.into()
count_display
..set_attribute("id", "count-display")
..set_attribute("style", "font-size: 3em; margin: 0.5em 0;")
..set_text_content("0")
// Update display function
let update_display = fn() {
count_display.set_text_content(count.to_string())
}
// Create increment button with click handler
let increment_btn = @webapi.document.create_element("button")
increment_btn
..set_text_content("+")
..add_event_listener(
"click",
@webapi.EventListener::new(fn(_event) {
count = count + 1
update_display()
}),
)
// Append to DOM
let app = @webapi.document.get_element_by_id("app").unwrap()
app.append_child(count_display) |> ignore
app.append_child(increment_btn) |> ignore
}Demonstrates the Canvas 2D API with gradients, shapes, and text:
///|
fn main {
// Create canvas element
let canvas : @webapi.HTMLCanvasElement = @webapi.document
.create_element("canvas")
.into()
canvas..set_width(800)..set_height(500)
@webapi.document.get_element_by_id("app").unwrap().append_child(canvas)
|> ignore
// Get 2D rendering context
let ctx : @webapi.CanvasRenderingContext2D = canvas
.get_context("2d")
.unwrap()
.into()
// Create gradient
let gradient = ctx.create_linear_gradient(0.0, 0.0, 0.0, 300.0)
gradient..add_color_stop(0.0, "#1e3c72")..add_color_stop(1.0, "#87CEEB")
// Draw with gradient
ctx..set_fill_style(gradient)..fill_rect(0.0, 0.0, 800.0, 300.0)
// Draw shapes (arc uses radians: 2π for full circle)
ctx
..set_fill_style("#228B22")
..begin_path()
..arc(400.0, 250.0, 50.0, 0.0, @math.pi * 2.0)
..fill()
// Draw text
ctx
..set_font("bold 24px sans-serif")
..set_fill_style("#FFFFFF")
..fill_text("Hello, MoonBit!", 320.0, 400.0)
}The library provides direct access to browser global objects:
// Access the document object
@webapi.document.get_element_by_id("my-id")
// Access the window object
@webapi.window.inner_width()
// Access the navigator object
@webapi.navigator.user_agent()DOM elements are returned as generic Element types. Use into() to cast to specific element types:
// Create an element and cast to specific type
///|
let canvas : @webapi.HTMLCanvasElement = @webapi.document
.create_element("canvas")
.into()
// Cast to access type-specific methods
///|
let ctx : @webapi.CanvasRenderingContext2D = canvas
.get_context("2d")
.unwrap()
.into()Create event listeners using the EventListener::new constructor:
element.add_event_listener(
"click",
@webapi.EventListener::new(fn(event) {
// Handle click event
println("Clicked!")
}),
)Most setter methods return Unit, enabling method chaining with ..:
element
..set_attribute("id", "my-element")
..set_attribute("class", "container")
..set_text_content("Hello!")Many methods have optional parameters using MoonBit's ? syntax:
// With default options
document.create_element("div")
// With explicit options
document.create_element("div", options=ElementCreationOptions::new(is="custom-div"))This library is automatically generated from WebIDL specifications using a custom code generator written in MoonBit. The generator transforms WebIDL definitions into idiomatic MoonBit code.
| WebIDL Type | MoonBit Type |
|---|---|
DOMString, USVString |
String |
boolean |
Bool |
long, short |
Int |
unsigned long |
UInt |
long long |
Int64 |
double, float |
Double, Float |
any, object |
JsValue |
sequence<T> |
Array[T] |
Promise<T> |
JsPromise[T] |
T? (nullable) |
T? (Option) |
WebIDL interfaces are converted to MoonBit traits and external types:
WebIDL:
interface Element : Node {
attribute DOMString id;
DOMString? getAttribute(DOMString name);
undefined setAttribute(DOMString name, DOMString value);
};Generated MoonBit:
// External type wrapping JavaScript object
///|
#external
pub type Element
// Trait defining interface methods
///|
pub trait TElement: TNode {
id(self : Self) -> String = _
set_id(self : Self, id : String) -> Unit = _
get_attribute(self : Self, name : String) -> String? = _
set_attribute(self : Self, name : String, value : String) -> Unit = _
}
// Implementation using FFI
///|
extern "js" fn element_get_attribute_ffi(
obj : JsValue,
name : JsValue,
) -> JsValue = "(obj, name) => obj.getAttribute(name)"
///|
impl TElement with get_attribute(self : Self, name : String) -> String? {
let result = element_get_attribute_ffi(
TJsValue::to_js(self),
TJsValue::to_js(name),
)
if JsValue::is_null(result) {
None
} else {
Some(result.unsafe_cast())
}
}WebIDL enums become MoonBit enums with string conversion:
WebIDL:
enum ShadowRootMode { "open", "closed" };Generated MoonBit:
///|
pub(all) enum ShadowRootMode {
Open
Closed
} derive(Eq, Show)
///|
pub impl TJsValue for ShadowRootMode with to_js(self : ShadowRootMode) -> JsValue {
match self {
ShadowRootMode::Open => TJsValue::to_js("open")
ShadowRootMode::Closed => TJsValue::to_js("closed")
}
}
///|
pub fn ShadowRootMode::from(value : String) -> ShadowRootMode? {
match value {
"open" => Some(ShadowRootMode::Open)
"closed" => Some(ShadowRootMode::Closed)
_ => None
}
}WebIDL dictionaries become constructor functions:
WebIDL:
dictionary EventInit {
boolean bubbles = false;
boolean cancelable = false;
};Generated MoonBit:
///|
#external
pub type EventInit
///|
pub fn EventInit::new(bubbles? : Bool, cancelable? : Bool) -> EventInit {
event_init_ffi(opt_to_js(bubbles), opt_to_js(cancelable))
}Interface inheritance is modeled using trait bounds:
// Element extends Node, which extends EventTarget
pub trait TElement: TNode { ... }
pub trait TNode: TEventTarget { ... }
pub trait TEventTarget { ... }
// Implementations chain correctly
pub impl TElement for Element
pub impl TNode for Element
pub impl TEventTarget for Element- MoonBit toolchain (
moon) - Node.js and npm
# Install npm dependencies (WebIDL specs)
npm install
# Generate bindings from WebIDL
cd webapi_gen && moon run main
# Type-check generated code
moon check --target js
# Format code
moon fmtThe generator processes the following WebIDL specifications:
- DOM
- HTML
- Fetch
- URL
- XMLHttpRequest
- CSSOM
- CSSOM-View
- Geometry
- FileAPI
- Performance Timeline
- High Resolution Time
- Referrer Policy
- SVG
- Trusted Types
- UI Events
Apache-2.0