原生插件是 EmDash 导入到与 Astro 站点同一进程中的 npm 包。本教程创建一个记录内容保存的插件,将其安装到站点,并在 astro.config.mjs 中注册。
当插件需要进程内功能(如 React 管理组件、Astro 渲染组件或受信任的页面片段)时,使用原生格式。若钩子、路由、存储与 Block Kit 已覆盖该功能,请从沙箱插件开始。选择插件格式 比较了两种格式。
前提条件
从使用 pnpm 且能运行开发服务器的 EmDash 站点开始。站点必须已依赖 emdash,它提供下文使用的 emdash 命令。
命令将站点目录称为 my-emdash-site,并在其旁创建 plugin-activity。将 my-emdash-site 替换为你的站点目录名。
创建并注册包
-
在站点旁搭建原生包。
pnpm exec emdash plugin init --native --name @example/plugin-activity --dir ../plugin-activity命令在创建插件 ID 时会移除 npm 作用域。包名为
@example/plugin-activity,插件 ID 为plugin-activity。 -
安装包依赖。
cd ../plugin-activity pnpm install -
将生成的
src/index.ts替换为内容保存钩子。import { definePlugin } from "emdash"; import type { PluginDescriptor } from "emdash"; export interface ActivityPluginOptions { logUpdates?: boolean; } export function activityPlugin( options: ActivityPluginOptions = {}, ): PluginDescriptor<ActivityPluginOptions> { return { id: "plugin-activity", version: "0.1.0", format: "native", entrypoint: "@example/plugin-activity", options, }; } export function createPlugin(options: ActivityPluginOptions = {}) { return definePlugin({ id: "plugin-activity", version: "0.1.0", capabilities: ["content:read"], hooks: { "content:afterSave": async (event, ctx) => { if (!event.isNew && options.logUpdates === false) return; ctx.log.info("Content saved", { collection: event.collection, contentId: event.content.id, isNew: event.isNew, }); }, }, }); } export default createPlugin;content:afterSave需要content:read能力。缺少该能力时 EmDash 会跳过该钩子。 -
构建包。
pnpm build -
将本地包安装到站点。
cd ../my-emdash-site pnpm add ../plugin-activity -
在 EmDash 集成中注册描述符工厂。
import { defineConfig } from "astro/config"; import emdash from "emdash/astro"; import { activityPlugin } from "@example/plugin-activity"; export default defineConfig({ integrations: [ emdash({ plugins: [activityPlugin({ logUpdates: true })], }), ], });原生描述符属于
plugins,不属于sandboxed。EmDash 会拒绝sandboxed数组中的原生描述符。 -
启动站点并在管理面板中保存一条条目。
pnpm dev服务器日志包含带有集合、内容 ID 以及条目是否新建的
Content saved。
描述符与运行时边界
包导出有两项工作。EmDash 在不同阶段使用各自:
- 描述符工厂
activityPlugin()在 Astro 评估其配置时运行。它返回可序列化的构建时元数据:id、version、format、entrypoint与options。React 与 Astro 入口也属于此描述符。 - 具名
createPlugin()导出在 EmDash 初始化时运行。EmDash 从entrypoint导入它,传入序列化的options,并期望从definePlugin()得到已解析的插件。
具名 createPlugin 导出是必需的。默认导出可能对包使用者有用,但 EmDash 的原生加载器按名称导入 createPlugin。
在描述符与 definePlugin() 中保持 id 与 version 相同。使用无作用域、kebab-case 的插件 ID,如 plugin-activity;将 npm 作用域保留在包名与 entrypoint 中。这样 ID 可作为 API 路由 URL 中的单一插件段使用。
插件标识与版本 列出可接受的 ID 与版本形式。
运行时行为属于 definePlugin():
capabilities与allowedHostsstoragehooks与routesadmin设置、页面、小组件与 Portable Text 声明
描述符携带 Astro 必须在构建时导入或暴露的静态条目。专题指南说明哪些管理字段需要匹配的描述符与运行时声明。
原生路由处理程序
原生路由处理程序接收一个 RouteContext。它将已验证输入与请求数据与常规 PluginContext 合并:
routes: {
status: {
permission: "plugins:read",
handler: async (ctx) => ({
pluginId: ctx.plugin.id,
callerId: ctx.user?.id ?? null,
}),
},
},
等效的沙箱处理程序以两个参数接收 (routeCtx, ctx)。身份验证、权限、输入 schema 与路由 URL 在其他方面遵循共享的 API 路由 约定。
当原生路由声明 request.body 时,用 definePluginRoute() 包装它;该辅助函数从 body 模式推断
ctx.input。带 response: "raw" 的原生路由返回 pluginResponse()。
从 emdash 导入这两个辅助函数。共享 API 路由指南列出 body 模式、限制、响应策略与兼容性默认值。
添加另一个表面
- React 管理页面与小组件 涵盖设置、自定义页面、仪表盘小组件、编辑器面板与列表列。
- Portable Text 渲染组件 为插件块注册 Astro 组件。
- 页面片段 向公开页面添加受信任的脚本或 HTML。
- 分发原生插件 为 npm 打包构建与源入口。