Plugin System Overview

Shevky uses a hook-driven plugin model. Plugins are npm packages that export a definition object. The core discovers them from configuration, dynamically imports them, and invokes their hook handlers at defined build lifecycle points.

Discovery

Plugins are listed in src/site.json under the plugins key:

"plugins": [
  "@shevky/plugin-robots-txt",
  "@shevky/plugin-tailwindcss",
  "@shevky/plugin-rss"
]

Registration Contract

A plugin must default-export an object matching this shape:

export default {
  name: "my-plugin",       // unique identifier
  version: "0.0.1",        // semantic version
  hooks: { ... },          // hook handlers
  load: (ctx) => { ... },  // optional initialization
};

The formal TypeScript type is PluginDefinition in base/src/plugin.d.ts.

Hook Lifecycle

The build pipeline executes hooks in this order:

OrderHookWhenBest For
1dist:cleanAfter dist/ is createdGenerating dist-level files (robots.txt)
2assets:copyAfter static assets are copiedCSS/JS compilation (Tailwind, esbuild)
3content:loadAfter Markdown files are loadedInjecting external content (addContent)
4content:readyAfter collections are builtGenerating feeds, sitemaps, SEO metadata
5page:metaPer page during renderingPage-level metadata enrichment

Hooks 1-4 run once per build. Hook 5 runs once per rendered page.

Runtime Context

Every hook handler receives a context object with:

  • config - full site configuration with get(key) for plugin-specific config
  • log - logging functions (info, warn, err, debug, step)
  • file - read, write, exists
  • directory - read, exists, create
  • path - combine, resolve, name
  • paths - project directories (root, src, dist, content, etc.)
  • i18n - internationalization helpers

Hook-specific additions:

HookExtra Context
content:loadcontentFiles, addContent()
content:readycontentFiles, pages, footerPolicies, contentIndex
page:metafrontMatter, derivedFrontMatter, lang, slug, pageMeta, setPageMeta()

Plugin Configuration

Plugins read their settings from pluginConfigs in site.json:

"pluginConfigs": {
  "shevky-rss": { "feedFilename": "feed.xml", "feedTtl": 1440 }
}

Accessed at runtime via ctx.config.get("shevky-rss"). The key must match the plugin's exported name field.

Error Behavior

  • Plugin loading errors are caught and logged; the build continues without the failed plugin.
  • Hook execution errors are caught and logged; remaining plugins and build stages continue.
  • Implication: builds can succeed while plugin output is incomplete. Check build logs.

Related