Zum Inhalt springen

Seed-Dateiformat

Seed-Dateien sind JSON-Dokumente, die EmDash-Websites initialisieren. Sie definieren Sammlungen, Felder, Taxonomien, Menüs, Weiterleitungen, Widget-Bereiche, Website-Einstellungen und optional Beispielinhalte.

{
"$schema": "https://emdashcms.com/seed.schema.json",
"version": "1",
"meta": {},
"settings": {},
"collections": [],
"taxonomies": [],
"bylines": [],
"menus": [],
"redirects": [],
"widgetAreas": [],
"sections": [],
"content": {}
}
FeldTypErforderlichBeschreibung
$schemastringNeinJSON-Schema-URL für Editor-Validierung
version"1"JaSeed-Formatversion
metaobjectNeinMetadaten über den Seed
settingsobjectNeinWebsite-Einstellungen
collectionsarrayNeinSammlungsdefinitionen
taxonomiesarrayNeinTaxonomiedefinitionen
bylinesarrayNeinByline-Profildefinitionen
menusarrayNeinNavigationsmenüs
redirectsarrayNeinWeiterleitungsregeln
widgetAreasarrayNeinWidget-Bereichsdefinitionen
sectionsarrayNeinWiederverwendbare Inhaltsblöcke
contentobjectNeinBeispielinhaltseinträge

Optionale Metadaten über den Seed:

{
"meta": {
"name": "Blog-Starter",
"description": "Ein einfaches Blog mit Beitragen, Seiten und Kategorien",
"author": "EmDash"
}
}

Website-weite Konfigurationswerte:

{
"settings": {
"title": "Meine Website",
"tagline": "Ein modernes CMS",
"postsPerPage": 10,
"dateFormat": "MMMM d, yyyy"
}
}

Einstellungen werden auf die options-Tabelle mit dem Präfix site: angewendet. Der Einrichtungsassistent ermöglicht es Benutzern, title und tagline zu überschreiben.

Sammlungsdefinitionen erstellen Inhaltstypen in der Datenbank:

{
"collections": [
{
"slug": "posts",
"label": "Beitrage",
"labelSingular": "Beitrag",
"description": "Blogbeitrage",
"icon": "file-text",
"supports": ["drafts", "revisions"],
"fields": [
{
"slug": "title",
"label": "Titel",
"type": "string",
"required": true
},
{
"slug": "content",
"label": "Inhalt",
"type": "portableText"
},
{
"slug": "featured_image",
"label": "Beitragsbild",
"type": "image"
}
]
}
]
}
EigenschaftTypErforderlichBeschreibung
slugstringJaURL-sicherer Bezeichner (Kleinbuchstaben, Unterstriche)
labelstringJaPluraler Anzeigename
labelSingularstringNeinSingularer Anzeigename
descriptionstringNeinAdmin-UI-Beschreibung
iconstringNeinLucide-Icon-Name
supportsarrayNeinFunktionen: "drafts", "revisions"
fieldsarrayJaFelddefinitionen
EigenschaftTypErforderlichBeschreibung
slugstringJaSpaltenname (Kleinbuchstaben, Unterstriche)
labelstringJaAnzeigename
typestringJaFeldtyp
requiredbooleanNeinValidierung: Feld muss einen Wert haben
uniquebooleanNeinValidierung: Wert muss eindeutig sein
defaultValueanyNeinStandardwert für neue Einträge
validationobjectNeinZusätzliche Validierungsregeln
widgetstringNeinAdmin-UI-Widget-Überschreibung
optionsobjectNeinWidget-spezifische Konfiguration
TypBeschreibungGespeichert als
stringKurzer TextTEXT
textLanger Text (Textbereich)TEXT
numberNumerischer WertREAL
integerGanze ZahlINTEGER
booleanWahr/FalschINTEGER
dateDatumswertTEXT (ISO 8601)
datetimeDatum und UhrzeitTEXT (ISO 8601)
emailE-Mail-AdresseTEXT
urlURLTEXT
slugURL-sicherer StringTEXT
portableTextRich-Text-InhaltJSON
imageBildreferenzJSON
fileDateireferenzJSON
jsonBeliebiges JSONJSON
referenceReferenz zu einem anderen EintragTEXT

Klassifizierungssysteme für Inhalte:

{
"taxonomies": [
{
"name": "category",
"label": "Kategorien",
"labelSingular": "Kategorie",
"hierarchical": true,
"collections": ["posts"],
"terms": [
{ "slug": "news", "label": "Nachrichten" },
{ "slug": "tutorials", "label": "Tutorials" },
{
"slug": "advanced",
"label": "Fortgeschrittene Anleitungen",
"parent": "tutorials"
}
]
},
{
"name": "tag",
"label": "Schlagwörter",
"labelSingular": "Schlagwort",
"hierarchical": false,
"collections": ["posts"]
}
]
}
EigenschaftTypErforderlichBeschreibung
namestringJaEindeutiger Bezeichner
labelstringJaPluraler Anzeigename
labelSingularstringNeinSingularer Anzeigename
hierarchicalbooleanJaVerschachtelte Begriffe erlauben (Kategorien) oder flach (Tags)
collectionsarrayJaSammlungen, auf die diese Taxonomie angewendet wird
termsarrayNeinVordefinierte Begriffe
EigenschaftTypErforderlichBeschreibung
slugstringJaURL-sicherer Bezeichner
labelstringJaAnzeigename
descriptionstringNeinBegriffsbeschreibung
parentstringNeinÜbergeordneter Begriff-Slug (nur hierarchisch)

Navigationsmenüs, die über die Admin-Oberfläche bearbeitet werden können:

{
"menus": [
{
"name": "primary",
"label": "Hauptnavigation",
"items": [
{ "type": "custom", "label": "Startseite", "url": "/" },
{ "type": "page", "ref": "about" },
{ "type": "custom", "label": "Blog", "url": "/posts" },
{
"type": "custom",
"label": "Extern",
"url": "https://example.com",
"target": "_blank"
}
]
}
]
}
TypBeschreibungErforderliche Felder
customBenutzerdefinierte URLurl
pageLink zu einem Seiten-Eintragref
postLink zu einem Beitrags-Eintragref
taxonomyLink zu einem Taxonomie-Archivref, collection
collectionLink zu einem Sammlungs-Archivcollection
EigenschaftTypBeschreibung
typestringElementtyp (siehe oben)
labelstringAnzeigetext (automatisch für Seiten-/Beitrags-Referenzen generiert)
urlstringBenutzerdefinierte URL (für custom-Typ)
refstringInhalts-ID im Seed (für page/post-Typen)
collectionstringSammlungs-Slug
targetstring"_blank" für neues Fenster
titleAttrstringHTML-Titel-Attribut
cssClassesstringBenutzerdefinierte CSS-Klassen
childrenarrayVerschachtelte Menüpunkte

Autorenzeilen-Profile sind getrennt von der Eigentümerschaft (author_id). Definieren Sie wiederverwendbare Autorenzeilen-Identitäten einmal und referenzieren Sie sie dann von Inhalts-Einträgen aus.

{
"bylines": [
{
"id": "editorial",
"slug": "emdash-editorial",
"displayName": "EmDash Editorial"
},
{
"id": "guest",
"slug": "guest-contributor",
"displayName": "Gastautor",
"isGuest": true
}
]
}
EigenschaftTypErforderlichBeschreibung
idstringJaSeed-lokale ID, verwendet von content[].bylines
slugstringJaURL-sicherer Autorenzeilen-Slug
displayNamestringJaName, der in Vorlagen und APIs angezeigt wird
biostringNeinOptionale Profil-Biografie
websiteUrlstringNeinOptionale Website-URL
isGuestbooleanNeinMarkiert die Autorenzeile als Gastprofil

Weiterleitungsregeln, um Legacy-URLs nach einer Migration zu erhalten:

{
"redirects": [
{ "source": "/old-about", "destination": "/about" },
{ "source": "/legacy-feed", "destination": "/rss.xml", "type": 308 },
{
"source": "/category/news",
"destination": "/categories/news",
"groupName": "migration"
}
]
}
EigenschaftTypErforderlichBeschreibung
sourcestringJaQuellpfad (muss mit / beginnen)
destinationstringJaZielpfad (muss mit / beginnen)
typenumberNeinHTTP-Status: 301, 302, 307 oder 308
enabledbooleanNeinOb die Weiterleitung aktiv ist (Standard: true)
groupNamestringNeinOptionale Gruppierungsbezeichnung für Admin-Filterung/Suche

Konfigurierbare Inhaltsregionen:

{
"widgetAreas": [
{
"name": "sidebar",
"label": "Hauptseitenleiste",
"description": "Erscheint bei Blogbeitragen und Seiten",
"widgets": [
{
"type": "component",
"title": "Aktuelle Beitrage",
"componentId": "core:recent-posts",
"props": { "count": 5 }
},
{
"type": "menu",
"title": "Schnellzugriffe",
"menuName": "footer"
},
{
"type": "content",
"title": "Uber uns",
"content": [
{
"_type": "block",
"style": "normal",
"children": [{ "_type": "span", "text": "Willkommen auf unserer Website." }]
}
]
}
]
}
]
}
TypBeschreibungErforderliche Felder
contentRich-Text-Inhaltcontent (Portable Text)
menuRendert ein MenümenuName
componentRegistrierte KomponentecomponentId
Komponenten-IDBeschreibung
core:recent-postsListe aktueller Beiträge
core:categoriesKategorieliste
core:tagsTag-Wolke
core:searchSuchformular
core:archivesMonatliche Archive

Wiederverwendbare Inhaltsblöcke, die Redakteure über den /section-Slash-Befehl in Portable-Text-Felder einfügen können:

{
"sections": [
{
"slug": "hero-centered",
"title": "Zentrierter Hero",
"description": "Hero uber die volle Breite mit zentrierter Uberschrift und CTA-Button",
"keywords": ["hero", "banner", "header", "landing"],
"content": [
{
"_type": "block",
"style": "h1",
"children": [{ "_type": "span", "text": "Willkommen auf unserer Website" }]
},
{
"_type": "block",
"children": [
{ "_type": "span", "text": "Hier steht dein starkes Markenversprechen." }
]
}
]
}
]
}
EigenschaftTypErforderlichBeschreibung
slugstringJaURL-sicherer Bezeichner
titlestringJaAnzeigename im Abschnitts-Auswahlmenü
descriptionstringNeinErklärt, wann dieser Abschnitt verwendet werden soll
keywordsarrayNeinSuchbegriffe zum Finden des Abschnitts
contentarrayJaPortable-Text-Blöcke
sourcestringNein"theme" (Standard für Seeds) oder "import"

Abschnitte aus Seed-Dateien sind mit source: "theme" markiert und können nicht aus der Admin-UI gelöscht werden. Redakteure können eigene Abschnitte erstellen (source: "user") und jeden Abschnittstyp beim Bearbeiten von Inhalten einfügen.

Beispielinhalt, organisiert nach Sammlungen:

{
"content": {
"posts": [
{
"id": "hello-world",
"slug": "hello-world",
"status": "published",
"bylines": [
{ "byline": "editorial" },
{ "byline": "guest", "roleLabel": "Gastbeitrag" }
],
"data": {
"title": "Hallo Welt",
"content": [
{
"_type": "block",
"style": "normal",
"children": [{ "_type": "span", "text": "Willkommen." }]
}
],
"excerpt": "Dein erster Beitrag."
},
"taxonomies": {
"category": ["news"],
"tag": ["welcome", "first-post"]
}
}
],
"pages": [
{
"id": "about",
"slug": "about",
"status": "published",
"data": {
"title": "Uber uns",
"content": [
{
"_type": "block",
"style": "normal",
"children": [{ "_type": "span", "text": "Inhalt der Uber-uns-Seite." }]
}
]
}
}
]
}
}
EigenschaftTypErforderlichBeschreibung
idstringJaSeed-lokale ID für Referenzen
slugstringJaURL-Slug
statusstringNein"published" oder "draft" (Standard: "published")
dataobjectJaFeldwerte
bylinesarrayNeinGeordnete Autorenzeilen-Nennungen (byline, optional roleLabel)
taxonomiesobjectNeinZuweisungen von Begriffen nach Taxonomie-Name

Verweisen Sie auf andere Inhalts-Einträge mit dem Präfix $ref::

{
"data": {
"related_posts": ["$ref:another-post", "$ref:third-post"]
}
}

Das Präfix $ref: löst Seed-IDs während des Seedings in Datenbank-IDs auf.

Binden Sie Bilder von URLs ein:

{
"data": {
"featured_image": {
"$media": {
"url": "https://images.unsplash.com/photo-xxx",
"alt": "Beschreibung des Bildes",
"filename": "hero.jpg",
"caption": "Foto von jemandem"
}
}
}
}

Lokale Bilder aus .emdash/media/ einbinden:

{
"data": {
"featured_image": {
"$media": {
"file": "hero.jpg",
"alt": "Beschreibung des Bildes"
}
}
}
}
EigenschaftTypErforderlichBeschreibung
urlstringJa*Remote-URL zum Herunterladen
filestringJa*Lokaler Dateiname in .emdash/media/
altstringNeinAlt-Text für Barrierefreiheit
filenamestringNeinDateinamen überschreiben
captionstringNeinMedien-Beschriftung

*Entweder url oder file ist erforderlich, nicht beides.

Verwenden Sie die Seed-API für CLI-Tools oder Skripte:

import { applySeed, validateSeed } from "emdash/seed";
import seedData from "../../themes/.emdash/seed.json";
// Zuerst validieren
const validation = validateSeed(seedData);
if (!validation.valid) {
console.error(validation.errors);
process.exit(1);
}
// Seed anwenden
const result = await applySeed(db, seedData, {
includeContent: true,
onConflict: "skip",
storage: myStorage,
baseUrl: "http://localhost:4321",
});
console.log(result);
// {
// collections: { created: 2, skipped: 0 },
// fields: { created: 8, skipped: 0 },
// taxonomies: { created: 2, terms: 5 },
// bylines: { created: 2, skipped: 0 },
// menus: { created: 1, items: 4 },
// redirects: { created: 3, skipped: 0 },
// widgetAreas: { created: 1, widgets: 3 },
// settings: { applied: 3 },
// content: { created: 3, skipped: 0 },
// media: { created: 2, skipped: 0 }
// }
OptionTypStandardBeschreibung
includeContentbooleanfalseBeispielinhalte erstellen
onConflictstring"skip""skip", "update" oder "error"
mediaBasePathstring—Basispfad für lokale Mediendateien
storageStorage—Storage-Adapter für Medien-Uploads
baseUrlstring—Basis-URL für Medien-URLs

Seeding kann mehrfach sicher ausgeführt werden. Konfliktverhalten nach Entitätstyp:

EntitätVerhalten
SammlungÜberspringen, wenn Slug existiert
FeldÜberspringen, wenn Sammlung + Slug existiert
Taxonomy-DefinitionÜberspringen, wenn Name existiert
Taxonomie-BegriffÜberspringen, wenn Name + Slug existiert
Byline-ProfilÜberspringen, wenn Slug existiert
MenuÜberspringen, wenn Name existiert
MenueintrageAlle ersetzen (Menu wird neu erstellt)
WeiterleitungÜberspringen, wenn Quelle existiert
Widget-BereichÜberspringen, wenn Name existiert
WidgetsAlle ersetzen (Bereich wird neu erstellt)
AbschnittÜberspringen, wenn Slug existiert
EinstellungenAktualisieren (Einstellungen sollen sich andern)
InhaltÜberspringen, wenn Slug in der Sammlung existiert

Seed-Dateien werden vor der Anwendung validiert:

import { validateSeed } from "emdash/seed";
const { valid, errors, warnings } = validateSeed(seedData);
if (!valid) {
errors.forEach((e) => console.error(e));
}
warnings.forEach((w) => console.warn(w));

Validierungsprüfungen:

  • Erforderliche Felder sind vorhanden
  • Slugs folgen Namenskonventionen (Kleinbuchstaben, Unterstriche)
  • Feldtypen sind gültig
  • Verweise zeigen auf existierende Inhalte
  • Übergeordnete Elemente hierarchischer Begriffe existieren
  • Weiterleitungs-Pfade sind sichere lokale URLs
  • Weiterleitungs-Quellen sind eindeutig
  • Keine doppelten Slugs innerhalb von Collections
Terminal-Fenster
# Seed-Datei anwenden
npx emdash seed .emdash/seed.json
# Ohne Beispielinhalte anwenden
npx emdash seed .emdash/seed.json --no-content
# Nur validieren
npx emdash seed .emdash/seed.json --validate
# Aktuelles Schema als Seed exportieren
npx emdash export-seed > seed.json
# Mit Inhalten exportieren
npx emdash export-seed --with-content > seed.json