Pular para o conteúdo

EmDash para Desenvolvedores Astro

O EmDash é um CMS construído especificamente para Astro — não um CMS headless genérico com um adaptador para Astro. Ele estende seu site Astro com conteúdo baseado em banco de dados, uma interface de administração polida e recursos no estilo WordPress (menus, widgets, taxonomias), preservando a experiência de desenvolvedor que você espera.

Tudo o que você sabe sobre Astro ainda se aplica. O EmDash aprimora seu site; ele não substitui seu fluxo de trabalho.

O EmDash fornece os recursos de gerenciamento de conteúdo que os sites Astro baseados em arquivos não possuem:

RecursoDescrição
Interface de AdministraçãoInterface completa de edição WYSIWYG em /_emdash/admin
Armazenamento em Banco de DadosConteúdo armazenado em SQLite, libSQL ou Cloudflare D1
Biblioteca de MídiaFaça upload, organize e sirva imagens e arquivos
Menus de NavegaçãoGerenciamento de menus com arrastar e soltar e aninhamento
Áreas de WidgetsBarras laterais e regiões de rodapé dinâmicas
Configurações do SiteConfiguração global (título, logotipo, links sociais)
TaxonomiasCategorias, tags e taxonomias personalizadas
Sistema de Pré-visualizaçãoURLs de pré-visualização assinadas para conteúdo em rascunho
RevisõesHistórico de versões do conteúdo

As coleções astro:content do Astro são baseadas em arquivos e resolvidas no momento da construção. As coleções do EmDash são baseadas em banco de dados e resolvidas em tempo de execução.

Coleções do AstroColeções do EmDash
ArmazenamentoArquivos Markdown/MDX em src/content/Banco de dados SQLite/D1
EdiçãoEditor de códigoInterface de Administração
Formato de ConteúdoMarkdown com frontmatterPortable Text (JSON estruturado)
AtualizaçõesRequer reconstruçãoInstantâneas (SSR)
EsquemaZod em content.config.tsDefinido na administração, armazenado no banco de dados
Melhor paraConteúdo gerenciado por desenvolvedoresConteúdo gerenciado por editores

As coleções do Astro e o EmDash podem coexistir. Use coleções do Astro para conteúdo de desenvolvedor (documentação, registros de alterações) e EmDash para conteúdo de editores (postagens de blog, páginas):

src/pages/index.astro
---
import { getCollection } from "astro:content";
import { getEmDashCollection } from "emdash";
// Documentação gerenciada por desenvolvedor a partir de arquivos
const docs = await getCollection("docs");
// Postagens gerenciadas por editor a partir do banco de dados
const { entries: posts } = await getEmDashCollection("posts", {
status: "published",
limit: 5,
});
---

O EmDash requer dois arquivos de configuração.

ts title="astro.config.mjs"
import { defineConfig } from "astro/config";
import emdash, { local } from "emdash/astro";
import { sqlite } from "emdash/db";
export default defineConfig({
output: "server", // Required for EmDash
integrations: [
emdash({
database: sqlite({ url: "file:./data.db" }),
storage: local({
directory: "./uploads",
baseUrl: "/_emdash/api/media/file",
}),
}),
],
});
ts title="src/live.config.ts"
import { defineLiveCollection } from "astro:content";
import { emdashLoader } from "emdash/runtime";
export const collections = {
_emdash: defineLiveCollection({
loader: emdashLoader(),
}),
};

Isso registra o EmDash como uma fonte de conteúdo ao vivo. A coleção _emdash roteia internamente para seus tipos de conteúdo (postagens, páginas, produtos).

O EmDash fornece funções de consulta que seguem o padrão de coleções de conteúdo ao vivo do Astro, retornando { entries, error } ou { entry, error }:

import { getEmDashCollection, getEmDashEntry } from "emdash";
// Obtenha todas as postagens publicadas - retorna { entries, error }
const { entries: posts } = await getEmDashCollection("posts", {
status: "published",
});
// Obtenha uma única postagem por slug - retorna { entry, error, isPreview }
const { entry: post } = await getEmDashEntry("posts", "my-post");

getEmDashCollection suporta filtragem que o getCollection do Astro não suporta:

const { entries: posts } = await getEmDashCollection("posts", {
status: "published", // draft | published | archived
limit: 10, // max results
where: { category: "news" }, // taxonomy filter
});

O EmDash armazena texto rico como Portable Text, um formato JSON estruturado. Renderize-o com o componente PortableText:

astro title="src/pages/posts/[slug].astro"
---
import { getEmDashEntry } from "emdash";
import { PortableText } from "emdash/ui";
const { slug } = Astro.params;
const { entry: post } = await getEmDashEntry("posts", slug);
if (!post) {
return Astro.redirect("/404");
}
---
<article>
<h1>{post.data.title}</h1>
<PortableText value={post.data.content} />
</article>

O EmDash fornece APIs para recursos no estilo WordPress que não existem na camada de conteúdo do Astro.

astro title="src/layouts/Base.astro"
---
import { getMenu } from "emdash";
const primaryMenu = await getMenu("primary");
---
{primaryMenu && (
<nav>
<ul>
{primaryMenu.items.map(item => (
<li>
<a href={item.url}>{item.label}</a>
{item.children.length > 0 && (
<ul>
{item.children.map(child => (
<li><a href={child.url}>{child.label}</a>
</li>
))}
</ul>
)}
</li>
))}
</ul>
</nav>
)}
astro title="src/layouts/BlogPost.astro"
---
import { getWidgetArea } from "emdash";
import { PortableText } from "emdash/ui";
const sidebar = await getWidgetArea("sidebar");
---
{sidebar && sidebar.widgets.length > 0 && (
<aside>
{sidebar.widgets.map(widget => (
<div class="widget">
{widget.title && <h3>{widget.title}</h3>
}
{widget.type === "content" && widget.content && (
<PortableText value={widget.content} />
)}
</div>
))}
</aside>
)}
astro title="src/components/Header.astro"
---
import { getSiteSettings, getSiteSetting } from "emdash";
const settings = await getSiteSettings();
// Ou busque valores individuais:
const title = await getSiteSetting("title");
---
<header>
{settings.logo ? (
<img src={settings.logo.url} alt={settings.title} />
) : (
<span>{settings.title}</span>
)}
{settings.tagline && <p>{settings.tagline}</p>
}
</header>

Estenda o EmDash com plugins que adicionam hooks, armazenamento, configurações e interface de administração:

ts title="astro.config.mjs"
import emdash from "emdash/astro";
import seoPlugin from "@emdash-cms/plugin-seo";
export default defineConfig({
integrations: [
emdash({
// ...
plugins: [seoPlugin({ generateSitemap: true })],
}),
],
});

Crie plugins personalizados com definePlugin:

ts title="src/plugins/analytics.ts"
import { definePlugin } from "emdash";
export default definePlugin({
id: "analytics",
version: "1.0.0",
capabilities: ["read:content"],
hooks: {
"content:afterSave": async (event, ctx) => {
ctx.log.info("Conteúdo salvo", { id: event.content.id });
},
},
admin: {
settingsSchema: {
trackingId: { type: "string", label: "Tracking ID" },
},
},
});

Os sites EmDash rodam em modo SSR. As alterações de conteúdo aparecem imediatamente sem reconstruções.

Para páginas estáticas com getStaticPaths, o conteúdo é buscado no momento da construção:

astro title="src/pages/posts/[slug].astro"
---
import { getEmDashCollection, getEmDashEntry } from "emdash";
export async function getStaticPaths() {
const { entries: posts } = await getEmDashCollection("posts", {
status: "published",
});
return posts.map((post) => ({
params: { slug: post.data.slug },
}));
}
const { slug } = Astro.params;
const { entry: post } = await getEmDashEntry("posts", slug);
---

Para páginas dinâmicas, defina prerender = false para buscar o conteúdo em cada requisição:

astro title="src/pages/posts/[slug].astro"
---
export const prerender = false;
import { getEmDashEntry } from "emdash";
const { slug } = Astro.params;
const { entry: post, error } = await getEmDashEntry("posts", slug);
if (error) {
return new Response("Erro no servidor", { status: 500 });
}
if (!post) {
return new Response(null, { status: 404 });
}
---