Seedファイルは、EmDashサイトを初期化するJSONドキュメントです。コレクション、フィールド、タクソノミー、メニュー、リダイレクト、ウィジェットエリア、サイト設定、およびオプションのサンプルコンテンツを定義します。
ルート構造
Seedファイルには次のトップレベルの構造があります:
{
"$schema": "https://emdashcms.com/seed.schema.json",
"version": "1",
"meta": {},
"settings": {},
"collections": [],
"taxonomies": [],
"bylines": [],
"menus": [],
"redirects": [],
"widgetAreas": [],
"sections": [],
"content": {}
}
| フィールド | 型 | 必須 | 説明 |
|---|---|---|---|
$schema | string | いいえ | エディタ検証用のJSONスキーマURL |
version | "1" | はい | Seedフォーマットバージョン |
meta | object | いいえ | Seedに関するメタデータ |
settings | object | いいえ | サイト設定 |
collections | array | いいえ | コレクション定義 |
taxonomies | array | いいえ | タクソノミー定義 |
bylines | array | いいえ | バイラインプロファイル定義 |
menus | array | いいえ | ナビゲーションメニュー |
redirects | array | いいえ | リダイレクトルール |
widgetAreas | array | いいえ | ウィジェットエリア定義 |
sections | array | いいえ | 再利用可能なコンテンツブロック |
content | object | いいえ | サンプルコンテンツエントリ |
Meta
metaオブジェクトには、Seedに関するオプションの説明的なメタデータが含まれます:
{
"meta": {
"name": "Blog Starter",
"description": "A simple blog with posts, pages, and categories",
"author": "EmDash"
}
}
Settings
settingsオブジェクトには、サイト全体の設定値が含まれます:
{
"settings": {
"title": "My Site",
"tagline": "A modern CMS",
"postsPerPage": 10,
"dateFormat": "MMMM d, yyyy"
}
}
設定はsite:プレフィックス付きでoptionsテーブルに適用されます。セットアップウィザードは、Seedファイルからtitleとtaglineを事前入力し(提供されている場合)、ユーザーが初期設定中にそれらを上書きできるようにします。
Collections
各コレクション定義は、データベースにコンテンツタイプを作成します:
{
"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"
}
]
}
]
}
コレクションプロパティ
| プロパティ | 型 | 必須 | 説明 |
|---|---|---|---|
slug | string | はい | URL安全な識別子(小文字、アンダースコア) |
label | string | はい | 複数形の表示名 |
labelSingular | string | いいえ | 単数形の表示名 |
description | string | いいえ | 管理UI説明 |
icon | string | いいえ | Lucideアイコン名 |
supports | array | いいえ | 機能:"drafts"、"revisions" |
fields | array | はい | フィールド定義 |
フィールドプロパティ
| プロパティ | 型 | 必須 | 説明 |
|---|---|---|---|
slug | string | はい | カラム名(小文字、アンダースコア) |
label | string | はい | 表示名 |
type | string | はい | フィールドタイプ |
required | boolean | いいえ | 検証:フィールドには値が必要 |
unique | boolean | いいえ | 検証:値は一意である必要がある |
defaultValue | any | いいえ | 新しいエントリのデフォルト値 |
validation | object | いいえ | 追加の検証ルール |
widget | string | いいえ | 管理UIウィジェットオーバーライド |
options | object | いいえ | ウィジェット固有の設定 |
フィールドタイプ
| タイプ | 説明 | 格納形式 |
|---|---|---|
string | 短いテキスト | TEXT |
text | 長いテキスト(textarea) | TEXT |
number | 数値 | REAL |
integer | 整数 | INTEGER |
boolean | 真/偽 | INTEGER |
date | 日付値 | TEXT (ISO 8601) |
datetime | 日時 | TEXT (ISO 8601) |
email | メールアドレス | TEXT |
url | URL | TEXT |
slug | URL安全な文字列 | TEXT |
portableText | リッチテキストコンテンツ | JSON |
image | 画像参照 | JSON |
file | ファイル参照 | JSON |
json | 任意のJSON | JSON |
reference | 別のエントリへの参照 | TEXT |
Taxonomies
タクソノミーはコンテンツの分類システムです:
{
"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"]
}
]
}
タクソノミープロパティ
| プロパティ | 型 | 必須 | 説明 |
|---|---|---|---|
name | string | はい | 一意の識別子 |
label | string | はい | 複数形の表示名 |
labelSingular | string | いいえ | 単数形の表示名 |
hierarchical | boolean | はい | ネストされたタームを許可(カテゴリ)またはフラット(タグ) |
collections | array | はい | このタクソノミーが適用されるコレクション |
terms | array | いいえ | 事前定義されたターム |
タームプロパティ
| プロパティ | 型 | 必須 | 説明 |
|---|---|---|---|
slug | string | はい | URL安全な識別子 |
label | string | はい | 表示名 |
description | string | いいえ | ターム説明 |
parent | string | いいえ | 親タームスラッグ(階層のみ) |
Menus
menus配列は、管理画面から編集可能なナビゲーションメニューを定義します:
{
"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 | カスタムURL | url |
page | ページエントリへのリンク | ref |
post | 投稿エントリへのリンク | ref |
taxonomy | タクソノミーアーカイブへのリンク | ref、collection |
collection | コレクションアーカイブへのリンク | collection |
メニューアイテムプロパティ
| プロパティ | 型 | 説明 |
|---|---|---|
type | string | アイテムタイプ(上記参照) |
label | string | 表示テキスト(ページ/投稿refの場合は自動生成) |
url | string | カスタムURL(customタイプの場合) |
ref | string | Seed内のコンテンツID(page/postタイプの場合) |
collection | string | コレクションスラッグ |
target | string | 新しいウィンドウの場合は"_blank" |
titleAttr | string | HTMLタイトル属性 |
cssClasses | string | カスタムCSSクラス |
children | array | ネストされたメニューアイテム |
Bylines
バイラインプロファイルは所有権(author_id)とは別です。再利用可能なバイラインIDを一度定義し、コンテンツエントリから参照します。
{
"bylines": [
{
"id": "editorial",
"slug": "emdash-editorial",
"displayName": "EmDash Editorial"
},
{
"id": "guest",
"slug": "guest-contributor",
"displayName": "Guest Contributor",
"isGuest": true
}
]
}
| プロパティ | 型 | 必須 | 説明 |
|---|---|---|---|
id | string | はい | content[].bylinesで使用されるSeedローカルID |
slug | string | はい | URL安全なバイラインスラッグ |
displayName | string | はい | テンプレートとAPIに表示される名前 |
bio | string | いいえ | オプションのプロファイル略歴 |
websiteUrl | string | いいえ | オプションのウェブサイトURL |
isGuest | boolean | いいえ | バイラインをゲストプロファイルとしてマーク |
Redirects
redirects配列は、移行後にレガシーURLを保持するリダイレクトルールを定義します:
{
"redirects": [
{ "source": "/old-about", "destination": "/about" },
{ "source": "/legacy-feed", "destination": "/rss.xml", "type": 308 },
{
"source": "/category/news",
"destination": "/categories/news",
"groupName": "migration"
}
]
}
リダイレクトプロパティ
| プロパティ | 型 | 必須 | 説明 |
|---|---|---|---|
source | string | はい | ソースパス(/で始まる必要があります) |
destination | string | はい | 宛先パス(/で始まる必要があります) |
type | number | いいえ | HTTPステータス:301、302、307、または308 |
enabled | boolean | いいえ | リダイレクトがアクティブかどうか(デフォルト:true) |
groupName | string | いいえ | 管理フィルタリング/検索用のオプションのグループ化ラベル |
Widget Areas
widgetAreas配列は、設定可能なコンテンツ領域を定義します:
{
"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 | 月別アーカイブ |
Sections
セクションは、エディタが/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." }
]
}
]
}
]
}
セクションプロパティ
| プロパティ | 型 | 必須 | 説明 |
|---|---|---|---|
slug | string | はい | URL安全な識別子 |
title | string | はい | セクションピッカーに表示される表示名 |
description | string | いいえ | このセクションを使用するタイミングを説明 |
keywords | array | いいえ | セクションを見つけるための検索用語 |
content | array | はい | Portable Textブロック |
source | string | いいえ | "theme"(Seedのデフォルト)または"import" |
Seedファイルからのセクションはsource: "theme"とマークされ、管理UIから削除できません。エディタは独自のセクション(source: "user")を作成でき、コンテンツの編集時に任意のセクションタイプを挿入できます。
Content
contentオブジェクトには、コレクション別に整理されたサンプルコンテンツエントリが含まれます:
{
"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." }]
}
]
}
}
]
}
}
コンテンツエントリプロパティ
| プロパティ | 型 | 必須 | 説明 |
|---|---|---|---|
id | string | はい | 参照用のSeedローカルID |
slug | string | はい | URLスラッグ |
status | string | いいえ | "published"または"draft"(デフォルト:"published") |
data | object | はい | フィールド値 |
bylines | array | いいえ | 順序付けられたバイラインクレジット(byline、オプションroleLabel) |
taxonomies | object | いいえ | タクソノミー名によるターム割り当て |
Content References
$ref:プレフィックスを使用して他のコンテンツエントリを参照します:
{
"data": {
"related_posts": ["$ref:another-post", "$ref:third-post"]
}
}
$ref:プレフィックスは、シード中にSeed IDをデータベースIDに解決します。
Media References
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"
}
}
}
}
メディアプロパティ
| プロパティ | 型 | 必須 | 説明 |
|---|---|---|---|
url | string | はい* | ダウンロードするリモートURL |
file | string | はい* | .emdash/media/内のローカルファイル名 |
alt | string | いいえ | アクセシビリティ用の代替テキスト |
filename | string | いいえ | ファイル名を上書き |
caption | string | いいえ | メディアキャプション |
*urlまたはfileのいずれかが必要です。両方は不可。
Applying Seeds Programmatically
CLIツールまたはスクリプト用のSeed APIを使用します:
import { applySeed, validateSeed } from "emdash/seed";
import seedData from "./.emdash/seed.json";
// 最初に検証
const validation = validateSeed(seedData);
if (!validation.valid) {
console.error(validation.errors);
process.exit(1);
}
// 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 }
// }
Applyオプション
| オプション | 型 | デフォルト | 説明 |
|---|---|---|---|
includeContent | boolean | false | サンプルコンテンツエントリを作成 |
onConflict | string | "skip" | "skip"、"update"、または"error" |
mediaBasePath | string | — | ローカルメディアファイルのベースパス |
storage | Storage | — | メディアアップロード用のストレージアダプタ |
baseUrl | string | — | メディアURLのベースURL |
Idempotency
シードは複数回安全に実行できます。エンティティタイプ別の競合動作:
| エンティティ | 動作 |
|---|---|
| Collection | スラッグが存在する場合はスキップ |
| Field | collection + スラッグが存在する場合はスキップ |
| タクソノミー定義 | 名前が存在する場合はスキップ |
| タクソノミーターム | 名前 + スラッグが存在する場合はスキップ |
| バイラインプロファイル | スラッグが存在する場合はスキップ |
| メニュー | 名前が存在する場合はスキップ |
| メニューアイテム | すべて置換(メニューは再作成されます) |
| リダイレクト | ソースが存在する場合はスキップ |
| ウィジェットエリア | 名前が存在する場合はスキップ |
| ウィジェット | すべて置換(エリアは再作成されます) |
| セクション | スラッグが存在する場合はスキップ |
| 設定 | 更新(設定は変更されることを意図しています) |
| コンテンツ | コレクション内にスラッグが存在する場合はスキップ |
Validation
Seedファイルは適用前に検証されます:
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));
検証は次のことを確認します:
- 必須フィールドが存在する
- スラッグがそのタイプに対して有効である(コレクションとフィールドのスラッグは小文字、数字、アンダースコアを許可します。他のスラッグはハイフンも許可します)
- フィールドタイプが有効である
- 参照が既存のコンテンツを指している
- 階層タームの親が存在する
- リダイレクトパスが安全なローカルURLである
- リダイレクトソースが一意である
- コレクション内に重複するスラッグがない
CLI Commands
.emdash/seed.json、package.json#emdash.seed、またはseed/seed.jsonのSeedファイルは、ビルドにインライン化され、データベースが空の場合の最初のリクエストで適用されます。既存のサイトのスキーマ(およびオプションでそのコンテンツ)をSeedファイルとしてエクスポートするには:
# 新しいプロジェクトではまだ.emdash/が存在しない可能性があるため`mkdir -p`
mkdir -p .emdash
# 現在のスキーマをSeedファイルとしてエクスポート
npx emdash export-seed > .emdash/seed.json
# コンテンツ付きでエクスポート
npx emdash export-seed --with-content > .emdash/seed.json
Next Steps
- Creating Themes — 完全なテーマを作成
- Themes Overview — テーマの仕組み