Fix readme
This commit is contained in:
parent
93121bc92a
commit
72073bfe79
638
README.md
638
README.md
|
@ -12,23 +12,24 @@ This is a part of [Node3D](https://github.com/node-3d) project.
|
||||||
|
|
||||||
## Synopsis
|
## Synopsis
|
||||||
|
|
||||||
Helpers for Node.js addons and dependency packages:
|
Helpers for Node.js **NAPI** addons and dependency packages:
|
||||||
|
|
||||||
* C++ macros and shortcuts.
|
|
||||||
* `consoleLog()` C++ helper.
|
|
||||||
* `eventEmit()` C++ helper.
|
|
||||||
* `getData()` C++ helper.
|
|
||||||
* Crossplatform commands for GYP: `cp`, `rm`, `mkdir`.
|
|
||||||
* Supported platforms (x64): Windows, Linux, OSX.
|
* Supported platforms (x64): Windows, Linux, OSX.
|
||||||
|
* C++ helpers:
|
||||||
|
* Macro shortcuts for NAPI.
|
||||||
|
* `consoleLog()` function.
|
||||||
|
* `eventEmit()` function.
|
||||||
|
* `getData()` function.
|
||||||
|
* Module helpers:
|
||||||
|
* Crossplatform commands for GYP: `cp`, `rm`, `mkdir`.
|
||||||
|
* Deps unzip installer.
|
||||||
|
* Url-to-buffer downloader.
|
||||||
|
|
||||||
Useful links: [N-API Docs](https://nodejs.org/api/n-api.html),
|
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),
|
[Napi Docs](https://github.com/nodejs/node-addon-api/blob/master/doc/setup.md),
|
||||||
[GYP Docs](https://gyp.gsrc.io/docs/UserDocumentation.md).
|
[GYP Docs](https://gyp.gsrc.io/docs/UserDocumentation.md).
|
||||||
|
|
||||||
---
|
**Jump to**:
|
||||||
|
|
||||||
|
|
||||||
## Contents
|
|
||||||
|
|
||||||
[Snippets](#snippets)
|
[Snippets](#snippets)
|
||||||
|
|
||||||
|
@ -107,16 +108,41 @@ On both Windows and Unix those commands now have the same result:
|
||||||
],
|
],
|
||||||
```
|
```
|
||||||
|
|
||||||
Those are the directory paths to C++ include files for Addon Tools and Napi
|
Those are the directory paths to C++ include files for **Addon Tools** and
|
||||||
(which is preinstalled with Addon Tools)
|
**Napi** (which is preinstalled with Addon Tools).
|
||||||
|
|
||||||
|
|
||||||
### Binary dependency package
|
### Binary dependency package
|
||||||
|
|
||||||
If you design a module with binary dependencies for several platforms, **Addon Tools**
|
If you design a module with binary dependencies for several platforms,
|
||||||
would encourage you to abide by the following rules:
|
**Addon Tools** may help within the following guidelines:
|
||||||
|
|
||||||
* Your binary directories are:
|
* In **package.json** use a `"postinstall"` script to download the libraries.
|
||||||
|
For example the following structure might work. Note that **Addon Tools** will
|
||||||
|
append any given URL with ``/${platform}.zip``
|
||||||
|
|
||||||
|
```
|
||||||
|
"config" : {
|
||||||
|
"install" : "v1.0.0"
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"postinstall": "install",
|
||||||
|
},
|
||||||
|
```
|
||||||
|
|
||||||
|
where `config.install` is **YOUR install.js** is:
|
||||||
|
|
||||||
|
```
|
||||||
|
const install = require('addon-tools-raub/install');
|
||||||
|
const prefix = 'https://github.com/user/addon/releases/download';
|
||||||
|
const tag = process.env.npm_package_config_install;
|
||||||
|
install(`${prefix}/${tag}`);
|
||||||
|
```
|
||||||
|
|
||||||
|
**Addon Tools** will unzip the downloaded file into the platform binary
|
||||||
|
directory. E.g. on Windows it will be **bin-windows**.
|
||||||
|
|
||||||
|
* Per platform binary directories:
|
||||||
|
|
||||||
* bin-windows
|
* bin-windows
|
||||||
* bin-linux
|
* bin-linux
|
||||||
|
@ -129,69 +155,73 @@ is described [here](#indexjs).
|
||||||
module.exports = require('addon-tools-raub').paths(__dirname);
|
module.exports = require('addon-tools-raub').paths(__dirname);
|
||||||
```
|
```
|
||||||
|
|
||||||
<details>
|
* Publishing is done by attaching a zipped platform folder to the Github
|
||||||
|
release. Zip file must NOT contain platform folder as a subfolder, but rather
|
||||||
|
contain the final binaries. The tag of the release should be the same as in
|
||||||
|
`npm_package_config_install` - that is the way installer will find it.
|
||||||
|
|
||||||
<summary>Show binding.gyp</summary>
|
* NOTE: You can publish your binaries to anywhere, not necessarily Github.
|
||||||
|
Just tweak the **install.js** script as appropriate. The only limitation
|
||||||
```
|
from **Addon Tools** is that it should be a zipped set of files/folders.
|
||||||
{
|
|
||||||
'variables': {
|
|
||||||
'rm' : '<!(node -e "require(\'addon-tools-raub\').rm()")',
|
|
||||||
'rem' : '<!(node -e "require(\'.\').rem()")',
|
|
||||||
'XALL%': 'false',
|
|
||||||
},
|
|
||||||
'targets': [
|
|
||||||
{
|
|
||||||
'target_name' : 'remove_extras',
|
|
||||||
'type' : 'none',
|
|
||||||
'conditions' : [['XALL=="false"', {'actions': [
|
|
||||||
{
|
|
||||||
'action_name' : 'Unnecessary binaries removed.',
|
|
||||||
'inputs' : [],
|
|
||||||
'outputs' : ['build'],
|
|
||||||
'action' : ['<(rm)', '-rf', '<@(rem)'],
|
|
||||||
}
|
|
||||||
]}]],
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Notice the `XALL` variable here. If the package is installed with `npm i`, then
|
|
||||||
quite expectedly all but the required arch directories are removed. But with
|
|
||||||
`npm i --XALL` you can keep all the binaries. It might be useful when debugging
|
|
||||||
multiple archs and switching Node.js versions with
|
|
||||||
[NVM](https://github.com/creationix/nvm).
|
|
||||||
|
|
||||||
</details>
|
|
||||||
|
|
||||||
|
|
||||||
### Compiled addon
|
### Compiled addon
|
||||||
|
|
||||||
It is easy to build a C++ addon with **Addon Tools**. To have a full picture, you
|
With the advent of N-API the focus of compiled addons shifted towards the
|
||||||
can view the
|
word "addons". Since ABI is now compatible across Node.js versions, now addons
|
||||||
[official example](https://github.com/node-3d/addon-tools-raub/tree/master/examples/addon).
|
are just plain DLLs. Therefore distribution of the binaries is covered in the
|
||||||
|
previous section. But for an addon you have to provide a GYP compilation step.
|
||||||
|
N-API changes it's role to out-of-install compilation, so we can't/shouldnt
|
||||||
|
put the file **binding.gyp** to the module root anymore.
|
||||||
|
|
||||||
The main file for an addon is **binding.gyp**. Here's a snippet with most of the features.
|
The workaround would be to have a separate directory within your project
|
||||||
|
(with simplified package.json) for the sake of addon compilation.
|
||||||
|
|
||||||
<details>
|
```
|
||||||
|
{
|
||||||
<summary>binding.gyp</summary>
|
"name": "build",
|
||||||
|
"version": "0.0.0",
|
||||||
|
"private": true,
|
||||||
|
"dependencies": {
|
||||||
|
"addon-tools-raub": "5.0.0",
|
||||||
|
"deps-EXT_LIB": "1.0.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
* Assume `EXT_LIB` is the name of a binary dependency.
|
* 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 `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 `MY_ADDON` is the name of this addon's resulting binary.
|
||||||
* Assume C++ code goes to `cpp` directory.
|
* Assume C++ code goes to `cpp` subdirectory.
|
||||||
|
|
||||||
|
That together with **binding.gyp**, this would be enough to get the addon compiled.
|
||||||
|
Then the binaries are published to the Github release. When the addon
|
||||||
|
is installed, its **index.js** is responsible for reexport of the binary.
|
||||||
|
Just require the built module like this:
|
||||||
|
|
||||||
|
```
|
||||||
|
const { bin } = require('addon-tools-raub');
|
||||||
|
const core = require(`./${bin}/MY_ADDON`);
|
||||||
|
```
|
||||||
|
|
||||||
|
<details>
|
||||||
|
|
||||||
|
<summary>See a snipped for binding.gyp here</summary>
|
||||||
|
|
||||||
|
* 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': {
|
'variables': {
|
||||||
'rm' : '<!(node -e "require(\'addon-tools-raub\').rm()")',
|
'rm' : '<!(node -e "require(\'addon-tools-raub\').rm")',
|
||||||
'cp' : '<!(node -e "require(\'addon-tools-raub\').cp()")',
|
'cp' : '<!(node -e "require(\'addon-tools-raub\').cp")',
|
||||||
'mkdir' : '<!(node -e "require(\'addon-tools-raub\').mkdir()")',
|
'mkdir' : '<!(node -e "require(\'addon-tools-raub\').mkdir")',
|
||||||
'binary' : '<!(node -e "require(\'addon-tools-raub\').bin()")',
|
'binary' : '<!(node -e "require(\'addon-tools-raub\').bin")',
|
||||||
'EXT_LIB_include' : '<!(node -e "require(\'deps-EXT_LIB\').include()")',
|
'EXT_LIB_include' : '<!(node -e "require(\'deps-EXT_LIB\').include")',
|
||||||
'EXT_LIB_bin' : '<!(node -e "require(\'deps-EXT_LIB\').bin()")',
|
'EXT_LIB_bin' : '<!(node -e "require(\'deps-EXT_LIB\').bin")',
|
||||||
},
|
},
|
||||||
'targets': [
|
'targets': [
|
||||||
{
|
{
|
||||||
|
@ -200,7 +230,7 @@ The main file for an addon is **binding.gyp**. Here's a snippet with most of the
|
||||||
'cpp/MY_ADDON.cpp',
|
'cpp/MY_ADDON.cpp',
|
||||||
],
|
],
|
||||||
'include_dirs': [
|
'include_dirs': [
|
||||||
'<!(node -e "require(\'addon-tools-raub\').include()")',
|
'<!(node -e "require(\'addon-tools-raub\').include")',
|
||||||
'<(EXT_LIB_include)',
|
'<(EXT_LIB_include)',
|
||||||
'<(module_root_dir)/include',
|
'<(module_root_dir)/include',
|
||||||
],
|
],
|
||||||
|
@ -222,6 +252,9 @@ The main file for an addon is **binding.gyp**. Here's a snippet with most of the
|
||||||
'-Wl,-rpath,<(EXT_LIB_bin)',
|
'-Wl,-rpath,<(EXT_LIB_bin)',
|
||||||
'<(EXT_LIB_bin)/EXT_LIB.dylib',
|
'<(EXT_LIB_bin)/EXT_LIB.dylib',
|
||||||
],
|
],
|
||||||
|
'xcode_settings': {
|
||||||
|
'DYLIB_INSTALL_NAME_BASE': '@rpath',
|
||||||
|
},
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
|
@ -270,35 +303,10 @@ The main file for an addon is **binding.gyp**. Here's a snippet with most of the
|
||||||
'action_name' : 'Module copied.',
|
'action_name' : 'Module copied.',
|
||||||
'inputs' : [],
|
'inputs' : [],
|
||||||
'outputs' : ['binary'],
|
'outputs' : ['binary'],
|
||||||
'action' : ['<(cp)', 'build/Release/MY_ADDON.node', '<(binary)/MY_ADDON.node'],
|
'action' : [
|
||||||
}],
|
'<(cp)',
|
||||||
},
|
'build/Release/MY_ADDON.node',
|
||||||
|
'<(binary)/MY_ADDON.node',
|
||||||
{
|
|
||||||
'target_name' : 'remove_extras',
|
|
||||||
'type' : 'none',
|
|
||||||
'dependencies' : ['copy_binary'],
|
|
||||||
'actions' : [{
|
|
||||||
'action_name' : 'Build intermediates removed.',
|
|
||||||
'inputs' : [],
|
|
||||||
'outputs' : ['cpp'],
|
|
||||||
'conditions' : [
|
|
||||||
[ 'OS=="linux"', { 'action' : [
|
|
||||||
'rm',
|
|
||||||
'<(module_root_dir)/build/Release/obj.target/MY_ADDON/cpp/MY_ADDON.o',
|
|
||||||
'<(module_root_dir)/build/Release/obj.target/MY_ADDON.node',
|
|
||||||
'<(module_root_dir)/build/Release/MY_ADDON.node'
|
|
||||||
] } ],
|
|
||||||
[ 'OS=="mac"', { 'action' : [
|
|
||||||
'rm',
|
|
||||||
'<(module_root_dir)/build/Release/obj.target/MY_ADDON/cpp/MY_ADDON.o',
|
|
||||||
'<(module_root_dir)/build/Release/MY_ADDON.node'
|
|
||||||
] } ],
|
|
||||||
[ 'OS=="win"', { 'action' : [
|
|
||||||
'<(_del)',
|
|
||||||
'<(module_root_dir)/build/Release/MY_ADDON.*',
|
|
||||||
'<(module_root_dir)/build/Release/obj/MY_ADDON/*.*'
|
|
||||||
] } ],
|
|
||||||
],
|
],
|
||||||
}],
|
}],
|
||||||
},
|
},
|
||||||
|
@ -307,13 +315,6 @@ The main file for an addon is **binding.gyp**. Here's a snippet with most of the
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Then require the built module like this:
|
|
||||||
|
|
||||||
```
|
|
||||||
const { binPath } = require('addon-tools-raub');
|
|
||||||
const core = require(`./${binPath}/MY_ADDON`);
|
|
||||||
```
|
|
||||||
|
|
||||||
</details>
|
</details>
|
||||||
|
|
||||||
---
|
---
|
||||||
|
@ -332,99 +333,55 @@ so that you can replace:
|
||||||
with
|
with
|
||||||
|
|
||||||
```
|
```
|
||||||
#include <addon-tools.hpp> // or event-emitter.hpp
|
#include <addon-tools.hpp>
|
||||||
```
|
```
|
||||||
|
|
||||||
In gyp, the include directory should be set for your addon to know where to get it.
|
In gyp, the include directory should be set for your addon to know where
|
||||||
As it was mentioned above, this can be done automatically. Also an actual path to the
|
to get it. An actual path to the directory is exported from the module
|
||||||
directory is exported from the module and is accessible like this:
|
and is accessible like this:
|
||||||
|
|
||||||
```
|
```
|
||||||
require('addon-tools-raub').include // a string
|
require('addon-tools-raub').include // a string
|
||||||
```
|
```
|
||||||
|
|
||||||
Currently, there are following helpers in **addon-tools.hpp**:
|
### Helpers in **addon-tools.hpp**:
|
||||||
|
|
||||||
|
Usually all the helpers work within the context of JS call. In this case we
|
||||||
<details>
|
have `CallbackInfo info` passed as an argument.
|
||||||
|
|
||||||
<summary>Handle scope</summary>
|
|
||||||
|
|
||||||
* `NAN_HS` - creates a HandleScope. Also, you do not need them within `NAN_METHOD`,
|
|
||||||
`NAN_SETTER`, and `NAN_GETTER`, as it is stated in
|
|
||||||
[Nan doc](https://github.com/nodejs/nan/blob/master/doc/methods.md#api_nan_method).
|
|
||||||
So it is most likely to be used in parts of code called from C++ land.
|
|
||||||
|
|
||||||
```
|
```
|
||||||
void windowFocusCB(GLFWwindow *window, int focused) { NAN_HS;
|
#define NAPI_ENV Napi::Env env = info.Env();
|
||||||
...
|
#define NAPI_HS Napi::HandleScope scope(env);
|
||||||
}
|
|
||||||
...
|
|
||||||
glfwSetWindowFocusCallback(window, windowFocusCB);
|
|
||||||
```
|
```
|
||||||
|
|
||||||
</details>
|
|
||||||
|
|
||||||
|
|
||||||
<details>
|
<details>
|
||||||
|
|
||||||
<summary>Method return</summary>
|
<summary>Return value</summary>
|
||||||
|
|
||||||
* `RET_VALUE(VAL)` - set method return value, where `VAL` is `v8::Local<v8::Value>`.
|
* `RET_VALUE(VAL)`- return a given Napi::Value.
|
||||||
* `RET_UNDEFINED` - set method return value as `undefined`.
|
* `RET_UNDEFINED`- return `undefined`.
|
||||||
* `RET_STR(VAL)` - set method return value, where `VAL` is `const char *`.
|
* `RET_NULL` - return `null`.
|
||||||
* `RET_UTF8(VAL)` - set method return value, where `VAL` is `const char *`.
|
* `RET_STR(VAL)` - return `Napi::String`, expected `VAL` is `const char *`.
|
||||||
* `RET_INT(VAL)` - set method return value, where `VAL` is `int32`.
|
* `RET_NUM(VAL)` - return `Napi::Number`, expected `VAL` is `double`.
|
||||||
* `RET_INT32(VAL)` - set method return value, where `VAL` is `int32`.
|
* `RET_EXT(VAL)` - return `Napi::External`, expected `VAL` is `void *`.
|
||||||
* `RET_UINT32(VAL)` - set method return value, where `VAL` is `uint32`.
|
* `RET_BOOL(VAL)` - return `Napi::Boolean`, expected `VAL` is `bool`.
|
||||||
* `RET_NUM(VAL)` - set method return value, where `VAL` is `double`.
|
* `RET_FUN(VAL)` - return `Napi::Function`, expected `VAL` is a `napi_value`.
|
||||||
* `RET_OFFS(VAL)` - set method return value, where `VAL` is `size_t`.
|
* `RET_OBJ(VAL)` - return `Napi::Object`, expected `VAL` is a `napi_value`.
|
||||||
* `RET_FLOAT(VAL)` - set method return value, where `VAL` is `float`.
|
|
||||||
* `RET_DOUBLE(VAL)` - set method return value, where `VAL` is `double`.
|
|
||||||
* `RET_EXT(VAL)` - set method return value, where `VAL` is `void *`.
|
|
||||||
* `RET_BOOL(VAL)` - set method return value, where `VAL` is `bool`.
|
|
||||||
* `RET_FUN(VAL)` - set method return value, where `VAL` is `Nan::Persistent<v8::Function>`.
|
|
||||||
* `RET_OBJ(VAL)` - set method return value, where `VAL` is `Nan::Persistent<v8::Object>`.
|
|
||||||
|
|
||||||
</details>
|
</details>
|
||||||
|
|
||||||
|
|
||||||
<details>
|
|
||||||
|
|
||||||
<summary>Shortcut types</summary>
|
|
||||||
|
|
||||||
* `V8_VAR_VAL` = `v8::Local<v8::Value>`
|
|
||||||
* `V8_VAR_OBJ` = `v8::Local<v8::Object>`
|
|
||||||
* `V8_VAR_ARR` = `v8::Local<v8::Array>`
|
|
||||||
* `V8_VAR_STR` = `v8::Local<v8::String>`
|
|
||||||
* `V8_VAR_FUNC` = `v8::Local<v8::Function>`
|
|
||||||
* `V8_VAR_FT` = `v8::Local<v8::FunctionTemplate>`
|
|
||||||
* `V8_VAR_OT` = `v8::Local<v8::ObjectTemplate>`
|
|
||||||
* `V8_STORE_FT` = `Nan::Persistent<v8::FunctionTemplate>`
|
|
||||||
* `V8_STORE_FUNC` = `Nan::Persistent<v8::Function>`
|
|
||||||
* `V8_STORE_OBJ` = `Nan::Persistent<v8::Object>`
|
|
||||||
* `V8_STORE_VAL` = `Nan::Persistent<v8::Value>`
|
|
||||||
|
|
||||||
</details>
|
|
||||||
|
|
||||||
|
|
||||||
<details>
|
<details>
|
||||||
|
|
||||||
<summary>New JS value</summary>
|
<summary>New JS value</summary>
|
||||||
|
|
||||||
* `JS_STR(...)` - create a string value
|
* `JS_STR(VAL)` - create a `Napi::String` value.
|
||||||
* `JS_UTF8(...)` - same as JS_STR
|
* `JS_NUM(VAL)` - create a `Napi::Number` value.
|
||||||
* `JS_INT(val)` - create an integer value
|
* `JS_EXT(VAL)` - create a `Napi::External` (from pointer) value.
|
||||||
* `JS_INT32(val)` - same as `JS_INT`
|
* `JS_BOOL(VAL)` - create a `Napi::Boolean` value.
|
||||||
* `JS_UINT32(val)` - same as `JS_INT`
|
* `JS_FUN(VAL)` - create a `Napi::Function` value.
|
||||||
* `JS_NUM(val)` - create a numeric value
|
* `JS_OBJ(VAL)` - create a `Napi::Object` value.
|
||||||
* `JS_OFFS(val)` - same as `JS_NUM`, but has a cast designed to avoid `size_t -> double` warning
|
|
||||||
* `JS_FLOAT(val)` - same as `JS_NUM`
|
|
||||||
* `JS_DOUBLE(val)` - same as `JS_NUM`
|
|
||||||
* `JS_EXT(val)` - create an external (pointer) value
|
|
||||||
* `JS_BOOL(val)` - create a boolean value
|
|
||||||
* `JS_FUN(val)` - get a function from persistent `Nan::Persistent<v8::Function>`.
|
|
||||||
* `JS_OBJ(val)` - get an object from persistent `Nan::Persistent<v8::Object>`.
|
|
||||||
|
|
||||||
</details>
|
</details>
|
||||||
|
|
||||||
|
@ -434,8 +391,8 @@ glfwSetWindowFocusCallback(window, windowFocusCB);
|
||||||
<summary>Method check</summary>
|
<summary>Method check</summary>
|
||||||
|
|
||||||
These checks throw JS TypeError if not passed. Here `T` is always used as a typename
|
These checks throw JS TypeError if not passed. Here `T` is always used as a typename
|
||||||
in error messages. `C` is
|
in error messages. `C` is a
|
||||||
[v8::Value](https://v8docs.nodesource.com/node-0.8/dc/d0a/classv8_1_1_value.html)
|
[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]`,
|
check method, like `IsObject()`. `I` is the index of argument as in `info[I]`,
|
||||||
starting from `0`.
|
starting from `0`.
|
||||||
|
|
||||||
|
@ -445,7 +402,8 @@ starting from `0`.
|
||||||
* `CHECK_LET_ARG(I, C, T)` - check if argument `I` is approved by `C` check or empty.
|
* `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
|
* `CTOR_CHECK(T)` - check if method is called as a constructor
|
||||||
* `SETTER_CHECK(C, T)` - check if setter `value` is approved by `C` check.
|
* `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()`.
|
* `DES_CHECK` - within dynamic method check if the instance wasn't
|
||||||
|
destroyed by `destroy()`.
|
||||||
|
|
||||||
</details>
|
</details>
|
||||||
|
|
||||||
|
@ -454,34 +412,64 @@ starting from `0`.
|
||||||
|
|
||||||
<summary>Method arguments</summary>
|
<summary>Method arguments</summary>
|
||||||
|
|
||||||
Two types of argument retrieval are supported: `REQ_` and `LET_`. The difference
|
The idea is to ease the transition from what inside the `CallbackInfo` to
|
||||||
is that `LET_` allows the argument to be empty, using some zero-default in this case.
|
what you work with in C++.
|
||||||
`I` is the index of argument as in `info[I]`,
|
Three types of argument retrieval are supported: `REQ_`, `USE_` and `LET_`.
|
||||||
starting from `0`. `VAR` is the name of the variable to be created.
|
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<uint8_t>`.
|
||||||
|
|
||||||
* `REQ_UTF8_ARG(I, VAR)` - require `I`'th argument to be a `string`. Stored at `Nan::Utf8String VAR`.
|
|
||||||
* `LET_UTF8_ARG(I, VAR)` - let optional `I`'th argument to be a `string`, the default is `""`. Stored at `Nan::Utf8String VAR`.
|
|
||||||
* `REQ_STR_ARG(I, VAR)` - require `I`'th argument to be a `string`. Stored at `Nan::Utf8String VAR`.
|
|
||||||
* `LET_STR_ARG(I, VAR)` - let optional `I`'th argument to be a `string`, the default is `""`. Stored at `Nan::Utf8String VAR`.
|
|
||||||
* `REQ_INT32_ARG(I, VAR)` - require `I`'th argument to be a `number`. Stored at `int VAR`.
|
|
||||||
* `LET_INT32_ARG(I, VAR)` - let optional `I`'th argument to be a `number`, the default is `0`. Stored at `int VAR`.
|
|
||||||
* `REQ_INT32_ARG(I, VAR)` - require `I`'th argument to be a `number`. Stored at `int VAR`.
|
|
||||||
* `LET_INT32_ARG(I, VAR)` - let optional `I`'th argument to be a `number`, the default is `0`. Stored at `int VAR`.
|
|
||||||
* `REQ_UINT32_ARG(I, VAR)` - require `I`'th argument to be a `number`. Stored at `unsigned VAR`.
|
|
||||||
* `LET_UINT32_ARG(I, VAR)` - let optional `I`'th argument to be a `number`, the default is `0`. Stored at `unsigned VAR`.
|
|
||||||
* `REQ_BOOL_ARG(I, VAR)` - require `I`'th argument to be a `boolean`. Stored at `bool VAR`.
|
|
||||||
* `LET_BOOL_ARG(I, VAR)` - let optional `I`'th argument to be a `boolean`, the default is `false`. Stored at `Nan::Utf8String VAR`.
|
|
||||||
* `REQ_OFFS_ARG(I, VAR)` - require `I`'th argument to be a `number`. Stored at `size_t VAR`.
|
|
||||||
* `LET_OFFS_ARG(I, VAR)` - let optional `I`'th argument to be a `number`, the default is `0`. Stored at `Nan::Utf8String VAR`.
|
|
||||||
* `REQ_DOUBLE_ARG(I, VAR)` - require `I`'th argument to be a `number`. Stored at `double VAR`.
|
|
||||||
* `LET_DOUBLE_ARG(I, VAR)` - let optional `I`'th argument to be a `number`, the default is `0.0`. Stored at `Nan::Utf8String VAR`.
|
|
||||||
* `REQ_FLOAT_ARG(I, VAR)` - require `I`'th argument to be a `number`. Stored at `float VAR`.
|
|
||||||
* `LET_FLOAT_ARG(I, VAR)` - let optional `I`'th argument to be a `number`, the default is `0.0f`. Stored at `Nan::Utf8String VAR`.
|
|
||||||
* `REQ_EXT_ARG(I, VAR)` - require `I`'th argument to be an `external`. Stored at `Local<External> VAR`.
|
|
||||||
* `LET_EXT_ARG(I, VAR)` - let optional `I`'th argument to be an `external`, the default is `nullptr`. Stored at `Nan::Utf8String VAR`.
|
|
||||||
* `REQ_FUN_ARG(I, VAR)` - require `I`'th argument to be a `function`. Stored at `Local<Function> VAR`.
|
|
||||||
* `REQ_OBJ_ARG(I, VAR)` - require `I`'th argument to be an `object`. Stored at `Local<Object> VAR`.
|
|
||||||
* `REQ_ARRV_ARG(I, VAR)` - require `I`'th argument to be a `TypedArray`. Stored at `Local<ArrayBufferView> VAR`.
|
|
||||||
|
|
||||||
```
|
```
|
||||||
NAN_METHOD(test) {
|
NAN_METHOD(test) {
|
||||||
|
@ -493,22 +481,6 @@ NAN_METHOD(test) {
|
||||||
...
|
...
|
||||||
```
|
```
|
||||||
|
|
||||||
> Note: The conversion from `Nan::Utf8String` to `std::string` (via `char *`)
|
|
||||||
is possible with unary `*` operator.
|
|
||||||
|
|
||||||
</details>
|
|
||||||
|
|
||||||
|
|
||||||
<details>
|
|
||||||
|
|
||||||
<summary>Set properties</summary>
|
|
||||||
|
|
||||||
Set-helpers for string and numeric keys. String keys are converted to JS strings
|
|
||||||
automatically.
|
|
||||||
|
|
||||||
* `SET_PROP(OBJ, KEY, VAL)`
|
|
||||||
* `SET_I(ARR, I, VAL)`
|
|
||||||
|
|
||||||
</details>
|
</details>
|
||||||
|
|
||||||
|
|
||||||
|
@ -519,18 +491,25 @@ automatically.
|
||||||
Simplified accessor assignment, adds accessors of NAME for OBJ. Read accessor is
|
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'`.
|
assumed to have the name `NAME+'Getter'` and write accessor is `NAME+'Setter'`.
|
||||||
|
|
||||||
* `ACCESSOR_RW(OBJ, NAME)` - add read and write accessors of NAME for OBJ.
|
* `ACCESSOR_RW(CLASS, NAME)` - add read and write accessors NAME of CLASS.
|
||||||
* `ACCESSOR_R(OBJ, NAME)` - read-only property.
|
* `ACCESSOR_R(CLASS, NAME)` - add read accessor NAME of CLASS.
|
||||||
|
* `ACCESSOR_M(CLASS, NAME)` - add method NAME of CLASS.
|
||||||
|
|
||||||
|
|
||||||
```
|
```
|
||||||
void MyClass::init(Handle<Object> target) {
|
void MyClass::init(Napi::Env env, Napi::Object exports) {
|
||||||
...
|
...
|
||||||
Local<ObjectTemplate> proto = ctor->PrototypeTemplate();
|
Napi::Function ctor = DefineClass(env, "MyClass", {
|
||||||
ACCESSOR_RW(proto, message);
|
ACCESSOR_R(MyClass, isDestroyed),
|
||||||
|
ACCESSOR_RW(MyClass, x),
|
||||||
|
ACCESSOR_M(MyClass, reset),
|
||||||
|
});
|
||||||
...
|
...
|
||||||
}
|
}
|
||||||
NAN_GETTER(MyClass::messageGetter) { ...
|
JS_GETTER(MyClass::isDestroyedGetter) { ...
|
||||||
NAN_SETTER(MyClass::messageSetter) { ...
|
JS_GETTER(MyClass::xGetter) { ...
|
||||||
|
JS_SETTER(MyClass::xSetter) { ...
|
||||||
|
JS_METHOD(MyClass::save) { ...
|
||||||
```
|
```
|
||||||
|
|
||||||
</details>
|
</details>
|
||||||
|
@ -540,26 +519,26 @@ NAN_SETTER(MyClass::messageSetter) { ...
|
||||||
|
|
||||||
<summary>Setter argument</summary>
|
<summary>Setter argument</summary>
|
||||||
|
|
||||||
Useful addition to NAN_SETTER macro. Works similar to method arguments. But there
|
Works similar to method arguments. But there is always `value`
|
||||||
is always only one required argument stored in `v`.
|
argument, from which a C++ value is extracted.
|
||||||
|
|
||||||
* `SETTER_UTF8_ARG` - require the value to be a `string`. Stored at `Nan::Utf8String v`.
|
* `SETTER_STR_ARG`
|
||||||
* `SETTER_STR_ARG` - require the value to be a `string`. Stored at `Nan::Utf8String v`.
|
* `SETTER_INT32_ARG`
|
||||||
* `SETTER_INT32_ARG` - require the value to be a `number`. Stored at `int v`.
|
* `SETTER_INT_ARG`
|
||||||
* `SETTER_INT_ARG` - require the value to be a `number`. Stored at `int v`.
|
* `SETTER_BOOL_ARG`
|
||||||
* `SETTER_UINT32_ARG` - require the value to be a `number`. Stored at `unsigned v`.
|
* `SETTER_UINT32_ARG`
|
||||||
* `SETTER_BOOL_ARG` - require the value to be a `boolean`. Stored at `bool v`.
|
* `SETTER_UINT_ARG`
|
||||||
* `SETTER_OFFS_ARG` - require the value to be a `number`. Stored at `size_t v`.
|
* `SETTER_OFFS_ARG`
|
||||||
* `SETTER_DOUBLE_ARG` - require the value to be a `number`. Stored at `double v`.
|
* `SETTER_DOUBLE_ARG`
|
||||||
* `SETTER_FLOAT_ARG` - require the value to be a `number`. Stored at `float v`.
|
* `SETTER_FLOAT_ARG`
|
||||||
* `SETTER_EXT_ARG` - require the value to be an `external`. Stored at `Local<External> v`.
|
* `SETTER_EXT_ARG`
|
||||||
* `SETTER_FUN_ARG` - require the value to be a `function`. Stored at `Local<Function> v`.
|
* `SETTER_FUN_ARG`
|
||||||
* `SETTER_OBJ_ARG` - require the value to be an `object`. Stored at `Local<Object> v`.
|
* `SETTER_OBJ_ARG`
|
||||||
* `SETTER_ARRV_ARG` - require the value to be a `TypedArray`. Stored at `Local<ArrayBufferView> v`.
|
* `SETTER_ARRV_ARG`
|
||||||
|
|
||||||
```
|
```
|
||||||
NAN_SETTER(MyClass::messageSetter) { SETTER_UTF8_ARG;
|
JS_SETTER(MyClass::x) { SETTER_STR_ARG;
|
||||||
// Variable created: Nan::Utf8String v;
|
// Variable created: std::string v;
|
||||||
...
|
...
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -571,9 +550,8 @@ NAN_SETTER(MyClass::messageSetter) { SETTER_UTF8_ARG;
|
||||||
<summary>Data retrieval</summary>
|
<summary>Data retrieval</summary>
|
||||||
|
|
||||||
* `T *getArrayData(value, num = NULL)` - extracts TypedArray data of any type from
|
* `T *getArrayData(value, num = NULL)` - extracts TypedArray data of any type from
|
||||||
the given JS value. Does not accept Array, checked with `IsArrayBufferView()`.
|
the given JS value. Does not accept `Array`. Checks with `IsArrayBuffer()`.
|
||||||
Returns `NULL` for empty JS values. For unacceptable values throws TypeError.
|
Returns `nullptr` for empty JS values. For unacceptable values throws TypeError.
|
||||||
|
|
||||||
|
|
||||||
* `void *getData(value)` - if value is a TypedArray, then the result of
|
* `void *getData(value)` - if value is a TypedArray, then the result of
|
||||||
`getArrayData(value)` is returned. Otherwise if value has `'data'` property, it's
|
`getArrayData(value)` is returned. Otherwise if value has `'data'` property, it's
|
||||||
|
@ -589,41 +567,18 @@ content is then returned as `node::Buffer`. Returns `nullptr` in other cases.
|
||||||
Exports:
|
Exports:
|
||||||
* `paths(dir)` - function. Returns a set of platform dependent paths depending on
|
* `paths(dir)` - function. Returns a set of platform dependent paths depending on
|
||||||
input `dir`.
|
input `dir`.
|
||||||
* `bin()` - prints platform binary directory absolute path.
|
* `bin` - platform binary directory absolute path.
|
||||||
* `rem()` - prints a space-separated list of binary paths to be cleaned on this platform.
|
* `include` - include directory for this `dir`.
|
||||||
* `include()` - prints include directory for this `dir`.
|
* `include` - both `'addon-tools-raub'` and `'node-addon-api'` include paths.
|
||||||
* `binPath` - platform binary directory absolute path.
|
Use with `node -p` through list context command expansion `<!@(...)`
|
||||||
* `remPath` - a space-separated list of binary paths to be cleaned on this platform.
|
* `rm` - the location of `'_rm.bat'` file on Windows and plain `rm` on Unix.
|
||||||
* `includePath` - include directory for this `dir`.
|
* `cp` - the location of `'_cp.bat'` file on Windows and plain `cp` on Unix.
|
||||||
* `root()` - prints where `'addon-tools-raub'` module is situated.
|
* `mkdir` - the location of `'_mkdir.bat'` file on Windows and plain `mkdir` on Unix.
|
||||||
* `include()` - prints both `'addon-tools-raub'` and `'nan'` include paths. Use with
|
* `bin` - platform binary directory name.
|
||||||
`node -e` through list context command expansion `<!@(...)`
|
|
||||||
* `rm()` - prints the location of `'_rm.bat'` file on Windows and plain `rm` on Unix.
|
|
||||||
* `cp()` - prints the location of `'_cp.bat'` file on Windows and plain `cp` on Unix.
|
|
||||||
* `mkdir()` - prints the location of `'_mkdir.bat'` file on Windows and plain `mkdir` on Unix.
|
|
||||||
* `bin()` - prints platform binary directory name.
|
|
||||||
* `binPath` - platform binary directory name.
|
|
||||||
* `rootPath` - where `'addon-tools-raub'` module is situated.
|
|
||||||
* `includePath` - both `'addon-tools-raub'` and `'nan'` include paths.
|
|
||||||
* `rmPath` - the location of `'_rm.bat'` file on Windows and plain `rm` on Unix.
|
|
||||||
* `cpPath` - the location of `'_cp.bat'` file on Windows and plain `cp` on Unix.
|
|
||||||
* `mkdirPath` - the location of `'_mkdir.bat'` file on Windows and plain `mkdir` on Unix.
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
||||||
## Crossplatform commands
|
|
||||||
|
|
||||||
Because of the differences between Windows and Unix command shells, often a whole
|
|
||||||
lot of conditions have to be introduced in **binding.gyp** file. Now some of
|
|
||||||
them can be easily omitted with the new crossplatform commands, supplied by this
|
|
||||||
package.
|
|
||||||
|
|
||||||
This comes especially handy together with GYP's executable list expansion. For
|
|
||||||
example a list of files to be removed for cleaning. Or a list of unnecessary
|
|
||||||
binaries to be removed upon installation of a binary-dependency package.
|
|
||||||
|
|
||||||
|
|
||||||
### mkdir
|
### mkdir
|
||||||
|
|
||||||
On Unix, it will be an actual system `mkdir`, whereas on Windows it will use the
|
On Unix, it will be an actual system `mkdir`, whereas on Windows it will use the
|
||||||
|
@ -635,7 +590,7 @@ folder. This can possibly be bypassed by supplying `./-p` or something like this
|
||||||
|
|
||||||
```
|
```
|
||||||
'variables': {
|
'variables': {
|
||||||
'mkdir' : '<!(node -e "require(\'addon-tools-raub\').mkdir()")',
|
'mkdir' : '<!(node -p "require(\'addon-tools-raub\').mkdir")',
|
||||||
},
|
},
|
||||||
...
|
...
|
||||||
'action' : ['<(mkdir)', '-p', 'binary'],
|
'action' : ['<(mkdir)', '-p', 'binary'],
|
||||||
|
@ -649,11 +604,10 @@ be used on all platforms to remove single and multiple files and directories.
|
||||||
|
|
||||||
```
|
```
|
||||||
'variables': {
|
'variables': {
|
||||||
'rm' : '<!(node -e "require(\'addon-tools-raub\').rm()")',
|
'rm' : '<!(node -e "require(\'addon-tools-raub\').rm")',
|
||||||
'rem' : '<!(node -e "require(\'.\').rem()")',
|
|
||||||
},
|
},
|
||||||
...
|
...
|
||||||
'action' : ['<(rm)', '-rf', '<@(rem)'],
|
'action' : ['<(rm)', '-rf', 'dirname'],
|
||||||
```
|
```
|
||||||
|
|
||||||
### cp
|
### cp
|
||||||
|
@ -662,101 +616,12 @@ For Windows the `/y` flag was embedded.
|
||||||
|
|
||||||
```
|
```
|
||||||
'variables': {
|
'variables': {
|
||||||
'cp' : '<!(node -e "require(\'addon-tools-raub\').cp()")',
|
'cp' : '<!(node -e "require(\'addon-tools-raub\').cp")',
|
||||||
},
|
},
|
||||||
...
|
...
|
||||||
'action' : ['<(cp)', 'a', 'b'],
|
'action' : ['<(cp)', 'a', 'b'],
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
|
|
||||||
## Class EventEmitter
|
|
||||||
|
|
||||||
A C++ implementation of [Events API](https://nodejs.org/api/events.html).
|
|
||||||
|
|
||||||
> Note: This implementation has some minor deviations from the above standard.
|
|
||||||
Specifically there is no static `EventEmitter.defaultMaxListeners` property.
|
|
||||||
However the dynamic one persists and is infinite (`0`) by default.
|
|
||||||
|
|
||||||
Also
|
|
||||||
[EventTarget](https://developer.mozilla.org/en-US/docs/Web/API/EventTarget)
|
|
||||||
is implemented. Not in full detail, but should be fine for callers.
|
|
||||||
|
|
||||||
An example can be found in **examples/node-addon** directory.
|
|
||||||
There is `Example` class, implemented in **cpp/example.cpp**, that inherits
|
|
||||||
EventEmitter behavior and is exported to JS.
|
|
||||||
|
|
||||||
For the C++ side `EventEmitter` has following public methods:
|
|
||||||
|
|
||||||
* `void emit(const std::string &name, int argc = 0, v8::Local<v8::Value> *argv = NULL)` -
|
|
||||||
emits an event with the given `name` and, optionally, some additional arguments where
|
|
||||||
`argc` is the number of arguments and `argv` is a pointer to the arguments array.
|
|
||||||
|
|
||||||
* `void on(const std::string &name, V8_VAR_FUNC cb)` -
|
|
||||||
subscribes `cb` to receive `name` events from this emitter, basically
|
|
||||||
`emitter.on(name, cb)`.
|
|
||||||
|
|
||||||
* `void destroy()` - destroys the object, i.e. deactivates it and frees
|
|
||||||
resources. This is what also called inside
|
|
||||||
`~EventEmitter()`, but only the first call is effective anyway.
|
|
||||||
|
|
||||||
|
|
||||||
Be sure to add the include directory in **binding.gyp**:
|
|
||||||
|
|
||||||
```
|
|
||||||
'include_dirs': [
|
|
||||||
'<!@(node -e "require(\'addon-tools-raub\').include()")',
|
|
||||||
],
|
|
||||||
```
|
|
||||||
|
|
||||||
Then include the **event-emitter.hpp**, it also includes **addon-tools.hpp**.
|
|
||||||
Inherit from `EventEmitter`, it already inherits from `Nan::ObjectWrap`:
|
|
||||||
|
|
||||||
```
|
|
||||||
#include <event-emitter.hpp>
|
|
||||||
|
|
||||||
class Example : public EventEmitter {
|
|
||||||
...
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
> Note: Do not forget to call `EventEmitter::init()` once, in the module `init()`.
|
|
||||||
|
|
||||||
|
|
||||||
<details>
|
|
||||||
|
|
||||||
<summary>V8 Inheritance</summary>
|
|
||||||
|
|
||||||
Now that everything is in place, consider providing **V8** with JS inheritance info:
|
|
||||||
|
|
||||||
```
|
|
||||||
void Example::init(Handle<Object> target) {
|
|
||||||
|
|
||||||
Local<FunctionTemplate> proto = Nan::New<FunctionTemplate>(newCtor);
|
|
||||||
|
|
||||||
// -------------------------- HERE!
|
|
||||||
// class Example extends EventEmitter
|
|
||||||
Local<FunctionTemplate> parent = Nan::New(EventEmitter::_prototype);
|
|
||||||
proto->Inherit(parent);
|
|
||||||
// --------------------------
|
|
||||||
|
|
||||||
proto->InstanceTemplate()->SetInternalFieldCount(1);
|
|
||||||
proto->SetClassName(JS_STR("Example"));
|
|
||||||
|
|
||||||
Local<Function> ctor = Nan::GetFunction(proto).ToLocalChecked();
|
|
||||||
|
|
||||||
_constructor.Reset(ctor);
|
|
||||||
|
|
||||||
Nan::Set(target, JS_STR("Example"), ctor);
|
|
||||||
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
</details>
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
|
|
||||||
## Function consoleLog
|
## Function consoleLog
|
||||||
|
|
||||||
|
@ -766,7 +631,7 @@ At first it may look as if `cout << "msg" << endl;` works nice, but it doesn't.
|
||||||
After a while, it just ceases on a midword, and you end up thinking something has
|
After a while, it just ceases on a midword, and you end up thinking something has
|
||||||
broken really hard in your addon.
|
broken really hard in your addon.
|
||||||
|
|
||||||
To overcome this, we can use some V8 `eval` magic to make a real `console.log`
|
To overcome this, we can use some `eval` magic to make a real `console.log`
|
||||||
call from C++ land. And this is where `consoleLog` comes into play.
|
call from C++ land. And this is where `consoleLog` comes into play.
|
||||||
|
|
||||||
* `inline void consoleLog(int argc, V8_VAR_VAL *argv)` - a generic logger,
|
* `inline void consoleLog(int argc, V8_VAR_VAL *argv)` - a generic logger,
|
||||||
|
@ -775,4 +640,39 @@ receives any set of arguments.
|
||||||
* `inline void consoleLog(const std::string &message)` - an alias to log a single
|
* `inline void consoleLog(const std::string &message)` - an alias to log a single
|
||||||
string.
|
string.
|
||||||
|
|
||||||
> Note: Don't do it in GC-accessible code: sometimes it works, sometimes it crashes.
|
> 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;
|
||||||
|
eventEmit(env, info.This().As<Napi::Object>(), name);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Signature:
|
||||||
|
```
|
||||||
|
inline void eventEmit(
|
||||||
|
Napi::Env env,
|
||||||
|
Napi::Object that,
|
||||||
|
const std::string &name,
|
||||||
|
int argc = 0,
|
||||||
|
Napi::Value *argv = nullptr
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
|
@ -9,27 +9,27 @@
|
||||||
#define NAPI_HS Napi::HandleScope scope(env);
|
#define NAPI_HS Napi::HandleScope scope(env);
|
||||||
|
|
||||||
|
|
||||||
#define JS_STR(val) Napi::String::New(env, val)
|
#define JS_STR(VAL) Napi::String::New(env, VAL)
|
||||||
#define JS_NUM(val) Napi::Number::New(env, static_cast<double>(val))
|
#define JS_NUM(VAL) Napi::Number::New(env, static_cast<double>(VAL))
|
||||||
#define JS_EXT(val) Napi::External::New(env, reinterpret_cast<void*>(val))
|
#define JS_EXT(VAL) Napi::External::New(env, reinterpret_cast<void*>(VAL))
|
||||||
#define JS_BOOL(val) Napi::Boolean::New(env, val)
|
#define JS_BOOL(VAL) Napi::Boolean::New(env, VAL)
|
||||||
#define JS_FUN(val) Napi::Function::Function(env, val)
|
#define JS_FUN(VAL) Napi::Function::Function(env, VAL)
|
||||||
#define JS_OBJ(val) Napi::Object::Object(env, val)
|
#define JS_OBJ(VAL) Napi::Object::Object(env, VAL)
|
||||||
|
|
||||||
|
|
||||||
#define RET_VALUE(VAL) return VAL;
|
#define RET_VALUE(VAL) return VAL;
|
||||||
#define RET_UNDEFINED RET_VALUE(env.Undefined())
|
#define RET_UNDEFINED RET_VALUE(env.Undefined())
|
||||||
#define RET_NULL RET_VALUE(env.Null())
|
#define RET_NULL RET_VALUE(env.Null())
|
||||||
#define RET_STR(val) RET_VALUE(JS_STR(val))
|
#define RET_STR(VAL) RET_VALUE(JS_STR(VAL))
|
||||||
#define RET_NUM(val) RET_VALUE(JS_NUM(val))
|
#define RET_NUM(VAL) RET_VALUE(JS_NUM(VAL))
|
||||||
#define RET_EXT(val) RET_VALUE(JS_EXT(val))
|
#define RET_EXT(VAL) RET_VALUE(JS_EXT(VAL))
|
||||||
#define RET_BOOL(val) RET_VALUE(JS_BOOL(val))
|
#define RET_BOOL(VAL) RET_VALUE(JS_BOOL(VAL))
|
||||||
#define RET_FUN(val) RET_VALUE(JS_FUN(val))
|
#define RET_FUN(VAL) RET_VALUE(JS_FUN(VAL))
|
||||||
#define RET_OBJ(val) RET_VALUE(JS_OBJ(val))
|
#define RET_OBJ(VAL) RET_VALUE(JS_OBJ(VAL))
|
||||||
|
|
||||||
|
|
||||||
#define JS_THROW(val) \
|
#define JS_THROW(VAL) \
|
||||||
Napi::Error::New(env, val).ThrowAsJavaScriptException();
|
Napi::Error::New(env, VAL).ThrowAsJavaScriptException();
|
||||||
|
|
||||||
|
|
||||||
#define REQ_ARGS(N) \
|
#define REQ_ARGS(N) \
|
||||||
|
@ -38,7 +38,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#define IS_EMPTY(val) (val.IsNull() || val.IsUndefined())
|
#define IS_EMPTY(VAL) (VAL.IsNull() || VAL.IsUndefined())
|
||||||
#define IS_ARG_EMPTY(I) IS_EMPTY(info[I])
|
#define IS_ARG_EMPTY(I) IS_EMPTY(info[I])
|
||||||
|
|
||||||
|
|
||||||
|
@ -173,17 +173,6 @@
|
||||||
#define LET_OBJ_ARG(I, VAR) USE_OBJ_ARG(I, VAR, info[I].As<Napi::Object>())
|
#define LET_OBJ_ARG(I, VAR) USE_OBJ_ARG(I, VAR, info[I].As<Napi::Object>())
|
||||||
|
|
||||||
|
|
||||||
#define REQ_OBJ_ARG(I, VAR) \
|
|
||||||
CHECK_REQ_ARG(I, IsObject(), "Object"); \
|
|
||||||
Napi::Object VAR = info[I].As<Napi::Object>();
|
|
||||||
|
|
||||||
#define USE_OBJ_ARG(I, VAR, DEF) \
|
|
||||||
CHECK_LET_ARG(I, IsObject(), "Object"); \
|
|
||||||
Napi::Object VAR = IS_ARG_EMPTY(I) ? (DEF) : info[I].As<Napi::Object>();
|
|
||||||
|
|
||||||
#define LET_OBJ_ARG(I, VAR) USE_OBJ_ARG(I, VAR, info[I].As<Napi::Object>())
|
|
||||||
|
|
||||||
|
|
||||||
#define REQ_ARRV_ARG(I, VAR) \
|
#define REQ_ARRV_ARG(I, VAR) \
|
||||||
CHECK_REQ_ARG(I, IsArrayBuffer(), "Object"); \
|
CHECK_REQ_ARG(I, IsArrayBuffer(), "Object"); \
|
||||||
Napi::ArrayBuffer VAR = info[I].As<Napi::ArrayBuffer>();
|
Napi::ArrayBuffer VAR = info[I].As<Napi::ArrayBuffer>();
|
||||||
|
@ -224,6 +213,8 @@
|
||||||
#define ACCESSOR_M(CLASS, NAME) \
|
#define ACCESSOR_M(CLASS, NAME) \
|
||||||
InstanceMethod(#NAME, &CLASS::NAME)
|
InstanceMethod(#NAME, &CLASS::NAME)
|
||||||
|
|
||||||
|
#define THIS_OBJ(VAR) \
|
||||||
|
Napi::Object VAR = info.This().As<Napi::Object>();
|
||||||
|
|
||||||
#define SETTER_STR_ARG \
|
#define SETTER_STR_ARG \
|
||||||
SETTER_CHECK(IsNumber(), "String"); \
|
SETTER_CHECK(IsNumber(), "String"); \
|
||||||
|
|
69
install.js
69
install.js
|
@ -2,35 +2,56 @@
|
||||||
|
|
||||||
|
|
||||||
const https = require('https');
|
const https = require('https');
|
||||||
const unzipper = require('unzipper');
|
const http = require('http');
|
||||||
|
|
||||||
const unzipper = require('unzipper');
|
const unzipper = require('unzipper');
|
||||||
|
|
||||||
const REPO = process.argv[1];
|
const { bin, platform } = require('.');
|
||||||
|
|
||||||
|
|
||||||
const download = (url, count = 1) => {
|
const protocols = { http, https };
|
||||||
const sendReq = https.get(url, response => {
|
|
||||||
if ([301, 302, 303, 307].includes(response.statusCode)) {
|
|
||||||
if (count < 5) {
|
|
||||||
return download(response.headers.location, count + 1);
|
|
||||||
}
|
|
||||||
console.error('Error: Too many redirects.');
|
|
||||||
process.exit(-1);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (response.statusCode !== 200) {
|
|
||||||
console.log('Response status was ' + response.statusCode);
|
|
||||||
process.exit(-1);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
response.pipe(unzipper.Extract({ path: 'bin' }));
|
|
||||||
});
|
|
||||||
|
|
||||||
sendReq.on('error', err => {
|
const onError = msg => {
|
||||||
console.log(err.message);
|
console.error(msg);
|
||||||
process.exit(-1);
|
process.exit(-1);
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
download('https://github.com/raub/test-download/releases/download/v1.0.0/win.zip');
|
|
||||||
|
const install = (url, count = 1) => {
|
||||||
|
|
||||||
|
url = url.toLowerCase();
|
||||||
|
const proto = protocols[url.match(/^https?/)[0]];
|
||||||
|
|
||||||
|
const request = https.get(url, response => {
|
||||||
|
|
||||||
|
// Handle redirects
|
||||||
|
if ([301, 302, 303, 307].includes(response.statusCode)) {
|
||||||
|
if (count < 5) {
|
||||||
|
return install(response.headers.location, count + 1);
|
||||||
|
}
|
||||||
|
return onError('Error: Too many redirects.');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle bad status
|
||||||
|
if (response.statusCode !== 200) {
|
||||||
|
return onError(`Response status was ${response.statusCode}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
response.on('error', err => onError(err.message));
|
||||||
|
|
||||||
|
const extractor = unzipper.Extract({ path: bin });
|
||||||
|
extractor.on('error', err => onError(err.message));
|
||||||
|
|
||||||
|
response.pipe(extractor);
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
request.on('error', err => onError(err.message));
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
module.exports = folder => {
|
||||||
|
const url = `${folder}/${platform}`;
|
||||||
|
install(url);
|
||||||
|
};
|
||||||
|
|
Loading…
Reference in New Issue