---
title: コンテンツソース
description: ローカルファイル、リモートリポジトリ、または任意のカスタムバックエンドからドキュメントを取得し、複数のソースをビルド時に読み込む1つのスタティックファーストなサイトに統合できます。
---

Blume はデフォルトで `.md`/`.mdx` ファイルのフォルダーを読み込みます。**コンテンツソース**を使うと、リモートリポジトリ、CMS、任意のカスタムバックエンドなど、別の場所からページを取得し、複数のソースを1つのサイトに統合できます。ソースはビルド時に読み込まれ、Blume はスタティックファーストのままです。

## デフォルトの動作 [#the-default]

設定を行わない場合、Blume はコンテンツルート（デフォルトでは `docs`）を1つの暗黙的なファイルシステムソースとしてスキャンします。トップレベルの `content.root`/`include`/`exclude` オプションはこれまでどおり動作するため、変更は不要です。

```ts blume.config.ts
import { defineConfig } from "blume";

export default defineConfig({
  content: { root: "docs" },
});
```

## 複数のソース [#multiple-sources]

ソースを組み合わせるには `content.sources` 配列を追加します。各エントリは任意の `prefix` によって名前空間が分けられ、そのルートは `/<prefix>/…` の下にネストされます。`sources` が存在する場合は暗黙のデフォルトが置き換えられるため、ローカルドキュメント用に `filesystem` エントリを含めてください。

```ts blume.config.ts
import { defineConfig } from "blume";

export default defineConfig({
  content: {
    sources: [
      // Local docs at the site root
      { type: "filesystem", root: "docs" },

      // Remote MDX from a GitHub repo, mounted under /sdk
      {
        type: "mdx-remote",
        prefix: "sdk",
        github: { owner: "acme", repo: "sdk", ref: "main", path: "docs" },
      },
    ],
  },
});
```

2つのソースが同じルートに解決される場合、Blume は `BLUME_DUPLICATE_ROUTE` ビルドエラーを報告します。各ソースには異なる `prefix` を指定してください。

## Obsidian

組み込みの `obsidian` ソースは、[Obsidian](https://obsidian.md) の Vault をその場で読み込みます。エクスポート手順は不要で、リポジトリに何かが生成されることもありません。Vault が信頼できる唯一の情報源のままであり、Blume は読み込みの際に Obsidian の方言を Markdown へと変換します。

```ts blume.config.ts
import { defineConfig } from "blume";

export default defineConfig({
  content: {
    sources: [
      { type: "filesystem", root: "docs" },
      {
        type: "obsidian",
        prefix: "notes",
        vault: "vault",
        // Vault folder names to skip at any depth, on top of dot-folders
        exclude: ["Templates", "Daily"],
      },
    ],
  },
});
```

`[[Wikilinks]]` はルートへのリンクになり、Obsidian がノートを指し示すのと同じように、パスではなく Vault 全体を通じたノート名で解決されます。カスタムのリンクテキスト（`[[Note|label]]`）、見出しアンカー（`[[Note#Install]]`）、フルパス（`[[folder/Note]]` および `[[folder/Note.md]]`）、Obsidian のデフォルト設定「可能な場合は最短のパス」が書き出す部分パス（`[[guides/Note]]`）、そして Obsidian がテーブルセル内で書き出す `[[Note\|label]]` の形式はいずれも動作し、フロントマターで `slug` を設定しているノートは、その slug が公開するルートへリンクされます。2つのノートが同じ名前を共有する場合は、Vault のフルパスがちょうどその名前と一致するノートが優先され（Obsidian はリンクを名前より先にパスとして解決します）、次に Vault の順序で最初のもの（Obsidian のファイルエクスプローラーと同じく、大文字小文字を区別せず、ノートよりフォルダーが先）が優先されます。Blume が警告を出すのは、wikilink が実際にそのような衝突を経て解決された場合のみです。曖昧さを解消するには、より長いパスを書いてください。ブロック参照（`[[Note#^id]]`）は、アンカーなしでそのノートへリンクします。ブロックは着地先となる id を持たずにレンダリングされるためです。見出しアンカーは対象ノートの実際の見出しに対して解決され、Obsidian のオートコンプリートが書き出すのと同じ方法（`**bold**`、`` `code` ``、リンク記法を取り除いた形）で照合され、ページマニフェストを埋めるのと同じ `extractHeadings` の処理によって slug 化されます。そのため `#Install` へのリンクは、どのページも出力しない id ではなく、その見出しに着地します。`[[#Install]]` は、書いているノート自身の見出しを指します。存在しない見出しへのリンクは、ページへのリンクは維持したままアンカーを削除し、警告を出します。

フロントマターは、Blume の[ページスキーマ](/docs/reference/frontmatter)が受け付けるものに加え、[`frontmatter.extend`](/docs/reference/frontmatter#custom-keys)（または、その `type` のノートについてはコンテンツタイプの `frontmatter`）で宣言したキーを保持します。それ以外の Obsidian のプロパティ — Dataview のフィールド、Templater の日付、`publish`、および Obsidian 自身の `tags`、`aliases`、`cssclasses` — は、ノートの変換時に削除されます。そのため、Properties UI で書かれた Vault もフロントマターのエラーなくビルドできます。`aliases` は解決されるのではなく削除されます。エイリアスによるリンク先はまだサポートされていません。ノートの隣にある相対パスの Markdown 画像（`![chart](./chart.png)`）は Vault から提供され、Vault が git リポジトリ内にある場合、Vault のページも他のページと同様に git 由来の[「最終更新」日付](/docs/configuration#last-modified)を取得します。「このページを編集」リンクは `github.dir` を通じて解決されるため、モノレポでドキュメントアプリの隣に置かれた Vault でもそのファイルへリンクされます。リポジトリ外の Vault にはリンクは付きません。

Vault 内のロケールディレクトリとバージョンスナップショットは、ファイルシステムソースが読み込むのと同じように読み込まれます。[i18n](/docs/content/i18n) を設定していれば `fr/Note.md` は `/fr/` 配下に、[バージョン](/docs/content/versioning)を設定していれば `v1.0/Note.md` は `/v1.0/` 配下に公開され、それらのノートへの wikilink はそれぞれが公開するルートを指します。

:::note
それ自体にリンクを含む見出しは、マニフェスト用のアンカーを見出しの Markdown から、レンダリングされる `id` をそのテキストコンテンツから取得します。その見出しについては両者が異なるため、そこへの wikilink はセクションではなくページに着地する場合があります。
:::

`index` ノートへのリンクは、実体のない `/index` ではなくそのフォルダーのルートに着地します。**解決できない wikilink は、ビルドを失敗させる代わりに、ビルド警告を出したうえでプレーンテキストに縮退します**。そのため、リファクタリング途中の Vault でも公開できます。単一行の `%%comments%%` は取り除かれ、HTML コメント内の wikilink（`<!-- [[Draft]] -->`）は Obsidian でも非表示になるためそのまま残され、フロントマターに `title` がないノートはファイル名がタイトルになります — これは Obsidian 自身が適用するのと同じルールです。唯一の例外は `index` ノートで、これはノートではなくルートを指すため、そのタイトルは Blume の通常の導出（最初の見出し、次に人間可読化したセグメント）にフォールバックします。フェンス付き、インデント付き、およびインラインのコードはそのまま通過するため、この構文を説明するノートも壊れません。

ドットフォルダーはスキップされます。これには Obsidian 自身の `.obsidian` 設定ディレクトリと `.trash` が含まれ、開発時のウォッチャーもこれらを無視するため、アプリでペインを移動したりノートをゴミ箱に削除したりしてもサイトは再ビルドされません。ノートの編集では再ビルドされます。どのコンテンツスキャンも読み込まないディレクトリ（`node_modules`、`dist`、`.git` など）もスキップされるため、プロジェクト自体をルートとする Vault が依存関係の README を公開することはありません。Vault 内のシンボリックリンクは、ファイルシステムソースがたどるのと同じようにたどられるため、Vault にリンクされた共有フォルダーも一緒に公開されます。`content.root` 内にある Vault は、ファイルシステムソースから除外する必要があります（`exclude: ["vault/**"]`）。そうすれば `blume version cut` はそれをスナップショットから除外し、Vault は自身のノートを引き続き最新版として公開します。

まだ変換されていないもの: コールアウト（`> [!note]`）はプレーンな引用ブロックとしてレンダリングされ、埋め込み（`![[image.png]]`）はそのまま通過し、複数行の `%%comments%%` はそのまま残され、バックリンクグラフはありません。

## リモート MDX [#remote-mdx]

組み込みの `mdx-remote` ソースは、生の `.md`/`.mdx` を HTTP 経由で取得します。ファイルの列挙は、GitHub リポジトリのサブツリー（`github`）から行うか、raw ベース URL に対して明示的に指定（`url` + `files`）します。

```ts blume.config.ts
{
  type: "mdx-remote",
  prefix: "sdk",
  url: "https://raw.githubusercontent.com/acme/sdk/main/docs",
  files: ["intro.mdx", "guide.mdx"],
}
```

プライベートリポジトリのトークンは `GITHUB_TOKEN` 環境変数から読み込まれます。設定ファイルや生成された出力にインライン展開されることはなく、送信先は GitHub 自身のホスト（`api.github.com`、`raw.githubusercontent.com`）のみで、カスタムの `url` ベースに送信されることはありません。

リモートページは MDX とコンポーネントの機能をすべて備えた形でレンダリングされます。その本文は隠しステージングディレクトリに実体化され、ローカルドキュメントと並んで Astro を通してレンダリングされるため、コールアウト、タブ、その他すべての Blume コンポーネントが引き続き動作します。

### キャッシュとオフラインビルド [#caching-and-offline-builds]

各リモートソースは `.blume/cache/<source>/` 配下にスナップショットを保持します。ネットワークの一時的な不調や CMS の障害などで取得に失敗した場合、Blume はビルドを失敗させる代わりに、警告を出しつつ最後に取得できた正常なスナップショットを提供します。キャッシュは `.blume/` 内に置かれ、再生成されるものであり、コミットすることはありません。

開発環境では、リモートコンテンツは一度だけ取得され、そのセッション中は固定されます。更新するには開発サーバーを再起動してください。ローカルのファイルシステムソースは通常どおりホットリロードされます。代わりにリモートソースの変更をポーリングするには、そのソースに `pollInterval`（秒）を設定します。開発サーバーはその間隔で再取得を行い、コンテンツが実際に変更された場合にのみリロードします。作業中に API へアクセスしないようにするには、未設定のままにしてください。

## GitHub Releases

組み込みの `github-releases` ソースは、リポジトリのリリースを変更履歴（changelog）に変換します。各リリースは `type: changelog` のエントリになるため、リリースノートが*そのまま*変更履歴になり、二重に書く必要はありません。生成される[変更履歴タイムライン](/docs/advanced/changelog)と組み合わせれば、GitHub リリースを公開するだけで変更履歴エントリが出荷されます。

```ts blume.config.ts
import { defineConfig } from "blume";

export default defineConfig({
  content: {
    sources: [
      { type: "filesystem", root: "content" },
      {
        type: "github-releases",
        prefix: "changelog",
        owner: "acme",
        repo: "sdk",
        // prereleases: false,  // include prereleases (default off)
        // drafts: false,       // include drafts (needs a write token)
        // limit: 100,          // cap releases, newest-first
      },
    ],
  },
});
```

各リリースは変更履歴のフィールドへ自動的にマッピングされます。名前（またはタグ）がタイトルになり、公開日がタイムラインの並び順を決定し、タグが `changelog.version` になり、プレリリースには `Prerelease`（それ以外には `Release`）のタグが付きます。リリースノートはエントリの本文としてレンダリングされます。リリースページを `/changelog/v1-2-0` のようなルートにネストさせるには、ソースに `prefix` を指定してください。

プライベートリポジトリの認証には `GITHUB_TOKEN` 環境変数を使用します。これは他の GitHub 機能が使うものと同じトークンで、設定ファイルにインライン展開されることはありません。他のすべてのリモートソースと同様に `.blume/cache/<source>/` にキャッシュされ、API に到達できない場合はオフラインで提供されます。変更履歴は補助的なものであるため、キャッシュがない状態で取得に失敗した場合（トークンのない CI ビルドなど）は、ビルドを失敗させるのではなく、警告を出して空の変更履歴に縮退します。内容を反映させるには、CI やデプロイ環境で `GITHUB_TOKEN` を設定してください。

## Sanity

組み込みの `sanity` ソースは GROQ クエリを実行し、各ドキュメントのフィールドをフロントマターに、Portable Text の本文を Markdown にマッピングします。`@sanity/client` パッケージは任意の peer dependency です。このソースを使う場合にのみインストールしてください。

```ts blume.config.ts
import { defineConfig } from "blume";

export default defineConfig({
  content: {
    sources: [
      { type: "filesystem", root: "docs" },
      {
        type: "sanity",
        prefix: "guides",
        projectId: "abc123",
        dataset: "production",
        query: `*[_type == "guide"]`,
        // Field paths default to title / slug.current / body / _updatedAt
        fields: { slug: "slug.current", body: "content" },
      },
    ],
  },
});
```

プライベートデータセット用の読み取りトークンは `SANITY_TOKEN` 環境変数から取得されます。カスタムの Portable Text ブロックタイプは、アダプターの `serializers` オプションを通じて Blume コンポーネントにマッピングできます。このオプションは[カスタムソース](#custom-sources)として `sanitySource` を直接構築する場合に利用できます。

## Notion

組み込みの `notion` ソースは、Notion のデータベースをコレクションに変換します。各行がページになり、そのプロパティがフロントマターに、ブロックツリーが MDX になります。コールアウト、トグル、カラム、コードブロックは対応する Blume コンポーネントにマッピングされます。動画ブロックは、YouTube のリンクを保持している場合は `<YouTube>` 埋め込みに、それ以外の場合は `<video>` プレーヤーになり、いずれの場合もブロックのキャプションが `<Frame>` のキャプションになります。メディアファイルではなく動画ページへのリンク（たとえば Vimeo や Loom の URL）は、埋め込まれる代わりに警告として報告されます。`@notionhq/client`（v5 以降）は任意の peer dependency です。Blume はデータベースをその最初のデータソースを通じて読み込みます。

```ts blume.config.ts
import { defineConfig } from "blume";

export default defineConfig({
  content: {
    sources: [
      { type: "filesystem", root: "docs" },
      {
        type: "notion",
        prefix: "handbook",
        database: process.env.NOTION_DB_ID,
        // Property names default to the title-typed prop / Description / Slug / Order
        // Set publishedValue to treat Status as a publish gate (opt-in)
        publishedValue: "Published",
      },
    ],
  },
});
```

インテグレーショントークンは `NOTION_TOKEN` 環境変数から取得されます（データベースをインテグレーションと共有してください）。デフォルトではすべてのページがインポートされます。`Status` プロパティを公開ゲートとして扱うには `publishedValue` を設定してください。その場合、それ以外の値は `draft: true` にマッピングされ、本番ビルドでは除外されます。**Notion の画像および動画の URL は署名付きで有効期限がある**ため、アダプターはビルド時にそれらをダウンロードしてサイトのアセットに取り込み、参照を書き換えます。CMS のアセットが静的ビルドを壊すことはありません。API 呼び出しは小さなリクエストプール（Notion のインテグレーションごとのレート制限に合わせて同時3件）を通してペース調整されるため、数百ページ規模のデータベースでも `429` レスポンスを引き起こすことなくインポートできます。調整するには、ソースに `concurrency` を設定してください。

## プレビューと同期 [#preview-and-sync]

リモートコンテンツの取得方法と対象範囲は、2つのフラグで制御します。

- **`--preview`** を `blume dev` または `blume build` に付けると、下書きがレンダリングされ、未公開の CMS コンテンツが取得されます。Sanity は `previewDrafts` パースペクティブに切り替わり、Notion は `Status` によるフィルタリングを停止します。フラグなしの本番ビルドはこれまでどおり下書きを除外するため、プレビュービルドは出荷前に未公開の作業を安全に確認する手段になります。
- **`blume sync`** はすべてのリモートソースを再取得し、ランタイムを再生成します。開発環境はキャッシュ優先で、リモートソースは一度取得されると再起動時には `.blume/cache` から提供されます（高速でオフラインにも強い）。そのため、開発サーバーを再起動せずに最新の CMS コンテンツを取得する手段が `blume sync` です（起動中のサーバーはホットリロードします）。先にキャッシュを破棄するには `--force` を追加するか、ソースに `pollInterval` を設定して自動更新してください。

```sh
blume dev --preview      # author workflow: see drafts live
blume build --preview    # render a full preview build
blume sync               # refresh remote content now
blume sync --force       # ...ignoring any cached snapshot
```

## カスタムソース [#custom-sources]

`ContentSource` インターフェースを実装したオブジェクトはそのまま渡せます。これにより、カスタムシリアライザーを備えたアダプターや、組み込みでない任意のバックエンドを、その SDK をコアのインストールに持ち込むことなく組み込めます。

```ts blume.config.ts
import { defineConfig } from "blume";
import { sanitySource } from "blume/sources/sanity.ts";

export default defineConfig({
  content: {
    sources: [
      { type: "filesystem", root: "docs" },
      {
        type: "custom",
        source: sanitySource({
          name: "guides",
          prefix: "guides",
          projectId: "abc123",
          dataset: "production",
          query: `*[_type == "guide"]`,
          // Map custom Portable Text blocks to Blume components
          serializers: {
            callout: (block) => `<Callout>${block.text}</Callout>`,
          },
        }),
      },
    ],
  },
});
```

ソースは自身のネイティブな形式（Portable Text、Notion ブロック、リモート HTML）を Markdown/MDX テキストに正規化するため、ページがどこから来たものであっても、同じコンポーネントと Markdown の機能が適用されます。

ローカルファイルを読み込むカスタムソースでは、各エントリに `sourcePath` を、ソース自体に `contentRoot` を設定してください。`sourcePath` は診断メッセージ内でファイルを示し、その隣にある相対パスの画像を解決します。`contentRoot` はページの日付を求める git の `log` の範囲を限定するもので、これがないとそのソースのページは git 由来の[「最終更新」日付](/docs/configuration#last-modified)を取得できません。
