Sol Framework

Sol Framework

実験的: Solは開発中です。APIは変更される可能性があります。

SolはLuna UIとMoonBitで構築されたフルスタックSSRフレームワークです。Island Architectureによるサーバーサイドレンダリングと部分的ハイドレーションを提供します。

特徴

  • Hono統合 - 高速で軽量なHTTPサーバー

  • Island Architecture - スマートなトリガーで最小限のJavaScriptを配信

  • ファイルベースルーティング - ディレクトリ構造からページとAPIルートを生成

  • 型安全 - MoonBitの型がサーバーからブラウザまで流れる

  • ストリーミングSSR - 非同期コンテンツのストリーミング対応

  • CSRナビゲーション - sol-linkによるSPAライクなページ遷移

  • ミドルウェア - Railway Oriented Programmingベースのミドルウェア

  • Server Actions - CSRF保護付きのサーバーサイド関数

  • ネストされたレイアウト - 階層的なレイアウト構造

  • SSGモード - Sol SSGによる静的サイト生成(設定から自動検出)

プロジェクト構造

myapp/
├── moon.mod.json           # MoonBitモジュール
├── package.json            # npmパッケージ
├── sol.config.json         # Sol設定
├── app/
│   ├── server/             # サーバーコンポーネント
│   │   ├── moon.pkg.json
│   │   └── routes.mbt      # routes() + config() + ページ
│   ├── client/             # クライアントコンポーネント (Islands)
│   │   └── counter/
│   │       └── counter.mbt
│   └── __gen__/            # 自動生成 (sol generate)
└── static/
    └── loader.min.js       # Islandローダー

CLIリファレンス

sol new <name>

新規プロジェクトを作成。

sol new myapp --user mizchi         # mizchi/myapp パッケージを作成
sol new myapp --user mizchi --dev   # ローカル luna パスを使用

sol dev

開発サーバーを起動。以下を自動実行:

  1. sol generate --mode dev - コード生成

  2. moon build - MoonBitビルド

  3. rolldown - クライアントバンドル

  4. サーバー起動

sol dev              # デフォルトポート 3000
sol dev --port 8080  # ポート指定
sol dev --clean      # キャッシュクリアしてビルド

sol build

本番用ビルド。.sol/prod/に出力。

sol build                 # JSターゲット (デフォルト)
sol build --target wasm   # WASMターゲット
sol build --clean         # キャッシュクリアしてビルド

sol serve

本番ビルドを配信。sol buildが必要。

sol serve              # デフォルトポート 3000
sol serve --port 8080  # ポート指定

SolRoutes定義

宣言的なルート定義:

pub fn routes() -> Array[SolRoutes] {
  [
    // ページルート
    SolRoutes::Page(
      path="/",
      handler=PageHandler(home_page),
      title="Home",
      meta=[],
    ),
    // GET APIルート
    SolRoutes::Get(
      path="/api/health",
      handler=ApiHandler(api_health),
    ),
    // ネストされたレイアウト
    SolRoutes::Layout(
      segment="admin",
      layout=admin_layout,
      children=[
        SolRoutes::Page(path="/admin", handler=PageHandler(admin_dashboard), title="Admin"),
      ],
    ),
    // ミドルウェア適用
    SolRoutes::WithMiddleware(
      middleware=[@middleware.cors(), @middleware.logger()],
      children=[...],
    ),
  ]
}

SolRoutesバリアント

バリアント説明
Pageページルート(HTMLレスポンス)
GetGET APIルート(JSONレスポンス)
PostPOST APIルート(JSONレスポンス)
Layoutネストされたレイアウトグループ
WithMiddlewareミドルウェアを適用したルートグループ

ミドルウェア

Railway Oriented Programmingベースのミドルウェアシステム。

基本的な使い方

let middleware = @middleware.logger()
  .then(@middleware.cors())
  .then(@middleware.security_headers())

SolRoutes::WithMiddleware(
  middleware=[middleware],
  children=[...],
)

組み込みミドルウェア

ミドルウェア説明
logger()リクエストログ
cors()CORSヘッダー
csrf()CSRF保護
security_headers()セキュリティヘッダー
nosniff()X-Content-Type-Options
frame_options(value)X-Frame-Options

Server Actions

CSRF保護付きのサーバーサイド関数。

let submit_handler = ActionHandler(async fn(ctx) {
  let body = ctx.body
  ActionResult::ok(@js.any({ "success": true }))
})

pub fn action_registry() -> ActionRegistry {
  ActionRegistry::new(allowed_origins=["http://localhost:3000"])
    .register(ActionDef::new("submit-form", submit_handler))
}

ActionResultタイプ

タイプ説明
Success(data)成功、JSONデータを返す
Redirect(url)成功、リダイレクト
ClientError(status, msg)クライアントエラー (4xx)
ServerError(msg)サーバーエラー (5xx)

Islandコンポーネント

Islandはサーバーとクライアントで共有されるコンポーネント:

pub fn counter(count : Signal[Int]) -> @luna.Node[CounterAction] {
  div(class="counter", [
    span(class="count-display", [text_signal(count)]),
    button(onclick=@luna.action(Increment), [text("+")]),
  ])
}

Hydrationトリガー

トリガー説明
Loadページロード時即座
IdlerequestIdleCallback時
VisibleIntersectionObserver検知時
Media(query)メディアクエリマッチ時
None手動トリガー

CSRナビゲーション

sol-link属性を持つリンクはCSRで処理:

a(href="/about", attrs=[("sol-link", @luna.attr_static(""))], [text("About")])

クリック時の動作:

  1. sol-nav.jsがクリックをインターセプト

  2. fetchで新しいページのHTMLを取得

  3. <main id="main-content">の内容を置換

  4. History APIでブラウザ履歴を更新

  5. 新しいページのIslandをhydrate

ストリーミングSSR

非同期コンテンツのストリーミング:

@luna.vasync(async fn() {
  let data = fetch_data().await
  div([text(data)])
})

SSGモード

プロジェクトにssgまたはdocsセクションを持つsol.config.jsonがある場合、SolはSSGモードを自動検出します。

# SSGプロジェクトを作成
sol new my-docs --ssg

# dev/buildコマンドは同じように動作
sol dev    # HMR付きSSG開発サーバーを起動
sol build  # 静的サイトを生成
sol lint   # SSGコンテンツをリント

SSG固有の機能と設定については、Sol SSGを参照してください。

関連項目

  • Luna UI - コアリアクティビティの概念

  • Sol SSG - 静的サイト生成(Sol CLI経由で実行)