跳转到内容

种子文件格式

种子文件是用于引导 EmDash 站点的 JSON 文档。它们定义了集合、字段、分类法、菜单、重定向、小部件区域、站点设置以及可选的示例内容。

{
"$schema": "https://emdashcms.com/seed.schema.json",
"version": "1",
"meta": {},
"settings": {},
"collections": [],
"taxonomies": [],
"bylines": [],
"menus": [],
"redirects": [],
"widgetAreas": [],
"sections": [],
"content": {}
}
字段类型必需描述
$schemastring否用于编辑器验证的 JSON 模式 URL
version"1"是种子格式版本
metaobject否关于种子的元数据
settingsobject否站点设置
collectionsarray否集合定义
taxonomiesarray否分类法定义
bylinesarray否署名作者资料定义
menusarray否导航菜单
redirectsarray否重定向规则
widgetAreasarray否小部件区域定义
sectionsarray否可复用内容块
contentobject否示例内容条目

关于种子的可选元数据:

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

站点范围的配置值:

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

设置会应用到带有 site: 前缀的 options 表中。设置向导允许用户覆盖 title 和 tagline。

集合定义在数据库中创建内容类型:

{
"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"
}
]
}
]
}
属性类型必需描述
slugstring是URL 安全标识符(小写,下划线)
labelstring是复数显示名称
labelSingularstring否单数显示名称
descriptionstring否管理界面描述
iconstring否Lucide 图标名称
supportsarray否支持的功能:"drafts", "revisions"
fieldsarray是字段定义
属性类型必需描述
slugstring是列名(小写,下划线)
labelstring是显示名称
typestring是字段类型
requiredboolean否验证:字段必须有值
uniqueboolean否验证:值必须唯一
defaultValueany否新条目的默认值
validationobject否额外的验证规则
widgetstring否管理界面小部件覆盖
optionsobject否小部件特定配置
类型描述存储为
string短文本TEXT
text长文本(文本区域)TEXT
number数值REAL
integer整数INTEGER
boolean真/假INTEGER
date日期值TEXT (ISO 8601)
datetime日期和时间TEXT (ISO 8601)
email电子邮件地址TEXT
urlURLTEXT
slugURL 安全字符串TEXT
portableText富文本内容JSON
image图片引用JSON
file文件引用JSON
json任意 JSONJSON
reference对其他条目的引用TEXT

内容的分类系统:

{
"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"]
}
]
}
属性类型必需描述
namestring是唯一标识符
labelstring是复数显示名称
labelSingularstring否单数显示名称
hierarchicalboolean是允许嵌套术语(分类)或扁平结构(标签)
collectionsarray是此分类法适用的集合
termsarray否预定义术语
属性类型必需描述
slugstring是URL 安全标识符
labelstring是显示名称
descriptionstring否术语描述
parentstring否父术语 slug(仅限分层分类法)

可从管理界面编辑的导航菜单:

{
"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"
}
]
}
]
}
类型描述必需字段
custom自定义 URLurl
page链接到页面条目ref
post链接到文章条目ref
taxonomy链接到分类法归档页ref, collection
collection链接到集合归档页collection
属性类型描述
typestring项目类型(见上文)
labelstring显示文本(对于页面/文章引用会自动生成)
urlstring自定义 URL(用于 custom 类型)
refstring种子中的内容 ID(用于 page/post 类型)
collectionstring集合 slug
targetstring"_blank" 表示在新窗口打开
titleAttrstringHTML title 属性
cssClassesstring自定义 CSS 类
childrenarray嵌套菜单项

署名作者资料独立于所有权(author_id)。定义一次可复用的署名作者身份,然后从内容条目中引用它们。

{
"bylines": [
{
"id": "editorial",
"slug": "emdash-editorial",
"displayName": "EmDash Editorial"
},
{
"id": "guest",
"slug": "guest-contributor",
"displayName": "Guest Contributor",
"isGuest": true
}
]
}
属性类型必需描述
idstring是种子本地 ID,供 content[].bylines 使用
slugstring是URL 安全的署名作者 slug
displayNamestring是在模板和 API 中显示的名称
biostring否可选的个人简介
websiteUrlstring否可选的网站 URL
isGuestboolean否将署名作者标记为访客资料

用于在迁移后保留旧 URL 的重定向规则:

{
"redirects": [
{ "source": "/old-about", "destination": "/about" },
{ "source": "/legacy-feed", "destination": "/rss.xml", "type": 308 },
{
"source": "/category/news",
"destination": "/categories/news",
"groupName": "migration"
}
]
}
属性类型必需描述
sourcestring是源路径(必须以 / 开头)
destinationstring是目标路径(必须以 / 开头)
typenumber否HTTP 状态码:301, 302, 307, 或 308
enabledboolean否重定向是否激活(默认:true)
groupNamestring否用于管理界面过滤/搜索的可选分组标签

可配置的内容区域:

{
"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!" }]
}
]
}
]
}
]
}
类型描述必需字段
content富文本内容content (Portable Text)
menu渲染一个菜单menuName
component已注册的组件componentId
组件 ID描述
core:recent-posts最近文章列表
core:categories分类列表
core:tags标签云
core:search搜索表单
core:archives月度归档

可复用的内容块,编辑者可以通过 /section 斜杠命令将其插入到 Portable Text 字段中:

{
"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." }
]
}
]
}
]
}
属性类型必需描述
slugstring是URL 安全标识符
titlestring是在区块选择器中显示的显示名称
descriptionstring否说明何时使用此区块
keywordsarray否用于查找区块的搜索词
contentarray是Portable Text 块
sourcestring否"theme"(种子的默认值)或 "import"

来自种子文件的区块标记为 source: "theme",不能从管理界面删除。编辑者可以创建自己的区块(source: "user"),并在编辑内容时插入任何类型的区块。

按集合组织的示例内容:

{
"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." }]
}
]
}
}
]
}
}
属性类型必需描述
idstring是用于引用的种子本地 ID
slugstring是URL slug
statusstring否"published" 或 "draft"(默认:"published")
dataobject是字段值
bylinesarray否有序的署名作者列表(byline,可选的 roleLabel)
taxonomiesobject否按分类法名称分配的术语

使用 $ref: 前缀引用其他内容条目:

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

$ref: 前缀在种子应用期间将种子 ID 解析为数据库 ID。

从 URL 包含图片:

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

从 .emdash/media/ 包含本地图片:

{
"data": {
"featured_image": {
"$media": {
"file": "hero.jpg",
"alt": "Description of the image"
}
}
}
}
属性类型必需描述
urlstring是*要下载的远程 URL
filestring是*.emdash/media/ 中的本地文件名
altstring否无障碍访问的替代文本
filenamestring否覆盖文件名
captionstring否媒体说明文字

*url 或 file 必须提供其一,不能同时提供。

为 CLI 工具或脚本使用种子 API:

import { applySeed, validateSeed } from "emdash/seed";
import seedData from "./.emdash/seed.json";
// Validate first
const validation = validateSeed(seedData);
if (!validation.valid) {
console.error(validation.errors);
process.exit(1);
}
// Apply 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 }
// }
选项类型默认值描述
includeContentbooleanfalse创建示例内容条目
onConflictstring"skip""skip", "update", 或 "error"
mediaBasePathstring—本地媒体文件的基础路径
storageStorage—用于媒体上传的存储适配器
baseUrlstring—媒体 URL 的基础 URL

种子应用可以安全地多次运行。按实体类型的冲突行为:

实体行为
集合如果 slug 已存在则跳过
字段如果集合 + slug 已存在则跳过
分类法定义如果名称已存在则跳过
分类法术语如果名称 + slug 已存在则跳过
署名作者资料如果 slug 已存在则跳过
菜单如果名称已存在则跳过
菜单项替换所有(菜单会重新创建)
重定向如果源路径已存在则跳过
小部件区域如果名称已存在则跳过
小部件替换所有(区域会重新创建)
区块如果 slug 已存在则跳过
设置更新(设置旨在可以更改)
内容如果集合中 slug 已存在则跳过

种子文件在应用前会进行验证:

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

验证检查:

  • 必需字段存在
  • Slug 遵循命名约定(小写,下划线)
  • 字段类型有效
  • 引用指向现有内容
  • 分层术语的父级存在
  • 重定向路径是安全的本地 URL
  • 重定向源路径唯一
  • 集合内没有重复的 slug
Terminal window
# Apply seed file
npx emdash seed .emdash/seed.json
# Apply without sample content
npx emdash seed .emdash/seed.json --no-content
# Validate only
npx emdash seed .emdash/seed.json --validate
# Export current schema as seed
npx emdash export-seed > seed.json
# Export with content
npx emdash export-seed --with-content > seed.json