EmDash per sviluppatori Astro

In questa pagina

EmDash è un CMS costruito specificamente per Astro. Estende il tuo sito Astro con contenuti supportati da database, un’interfaccia di amministrazione raffinata e funzionalità in stile WordPress (menu, widget, tassonomie) preservando l’esperienza di sviluppo che ti aspetti.

Tutto ciò che sai su Astro rimane valido. EmDash aggiunge la gestione dei contenuti al tuo flusso di lavoro Astro esistente.

Cosa aggiunge EmDash

EmDash fornisce le funzionalità di gestione dei contenuti che mancano ai siti Astro basati su file:

FunzionalitàDescrizione
Interfaccia amministrativaInterfaccia di modifica WYSIWYG completa su /_emdash/admin
Archiviazione databaseContenuti archiviati in SQLite, libSQL, Cloudflare D1 o PostgreSQL
Libreria mediaCarica, organizza e servi immagini e file
Menu di navigazioneGestione menu drag-and-drop con annidamento
Aree widgetBarre laterali dinamiche e regioni footer
Impostazioni sitoConfigurazione globale (titolo, logo, link social)
TassonomieCategorie, tag e tassonomie personalizzate
Sistema di anteprimaURL di anteprima firmati per contenuti in bozza
RevisioniCronologia delle versioni dei contenuti

Collezioni Astro vs EmDash

Le collezioni astro:content di Astro sono basate su file e risolte al momento della build. Le collezioni EmDash sono supportate da database e risolte in fase di esecuzione.

Collezioni AstroCollezioni EmDash
ArchiviazioneFile Markdown/MDX in src/content/Database SQL (SQLite, libSQL, D1 o Postgres)
ModificaEditor di codiceInterfaccia amministrativa
Formato contenutoMarkdown con frontmatterPortable Text (JSON strutturato)
AggiornamentiRichiede ricostruzioneIstantaneo (SSR)
SchemaZod in content.config.tsDefinito nell’admin, archiviato nel database
Ideale perContenuti gestiti da sviluppatoriContenuti gestiti da editor

Usare entrambi insieme

Le collezioni Astro ed EmDash possono coesistere. Usa le collezioni Astro per i contenuti degli sviluppatori (documentazione, changelog) ed EmDash per i contenuti degli editor (post del blog, pagine):

---
import { getCollection } from "astro:content";
import { getEmDashCollection } from "emdash";

// Developer-managed docs from files
const docs = await getCollection("docs");

// Editor-managed posts from database
const { entries: posts } = await getEmDashCollection("posts", {
  status: "published",
  limit: 5,
});
---

Configurazione

EmDash richiede due file di configurazione.

Integrazione Astro

La seguente configurazione registra EmDash come integrazione Astro in modalità output server:

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",
			}),
		}),
	],
});

Caricatore di collezioni live

Il seguente file registra EmDash come sorgente di contenuti live:

import { defineLiveCollection } from "astro:content";
import { emdashLoader } from "emdash/runtime";

export const collections = {
	_emdash: defineLiveCollection({
		loader: emdashLoader(),
	}),
};

La collezione _emdash indirizza internamente ai tuoi tipi di contenuto (post, pagine, prodotti).

Interrogare i contenuti

EmDash fornisce funzioni di query che seguono il pattern delle collezioni di contenuti live di Astro, restituendo { entries, error } o { entry, error }:

EmDash

import { getEmDashCollection, getEmDashEntry } from "emdash";

// Get all published posts - returns { entries, error }
const { entries: posts } = await getEmDashCollection("posts", {
status: "published",
});

// Get a single post by slug - returns { entry, error, isPreview }
const { entry: post } = await getEmDashEntry("posts", "my-post");

Astro

import { getCollection, getEntry } from "astro:content";

// Get all blog entries
const posts = await getCollection("blog");

// Get a single entry by slug
const post = await getEntry("blog", "my-post");

Opzioni di filtraggio

getEmDashCollection supporta il filtraggio che getCollection di Astro non offre:

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

Renderizzare i contenuti

EmDash archivia il testo ricco come Portable Text, un formato JSON strutturato. Renderizzalo con il componente PortableText:

EmDash

---
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>

Astro

---
import { getEntry, render } from "astro:content";

const { slug } = Astro.params;
const post = await getEntry("blog", slug);
const { Content } = await render(post);

---

<article>
  <h1>{post.data.title}</h1>
  <Content />
</article>

Funzionalità dinamiche

EmDash fornisce API per funzionalità in stile WordPress che non esistono nel livello di contenuti di Astro.

Il seguente layout recupera un menu per posizione e lo renderizza con elementi annidati:

---
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>
)}

Aree widget

Il seguente layout recupera un’area widget e renderizza ogni widget:

---
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>
)}

Impostazioni sito

Il seguente componente legge le impostazioni globali del sito e renderizza un logo o un titolo:

---
import { getSiteSettings, getSiteSetting } from "emdash";

const settings = await getSiteSettings();
// Or fetch individual values:
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>

Plugin

Estendi EmDash con plugin che aggiungono hook, archiviazione, impostazioni e interfaccia amministrativa:

import emdash from "emdash/astro";
import seoPlugin from "@emdash-cms/plugin-seo";

export default defineConfig({
	integrations: [
		emdash({
			// ...
			plugins: [seoPlugin({ generateSitemap: true })],
		}),
	],
});

Crea plugin personalizzati con definePlugin:

import { definePlugin } from "emdash";

export default definePlugin({
	id: "analytics",
	version: "1.0.0",
	capabilities: ["content:read"],

	hooks: {
		"content:afterSave": async (event, ctx) => {
			ctx.log.info("Content saved", { id: event.content.id });
		},
	},

	admin: {
		settingsSchema: {
			trackingId: { type: "string", label: "Tracking ID" },
		},
	},
});

Rendering lato server

I siti EmDash vengono eseguiti in modalità SSR, quindi il contenuto viene servito in fase di esecuzione e le modifiche appaiono immediatamente.

Per le pagine statiche con getStaticPaths, il contenuto viene recuperato al momento della build:

---
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);
---

Per le pagine dinamiche, imposta prerender = false per recuperare il contenuto ad ogni richiesta:

---
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("Server error", { status: 500 });
}

if (!post) {
  return new Response(null, { status: 404 });
}
---

Prossimi passi