原生插件是安装在宿主项目中并在 astro.config.mjs 中注册的 npm 包。包需要为其描述符与 createPlugin() 构建的服务器入口。若还附带 React 或 Astro 组件,请将它们导出为单独的源入口,以便宿主能为正确环境编译它们。
包布局
以下布局将服务器运行时与浏览器和 Astro 源码分开:
plugin-activity/
├── src/
│ ├── index.ts
│ ├── admin/
│ │ ├── index.tsx
│ │ └── ActivityPage.tsx
│ └── astro/
│ ├── index.ts
│ └── ActivityBlock.astro
├── dist/
│ ├── index.mjs
│ └── index.d.mts
├── package.json
├── tsconfig.json
└── README.md
dist/ 是生成的。请在已发布的 tarball 中保留 src/admin/ 与 src/astro/,因为宿主的 Vite 与 Astro 构建必须处理这些入口。
包导出
以下 package.json 构建服务器入口并发布全部三个入口:
{
"name": "@example/plugin-activity",
"version": "0.1.0",
"type": "module",
"main": "./dist/index.mjs",
"exports": {
".": {
"types": "./dist/index.d.mts",
"import": "./dist/index.mjs"
},
"./admin": "./src/admin/index.tsx",
"./astro": "./src/astro/index.ts"
},
"files": ["dist", "src/admin", "src/astro"],
"scripts": {
"build": "tsdown src/index.ts --format esm --dts --clean",
"dev": "tsdown src/index.ts --format esm --dts --watch",
"typecheck": "tsc --noEmit",
"prepublishOnly": "pnpm typecheck && pnpm build"
},
"peerDependencies": {
"@cloudflare/kumo": "*",
"@emdash-cms/admin": "*",
"@lingui/core": "*",
"@lingui/react": "*",
"@tanstack/react-query": "*",
"astro": ">=6.0.0-beta.0",
"emdash": "*",
"react": "^18.0.0 || ^19.0.0"
},
"devDependencies": {
"@types/react": "^19.0.0",
"tsdown": "^0.20.0",
"typescript": "^5.9.0"
},
"keywords": ["emdash", "emdash-plugin"],
"license": "MIT"
}
当插件没有受信任的 React UI 时,移除 ./admin、src/admin 以及仅管理端的 peerDependencies。当没有 Portable Text 渲染器时,移除 ./astro、src/astro 与 astro peer。为每个导出的源入口导入的宿主拥有库添加 peerDependency;这可防止第二个 React、Kumo、Lingui 或 React Query 实例进入管理包。
入口有不同的消费者:
| 导出 | 何时需要 | 消费者 |
|---|---|---|
. | 始终 | Astro 配置导入描述符工厂;EmDash 在运行时导入具名 createPlugin()。 |
./admin | 设置了 adminEntry | 宿主的浏览器构建导入 React 组件映射。 |
./astro | 设置了 componentsEntry | 宿主的 Astro 构建导入 blockComponents。 |
描述符与运行时中的模块说明符必须与这些导出匹配:
export function activityPlugin(): PluginDescriptor {
return {
id: "plugin-activity",
version: "0.1.0",
format: "native",
entrypoint: "@example/plugin-activity",
adminEntry: "@example/plugin-activity/admin",
componentsEntry: "@example/plugin-activity/astro",
};
}
export function createPlugin() {
return definePlugin({
id: "plugin-activity",
version: "0.1.0",
admin: {
entry: "@example/plugin-activity/admin",
},
});
}
保持 npm 包版本、描述符版本与 definePlugin() 版本同步。向站点管理员显示的版本来自插件定义,而不是自动来自 package.json。
插件标识与版本
definePlugin() 接受包含小写字母、数字与连字符的无作用域 ID,或形如 @scope/name 的作用域 ID。对站点插件使用无作用域、kebab-case 的 ID,因为 ID 也占用 /_emdash/api/plugins/<plugin-id>/<route> 中的一个路径段。
以下值显示可接受形式以及插件 ID 与 npm 包名之间的推荐分离:
id: "plugin-activity"; // 推荐:在插件路由 URL 中有效
id: "@example/plugin-activity"; // definePlugin() 接受,但不是一个 URL 段
entrypoint: "@example/plugin-activity"; // npm 包可以保持作用域
版本必须以语义化 major.minor.patch 序列开头。对描述符与运行时都使用完整语义版本:
version: "1.0.0"; // 有效
version: "1.2.3-beta.1"; // 有效的预发布
version: "1.0"; // 无效:缺少补丁版本
TypeScript 配置
原生脚手架会创建合适的 tsconfig.json。若插件在脚手架之后添加 React 与 Astro 源码,请包含两种 JSX 环境:
{
"compilerOptions": {
"target": "ES2022",
"module": "preserve",
"moduleResolution": "bundler",
"strict": true,
"declaration": true,
"outDir": "./dist",
"rootDir": "./src",
"jsx": "react-jsx",
"types": ["astro/client"]
},
"include": ["src/**/*"],
"exclude": ["node_modules", "dist"]
}
打包前对源入口运行 pnpm typecheck。build 脚本仅编译 src/index.ts;宿主在消费包时编译导出的管理端与 Astro 源码。
检查包
发布前测试确切的 tarball 内容。以下命令假定名为 my-emdash-site 的临时站点位于插件目录旁。
-
构建并类型检查包。
pnpm typecheck pnpm build -
创建 npm tarball 并查看 npm 打印的文件列表。
npm pack对于示例包,npm 会创建
example-plugin-activity-0.1.0.tgz。 -
确认输出包含
dist/index.mjs、dist/index.d.mts,以及从导出的./admin与./astro模块可达的每个源文件。 -
将生成的 tarball 安装到临时 EmDash 站点。
cd ../my-emdash-site pnpm add ../plugin-activity/example-plugin-activity-0.1.0.tgz -
按照 创建并注册包,在临时站点的
astro.config.mjs中导入并注册描述符工厂。然后构建宿主站点。pnpm build打开每个插件管理表面,并渲染每个贡献的 Portable Text 块。在构建前注册可使 Astro 解析 tarball 的
./admin与./astro导出;仅服务器的包测试无法发现缺失的浏览器或.astro源文件。
README 内容
为运营者提供足够信息,使其无需阅读源码即可安装并评估包。包括:
- 一句话描述与支持的 EmDash 版本
- 安装命令与完整的
astro.config.mjs注册 - 原生信任边界以及插件为何需要原生执行
- 每个已声明能力与允许的主机,以及使用它的功能
- 设置及其默认值
- 必需的布局组件,例如用于 body-end 片段的
EmDashBodyEnd - 需要运营者操作的更改的升级步骤
不要将能力声明描述为隔离边界。它们对 ctx API 设门,但原生代码仍可使用宿主进程可用的导入、环境变量与直接网络调用。
发布到 npm
tarball 测试通过后发布:
npm publish --access public
作用域包的首次公开发布需要 --access public。后续发布使用语义化版本。将构造函数选项、已存数据、必需的宿主更改、包导出或插件信任要求的更改视为兼容性决策。若升级需要新能力或允许的主机,即使原生安装没有能力同意提示,也请在发布说明中指出。
从 npm 安装
运营者将已发布的包安装到 EmDash 站点:
pnpm add @example/plugin-activity
然后按 创建并注册包 所示,在 astro.config.mjs 中导入并注册其描述符工厂。仅安装依赖不会激活插件;更改 Astro 配置并部署站点才完成安装。
针对宿主站点开发
以监视模式构建插件:
pnpm dev
从宿主站点安装本地目录:
pnpm add ../plugin-activity
在 astro.config.mjs 中注册插件的描述符工厂,然后启动宿主开发服务器。更改描述符元数据或包导出后重启服务器。若包管理器文件依赖在你的设置中复制文件而不是链接,请在重建后重新安装;工作区依赖或 pnpm link 可在开发期间保持本地包连接。
注册表边界
原生包不能发布到 EmDash 注册表。注册表插件使用沙箱包格式、签名发布工作流与安装同意流程。若插件不再需要 React 管理代码、Astro 渲染器、受信任片段或其他进程内依赖,请在通过注册表发布前 转换为沙箱格式。