# Addon Tools This is a part of [Node3D](https://github.com/node-3d) project. [![NPM](https://nodei.co/npm/addon-tools-raub.png?compact=true)](https://www.npmjs.com/package/addon-tools-raub) [![Build Status](https://api.travis-ci.com/node-3d/addon-tools-raub.svg?branch=master)](https://travis-ci.com/node-3d/addon-tools-raub) [![CodeFactor](https://www.codefactor.io/repository/github/node-3d/addon-tools-raub/badge)](https://www.codefactor.io/repository/github/node-3d/addon-tools-raub) > npm i addon-tools-raub ## Synopsis Helpers for Node.js **NAPI** addons and dependency packages: * Supported platforms (x64): Windows, Linux, OSX. * C++ helpers: * Macro shortcuts for NAPI. * `eventEmit()` function. * `getData()` function. * **Es5** class wrapper (like `Napi::ObjectWrap`, but Es5 functions). * Module helpers: * Crossplatform commands for GYP: `cp`, `rm`, `mkdir`. * Deps unzip installer. * Url-to-buffer downloader. * Binary copy helper. Useful links: [N-API Docs](https://nodejs.org/api/n-api.html), [Napi Docs](https://github.com/nodejs/node-addon-api/blob/master/doc/setup.md), [GYP Docs](https://gyp.gsrc.io/docs/UserDocumentation.md). **Jump to**: [Snippets](#snippets) [include/addon-tools.hpp](#includeaddon-toolshpp) [index.js](#indexjs) [cpbin.js](#cpbinjs) [download.js](#downloadjs) [install.js](#installjs) [writable-buffer.js](#writablebufferjs) [Crossplatform commands](#crossplatform-commands) [Function eventEmit](#function-eventEmit) [Es5 class wrapper](#es5-class-wrapper) --- ## Snippets ### Crossplatform commands ``` 'variables': { 'rm' : ' See a snipped for binding.gyp here * Assume `EXT_LIB` is the name of a binary dependency. * Assume `deps-EXT_LIB` is the name of an Addon Tools compliant dependency module. * Assume `MY_ADDON` is the name of this addon's resulting binary. * Assume C++ code goes to `cpp` subdirectory. ``` { 'variables': { 'rm' : ' --- ## include/addon-tools.hpp There is a C++ header file, `addon-tools.hpp`, shipped with this package. It introduces several useful macros and utilities. Also it includes Napi automatically, so that you can replace: ``` #include ``` with ``` #include ``` In gyp, the include directory should be set for your addon to know where to get it. An actual path to the directory is exported from the module and is accessible like this: ``` require('addon-tools-raub').include // a string ``` ### Helpers in **addon-tools.hpp**: Usually all the helpers work within the context of JS call. In this case we have `CallbackInfo info` passed as an argument. ``` #define NAPI_ENV Napi::Env env = info.Env(); #define NAPI_HS Napi::HandleScope scope(env); ```
Return value * `RET_VALUE(VAL)`- return a given Napi::Value. * `RET_UNDEFINED`- return `undefined`. * `RET_NULL` - return `null`. * `RET_STR(VAL)` - return `Napi::String`, expected `VAL` is `const char *`. * `RET_NUM(VAL)` - return `Napi::Number`, expected `VAL` is `double`. * `RET_EXT(VAL)` - return `Napi::External`, expected `VAL` is `void *`. * `RET_BOOL(VAL)` - return `Napi::Boolean`, expected `VAL` is `bool`. * `RET_FUN(VAL)` - return `Napi::Function`, expected `VAL` is a `napi_value`. * `RET_OBJ(VAL)` - return `Napi::Object`, expected `VAL` is a `napi_value`.
New JS value * `JS_STR(VAL)` - create a `Napi::String` value. * `JS_NUM(VAL)` - create a `Napi::Number` value. * `JS_EXT(VAL)` - create a `Napi::External` (from pointer) value. * `JS_BOOL(VAL)` - create a `Napi::Boolean` value. * `JS_FUN(VAL)` - create a `Napi::Function` value. * `JS_OBJ(VAL)` - create a `Napi::Object` value.
Method check These checks throw JS TypeError if not passed. Here `T` is always used as a typename in error messages. `C` is a [Napi::Value](https://github.com/nodejs/node-addon-api/blob/master/doc/value.md#isboolean) check method, like `IsObject()`. `I` is the index of argument as in `info[I]`, starting from `0`. * `REQ_ARGS(N)` - check if at least `N` arguments passed * `IS_ARG_EMPTY(I)` - check if argument `I` is `undefined` or `null` * `CHECK_REQ_ARG(I, C, T)` - check if argument `I` is approved by `C` check. * `CHECK_LET_ARG(I, C, T)` - check if argument `I` is approved by `C` check or empty. * `CTOR_CHECK(T)` - check if method is called as a constructor * `SETTER_CHECK(C, T)` - check if setter `value` is approved by `C` check. * `DES_CHECK` - within dynamic method check if the instance wasn't destroyed by `destroy()`.
Method arguments The idea is to ease the transition from what inside the `CallbackInfo` to what you work with in C++. Three types of argument retrieval are supported: `REQ_`, `USE_` and `LET_`. The difference: * `REQ_` - 2 params, requires an argument to have a value * `USE_` - 3 params, allows the argument to be empty and have a default * `LET_` - 2 params, is `USE_` with a preset zero-default. What it does, basically: ``` // REQ_DOUBLE_ARG(0, x) double x = info[0].ToNumber().DoubleValue(); // USE_DOUBLE_ARG(0, x, 5.7) double x = IS_ARG_EMPTY(0) ? 5.7 : info[0].ToNumber().DoubleValue(); // LET_DOUBLE_ARG(0, x) double x = IS_ARG_EMPTY(0) ? 0.0 : info[0].ToNumber().DoubleValue(); ``` That extrapolates well to all the helpers below: * `REQ_STR_ARG` - JS `string` => C++ `std::string`. * `USE_STR_ARG` * `LET_STR_ARG` - default: `""`. * `REQ_INT32_ARG` - JS `number` => C++ `int32_t`. * `USE_INT32_ARG` * `LET_INT32_ARG` - default: `0`. * `REQ_INT_ARG` - JS `number` => C++ `int32_t`. * `USE_INT_ARG` * `LET_INT_ARG` - default: `0`. * `REQ_UINT32_ARG` - JS `number` => C++ `uint32_t`. * `USE_UINT32_ARG` * `LET_UINT32_ARG` - default: `0`. * `REQ_UINT_ARG` - JS `number` => C++ `uint32_t`. * `USE_UINT_ARG` * `LET_UINT_ARG` - default: `0`. * `REQ_BOOL_ARG` - JS `Boolean` => C++ `bool`. * `USE_BOOL_ARG` * `LET_BOOL_ARG` - default: `false`. * `REQ_OFFS_ARG` - JS `number` => C++ `size_t`. * `USE_OFFS_ARG` * `LET_OFFS_ARG` - default: `0`. * `REQ_DOUBLE_ARG` - JS `number` => C++ `double`. * `USE_DOUBLE_ARG` * `LET_DOUBLE_ARG` - default: `0.0`. * `REQ_FLOAT_ARG` - JS `number` => C++ `float`. * `USE_FLOAT_ARG` * `LET_FLOAT_ARG` - default: `0.f`. * `REQ_EXT_ARG` - JS `native` => C++ `void*`. * `USE_EXT_ARG` * `LET_EXT_ARG` - default: `nullptr`. * `REQ_FUN_ARG` - JS `function` => C++ `Napi::Function`. * `REQ_OBJ_ARG` - JS `object` => C++ `Napi::Object`. * `USE_OBJ_ARG` * `LET_OBJ_ARG` - default: `{}`. * `REQ_ARRV_ARG` - JS `ArrayBuffer` => C++ `Napi::ArrayBuffer`. * `REQ_BUF_ARG` - JS `Buffer` => C++ `Napi::Buffer`. ``` NAN_METHOD(test) { REQ_UINT32_ARG(0, width); REQ_UINT32_ARG(1, height); LET_FLOAT_ARG(2, z); // Variables created: unsigned int width, height; float z; ... ```
Set object accessors Simplified accessor assignment, adds accessors of NAME for OBJ. Read accessor is assumed to have the name `NAME+'Getter'` and write accessor is `NAME+'Setter'`. * `ACCESSOR_RW(CLASS, NAME)` - add read and write accessors NAME of CLASS. * `ACCESSOR_R(CLASS, NAME)` - add read accessor NAME of CLASS. * `ACCESSOR_M(CLASS, NAME)` - add method NAME of CLASS. ``` void MyClass::init(Napi::Env env, Napi::Object exports) { ... Napi::Function ctor = DefineClass(env, "MyClass", { ACCESSOR_R(MyClass, isDestroyed), ACCESSOR_RW(MyClass, x), ACCESSOR_M(MyClass, reset), }); ... } JS_GETTER(MyClass::isDestroyedGetter) { ... JS_GETTER(MyClass::xGetter) { ... JS_SETTER(MyClass::xSetter) { ... JS_METHOD(MyClass::save) { ... ```
Setter argument Works similar to method arguments. But there is always `value` argument, from which a C++ value is extracted. * `SETTER_STR_ARG` * `SETTER_INT32_ARG` * `SETTER_INT_ARG` * `SETTER_BOOL_ARG` * `SETTER_UINT32_ARG` * `SETTER_UINT_ARG` * `SETTER_OFFS_ARG` * `SETTER_DOUBLE_ARG` * `SETTER_FLOAT_ARG` * `SETTER_EXT_ARG` * `SETTER_FUN_ARG` * `SETTER_OBJ_ARG` * `SETTER_ARRV_ARG` ``` JS_SETTER(MyClass::x) { SETTER_STR_ARG; // Variable created: std::string v; ... ```
Data retrieval * `T *getArrayData(value, num = NULL)` - extracts TypedArray data of any type from the given JS value. Does not accept `Array`. Checks with `IsArrayBuffer()`. Returns `nullptr` for empty JS values. For unacceptable values throws TypeError. * `void *getData(value)` - if value is a TypedArray, then the result of `getArrayData(value)` is returned. Otherwise if value has `'data'` property, it's content is then returned as `node::Buffer`. Returns `nullptr` in other cases.
--- ## index.js Exports: * `paths(dir)` - function. Returns a set of platform dependent paths depending on input `dir`. * `bin` - platform binary directory absolute path. * `include` - include directory for this `dir`. * `include` - both `'addon-tools-raub'` and `'node-addon-api'` include paths. Use with `node -p` through list context command expansion ` Note: only use this within JS function stack ## Function eventEmit In N-API there is no inheritance. And no `eval('require(...)')` possible at the time of module init. But `require('events')` is very tempting... The solution is: ``` // JS const EventEmitter = require('events'); const { bin } = require('addon-tools-raub'); const MyClass = require(`./${bin}/addon`); MyClass.prototype.__proto__ = EventEmitter.prototype; // Since now it is possible to call `emit` from instancess of MyClass ``` ``` // C++ void MyClass::emit(const Napi::CallbackInfo& info, const char* name) { NAPI_ENV; THIS_OBJ(that); eventEmit(env, that, name); } ``` Signature: ``` inline void eventEmit( Napi::Env env, Napi::Object that, const std::string &name, int argc = 0, Napi::Value *argv = nullptr ) ```