Lesson 08-Bun.js Plugins and Extensions

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:

  1. Plugin discovery phase
  2. Dependency resolution phase
  3. Initialization phase
  4. Runtime integration phase

Plugin Type Classification

Core Plugin Types:

TypePurposeExamples
Middleware PluginsExtend request processing pipelineLogging middleware, Authentication middleware
Command PluginsExtend CLI commandsProject scaffolding commands
Hook PluginsInject lifecycle logicDatabase migration plugins
Compiler PluginsExtend module resolutionSass/Less preprocessors
Utility PluginsProvide utility toolsPerformance 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.toml

Plugin 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 MyFirstPlugin

CLI 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 conventions

Advanced 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 → middleware3

Command 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.md

Documentation Guidelines:

# my-bun-plugin

## Features
- Feature 1 description
- Feature 2 description

## Installation
```bash
bun add my-bun-plugin

Usage

As Middleware

import MyPlugin from 'my-bun-plugin'
app.registerPlugin(MyPlugin)

CLI Commands

bun run my-plugin greet --name=World

Configuration Options

OptionTypeDefaultDescription
debugbooleanfalseEnable debug mode
“`
Share your love