Astro開発者のためのEmDash

このページ

EmDashは、Astroサイトに管理アプリケーション、データベース連携コレクション、メディア、メニュー、タクソノミー、設定、リビジョン、プレビューを追加します。ページとコンポーネントは通常のAstroファイルのままです。

EmDashが追加するもの

機能提供するもの
Admin/_emdash/admin でのブラウザベースのコレクション、メディア、メニュー、タクソノミー、設定管理
データベースコレクションリクエスト時に照会されるエディター管理コンテンツ
メディアライブラリテンプレート用のメディアフィールド値を持つ保存済み画像とファイル
下書き、リビジョン、プレビュー公開前の編集作業
メニューとウィジェットエリアエントリフィールド外の順序付けされた編集可能なサイト領域
サイト設定タイトル、タグライン、ロゴ、ページネーションサイズなどの共有アイデンティティと表示値
プラグインフック、ルート、ストレージ、オプションの管理拡張

これらの機能はAstroを置き換えるのではなく、Astroと共存します。Astroは引き続きルーティング、レイアウト、レンダリング、スタイル、デプロイアダプターを制御します。

EmDashとAstroコレクション

AstroコンテンツコレクションとEmDashコレクションは共存できます。リポジトリ所有のコンテンツにはAstroコレクションを、/_emdash/adminで管理されるコンテンツにはEmDashを使用してください。

AstroコンテンツコレクションEmDashコレクション
ストレージプロジェクト内のファイルSQLデータベース
編集リポジトリワークフローEmDash管理画面
クエリgetCollection()getEmDashCollection()
リッチテキストMarkdownまたはMDXPortable 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という名前の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("投稿を読み込めませんでした", { 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つのエントリをクエリする

スラグまたはデータベースIDをgetEmDashEntry()に渡します。以下のルートはURLスラグを使用します:

---
import { decodeSlug, getEmDashEntry } from "emdash";
import { PortableText } from "emdash/ui";

const slug = decodeSlug(Astro.params.slug);
if (!slug) return Astro.redirect("/404");

const { entry: post, error, cacheHint } = await getEmDashEntry("posts", slug);
if (error) return new Response("投稿を読み込めませんでした", { status: 500 });
if (!post) return Astro.redirect("/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()の例をサンドボックスパッケージにコピーしないでください。

次のステップ