Plugin System Basics
Plugin Architecture Design
Bun Plugin Core Mechanism:
// Plugin base structure example
interface BunPlugin {
name: string // Unique plugin identifier
setup?: (app: Bun.Application) => void // Application initialization hook
commands?: Record<string, CommandHandler> // CLI commands
hooks?: Record<string, HookHandler> // Lifecycle hooks
}
// Actual plugin example
const MyPlugin: BunPlugin = {
name: 'my-plugin',
// Initialize during application startup
setup(app) {
app.use((ctx, next) => {
console.log('Request intercepted by my-plugin')
return next()
})
},
// Register CLI commands
commands: {
'greet [name]': async (args) => {
console.log(`Hello, ${args.name || 'World'} from my-plugin!`)
}
},
// Lifecycle hooks
hooks: {
'beforeStart': () => {
console.log('Plugin: Preparing to start...')
},
'afterStart': () => {
console.log('Plugin: Application started!')
}
}
}
// Register plugin
const app = new Bun.Application()
app.registerPlugin(MyPlugin)Plugin Loading Process:
- Plugin discovery phase
- Dependency resolution phase
- Initialization phase
- Runtime integration phase
Plugin Type Classification
Core Plugin Types:
| Type | Purpose | Examples |
|---|---|---|
| Middleware Plugins | Extend request processing pipeline | Logging middleware, Authentication middleware |
| Command Plugins | Extend CLI commands | Project scaffolding commands |
| Hook Plugins | Inject lifecycle logic | Database migration plugins |
| Compiler Plugins | Extend module resolution | Sass/Less preprocessors |
| Utility Plugins | Provide utility tools | Performance monitoring plugins |
Development Practice
Creating Your First Plugin
File Structure:
my-bun-plugin/
├── src/
│ ├── index.ts # Plugin entry point
│ └── commands/ # CLI commands
│ └── greet.ts
├── package.json
└── bunfig.tomlPlugin Implementation:
// src/index.ts
import type { BunPlugin } from 'bun'
export const MyFirstPlugin: BunPlugin = {
name: 'my-first-plugin',
setup(app) {
// Add request logging middleware
app.use((ctx, next) => {
const start = Date.now()
console.log(`[${new Date().toISOString()}] ${ctx.request.method} ${ctx.request.url}`)
return next().then(() => {
console.log(`[${new Date().toISOString()}] ${ctx.response.status} ${Date.now() - start}ms`)
})
})
},
commands: {
'greet [name]': async (args) => {
console.log(`Hello, ${args.name || 'Plugin User'} from my-first-plugin!`)
}
}
}
// Export plugin
export default MyFirstPluginCLI Command Implementation:
// src/commands/greet.ts
export async function greetCommand(args: { name?: string }) {
console.log(`Custom greet command: Hello, ${args.name || 'World'}!`)
}Plugin Registration and Distribution
Local Plugin Registration:
// Use plugin in application
import MyFirstPlugin from './plugins/my-first-plugin'
const app = new Bun.Application()
app.registerPlugin(MyFirstPlugin)
// Or dynamically load
import('my-bun-plugin').then(module => {
app.registerPlugin(module.default)
})Plugin Publishing and Installation:
# Publish plugin to private registry
bun publish --access=private
# Install plugin
bun add my-bun-plugin
# Automatically load in application (by convention)
# Bun will automatically load plugins from node_modules that follow naming conventionsAdvanced Extension Techniques
Middleware Extension System
Custom Middleware Plugin:
// src/middleware-plugin.ts
import type { BunPlugin } from 'bun'
export const MiddlewarePlugin: BunPlugin = {
name: 'middleware-plugin',
setup(app) {
// Request timing middleware
app.use((ctx, next) => {
const start = performance.now()
return next().then(() => {
const duration = performance.now() - start
ctx.response.headers.set('X-Response-Time', `${duration.toFixed(2)}ms`)
})
})
// Error handling middleware
app.use(async (ctx, next) => {
try {
await next()
} catch (err) {
console.error('Middleware error:', err)
ctx.response.status = 500
ctx.response.body = { error: 'Internal Server Error' }
}
})
}
}Middleware Execution Order Control:
app.use(middleware1) // Executed first
app.use(middleware2) // Executed second
app.use(middleware3) // Executed last
// Actual execution order: middleware1 → middleware2 → business logic → middleware3Command Line Extension
Complex Command Implementation:
// src/commands/init-project.ts
import { Command } from 'bun'
export async function initProjectCommand(args: {
template?: string
force?: boolean
}) {
const { template = 'default', force = false } = args
console.log(`Initializing project with template: ${template}`)
if (force) {
console.log('Force mode enabled - overwriting existing files')
}
// Actual project initialization logic...
}
// Register command in plugin
commands: {
'init [template]': {
description: 'Initialize a new project',
options: [
{ name: 'force', alias: 'f', type: 'boolean' }
],
handler: initProjectCommand
}
}Command Autocompletion:
// Register completion script in plugin
hooks: {
'registerCompletions': () => {
return {
'my-plugin init': {
description: 'Initialize a new project',
options: [
{ name: '--force', description: 'Overwrite existing files' }
]
}
}
}
}Lifecycle Hooks
Core Lifecycle:
hooks: {
// Before application start
'beforeStart': () => {
console.log('Running pre-start tasks...')
},
// After application start
'afterStart': () => {
console.log('Application is running!')
},
// Request processing start
'requestStart': (ctx) => {
ctx.state.startTime = Date.now()
},
// Request processing end
'requestEnd': (ctx) => {
const duration = Date.now() - ctx.state.startTime
console.log(`Request took ${duration}ms`)
}
}Custom Hook Extension:
// Extend plugin system to support custom hooks
interface ExtendedBunPlugin extends BunPlugin {
customHooks?: Record<string, HookHandler>
}
// Usage example
const MyPlugin: ExtendedBunPlugin = {
name: 'my-plugin',
customHooks: {
'dataProcessing': (data) => {
console.log('Processing data:', data)
return data.toUpperCase()
}
}
}
// Trigger custom hook in application
app.triggerHook('dataProcessing', 'sample-data')Performance Optimization Techniques
Plugin Lazy Loading
Dynamic Plugin Loading Implementation:
// Load plugins on demand
async function loadPlugin(pluginName: string) {
if (!loadedPlugins.has(pluginName)) {
const pluginModule = await import(`./plugins/${pluginName}`)
app.registerPlugin(pluginModule.default)
loadedPlugins.add(pluginName)
}
}
// Lazy load plugin in route
app.get('/admin', async (ctx) => {
await loadPlugin('admin-panel') // Load on first access
// ...handle request
})Plugin Group Management:
// Group plugins by functionality
const pluginGroups = {
'core': ['logger', 'router'],
'dev': ['hot-reload', 'debugger'],
'prod': ['monitoring', 'compression']
}
// Environment-aware plugin loading
function loadPluginsByEnv(env: string) {
Object.entries(pluginGroups).forEach(([group, plugins]) => {
if (group === env || group === 'core') {
plugins.forEach(loadPlugin)
}
})
}Plugin Caching Mechanism
Plugin Cache Strategy:
// Plugin cache manager
class PluginCache {
private cache = new Map<string, any>()
async get(pluginName: string) {
if (this.cache.has(pluginName)) {
return this.cache.get(pluginName)
}
const plugin = await import(`./plugins/${pluginName}`)
this.cache.set(pluginName, plugin.default)
return plugin.default
}
clear() {
this.cache.clear()
}
}
// Use cache in application
const pluginCache = new PluginCache()
app.registerPlugin(await pluginCache.get('my-plugin'))Persistent Cache Implementation:
// Use filesystem to cache plugin compilation results
import { writeFileSync, readFileSync } from 'fs'
class PersistentPluginCache {
private cacheDir = './.plugin-cache'
constructor() {
if (!existsSync(this.cacheDir)) {
mkdirSync(this.cacheDir)
}
}
async get(pluginName: string) {
const cacheFile = `${this.cacheDir}/${pluginName}.cache`
if (existsSync(cacheFile)) {
const cached = readFileSync(cacheFile, 'utf8')
return JSON.parse(cached)
}
const plugin = await import(`./plugins/${pluginName}`)
const serialized = JSON.stringify(plugin.default)
writeFileSync(cacheFile, serialized)
return plugin.default
}
}Debugging and Maintenance
Plugin Debugging Techniques
Debugging Configuration:
// .vscode/launch.json
{
"version": "0.2.0",
"configurations": [
{
"type": "node",
"request": "launch",
"name": "Debug Bun Plugin",
"runtimeExecutable": "bun",
"runtimeArgs": ["run", "--inspect-brk", "src/index.ts"],
"port": 9229,
"cwd": "${workspaceFolder}"
}
]
}Logging Debugging Strategy:
// Debug logging for plugin development
const debug = require('debug')('my-plugin')
export const MyPlugin: BunPlugin = {
name: 'my-plugin',
setup(app) {
debug('Initializing plugin...')
app.use((ctx, next) => {
debug(`Request: ${ctx.request.method} ${ctx.request.url}`)
return next().then(() => {
debug(`Response: ${ctx.response.status}`)
})
})
}
}Version Compatibility Management
Plugin Version Constraints:
// package.json
{
"name": "my-bun-plugin",
"version": "1.0.0",
"peerDependencies": {
"bun": ">=1.0.0 <2.0.0"
},
"engines": {
"bun": ">=1.0.0"
}
}Compatibility Check Implementation:
// Check version during plugin initialization
setup(app) {
const bunVersion = process.env.BUN_VERSION || '0.0.0'
const [major] = bunVersion.split('.').map(Number)
if (major < 1) {
throw new Error(`my-plugin requires Bun >=1.0.0, current version: ${bunVersion}`)
}
// ...normal initialization...
}Best Practices
Plugin Development Guidelines
Recommended Directory Structure:
my-bun-plugin/
├── src/
│ ├── index.ts # Main entry point
│ ├── commands/ # CLI commands
│ ├── middleware/ # Middleware
│ ├── hooks/ # Lifecycle hooks
│ └── types/ # Type definitions
├── test/ # Test code
├── examples/ # Usage examples
├── package.json
└── README.mdDocumentation Guidelines:
# my-bun-plugin
## Features
- Feature 1 description
- Feature 2 description
## Installation
```bash
bun add my-bun-pluginUsage
As Middleware
import MyPlugin from 'my-bun-plugin'
app.registerPlugin(MyPlugin)CLI Commands
bun run my-plugin greet --name=WorldConfiguration Options
| Option | Type | Default | Description |
|---|---|---|---|
debug | boolean | false | Enable debug mode |
| “` |



