EmDash は、管理アプリケーション、データベースを基盤とするコレクション、メディア、メニュー、タクソノミー、設定、リビジョン、プレビューを Astro サイトに追加します。ページとコンポーネントは、これまでどおり通常の Astro ファイルのままです。
EmDash が追加するもの
| 機能 | 提供されるもの |
|---|---|
| 管理画面 | /_emdash/admin で、ブラウザーからコレクション、メディア、メニュー、タクソノミー、設定を管理 |
| データベースコレクション | リクエスト時にクエリされる、エディターが管理するコンテンツ |
| メディアライブラリ | 保存された画像とファイル、およびテンプレート向けのメディアフィールド値 |
| 下書き、リビジョン、プレビュー | 公開前の編集作業 |
| メニューとウィジェットエリア | エントリのフィールド外にある、順序付けられた編集可能なサイト領域 |
| サイト設定 | タイトル、タグライン、ロゴ、ページネーションのサイズなど、共有されるアイデンティティと表示の値 |
| プラグイン | フック、ルート、ストレージ、オプションの管理画面拡張 |
これらの機能は Astro を置き換えるのではなく、Astro と並んで存在します。ルーティング、レイアウト、レンダリング、スタイル、デプロイアダプターは、引き続き Astro が制御します。
EmDash と Astro コレクション
Astro のコンテンツコレクションと EmDash のコレクションは共存できます。リポジトリが所有するコンテンツには Astro コレクションを、/_emdash/admin で管理するコンテンツには EmDash を使用します。
| Astro コンテンツコレクション | EmDash コレクション | |
|---|---|---|
| ストレージ | プロジェクト内のファイル | SQL データベース |
| 編集 | リポジトリのワークフロー | EmDash 管理画面 |
| クエリ | getCollection() | getEmDashCollection() |
| リッチテキスト | Markdown または MDX | Portable Text |
| 配信 | ビルド時またはライブローダー | ランタイムのライブローダー |
所有者が異なる場合は、両方のコレクションシステムを使用します。たとえば、プロダクトのサイトでは、開発者が書くリリースノートを Astro コンテンツコレクションに、エディターが書く記事を EmDash に保持できます。
---
import { getCollection } from "astro:content";
import { getEmDashCollection } from "emdash";
const [releaseNotes, { entries: articles }] = await Promise.all([
getCollection("releases"),
getEmDashCollection("articles", { limit: 3 }),
]);
---
2 つの結果は別々のままです。EmDash は、ファイルベースのエントリを自身のデータベースにコピーしません。
サイトを構成する
現在の Node テンプレートは、Astro をサーバー出力用に構成し、EmDash インテグレーションを追加し、SQLite とローカルストレージのアダプターを使用します。
次の簡略化した構成には、それらの必須の要素が含まれています。
import node from "@astrojs/node";
import react from "@astrojs/react";
import { defineConfig } from "astro/config";
import emdash, { local } from "emdash/astro";
import { sqlite } from "emdash/db";
export default defineConfig({
output: "server",
adapter: node({ mode: "standalone" }),
integrations: [
react(),
emdash({
database: sqlite({ url: "file:./data.db" }),
storage: local({
directory: "./uploads",
baseUrl: "/_emdash/api/media/file",
}),
}),
],
});
EmDash は、D1 と R2 向けに構成された Cloudflare テンプレートも提供しています。Node アダプターを手作業で置き換えるのではなく、デプロイ先に合ったテンプレートから始めてください。
ライブコレクションを登録する
テンプレートは、_emdash という名前の 1 つの Astro ライブコレクションを通じて、EmDash のコンテンツを公開します。
import { defineLiveCollection } from "astro:content";
import { emdashLoader } from "emdash/runtime";
export const collections = {
_emdash: defineLiveCollection({ loader: emdashLoader() }),
};
getEmDashCollection() と getEmDashEntry() は、このローダーを通じて、リクエストされたコンテンツタイプを選択します。
コレクションをクエリする
次のクエリは、最近公開された投稿を読み取ります。orderBy は保存されたフィールド名を使用し、各名前を "asc" または "desc" にマッピングします。
---
import { getEmDashCollection } from "emdash";
const { entries: posts, error, cacheHint } = await getEmDashCollection("posts", {
orderBy: { published_at: "desc" },
limit: 10,
});
if (error) return new Response("Could not load posts", { status: 500 });
if (Astro.cache?.enabled) Astro.cache.set(cacheHint);
---
{posts.map((post) => (
<article>
<h2><a href={`/posts/${post.id}`}>{post.data.title}</a></h2>
</article>
))}
匿名のクエリは、公開済みのコンテンツを返します。明示的な status フィルターは、認証済みのコードやプレビューに対応したコードで役立ちます。where はコンテンツフィールドとタクソノミー名を受け付けます。フィルターとページネーションの完全な形式は、コンテンツのクエリを参照してください。
1 つのエントリをクエリする
getEmDashEntry() に、スラグまたはデータベース ID を渡します。次のルートは、URL のスラグを使用します。
---
import { decodeSlug, getEmDashEntry } from "emdash";
import { PortableText } from "emdash/ui";
const slug = decodeSlug(Astro.params.slug);
if (!slug) return Astro.rewrite("/404");
const { entry: post, error, cacheHint } = await getEmDashEntry("posts", slug);
if (error) return new Response("Could not load the post", { status: 500 });
if (!post) return Astro.rewrite("/404");
if (Astro.cache?.enabled) Astro.cache.set(cacheHint);
---
<article>
<h1>{post.data.title}</h1>
<PortableText value={post.data.content} />
</article>
返される entry.id は Astro のルート識別子で、通常はスラグです。データベースのコンテンツ ID は entry.data.id です。保存済みのコンテンツ ID を必要とするヘルパーには、data.id を使用してください。
動的 CMS 機能を使用する
EmDash は、単一のコレクションエントリに属さないデータのためのサーバーヘルパーをエクスポートしています。
---
import { getMenu, getSiteSettings } from "emdash";
import { WidgetArea } from "emdash/ui";
const [menu, settings] = await Promise.all([
getMenu("primary"),
getSiteSettings(),
]);
---
<header>
<a href="/">{settings.title}</a>
<nav>
{menu?.items.map((item) => <a href={item.url}>{item.label}</a>)}
</nav>
</header>
<main><slot /></main>
<aside><WidgetArea name="sidebar" /></aside>
プラグイン形式を選択する
サンドボックス化されたプラグインとネイティブプラグインでは、パッケージの構成が異なります。サンドボックス化されたプラグインは、emdash-plugin.jsonc と、デフォルトエクスポートされた src/plugin.ts オブジェクトを使用します。ネイティブプラグインは、ディスクリプターファクトリーと、definePlugin() で構築した createPlugin() をエクスポートします。
プラグインを追加する前に、プラグイン形式の選択を読んでください。ネイティブの definePlugin() の例を、サンドボックス化されたパッケージにコピーしないでください。