Skip to content

Plugin Prepare

worable edited this page Oct 20, 2026 · 2 revisions

插件开发准备

最快开始:使用生成器

仓库内置了插件脚手架模板 packages/plugins/template,并提供了生成器脚本:

pnpm plugin:create <your-id> --name "显示名称" --frontend

它会按 packages/plugins/template 的形态生成 packages/plugins/<your-id>(后端入口 src/index.ts,--frontend 时另含前端包)、写入包名与插件 ID、刷新 workspace lockfile 并打印后续步骤。手工方式则是:

cp -r packages/plugins/template packages/plugins/my-plugin
cd packages/plugins/my-plugin

然后修改 package.json 中的包名(@stackpanel/plugin-my-plugin)和 src/index.ts 中的插件 ID。

加入 workspace

编辑 pnpm-workspace.yaml(如果 packages/plugins/* 已包含则无需修改),然后更新 lockfile:

pnpm install --no-frozen-lockfile

开发依赖

插件的 package.json 至少需要:

{
  "name": "@stackpanel/plugin-my-plugin",
  "version": "1.0.0",
  "private": true,
  "type": "module",
  "scripts": {
    "build": "node ../../../scripts/build-plugin.mjs",
    "typecheck": "tsc -p tsconfig.json"
  },
  "dependencies": {
    "@stackpanel/sdk": "workspace:*"
  },
  "devDependencies": {
    "@types/node": "^22.20.1",
    "typescript": "^6.0.3"
  }
}

scripts/build-plugin.mjs 是内置插件的标准构建:先 tsc --emitDeclarationOnly 出 .d.ts,再用 esbuild 把源码打包成单文件 dist/index.js(不留裸相对导入,Node ESM 可直接跑)。不要用纯 tsc 直接产出运行入口——共享配置下 moduleResolution: bundler 会留下无扩展名的相对导入,Node ESM 解析不了。也不再用 tsup(已从插件脚手架移除)。

构建配置

内置插件不需要 tsup.config.ts,只用两个 tsconfig:

// tsconfig.json —— 类型检查(noEmit)
{ "extends": "../../../tsconfig.base.json", "compilerOptions": { "noEmit": true, "types": ["node"] }, "include": ["src/**/*.ts"] }

// tsconfig.build.json —— 出声明
{ "extends": "../../../tsconfig.base.json", "compilerOptions": { "noEmit": false, "declaration": true, "declarationMap": true, "sourceMap": true, "outDir": "dist", "rootDir": "src", "types": ["node"] }, "include": ["src/**/*.ts"] }

开发流程

  1. 编写插件代码(src/index.ts)
  2. pnpm --filter @stackpanel/plugin-my-plugin build 编译到 dist/
  3. 若含前端包,pnpm build:frontend 编译 frontend/ 并更新前端注册表
  4. 重启 API(开发模式下 tsx watch 会自动重启)
  5. API 启动时自动 seed 内置插件;非内置插件需打包上传安装

touch apps/api/src/app.ts 可手动触发 tsx watch 重新 seed 内置插件。

内置插件开发

如果你的插件在 packages/plugins/ 下且被 apps/api 的 package.json 依赖,它会被视为内置插件,API 启动时自动 seed。修改源码后重新构建并重启 API 即可生效。

非内置插件开发

如果你在开发一个将通过 ZIP 上传安装的插件:

  1. 编写并构建
  2. 打包为 ZIP(见 打包与签名)
  3. 通过管理后台 /admin/plugins 上传
  4. 启用插件

数据模型

插件数据自治(ADR-0008 / ADR-0009):插件零裸 DB,不得把业务表写进内核 schema,也不通过 ctx.db 操作数据。用 defineModel 声明模型,放进 PluginDefinition.customModels,由内核在激活时建表并暴露类型化访问:

import { defineModel } from '@stackpanel/sdk';
import { z } from 'zod';

export const itemModel = defineModel({
  kind: 'my-plugin/item', // 命名空间/名称
  label: '条目',
  schema: z.object({ title: z.string(), stock: z.number().int().default(0) }),
  indexes: [{ fields: ['stock'], types: { stock: 'integer' } }],
});

读写只经 ctx.extensions(get / list / listAll / create / update / delete / patch)或 ctx.tx 事务。内核同时提供 /custom/<namespace>/<name> 通用 REST 面。PluginContext 不再提供任何数据库句柄(ADR-0008 §1:ctx.db 已物理删除),插件拿不到 PrismaClient,也无法绕过 Extension 引擎直连数据库。内核自身的表结构变更与插件无关,属内核维护范畴。

Clone this wiki locally