シードファイル形式
このページで分かること
- シードファイルの役割(初回のセットアップ用で、デプロイのたびに実行されるマイグレーションではない)と、ファイルを探す順番、ルートの構造
- 設定・コレクション・フィールド・タクソノミー・バイライン・コンテンツ・参照・メディア・メニュー・リダイレクト・ウィジェットエリア・セクション・ローカライズの書き方
applySeed()による適用と競合したときの動作、validateSeed()が確かめること・確かめないこと、CLIのコマンド
このページの目次
シードファイルは、EmDashのサイトの最初のモデルと、任意のサンプルデータを記述するファイルです。現在のテンプレートは、シードファイルを seed/seed.json に置き、package.json#emdash.seed でその場所を指しています。
EmDashは、ビルド時にシードを埋め込みます。シードは、初回のセットアップと明示的なシードのコマンドのためのもので、デプロイのたびに実行されるマイグレーションではありません。
ファイルを探す順番
Astroのインテグレーションは、次の順番でシードを探します。
.emdash/seed.json。package.json#emdash.seedのパス。seed/seed.json。- ユーザーのシードがない場合は、組み込みの標準のシード。
次のパッケージのフィールドは、テンプレートの慣例的なパスを指定します。
{
"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": {},
"collections": [],
"taxonomies": [],
"bylines": [],
"content": {},
"menus": [],
"redirects": [],
"widgetAreas": [],
"sections": []
}
| プロパティ | 必須 | 用途 |
|---|---|---|
$schema |
いいえ | エディター用のスキーマのURL |
version |
はい | シードの形式。受け付ける値は "1" だけです |
defaultLocale |
いいえ | ロケールを持つ行のうち locale を省略したもののロケール。省略すると実行時の設定、次に en になります |
meta |
いいえ | セットアップ中に表示する名前、説明、作成者 |
settings |
いいえ | サイトの設定の一部 |
collections |
いいえ | コレクションとフィールドの定義 |
taxonomies |
いいえ | タクソノミーの定義と、任意のターム |
bylines |
いいえ | 表示用のクレジットの任意のプロフィール |
content |
いいえ | コレクションのスラッグごとにまとめたサンプルのエントリー |
menus |
いいえ | メニューと、入れ子になった項目 |
redirects |
いいえ | サイト内のリダイレクトの規則 |
widgetAreas |
いいえ | ウィジェットエリアとウィジェット |
sections |
いいえ | 再利用できるPortable Textのセクション |
defaultLocale は、先頭と末尾に空白のない、空でない文字列でなければなりません。
設定
settings は、サイトの設定の一部を表すオブジェクトです。よく使うプロパティは、title、tagline、logo、favicon、url、postsPerPage、dateFormat、timezone、social、seo です。
セットアップウィザードでは、管理者がシードのタイトルとキャッチフレーズを置き換えられます。プログラムからシードを適用する場合は、onConflict にかかわらず、指定されたすべての設定が書き込まれます。
{
"version": "1",
"settings": {
"title": "Field Notes",
"tagline": "Reports from the team",
"postsPerPage": 12,
"dateFormat": "MMMM d, yyyy",
"timezone": "Europe/London"
}
}
コレクション
コレクションには、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" }
]
}
]
}
コレクションのプロパティ
| プロパティ | 型 | 動作 |
|---|---|---|
slug |
string |
必須のデータベースとAPIでの名前。英小文字で始まり、英小文字・数字・アンダースコアを含みます |
label |
string |
必須のUI用のラベル(複数形) |
labelSingular |
string |
任意のUI用のラベル(単数形) |
description |
string |
任意の管理画面用の説明 |
icon |
string |
任意のアイコン名 |
admin.listColumns |
string[] |
コンテンツの一覧に表示する、宣言済みのフィールドのスラッグ(4つまで) |
supports |
string[] |
drafts、revisions、preview、scheduling、search、seo のうち任意のもの |
urlPattern |
string |
/posts/{slug} のような公開側のパターン |
routable |
boolean |
公開するエントリーにスラッグが必要かどうか。初期値は true |
hidden |
boolean |
自動で作られるサイドバーのリンクと、ダッシュボードのクイックアクションを非表示にします。コレクションには、URLとAPIから引き続きアクセスできます |
sortOrder |
number |
管理画面のサイドバーでの明示的な位置。順番を指定したコレクションが先に、昇順で並びます |
group |
string |
管理画面のサイドバーのフォルダー。同じグループのコレクションは、折りたためる1つの項目にまとまります |
commentsEnabled |
boolean |
コレクションのコメントを有効にします |
editLocking |
boolean |
編集のロックを有効にします。初期値は true |
titleField |
string |
コンテンツの一覧のタイトルに使うフィールド |
dateField |
string |
コンテンツの一覧の日付に使う datetime フィールド |
fields |
SeedField[] |
必須のフィールドの定義 |
sortOrder はコレクションのプロパティで、サイドバーの並び順を制御します。SeedField には sortOrder プロパティがありません。フィールドは、配列の順番に作成されます。
フィールドのプロパティ
| プロパティ | 型 | 用途 |
|---|---|---|
slug |
string |
必須のフィールド名。コレクションのスラッグと同じパターンを使います |
label |
string |
必須のUI用のラベル |
type |
FieldType |
必須の、保存するフィールドの型 |
required |
boolean |
必須の値が空の場合に拒否します |
unique |
boolean |
一意性の制約を追加します |
searchable |
boolean |
コレクションの検索の対象にします |
indexed |
boolean |
対応するスカラー型に、クエリ用のインデックスを追加します |
defaultValue |
任意 | フィールドが省略された場合の初期値 |
validation |
オブジェクト | 生成されるコンテンツのスキーマが使う検証の規則 |
widget |
string |
管理画面のフィールドのウィジェットの上書き |
options |
オブジェクト | ウィジェットごとのオプション |
対応しているフィールドの型は次のとおりです。
string、text、url、slug。number、integer、boolean。datetime。select、multiSelect。portableText、json、repeater。image、file、reference。
indexed: true を設定できるのは、string、url、number、integer、boolean、datetime、select、reference、slug だけです。
フィールドの検証
生成されるコレクションのスキーマは、フィールドの型が対応している場合に、次の規則を認識します。
| 規則 | 使う型 |
|---|---|
min、max |
数値のフィールド |
minLength、maxLength、pattern |
文字列の形のフィールド |
options |
select と multiSelect |
subFields、minItems、maxItems |
repeater |
allowedMimeTypes |
メディアのフィールド |
validateSeed() は、validation や options のすべての規則を、深い部分まで型チェックするわけではありません。そのため、不正な規則がシードの検証に通り、あとでコレクションのスキーマを組み立てるときや、コンテンツを書き込むときに失敗する場合があります。
やさしい解説
WordPressでいえば、カスタム投稿タイプとその入力項目を、コードではなく1つのJSONファイルにまとめて書いておくイメージです。collections の1つ1つがコレクション(管理画面では「コンテンツタイプ」)で、その中の fields がフィールドです。slug はデータベースとAPIで使う名前なので、英小文字で始め、英小文字・数字・アンダースコアだけを使います。validateSeed() はすべての値を細かく確かめるわけではないため、検証に通っても、適用するときに失敗する場合があります。
タクソノミー
タクソノミーの定義では、対象にするコレクションを指定します。タームはサンプルデータで、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" }
]
}
]
}
タクソノミーは、シードの中だけの id、locale、translationOf を持てます。タームも同じプロパティを持てます。translationOf は、シードの中だけの別のIDを参照します。シードを適用するときは、参照先より後ろに並べる必要があります。
タームの parent は、同じロケールにある親のタームのスラッグです。階層を持たないタクソノミーで親を指定すると、警告が出て、その指定は無視されます。
バイライン
ルートの bylines は、表示用のクレジットを定義します。バイラインはサンプルデータで、includeContent: true が必要です。
{
"version": "1",
"bylines": [
{
"id": "byline-editor",
"slug": "alex-editor",
"displayName": "Alex Editor",
"isGuest": true
}
]
}
id はシードの中だけの値で、コンテンツのクレジットで使います。任意のプロパティは、bio、websiteUrl、isGuest、avatar です。
バイラインのアバターは、設定済みのストレージにすでにあるファイルを指します。
{
"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
}
}
バイラインのアバターのシードは、ストレージのキーに対応するメディアの行を作成するか、既存の行を再利用します。ファイルのアップロードやダウンロードはしません。
コンテンツ
content は、エントリーをコレクションのスラッグごとにまとめます。各エントリーには、シードの中だけの id と、data オブジェクトが必要です。ルートを持つコレクションでは、空でない 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" }
]
}
]
}
}
| プロパティ | 必須 | 動作 |
|---|---|---|
id |
はい | シードの中だけで使う参照用のID |
slug |
ルートを持つコレクションでは必須 | 公開側のスラッグで、競合を判定するキー |
status |
いいえ | published または draft。初期値は published |
data |
はい | コレクションのフィールドのスラッグをキーにした値 |
taxonomies |
いいえ | タクソノミー名と、タームのスラッグの配列の組 |
bylines |
いいえ | ルートのバイラインのIDを参照する、順番付きのクレジット |
locale |
いいえ | BCP 47のロケール。省略すると defaultLocale を通じて決まります |
translationOf |
いいえ | 同じコレクションにある、シードの中だけのコンテンツのID |
ルートを持つエントリーでは、シードの中だけの id はデータベース上の識別子ではありません。EmDashはデータベースのIDを作成し、あとの参照のために対応関係を記録します。routable: false のコレクションにあるスラッグのないエントリーでは、再適用しても同じ結果になるように、EmDashはシードの id を保存するIDとして使います。
読み込むときの entry.id はAstroのルートの識別子で、通常はスラッグです。保存されたデータベースのIDは entry.data.id です。
コンテンツの参照
data の中で $ref: の文字列を使うと、シードの中だけのコンテンツのIDを、作成されたデータベースのIDに置き換えられます。
{
"id": "event-opening",
"slug": "opening-night",
"data": {
"title": "Opening night",
"venue": "$ref:venue-main-hall"
}
}
参照先は、適用の処理のIDの対応表に入っているように、十分に前に並べる必要があります。解決できない $ref: の値は、元の文字列のまま残ります。validateSeed() はこれを拒否しません。
メディアの参照
コンテンツのデータで $media を使うと、URLのファイルをダウンロードし、指定したストレージのアダプターでアップロードし、メディアの行を作成して、そのオブジェクトをメディアのフィールドの値に置き換えます。
{
"featured_image": {
"$media": {
"url": "https://example.com/images/launch.jpg",
"filename": "launch.jpg",
"alt": "A product launch on stage",
"caption": "Launch event"
}
}
}
1回の適用の中では、同じURLへの参照が繰り返されると、解決済みのメディアの値を再利用します。シードのメディアの参照は、ローカルの 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 にシードのコンテンツのIDを指定します。参照先がない場合は、検証の警告が出て、コンテンツへの参照が解決されていないメニュー項目が作られます。メニューを適用するたびに、onConflict とは関係なく、そのメニューの既存の項目は削除され、作り直されます。
リダイレクト
リダイレクトには、サイト内の転送元と転送先のパスが必要です。
{
"version": "1",
"redirects": [
{
"source": "/old-path",
"destination": "/new-path",
"type": 308,
"enabled": true,
"groupName": "WordPress migration"
}
]
}
どちらのパスも、1つの / で始まる必要があります。プロトコル相対URL、パスをさかのぼるセグメント、改行は拒否されます。使えるステータスコードは、301、302、307、308 です。
ウィジェットエリア
ウィジェットエリアには、content、menu、component のウィジェットを入れます。
{
"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 }
}
]
}
]
}
コンテンツのウィジェットは、content にPortable Textを保存します。メニューのウィジェットには menuName が必要です。コンポーネントのウィジェットには componentId が必要で、props を渡せます。SeedWidget には settings プロパティはありません。
ウィジェットエリアを適用するたびに、onConflict とは関係なく、そのエリアの既存のウィジェットは削除され、作り直されます。
セクション
セクションには、再利用できるPortable Textのコンテンツを入れます。
{
"version": "1",
"sections": [
{
"slug": "newsletter-signup",
"title": "Newsletter signup",
"description": "Signup call to action",
"keywords": ["newsletter", "email"],
"source": "theme",
"content": []
}
]
}
セクションのスラッグには、英小文字、数字、ハイフンを使います。source は theme または import で、シードでは初期値が theme になります。セクションは構造のデータで、includeContent がfalseの場合でも適用されます。
ローカライズ
defaultLocale は、タクソノミー、ターム、メニュー、メニュー項目、コンテンツで省略されたロケールを補います。実行時のi18nの設定が有効な場合は、そちらが優先されます。
ローカライズしたタクソノミー、ターム、メニュー、メニュー項目、コンテンツは、シードの中だけの id と translationOf のフィールドを使います。適用の処理が翻訳のグループを解決できるように、元の項目を翻訳より前に置きます。翻訳したコンテンツのエントリーには locale を設定する必要があり、その translationOf には同じコレクションの別のエントリーを指定する必要があります。
プログラムからのシードの適用
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
| オプション | 初期値 | 現在の動作 |
|---|---|---|
includeContent |
false |
コンテンツのエントリー、バイライン、タクソノミーのタームを含めます |
onConflict |
"skip" |
対応しているものの競合時の動作。"skip"、"update"、"error" のいずれか |
storage |
なし | $media のURLをダウンロードするために必要なストレージのアダプター |
skipMediaDownload |
false |
$media のURLを外部のメディアの値のままにします |
mediaBasePath |
なし | 公開されている型にはありますが、現在の適用の処理では使われません |
プログラムから適用する場合、includeContent の初期値は false です。セットアップウィザードは、管理者がサンプルコンテンツについて選んだ内容を渡します。emdash seed のCLIは、--no-content を指定しない限り、初期状態でコンテンツを含めます。
競合したときの動作
onConflict は、シード全体に対するトランザクションのポリシーではありません。
- コレクション、フィールド、バイライン、コンテンツ、リダイレクト、セクションは、スキップ、更新、エラーの動作に対応しています。
- タクソノミーの定義とタームは、該当する競合時のモードに従います。
- 設定は常に適用されます。
- 既存のメニューは、メニューの行を残したまま、すべての項目を置き換えます。
- 既存のウィジェットエリアは、エリアの行を残したまま、すべてのウィジェットを置き換えます。
- コンテンツの競合は、コレクション、スラッグ、ロケールで照合します。ルートを持たないコレクションにあるスラッグのないエントリーは、シードのIDで照合します。
onConflict: "update" の場合、コンテンツのデータは置き換えられ、そのバイラインとタクソノミーの割り当ては、シードの内容に合わせて調整されます。既存のサイトに対して使う前に、コピーで更新のモードをテストします。
applySeed() は、コレクション、フィールド、タクソノミー、バイライン、メニュー、リダイレクト、ウィジェットエリア、セクション、設定、コンテンツ、メディアの件数を返します。
やさしい解説
WordPressでは、サイトの設定やメニューを管理画面で1つずつ変更します。シードの適用は、ファイルに書いた内容をまとめてデータベースに書き込む処理で、すでにあるデータとぶつかる場合の動作が種類ごとに決まっています。onConflict で、すでにあるものをスキップするか、更新するか、エラーにするかを選べますが、設定はいつも書き込まれ、メニューとウィジェットエリアは中身がいつも作り直されます。"update" ではコンテンツのデータも置き換えられるため、公式ドキュメントは、既存のサイトに使う前にコピーで試すよう求めています。
検証の動作
validateSeed() は { valid, errors, warnings } を返します。applySeed() はこれを呼び出し、エラーがある場合は Invalid seed file を投げます。
検証では、適用の処理が必要とする構造の規則を確かめます。たとえば次のものです。
- バージョンと、空でない
defaultLocale。 - コレクション、フィールド、タクソノミー、ターム、メニュー、ウィジェットエリア、セクション、バイライン、コンテンツの入れ物の形。
- 必須の名前、ラベル、ID、スラッグと、対応しているフィールドやウィジェットの型。
- それぞれの範囲の中での識別子の重複。
- インデックスを付けるフィールドの型と、
admin.listColumnsの参照。 - タクソノミーの親、コンテンツの翻訳、コンテンツのバイラインの参照、メニュー項目の要件。
- サイト内のリダイレクトのパスが安全であることと、ステータスコード。
エラーではなく警告になる条件もあります。たとえば、コレクションのないタクソノミー、階層を持たないタクソノミーでの親の指定、シードにないコンテンツを参照するメニューなどです。
検証では、すべての data の値がコレクションのフィールドに合っていることまでは確かめません。また、サイトの設定、フィールドの validation、フィールドの options、任意のPortable Textのブロック、コンポーネントのウィジェットのprops、コンテンツのデータの中の $ref: の参照先、リモートの $media を取得できるかどうかも、深い部分までは検証しません。有効なシードでも、スキーマの作成、コンテンツの検証、ネットワークからのダウンロード、ストレージへのアップロードの途中で失敗する場合があります。
エディターでの補助には $schema のURLを使い、適用する前に実行できる検証を実行します。
npx emdash seed seed/seed.json --validate
CLIのコマンド
競合したときの動作を明示して、ローカルのSQLiteデータベースにシードを適用します。
npx emdash seed seed/seed.json --database ./data.db --on-conflict skip
現在のローカルのモデルとすべてのコンテンツを、テンプレートのパスにエクスポートします。
npx emdash export-seed --database ./data.db --with-content=all > seed/seed.json
export-seed は、ローカルのSQLiteファイルに対して直接動きます。デプロイ済みのD1データベースの場合は、まずローカルのファイルにエクスポートします。結果をコミットする前に、エクスポートされた設定、コンテンツ、メディアの参照を確認します。
次のステップ
- テーマの作成:再利用できるAstroのテンプレートでシードを使う
- 運用中サイトのスキーマ変更:デプロイ済みの既存のサイトのモデルを更新する
- CLIリファレンス:データベースとエクスポートのオプション