✨ 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/api 的 prebuild 步骤接入,因此每次构建都会重新生成注册表。
运行时加载器(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)、displayName、tagline、capabilities(插件和模板必需,由构建时交叉检查使用),以及 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 dev 和 pnpm 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-onlyexports具有弹性。@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_EXPORTED、ERR_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.listen和checkPortInUse探测现在绑定到127.0.0.1而不是0.0.0.0。LAN 访问通过DOCMD_HOST=0.0.0.0或--host 0.0.0.0显式开启,激活时显示 TUI 警告。docker/DOCKER.md示例现在使用回环默认值。 - 工具:
scriptLiteral和jsonInject加固内联脚本转义。 二者现在除了现有的 JSON 安全编码外,还能正确转义</script、<!--、U+2028 和 U+2029。该加固对非冲突字符串静默生效;通过JSON.parse的往返仍然可用,因为转义使用 JSON 安全的序列。
Changelog
- Registry:新增
scripts/build-plugin-registry.mjs—— 从每个官方包的docmd命名空间生成packages/api/registry/plugins.generated.json。 - Loader:
getPluginRegistry读取生成的文件,具有两种解析路径;安装程序中手工维护的installer/registry/plugins.json在运行时不再被查询。 - 命名空间:为所有 14 个官方
@docmd/*包(12 个插件、2 个引擎)添加docmd命名空间,并使用key: "summer"更新@docmd/template-summer。 - Loader:按 key 缓存能力集合 + 清单漂移检查(
checkManifestCapabilityDrift)。 - 自动安装:retry 路径使用
await import(name)代替require.resolve + import(file://path),并具有纵深防御的注册表重新检查。 - 自动安装错误:显示
err.code+err.message的第一行;显示包管理器的 stderr + 提示。 - CLI:新增
docmd doctor子命令,带--config、--fix、--json标志。在 CLI 派发器中注册;帮助文本已更新。 - CLI:monorepo 中的新 pnpm 脚本 ——
doctor、validate、migrate、gen:deploy、mcp、plugin:add、plugin:remove、build:playground。 - UI:
packages/ui/package.json#files现在包含translations/。 - Template:
packages/templates/summer/package.json#files现在包含templates/和assets/。 - Dev-Server:
serveStatic中的safePath()会去掉 URL 路径名的前导/。 - Live-Editor:
server.listen和checkPortInUse绑定到127.0.0.1;DOCMD_HOST=0.0.0.0opt-in。 - Docker 文档:三个
command: dev --host 0.0.0.0示例改用回环默认值;“Network Issues” 排错文档化了 opt-in 路径。 - 工具:
scriptLiteral和jsonInject转义</script、<!--、U+2028、U+2029。 - 测试:更新了
packages/utils/test/html-escape.test.js以验证新的转义行为。 - 文档:在
building-plugins.md和building-templates.md中新增 “ESM Exports — thedefaultCondition” 章节;新增 “Bundled registry removal in 0.9.0” 提示;在reference/cli-commands.md中新增docmd doctor条目。