Seed File Format

In questa pagina

I file seed sono documenti JSON che inizializzano i siti EmDash. Definiscono collezioni, campi, tassonomie, menu, redirect, aree widget, impostazioni del sito e contenuti di esempio opzionali.

Struttura radice

{
	"$schema": "https://emdashcms.com/seed.schema.json",
	"version": "1",
	"meta": {},
	"settings": {},
	"collections": [],
	"taxonomies": [],
	"bylines": [],
	"menus": [],
	"redirects": [],
	"widgetAreas": [],
	"sections": [],
	"content": {}
}
CampoTipoObbligatorioDescrizione
$schemastringNoURL dello schema JSON per la validazione editor
version"1"Versione del formato seed
metaobjectNoMetadati sul seed
settingsobjectNoImpostazioni del sito
collectionsarrayNoDefinizioni delle collezioni
taxonomiesarrayNoDefinizioni delle tassonomie
bylinesarrayNoDefinizioni dei profili byline
menusarrayNoMenu di navigazione
redirectsarrayNoRegole di redirect
widgetAreasarrayNoDefinizioni delle aree widget
sectionsarrayNoBlocchi di contenuto riutilizzabili
contentobjectNoContenuti di esempio

Meta

Metadati opzionali sul seed:

{
	"meta": {
		"name": "Blog Starter",
		"description": "A simple blog with posts, pages, and categories",
		"author": "EmDash"
	}
}

Settings

Valori di configurazione a livello di sito:

{
	"settings": {
		"title": "My Site",
		"tagline": "A modern CMS",
		"postsPerPage": 10,
		"dateFormat": "MMMM d, yyyy"
	}
}

Le impostazioni vengono applicate alla tabella options con il prefisso site:. La procedura guidata di setup consente agli utenti di sovrascrivere title e tagline.

Collections

Le definizioni delle collezioni creano tipi di contenuto nel database:

{
	"collections": [
		{
			"slug": "posts",
			"label": "Posts",
			"labelSingular": "Post",
			"description": "Blog posts",
			"icon": "file-text",
			"supports": ["drafts", "revisions"],
			"fields": [
				{
					"slug": "title",
					"label": "Title",
					"type": "string",
					"required": true
				},
				{
					"slug": "content",
					"label": "Content",
					"type": "portableText"
				},
				{
					"slug": "featured_image",
					"label": "Featured Image",
					"type": "image"
				}
			]
		}
	]
}

Proprietà della collezione

ProprietàTipoObbligatorioDescrizione
slugstringIdentificatore URL-safe (minuscolo, underscore)
labelstringNome visualizzato plurale
labelSingularstringNoNome visualizzato singolare
descriptionstringNoDescrizione nell’interfaccia admin
iconstringNoNome icona Lucide
supportsarrayNoFunzionalità: "drafts", "revisions"
fieldsarrayDefinizioni dei campi

Proprietà del campo

ProprietàTipoObbligatorioDescrizione
slugstringNome colonna (minuscolo, underscore)
labelstringNome visualizzato
typestringTipo di campo
requiredbooleanNoValidazione: il campo deve avere un valore
uniquebooleanNoValidazione: il valore deve essere univoco
defaultValueanyNoValore predefinito per le nuove voci
validationobjectNoRegole di validazione aggiuntive
widgetstringNoOverride del widget nell’interfaccia admin
optionsobjectNoConfigurazione specifica del widget

Tipi di campo

TipoDescrizioneMemorizzato come
stringTesto breveTEXT
textTesto lungo (textarea)TEXT
numberValore numericoREAL
integerNumero interoINTEGER
booleanVero/falsoINTEGER
dateValore dataTEXT (ISO 8601)
datetimeData e oraTEXT (ISO 8601)
emailIndirizzo emailTEXT
urlURLTEXT
slugStringa URL-safeTEXT
portableTextContenuto rich textJSON
imageRiferimento immagineJSON
fileRiferimento fileJSON
jsonJSON arbitrarioJSON
referenceRiferimento a un’altra voceTEXT

Taxonomies

Sistemi di classificazione per i contenuti:

{
	"taxonomies": [
		{
			"name": "category",
			"label": "Categories",
			"labelSingular": "Category",
			"hierarchical": true,
			"collections": ["posts"],
			"terms": [
				{ "slug": "news", "label": "News" },
				{ "slug": "tutorials", "label": "Tutorials" },
				{
					"slug": "advanced",
					"label": "Advanced Tutorials",
					"parent": "tutorials"
				}
			]
		},
		{
			"name": "tag",
			"label": "Tags",
			"labelSingular": "Tag",
			"hierarchical": false,
			"collections": ["posts"]
		}
	]
}

Proprietà della tassonomia

ProprietàTipoObbligatorioDescrizione
namestringIdentificatore univoco
labelstringNome visualizzato plurale
labelSingularstringNoNome visualizzato singolare
hierarchicalbooleanTermini annidati (categorie) o piatti (tag)
collectionsarrayCollezioni a cui si applica questa tassonomia
termsarrayNoTermini predefiniti

Proprietà del termine

ProprietàTipoObbligatorioDescrizione
slugstringIdentificatore URL-safe
labelstringNome visualizzato
descriptionstringNoDescrizione del termine
parentstringNoSlug del termine genitore (solo per gerarchici)

Menu di navigazione modificabili dall’interfaccia admin:

{
	"menus": [
		{
			"name": "primary",
			"label": "Primary Navigation",
			"items": [
				{ "type": "custom", "label": "Home", "url": "/" },
				{ "type": "page", "ref": "about" },
				{ "type": "custom", "label": "Blog", "url": "/posts" },
				{
					"type": "custom",
					"label": "External",
					"url": "https://example.com",
					"target": "_blank"
				}
			]
		}
	]
}

Tipi di voce menu

TipoDescrizioneCampi obbligatori
customURL personalizzatourl
pageLink a una voce paginaref
postLink a una voce articoloref
taxonomyLink a un archivio tassonomiaref, collection
collectionLink a un archivio collezionecollection

Proprietà della voce menu

ProprietàTipoDescrizione
typestringTipo di voce (vedi sopra)
labelstringTesto visualizzato (generato automaticamente per ref page/post)
urlstringURL personalizzato (per il tipo custom)
refstringID contenuto nel seed (per i tipi page/post)
collectionstringSlug della collezione
targetstring"_blank" per nuova finestra
titleAttrstringAttributo title HTML
cssClassesstringClassi CSS personalizzate
childrenarrayVoci menu annidate

Bylines

I profili byline sono separati dalla proprietà (author_id). Definisci identità byline riutilizzabili una volta, poi referenziale dalle voci di contenuto.

{
	"bylines": [
		{
			"id": "editorial",
			"slug": "emdash-editorial",
			"displayName": "EmDash Editorial"
		},
		{
			"id": "guest",
			"slug": "guest-contributor",
			"displayName": "Guest Contributor",
			"isGuest": true
		}
	]
}
ProprietàTipoObbligatorioDescrizione
idstringID locale del seed usato da content[].bylines
slugstringSlug byline URL-safe
displayNamestringNome mostrato nei template e nelle API
biostringNoBio del profilo opzionale
websiteUrlstringNoURL del sito web opzionale
isGuestbooleanNoContrassegna la byline come profilo ospite

Redirects

Regole di redirect per preservare gli URL legacy dopo la migrazione:

{
	"redirects": [
		{ "source": "/old-about", "destination": "/about" },
		{ "source": "/legacy-feed", "destination": "/rss.xml", "type": 308 },
		{
			"source": "/category/news",
			"destination": "/categories/news",
			"groupName": "migration"
		}
	]
}

Proprietà del redirect

ProprietàTipoObbligatorioDescrizione
sourcestringPercorso sorgente (deve iniziare con /)
destinationstringPercorso destinazione (deve iniziare con /)
typenumberNoStato HTTP: 301, 302, 307 o 308
enabledbooleanNoSe il redirect è attivo (predefinito: true)
groupNamestringNoEtichetta di raggruppamento opzionale per filtro/ricerca admin

Widget Areas

Aree di contenuto configurabili:

{
	"widgetAreas": [
		{
			"name": "sidebar",
			"label": "Main Sidebar",
			"description": "Appears on blog posts and pages",
			"widgets": [
				{
					"type": "component",
					"title": "Recent Posts",
					"componentId": "core:recent-posts",
					"props": { "count": 5 }
				},
				{
					"type": "menu",
					"title": "Quick Links",
					"menuName": "footer"
				},
				{
					"type": "content",
					"title": "About",
					"content": [
						{
							"_type": "block",
							"style": "normal",
							"children": [{ "_type": "span", "text": "Welcome to our site!" }]
						}
					]
				}
			]
		}
	]
}

Tipi di widget

TipoDescrizioneCampi obbligatori
contentContenuto rich textcontent (Portable Text)
menuRenderizza un menumenuName
componentComponente registratocomponentId

Componenti integrati

ID ComponenteDescrizione
core:recent-postsLista articoli recenti
core:categoriesLista categorie
core:tagsCloud di tag
core:searchModulo di ricerca
core:archivesArchivi mensili

Sections

Blocchi di contenuto riutilizzabili che gli editor possono inserire nei campi Portable Text tramite il comando slash /section:

{
	"sections": [
		{
			"slug": "hero-centered",
			"title": "Centered Hero",
			"description": "Full-width hero with centered heading and CTA button",
			"keywords": ["hero", "banner", "header", "landing"],
			"content": [
				{
					"_type": "block",
					"style": "h1",
					"children": [{ "_type": "span", "text": "Welcome to Our Site" }]
				},
				{
					"_type": "block",
					"children": [
						{ "_type": "span", "text": "Your compelling tagline goes here." }
					]
				}
			]
		}
	]
}

Proprietà della sezione

ProprietàTipoObbligatorioDescrizione
slugstringIdentificatore URL-safe
titlestringNome visualizzato nel selettore sezioni
descriptionstringNoSpiega quando usare questa sezione
keywordsarrayNoTermini di ricerca per trovare la sezione
contentarrayBlocchi Portable Text
sourcestringNo"theme" (predefinito per i seed) o "import"

Le sezioni dai file seed sono contrassegnate come source: "theme" e non possono essere eliminate dall’interfaccia admin. Gli editor possono creare le proprie sezioni (source: "user") e inserire qualsiasi tipo di sezione durante la modifica dei contenuti.

Content

Contenuti di esempio organizzati per collezione:

{
	"content": {
		"posts": [
			{
				"id": "hello-world",
				"slug": "hello-world",
				"status": "published",
				"bylines": [
					{ "byline": "editorial" },
					{ "byline": "guest", "roleLabel": "Guest essay" }
				],
				"data": {
					"title": "Hello World",
					"content": [
						{
							"_type": "block",
							"style": "normal",
							"children": [{ "_type": "span", "text": "Welcome!" }]
						}
					],
					"excerpt": "Your first post."
				},
				"taxonomies": {
					"category": ["news"],
					"tag": ["welcome", "first-post"]
				}
			}
		],
		"pages": [
			{
				"id": "about",
				"slug": "about",
				"status": "published",
				"data": {
					"title": "About Us",
					"content": [
						{
							"_type": "block",
							"style": "normal",
							"children": [{ "_type": "span", "text": "About page content." }]
						}
					]
				}
			}
		]
	}
}

Proprietà della voce di contenuto

ProprietàTipoObbligatorioDescrizione
idstringID locale del seed per i riferimenti
slugstringSlug URL
statusstringNo"published" o "draft" (predefinito: "published")
dataobjectValori dei campi
bylinesarrayNoCrediti byline ordinati (byline, roleLabel opzionale)
taxonomiesobjectNoAssegnazioni di termini per nome della tassonomia

Riferimenti ai contenuti

Referenzia altre voci di contenuto usando il prefisso $ref::

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

Il prefisso $ref: risolve gli ID del seed in ID del database durante il seeding.

Riferimenti ai media

Includi immagini da URL:

{
	"data": {
		"featured_image": {
			"$media": {
				"url": "https://images.unsplash.com/photo-xxx",
				"alt": "Description of the image",
				"filename": "hero.jpg",
				"caption": "Photo by Someone"
			}
		}
	}
}

Includi immagini locali da .emdash/media/:

{
	"data": {
		"featured_image": {
			"$media": {
				"file": "hero.jpg",
				"alt": "Description of the image"
			}
		}
	}
}

Proprietà dei media

ProprietàTipoObbligatorioDescrizione
urlstringSì*URL remoto da scaricare
filestringSì*Nome file locale in .emdash/media/
altstringNoTesto alternativo per accessibilità
filenamestringNoOverride del nome file
captionstringNoDidascalia del media

*È richiesto url o file, non entrambi.

Applicare i seed in modo programmatico

Usa l’API seed per strumenti CLI o script:

import { applySeed, validateSeed } from "emdash/seed";
import seedData from "./.emdash/seed.json";

// Validate first
const validation = validateSeed(seedData);
if (!validation.valid) {
	console.error(validation.errors);
	process.exit(1);
}

// Apply seed
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 }
// }

Opzioni di applicazione

OpzioneTipoPredefinitoDescrizione
includeContentbooleanfalseCrea voci di contenuto di esempio
onConflictstring"skip""skip", "update" o "error"
mediaBasePathstringPercorso base per i file media locali
storageStorageAdattatore di storage per l’upload dei media
baseUrlstringURL base per gli URL dei media

Idempotenza

Il seeding è sicuro da eseguire più volte. Comportamento in caso di conflitto per tipo di entità:

EntitàComportamento
CollezioneSalta se lo slug esiste
CampoSalta se collezione + slug esiste
Definizione tassonomiaSalta se il nome esiste
Termine tassonomiaSalta se nome + slug esiste
Profilo bylineSalta se lo slug esiste
MenuSalta se il nome esiste
Voci menuSostituisce tutte (il menu viene ricreato)
RedirectSalta se la sorgente esiste
Area widgetSalta se il nome esiste
WidgetSostituisce tutti (l’area viene ricreata)
SezioneSalta se lo slug esiste
ImpostazioniAggiorna (le impostazioni sono destinate a cambiare)
ContenutoSalta se lo slug esiste nella collezione

Validazione

I file seed vengono validati prima dell’applicazione:

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

Controlli di validazione:

  • I campi obbligatori sono presenti
  • Gli slug seguono le convenzioni di denominazione (minuscolo, underscore)
  • I tipi di campo sono validi
  • I riferimenti puntano a contenuti esistenti
  • I termini genitori gerarchici esistono
  • I percorsi di redirect sono URL locali sicuri
  • Le sorgenti di redirect sono univoche
  • Nessun slug duplicato all’interno delle collezioni

Comandi CLI

# Apply seed file
npx emdash seed .emdash/seed.json

# Apply without sample content
npx emdash seed .emdash/seed.json --no-content

# Validate only
npx emdash seed .emdash/seed.json --validate

# Export current schema as seed
npx emdash export-seed > seed.json

# Export with content
npx emdash export-seed --with-content > seed.json

Prossimi passi