Skip to content

Commit eaa03dd

Browse files
cursoragentabhi
andcommitted
feat: Add support for custom bundler engines
Co-authored-by: abhi <abhi@mastra.ai>
1 parent 9d7cf89 commit eaa03dd

File tree

19 files changed

+778
-294
lines changed

19 files changed

+778
-294
lines changed

packages/bundler-bun/CHANGELOG.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
# @mastra/bundler-bun
2+
3+
## 1.0.0-beta.1
4+
5+
### Minor Changes
6+
7+
- Initial release of the Bun bundler engine for Mastra
8+
- Provides `BunBundlerEngine` class implementing the `BundlerEngine` interface
9+
- Supports minification, code splitting, and source maps
10+
- Includes `createBunEngine` factory function for convenience

packages/bundler-bun/README.md

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
# @mastra/bundler-bun
2+
3+
Bun bundler engine for Mastra. This package provides a fast, native bundling solution using Bun's built-in bundler.
4+
5+
## Installation
6+
7+
```bash
8+
# Using bun
9+
bun add @mastra/bundler-bun
10+
11+
# Using npm
12+
npm install @mastra/bundler-bun
13+
14+
# Using pnpm
15+
pnpm add @mastra/bundler-bun
16+
```
17+
18+
## Requirements
19+
20+
- Bun runtime must be installed and available
21+
- Mastra core package (`@mastra/core`)
22+
23+
## Usage
24+
25+
```typescript
26+
import { Mastra } from '@mastra/core';
27+
import { BunBundlerEngine } from '@mastra/bundler-bun';
28+
29+
export const mastra = new Mastra({
30+
agents: { myAgent },
31+
bundler: {
32+
engine: new BunBundlerEngine(),
33+
sourcemap: true,
34+
},
35+
});
36+
```
37+
38+
### With Configuration
39+
40+
```typescript
41+
import { BunBundlerEngine } from '@mastra/bundler-bun';
42+
43+
const engine = new BunBundlerEngine({
44+
minify: true, // Minify output (default: true)
45+
target: 'bun', // Target: 'bun' | 'node' | 'browser' (default: 'bun')
46+
splitting: true, // Enable code splitting (default: true)
47+
external: ['sharp'], // Additional external packages
48+
});
49+
50+
export const mastra = new Mastra({
51+
bundler: {
52+
engine,
53+
},
54+
});
55+
```
56+
57+
### Using the Factory Function
58+
59+
```typescript
60+
import { createBunEngine } from '@mastra/bundler-bun';
61+
62+
export const mastra = new Mastra({
63+
bundler: {
64+
engine: createBunEngine({ minify: true }),
65+
},
66+
});
67+
```
68+
69+
## Configuration Options
70+
71+
| Option | Type | Default | Description |
72+
| ----------- | ------------------------------ | ------- | --------------------------------------- |
73+
| `minify` | `boolean` | `true` | Whether to minify the output |
74+
| `target` | `'bun' \| 'node' \| 'browser'` | `'bun'` | Target environment for the bundle |
75+
| `splitting` | `boolean` | `true` | Enable code splitting |
76+
| `external` | `string[]` | `[]` | Additional packages to mark as external |
77+
78+
## Why Use Bun Bundler?
79+
80+
- **Speed**: Bun's bundler is significantly faster than traditional JavaScript bundlers
81+
- **Native**: No additional dependencies required when running with Bun
82+
- **Modern**: Built-in support for TypeScript, JSX, and modern JavaScript features
83+
- **Simple**: Minimal configuration required
84+
85+
## License
86+
87+
Apache-2.0
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
import { createConfig } from '@internal/lint/eslint';
2+
3+
const config = await createConfig();
4+
5+
/** @type {import("eslint").Linter.Config[]} */
6+
export default [...config];

packages/bundler-bun/package.json

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
{
2+
"name": "@mastra/bundler-bun",
3+
"version": "1.0.0-beta.1",
4+
"description": "Bun bundler engine for Mastra",
5+
"license": "Apache-2.0",
6+
"type": "module",
7+
"main": "dist/index.js",
8+
"types": "dist/index.d.ts",
9+
"exports": {
10+
".": {
11+
"import": {
12+
"types": "./dist/index.d.ts",
13+
"default": "./dist/index.js"
14+
},
15+
"require": {
16+
"types": "./dist/index.d.ts",
17+
"default": "./dist/index.cjs"
18+
}
19+
},
20+
"./package.json": "./package.json"
21+
},
22+
"files": [
23+
"dist",
24+
"CHANGELOG.md"
25+
],
26+
"homepage": "https://mastra.ai",
27+
"repository": {
28+
"type": "git",
29+
"url": "git+https://github.com/mastra-ai/mastra.git",
30+
"directory": "packages/bundler-bun"
31+
},
32+
"bugs": {
33+
"url": "https://github.com/mastra-ai/mastra/issues"
34+
},
35+
"scripts": {
36+
"build": "tsup --silent --config tsup.config.ts",
37+
"build:watch": "tsup --watch --silent --config tsup.config.ts",
38+
"test": "vitest run",
39+
"lint": "eslint ."
40+
},
41+
"peerDependencies": {
42+
"@mastra/core": ">=1.0.0-0 <2.0.0-0"
43+
},
44+
"devDependencies": {
45+
"@internal/lint": "workspace:*",
46+
"@internal/types-builder": "workspace:*",
47+
"@mastra/core": "workspace:*",
48+
"@types/node": "22.13.17",
49+
"@vitest/coverage-v8": "catalog:",
50+
"@vitest/ui": "catalog:",
51+
"bun-types": "^1.2.20",
52+
"eslint": "^9.37.0",
53+
"tsup": "^8.5.0",
54+
"typescript": "^5.8.3",
55+
"vitest": "catalog:"
56+
},
57+
"engines": {
58+
"node": ">=22.13.0"
59+
},
60+
"keywords": [
61+
"mastra",
62+
"bundler",
63+
"bun",
64+
"ai",
65+
"agent"
66+
]
67+
}
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
import { describe, it, expect } from 'vitest';
2+
import { BunBundlerEngine, createBunEngine } from './index';
3+
import type { BundlerEngineOptions } from '@mastra/core/bundler';
4+
5+
describe('BunBundlerEngine', () => {
6+
it('should have the correct name', () => {
7+
const engine = new BunBundlerEngine();
8+
expect(engine.name).toBe('bun');
9+
});
10+
11+
it('should be creatable with default config', () => {
12+
const engine = new BunBundlerEngine();
13+
expect(engine).toBeInstanceOf(BunBundlerEngine);
14+
});
15+
16+
it('should be creatable with custom config', () => {
17+
const engine = new BunBundlerEngine({
18+
minify: false,
19+
target: 'node',
20+
splitting: false,
21+
external: ['sharp'],
22+
});
23+
expect(engine).toBeInstanceOf(BunBundlerEngine);
24+
});
25+
26+
it('should throw error when Bun is not available', async () => {
27+
const engine = new BunBundlerEngine();
28+
const options: BundlerEngineOptions = {
29+
input: { index: '/tmp/test-entry.ts' },
30+
outputDir: '/tmp/test-output',
31+
external: [],
32+
sourcemap: false,
33+
platform: 'node',
34+
};
35+
36+
// Since we're running in Node.js (not Bun), this should throw
37+
await expect(engine.bundle(options)).rejects.toThrow('BunBundlerEngine requires Bun runtime');
38+
});
39+
});
40+
41+
describe('createBunEngine', () => {
42+
it('should create a BunBundlerEngine instance', () => {
43+
const engine = createBunEngine();
44+
expect(engine).toBeInstanceOf(BunBundlerEngine);
45+
expect(engine.name).toBe('bun');
46+
});
47+
48+
it('should pass config to the engine', () => {
49+
const engine = createBunEngine({
50+
minify: true,
51+
target: 'bun',
52+
});
53+
expect(engine).toBeInstanceOf(BunBundlerEngine);
54+
});
55+
});

packages/bundler-bun/src/index.ts

Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
import type { BundlerEngine, BundlerEngineOptions, BundlerEngineOutput } from '@mastra/core/bundler';
2+
3+
/**
4+
* Configuration options for the Bun bundler engine.
5+
*/
6+
export interface BunBundlerEngineConfig {
7+
/**
8+
* Whether to minify the output.
9+
* @default true
10+
*/
11+
minify?: boolean;
12+
13+
/**
14+
* Target environment for the bundle.
15+
* @default 'bun'
16+
*/
17+
target?: 'bun' | 'node' | 'browser';
18+
19+
/**
20+
* Additional packages to mark as external (in addition to those passed via options).
21+
*/
22+
external?: string[];
23+
24+
/**
25+
* Enable splitting for code splitting.
26+
* @default true
27+
*/
28+
splitting?: boolean;
29+
}
30+
31+
/**
32+
* Bun-based bundler engine for Mastra.
33+
*
34+
* This engine uses Bun's native bundler for fast, efficient bundling.
35+
* It requires Bun to be installed and available in the environment.
36+
*
37+
* @example
38+
* ```typescript
39+
* import { Mastra } from '@mastra/core';
40+
* import { BunBundlerEngine } from '@mastra/bundler-bun';
41+
*
42+
* export const mastra = new Mastra({
43+
* agents: { myAgent },
44+
* bundler: {
45+
* engine: new BunBundlerEngine(),
46+
* },
47+
* });
48+
* ```
49+
*
50+
* @example With configuration
51+
* ```typescript
52+
* import { BunBundlerEngine } from '@mastra/bundler-bun';
53+
*
54+
* const engine = new BunBundlerEngine({
55+
* minify: true,
56+
* target: 'bun',
57+
* splitting: true,
58+
* });
59+
* ```
60+
*/
61+
export class BunBundlerEngine implements BundlerEngine {
62+
readonly name = 'bun';
63+
64+
private config: BunBundlerEngineConfig;
65+
66+
constructor(config: BunBundlerEngineConfig = {}) {
67+
this.config = {
68+
minify: true,
69+
target: 'bun',
70+
splitting: true,
71+
...config,
72+
};
73+
}
74+
75+
async bundle(options: BundlerEngineOptions): Promise<BundlerEngineOutput> {
76+
// Check if Bun is available
77+
if (typeof globalThis.Bun === 'undefined') {
78+
throw new Error(
79+
'BunBundlerEngine requires Bun runtime. ' +
80+
'Please run with Bun or install Bun: https://bun.sh\n' +
81+
'If you want to use Rollup instead, remove the engine option from your bundler config.',
82+
);
83+
}
84+
85+
const entrypoints = Object.values(options.input);
86+
87+
// Merge external dependencies
88+
const external = [...options.external, ...(this.config.external || [])];
89+
90+
// Store the build result for writing
91+
let buildResult: Awaited<ReturnType<typeof Bun.build>> | undefined;
92+
93+
return {
94+
write: async () => {
95+
buildResult = await Bun.build({
96+
entrypoints,
97+
outdir: options.outputDir,
98+
target: this.config.target,
99+
minify: this.config.minify,
100+
splitting: this.config.splitting,
101+
sourcemap: options.sourcemap ? 'external' : 'none',
102+
external,
103+
define: options.define,
104+
naming: {
105+
// Match Rollup's output naming convention
106+
entry: '[dir]/[name].mjs',
107+
chunk: '[name]-[hash].mjs',
108+
},
109+
});
110+
111+
if (!buildResult.success) {
112+
const errors = buildResult.logs
113+
.filter(log => log.level === 'error')
114+
.map(log => log.message)
115+
.join('\n');
116+
117+
throw new Error(`Bun bundler failed:\n${errors}`);
118+
}
119+
120+
return buildResult;
121+
},
122+
close: async () => {
123+
// Bun.build doesn't require explicit cleanup
124+
buildResult = undefined;
125+
},
126+
};
127+
}
128+
}
129+
130+
/**
131+
* Create a Bun bundler engine with the given configuration.
132+
* This is a convenience function for creating a BunBundlerEngine instance.
133+
*
134+
* @example
135+
* ```typescript
136+
* import { createBunEngine } from '@mastra/bundler-bun';
137+
*
138+
* export const mastra = new Mastra({
139+
* bundler: {
140+
* engine: createBunEngine({ minify: true }),
141+
* },
142+
* });
143+
* ```
144+
*/
145+
export function createBunEngine(config?: BunBundlerEngineConfig): BunBundlerEngine {
146+
return new BunBundlerEngine(config);
147+
}
148+
149+
// Re-export types for convenience
150+
export type { BundlerEngine, BundlerEngineOptions, BundlerEngineOutput } from '@mastra/core/bundler';
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"extends": "./tsconfig.json",
3+
"compilerOptions": {
4+
"declaration": true,
5+
"declarationMap": true
6+
},
7+
"exclude": ["**/*.test.ts", "**/__tests__/**"]
8+
}

packages/bundler-bun/tsconfig.json

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
{
2+
"extends": "../../tsconfig.node.json",
3+
"compilerOptions": {
4+
"outDir": "dist",
5+
"rootDir": "src",
6+
"types": ["bun-types"]
7+
},
8+
"include": ["src/**/*"]
9+
}

0 commit comments

Comments
 (0)