✨ Highlights

本版本是对插件和模板生态的一次加固。最重要的改动是结构性的:每个官方 @docmd/* 包现在在其 package.json 中携带 docmd 命名空间,构建时注册表生成器读取这些命名空间,生成运行时加载器使用的唯一事实来源。插件自动安装器现在对仅提供 import 条件 exports 字段的包具有弹性,新增的 docmd doctor 预检命令在构建前捕获配置漂移。同时还修复了已发布 tarball 中两个原本静默的 bug:404 页面现在会使用夏季模板渲染并显示翻译后的字符串,而不再回退到默认模板并显示原始的翻译键。

没有公开的 API 变更。没有破坏性的配置变更。纯粹的加固版本。

🔌 构建时插件注册表(唯一事实来源)

一个新的 workspace 级别脚本 —— scripts/build-plugin-registry.mjs —— 遍历 packages/{plugins,templates,engines}/*,读取每个包的 package.json#docmd 命名空间,并生成一个 JSON 目录(packages/api/registry/plugins.generated.json)。作为 @docmd/apiprebuild 步骤接入,因此每次构建都会重新生成注册表。

运行时加载器(packages/api/src/hooks.ts getPluginRegistry)现在从生成的文件读取,有两种解析路径(已发布布局 <pkg>/registry/... 和 monorepo 开发布局 <repo>/packages/api/registry/...)。安装程序中曾经手工维护的 packages/plugins/installer/registry/plugins.json 在运行时不再被查询。

📦 docmd 命名空间标准化

每个官方 @docmd/* 包现在在其 package.json 中携带 docmd 命名空间。命名空间是注册表生成器读取的合约,也是加载器在加载时与 JS 描述符进行交叉检查的依据。字段:key(面向用户的标识符)、kind(plugin / template / engine)、displayNametaglinecapabilities(插件和模板必需,由构建时交叉检查使用),以及 preview(仅模板)。

引擎具有相同的形状但没有 capabilities —— 它们不参与钩子系统。自动安装器检查 kind === 'engine' 并拒绝安装它们。

🩺 docmd doctor —— 预检

一个新的 CLI 子命令用于诊断。不写文件、没有构建副作用 —— 纯粹用于诊断。

npx @docmd/core doctor [选项]
选项 说明
--config <路径> 指向非默认 docmd.config.json(或 .ts/.js/.mjs)的路径。
--fix 自动安装标记为缺失的官方插件或模板。
--json 将完整报告以机器可读的 JSON 形式输出。

默认情况下,doctor 会打印一份人类可读的摘要,涵盖:已安装的 @docmd/core 版本、每个已配置的插件(附带版本和 ✓ installed / ⚠ missing 状态)、当前激活的模板、请求的引擎(js 始终启用,rust 可选),以及一份自动安装候选清单。带上 --fix,它会调用项目所用的包管理器来安装这些候选。带上 --json,同样的数据会作为一个 JSON 对象输出 —— 适合接入 pre-commit 钩子和 CI 闸门。

也可作为 pnpm doctor 使用(在 monorepo 中通过 workspace 的 docmd 脚本路由)。

🛠 monorepo 中的新 pnpm 脚本

monorepo 的 package.json 新增了一批 pnpm 脚本,全部通过现有的 --cwd 标志指向 playground(与 pnpm devpnpm live 相同的模式):

pnpm doctor           # → docmd doctor
pnpm validate         # → docmd validate
pnpm migrate          # → docmd migrate
pnpm gen:deploy       # → docmd deploy
pnpm mcp              # → docmd mcp
pnpm plugin:add foo   # → docmd add foo
pnpm plugin:remove foo # → docmd remove foo
pnpm build:playground # → docmd build(与 `pnpm build` 不同,后者是 monorepo 范围的构建)

🐛 错误修复

  • 插件自动安装:对 import-only exports 具有弹性。 @docmd/api 中的自动安装器此前使用 require.resolve 解析已安装的包,对于那些在 exports 字段中只携带 import 条件的包会抛出 ERR_PACKAGE_PATH_NOT_EXPORTED。retry 路径现在直接使用 await import(name),原生支持 exports 字段。在 retry 路径中执行一次注册表的纵深防御式重新检查 —— 可以自动安装的名称集合保持不变,只是它们可以携带的 exports 条件集合变得更大了。
  • 插件加载器:能力缓存与清单漂移检查。 按 key 缓存能力集合,避免在每次 dev-server 重新构建时重新遍历注册表。新的清单漂移检查在 JS 描述符的 capabilities 数组与清单的 capabilities 数组不一致时发出警告 —— 这修复了静默的钩子丢弃 bug,即实现了一个钩子但忘记在描述符中声明相应 capability 的插件,其钩子会被静默跳过。
  • 更好的 “Could not load X after auto-install” 错误信息。 post-install retry 的 catch 块现在会显示 err.code(如 ERR_PACKAGE_PATH_NOT_EXPORTEDERR_MODULE_NOT_FOUND)和 err.message 的第一行。autoInstallPlugin 的 catch 块也会显示包管理器的底层 stderr,并为最常见的情况打印提示。
  • @docmd/ui:将 translations/ 加入发布的 tarball。 服务端翻译加载器会在 __dirname/../translations/ 查找翻译 JSON 文件。package.json#files 缺少 "translations",因此 npm 在打包时把翻译文件漏掉了。404 页面是最显眼的症状:由已部署站点的静态文件回退机制懒加载渲染,翻译缓存是空的,键就漏出来了。现在全部 7 个语言文件都发布了。
  • @docmd/template-summer:将 templates/assets/ 加入发布的 tarball。 夏季模板的运行时会用到 new URL('../templates/...', import.meta.url),在 dist/index.js 里它解析到 <package-root>/templates/...(不是 <package-root>/dist/templates/...)。package.json#files 当时只有 ["dist"],因此发布的 tarball 中只有 dist/,解析器找不到任何模板片段,便静默地回退到默认模板。现在发布的 tarball 与 monorepo 开发布局一致,夏季模板能正确渲染。
  • Dev-Server:在 safePath() 之前去掉前导斜杠。 0.8.9 中的 CWE-22 修复(用 safePath(rootAbs, ...) 取代 filePath.startsWith(rootAbs))在 dev-server 上引发了一个回归:URL 路径名总是以 / 开头(例如 /index.html),而 path.resolve('/abs/root', '/index.html') 会返回 /index.html(把第二个参数视作绝对路径)—— 总是让 safePath 边界检查失败,对每一个合法请求都返回 403 Forbidden。修复:在把 URL 路径名传给 safePath() 之前去掉前导的 /
  • Live-Editor、端口探测和 Docker 文档:默认 Loopback。 夏季编辑器的 server.listencheckPortInUse 探测现在绑定到 127.0.0.1 而不是 0.0.0.0。LAN 访问通过 DOCMD_HOST=0.0.0.0--host 0.0.0.0 显式开启,激活时显示 TUI 警告。docker/DOCKER.md 示例现在使用回环默认值。
  • 工具:scriptLiteraljsonInject 加固内联脚本转义。 二者现在除了现有的 JSON 安全编码外,还能正确转义 </script<!--、U+2028 和 U+2029。该加固对非冲突字符串静默生效;通过 JSON.parse 的往返仍然可用,因为转义使用 JSON 安全的序列。

Changelog

  1. Registry:新增 scripts/build-plugin-registry.mjs —— 从每个官方包的 docmd 命名空间生成 packages/api/registry/plugins.generated.json
  2. LoadergetPluginRegistry 读取生成的文件,具有两种解析路径;安装程序中手工维护的 installer/registry/plugins.json 在运行时不再被查询。
  3. 命名空间:为所有 14 个官方 @docmd/* 包(12 个插件、2 个引擎)添加 docmd 命名空间,并使用 key: "summer" 更新 @docmd/template-summer
  4. Loader:按 key 缓存能力集合 + 清单漂移检查(checkManifestCapabilityDrift)。
  5. 自动安装:retry 路径使用 await import(name) 代替 require.resolve + import(file://path),并具有纵深防御的注册表重新检查。
  6. 自动安装错误:显示 err.code + err.message 的第一行;显示包管理器的 stderr + 提示。
  7. CLI:新增 docmd doctor 子命令,带 --config--fix--json 标志。在 CLI 派发器中注册;帮助文本已更新。
  8. CLI:monorepo 中的新 pnpm 脚本 —— doctorvalidatemigrategen:deploymcpplugin:addplugin:removebuild:playground
  9. UIpackages/ui/package.json#files 现在包含 translations/
  10. Templatepackages/templates/summer/package.json#files 现在包含 templates/assets/
  11. Dev-ServerserveStatic 中的 safePath() 会去掉 URL 路径名的前导 /
  12. Live-Editorserver.listencheckPortInUse 绑定到 127.0.0.1DOCMD_HOST=0.0.0.0 opt-in。
  13. Docker 文档:三个 command: dev --host 0.0.0.0 示例改用回环默认值;“Network Issues” 排错文档化了 opt-in 路径。
  14. 工具scriptLiteraljsonInject 转义 </script<!--、U+2028、U+2029。
  15. 测试:更新了 packages/utils/test/html-escape.test.js 以验证新的转义行为。
  16. 文档:在 building-plugins.mdbuilding-templates.md 中新增 “ESM Exports — the default Condition” 章节;新增 “Bundled registry removal in 0.9.0” 提示;在 reference/cli-commands.md 中新增 docmd doctor 条目。