Documentation
¶
Overview ¶
Package plugin provides the ForgeUI plugin system.
Plugins extend ForgeUI functionality through a common interface. The registry manages plugin lifecycle, dependencies, and hooks.
Basic usage:
registry := plugin.NewRegistry()
registry.Use(
myplugin.New(),
anotherplugin.New(),
)
if err := registry.Initialize(ctx); err != nil {
log.Fatal(err)
}
defer registry.Shutdown(ctx)
Index ¶
- Constants
- type AlpineComponent
- type AlpineDirective
- type AlpineMagic
- type AlpinePlugin
- type AlpinePluginBase
- type AlpineStore
- type ComponentConstructor
- type ComponentPlugin
- type ComponentPluginBase
- func (c *ComponentPluginBase) AddCVA(name string, cva *forgeui.CVA)
- func (c *ComponentPluginBase) AddComponent(name string, constructor ComponentConstructor)
- func (c *ComponentPluginBase) CVAExtensions() map[string]*forgeui.CVA
- func (c *ComponentPluginBase) Components() map[string]ComponentConstructor
- type Dependency
- type HookContext
- type HookFunc
- type HookManager
- type MiddlewarePlugin
- type MiddlewarePluginBase
- type Plugin
- type PluginBase
- type PluginInfo
- type Registry
- func (r *Registry) All() []Plugin
- func (r *Registry) CollectAlpineComponents() []AlpineComponent
- func (r *Registry) CollectComponents() map[string]ComponentConstructor
- func (r *Registry) CollectDirectives() []AlpineDirective
- func (r *Registry) CollectMagics() []AlpineMagic
- func (r *Registry) CollectMiddleware() []MiddlewarePlugin
- func (r *Registry) CollectScripts() []Script
- func (r *Registry) CollectStores() []AlpineStore
- func (r *Registry) Count() int
- func (r *Registry) Discover(dir string) error
- func (r *Registry) DiscoverSafe(dir string) []error
- func (r *Registry) Get(name string) (Plugin, bool)
- func (r *Registry) GetAlpinePlugin(name string) (AlpinePlugin, bool)
- func (r *Registry) GetComponentPlugin(name string) (ComponentPlugin, bool)
- func (r *Registry) GetThemePlugin(name string) (ThemePlugin, bool)
- func (r *Registry) Has(name string) bool
- func (r *Registry) Hooks() *HookManager
- func (r *Registry) Initialize(ctx context.Context) error
- func (r *Registry) Register(p Plugin) error
- func (r *Registry) ResolveDependencies() error
- func (r *Registry) Shutdown(ctx context.Context) error
- func (r *Registry) TopologicalSort() ([]Plugin, error)
- func (r *Registry) Unregister(name string) error
- func (r *Registry) Use(plugins ...Plugin) *Registry
- type Script
- type ThemePlugin
- type ThemePluginBase
- func (t *ThemePluginBase) AddFont(font theme.Font)
- func (t *ThemePluginBase) AddTheme(name string, th theme.Theme)
- func (t *ThemePluginBase) CSS() string
- func (t *ThemePluginBase) DefaultTheme() string
- func (t *ThemePluginBase) Fonts() []theme.Font
- func (t *ThemePluginBase) Themes() map[string]theme.Theme
- type Version
- type VersionConstraint
Constants ¶
const ( HookBeforeInit = "before_init" HookAfterInit = "after_init" HookBeforeShutdown = "before_shutdown" HookAfterShutdown = "after_shutdown" HookBeforeRender = "before_render" HookAfterRender = "after_render" HookBeforeHead = "before_head" HookAfterHead = "after_head" HookBeforeBody = "before_body" HookAfterBody = "after_body" HookBeforeScripts = "before_scripts" HookAfterScripts = "after_scripts" )
Hook names for plugin lifecycle and rendering.
Available hooks:
Lifecycle hooks:
- before_init: Before plugin initialization
- after_init: After plugin initialization
- before_shutdown: Before plugin shutdown
- after_shutdown: After plugin shutdown
Render hooks:
- before_render: Before page render
- after_render: After page render
- before_head: Before <head> content
- after_head: After <head> content
- before_body: Before <body> content
- after_body: After </body> (scripts area)
- before_scripts: Before script tags
- after_scripts: After script tags
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type AlpineComponent ¶
type AlpineComponent struct {
// Name is the component name (used with x-data="name")
Name string
// Definition is the JavaScript function returning the component state
// Signature: (...args) => ({ state, methods })
Definition string
}
AlpineComponent represents an Alpine.data component.
Components encapsulate reactive state and methods that can be reused across the application.
Example:
AlpineComponent{
Name: "dropdown",
Definition: `
() => ({
open: false,
toggle() {
this.open = !this.open;
},
close() {
this.open = false;
}
})
`,
}
type AlpineDirective ¶
type AlpineDirective struct {
// Name is the directive name (used as x-name)
Name string
// Definition is the JavaScript function implementing the directive
// Signature: (el, { expression, modifiers }, { evaluate, effect, cleanup }) => {}
Definition string
}
AlpineDirective represents a custom Alpine directive.
Directives extend Alpine's x- attribute system. The definition is JavaScript code that Alpine will execute.
Example:
AlpineDirective{
Name: "sortable",
Definition: `
(el, { expression, modifiers }, { evaluate }) => {
let options = expression ? evaluate(expression) : {};
new Sortable(el, options);
}
`,
}
type AlpineMagic ¶
type AlpineMagic struct {
// Name is the magic property name (accessed via $name)
Name string
// Definition is the JavaScript function that returns the magic value
// Signature: (el, { Alpine }) => value
Definition string
}
AlpineMagic represents a custom magic property.
Magic properties are accessed via $name and can return any value.
Example:
AlpineMagic{
Name: "clipboard",
Definition: `
(el) => {
return {
copy(text) {
navigator.clipboard.writeText(text);
}
}
}
`,
}
type AlpinePlugin ¶
type AlpinePlugin interface {
Plugin
// Scripts returns external scripts to load (libraries, dependencies).
Scripts() []Script
// Directives returns custom Alpine directives.
Directives() []AlpineDirective
// Stores returns Alpine stores to register globally.
Stores() []AlpineStore
// Magics returns custom magic properties.
Magics() []AlpineMagic
// AlpineComponents returns Alpine.data components.
AlpineComponents() []AlpineComponent
}
AlpinePlugin extends Alpine.js functionality.
An AlpinePlugin can provide:
- External scripts/libraries to load
- Custom Alpine directives (e.g., x-sortable)
- Global Alpine stores for state management
- Magic properties (e.g., $myPlugin)
- Alpine.data components
Example:
type SortablePlugin struct {
*PluginBase
}
func (p *SortablePlugin) Scripts() []Script {
return []Script{
{Name: "sortablejs", URL: "https://cdn.jsdelivr.net/.../Sortable.min.js"},
}
}
func (p *SortablePlugin) Directives() []AlpineDirective {
return []AlpineDirective{
{Name: "sortable", Definition: "..."},
}
}
type AlpinePluginBase ¶
type AlpinePluginBase struct {
*PluginBase
}
AlpinePluginBase provides default implementations for AlpinePlugin. Embed this to implement only the methods you need.
func NewAlpinePluginBase ¶
func NewAlpinePluginBase(info PluginInfo) *AlpinePluginBase
NewAlpinePluginBase creates a new AlpinePluginBase.
func (*AlpinePluginBase) AlpineComponents ¶
func (a *AlpinePluginBase) AlpineComponents() []AlpineComponent
AlpineComponents returns an empty slice by default.
func (*AlpinePluginBase) Directives ¶
func (a *AlpinePluginBase) Directives() []AlpineDirective
Directives returns an empty slice by default.
func (*AlpinePluginBase) Magics ¶
func (a *AlpinePluginBase) Magics() []AlpineMagic
Magics returns an empty slice by default.
func (*AlpinePluginBase) Scripts ¶
func (a *AlpinePluginBase) Scripts() []Script
Scripts returns an empty slice by default.
func (*AlpinePluginBase) Stores ¶
func (a *AlpinePluginBase) Stores() []AlpineStore
Stores returns an empty slice by default.
type AlpineStore ¶
type AlpineStore struct {
// Name is the store identifier (accessed via $store.name)
Name string
// InitialState is the initial state as a map
InitialState map[string]any
// Methods is JavaScript code defining store methods
// These are merged with the initial state
Methods string
}
AlpineStore represents a global Alpine store.
Stores provide reactive state accessible via $store.storeName. The initial state and methods are combined into a single object.
Example:
AlpineStore{
Name: "notifications",
InitialState: map[string]any{
"items": []any{},
"count": 0,
},
Methods: `
add(item) {
this.items.push(item);
this.count++;
},
remove(id) {
this.items = this.items.filter(i => i.id !== id);
this.count--;
}
`,
}
type ComponentConstructor ¶
ComponentConstructor is a function that creates a component. It receives props (any type) and optional children nodes.
Example:
func lineChartConstructor(props any, children ...g.Node) g.Node {
opts := props.(*ChartOptions)
return html.Div(
html.Class("chart-container"),
g.Attr("data-chart-type", "line"),
g.Attr("data-chart-data", opts.DataJSON()),
g.Group(children),
)
}
type ComponentPlugin ¶
type ComponentPlugin interface {
Plugin
// Components returns a map of component names to their constructors.
// Component names should be CamelCase and unique.
Components() map[string]ComponentConstructor
// CVAExtensions returns CVA configurations for component variants.
// The keys should match component names from Components().
CVAExtensions() map[string]*forgeui.CVA
}
ComponentPlugin extends ForgeUI with new UI components.
A ComponentPlugin provides:
- Component constructors for creating new components
- Optional CVA extensions for variant styling
Example:
type ChartPlugin struct {
*ComponentPluginBase
}
func NewChartPlugin() *ChartPlugin {
return &ChartPlugin{
ComponentPluginBase: NewComponentPluginBase(
PluginInfo{Name: "charts", Version: "1.0.0"},
map[string]ComponentConstructor{
"LineChart": lineChartConstructor,
"BarChart": barChartConstructor,
},
),
}
}
type ComponentPluginBase ¶
type ComponentPluginBase struct {
*PluginBase
// contains filtered or unexported fields
}
ComponentPluginBase provides a base implementation for component plugins. Embed this in your plugin to inherit default behavior.
Example:
type MyPlugin struct {
*ComponentPluginBase
}
func NewComponentPluginBase ¶
func NewComponentPluginBase(info PluginInfo, components map[string]ComponentConstructor) *ComponentPluginBase
NewComponentPluginBase creates a new ComponentPluginBase.
func NewComponentPluginBaseWithCVA ¶
func NewComponentPluginBaseWithCVA( info PluginInfo, components map[string]ComponentConstructor, cva map[string]*forgeui.CVA, ) *ComponentPluginBase
NewComponentPluginBaseWithCVA creates a ComponentPluginBase with CVA extensions.
func (*ComponentPluginBase) AddCVA ¶
func (c *ComponentPluginBase) AddCVA(name string, cva *forgeui.CVA)
AddCVA adds a CVA configuration for a component.
func (*ComponentPluginBase) AddComponent ¶
func (c *ComponentPluginBase) AddComponent(name string, constructor ComponentConstructor)
AddComponent adds a component constructor to the plugin. This can be called during plugin initialization.
func (*ComponentPluginBase) CVAExtensions ¶
func (c *ComponentPluginBase) CVAExtensions() map[string]*forgeui.CVA
CVAExtensions returns the CVA configurations.
func (*ComponentPluginBase) Components ¶
func (c *ComponentPluginBase) Components() map[string]ComponentConstructor
Components returns the component constructors.
type Dependency ¶
type Dependency struct {
Name string
Version string // semver constraint, e.g., ">=1.0.0"
Optional bool
}
Dependency represents a plugin dependency with version constraints.
func (Dependency) Satisfies ¶
func (d Dependency) Satisfies(version string) bool
Satisfies checks if a version satisfies the constraint. Supports: =, >=, <=, >, <, ~, ^
type HookContext ¶
type HookContext struct {
Context context.Context //nolint:containedctx // Context is intentionally passed to hook handlers
Data map[string]any
Nodes []g.Node // For render hooks
}
HookContext provides context to hook handlers.
type HookManager ¶
type HookManager struct {
// contains filtered or unexported fields
}
HookManager manages hook registration and execution.
func (*HookManager) Count ¶
func (m *HookManager) Count(hook string) int
Count returns the number of handlers for a hook.
func (*HookManager) Has ¶
func (m *HookManager) Has(hook string) bool
Has checks if a hook has any handlers registered.
func (*HookManager) Off ¶
func (m *HookManager) Off(hook string)
Off removes all handlers for a hook.
func (*HookManager) On ¶
func (m *HookManager) On(hook string, fn HookFunc)
On registers a hook handler.
func (*HookManager) Trigger ¶
func (m *HookManager) Trigger(hook string, ctx *HookContext) error
Trigger executes all handlers for a hook. Returns the first error encountered.
type MiddlewarePlugin ¶
type MiddlewarePlugin interface {
Plugin
// Middleware returns the HTTP middleware function.
// The middleware should call next.ServeHTTP(w, r) to continue the chain.
Middleware() func(http.Handler) http.Handler
// Priority determines execution order.
// Lower values execute first (e.g., 1 = first, 100 = last).
// Default priority if not specified: 50.
Priority() int
}
MiddlewarePlugin provides HTTP middleware for the application.
Middleware plugins can intercept and modify HTTP requests/responses. They are executed in priority order (lower priority = executes first).
Example:
type HTMXPlugin struct {
*PluginBase
}
func (p *HTMXPlugin) Middleware() func(http.Handler) http.Handler {
return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// Detect HTMX requests
if r.Header.Get("HX-Request") == "true" {
ctx := context.WithValue(r.Context(), htmxRequestKey, true)
r = r.WithContext(ctx)
}
next.ServeHTTP(w, r)
})
}
}
func (p *HTMXPlugin) Priority() int {
return 10 // Execute early
}
type MiddlewarePluginBase ¶
type MiddlewarePluginBase struct {
*PluginBase
// contains filtered or unexported fields
}
MiddlewarePluginBase provides a base implementation for middleware plugins.
func NewMiddlewarePluginBase ¶
func NewMiddlewarePluginBase( info PluginInfo, middleware func(http.Handler) http.Handler, priority int, ) *MiddlewarePluginBase
NewMiddlewarePluginBase creates a new MiddlewarePluginBase.
func (*MiddlewarePluginBase) Middleware ¶
func (m *MiddlewarePluginBase) Middleware() func(http.Handler) http.Handler
Middleware returns the middleware function.
func (*MiddlewarePluginBase) Priority ¶
func (m *MiddlewarePluginBase) Priority() int
Priority returns the execution priority.
type Plugin ¶
type Plugin interface {
// Name returns the unique plugin identifier
Name() string
// Version returns the plugin version (semver)
Version() string
// Description returns a human-readable description
Description() string
// Dependencies returns required plugin dependencies
Dependencies() []Dependency
// Init initializes the plugin
Init(ctx context.Context, registry *Registry) error
// Shutdown cleanly shuts down the plugin
Shutdown(ctx context.Context) error
}
Plugin is the base interface all plugins must implement.
type PluginBase ¶
type PluginBase struct {
// contains filtered or unexported fields
}
PluginBase provides a base implementation for plugins. Embed this in custom plugins to inherit default implementations.
func NewPluginBase ¶
func NewPluginBase(info PluginInfo) *PluginBase
NewPluginBase creates a new PluginBase with the given info.
func (*PluginBase) Dependencies ¶
func (p *PluginBase) Dependencies() []Dependency
Dependencies returns the plugin dependencies.
func (*PluginBase) Description ¶
func (p *PluginBase) Description() string
Description returns the plugin description.
func (*PluginBase) Init ¶
func (p *PluginBase) Init(ctx context.Context, r *Registry) error
Init is the default implementation (override in plugins).
func (*PluginBase) Shutdown ¶
func (p *PluginBase) Shutdown(ctx context.Context) error
Shutdown is the default implementation (override in plugins).
func (*PluginBase) Version ¶
func (p *PluginBase) Version() string
Version returns the plugin version.
type PluginInfo ¶
type PluginInfo struct {
Name string
Version string
Description string
Author string
License string
Homepage string
Repository string
Tags []string
Dependencies []Dependency
}
PluginInfo contains plugin metadata.
type Registry ¶
type Registry struct {
// contains filtered or unexported fields
}
Registry manages plugin registration and lifecycle.
func (*Registry) CollectAlpineComponents ¶
func (r *Registry) CollectAlpineComponents() []AlpineComponent
CollectAlpineComponents collects all Alpine.data components from Alpine plugins.
func (*Registry) CollectComponents ¶
func (r *Registry) CollectComponents() map[string]ComponentConstructor
CollectComponents collects all component constructors from component plugins. Returns a map of component name to constructor.
func (*Registry) CollectDirectives ¶
func (r *Registry) CollectDirectives() []AlpineDirective
CollectDirectives collects all Alpine directives from Alpine plugins.
func (*Registry) CollectMagics ¶
func (r *Registry) CollectMagics() []AlpineMagic
CollectMagics collects all Alpine magic properties from Alpine plugins.
func (*Registry) CollectMiddleware ¶
func (r *Registry) CollectMiddleware() []MiddlewarePlugin
CollectMiddleware returns all middleware plugins in priority order. Middleware is already sorted by priority in the registry.
func (*Registry) CollectScripts ¶
CollectScripts collects all scripts from Alpine plugins. Scripts are returned in priority order (lower priority first).
func (*Registry) CollectStores ¶
func (r *Registry) CollectStores() []AlpineStore
CollectStores collects all Alpine stores from Alpine plugins.
func (*Registry) Discover ¶
Discover loads plugins from a directory (Go plugins).
Note: This is optional and experimental. Go plugins have significant limitations:
- Only supported on Linux, FreeBSD, and macOS
- Must be built with the same Go version as the main program
- Must use the same versions of all dependencies
- Cannot be unloaded once loaded
For production use, consider statically linking plugins instead.
Plugin files must:
- Have a .so extension
- Export a variable named "Plugin" of type plugin.Plugin
Example plugin:
package main
import "github.com/xraph/forgeui/plugin"
var Plugin = &MyPlugin{}
type MyPlugin struct {
plugin.PluginBase
}
func (*Registry) DiscoverSafe ¶
DiscoverSafe is like Discover but continues on error, collecting all errors. Returns all errors encountered during discovery.
func (*Registry) GetAlpinePlugin ¶
func (r *Registry) GetAlpinePlugin(name string) (AlpinePlugin, bool)
GetAlpinePlugin retrieves an Alpine plugin by name.
func (*Registry) GetComponentPlugin ¶
func (r *Registry) GetComponentPlugin(name string) (ComponentPlugin, bool)
GetComponentPlugin retrieves a component plugin by name.
func (*Registry) GetThemePlugin ¶
func (r *Registry) GetThemePlugin(name string) (ThemePlugin, bool)
GetThemePlugin retrieves a theme plugin by name.
func (*Registry) Hooks ¶
func (r *Registry) Hooks() *HookManager
Hooks returns the hook manager for this registry.
func (*Registry) Initialize ¶
Initialize initializes all plugins in dependency order.
The initialization process: 1. Resolves all dependencies 2. Performs topological sort to determine order 3. Initializes each plugin in order 4. Triggers before_init and after_init hooks
If any plugin fails to initialize, the process stops and returns an error.
func (*Registry) Register ¶
Register adds a plugin to the registry. Automatically detects plugin type and stores in appropriate registry.
func (*Registry) ResolveDependencies ¶
ResolveDependencies checks all dependencies are satisfied.
func (*Registry) Shutdown ¶
Shutdown shuts down all plugins in reverse initialization order.
Unlike initialization, shutdown attempts to shut down all plugins even if some fail. All errors are collected and returned as a single error.
This ensures that resources are cleaned up as much as possible.
func (*Registry) TopologicalSort ¶
TopologicalSort returns plugins in dependency order using Kahn's algorithm. Returns an error if a circular dependency is detected.
func (*Registry) Unregister ¶
Unregister removes a plugin.
type Script ¶
type Script struct {
// Name is a unique identifier for the script
Name string
// URL is the script source (https://... or /path/to/script.js)
URL string
// Inline is JavaScript code to execute (alternative to URL)
Inline string
// Defer adds the defer attribute (loads after HTML parsing)
Defer bool
// Async adds the async attribute (loads asynchronously)
Async bool
// Priority determines load order (lower = loads first)
// Default: 50. Use 1-10 for critical dependencies.
Priority int
// Module adds type="module" attribute
Module bool
// Integrity is the SRI hash for the script
Integrity string
// Crossorigin sets the crossorigin attribute
Crossorigin string
}
Script represents an external script to load.
Scripts can be loaded from URLs or inlined. Priority determines load order (lower values load first).
Example:
Script{
Name: "chartjs",
URL: "https://cdn.jsdelivr.net/npm/chart.js",
Priority: 10,
Defer: true,
}
type ThemePlugin ¶
type ThemePlugin interface {
Plugin
// Themes returns a map of theme names to Theme configurations.
// Theme names should be unique and descriptive (e.g., "corporate-light").
Themes() map[string]theme.Theme
// DefaultTheme returns the name of the default theme to use.
// This should match one of the keys from Themes().
// Return empty string to let the system choose.
DefaultTheme() string
// CSS returns additional CSS to inject.
// This can include custom properties, utility classes, or font-face rules.
CSS() string
// Fonts returns fonts to load.
// These will be loaded automatically via Google Fonts or custom URLs.
Fonts() []theme.Font
}
ThemePlugin provides custom themes for ForgeUI.
A ThemePlugin can provide:
- Multiple theme presets (light/dark variants)
- Additional CSS for custom properties or utilities
- Custom fonts to load
Example:
type CorporateThemePlugin struct {
*PluginBase
}
func (p *CorporateThemePlugin) Themes() map[string]theme.Theme {
return map[string]theme.Theme{
"corporate-light": corporateLightTheme,
"corporate-dark": corporateDarkTheme,
}
}
func (p *CorporateThemePlugin) Fonts() []theme.Font {
return []theme.Font{
{Family: "Inter", Weights: []int{400, 600, 700}},
}
}
type ThemePluginBase ¶
type ThemePluginBase struct {
*PluginBase
// contains filtered or unexported fields
}
ThemePluginBase provides default implementations for ThemePlugin. Embed this to implement only the methods you need.
func NewThemePluginBase ¶
func NewThemePluginBase( info PluginInfo, themes map[string]theme.Theme, defaultTheme string, ) *ThemePluginBase
NewThemePluginBase creates a new ThemePluginBase.
func NewThemePluginBaseWithFonts ¶
func NewThemePluginBaseWithFonts( info PluginInfo, themes map[string]theme.Theme, defaultTheme string, fonts []theme.Font, ) *ThemePluginBase
NewThemePluginBaseWithFonts creates a ThemePluginBase with fonts.
func (*ThemePluginBase) AddFont ¶
func (t *ThemePluginBase) AddFont(font theme.Font)
AddFont adds a font to the plugin.
func (*ThemePluginBase) AddTheme ¶
func (t *ThemePluginBase) AddTheme(name string, th theme.Theme)
AddTheme adds a theme to the plugin. This can be called during plugin initialization.
func (*ThemePluginBase) CSS ¶
func (t *ThemePluginBase) CSS() string
CSS returns empty string by default. Override this method to provide custom CSS.
func (*ThemePluginBase) DefaultTheme ¶
func (t *ThemePluginBase) DefaultTheme() string
DefaultTheme returns the default theme name.
func (*ThemePluginBase) Fonts ¶
func (t *ThemePluginBase) Fonts() []theme.Font
Fonts returns the font configurations.
type Version ¶
Version represents a semantic version.
func ParseVersion ¶
ParseVersion parses a semantic version string.
type VersionConstraint ¶
VersionConstraint represents a version constraint.
func ParseConstraint ¶
func ParseConstraint(constraint string) (*VersionConstraint, error)
ParseConstraint parses a version constraint string.
func (*VersionConstraint) Check ¶
func (c *VersionConstraint) Check(version *Version) bool
Check checks if a version satisfies the constraint.
func (*VersionConstraint) String ¶
func (c *VersionConstraint) String() string
String returns the constraint as a string.