Skip to content

Plugins

EreoJS's plugin system allows extending the framework with custom functionality. Plugins can hook into the build process, add middleware, transform code, and more.

Import

ts
import {
  definePlugin,
  composePlugins,
  PluginRegistry,
  securityHeadersPlugin,
  isPlugin
} from '@ereo/core'

definePlugin

Creates a plugin with proper typing.

Signature

ts
function definePlugin(plugin: Plugin): Plugin

Plugin Interface

ts
interface Plugin {
  // Plugin name (required, must be unique)
  name: string

  // Plugin initialization — called once when the plugin is registered
  setup?: (context: PluginContext) => void | Promise<void>

  // Vite-compatible hooks
  resolveId?: (id: string) => string | null
  load?: (id: string) => string | null | Promise<string | null>
  transform?: (code: string, id: string) => string | null | Promise<string | null>
  // Return null from transform to indicate "no transformation" for this file

  // Build hooks
  buildStart?: () => void | Promise<void>
  buildEnd?: () => void | Promise<void>

  // Dev server hooks
  configureServer?: (server: DevServer) => void | Promise<void>

  // Framework hooks
  extendConfig?: (config: FrameworkConfig) => FrameworkConfig
  transformRoutes?: (routes: Route[]) => Route[]
  runtimeMiddleware?: MiddlewareHandler[]
  virtualModules?: Record<string, string>
}

Example

ts
const myPlugin = definePlugin({
  name: 'my-plugin',

  setup(context) {
    console.log('Plugin initialized')
  },

  transform(code, id) {
    if (id.endsWith('.tsx')) {
      // Transform TSX files
      return code.replace(/console\.log/g, 'logger.log')
    }
    return null // Return null to skip transformation for this file
  },

  buildStart() {
    console.log('Build starting...')
  },

  buildEnd() {
    console.log('Build complete!')
  }
})

composePlugins

Combines multiple plugins into a single plugin.

Signature

ts
function composePlugins(name: string, plugins: Plugin[]): Plugin

Example

ts
const combinedPlugin = composePlugins('combined', [
  loggingPlugin(),
  analyticsPlugin(),
  securityPlugin()
])

const app = createApp({
  plugins: [combinedPlugin]
})

PluginRegistry

Manages registered plugins.

Methods

MethodDescription
register(plugin)Register a single plugin
registerAll(plugins)Register multiple plugins
getPlugins()Get all registered plugins
getPlugin(name)Get a plugin by name
transform(code, id)Run all transform hooks
resolveId(id)Run all resolveId hooks
load(id)Run all load hooks
configureServer(server)Configure dev server
buildStart()Run all buildStart hooks
buildEnd()Run all buildEnd hooks

Example

ts
const registry = app.getPluginRegistry()

// Check if a plugin is registered
const tailwind = registry.getPlugin('tailwind')
if (tailwind) {
  console.log('Tailwind is configured')
}

// Get all plugins
const plugins = registry.getPlugins()
console.log(`${plugins.length} plugins registered`)

Built-in Plugins

securityHeadersPlugin

A built-in plugin that adds sensible security defaults to responses. The plugin is pre-configured with no options required.

ts
import { securityHeadersPlugin, createApp, defineConfig } from '@ereo/core'

const app = createApp({
  config: defineConfig({
    plugins: [securityHeadersPlugin]
  })
})

Note: For custom security header configuration, create a custom plugin using definePlugin.

Creating Custom Plugins

Basic Plugin

ts
import { definePlugin } from '@ereo/core'

export function myPlugin(options = {}) {
  return definePlugin({
    name: 'my-plugin',

    setup(context) {
      // Initialize with options
      console.log('Options:', options)
    }
  })
}

Transform Plugin

ts
export function importAliasPlugin(aliases: Record<string, string>) {
  return definePlugin({
    name: 'import-alias',

    resolveId(id) {
      for (const [alias, target] of Object.entries(aliases)) {
        if (id.startsWith(alias)) {
          return id.replace(alias, target)
        }
      }
      return null
    }
  })
}

// Usage
const app = createApp({
  plugins: [
    importAliasPlugin({
      '@/': './src/',
      '~/': './src/'
    })
  ]
})

Middleware Plugin

ts
export function requestIdPlugin() {
  return definePlugin({
    name: 'request-id',

    runtimeMiddleware: [
      async (request, context, next) => {
        const requestId = crypto.randomUUID()
        context.set('requestId', requestId)

        const response = await next()
        response.headers.set('X-Request-Id', requestId)

        return response
      }
    ]
  })
}

Dev Server Plugin

ts
export function devToolsPlugin() {
  return definePlugin({
    name: 'dev-tools',

    configureServer(server) {
      // Add middleware to the dev server
      // server.middlewares is a MiddlewareHandler[] array
      server.middlewares.push(async (request, context, next) => {
        const url = new URL(request.url)
        if (url.pathname === '/__dev/health') {
          return Response.json({ status: 'ok' })
        }
        return next()
      })

      // Watch additional files for changes
      server.watcher?.add('./config')
      server.watcher?.on('change', (file) => {
        console.log(`Config file changed: ${file}`)
        server.ws.send({ type: 'full-reload' })
      })
    }
  })
}

Build Plugin

ts
export function bundleAnalyzerPlugin() {
  let startTime: number

  return definePlugin({
    name: 'bundle-analyzer',

    buildStart() {
      startTime = Date.now()
      console.log('Build started...')
    },

    buildEnd() {
      const duration = Date.now() - startTime
      console.log(`Build completed in ${duration}ms`)

      // Analyze bundle size
      const stats = analyzeBundles()
      console.log('Bundle sizes:', stats)
    }
  })
}

Plugin Context

The PluginContext provides access to framework internals:

ts
interface PluginContext {
  // Framework configuration
  config: FrameworkConfig

  // Project root directory (process.cwd())
  root: string

  // Environment mode
  mode: 'development' | 'production'
}

Example usage:

ts
const myPlugin = definePlugin({
  name: 'my-plugin',
  setup(context) {
    console.log('Running in', context.mode, 'mode')
    console.log('Project root:', context.root)
    console.log('Server port:', context.config.server?.port)
  }
})

isPlugin

Type guard for Plugin objects.

ts
function isPlugin(value: unknown): value is Plugin
ts
const maybePlugin = loadConfig().plugin

if (isPlugin(maybePlugin)) {
  registry.register(maybePlugin)
}

Released under the MIT License.