articles 【Astrobit】MoonBitでAstroのコンポーネントを書いてみた

【Astrobit】MoonBitでAstroのコンポーネントを書いてみた

Astroにはisland機能でReactやSvelteなどで書いたcomponentが使用できるように公式でインテグレーションが用意されているのですが、その他のUIフレームワークでもcomponentを書くためにカスタムインテグレーションが作成できるようになっています。

そこで、moonbitのコードでastroのcomponentを記述するUIフレームワークAstrobitを作成しました。リアクティブな動作はmizchiさんの signals を用いています。これによりsolid.jsのようなsignalベースのDOM更新が可能になっています。

使い方(例)

具体的にはastrobit-sampleリポジトリのREADMEや、astrobit本体を参照してください。

環境構築は(結構簡単にできるようになっているつもりなので)省略して、コア部分を紹介します

src/components/counter.mbtなどに以下のようにコンポーネントを書きます

fn counter(props : @dom.Props) -> @a.Node {
  let initial = props.get_int("initial")
  let count = @signals.signal(initial)
  @a.div(attrs={ "class": "counter-widget" }, [
    @a.div(attrs={ "class": "counter-display" }, [
      @a.span(
        attrs={ "class": "counter-value" },
        @a.dyn_text(fn() { count.get().to_string() }),
      ),
    ]),
    @a.div(attrs={ "class": "counter-controls" }, [
      @a.button(attrs={ "class": "counter-btn btn-dec" }, "−")
      |> @a.on_click(fn(_) { count.update(fn(n) { n - 1 }) }),
      @a.button(attrs={ "class": "counter-btn btn-reset" }, "Reset")
      |> @a.on_click(fn(_) { count.set(initial) }),
      @a.button(attrs={ "class": "counter-btn btn-inc" }, "+")
      |> @a.on_click(fn(_) { count.update(fn(n) { n + 1 }) }),
    ]),
  ])
}
///|
pub fn mount(element : @dom.Element, props : @dom.Props) -> Unit {
  @a.mount_dom(element, counter(props))
}

///|
pub fn render(props : @dom.Props) -> String {
  @a.render_to_html(counter(props))
}

///|
pub fn hydrate(element : @dom.Element, props : @dom.Props) -> Unit {
  @a.hydrate_dom(element, counter(props))
}

あとはsrc/components/Welcome.astroなどのファイルから

import Counter from './counter/counter.mbt'

// ...

<Counter client:load initial={0} />

このように書くだけで使用できます

ディレクトリ構造はこんな感じです

src/
├── assets
│   ├── astro.svg
│   └── background.svg
├── components
│   ├── Welcome.astro
│   ├── codeSnippets.ts
│   ├── counter
│   │   ├── counter.mbt
│   │   └── moon.pkg
│   └── todos
│       ├── moon.pkg
│       └── todos.mbt
├── layouts
│   └── Layout.astro
└── pages

Moonbitもっと使いたい

個人的にはwasm-gcをターゲットとしたビルドに惹かれてMoonbitを触ってみたので、wasmを使用した面白い例も書いてみたいです。Astrobitの前に、spin frameworkを使用したwasmで動作するAPIサーバをClaudeに書かせたりしたのですが、こちらももう少しちゃんと公開する価値のあるものになれば公開したいです。

最後までお読みいただき、ありがとうございます