EmDash para desarrolladores de Astro

En esta página

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ísticaQué proporciona
AdminGestión basada en navegador de colecciones, medios, menús, taxonomías y configuraciones en /_emdash/admin
Colecciones de base de datosContenido gestionado por editores consultado en tiempo de solicitud
Biblioteca de mediosImágenes y archivos almacenados con valores de campo de medios para plantillas
Borradores, revisiones y vistas previasTrabajo editorial antes de la publicación
Menús y áreas de widgetsRegiones del sitio ordenadas y editables fuera de los campos de entrada
Configuración del sitioValores compartidos de identidad y visualización como título, eslogan, logo y tamaño de paginación
PluginsHooks, 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 AstroColección EmDash
AlmacenamientoArchivos en el proyectoBase de datos SQL
EdiciónFlujo de trabajo del repositorioAdmin de EmDash
ConsultagetCollection()getEmDashCollection()
Texto enriquecidoMarkdown o MDXPortable Text
EntregaTiempo de compilación o cargador en vivoCargador 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.

Próximos pasos