new·The score now tells you which way it movedA brain's exam only ever grows: its own material writes questions, and so does every question a real caller asked and did not get answered. The score is a percentage over that growing set, so a brain that learned more could post a smaller number — and this week three did. One of them answered two MORE questions than the week before and showed eighteen points less. Printed as a single percentage, that reads as decline to a reader and as punishment to anyone who contributes material.all news →
mozg.beta
Sign in

Bun · Bundler · all subjects

bun.build/plugins

35 notes, read out of this brain and free to use. Each one was extracted from a source and is re-checked against its exam.

BunPlugin structure and setup method

A BunPlugin object has a name property and a setup method that receives a builder object. The setup method is where plugin behavior is defined.

Builder object methods in Bun plugins

Bun implements onStart, onEnd, onResolve, and onLoad methods on the builder object. Bun does not implement the esbuild hooks onDispose and resolve. The initialOptions property is partially implemented and read-only, exposing only a subset of esbuild's options; use config instead, which is the equivalent in Bun's BuildConfig format.

onStart hook

The onStart hook is called when the bundle starts. It receives no arguments.

onEnd hook

The onEnd hook is called when the bundle is complete. It receives the result object as an argument.

onResolve options: supported fields

The onResolve hook options support filter and namespace fields (both marked as supported).

onResolve arguments: supported fields

The onResolve hook receives arguments with the following supported fields: path, importer, namespace, resolveDir, and kind. The pluginData field is not supported.

onResolve results: supported and unsupported fields

The onResolve hook can return results with the following supported fields: namespace, path, and external. The following fields are not supported: errors, pluginData, pluginName, sideEffects, suffix, warnings, watchDirs, and watchFiles.

onLoad options: supported fields

The onLoad hook options support filter and namespace fields (both marked as supported).

onLoad arguments: supported and unsupported fields

The onLoad hook receives arguments with the following supported fields: path and namespace. The following fields are not supported: suffix and pluginData.

onLoad results: supported and unsupported fields

The onLoad hook can return results with the following supported fields: contents and loader. The following fields are not supported: errors, pluginData, pluginName, resolveDir, warnings, watchDirs, and watchFiles.

Bun plugin API esbuild compatibility

Bun's plugin API is designed to be esbuild-compatible. Bun does not support esbuild's entire plugin API surface, but core functionality is implemented and many third-party esbuild plugins work with Bun without modification. The long-term aim is for feature parity with esbuild's API.

Plugins only supported via API or bunfig.toml for HTML

Plugins are only supported through Bun.build's API or through bunfig.toml with the frontend dev server, not through `bun build`'s CLI when using HTML.

HTMLRewriter plugin example

Example plugin using HTMLRewriter to make every HTML tag lowercase: `await Bun.build({ entrypoints: ["./index.html"], outdir: "./dist", minify: true, plugins: [{ name: "lowercase-html-plugin", setup({ onLoad }) { const rewriter = new HTMLRewriter().on("*", { element(element) { element.tagName = element.tagName.toLowerCase(); }, text(element) { element.replace(element.text.toLowerCase()); } }); onLoad({ filter: /\.html$/ }, async args => { const html = await Bun.file(args.path).text(); return { contents: rewriter.transform(html), loader: "html" }; }); } }] });`. Bun's bundler automatically scans the transformed HTML for script tags, stylesheet links, and other assets and bundles them.

plugins option for custom loaders

The `plugins` option accepts an array of BunPlugin objects to override or extend bundler behavior during bundling. Bun's plugin system is shared by the runtime and bundler.

Plugin lifecycle hooks overview

Bun's plugin system provides five lifecycle hooks that run at different points during bundling: onStart() runs once the bundler starts a bundle, onResolve() runs before a module is resolved, onLoad() runs before a module is loaded, onBeforeParse() runs native addons before a file is parsed, and onEnd() runs after the bundle is complete.

Plugin structure and basic usage

A plugin is a JavaScript object with a name property and a setup function. The plugin object has the type BunPlugin. Plugins are passed to Bun.build() via the plugins array configuration option.

onResolve hook signature and behavior

onResolve accepts two arguments: a configuration object with filter (RegExp) and optional namespace properties, and a callback function. The callback receives args with path and importer string properties, and can return an object with path and optional namespace properties or void to apply custom module resolution logic.

onLoad hook signature and behavior

onLoad accepts two arguments: a configuration object with filter (RegExp) and optional namespace properties, and a callback function. The callback receives args with path, namespace, loader, and defer function properties. It can return an object with optional loader, contents string, and exports object properties to modify module contents before parsing.

onStart hook signature and behavior

onStart registers a callback with signature onStart(callback: () => void): Promise<void> | void. The callback runs when the bundler starts a new bundle and can return a Promise. The bundler waits until all onStart() callbacks complete before continuing the bundling process.

onEnd hook signature and behavior

onEnd registers a callback with signature onEnd(callback: (result: BuildOutput) => void | Promise<void>): void. The callback runs after the bundle is complete and receives the BuildOutput object containing build results, output files, and build messages. The Bun.build() promise does not resolve until all onEnd() callbacks complete.

Namespaces in plugins

Every module has a namespace. Namespaces prefix the import in transpiled code; for example, a loader with filter /\.yaml$/ and namespace 'yaml:' transforms import from './myfile.yaml' into 'yaml:./myfile.yaml'. The default namespace is 'file'. Common namespaces include 'bun' for Bun-specific modules like 'bun:test' and 'bun:sqlite', and 'node' for Node.js modules like 'node:fs' and 'node:path'.

defer() function in onLoad

The defer() function is passed to onLoad callbacks and returns a Promise that resolves once all other modules have been loaded. It should be awaited when a module's contents depend on other modules. The defer() function can only be called once per onLoad callback.

Cannot modify build.config in lifecycle callbacks

onStart() callbacks, and all other lifecycle callbacks, cannot modify the build.config object. To mutate build.config, do so directly in the setup() function before any callbacks run.

Native plugins with onBeforeParse

Native plugins are NAPI modules that can run on multiple threads and execute much faster than JavaScript plugins. The onBeforeParse() hook is available to native plugins and is called on any thread before a file is parsed by Bun's bundler. Native plugins skip UTF-8 to UTF-16 conversion overhead needed for JavaScript plugins.

onBeforeParse hook signature

onBeforeParse has signature onBeforeParse(args: { filter: RegExp; namespace?: string }, callback: { napiModule: NapiModule; symbol: string; external?: unknown }): void. It receives a configuration object with filter and optional namespace, and a callback object specifying the NAPI module, the symbol name to call, and optional external state. The NAPI module implementation must be thread-safe.

PluginBuilder type definition

PluginBuilder is a type with methods: onStart(callback: () => void): void, onResolve with filter/namespace args and callback returning path/namespace or void, onLoad with filter/namespace args and callback returning loader/contents/exports or void, onEnd(callback: (result: BuildOutput) => void | Promise<void>): void, and a config property of type BuildConfig.

Example: onResolve redirecting imports

This example shows redirecting all imports to 'images/' directory to './public/images/': plugin({ name: 'onResolve example', setup(build) { build.onResolve({ filter: /.*/, namespace: 'file' }, args => { if (args.path.startsWith('images/')) { return { path: args.path.replace('images/', './public/images/') }; } }); } });

Example: onLoad environment variable plugin

This example creates a plugin that transforms imports of 'env' into a JavaScript module exporting environment variables: const envPlugin: BunPlugin = { name: 'env plugin', setup(build) { build.onLoad({ filter: /env/, namespace: 'file' }, args => { return { contents: `export default ${JSON.stringify(process.env)}`, loader: 'js' }; }); } }; Bun.build({ entrypoints: ['./app.ts'], outdir: './dist', plugins: [envPlugin] });

Example: defer() for tracking imports

This example uses defer() to track all imports across modules before emitting statistics. The first onLoad callback scans imports from each .ts file. The second onLoad callback for stats.json awaits defer() to ensure all files have been processed, then returns JSON containing import statistics: build.onLoad({ filter: /stats\.json/ }, async ({ defer }) => { await defer(); return { contents: `export default ${JSON.stringify(trackedImports)}`, loader: 'json' }; });

Example: onStart with async operations

This example shows multiple onStart callbacks that can perform async operations: Bun.build({ entrypoints: ['./app.ts'], outdir: './dist', plugins: [{ name: 'Sleep for 10 seconds', setup(build) { build.onStart(async () => { await Bun.sleep(10_000); }); } }, { name: 'Log bundle time to a file', setup(build) { build.onStart(async () => { const now = Date.now(); await Bun.$`echo ${now} > bundle-time.txt`; }); } }] });

Example: onEnd to report build results

This example uses onEnd to log the number of output files and all build logs: Bun.build({ entrypoints: ['./app.ts'], outdir: './dist', plugins: [{ name: 'onEnd example', setup(build) { build.onEnd(result => { console.log(`Build completed with ${result.outputs.length} files`); for (const log of result.logs) { console.log(log); } }); } }] });

Example: onEnd with S3 upload

This example uses onEnd to conditionally upload build outputs to S3: build.onEnd(async result => { if (!result.success) return; for (const output of result.outputs) { await uploadToS3(output); } });

Example: native plugin in Rust with onBeforeParse

This Rust example implements onBeforeParse to replace all occurrences of 'foo' with 'bar': use bun_native_plugin::{define_bun_plugin, OnBeforeParse, bun, Result, BunLoader}; define_bun_plugin!("replace-foo-with-bar"); #[bun] pub fn replace_foo_with_bar(handle: &mut OnBeforeParse) -> Result<()> { let input_source_code = handle.input_source_code()?; let output_source_code = input_source_code.replace("foo", "bar"); handle.set_output_source_code(output_source_code, BunLoader::BUN_LOADER_JSX); Ok(()) }

Example: using native plugin in Bun.build()

This example shows how to use a native plugin in Bun.build(): import myNativeAddon from './my-native-addon'; Bun.build({ entrypoints: ['./app.tsx'], plugins: [{ name: 'my-plugin', setup(build) { build.onBeforeParse({ namespace: 'file', filter: /\.tsx$/ }, { napiModule: myNativeAddon, symbol: 'replace_foo_with_bar' }); } }] });

Plugin use cases in the bundler

Plugins can implement framework-level features in the bundler including CSS extraction, macros, and client-server code co-location. Plugins can also add support for additional file types like .scss or .yaml.

Give your agent this brain