mirror of
https://github.com/oven-sh/bun
synced 2026-02-02 15:08:46 +00:00
204 lines
5.8 KiB
Plaintext
204 lines
5.8 KiB
Plaintext
# Registering Functions, Objects, and Modules in Bun
|
|
|
|
This guide documents the process of adding new functionality to the Bun global object and runtime.
|
|
|
|
## Overview
|
|
|
|
Bun's architecture exposes functionality to JavaScript through a set of carefully registered functions, objects, and modules. Most core functionality is implemented in Zig, with JavaScript bindings that make these features accessible to users.
|
|
|
|
There are several key ways to expose functionality in Bun:
|
|
|
|
1. **Global Functions**: Direct methods on the `Bun` object (e.g., `Bun.serve()`)
|
|
2. **Getter Properties**: Lazily initialized properties on the `Bun` object (e.g., `Bun.sqlite`)
|
|
3. **Constructor Classes**: Classes available through the `Bun` object (e.g., `Bun.ValkeyClient`)
|
|
4. **Global Modules**: Modules that can be imported directly (e.g., `import {X} from "bun:*"`)
|
|
|
|
## The Registration Process
|
|
|
|
Adding new functionality to Bun involves several coordinated steps across multiple files:
|
|
|
|
### 1. Implement the Core Functionality in Zig
|
|
|
|
First, implement your feature in Zig, typically in its own directory in `src/`. Examples:
|
|
|
|
- `src/valkey/` for Redis/Valkey client
|
|
- `src/semver/` for SemVer functionality
|
|
- `src/smtp/` for SMTP client
|
|
|
|
### 2. Create JavaScript Bindings
|
|
|
|
Create bindings that expose your Zig functionality to JavaScript:
|
|
|
|
- Create a class definition file (e.g., `js_bindings.classes.ts`) to define the JavaScript interface
|
|
- Implement `JSYourFeature` struct in a file like `js_your_feature.zig`
|
|
|
|
Example from a class definition file:
|
|
|
|
```typescript
|
|
// Example from a .classes.ts file
|
|
import { define } from "../../codegen/class-definitions";
|
|
|
|
export default [
|
|
define({
|
|
name: "YourFeature",
|
|
construct: true,
|
|
finalize: true,
|
|
hasPendingActivity: true,
|
|
memoryCost: true,
|
|
klass: {},
|
|
JSType: "0b11101110",
|
|
proto: {
|
|
yourMethod: {
|
|
fn: "yourZigMethod",
|
|
length: 1,
|
|
},
|
|
property: {
|
|
getter: "getProperty",
|
|
},
|
|
},
|
|
values: ["cachedValues"],
|
|
}),
|
|
];
|
|
```
|
|
|
|
### 3. Register with BunObject in `src/bun.js/bindings/BunObject+exports.h`
|
|
|
|
Add an entry to the `FOR_EACH_GETTER` macro:
|
|
|
|
```c
|
|
// In BunObject+exports.h
|
|
#define FOR_EACH_GETTER(macro) \
|
|
macro(CSRF) \
|
|
macro(CryptoHasher) \
|
|
... \
|
|
macro(YourFeature) \
|
|
```
|
|
|
|
### 4. Create a Getter Function in `src/bun.js/api/BunObject.zig`
|
|
|
|
Implement a getter function in `BunObject.zig` that returns your feature:
|
|
|
|
```zig
|
|
// In BunObject.zig
|
|
pub const YourFeature = toJSGetter(Bun.getYourFeatureConstructor);
|
|
|
|
// In the exportAll() function:
|
|
@export(&BunObject.YourFeature, .{ .name = getterName("YourFeature") });
|
|
```
|
|
|
|
### 5. Implement the Getter Function in a Relevant Zig File
|
|
|
|
Implement the function that creates your object:
|
|
|
|
```zig
|
|
// In your main module file (e.g., src/your_feature/your_feature.zig)
|
|
pub fn getYourFeatureConstructor(globalThis: *JSC.JSGlobalObject, _: *JSC.JSObject) JSC.JSValue {
|
|
return JSC.API.YourFeature.getConstructor(globalThis);
|
|
}
|
|
```
|
|
|
|
### 6. Add to Build System
|
|
|
|
Ensure your files are included in the build system by adding them to the appropriate targets.
|
|
|
|
## Example: Adding a New Module
|
|
|
|
Here's a comprehensive example of adding a hypothetical SMTP module:
|
|
|
|
1. Create implementation files in `src/smtp/`:
|
|
|
|
- `index.zig`: Main entry point that exports everything
|
|
- `SmtpClient.zig`: Core SMTP client implementation
|
|
- `js_smtp.zig`: JavaScript bindings
|
|
- `js_bindings.classes.ts`: Class definition
|
|
|
|
2. Define your JS class in `js_bindings.classes.ts`:
|
|
|
|
```typescript
|
|
import { define } from "../../codegen/class-definitions";
|
|
|
|
export default [
|
|
define({
|
|
name: "EmailClient",
|
|
construct: true,
|
|
finalize: true,
|
|
hasPendingActivity: true,
|
|
configurable: false,
|
|
memoryCost: true,
|
|
klass: {},
|
|
JSType: "0b11101110",
|
|
proto: {
|
|
send: {
|
|
fn: "send",
|
|
length: 1,
|
|
},
|
|
verify: {
|
|
fn: "verify",
|
|
length: 0,
|
|
},
|
|
close: {
|
|
fn: "close",
|
|
length: 0,
|
|
},
|
|
},
|
|
values: ["connectionPromise"],
|
|
}),
|
|
];
|
|
```
|
|
|
|
3. Add getter to `BunObject+exports.h`:
|
|
|
|
```c
|
|
#define FOR_EACH_GETTER(macro) \
|
|
macro(CSRF) \
|
|
... \
|
|
macro(SMTP) \
|
|
```
|
|
|
|
4. Add getter function to `BunObject.zig`:
|
|
|
|
```zig
|
|
pub const SMTP = toJSGetter(Bun.getSmtpConstructor);
|
|
|
|
// In exportAll:
|
|
@export(&BunObject.SMTP, .{ .name = getterName("SMTP") });
|
|
```
|
|
|
|
5. Implement getter in your module:
|
|
|
|
```zig
|
|
pub fn getSmtpConstructor(globalThis: *JSC.JSGlobalObject, _: *JSC.JSObject) JSC.JSValue {
|
|
return JSC.API.JSEmailClient.getConstructor(globalThis);
|
|
}
|
|
```
|
|
|
|
## Best Practices
|
|
|
|
1. **Follow Naming Conventions**: Align your naming with existing patterns
|
|
2. **Reference Existing Modules**: Study similar modules like Valkey or S3Client for guidance
|
|
3. **Memory Management**: Be careful with memory management and reference counting
|
|
4. **Error Handling**: Use `bun.JSError!JSValue` for proper error propagation
|
|
5. **Documentation**: Add JSDoc comments to your JavaScript bindings
|
|
6. **Testing**: Add tests for your new functionality
|
|
|
|
## Common Gotchas
|
|
|
|
- Be sure to handle reference counting properly with `ref()`/`deref()`
|
|
- Always implement proper cleanup in `deinit()` and `finalize()`
|
|
- For network operations, manage socket lifetimes correctly
|
|
- Use `JSC.Codegen` correctly to generate necessary binding code
|
|
|
|
## Related Files
|
|
|
|
- `src/bun.js/bindings/BunObject+exports.h`: Registration of getters and functions
|
|
- `src/bun.js/api/BunObject.zig`: Implementation of getters and object creation
|
|
- `src/bun.js/api/BunObject.classes.ts`: Class definitions
|
|
- `.cursor/rules/zig-javascriptcore-classes.mdc`: More details on class bindings
|
|
|
|
## Additional Resources
|
|
|
|
For more detailed information on specific topics:
|
|
|
|
- See `zig-javascriptcore-classes.mdc` for details on creating JS class bindings
|
|
- Review existing modules like `valkey`, `sqlite`, or `s3` for real-world examples
|