Node.js package using node-addon-api

Ref: https://github.com/nodejs/node-addon-api#api

Installation

curl -sL https://deb.nodesource.com/setup_12.x | sudo -E bash - \
&& sudo apt-get install -y nodejs
mkdir test \
&& cd test \
&& npm init
sudo npm install -g node-gyp
npm install node-addon-api
<package>
โ”œโ”€โ”€ binding.gyp
โ”œโ”€โ”€ index.js
โ”œโ”€โ”€ node_modules
โ”‚ โ””โ”€โ”€ node-addon-api
โ”‚ โ””โ”€โ”€ ...
โ”œโ”€โ”€ package.json
โ”œโ”€โ”€ package-lock.json
โ”œโ”€โ”€ test
โ”‚ โ””โ”€โ”€ ...
โ”œโ”€โ”€ .clang-format
โ””โ”€โ”€ c_src
โ”œโ”€โ”€ funcs.cpp
โ”œโ”€โ”€ funcs.h
โ”œโ”€โ”€ funcs_wrapper.cpp
โ”œโ”€โ”€ funcs_wrapper.h
โ””โ”€โ”€ <target>.cpp

package.json

Ref: https://docs.npmjs.com/files/package.json

{
"name": "<package>",
...
"gypfile": true,
...
"scripts": {
"install": "node-gyp rebuild",
"build": "node-gyp rebuild --verbose",
"clean": "node-gyp clean",
"cf": "clang-format -style=file -i -verbose c_src/*"
},
...
"dependencies": {
"node-addon-api": "*",
},
...
}

binding.gyp

Ref: https://github.com/nodejs/node-addon-api/blob/master/doc/setup.md

{
"targets": [
{
"target_name": "<target>",
"include_dirs": ["<!@(node -p \"require('node-addon-api').include\")"],
"dependencies": ["<!(node -p \"require('node-addon-api').gyp\")"],
"cflags!": ["-fno-exceptions"],
"cflags_cc!": ["-fno-exceptions"],
"cflags": [],
"cflags_cc": [],
"defines": [],
"sources": [
"c_src/funcs.cpp",
"c_src/funcs.h",
"c_src/funcs_wrapper.cpp",
"c_src/funcs_wrapper.h",
"c_src/<target>.cpp",
],
"libraries": [
# "/usr/lib/libxxx.so"
]
}
]
}
caution

target_name์— '-' ๋“ฑ์€ ๋“ค์–ด๊ฐˆ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.

index.js

module.exports = require("./build/Release/<target>");

Wrapper

Ref: https://medium.com/@atulanand94/beginners-guide-to-writing-nodejs-addons-using-c-and-n-api-node-addon-api-9b3b718a9a7f

c_src/funcs.h

#pragma once
#include <string>
std::string hello(void);
int add(int a, int b);

c_src/funcs.cpp

#include "funcs.h"
std::string hello(void) { return "Hello World!"; }
int add(int a, int b) { return a + b; }

c_src/funcs_wrapper.h

#pragma once
#include <napi.h>
Napi::Object init_funcs(Napi::Env env, Napi::Object exports);
Napi::String hello_wrapper(const Napi::CallbackInfo &info);
Napi::Number add_wrapper(const Napi::CallbackInfo &info);

c_src/funcs_wrapper.cpp

#include "funcs.h"
#include "funcs_wrapper.h"
Napi::Object init_funcs(Napi::Env env, Napi::Object exports) {
exports.Set("hello", Napi::Function::New(env, hello_wrapper));
exports.Set("add", Napi::Function::New(env, add_wrapper));
return exports;
}
Napi::String hello_wrapper(const Napi::CallbackInfo &info) {
Napi::Env env = info.Env();
return Napi::String::New(env, hello());
}
Napi::Number add_wrapper(const Napi::CallbackInfo &info) {
Napi::Env env = info.Env();
if(info.Length() < 2 || ! info[0].IsNumber() || ! info[1].IsNumber()) {
Napi::TypeError::New(env,
"Arguments must be (int a, int b) such as (1, 2).")
.ThrowAsJavaScriptException();
}
// Implicit type conversion
int a = info[0].As<Napi::Number>();
int b = info[1].As<Napi::Number>();
return Napi::Number::New(env, add(a, b));
}

c_src/<target>.cpp

#include "funcs_wrapper.h"
#include <napi.h>
Napi::Object init_all(Napi::Env env, Napi::Object exports) {
return init_funcs(env, exports);
}
NODE_API_MODULE(NODE_GYP_MODULE_NAME, init_all)

Build

npm run build

test/test.js

const addon = require("../build/Release/<target>");
console.log("wrapper info", addon);
console.log(addon.hello());
console.log(addon.add(1, 2));
node test/test.js
wrapper info { hello: [Function], add: [Function] }
Hello World!
3
Last updated on