Ir al contenido

Formato del archivo seed

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

{
"$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"SíVersión del formato seed
metaobjectNoMetadatos sobre el seed
settingsobjectNoConfiguraciones del sitio
collectionsarrayNoDefiniciones de colecciones
taxonomiesarrayNoDefiniciones de taxonomías
bylinesarrayNoDefiniciones de perfiles de autores
menusarrayNoMenús de navegación
redirectsarrayNoReglas de redirección
widgetAreasarrayNoDefiniciones de áreas de widgets
sectionsarrayNoBloques de contenido reutilizables
contentobjectNoEntradas de contenido de muestra

Metadatos opcionales sobre el seed:

{
"meta": {
"name": "Base para blog",
"description": "Un blog sencillo con entradas, paginas y categorias",
"author": "EmDash"
}
}

Valores de configuración para todo el sitio:

{
"settings": {
"title": "Mi sitio",
"tagline": "Un CMS moderno",
"postsPerPage": 10,
"dateFormat": "MMMM d, yyyy"
}
}

Las configuraciones se aplican a la tabla options con el prefijo site:. El Asistente de Configuración permite a los usuarios sobrescribir title y tagline.

Las definiciones de colecciones crean tipos de contenido en la base de datos:

{
"collections": [
{
"slug": "posts",
"label": "Entradas",
"labelSingular": "Entrada",
"description": "Entradas del blog",
"icon": "file-text",
"supports": ["drafts", "revisions"],
"fields": [
{
"slug": "title",
"label": "Titulo",
"type": "string",
"required": true
},
{
"slug": "content",
"label": "Contenido",
"type": "portableText"
},
{
"slug": "featured_image",
"label": "Imagen destacada",
"type": "image"
}
]
}
]
}
PropiedadTipoRequeridoDescripción
slugstringSíIdentificador seguro para URL (minúsculas, guiones bajos)
labelstringSíNombre para mostrar en plural
labelSingularstringNoNombre para mostrar en singular
descriptionstringNoDescripción en la interfaz de administración
iconstringNoNombre del icono de Lucide
supportsarrayNoCaracterísticas: "drafts", "revisions"
fieldsarraySíDefiniciones de campos
PropiedadTipoRequeridoDescripción
slugstringSíNombre de la columna (minúsculas, guiones bajos)
labelstringSíNombre para mostrar
typestringSíTipo de campo
requiredbooleanNoValidación: el campo debe tener un valor
uniquebooleanNoValidación: el valor debe ser único
defaultValueanyNoValor por defecto para nuevas entradas
validationobjectNoReglas de validación adicionales
widgetstringNoAnulación del widget en la interfaz de administración
optionsobjectNoConfiguración específica del widget
TipoDescripciónAlmacenado Como
stringTexto cortoTEXT
textTexto largo (área de texto)TEXT
numberValor numéricoREAL
integerNúmero enteroINTEGER
booleanVerdadero/falsoINTEGER
dateValor de fechaTEXT (ISO 8601)
datetimeFecha y horaTEXT (ISO 8601)
emailDirección de correoTEXT
urlURLTEXT
slugCadena segura para URLTEXT
portableTextContenido de texto enriquecidoJSON
imageReferencia de imagenJSON
fileReferencia de archivoJSON
jsonJSON arbitrarioJSON
referenceReferencia a otra entradaTEXT

Sistemas de clasificación para el contenido:

{
"taxonomies": [
{
"name": "category",
"label": "Categorias",
"labelSingular": "Categoria",
"hierarchical": true,
"collections": ["posts"],
"terms": [
{ "slug": "news", "label": "Noticias" },
{ "slug": "tutorials", "label": "Tutoriales" },
{
"slug": "advanced",
"label": "Tutoriales avanzados",
"parent": "tutorials"
}
]
},
{
"name": "tag",
"label": "Etiquetas",
"labelSingular": "Etiqueta",
"hierarchical": false,
"collections": ["posts"]
}
]
}
PropiedadTipoRequeridoDescripción
namestringSíIdentificador único
labelstringSíNombre para mostrar en plural
labelSingularstringNoNombre para mostrar en singular
hierarchicalbooleanSíPermitir términos anidados (categorías) o planos (etiquetas)
collectionsarraySíColecciones a las que aplica esta taxonomía
termsarrayNoTérminos predefinidos
PropiedadTipoRequeridoDescripción
slugstringSíIdentificador seguro para URL
labelstringSíNombre para mostrar
descriptionstringNoDescripción del término
parentstringNoSlug del término padre (solo jerárquico)

Menús de navegación editables desde la administración:

{
"menus": [
{
"name": "primary",
"label": "Navegacion principal",
"items": [
{ "type": "custom", "label": "Inicio", "url": "/" },
{ "type": "page", "ref": "about" },
{ "type": "custom", "label": "Blog", "url": "/posts" },
{
"type": "custom",
"label": "Externo",
"url": "https://example.com",
"target": "_blank"
}
]
}
]
}
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
PropiedadTipoDescripción
typestringTipo de elemento (ver arriba)
labelstringTexto mostrado (generado automáticamente para refs de página/post)
urlstringURL personalizada (para tipo custom)
refstringID de contenido en seed (para tipos page/post)
collectionstringSlug de la colección
targetstring"_blank" para nueva ventana
titleAttrstringAtributo HTML title
cssClassesstringClases CSS personalizadas
childrenarrayElementos de menú anidados

Los perfiles de firma son independientes de la propiedad (author_id). Define identidades de firma reutilizables una vez, luego haz referencia a ellas desde las entradas de contenido.

{
"bylines": [
{
"id": "editorial",
"slug": "emdash-editorial",
"displayName": "EmDash Editorial"
},
{
"id": "guest",
"slug": "guest-contributor",
"displayName": "Colaborador invitado",
"isGuest": true
}
]
}
PropiedadTipoRequeridoDescripción
idstringSíID local en seed usado por content[].bylines
slugstringSíSlug seguro para URL de la firma
displayNamestringSíNombre mostrado en plantillas y APIs
biostringNoBiografía de perfil opcional
websiteUrlstringNoURL de sitio web opcional
isGuestbooleanNoMarca la firma como perfil de invitado

Reglas de redirección para preservar URLs heredados 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"
}
]
}
PropiedadTipoRequeridoDescripción
sourcestringSíRuta de origen (debe comenzar con /)
destinationstringSíRuta de destino (debe comenzar con /)
typenumberNoEstado HTTP: 301, 302, 307, o 308
enabledbooleanNoSi la redirección está activa (por defecto: true)
groupNamestringNoEtiqueta de agrupación opcional para filtros/búsqueda en admin

Regiones de contenido configurables:

{
"widgetAreas": [
{
"name": "sidebar",
"label": "Barra lateral principal",
"description": "Aparece en entradas y paginas del blog",
"widgets": [
{
"type": "component",
"title": "Entradas recientes",
"componentId": "core:recent-posts",
"props": { "count": 5 }
},
{
"type": "menu",
"title": "Enlaces rapidos",
"menuName": "footer"
},
{
"type": "content",
"title": "Sobre nosotros",
"content": [
{
"_type": "block",
"style": "normal",
"children": [{ "_type": "span", "text": "Bienvenido a nuestro sitio." }]
}
]
}
]
}
]
}
TipoDescripciónCampos Requeridos
contentContenido de texto enriquecidocontent (Portable Text)
menuRenderiza un menúmenuName
componentComponente registradocomponentId
ID del 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

Bloques de contenido reutilizables que los editores pueden insertar en campos Portable Text mediante el comando de barra /section:

{
"sections": [
{
"slug": "hero-centered",
"title": "Hero centrado",
"description": "Hero de ancho completo con titulo centrado y boton CTA",
"keywords": ["hero", "banner", "header", "landing"],
"content": [
{
"_type": "block",
"style": "h1",
"children": [{ "_type": "span", "text": "Bienvenido a nuestro sitio" }]
},
{
"_type": "block",
"children": [
{ "_type": "span", "text": "Aqui va tu propuesta de valor principal." }
]
}
]
}
]
}
PropiedadTipoRequeridoDescripción
slugstringSíIdentificador seguro para URL
titlestringSíNombre mostrado en el selector de secciones
descriptionstringNoExplica cuándo usar esta sección
keywordsarrayNoTérminos de búsqueda para encontrar la sección
contentarraySíBloques de Portable Text
sourcestringNo"theme" (por defecto para seeds) o "import"

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

Contenido de ejemplo organizado por colección:

{
"content": {
"posts": [
{
"id": "hello-world",
"slug": "hello-world",
"status": "published",
"bylines": [
{ "byline": "editorial" },
{ "byline": "guest", "roleLabel": "Articulo invitado" }
],
"data": {
"title": "Hola mundo",
"content": [
{
"_type": "block",
"style": "normal",
"children": [{ "_type": "span", "text": "Bienvenido." }]
}
],
"excerpt": "Tu primera entrada."
},
"taxonomies": {
"category": ["news"],
"tag": ["welcome", "first-post"]
}
}
],
"pages": [
{
"id": "about",
"slug": "about",
"status": "published",
"data": {
"title": "Sobre nosotros",
"content": [
{
"_type": "block",
"style": "normal",
"children": [{ "_type": "span", "text": "Contenido de la pagina Sobre nosotros." }]
}
]
}
}
]
}
}
PropiedadTipoRequeridoDescripción
idstringSíID local en seed para referencias
slugstringSíSlug de URL
statusstringNo"published" o "draft" (por defecto: "published")
dataobjectSíValores de los campos
bylinesarrayNoCréditos de firma ordenados (byline, opcional roleLabel)
taxonomiesobjectNoAsignaciones de términos por nombre de taxonomía

Referencia otras entradas de contenido usando el prefijo $ref::

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

El prefijo $ref: resuelve los IDs del seed a IDs de la base de datos durante la siembra.

Incluye imágenes desde URLs:

{
"data": {
"featured_image": {
"$media": {
"url": "https://images.unsplash.com/photo-xxx",
"alt": "Descripcion de la imagen",
"filename": "hero.jpg",
"caption": "Foto de alguien"
}
}
}
}

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

{
"data": {
"featured_image": {
"$media": {
"file": "hero.jpg",
"alt": "Descripcion de la imagen"
}
}
}
}
PropiedadTipoRequeridoDescripción
urlstringSí*URL remota para descargar
filestringSí*Nombre de archivo local en .emdash/media/
altstringNoTexto alternativo para accesibilidad
filenamestringNoSobrescribir el nombre del archivo
captionstringNoPie de foto del medio

*Se requiere url o file, no ambos.

Utilice la API de semillas para herramientas CLI o scripts:

import { applySeed, validateSeed } from "emdash/seed";
import seedData from "../../themes/.emdash/seed.json";
// Validar primero
const validation = validateSeed(seedData);
if (!validation.valid) {
console.error(validation.errors);
process.exit(1);
}
// Aplicar semilla
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 }
// }
OpciónTipoPor defectoDescripción
includeContentbooleanfalseCrear entradas de contenido de ejemplo
onConflictstring"skip""skip", "update", o "error"
mediaBasePathstring—Ruta base para archivos de medios locales
storageStorage—Adaptador de almacenamiento para subidas de medios
baseUrlstring—URL base para las URLs de medios

La siembra es segura para ejecutar múltiples veces. Comportamiento en caso de conflicto por tipo de entidad:

EntidadComportamiento
ColecciónOmitir si el slug existe
CampoOmitir si colección + slug existe
Definición de taxonomíaOmitir si el nombre existe
Término de taxonomíaOmitir si nombre + slug existe
Perfil de autorOmitir si el slug existe
MenúOmitir si el nombre existe
Elementos del menúReemplazar todos (el menú se recrea)
RedirecciónOmitir si la fuente existe
Área de widgetsOmitir si el nombre existe
WidgetsReemplazar todos (el área se recrea)
SecciónOmitir si el slug existe
ConfiguracionesActualizar (las configuraciones están destinadas a cambiar)
ContenidoOmitir si el slug existe en la colección

Los archivos de semilla se validan antes de su 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));

Comprobaciones de validación:

  • Los campos obligatorios están presentes
  • Los slugs siguen las convenciones de nomenclatura (minúsculas, guiones bajos)
  • Los tipos de campo son válidos
  • Las referencias apuntan a contenido existente
  • Los padres de términos jerárquicos existen
  • Las rutas de redirección son URLs locales seguras
  • Las fuentes de redirección son únicas
  • No hay slugs duplicados dentro de las colecciones
Ventana de terminal
# Aplicar archivo seed
npx emdash seed .emdash/seed.json
# Aplicar sin contenido de ejemplo
npx emdash seed .emdash/seed.json --no-content
# Solo validar
npx emdash seed .emdash/seed.json --validate
# Exportar el esquema actual como semilla
npx emdash export-seed > seed.json
# Exportar con contenido
npx emdash export-seed --with-content > seed.json