Repository navigation
🐛 [Bug] pnpm dev 启动失败:plugin.app.dev-workspace failed to start — 三重问题合并修复 #904
Description
概述
运行 pnpm dev 时,服务器启动失败并退出:
Loading objectstack.config.ts...
(node:73691) ExperimentalWarning: CommonJS module .../plugin-auth/dist/index.js is loading ES Module .../better-auth/dist/index.mjs using require().
✗ Plugin plugin.app.dev-workspace failed to start - rollback complete
ELIFECYCLE Command failed with exit code 1.
经过对启动流程的完整追踪分析,发现这是 三个相互关联的问题 共同导致的,需要整体修复。
问题 1:CLI serve 命令对根聚合配置的误判(核心触发点)
分析
serve.ts 第 192-201 行存在一个有缺陷的自动注册逻辑:
// packages/cli/src/commands/serve.ts L192-201
if (config.objects || config.manifest || config.apps) {
try {
const { AppPlugin } = await import('@objectstack/runtime');
await kernel.use(new AppPlugin(config)); // ← 问题所在
trackPlugin('App');
} catch (e: any) {
// silent
}
}根配置 objectstack.config.ts 含有 manifest 字段,因此该条件为 true。CLI 将**整个 root config(包含 plugins 数组)**包装为一个 AppPlugin(config),生成名为 plugin.app.dev-workspace 的插件。
但是,root config 是一个聚合器/宿主配置(Host Config),它的 plugins 数组中已经包含了实例化的插件对象(ObjectQLPlugin、DriverPlugin、AuthPlugin、AppPlugin(CrmApp) 等)。它不是一个普通的 App Bundle,不应被 AppPlugin 包装。
后果
在 kernel.bootstrap() 的 Phase 2 (Start) 中,plugin.app.dev-workspace 的 start() 方法尝试:
- 获取 ObjectQL 引擎 → 可能失败(因为重复注册冲突)
- 调用
runtime.onEnable()→ root config 没有 onEnable - 处理 seed data → root config 没有 data
- 但更严重的是,init 阶段的
registerService('app.dev-workspace', ...)传入了整个 config 对象(含 plugins 数组实例),可能导致序列化或处理错误
同时,同一批 plugins 被注册了两次:一次通过 CLI 的 plugins.forEach(kernel.use) 循环,一次通过 AppPlugin(config) 的服务注册。
修复方案
在 serve.ts 中增加检测逻辑:当 config 已包含 plugins 数组(且数组中存在已实例化的 Plugin 对象),则跳过 auto-AppPlugin 包装。
// 检测是否为聚合器配置(已有 plugins 数组含实例化插件)
const isHostConfig = Array.isArray(config.plugins) &&
config.plugins.some((p: any) => typeof p?.init === 'function');
if (!isHostConfig && (config.objects || config.manifest || config.apps)) {
// 只为纯 App Bundle 创建 AppPlugin 包装
const { AppPlugin } = await import('@objectstack/runtime');
await kernel.use(new AppPlugin(config));
trackPlugin('App');
}问题 2:plugin-auth CJS→ESM 互操作问题(连锁放大器)
分析
ExperimentalWarning: CommonJS module .../plugin-auth/dist/index.js is loading
ES Module .../better-auth/dist/index.mjs using require()
根因链条:
| 层级 | 配置 | 结果 |
|---|---|---|
Root package.json |
无 "type": "module" |
默认 CJS |
plugin-auth/package.json |
"main": "dist/index.js", 无 "type" |
继承 CJS |
共享 tsup.config.ts |
format: ['esm', 'cjs'] |
输出 .js(CJS) + .mjs(ESM) |
plugin-auth/package.json |
"main": "dist/index.js" |
Node.js 加载 CJS 版本 |
better-auth |
只导出 ESM (.mjs) |
CJS require() 加载 ESM → 实验性警告 |
plugin-auth 通过 tsup 构建输出了 CJS 和 ESM 双格式,但 package.json 中 "main" 指向 .js(CJS),没有 "module" 或 "exports" 字段。Node.js 默认加载 CJS 版本,当它 require('better-auth') 时触发 CJS→ESM 互操作,导致实验性警告,在某些 Node.js 版本下可能直接抛出异常。
修复方案
更新 plugin-auth/package.json 添加 exports 字段,优先使用 ESM:
{
"main": "dist/index.js",
"module": "dist/index.mjs",
"types": "dist/index.d.ts",
"exports": {
".": {
"import": "./dist/index.mjs",
"require": "./dist/index.js",
"types": "./dist/index.d.ts"
}
}
}这样 serve.ts 通过 await import('@objectstack/plugin-auth') 加载时,Node.js 会选择 .mjs(ESM)版本,避免 CJS→ESM 互操作问题。
问题 3:与 #893 的关联(better-auth 字段名映射)
Issue #893 报告了 better-auth 的 camelCase↔snake_case 字段映射问题导致登录失败。这个问题与本 Issue 形成了阻塞链:
- 即使修复了问题 1 和 2,AuthPlugin 成功启动后
- 用户注册/登录时仍会遇到 [Bug] ObjectQL Adapter field name mapping error: camelCase vs snake_case causes 'Credential account not found' on sign-in #893 的字段映射错误
objectql-adapter.ts需要实现字段名自动转换
本 Issue 修复后,#893 的修复才能被验证,因为服务器必须先能成功启动。
影响范围
| 文件 | 修改类型 |
|---|---|
packages/cli/src/commands/serve.ts |
修复自动 AppPlugin 包装逻辑 |
packages/plugins/plugin-auth/package.json |
添加 ESM exports 字段 |
packages/cli/src/commands/serve.ts (测试) |
添加聚合器配置测试 |
CHANGELOG.md |
记录修复 |
验收标准
-
pnpm dev能够成功启动,不再出现plugin.app.dev-workspace failed to start错误 -
ExperimentalWarning: CommonJS module ... is loading ES Module警告消除 - Root config 中的 plugins 不会被重复注册
-
examples/app-host/objectstack.config.ts等聚合器配置也能正常工作 - 纯 App Bundle 配置(无 plugins 数组)的 auto-AppPlugin 包装功能不受影响
- 所��现有测试通过:
pnpm test - 更新 CHANGELOG.md
相关
- 阻塞: [Bug] ObjectQL Adapter field name mapping error: camelCase vs snake_case causes 'Credential account not found' on sign-in #893 (better-auth 字段名映射 — 需要本 Issue 修复后服务器才能启动)
- 相关代码:
Labels
bug, P0, cli, plugin-auth, kernel
Activity