Formato de Archivo Seed

En esta página

Los archivos seed son documentos JSON que inicializan sitios EmDash. Definen colecciones, campos, taxonomías, menús, redirecciones, áreas de widgets, configuraciones del sitio y contenido de muestra opcional.

Estructura Raíz

Un archivo seed tiene la siguiente estructura de nivel superior:

{
	"$schema": "https://emdashcms.com/seed.schema.json",
	"version": "1",
	"meta": {},
	"settings": {},
	"collections": [],
	"taxonomies": [],
	"bylines": [],
	"menus": [],
	"redirects": [],
	"widgetAreas": [],
	"sections": [],
	"content": {}
}
CampoTipoRequeridoDescripción
$schemastringNoURL del esquema JSON para validación del editor
version"1"Versión del formato seed
metaobjectNoMetadatos sobre el seed
settingsobjectNoConfiguraciones del sitio
collectionsarrayNoDefiniciones de colecciones
taxonomiesarrayNoDefiniciones de taxonomías
bylinesarrayNoDefiniciones de perfiles de byline
menusarrayNoMenús de navegación
redirectsarrayNoReglas de redirección
widgetAreasarrayNoDefiniciones de áreas de widgets
sectionsarrayNoBloques de contenido reutilizables
contentobjectNoEntradas de contenido de muestra

Meta

El objeto meta contiene metadatos descriptivos opcionales sobre el seed:

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

Settings

El objeto settings contiene valores de configuración a nivel del sitio:

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

Las configuraciones se aplican a la tabla options con el prefijo site:. El Asistente de Configuración prellenará title y tagline desde el archivo seed (si se proporciona), permitiendo a los usuarios sobrescribirlos durante la configuración inicial.

Collections

Cada definición de colección crea un tipo de contenido en la base de datos:

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

Propiedades de Collection

PropiedadTipoRequeridoDescripción
slugstringIdentificador seguro para URL (minúsculas, guiones bajos)
labelstringNombre de visualización plural
labelSingularstringNoNombre de visualización singular
descriptionstringNoDescripción de la UI de administración
iconstringNoNombre del icono Lucide
supportsarrayNoFunciones: "drafts", "revisions"
fieldsarrayDefiniciones de campos

Propiedades de Field

PropiedadTipoRequeridoDescripción
slugstringNombre de columna (minúsculas, guiones bajos)
labelstringNombre de visualización
typestringTipo de campo
requiredbooleanNoValidación: el campo debe tener un valor
uniquebooleanNoValidación: el valor debe ser único
defaultValueanyNoValor predeterminado para nuevas entradas
validationobjectNoReglas de validación adicionales
widgetstringNoAnulación de widget de la UI de administración
optionsobjectNoConfiguración específica del widget

Tipos de Field

TipoDescripciónAlmacenado como
stringTexto cortoTEXT
textTexto largo (textarea)TEXT
numberValor numéricoREAL
integerNúmero enteroINTEGER
booleanVerdadero/falsoINTEGER
dateValor de fechaTEXT (ISO 8601)
datetimeFecha y horaTEXT (ISO 8601)
emailDirección de correo electrónicoTEXT
urlURLTEXT
slugCadena segura para URLTEXT
portableTextContenido de texto enriquecidoJSON
imageReferencia de imagenJSON
fileReferencia de archivoJSON
jsonJSON arbitrarioJSON
referenceReferencia a otra entradaTEXT

Taxonomies

Las taxonomías son sistemas de clasificación para el contenido:

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

Propiedades de Taxonomy

PropiedadTipoRequeridoDescripción
namestringIdentificador único
labelstringNombre de visualización plural
labelSingularstringNoNombre de visualización singular
hierarchicalbooleanPermitir términos anidados (categorías) o planos (etiquetas)
collectionsarrayColecciones a las que se aplica esta taxonomía
termsarrayNoTérminos predefinidos

Propiedades de Term

PropiedadTipoRequeridoDescripción
slugstringIdentificador seguro para URL
labelstringNombre de visualización
descriptionstringNoDescripción del término
parentstringNoSlug del término padre (solo jerárquico)

El array menus define menús de navegación editables desde el administrador:

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

Tipos de Menu Item

TipoDescripciónCampos requeridos
customURL personalizadaurl
pageEnlace a una entrada de páginaref
postEnlace a una entrada de postref
taxonomyEnlace a un archivo de taxonomíaref, collection
collectionEnlace a un archivo de coleccióncollection

Propiedades de Menu Item

PropiedadTipoDescripción
typestringTipo de elemento (ver arriba)
labelstringTexto de visualización (autogenerado para refs de página/post)
urlstringURL personalizada (para tipo custom)
refstringID de contenido en seed (para tipos page/post)
collectionstringSlug de colección
targetstring"_blank" para nueva ventana
titleAttrstringAtributo title HTML
cssClassesstringClases CSS personalizadas
childrenarrayElementos de menú anidados

Bylines

Los perfiles de byline están separados de la propiedad (author_id). Defina identidades de byline reutilizables una vez, luego haga referencia a ellas desde entradas de contenido.

{
	"bylines": [
		{
			"id": "editorial",
			"slug": "emdash-editorial",
			"displayName": "EmDash Editorial"
		},
		{
			"id": "guest",
			"slug": "guest-contributor",
			"displayName": "Guest Contributor",
			"isGuest": true
		}
	]
}
PropiedadTipoRequeridoDescripción
idstringID local del seed usado por content[].bylines
slugstringSlug de byline seguro para URL
displayNamestringNombre mostrado en plantillas y APIs
biostringNoBiografía de perfil opcional
websiteUrlstringNoURL de sitio web opcional
isGuestbooleanNoMarca byline como perfil de invitado

Redirects

El array redirects define reglas de redirección que preservan URLs heredadas después de la migración:

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

Propiedades de Redirect

PropiedadTipoRequeridoDescripción
sourcestringRuta de origen (debe comenzar con /)
destinationstringRuta de destino (debe comenzar con /)
typenumberNoEstado HTTP: 301, 302, 307 o 308
enabledbooleanNoSi la redirección está activa (predeterminado: true)
groupNamestringNoEtiqueta de agrupación opcional para filtrado/búsqueda del admin

Widget Areas

El array widgetAreas define regiones de contenido 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!" }]
						}
					]
				}
			]
		}
	]
}

Tipos de Widget

TipoDescripciónCampos requeridos
contentContenido de texto enriquecidocontent (Portable Text)
menuRenderiza un menúmenuName
componentComponente registradocomponentId

Componentes Integrados

ID de componenteDescripción
core:recent-postsLista de posts recientes
core:categoriesLista de categorías
core:tagsNube de etiquetas
core:searchFormulario de búsqueda
core:archivesArchivos mensuales

Sections

Las sections son bloques de contenido reutilizables que los editores insertan en campos Portable Text mediante el comando de barra /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." }
					]
				}
			]
		}
	]
}

Propiedades de Section

PropiedadTipoRequeridoDescripción
slugstringIdentificador seguro para URL
titlestringNombre de visualización mostrado en el selector de sections
descriptionstringNoExplica cuándo usar esta section
keywordsarrayNoTérminos de búsqueda para encontrar la section
contentarrayBloques Portable Text
sourcestringNo"theme" (predeterminado para seeds) o "import"

Las sections de archivos seed están marcadas con source: "theme" y no se pueden eliminar desde la UI de administración. Los editores pueden crear sus propias sections (source: "user") e insertar cualquier tipo de section al editar contenido.

Content

El objeto content contiene entradas de contenido de muestra organizadas por colección:

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

Propiedades de Content Entry

PropiedadTipoRequeridoDescripción
idstringID local del seed para referencias
slugstringSlug de URL
statusstringNo"published" o "draft" (predeterminado: "published")
dataobjectValores de campos
bylinesarrayNoCréditos de byline ordenados (byline, opcional roleLabel)
taxonomiesobjectNoAsignaciones de términos por nombre de taxonomía

Content References

Haga referencia a otras entradas de contenido usando el prefijo $ref::

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

El prefijo $ref: resuelve IDs del seed a IDs de base de datos durante el seeding.

Media References

Incluir imágenes desde URLs:

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

Incluir imágenes locales desde .emdash/media/:

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

Propiedades de Media

PropiedadTipoRequeridoDescripción
urlstringSí*URL remota para descargar
filestringSí*Nombre de archivo local en .emdash/media/
altstringNoTexto alternativo para accesibilidad
filenamestringNoSobrescribir nombre de archivo
captionstringNoPie de foto del medio

*Se requiere url o file, no ambos.

Applying Seeds Programmatically

Use la API de seed para herramientas CLI o scripts:

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

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

// Aplicar 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 }
// }

Opciones de Apply

OpciónTipoPredeterminadoDescripción
includeContentbooleanfalseCrear entradas de contenido de muestra
onConflictstring"skip""skip", "update" o "error"
mediaBasePathstringRuta base para archivos de medios locales
storageStorageAdaptador de almacenamiento para cargas de medios
baseUrlstringURL base para URLs de medios

Idempotency

El seeding es seguro para ejecutar múltiples veces. Comportamiento de conflicto por tipo de entidad:

EntidadComportamiento
CollectionOmitir si existe el slug
FieldOmitir si existe collection + slug
Definición de taxonomíaOmitir si existe el nombre
Término de taxonomíaOmitir si existe nombre + slug
Perfil de bylineOmitir si existe el slug
MenúOmitir si existe el nombre
Elementos de menúReemplazar todos (el menú se recrea)
RedirecciónOmitir si existe el origen
Área de widgetOmitir si existe el nombre
WidgetsReemplazar todos (el área se recrea)
SectionOmitir si existe el slug
ConfiguracionesActualizar (las configuraciones están destinadas a cambiar)
ContenidoOmitir si existe el slug en la colección

Validation

Los archivos seed se validan antes de la aplicación:

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 validación verifica que:

  • Los campos requeridos estén presentes
  • Los slugs sean válidos para su tipo (los slugs de collection y field permiten letras minúsculas, dígitos y guiones bajos; otros slugs también permiten guiones)
  • Los tipos de campo sean válidos
  • Las referencias apunten a contenido existente
  • Existan padres de términos jerárquicos
  • Las rutas de redirección sean URLs locales seguras
  • Los orígenes de redirección sean únicos
  • No haya slugs duplicados dentro de las colecciones

CLI Commands

El archivo seed en .emdash/seed.json, package.json#emdash.seed o seed/seed.json se incluye en la compilación y se aplica en la primera solicitud cuando la base de datos está vacía. Para exportar el esquema de un sitio existente (y opcionalmente su contenido) como un archivo seed:

# `mkdir -p` porque .emdash/ puede no existir aún en un proyecto nuevo
mkdir -p .emdash

# Exportar el esquema actual como un archivo seed
npx emdash export-seed > .emdash/seed.json

# Exportar con contenido
npx emdash export-seed --with-content > .emdash/seed.json

Next Steps