definePlugin
Identity helper that types a plugin module's default export as a ZfbPlugin, surfacing hook signatures and field-level typos at compile time.
Signature
definePlugin(plugin: ZfbPlugin): ZfbPlugindefinePlugin is exported from @takazudo/. Like defineConfig, it is an identity helper: it returns its argument unchanged. Its only job is to type the object literal as ZfbPlugin so an editor surfaces field-level types and hook-signature typos at compile time.
// plugins/my-plugin.ts
import { definePlugin } from "@takazudo/zfb/plugins";
export default definePlugin({
name: "my-plugin",
async preBuild({ outDir, logger }) {
logger.info(`generating index into ${outDir}`);
},
});A plugin module's default export must be the object returned by definePlugin (or an equivalent ZfbPlugin-shaped literal) — zfb.config.ts references the module by name, and the Rust-side plugin host loads it via dynamic import().
The ZfbPlugin shape
interface ZfbPlugin {
name: string;
setup?(ctx: ZfbSetupContext): Promise<void> | void;
preBuild?(ctx: ZfbBuildHookContext): Promise<void> | void;
postBuild?(ctx: ZfbBuildHookContext): Promise<void> | void;
devMiddleware?(ctx: ZfbDevMiddlewareContext): Promise<void> | void;
previewMiddleware?(ctx: ZfbPreviewMiddlewareContext): Promise<void> | void;
}name is informational — the resolved module specifier, not name, is what zfb uses to identify the plugin internally; name only surfaces in log/error lines. All five hooks are optional and independent; a plugin declares any subset. Declaration order in zfb.config.ts's plugins array matters when multiple plugins touch the same surface.
See Plugins for the full lifecycle narrative — hook ordering, setup's four registration methods, the devMiddleware/previewMiddleware request/response contract, and worked examples.
Hook signatures
setup?(ctx: ZfbSetupContext)— runs once per host boot (zfb build,zfb dev, andzfb preview), beforepreBuild.ctxexposescommand("build" | "dev" | "preview"),projectRoot,config,options,logger, and four registration methods:addAlias,addVirtualModule,injectRoute,addClientEntry.addVirtualModule(specifier, loader, options?)takes an optional third argument,ZfbVirtualModuleOptions, whosewatchFiles?: string[]names absolute pathszfb devwatches on the loader's behalf — a change to one re-invokes that loader with its memoised source bypassed. Registrations made under"preview"are accepted for shape-consistency but are inert — preview never re-enters the scan → bundle → render pipeline those registries feed.preBuild?(ctx: ZfbBuildHookContext)— file-generation work before the bundler/renderer/CSS/islands passes. Runs once perzfb buildand once perzfb devboot. Does not fire underzfb preview.postBuild?(ctx: ZfbBuildHookContext)— finalisation afterdist/has been fully written.ctx.routes— the complete route manifest — is present only here (undefinedonpreBuild). Does not fire underzfb preview.devMiddleware?(ctx: ZfbDevMiddlewareContext)— registers per-request HTTP handlers forzfb devviactx.register(path, handler). Fires only duringzfb dev.previewMiddleware?(ctx: ZfbPreviewMiddlewareContext)— thezfb preview(static mode) counterpart ofdevMiddleware; same registration call and request/response shape, but a separate per-mode opt-in — a plugin registering onlydevMiddlewaregets no handlers underzfb preview.
Both preBuild/postBuild receive { projectRoot, outDir, config, options, logger } (plus routes on postBuild); devMiddleware/previewMiddleware receive { projectRoot, config, options, logger, register }; setup receives { command, projectRoot, config, options, logger, addAlias, addVirtualModule, injectRoute, addClientEntry }. options on every context is the plugin's own PluginConfig.options block, copied verbatim.
ZfbBuildHookContext, ZfbDevMiddlewareContext, ZfbPreviewMiddlewareContext, ZfbSetupContext, ZfbPluginLogger, ZfbRouteEntry, ZfbRouteManifest, ZfbVirtualModuleLoader, and ZfbVirtualModuleOptions are all exported from @takazudo/ alongside ZfbPlugin and definePlugin.
See also
Plugins — the full plugin hook contract, worked examples, and conflict-detection errors (
AliasConflict,VirtualModuleConflict,InjectRouteConflict,ClientEntryConflict,InvalidClientEntry).defineConfig— wiringplugins: [{ name: "...", options: {...} }]inzfb.config.ts.definePreset— stamping preset-contributed plugins with provenance so relative-path plugin specifiers resolve against the preset package.