Pular para o conteúdo

Formato do arquivo seed

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

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

Metadados opcionais sobre o seed:

{
"meta": {
"name": "Base para blog",
"description": "Um blog simples com posts, páginas e categorias",
"author": "EmDash"
}
}

Valores de configuração em todo o site:

{
"settings": {
"title": "Meu site",
"tagline": "Um CMS moderno",
"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 usuários substituam title e tagline.

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

{
"collections": [
{
"slug": "posts",
"label": "Posts",
"labelSingular": "Post",
"description": "Posts do blog",
"icon": "file-text",
"supports": ["drafts", "revisions"],
"fields": [
{
"slug": "title",
"label": "Título",
"type": "string",
"required": true
},
{
"slug": "content",
"label": "Conteúdo",
"type": "portableText"
},
{
"slug": "featured_image",
"label": "Imagem destacada",
"type": "image"
}
]
}
]
}
PropriedadeTipoObrigatórioDescrição
slugstringSimIdentificador seguro para URL (minúsculas, underscores)
labelstringSimNome de exibição no plural
labelSingularstringNãoNome de exibição no singular
descriptionstringNãoDescrição na interface de administração
iconstringNãoNome do ícone Lucide
supportsarrayNãoRecursos: "drafts", "revisions"
fieldsarraySimDefinições de campos
PropriedadeTipoObrigatórioDescrição
slugstringSimNome da coluna (minúsculas, underscores)
labelstringSimNome de exibição
typestringSimTipo do 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 do widget na interface de administração
optionsobjectNãoConfiguração específica do widget
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 de texto ricoJSON
imageReferência de imagemJSON
fileReferência de arquivoJSON
jsonJSON arbitrárioJSON
referenceReferência a outra entradaTEXT

Sistemas de classificação para conteúdo:

{
"taxonomies": [
{
"name": "category",
"label": "Categorias",
"labelSingular": "Categoria",
"hierarchical": true,
"collections": ["posts"],
"terms": [
{ "slug": "news", "label": "Notícias" },
{ "slug": "tutorials", "label": "Tutoriais" },
{
"slug": "advanced",
"label": "Tutoriais avançados",
"parent": "tutorials"
}
]
},
{
"name": "tag",
"label": "Tags",
"labelSingular": "Tag",
"hierarchical": false,
"collections": ["posts"]
}
]
}
PropriedadeTipoObrigatórioDescrição
namestringSimIdentificador único
labelstringSimNome de exibição no plural
labelSingularstringNãoNome de exibição no singular
hierarchicalbooleanSimPermitir termos aninhados (categorias) ou planos (tags)
collectionsarraySimColeções às quais esta taxonomia se aplica
termsarrayNãoTermos predefinidos
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 da administração:

{
"menus": [
{
"name": "primary",
"label": "Navegação principal",
"items": [
{ "type": "custom", "label": "Início", "url": "/" },
{ "type": "page", "ref": "about" },
{ "type": "custom", "label": "Blog", "url": "/posts" },
{
"type": "custom",
"label": "Externo",
"url": "https://example.com",
"target": "_blank"
}
]
}
]
}
TipoDescriçãoCampos Obrigatórios
customURL personalizadaurl
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
PropriedadeTipoDescrição
typestringTipo do item (veja acima)
labelstringTexto de exibição (gerado automaticamente para refs de página/post)
urlstringURL personalizada (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

Perfis de linha de autoria são separados da propriedade (author_id). Defina identidades de linha de autoria reutilizáveis uma vez e, em seguida, referencie-as a partir de entradas de conteúdo.

{
"bylines": [
{
"id": "editorial",
"slug": "emdash-editorial",
"displayName": "EmDash Editorial"
},
{
"id": "guest",
"slug": "guest-contributor",
"displayName": "Colaborador convidado",
"isGuest": true
}
]
}
PropriedadeTipoObrigatórioDescrição
idstringSimID local do seed usado por content[].bylines
slugstringSimSlug seguro para URL da linha de autoria
displayNamestringSimNome exibido em modelos e APIs
biostringNãoBiografia opcional do perfil
websiteUrlstringNãoURL opcional do site
isGuestbooleanNãoMarca a linha de autoria como perfil de convidado

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"
}
]
}
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/busca administrativa

Regiões de conteúdo configuráveis:

{
"widgetAreas": [
{
"name": "sidebar",
"label": "Barra lateral principal",
"description": "Aparece em posts e páginas do blog",
"widgets": [
{
"type": "component",
"title": "Posts recentes",
"componentId": "core:recent-posts",
"props": { "count": 5 }
},
{
"type": "menu",
"title": "Links rápidos",
"menuName": "footer"
},
{
"type": "content",
"title": "Sobre",
"content": [
{
"_type": "block",
"style": "normal",
"children": [{ "_type": "span", "text": "Bem-vindo ao nosso site." }]
}
]
}
]
}
]
}
TipoDescriçãoCampos Obrigatórios
contentConteúdo de texto ricocontent (Portable Text)
menuRenderiza um menumenuName
componentComponente registradocomponentId
ID do ComponenteDescrição
core:recent-postsLista de posts recentes
core:categoriesLista de categorias
core:tagsNuvem de tags
core:searchFormulário de busca
core:archivesArquivos mensais

Blocos de conteúdo reutilizáveis que editores podem inserir em campos Portable Text através do comando de barra /section:

{
"sections": [
{
"slug": "hero-centered",
"title": "Hero centralizado",
"description": "Hero em largura total com título centralizado e botão de chamada para ação",
"keywords": ["hero", "banner", "header", "landing"],
"content": [
{
"_type": "block",
"style": "h1",
"children": [{ "_type": "span", "text": "Bem-vindo ao nosso site" }]
},
{
"_type": "block",
"children": [
{ "_type": "span", "text": "Coloque aqui sua principal proposta de valor." }
]
}
]
}
]
}
PropriedadeTipoObrigatórioDescrição
slugstringSimIdentificador seguro para URL
titlestringSimNome de exibição mostrado no seletor de seções
descriptionstringNãoExplica quando usar esta seção
keywordsarrayNãoTermos de busca para encontrar a seção
contentarraySimBlocos Portable Text
sourcestringNão"theme" (padrão para seeds) ou "import"

Seções de arquivos seed são marcadas como source: "theme" e não podem ser excluídas da interface administrativa. Editores podem criar suas próprias seções (source: "user") e inserir qualquer tipo de seção ao editar 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": "Artigo convidado" }
],
"data": {
"title": "Olá mundo",
"content": [
{
"_type": "block",
"style": "normal",
"children": [{ "_type": "span", "text": "Bem-vindo!" }]
}
],
"excerpt": "Seu primeiro post."
},
"taxonomies": {
"category": ["news"],
"tag": ["welcome", "first-post"]
}
}
],
"pages": [
{
"id": "about",
"slug": "about",
"status": "published",
"data": {
"title": "Sobre nós",
"content": [
{
"_type": "block",
"style": "normal",
"children": [{ "_type": "span", "text": "Conteúdo da página Sobre nós." }]
}
]
}
}
]
}
}
PropriedadeTipoObrigatórioDescrição
idstringSimID local do seed para referências
slugstringSimSlug da URL
statusstringNão"published" ou "draft" (padrão: "published")
dataobjectSimValores dos campos
bylinesarrayNãoCréditos de linha de autoria ordenados (byline, opcional roleLabel)
taxonomiesobjectNãoAtribuições de termos por nome de taxonomia

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 do banco de dados durante a semeadura.

Inclua imagens de URLs:

{
"data": {
"featured_image": {
"$media": {
"url": "https://images.unsplash.com/photo-xxx",
"alt": "Descrição da imagem",
"filename": "hero.jpg",
"caption": "Foto de alguém"
}
}
}
}

Incluir imagens locais de .emdash/media/:

{
"data": {
"featured_image": {
"$media": {
"file": "hero.jpg",
"alt": "Descrição da imagem"
}
}
}
}
PropriedadeTipoObrigatórioDescrição
urlstringSim*URL remota para download
filestringSim*Nome do arquivo local em .emdash/media/
altstringNãoTexto alternativo para acessibilidade
filenamestringNãoSubstituir o nome do arquivo
captionstringNãoLegenda da mídia

*Ou url ou file é obrigatório, não ambos.

Use a API de semente para ferramentas CLI ou scripts:

import { applySeed, validateSeed } from "emdash/seed";
import seedData from "../../themes/.emdash/seed.json";
// Validar primeiro
const validation = validateSeed(seedData);
if (!validation.valid) {
console.error(validation.errors);
process.exit(1);
}
// Aplicar semente
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çãoTipoPadrãoDescrição
includeContentbooleanfalseCriar entradas de conteúdo de exemplo
onConflictstring"skip""skip", "update", ou "error"
mediaBasePathstring—Caminho base para arquivos de mídia locais
storageStorage—Adaptador de armazenamento para uploads de mídia
baseUrlstring—URL base para URLs de mídia

A semeadura é segura para executar várias 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 do menuSubstituir todos (o menu é recriado)
RedirecionamentoIgnorar se a origem existir
Área de widgetIgnorar se o nome existir
WidgetsSubstituir todos (a área é recriada)
SeçãoIgnorar se o slug existir
ConfiguraçõesAtualizar (configurações devem mudar)
ConteúdoIgnorar se slug existir na coleção

Arquivos de semente 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:

  • Campos obrigatórios estão presentes
  • Slugs seguem convenções de nomenclatura (minúsculas, sublinhados)
  • Tipos de campo são válidos
  • Referências apontam para conteúdo existente
  • Pais de termos hierárquicos existem
  • Caminhos de redirecionamento são URLs locais seguras
  • Origens de redirecionamento são únicas
  • Sem slugs duplicados dentro de coleções
Terminal window
# Apply seed file
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 semente
npx emdash export-seed > seed.json
# Exportar com conteúdo
npx emdash export-seed --with-content > seed.json