/denim

Node 💖 Nim = Denim! Build powerful NodeJS / BunJS addons with Nim language via Node API (NAPI)

Primary LanguageNimMIT LicenseMIT

Denim
Denim - Native NodeJS/BunJS addons powered by Nim
👑 Written in Nim language

nimble install denim

API reference
Github Actions Github Actions

😍 Key Features

  • CLI build via Nim + node-gyp or CMake.js (faster)
  • CLI publish to NPM
  • Low-level API
  • High-level API
  • Open Source | MIT License
  • Written in 👑 Nim language

Requirements

  • Nim (latest / via choosenim)
  • Node (latest) and node-gyp or CMake.js

CLI

Denim is a hybrid package, you can use it as a CLI for compiling Nim code to .node addon via Nim + NodeGYP and as a library for importing NAPI bindings.

Simply run denim -h

DENIM 🔥 Native Node/BunJS addons powered by Nim

  build <entry> <links> --cmake --yes --verbose          Build Nim project to a native NodeJS addon
  publish                                                Publish addon to NPM (requires npm cli)

Use Denim as a Nimble task:

task napi, "Build a .node addon":
  exec "denim build src/myprogram.nim"

Want to pass custom flags to Nim Compiler? Create a .nims file:

when defined napibuild:
  # add some flags

Note Check fully-working examples in /tests

Defining a module

Use init to define module initialization.

when defined napibuild:
  # optionally, you can use `napibuild` flag to wrap your code
  # this flag is set when compiling via `denim build src/myprogram.nim` 
  import denim # import NAPI bindings 
  init proc(module: Module) =
    # registering properties and functions here
    # this is similar with javascript `module.exports`
elif isMainModule:
  echo "just a normal nim program"

Nim Type to NapiValueType

Use low-level API to convert Nim values to napi_value (NapiValueType). Use assert to check if a low-level function returns a success or failure. Currently, the following status codes are supported

import denim
init proc(module: Module) =
  module.registerFn(0, "awesome"):
    var str2napi: napi_value
    var str = "Nim is awesome!"
    assert Env.napi_create_string_utf8(str, str.len.csize_t, str2napi.addr) 
    return str2napi

Alternatively, use %* to auto-convert Nim values to NapiValueType.

let
  a: napi_value = %* "Hey"
  b: napi_value = %* true
assert a.kind == napi_string
assert b.kind == napi_boolean

Exports

Since v0.1.5, you can use {.export_napi.} pragma to export functions and object properties.

import denim

init proc(module: Module): # the name `module` is required
  proc hello(name: string) {.export_napi} =
    ## A simple function from Nim
    return %*("Hello, " & args.get("name").getStr)

  var awesome {.export_napi.} = "Nim is Awesome!"

Calling a function/property from Node/Bun

const app = require('myaddon.node')
console.log(app.hello("World!"))       // Hello, World!
console.log(app.awesome)               // Nim is Awesome!

Built-in type checker

app.hello()
/*
 * A simple function from Nim
 * @param {string} name
 * @return {string}
 */
Type mismatch parameter: `name`. Got `undefined`, expected `string`

Real-World Examples

  • Tim Engine — A template engine. GitHub
  • Bro — A fast stylesheet language, alternative to SassC, DartSass. GitHub
  • HappyX — Macro-oriented asynchronous web-framework written in Nim. GitHub

Todo

  • Option to link external C Headers/libraries
  • Extend High-level API with compile-time functionality.

❤ Contributions & Support

🎩 License

Denim | MIT license. Made by Humans from OpenPeeps
Thanks to Andrew Breidenbach and Andrei Rosca for their work.

Copyright © 2023 OpenPeeps & Contributors — All rights reserved.