Aller au contenu

Format du fichier seed

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 un contenu d’exemple optionnel.

{
"$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 validation de l’éditeur
version"1"OuiVersion du format seed
metaobjectNonMétadonnées sur le seed
settingsobjectNonParamètres du site
collectionsarrayNonDéfinitions des collections
taxonomiesarrayNonDéfinitions des taxonomies
bylinesarrayNonDéfinitions des profils de rédacteurs
menusarrayNonMenus de navigation
redirectsarrayNonRègles de redirection
widgetAreasarrayNonDéfinitions des zones de widgets
sectionsarrayNonBlocs de contenu réutilisables
contentobjectNonEntrées de contenu d’exemple

Métadonnées optionnelles sur le seed :

{
"meta": {
"name": "Base de blog",
"description": "Un blog simple avec des articles, des pages et des catégories",
"author": "EmDash"
}
}

Valeurs de configuration à l’échelle du site :

{
"settings": {
"title": "Mon site",
"tagline": "Un CMS moderne",
"postsPerPage": 10,
"dateFormat": "MMMM d, yyyy"
}
}

Les paramètres sont appliqués à la table options avec le préfixe site:. L’Assistant de Configuration permet aux utilisateurs de remplacer title et tagline.

Les définitions de collections créent des types de contenu dans la base de données :

{
"collections": [
{
"slug": "posts",
"label": "Articles",
"labelSingular": "Article",
"description": "Articles du blog",
"icon": "file-text",
"supports": ["drafts", "revisions"],
"fields": [
{
"slug": "title",
"label": "Titre",
"type": "string",
"required": true
},
{
"slug": "content",
"label": "Contenu",
"type": "portableText"
},
{
"slug": "featured_image",
"label": "Image mise en avant",
"type": "image"
}
]
}
]
}
PropriétéTypeRequisDescription
slugstringOuiIdentifiant sûr pour les URL (minuscules, tirets bas)
labelstringOuiNom d’affichage au pluriel
labelSingularstringNonNom d’affichage au singulier
descriptionstringNonDescription dans l’interface d’administration
iconstringNonNom de l’icône Lucide
supportsarrayNonFonctionnalités : "drafts", "revisions"
fieldsarrayOuiDéfinitions des champs
PropriétéTypeRequisDescription
slugstringOuiNom de la colonne (minuscules, tirets bas)
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
widgetstringNonSurcharge du widget de l’interface d’administration
optionsobjectNonConfiguration spécifique au widget
TypeDescriptionStocké sous
stringTexte courtTEXT
textTexte long (zone de texte)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ûre pour les URLTEXT
portableTextContenu de texte enrichiJSON
imageRéférence d’imageJSON
fileRéférence de fichierJSON
jsonJSON arbitraireJSON
referenceRéférence à une autre entréeTEXT

Systèmes de classification pour le contenu :

{
"taxonomies": [
{
"name": "category",
"label": "Catégories",
"labelSingular": "Catégorie",
"hierarchical": true,
"collections": ["posts"],
"terms": [
{ "slug": "news", "label": "Actualités" },
{ "slug": "tutorials", "label": "Tutoriels" },
{
"slug": "advanced",
"label": "Tutoriels avancés",
"parent": "tutorials"
}
]
},
{
"name": "tag",
"label": "Étiquettes",
"labelSingular": "Étiquette",
"hierarchical": false,
"collections": ["posts"]
}
]
}
PropriétéTypeRequisDescription
namestringOuiIdentifiant unique
labelstringOuiNom d’affichage au pluriel
labelSingularstringNonNom d’affichage au singulier
hierarchicalbooleanOuiAutoriser les termes imbriqués (catégories) ou plats (étiquettes)
collectionsarrayOuiCollections auxquelles cette taxonomie s’applique
termsarrayNonTermes prédéfinis
PropriétéTypeRequisDescription
slugstringOuiIdentifiant sûr pour les URL
labelstringOuiNom d’affichage
descriptionstringNonDescription du terme
parentstringNonSlug du terme parent (hiérarchique uniquement)

Menus de navigation modifiables depuis l’administration :

{
"menus": [
{
"name": "primary",
"label": "Navigation principale",
"items": [
{ "type": "custom", "label": "Accueil", "url": "/" },
{ "type": "page", "ref": "about" },
{ "type": "custom", "label": "Blog", "url": "/posts" },
{
"type": "custom",
"label": "Externe",
"url": "https://example.com",
"target": "_blank"
}
]
}
]
}
TypeDescriptionChamps requis
customURL personnaliséeurl
pageLien vers une pageref
postLien vers un articleref
taxonomyLien vers une archive de taxonomieref, collection
collectionLien vers une archive de collectioncollection
PropriétéTypeDescription
typestringType d’élément (voir ci-dessus)
labelstringTexte affiché (généré automatiquement pour les références page/article)
urlstringURL personnalisée (pour le type custom)
refstringID de contenu dans le seed (pour les types page/post)
collectionstringSlug de la collection
targetstring"_blank" pour une nouvelle fenêtre
titleAttrstringAttribut HTML title
cssClassesstringClasses CSS personnalisées
childrenarrayÉléments de menu imbriqués

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

{
"bylines": [
{
"id": "editorial",
"slug": "emdash-editorial",
"displayName": "EmDash Editorial"
},
{
"id": "guest",
"slug": "guest-contributor",
"displayName": "Contributeur invité",
"isGuest": true
}
]
}
PropriétéTypeRequisDescription
idstringOuiID local au seed utilisé par content[].bylines
slugstringOuiSlug de byline adapté aux URL
displayNamestringOuiNom affiché dans les modèles et les API
biostringNonBio de profil optionnelle
websiteUrlstringNonURL de site web optionnelle
isGuestbooleanNonMarque le byline comme profil invité

Règles de redirection pour préserver les URL héritées après 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é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)
groupNamestringNonLibellé de regroupement optionnel pour le filtrage/recherche admin

Régions de contenu configurables :

{
"widgetAreas": [
{
"name": "sidebar",
"label": "Barre latérale principale",
"description": "Apparaît sur les articles et les pages du blog",
"widgets": [
{
"type": "component",
"title": "Articles récents",
"componentId": "core:recent-posts",
"props": { "count": 5 }
},
{
"type": "menu",
"title": "Liens rapides",
"menuName": "footer"
},
{
"type": "content",
"title": "À propos",
"content": [
{
"_type": "block",
"style": "normal",
"children": [{ "_type": "span", "text": "Bienvenue sur notre site." }]
}
]
}
]
}
]
}
TypeDescriptionChamps requis
contentContenu texte enrichicontent (Portable Text)
menuAffiche un menumenuName
componentComposant enregistrécomponentId
ID du composantDescription
core:recent-postsListe des articles récents
core:categoriesListe des catégories
core:tagsNuage de tags
core:searchFormulaire de recherche
core:archivesArchives mensuelles

Blocs de contenu réutilisables que les éditeurs peuvent insérer dans les champs Portable Text via la commande slash /section :

{
"sections": [
{
"slug": "hero-centered",
"title": "Hero centré",
"description": "Hero pleine largeur avec titre centré et bouton d'appel à l'action",
"keywords": ["hero", "banner", "header", "landing"],
"content": [
{
"_type": "block",
"style": "h1",
"children": [{ "_type": "span", "text": "Bienvenue sur notre site" }]
},
{
"_type": "block",
"children": [
{ "_type": "span", "text": "Ajoutez ici votre promesse de marque principale." }
]
}
]
}
]
}
PropriétéTypeRequisDescription
slugstringOuiIdentifiant adapté aux URL
titlestringOuiNom 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 depuis 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 du contenu.

Contenu exemple organisé par collection :

{
"content": {
"posts": [
{
"id": "hello-world",
"slug": "hello-world",
"status": "published",
"bylines": [
{ "byline": "editorial" },
{ "byline": "guest", "roleLabel": "Article invité" }
],
"data": {
"title": "Bonjour tout le monde",
"content": [
{
"_type": "block",
"style": "normal",
"children": [{ "_type": "span", "text": "Bienvenue !" }]
}
],
"excerpt": "Votre premier article."
},
"taxonomies": {
"category": ["news"],
"tag": ["welcome", "first-post"]
}
}
],
"pages": [
{
"id": "about",
"slug": "about",
"status": "published",
"data": {
"title": "À propos de nous",
"content": [
{
"_type": "block",
"style": "normal",
"children": [{ "_type": "span", "text": "Contenu de la page À propos." }]
}
]
}
}
]
}
}
PropriétéTypeRequisDescription
idstringOuiID local au seed pour les références
slugstringOuiSlug d’URL
statusstringNon"published" ou "draft" (par défaut : "published")
dataobjectOuiValeurs des champs
bylinesarrayNonCrédits de byline ordonnés (byline, roleLabel optionnel)
taxonomiesobjectNonAssignations de termes par nom de taxonomie

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 IDs de seed en IDs de base de données lors du seeding.

Incluez des images depuis des URL :

{
"data": {
"featured_image": {
"$media": {
"url": "https://images.unsplash.com/photo-xxx",
"alt": "Description de l'image",
"filename": "hero.jpg",
"caption": "Photo par quelqu'un"
}
}
}
}

Inclure des images locales depuis .emdash/media/ :

{
"data": {
"featured_image": {
"$media": {
"file": "hero.jpg",
"alt": "Description de l'image"
}
}
}
}
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.

Utilisez l’API de semence pour les outils CLI ou les scripts :

import { applySeed, validateSeed } from "emdash/seed";
import seedData from "../../themes/.emdash/seed.json";
// Valider d'abord
const validation = validateSeed(seedData);
if (!validation.valid) {
console.error(validation.errors);
process.exit(1);
}
// Appliquer la semence
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 }
// }
OptionTypePar défautDescription
includeContentbooleanfalseCréer des entrées de contenu d’exemple
onConflictstring"skip""skip", "update", ou "error"
mediaBasePathstring—Chemin de base pour les fichiers multimédias locaux
storageStorage—Adaptateur de stockage pour les téléversements multimédias
baseUrlstring—URL de base pour les URL multimédias

L’ensemencement peut être exécuté plusieurs fois en toute sécurité. Comportement en cas 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 signatureIgnorer 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

Les fichiers de semence sont validés avant 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));

Vérifications de validation :

  • Les champs obligatoires sont présents
  • Les slugs suivent les conventions de nommage (minuscules, tirets bas)
  • Les types de champs sont valides
  • Les références pointent vers du contenu existant
  • Les parents des 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 au sein des collections
Fenêtre de terminal
# Apply seed file
npx emdash seed .emdash/seed.json
# Appliquer sans contenu d'exemple
npx emdash seed .emdash/seed.json --no-content
# Valider uniquement
npx emdash seed .emdash/seed.json --validate
# Exporter le schéma actuel en tant que semence
npx emdash export-seed > seed.json
# Exporter avec contenu
npx emdash export-seed --with-content > seed.json