Giter Club home page Giter Club logo

denim's Introduction

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.

denim's People

Contributors

ethosa avatar georgelemon avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar

Forkers

ethosa

denim's Issues

Nim exceptions break the node process

I noticed that when an exception is raised from the Nim code in the addon, the exception not displayed in the console and any future Nim calls don't work. The node process has to be restarted.

It might be useful for Denim to catch all exceptions and log them to the console in a harmless way.

denim -h doesn't do anything

I ran: denim -h but there was no output. Running echo $? returned 0 so the program ran OK.

This is with v.0.1.4.

Build binding.gyp given a package name

It would be very useful if Denim included a utility to build a binding.gyp file given a package name.

I would also like to know: must I list every .c file in the project's cache? Or only the main .c file? Thanks.

Compiled binary segfaults when run

Here's my test Nim program:

import myprogramapppkg/submodule

when defined napibuild:
  # Running `denim build myprogram.nim` compiles with `-d:napibuild`
  import denim # NAPI bindings
  import jsony

  init proc(module: Module) =
    module.registerFn(0, "getWelcomeMessage"):
      return %* getWelcomeMessage()

elif isMainModule:
  echo(getWelcomeMessage())

When I run the binary created in the bin directory I get a segfault 11. I suspect node-gyp has a problem somewhere. When I searched on this problem I saw that people logged similar issues for previous versions of node-gyp, but those are fixed by now.

Version information:

$ node --version
v18.16.0
$ node-gyp --version
v9.4.0
$ nim --version
Nim Compiler Version 1.6.12 [Linux: amd64]
Compiled at 2023-03-10
Copyright (c) 2006-2023 by Andreas Rumpf

git hash: 1aa9273640c0c51486cf3a7b67282fe58f360e91
active boot switches: -d:release

Code breaks with latest head

I installed the latest denim@#head to try the high level API, but unfortunately it doesn't work for me, and the low-level API seems to be broken too.

Low-level API has two problems:

  1. The Env.expect call I had no longer works:
Error: type mismatch: got <napi_env, seq[napi_value], string, (string, NapiValueType), (string, NapiValueType)>
but expected one of:
proc expect(env: napi_env; n: napi_value; expectKind: NapiValueType): bool
  first type mismatch at position: 2
  required type for n: napi_value
  but expression 'args' is of type: seq[napi_value]
proc expect(env: napi_env; v: seq[napi_value]; errorName, fnIdent: string): bool
  first type mismatch at position: 4
  required type for fnIdent: string
  but expression '("accountUserId", napi_number)' is of type: (string, NapiValueType)
  1. Node-gyp gives an unhelpful error: Error: unhandled exception: field 'vStr' is not accessible for type 'Parameter' using 'ptype = LongFlag' [FieldDefect]

The high-level API didn't work for me either, also two problems:

  1. The example on the GitHub README.md errors on a warning line:
Error: expression expected, but found 'A simple function from Nim'
  1. Calling args.get("name") also didn't work for me.

node-gyp put node_api.h in an unexpected place

I'm logging this issue in case anyone has a similar problem.

This caused nimble install denim to fail because it couldn't find node_api.h.

I found the file here: $HOME/.cache/node-gyp/18.16.0/include/node/node_api.h

So to fix the error, I copied the node directory with: cp -r /home/jasonfi/.cache/node-gyp/18.16.0/include/* /usr/include

After that denim installed fine via nimble.

Macros for cleaner export Nim code

It would be great to use macros to simplify the syntax of repetitive code.

E.g. instead of module.registerFn(3, "testWithArgs"): you could write registerFn testWithArgs (3 args):.

There are other possibilities too. Just an idea, not high priority.

Calling callback JS functions in Nim

At JS side I pass some functions in Nim procedure:

...
lib.callFunc(() => {
  console.log(1);
});
...

But at Nim side I have error on denim build:

Error: internal error: getTypeDescAux(tyEmpty)
No stack traceback available
To create a stacktrace, rerun compilation with './koch temp c <file>', see https://nim-lang.github.io/Nim/intern.html#debugging-the-compiler for details

Nim code:

init proc(module: Module) =

  proc callFunc(callback: napi_function): void {.export_napi.} =
    discard callFunction(args.get("callback"), [])

Nim version:

Nim Compiler Version 2.0.0 [Linux: amd64]
Compiled at 2023-08-01
Copyright (c) 2006-2023 by Andreas Rumpf

git hash: a488067a4130f029000be4550a0fb1b39e0e9e7c
active boot switches: -d:release

UPD: on 1.6.14 I got same error
UPD2: same on 1.6.8

Frequent: SIGSEGV: Illegal storage access. (Attempt to read from nil?)

My node module is causing Node to die quite often. It is typically (always?) a Nim GC function:

$HOME/.choosenim/toolchains/nim-1.6.14/lib/system/gc.nim(486) newObj
$HOME/.choosenim/toolchains/nim-1.6.14/lib/system/gc_common.nim(422) prepareDealloc

I'm not sure if a different GC would help, I think Denim uses the default? I usually specify the ORC GC when compiling the code I'm using for the node addon. I think it might help if Denim had an option to pass in extra args to the Nim compiler called by the Denim build command.

Build for Windows?

I see that this library builds Node addons only on Linux and only for Linux.

It possible to create builds for Windows?

Can't use add-on in an external project

I successfully build an add-on, but when I copy it to an external project, I can't get it to load. This is a Next.js project. Every technique I try encounters a different error.

Do I need to wait for the ability to build an npm from the add-on?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.