Format de Fichier Seed

Sur cette page

Les fichiers seed sont des documents JSON qui initialisent les sites EmDash. Ils définissent les collections, les champs, les taxonomies, les menus, les redirections, les zones de widgets, les paramètres du site et le contenu d’exemple optionnel.

Structure Racine

Un fichier seed a la structure de niveau supérieur suivante :

{
	"$schema": "https://emdashcms.com/seed.schema.json",
	"version": "1",
	"meta": {},
	"settings": {},
	"collections": [],
	"taxonomies": [],
	"bylines": [],
	"menus": [],
	"redirects": [],
	"widgetAreas": [],
	"sections": [],
	"content": {}
}
ChampTypeRequisDescription
$schemastringNonURL du schéma JSON pour la validation de l’éditeur
version"1"OuiVersion du format seed
metaobjectNonMétadonnées sur le seed
settingsobjectNonParamètres du site
collectionsarrayNonDéfinitions de collections
taxonomiesarrayNonDéfinitions de taxonomies
bylinesarrayNonDéfinitions de profils de byline
menusarrayNonMenus de navigation
redirectsarrayNonRègles de redirection
widgetAreasarrayNonDéfinitions de zones de widgets
sectionsarrayNonBlocs de contenu réutilisables
contentobjectNonEntrées de contenu d’exemple

Meta

L’objet meta contient des métadonnées descriptives optionnelles sur le seed :

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

Settings

L’objet settings contient les valeurs de configuration à l’échelle du site :

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

Les paramètres sont appliqués à la table options avec le préfixe site:. L’Assistant de Configuration pré-remplira title et tagline à partir du fichier seed (s’il est fourni), permettant aux utilisateurs de les remplacer lors de la configuration initiale.

Collections

Chaque définition de collection crée un type de contenu dans la base de données :

{
	"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"
				}
			]
		}
	]
}

Propriétés de Collection

PropriétéTypeRequisDescription
slugstringOuiIdentifiant sécurisé pour URL (minuscules, underscores)
labelstringOuiNom d’affichage au pluriel
labelSingularstringNonNom d’affichage au singulier
descriptionstringNonDescription de l’interface d’administration
iconstringNonNom de l’icône Lucide
supportsarrayNonFonctionnalités : "drafts", "revisions"
fieldsarrayOuiDéfinitions de champs

Propriétés de Field

PropriétéTypeRequisDescription
slugstringOuiNom de colonne (minuscules, underscores)
labelstringOuiNom d’affichage
typestringOuiType de champ
requiredbooleanNonValidation : le champ doit avoir une valeur
uniquebooleanNonValidation : la valeur doit être unique
defaultValueanyNonValeur par défaut pour les nouvelles entrées
validationobjectNonRègles de validation supplémentaires
widgetstringNonRemplacement du widget de l’interface d’administration
optionsobjectNonConfiguration spécifique au widget

Types de Field

TypeDescriptionStocké comme
stringTexte courtTEXT
textTexte long (textarea)TEXT
numberValeur numériqueREAL
integerNombre entierINTEGER
booleanVrai/fauxINTEGER
dateValeur de dateTEXT (ISO 8601)
datetimeDate et heureTEXT (ISO 8601)
emailAdresse e-mailTEXT
urlURLTEXT
slugChaîne sécurisée pour URLTEXT
portableTextContenu de texte enrichiJSON
imageRéférence d’imageJSON
fileRéférence de fichierJSON
jsonJSON arbitraireJSON
referenceRéférence à une autre entréeTEXT

Taxonomies

Les taxonomies sont des systèmes de classification pour le contenu :

{
	"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"]
		}
	]
}

Propriétés de Taxonomy

PropriétéTypeRequisDescription
namestringOuiIdentifiant unique
labelstringOuiNom d’affichage au pluriel
labelSingularstringNonNom d’affichage au singulier
hierarchicalbooleanOuiAutoriser les termes imbriqués (catégories) ou plats (tags)
collectionsarrayOuiCollections auxquelles cette taxonomie s’applique
termsarrayNonTermes prédéfinis

Propriétés de Term

PropriétéTypRequisDescription
slugstringOuiIdentifiant sécurisé pour URL
labelstringOuiNom d’affichage
descriptionstringNonDescription du terme
parentstringNonSlug du terme parent (hiérarchique uniquement)

Le tableau menus définit les menus de navigation modifiables depuis l’administration :

{
	"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"
				}
			]
		}
	]
}

Types de Menu Item

TypeDescriptionChamps requis
customURL personnaliséeurl
pageLien vers une entrée de pageref
postLien vers une entrée d’articleref
taxonomyLien vers une archive de taxonomieref, collection
collectionLien vers une archive de collectioncollection

Propriétés de Menu Item

PropriétéTypeDescription
typestringType d’élément (voir ci-dessus)
labelstringTexte d’affichage (auto-généré pour les refs page/post)
urlstringURL personnalisée (pour le type custom)
refstringID de contenu dans le seed (pour les types page/post)
collectionstringSlug de collection
targetstring"_blank" pour une nouvelle fenêtre
titleAttrstringAttribut title HTML
cssClassesstringClasses CSS personnalisées
childrenarrayÉléments de menu imbriqués

Bylines

Les profils de byline sont séparés de la propriété (author_id). Définissez des identités de byline réutilisables une fois, puis référencez-les à partir d’entrées de contenu.

{
	"bylines": [
		{
			"id": "editorial",
			"slug": "emdash-editorial",
			"displayName": "EmDash Editorial"
		},
		{
			"id": "guest",
			"slug": "guest-contributor",
			"displayName": "Guest Contributor",
			"isGuest": true
		}
	]
}
PropriétéTypeRequisDescription
idstringOuiID local du seed utilisé par content[].bylines
slugstringOuiSlug de byline sécurisé pour URL
displayNamestringOuiNom affiché dans les templates et les API
biostringNonBiographie de profil optionnelle
websiteUrlstringNonURL de site web optionnelle
isGuestbooleanNonMarque la byline comme profil invité

Redirects

Le tableau redirects définit les règles de redirection qui préservent les URL héritées après la migration :

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

Propriétés de Redirect

PropriétéTypeRequisDescription
sourcestringOuiChemin source (doit commencer par /)
destinationstringOuiChemin de destination (doit commencer par /)
typenumberNonStatut HTTP : 301, 302, 307 ou 308
enabledbooleanNonSi la redirection est active (par défaut : true)
groupNamestringNonÉtiquette de regroupement optionnelle pour le filtrage/recherche admin

Widget Areas

Le tableau widgetAreas définit les régions de contenu configurables :

{
	"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!" }]
						}
					]
				}
			]
		}
	]
}

Types de Widget

TypeDescriptionChamps requis
contentContenu de texte enrichicontent (Portable Text)
menuAffiche un menumenuName
componentComposant enregistrécomponentId

Composants Intégrés

ID de composantDescription
core:recent-postsListe des articles récents
core:categoriesListe de catégories
core:tagsNuage de tags
core:searchFormulaire de recherche
core:archivesArchives mensuelles

Sections

Les sections sont des blocs de contenu réutilisables que les éditeurs insèrent dans les champs Portable Text via la commande 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." }
					]
				}
			]
		}
	]
}

Propriétés de Section

PropriétéTypeRequisDescription
slugstringOuiIdentifiant sécurisé pour URL
titlestringOuiNom d’affichage affiché dans le sélecteur de sections
descriptionstringNonExplique quand utiliser cette section
keywordsarrayNonTermes de recherche pour trouver la section
contentarrayOuiBlocs Portable Text
sourcestringNon"theme" (par défaut pour les seeds) ou "import"

Les sections des fichiers seed sont marquées source: "theme" et ne peuvent pas être supprimées de l’interface d’administration. Les éditeurs peuvent créer leurs propres sections (source: "user") et insérer n’importe quel type de section lors de l’édition de contenu.

Content

L’objet content contient des entrées de contenu d’exemple organisées par collection :

{
	"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." }]
						}
					]
				}
			}
		]
	}
}

Propriétés de Content Entry

PropriétéTypeRequisDescription
idstringOuiID local du seed pour les références
slugstringOuiSlug d’URL
statusstringNon"published" ou "draft" (par défaut : "published")
dataobjectOuiValeurs de champs
bylinesarrayNonCrédits de byline ordonnés (byline, roleLabel optionnel)
taxonomiesobjectNonAffectations de termes par nom de taxonomie

Content References

Référencez d’autres entrées de contenu en utilisant le préfixe $ref: :

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

Le préfixe $ref: résout les ID du seed en ID de base de données pendant le seeding.

Media References

Inclure des images depuis des URL :

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

Inclure des images locales depuis .emdash/media/ :

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

Propriétés de Media

PropriétéTypeRequisDescription
urlstringOui*URL distante à télécharger
filestringOui*Nom de fichier local dans .emdash/media/
altstringNonTexte alternatif pour l’accessibilité
filenamestringNonRemplacer le nom de fichier
captionstringNonLégende du média

*Soit url soit file est requis, pas les deux.

Applying Seeds Programmatically

Utilisez l’API seed pour les outils CLI ou les scripts :

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

// Valider d'abord
const validation = validateSeed(seedData);
if (!validation.valid) {
	console.error(validation.errors);
	process.exit(1);
}

// Appliquer le 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 }
// }

Options Apply

OptionTypePar défautDescription
includeContentbooleanfalseCréer des entrées de contenu d’exemple
onConflictstring"skip""skip", "update" ou "error"
mediaBasePathstringChemin de base pour les fichiers multimédias locaux
storageStorageAdaptateur de stockage pour les uploads multimédias
baseUrlstringURL de base pour les URL multimédias

Idempotency

Le seeding peut être exécuté plusieurs fois en toute sécurité. Comportement de conflit par type d’entité :

EntitéComportement
CollectionIgnorer si le slug existe
ChampIgnorer si collection + slug existe
Définition de taxonomieIgnorer si le nom existe
Terme de taxonomieIgnorer si nom + slug existe
Profil de bylineIgnorer si le slug existe
MenuIgnorer si le nom existe
Éléments de menuRemplacer tous (le menu est recréé)
RedirectionIgnorer si la source existe
Zone de widgetIgnorer si le nom existe
WidgetsRemplacer tous (la zone est recréée)
SectionIgnorer si le slug existe
ParamètresMettre à jour (les paramètres sont destinés à changer)
ContenuIgnorer si le slug existe dans la collection

Validation

Les fichiers seed sont validés avant l’application :

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

La validation vérifie que :

  • Les champs requis sont présents
  • Les slugs sont valides pour leur type (les slugs de collection et de champ permettent les lettres minuscules, les chiffres et les underscores ; les autres slugs permettent également les tirets)
  • Les types de champs sont valides
  • Les références pointent vers du contenu existant
  • Les parents de termes hiérarchiques existent
  • Les chemins de redirection sont des URL locales sûres
  • Les sources de redirection sont uniques
  • Pas de slugs en double dans les collections

CLI Commands

Le fichier seed à .emdash/seed.json, package.json#emdash.seed ou seed/seed.json est intégré dans le build et appliqué à la première requête lorsque la base de données est vide. Pour exporter le schéma d’un site existant (et éventuellement son contenu) sous forme de fichier seed :

# `mkdir -p` car .emdash/ peut ne pas encore exister sur un projet neuf
mkdir -p .emdash

# Exporter le schéma actuel sous forme de fichier seed
npx emdash export-seed > .emdash/seed.json

# Exporter avec le contenu
npx emdash export-seed --with-content > .emdash/seed.json

Next Steps