Skip to content

Instantly share code, notes, and snippets.

@gabrielschulhof
Last active February 22, 2019 21:36
Show Gist options
  • Save gabrielschulhof/df079c9a89fe05dfa740b7171e0aa760 to your computer and use it in GitHub Desktop.
Save gabrielschulhof/df079c9a89fe05dfa740b7171e0aa760 to your computer and use it in GitHub Desktop.
Example of an addon conf file stored in the addon's build directory
#include <napi.h>
#include <stdio.h>
namespace {
// Per-instance addon data. An instance of this class is passed to every binding
// and makes it possible to avoid setting global static variables.
class AddonData {
public:
AddonData(std::string config_file): config_file(config_file) {}
std::string config_file;
static void Delete(napi_env env, void* data, void* hint) {
delete reinterpret_cast<AddonData*>(data);
}
};
// The actual binding.
Napi::Value DoSomethingUseful(const Napi::CallbackInfo& info) {
// Retrieve the addon data.
AddonData* addon_data = reinterpret_cast<AddonData*>(info.Data());
char* data = nullptr;
size_t length = 0;
// Open the configuration file.
FILE* file = fopen(addon_data->config_file.c_str(), "r");
if (file == nullptr) {
Napi::Error::New(info.Env(), "Failed to open config file")
.ThrowAsJavaScriptException();
return info.Env().Undefined();
}
// Seek to the end so we can gauge its size.
if (fseek(file, 0L, SEEK_END) != 0) {
Napi::Error::New(info.Env(), "Failed to seek to the end of the config file")
.ThrowAsJavaScriptException();
fclose(file);
return info.Env().Undefined();
}
// Retrieve the size of the file.
length = ftell(file);
// Seek back to the beginning.
if (fseek(file, 0L, SEEK_SET) != 0) {
Napi::Error::New(info.Env(),
"Failed to seek to the beginning of the config file")
.ThrowAsJavaScriptException();
fclose(file);
return info.Env().Undefined();
}
// Allocate a buffer to receive the contents of the file.
data = new char[length + 1];
// Read the file into the buffer.
if (fread(data, length, 1, file) != 1) {
Napi::Error::New(info.Env(), "Failed to read the config file")
.ThrowAsJavaScriptException();
fclose(file);
return info.Env().Undefined();
}
// Close the file.
fclose(file);
// Make sure the buffer is null-terminated, and construct a JavaScript string
// wherein we return the contents of the file to JavaScript.
data[length] = 0;
Napi::String result = Napi::String::New(info.Env(),
std::string("---8<------\n") + data + "---8<------\n");
// Delete the buffer that held the contents of the file.
delete[] data;
// Return the string we constructed to JavaScript.
return result;
}
// The real addon initialization function. It receives the addon's exports nad
// the path to the configuration file.
Napi::Value Initialize(const Napi::CallbackInfo& info) {
// Make sure the first parameter is an object, so we can attach the bindings
// as its properties.
if (!info[0].IsObject()) {
Napi::Error::New(info.Env(), "First parameter must be an object")
.ThrowAsJavaScriptException();
return info.Env().Undefined();
}
// Make sure the second parameter is a string, so we can use it as the config
// file path.
if (!info[1].IsString()) {
Napi::Error::New(info.Env(), "Second parameter must be a string")
.ThrowAsJavaScriptException();
return info.Env().Undefined();
}
// Allocate and initialize a new instance of the per-addon data.
AddonData* addon_data = new AddonData(info[1].As<Napi::String>());
// Cast the second parameter as a `Napi::Object`.
Napi::Object exports = info[0].As<Napi::Object>();
// Weakly associate the per-addon data with the exports object. This will
// ensure that the per-addon data will be deleted when the addon itself is
// deleted, at environment cleanup time.
if (napi_wrap(info.Env(),
exports,
addon_data,
AddonData::Delete,
nullptr,
nullptr) != napi_ok) {
Napi::Error::New(info.Env(), "Failed to initialize addon data")
.ThrowAsJavaScriptException();
delete addon_data;
return info.Env().Undefined();
}
// Save the config file path in the addon data.
addon_data->config_file = info[1].As<Napi::String>();
// Decorate the exports object with the actual bindings.
exports["doSomethingUseful"] =
Napi::Function::New(info.Env(), DoSomethingUseful, nullptr, addon_data);
// Return the exports object. This enables chaining on the JavaScript side.
return exports;
}
// The addon initialization function.
Napi::Object Init(Napi::Env env, Napi::Object exports) {
// Expose the actual initialization function to JavaScript.
exports["initialize"] = Napi::Function::New(env, Initialize);
return exports;
}
} // end of anonymous namespace
NODE_API_MODULE(NODE_GYP_MODULE_NAME, Init)
[config]
some_key = some_value
{
'targets': [
{
'target_name': 'addon',
'sources': [ 'addon.cc' ],
'include_dirs': ["<!@(node -p \"require('node-addon-api').include\")"],
'dependencies': ["<!(node -p \"require('node-addon-api').gyp\")"],
'defines': [ 'NAPI_DISABLE_CPP_EXCEPTIONS' ],
'cflags': [ '-fno-exceptions' ],
'cflags_cc': [ '-fno-exceptions' ],
'msvs_settings': {
'VCCLCompilerTool': {
'ExceptionHandling': 0,
'EnablePREfast': 'true',
},
},
'xcode_settings': {
'CLANG_CXX_LIBRARY': 'libc++',
'MACOSX_DEPLOYMENT_TARGET': '10.7',
'GCC_ENABLE_CPP_EXCEPTIONS': 'NO',
},
},
{
'target_name': 'config_file',
'type': 'none',
'copies': [
{
'destination': '<(PRODUCT_DIR)',
'files': [ 'addon.conf' ]
}
]
}
]
}
const path = require('path');
// Load the addon in a two-step process. First, use bindings to locate it, and
// then call its `initialize()` method, passing in the path to its configuration
// file. The addon returns itself from `initialize()`, decorated with its actual
// bindings. The result becomes the exports of this package.
module.exports = ((addon) => addon.initialize(addon,
path.join(path.dirname(addon.path), 'addon.conf')))
(require('bindings')('addon'));
{
"name": "module-with-config-file",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"install": "node-gyp rebuild"
},
"author": "",
"license": "Apache-2.0",
"gypfile": true,
"dependencies": {
"bindings": "^1.4.0",
"node-addon-api": "^1.6.2"
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment