Plugins

Davaux plugins let you extend the framework in two ways: adding esbuild transforms to the build pipeline, and registering new route file suffixes so the scanner recognises them. Both capabilities are exposed through a single DavauxPlugin interface.

Using plugins

Pass plugins to davaux.config.ts:

// davaux.config.ts
import { defineConfig } from 'davaux/config'
import { markdown } from '@davaux/markdown'
import { mdx } from '@davaux/mdx'

export default defineConfig({
  plugins: [markdown(), mdx()],
})

Plugin esbuild contributions are added to every build context — server routes, the islands client bundle, and the optional user client bundle. Scanner suffix entries extend which files the route scanner picks up.

Official plugins

PackageRoute suffixDescription
@davaux/markdown.page.mdMarkdown routes with YAML frontmatter
@davaux/mdx.page.mdxMDX routes — markdown with JSX component imports

Authoring a plugin

A plugin is a factory function that returns a DavauxPlugin object:

import type { DavauxPlugin } from 'davaux/config'

export function myPlugin(): DavauxPlugin {
  return {
    name: 'my-plugin',
    esbuild: [myEsbuildPlugin()],
    scanner: {
      suffixes: [['.page.myext', 'page']],
    },
  }
}

DavauxPlugin fields:

FieldTypeDescription
namestringUnique identifier, used in error messages
esbuildPlugin[]esbuild plugins added to all build contexts
scanner.suffixes[string, RouteType][]File suffix + route type pairs for the scanner

RouteType is 'page' | 'get' | 'post' | 'put' | 'patch' | 'delete' | 'head' | 'options'.

Example: a YAML front-matter page plugin

This example adds .page.yaml as a route type that renders a simple data dump page — a minimal but complete plugin:

import type { Plugin } from 'esbuild'
import type { DavauxPlugin } from 'davaux/config'
import { readFileSync } from 'node:fs'

function yamlPagePlugin(): Plugin {
  return {
    name: 'davaux-yaml-page',
    setup(build) {
      build.onLoad({ filter: /\.page\.yaml$/ }, (args) => {
        const raw = readFileSync(args.path, 'utf-8')
        // Real implementation would parse YAML; this serialises the raw string
        const contents = `
          import { definePage } from 'davaux'
          export default definePage((ctx) => {
            ctx.head.title = 'YAML Data'
            return \`<pre>\${${JSON.stringify(raw)}}</pre>\`
          })
        `
        return { contents, loader: 'ts' }
      })
    },
  }
}

export function yaml(): DavauxPlugin {
  return {
    name: 'davaux-yaml',
    esbuild: [yamlPagePlugin()],
    scanner: { suffixes: [['.page.yaml', 'page']] },
  }
}

esbuild plugin context

Davaux runs three separate esbuild build contexts:

  1. Server routes — all files under src/routes/ and src/islands/ (server JSX runtime)
  2. Islands client bundle — island files recompiled with the client JSX runtime
  3. User client bundlesrc/client.ts if present

Your plugin's esbuild array is added to all three contexts. If your transform only makes sense in one context (e.g. a server-only loader), check the build options or use a filter that matches only the relevant files.

Scanner suffixes

The scanner looks for files matching known suffixes to build the route manifest. Without registering a suffix, files with your custom extension are silently ignored even if they export the right define* wrappers.

The suffix registration format is [suffix, routeType]:

scanner: {
  suffixes: [
    ['.page.md', 'page'],   // GET page route
    ['.api.ts', 'get'],     // explicit GET API route
  ]
}

Route type controls which HTTP methods the route responds to — 'page' responds to GET and HEAD, 'get' responds to GET only, and so on.