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 · Runtime · all subjects

transpiler & bundler

73 notes in this subject, read out of this brain and free to use. This is page 1 of 2.

Transpiler API

Bun provides Bun.Transpiler for transpiling code.

bunfig.toml define field for identifier replacement

The define field replaces global identifiers with constant expressions. Values are parsed as JSON, with support for single-quoted strings. 'undefined' becomes undefined in JS. Example: [define] "process.env.bagel" = "'lox'" replaces any usage of process.env.bagel with the string lox.

bunfig.toml loader configuration

Configure how file extensions map to loaders using the [loader] section. Supported loaders are: jsx, js, ts, tsx, css, file, json, toml, wasm, napi, base64, dataurl, text. Example: [".bagel"] = "tsx" treats .bagel files as tsx files.

bunfig.toml JSX configuration

Configure JSX handling with the following fields: jsx, jsxFactory, jsxFragment, and jsxImportSource. These can be set in bunfig.toml for non-TypeScript projects as an alternative to tsconfig.json compilerOptions. Example: jsx = "react", jsxFactory = "h", jsxFragment = "Fragment", jsxImportSource = "react".

jsx loader behavior

The `jsx` loader is the default for .js and .jsx files. It is the same as the `js` loader but supports JSX syntax. By default, Bun down-converts JSX to plain JavaScript according to the `jsx*` compiler options in tsconfig.json.

ts loader behavior

The `ts` loader is the default for .ts, .mts, and .cts files. It strips out all TypeScript syntax and then behaves identically to the `js` loader. Bun does not perform typechecking.

tsx loader behavior

The `tsx` loader is the default for .tsx files. It transpiles both TypeScript and JSX to vanilla JavaScript.

json loader behavior

The `json` loader is the default for .json files. JSON files can be directly imported. During bundling, Bun inlines the parsed JSON into the bundle as a JavaScript object. If a .json file is passed as an entrypoint to the bundler, Bun converts it to a .js module that `export default`s the parsed object.

jsonc loader behavior

The `jsonc` loader is the default for .jsonc files. JSONC (JSON with Comments) files can be directly imported. Bun parses them, stripping out comments and trailing commas. During bundling, Bun inlines the parsed JSONC into the bundle as a JavaScript object. Bun automatically uses the `jsonc` loader for tsconfig.json, jsconfig.json, package.json, and bun.lock files.

toml loader behavior

The `toml` loader is the default for .toml files. TOML files can be directly imported. Bun parses them with its fast native TOML parser. During bundling, Bun inlines the parsed TOML into the bundle as a JavaScript object. If a .toml file is passed as an entrypoint, Bun converts it to a .js module that `export default`s the parsed object.

yaml loader behavior

The `yaml` loader is the default for .yaml and .yml files. YAML files can be directly imported. Bun parses them with its fast native YAML parser. During bundling, Bun inlines the parsed YAML into the bundle as a JavaScript object. If a .yaml or .yml file is passed as an entrypoint, Bun converts it to a .js module that `export default`s the parsed object.

Supported file types in Bun bundler and runtime

Bun supports the following file types: .js, .cjs, .mjs, .mts, .cts, .ts, .tsx, .jsx, .css, .json, .jsonc, .json5, .toml, .yaml, .yml, .xml, .txt, .text, .md, .markdown, .wasm, .node, .html, .sh

Import attributes for specifying loader type

Use the `type` import attribute to explicitly specify a loader for a file. Example: `import my_toml from "./my_file" with { type: "toml" };` or with dynamic imports: `const { default: my_toml } = await import("./my_file", { with: { type: "toml" } });`

js loader behavior

The `js` loader is the default for .cjs and .mjs files. It parses the code and applies default transforms like dead-code elimination and tree shaking. Bun does not down-convert syntax.

html loader selectors

The html loader extracts the following selectors as entrypoints or assets: audio[src], img[src], img[srcset], link[as='font'][href], link[type^='font/'][href], link[as='image'][href], link[as='style'][href], link[as='video'][href], link[as='audio'][href], link[as='worker'][href], link[rel='icon'][href], link[rel='apple-touch-icon'][href], link[rel='manifest'][href], link[rel='stylesheet'][href], script[src], source[src], source[srcset], video[poster], video[src]

css loader behavior

The `css` loader is the default for .css files. CSS files can be directly imported. This is primarily useful when bundling HTML, where CSS is bundled alongside HTML. The import returns no value and is only used for its side effects.

file loader behavior

The `file` loader is the default for all unrecognized file types. The file loader resolves the import as a path/URL to the imported file. It is commonly used for referencing media or font assets. In the runtime, Bun checks that the file exists and resolves the import to its absolute path on disk. In the bundler, Bun copies the file into outdir as-is, and the import resolves to a relative path pointing to the copied file. If publicPath is set, the import uses its value as a prefix to construct an absolute path/URL. With publicPath set to empty string (default), the resolved import is './logo.svg'. With publicPath set to '/assets/', the resolved import is '/assets/logo.svg'. With publicPath set to 'https://cdn.example.com/', the resolved import is 'https://cdn.example.com/logo.svg'.

TypeScript declaration file for file loader imports

To fix TypeScript errors when importing files with the file loader, create a *.d.ts file anywhere in your project with content like: `declare module "*.svg" { const content: string; export default content; }`. This tells TypeScript to treat any default import from .svg (or other file types) as a string.

json5 loader behavior

The `json5` loader is the default for .json5 files. JSON5 files can be directly imported. Bun parses them with its fast native JSON5 parser. JSON5 is a superset of JSON that adds comments, trailing commas, unquoted keys, single-quoted strings, and more. During bundling, Bun inlines the parsed JSON5 into the bundle as a JavaScript object. If a .json5 file is passed as an entrypoint, Bun converts it to a .js module that `export default`s the parsed object.

xml loader behavior

The `xml` loader is the default for .xml files. XML files can be directly imported. Bun parses them with its native XML 1.0 parser into the compact object shape of Bun.XML.parse with one key for the root element, '@name' keys for attributes, arrays for repeated child elements, '#text' for text next to attributes or children, and every value is a string. During bundling, Bun inlines the parsed XML into the bundle as a JavaScript object. If an .xml file is passed as an entrypoint, Bun converts it to a .js module that `export default`s the parsed object.

text loader behavior

The `text` loader is the default for .txt and .text files. Text files can be directly imported and Bun reads the file and returns it as a string. The 'type' attribute can be used to override the default loader, such as `import html from "./index.html" with { type: "text" };`. During bundling, Bun inlines the contents into the bundle as a string. If a .txt file is passed as an entrypoint, Bun converts it to a .js module that `export default`s the file contents.

md loader behavior

The `md` loader is the default for .md and .markdown files. Markdown files can be directly imported. Bun renders the file to HTML and returns the HTML as a string. The `markdown` is an alias of `md` for the import attribute. During bundling, Bun inlines the rendered HTML into the bundle as a string.

napi loader behavior

The `napi` loader is the default for .node files. In the runtime, native addons can be directly imported. In the bundler, Bun handles .node files using the file loader.

html loader behavior

The `html` loader processes HTML files and bundles any referenced assets. It bundles and hashes referenced JavaScript files (<script src="...">), bundles and hashes referenced CSS files (<link rel="stylesheet" href="...">), hashes referenced images (<img src="...">), and preserves external URLs by default (anything starting with http:// or https://). The loader uses lol-html to extract script and link tags as entrypoints, and other assets as external. The html loader behaves differently depending on context: (1) Static Build: When running `bun build ./index.html`, Bun produces a static site with all assets bundled and hashed. (2) Runtime: When running `bun run server.ts` (where server.ts imports an HTML file), Bun bundles assets on-the-fly during development, enabling features like hot module replacement. (3) Full-stack Build: When running `bun build --target=bun server.ts` (where server.ts imports an HTML file), the import resolves to a manifest object that Bun.serve uses to efficiently serve pre-bundled assets in production.

Runtime transpiler cache details

For files larger than 4 KB, Bun caches transpiled output and sourcemaps. The cache is global, shared across all projects, and content-addressable so it never contains duplicate entries. Cached files use the .pile extension. The cache is safe to delete at any time, even while a Bun process is running. The cache should be disabled when using ephemeral filesystems like Docker.

BUN_RUNTIME_TRANSPILER_CACHE_PATH environment variable

The runtime transpiler caches the transpiled output of source files larger than 4 KB. If BUN_RUNTIME_TRANSPILER_CACHE_PATH is set, Bun writes the cache to that directory. If it is set to an empty string or the string '0', caching is disabled. If it is unset, Bun writes the cache to the platform-specific cache directory.

Bun generates sourcemaps for transpiled files

Bun transpiles every file and generates sourcemaps for all transpiled files. Bun loads sourcemaps both at runtime when transpiling files on-demand and when using bun build to precompile files ahead of time. This allows stack traces to point to original source code even when it was TypeScript, JSX, or underwent other transformations.

Bun supports TypeScript and JSX without configuration

Bun supports TypeScript and JSX with no configuration. Bun transpiles every file on the fly with its native transpiler before running it. You can run `.js`, `.jsx`, `.ts`, and `.tsx` files with `bun run`.

jsxFragmentFactory compiler option

The jsxFragmentFactory option sets the function name used to represent JSX fragments such as <>Hello</>. This option only applies when jsx is "react". The default value is "React.Fragment".

Bun supports JSX and TSX files

Bun supports .jsx and .tsx files. Bun's internal transpiler converts JSX syntax into vanilla JavaScript before execution.

JSX configuration via tsconfig.json or jsconfig.json

Bun reads your tsconfig.json or jsconfig.json to determine how to perform the JSX transform internally. You can alternatively set the same options in bunfig.toml.

jsx compiler option transpilation modes

The jsx compiler option controls how Bun transforms JSX constructs. Possible values and their outputs for <Box width={5}>Hello</Box> are: jsx: "react" transpiles to React.createElement(Box, { width: 5 }, "Hello"); jsx: "react-jsx" transpiles to import { jsx } from "react/jsx-runtime"; jsx(Box, { width: 5, children: "Hello" }); jsx: "react-jsxdev" transpiles to import { jsxDEV } from "react/jsx-dev-runtime"; jsxDEV(Box, { width: 5, children: "Hello" }, undefined, false, undefined, this,); jsx: "preserve" does not transpile JSX and is not currently supported by Bun.

jsxFactory compiler option

The jsxFactory option sets the function name used to represent JSX constructs. This option only applies when jsx is "react". The default value is "React.createElement". Use this for libraries like Preact that use a different function name such as "h".

jsxImportSource compiler option

The jsxImportSource option sets the module that the component factory function is imported from. This option only applies when jsx is "react-jsx" or "react-jsxdev". The default value is "react". Use this when using a component library like Preact. When using react-jsxdev with jsxImportSource, the /jsx-dev-runtime path is automatically appended.

JSX pragmas for per-file compiler options

You can set compiler options per file using pragmas, which are comments that set a compiler option in a particular file. The supported pragmas are: // @jsx h (sets jsxFactory), // @jsxFrag MyFragment (sets jsxFragmentFactory), and // @jsxImportSource preact (sets jsxImportSource).

Bun pretty-prints JSX component trees in logging

Bun implements special logging for JSX to help with debugging. When you console.log a JSX component tree, Bun pretty-prints the component tree in a formatted way.

JSX prop punning shorthand

Bun supports prop punning for JSX, which is a shorthand for assigning a variable to a prop with the same name. Instead of writing <div className={className} />, you can write <div {className} />.

node:sea not implemented

node:sea is not implemented. Use bun build --compile to build single-file executables instead.

Plugin API lifecycle hooks

Bun's universal plugin API provides four main lifecycle hooks: onStart() runs once the bundler has started a bundle; onResolve() runs before the bundler resolves a module; onLoad() runs before the bundler loads a module; onBeforeParse() runs zero-copy native addons in the parser thread before the bundler parses a file.

Plugin structure and usage

A plugin is a JavaScript object containing a 'name' property and a 'setup' function. The setup function receives a build object and registers callbacks. Plugins are passed in the 'plugins' array when calling Bun.build().

Loader type values in plugins

The Loader type in plugins can be: 'js', 'jsx', 'ts', 'tsx', 'json', 'jsonc', 'toml', 'yaml', 'file', 'napi', 'wasm', 'text', 'css', or 'html'.

Module namespaces in plugins

Every module has a namespace that prefixes the import in transpiled code. The default namespace is 'file' (e.g., 'import myModule from "./my-module.ts"' is the same as 'import myModule from "file:./my-module.ts"'). Other common namespaces are 'bun' for Bun-specific modules and 'node' for Node.js modules. A custom namespace like 'yaml:' with filter /\.yaml$/ transforms 'import myFile from "./myfile.yaml"' into 'import myFile from "yaml:./myfile.yaml"'.

onStart callback signature and behavior

onStart(callback: () => void | Promise<void>): void registers a callback that runs when the bundler starts a new bundle. The callback can return a Promise. The bundler waits until all onStart() callbacks have completed before continuing. onStart() callbacks cannot modify the build.config object; mutations must be done directly in the setup() function.

onResolve callback signature and purpose

onResolve(args: { filter: RegExp; namespace?: string }, callback: (args: { path: string; importer: string }) => { path: string; namespace?: string } | void): void customizes how Bun resolves a module. The filter is a regular expression run on the import string. The callback receives the path to the matching module and can return a new path for it. Bun reads the contents of the returned path and parses it as a module.

onLoad callback signature and purpose

onLoad(args: { filter: RegExp; namespace?: string }, callback: (args: { path: string; namespace: string; loader: Loader; defer: () => Promise<void> }) => { loader?: Loader; contents?: string; exports?: Record<string, any> }): void modifies the contents of a module before Bun reads and parses it. The callback receives the matching module's path, namespace, default loader, and a defer function. The callback can return a new contents string and a new loader.

onLoad defer() function

The defer() function in onLoad callback returns a Promise that resolves once Bun has loaded all other modules. Use it when a module's contents depend on other modules. The defer() function can only be called once per onLoad callback.

Native plugins with onBeforeParse

Native plugins are NAPI modules that can run on multiple threads, making them faster than JavaScript plugins. The onBeforeParse() lifecycle hook is available for native plugins and is called on any thread before Bun's bundler parses a file. Native plugins must be thread-safe.

onBeforeParse callback signature

onBeforeParse(args: { filter: RegExp; namespace?: string }, callback: { napiModule: NapiModule; symbol: string; external?: unknown }): void runs immediately before Bun's bundler parses a file. It receives the file's contents and can return new source code. The callback specifies a NAPI module with a symbol name to call.

PluginBuilder type definition

PluginBuilder type has: onStart(callback: () => void | Promise<void>): void; onResolve: (args: { filter: RegExp; namespace?: string }, callback: (args: { path: string; importer: string }) => { path: string; namespace?: string } | void) => void; onLoad: (args: { filter: RegExp; namespace?: string }, callback: (args: { path: string; loader: Loader; namespace: string; defer: () => Promise<void> }) => { loader?: Loader; contents?: string; exports?: Record<string, any> }) => void; config: BuildConfig.

Example: onResolve plugin

This example redirects all imports starting with 'images/' to './public/images/': import { plugin } from "bun"; 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 env plugin

This plugin transforms imports of 'import env from "env"' into a JavaScript module that exports environment variables: import type { BunPlugin } from "bun"; const envPlugin: BunPlugin = { name: "env plugin", setup(build) { build.onResolve({ filter: /^env$/ }, args => { return { path: args.path, namespace: "env" }; }); build.onLoad({ filter: /env/, namespace: "env" }, args => { return { contents: `export default ${JSON.stringify(process.env)}`, loader: "js", }; }); }, }; Bun.build({ entrypoints: ["./app.ts"], outdir: "./dist", plugins: [envPlugin], }); // import env from "env" // env.FOO === "bar"

Example: onStart plugin

This example shows an onStart callback that logs when a bundle starts: await Bun.build({ entrypoints: ["./app.ts"], plugins: [ { name: "onStart example", setup(build) { build.onStart(() => { console.log("Bundle started!"); }); }, }, ], });

Example: onStart with async callbacks

This example shows multiple onStart callbacks, one that sleeps for 10 seconds and another that writes the bundle time to a file. The bundler waits for both to complete: const result = await Bun.build({ entrypoints: ["./app.ts"], outdir: "./dist", sourcemap: "external", 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: onLoad with defer tracking unused exports

This plugin tracks imports across all TypeScript modules and reports statistics using the defer() function: import { plugin } from "bun"; plugin({ name: "track imports", setup(build) { const transpiler = new Bun.Transpiler(); let trackedImports: Record<string, number> = {}; build.onLoad({ filter: /\.ts/ }, async ({ path }) => { const contents = await Bun.file(path).arrayBuffer(); const imports = transpiler.scanImports(contents); for (const i of imports) { trackedImports[i.path] = (trackedImports[i.path] || 0) + 1; } return undefined; }); build.onLoad({ filter: /stats\.json/ }, async ({ defer }) => { await defer(); return { contents: `export default ${JSON.stringify(trackedImports)}`, loader: "json", }; }); }, });

Example: Native Rust plugin with onBeforeParse

This Rust example implements onBeforeParse to replace 'foo' with 'bar': use bun_native_plugin::{define_bun_plugin, OnBeforeParse, bun, Result, anyhow, BunLoader}; use napi_derive::napi; 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 loader = handle.output_loader(); 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 a native plugin in Bun.build

This shows how to use a native NAPI addon 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", // external: myNativeAddon.getSharedState() }, ); }, }, ], });

REPL TypeScript and JSX support

The REPL supports writing TypeScript and JSX directly. Bun transpiles everything on the fly.

@types/bun installation for TypeScript support

Install the @types/bun package as a dev dependency to get TypeScript definitions for Bun's built-in APIs. Use the command 'bun add -d @types/bun'. After installation, the Bun global can be referenced in TypeScript files without editor errors.

Recommended tsconfig.json compilerOptions for Bun projects

The following compilerOptions are recommended for Bun projects: lib: ["ESNext"], target: "ESNext", module: "Preserve", moduleDetection: "force", jsx: "react-jsx", allowJs: true, types: ["bun"], moduleResolution: "bundler", allowImportingTsExtensions: true, verbatimModuleSyntax: true, noEmit: true, strict: true, skipLibCheck: true, noFallthroughCasesInSwitch: true, noUncheckedIndexedAccess: true, noImplicitOverride: true, noUnusedLocals: false, noUnusedParameters: false, noPropertyAccessFromIndexSignature: false. These settings enable Bun-specific features like top-level await, JSX, and imports with .ts extensions without TypeScript compiler warnings.

bun init generates tsconfig.json

Running 'bun init' in a new directory automatically generates a tsconfig.json file with recommended compiler options. The stricter flags (noUnusedLocals, noUnusedParameters, noPropertyAccessFromIndexSignature) are disabled by default.

Give your agent this brain