MoonBit port of the tutuca UI framework (value language, templates, vdom, components, app runtime, lint, CLI)
| Layer | Package(s) | What it does |
|---|---|---|
| Value language | core/ — marianoguerra/tutuca/core (value_*.mbt, path_*.mbt) | The tutuca value syntax — parse/tokenize/eval — plus the reactive path/dispatch system (COW spine rebuild, handler dispatch, change sets). |
| Templates | anode/ | Parses the HTML-ish view syntax into an AST: attributes, directives, x- ops, macros, whitespace handling, optimization. |
| Virtual DOM | vdom/ (+ vdom/memdom, vdom/browser, vdom/wasm) | Builds and incrementally morphs a VDOM against any DOM implementing the DomNode trait. |
| Render | render/ | Turns a parsed view + a value stack into a @vdom.Vdom tree (loops, scopes, event-path metas, teleport). |
| Components / App | component/, app/ (+ app/browser, app/wasm), transactor/ | Typed-state component definitions (a plain derive(ToJson, FromJson) struct + one Dispatch update match), the app runtime, and the transactor that routes events at the root and settles state. |
| Tooling | lint/, inspector/, viewgen/, cli/ | The linter (parse-issue rules + a WHATWG-tokenizer structural HTML linter), a schema inspector, the ahead-of-time view compiler, and the native tutuca CLI. |
| Testing | testing/harness | A reusable harness to mount and drive a ModuleDef on the in-memory DOM. |
| Demos & docs | demo/, playground/, storybook/ | 51 ported examples (storybook/examples/), browser/CLI/wasm demo hosts, an in-browser playground, and a compiled storybook gallery. |
moon run --target native cmd/main -- gen-views demo/counterlib/counter.html --name Counter
# -> demo/counterlib/counter_view_gen.mbt (the view vocabulary as types)
# -> demo/counterlib/counter_view_ir_gen.mbt (the compiled `counter_views()`)
# both checked in; regenerate, never edit
update=(s : CounterState, msg, _ctx) => match CounterMsg::of_dispatch(msg) {
Some(Add(d)) => ... // `d` is a Double: `@on.click="add 1"`
Some(SetLabel(l)) => ... // `l` is a String: `@on.input="setLabel value"`
Some(ResetCount) => None
Some(Unknown(_, _)) | None => None
}| id | |
|---|---|
| (none) | the single unnamed component's main view |
| row | …its row view |
| Counter:main | the Counter component's main view |
| Counter | shorthand for Counter:main |
| macro:icon | a macro shared by every component in the file |
<template id="macro:icon" data-size="'24'" data-color="'currentColor'">
<svg :width="^size" :height="^size" :stroke="^color"><path :d="^path"></path></svg>
</template>
<template id="Gallery">
<x:icon :size=".size" :path=".heart"></x:icon>
</template>@component.component(
name="Counter",
views=counter_views(),
init=CounterState::{ label: "Counter", count: 0, history: [] },
update=...,
)moon run --target native cmd/dev -- gen-views # generate + fmt
git diff --exit-code # drift checktutuca watch demo/counterlib # or a file, or bare for the whole project| Tab | |
|---|---|
| Component | the MoonBit you write |
| View | the .html its views live in (name the component with <!-- name: Counter -->) |
| Generated | read-only: what gen-views makes of the View tab, updating as you type |
///|
test "render a tree into memdom" {
let doc = @memdom.document()
let container = @vdom.DomNode::create_element(doc, "DIV", None, None)
let opts = @vdom.RenderOpts::new(doc)
let prev = @vdom.render(
@vdom.h("ul", attrs={ "className": Str("list") }, childs=[
@vdom.h("li", key="a", childs=[@vdom.text("one")]),
@vdom.h("li", key="b", childs=[@vdom.text("two")]),
]),
container,
opts,
)
// incremental re-render: morphs in place, preserves keyed nodes
let _ = @vdom.render(
@vdom.h("ul", attrs={ "className": Str("list") }, childs=[
@vdom.h("li", key="b", childs=[@vdom.text("two")]),
@vdom.h("li", key="a", childs=[@vdom.text("one!")]),
]),
container,
opts,
prev~,
)
inspect(
container.to_html(),
content=(
#|<div><ul class="list"><li>two</li><li>one!</li></ul></div>
),
)
}///|
let opts = @browser.window_opts()
///|
let container = @browser.BrowserNode::from_element(
@dom.window().document().getElementById("app").unwrap(),
)
///|
let prev = @vdom.render(view(state), container, opts)moon run --target native cmd/dev -- setup # npm install (happy-dom) + enable git hooks
moon run --target native cmd/dev -- check # moon check across wasm-gc, js, native
moon run --target native cmd/dev -- test # moon test across the three targets
moon run --target native cmd/dev -- build # moon build wasm-gc + native CLI + js
moon run --target native cmd/dev -- dist # assemble a self-contained dist/Install
Download zipMoonBit port of the tutuca UI framework (value language, templates, vdom, components, app runtime, lint, CLI)