シードファイル形式

このページ

シードファイルは EmDash サイトをブートストラップする JSON ドキュメントです。コレクション、フィールド、タクソノミー、メニュー、リダイレクト、ウィジェットエリア、サイト設定、および任意のサンプルコンテンツを定義します。

ルート構造

{
	"$schema": "https://emdashcms.com/seed.schema.json",
	"version": "1",
	"meta": {},
	"settings": {},
	"collections": [],
	"taxonomies": [],
	"bylines": [],
	"menus": [],
	"redirects": [],
	"widgetAreas": [],
	"sections": [],
	"content": {}
}
フィールド必須説明
$schemastringNoエディタバリデーション用の JSON スキーマ URL
version"1"Yesシードフォーマットバージョン
metaobjectNoシードに関するメタデータ
settingsobjectNoサイト設定
collectionsarrayNoコレクション定義
taxonomiesarrayNoタクソノミー定義
bylinesarrayNo署名プロフィール定義
menusarrayNoナビゲーションメニュー
redirectsarrayNoリダイレクトルール
widgetAreasarrayNoウィジェットエリア定義
sectionsarrayNo再利用可能なコンテンツブロック
contentobjectNoサンプルコンテンツエントリ

Meta

シードに関する任意のメタデータ:

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

Settings

サイト全体の設定値:

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

設定は site: プレフィックスで options テーブルに適用されます。セットアップウィザードでユーザーは titletagline を上書きできます。

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

コレクションプロパティ

プロパティ必須説明
slugstringYesURL セーフな識別子(小文字、アンダースコア)
labelstringYes複数形の表示名
labelSingularstringNo単数形の表示名
descriptionstringNo管理 UI の説明
iconstringNoLucide アイコン名
supportsarrayNo機能: "drafts", "revisions"
fieldsarrayYesフィールド定義

フィールドプロパティ

プロパティ必須説明
slugstringYesカラム名(小文字、アンダースコア)
labelstringYes表示名
typestringYesフィールドタイプ
requiredbooleanNoバリデーション: 値が必須
uniquebooleanNoバリデーション: 値が一意でなければならない
defaultValueanyNo新規エントリのデフォルト値
validationobjectNo追加のバリデーションルール
widgetstringNo管理 UI ウィジェットのオーバーライド
optionsobjectNoウィジェット固有の設定

フィールドタイプ

タイプ説明保存形式
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

コンテンツの分類システム:

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

タクソノミープロパティ

プロパティ必須説明
namestringYes一意の識別子
labelstringYes複数形の表示名
labelSingularstringNo単数形の表示名
hierarchicalbooleanYesネストされたターム(カテゴリ)またはフラット(タグ)を許可
collectionsarrayYesこのタクソノミーが適用されるコレクション
termsarrayNo事前定義されたターム

タームプロパティ

プロパティ必須説明
slugstringYesURL セーフな識別子
labelstringYes表示名
descriptionstringNoタームの説明
parentstringNo親タームのスラッグ(階層型のみ)

管理画面から編集可能なナビゲーションメニュー:

{
	"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表示テキスト(page/post ref では自動生成)
urlstringカスタム URL(custom タイプ用)
refstringシード内コンテンツ ID(page/post タイプ用)
collectionstringコレクションスラッグ
targetstring新しいウィンドウの場合 "_blank"
titleAttrstringHTML title 属性
cssClassesstringカスタム CSS クラス
childrenarrayネストされたメニューアイテム

Bylines

署名プロフィールは所有権(author_id)とは別のものです。再利用可能な署名アイデンティティを一度定義し、コンテンツエントリから参照します。

{
	"bylines": [
		{
			"id": "editorial",
			"slug": "emdash-editorial",
			"displayName": "EmDash Editorial"
		},
		{
			"id": "guest",
			"slug": "guest-contributor",
			"displayName": "Guest Contributor",
			"isGuest": true
		}
	]
}
プロパティ必須説明
idstringYescontent[].bylines で使用されるシードローカル ID
slugstringYesURL セーフな署名スラッグ
displayNamestringYesテンプレートと API で表示される名前
biostringNo任意のプロフィール略歴
websiteUrlstringNo任意のウェブサイト URL
isGuestbooleanNoゲストプロフィールとしてマーク

Redirects

移行後にレガシー URL を保持するリダイレクトルール:

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

リダイレクトプロパティ

プロパティ必須説明
sourcestringYesソースパス(/ で始まる必要あり)
destinationstringYes宛先パス(/ で始まる必要あり)
typenumberNoHTTP ステータス: 301, 302, 307, 308
enabledbooleanNoリダイレクトが有効か(デフォルト: true
groupNamestringNo管理フィルタリング/検索用の任意のグループラベル

Widget Areas

設定可能なコンテンツ領域:

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

セクションプロパティ

プロパティ必須説明
slugstringYesURL セーフな識別子
titlestringYesセクションピッカーで表示される表示名
descriptionstringNoこのセクションの使用タイミングの説明
keywordsarrayNoセクション検索用の検索語
contentarrayYesPortable Text ブロック
sourcestringNo"theme"(シードのデフォルト)または "import"

シードファイルからのセクションは source: "theme" としてマークされ、管理 UI から削除できません。編集者は独自のセクション(source: "user")を作成でき、コンテンツ編集時に任意のセクションタイプを挿入できます。

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

コンテンツエントリプロパティ

プロパティ必須説明
idstringYes参照用のシードローカル ID
slugstringYesURL スラッグ
statusstringNo"published" または "draft"(デフォルト: "published"
dataobjectYesフィールド値
bylinesarrayNo順序付き署名クレジット(byline、任意の roleLabel
taxonomiesobjectNoタクソノミー名によるターム割り当て

コンテンツ参照

$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"
			}
		}
	}
}

メディアプロパティ

プロパティ必須説明
urlstringYes*ダウンロード元のリモート URL
filestringYes*.emdash/media/ 内のローカルファイル名
altstringNoアクセシビリティ用の代替テキスト
filenamestringNoファイル名のオーバーライド
captionstringNoメディアキャプション

*url または file のいずれかが必須で、両方は不可。

プログラムによるシードの適用

CLI ツールやスクリプト用のシード 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);
}

// シードの適用
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

冪等性

シード適用は複数回実行しても安全です。エンティティタイプ別の競合動作:

エンティティ動作
コレクションスラッグが存在する場合はスキップ
フィールドコレクション + スラッグが存在する場合はスキップ
タクソノミー定義名前が存在する場合はスキップ
タクソノミーターム名前 + スラッグが存在する場合はスキップ
署名プロフィールスラッグが存在する場合はスキップ
メニュー名前が存在する場合はスキップ
メニューアイテムすべて置換(メニューが再作成される)
リダイレクトソースが存在する場合はスキップ
ウィジェットエリア名前が存在する場合はスキップ
ウィジェットすべて置換(エリアが再作成される)
セクションスラッグが存在する場合はスキップ
設定更新(設定は変更されることが前提)
コンテンツコレクション内にスラッグが存在する場合はスキップ

バリデーション

シードファイルは適用前にバリデーションされます:

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 コマンド

# シードファイルの適用
npx emdash seed .emdash/seed.json

# サンプルコンテンツなしで適用
npx emdash seed .emdash/seed.json --no-content

# バリデーションのみ
npx emdash seed .emdash/seed.json --validate

# 現在のスキーマをシードとしてエクスポート
npx emdash export-seed > seed.json

# コンテンツ付きでエクスポート
npx emdash export-seed --with-content > seed.json

次のステップ