building-emdash-site
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseBuilding an EmDash Site
构建 EmDash 站点
EmDash is a CMS built on Astro. It stores schema in the database (not in code), serves content via live content collections, and provides a full admin UI at . Sites are standard Astro projects with the integration.
/_emdash/adminemdashEmDash 是一个基于 Astro 构建的 CMS。它将 schema 存储在数据库中(而不是代码中),通过实时 content collections 提供内容,并在 提供完整的管理 UI。站点是带有 integration 的标准 Astro 项目。
/_emdash/adminemdashCommon Gotchas
常见陷阱
These are the things that silently break sites. Know them before you start.
-
Image fields are objects, not strings.is
post.data.featured_image. Writing{ id, src, alt }renders<img src={post.data.featured_image} />. Use[object Object]from<Image image={post.data.featured_image} />."emdash/ui" -
vs
entry.idare different things.entry.data.idis the slug, prefixed with the locale (entry.id) for locales whose URLs are prefixed (use in URLs;en/my-postis the bare slug).entry.data.slugis the database ULID (use forentry.data.id,getEntryTerms, and other API calls that need the real ID). Mixing them up causes silent empty results.Comments -
Taxonomy names must match the seed exactly. If your seed defines, you must query
"name": "category"-- notgetTerm("category", slug). Wrong name = empty results, no error."categories" -
Register cache hints when Astro's cache is enabled. Content queries return a; pass it to
cacheHint. UseAstro.cache.set(cacheHint),getSiteSettingsWithCacheHint(),getMenuWithCacheHint(), andgetTaxonomyTermsWithCacheHint()when a cached route renders those values.getWidgetAreaWithCacheHint() -
Nofor CMS content. EmDash content is dynamic. Pages must be server-rendered (
getStaticPathsinoutput: "server").astro.config.mjs
以下情况会悄无声息地破坏站点。开始之前请先了解它们。
-
图片字段是对象,而不是字符串。是
post.data.featured_image。编写{ id, src, alt }会渲染成<img src={post.data.featured_image} />。请使用从[object Object]导入的"emdash/ui"。<Image image={post.data.featured_image} /> -
与
entry.id是不同的东西。entry.data.id是带 locale 前缀的 slug(对于 URL 带前缀的 locale,格式为entry.id;用于 URL;en/my-post是不带前缀的 slug)。entry.data.slug是数据库 ULID(用于entry.data.id、getEntryTerms以及其他需要真实 ID 的 API 调用)。混用二者会悄无声息地产生空结果。Comments -
Taxonomy 名称必须与 seed 完全一致。 如果 seed 定义了,则必须查询
"name": "category",而不是getTerm("category", slug)。名称错误会得到空结果,且不会报错。"categories" -
启用 Astro 的 cache 时,必须注册 cache hints。 内容查询会返回;将其传给
cacheHint。当缓存路由渲染这些值时,使用Astro.cache.set(cacheHint)、getSiteSettingsWithCacheHint()、getMenuWithCacheHint()和getTaxonomyTermsWithCacheHint()。getWidgetAreaWithCacheHint() -
CMS 内容不要使用。 EmDash 内容是动态的。页面必须进行服务端渲染(在
getStaticPaths中设置astro.config.mjs)。output: "server"
File Structure
文件结构
Every EmDash site has these key files:
my-site/
├── astro.config.mjs # Astro config with emdash() integration
├── src/
│ ├── live.config.ts # EmDash loader registration (boilerplate)
│ ├── pages/ # Astro pages (all server-rendered)
│ ├── layouts/ # Layout components
│ └── components/ # Reusable components
├── seed/
│ └── seed.json # Schema + demo content
├── emdash-env.d.ts # Generated types (from `emdash types`)
└── package.json每个 EmDash 站点都有以下关键文件:
my-site/
├── astro.config.mjs # Astro config with emdash() integration
├── src/
│ ├── live.config.ts # EmDash loader registration (boilerplate)
│ ├── pages/ # Astro pages (all server-rendered)
│ ├── layouts/ # Layout components
│ └── components/ # Reusable components
├── seed/
│ └── seed.json # Schema + demo content
├── emdash-env.d.ts # Generated types (from `emdash types`)
└── package.jsonWorkflow
工作流
1. Configure the project
1. 配置项目
Read references/configuration.md for , , deployment targets (Node vs Cloudflare), and type generation.
astro.config.mjslive.config.ts阅读 references/configuration.md,了解 、、部署目标(Node 与 Cloudflare)以及类型生成。
astro.config.mjslive.config.ts2. Design the schema
2. 设计 schema
Read references/schema-and-seed.md for collection definitions, field types, taxonomies, menus, widget areas, sections, bylines, and the complete seed file format.
阅读 references/schema-and-seed.md,了解 collection 定义、字段类型、taxonomies、menus、widget areas、sections、bylines,以及完整的 seed 文件格式。
3. Build the pages
3. 构建页面
Read references/querying-and-rendering.md for content queries, Portable Text rendering, the Image component, visual editing attributes, caching, and common page patterns (list, detail, taxonomy archive, RSS, search, 404).
阅读 references/querying-and-rendering.md,了解内容查询、Portable Text 渲染、Image 组件、可视化编辑属性、缓存,以及常见页面模式(列表、详情、taxonomy 归档、RSS、搜索、404)。
4. Wire up site features
4. 接入站点功能
Read references/site-features.md for site settings, navigation menus, taxonomies, widget areas, search, SEO meta, comments, and page contributions.
阅读 references/site-features.md,了解站点设置、导航菜单、taxonomies、widget areas、搜索、SEO meta、comments 和页面贡献。
5. Create the seed file
5. 创建 seed 文件
Write with collections, fields, taxonomies, menus, widgets, and sample content.
seed/seed.json编写 ,其中包含 collections、fields、taxonomies、menus、widgets 和示例内容。
seed/seed.json6. Run and verify
6. 运行并验证
bash
pnpm dev # Start the Astro dev serverThe runtime runs pending migrations on the first request and, before setup is completed, applies the seed's schema and structure once. Sample content needs an explicit step; see Applying Seeds. The Astro integration generates when the server starts. The admin UI is at .
emdash-env.d.tshttp://localhost:4321/_emdash/adminbash
pnpm dev # Start the Astro dev server运行时会在首次请求时执行待处理的 migration,并在 setup 完成前应用一次 seed 的 schema 和结构。示例内容需要显式应用;请参阅应用 Seeds。服务器启动时,Astro integration 会生成 。管理 UI 位于 。
emdash-env.d.tshttp://localhost:4321/_emdash/adminQuick API Cheat Sheet
快速 API 速查表
typescript
// Content (entries have .data.byline and .data.bylines eagerly loaded)
import { getEmDashCollection, getEmDashEntry } from "emdash";
const { entries, nextCursor, cacheHint } = await getEmDashCollection("posts", {
limit: 10,
cursor,
orderBy: { published_at: "desc" },
});
const { entry: post, cacheHint: entryCacheHint } = await getEmDashEntry("posts", slug);
// Site features
import {
getSiteSettings,
getSiteSettingsWithCacheHint,
getMenu,
getMenuWithCacheHint,
getTaxonomyTerms,
getTaxonomyTermsWithCacheHint,
getTerm,
getEntryTerms,
getEntriesByTerm,
getWidgetArea,
getWidgetAreaWithCacheHint,
search,
getSection,
getSeoMeta,
} from "emdash";
// Bylines (standalone queries -- usually not needed since entries have bylines attached)
import { getByline, getBylineBySlug } from "emdash";
// UI components
import {
PortableText,
Blocks,
defineBlockComponents,
Image,
WidgetArea,
EmDashHead,
EmDashBodyStart,
EmDashBodyEnd,
} from "emdash/ui";
import { Comments, CommentForm } from "emdash/ui/comments";
import LiveSearch from "emdash/ui/search";
// Page context (for plugin contributions)
import { createPublicPageContext } from "emdash/page";typescript
// Content (entries have .data.byline and .data.bylines eagerly loaded)
import { getEmDashCollection, getEmDashEntry } from "emdash";
const { entries, nextCursor, cacheHint } = await getEmDashCollection("posts", {
limit: 10,
cursor,
orderBy: { published_at: "desc" },
});
const { entry: post, cacheHint: entryCacheHint } = await getEmDashEntry("posts", slug);
// Site features
import {
getSiteSettings,
getSiteSettingsWithCacheHint,
getMenu,
getMenuWithCacheHint,
getTaxonomyTerms,
getTaxonomyTermsWithCacheHint,
getTerm,
getEntryTerms,
getEntriesByTerm,
getWidgetArea,
getWidgetAreaWithCacheHint,
search,
getSection,
getSeoMeta,
} from "emdash";
// Bylines (standalone queries -- usually not needed since entries have bylines attached)
import { getByline, getBylineBySlug } from "emdash";
// UI components
import {
PortableText,
Blocks,
defineBlockComponents,
Image,
WidgetArea,
EmDashHead,
EmDashBodyStart,
EmDashBodyEnd,
} from "emdash/ui";
import { Comments, CommentForm } from "emdash/ui/comments";
import LiveSearch from "emdash/ui/search";
// Page context (for plugin contributions)
import { createPublicPageContext } from "emdash/page";Plugins
Plugins
EmDash supports plugins for extending the CMS with hooks, storage, settings, admin UI, API routes, and custom Portable Text block types. Consider a plugin when you need to:
- React to content lifecycle events (e.g., send a notification on publish, sync to an external service)
- Add custom admin pages or dashboard widgets
- Add reusable behavior or custom Portable Text types (e.g., embedded maps or code playgrounds)
- Provide a reusable service (e.g., analytics, forms, comments via a third-party provider)
Plugins are registered in :
astro.config.mjsjavascript
emdash({
database: sqlite({ url: "file:./data.db" }),
storage: local({ directory: "./uploads", baseUrl: "/_emdash/api/media/file" }),
plugins: [myPlugin()],
}),To build a plugin, load the skill (in ). It covers plugin anatomy, hooks, storage, admin UI, API routes, Portable Text blocks, capabilities, and the full API.
creating-plugins.agents/skills/creating-plugins/definePlugin()EmDash 支持通过 hooks、storage、settings、admin UI、API routes 和自定义 Portable Text block types 扩展 CMS。出现以下需求时,可以考虑使用 plugin:
- 响应内容生命周期事件(例如,在发布时发送通知、同步到外部服务)
- 添加自定义管理页面或 dashboard widgets
- 添加可复用行为或自定义 Portable Text 类型(例如,嵌入式地图或代码 playground)
- 提供可复用服务(例如,analytics、forms,或通过第三方提供商实现 comments)
Plugins 在 中注册:
astro.config.mjsjavascript
emdash({
database: sqlite({ url: "file:./data.db" }),
storage: local({ directory: "./uploads", baseUrl: "/_emdash/api/media/file" }),
plugins: [myPlugin()],
}),要构建 plugin,请加载 skill(位于 )。它涵盖 plugin 结构、hooks、storage、admin UI、API routes、Portable Text blocks、capabilities,以及完整的 API。
creating-plugins.agents/skills/creating-plugins/definePlugin()Reference Documents
参考文档
| File | Contents |
|---|---|
| references/configuration.md | Project setup, astro.config, live.config, deployment, types |
| references/schema-and-seed.md | Collections, fields, taxonomies, menus, widgets, seed format |
| references/querying-and-rendering.md | Content APIs, PortableText, Image, caching, page patterns |
| references/site-features.md | Settings, menus, widgets, search, SEO, comments, page contributions |
| 文件 | 内容 |
|---|---|
| references/configuration.md | 项目设置、 |
| references/schema-and-seed.md | Collections、fields、taxonomies、menus、widgets、seed 格式 |
| references/querying-and-rendering.md | 内容 APIs、PortableText、Image、缓存、页面模式 |
| references/site-features.md | 设置、menus、widgets、搜索、SEO、comments、页面贡献 |