Macros are a mechanism for running JavaScript functions at bundle-time. The values returned by these functions are directly inlined into your bundle. As a toy example, consider this simple function that returns a random number.Documentation Index
Fetch the complete documentation index at: https://mintlify.com/zhcndoc/bun/llms.txt
Use this file to discover all available pages before exploring further.
random.ts
cli.tsx
Macros are identified using the import attributes syntax. If you haven’t seen this syntax before, it’s a Stage 3 TC39 proposal
that allows you to attach additional metadata to import statements.
bun build. The bundled file will be printed to stdout.
random function doesn’t appear in the bundle. Instead, it was executed at bundle-time, and the function call (random()) was replaced with the result of the function. Because the source code is never included in the bundle, macros can safely perform privileged operations like reading from a database.
When to use macros
If you have several one-off build scripts for small tasks, executing code at bundle-time can be easier to maintain. Macros live alongside other code, run in sync with the build, are automatically parallelized, and fail the build if they fail. However, if you find yourself running lots of code at bundle-time, consider running a server instead.Import attributes
Bun macros are annotated with import statements using:with { type: 'macro' }— An import attribute, a Stage 3 ECMA Script proposalassert { type: 'macro' }— An import assertion, an older form of import attributes that is now deprecated (but already shipped in multiple browsers and runtimes)
Safety considerations
Macros must be explicitly imported with{ type: "macro" } to be executed at bundle-time. Unless you call those macro imports, they have no effect, whereas normal JavaScript imports can have side effects.
You can completely disable macros by passing --no-macros to Bun, which will produce a build error like this:
node_modules/**/*. If a package tries to call a macro, you’ll see an error like this:
node_modules and call them.
cli.tsx
Export condition “macro”
When publishing a library with macros to npm or other package registries, use the"macro" export condition to provide a special version for the macro environment.
package.json
./node_modules/my-package/index.js, while the second import is resolved by Bun’s bundler to ./node_modules/my-package/index.macro.js.
Execution
When the Bun transpiler encounters a macro import, it uses Bun’s JavaScript runtime to call the function inside the transpiler and convert the JavaScript return value to an AST node. These functions are called at bundle-time, not runtime. Macros execute synchronously in the transpiler’s visit pass—after plugins, before generating the AST. They execute in import order. The transpiler waits for macros to execute before continuing. If a macro returns a Promise, the transpiler will also wait for it to complete. Bun’s bundler is multi-threaded, so macros execute in parallel across multiple JavaScript “worker threads”.Dead code elimination
After macros execute and are inlined, the bundler performs dead code elimination. For instance, this macro:returnFalse.ts
Serializability
Bun’s transpiler must be able to serialize a macro’s return value to inline it into the AST. All JSON-compatible data structures are supported:macro.ts
macro.ts
Response, Blob, and TypedArray.
- TypedArray: Resolved to a base64-encoded string.
- Response: Bun reads the
Content-Typeand serializes accordingly; e.g.application/jsontypes are automatically parsed to objects,text/plainis inlined as a string. Unknown or undefined Response types are converted to base64. - Blob: Like Response, serialization depends on its
typeproperty.
fetch returns a Promise<Response>, which can be returned directly.
macro.ts
macro.ts
Arguments
Macros can accept inputs, but only in limited cases. Argument values must be statically known. For instance, the following usage is not allowed:foo is known at bundle-time (e.g. it’s a constant or the result of another macro), then the following usage is allowed:
Type safety
Macros are just regular functions, so they work seamlessly with TypeScript:random.ts
cli.ts
Examples
Reading files at build time
readFile.ts
app.ts
Environment variables
env.ts
app.ts
Build-time calculations
calculate.ts
app.ts
Fetching data at build time
fetch.ts
app.ts
Generate code from database
db.ts
app.ts
Inline build metadata
build-info.ts
app.ts
Debugging macros
If a macro throws an error, the build will fail with a stack trace:console.log() statements to debug macros:
macro.ts
Limitations
- Macros cannot import files that are not yet processed by the bundler
- Circular dependencies between macros are not supported
- Macros cannot use dynamic imports (
import()) - Macro arguments must be statically analyzable
- Only serializable values can be returned
Performance considerations
Macros run during the build, so expensive operations will slow down your build time:- Cache results when possible
- Avoid unnecessary network requests
- Consider using build-time constants instead of macros for simple values
- Use
--no-macrosin development if macros are slow