🎯

implementing-jsc-classes-zig

🎯Skill

from oven-sh/bun

VibeIndex|
What it does

implementing-jsc-classes-zig skill from oven-sh/bun

πŸ“¦

Part of

oven-sh/bun(5 items)

implementing-jsc-classes-zig

Installation

BunRun with Bun
bun run index.tsx # TS and JSX supported out-of-the-box
BunRun with Bun
bun run start # run the `start` script in `package.json`
BunRun with Bun
bun install <pkg> # install a package
Install ScriptRun install script
curl -fsSL https://bun.com/install | bash
npm installInstall npm package
npm install -g bun

+ 1 more commands

πŸ“– Extracted from docs: oven-sh/bun
13Installs
87,034
-
Last UpdatedJan 26, 2026

Skill Details

SKILL.md

Creates JavaScript classes using Bun's Zig bindings generator (.classes.ts). Use when implementing new JS APIs in Zig with JSC integration.

Overview

# Bun's JavaScriptCore Class Bindings Generator

Bridge JavaScript and Zig through .classes.ts definitions and Zig implementations.

Architecture

  1. Zig Implementation (.zig files)
  2. JavaScript Interface Definition (.classes.ts files)
  3. Generated Code (C++/Zig files connecting them)

Class Definition (.classes.ts)

```typescript

define({

name: "TextDecoder",

constructor: true,

JSType: "object",

finalize: true,

proto: {

decode: { args: 1 },

encoding: { getter: true, cache: true },

fatal: { getter: true },

},

});

```

Options:

  • name: Class name
  • constructor: Has public constructor
  • JSType: "object", "function", etc.
  • finalize: Needs cleanup
  • proto: Properties/methods
  • cache: Cache property values via WriteBarrier

Zig Implementation

```zig

pub const TextDecoder = struct {

pub const js = JSC.Codegen.JSTextDecoder;

pub const toJS = js.toJS;

pub const fromJS = js.fromJS;

pub const fromJSDirect = js.fromJSDirect;

encoding: []const u8,

fatal: bool,

pub fn constructor(

globalObject: *JSGlobalObject,

callFrame: *JSC.CallFrame,

) bun.JSError!*TextDecoder {

return bun.new(TextDecoder, .{ .encoding = "utf-8", .fatal = false });

}

pub fn decode(

this: *TextDecoder,

globalObject: *JSGlobalObject,

callFrame: *JSC.CallFrame,

) bun.JSError!JSC.JSValue {

const args = callFrame.arguments();

if (args.len < 1 or args.ptr[0].isUndefinedOrNull()) {

return globalObject.throw("Input cannot be null", .{});

}

return JSC.JSValue.jsString(globalObject, "result");

}

pub fn getEncoding(this: TextDecoder, globalObject: JSGlobalObject) JSC.JSValue {

return JSC.JSValue.createStringFromUTF8(globalObject, this.encoding);

}

fn deinit(this: *TextDecoder) void {

// Release resources

}

pub fn finalize(this: *TextDecoder) void {

this.deinit();

bun.destroy(this);

}

};

```

Key patterns:

  • Use bun.JSError!JSValue return type for error handling
  • Use globalObject not ctx
  • deinit() for cleanup, finalize() called by GC
  • Update src/bun.js/bindings/generated_classes_list.zig

CallFrame Access

```zig

const args = callFrame.arguments();

const first_arg = args.ptr[0]; // Access as slice

const argCount = args.len;

const thisValue = callFrame.thisValue();

```

Property Caching

For cache: true properties, generated accessors:

```zig

// Get cached value

pub fn encodingGetCached(thisValue: JSC.JSValue) ?JSC.JSValue {

const result = TextDecoderPrototype__encodingGetCachedValue(thisValue);

if (result == .zero) return null;

return result;

}

// Set cached value

pub fn encodingSetCached(thisValue: JSC.JSValue, globalObject: *JSC.JSGlobalObject, value: JSC.JSValue) void {

TextDecoderPrototype__encodingSetCachedValue(thisValue, globalObject, value);

}

```

Error Handling

```zig

pub fn method(this: MyClass, globalObject: JSGlobalObject, callFrame: *JSC.CallFrame) bun.JSError!JSC.JSValue {

const args = callFrame.arguments();

if (args.len < 1) {

return globalObject.throw("Missing required argument", .{});

}

return JSC.JSValue.jsString(globalObject, "Success!");

}

```

Memory Management

```zig

pub fn deinit(this: *TextDecoder) void {

this._encoding.deref();

if (this.buffer) |buffer| {

bun.default_allocator.free(buffer);

}

}

pub fn finalize(this: *TextDecoder) void {

JSC.markBinding(@src());

this.deinit();

bun.default_allocator.destroy(this);

}

```

Creating a New Binding

  1. Define interface in .classes.ts:

```typescript

define({

name: "MyClass",

constructor: true,

finalize: true,

proto: {

myMethod: { args: 1 },

myProperty: { getter: true, cache: true },

},

});

```

  1. Implement in .zig:

```zig

pub const MyClass = struct {

pub const js = JSC.Codegen.JSMyClass;

pub const toJS = js.toJS;

pub const fromJS = js.fromJS;

value: []const u8,

pub const new = bun.TrivialNew(@This());

pub fn constructor(globalObject: JSGlobalObject, callFrame: JSC.CallFrame) bun.JSError!*MyClass {

return MyClass.new(.{ .value = "" });

}

pub fn myMethod(this: MyClass, globalObject: JSGlobalObject, callFrame: *JSC.CallFrame) bun.JSError!JSC.JSValue {

return JSC.JSValue.jsUndefined();

}

pub fn getMyProperty(this: MyClass, globalObject: JSGlobalObject) JSC.JSValue {

return JSC.JSValue.jsString(globalObject, this.value);

}

pub fn deinit(this: *MyClass) void {}

pub fn finalize(this: *MyClass) void {

this.deinit();

bun.destroy(this);

}

};

```

  1. Add to src/bun.js/bindings/generated_classes_list.zig

Generated Components

  • C++ Classes: JSMyClass, JSMyClassPrototype, JSMyClassConstructor
  • Method Bindings: MyClassPrototype__myMethodCallback
  • Property Accessors: MyClassPrototype__myPropertyGetterWrap
  • Zig Bindings: External function declarations, cached value accessors