📝 🎨 Refactor docs and examples

This commit is contained in:
raub 2018-05-04 10:32:56 +03:00
parent 20e7eead8b
commit 08635b0993
5 changed files with 120 additions and 46 deletions

132
README.md
View File

@ -1,15 +1,18 @@
# Addon Tools # Addon Tools
This is a part of [Node3D](https://github.com/node-3d) project.
## Synopsis ## Synopsis
This is a set of helpers for simplification and standardization of addons and Helpers for Node.js addons and dependency packages:
dependency packages.
* EventEmitter C++ implementation. * `EventEmitter` C++ implementation.
* Contains helpers of following types: GYP, C++, JS, BAT (Windows). * C++ macros and shortcuts.
* Platforms: win x32/x64, linux x32/x64, mac x64. * Crossplatform commands for GYP: `cp`, `rm`, `mkdir`.
* Useful links: [V8 Ref](https://v8docs.nodesource.com/node-0.8/d2/dc3/namespacev8.html), * Regarded platforms: win x32/x64, linux x32/x64, mac x64.
Useful links: [V8 Ref](https://v8docs.nodesource.com/node-0.8/d2/dc3/namespacev8.html),
[Nan Docs](https://github.com/nodejs/nan#api), [Nan Docs](https://github.com/nodejs/nan#api),
[GYP Docs](https://gyp.gsrc.io/docs/UserDocumentation.md). [GYP Docs](https://gyp.gsrc.io/docs/UserDocumentation.md).
@ -29,7 +32,7 @@ dependency packages.
[index.js](#indexjs) [index.js](#indexjs)
[Cross-platform commands](#cross-platform-commands) [Crossplatform commands](#crossplatform-commands)
[Class EventEmitter](#class-eventemitter) [Class EventEmitter](#class-eventemitter)
@ -38,9 +41,10 @@ dependency packages.
## Snippets ## Snippets
### binding.gyp ### binding.gyp
* Cross-platform file/folder removers/creators are present, you can put them into variables for later use. * Crossplatform commands can be put into the variables for later use.
``` ```
'variables': { 'variables': {
@ -59,7 +63,11 @@ are accessible as shown below.
], ],
``` ```
* Intermediate files can be removed in a separate build-step with `<(rm)`. * Intermediate build-files can be removed in a separate build-step with `<(rm)`.
<details>
<summary>Show Snippet</summary>
``` ```
[ 'OS=="linux"', { 'action' : [ [ 'OS=="linux"', { 'action' : [
@ -79,6 +87,8 @@ are accessible as shown below.
] } ], ] } ],
``` ```
</details>
### Binary dependencies ### Binary dependencies
@ -101,30 +111,42 @@ module.exports = require('addon-tools-raub').paths(__dirname);
* Your whole `binding.gyp`: * Your whole `binding.gyp`:
<details>
<summary>Show Snippet</summary>
``` ```
{ {
'variables': { 'variables': {
'rm' : '<!(node -e "require(\'addon-tools-raub\').rm()")', 'rm' : '<!(node -e "require(\'addon-tools-raub\').rm()")',
'rem' : '<!(node -e "require(\'.\').rem()")', 'rem' : '<!(node -e "require(\'.\').rem()")',
'XALL%': 'false',
}, },
'targets': [ 'targets': [
{ {
'target_name' : 'remove_extras', 'target_name' : 'remove_extras',
'type' : 'none', 'type' : 'none',
'actions' : [ 'conditions' : [['XALL=="false"', {'actions': [
{ {
'action_name' : 'Unnecessary binaries removed.', 'action_name' : 'Unnecessary binaries removed.',
'inputs' : [], 'inputs' : [],
'outputs' : ['build'], 'outputs' : ['build'],
'action' : ['<(rm)', '-rf', '<@(rem)'], '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
@ -132,6 +154,10 @@ module.exports = require('addon-tools-raub').paths(__dirname);
If you always copy your compiled addon to the `binary` directory, it will be easy to If you always copy your compiled addon to the `binary` directory, it will be easy to
`require()` it without any hesitation. For copying, you can use the following snippet: `require()` it without any hesitation. For copying, you can use the following snippet:
<details>
<summary>Show Snippet</summary>
``` ```
{ {
'target_name' : 'make_directory', 'target_name' : 'make_directory',
@ -157,6 +183,8 @@ If you always copy your compiled addon to the `binary` directory, it will be eas
}, },
``` ```
</details>
Here `MY_ADDON` should be replaced by any name you like. Then require like Here `MY_ADDON` should be replaced by any name you like. Then require like
this: this:
@ -315,7 +343,7 @@ so that you can replace:
with with
``` ```
#include <addon-tools.hpp> #include <addon-tools.hpp> // or event-emitter.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 to get it.
@ -327,10 +355,12 @@ require('addon-tools-raub').include() // implicit console.log()
require('addon-tools-raub').includePath // just a string require('addon-tools-raub').includePath // just a string
``` ```
In the file, currently there are following helpers: Currently, there are following helpers in **addon-tools.hpp**:
#### Handle scope <details>
<summary>Handle scope</summary>
* `NAN_HS` - creates a HandleScope. Also, you do not need them within `NAN_METHOD`, * `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_SETTER`, and `NAN_GETTER`, as it is stated in
@ -345,14 +375,22 @@ void windowFocusCB(GLFWwindow *window, int focused) { NAN_HS;
glfwSetWindowFocusCallback(window, windowFocusCB); glfwSetWindowFocusCallback(window, windowFocusCB);
``` ```
</details>
#### Method return
<details>
<summary>Method return</summary>
* `RET_VALUE(VAL)` - set method return value * `RET_VALUE(VAL)` - set method return value
* `RET_UNDEFINED` - set method return value as undefined * `RET_UNDEFINED` - set method return value as undefined
</details>
#### Shortcut types
<details>
<summary>Shortcut types</summary>
* `V8_VAR_VAL` = `v8::Local<v8::Value>` * `V8_VAR_VAL` = `v8::Local<v8::Value>`
* `V8_VAR_OBJ` = `v8::Local<v8::Object>` * `V8_VAR_OBJ` = `v8::Local<v8::Object>`
@ -366,8 +404,12 @@ glfwSetWindowFocusCallback(window, windowFocusCB);
* `V8_STORE_OBJ` = `Nan::Persistent<v8::Object>` * `V8_STORE_OBJ` = `Nan::Persistent<v8::Object>`
* `V8_STORE_VAL` = `Nan::Persistent<v8::Value>` * `V8_STORE_VAL` = `Nan::Persistent<v8::Value>`
</details>
#### New JS value
<details>
<summary>New JS value</summary>
* `JS_STR(...)` - create a string value * `JS_STR(...)` - create a string value
* `JS_UTF8(...)` - same as JS_STR * `JS_UTF8(...)` - same as JS_STR
@ -383,8 +425,12 @@ glfwSetWindowFocusCallback(window, windowFocusCB);
* `JS_FUN(val)` - get a function from persistent. * `JS_FUN(val)` - get a function from persistent.
* `JS_OBJ(val)` - get an object from persistent. * `JS_OBJ(val)` - get an object from persistent.
</details>
#### Method check
<details>
<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
@ -395,13 +441,17 @@ starting from `0`.
* `REQ_ARGS(N)` - check if at least `N` arguments passed * `REQ_ARGS(N)` - check if at least `N` arguments passed
* `IS_ARG_EMPTY(I)` - check if argument `I` is `undefined` or `null` * `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_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. * `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>
#### Method arguments
<details>
<summary>Method arguments</summary>
Two types of argument retrieval are supported: `REQ_` and `LET_`. The difference Two types of argument retrieval are supported: `REQ_` and `LET_`. The difference
is that `LET_` allows the argument to be empty, using some zero-default in this case. is that `LET_` allows the argument to be empty, using some zero-default in this case.
@ -444,8 +494,12 @@ NAN_METHOD(test) {
NOTE: The conversion from `Nan::Utf8String` to `std::string` (via `char *`) is possible with unary `*` operator. NOTE: The conversion from `Nan::Utf8String` to `std::string` (via `char *`) is possible with unary `*` operator.
</details>
#### Set properties
<details>
<summary>Set properties</summary>
Set-helpers for string and numeric keys. String keys are converted to JS strings Set-helpers for string and numeric keys. String keys are converted to JS strings
automatically. automatically.
@ -453,8 +507,12 @@ automatically.
* `SET_PROP(OBJ, KEY, VAL)` * `SET_PROP(OBJ, KEY, VAL)`
* `SET_I(ARR, I, VAL)` * `SET_I(ARR, I, VAL)`
</details>
#### Set object accessors
<details>
<summary>Set object accessors</summary>
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'`.
@ -473,8 +531,12 @@ NAN_GETTER(MyClass::messageGetter) { ...
NAN_SETTER(MyClass::messageSetter) { ... NAN_SETTER(MyClass::messageSetter) { ...
``` ```
</details>
#### Setter argument
<details>
<summary>Setter argument</summary>
Useful addition to NAN_SETTER macro. Works similar to method arguments. But there Useful addition to NAN_SETTER macro. Works similar to method arguments. But there
is always only one required argument stored in `v`. is always only one required argument stored in `v`.
@ -499,8 +561,12 @@ NAN_SETTER(MyClass::messageSetter) { SETTER_UTF8_ARG;
... ...
``` ```
</details>
#### Data retrieval
<details>
<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, checked with `IsArrayBufferView()`.
@ -512,6 +578,8 @@ Returns `NULL` for empty JS values. For unacceptable values throws TypeError.
content is then returned as `node::Buffer`. Returns `NULL` for empty JS values. content is then returned as `node::Buffer`. Returns `NULL` for empty JS values.
For unacceptable values throws TypeError. For unacceptable values throws TypeError.
</details>
--- ---
@ -541,7 +609,7 @@ input `dir`.
--- ---
## Cross-platform commands ## Crossplatform commands
Because of the differences between Windows and Unix command shells, often a whole 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 lot of conditions have to be introduced in **binding.gyp** file. Now some of
@ -573,7 +641,7 @@ folder. This can possibly be bypassed by supplying `./-p` or something like this
### rm ### rm
Disregard `del` vs `rd` aspect of Windows command line. Now the same command can Disregard `del` and `rd` on Windows command line. Now the same command can
be used on all platforms to remove single and multiple files and directories. be used on all platforms to remove single and multiple files and directories.
``` ```
@ -627,7 +695,7 @@ subscribes `that[method]` to receive `name` events from this emitter, basically
`emitter.on(name, that[method])`. `emitter.on(name, that[method])`.
* `virtual void _destroy()` - destroys the object, i.e. deactivates it and frees * `virtual void _destroy()` - destroys the object, i.e. deactivates it and frees
resources, also emitting a `'destroy'` event. This is what also called inside resources. This is what also called inside
`~EventEmitter()`, but only the first call is effective anyway. `~EventEmitter()`, but only the first call is effective anyway.
@ -639,7 +707,6 @@ Be sure to add the include directory in **binding.gyp**:
], ],
``` ```
Though it is the same directory for all the **addon-tools**.
Then include the **event-emitter.hpp**, it also includes **addon-tools.hpp**. Then include the **event-emitter.hpp**, it also includes **addon-tools.hpp**.
Inherit from `EventEmitter`, it already inherits from `Nan::ObjectWrap`: Inherit from `EventEmitter`, it already inherits from `Nan::ObjectWrap`:
@ -653,6 +720,9 @@ class Example : public EventEmitter {
NOTE: Do not forget to call `EventEmitter::init()` once, in the module `init()`. 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: Now that everything is in place, consider providing **V8** with JS inheritance info:
@ -678,3 +748,5 @@ void Example::init(Handle<Object> target) {
} }
``` ```
</details>

View File

@ -1,20 +1,21 @@
{ {
'variables': { 'variables': {
'rm' : '<!(node -e "require(\'addon-tools-raub\').rm()")', 'rm' : '<!(node -e "require(\'addon-tools-raub\').rm()")',
'rem' : '<!(node -e "require(\'.\').rem()")', 'rem' : '<!(node -e "require(\'.\').rem()")',
'XALL%': 'false',
}, },
'targets': [ 'targets': [
{ {
'target_name' : 'remove_extras', 'target_name' : 'remove_extras',
'type' : 'none', 'type' : 'none',
'actions' : [ 'conditions' : [['XALL=="false"', {'actions': [
{ {
'action_name' : 'Unnecessary binaries removed.', 'action_name' : 'Unnecessary binaries removed.',
'inputs' : [], 'inputs' : [],
'outputs' : ['build'], 'outputs' : ['build'],
'action' : ['<(rm)', '-rf', '<@(rem)'], 'action' : ['<(rm)', '-rf', '<@(rem)'],
} }
], ]}]],
} }
] ]
} }

View File

@ -125,7 +125,7 @@ public:
// C++ side on() method // C++ side on() method
void on(const std::string &name, V8_VAR_VAL that, const std::string &method) { void on(const std::string &name, V8_VAR_VAL that, const std::string &method) {
v8::Local<v8::String> code = JS_STR( V8_VAR_STR code = JS_STR(
"((emitter, name, that, method) => emitter.on(name, that[method]))" "((emitter, name, that, method) => emitter.on(name, that[method]))"
); );
@ -247,7 +247,7 @@ private:
static NAN_METHOD(jsEventNames) { THIS_EVENT_EMITTER; EVENT_EMITTER_THIS_CHECK; static NAN_METHOD(jsEventNames) { THIS_EVENT_EMITTER; EVENT_EMITTER_THIS_CHECK;
v8::Local<v8::Array> jsNames = Nan::New<v8::Array>(eventEmitter->_raw.size()); V8_VAR_ARR jsNames = Nan::New<v8::Array>(eventEmitter->_raw.size());
if (eventEmitter->_raw.empty()) { if (eventEmitter->_raw.empty()) {
RET_VALUE(jsNames); RET_VALUE(jsNames);
@ -290,7 +290,7 @@ private:
VEC_TYPE &list = eventEmitter->_listeners[*name]; VEC_TYPE &list = eventEmitter->_listeners[*name];
v8::Local<v8::Array> jsListeners = Nan::New<v8::Array>(list.size()); V8_VAR_ARR jsListeners = Nan::New<v8::Array>(list.size());
if (list.empty()) { if (list.empty()) {
RET_VALUE(jsListeners); RET_VALUE(jsListeners);
@ -336,10 +336,10 @@ private:
std::cout << name << "' event, possible memory leak." << std::endl; std::cout << name << "' event, possible memory leak." << std::endl;
// Some JS magic to retrieve the call stack // Some JS magic to retrieve the call stack
v8::Local<v8::String> code = JS_STR( V8_VAR_STR code = JS_STR(
"(new Error()).stack.split('\\n').slice(1).join('\\n')" "(new Error()).stack.split('\\n').slice(1).join('\\n')"
); );
v8::Local<v8::String> stack = v8::Local<v8::String>::Cast( V8_VAR_STR stack = V8_VAR_STR::Cast(
v8::Script::Compile(code)->Run() v8::Script::Compile(code)->Run()
); );
Nan::Utf8String stackStr(stack); Nan::Utf8String stackStr(stack);
@ -398,10 +398,10 @@ private:
std::cout << name << "' event, possible memory leak." << std::endl; std::cout << name << "' event, possible memory leak." << std::endl;
// Some JS magic to retrieve the call stack // Some JS magic to retrieve the call stack
v8::Local<v8::String> code = JS_STR( V8_VAR_STR code = JS_STR(
"(new Error()).stack.split('\\n').slice(1).join('\\n')" "(new Error()).stack.split('\\n').slice(1).join('\\n')"
); );
v8::Local<v8::String> stack = v8::Local<v8::String>::Cast( V8_VAR_STR stack = V8_VAR_STR::Cast(
v8::Script::Compile(code)->Run() v8::Script::Compile(code)->Run()
); );
Nan::Utf8String stackStr(stack); Nan::Utf8String stackStr(stack);
@ -420,7 +420,7 @@ private:
REQ_UTF8_ARG(0, name); REQ_UTF8_ARG(0, name);
REQ_FUN_ARG(1, raw); REQ_FUN_ARG(1, raw);
v8::Local<v8::String> code = JS_STR( V8_VAR_STR code = JS_STR(
"((emitter, name, cb) => (...args) => {\n\ "((emitter, name, cb) => (...args) => {\n\
cb(...args);\n\ cb(...args);\n\
emitter.removeListener(name, cb);\n\ emitter.removeListener(name, cb);\n\
@ -633,7 +633,7 @@ private:
VEC_TYPE &list = eventEmitter->_raw[*name]; VEC_TYPE &list = eventEmitter->_raw[*name];
v8::Local<v8::Array> jsListeners = Nan::New<v8::Array>(list.size()); V8_VAR_ARR jsListeners = Nan::New<v8::Array>(list.size());
if (list.empty()) { if (list.empty()) {
RET_VALUE(jsListeners); RET_VALUE(jsListeners);

View File

@ -63,6 +63,7 @@ const mkdirPath = isWindows ? `${rootPath}/_mkdir.bat` : 'mkdir';
const rmPath = isWindows ? `${rootPath}/_rm.bat` : 'rm'; const rmPath = isWindows ? `${rootPath}/_rm.bat` : 'rm';
const cpPath = isWindows ? `${rootPath}/_cp.bat` : 'cp'; const cpPath = isWindows ? `${rootPath}/_cp.bat` : 'cp';
module.exports = { module.exports = {
paths, paths,

View File

@ -1,8 +1,8 @@
{ {
"author": "Luis Blanco <luisblanco1337@gmail.com>", "author": "Luis Blanco <luisblanco1337@gmail.com>",
"name": "addon-tools-raub", "name": "addon-tools-raub",
"version": "0.1.8", "version": "0.1.9",
"description": "A set of extra tools for Node.js addons", "description": "Helpers for Node.js addons and dependency packages",
"license": "MIT", "license": "MIT",
"main": "index.js", "main": "index.js",
"keywords": [ "keywords": [
@ -25,7 +25,7 @@
], ],
"repository": { "repository": {
"type": "git", "type": "git",
"url": "https://github.com/node-3d/node-addon-tools.git" "url": "https://github.com/node-3d/addon-tools-raub.git"
}, },
"dependencies": { "dependencies": {
"nan": "2.10.0" "nan": "2.10.0"