Skip to content
fandhe-frontendcore v0.4.3
GitHub

最小埋め込みガイド(TASK-7.1b)

本ドキュメントは REQ-7(docs/spec/04-requirements.md REQ-7 節「最小埋め込み〜フルスタックの共通コア(思想のグラデーション)」)の受け入れ基準「最小埋め込み構成(既存 HTML ページの <div> へのマウント)とフルスタック構成(SSR+ルーティング)が、コンポーネントロジックに一切分岐を持たない同一関数を呼び出すこと」を、利用者が実際に手を動かして体験するためのガイドです。TASK-7.1(#51)の子タスクとして、templates/embed/embed.html(TASK-7.1a・#52)と対になる成果物として作成します。

Note

本書のステータスと前提: 本書は fandhe-frontend-core・fandhe-frontend-app(いずれもマージ済み)の公開 API と docs/api/hydration-api.md(TASK-6.2a、設計確定済み)の凍結契約を前提として執筆しています。templates/embed/embed.html(TASK-7.1a・#52)はマージ済みで、3.1 節のコード例は実物と一致させています。fandhe-frontend-wasm-client クレート本体の実装(TASK-6.2b・#48)・fandhe-frontend-server(TASK-6.1c・#44)は本書更新時点ではいずれも未マージで並行進行中です。したがって本書に登場する mount_csr() / hydrate() の呼び出し例は、クレート実装済みコードの引用ではなく docs/api/hydration-api.md で確定した設計契約として提示しています。実装が完了しマージされた時点で、本書の記述と実物に乖離があれば、各設計確定書・実装を正として本書を追随更新します。

1. 目的と対象読者

このフレームワークは「思想のグラデーション」を中核価値の一つとして掲げています。既存の静的 HTML ページの一部分(アイランド的な <div> 一つ)だけをフレームワークで動かす最小埋め込み構成から始めて、プロジェクトが成長したらフルスタック(SSR+ルーティング)構成へ移行できます。このとき、コンポーネントの実装コードを書き直す必要はありません。

対象読者は、次のいずれかに当てはまる開発者です。

  • 既存の静的サイト・レガシーページの一部だけを部分的にリッチにしたい
  • 「最初は小さく、後で大きく」育てたいが、将来的なフレームワーク乗り換えコストを避けたい
  • REQ-7 の受け入れ基準(分岐なしの共通コア)を具体的なコードで確認したい

トレーサビリティ: REQ-7(docs/spec/04-requirements.md)/TASK-7.1(docs/spec/05-tasks.md)/PoC-3(docs/spec/03-poc/rendering-web-standards/、差別化空白 E への対応)。

2. 仕組み — 共通コアと分岐なし契約

最小埋め込み構成とフルスタック構成の違いは「コンポーネント関数をどこから呼ぶか」だけであり、コンポーネント自身の実装には一切現れません。

        fandhe-frontend-app(list_page / detail_page / page_shell)
                    ▲                    ▲
                    │                    │
     CSR(最小埋め込み)           SSR/SSG(フルスタック)
     fandhe-frontend-wasm-client の            fandhe-frontend-server(axum ハンドラ想定)
     mount_csr("app-list")         が同じ関数を呼び出す
     が同じ関数を呼び出す

crates/app/src/lib.rs の list_page / detail_page / page_shell は、SSR・SSG・CSR のいずれのモードからも同一関数がそのまま呼ばれることを前提に設計されています(三モード契約、REQ-6)。関数側にモード分岐の if や #[cfg(...)] は存在しません。呼び出し側(CSR なら fandhe-frontend-wasm-client、SSR/SSG なら fandhe-frontend-server)が変わるだけで、コンポーネントの記述は共通です。

この契約の一次情報源は crates/app/src/lib.rs の rustdoc(cargo doc -p fandhe-frontend-app --open)です。本ガイドはそこへの導線であり、契約そのものの詳細は rustdoc を参照してください。

3. 最小埋め込みの手順

最小埋め込みは、既存の静的 HTML ページに次の 3 要素を足すだけで成立します。

  1. マウントポイントとなる <div id="..."> を置く
  2. <script type="module"> から WASM モジュールを init() し、mount_csr("...") を呼ぶ
  3. WASM モジュールをビルドし、ページと同一オリジンで配信する

3.1 マウントポイントを置く

docs/spec/05-tasks.md(TASK-7.1 節)が定める製品版の正典テンプレートは templates/embed/embed.html(TASK-7.1a・#52、マージ済み)です。以下は同ファイルの抜粋(実物)です。既存の素の HTML はそのままに、<div> 一つだけをフレームワークの管理下に置きます。

HTML
<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="utf-8">
  <title>既存ページへの部分埋め込みサンプル</title>
</head>
<body>
  <h1>既存の静的ページ(フレームワーク外で書かれた想定)</h1>
  <p>この段落は素の HTML であり、フレームワークの管理下にありません。</p>

  <!-- ここだけ fandhe-frontend-app の CSR コンポーネントで埋める(アイランド的な最小構成) -->
  <div id="app-list"></div>

  <p>この段落も素の HTML です。フレームワークは上の div の中身にしか関与しません。</p>
</body>
</html>

Note

正典テンプレートの所在: 上記は templates/embed/embed.html の抜粋です。コメント(役割・責務境界・呼び出し契約の説明)を含む完全な内容は同ファイルを直接参照してください。本節のコード例と実物が乖離した場合は、実物(templates/embed/embed.html)を正とします。

Note

fw new --template embed での生成(イシュー #410): 上記テンプレートを手動コピーする代わりに fw new <project-name> --template embed を実行すると、embed.html(上記正典テンプレートとバイト一致)と structure.toml(fw gate が唯一の情報源として読む静的専用マニフェスト)の 2 ファイルが決定的に生成されます。生成直後に fw gate --project <project-name> を実行すると、cargo パッケージを持たない静的単一ファイル構成として無編集で PASS します(詳細は docs/design/fw-new-design.md §3.3・docs/design/gate-design.md §2.5 を参照)。手動コピーの手順(本節)は既存ページへ後から部分埋め込みを追加する場合に引き続き使えます。

3.2 WASM を初期化してマウントする

<script type="module"> から WASM を初期化し、mount_csr(root_id) を呼びます。mount_csr は docs/api/hydration-api.md 第 3 節で凍結された API で、指定 ID の要素に対して fandhe_frontend_app のページ関数 → fandhe_frontend_core::render() の既定エスケープ済み出力を反映します(CSR が SSR/SSG と同一関数を呼ぶという REQ-6 受け入れ基準そのものです)。

HTML
<script type="module">
  import init, { mount_csr } from "./wasm/fandhe_frontend_wasm_client.js";
  async function main() {
    await init();
    mount_csr("app-list");
  }
  main();
</script>

mount_csr は fandhe-frontend-wasm-client クレート(TASK-6.2b・#48、本書執筆時点で未マージ)が提供する予定の API です。設計契約(シグネチャ・不変条件)は docs/api/hydration-api.md 第 3 節・第 6 節で確定済みですが、クレート本体・実際にビルドされる .js/.wasm 成果物は #48 のマージを待って利用可能になります。

3.3 ビルド・配置

WASM モジュールのビルド手順の正式な確定はクレート実装(#48)と関連テスト整備(TASK-6.2c・#49)の完了後に固まります。本書執筆時点で docs/api/hydration-api.md が示す方向性は次のとおりです。

  • ターゲットは wasm32-unknown-unknown(cargo build --target wasm32-unknown-unknown -p fandhe-frontend-wasm-client でコンパイル成立を確認する、docs/api/hydration-api.md 第 7 節)
  • wasm-pack 等のツールで JS グルーコード(init / mount_csr のバインディング)を生成する運用を想定
  • 生成物はページと同一オリジンで配信する(4 節・5 節参照)

具体的なビルドコマンド・出力パスの確定手順は #48/#49 の実装完了後に本書へ追記します。現時点では固定の手順書として案内できる段階にないため、本書では方向性の提示にとどめます。

4. フルスタック構成への移行パス

プロジェクトが成長し、SSR やルーティングが必要になったら、同じ list_page / detail_page / page_shell を fandhe-frontend-server(TASK-6.1c・#44、本書執筆時点で未マージ)の axum ハンドラから呼び出す構成に移行できます。

最小埋め込み(本書)フルスタック
呼び出し元fandhe-frontend-wasm-client の mount_csrfandhe-frontend-server の SSR/SSG ハンドラ
呼び出す関数fandhe_frontend_app::list_page 等同じ fandhe_frontend_app::list_page 等
コンポーネント側の分岐なしなし
出力の反映先<div> の innerHTML(CSR)HTTP レスポンス本文(SSR/SSG)

移行時にコンポーネントの記述(crates/app/src/lib.rs 相当のコード)を書き直す必要はありません。書き直すのは「どこから呼ぶか」(埋め込みページの <script> か、サーバーのルーティングハンドラか)だけです。これが REQ-7 の「フレームワークを乗り換えたくない」というユーザーストーリーに対する具体的な回答です。

5. セキュリティ不変条件

最小埋め込み構成であっても、既定エスケープ(REQ-1)はフルスタック構成と同じ強度で維持されます。

  • mount_csr が DOM へ反映する内容は、fandhe_frontend_app のページ関数 → fandhe_frontend_core::render() を経由した既定エスケープ済みの HTML のみです。format! によるタグ文字列の直接組み立てや、ユーザー入力を直接 innerHTML に代入するコードは、フレームワーク側にも埋め込みページ側にも書いてはいけません(.claude/rules/coding-rust.md「HTML 文字列の直接組み立て禁止」)。
  • fandhe_frontend_core::raw_html() は既定エスケープを迂回する明示的オプトイン API です。信頼できない入力(ユーザー投稿・外部 API のレスポンス等)を raw_html() に渡してはいけません。埋め込みページのコンポーネントであっても、この制約はフルスタック構成と同一です。
  • WASM モジュール(.wasm / グルー .js)は埋め込み先ページと同一オリジンで配信することを前提とします。外部 CDN からの読み込みはサプライチェーン面の拡大につながるため推奨しません(.claude/rules/security.md)。
  • 責務境界の明示: フレームワークが安全性を保証するのは、フレームワークが管理する <div>(マウントポイント)の内側だけです。埋め込みページの残りの部分(素の HTML・他の <script> タグ・ページ全体の CSP 設定等)はフレームワークの管理下になく、その安全性は埋め込みページの作者の責務です。この境界を誤解し、「フレームワークを使えばページ全体が自動的に安全になる」という期待を持たないでください。

6. v1 スコープと制約

REQ-7 の受け入れ基準にあるとおり、v1 の共通コアはパスマッチングによる基本的なルート解決(TASK-7.2)までを提供します。ネストレイアウト・データローディング・高度なルーティング等のフルスタック機能一式の完全実装は本要件の対象外です(docs/spec/04-requirements.md の対象外事項一覧と整合)。

「最小埋め込みとフルスタックがコンポーネントロジックに分岐を持たない」ことの機械的検証(静的解析・テスト)は TASK-7.3(crates/core/tests/no_branching_across_modes.rs)のスコープであり、本ガイドの対象外です。

7. 関連文書・並行タスクとの整合

本書は次の一次情報源を前提とし、そこに矛盾があれば各文書・実装を正とします。

  • crates/app/src/lib.rs の rustdoc(三モード契約・既定エスケープの引き継ぎ、マージ済み)
  • docs/api/hydration-api.md(mount_csr / hydrate の設計確定、TASK-6.2a・マージ済み)
  • docs/guides/component-authoring.md(コンポーネント記述の一般的な書き方)
  • templates/embed/embed.html(TASK-7.1a・#52、マージ済み。3.1 節の正典テンプレート)

以下は本書更新時点で未マージ・並行進行中であり、マージされ次第、本書の該当箇所(3 節・4 節)を追随更新します。

未マージの成果物対応タスク影響する本書の節
fandhe-frontend-wasm-client クレート実装TASK-6.2b(#48)3.2・3.3 節(ビルド・配置)
fandhe-frontend-server(SSR/SSG エントリ)TASK-6.1c(#44)4 節(移行パス)

8. スコープ外(本書では扱わない事項)

  • mount_csr / hydrate の実装(TASK-6.2b・#48)
  • fandhe-frontend-server の SSR/SSG エントリ(TASK-6.1c・#44)
  • 分岐なしの静的解析・テストによる機械検証(TASK-7.3)
  • View Transitions API の <meta> タグ・薄いラッパー(TASK-8.1、docs/spec/04-requirements.md REQ-8)

これらはいずれも既存タスク・Issue が存在するため、本書では新規の起票提案は行いません。