EmDash añade a un sitio de Astro una aplicación de administración, colecciones respaldadas por base de datos, medios, menús, taxonomías, ajustes, revisiones y vistas previas. Las páginas y los componentes siguen siendo archivos de Astro normales.
Qué agrega EmDash
| Función | Qué ofrece |
|---|---|
| Administración | Gestión en el navegador de colecciones, medios, menús, taxonomías y ajustes en /_emdash/admin |
| Colecciones de base de datos | Contenido gestionado por editores y consultado en el momento de la solicitud |
| Biblioteca de medios | Imágenes y archivos almacenados, con valores de campos de medios para las plantillas |
| Borradores, revisiones y vistas previas | Trabajo editorial antes de la publicación |
| Menús y áreas de widgets | Regiones ordenadas y editables del sitio, fuera de los campos de las entradas |
| Ajustes del sitio | Valores compartidos de identidad y visualización, como el título, el eslogan, el logotipo y el tamaño de paginación |
| Plugins | Hooks, rutas, almacenamiento y extensiones opcionales de la administración |
Estas funciones conviven con Astro en lugar de reemplazarlo. Astro sigue controlando el enrutamiento, los layouts, el renderizado, los estilos y el adaptador de despliegue.
EmDash y colecciones de Astro
Las colecciones de contenido de Astro y las colecciones de EmDash pueden coexistir. Usa colecciones de Astro para el contenido que pertenece al repositorio y EmDash para el contenido gestionado mediante /_emdash/admin.
| Colección de contenido de Astro | Colección de EmDash | |
|---|---|---|
| Almacenamiento | Archivos en el proyecto | Base de datos SQL |
| Edición | Flujo de trabajo del repositorio | Administración de EmDash |
| Consulta | getCollection() | getEmDashCollection() |
| Texto enriquecido | Markdown o MDX | Portable Text |
| Entrega | En tiempo de compilación o con un loader en vivo | Loader en vivo en tiempo de ejecución |
Usa ambos sistemas de colecciones cuando la propiedad del contenido sea distinta. Por ejemplo, el sitio de un producto puede mantener las notas de versión escritas por desarrolladores en una colección de contenido de Astro y los artículos escritos por editores en EmDash:
---
import { getCollection } from "astro:content";
import { getEmDashCollection } from "emdash";
const [releaseNotes, { entries: articles }] = await Promise.all([
getCollection("releases"),
getEmDashCollection("articles", { limit: 3 }),
]);
---
Los dos resultados permanecen separados. EmDash no copia las entradas basadas en archivos a su base de datos.
Configurar un sitio
Las plantillas actuales de Node configuran Astro para la salida de servidor, añaden la integración de EmDash y usan los adaptadores de SQLite y de almacenamiento local.
La siguiente configuración reducida contiene esas piezas necesarias:
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 también incluye plantillas de Cloudflare configuradas para D1 y R2. Empieza desde la plantilla del destino de despliegue en lugar de traducir a mano los adaptadores de Node.
Registrar la colección en vivo
Las plantillas exponen el contenido de EmDash mediante una única colección en vivo de Astro llamada _emdash:
import { defineLiveCollection } from "astro:content";
import { emdashLoader } from "emdash/runtime";
export const collections = {
_emdash: defineLiveCollection({ loader: emdashLoader() }),
};
getEmDashCollection() y getEmDashEntry() seleccionan el tipo de contenido solicitado a través de este loader.
Consultar colecciones
La siguiente consulta lee las entradas publicadas más recientemente. orderBy usa los nombres de campo almacenados y asigna a cada nombre "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>
))}
Las consultas anónimas devuelven contenido publicado. Un filtro status explícito resulta útil en código autenticado o con soporte de vista previa. where acepta campos de contenido y nombres de taxonomías; consulta Consultar contenido para ver las formas completas de los filtros y la paginación.
Consultar una entrada
Pasa un slug o un ID de base de datos a getEmDashEntry(). La siguiente ruta usa el slug de su 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>
El entry.id devuelto es el identificador de ruta de Astro y normalmente es el slug. El ID de contenido de la base de datos es entry.data.id. Usa data.id con los helpers que requieren un ID de contenido almacenado.
Usar características dinámicas del CMS
EmDash exporta helpers de servidor para datos que no pertenecen a una sola entrada de una colección:
---
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>
Elegir un formato de plugin
Los plugins en sandbox y los plugins nativos tienen estructuras de paquete diferentes. Los plugins en sandbox usan emdash-plugin.jsonc más un objeto src/plugin.ts exportado por defecto. Los plugins nativos exportan una fábrica de descriptores y un createPlugin() creado con definePlugin().
Lee Elegir un formato de plugin antes de añadir un plugin. No copies un ejemplo nativo de definePlugin() en un paquete en sandbox.