Important
This project is 100% AI generated as a proof-of-concept and should not be used in production.
Rolldown plugin for Bun-compatible build macros using import attributes.
Detects import { fn } from './macros' with { type: 'macro' }, evaluates macro function calls at build time, and inlines the results as static values.
bun add -D rolldown-plugin-macros// rolldown.config.ts
import { macros } from 'rolldown-plugin-macros';
export default {
input: 'src/index.ts',
plugins: [macros()],
};// macros.js
export function greet(name) {
return `Hello, ${name}!`;
}
export function getConfig(env) {
return {
debug: env === 'development',
version: '1.0.0',
};
}import { greet, getConfig } from './macros' with { type: 'macro' };
const message = greet('world');
const config = getConfig('production');const message = 'Hello, world!';
const config = { debug: false, version: '1.0.0' };The macro import is removed entirely, and each call is replaced with the inlined return value.
- Detection β scans each module's AST for
ImportDeclarationnodes withwith { type: 'macro' }attributes. - Static argument evaluation β extracts call arguments as static JS values (literals, arrays, plain objects). Non-static arguments are rejected with a clear error.
- Execution β loads the macro module via
import()and calls the referenced export with the static arguments. Async macros are awaited. - Inlining β serializes the return value to source code and replaces the call expression using
RolldownMagicStringfor correct source maps. - Cleanup β removes the macro import declaration from the output.
macros({
// Custom file filter. Defaults to .js/.ts/.jsx/.tsx/.mjs/.mts files.
filter: (id) => id.endsWith('.ts'),
});| Pattern | Supported |
|---|---|
import { fn } from './m' with { type: 'macro' } |
Yes |
import fn from './m' with { type: 'macro' } |
Yes (default export) |
import { fn as alias } from './m' with { type: 'macro' } |
Yes |
fn("string", 42, true, null) |
Yes (literal args) |
fn([1, 2, 3]) |
Yes (array args) |
fn({ key: "value" }) |
Yes (object args) |
fn(-42) |
Yes (unary +/-/!) |
fn() |
Yes (no args) |
| Async macro functions | Yes |
| Source maps | Yes |
This is a proof-of-concept. The following are not supported:
- Namespace imports β
import * as macros from './m' with { type: 'macro' }is rejected. - Non-call usage β passing a macro binding as a value (e.g.,
const fn = macroFn; fn()) is not detected. - Dynamic arguments β only statically evaluable arguments are allowed. Variable references, function calls, or expressions are rejected.
- Non-serializable returns β macro functions must return JSON-serializable values (plus
undefinedandbigint). Functions, symbols, etc. will throw. - Spread arguments β
fn(...args)is not supported. - Template literals with expressions β
fn(`${x}`)is rejected (plain template strings are fine). - Computed property keys β
fn({ [key]: value })is rejected. - Module side effects β macro modules are imported and executed at build time. Side effects in the macro module will run during the build.
- Watch mode cache invalidation β the macro module cache is cleared at
buildStartbut not on individual file changes within a build.
This plugin implements the same with { type: 'macro' } import attribute syntax as Bun's build macros. The intent is to allow the same macro source files to work with both Bun's bundler and Rolldown.
Key differences from Bun:
- Bun evaluates macros in a Bun-specific runtime; this plugin uses Node's
import(). - Bun supports additional patterns (e.g.,
JSXreturn values) that are not covered here. - Bun's macro execution has access to Bun-specific APIs; this plugin runs macros in a standard Node.js environment.
MIT