EmDash aggiunge un’applicazione di amministrazione, collezioni salvate in database, media, menu, tassonomie, impostazioni, revisioni e anteprime a un sito Astro. Le pagine e i componenti rimangono file Astro ordinari.
Cosa aggiunge EmDash
| Funzionalità | Cosa fornisce |
|---|---|
| Admin | Gestione basata su browser di collezioni, media, menu, tassonomie e impostazioni su /_emdash/admin |
| Collezioni database | Contenuti gestiti dall’editor interrogati al momento della richiesta |
| Libreria media | Immagini e file archiviati con valori dei 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, tagline, logo e dimensione della paginazione |
| Plugin | Hook, route, archiviazione ed estensioni admin opzionali |
Queste funzionalità vivono accanto ad Astro anziché sostituirlo. Astro controlla ancora il routing, i layout, il rendering, gli stili e l’adattatore di distribuzione.
EmDash e collezioni Astro
Le collezioni di contenuto Astro e le collezioni 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 contenuto Astro | Collezione EmDash | |
|---|---|---|
| Archiviazione | File nel progetto | Database SQL |
| Modifica | Flusso di lavoro del repository | Admin EmDash |
| Query | getCollection() | getEmDashCollection() |
| Testo ricco | Markdown o MDX | Portable Text |
| Consegna | Build-time o live loader | Live loader a runtime |
Usa entrambi i sistemi di collezioni quando la proprietà differisce. Ad esempio, un sito prodotto può mantenere le note di rilascio scritte dagli sviluppatori in una collezione di contenuto 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 rimangono separati. EmDash non copia le voci basate su file nel suo database.
Configurare un sito
I template Node attuali configurano Astro per l’output server, aggiungono l’integrazione EmDash e usano gli adattatori SQLite e archiviazione locale.
La seguente configurazione ridotta contiene questi elementi richiesti:
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 il target di distribuzione anziché tradurre gli adattatori Node a mano.
Registrare la collezione live
I template espongono il contenuto EmDash attraverso una collezione live 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 archiviati e mappa 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("Impossibile caricare gli articoli", { 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 il contenuto pubblicato. Un filtro status esplicito è utile nel codice autenticato o con anteprima. where accetta campi di contenuto e nomi di tassonomia; vedi Interrogare i contenuti per le forme complete di filtro e paginazione.
Interrogare una voce
Passa uno slug o un ID del database a getEmDashEntry(). La seguente route usa il suo slug 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("Impossibile caricare l'articolo", { 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>
L’entry.id restituito è l’identificatore di route di Astro ed è normalmente lo slug. L’ID del contenuto del database è entry.data.id. Usa data.id con le funzioni di supporto che richiedono un ID di contenuto archiviato.
Usare le funzionalità CMS dinamiche
EmDash esporta helper lato server per dati che non appartengono a una singola voce di 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 sandboxed e nativi hanno strutture di pacchetto diverse. I plugin sandboxed usano emdash-plugin.jsonc più un oggetto src/plugin.ts esportato di default. I plugin nativi esportano una factory di descrittori e createPlugin() costruito con definePlugin().
Leggi Scegliere un formato di plugin prima di aggiungere un plugin. Non copiare un esempio nativo definePlugin() in un pacchetto sandboxed.