Thanks to visit codestin.com
Credit goes to pkg.go.dev

jsgo

package module
v0.1.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Apr 15, 2025 License: MIT Imports: 11 Imported by: 0

README

jsgo - Seamless Go/JavaScript Interop 🚀

Production-Grade Go/JavaScript Type Conversion - The missing bridge for complex Go/WebAssembly applications

Why?
Go's syscall/js requires manual type handling. jsgo enables:

  • Automatic struct ↔ object conversion
  • JS Class inheritance (extends tag)
  • Complex type support (Dates, Functions, BigInt, Protobufs)
  • Familiar struct tags like jsgo:"fieldName,omitempty,extends=Parent"

🚀 Quick Start (Problem → Solution)

Problem: Manual type conversion hell

// Old way - error-prone and tedious
js.Global().Set("user", map[string]any{
    "name": u.Name,
    "age":  u.Age,
})

jsgo Solution:

package main

import (
    "github.com/veecore/jsgo"
    "syscall/js"
    "time"
)

type User struct {
    Name string `jsgo:"name"`
    Age  int    `jsgo:"age,omitempty"`
    Feelings func(at time.Time) string // can be executed js side with js Date as argument
}

func main() {
    user := User{Name: "Alice", Age: 30}
    jsVal, _ := jsgo.Marshal(user)
    js.Global().Set("user", jsVal)
}

Problem: Manual class inheritance

// Old way - fragile prototype chains
class MyGoClass extends Parent {
  constructor() {
    super();  // 😖 Manual super handling
  }
}

jsgo Solution:

type Parent struct {
    js.Value `jsgo:",extends=HTMLElement"` // Inherit from browser API
}

jsgo.ExportGoType[Parent](nil)

type WebComponent struct {
    Parent  `jsgo:",extends=Parent"`  // Embedded parent
    State   string `jsgo:"state"`  // Custom fields
    private string                // Unexported fields stay in Go
}

// Register as custom element
jsgo.ExportGoType[WebComponent](nil) // supports constructor
// Now use as native class!
customElements.define('my-component', window.WebComponent)

🔑 Key Differentiators

Feature jsgo Standard Library
Class Inheritance ✅ Prototype Chain ❌ Manual
Struct Tags ✅ Extends/Name
Date ↔ Time ✅ Auto ❌ Manual
Cyclic Detection
Constructor Control ✅ Super Args
Production Benchmarks ✅ 5 allocs
Function Handling ✅ Variadic/Auto ❌ Manual

🛠 Installation

go get github.com/veecore/jsgo@latest

🏗 Class Inheritance Deep Dive

1. Simple Extension

type Animal struct {
    js.Value `jsgo:",extends=BaseCreature"`
    Legs int `jsgo:"limbs"`
}

// JS: class Animal extends BaseCreature { ... }

2. Custom Super Arguments

cfg := jsgo.ConstructorConfig{
    Fn: func(size int) *Widget {
        return &Widget{Size: size*2}
    },
    SuperArgs: func(args []js.Value) []js.Value {
        return []js.Value{js.ValueOf("processed")}
    },
}

jsgo.ExportGoTypeWithName[Widget](cfg, "AdvancedWidget")

📈 Real-World Use Cases

Web Component Framework

type WebButton struct {
    jsgo.Value `jsgo:",extends=HTMLButtonElement"`
    Theme string `jsgo:"data-theme,mustBe"`
}

// Constructor with theme validation
func NewButton(theme string) (*WebButton, error) {
    if theme == "" {
        return nil, errors.New("Theme required")
    }
    return &WebButton{Theme: theme}, nil
}

// Export as custom element
jsgo.ExportGoTypeWithName[WebButton](
    NewButton, 
    "MaterialButton",
)
// Browser usage
const btn = new MaterialButton("dark");
document.body.appendChild(btn);
btn.dataset.theme = "light";  // Type-safe updates

🏆 Performance Matters

Competitive with handwritten implementations while maintaining developer ergonomics

Benchmark jsgo Performance Handwritten Advantage
Marshaling
Complex Struct 431k ops/sec 367k ops/sec -17%
Primitive Types 1.8M ops/sec - _
Unmarshaling 589k ops/sec 367k ops/sec +38%
Memory Efficiency 5 allocs/op 25+ allocs/op 5x
# Object key
input_size_10     106,949 ns/op    (5 allocs)
input_size_2000   1,392,638 ns/op  (5 allocs)  # 13x slower for 200x data

# Vanilla JS approach comparison
input_size_2000  60,768,849 ns/op  (5992 allocs) # 43x slower than GoField

# Class creation (50k ops/sec)
BenchmarkClassCreate-12      50,265 ops/s     19.8µs/op     0 B/op

# Method calls (1.2M ops/sec)
BenchmarkMethodCall-12    1,234,548 ops/s      812ns/op     0 B/op

Documentation

Overview

Package jsgo provides complete Go↔JavaScript interoperability for WebAssembly with advanced features beyond standard library capabilities:

Features: - Bidirectional type conversion with struct tags (`jsgo:"field"`) - Export any Go function to JavaScript - Create JS classes from Go structs with inheritance - Automatic cycle detection and error handling - Pooled memory management for high-performance - Extended type support (time.Time, big.Int)

Core Concepts

  1. Function Export: Export Go functions to JS with any signature using FuncOfAny:

    jsgo.FuncOfAny(func(x int) string { ... })

  2. Class System: Create JS classes from Go structs with inheritance:

    type User struct { Base `jsgo:",extends=BaseClass"` Name string `jsgo:"username"` }

  3. Type Conversion: Convert complex types bidirectionally:

    jsgo.Marshal(userStruct) → JS object jsgo.Unmarshal(jsObj, &userStruct)

Security

- Validate all JS callback arguments - Never expose unguarded system functions - Use Release() for sensitive resources

# Struct Tags `jsgo:"-"` // Ignore field `jsgo:"fieldName"` // Custom property name `jsgo:",omitempty"` // Omit if empty `jsgo:",extends=BaseClass"` // Inheritance

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

func Bytes

func Bytes(b []byte) js.Value

Bytes efficiently converts Go byte slices to JavaScript Uint8Array. Panics if the copy is incomplete to prevent subtle data bugs.

For partial copies, use BytesN and check the returned count.

func BytesN

func BytesN(b []byte) (js.Value, int)

func CopyObject

func CopyObject(dst, src js.Value) (err error)

func ExportGoType

func ExportGoType[T any](constructor any) (release func(), err error)

ExportGoType exports a Go type as a JavaScript class with automatic resource management. Returns a cleanup function that should be called when the class is no longer needed.

func ExportGoTypeWithName

func ExportGoTypeWithName[T any](constructor any, name string) (release func(), err error)

ExportGoTypeWithName exports a Go type with a custom JavaScript class name. Useful for renaming types in the JS environment.

func FuncOf

func FuncOf(f func(this js.Value, jsArgs []js.Value) interface{}) js.Func

FuncOf provides a safer and more ergonomic alternative to js.FuncOf with automatic error handling. Converts Go errors to JS exceptions automatically. Return types are not limited unlike js.FuncOf

func FuncOfAny

func FuncOfAny(f any) js.Func

FuncOfAny wraps Go functions into JS functions with automatic type conversion. Supports variadic functions and handles error propagation.

Example:

jsFunc := FuncOfAny(func(x int) int { return x*2 })
js.Global().Set("double", jsFunc)
defer jsFunc.Release()

Note: Argument modifications won't affect JS side and returned values are copies.

Example

Function export and invocation

// Export Go function
double := jsgo.FuncOfAny(func(x int) int {
	return x * 2
})
defer double.Release()

js.Global().Set("double", double)

// JavaScript equivalent:
// console.log(double(4)) // Output: 8

func IsArray

func IsArray(v js.Value) bool

IsArray checks if a JS value is an array Equivalent to Array.isArray() in JavaScript

func IsObject

func IsObject(v js.Value) bool

func MakeArray

func MakeArray(len int, cap int) js.Value

func MakeArrayNoBuffer

func MakeArrayNoBuffer(len int) js.Value

func MakeBytes

func MakeBytes(len int, cap int) js.Value

MakeBytes creates a byte buffer with both length and capacity Underlying storage uses ArrayBuffer for optimal memory layout

Example:

buf := MakeBytes(1024, 4096) // 1KB view, 4KB capacity

func MakeBytesNoBuffer

func MakeBytesNoBuffer(len int) js.Value

func Marshal

func Marshal(goVal any) (js.Value, error)

Marshal converts Go values to JavaScript values with special handling for: - time.Time → JS Date - []byte → Uint8Array - big.Int → JS BigInt - Functions → JS callbacks

Example:

type User struct {
    Name string `jsgo:"username"`
}
u := &User{Name: "Alice"}
jsVal, err := Marshal(u) // Returns JS object {username: "Alice"}

Gotchas: - Cyclic structures return error - Channels are not supported

Example

Marshal example (Go → JS conversion)

type User struct {
	Name string `jsgo:"username"`
	Age  int    `jsgo:"userAge"`
}

u := User{Name: "Alice", Age: 30}
jsVal, _ := jsgo.Marshal(u)
fmt.Println(jsVal.Get("username").String())
Output:

Alice

func MarshalInto

func MarshalInto(jsVal js.Value, goVal any) error

MarshalInto marshals a Go value into an existing JavaScript reference type (Object). More efficient than Marshal for repeated operations as it reuses JS containers instead of creating new ones.

Parameters:

  • jsVal: Must be a JS reference type (Object, Array, TypedArray)
  • goVal: Go value to marshal (struct, map, slice, etc)

Returns:

  • error if marshaling fails (cyclic ref, unsupported type, etc)

Example:

jsObj := js.Global().Get("Object").New()
err := MarshalInto(jsObj, myStruct)
jsObj.Call("someMethod")

Notes:

  • Prefer for hot paths where JS object reuse matters
  • Does not clear existing properties

func New

func New(name string, args ...any) js.Value

New creates a JavaScript object using the specified constructor. Equivalent to `new ConstructorName(...args)` in JavaScript.

Example:

arr := jsgo.New("Array", 10) // Creates new Array(10)

func NewObject

func NewObject() js.Value

func ObjectKeys

func ObjectKeys(v js.Value) []string

ObjectKeys returns the enumerable own properties of a JS object.

func PrototypeOf

func PrototypeOf(obj js.Value) js.Value

func Throw

func Throw(err error) js.Value

Throw converts Go errors to JS exceptions. Use in function callbacks to propagate errors properly to JS.

Example:

js.FuncOf(func(this js.Value, args []js.Value) any {
    result, err := riskyOperation()
    if err != nil {
        return Throw(err)
    }
    return result
})

func Type

func Type(name string) js.Value

Type returns the JavaScript constructor for the given global name.

func TypeJSParallel

func TypeJSParallel[T any]() js.Type

func Unmarshal

func Unmarshal(jsVal js.Value, goVal any) error

Unmarshal populates Go values from JavaScript values with strict type checking.

similar to JSON unmarshaling.

Requires a pointer to writable memory.

Handles:

- JS Date → time.Time (UTC) - JS BigInt → int64/uint64/big.Int - JS Array → Go slice/array

Example:

var user struct{Name string `jsgo:"username"`}
err := Unmarshal(jsObject, &user)

Gotchas: - Numeric overflow returns error - JS null converts to Go zero values

Implement to encoding.TextUnmarshaler to support complex key types when unmarshaling JS objects to Go maps.

Example

Unmarshal example (JS → Go conversion)

type User struct {
	Name string `jsgo:"username"`
	Age  int    `jsgo:"userAge"`
}

jsObj := js.Global().Get("Object").New()
jsObj.Set("username", "Bob")
jsObj.Set("userAge", 25)

var result User
jsgo.Unmarshal(jsObj, &result)
fmt.Println(result.Name)
Output:

Bob

Types

type Class

type Class struct {
	Constructor js.Func
	Methods     map[string]js.Func
}

Class represents a JavaScript class with its constructor and methods. Use Release() to clean up resources when done

func TypeFor

func TypeFor[T any](constructor any) (Class, error)

TypeFor creates a Go type as a JavaScript class with proper inheritance support.

Parameters:

  • T: Go type to export (must be struct, array types, or map)
  • constructor: Either:
  • nil for default constructor
  • Constructor function (func(...) (T, [error])) // optional error
  • ConstructorConfig for advanced control

Returns:

  • error: Initialization error if any
  • release: Cleanup function to free JS resources
  • class: type as a js.Value

Features: - Inheritance via embedded struct tags: `jsgo:",extends=ParentClass"` - Custom super constructor arguments via ConstructorConfig.SuperArgs - Automatic resource management - Method export for exported Go methods

Example:

type MyClass struct {
    BaseClass `jsgo:",extends=BaseJSClass"`  // can be js.Value or formerly exported types
}

func NewMyClass(arg string) (*MyClass, error) { ... }

// Export with custom super args
cfg := ConstructorConfig{
    Fn: NewMyClass,
    SuperArgs: func(args []js.Value) []js.Value {
        return []js.Value{js.ValueOf("processed")}
    },
}
class, release, err := TypeFor[MyClass](cfg)
Example

Class creation and inheritance

type Base struct {
	ID string `jsgo:"id"`
}

type User struct {
	Base     `jsgo:",extends=BaseClass"`
	Username string `jsgo:"username"`
}

// Export as JS class
class, _ := jsgo.TypeFor[User](jsgo.ConstructorConfig{
	Fn: func(id, name string) *User {
		return &User{Base: Base{ID: id}, Username: name}
	},
})
defer class.Release()

// JavaScript equivalent:
// const user = new User("123", "alice");
// console.log(user.id, user.username); // 123, alice

func (Class) Release

func (c Class) Release()

Release cleans up all JS resources associated with the class.

type ConstructorConfig

type ConstructorConfig struct {
	// Constructor function that returns an instance of the type
	Fn interface{}

	// SuperArgs transforms arguments before passing to parent constructor.
	// If nil, all arguments are passed through.
	SuperArgs func([]js.Value) []js.Value
}

ConstructorConfig defines configuration options for class constructors

type Marshaler

type Marshaler interface {
	MarshalJS() (js.Value, error)
}

Marshaler is the interface implemented by types that can convert themselves to JavaScript values. Use for custom serialization logic.

Example:

type CustomID string
func (id CustomID) MarshalJS() (js.Value, error) {
    return js.ValueOf("ID:"+string(id)), nil
}

type MarshalerInto

type MarshalerInto interface {
	MarshalIntoJS(js.Value) error
}

MarshalerInto allows certain types(types have jsgo.TypeJSParallel[T]() == js.TypeObject) to marshal into existing JS values. Implementing this interface provides both MarshalJS and MarshalIntoJS capabilities.

Example:

type MySophisticatedType struct {
		myPrivateField int
}

func (m MySophisticatedType) MarshalIntoJS(v js.Value) error {
		v.Set("exposedField", m.myPrivateField)
		return nil
}

type Unmarshaler

type Unmarshaler interface {
	UnmarshalJS(v js.Value) error
}

Unmarshaler is the interface implemented by types that can decode themselves from JavaScript values. Use for custom validation/parsing.

Example:

type Account struct{ Balance float64 }
func (a *Account) UnmarshalJS(v js.Value) error {
    if v.Type() != js.TypeObject {
        return errors.New("must be object")
    }
    a.Balance = v.Get("balance").Float()
    return nil
}

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL