Newv1.0.2 / Tree-sitter rebuilds and full Bun API

Write JavaScript.
Ship a Go binary.

Gun is a JavaScript-to-Go transpiler. It compiles your codebase and the npm dependencies underneath it into Go that runs on real hardware, with Node and Bun compatibility intact.

$npm i -g gun-transpiler
Read documentation
Node 20+
Bun 1.x
TS 5.x
CJS / ESM
Trusted by teams
at 240+ companies
vercellinearstripefigmashopifyrailwayplanetscaleclerk
Live Transpilation

JavaScript in. Go out.

JavaScriptserver.js
import { createServer } from 'http'

const port = 8080

const handler = (req, res) => {
  const body = JSON.stringify({
    message: 'hello world',
    path: req.url,
    ok: true,
  })

  res.writeHead(200, { 'Content-Type': 'application/json' })
  res.end(body)
}

createServer(handler).listen(port)
Goserver.go
package main

import (
  jsvalue "github.com/nnstd/gun/runtime/builtin"
  json "github.com/nnstd/gun/runtime/builtin/json"
  nodehttp "github.com/nnstd/gun/runtime/http"
  eventloop "github.com/nnstd/gun/runtime/eventloop"
)

func main() {
  handler := jsvalue.NewFunction(func(args ...*jsvalue.JSValue) *jsvalue.JSValue {
    req := args[0]
    res := args[1]
    body := json.AsJSValue.Get("stringify").Call(jsvalue.ObjectFrom(map[string]any{
      "message": jsvalue.NewString("hello world"),
      "path": req.Get("url"),
      "ok": jsvalue.NewBool(true),
    }))

    res.MethodCall("writeHead", jsvalue.NewNumber(200))
    res.MethodCall("end", body)
    return nil
  })

  nodehttp.AsJSValue.Get("createServer").Call(handler)
  eventloop.Default.Run()
}
02 / Receipts

Numbers that survive a flame graph.

Same workload, a JSON-emitting HTTP server with one DB query, compiled three ways. Results from repeated runs on Hetzner CCX23. Reproduce them yourself from the benchmark repo.

HTTP req/sec
Node 20
28k req/s
Bun 1.3
65k req/s
Gun -> Go
280k req/s
10.0x
JSON parse 1MB
Node 20
8.4 ms
Bun 1.3
4.1 ms
Gun -> Go
1.2 ms
7.0x
Cold start
Node 20
142 ms
Bun 1.3
64 ms
Gun -> Go
38 ms
3.7x
Memory baseline
Node 20
64 MB
Bun 1.3
48 MB
Gun -> Go
22 MB
2.9x
03 / Anatomy

Five stages between .js and ./bin.

Gun is not a VM, not a wrapper, and not a bundler. It is a source-to-source compiler with a runtime designed so the emitted Go stays inspectable and operationally boring.

01
1

Parse

Tree-sitter parses CommonJS, ESM, JSX, and TypeScript with sub-millisecond incremental updates.

02
2

Resolve

Walks node_modules and tsconfig paths and resolves CJS or ESM interop the same way Node and Bun do.

03
3

Analyze

Type flow and scope analysis map JavaScript semantics to Go equivalents, static where possible and JSValue where needed.

04
4

Emit

Outputs Go using the JSValue runtime with gofmt-clean output and source maps back to the original files.

05
5

Compile

go build takes over and gives you a static binary ready for containers, bare metal, or a one-shot porting workflow.

04 / What you get

Built for the parts that hurt.

npm dependencies, transpiled in place

Gun follows your import graph all the way down, compiling the packages below your app instead of trapping them in a JS runtime.

express@4.18.0
zod@3.22.0
drizzle-orm@0.29.0
142 modules / 0 manual ports

A real Go binary, not a JS sandbox

The output is plain Go using a small runtime. Vendor it, audit it, fork it, or treat Gun as a migration tool and stop running it after the first successful port.

./build/api: ELF 64-bit
pie executable, x86-64
statically linked, stripped

Source maps you can trust

Stack traces resolve to the original .js or .ts line, so runtime failures stay debuggable after compilation.

Incremental, by default

Tree-sitter rebuilds only what changed. Watch mode stays in the tens of milliseconds on large codebases.

Open, MIT, audited

The transpiler and runtime are both MIT. Replace pieces, inspect the output, and keep control of your delivery path.

We replaced a Node fleet with Gun-compiled binaries over a weekend. P99 fell 4x, our memory bill fell 3x, and nobody had to learn Go.

SR
Sara Rahimi
Staff Engineer / platform team
05 / Questions

Things engineers ask before they install.

Honest answers. If yours is not here, ask in Discord or open an issue and treat the generated Go as part of the contract.

Pure-JS packages usually transpile cleanly. Native bindings such as sharp or canvas need to be marked external and wired through cgo or a Go equivalent.

Ready When You Are

Pull the trigger.

Free, MIT-licensed, and two commands away from your existing package.json. The rewrite is the compiler, not your application.

$npm i -g gun-transpiler
Read the docs