Plugin System Overview
Extend tulip with TypeScript/JavaScript plugins that hook into every stage of the build pipeline.
How plugins work
Tulip plugins are TypeScript or JavaScript modules with typed contracts. Each plugin lives in its own directory under plugins/ and declares the lifecycle hooks it needs. The engine loads plugins in the order they appear in tulip.toml and calls each hook at the appropriate stage of the build.
Plugin structure
Every plugin follows a standard layout:
plugins/seo/
plugin.toml # metadata and default config
index.ts # entry point (TulipPlugin interface)
types.d.ts # TypeScript type definitions
README.md # documentationplugin.toml— declares the plugin name, version, description, author, and default configuration values.index.ts— exports aTulipPluginobject with one or more lifecycle hooks.types.d.ts— provides TypeScript definitions for the plugin contract.
Installing plugins
Install a plugin from a GitHub repository:
tulip plugin add https://github.com/user/tulip-plugin-seo@v0.1.0This downloads the plugin, reads the name from plugin.toml, installs to plugins/{name}/, and saves the source URL in tulip.toml. Pin a version with @tag.
After cloning a project, install all missing plugins:
tulip installYou can also scaffold a new plugin locally:
tulip plugin new seoLifecycle hooks
Plugins can implement any combination of hooks. All hooks are optional:
| Hook | When it fires |
|---|---|
setup(config) | Once when the plugin is loaded |
beforeBuild(ctx) | Before the build starts |
transformMarkdown(content, page) | Before markdown is converted to HTML |
transformContext(ctx, page) | Before template rendering |
transformHtml(html, page) | After template rendering |
head() | Collect tags to inject into the HTML head |
generatePages(ctx) | Create new files (RSS, sitemap, etc.) |
afterBuild(ctx) | After the build completes |
Execution order
Plugins run in the order they are declared in tulip.toml. For hooks that transform content, the output of one plugin is passed as input to the next:
[[plugins]]
name = "seo" # runs first
config = { site_name = "My Site" }
[[plugins]]
name = "analytics" # runs second
config = { id = "G-XXXXX" }Per-hook timing
Every hook reports its execution time in the build output. Hooks exceeding 500ms are flagged as slow:
plugin: seo.setup 0.1ms
plugin: seo.beforeBuild 0.2ms
plugin: seo.transformHtml 0.3ms
plugin: seo.generatePages 0.5ms
plugin: analytics.transformHtml 0.2ms
plugin: analytics.afterBuild 1.2ms (slow)Configuration
Default config values are defined in plugin.toml. You override them per-project in tulip.toml:
# plugin.toml (defaults)
[config]
site_name = "My Site"
twitter_handle = ""
# tulip.toml (project overrides)
[[plugins]]
name = "seo"
config = { site_name = "My Blog", twitter_handle = "@myblog" }The merged config object is passed to the setup hook when the plugin loads.