Formato do Ficheiro Seed

Nesta página

Os ficheiros seed são documentos JSON que inicializam sites EmDash. Definem coleções, campos, taxonomias, menus, redirecionamentos, áreas de widgets, configurações do site e conteúdo de exemplo opcional.

Estrutura Raiz

{
	"$schema": "https://emdashcms.com/seed.schema.json",
	"version": "1",
	"meta": {},
	"settings": {},
	"collections": [],
	"taxonomies": [],
	"bylines": [],
	"menus": [],
	"redirects": [],
	"widgetAreas": [],
	"sections": [],
	"content": {}
}
CampoTipoObrigatórioDescrição
$schemastringNãoURL do esquema JSON para validação no editor
version"1"SimVersão do formato seed
metaobjectNãoMetadados sobre o seed
settingsobjectNãoConfigurações do site
collectionsarrayNãoDefinições de coleções
taxonomiesarrayNãoDefinições de taxonomias
bylinesarrayNãoDefinições de perfis de autoria
menusarrayNãoMenus de navegação
redirectsarrayNãoRegras de redirecionamento
widgetAreasarrayNãoDefinições de áreas de widgets
sectionsarrayNãoBlocos de conteúdo reutilizáveis
contentobjectNãoEntradas de conteúdo de exemplo

Meta

Metadados opcionais sobre o seed:

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

Configurações

Valores de configuração de todo o site:

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

As configurações são aplicadas à tabela options com o prefixo site:. O Assistente de Configuração permite que os utilizadores alterem title e tagline.

Coleções

As definições de coleção criam tipos de conteúdo na base de dados:

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

Propriedades da Coleção

PropriedadeTipoObrigatórioDescrição
slugstringSimIdentificador seguro para URL (minúsculas, underscores)
labelstringSimNome de exibição plural
labelSingularstringNãoNome de exibição singular
descriptionstringNãoDescrição no painel de administração
iconstringNãoNome do ícone Lucide
supportsarrayNãoFuncionalidades: "drafts", "revisions"
fieldsarraySimDefinições de campos

Propriedades do Campo

PropriedadeTipoObrigatórioDescrição
slugstringSimNome da coluna (minúsculas, underscores)
labelstringSimNome de exibição
typestringSimTipo de campo
requiredbooleanNãoValidação: o campo deve ter um valor
uniquebooleanNãoValidação: o valor deve ser único
defaultValueanyNãoValor padrão para novas entradas
validationobjectNãoRegras de validação adicionais
widgetstringNãoSubstituição de widget no admin
optionsobjectNãoConfiguração específica do widget

Tipos de Campo

TipoDescriçãoArmazenado Como
stringTexto curtoTEXT
textTexto longo (textarea)TEXT
numberValor numéricoREAL
integerNúmero inteiroINTEGER
booleanVerdadeiro/falsoINTEGER
dateValor de dataTEXT (ISO 8601)
datetimeData e horaTEXT (ISO 8601)
emailEndereço de e-mailTEXT
urlURLTEXT
slugString segura para URLTEXT
portableTextConteúdo rich textJSON
imageReferência a imagemJSON
fileReferência a ficheiroJSON
jsonJSON arbitrárioJSON
referenceReferência a outra entradaTEXT

Taxonomias

Sistemas de classificação para conteúdo:

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

Propriedades da Taxonomia

PropriedadeTipoObrigatórioDescrição
namestringSimIdentificador único
labelstringSimNome de exibição plural
labelSingularstringNãoNome de exibição singular
hierarchicalbooleanSimPermitir termos aninhados (categorias) ou planos (tags)
collectionsarraySimColeções às quais esta taxonomia se aplica
termsarrayNãoTermos pré-definidos

Propriedades do Termo

PropriedadeTipoObrigatórioDescrição
slugstringSimIdentificador seguro para URL
labelstringSimNome de exibição
descriptionstringNãoDescrição do termo
parentstringNãoSlug do termo pai (apenas hierárquico)

Menus de navegação editáveis a partir do 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"
				}
			]
		}
	]
}

Tipos de Item de Menu

TipoDescriçãoCampos Obrigatórios
customURL personalizadourl
pageLink para uma entrada de páginaref
postLink para uma entrada de postref
taxonomyLink para um arquivo de taxonomiaref, collection
collectionLink para um arquivo de coleçãocollection

Propriedades do Item de Menu

PropriedadeTipoDescrição
typestringTipo de item (ver acima)
labelstringTexto de exibição (gerado auto. para refs de página/post)
urlstringURL personalizado (para tipo custom)
refstringID do conteúdo no seed (para tipos page/post)
collectionstringSlug da coleção
targetstring"_blank" para nova janela
titleAttrstringAtributo HTML title
cssClassesstringClasses CSS personalizadas
childrenarrayItens de menu aninhados

Bylines

Os perfis de autoria são separados da propriedade (author_id). Defina identidades de autoria reutilizáveis uma vez e depois referencie-as a partir de entradas de conteúdo.

{
	"bylines": [
		{
			"id": "editorial",
			"slug": "emdash-editorial",
			"displayName": "EmDash Editorial"
		},
		{
			"id": "guest",
			"slug": "guest-contributor",
			"displayName": "Guest Contributor",
			"isGuest": true
		}
	]
}
PropriedadeTipoObrigatórioDescrição
idstringSimID local do seed usado por content[].bylines
slugstringSimSlug de autoria seguro para URL
displayNamestringSimNome mostrado em templates e APIs
biostringNãoBio de perfil opcional
websiteUrlstringNãoURL de website opcional
isGuestbooleanNãoMarca o perfil de autoria como convidado

Redirecionamentos

Regras de redirecionamento para preservar URLs legados após migração:

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

Propriedades do Redirecionamento

PropriedadeTipoObrigatórioDescrição
sourcestringSimCaminho de origem (deve começar com /)
destinationstringSimCaminho de destino (deve começar com /)
typenumberNãoStatus HTTP: 301, 302, 307 ou 308
enabledbooleanNãoSe o redirecionamento está ativo (padrão: true)
groupNamestringNãoRótulo de agrupamento opcional para filtragem/pesquisa no admin

Áreas de Widgets

Regiões de conteúdo configuráveis:

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

TipoDescriçãoCampos Obrigatórios
contentConteúdo rich textcontent (Portable Text)
menuRenderiza um menumenuName
componentComponente registadocomponentId

Componentes Integrados

ID do ComponenteDescrição
core:recent-postsLista de posts recentes
core:categoriesLista de categorias
core:tagsNuvem de tags
core:searchFormulário de pesquisa
core:archivesArquivos mensais

Secções

Blocos de conteúdo reutilizáveis que os editores podem inserir em campos Portable Text através do 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." }
					]
				}
			]
		}
	]
}

Propriedades da Secção

PropriedadeTipoObrigatórioDescrição
slugstringSimIdentificador seguro para URL
titlestringSimNome de exibição mostrado no seletor de secções
descriptionstringNãoExplica quando usar esta secção
keywordsarrayNãoTermos de pesquisa para encontrar a secção
contentarraySimBlocos Portable Text
sourcestringNão"theme" (padrão para seeds) ou "import"

As secções dos ficheiros seed são marcadas como source: "theme" e não podem ser eliminadas do painel de administração. Os editores podem criar as suas próprias secções (source: "user") e inserir qualquer tipo de secção ao editar conteúdo.

Conteúdo

Conteúdo de exemplo organizado por coleção:

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

Propriedades da Entrada de Conteúdo

PropriedadeTipoObrigatórioDescrição
idstringSimID local do seed para referências
slugstringSimSlug de URL
statusstringNão"published" ou "draft" (padrão: "published")
dataobjectSimValores dos campos
bylinesarrayNãoCréditos de autoria ordenados (byline, roleLabel opcional)
taxonomiesobjectNãoAtribuições de termos por nome de taxonomia

Referências de Conteúdo

Referencie outras entradas de conteúdo usando o prefixo $ref::

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

O prefixo $ref: resolve IDs do seed para IDs da base de dados durante o seeding.

Referências de Media

Incluir imagens a partir de 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 imagens locais de .emdash/media/:

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

Propriedades de Media

PropriedadeTipoObrigatórioDescrição
urlstringSim*URL remoto para descarregar
filestringSim*Nome do ficheiro local em .emdash/media/
altstringNãoTexto alternativo para acessibilidade
filenamestringNãoSubstituir nome do ficheiro
captionstringNãoLegenda da media

*É necessário url ou file, não ambos.

Aplicar Seeds Programaticamente

Use a API de seed para ferramentas CLI ou scripts:

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

// Validar primeiro
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 }
// }

Opções de Aplicação

OpçãoTipoPadrãoDescrição
includeContentbooleanfalseCriar entradas de conteúdo de exemplo
onConflictstring"skip""skip", "update" ou "error"
mediaBasePathstringCaminho base para ficheiros de media locais
storageStorageAdaptador de armazenamento para uploads de media
baseUrlstringURL base para URLs de media

Idempotência

O seeding é seguro para executar múltiplas vezes. Comportamento de conflito por tipo de entidade:

EntidadeComportamento
ColeçãoIgnorar se o slug existir
CampoIgnorar se coleção + slug existir
Definição de taxonomiaIgnorar se o nome existir
Termo de taxonomiaIgnorar se nome + slug existir
Perfil de autoriaIgnorar se o slug existir
MenuIgnorar se o nome existir
Itens de menuSubstituir todos (menu é recriado)
RedirecionamentoIgnorar se a origem existir
Área de widgetsIgnorar se o nome existir
WidgetsSubstituir todos (área é recriada)
SecçãoIgnorar se o slug existir
ConfiguraçõesAtualizar (configurações devem poder mudar)
ConteúdoIgnorar se o slug existir na coleção

Validação

Os ficheiros seed são validados antes da aplicação:

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

Verificações de validação:

  • Os campos obrigatórios estão presentes
  • Os slugs seguem as convenções de nomenclatura (minúsculas, underscores)
  • Os tipos de campo são válidos
  • As referências apontam para conteúdo existente
  • Os pais de termos hierárquicos existem
  • Os caminhos de redirecionamento são URLs locais seguros
  • As origens de redirecionamento são únicas
  • Sem slugs duplicados dentro das coleções

Comandos CLI

# Aplicar ficheiro seed
npx emdash seed .emdash/seed.json

# Aplicar sem conteúdo de exemplo
npx emdash seed .emdash/seed.json --no-content

# Apenas validar
npx emdash seed .emdash/seed.json --validate

# Exportar esquema atual como seed
npx emdash export-seed > seed.json

# Exportar com conteúdo
npx emdash export-seed --with-content > seed.json

Próximos Passos