Documentation
¶
Overview ¶
Package plugin - external.go implements a plugin backed by an external executable.
External plugins are standalone programs that communicate with mdpress via JSON over stdin/stdout. Each hook invocation starts the process, writes a JSON request to stdin, and reads a JSON response from stdout. Any output written to stderr is captured and surfaced as debug/warning log messages.
Package plugin - loader.go loads plugins declared in book.yaml and returns an initialized Manager ready for use throughout the build pipeline.
Package plugin defines the plugin system interface for mdpress.
Plugins are external executables that communicate with mdpress over JSON stdin/stdout. They can intercept any of the seven build lifecycle phases to transform content, perform validation, or trigger side-effects such as uploading artifacts or sending notifications.
The Manager handles plugin registration, ordering, and hook dispatch. Use plugin.LoadPlugins to create a Manager from a BookConfig, or call plugin.NewManager and Register plugins manually in tests.
Index ¶
- type ExternalPlugin
- func (p *ExternalPlugin) Cleanup() error
- func (p *ExternalPlugin) Description() string
- func (p *ExternalPlugin) Execute(hookCtx *HookContext) (*HookResult, error)
- func (p *ExternalPlugin) Hooks() []Phase
- func (p *ExternalPlugin) Init(_ *config.BookConfig) error
- func (p *ExternalPlugin) Name() string
- func (p *ExternalPlugin) Version() string
- type ExternalPluginRequest
- type ExternalPluginResponse
- type HookContext
- type HookResult
- type Manager
- type Phase
- type Plugin
- type PluginError
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type ExternalPlugin ¶ added in v0.3.0
type ExternalPlugin struct {
// contains filtered or unexported fields
}
ExternalPlugin represents a plugin implemented as an external executable. For each hook invocation it spawns a new process, sends a JSON request via stdin, and reads the JSON response from stdout.
func NewExternalPlugin ¶ added in v0.3.0
func NewExternalPlugin(name, execPath string, pluginCfg map[string]any) (*ExternalPlugin, error)
NewExternalPlugin creates a new ExternalPlugin. execPath is resolved to an absolute path; relative paths are based on the current working directory at the time of the call.
func (*ExternalPlugin) Cleanup ¶ added in v0.3.0
func (p *ExternalPlugin) Cleanup() error
Cleanup is a no-op for external plugins.
func (*ExternalPlugin) Description ¶ added in v0.3.0
func (p *ExternalPlugin) Description() string
Description returns the plugin description.
func (*ExternalPlugin) Execute ¶ added in v0.3.0
func (p *ExternalPlugin) Execute(hookCtx *HookContext) (*HookResult, error)
Execute runs the external plugin process for the given hook context. It writes a JSON request to stdin, reads the JSON response from stdout, and collects stderr for diagnostic purposes.
func (*ExternalPlugin) Hooks ¶ added in v0.3.0
func (p *ExternalPlugin) Hooks() []Phase
Hooks returns the list of hook phases this plugin handles.
func (*ExternalPlugin) Init ¶ added in v0.3.0
func (p *ExternalPlugin) Init(_ *config.BookConfig) error
Init is a no-op for external plugins.
func (*ExternalPlugin) Name ¶ added in v0.3.0
func (p *ExternalPlugin) Name() string
Name returns the plugin name.
func (*ExternalPlugin) Version ¶ added in v0.3.0
func (p *ExternalPlugin) Version() string
Version returns the plugin version.
type ExternalPluginRequest ¶ added in v0.3.0
type ExternalPluginRequest struct {
// Phase is the name of the current hook phase.
Phase string `json:"phase"`
// Content is the text being processed (Markdown or HTML, depending on phase).
Content string `json:"content"`
// ChapterIndex is the zero-based chapter index (-1 for non-chapter contexts).
ChapterIndex int `json:"chapter_index"`
// ChapterFile is the source path of the current chapter.
ChapterFile string `json:"chapter_file"`
// OutputPath is the output file path (valid in AfterBuild phase only).
OutputPath string `json:"output_path"`
// OutputFormat is the output format name (valid in AfterBuild phase only).
OutputFormat string `json:"output_format"`
// Config holds the plugin-specific settings from book.yaml plugins[n].config.
Config map[string]any `json:"config"`
// Metadata is a shared key-value store for inter-plugin and inter-phase communication.
Metadata map[string]any `json:"metadata"`
}
ExternalPluginRequest is the JSON body sent to the external plugin process. It is serialized and written to the plugin's stdin on every hook invocation.
type ExternalPluginResponse ¶ added in v0.3.0
type ExternalPluginResponse struct {
// Content is the modified text. An empty string means "keep the original".
Content string `json:"content"`
// Stop instructs the manager to skip subsequent plugins for this phase.
Stop bool `json:"stop"`
// Error is an optional error message reported by the plugin.
Error string `json:"error,omitempty"`
}
ExternalPluginResponse is the JSON body read from the external plugin process.
type HookContext ¶
type HookContext struct {
// Context is a standard Go context used for timeout and cancellation.
Context context.Context
// Config is the current build configuration. Plugins may read but should
// not modify it after the BeforeBuild phase.
Config *config.BookConfig
// Phase is the lifecycle phase that triggered this invocation.
Phase Phase
// Content is the text being processed.
// In AfterParse it holds the chapter HTML; in BeforeRender / AfterRender it
// holds the cover HTML (as a representative payload).
// A plugin that returns a non-empty Content replaces this value.
Content string
// ChapterIndex is the zero-based index of the chapter being processed.
// Set to -1 in non-chapter contexts.
ChapterIndex int
// ChapterFile is the source file path of the chapter (AfterParse only).
ChapterFile string
// OutputPath is the path of the generated output file (AfterBuild only).
OutputPath string
// OutputFormat is the output format name (AfterBuild only).
OutputFormat string
// Metadata is a shared key-value map for passing data between phases or
// between consecutive plugins.
// No sync protection is needed: each HookContext instance gets its own map,
// and RunHook is always called sequentially from the build pipeline.
Metadata map[string]any
}
HookContext carries all data available to a plugin at the point of invocation. Plugins may read any field and may modify Content to influence the build output.
type HookResult ¶
type HookResult struct {
// Content replaces HookContext.Content when non-empty.
Content string
// Stop instructs the Manager to skip all subsequent plugins for this phase.
Stop bool
}
HookResult is returned by a plugin's Execute method.
type Manager ¶
type Manager struct {
// contains filtered or unexported fields
}
Manager registers plugins and dispatches hook calls to them in order.
func LoadPlugins ¶ added in v0.3.0
func LoadPlugins(cfg *config.BookConfig) (*Manager, error)
LoadPlugins reads the plugin declarations from cfg.Plugins, creates an ExternalPlugin for each entry, registers them with a new Manager, and initializes the whole set. Returns an empty (no-op) Manager when no plugins are configured. Plugins are registered and executed in declaration order.
func MustLoadPlugins ¶ added in v0.3.0
func MustLoadPlugins(cfg *config.BookConfig, warnFn func(msg string)) *Manager
MustLoadPlugins is like LoadPlugins but never fails the build. Any loading error is passed to warnFn (if non-nil) and an empty Manager is returned.
func (*Manager) CleanupAll ¶
CleanupAll calls Cleanup on every registered plugin. Errors do not prevent subsequent plugins from being cleaned up. Returns all errors encountered combined with errors.Join, or nil if no errors.
func (*Manager) InitAll ¶
func (m *Manager) InitAll(cfg *config.BookConfig) error
InitAll calls Init on every registered plugin. Returns the first error encountered, wrapped in a PluginError.
func (*Manager) Register ¶
Register appends p to the manager's plugin list. Plugins are executed in registration order.
func (*Manager) RunHook ¶
func (m *Manager) RunHook(hookCtx *HookContext) error
RunHook dispatches the hook to every plugin that listed hookCtx.Phase in its Hooks list. Plugins are called in registration order. If a plugin sets HookResult.Stop the remaining plugins are skipped. The first plugin error is returned wrapped in a PluginError.
type Phase ¶
type Phase string
Phase identifies a stage in the build pipeline at which plugins are invoked.
const ( // PhaseBeforeBuild fires after configuration is loaded, before chapter processing begins. // Plugins can inspect or modify the configuration and perform environment checks. PhaseBeforeBuild Phase = "before_build" // PhaseAfterParse fires after each chapter's Markdown has been converted to HTML. // Plugins can modify the rendered HTML (e.g. inject custom elements, check links). PhaseAfterParse Phase = "after_parse" // PhaseBeforeRender fires before the per-format HTML assembly step. // Plugins can modify cover, TOC, or per-chapter HTML before the final document // is assembled. PhaseBeforeRender Phase = "before_render" // PhaseAfterRender fires after the HTML document has been fully assembled. // Plugins can post-process the complete HTML (e.g. inject SEO tags, watermarks). PhaseAfterRender Phase = "after_render" // PhaseAfterBuild fires after all output formats have been written to disk. // Plugins can run post-build tasks such as CDN uploads or build notifications. PhaseAfterBuild Phase = "after_build" // PhaseBeforeServe fires before the local preview server starts listening. // Plugins can run pre-serve checks or warm caches. PhaseBeforeServe Phase = "before_serve" // PhaseAfterServe fires after the local preview server shuts down. // Plugins can perform cleanup tasks. PhaseAfterServe Phase = "after_serve" )
type Plugin ¶
type Plugin interface {
// Name returns the unique plugin identifier (lowercase, hyphen-separated).
Name() string
// Version returns the semantic version of the plugin (e.g. "1.0.0").
Version() string
// Description returns a short human-readable description of the plugin.
Description() string
// Init is called once before the build starts. It receives the complete
// BookConfig so the plugin can validate its configuration.
Init(cfg *config.BookConfig) error
// Hooks returns the list of phases this plugin wants to handle.
// The Manager only calls Execute for phases listed here.
Hooks() []Phase
// Execute is invoked at each phase listed by Hooks.
// It receives a HookContext with the current build state and returns a
// HookResult (or nil) plus any error.
Execute(hookCtx *HookContext) (*HookResult, error)
// Cleanup is called after the build finishes, regardless of success.
Cleanup() error
}
Plugin is the interface that all mdpress plugins must implement.
type PluginError ¶
PluginError wraps an error returned by a plugin, adding the plugin name and the hook phase for diagnostic purposes.
func (*PluginError) Error ¶
func (e *PluginError) Error() string
Error implements the error interface.
func (*PluginError) Unwrap ¶
func (e *PluginError) Unwrap() error
Unwrap supports errors.Is / errors.As unwrapping.