EmDash agrega una aplicación de administración, colecciones respaldadas por base de datos, medios, menús, taxonomías, configuraciones, revisiones y vistas previas a un sitio Astro. Las páginas y componentes siguen siendo archivos Astro ordinarios.
Qué agrega EmDash
| Característica | Qué proporciona |
|---|---|
| Admin | Gestión basada en navegador de colecciones, medios, menús, taxonomías y configuraciones en /_emdash/admin |
| Colecciones de base de datos | Contenido gestionado por editores consultado en tiempo de solicitud |
| Biblioteca de medios | Imágenes y archivos almacenados con valores de campo de medios para plantillas |
| Borradores, revisiones y vistas previas | Trabajo editorial antes de la publicación |
| Menús y áreas de widgets | Regiones del sitio ordenadas y editables fuera de los campos de entrada |
| Configuración del sitio | Valores compartidos de identidad y visualización como título, eslogan, logo y tamaño de paginación |
| Plugins | Hooks, rutas, almacenamiento y extensiones de administración opcionales |
Estas características conviven con Astro en lugar de reemplazarlo. Astro sigue controlando el enrutamiento, layouts, renderizado, 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 contenido propio del repositorio y EmDash para contenido gestionado a través de /_emdash/admin.
| Colección de contenido Astro | Colección EmDash | |
|---|---|---|
| Almacenamiento | Archivos en el proyecto | Base de datos SQL |
| Edición | Flujo de trabajo del repositorio | Admin de EmDash |
| Consulta | getCollection() | getEmDashCollection() |
| Texto enriquecido | Markdown o MDX | Portable Text |
| Entrega | Tiempo de compilación o cargador en vivo | Cargador en vivo en tiempo de ejecución |
Usa ambos sistemas de colecciones cuando la propiedad difiere. Por ejemplo, un sitio de producto puede mantener notas de lanzamiento escritas por desarrolladores en una colección de contenido Astro y 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 en su base de datos.
Configurar un sitio
Las plantillas Node actuales configuran Astro para salida de servidor, agregan la integración EmDash y usan los adaptadores SQLite y almacenamiento local.
La siguiente configuración reducida contiene esas piezas requeridas:
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 proporciona plantillas de Cloudflare configuradas para D1 y R2. Comienza desde la plantilla para el objetivo de despliegue en lugar de traducir los adaptadores Node a mano.
Registrar la colección en vivo
Las plantillas exponen el contenido de EmDash a través de una 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 cargador.
Consultar colecciones
La siguiente consulta lee las publicaciones más recientemente publicadas. orderBy usa nombres de campos almacenados y mapea cada nombre 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("No se pudieron cargar las publicaciones", { 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 es útil en código autenticado o con vista previa. where acepta campos de contenido y nombres de taxonomía; consulta Consultar contenido para las formas completas de filtro y paginación.
Consultar una entrada
Pasa un slug o ID de base de datos a getEmDashEntry(). La siguiente ruta usa su slug de 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("No se pudo cargar la publicación", { 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>
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 funciones auxiliares que requieran un ID de contenido almacenado.
Usar características dinámicas del CMS
EmDash exporta funciones auxiliares del servidor para datos que no pertenecen a una sola entrada de 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 sandboxed y nativos tienen diferentes formas de paquete. Los plugins sandboxed usan emdash-plugin.jsonc más un objeto src/plugin.ts exportado por defecto. Los plugins nativos exportan una fábrica de descriptores y createPlugin() construido con definePlugin().
Lee Elegir un formato de plugin antes de agregar un plugin. No copies un ejemplo nativo de definePlugin() en un paquete sandboxed.