CMS后端前端插件系统【免费下载链接】emdashEmDash is a full-stack TypeScript CMS based on Astro; the spiritual successor to WordPress项目地址https://gitcode.com/gh_mirrors/emdas/emdash点击查看免费下载导读Seed 文件seed.json是 EmDash 站点初始化与复制的核心载体一份 DB 无关的 JSON 文档即可完整声明站点的内容模型Collections/Fields、分类法Taxonomies、导航菜单、Widget 区域、可复用内容块、作者署名、站点设置以及可选的演示内容。本文基于 EmDash 仓库中的官方参考文档与源码实现系统讲解 Seed 文件的完整结构与每个配置节Section的字段语义并结合 seed 类型定义、applySeed 引擎、validateSeed 校验器 与 export-seed 命令 的源码说明种子如何被应用、校验与导出。读完本文你将掌握编写、验证、应用与导出 EmDash Seed 文件的完整实战能力。Seed 文件是什么位置、加载时机与整体结构Seed 文件seed/seed.json定义了站点的整个 Schema和可选的演示内容。它会在构建时被内联inlined进产物并在数据库为空且设置向导setup wizard尚未完成时于首次请求自动应用。Seed 文件的实际存放位置有三种按优先级查找.emdash/seed.jsonpackage.json中的emdash.seed字段指向的路径seed/seed.json以官方模板为例marketing 模板的 package.json 中声明了emdash: { seed: seed/seed.json }对应的实体文件即 templates/marketing/seed/seed.json。仓库内 templates 下的 blog、portfolio、starter 等全部模板都遵循这一约定。顶层结构总览一份 Seed 文件的顶层骨架如下{ $schema: https://emdashcms.com/seed.schema.json, version: 1, meta: { name: My Site, description: A description of this site, author: Author Name }, settings: { ... }, collections: [ ... ], taxonomies: [ ... ], menus: [ ... ], widgetAreas: [ ... ], sections: [ ... ], bylines: [ ... ], content: { ... } }对照源码 SeedFile 接口除了上述字段外还有两个值得注意的扩展version当前仅支持字符串1校验器会拒绝其他版本见 validate.ts 中的版本检查。defaultLocale声明种子中省略显式locale的本地化行的默认语言。它的作用非常关键export-seed在 CLI 环境下运行、读不到 Astro 运行时的 i18n 配置apply阶段也不在运行时内因此省略的 locale 默认会被回填为en。通过defaultLocale自述默认语言可以让一个非en的单语言项目在导出 → 导入往返后保持语言不变源码注释中引用了 issue #1421。meta仅作元信息展示不参与数据写入。Collections内容类型与数据库表映射Collections 定义内容类型。每个 Collection 对应数据库中的一张表表名规则为ec_{slug}——例如 slug 为posts的 Collection 会生成ec_posts表。这一映射在 apply.ts 的表结构创建逻辑 与 export-seed.ts 的按集合导出逻辑 中均有体现。一个 Collection 定义示例{ slug: posts, label: Posts, labelSingular: Post, supports: [drafts, revisions, search, seo], commentsEnabled: true, fields: [ ... ] }Collection Supportssupports数组声明该内容类型启用的内置能力SupportDescriptiondraftsDraft/published workflowrevisionsRevision historysearchFull-text search indexingseoSEO meta fields in admin从 SeedCollection 类型定义 看supports实际还支持preview预览与scheduling定时发布两个取值。Collection 的其他可选属性description/icon管理后台展示用。admin后台列表列配置admin.listColumns等。urlPattern条目的 URL 模式。routable是否要求 slug 才能发布默认true设为false可让条目在导出时省略 slug。hidden从后台侧边栏与快捷操作中隐藏该集合但仍可通过 API、MCP、插件钩子与直接/content/:collectionURL 访问。sortOrder后台侧边栏中的显式排序位置升序。group后台侧边栏分组同组集合共享一个文件夹。commentsEnabled是否启用评论默认为 false。editLocking打开条目时是否获取编辑锁默认为 true。titleField/dateField分别指定后台列表标题列与日期列所对应的字段 slug。Slug 规则Collection slug 需满足小写字母 数字 下划线/^[a-z][a-z0-9_]*$/最大 63 个字符不能与保留 slug 冲突字段系统类型、定义与常见模式Field Types字段类型决定数据库列类型、运行时形状与后台控件。下表是完整的类型映射源自官方参考文档并与 FieldType 一致TypeColumn typeRuntime shapeNotesstringTEXTstringSingle line texttextTEXTstringMulti-line text (textarea)numberREALnumberFloating pointintegerINTEGERnumberWhole numbersbooleanINTEGERbooleanStored as 0/1datetimeTEXTDateISO 8601 string in DBimageTEXT{ id, src?, alt?, width?, height? }Object, not a stringreferenceTEXTstring(ID)Reference to another entryportableTextJSONPortableTextBlock[]Rich text as structured JSONjsonJSONanyArbitrary JSON data两个最容易被误用的类型image的运行时值是对象而非字符串直接塞 URL 会破坏数据形状参见下文验证规则中的常见错误。portableText是结构化的富文本 JSON 数组不是 HTML 字符串。Field Definition 与扩展属性一个字段定义的最小形态{ slug: title, label: Title, type: string, required: true, searchable: true }字段可以拥有SeedField 定义slug必填—— 字段标识符label必填—— 后台展示标签type必填—— 上表中的一种类型required—— 校验必填searchable—— 是否纳入全文搜索索引unique—— 唯一约束indexed—— 是否建索引部分类型不允许索引校验器会拦截defaultValue—— 默认值validation—— 自定义校验规则widget—— 后台使用的输入控件options—— 控件/字段的附加选项常见字段模式博客文章Blog postfields: [ { slug: title, label: Title, type: string, required: true, searchable: true }, { slug: featured_image, label: Featured Image, type: image }, { slug: content, label: Content, type: portableText, searchable: true }, { slug: excerpt, label: Excerpt, type: text } ]作品集项目Portfolio projectfields: [ { slug: title, label: Title, type: string, required: true, searchable: true }, { slug: featured_image, label: Featured Image, type: image, required: true }, { slug: client, label: Client, type: string }, { slug: year, label: Year, type: string }, { slug: summary, label: Summary, type: text, searchable: true }, { slug: content, label: Content, type: portableText, searchable: true }, { slug: gallery, label: Gallery, type: json }, { slug: url, label: Project URL, type: string } ]页面Page最小化fields: [ { slug: title, label: Title, type: string, required: true, searchable: true }, { slug: content, label: Content, type: portableText, searchable: true } ]Taxonomies分类与标签系统Taxonomies 是挂载到 Collections 上的标签/分类体系语义上对应 WordPress 的分类法category 为层级结构tag 为扁平列表。{ name: category, label: Categories, labelSingular: Category, hierarchical: true, collections: [posts], terms: [ { slug: development, label: Development }, { slug: design, label: Design } ] }hierarchical: true—— 树形结构类似 WordPress 分类目录hierarchical: false—— 扁平列表类似 WordPress 标签collections—— 该分类法作用于哪些集合terms—— 预置的词条term从 SeedTaxonomy / SeedTaxonomyTerm 的类型定义看词条还支持description词条描述。parent父词条的 slug用于在层级分类法中构建父子关系。多语言导出时每个词条可带locale与translationOf指向同组锚点词条的 seed 内 id翻译关系在导出时按翻译组自动生成。Menus导航菜单导航菜单由后台 UI 管理Seed 中负责预置结构与初始条目{ name: primary, label: Primary Navigation, items: [ { type: custom, label: Home, url: / }, { type: custom, label: About, url: /pages/about }, { type: custom, label: Posts, url: /posts } ] }菜单项类型custom—— 任意 URL内容引用type为 page/post 等时使用refcollection在渲染时解析SeedMenuItem 还支持target_blank/_self、titleAttr、cssClasses、children递归子菜单等属性多语言导出时同样带locale/translationOf且通过ref指向内容在 seed 中的 id、collection指明目标集合。一个可参考的真实示例是 marketing 模板的菜单节其中同时定义了主菜单与多个页脚菜单footer_product、footer_company、footer_support。Widget Areas组件挂载区域Widget Areas 是命名区域编辑器可在其中添加可配置的 Widget{ name: sidebar, label: Sidebar, description: Widget area displayed on single post pages, widgets: [ { type: component, componentId: core:search, title: Search }, { type: component, componentId: core:categories, title: Categories }, { type: component, componentId: core:tags, title: Tags }, { type: component, componentId: core:recent-posts, title: Recent Posts, settings: { count: 5, showDate: true } }, { type: component, componentId: core:archives, title: Archives, settings: { type: monthly, limit: 6 } }, { type: content, title: About, content: [ { _type: block, style: normal, children: [{ _type: span, text: Some rich text content. }] } ] } ] }Widget 类型TypeDescriptionKey fieldscontentRich text (Portable Text)contentmenuNavigation menumenuNamecomponentCore or custom componentcomponentId,settings对照 SeedWidget 类型组件 widget 的配置字段在源码中实际命名为props组件属性对象与文档示例中的settings语义一致、均为组件可接收的配置对象。export-seed在导出组件 widget 时会读取数据库中的component_props并序列化为props见 export-seed.ts 的 widget 导出。核心 Widget 组件core:search—— 搜索表单core:categories—— 带计数的分类列表core:tags—— 标签云core:recent-posts—— 最新文章列表core:archives—— 按月归档链接Sections可复用内容块Sections 是可复用的内容块编辑器可在编辑器中通过/section斜杠命令插入语义对应 WordPress 的 pattern/可复用块{ slug: newsletter-signup, title: Newsletter Signup, description: A call-to-action block for newsletter subscriptions, keywords: [newsletter, subscribe, email, cta], source: theme, content: [ { _type: block, style: h3, children: [{ _type: span, text: Stay in the loop }] }, { _type: block, style: normal, children: [{ _type: span, text: Get notified when new posts are published. }] } ] }其中keywords用于编辑器内的搜索匹配source取值为theme种子提供或importWordPress 导入产生见 SeedSection 定义。Bylines作者署名Bylines 是命名作者档案独立于用户账号体系用户账号管登录权限byline 管文章署名展示{ id: byline-editorial, slug: emdash-editorial, displayName: EmDash Editorial }客座作者guest署名{ id: byline-guest, slug: guest-contributor, displayName: Guest Contributor, isGuest: true }SeedByline 还支持bio、websiteUrl以及通过avatar挂载已存储的头像媒体此时不会像内容$media那样触发下载而是由调用方提供已存在于存储中的storageKey常见于配合媒体迁移一起播种的场景。内容条目通过bylines: [{ byline: byline-editorial }]引用byline指向根级bylines[]中定义的 seed 内 id可选roleLabel标注角色。Settings站点级设置站点级设置settings: { title: My Blog, tagline: Thoughts on building for the web }可用键title、tagline、logo、favicon、social、timezone、dateFormat。在数据库中它们以site:前缀的 options 存储见 export-seed.ts 的 SETTINGS_PREFIX。Content演示内容与特殊语法示例内容按 Collection slug 分组组织content: { posts: [ { id: post-1, slug: hello-world, status: published, data: { title: Hello World, excerpt: My first post., featured_image: { $media: { url: https://images.unsplash.com/photo-xxx?w1200h800fitcrop, alt: Description of image, filename: hello-world.jpg } }, content: [ { _type: block, style: normal, children: [{ _type: span, text: This is the body text. }] } ] }, bylines: [ { byline: byline-editorial } ], taxonomies: { category: [development], tag: [webdev, opinion] } } ], pages: [ { id: about, slug: about, status: published, data: { title: About, content: [ { _type: block, style: normal, children: [{ _type: span, text: About this site. }] } ] } } ] }SeedContentEntry 还支持localeBCP 47 语言代码省略时回退到defaultLocale。translationOf指向同集合内另一条目的 seed 内 id用于声明翻译关系。媒体引用$media 语法对image字段使用$media语法EmDash 会下载并存储该图片featured_image: { $media: { url: https://images.unsplash.com/photo-xxx?w1200h800fitcrop, alt: Description, filename: my-image.jpg } }从源码看applySeed 的 $media 处理流程 依次是① 从 URL 下载图片 → ② 上传到配置的存储适配器 → ③ 在数据库中创建 media 记录 → ④ 将$media对象替换为真正的字段值。filename缺省时取 URL 文件名或自动生成还可选caption媒体说明。若不需要下载例如仅作外链展示可直接用字符串 URLfeatured_image: https://images.unsplash.com/photo-xxx?w1200另外 SeedApplyOptions.skipMediaDownload 可在整体层面跳过下载此时$media引用会被解析为一个src直接指向原外部 URL、provider为external的 MediaValue无需存储适配器适合 playground/演示环境。引用字段$ref 语法使用$ref:id格式引用其他条目author: $ref:byline-editorialseed 内的 id如byline-editorial、post-1由 applySeed 维护的 seedIdMap 在应用时解析为真实的数据库 id因此引用顺序很关键详见下文应用顺序。Portable Text结构化富文本portableText类型字段的值是块block数组[ { _type: block, style: normal, children: [{ _type: span, text: A paragraph. }] }, { _type: block, style: h2, children: [{ _type: span, text: A heading }] }, { _type: block, style: blockquote, children: [{ _type: span, text: A quote. }] } ]块样式Block stylesnormal、h1-h6、blockquote。行内标记粗体、斜体、链接通过marks数组声明{ _type: block, style: normal, children: [ { _type: span, text: This is }, { _type: span, text: bold, marks: [strong] }, { _type: span, text: and }, { _type: span, text: italic, marks: [em] } ] }草稿内容设置status: draft即可创建未发布内容{ id: post-draft, slug: work-in-progress, status: draft, data: { ... } }应用 Seeds触发时机、应用顺序与冲突策略Seed 在构建时被内联并在数据库为空且设置向导未完成时于首次请求自动应用。已有数据永远不会被覆盖。源码 applySeed 是幂等idempotent的可安全重复执行它遵循固定的应用顺序这对外键与引用解析至关重要Site settings站点设置Collections Fields集合与字段Taxonomy definitions Terms分类法定义与词条Content内容 —— 先于菜单这样菜单引用才能解析Menus Menu items菜单与菜单项 —— 此时可解析内容引用Redirects重定向Widget areas WidgetsWidget 区域与组件冲突策略 onConflict应用选项onConflict默认skip控制遇到已存在数据时的行为SeedApplyOptionsskip只创建缺失项站点设置按 key 处理仅创建缺失的 keyupdate覆盖种子中提供的 key / 更新已有集合与字段error遇到第一个已存在项即报错不会回滚种子中先前已创建的项includeContent默认falseSchema 与结构集合、字段、分类法定义、菜单、设置、重定向、Widget 区域、Sections总是会应用只有内容条目、byline、分类法词条属于示例数据范畴需要显式开启。校验规则常见的种子错误校验validation在应用时执行validateSeed 会返回valid、errors、warnings。以下是最常被捕获的错误类型图片字段使用裸 URL应使用$media引用字段使用裸 ID应使用$ref:idPortableText 不是数组或缺少_type类型不匹配string 与 number 混用等此外校验器还会检查顶层必须是对象、必须有version字段且版本受支持、defaultLocale必须是去空白后的非空字符串、集合 slug 必填且不可重复、routable/editLocking/indexed必须是布尔、indexed不得用于不可索引的类型、分类法必须有name/label/hierarchical等。如果种子无效首次请求会失败并在日志中记录错误修复后需要重启开发服务器。导出 Seeds把现有站点变成种子使用emdashCLI 的export-seed命令可以把现有数据库导出为种子文件npx emdash export-seed # 仅 Schema npx emdash export-seed --with-content # Schema 全部内容 npx emdash export-seed --with-contentposts,pages # 指定集合从 export-seed.ts 的实现 可以提炼出几个值得注意的工程细节前置条件数据库 schema 必须与当前 EmDash 版本一致——存在未执行的迁移pending migrations时命令会提示先运行emdash migrate存在由更新版本迁移的数据库时会被拒绝导出。输出约定种子文档直接写入 stdout便于emdash export-seed seed.json重定向诊断信息走 stderr避免污染 JSON 输出。导出内容转换image字段会转回$media语法、reference字段转回$ref:{seedId}语法$ref指向的 seed id 由本次导出统一分配而不是数据库行 id因为导入时会重新编号。引用顺序重排reference的目标集合会被排到引用者之前保证导入时$ref可解析存在引用环时环上集合保持原顺序而不中断导出。多语言感知CLI 运行在 Astro 运行时之外导出器通过统计 i18n 表内去重后的 locale 数量判断项目是否多语言单语言导出使用裸 id 并自述defaultLocale多语言导出为菜单/分类/条目生成locale与translationOf后缀避免 seed id 冲突。settings 导出数据库中以site:前缀存储的 options 会被剥掉前缀还原为settings下的普通键。结语Seed 文件是 EmDash 站点从零到一、以及站点间迁移复制的最小可移植单元collections定义内容模型并映射到ec_*表taxonomies/menus/widgetAreas/sections/bylines声明站点结构settings与content注入初始配置与演示数据。配合$media、$ref与 Portable Text 三种特殊语法一套种子即可完整复刻一个带媒体、带引用、带富文本的站点。理解 applySeed 的幂等应用顺序、validateSeed 的校验拦截与export-seed的双向转换能力你就能安全地在模板、演示环境与生产站点之间流转数据而不必担心覆盖已有内容。赞分享CMS后端前端插件系统【免费下载链接】emdashEmDash is a full-stack TypeScript CMS based on Astro; the spiritual successor to WordPress项目地址https://gitcode.com/gh_mirrors/emdas/emdash点击查看免费下载相关推荐EmDash CMS 的 Schema 与 Seed 文件完全指南从站点结构声明到内容灌入EmDash CMS 的 Schema 与 Seed 文件完全指南从站点结构声明到内容灌入 导读 本文是 EmDash基于 Astro 的全栈 TypeScCMS后端前端插件系统EmDash Schema 与 Seed 文件完全指南用 seed.json 定义内容模型与示例数据EmDash Schema 与 Seed 文件完全指南用 seed.json 定义内容模型与示例数据 导读 本文基于 EmDash 官方技能文档 schemCMS后端前端插件系统EmDash 建站指南Schema 与 Seed 文件完全解析EmDash 建站指南Schema 与 Seed 文件完全解析 导读 在 EmDash基于 Astro 的全栈 TypeScript CMS中 seedCMS后端前端插件系统创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
