The docmd 0.7.1 release introduces a major architectural improvement — the plugin system has been extracted into a dedicated @docmd/api package, bringing plugin descriptors, crash isolation, and capability enforcement to the ecosystem.
✨ Highlights
📦 @docmd/api — Dedicated Plugin Package
The plugin API surface — hook registration, WebSocket RPC dispatch, and source editing tools — now lives in its own dedicated package: @docmd/api.
npm install @docmd/api
This decouples the plugin ecosystem from the build engine, allowing plugin authors to depend on a lightweight API contract without pulling in the entire @docmd/core package.
Backward Compatible: All exports from
@docmd/apiare re-exported from@docmd/core. Existing code using@docmd/coreimports continues to work without changes.
🛡️ Plugin Descriptors
Plugins can now export a plugin descriptor declaring their identity and capabilities:
export default {
plugin: {
name: 'my-analytics',
version: '1.0.0',
capabilities: ['head', 'body', 'post-build']
},
generateScripts: (config, opts) => { ... },
onPostBuild: async (ctx) => { ... }
};
The engine validates descriptors at load time — invalid names, versions, or unknown capabilities are rejected immediately for official plugins, and emit warnings for third-party packages.
Migration Note: Descriptors are optional in 0.7.x. A soft deprecation warning is emitted for plugins without one. This will become a hard requirement in 0.8.0.
🔒 Plugin Isolation
Every hook invocation is now wrapped in a try/catch boundary. A broken plugin cannot crash the build or interfere with other plugins. Errors are logged and collected into a summary displayed at the end of the build:
⚠️ 2 plugin error(s) occurred (build completed)
🔑 Capability Enforcement
Plugins that declare capabilities can only register for hooks matching those declarations. If a plugin exports a hook it didn’t declare, the engine skips it with a warning:
Plugin "analytics" exports markdownSetup but didn't declare "markdown" capability — skipped
| Capability | Allowed Hooks | Phase |
|---|---|---|
init |
onConfigResolved |
Init |
markdown |
markdownSetup |
Setup |
head |
generateMetaTags, generateScripts (head) |
Render |
body |
generateScripts (body) |
Render |
build |
onBeforeParse, onAfterParse, onPageReady |
Build |
post-build |
onPostBuild |
Post-Build |
dev |
onDevServerReady |
Dev Server |
assets |
getAssets |
Output |
actions |
actions |
Interactive |
events |
events |
Interactive |
translations |
translations |
i18n |
Legacy plugins without a descriptor continue to have full access to all hooks.
📝 Complete Changelog
📦 Architecture
@docmd/apipackage: ExtractedloadPlugins,createActionDispatcher,createSourceTools, and all plugin/RPC types intopackages/api/.@docmd/corere-exports: All moved API symbols are re-exported from@docmd/core/src/index.tsfor seamless backward compatibility.- Dead code removal: Deleted the original
plugin-loader.ts,action-dispatcher.ts,source-tools.ts, andtypes.tsfrom@docmd/coreafter migration.
🔌 Plugin System
- Plugin Descriptor (
pluginexport): Name, version, and capability declaration. - Validation (§1): Strict enforcement for
@docmd/plugin-*packages; soft warnings for third-party plugins. - Isolation (§2):
safeCall()wrappers around all synchronous hooks; async try/catch foronPostBuild. - Capability Enforcement (§3): Hook registration gated by declared capabilities.
- Expanded Lifecycle Hooks (§4): Introduced
onConfigResolved,onDevServerReady,onBeforeParse,onAfterParse, andonPageReadyto handle complex site integration. - Error Summary: Plugin errors are collected and displayed as a count at the end of the build, without halting the process.
🐛 Bug Fixes
- 404 page raw key display: Fixed an issue where the 404 page could display
errorCode404as literal text when translations failed to load. The error code is now hardcoded as404. - i18n
stringModescript leak: Fixedno-style.ejsinjecting thedocmd-i18n-strings.jsruntime whenstringModeis active. The client-side locale runtime is now correctly suppressed in string mode. - Plugin resolution in pnpm workspaces: Fixed
loadPluginsfailing to locate@docmd/plugin-*packages when called from@docmd/api— the function now acceptsresolvePathsfrom the caller to support pnpm’s strictnode_moduleslayout.
⚠️ Breaking Changes
Plugin API Import Path (Recommended Migration)
The canonical home for the plugin API is now @docmd/api. While @docmd/core re-exports everything for backward compatibility, plugin authors are encouraged to update their imports:
-import { createActionDispatcher, createSourceTools } from '@docmd/core';
+import { createActionDispatcher, createSourceTools } from '@docmd/api';
-import type { PluginModule, ActionContext, SourceTools } from '@docmd/core';
+import type { PluginModule, ActionContext, SourceTools, PluginDescriptor, Capability } from '@docmd/api';
No action required for end users. This only affects plugin developers who directly import API utilities. The
@docmd/corere-exports will remain available indefinitely.
Threads Plugin Peer Dependency
The @docmd/plugin-threads package now peer-depends on @docmd/api instead of @docmd/core. If you install Threads manually (rather than via docmd add threads), ensure @docmd/api is available in your dependency tree.
Migration Guide
For end users: No changes required. npm install docmd@latest is sufficient.
For plugin authors:
- Add a plugin descriptor to your default export (optional now, required in 0.8.0):
export default { plugin: { name: 'my-plugin', version: '1.0.0', capabilities: ['head', 'post-build'] }, // ... hooks }; - Update imports from
@docmd/coreto@docmd/apiforcreateActionDispatcher,createSourceTools,loadPlugins, and all type exports. - Update peer dependencies from
@docmd/coreto@docmd/apiin yourpackage.jsonif your plugin uses the RPC or source editing APIs.
See Building Plugins for the full updated API reference.