Color mode

Content System

Content System は、Markdown や画像などのファイルを、Riebeckite がサイトとして扱えるコンテンツへ変換する仕組みです。

単に Markdown を HTML に変換するだけではありません。

Riebeckite が、

  • このファイルは何の記事なのか
  • 公開してよいのか
  • どの URL で公開するのか
  • 他の記事とどうつながっているのか
  • Plugin によって何が追加・変更されたのか

を解決し、Site 全体から同じ情報を利用できる状態にします。

全体の流れ

Content System の大まかな流れは次のとおりです。

Diagram source
text
flowchart TD
    Files["Markdown / Assets"]
    Source["ContentSource<br/>コンテンツを読み込む"]
    Manager["ContentManager<br/>コンテンツを解決・処理する"]
    Plugin["Plugin Hooks"]
    Manifest["Manifest"]
    Graph["Content Graph"]
    Location["Public Location"]
    Site["Page Generation / Runtime"]
 
    Files --> Source
    Source --> Manager
 
    Manager <--> Plugin
 
    Manager --> Manifest
    Manager --> Graph
    Manager --> Location
 
    Manifest --> Site
    Graph --> Site
    Location --> Site

中心になるのが ContentSource と ContentManager です。

簡単に言えば、

text
ContentSource
  = どこからコンテンツを読むか
 
ContentManager
  = 読み込んだコンテンツをどう扱うか

という役割分担です。

ContentSource

ContentSource は、Markdown やアセットをどこから、どう読み込むかを抽象化した API です。

通常は、

text
content/

のような content.directory で指定されたディレクトリから読み込みます。

しかし Content System 自体は、コンテンツが必ず Site repository 内に存在するとは考えません。

たとえば、

text
Site Repository
└─ content/
 
External Repository
└─ notes/
 
Obsidian Vault
└─ notes/

のどこから取得した場合でも、最終的には ContentSource という同じ interface を通して ContentManager に渡します。

Diagram source
text
flowchart LR
    Local["Site Repository"]
    External["External Repository"]
    Vault["Obsidian Vault"]
 
    Local --> Source["ContentSource"]
    External --> Source
    Vault --> Source
 
    Source --> Manager["ContentManager"]

これにより、コンテンツの保存場所が変わっても、それ以降の処理を同じ仕組みで扱えます。

3種類の「場所」

Content System を理解するときに重要なのが、次の3つを区別することです。

種類 意味
ファイルシステム上のパス 実際にファイルが保存されている場所
論理パス content root から見たコンテンツの識別子
公開先 Web Site 上の URL

たとえば、

text
C:\projects\garden\content\posts\hello.md

というファイルがあったとしても、Riebeckite 内部では、

text
posts/hello.md

という論理パスとして扱えます。

さらに、実際の公開先は、

text
/blog/hello/

かもしれません。

Diagram source
text
flowchart LR
    FS["Filesystem Path<br/>C:/.../content/posts/hello.md"]
    Logical["Logical Path<br/>posts/hello.md"]
    Public["Public Location<br/>/blog/hello/"]
 
    FS --> Logical
    Logical --> Public

この3つを分離することで、content repository を別の repository に移しても、Site 側の URL やコンテンツ処理を同じ規則で扱えます。

ContentManager

ContentManager は ContentSource から受け取ったコンテンツを管理し、Site で利用できる状態へ変換します。

主な役割は次のとおりです。

  • Markdown の frontmatter と本文を読み込む
  • コンテンツを公開するか判断する
  • slug、permalink、content ID を整理する
  • Plugin hooks を実行する
  • 公開先を解決する
  • Manifest を生成する
  • Content Graph を生成する
  • Query 用の index を用意する

つまり ContentManager は、Content System の中心となる orchestrator です。

Plugin が処理に参加する

Plugin は ContentManager の lifecycle に hook できます。

代表的な hook には、

text
onContentLoaded
onPostParsed
onPostProcessed
onManifestCreated

などがあります。

概念的には次のような流れになります。

Diagram source
text
flowchart TD
    Load["Content Loaded"]
    H1["onContentLoaded"]
    Parse["Markdown Parse"]
    H2["onPostParsed"]
    Process["Content Processing"]
    H3["onPostProcessed"]
    Manifest["Manifest Created"]
    H4["onManifestCreated"]
 
    Load --> H1
    H1 --> Parse
    Parse --> H2
    H2 --> Process
    Process --> H3
    H3 --> Manifest
    Manifest --> H4

Plugin はこの lifecycle を利用してコンテンツを拡張します。

slug / permalink / content ID

この3つは似ていますが、役割が異なります。

名前 何を表す? 例
slug コンテンツの短い名前 hello-world
permalink Site 上の公開 URL /blog/hello-world/
content ID コンテンツそのものを安定して識別する ID article-01

特に、slug と公開 URL は同じものではありません。

たとえば、

text
slug
  hello-world
 
permalink
  /blog/hello-world/
 
content ID
  019abc...

のように、それぞれ別の目的を持ちます。

Default Public Location

標準では resolveDefaultContentLocation が公開先を解決します。

基本的には、

text
index
  ↓
/
 
その他
  ↓
/{slug}

として扱います。

ただし、これはあくまで default resolver です。

Plugin は、

text
resolveContentLocations

hook を使って公開先を追加・変更できます。

ContentPublicLocation

ContentPublicLocation は、コンテンツが Site 上のどこで公開されるか を表します。

通常は1つの記事に1つの canonical な公開先があります。

text
Article
   ↓
/blog/article/

しかし Plugin によって、別名 URL や redirect が追加される場合があります。

Diagram source
text
flowchart LR
    Article["Article"]
 
    Article --> Canonical["Canonical<br/>/blog/article/"]
    Article --> Alias["Alias<br/>/article/"]
    Alias -->|"redirect"| Canonical

公開先はページ生成だけで使う情報ではありません。

たとえば、

  • Site 内リンク
  • redirect
  • sitemap
  • search index
  • language switcher
  • Content Graph

なども公開先を参照します。

そのため Riebeckite では URL を単なる文字列として各機能が独自に計算するのではなく、ContentPublicLocation として明示的に管理します。

Manifest

Manifest は、Build 時に確定したコンテンツの一覧です。

各 entry には、たとえば次の情報が含まれます。

  • 論理パス
  • metadata
  • 公開先
  • Plugin による処理結果
  • incremental build に必要な情報
Diagram source
text
flowchart LR
    Manager["ContentManager"]
    Manifest["Manifest"]
 
    Manager --> Manifest
 
    Manifest --> Page["Page Generation"]
    Manifest --> Runtime["Runtime Queries"]
    Manifest --> Build["Incremental Build"]
    Manifest --> Inspect["inspect content"]

Manifest は、Content System が解決した結果を他の仕組みへ渡す重要な境界です。

各 consumer が Markdown を読み直して同じ情報を再計算するのではなく、解決済みの Manifest を利用します。

Content Graph

Content Graph は、コンテンツ同士の関係を表します。

たとえば、

markdown
[[Article B]]

という WikiLink があれば、

Diagram source
text
graph LR
    A["Article A"] --> B["Article B"]

という関係が成り立ちます。

この情報から backlink も扱えます。

Diagram source
text
graph LR
    A["Article A"] --> B["Article B"]
    C["Article C"] --> B
 
    B -. "backlinks" .-> A
    B -. "backlinks" .-> C

Content Graph は単なるグラフ表示用のデータではありません。

たとえば、

  • WikiLink
  • Markdown link
  • backlinks
  • taxonomy
  • series
  • related posts
  • local graph
  • garden explorer

などの基盤として利用できます。

Plugin は、

text
extendContentGraph

を使ってグラフへ情報を追加できます。

Content Queries

Build 後のコンテンツを検索・整理するために、Query API が用意されています。

代表的な API は次のとおりです。

text
queryContentEntries
queryContentPage
groupContentEntries

これらはファイルを検索する API ではありません。

Diagram source
text
flowchart LR
    Files["Markdown Files"]
    Build["Build / ContentManager"]
    Index["Resolved Content Index"]
    Query["Content Query"]
    Result["Result"]
 
    Files --> Build
    Build --> Index
    Query --> Index
    Index --> Result

Query は、Build 時にすでに解決された index に対して実行します。

Runtime や Plugin が Markdown を直接読み直して独自に状態を再構築することは避けます。

Content Collections

buildContentCollections は、複数のコンテンツを一覧として扱うための仕組みです。

たとえば、

text
すべての記事
     ↓
日付順に並べる
     ↓
タグごとに分類する
     ↓
ページ単位に分割する

といった処理に利用できます。

pageSize を指定すると pagination も扱えます。

Diagram source
text
flowchart LR
    Entries["Published Entries"]
    Sort["Sort"]
    Group["Group"]
    Paginate["Paginate"]
    Pages["Collection Pages"]
 
    Entries --> Sort
    Sort --> Group
    Group --> Paginate
    Paginate --> Pages

Plugin や Site Application は、この仕組みを使って記事一覧、タグ一覧などを作成できます。

Assets

画像や添付ファイルは Markdown 本文とは別の entry として扱います。

たとえば、

text
content/
├─ article.md
└─ images/
   └─ example.png

があった場合、Riebeckite はアセットの論理パスを保ちながら、Site 上で利用できる URL へ対応付けます。

Diagram source
text
flowchart LR
    Asset["Content Asset<br/>images/example.png"]
    Resolver["Asset Resolution"]
    Public["Public Asset URL"]
 
    Asset --> Resolver
    Resolver --> Public

実際の URL 形式は Plugin や設定によって変わる場合があります。

content root の外にある任意のファイルを公開 URL に変換しないことが重要です。

Content System の公開境界を通して、安全に公開対象を決定します。

Content Repository を分離する場合

Site と Content を別 repository にしても、Content System の基本的な流れは変わりません。

Diagram source
text
flowchart LR
    ContentRepo["Content Repository"]
    Checkout["Checkout / Content Source"]
    SiteRepo["Site Repository"]
    Build["Riebeckite Build"]
    Site["Generated Site"]
 
    ContentRepo --> Checkout
    Checkout --> Build
    SiteRepo --> Build
    Build --> Site

Content Repository はコンテンツを提供します。

最終的に、

  • 何を公開するか
  • どの Plugin を使うか
  • どの URL で公開するか
  • どの Site を生成するか

を決定するのは Site Repository 側の Build です。

このため、Content Repository が分離されていても ContentManager や Plugin が filesystem の配置を特別扱いする必要はありません。

Content System の境界

Content System を変更するときは、次の原則を維持してください。

原則 理由
content.directory は appRoot 基準で解決する Site ごとに安定した基準を持つため
Plugin は filesystem path に依存しない Content Source を交換可能にするため
公開判定は publishStrategy と frontmatter に従う 公開境界を一元化するため
公開先は ContentPublicLocation として登録する URL を各機能が独自計算しないため
Runtime は Build 済み index を利用する Runtime から source filesystem を分離するため
Site Build が最終的な公開状態を決める Content Repository と Site の責務を分離するため

全体として、

Diagram source
text
flowchart LR
    Source["Source<br/>どこから読む?"]
    Content["ContentManager<br/>何として扱う?"]
    Public["Public Location<br/>どこで公開する?"]
    Index["Manifest / Graph<br/>何が解決された?"]
    Consumer["Site / Plugin<br/>どう利用する?"]
 
    Source --> Content
    Content --> Public
    Public --> Index
    Index --> Consumer

という境界を崩さないことが重要です。

Source は読み込み方、ContentManager はコンテンツの解決、Public Location は公開先、Manifest / Graph は解決結果を表します。

各機能が filesystem や Markdown を独自に読み直すのではなく、この Content System を通して同じ解決結果を共有することが、Riebeckite の Content Architecture の基本です。

関連ページ

History

1 changesCollapseExpand
1 + # Content System
2 +
3 + Content System は、Markdown や画像などのファイルを、Riebeckite がサイトとして扱えるコンテンツへ変換する仕組みです。
4 +
5 + 単に Markdown を HTML に変換するだけではありません。
6 +
7 + Riebeckite が、
8 +
9 + - このファイルは何の記事なのか
10 + - 公開してよいのか
11 + - どの URL で公開するのか
12 + - 他の記事とどうつながっているのか
13 + - Plugin によって何が追加・変更されたのか
14 +
15 + を解決し、Site 全体から同じ情報を利用できる状態にします。
16 +
17 + # 全体の流れ
18 +
19 + Content System の大まかな流れは次のとおりです。
20 +
21 + ```mermaid id="r6gvss"
22 + flowchart TD
23 + Files["Markdown / Assets"]
24 + Source["ContentSource<br/>コンテンツを読み込む"]
25 + Manager["ContentManager<br/>コンテンツを解決・処理する"]
26 + Plugin["Plugin Hooks"]
27 + Manifest["Manifest"]
28 + Graph["Content Graph"]
29 + Location["Public Location"]
30 + Site["Page Generation / Runtime"]
31 +
32 + Files --> Source
33 + Source --> Manager
34 +
35 + Manager <--> Plugin
36 +
37 + Manager --> Manifest
38 + Manager --> Graph
39 + Manager --> Location
40 +
41 + Manifest --> Site
42 + Graph --> Site
43 + Location --> Site
44 + ```
45 +
46 + 中心になるのが `ContentSource` と `ContentManager` です。
47 +
48 + 簡単に言えば、
49 +
50 + ```text id="a4xsh7"
51 + ContentSource
52 + = どこからコンテンツを読むか
53 +
54 + ContentManager
55 + = 読み込んだコンテンツをどう扱うか
56 + ```
57 +
58 + という役割分担です。
59 +
60 + # ContentSource
61 +
62 + `ContentSource` は、Markdown やアセットを**どこから、どう読み込むか**を抽象化した API です。
63 +
64 + 通常は、
65 +
66 + ```text id="9u1ygf"
67 + content/
68 + ```
69 +
70 + のような `content.directory` で指定されたディレクトリから読み込みます。
71 +
72 + しかし Content System 自体は、コンテンツが必ず Site repository 内に存在するとは考えません。
73 +
74 + たとえば、
75 +
76 + ```text id="eynh5j"
77 + Site Repository
78 + └─ content/
79 +
80 + External Repository
81 + └─ notes/
82 +
83 + Obsidian Vault
84 + └─ notes/
85 + ```
86 +
87 + のどこから取得した場合でも、最終的には `ContentSource` という同じ interface を通して ContentManager に渡します。
88 +
89 + ```mermaid id="0xpl01"
90 + flowchart LR
91 + Local["Site Repository"]
92 + External["External Repository"]
93 + Vault["Obsidian Vault"]
94 +
95 + Local --> Source["ContentSource"]
96 + External --> Source
97 + Vault --> Source
98 +
99 + Source --> Manager["ContentManager"]
100 + ```
101 +
102 + これにより、コンテンツの保存場所が変わっても、それ以降の処理を同じ仕組みで扱えます。
103 +
104 + ## 3種類の「場所」
105 +
106 + Content System を理解するときに重要なのが、次の3つを区別することです。
107 +
108 + | 種類 | 意味 |
109 + | --- | --- |
110 + | ファイルシステム上のパス | 実際にファイルが保存されている場所 |
111 + | 論理パス | content root から見たコンテンツの識別子 |
112 + | 公開先 | Web Site 上の URL |
113 +
114 + たとえば、
115 +
116 + ```text id="dfblcr"
117 + C:\projects\garden\content\posts\hello.md
118 + ```
119 +
120 + というファイルがあったとしても、Riebeckite 内部では、
121 +
122 + ```text id="ej2j1k"
123 + posts/hello.md
124 + ```
125 +
126 + という論理パスとして扱えます。
127 +
128 + さらに、実際の公開先は、
129 +
130 + ```text id="dkv7ak"
131 + /blog/hello/
132 + ```
133 +
134 + かもしれません。
135 +
136 + ```mermaid id="qvyr9m"
137 + flowchart LR
138 + FS["Filesystem Path<br/>C:/.../content/posts/hello.md"]
139 + Logical["Logical Path<br/>posts/hello.md"]
140 + Public["Public Location<br/>/blog/hello/"]
141 +
142 + FS --> Logical
143 + Logical --> Public
144 + ```
145 +
146 + この3つを分離することで、content repository を別の repository に移しても、Site 側の URL やコンテンツ処理を同じ規則で扱えます。
147 +
148 + # ContentManager
149 +
150 + `ContentManager` は `ContentSource` から受け取ったコンテンツを管理し、Site で利用できる状態へ変換します。
151 +
152 + 主な役割は次のとおりです。
153 +
154 + - Markdown の frontmatter と本文を読み込む
155 + - コンテンツを公開するか判断する
156 + - slug、permalink、content ID を整理する
157 + - Plugin hooks を実行する
158 + - 公開先を解決する
159 + - Manifest を生成する
160 + - Content Graph を生成する
161 + - Query 用の index を用意する
162 +
163 + つまり ContentManager は、Content System の中心となる orchestrator です。
164 +
165 + ## Plugin が処理に参加する
166 +
167 + Plugin は ContentManager の lifecycle に hook できます。
168 +
169 + 代表的な hook には、
170 +
171 + ```text id="l0z0fr"
172 + onContentLoaded
173 + onPostParsed
174 + onPostProcessed
175 + onManifestCreated
176 + ```
177 +
178 + などがあります。
179 +
180 + 概念的には次のような流れになります。
181 +
182 + ```mermaid id="c1cjn0"
183 + flowchart TD
184 + Load["Content Loaded"]
185 + H1["onContentLoaded"]
186 + Parse["Markdown Parse"]
187 + H2["onPostParsed"]
188 + Process["Content Processing"]
189 + H3["onPostProcessed"]
190 + Manifest["Manifest Created"]
191 + H4["onManifestCreated"]
192 +
193 + Load --> H1
194 + H1 --> Parse
195 + Parse --> H2
196 + H2 --> Process
197 + Process --> H3
198 + H3 --> Manifest
199 + Manifest --> H4
200 + ```
201 +
202 + Plugin はこの lifecycle を利用してコンテンツを拡張します。
203 +
204 + # slug / permalink / content ID
205 +
206 + この3つは似ていますが、役割が異なります。
207 +
208 + | 名前 | 何を表す? | 例 |
209 + | --- | --- | --- |
210 + | `slug` | コンテンツの短い名前 | `hello-world` |
211 + | `permalink` | Site 上の公開 URL | `/blog/hello-world/` |
212 + | content ID | コンテンツそのものを安定して識別する ID | `article-01` |
213 +
214 + 特に、**slug と公開 URL は同じものではありません**。
215 +
216 + たとえば、
217 +
218 + ```text id="73lmb5"
219 + slug
220 + hello-world
221 +
222 + permalink
223 + /blog/hello-world/
224 +
225 + content ID
226 + 019abc...
227 + ```
228 +
229 + のように、それぞれ別の目的を持ちます。
230 +
231 + ## Default Public Location
232 +
233 + 標準では `resolveDefaultContentLocation` が公開先を解決します。
234 +
235 + 基本的には、
236 +
237 + ```text id="olpxjg"
238 + index
239 + ↓
240 + /
241 +
242 + その他
243 + ↓
244 + /{slug}
245 + ```
246 +
247 + として扱います。
248 +
249 + ただし、これはあくまで default resolver です。
250 +
251 + Plugin は、
252 +
253 + ```text id="sc5pbi"
254 + resolveContentLocations
255 + ```
256 +
257 + hook を使って公開先を追加・変更できます。
258 +
259 + # ContentPublicLocation
260 +
261 + `ContentPublicLocation` は、コンテンツが **Site 上のどこで公開されるか** を表します。
262 +
263 + 通常は1つの記事に1つの canonical な公開先があります。
264 +
265 + ```text id="ul2l2c"
266 + Article
267 + ↓
268 + /blog/article/
269 + ```
270 +
271 + しかし Plugin によって、別名 URL や redirect が追加される場合があります。
272 +
273 + ```mermaid id="k15b5h"
274 + flowchart LR
275 + Article["Article"]
276 +
277 + Article --> Canonical["Canonical<br/>/blog/article/"]
278 + Article --> Alias["Alias<br/>/article/"]
279 + Alias -->|"redirect"| Canonical
280 + ```
281 +
282 + 公開先はページ生成だけで使う情報ではありません。
283 +
284 + たとえば、
285 +
286 + - Site 内リンク
287 + - redirect
288 + - sitemap
289 + - search index
290 + - language switcher
291 + - Content Graph
292 +
293 + なども公開先を参照します。
294 +
295 + そのため Riebeckite では URL を単なる文字列として各機能が独自に計算するのではなく、`ContentPublicLocation` として明示的に管理します。
296 +
297 + # Manifest
298 +
299 + Manifest は、**Build 時に確定したコンテンツの一覧**です。
300 +
301 + 各 entry には、たとえば次の情報が含まれます。
302 +
303 + - 論理パス
304 + - metadata
305 + - 公開先
306 + - Plugin による処理結果
307 + - incremental build に必要な情報
308 +
309 + ```mermaid id="s23io5"
310 + flowchart LR
311 + Manager["ContentManager"]
312 + Manifest["Manifest"]
313 +
314 + Manager --> Manifest
315 +
316 + Manifest --> Page["Page Generation"]
317 + Manifest --> Runtime["Runtime Queries"]
318 + Manifest --> Build["Incremental Build"]
319 + Manifest --> Inspect["inspect content"]
320 + ```
321 +
322 + Manifest は、Content System が解決した結果を他の仕組みへ渡す重要な境界です。
323 +
324 + 各 consumer が Markdown を読み直して同じ情報を再計算するのではなく、解決済みの Manifest を利用します。
325 +
326 + # Content Graph
327 +
328 + Content Graph は、コンテンツ同士の関係を表します。
329 +
330 + たとえば、
331 +
332 + ```markdown id="9o2p6a"
333 + [[Article B]]
334 + ```
335 +
336 + という WikiLink があれば、
337 +
338 + ```mermaid id="pdu2pi"
339 + graph LR
340 + A["Article A"] --> B["Article B"]
341 + ```
342 +
343 + という関係が成り立ちます。
344 +
345 + この情報から backlink も扱えます。
346 +
347 + ```mermaid id="8grb4h"
348 + graph LR
349 + A["Article A"] --> B["Article B"]
350 + C["Article C"] --> B
351 +
352 + B -. "backlinks" .-> A
353 + B -. "backlinks" .-> C
354 + ```
355 +
356 + Content Graph は単なるグラフ表示用のデータではありません。
357 +
358 + たとえば、
359 +
360 + - WikiLink
361 + - Markdown link
362 + - backlinks
363 + - taxonomy
364 + - series
365 + - related posts
366 + - local graph
367 + - garden explorer
368 +
369 + などの基盤として利用できます。
370 +
371 + Plugin は、
372 +
373 + ```text id="g5uz6h"
374 + extendContentGraph
375 + ```
376 +
377 + を使ってグラフへ情報を追加できます。
378 +
379 + # Content Queries
380 +
381 + Build 後のコンテンツを検索・整理するために、Query API が用意されています。
382 +
383 + 代表的な API は次のとおりです。
384 +
385 + ```text id="5yib25"
386 + queryContentEntries
387 + queryContentPage
388 + groupContentEntries
389 + ```
390 +
391 + これらは**ファイルを検索する API ではありません**。
392 +
393 + ```mermaid id="bf0k8w"
394 + flowchart LR
395 + Files["Markdown Files"]
396 + Build["Build / ContentManager"]
397 + Index["Resolved Content Index"]
398 + Query["Content Query"]
399 + Result["Result"]
400 +
401 + Files --> Build
402 + Build --> Index
403 + Query --> Index
404 + Index --> Result
405 + ```
406 +
407 + Query は、Build 時にすでに解決された index に対して実行します。
408 +
409 + Runtime や Plugin が Markdown を直接読み直して独自に状態を再構築することは避けます。
410 +
411 + # Content Collections
412 +
413 + `buildContentCollections` は、複数のコンテンツを一覧として扱うための仕組みです。
414 +
415 + たとえば、
416 +
417 + ```text id="63ph8a"
418 + すべての記事
419 + ↓
420 + 日付順に並べる
421 + ↓
422 + タグごとに分類する
423 + ↓
424 + ページ単位に分割する
425 + ```
426 +
427 + といった処理に利用できます。
428 +
429 + `pageSize` を指定すると pagination も扱えます。
430 +
431 + ```mermaid id="is4p7e"
432 + flowchart LR
433 + Entries["Published Entries"]
434 + Sort["Sort"]
435 + Group["Group"]
436 + Paginate["Paginate"]
437 + Pages["Collection Pages"]
438 +
439 + Entries --> Sort
440 + Sort --> Group
441 + Group --> Paginate
442 + Paginate --> Pages
443 + ```
444 +
445 + Plugin や Site Application は、この仕組みを使って記事一覧、タグ一覧などを作成できます。
446 +
447 + # Assets
448 +
449 + 画像や添付ファイルは Markdown 本文とは別の entry として扱います。
450 +
451 + たとえば、
452 +
453 + ```text id="0yuczl"
454 + content/
455 + ├─ article.md
456 + └─ images/
457 + └─ example.png
458 + ```
459 +
460 + があった場合、Riebeckite はアセットの論理パスを保ちながら、Site 上で利用できる URL へ対応付けます。
461 +
462 + ```mermaid id="r3f54e"
463 + flowchart LR
464 + Asset["Content Asset<br/>images/example.png"]
465 + Resolver["Asset Resolution"]
466 + Public["Public Asset URL"]
467 +
468 + Asset --> Resolver
469 + Resolver --> Public
470 + ```
471 +
472 + 実際の URL 形式は Plugin や設定によって変わる場合があります。
473 +
474 + content root の外にある任意のファイルを公開 URL に変換しないことが重要です。
475 +
476 + Content System の公開境界を通して、安全に公開対象を決定します。
477 +
478 + # Content Repository を分離する場合
479 +
480 + Site と Content を別 repository にしても、Content System の基本的な流れは変わりません。
481 +
482 + ```mermaid id="48opm6"
483 + flowchart LR
484 + ContentRepo["Content Repository"]
485 + Checkout["Checkout / Content Source"]
486 + SiteRepo["Site Repository"]
487 + Build["Riebeckite Build"]
488 + Site["Generated Site"]
489 +
490 + ContentRepo --> Checkout
491 + Checkout --> Build
492 + SiteRepo --> Build
493 + Build --> Site
494 + ```
495 +
496 + Content Repository はコンテンツを提供します。
497 +
498 + 最終的に、
499 +
500 + - 何を公開するか
501 + - どの Plugin を使うか
502 + - どの URL で公開するか
503 + - どの Site を生成するか
504 +
505 + を決定するのは Site Repository 側の Build です。
506 +
507 + このため、Content Repository が分離されていても ContentManager や Plugin が filesystem の配置を特別扱いする必要はありません。
508 +
509 + # Content System の境界
510 +
511 + Content System を変更するときは、次の原則を維持してください。
512 +
513 + | 原則 | 理由 |
514 + | --- | --- |
515 + | `content.directory` は `appRoot` 基準で解決する | Site ごとに安定した基準を持つため |
516 + | Plugin は filesystem path に依存しない | Content Source を交換可能にするため |
517 + | 公開判定は `publishStrategy` と frontmatter に従う | 公開境界を一元化するため |
518 + | 公開先は `ContentPublicLocation` として登録する | URL を各機能が独自計算しないため |
519 + | Runtime は Build 済み index を利用する | Runtime から source filesystem を分離するため |
520 + | Site Build が最終的な公開状態を決める | Content Repository と Site の責務を分離するため |
521 +
522 + 全体として、
523 +
524 + ```mermaid id="gjgygr"
525 + flowchart LR
526 + Source["Source<br/>どこから読む?"]
527 + Content["ContentManager<br/>何として扱う?"]
528 + Public["Public Location<br/>どこで公開する?"]
529 + Index["Manifest / Graph<br/>何が解決された?"]
530 + Consumer["Site / Plugin<br/>どう利用する?"]
531 +
532 + Source --> Content
533 + Content --> Public
534 + Public --> Index
535 + Index --> Consumer
536 + ```
537 +
538 + という境界を崩さないことが重要です。
539 +
540 + **Source は読み込み方、ContentManager はコンテンツの解決、Public Location は公開先、Manifest / Graph は解決結果を表します。**
541 +
542 + 各機能が filesystem や Markdown を独自に読み直すのではなく、この Content System を通して同じ解決結果を共有することが、Riebeckite の Content Architecture の基本です。
543 +
544 + ## 関連ページ
545 +
546 + - [Configuration](../reference/configuration.md)
547 + - [Plugin API](../reference/plugin-api.md)
548 + - [Content Repositories](../guides/content-repositories.md)
549 + - [Separate Content Repository](../guides/deployment/separate-content-repository.md)
550 +