EmDash aggiunge a un sito Astro un’applicazione di amministrazione, collezioni supportate da database, media, menu, tassonomie, impostazioni, revisioni e anteprime. Le pagine e i componenti restano normali file Astro.
Cosa aggiunge EmDash
| Funzionalità | Cosa offre |
|---|---|
| Amministrazione | Gestione nel browser di collezioni, media, menu, tassonomie e impostazioni su /_emdash/admin |
| Collezioni su database | Contenuti gestiti dagli editor e interrogati al momento della richiesta |
| Libreria multimediale | Immagini e file archiviati, con valori di campi media per i template |
| Bozze, revisioni e anteprime | Lavoro editoriale prima della pubblicazione |
| Menu e aree widget | Regioni del sito ordinate e modificabili, al di fuori dei campi delle voci |
| Impostazioni del sito | Valori condivisi di identità e visualizzazione, come titolo, slogan, logo e dimensione della paginazione |
| Plugin | Hook, route, archiviazione ed estensioni opzionali dell’amministrazione |
Queste funzionalità convivono con Astro anziché sostituirlo. Astro continua a controllare il routing, i layout, il rendering, gli stili e l’adattatore di distribuzione.
EmDash e collezioni Astro
Le collezioni di contenuti di Astro e le collezioni di EmDash possono coesistere. Usa le collezioni Astro per i contenuti di proprietà del repository e EmDash per i contenuti gestiti tramite /_emdash/admin.
| Collezione di contenuti Astro | Collezione EmDash | |
|---|---|---|
| Archiviazione | File nel progetto | Database SQL |
| Modifica | Flusso di lavoro del repository | Amministrazione EmDash |
| Query | getCollection() | getEmDashCollection() |
| Rich text | Markdown o MDX | Portable Text |
| Distribuzione | In fase di build o con un loader live | Loader live a runtime |
Usa entrambi i sistemi di collezioni quando la proprietà dei contenuti è diversa. Ad esempio, il sito di un prodotto può mantenere le note di rilascio scritte dagli sviluppatori in una collezione di contenuti Astro e gli articoli scritti dagli editor in EmDash:
---
import { getCollection } from "astro:content";
import { getEmDashCollection } from "emdash";
const [releaseNotes, { entries: articles }] = await Promise.all([
getCollection("releases"),
getEmDashCollection("articles", { limit: 3 }),
]);
---
I due risultati restano separati. EmDash non copia le voci basate su file nel proprio database.
Configurare un sito
I template Node attuali configurano Astro per l’output server, aggiungono l’integrazione EmDash e usano gli adattatori SQLite e di archiviazione locale.
La seguente configurazione ridotta contiene questi elementi necessari:
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 fornisce anche template Cloudflare configurati per D1 e R2. Parti dal template per la destinazione di distribuzione invece di convertire a mano gli adattatori Node.
Registrare la collezione live
I template espongono i contenuti di EmDash tramite un’unica collezione live di Astro chiamata _emdash:
import { defineLiveCollection } from "astro:content";
import { emdashLoader } from "emdash/runtime";
export const collections = {
_emdash: defineLiveCollection({ loader: emdashLoader() }),
};
getEmDashCollection() e getEmDashEntry() selezionano il tipo di contenuto richiesto tramite questo loader.
Interrogare le collezioni
La seguente query legge gli articoli pubblicati più di recente. orderBy usa i nomi dei campi memorizzati e associa ogni nome a "asc" o "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>
))}
Le query anonime restituiscono i contenuti pubblicati. Un filtro status esplicito è utile nel codice autenticato o compatibile con l’anteprima. where accetta campi di contenuto e nomi di tassonomie; consulta Interrogare i contenuti per le forme complete di filtri e paginazione.
Interrogare una voce
Passa uno slug o un ID del database a getEmDashEntry(). La seguente route usa lo slug del proprio 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>
L’entry.id restituito è l’identificatore di route di Astro e normalmente è lo slug. L’ID del contenuto nel database è entry.data.id. Usa data.id con gli helper che richiedono un ID di contenuto memorizzato.
Usare le funzionalità CMS dinamiche
EmDash esporta helper lato server per i dati che non appartengono a una singola voce di una collezione:
---
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>
Scegliere un formato di plugin
I plugin in sandbox e i plugin nativi hanno strutture di pacchetto diverse. I plugin in sandbox usano emdash-plugin.jsonc più un oggetto src/plugin.ts esportato come default. I plugin nativi esportano una factory di descrittore e un createPlugin() creato con definePlugin().
Leggi Scegliere un formato di plugin prima di aggiungere un plugin. Non copiare un esempio nativo di definePlugin() in un pacchetto in sandbox.