Skip to content
fandhe-frontend
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)と対になる成果物として作成します。

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.rslist_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> 一つだけをフレームワーク の管理下に置きます。

<!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>

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

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

<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_csrfandhe-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-unknowncargo 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_shellfandhe-frontend-server(TASK-6.1c・#44、本書執筆時点で未マージ) の axum ハンドラから呼び出す構成に移行できます。

最小埋め込み(本書)フルスタック
呼び出し元fandhe-frontend-wasm-clientmount_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.mdmount_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 が存在するため、本書では新規の起票提案は行いません。