while bun aims to have 1:1 parity with nodejs, I suspect nim targeting nodejs will be compatible with bun.sh
however, bun also has native APIs distinct from nodejs
Does the core team have any thoughts/plans around adding a specific bun target?
I believe such a combination could be attractive for anybody who wants to e.g. prototype stuff with a dynamic language and use strongly-typed (C/C++)-implementations at the same time. Nim would one-more-time shine for being able to build-the-bridge, show what magic-wonders macros can do and present strong packets from the nim-community e.g. like Arraymancer and others. And if that idea grows, maybe one day one might consider making this a compiler-backend - maybe when V3.0 will be around the corner in 2026 or so. But all needed pieces are already around. A feature-complete QuickJS4Nim-wrapper, the matured Quick-JS-engine itself and at least two people who imagine what fancy things one could do with it - big-tech-free.
greets & beatz, Andreas
Nim code > .node API (NAPI) with Denim: https://github.com/openpeeps/denim. Works with Node & Bun!
Some examples https://github.com/openpeeps/bro/blob/main/src/bro.nim https://github.com/openpeeps/tim/blob/main/src/tim.nim
Yep, I need a cool logo (and full examples)
Does Denim provide anyway to automatically build a binding.gyp file
Yes, Denim is a hybrid package. Check ~/.nimble/denim after installation.
Simply denim -h
DENIM 🔥 Native Node/BunJS addons powered by Nim
build <entry> <links> --release
Create a hybrid project via nimble init, then
import myprogram/submodule
when defined napibuild:
# Running `denim build myprogram.nim` compiles with `-d:napibuild`
import denim # NAPI bindings
import jsony
from strutils import `%`
type
User = object
name: string
enabled: bool
init proc(module: Module) =
# `Env` singleton represent the current `napi_env` context.
module.registerFn(0, "getWelcomeMessage"):
# Create and export a function named `getWelcomeMessage` with no arguments
return %* getWelcomeMessage()
module.registerFn(3, "testWithArgs"):
# a function with 3 arguments.
if not Env.expect(args, "MyProgram", ("name", napi_string), ("city", napi_string), ("?zipcode", napi_number)): return
# retrieve `napi_value` values. ugly too.
# todo macro to `args.has("zipcode")` and `args.get("zipcode")`
let
name = args[0].getStr
city = args[1].getStr
zipcode = if args.len == 3: $(args[2].getInt) else: "n/a"
let output = "Name: $1\nCity: $2\nZipcode: $3" % [name, city, zipcode]
return %* output
module.registerFn(1, "testNapiCall"):
if not Env.expect(args, "ErrorName", ("name", napi_string)): return
var user = User(name: args[0].getStr, enabled: true)
return napiCall("JSON.parse", [%* toJson(user)])
elif isMainModule:
echo(getWelcomeMessage())
Basically, denim build combines Nim compilation + NodeGYP
denim build src/myprogram.nim
This will generate a dir called denim_build in root of your project (contains necessary c sources and binding.gyp - this directory gets flushed everytime you run denim build).
For myprogram.node check in myprogram/bin directory
JavaScript
const nim = require('./myprogram.node')
console.log(nim) // {}
console.log(nim.getWelcomeMessage()) // Hello, World!
// nim.testWithArgs()
// Error: Type mismatch parameter: `name`. Got `undefined`, expected `string`
// nim.testWithArgs("J. Doe")
// Error: Type mismatch parameter: `city`. Got `undefined`, expected `string`
let x = nim.testWithArgs("J. Doe", "London") // works because `zipcode` is optional
console.log(x)
// nim.testWithArgs("Doe Again", "London", true) // ...error Got `bool`, expected `number``
let user = nim.testNapiCall("John Do the Do")
console.log(user) // {...}
console.log(user.name) // John Do the Do
What will the High-level API be like? Is it worth waiting for before I start?
Anything that can make code more readable (no ideas yet). A good start would be type definition (when calling registerFn. Anyway, the low-level API is pretty stable, you can give a try.
On my todo list for cli:
async red/blue functions
That "problem" is completely overblown and comes up all the time anyway in other contexts. Other programming languages have a distinction between "pure" functions and impure ones, some environments have the notion of "save to call in a signal handler".
You can call an async proc from a sync proc and you can call a sync proc from an async proc. Yes, it might be "blocking", omg, so what, at least you are not worse off than with the strictly sequential programming model.
The real problem here is the complexity of modelling concurrency in the async mindset where the event loop is hidden from you but not well enough so that concurrency bugs can easily creep into your code. This has nothing to do with "red/blue" functions.
moreover JS supports to run everything as async function, just await pure function
js
function sync() {
console.log('i am not async');
}
async function a() {
await sync(); // hidden transform to Promise.resolve
}
await a();
Mine: node-gyp v9.3.1 / node v19.9.0 / bun 0.5.9 .
Will try with the new versions. Anyway, let's forward this to /denim/issues
proc getWelcomeMessage(): string =
result = "Hello!"
init proc(module: Module) =
module.registerFn(0, "getWelcomeMessage"):
return %* getWelcomeMessage()
const {getWelcomeMessage} = require('./my.node')
console.log(getWelcomeMessage()) // Hello!
Just tried Bun, it's way better than node.
Node.JS seems to became outdated nowadays and lacks features, like import maps, no ES Modules by default etc.