Plugins
Furin uses Bun plugins in three different execution contexts. Configure only the contexts where the transform is needed.
| Context | Configuration | Why it exists |
|---|---|---|
| Development browser graph | [serve.static].plugins in bunfig.toml | Bun's full-stack dev server transforms client imports |
| Development server imports | Bun.plugin() before furin() | Bun can import transformed page dependencies during SSR |
| Production route scan and bundles | plugins in furin.config.ts | Furin imports routes, then builds client/server artifacts |
Tailwind
Tailwind affects the browser asset graph but does not need runtime route-import registration:
[serve.static]
plugins = ["bun-plugin-tailwind", "@teyik0/furin/strip-plugin"]
env = "FURIN_PUBLIC_*"
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:
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;
[serve.static]
plugins = ["@teyik0/furin/strip-plugin", "./src/lib/mdx-plugin.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);
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.