Color mode

HonoX Integration

@riebeckite/honox は、Riebeckite Core と HonoX / Vite を接続する integration です。

Core はコンテンツやプラグインの処理を担当しますが、HonoX の route や Vite の build 方法については知りません。

その間を接続するのが @riebeckite/honox です。

Diagram source
text
flowchart LR
    A["Riebeckite Core<br/>Content / Plugin / Manifest"]
    B["@riebeckite/honox<br/>Integration"]
    C["HonoX / Vite<br/>Application"]
 
    A --> B
    B --> C

主に次の処理を担当します。

  • application root / config の解決
  • Vite の development / build
  • SSG の設定
  • plugin / theme の style entry 生成
  • client entry の生成
  • Riebeckite のコンテンツと HonoX application の接続

これにより、通常の Site は Riebeckite 内部の Vite / HonoX 設定を毎回組み立てる必要がありません。

このページの構成

基本的な使い方

通常は vite.config.ts で riebeckiteVite() を登録します。

ts
import { riebeckiteVite } from "@riebeckite/honox";
import { defineConfig } from "vite";
 
export default defineConfig({
  plugins: [honox({ ... }), ...riebeckiteVite(), build()],
});

riebeckiteVite() は通常の Site 向けの higher-level helper です。

Riebeckite の Vite plugin を追加するだけでなく、次の設定もまとめて行います。

  • SSG entry の設定
  • extension mapping
  • SSR に必要な external dependency の設定
  • plugin / theme の生成 entry の接続

HonoX plugin、deployment 用 build plugin、Tailwind など、Site 自身が必要とする Vite plugin と組み合わせて利用できます。

Root と Config

riebeckiteVite() では、必要に応じて次の場所を指定できます。

Option 意味
appRoot Site application の基準ディレクトリ
configRoot Riebeckite config を探す基準
configFile 使用する config file
workspaceRoot monorepo 開発時の workspace root

通常は指定する必要はありません。

appRoot の既定値は Vite root、configRoot の既定値は appRoot です。

Riebeckite config は configRoot を基準に読み込みます。

一方、

ts
content: {
  directory: "./content",
}

のような content directory は appRoot を基準に解決します。

resolveHonoxApplication() は、これらの root と解決済み config をまとめて返します。

CLI と Vite がこの共通モデルを利用することで、それぞれが異なる方法で application を解決しないようにしています。

workspaceRoot

workspaceRoot は、Riebeckite 自体を monorepo で開発するときに source package alias を利用するための設定です。

npm から Riebeckite をインストールした通常の Site では必要ありません。

その場合は Site 自身の node_modules から package が解決されます。

.riebeckite に生成されるファイル

Integration は application 内の

text
app/.riebeckite/

へ、plugin や theme を接続するためのファイルを生成します。

たとえば plugin style や theme style です。client module は .riebeckite には生成されず、virtual module として提供されます。

Diagram source
text
flowchart LR
    A["Installed Plugins / Themes"]
    B["@riebeckite/honox"]
    C["app/.riebeckite/"]
    D["Site Application"]
 
    A --> B
    B -->|"generated entries"| C
    C --> D

.riebeckite は integration が管理する生成物です。

Site の source code として直接編集しないでください。

Bootstrap module

generated Site は Framework 所有の bootstrap module を import します。解決済み config は virtual:riebeckite/config、構成済みの content runtime は virtual:riebeckite/content です。そのため app/config.ts、app/content.ts、app/constants/paths.ts は生成されません。SSG entry の app/server.ts はこの2つを re-export し、riebeckiteSsg はそこから manifest を見つけます。

Vite の外で動く script(tsx で起動する Node script など)は @riebeckite/honox/runtime の resolveHonoxConfig で同じ config を解決できます。

Bootstrap module

generated Site は Framework 所有の bootstrap module を import します。解決済み config は virtual:riebeckite/config、構成済みの content runtime は virtual:riebeckite/content です。そのため app/config.ts、app/content.ts、app/constants/paths.ts は生成されません。SSG entry の app/server.ts はこの2つを re-export し、riebeckiteSsg はそこから manifest を見つけます。

Vite の外で動く script(tsx で起動する Node script など)は @riebeckite/honox/runtime の resolveHonoxConfig で同じ config を解決できます。

Lower-level API

より細かく integration を制御したい場合は、lower-level API も利用できます。

  • riebeckite
  • riebeckiteSsg
  • riebeckiteSsgExtensionMap
  • createRiebeckiteSsg

通常の Site では riebeckiteVite() を利用し、独自の build integration が必要な場合のみ lower-level API を利用してください。

Routing と SSG

HonoX の runtime routing と静的生成では、同じ URL が同じページとして扱われる必要があります。

特に catch-all route がある場合、SSG の route 列挙に注意が必要です。

Riebeckite はこのために2つの helper を提供します。

contentRouteSsgParams

ts
contentRouteSsgParams(routePath, params)

hono/ssg の ssgParams の代わりとして使用します。

この helper は、その route 自身に属する params だけを返します。

たとえば、

text
/:slug{.+}

という catch-all route があっても、

text
/tags/:slug{.+}

に属するページまで横取りしません。

ssgEnumerableHandler

ts
ssgEnumerableHandler(handler)

next() を使って sibling route に処理を渡す handler を、SSG の列挙対象として残すための helper です。

Hono は middleware 形式の handler を通常 SSG の列挙対象から外すため、この差を補います。

Plugin Page

Plugin は通常の content とは別に、独自のページを提供できます。

その場合は、

ts
resolveContentRoute(manifest, path)

ではなく、

ts
resolveRiebeckiteRoute(content, path)

を使用します。

SSG params には、

ts
pluginPageSsgParams(content)

を追加します。

生成された Site の catch-all route は、これらをまとめた resolveRiebeckiteContentRequest(c, content) を使用します。この helper が content / Plugin Page / redirect / not-found を解決し、htmlLanguage と headTags を context へ設定するため、Site は返された結果を自身の composition に渡すだけで済みます。root / も同じ mechanics を共有する resolveRiebeckiteHomeRequest(c, content) で解決します。

Route resolver は次の順序で URL を解決します。

Diagram source
text
flowchart TD
    A["Request Path"]
    B{"Plugin Page?"}
    C["Plugin Page"]
    D{"Content?"}
    E["Content"]
    F{"Redirect?"}
    G["Redirect"]
    H["Not Found"]
 
    A --> B
    B -->|Yes| C
    B -->|No| D
    D -->|Yes| E
    D -->|No| F
    F -->|Yes| G
    F -->|No| H

Plugin Page の body は意図的に文字列として扱います。

Site が持つ既存の document frame 内へ描画し、page.headTags も Site の frame へ渡します。

この仕組みにより、Plugin が独自ページを提供するためだけに HonoX の route file を追加する必要はありません。

Integration の境界

Riebeckite の routing では、すでに解決された公開 URL を使用します。

基本的には、

text
byPermalink
    ↓
redirects

の順で request を解決します。

filesystem path やディレクトリ構造から公開 URL を逆算しません。

また、slug はコンテンツを内部で検索するためのキーです。

実際に公開される URL は、解決済みの permalink です。

責務のまとめ

Riebeckite 全体では、次のように責務を分離します。

Diagram source
text
flowchart LR
    Core["Core<br/>Content / Manifest / Plugin API"]
    Integration["HonoX Integration<br/>Vite / SSG / Route Resolution"]
    Plugin["Plugin<br/>Content Extension / Page / Asset / Client Entry"]
    Site["Site Application<br/>Route / Shell / UI / Island / CSS"]
 
    Core --> Integration
    Plugin --> Core
    Integration --> Site
    Plugin -. "提供した情報を<br/>Site が配置" .-> Site

HonoX / Vite / Cloudflare 固有の処理は integration または Site Application に閉じます。

Core は HonoX routing を所有しません。

Plugin はページ、アセット、client entry などを提供できますが、Site 全体の route composition や UI 構造は所有しません。

Core はコンテンツを扱い、Integration は HonoX と接続し、Plugin は機能を提供し、Site が最終的な表示を決める、という境界を維持してください。

build state の扱いについては Build system、package ごとの責務については Architecture を参照してください。

History

1 changesCollapseExpand
1 + ---
2 + title: HonoX Integration
3 + sidebar:
4 + label: HonoX Integration
5 + ---
6 + # HonoX Integration
7 +
8 + `@riebeckite/honox` は、Riebeckite Core と HonoX / Vite を接続する integration です。
9 +
10 + Core はコンテンツやプラグインの処理を担当しますが、HonoX の route や Vite の build 方法については知りません。
11 +
12 + その間を接続するのが `@riebeckite/honox` です。
13 +
14 + ```mermaid
15 + flowchart LR
16 + A["Riebeckite Core<br/>Content / Plugin / Manifest"]
17 + B["@riebeckite/honox<br/>Integration"]
18 + C["HonoX / Vite<br/>Application"]
19 +
20 + A --> B
21 + B --> C
22 + ```
23 +
24 + 主に次の処理を担当します。
25 +
26 + - application root / config の解決
27 + - Vite の development / build
28 + - SSG の設定
29 + - plugin / theme の style entry 生成
30 + - client entry の生成
31 + - Riebeckite のコンテンツと HonoX application の接続
32 +
33 + これにより、通常の Site は Riebeckite 内部の Vite / HonoX 設定を毎回組み立てる必要がありません。
34 +
35 +
36 + ## このページの構成
37 +
38 + - [UI Primitive](./honox-integration/ui.ja.md)
39 + - [Site Application の責務](./honox-integration/site.ja.md)
40 +
41 + ## 基本的な使い方
42 +
43 + 通常は `vite.config.ts` で `riebeckiteVite()` を登録します。
44 +
45 + ```ts
46 + import { riebeckiteVite } from "@riebeckite/honox";
47 + import { defineConfig } from "vite";
48 +
49 + export default defineConfig({
50 + plugins: [honox({ ... }), ...riebeckiteVite(), build()],
51 + });
52 + ```
53 +
54 + `riebeckiteVite()` は通常の Site 向けの higher-level helper です。
55 +
56 + Riebeckite の Vite plugin を追加するだけでなく、次の設定もまとめて行います。
57 +
58 + - SSG entry の設定
59 + - extension mapping
60 + - SSR に必要な external dependency の設定
61 + - plugin / theme の生成 entry の接続
62 +
63 + HonoX plugin、deployment 用 build plugin、Tailwind など、Site 自身が必要とする Vite plugin と組み合わせて利用できます。
64 +
65 + ## Root と Config
66 +
67 + `riebeckiteVite()` では、必要に応じて次の場所を指定できます。
68 +
69 + | Option | 意味 |
70 + | --- | --- |
71 + | `appRoot` | Site application の基準ディレクトリ |
72 + | `configRoot` | Riebeckite config を探す基準 |
73 + | `configFile` | 使用する config file |
74 + | `workspaceRoot` | monorepo 開発時の workspace root |
75 +
76 + 通常は指定する必要はありません。
77 +
78 + `appRoot` の既定値は Vite root、`configRoot` の既定値は `appRoot` です。
79 +
80 + Riebeckite config は `configRoot` を基準に読み込みます。
81 +
82 + 一方、
83 +
84 + ```ts
85 + content: {
86 + directory: "./content",
87 + }
88 + ```
89 +
90 + のような content directory は `appRoot` を基準に解決します。
91 +
92 + `resolveHonoxApplication()` は、これらの root と解決済み config をまとめて返します。
93 +
94 + CLI と Vite がこの共通モデルを利用することで、それぞれが異なる方法で application を解決しないようにしています。
95 +
96 + ### `workspaceRoot`
97 +
98 + `workspaceRoot` は、Riebeckite 自体を monorepo で開発するときに source package alias を利用するための設定です。
99 +
100 + npm から Riebeckite をインストールした通常の Site では必要ありません。
101 +
102 + その場合は Site 自身の `node_modules` から package が解決されます。
103 +
104 + ## `.riebeckite` に生成されるファイル
105 +
106 + Integration は application 内の
107 +
108 + ```text
109 + app/.riebeckite/
110 + ```
111 +
112 + へ、plugin や theme を接続するためのファイルを生成します。
113 +
114 + たとえば plugin style や theme style です。client module は `.riebeckite` には生成されず、virtual module として提供されます。
115 +
116 + ```mermaid
117 + flowchart LR
118 + A["Installed Plugins / Themes"]
119 + B["@riebeckite/honox"]
120 + C["app/.riebeckite/"]
121 + D["Site Application"]
122 +
123 + A --> B
124 + B -->|"generated entries"| C
125 + C --> D
126 + ```
127 +
128 + `.riebeckite` は integration が管理する生成物です。
129 +
130 + **Site の source code として直接編集しないでください。**
131 +
132 + ## Bootstrap module
133 +
134 + generated Site は Framework 所有の bootstrap module を import します。解決済み config は `virtual:riebeckite/config`、構成済みの content runtime は `virtual:riebeckite/content` です。そのため `app/config.ts`、`app/content.ts`、`app/constants/paths.ts` は生成されません。SSG entry の `app/server.ts` はこの2つを re-export し、`riebeckiteSsg` はそこから manifest を見つけます。
135 +
136 + Vite の外で動く script(`tsx` で起動する Node script など)は `@riebeckite/honox/runtime` の `resolveHonoxConfig` で同じ config を解決できます。
137 +
138 + ## Bootstrap module
139 +
140 + generated Site は Framework 所有の bootstrap module を import します。解決済み config は `virtual:riebeckite/config`、構成済みの content runtime は `virtual:riebeckite/content` です。そのため `app/config.ts`、`app/content.ts`、`app/constants/paths.ts` は生成されません。SSG entry の `app/server.ts` はこの2つを re-export し、`riebeckiteSsg` はそこから manifest を見つけます。
141 +
142 + Vite の外で動く script(`tsx` で起動する Node script など)は `@riebeckite/honox/runtime` の `resolveHonoxConfig` で同じ config を解決できます。
143 +
144 + ## Lower-level API
145 +
146 + より細かく integration を制御したい場合は、lower-level API も利用できます。
147 +
148 + - `riebeckite`
149 + - `riebeckiteSsg`
150 + - `riebeckiteSsgExtensionMap`
151 + - `createRiebeckiteSsg`
152 +
153 + 通常の Site では `riebeckiteVite()` を利用し、独自の build integration が必要な場合のみ lower-level API を利用してください。
154 +
155 + ## Routing と SSG
156 +
157 + HonoX の runtime routing と静的生成では、同じ URL が同じページとして扱われる必要があります。
158 +
159 + 特に catch-all route がある場合、SSG の route 列挙に注意が必要です。
160 +
161 + Riebeckite はこのために2つの helper を提供します。
162 +
163 + ### `contentRouteSsgParams`
164 +
165 + ```ts
166 + contentRouteSsgParams(routePath, params)
167 + ```
168 +
169 + `hono/ssg` の `ssgParams` の代わりとして使用します。
170 +
171 + この helper は、その route 自身に属する params だけを返します。
172 +
173 + たとえば、
174 +
175 + ```text
176 + /:slug{.+}
177 + ```
178 +
179 + という catch-all route があっても、
180 +
181 + ```text
182 + /tags/:slug{.+}
183 + ```
184 +
185 + に属するページまで横取りしません。
186 +
187 + ### `ssgEnumerableHandler`
188 +
189 + ```ts
190 + ssgEnumerableHandler(handler)
191 + ```
192 +
193 + `next()` を使って sibling route に処理を渡す handler を、SSG の列挙対象として残すための helper です。
194 +
195 + Hono は middleware 形式の handler を通常 SSG の列挙対象から外すため、この差を補います。
196 +
197 + ## Plugin Page
198 +
199 + Plugin は通常の content とは別に、独自のページを提供できます。
200 +
201 + その場合は、
202 +
203 + ```ts
204 + resolveContentRoute(manifest, path)
205 + ```
206 +
207 + ではなく、
208 +
209 + ```ts
210 + resolveRiebeckiteRoute(content, path)
211 + ```
212 +
213 + を使用します。
214 +
215 + SSG params には、
216 +
217 + ```ts
218 + pluginPageSsgParams(content)
219 + ```
220 +
221 + を追加します。
222 +
223 + 生成された Site の catch-all route は、これらをまとめた `resolveRiebeckiteContentRequest(c, content)` を使用します。この helper が content / Plugin Page / redirect / not-found を解決し、`htmlLanguage` と `headTags` を context へ設定するため、Site は返された結果を自身の composition に渡すだけで済みます。root `/` も同じ mechanics を共有する `resolveRiebeckiteHomeRequest(c, content)` で解決します。
224 +
225 + Route resolver は次の順序で URL を解決します。
226 +
227 + ```mermaid
228 + flowchart TD
229 + A["Request Path"]
230 + B{"Plugin Page?"}
231 + C["Plugin Page"]
232 + D{"Content?"}
233 + E["Content"]
234 + F{"Redirect?"}
235 + G["Redirect"]
236 + H["Not Found"]
237 +
238 + A --> B
239 + B -->|Yes| C
240 + B -->|No| D
241 + D -->|Yes| E
242 + D -->|No| F
243 + F -->|Yes| G
244 + F -->|No| H
245 + ```
246 +
247 + Plugin Page の body は意図的に文字列として扱います。
248 +
249 + Site が持つ既存の document frame 内へ描画し、`page.headTags` も Site の frame へ渡します。
250 +
251 + この仕組みにより、Plugin が独自ページを提供するためだけに HonoX の route file を追加する必要はありません。
252 +
253 + ## Integration の境界
254 +
255 + Riebeckite の routing では、すでに解決された公開 URL を使用します。
256 +
257 + 基本的には、
258 +
259 + ```text
260 + byPermalink
261 + ↓
262 + redirects
263 + ```
264 +
265 + の順で request を解決します。
266 +
267 + filesystem path やディレクトリ構造から公開 URL を逆算しません。
268 +
269 + また、`slug` はコンテンツを内部で検索するためのキーです。
270 +
271 + 実際に公開される URL は、解決済みの `permalink` です。
272 +
273 + ### 責務のまとめ
274 +
275 + Riebeckite 全体では、次のように責務を分離します。
276 +
277 + ```mermaid
278 + flowchart LR
279 + Core["Core<br/>Content / Manifest / Plugin API"]
280 + Integration["HonoX Integration<br/>Vite / SSG / Route Resolution"]
281 + Plugin["Plugin<br/>Content Extension / Page / Asset / Client Entry"]
282 + Site["Site Application<br/>Route / Shell / UI / Island / CSS"]
283 +
284 + Core --> Integration
285 + Plugin --> Core
286 + Integration --> Site
287 + Plugin -. "提供した情報を<br/>Site が配置" .-> Site
288 + ```
289 +
290 + HonoX / Vite / Cloudflare 固有の処理は integration または Site Application に閉じます。
291 +
292 + Core は HonoX routing を所有しません。
293 +
294 + Plugin はページ、アセット、client entry などを提供できますが、Site 全体の route composition や UI 構造は所有しません。
295 +
296 + **Core はコンテンツを扱い、Integration は HonoX と接続し、Plugin は機能を提供し、Site が最終的な表示を決める**、という境界を維持してください。
297 +
298 + build state の扱いについては [Build system](./build-system.ja.md)、package ごとの責務については [Architecture](./architecture.ja.md) を参照してください。
299 +