Plugins

Furin uses Bun plugins in three different execution contexts. Configure only the contexts where the transform is needed.

ContextConfigurationWhy it exists
Development browser graph[serve.static].plugins in bunfig.tomlBun's full-stack dev server transforms client imports
Development server importsBun.plugin() before furin()Bun can import transformed page dependencies during SSR
Production route scan and bundlesplugins in furin.config.tsFurin imports routes, then builds client/server artifacts

Tailwind

Tailwind affects the browser asset graph but does not need runtime route-import registration:

bunfig.toml
[serve.static]
plugins = ["bun-plugin-tailwind", "@teyik0/furin/strip-plugin"]
env = "FURIN_PUBLIC_*"
furin.config.ts
import { defineConfig } from "@teyik0/furin/config";
import tailwind from "bun-plugin-tailwind";

export default defineConfig({
  plugins: [{ ...tailwind, buildOnly: true }],
});

@teyik0/furin/strip-plugin is required in the development browser graph. It removes server-only route stages and provides browser stubs for server modules. Furin applies its equivalent internal transforms during production builds.

A Plugin Needed By Route Imports

MDX is imported while Furin discovers routes and while Bun bundles the browser. Register the same plugin in all relevant contexts:

src/lib/mdx-plugin.ts
import { compile } from "@mdx-js/mdx";

const mdxPlugin: Bun.BunPlugin = {
  name: "mdx",
  setup(build) {
    build.onLoad({ filter: /\.mdx$/ }, async ({ path }) => ({
      contents: String(await compile(await Bun.file(path).text())),
      loader: "js",
    }));
  },
};

export default mdxPlugin;
toml
[serve.static]
plugins = ["@teyik0/furin/strip-plugin", "./src/lib/mdx-plugin.ts"]
src/server.ts
import { Elysia } from "elysia";
import { furin } from "@teyik0/furin";
import mdxPlugin from "./lib/mdx-plugin";

Bun.plugin(mdxPlugin);

new Elysia()
  .use(await furin({ pagesDir: "./src/pages" }))
  .listen(3000);
furin.config.ts
import { defineConfig } from "@teyik0/furin/config";
import mdxPlugin from "./src/lib/mdx-plugin";

export default defineConfig({ plugins: [mdxPlugin] });

buildOnly

Set buildOnly: true when a plugin uses hooks unavailable to runtime Bun.plugin() or has no effect on server imports. Furin skips runtime registration but still passes it to production Bun builds. A plugin without buildOnly that fails to register at runtime fails furin build with the plugin name and this fix.

Do not mark a loader for .mdx, custom source files, or other page dependencies as build-only if route discovery must import those files.

Recommendation

Prefer standard JavaScript/TypeScript and a small plugin surface. When a transform is necessary, export one plugin object and reference it from the three configurations instead of maintaining separate implementations. This minimizes dev/production drift while keeping each registration explicit.

Comments