Seed 檔案描述 EmDash 網站的初始 schema 與可選範例資料。目前範本將其放在 seed/seed.json,並以 package.json#emdash.seed 指向它。
EmDash 在建置時嵌入 seed。它用於首次設定與明確的 seed 命令,而不是每次部署都執行的遷移。
檔案探索
Astro 整合依下列順序尋找 seed:
.emdash/seed.json。package.json#emdash.seed中的路徑。seed/seed.json。- 沒有使用者 seed 時的內建預設 seed。
下列套件欄位選擇範本的慣用路徑:
{
"emdash": {
"seed": "seed/seed.json"
}
}
根形狀
下列範例包含每個根屬性:
{
"$schema": "https://emdashcms.com/seed.schema.json",
"version": "1",
"defaultLocale": "en",
"meta": {
"name": "Publication",
"description": "A publication seed",
"author": "Example Studio"
},
"settings": {},
"blockTypes": [],
"collections": [],
"relations": [],
"taxonomies": [],
"bylines": [],
"content": {},
"menus": [],
"redirects": [],
"widgetAreas": [],
"sections": []
}
| Property | Required | Purpose |
|---|---|---|
$schema | No | 編輯器 schema URL |
version | Yes | Seed 格式;唯一接受的值是 "1" |
defaultLocale | No | 省略 locale 的帶 locale 列所用的 locale;預設為執行時期組態,然後是 en |
meta | No | 設定期間顯示的描述性名稱、說明與作者 |
settings | No | 部分網站設定 |
blockTypes | No | blocks 欄位使用的版本化定義 |
collections | No | Collection 與欄位定義 |
taxonomies | No | 分類法定義與可選術語 |
bylines | No | 可選的署名展示資料 |
content | No | 依 collection slug 分組的範例條目 |
menus | No | 選單與巢狀項目 |
redirects | No | 本機重新導向規則 |
widgetAreas | No | Widget 區域與 widget |
sections | No | 可重複使用的 Portable Text section |
defaultLocale 必須是非空字串,且首尾無空白。
Settings
settings 是部分網站設定物件。常見屬性包括 title、tagline、logo、favicon、url、postsPerPage、dateFormat、timezone、social 和 seo。
設定精靈允許管理員覆寫已 seed 的標題與標語。預設 onConflict: "skip" 在重新套用 seed 時保留這些值,並補齊仍缺失的已提供設定。
{
"version": "1",
"settings": {
"title": "Field Notes",
"tagline": "Reports from the team",
"postsPerPage": 12,
"dateFormat": "MMMM d, yyyy",
"timezone": "Europe/London"
}
}
區塊類型
blockTypes 定義 collection blocks 欄位使用的版本化形狀。EmDash 在 collection 之前套用這些定義,因此欄位可以在 validation.allowedTypes 中命名它們。
下列 seed 為已儲存內容與修訂保留版本 1,同時讓版本 2 成為新區塊的活動版本:
{
"version": "1",
"blockTypes": [
{
"slug": "hero",
"label": "Hero",
"category": "Layout",
"currentVersion": 2,
"versions": [
{
"version": 1,
"fields": [
{ "slug": "heading", "label": "Heading", "type": "string", "required": true }
]
},
{
"version": 2,
"fields": [
{ "slug": "title", "label": "Title", "type": "string", "required": true },
{ "slug": "image", "label": "Image", "type": "image" }
]
}
]
}
],
"collections": [
{
"slug": "pages",
"label": "Pages",
"fields": [
{
"slug": "layout",
"label": "Layout",
"type": "blocks",
"validation": { "allowedTypes": ["hero"], "maxItems": 20 }
}
]
}
]
}
版本號碼是從 1 開始的連續正整數。currentVersion 必須命名已宣告的版本。匯出並重新套用 seed 會保留確切的版本號碼與活動指標;EmDash 不會重新編號。
使用 onConflict: "update" 時,僅當新定義相容時,seed 才能變更已儲存版本。對不相容定義重用現有編號會以 BLOCK_TYPE_VERSION_CONFLICT 失敗。對不相容定義請新增版本號碼。
已儲存的區塊值包含 _type、_version 和 _key。當 seed 內容指向保留版本時請提供這些屬性。執行時期會在新寫入的區塊省略它們時指派活動版本與鍵。
Collections
Collection 需要 slug、label 和 fields:
{
"version": "1",
"collections": [
{
"slug": "posts",
"label": "Posts",
"labelSingular": "Post",
"description": "Published articles",
"supports": ["drafts", "revisions", "scheduling", "search", "seo"],
"urlPattern": "/posts/{slug}",
"routable": true,
"commentsEnabled": true,
"editLocking": true,
"titleField": "title",
"dateField": "event_date",
"admin": {
"listColumns": ["event_date"]
},
"fields": [
{ "slug": "title", "label": "Title", "type": "string", "required": true },
{ "slug": "event_date", "label": "Event date", "type": "datetime", "indexed": true },
{ "slug": "content", "label": "Content", "type": "portableText" }
]
}
]
}
Collection 屬性
| Property | Type | Behavior |
|---|---|---|
slug | string | 必要的資料庫與 API 名稱;以小寫字母開頭,包含小寫字母、數字和底線 |
label | string | 必要的複數 UI 標籤 |
labelSingular | string | 可選的單數 UI 標籤 |
description | string | 可選的管理說明 |
icon | string | 可選的圖示名稱 |
admin.listColumns | string[] | 內容清單中顯示的最多四個已宣告欄位 slug |
supports | string[] | drafts、revisions、preview、scheduling、search 和 seo 中的任意項 |
urlPattern | string | 如 /posts/{slug} 的公開模式 |
routable | boolean | 已發佈條目是否需要 slug;預設 true |
hidden | boolean | 隱藏產生的側邊欄連結與儀表板快捷操作;collection 仍可透過 URL 與 API 存取 |
sortOrder | number | 管理側邊欄中的明確位置;已排序的 collection 先出現,依升序 |
group | string | 管理側邊欄資料夾;相同 group 的 collection 共用一個可摺疊項目 |
commentsEnabled | boolean | 為 collection 啟用評論 |
editLocking | boolean | 啟用編輯鎖定;預設 true |
titleField | string | 用於內容清單標題的欄位 |
dateField | string | 用於內容清單日期的 datetime 欄位 |
fields | SeedField[] | 必要的欄位定義 |
sortOrder 屬於 collection,並控制側邊欄順序。SeedField 沒有 sortOrder 屬性。欄位依其陣列順序建立。
欄位屬性
| Property | Type | Purpose |
|---|---|---|
slug | string | 符合 collection slug 模式的必要欄位名稱 |
label | string | 必要的 UI 標籤 |
type | FieldType | 必要的儲存欄位類型 |
required | boolean | 拒絕空的必填值 |
unique | boolean | 新增唯一性約束 |
searchable | boolean | 將欄位納入 collection 搜尋 |
indexed | boolean | 為支援的純量類型新增查詢索引 |
translatable | boolean | 依 locale 儲存值;預設 true。false 在各翻譯間共用一個值 |
defaultValue | any | 省略欄位時的初始值 |
validation | object | 產生的內容 schema 使用的驗證規則 |
widget | string | 管理欄位 widget 覆寫 |
options | object | Widget 特定選項 |
支援的欄位類型為:
string、text、url和slug。number、integer和boolean。datetime。select和multiSelect。portableText、json和repeater。blocks。image、file和reference。
reference 欄位不在 collection 資料表中儲存任何內容。其連結存在於繫結到的關係中;
參見 Relations。
只有 string、url、number、integer、boolean、datetime、select、reference 和 slug 可以設定 indexed: true。在 reference 欄位上,該旗標僅在欄位尚無關係時適用,因為已繫結欄位沒有可索引的欄。
欄位驗證
產生的 collection schema 在欄位類型支援的地方辨識這些規則:
| Rule | Used by |
|---|---|
min、max | 數字欄位 |
minLength、maxLength、pattern | 字串形欄位 |
options | select 和 multiSelect |
subFields、minItems、maxItems | repeater |
allowedTypes、minItems、maxItems | blocks |
allowedMimeTypes | 媒體欄位 |
blocks 欄位上的 retiredTypes 由伺服器端處理。從 allowedTypes 移除一個 slug 會將其退役,使現有已儲存區塊保持有效,而新區塊不能使用它。
validateSeed() 不會深入檢查 validation 或 options 中的每條規則。因此無效規則可能通過 seed 驗證,並在之後建立 collection schema 或寫入內容時失敗。
Relations
關係連接兩個 collection,並擁有其條目之間的連結。reference 欄位繫結到
其中一個,並從一端查看其連結。下列 seed 宣告 posts 與
authors 之間的關係,允許每篇文章一位作者:
{
"relations": [
{
"slug": "post_authors",
"parentCollection": "posts",
"childCollection": "authors",
"parentLabel": "Posts",
"parentLabelSingular": "Post",
"childLabel": "Authors",
"childLabelSingular": "Author",
"maxChildrenPerParent": 1
}
]
}
| Property | Type | Required | Description |
|---|---|---|---|
slug | string | Yes | reference 欄位定址它的唯一名稱 |
parentCollection | string | Yes | 父端的 collection |
childCollection | string | Yes | 子端的 collection |
parentLabel | string | Yes | 從子端看父端角色的名稱 |
parentLabelSingular | string | No | parentLabel 的單數形式 |
childLabel | string | Yes | 從父端看子端角色的名稱 |
childLabelSingular | string | No | childLabel 的單數形式 |
maxChildrenPerParent | number | null | No | 一個父端可連結的子端數量(null:無限制) |
maxParentsPerChild | number | null | No | 一個子端可連結的父端數量(null:無限制) |
collections 中的 reference 欄位命名它所繫結的關係:
{
"slug": "author",
"label": "Author",
"type": "reference",
"validation": { "relation": "post_authors" }
}
欄位也可以改為命名 targetCollection 並讓系統為其建立關係,這是
只有一個 collection 能看到的連結的最短路徑。當兩個
collection 都需要看到它,或要設定其標籤與限制時,請在 relations 中宣告關係。
reference 記錄了兩種形式以及各自
接受的驗證鍵。
關係的兩個 collection 一旦存在即固定:命名不同 collection 的 seed 會失敗,而不是
讓其持有的連結指向不再是一端的 collection。標籤與
限制在以 onConflict: "update" 套用 seed 時更新。
分類法
分類法定義識別其目標 collection。術語是範例資料,僅在 includeContent 為 true 時套用。
{
"version": "1",
"taxonomies": [
{
"name": "category",
"label": "Categories",
"labelSingular": "Category",
"hierarchical": true,
"collections": ["posts"],
"terms": [
{ "slug": "engineering", "label": "Engineering" },
{ "slug": "platform", "label": "Platform", "parent": "engineering" }
]
}
]
}
分類法可以帶有 seed 本機的 id、locale 和 translationOf。術語也可以帶有這些屬性。translationOf 指向另一個 seed 本機 ID。術語必須排在它所翻譯的術語之後。同一 name 的分類法條目可以依任意順序出現,因為宣告分類法形狀的條目會在其翻譯之前套用。
hierarchical 和 collections 由分類法的每個 locale 共用,因此其 translationOf 指向同名條目的分類法條目可以省略它們。套用引擎會沿著同名條目跟隨 translationOf,並從最後一個取它們。當翻譯宣告的值與取到的不同,或兩個為某分類法宣告它們的條目不一致時,驗證會發出警告。現有分類法會保留其值,除非沒有 translationOf 的條目取代它們。這在 onConflict: "update" 時發生;當條目的 locale 擁有 Conflict behavior 中描述的未修改內建 category 或 tag 定義,或該內建定義是該分類法唯一的定義時,在任何模式下都會發生。匯出只把它們寫到翻譯所指向的條目上。
術語的 parent 是同一 locale 中父術語的 slug。非階層分類法上的 parent 會產生警告並被忽略。帶有 translationOf 且沒有 parent 的術語會取它所翻譯術語的 parent。
Bylines
根級 bylines 定義署名展示。它們是範例資料,需要 includeContent: true。
{
"version": "1",
"bylines": [
{
"id": "byline-editor",
"slug": "alex-editor",
"displayName": "Alex Editor",
"isGuest": true
}
]
}
id 是 seed 本機的,由內容署名使用。可選屬性有 bio、websiteUrl、isGuest 和 avatar。
Byline 頭像指向已設定儲存中已存在的檔案:
{
"id": "byline-editor",
"slug": "alex-editor",
"displayName": "Alex Editor",
"avatar": {
"storageKey": "avatars/alex.jpg",
"filename": "alex.jpg",
"mimeType": "image/jpeg",
"alt": "Alex Editor",
"width": 400,
"height": 400
}
}
Byline 頭像 seed 會為儲存鍵建立或重用媒體列。它不會上傳或下載檔案。
Content
content 依 collection slug 分組條目。每個條目需要一個 seed 本機 id 和一個 data 物件。可路由 collection 還需要非空 slug。
{
"version": "1",
"content": {
"posts": [
{
"id": "post-welcome",
"slug": "welcome",
"status": "published",
"data": {
"title": "Welcome",
"content": []
},
"taxonomies": {
"category": ["engineering"]
},
"bylines": [
{ "byline": "byline-editor", "roleLabel": "Editor" }
]
}
]
}
}
| Property | Required | Behavior |
|---|---|---|
id | Yes | Seed 本機參照 ID |
slug | 可路由 collection | 公開 slug 與衝突鍵 |
status | No | published 或 draft;預設 published |
data | Yes | 依 collection 欄位 slug 鍵控的值 |
taxonomies | No | 分類法名稱到術語 slug 陣列 |
bylines | No | 參照根 byline ID 的有序署名 |
locale | No | BCP 47 locale;透過 defaultLocale 預設 |
translationOf | No | 同一 collection 中的 seed 本機內容 ID |
對於可路由條目,seed 本機 id 不是其資料庫身分。EmDash 會建立資料庫 ID,並記錄對應供後續參照。對於 routable: false 的 collection 中無 slug 的條目,EmDash 使用 seed 的 id 作為儲存 ID,使重新套用保持冪等。
讀取時,entry.id 是 Astro 路由識別碼,通常是 slug。儲存的資料庫 ID 是 entry.data.id。
內容參照
在 data 中使用 $ref: 字串,將 seed 本機內容 ID 替換為已建立的資料庫 ID:
{
"id": "event-opening",
"slug": "opening-night",
"data": {
"title": "Opening night",
"venue": "$ref:venue-main-hall"
}
}
參照目標必須足夠早出現,以便進入套用引擎的 ID 對應。未解析的 $ref: 值會保留為原始字面字串;validateSeed() 不會拒絕它。
對於 reference 欄位,從其關係的父端宣告連結。兩端看到同一 組連結,因此子 collection 上的欄位會重複斷言父端已有的連結。
媒體參照
在內容資料中使用 $media 以下載 URL、用提供的儲存配接器上傳、建立媒體列,並將物件替換為媒體欄位值:
{
"featured_image": {
"$media": {
"url": "https://example.com/images/launch.jpg",
"filename": "launch.jpg",
"alt": "A product launch on stage",
"caption": "Launch event"
}
}
}
在 Portable Text image 區塊或 gallery 圖片中,asset 中的 $media 會變成媒體參照(_type: "reference"、_ref、url、provider),並在缺失時用媒體的替代文字與尺寸填入圖片的 alt、width 和 height。
在一次套用呼叫內,對同一 URL 的重複參照會重用已解析的媒體值。Seed 媒體參照不接受本機 file 屬性。mediaBasePath 仍在公開類型 SeedApplyOptions 中,但目前套用引擎不讀取它。
未提供儲存配接器時,$media 參照會被略過並解析為 null。使用 skipMediaDownload: true 時,它們成為外部媒體值,且不需要儲存配接器。
選單
選單是結構資料,即使 includeContent 為 false 也會套用:
{
"version": "1",
"menus": [
{
"name": "primary",
"label": "Primary navigation",
"items": [
{
"type": "page",
"label": "About",
"ref": "page-about",
"collection": "pages"
},
{
"type": "custom",
"label": "Contact",
"url": "/contact",
"target": "_self"
}
]
}
]
}
允許的項目類型為 custom、page、post、taxonomy 和 collection。custom 需要 url;page 和 post 需要 ref。項目可以包含 id、translationOf、label、collection、titleAttr、cssClasses、locale、target 和巢狀的 children。
對於 page 和 post,ref 命名 seed 內容 ID。缺失目標會產生驗證警告,並得到沒有已解析內容參照的選單項目。每當套用該選單時,現有選單項目都會被刪除並重建,與 onConflict 無關。
重新導向
重新導向需要本機來源路徑與目標路徑:
{
"version": "1",
"redirects": [
{
"source": "/old-path",
"destination": "/new-path",
"type": 308,
"enabled": true,
"groupName": "WordPress migration"
}
]
}
兩條路徑都必須以 / 開頭。協定相對 URL、路徑遍歷區段和換行會被拒絕。允許的狀態碼為 301、302、307 和 308。
Widget 區域
Widget 區域包含 content、menu 或 component widget:
{
"version": "1",
"widgetAreas": [
{
"name": "sidebar",
"label": "Sidebar",
"widgets": [
{
"type": "menu",
"title": "Explore",
"menuName": "primary"
},
{
"type": "component",
"title": "Recent posts",
"componentId": "core:recent-posts",
"props": { "count": 5 }
}
]
}
]
}
內容 widget 將 Portable Text 儲存在 content 中。選單 widget 需要 menuName。元件 widget 需要 componentId,並可傳遞 props。SeedWidget 上沒有 settings 屬性。
每當套用該區域時,區域中的現有 widget 都會被刪除並重建,與 onConflict 無關。
Sections
Section 包含可重複使用的 Portable Text 內容:
{
"version": "1",
"sections": [
{
"slug": "newsletter-signup",
"title": "Newsletter signup",
"description": "Signup call to action",
"keywords": ["newsletter", "email"],
"source": "theme",
"content": []
}
]
}
Section slug 包含小寫字母、數字和連字號。source 為 theme、user 或 import;seed 預設將其設為 theme。主題 section 不能在管理中刪除。Section 是結構性的,即使 includeContent 為 false 也會套用。
本地化
defaultLocale 為分類法、術語、選單、選單項目和內容填入缺失的 locale。存在活動執行時期 i18n 組態時,以後者為準。
本地化的分類法、術語、選單、選單項目和內容使用 seed 本機的 id 與 translationOf 欄位。將來源項目放在翻譯之前,以便套用引擎解析其翻譯群組。翻譯後的內容條目必須設定 locale,且其 translationOf 必須命名同一 collection 中的另一條目。
以程式方式套用 seed
applySeed() 和 validateSeed() 從 emdash/seed 匯出。下列輔助函式在套用前先驗證:
import {
applySeed,
validateSeed,
type SeedApplyOptions,
type SeedFile,
} from "emdash/seed";
type SeedDatabase = Parameters<typeof applySeed>[0];
export async function applyProjectSeed(
db: SeedDatabase,
seed: SeedFile,
options: SeedApplyOptions,
) {
const validation = validateSeed(seed);
if (!validation.valid) {
throw new Error(validation.errors.join("\n"));
}
return applySeed(db, seed, options);
}
SeedApplyOptions
| Option | Default | Current behavior |
|---|---|---|
includeContent | false | 包含內容條目、byline 和分類法術語 |
onConflict | "skip" | 對支援的實體衝突為 "skip"、"update" 或 "error" |
storage | none | 下載 $media URL 所需的儲存配接器 |
skipMediaDownload | false | 將 $media URL 保留為外部媒體值 |
mediaBasePath | none | 存在於公開類型中,但目前套用引擎不使用 |
程式套用預設將 includeContent 設為 false。設定精靈傳遞管理員的範例內容選擇。CLI emdash seed 預設包含內容,除非設定了 --no-content。
衝突行為
onConflict 不是整個 seed 的交易策略:
- Collection、欄位、byline、內容、重新導向和 section 支援 skip、update 和 error 行為。
- 分類法定義與術語遵循適用的衝突模式。例外是每個新資料庫開始時的內建
category和tag定義:在網站修改它們之前,宣告它們的 seed 在任何模式下都會取代它們。 - Settings 使用依鍵的衝突處理。
skip建立缺失設定並保留現有值。update覆寫每個已提供設定。error在第一個已存在設定處停止;依 seed 順序更早建立的設定仍保持已套用。 - 現有選單保留其選單列,但取代所有項目。
- 現有 widget 區域保留其區域列,但取代所有 widget。
- 內容衝突依 collection、slug 和 locale 比對。不可路由 collection 中無 slug 的條目依其 seed ID 比對。
使用 onConflict: "update" 時,內容資料會被取代,其 byline 與分類法指派會與 seed 對齊。在對現有網站使用之前,先在副本上測試 update 模式。
applySeed() 回傳 collection、欄位、分類法、byline、選單、重新導向、widget 區域、section、設定、內容和媒體的計數器。
驗證行為
validateSeed() 回傳 { valid, errors, warnings }。applySeed() 會呼叫它,並在存在錯誤時拋出 Invalid seed file。
驗證器檢查套用引擎所需的結構規則,包括:
- 版本與非空
defaultLocale。 - Collection、欄位、分類法、術語、選單、widget 區域、section、byline 和內容的容器形狀。
- 必要的名稱、標籤、ID、slug,以及支援的欄位或 widget 類型。
- 相關範圍內的重複識別碼。
- 已索引欄位類型與
admin.listColumns參照。 - 分類法父級、內容翻譯、內容 byline 參照,以及選單項目要求。
- 安全的本機重新導向路徑與狀態碼。
某些條件是警告而非錯誤。例如:沒有 collection 的分類法、扁平分類法上的 parent,或 seed 中缺失的選單內容參照。
驗證器不證明所有 data 值都合乎其 collection 欄位。它也不深入驗證網站設定、欄位 validation、欄位 options、任意 Portable Text 區塊、元件 widget props、內容資料中的 $ref: 目標,或 $media 的遠端可用性。有效 seed 仍可能在 schema 建立、內容驗證、網路下載或儲存上傳期間失敗。
使用 $schema URL 獲得編輯器輔助,並在套用前執行可執行驗證器:
npx emdash seed seed/seed.json --validate
CLI 命令
以明確衝突行為將 seed 套用到本機 SQLite 資料庫:
npx emdash seed seed/seed.json --database ./data.db --on-conflict skip
將目前本機 schema 與全部內容匯出回範本路徑:
npx emdash export-seed --database ./data.db --with-content=all > seed/seed.json
export-seed 直接處理本機 SQLite 檔案。對於已部署的 D1 資料庫,先將其匯出到本機檔案。在提交結果之前,請檢查匯出的設定、內容與媒體參照。要使匯出的媒體可在另一網站匯入,請傳入 --media-base-url;參見 Media URLs。
下一步
- Create a theme:在可重複使用的 Astro 範本中使用 seed。
- Schema evolution:更新現有已部署網站的 schema。
- CLI reference:資料庫與匯出選項。