├── .gitignore ├── .vscode └── spellright.dict ├── examples ├── .gitignore ├── index.js ├── package.json ├── napi │ ├── utils.nim │ ├── nodeApiTypes.nim │ ├── jsNativeApiTypes.nim │ ├── nodeApi.nim │ ├── jsNativeApi.nim │ └── napibindings.nim ├── package-lock.json └── main.nim ├── bin └── napi-nim ├── src ├── helpers │ ├── pretty.js │ └── executeCmd.js ├── nim │ ├── utils.nim │ ├── nodeApiTypes.nim │ ├── jsNativeApiTypes.nim │ ├── nodeApi.nim │ ├── jsNativeApi.nim │ └── napibindings.nim ├── cli.js ├── napiinit.js └── napibuild.js ├── package.json ├── LICENSE └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules -------------------------------------------------------------------------------- /.vscode/spellright.dict: -------------------------------------------------------------------------------- 1 | napi_value 2 | -------------------------------------------------------------------------------- /examples/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | nimcache 3 | build 4 | binding.gyp -------------------------------------------------------------------------------- /bin/napi-nim: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | require = require('esm')(module /*, options*/); 4 | require('../src/cli').cli(process.argv); -------------------------------------------------------------------------------- /src/helpers/pretty.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Prettifies json object 3 | * @param {any} data 4 | */ 5 | export const pretty = (data) => JSON.stringify(data, null, 2); -------------------------------------------------------------------------------- /examples/index.js: -------------------------------------------------------------------------------- 1 | const addon = require("bindings")("main"); 2 | addon.hello("world"); 3 | console.log(addon.someProperty); 4 | console.log(addon.lowLevel); 5 | -------------------------------------------------------------------------------- /examples/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "examples", 3 | "version": "1.0.0", 4 | "description": "Node JS extenstion written in Nim", 5 | "main": "index.js", 6 | "keywords": [], 7 | "author": "", 8 | "dependencies": { 9 | "bindings": "^1.5.0" 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/helpers/executeCmd.js: -------------------------------------------------------------------------------- 1 | const { spawnSync } = require("child_process"); 2 | /** 3 | * Executes command synchronously, 4 | * keeps command output colors 5 | * @param {string} cmd 6 | * @param {string[]} args 7 | * @param {string | undefined} cwd 8 | */ 9 | export const executeCmd = (cmd, args, cwd) => 10 | spawnSync(cmd, args, { stdio: "inherit", cwd }); 11 | -------------------------------------------------------------------------------- /src/nim/utils.nim: -------------------------------------------------------------------------------- 1 | iterator enumerate*[T](open: openarray[T]): (int, T) = 2 | var ct = 0 3 | for elem in open: 4 | yield (ct, elem) 5 | inc ct 6 | 7 | proc toUnchecked*[T](open: openarray[T]): ptr UncheckedArray[T] = 8 | result = cast[ptr UncheckedArray[T]](alloc(sizeof(T) * open.len)) 9 | for i, elem in open.enumerate: 10 | result[][i] = elem 11 | 12 | 13 | -------------------------------------------------------------------------------- /examples/napi/utils.nim: -------------------------------------------------------------------------------- 1 | iterator enumerate*[T](open: openarray[T]): (int, T) = 2 | var ct = 0 3 | for elem in open: 4 | yield (ct, elem) 5 | inc ct 6 | 7 | proc toUnchecked*[T](open: openarray[T]): ptr UncheckedArray[T] = 8 | result = cast[ptr UncheckedArray[T]](alloc(sizeof(T) * open.len)) 9 | for i, elem in open.enumerate: 10 | result[][i] = elem 11 | 12 | 13 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "napi-nim", 3 | "version": "1.0.8", 4 | "description": "", 5 | "main": "src/index.js", 6 | "repository": { 7 | "type": "git", 8 | "url": "https://github.com/andi23rosca/napi-nim" 9 | }, 10 | "bin": { 11 | "@andi23rosca/napi-nim": "bin/napi-nim", 12 | "napi-nim": "bin/napi-nim" 13 | }, 14 | "scripts": { 15 | "test": "echo \"Error: no test specified\" && exit 1" 16 | }, 17 | "keywords": [], 18 | "author": "", 19 | "license": "ISC", 20 | "dependencies": { 21 | "chalk": "^4.1.0", 22 | "esm": "^3.2.25", 23 | "which": "^2.0.2" 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/cli.js: -------------------------------------------------------------------------------- 1 | function printHelp() { 2 | console.log( 3 | ` 4 | napi-nim -h Show all options. 5 | napi-nim init Create a new n-api addon project directory. 6 | napi-nim [options] Create a node-gyp build of a nim n-api project. 7 | 8 | options 9 | 10 | -r Build project in release mode. Without this flag will default to debug mode. 11 | ` 12 | ); 13 | } 14 | export function cli(args) { 15 | switch (args[2]) { 16 | case "init": 17 | require("./napiinit"); 18 | break; 19 | case "-h": 20 | printHelp(); 21 | break; 22 | default: 23 | require("./napibuild"); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/nim/nodeApiTypes.nim: -------------------------------------------------------------------------------- 1 | import jsNativeApiTypes 2 | type 3 | napi_callback_scope* {.header:"".} = pointer 4 | napi_async_context* {.header:"".} = pointer 5 | napi_async_work* {.header:"".} = pointer 6 | napi_threadsafe_function* {.header:"".} = pointer 7 | 8 | 9 | proc napi_async_execute_callback*(env: napi_env, data: pointer): void {.header:"".} 10 | proc napi_async_complete_callback*(env: napi_env, status: NapiStatus, data: pointer): void {.header:"".} 11 | 12 | 13 | type NapiNodeVersion* {.importc:"napi_node_version", header: "".} = object 14 | major: uint32 15 | minor: uint32 16 | patch: uint32 17 | release: cstring 18 | -------------------------------------------------------------------------------- /examples/napi/nodeApiTypes.nim: -------------------------------------------------------------------------------- 1 | import jsNativeApiTypes 2 | type 3 | napi_callback_scope* {.header:"".} = pointer 4 | napi_async_context* {.header:"".} = pointer 5 | napi_async_work* {.header:"".} = pointer 6 | napi_threadsafe_function* {.header:"".} = pointer 7 | 8 | 9 | proc napi_async_execute_callback*(env: napi_env, data: pointer): void {.header:"".} 10 | proc napi_async_complete_callback*(env: napi_env, status: NapiStatus, data: pointer): void {.header:"".} 11 | 12 | 13 | type NapiNodeVersion* {.importc:"napi_node_version", header: "".} = object 14 | major: uint32 15 | minor: uint32 16 | patch: uint32 17 | release: cstring 18 | -------------------------------------------------------------------------------- /examples/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "examples", 3 | "version": "1.0.0", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "bindings": { 8 | "version": "1.5.0", 9 | "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", 10 | "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", 11 | "requires": { 12 | "file-uri-to-path": "1.0.0" 13 | } 14 | }, 15 | "file-uri-to-path": { 16 | "version": "1.0.0", 17 | "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", 18 | "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==" 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Andi Rosca 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /examples/main.nim: -------------------------------------------------------------------------------- 1 | import ./napi/napibindings 2 | 3 | # Main registration of a module using the init macro 4 | init proc(module: Module) = 5 | 6 | # This is how you register a function onto the module. 7 | # The first argument is the number of arguments you expect 8 | # And the second one is the name of the function 9 | # It more or less translates to module.exports.hello = function() ... 10 | module.registerFn(1, "hello"): 11 | # All function args can be found in the args array 12 | # They are stored as napi_values and you need to use 13 | # conversion methods such as getStr, getInt, getBool, etc. to 14 | # get the equivalent Nim value 15 | let toGreet = args[0].getStr; 16 | echo "Hello " & toGreet 17 | 18 | # For the opposite conversion, Nim type -> napi_value 19 | # You can use the %* operator 20 | let obj = %* {"someNestedProp": true, "someOtherProp": 43, "arrayProp": [1, 2, "str"]} 21 | # Use register if you want to register a property 22 | module.register("someProperty", obj) 23 | 24 | 25 | # The previous examples use the high level APIs of the library. 26 | # If something is not supported in the high level you can work around by 27 | # using the lower level api. 28 | # The low level api simply defines Nim types for most of the definitions 29 | # in the node_api header file 30 | # You can find the original docs for them here https://nodejs.org/api/n-api.html#n_api_basic_n_api_data_types 31 | 32 | var 33 | lowObj: napi_value 34 | value: napi_value 35 | assessStatus module.env.napi_create_string_utf8("level", 5, addr value) 36 | assessStatus module.env.napi_create_object(addr lowObj); 37 | assessStatus module.env.napi_set_named_property(lowObj, "low", value) 38 | module.register("lowLevel", lowObj) -------------------------------------------------------------------------------- /src/napiinit.js: -------------------------------------------------------------------------------- 1 | const { argv } = require("process"); 2 | const fs = require("fs"); 3 | const path = require("path"); 4 | const chalk = require("chalk"); 5 | const { executeCmd } = require("./helpers/executeCmd"); 6 | const { pretty } = require("./helpers/pretty"); 7 | 8 | const projectName = argv[3]; 9 | const filesToInclude = [ 10 | "napibindings.nim", 11 | "jsNativeApi.nim", 12 | "jsNativeApiTypes.nim", 13 | "nodeApi.nim", 14 | "nodeApiTypes.nim", 15 | "utils.nim", 16 | ]; 17 | const projectDir = path.resolve(projectName); 18 | 19 | fs.mkdirSync(projectDir); 20 | fs.mkdirSync(path.join(projectDir, "napi")); 21 | 22 | filesToInclude.forEach((file) => { 23 | fs.copyFileSync( 24 | path.resolve(__dirname, "nim", file), 25 | path.resolve(projectDir, "napi", file) 26 | ); 27 | }); 28 | 29 | const packageFileContents = { 30 | name: projectName, 31 | version: "1.0.0", 32 | description: "Node JS extenstion written in Nim", 33 | main: "index.js", 34 | keywords: [], 35 | author: "", 36 | }; 37 | fs.writeFileSync( 38 | path.join(projectDir, "package.json"), 39 | pretty(packageFileContents) 40 | ); 41 | 42 | executeCmd("npm", ["install", "bindings"], projectDir); 43 | 44 | fs.writeFileSync( 45 | path.resolve(projectDir, "index.js"), 46 | `const addon = require("bindings")("main"); 47 | addon.hello();` 48 | ); 49 | 50 | fs.writeFileSync( 51 | path.resolve(projectDir, "main.nim"), 52 | `import ./napi/napibindings 53 | init proc(exports: Module) = 54 | exports.registerFn(0, "hello"): 55 | echo "Hello world"` 56 | ); 57 | 58 | fs.writeFileSync( 59 | path.resolve(projectDir, ".gitignore"), 60 | `node_modules 61 | nimcache 62 | build 63 | binding.gyp` 64 | ); 65 | 66 | console.log("%s Project ready.", chalk.green.bold("DONE")); 67 | console.log("\nTest that it works by building and running:"); 68 | console.log( 69 | chalk.cyanBright.bold( 70 | ` cd ${projectName} 71 | napi-nim main.nim 72 | node --napi-modules index.js` 73 | ) 74 | ); 75 | -------------------------------------------------------------------------------- /src/napibuild.js: -------------------------------------------------------------------------------- 1 | const { basename, dirname, resolve, join, sep } = require("path"); 2 | const { argv } = require("process"); 3 | const which = require("which"); 4 | const { writeFileSync, realpathSync } = require("fs"); 5 | const { executeCmd } = require("./helpers/executeCmd"); 6 | const { pretty } = require("./helpers/pretty"); 7 | 8 | const isReleaseBuild = argv.includes("-r"); 9 | 10 | const entryFilePath = argv[2]; 11 | const entryName = basename(entryFilePath, ".nim"); 12 | const entryDirectory = dirname(entryFilePath); 13 | 14 | /** 15 | * Pretty ugly code but it finds the nim lib folder that will need 16 | * to included when building the extension. 17 | */ 18 | let nimbase = dirname(realpathSync(which.sync("nim"))).split(sep); 19 | nimbase[nimbase.length-1] = "lib"; 20 | if(nimbase[0] === "") { 21 | nimbase.splice(0, 1); 22 | nimbase[0] = `/${nimbase[0]}`; 23 | } 24 | nimbase = resolve(...nimbase); 25 | /** 26 | * Path to where the nim cache folder will be. 27 | * Keeping it in the same directory makes building easier. 28 | */ 29 | const nimcache = resolve(entryDirectory, "nimcache"); 30 | 31 | const target = { 32 | target_name: "main", 33 | include_dirs: [nimbase], 34 | cflags: isReleaseBuild ? ["-w", "-O3", "-fno-strict-aliasing"] : ["-w"], 35 | linkflags: ["-ldl"], 36 | sources: [], 37 | }; 38 | // binding.gyp is a json so we hold the data in this variable 39 | const gyp = { 40 | targets: [target], 41 | }; 42 | 43 | /** 44 | * Runs the nim compiler on the entry file 45 | * and creates the equivalent C files in 46 | * nimcache to be used by node-gyp for 47 | * actual building later. 48 | */ 49 | executeCmd("nim", [ 50 | "c", 51 | `--nimcache:${nimcache}`, 52 | isReleaseBuild ? "-d:release" : "--embedsrc", 53 | "--compileOnly", 54 | "--noMain", 55 | entryFilePath, 56 | ]); 57 | 58 | /** 59 | * The nim compilation command will create some 60 | * files in the nimcache. Among them a .json file 61 | * which contains some metadata about how to compile 62 | * the generated C files. 63 | * We care about the source C files that node-gyp will 64 | * have to know about. We can get them from the json file's 65 | * compile property that has a list of the C files. 66 | */ 67 | const nimJson = require(resolve(nimcache, `${entryName}.json`)); 68 | target.sources = nimJson.compile.map((compile) => 69 | join("nimcache", basename(compile[0])) 70 | ); 71 | 72 | /** 73 | * Once the gyp object contains all data we write 74 | * the binding.gyp in the same directory as the 75 | * project's nim entry point file 76 | */ 77 | writeFileSync(resolve(entryDirectory, "binding.gyp"), pretty(gyp)); 78 | 79 | /** 80 | * Last step is to run the node-gyp build command 81 | * and create the final node extension which can be 82 | * imported into javascript 83 | */ 84 | executeCmd("node-gyp", [ 85 | "rebuild", 86 | `--directory=${entryDirectory}`, 87 | ...(isReleaseBuild ? [] : ["--debug"]), 88 | ]); 89 | -------------------------------------------------------------------------------- /src/nim/jsNativeApiTypes.nim: -------------------------------------------------------------------------------- 1 | type 2 | napi_env* {.header:"".} = pointer 3 | napi_value* {.header:"".} = pointer 4 | napi_ref* {.header:"".} = pointer 5 | napi_handle_scope* {.header:"".} = pointer 6 | napi_escapable_handle_scope* {.header:"".} = pointer 7 | napi_callback_info* {.header:"".} = pointer 8 | napi_deferred* {.header:"".} = pointer 9 | 10 | type NApiPropertyAttributes* {.importc: "napi_property_attributes", header:"".} = enum 11 | napi_default = 0 12 | napi_writable = 1 # 1 << 0 13 | napi_enumerable = 2 # 1 << 1 14 | napi_configurable = 4 # 1 << 2 15 | 16 | # Used with napi_define_class to distinguish static properties 17 | # from instance properties. Ignored by napi_define_properties. 18 | napi_static = 1024 # 1 << 10 19 | 20 | type NapiValueType* {.importc: "napi_valuetype", header:"".} = enum 21 | # ES6 types (corresponds to typeof) 22 | napi_undefined 23 | napi_null 24 | napi_boolean 25 | napi_number 26 | napi_string 27 | napi_symbol 28 | napi_object 29 | napi_function 30 | napi_external 31 | napi_bigint 32 | 33 | type NApiTypedArrayType* {.importc: "napi_typedarray_type", header:"".} = enum 34 | napi_int8_array 35 | napi_uint8_array 36 | napi_uint8_clamped_array 37 | napi_int16_array 38 | napi_uint16_array 39 | napi_int32_array 40 | napi_uint32_array 41 | napi_float32_array 42 | napi_float64_array 43 | napi_bigint64_array 44 | napi_biguint64_array 45 | 46 | type NapiStatus* {.importc: "napi_status", header:"".} = enum 47 | napi_ok 48 | napi_invalid_arg 49 | napi_object_expected 50 | napi_string_expected 51 | napi_name_expected 52 | napi_function_expected 53 | napi_number_expected 54 | napi_boolean_expected 55 | napi_array_expected 56 | napi_generic_failure 57 | napi_pending_exception 58 | napi_cancelled 59 | napi_escape_called_twice 60 | napi_handle_scope_mismatch 61 | napi_callback_scope_mismatch 62 | napi_queue_full 63 | napi_closing 64 | napi_bigint_expected 65 | napi_date_expected 66 | napi_arraybuffer_expected 67 | napi_detachable_arraybuffer_expected 68 | 69 | type napi_callback* = proc(environment: napi_env, info: napi_callback_info): napi_value {.cdecl.} 70 | 71 | type napi_finalize* = proc(environment: napi_env, finalize_data, finalize_hint: pointer): void {.cdecl.} 72 | 73 | type NapiPropertyDescriptor* {.importc: "napi_property_descriptor", header:"".} = object 74 | utf8name*: cstring 75 | name*, value*: napi_value 76 | attributes*: NApiPropertyAttributes 77 | `method`*, getter*, setter*: napi_callback 78 | data*: pointer 79 | 80 | 81 | type NapiExtendedErrorInfo* {.importc: "napi_extended_error_info", header:"".} = object 82 | error_message*: cstring 83 | engine_reserved*: pointer 84 | engine_error_code*: uint32 85 | error_code*: NapiStatus -------------------------------------------------------------------------------- /examples/napi/jsNativeApiTypes.nim: -------------------------------------------------------------------------------- 1 | type 2 | napi_env* {.header:"".} = pointer 3 | napi_value* {.header:"".} = pointer 4 | napi_ref* {.header:"".} = pointer 5 | napi_handle_scope* {.header:"".} = pointer 6 | napi_escapable_handle_scope* {.header:"".} = pointer 7 | napi_callback_info* {.header:"".} = pointer 8 | napi_deferred* {.header:"".} = pointer 9 | 10 | type NApiPropertyAttributes* {.importc: "napi_property_attributes", header:"".} = enum 11 | napi_default = 0 12 | napi_writable = 1 # 1 << 0 13 | napi_enumerable = 2 # 1 << 1 14 | napi_configurable = 4 # 1 << 2 15 | 16 | # Used with napi_define_class to distinguish static properties 17 | # from instance properties. Ignored by napi_define_properties. 18 | napi_static = 1024 # 1 << 10 19 | 20 | type NapiValueType* {.importc: "napi_valuetype", header:"".} = enum 21 | # ES6 types (corresponds to typeof) 22 | napi_undefined 23 | napi_null 24 | napi_boolean 25 | napi_number 26 | napi_string 27 | napi_symbol 28 | napi_object 29 | napi_function 30 | napi_external 31 | napi_bigint 32 | 33 | type NApiTypedArrayType* {.importc: "napi_typedarray_type", header:"".} = enum 34 | napi_int8_array 35 | napi_uint8_array 36 | napi_uint8_clamped_array 37 | napi_int16_array 38 | napi_uint16_array 39 | napi_int32_array 40 | napi_uint32_array 41 | napi_float32_array 42 | napi_float64_array 43 | napi_bigint64_array 44 | napi_biguint64_array 45 | 46 | type NapiStatus* {.importc: "napi_status", header:"".} = enum 47 | napi_ok 48 | napi_invalid_arg 49 | napi_object_expected 50 | napi_string_expected 51 | napi_name_expected 52 | napi_function_expected 53 | napi_number_expected 54 | napi_boolean_expected 55 | napi_array_expected 56 | napi_generic_failure 57 | napi_pending_exception 58 | napi_cancelled 59 | napi_escape_called_twice 60 | napi_handle_scope_mismatch 61 | napi_callback_scope_mismatch 62 | napi_queue_full 63 | napi_closing 64 | napi_bigint_expected 65 | napi_date_expected 66 | napi_arraybuffer_expected 67 | napi_detachable_arraybuffer_expected 68 | 69 | type napi_callback* = proc(environment: napi_env, info: napi_callback_info): napi_value {.cdecl.} 70 | 71 | type napi_finalize* = proc(environment: napi_env, finalize_data, finalize_hint: pointer): void {.cdecl.} 72 | 73 | type NapiPropertyDescriptor* {.importc: "napi_property_descriptor", header:"".} = object 74 | utf8name*: cstring 75 | name*, value*: napi_value 76 | attributes*: NApiPropertyAttributes 77 | `method`*, getter*, setter*: napi_callback 78 | data*: pointer 79 | 80 | 81 | type NapiExtendedErrorInfo* {.importc: "napi_extended_error_info", header:"".} = object 82 | error_message*: cstring 83 | engine_reserved*: pointer 84 | engine_error_code*: uint32 85 | error_code*: NapiStatus -------------------------------------------------------------------------------- /src/nim/nodeApi.nim: -------------------------------------------------------------------------------- 1 | import macros 2 | import nodeApiTypes, jsNativeApiTypes 3 | 4 | proc napi_addon_register_func*(env: napi_env, exports: napi_value): napi_value {.header: "".} 5 | 6 | type NapiModule* {.importc: "napi_module", header:"".} = object 7 | nm_version: cint 8 | nm_flags: cuint 9 | nm_filename: cstring 10 | nm_register_func: ptr napi_addon_register_func.typeof 11 | nm_modname: cstring 12 | nm_priv: pointer 13 | reserved: pointer 14 | 15 | macro napiModule*(initHook: proc(env: napi_env, exports: napi_value)): void = 16 | var nimmain = newProc(ident("NimMain")) 17 | nimmain.addPragma(ident("importc")) 18 | 19 | var initFunc = newProc( 20 | name = ident("initfunc"), 21 | params = [ 22 | ident("napi_value"), 23 | newIdentDefs(ident("env"), ident("napi_env")), 24 | newIdentDefs(ident("exports"), ident("napi_value")) 25 | ], 26 | body = newStmtList( 27 | nimmain, 28 | newCall("NimMain"), 29 | newCall(initHook, ident("env"), ident("exports")), 30 | newNimNode(nnkReturnStmt).add(ident("exports")) 31 | ) 32 | ) 33 | initFunc.addPragma(ident("exportc")) 34 | result = newStmtList( 35 | initFunc, 36 | newNimNode(nnkPragma).add(newColonExpr(ident("emit"), newStrLitNode("""/*VARSECTION*/ NAPI_MODULE(NODE_GYP_MODULE_NAME, initfunc)"""))) 37 | ) 38 | echo result.toStrLit 39 | 40 | 41 | proc napi_module_register*(module: NapiModule): void {.header: "".} 42 | 43 | proc napi_fatal_error*( 44 | location: cstring, 45 | location_len: csize_t, 46 | message: cstring, 47 | message_len: csize_t 48 | ): void {.header: "".} 49 | 50 | 51 | 52 | # Methods for custom handling of async operations 53 | proc napi_async_init*( 54 | env: napi_env, 55 | async_resource: napi_value, 56 | async_resource_name: napi_value, 57 | result: ptr napi_async_context 58 | ): NapiStatus {.header: "".} 59 | 60 | proc napi_async_destroy*( 61 | env: napi_env, 62 | async_context: napi_async_context 63 | ): NapiStatus {.header: "".} 64 | 65 | proc napi_make_callback*( 66 | env: napi_env, 67 | async_context: napi_async_context, 68 | recv: napi_value, 69 | fn: napi_value, 70 | argc: csize_t, 71 | argv: ptr napi_value, 72 | result: ptr napi_value 73 | ): NapiStatus {.header: "".} 74 | 75 | 76 | 77 | # Methods to provide node::Buffer functionality with napi types 78 | proc napi_create_buffer*( 79 | env: napi_env, 80 | length: csize_t, 81 | data: ptr pointer, 82 | result: ptr napi_value 83 | ): NapiStatus {.header: "".} 84 | 85 | proc napi_create_external_buffer*( 86 | env: napi_env, 87 | length: csize_t, 88 | data: pointer, 89 | finalize_cb: napi_finalize, 90 | finalize_hint: pointer, 91 | result: ptr napi_value 92 | ): NapiStatus {.header: "".} 93 | 94 | proc napi_create_buffer_copy*( 95 | env: napi_env, 96 | length: csize_t, 97 | data: pointer, 98 | result_data: ptr pointer, 99 | result: ptr napi_value 100 | ): NapiStatus {.header: "".} 101 | 102 | proc napi_is_buffer*( 103 | env: napi_env, 104 | value: napi_value, 105 | results: ptr bool 106 | ): NapiStatus {.header: "".} 107 | 108 | proc napi_get_buffer_info*( 109 | env: napi_env, 110 | value: napi_value, 111 | data: ptr pointer, 112 | length: ptr csize_t 113 | ): NapiStatus {.header: "".} 114 | 115 | 116 | 117 | # Methods to manage simple async operations 118 | proc napi_create_async_work*( 119 | env: napi_env, 120 | async_resource: napi_value, 121 | async_resource_name: napi_value, 122 | execute: ptr napi_async_execute_callback.typeof, 123 | complete: ptr napi_async_complete_callback.typeof, 124 | data: pointer, 125 | result: ptr napi_async_work 126 | ): NapiStatus {.header: "".} 127 | 128 | proc napi_delete_async_work*( 129 | env: napi_env, 130 | work: napi_async_work 131 | ): NapiStatus {.header: "".} 132 | 133 | proc napi_queue_async_work*( 134 | env: napi_env, 135 | work: napi_async_work 136 | ): NapiStatus {.header: "".} 137 | 138 | proc napi_cancel_async_work*( 139 | env: napi_env, 140 | work: napi_async_work 141 | ): NapiStatus {.header: "".} 142 | 143 | 144 | 145 | # Version management 146 | proc napi_get_node_version*(env: napi_env, version: ptr NapiNodeVersion): NapiStatus {.header: "".} 147 | -------------------------------------------------------------------------------- /examples/napi/nodeApi.nim: -------------------------------------------------------------------------------- 1 | import macros 2 | import nodeApiTypes, jsNativeApiTypes 3 | 4 | proc napi_addon_register_func*(env: napi_env, exports: napi_value): napi_value {.header: "".} 5 | 6 | type NapiModule* {.importc: "napi_module", header:"".} = object 7 | nm_version: cint 8 | nm_flags: cuint 9 | nm_filename: cstring 10 | nm_register_func: ptr napi_addon_register_func.typeof 11 | nm_modname: cstring 12 | nm_priv: pointer 13 | reserved: pointer 14 | 15 | macro napiModule*(initHook: proc(env: napi_env, exports: napi_value)): void = 16 | var nimmain = newProc(ident("NimMain")) 17 | nimmain.addPragma(ident("importc")) 18 | 19 | var initFunc = newProc( 20 | name = ident("initfunc"), 21 | params = [ 22 | ident("napi_value"), 23 | newIdentDefs(ident("env"), ident("napi_env")), 24 | newIdentDefs(ident("exports"), ident("napi_value")) 25 | ], 26 | body = newStmtList( 27 | nimmain, 28 | newCall("NimMain"), 29 | newCall(initHook, ident("env"), ident("exports")), 30 | newNimNode(nnkReturnStmt).add(ident("exports")) 31 | ) 32 | ) 33 | initFunc.addPragma(ident("exportc")) 34 | result = newStmtList( 35 | initFunc, 36 | newNimNode(nnkPragma).add(newColonExpr(ident("emit"), newStrLitNode("""/*VARSECTION*/ NAPI_MODULE(NODE_GYP_MODULE_NAME, initfunc)"""))) 37 | ) 38 | echo result.toStrLit 39 | 40 | 41 | proc napi_module_register*(module: NapiModule): void {.header: "".} 42 | 43 | proc napi_fatal_error*( 44 | location: cstring, 45 | location_len: csize_t, 46 | message: cstring, 47 | message_len: csize_t 48 | ): void {.header: "".} 49 | 50 | 51 | 52 | # Methods for custom handling of async operations 53 | proc napi_async_init*( 54 | env: napi_env, 55 | async_resource: napi_value, 56 | async_resource_name: napi_value, 57 | result: ptr napi_async_context 58 | ): NapiStatus {.header: "".} 59 | 60 | proc napi_async_destroy*( 61 | env: napi_env, 62 | async_context: napi_async_context 63 | ): NapiStatus {.header: "".} 64 | 65 | proc napi_make_callback*( 66 | env: napi_env, 67 | async_context: napi_async_context, 68 | recv: napi_value, 69 | fn: napi_value, 70 | argc: csize_t, 71 | argv: ptr napi_value, 72 | result: ptr napi_value 73 | ): NapiStatus {.header: "".} 74 | 75 | 76 | 77 | # Methods to provide node::Buffer functionality with napi types 78 | proc napi_create_buffer*( 79 | env: napi_env, 80 | length: csize_t, 81 | data: ptr pointer, 82 | result: ptr napi_value 83 | ): NapiStatus {.header: "".} 84 | 85 | proc napi_create_external_buffer*( 86 | env: napi_env, 87 | length: csize_t, 88 | data: pointer, 89 | finalize_cb: napi_finalize, 90 | finalize_hint: pointer, 91 | result: ptr napi_value 92 | ): NapiStatus {.header: "".} 93 | 94 | proc napi_create_buffer_copy*( 95 | env: napi_env, 96 | length: csize_t, 97 | data: pointer, 98 | result_data: ptr pointer, 99 | result: ptr napi_value 100 | ): NapiStatus {.header: "".} 101 | 102 | proc napi_is_buffer*( 103 | env: napi_env, 104 | value: napi_value, 105 | results: ptr bool 106 | ): NapiStatus {.header: "".} 107 | 108 | proc napi_get_buffer_info*( 109 | env: napi_env, 110 | value: napi_value, 111 | data: ptr pointer, 112 | length: ptr csize_t 113 | ): NapiStatus {.header: "".} 114 | 115 | 116 | 117 | # Methods to manage simple async operations 118 | proc napi_create_async_work*( 119 | env: napi_env, 120 | async_resource: napi_value, 121 | async_resource_name: napi_value, 122 | execute: ptr napi_async_execute_callback.typeof, 123 | complete: ptr napi_async_complete_callback.typeof, 124 | data: pointer, 125 | result: ptr napi_async_work 126 | ): NapiStatus {.header: "".} 127 | 128 | proc napi_delete_async_work*( 129 | env: napi_env, 130 | work: napi_async_work 131 | ): NapiStatus {.header: "".} 132 | 133 | proc napi_queue_async_work*( 134 | env: napi_env, 135 | work: napi_async_work 136 | ): NapiStatus {.header: "".} 137 | 138 | proc napi_cancel_async_work*( 139 | env: napi_env, 140 | work: napi_async_work 141 | ): NapiStatus {.header: "".} 142 | 143 | 144 | 145 | # Version management 146 | proc napi_get_node_version*(env: napi_env, version: ptr NapiNodeVersion): NapiStatus {.header: "".} 147 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Write NodeJS native extensions in Nim 2 | If you don't like the verbosity of C code and feel that C++ is too complex, then try to improve the performance of your NodeJS apps with `napi-nim`. 3 | 4 | The new `n-api` which is now part of NodeJS lets you interface with JavaScript code from any language that supports the C ABI. [Nim](https://nim-lang.org/) is a language that values efficiency above all else, but provides a lot of high level constructs and niceties for developer productivity, making it a very suitable choice for writing performant add-ons to your NodeJS code. 5 | 6 | - [Write NodeJS native extensions in Nim](#write-nodejs-native-extensions-in-nim) 7 | * [Prerequisites](#prerequisites) 8 | * [Installation](#installation) 9 | * [Basic usage](#basic-usage) 10 | * [API](#api) 11 | + [Defining a module](#defining-a-module) 12 | + [Registering module exports](#registering-module-exports) 13 | + [Converting between `napi_value` and Nim types](#converting-between--napi-value--and-nim-types) 14 | - [napi_value -> Nim](#napi-value----nim) 15 | - [Nim -> napi_value](#nim----napi-value) 16 | + [Function with arguments](#function-with-arguments) 17 | * [Low level API](#low-level-api) 18 | * [Roadmap](#roadmap) 19 | 20 | ## Prerequisites 21 | Since we're building a NodeJS extension in Nim you should install both from: 22 | - NodeJS: https://nodejs.org/en/ 23 | - Nim: https://nim-lang.org/install.html 24 | 25 | Make sure you have `node-gyp` installed globally by doing 26 | ``` 27 | npm i -g node-gyp 28 | ``` 29 | 30 | ## Installation 31 | Install `napi-nim` globally 32 | ``` 33 | npm i -g napi-nim 34 | ``` 35 | 36 | 37 | ## Basic usage 38 | To get started run the following command: 39 | ``` 40 | napi-nim init hello-world 41 | ``` 42 | If it didn't work one of the following things might be the cause: 43 | - The location in which you ran the command should not contain a folder named `hello-world`. 44 | - You need an internet connection when running the command since it does an `npm install` of the dependencies of the project for you. 45 | 46 | If everything went fine the output of the command should say: 47 | ``` 48 | DONE Project ready. 49 | 50 | Test that it works by building and running: 51 | cd hello-world 52 | napi-nim main.nim 53 | node --napi-modules index.js 54 | ``` 55 | 56 | `napi-nim` has now created a new folder in the directory in which you ran the command called `hello-world`. 57 | The folder contains all of the necessary dependencies to start working on your Nim add-on. 58 | 59 | Next up, follow the next steps outlined in the output: 60 | 61 | 1. `cd hello-world` to navigate to the project directory. 62 | 2. `napi-nim main.nim` will compile the Nim file to a C file, and then use `node-gyp` to build the final NodeJS add-on. 63 | 3. `node --napi-modules index.js` will run the JS file. the `--napi-modules` flag ensures that node knows to look for a n-api add-on. 64 | 65 | 66 | Running the last command should output `Hello world` in the command line. 67 | 68 | ## API 69 | `napi-nim` provides a high level API over the classic `n-api`. 70 | This readme might be incomplete, to see all available high level APIs see `napi/napibindings.nim`. 71 | 72 | ### Defining a module 73 | The `init` macro will help you define a module initialization proc. 74 | The proc definition following it should accept an argument of the type `Module`. 75 | ```nim 76 | init proc(module: Module) = 77 | ``` 78 | 79 | ### Registering module exports 80 | For exporting properties and functions there are 2 separate templates to use. 81 | ```nim 82 | init proc(module: Module) = 83 | # equivalent to module.exports.hello = function () .... 84 | module.registerFn(0, "hello"): 85 | echo "hello world" 86 | 87 | # equivalent to module.exports.num = 23 88 | module.register("num", 23) 89 | ``` 90 | 91 | ### Converting between `napi_value` and Nim types 92 | The node api defines the type `napi_value` which can take the value of the JS types such as number, string, object, etc. 93 | For you to be able to translate between JS types and Nim types and vice-versa, `napi-nim` exposes a few helpers. 94 | 95 | #### napi_value -> Nim 96 | ```nim 97 | jsString.getStr # to get the string equivalent 98 | jsNumber.getInt # to convert a js number to int, getInt32/64 and getFloat32/64 are also available 99 | ``` 100 | See the rest in `napi/napibindings.nim`. 101 | 102 | #### Nim -> napi_value 103 | Use the `%*` operator to convert Nim types to JS. 104 | Make sure there's an empty space between the operator and the definition. 105 | ```nim 106 | let obj = %* {"someProp": 23} 107 | ``` 108 | 109 | ### Function with arguments 110 | When registering a function, the first argument should be the number of arguments you expect for it. 111 | 112 | You can get the arguments from the `args` array that is made available by `registerFn`. Keep in mind the arguments you receive will be JS types so you have to convert to Nim types if you want to use them. 113 | ```nim 114 | module.registerFn(1, "hello"): 115 | let toGreet = args[0].getStr; # Getting the first and only argument as a string 116 | echo "Hello " & toGreet 117 | ``` 118 | 119 | ## Low level API 120 | For the things that the high level API might not support yet there's the option to use the lower level functions and types provided 121 | by the node_api header. 122 | You can see in the rest of the files in `napi` like `napi/jsNativeApi.nim` the wrappers which are just Nim type definitions equivalent to the header files from the n-api. 123 | 124 | Check the original docs here: https://nodejs.org/api/n-api.html#n_api_basic_n_api_data_types 125 | 126 | One function to mention is `assessStatus`. It checks that a low level api function returns a success, and if not throws an error. 127 | You could also use `discard` instead to ignore the returned status, but it's not recommended. 128 | 129 | ```nim 130 | var 131 | obj: napi_value 132 | value: napi_value 133 | assessStatus module.env.napi_create_string_utf8("level", 5, addr value) # create a js string and put it in value 134 | assessStatus module.env.napi_create_object(addr lowObj); # create a js object and put it in obj 135 | assessStatus module.env.napi_set_named_property(obj, "low", value) # Set the property low to be the value string 136 | ``` 137 | 138 | 139 | 140 | ## Roadmap 141 | The project is still new and unfinished. 142 | It can already be used to create add-ons but it is missing the following features/nice-to-haves: 143 | 144 | - [ ] Examples folder to show how to use. 145 | - [ ] In-depth explanation of the code-base. 146 | - [ ] Collaboration guide. 147 | - [ ] `napi-nim init` should include bundling and publishing an add-on out of the box. (Right now you have to figure it out yourself 😕) 148 | 149 | Credit to https://github.com/AjBreidenbach/napibindings which this project is heavily based on. I opted for a new repository instead of PR since it seems that the project is completely abandoned. 150 | -------------------------------------------------------------------------------- /src/nim/jsNativeApi.nim: -------------------------------------------------------------------------------- 1 | import jsNativeApiTypes 2 | 3 | proc napi_get_last_error_info*(env: napi_env, result: UncheckedArray[NapiExtendedErrorInfo]): NapiStatus {.header: "".} 4 | 5 | 6 | # Getters for defined singletons 7 | proc napi_get_undefined*(env: napi_env, result: ptr napi_value): NapiStatus {.header: "".} 8 | 9 | proc napi_get_null*(env: napi_env, result: ptr napi_value): NapiStatus {.header: "".} 10 | 11 | proc napi_get_global*(env: napi_env, result: ptr napi_value): NapiStatus {.header: "".} 12 | 13 | proc napi_get_boolean*(env: napi_env, value: bool, result: ptr napi_value): NapiStatus {.header: "".} 14 | 15 | 16 | 17 | # Methods to create Primitive types/Objects 18 | proc napi_create_object*(env: napi_env, result: ptr napi_value): NapiStatus {.header: "".} 19 | 20 | proc napi_create_array*(env: napi_env, result: ptr napi_value): NapiStatus {.header: "".} 21 | 22 | proc napi_create_array_with_length*(env: napi_env, length: csize_t, result: ptr napi_value): NapiStatus {.header: "".} 23 | 24 | proc napi_create_double*(env: napi_env, value: float64, result: ptr napi_value): NapiStatus {.header: "".} 25 | 26 | proc napi_create_int32*(env: napi_env, value: int32, result: ptr napi_value): NapiStatus {.header: "".} 27 | 28 | proc napi_create_uint32*(env: napi_env, value: uint32, result: ptr napi_value): NapiStatus {.header: "".} 29 | 30 | proc napi_create_int64*(env: napi_env, value: int64, result: ptr napi_value): NapiStatus {.header: "".} 31 | 32 | # Not part of original api 33 | proc napi_create_uint64*(env: napi_env, value: uint64, result: ptr napi_value): NapiStatus {.header: "".} 34 | 35 | proc napi_create_string_latin1*(env: napi_env, str: cstring, length: csize_t, result: ptr napi_value): NapiStatus {.header: "".} 36 | 37 | proc napi_create_string_utf8*(env: napi_env, str: cstring, length: csize_t, result: ptr napi_value): NapiStatus {.header: "".} 38 | 39 | # TODO napi_create_string_utf16 I'm not sure what the equivalent would be in nim 40 | proc napi_create_symbol*(env: napi_env, description: napi_value, result: ptr napi_value): NapiStatus {.header: "".} 41 | 42 | proc napi_create_function*(env: napi_env, utf8name: cstring, length: csize_t, cb: napi_callback, data: pointer, result: ptr napi_value): NapiStatus {.header:"".} 43 | 44 | proc napi_create_error*(env: napi_env, code: napi_value, msg: napi_value, result: ptr napi_value): NapiStatus {.header: "".} 45 | 46 | proc napi_create_type_error*(env: napi_env, code: napi_value, msg: napi_value, result: ptr napi_value): NapiStatus {.header: "".} 47 | 48 | proc napi_create_range_error*(env: napi_env, code: napi_value, msg: napi_value, result: ptr napi_value): NapiStatus {.header: "".} 49 | 50 | 51 | 52 | # Methods to get the native napi_value from Primitive type 53 | proc napi_typeof*(env: napi_env, value: napi_value, result: ptr NapiValueType): NapiStatus {.header: "".} 54 | 55 | proc napi_get_value_double*(env: napi_env, value: napi_value, result: ptr float64): NapiStatus {.header: "".} 56 | 57 | proc napi_get_value_int32*(env: napi_env, value: napi_value, result: ptr int32): NapiStatus {.header: "".} 58 | 59 | proc napi_get_value_uint32*(env: napi_env, value: napi_value, result: ptr uint32): NapiStatus {.header: "".} 60 | 61 | proc napi_get_value_int64*(env: napi_env, value: napi_value, result: ptr int64): NapiStatus {.header: "".} 62 | 63 | proc napi_get_value_bool*(env: napi_env, value: napi_value, result: ptr bool): NapiStatus {.header: "".} 64 | 65 | # Copies LATIN-1 encoded bytes from a string into a buffer. 66 | proc napi_get_value_string_latin1*(env: napi_env, value: napi_value, buf: cstring, bufsize: csize_t, result: ptr csize_t): NapiStatus {.header: "".} 67 | 68 | #Copies UTF-8 encoded bytes from a string into a buffer. 69 | proc napi_get_value_string_utf8*(env: napi_env, value: napi_value, buf: cstring, bufsize: csize_t, result: ptr csize_t): NapiStatus {.header: "".} 70 | # TODO napi_get_value_string_utf16 71 | 72 | 73 | 74 | # Methods to coerce values 75 | # These APIs may execute user scripts 76 | proc napi_coerce_to_bool*(env: napi_env, value: napi_value, result: ptr napi_value): NapiStatus {.header: "".} 77 | 78 | proc napi_coerce_to_number*(env: napi_env, value: napi_value, result: ptr napi_value): NapiStatus {.header: "".} 79 | 80 | proc napi_coerce_to_object*(env: napi_env, value: napi_value, result: ptr napi_value): NapiStatus {.header: "".} 81 | 82 | proc napi_coerce_to_string*(env: napi_env, value: napi_value, result: ptr napi_value): NapiStatus {.header: "".} 83 | 84 | 85 | 86 | 87 | # Methods to work with Objects 88 | proc napi_get_prototype*(env: napi_env, obj: napi_value, result: ptr napi_value): NapiStatus {.header: "".} 89 | 90 | proc napi_get_property_names*(env: napi_env, obj: napi_value, result: ptr napi_value): NapiStatus {.header: "".} 91 | 92 | proc napi_set_property*(env: napi_env, obj: napi_value, key: napi_value, value: napi_value): NapiStatus {.header: "".} 93 | 94 | proc napi_has_property*(env: napi_env, obj: napi_value, key: napi_value, result: ptr bool): NapiStatus {.header: "".} 95 | 96 | proc napi_get_property*(env: napi_env, obj: napi_value, key: napi_value, result: ptr napi_value): NapiStatus {.header: "".} 97 | 98 | proc napi_delete_property*(env: napi_env, obj: napi_value, key: napi_value, result: ptr bool): NapiStatus {.header: "".} 99 | 100 | proc napi_has_own_property*(env: napi_env, obj: napi_value, key: napi_value, result: ptr bool): NapiStatus {.header: "".} 101 | 102 | proc napi_set_named_property*(env: napi_env, obj: napi_value, utf8name: cstring, value: napi_value): NapiStatus {.header: "".} 103 | 104 | proc napi_has_named_property*(env: napi_env, obj: napi_value, utf8name: cstring, result: ptr bool): NapiStatus {.header: "".} 105 | 106 | proc napi_get_named_property*(env: napi_env, obj: napi_value, utf8name: cstring, result: ptr napi_value): NapiStatus {.header: "".} 107 | 108 | proc napi_set_element*(env: napi_env, obj: napi_value, index: uint32, value: napi_value): NapiStatus {.header: "".} 109 | 110 | proc napi_has_element*(env: napi_env, obj: napi_value, index: uint32, result: ptr bool): NapiStatus {.header: "".} 111 | 112 | proc napi_get_element*(env: napi_env, obj: napi_value, index: uint32, result: ptr napi_value): NapiStatus {.header: "".} 113 | 114 | proc napi_delete_element*(env: napi_env, obj: napi_value, index: uint32, result: ptr bool): NapiStatus {.header: "".} 115 | 116 | proc napi_define_properties*(env: napi_env, obj: napi_value, property_count: csize_t, properties: ptr NapiPropertyDescriptor): NapiStatus {.header: "".} 117 | 118 | 119 | 120 | # Methods to work with Arrays 121 | proc napi_is_array*(env: napi_env, value: napi_value, result: ptr bool): NapiStatus {.header: "".} 122 | 123 | proc napi_get_array_length*(env: napi_env, value: napi_value, result: ptr uint32): NapiStatus {.header: "".} 124 | 125 | 126 | 127 | # Methods to compare values 128 | proc napi_strict_equals*(env: napi_env, lhs: napi_value, rhs: napi_value, result: ptr bool): NapiStatus {.header: "".} 129 | 130 | 131 | 132 | # Methods to work with Functions 133 | proc napi_call_function*(env: napi_env, recv: napi_value, fn: napi_value, argc: csize_t, argv: ptr napi_value, result: ptr napi_value): NapiStatus {.header: "".} 134 | 135 | proc napi_new_instance*(env: napi_env, constructor: napi_value, argc: csize_t, argv: ptr napi_value, result: ptr napi_value): NapiStatus {.header: "".} 136 | 137 | proc napi_instanceof*(env: napi_env, obj: napi_value, constructor: napi_value, result: ptr bool): NapiStatus {.header: "".} 138 | 139 | 140 | 141 | # Methods to work with napi_callbacks 142 | 143 | # Gets all callback info in a single call. (Ugly, but faster.) 144 | proc napi_get_cb_info*( 145 | env: napi_env, 146 | cbinfo: napi_callback_info, 147 | argc: ptr csize_t, 148 | argv: ptr UncheckedArray[napi_value], 149 | this_arg: napi_value, 150 | data: ptr pointer): NapiStatus {.header:"".} 151 | 152 | proc napi_get_new_target*(env: napi_env, cbinfo: napi_callback_info, result: ptr napi_value): NapiStatus {.header:"".} 153 | 154 | proc napi_define_class*( 155 | env: napi_env, 156 | utf8name: cstring, 157 | length: csize_t, 158 | constructor: napi_callback, 159 | data: pointer, 160 | property_count: csize_t, 161 | properties: ptr NapiPropertyDescriptor, 162 | result: ptr napi_value): NapiStatus {.header:"".} 163 | 164 | 165 | 166 | # Methods to work with external data objects 167 | proc napi_wrap*( 168 | env: napi_env, 169 | js_object: napi_value, 170 | native_object: pointer, 171 | finalize_cb: napi_finalize, 172 | finalize_hint: pointer, 173 | result: ptr napi_ref): NapiStatus {.header:"".} 174 | 175 | proc napi_unrwap*(env: napi_env, js_object: napi_value, result: ptr pointer): NapiStatus {.header:"".} 176 | 177 | proc napi_remove_wrap*(env: napi_env, js_object: napi_value, result: ptr pointer): NapiStatus {.header:"".} 178 | 179 | proc napi_create_external*( 180 | env: napi_env, 181 | data: pointer, 182 | finalize_cb: napi_finalize, 183 | finalize_hint: pointer, 184 | result: ptr napi_value): NapiStatus {.header:"".} 185 | 186 | proc napi_get_value_external*(env: napi_env, value: napi_value, result: ptr pointer): NapiStatus {.header:"".} 187 | 188 | # TODO: Add "Methods to control object lifespan" 189 | 190 | 191 | # Methods to support error handling 192 | proc napi_throw*(env: napi_env, error: napi_value): NapiStatus {.header: "".} 193 | 194 | proc napi_throw_error*(env: napi_env, code: cstring, msg: cstring): NapiStatus {.header: "".} 195 | 196 | proc napi_throw_type_error*(env: napi_env, code: cstring, msg: cstring): NapiStatus {.header: "".} 197 | 198 | proc napi_throw_range_error*(env: napi_env, code: cstring, msg: cstring): NapiStatus {.header: "".} 199 | 200 | proc napi_is_error*(env: napi_env, value: napi_value, result: ptr bool): NapiStatus {.header: "".} 201 | 202 | 203 | 204 | # Methods to support catching exceptions 205 | proc napi_is_exception_pending*(env: napi_env, result: ptr bool): NapiStatus {.header: "".} 206 | 207 | proc napi_get_and_clear_last_exception*(env: napi_env, result: ptr napi_value): NapiStatus {.header: "".} 208 | 209 | 210 | 211 | # Methods to work with array buffers and typed arrays 212 | proc napi_is_arraybuffer*(env: napi_env, value: napi_value, result: ptr bool): NapiStatus {.header: "".} 213 | 214 | proc napi_create_arraybuffer*( 215 | env: napi_env, 216 | byte_length: csize_t, 217 | data: ptr pointer, 218 | result: ptr napi_value): NapiStatus {.header: "".} 219 | 220 | proc napi_create_external_arraybuffer*( 221 | env: napi_env, 222 | external_data: pointer, 223 | byte_length: csize_t, 224 | finalize_cb: napi_finalize, 225 | finalize_hint: pointer, 226 | result: ptr napi_value): NapiStatus {.header: "".} 227 | 228 | proc napi_get_arraybuffer_info*( 229 | env: napi_env, 230 | arraybuffer: napi_value, 231 | data: ptr pointer, 232 | byte_length: ptr csize_t): NapiStatus {.header: "".} 233 | 234 | proc napi_is_typedarray*(env: napi_env, value: napi_value, result: ptr bool): NapiStatus {.header: "".} 235 | 236 | proc napi_create_typedarray*( 237 | env: napi_env, 238 | array_type: NApiTypedArrayType, 239 | length: csize_t, 240 | arraybuffer: napi_value, 241 | byte_offset: csize_t, 242 | result: ptr napi_value): NapiStatus {.header: "".} 243 | 244 | proc napi_get_typedarray_info*( 245 | env: napi_env, 246 | typedarray: napi_value, 247 | array_type: ptr NApiTypedArrayType, 248 | length: ptr csize_t, 249 | data: ptr pointer, 250 | arraybuffer: ptr napi_value, 251 | byte_offset: csize_t): NapiStatus {.header: "".} 252 | 253 | proc napi_create_dataview*( 254 | env: napi_env, 255 | length: csize_t, 256 | arraybuffer: napi_value, 257 | byte_offset: csize_t, 258 | result: ptr napi_value): NapiStatus {.header: "".} 259 | 260 | proc napi_is_dataview*(env: napi_env, value: napi_value, result: ptr bool): NapiStatus {.header: "".} 261 | 262 | proc napi_get_dataview_info*( 263 | env: napi_env, 264 | dataview: napi_value, 265 | byte_length: ptr csize_t, 266 | data: ptr pointer, 267 | arraybuffer: ptr napi_value, 268 | byte_offset: ptr csize_t): NapiStatus {.header: "".} 269 | 270 | 271 | 272 | # Version management 273 | proc napi_get_version*(env: napi_env, result: ptr uint32): NapiStatus {.header: "".} 274 | 275 | 276 | 277 | # Promises 278 | proc napi_create_promise*(env: napi_env, deffered: ptr napi_deferred, promise: ptr napi_value): NapiStatus {.header: "".} 279 | 280 | proc napi_resolve_deferred*(env: napi_env, deffered: napi_deferred, resolution: napi_value): NapiStatus {.header: "".} 281 | 282 | proc napi_reject_deferred*(env: napi_env, deffered: napi_deferred, rejection: napi_value): NapiStatus {.header: "".} 283 | 284 | proc napi_is_promise*(env: napi_env, promise: napi_value, is_promise: ptr bool): NapiStatus {.header: "".} 285 | 286 | 287 | 288 | # Running a script 289 | proc napi_run_script*(env: napi_env, script: napi_value, result: ptr napi_value): NapiStatus {.header: "".} 290 | 291 | 292 | 293 | # Memory management 294 | proc napi_adjust_external_memory*(env: napi_env, change_in_bytes: int64, adjusted_value: ptr int64): NapiStatus {.header: "".} 295 | 296 | 297 | 298 | # Dates 299 | proc napi_create_date*(env: napi_env, time: float64, result: ptr napi_value): NapiStatus {.header: "".} 300 | 301 | proc napi_is_date*(env: napi_env, value: napi_value, is_date: ptr bool): NapiStatus {.header: "".} 302 | 303 | proc napi_get_date_value*(env: napi_env, value: napi_value, result: ptr float64): NapiStatus {.header: "".} 304 | 305 | proc napi_add_finalizer*( 306 | env: napi_env, 307 | js_object: napi_value, 308 | native_object: pointer, 309 | finalize_cb: napi_finalize, 310 | finalize_hint: pointer, 311 | result: ptr napi_ref): NapiStatus {.header: "".} 312 | 313 | 314 | 315 | # TODO Add experimental features? -------------------------------------------------------------------------------- /examples/napi/jsNativeApi.nim: -------------------------------------------------------------------------------- 1 | import jsNativeApiTypes 2 | 3 | proc napi_get_last_error_info*(env: napi_env, result: UncheckedArray[NapiExtendedErrorInfo]): NapiStatus {.header: "".} 4 | 5 | 6 | # Getters for defined singletons 7 | proc napi_get_undefined*(env: napi_env, result: ptr napi_value): NapiStatus {.header: "".} 8 | 9 | proc napi_get_null*(env: napi_env, result: ptr napi_value): NapiStatus {.header: "".} 10 | 11 | proc napi_get_global*(env: napi_env, result: ptr napi_value): NapiStatus {.header: "".} 12 | 13 | proc napi_get_boolean*(env: napi_env, value: bool, result: ptr napi_value): NapiStatus {.header: "".} 14 | 15 | 16 | 17 | # Methods to create Primitive types/Objects 18 | proc napi_create_object*(env: napi_env, result: ptr napi_value): NapiStatus {.header: "".} 19 | 20 | proc napi_create_array*(env: napi_env, result: ptr napi_value): NapiStatus {.header: "".} 21 | 22 | proc napi_create_array_with_length*(env: napi_env, length: csize_t, result: ptr napi_value): NapiStatus {.header: "".} 23 | 24 | proc napi_create_double*(env: napi_env, value: float64, result: ptr napi_value): NapiStatus {.header: "".} 25 | 26 | proc napi_create_int32*(env: napi_env, value: int32, result: ptr napi_value): NapiStatus {.header: "".} 27 | 28 | proc napi_create_uint32*(env: napi_env, value: uint32, result: ptr napi_value): NapiStatus {.header: "".} 29 | 30 | proc napi_create_int64*(env: napi_env, value: int64, result: ptr napi_value): NapiStatus {.header: "".} 31 | 32 | # Not part of original api 33 | proc napi_create_uint64*(env: napi_env, value: uint64, result: ptr napi_value): NapiStatus {.header: "".} 34 | 35 | proc napi_create_string_latin1*(env: napi_env, str: cstring, length: csize_t, result: ptr napi_value): NapiStatus {.header: "".} 36 | 37 | proc napi_create_string_utf8*(env: napi_env, str: cstring, length: csize_t, result: ptr napi_value): NapiStatus {.header: "".} 38 | 39 | # TODO napi_create_string_utf16 I'm not sure what the equivalent would be in nim 40 | proc napi_create_symbol*(env: napi_env, description: napi_value, result: ptr napi_value): NapiStatus {.header: "".} 41 | 42 | proc napi_create_function*(env: napi_env, utf8name: cstring, length: csize_t, cb: napi_callback, data: pointer, result: ptr napi_value): NapiStatus {.header:"".} 43 | 44 | proc napi_create_error*(env: napi_env, code: napi_value, msg: napi_value, result: ptr napi_value): NapiStatus {.header: "".} 45 | 46 | proc napi_create_type_error*(env: napi_env, code: napi_value, msg: napi_value, result: ptr napi_value): NapiStatus {.header: "".} 47 | 48 | proc napi_create_range_error*(env: napi_env, code: napi_value, msg: napi_value, result: ptr napi_value): NapiStatus {.header: "".} 49 | 50 | 51 | 52 | # Methods to get the native napi_value from Primitive type 53 | proc napi_typeof*(env: napi_env, value: napi_value, result: ptr NapiValueType): NapiStatus {.header: "".} 54 | 55 | proc napi_get_value_double*(env: napi_env, value: napi_value, result: ptr float64): NapiStatus {.header: "".} 56 | 57 | proc napi_get_value_int32*(env: napi_env, value: napi_value, result: ptr int32): NapiStatus {.header: "".} 58 | 59 | proc napi_get_value_uint32*(env: napi_env, value: napi_value, result: ptr uint32): NapiStatus {.header: "".} 60 | 61 | proc napi_get_value_int64*(env: napi_env, value: napi_value, result: ptr int64): NapiStatus {.header: "".} 62 | 63 | proc napi_get_value_bool*(env: napi_env, value: napi_value, result: ptr bool): NapiStatus {.header: "".} 64 | 65 | # Copies LATIN-1 encoded bytes from a string into a buffer. 66 | proc napi_get_value_string_latin1*(env: napi_env, value: napi_value, buf: cstring, bufsize: csize_t, result: ptr csize_t): NapiStatus {.header: "".} 67 | 68 | #Copies UTF-8 encoded bytes from a string into a buffer. 69 | proc napi_get_value_string_utf8*(env: napi_env, value: napi_value, buf: cstring, bufsize: csize_t, result: ptr csize_t): NapiStatus {.header: "".} 70 | # TODO napi_get_value_string_utf16 71 | 72 | 73 | 74 | # Methods to coerce values 75 | # These APIs may execute user scripts 76 | proc napi_coerce_to_bool*(env: napi_env, value: napi_value, result: ptr napi_value): NapiStatus {.header: "".} 77 | 78 | proc napi_coerce_to_number*(env: napi_env, value: napi_value, result: ptr napi_value): NapiStatus {.header: "".} 79 | 80 | proc napi_coerce_to_object*(env: napi_env, value: napi_value, result: ptr napi_value): NapiStatus {.header: "".} 81 | 82 | proc napi_coerce_to_string*(env: napi_env, value: napi_value, result: ptr napi_value): NapiStatus {.header: "".} 83 | 84 | 85 | 86 | 87 | # Methods to work with Objects 88 | proc napi_get_prototype*(env: napi_env, obj: napi_value, result: ptr napi_value): NapiStatus {.header: "".} 89 | 90 | proc napi_get_property_names*(env: napi_env, obj: napi_value, result: ptr napi_value): NapiStatus {.header: "".} 91 | 92 | proc napi_set_property*(env: napi_env, obj: napi_value, key: napi_value, value: napi_value): NapiStatus {.header: "".} 93 | 94 | proc napi_has_property*(env: napi_env, obj: napi_value, key: napi_value, result: ptr bool): NapiStatus {.header: "".} 95 | 96 | proc napi_get_property*(env: napi_env, obj: napi_value, key: napi_value, result: ptr napi_value): NapiStatus {.header: "".} 97 | 98 | proc napi_delete_property*(env: napi_env, obj: napi_value, key: napi_value, result: ptr bool): NapiStatus {.header: "".} 99 | 100 | proc napi_has_own_property*(env: napi_env, obj: napi_value, key: napi_value, result: ptr bool): NapiStatus {.header: "".} 101 | 102 | proc napi_set_named_property*(env: napi_env, obj: napi_value, utf8name: cstring, value: napi_value): NapiStatus {.header: "".} 103 | 104 | proc napi_has_named_property*(env: napi_env, obj: napi_value, utf8name: cstring, result: ptr bool): NapiStatus {.header: "".} 105 | 106 | proc napi_get_named_property*(env: napi_env, obj: napi_value, utf8name: cstring, result: ptr napi_value): NapiStatus {.header: "".} 107 | 108 | proc napi_set_element*(env: napi_env, obj: napi_value, index: uint32, value: napi_value): NapiStatus {.header: "".} 109 | 110 | proc napi_has_element*(env: napi_env, obj: napi_value, index: uint32, result: ptr bool): NapiStatus {.header: "".} 111 | 112 | proc napi_get_element*(env: napi_env, obj: napi_value, index: uint32, result: ptr napi_value): NapiStatus {.header: "".} 113 | 114 | proc napi_delete_element*(env: napi_env, obj: napi_value, index: uint32, result: ptr bool): NapiStatus {.header: "".} 115 | 116 | proc napi_define_properties*(env: napi_env, obj: napi_value, property_count: csize_t, properties: ptr NapiPropertyDescriptor): NapiStatus {.header: "".} 117 | 118 | 119 | 120 | # Methods to work with Arrays 121 | proc napi_is_array*(env: napi_env, value: napi_value, result: ptr bool): NapiStatus {.header: "".} 122 | 123 | proc napi_get_array_length*(env: napi_env, value: napi_value, result: ptr uint32): NapiStatus {.header: "".} 124 | 125 | 126 | 127 | # Methods to compare values 128 | proc napi_strict_equals*(env: napi_env, lhs: napi_value, rhs: napi_value, result: ptr bool): NapiStatus {.header: "".} 129 | 130 | 131 | 132 | # Methods to work with Functions 133 | proc napi_call_function*(env: napi_env, recv: napi_value, fn: napi_value, argc: csize_t, argv: ptr napi_value, result: ptr napi_value): NapiStatus {.header: "".} 134 | 135 | proc napi_new_instance*(env: napi_env, constructor: napi_value, argc: csize_t, argv: ptr napi_value, result: ptr napi_value): NapiStatus {.header: "".} 136 | 137 | proc napi_instanceof*(env: napi_env, obj: napi_value, constructor: napi_value, result: ptr bool): NapiStatus {.header: "".} 138 | 139 | 140 | 141 | # Methods to work with napi_callbacks 142 | 143 | # Gets all callback info in a single call. (Ugly, but faster.) 144 | proc napi_get_cb_info*( 145 | env: napi_env, 146 | cbinfo: napi_callback_info, 147 | argc: ptr csize_t, 148 | argv: ptr UncheckedArray[napi_value], 149 | this_arg: napi_value, 150 | data: ptr pointer): NapiStatus {.header:"".} 151 | 152 | proc napi_get_new_target*(env: napi_env, cbinfo: napi_callback_info, result: ptr napi_value): NapiStatus {.header:"".} 153 | 154 | proc napi_define_class*( 155 | env: napi_env, 156 | utf8name: cstring, 157 | length: csize_t, 158 | constructor: napi_callback, 159 | data: pointer, 160 | property_count: csize_t, 161 | properties: ptr NapiPropertyDescriptor, 162 | result: ptr napi_value): NapiStatus {.header:"".} 163 | 164 | 165 | 166 | # Methods to work with external data objects 167 | proc napi_wrap*( 168 | env: napi_env, 169 | js_object: napi_value, 170 | native_object: pointer, 171 | finalize_cb: napi_finalize, 172 | finalize_hint: pointer, 173 | result: ptr napi_ref): NapiStatus {.header:"".} 174 | 175 | proc napi_unrwap*(env: napi_env, js_object: napi_value, result: ptr pointer): NapiStatus {.header:"".} 176 | 177 | proc napi_remove_wrap*(env: napi_env, js_object: napi_value, result: ptr pointer): NapiStatus {.header:"".} 178 | 179 | proc napi_create_external*( 180 | env: napi_env, 181 | data: pointer, 182 | finalize_cb: napi_finalize, 183 | finalize_hint: pointer, 184 | result: ptr napi_value): NapiStatus {.header:"".} 185 | 186 | proc napi_get_value_external*(env: napi_env, value: napi_value, result: ptr pointer): NapiStatus {.header:"".} 187 | 188 | # TODO: Add "Methods to control object lifespan" 189 | 190 | 191 | # Methods to support error handling 192 | proc napi_throw*(env: napi_env, error: napi_value): NapiStatus {.header: "".} 193 | 194 | proc napi_throw_error*(env: napi_env, code: cstring, msg: cstring): NapiStatus {.header: "".} 195 | 196 | proc napi_throw_type_error*(env: napi_env, code: cstring, msg: cstring): NapiStatus {.header: "".} 197 | 198 | proc napi_throw_range_error*(env: napi_env, code: cstring, msg: cstring): NapiStatus {.header: "".} 199 | 200 | proc napi_is_error*(env: napi_env, value: napi_value, result: ptr bool): NapiStatus {.header: "".} 201 | 202 | 203 | 204 | # Methods to support catching exceptions 205 | proc napi_is_exception_pending*(env: napi_env, result: ptr bool): NapiStatus {.header: "".} 206 | 207 | proc napi_get_and_clear_last_exception*(env: napi_env, result: ptr napi_value): NapiStatus {.header: "".} 208 | 209 | 210 | 211 | # Methods to work with array buffers and typed arrays 212 | proc napi_is_arraybuffer*(env: napi_env, value: napi_value, result: ptr bool): NapiStatus {.header: "".} 213 | 214 | proc napi_create_arraybuffer*( 215 | env: napi_env, 216 | byte_length: csize_t, 217 | data: ptr pointer, 218 | result: ptr napi_value): NapiStatus {.header: "".} 219 | 220 | proc napi_create_external_arraybuffer*( 221 | env: napi_env, 222 | external_data: pointer, 223 | byte_length: csize_t, 224 | finalize_cb: napi_finalize, 225 | finalize_hint: pointer, 226 | result: ptr napi_value): NapiStatus {.header: "".} 227 | 228 | proc napi_get_arraybuffer_info*( 229 | env: napi_env, 230 | arraybuffer: napi_value, 231 | data: ptr pointer, 232 | byte_length: ptr csize_t): NapiStatus {.header: "".} 233 | 234 | proc napi_is_typedarray*(env: napi_env, value: napi_value, result: ptr bool): NapiStatus {.header: "".} 235 | 236 | proc napi_create_typedarray*( 237 | env: napi_env, 238 | array_type: NApiTypedArrayType, 239 | length: csize_t, 240 | arraybuffer: napi_value, 241 | byte_offset: csize_t, 242 | result: ptr napi_value): NapiStatus {.header: "".} 243 | 244 | proc napi_get_typedarray_info*( 245 | env: napi_env, 246 | typedarray: napi_value, 247 | array_type: ptr NApiTypedArrayType, 248 | length: ptr csize_t, 249 | data: ptr pointer, 250 | arraybuffer: ptr napi_value, 251 | byte_offset: csize_t): NapiStatus {.header: "".} 252 | 253 | proc napi_create_dataview*( 254 | env: napi_env, 255 | length: csize_t, 256 | arraybuffer: napi_value, 257 | byte_offset: csize_t, 258 | result: ptr napi_value): NapiStatus {.header: "".} 259 | 260 | proc napi_is_dataview*(env: napi_env, value: napi_value, result: ptr bool): NapiStatus {.header: "".} 261 | 262 | proc napi_get_dataview_info*( 263 | env: napi_env, 264 | dataview: napi_value, 265 | byte_length: ptr csize_t, 266 | data: ptr pointer, 267 | arraybuffer: ptr napi_value, 268 | byte_offset: ptr csize_t): NapiStatus {.header: "".} 269 | 270 | 271 | 272 | # Version management 273 | proc napi_get_version*(env: napi_env, result: ptr uint32): NapiStatus {.header: "".} 274 | 275 | 276 | 277 | # Promises 278 | proc napi_create_promise*(env: napi_env, deffered: ptr napi_deferred, promise: ptr napi_value): NapiStatus {.header: "".} 279 | 280 | proc napi_resolve_deferred*(env: napi_env, deffered: napi_deferred, resolution: napi_value): NapiStatus {.header: "".} 281 | 282 | proc napi_reject_deferred*(env: napi_env, deffered: napi_deferred, rejection: napi_value): NapiStatus {.header: "".} 283 | 284 | proc napi_is_promise*(env: napi_env, promise: napi_value, is_promise: ptr bool): NapiStatus {.header: "".} 285 | 286 | 287 | 288 | # Running a script 289 | proc napi_run_script*(env: napi_env, script: napi_value, result: ptr napi_value): NapiStatus {.header: "".} 290 | 291 | 292 | 293 | # Memory management 294 | proc napi_adjust_external_memory*(env: napi_env, change_in_bytes: int64, adjusted_value: ptr int64): NapiStatus {.header: "".} 295 | 296 | 297 | 298 | # Dates 299 | proc napi_create_date*(env: napi_env, time: float64, result: ptr napi_value): NapiStatus {.header: "".} 300 | 301 | proc napi_is_date*(env: napi_env, value: napi_value, is_date: ptr bool): NapiStatus {.header: "".} 302 | 303 | proc napi_get_date_value*(env: napi_env, value: napi_value, result: ptr float64): NapiStatus {.header: "".} 304 | 305 | proc napi_add_finalizer*( 306 | env: napi_env, 307 | js_object: napi_value, 308 | native_object: pointer, 309 | finalize_cb: napi_finalize, 310 | finalize_hint: pointer, 311 | result: ptr napi_ref): NapiStatus {.header: "".} 312 | 313 | 314 | 315 | # TODO Add experimental features? -------------------------------------------------------------------------------- /examples/napi/napibindings.nim: -------------------------------------------------------------------------------- 1 | import macros 2 | 3 | import 4 | jsNativeApiTypes, 5 | jsNativeApi, 6 | nodeApi, 7 | utils 8 | 9 | export jsNativeApiTypes, jsNativeApi, nodeApi 10 | 11 | type NapiStatusError = object of CatchableError 12 | 13 | var `env$`*: napi_env = nil 14 | ##Global environment variable; state maintained by various hooks; used internally 15 | 16 | 17 | proc assessStatus*(status: NapiStatus) {.raises: [NapiStatusError].} = 18 | ##Asserts that a call returns correctly; 19 | if status != napi_ok: 20 | raise newException(NapiStatusError, "NAPI call returned non-zero status (" & $status & ": " & $NapiStatus(status) & ")") 21 | 22 | 23 | 24 | type Module* = ref object 25 | val*: napi_value 26 | env*: napi_env 27 | descriptors: seq[NapiPropertyDescriptor] 28 | 29 | 30 | proc newNodeValue*(val: napi_value, env: napi_env): Module = 31 | ##Used internally, disregard 32 | Module(val: val, env: env, descriptors: @[]) 33 | 34 | proc kind(env: napi_env, val: napi_value): NapiValueType = 35 | assessStatus ( napi_typeof(env, val, addr result) ) 36 | 37 | 38 | 39 | proc create(env: napi_env, n: bool): napi_value = 40 | assessStatus (napi_get_boolean(env, n, addr result)) 41 | 42 | proc create(env: napi_env, n: int32): napi_value = 43 | assessStatus ( napi_create_int32(env, n, addr result) ) 44 | 45 | proc create(env: napi_env, n: int64): napi_value = 46 | assessStatus ( napi_create_int64(env, n, addr result) ) 47 | 48 | proc create(env: napi_env, n: uint32): napi_value = 49 | assessStatus ( napi_create_uint32(env, n, addr result) ) 50 | 51 | proc create(env: napi_env, n: uint64): napi_value = 52 | assessStatus ( napi_create_uint64(env, n, addr result) ) 53 | 54 | proc create(env: napi_env, n: float64): napi_value = 55 | assessStatus ( napi_create_double(env, n, addr result) ) 56 | 57 | proc create(env: napi_env, s: string): napi_value = 58 | assessStatus ( napi_create_string_utf8(env, s, s.len.csize_t, addr result) ) 59 | 60 | proc create(env: napi_env, p: openarray[(string, napi_value)]): napi_value = 61 | assessStatus napi_create_object(env, addr result) 62 | for name, val in items(p): 63 | assessStatus napi_set_named_property(env, result, name, val) 64 | 65 | 66 | proc create(env: napi_env, a: openarray[napi_value]): napi_value = 67 | assessStatus( napi_create_array_with_length(env, a.len.csize_t, addr result) ) 68 | for i, elem in a.enumerate: assessStatus napi_set_element(env, result, i.uint32, a[i]) 69 | 70 | proc create[T: int | uint | string](env: napi_env, a: openarray[T]): napi_value = 71 | var elements = newSeq[napi_value]() 72 | for elem in a: elements.add(env.create(elem)) 73 | env.create(elements) 74 | 75 | 76 | proc create[T: int | uint | string](env: napi_env, a: openarray[(string, T)]): napi_value = 77 | var properties = newSeq[(string, napi_value)]() 78 | for prop in a: properties.add((prop[0], create(prop[1]))) 79 | env.create(a) 80 | 81 | proc createFn*(env: napi_env, fname: string, cb: napi_callback): napi_value = 82 | assessStatus ( napi_create_function(env, fname, fname.len.csize_t, cb, nil, addr result) ) 83 | 84 | proc create(env: napi_env, v: napi_value): napi_value = v 85 | 86 | 87 | proc create*[T](n: Module, t: T): napi_value = 88 | n.env.create(t) 89 | 90 | proc kind*(val: napi_value): NapiValueType = 91 | kind(`env$`, val) 92 | 93 | proc getInt64*(n: napi_value): int64 = 94 | ##Retrieves value from node; raises exception on failure 95 | assessStatus napi_get_value_int64(`env$`, n, addr result) 96 | 97 | proc getInt64*(n: napi_value, default: int64): int64 = 98 | ##Retrieves value from node; returns default on failure 99 | try: assessStatus napi_get_value_int64(`env$`, n, addr result) 100 | except: result = default 101 | 102 | 103 | proc getInt32*(n: napi_value): int32 = 104 | ##Retrieves value from node; raises exception on failure 105 | assessStatus napi_get_value_int32(`env$`, n, addr result) 106 | 107 | proc getInt32*(n: napi_value, default: int32): int32 = 108 | ##Retrieves value from node; returns default on failure 109 | try: assessStatus napi_get_value_int32(`env$`, n, addr result) 110 | except: result = default 111 | 112 | 113 | template getInt*(n: napi_value): int = 114 | ##Retrieves value from node based on bitness of architecture; raises exception on failure 115 | when sizeof(int) == 8: 116 | int(n.getInt64()) 117 | else: 118 | int(n.getInt32()) 119 | 120 | template getInt*(n: napi_value, default: int): int = 121 | ##Retrieves value from node based on bitness of architecture; returns default on failure 122 | when sizeof(int) == 8: 123 | int(n.getInt64(default)) 124 | else: 125 | int(n.getInt32(default)) 126 | 127 | 128 | proc getBool*(n: napi_value): bool = 129 | ##Retrieves value from node; raises exception on failure 130 | assessStatus napi_get_value_bool(`env$`, n, addr result) 131 | 132 | proc getBool*(n: napi_value, default: bool): bool = 133 | ##Retrieves value from node; returns default on failure 134 | try: assessStatus napi_get_value_bool(`env$`, n, addr result) 135 | except: result = default 136 | 137 | 138 | proc getStr*(n: napi_value): string = 139 | var bufSize: csize_t 140 | assessStatus napi_get_value_string_utf8(`env$`, n, cast[cstring](nil), cast[csize_t](nil), addr bufSize) 141 | 142 | var buf = cast[cstring](alloc(bufSize + 1)) 143 | defer: dealloc(buf) 144 | assessStatus napi_get_value_string_utf8(`env$`, n, buf, bufSize + 1, addr bufSize) 145 | return $buf 146 | 147 | proc getStr*(n: napi_value, default: string, bufsize: int = 40): string = 148 | ##Retrieves utf8 encoded value from node; returns default on failure 149 | ##Maximum return string length is equal to ``bufsize`` 150 | var 151 | buf = cast[cstring](alloc(bufsize)) 152 | res: csize_t 153 | defer: dealloc(buf) 154 | try: 155 | assessStatus napi_get_value_string_utf8(`env$`, n, buf, bufsize.csize_t, addr res) 156 | result = ($buf)[0..res-1] 157 | except: result = default 158 | 159 | proc hasProperty*(obj: napi_value, key: string): bool {.raises: [ValueError, NapiStatusError].} = 160 | ##Checks whether or not ``obj`` has a property ``key``; Panics if ``obj`` is not an object 161 | if kind(obj) != napi_object: raise newException(ValueError, "value is not an object") 162 | 163 | assessStatus napi_has_named_property(`env$`, obj, (key), addr result) 164 | 165 | 166 | proc getProperty*(obj: napi_value, key: string): napi_value {.raises: [KeyError, ValueError, NapiStatusError].}= 167 | ##Retrieves property ``key`` from ``obj``; Panics if ``obj`` is not an object 168 | if not hasProperty(obj, key): raise newException(KeyError, "property not contained for key " & key) 169 | assessStatus napi_get_named_property(`env$`, obj, (key), addr result) 170 | 171 | proc getProperty*(obj: napi_value, key: string, default: napi_value): napi_value = 172 | ##Retrieves property ``key`` from ``obj``; returns default if ``obj`` is not an object or does not contain ``key`` 173 | try: obj.getProperty(key) 174 | except: default 175 | 176 | proc setProperty*(obj: napi_value, key: string, value: napi_value) {.raises: [ValueError, NapiStatusError].}= 177 | ##Sets property ``key`` in ``obj`` to ``value``; raises exception if ``obj`` is not an object 178 | if kind(obj) != napi_object: raise newException(ValueError, "value is not an object") 179 | assessStatus napi_set_named_property(`env$`, obj, key, value) 180 | 181 | proc `[]`*(obj: napi_value, key: string): napi_value = 182 | ##Alias for ``getProperty``, raises exception 183 | obj.getProperty(key) 184 | proc `[]=`*(obj: napi_value, key: string, value: napi_value) = 185 | ##Alias for ``setProperty``, raises exception 186 | obj.setProperty(key, value) 187 | 188 | 189 | 190 | proc isArray*(obj: napi_value): bool = 191 | assessStatus napi_is_array(`env$`, obj, addr result) 192 | 193 | proc hasElement*(obj: napi_value, index: int): bool = 194 | ##Checks whether element is contained in ``obj``; raises exception if ``obj`` is not an array 195 | if not isArray(obj): raise newException(ValueError, "value is not an array") 196 | assessStatus napi_has_element(`env$`, obj, uint32 index, addr result) 197 | 198 | proc getElement*(obj: napi_value, index: int): napi_value = 199 | ##Retrieves value from ``index`` in ``obj``; raises exception if ``obj`` is not an array or ``index`` is out of bounds 200 | if not hasElement(obj, index): raise newException(IndexDefect, "index out of bounds") 201 | assessStatus napi_get_element(`env$`, obj, uint32 index, addr result) 202 | 203 | proc getElement*(obj: napi_value, index: int, default: napi_value): napi_value = 204 | try: obj.getElement(index) 205 | except: default 206 | 207 | proc setElement*(obj: napi_value, index: int, value: napi_value) = 208 | ##Sets value at ``index``; raises exception if ``obj`` is not an array 209 | if not isArray(obj): raise newException(ValueError, "value is not an array") 210 | assessStatus napi_set_element(`env$`, obj, uint32 index, value) 211 | 212 | proc len*(arr: napi_value): int = 213 | if not isArray(arr): raise newException(ValueError, "value is not an array") 214 | arr.getProperty("length").getInt 215 | 216 | proc `[]`*(obj: napi_value, index: int): napi_value = 217 | ##Alias for ``getElement``; raises exception 218 | obj.getElement(index) 219 | proc `[]=`*(obj: napi_value, index: int, value: napi_value) = 220 | ##Alias for ``setElement``; raises exception 221 | obj.setElement(index, value) 222 | 223 | proc getNull*: napi_value = 224 | ##Returns JavaScript ``null`` value 225 | assessStatus napi_get_null(`env$`, addr result) 226 | 227 | proc getUndefined*: napi_value = 228 | ##Returns JavaScript ``undefined`` value 229 | assessStatus napi_get_undefined(`env$`, addr result) 230 | 231 | proc getGlobal*: napi_value = 232 | ##Returns NodeJS global variable 233 | assessStatus napi_get_global(`env$`, addr result) 234 | 235 | proc registerBase(obj: Module, name: string, value: napi_value, attr: int) = 236 | obj.descriptors.add( 237 | NapiPropertyDescriptor( 238 | utf8name: name, 239 | value: value, 240 | attributes: napi_default 241 | ) 242 | ) 243 | 244 | proc register*[T: int | uint | string | napi_value](obj: Module, name: string, value: T, attr: int = 0) = 245 | ##Adds field to exports object ``obj`` 246 | obj.registerBase(name, create(obj.env, value), attr) 247 | 248 | proc register*[T: int | uint | string | napi_value](obj: Module, name: string, values: openarray[T], attr: int = 0) = 249 | ##Adds field to exports object ``obj`` 250 | var elements = newSeq[napi_value]() 251 | for v in values: elements.add(obj.create(v)) 252 | 253 | obj.registerBase(name, create(obj.env, elements), attr) 254 | 255 | proc register*[T: int | uint | string | napi_value](obj: Module, name: string, values: openarray[(string, T)], attr: int = 0) = 256 | ##Adds field to exports object ``obj`` 257 | var properties = newSeq[(string, napi_value)]() 258 | for v in values: properties.add((v[0], obj.create(v[1]))) 259 | 260 | obj.registerBase(name, create(obj.env, properties), attr) 261 | 262 | proc register*(obj: Module, name: string, cb: napi_callback) = 263 | obj.registerBase(name, createFn(obj.env, name, cb), 0) 264 | 265 | 266 | proc `%`*[T](t: T): napi_value = 267 | `env$`.create(t) 268 | 269 | const emptyArr: array[0, (string, napi_value)] = [] 270 | 271 | proc callFunction*(fn: napi_value, args: openarray[napi_value] = [], this = %emptyArr): napi_value = 272 | assessStatus napi_call_function(`env$`, this, fn, args.len.csize_t, cast[ptr napi_value](args.toUnchecked()), addr result) 273 | 274 | proc callMethod*(instance: napi_value, methd: string, args: openarray[napi_value] = []): napi_value = 275 | assessStatus napi_call_function(`env$`, instance, instance.getProperty(methd), args.len.csize_t, cast[ptr napi_value](args.toUnchecked()), addr result) 276 | 277 | template getIdentStr*(n: untyped): string = $n 278 | 279 | 280 | template fn*(paramCt: int, name, cushy: untyped): untyped {.dirty.} = 281 | var name {.inject.}: napi_value 282 | block: 283 | proc `wrapper$`(environment: napi_env, info: napi_callback_info): napi_value {.cdecl.} = 284 | var 285 | `argv$` = cast[ptr UncheckedArray[napi_value]](alloc(paramCt * sizeof(napi_value))) 286 | argc: csize_t = paramCt 287 | this: napi_value 288 | args = newSeq[napi_value]() 289 | `env$` = environment 290 | assessStatus napi_get_cb_info(environment, info, addr argc, `argv$`, addr this, nil) 291 | for i in 0..