Compare commits
No commits in common. "master" and "v4.2.0" have entirely different histories.
|
@ -0,0 +1,116 @@
|
|||
{
|
||||
"root": true,
|
||||
"env": {
|
||||
"node" : true,
|
||||
"es6" : true,
|
||||
"mocha" : true
|
||||
},
|
||||
"globals": {
|
||||
"expect" : true,
|
||||
"chai" : true,
|
||||
"sinon" : true
|
||||
},
|
||||
"extends": ["eslint:recommended"],
|
||||
"parserOptions": {
|
||||
"ecmaVersion": 8,
|
||||
"ecmaFeatures": {
|
||||
"experimentalObjectRestSpread": true
|
||||
}
|
||||
},
|
||||
"rules": {
|
||||
"arrow-parens": ["error", "as-needed"],
|
||||
"no-trailing-spaces": [
|
||||
"error",
|
||||
{
|
||||
"skipBlankLines": true
|
||||
}
|
||||
],
|
||||
"indent": [
|
||||
"error",
|
||||
"tab",
|
||||
{
|
||||
"SwitchCase": 1
|
||||
}
|
||||
],
|
||||
"linebreak-style": [
|
||||
"error",
|
||||
"unix"
|
||||
],
|
||||
"max-len": ["error", 110],
|
||||
"quotes": [
|
||||
"error",
|
||||
"single"
|
||||
],
|
||||
"semi": [
|
||||
"error",
|
||||
"always"
|
||||
],
|
||||
"no-multiple-empty-lines": ["error", { "max": 3, "maxEOF": 1, "maxBOF": 1 }],
|
||||
"keyword-spacing": ["error", { "before": true, "after": true }],
|
||||
"space-before-blocks": ["error"],
|
||||
"space-before-function-paren": ["error", {"anonymous": "always", "named": "never", "asyncArrow": "always"}],
|
||||
"space-infix-ops": ["error"],
|
||||
"space-unary-ops": [
|
||||
"error", {
|
||||
"words": true,
|
||||
"nonwords": false,
|
||||
"overrides": {
|
||||
"!": true
|
||||
}
|
||||
}
|
||||
],
|
||||
"spaced-comment": [0],
|
||||
"camelcase": ["error"],
|
||||
"no-tabs": [0],
|
||||
"comma-dangle": [0],
|
||||
"global-require": [0],
|
||||
"func-names": [0],
|
||||
"no-param-reassign": [0],
|
||||
"no-underscore-dangle": [0],
|
||||
"no-restricted-syntax": [
|
||||
"error",
|
||||
{
|
||||
"selector": "LabeledStatement",
|
||||
"message": "Labels are a form of GOTO; using them makes code confusing and hard to maintain and understand."
|
||||
},
|
||||
{
|
||||
"selector": "WithStatement",
|
||||
"message": "`with` is disallowed in strict mode because it makes code impossible to predict and optimize."
|
||||
}
|
||||
],
|
||||
"no-mixed-operators": [0],
|
||||
"no-plusplus": [0],
|
||||
"comma-spacing": [0],
|
||||
"default-case": [0],
|
||||
"no-shadow": [0],
|
||||
"no-console": [0],
|
||||
"key-spacing": [0],
|
||||
"no-return-assign": [0],
|
||||
"consistent-return": [0],
|
||||
"class-methods-use-this": [0],
|
||||
"no-multi-spaces": [
|
||||
"error",
|
||||
{
|
||||
"exceptions": {
|
||||
"VariableDeclarator": true,
|
||||
"Property": true,
|
||||
"ImportDeclaration": true
|
||||
}
|
||||
}
|
||||
],
|
||||
"array-callback-return": [0],
|
||||
"no-use-before-define": [
|
||||
"error",
|
||||
{
|
||||
"functions": false,
|
||||
"classes": true,
|
||||
"variables": true
|
||||
}
|
||||
],
|
||||
"padded-blocks": [0],
|
||||
"space-in-parens": [0],
|
||||
"valid-jsdoc": [0],
|
||||
"no-unused-expressions": [0],
|
||||
"import/no-dynamic-require": [0]
|
||||
}
|
||||
}
|
|
@ -1,75 +0,0 @@
|
|||
{
|
||||
"ignorePatterns": [
|
||||
"src/**"
|
||||
],
|
||||
"extends": [
|
||||
"eslint:recommended",
|
||||
"plugin:node/recommended"
|
||||
],
|
||||
"parserOptions": {
|
||||
"ecmaVersion": 2022
|
||||
},
|
||||
"env": {
|
||||
"node": true,
|
||||
"es6": true
|
||||
},
|
||||
"rules": {
|
||||
"arrow-parens": ["error", "always"],
|
||||
"no-trailing-spaces": [
|
||||
"error",
|
||||
{
|
||||
"skipBlankLines": true
|
||||
}
|
||||
],
|
||||
"indent": [
|
||||
"error",
|
||||
"tab",
|
||||
{
|
||||
"SwitchCase": 1
|
||||
}
|
||||
],
|
||||
"operator-linebreak": [
|
||||
"error",
|
||||
"after",
|
||||
{
|
||||
"overrides": {
|
||||
"?": "before",
|
||||
":": "before"
|
||||
}
|
||||
}
|
||||
],
|
||||
"max-len": ["error", 110],
|
||||
"quotes": [
|
||||
"error",
|
||||
"single"
|
||||
],
|
||||
"semi": [
|
||||
"error",
|
||||
"always"
|
||||
],
|
||||
"no-multiple-empty-lines": ["error", { "max": 3, "maxEOF": 1, "maxBOF": 1 }],
|
||||
"keyword-spacing": ["error", { "before": true, "after": true }],
|
||||
"space-before-blocks": ["error"],
|
||||
"space-before-function-paren": ["error", {"anonymous": "always", "named": "never", "asyncArrow": "always"}],
|
||||
"camelcase": ["error"],
|
||||
"no-tabs": [0],
|
||||
"global-require": [0],
|
||||
"no-underscore-dangle": [0],
|
||||
"no-plusplus": [0],
|
||||
"no-shadow": [0],
|
||||
"node/no-unpublished-require": [0],
|
||||
"no-process-exit": [0],
|
||||
"linebreak-style": [0],
|
||||
"node/no-missing-require": [0],
|
||||
"no-console": [0],
|
||||
"node/no-unsupported-features/es-builtins": 0,
|
||||
"node/no-unsupported-features/node-builtins": 0,
|
||||
"func-names": [
|
||||
"error",
|
||||
"never",
|
||||
{
|
||||
"generators": "never"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
|
@ -1 +0,0 @@
|
|||
test-addon/binding.gyp linguist-vendored
|
|
@ -1,47 +0,0 @@
|
|||
name: Cpplint
|
||||
defaults:
|
||||
run:
|
||||
shell: bash
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
pull_request:
|
||||
branches:
|
||||
- master
|
||||
|
||||
jobs:
|
||||
eslint:
|
||||
name: Cpplint
|
||||
runs-on: ubuntu-20.04
|
||||
|
||||
steps:
|
||||
|
||||
- name: Fetch Repository
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- name: Install Node.js
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 18.16.0
|
||||
cache: 'npm'
|
||||
|
||||
- name: Install Modules
|
||||
run: npm ci
|
||||
|
||||
- name: Install Python
|
||||
uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: '3.12'
|
||||
|
||||
- name: Install Cpplint
|
||||
run: pip install cpplint
|
||||
|
||||
- name: Run Cpplint
|
||||
run: |
|
||||
node -e "require('.').cpcpplint()"
|
||||
cpplint --recursive ./test-addon
|
||||
cpplint --recursive ./include
|
|
@ -1,36 +0,0 @@
|
|||
name: ESLint
|
||||
defaults:
|
||||
run:
|
||||
shell: bash
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
pull_request:
|
||||
branches:
|
||||
- master
|
||||
|
||||
jobs:
|
||||
eslint:
|
||||
name: ESLint
|
||||
runs-on: ubuntu-20.04
|
||||
|
||||
steps:
|
||||
|
||||
- name: Fetch Repository
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- name: Install Node.js
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 18.16.0
|
||||
cache: 'npm'
|
||||
|
||||
- name: Install Modules
|
||||
run: npm ci
|
||||
|
||||
- name: Run ESLint
|
||||
run: npm run eslint
|
|
@ -1,46 +0,0 @@
|
|||
name: Publish to NPM
|
||||
defaults:
|
||||
run:
|
||||
shell: bash
|
||||
|
||||
on:
|
||||
workflow_dispatch
|
||||
|
||||
jobs:
|
||||
Publish:
|
||||
if: contains('["raub"]', github.actor)
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
|
||||
- name: Fetch Repository
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- name: Install Node.js
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 18.16.0
|
||||
cache: 'npm'
|
||||
|
||||
- name: Get Package Version
|
||||
id: package-version
|
||||
run: node -p "'version='+require('./package').version" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Publish
|
||||
run: |
|
||||
npm config set //registry.npmjs.org/:_authToken ${NPM_TOKEN}
|
||||
npm publish --ignore-scripts
|
||||
env:
|
||||
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
|
||||
|
||||
- name: Create Release
|
||||
id: create_release
|
||||
uses: softprops/action-gh-release@v1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
tag_name: ${{ steps.package-version.outputs.version }}
|
||||
name: Release ${{ steps.package-version.outputs.version }}
|
||||
body: Published at ${{ github.sha }}
|
|
@ -1,43 +0,0 @@
|
|||
name: Test
|
||||
defaults:
|
||||
run:
|
||||
shell: bash
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
pull_request:
|
||||
branches:
|
||||
- master
|
||||
|
||||
jobs:
|
||||
unit-tests:
|
||||
name: Unit Tests
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-20.04, windows-2022, macos-11, [self-hosted, linux, ARM64]]
|
||||
|
||||
runs-on: ${{ matrix.os }}
|
||||
|
||||
steps:
|
||||
|
||||
- name: Fetch Repository
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- name: Install Node.js
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 18.16.0
|
||||
cache: 'npm'
|
||||
|
||||
- name: Install Modules
|
||||
run: npm ci
|
||||
|
||||
- name: Build Sample Addon
|
||||
run: npm run build-test
|
||||
|
||||
- name: Run Unit Tests
|
||||
run: npm run test-ci
|
|
@ -1,9 +1,11 @@
|
|||
.cproject
|
||||
.idea
|
||||
.lock-wscript
|
||||
.DS_Store
|
||||
.cproject
|
||||
.project
|
||||
.lock-wscript
|
||||
build*/
|
||||
.DS_Store
|
||||
Debug/
|
||||
node_modules/
|
||||
test-addon/build/
|
||||
package-lock.json
|
||||
binary/
|
||||
*.log
|
||||
|
|
|
@ -0,0 +1,15 @@
|
|||
*.log
|
||||
.cproject
|
||||
.eslintrc
|
||||
.gitignore
|
||||
.idea
|
||||
.lock-wscript
|
||||
.project
|
||||
binary/
|
||||
build*/
|
||||
CPPLINT.cfg
|
||||
Debug/
|
||||
examples/
|
||||
package-lock.json
|
||||
test/
|
||||
qt/
|
|
@ -0,0 +1,19 @@
|
|||
language: node_js
|
||||
|
||||
node_js:
|
||||
- "10.13.0"
|
||||
|
||||
|
||||
matrix:
|
||||
include:
|
||||
- name: "Linux"
|
||||
os: linux
|
||||
dist: xenial
|
||||
sudo: false
|
||||
- name: "MacOS"
|
||||
os: osx
|
||||
|
||||
|
||||
install:
|
||||
- cd test
|
||||
- npm i
|
|
@ -1,17 +0,0 @@
|
|||
{
|
||||
"configurations": [
|
||||
{
|
||||
"name": "Win32",
|
||||
"includePath": [
|
||||
"${workspaceFolder}/**",
|
||||
"${LocalAppData}/node-gyp/Cache/16.17.0/include/node",
|
||||
"${LocalAppData}/node-gyp/Cache/18.16.0/include/node"
|
||||
],
|
||||
"windowsSdkVersion": "10.0.19041.0",
|
||||
"cStandard": "c17",
|
||||
"cppStandard": "c++17",
|
||||
"intelliSenseMode": "windows-msvc-x64"
|
||||
}
|
||||
],
|
||||
"version": 4
|
||||
}
|
|
@ -1,18 +1,15 @@
|
|||
set noparent
|
||||
linelength=110
|
||||
filter=-build/header_guard
|
||||
filter=-build/include
|
||||
filter=-build/include_order
|
||||
filter=-build/include_what_you_use
|
||||
filter=-build/namespaces
|
||||
filter=-legal/copyright
|
||||
filter=-readability/todo
|
||||
filter=-runtime/indentation_namespace
|
||||
filter=-build/include_order
|
||||
filter=-build/header_guard
|
||||
filter=-build/namespaces
|
||||
filter=-build/include_what_you_use
|
||||
filter=-whitespace/blank_line
|
||||
filter=-whitespace/braces
|
||||
filter=-whitespace/comments
|
||||
filter=-whitespace/tab
|
||||
filter=-whitespace/end_of_line
|
||||
filter=-whitespace/indent
|
||||
filter=-whitespace/operators
|
||||
filter=-whitespace/parens
|
||||
filter=-whitespace/tab
|
||||
filter=-readability/todo
|
2
LICENSE
2
LICENSE
|
@ -1,6 +1,6 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) 2023 Luis Blanco
|
||||
Copyright (c) 2018 Luis Blanco
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
|
802
README.md
802
README.md
|
@ -2,74 +2,802 @@
|
|||
|
||||
This is a part of [Node3D](https://github.com/node-3d) project.
|
||||
|
||||
[](https://badge.fury.io/js/addon-tools-raub)
|
||||
[](https://github.com/node-3d/addon-tools-raub/actions/workflows/eslint.yml)
|
||||
[](https://github.com/node-3d/addon-tools-raub/actions/workflows/test.yml)
|
||||
[](https://github.com/node-3d/addon-tools-raub/actions/workflows/cpplint.yml)
|
||||
[](https://www.npmjs.com/package/addon-tools-raub)
|
||||
|
||||
[](https://travis-ci.com/node-3d/addon-tools-raub)
|
||||
[](https://www.codefactor.io/repository/github/node-3d/addon-tools-raub)
|
||||
|
||||
> npm i -s addon-tools-raub
|
||||
|
||||
|
||||
## Synopsis
|
||||
|
||||
Helpers for Node.js addons and dependency packages:
|
||||
|
||||
* `consoleLog()` C++ implementation.
|
||||
* `EventEmitter` C++ implementation.
|
||||
* C++ macros and shortcuts.
|
||||
* Crossplatform commands for GYP: `cp`, `rm`, `mkdir`.
|
||||
* Regarded platforms: win x32/x64, linux x64, mac x64.
|
||||
|
||||
Useful links: [V8 Ref](https://v8.paulfryzel.com/docs/master/),
|
||||
[Nan Docs](https://github.com/nodejs/nan#api),
|
||||
[GYP Docs](https://gyp.gsrc.io/docs/UserDocumentation.md).
|
||||
|
||||
---
|
||||
|
||||
|
||||
## Contents
|
||||
|
||||
[Snippets](#snippets)
|
||||
|
||||
[include/addon-tools.hpp](#includeaddon-toolshpp)
|
||||
|
||||
[index.js](#indexjs)
|
||||
|
||||
[Crossplatform commands](#crossplatform-commands)
|
||||
|
||||
[Class EventEmitter](#class-eventemitter)
|
||||
|
||||
[Function consoleLog](#function-consolelog)
|
||||
|
||||
---
|
||||
|
||||
|
||||
## Snippets
|
||||
|
||||
|
||||
### binding.gyp
|
||||
|
||||
<details>
|
||||
|
||||
<summary>Crossplatform commands</summary>
|
||||
|
||||
```
|
||||
'variables': {
|
||||
'rm' : '<!(node -e "require(\'addon-tools-raub\').rm()")',
|
||||
'cp' : '<!(node -e "require(\'addon-tools-raub\').cp()")',
|
||||
'mkdir' : '<!(node -e "require(\'addon-tools-raub\').mkdir()")',
|
||||
},
|
||||
```
|
||||
|
||||
On both Windows and Unix those are the console commands for various
|
||||
file system operations. No need for GYP conditions, yay!
|
||||
|
||||
</details>
|
||||
|
||||
|
||||
<details>
|
||||
|
||||
<summary>Addon binary directory</summary>
|
||||
|
||||
```
|
||||
'variables': {
|
||||
'binary' : '<!(node -e "require(\'addon-tools-raub\').bin()")',
|
||||
},
|
||||
```
|
||||
|
||||
In some cases, you'd like to have your addon installed for multiple architectures
|
||||
simultaneously. For example, when using NVM to fluently switch environments.
|
||||
Because the target directory is different for each arch, you only have to do
|
||||
`npm rebuild` after the first switch.
|
||||
|
||||
</details>
|
||||
|
||||
|
||||
<details>
|
||||
|
||||
<summary>Include directories</summary>
|
||||
|
||||
```
|
||||
'include_dirs': [
|
||||
'<!@(node -e "require(\'addon-tools-raub\').include()")',
|
||||
],
|
||||
```
|
||||
|
||||
Those are the directory paths to C++ include files for Addon Tools and Nan
|
||||
(which is preinstalled with Addon Tools)
|
||||
|
||||
</details>
|
||||
|
||||
|
||||
<details>
|
||||
|
||||
<summary>Remove intermediates</summary>
|
||||
|
||||
```
|
||||
[ 'OS=="linux"', { 'action' : [
|
||||
'<(rm)',
|
||||
'<(module_root_dir)/build/Release/obj.target/addon/cpp/addon.o',
|
||||
'<(module_root_dir)/build/Release/addon.node'
|
||||
] } ],
|
||||
[ 'OS=="mac"', { 'action' : [
|
||||
'<(rm)',
|
||||
'<(module_root_dir)/build/Release/obj.target/addon/cpp/addon.o',
|
||||
'<(module_root_dir)/build/Release/addon.node'
|
||||
] } ],
|
||||
[ 'OS=="win"', { 'action' : [
|
||||
'<(rm)',
|
||||
'<(module_root_dir)/build/Release/addon.*',
|
||||
'<(module_root_dir)/build/Release/obj/addon/*.*'
|
||||
] } ],
|
||||
```
|
||||
|
||||
Build-files can be removed in a separate build-step with `<(rm)`. Those are
|
||||
usually PDB and OBJ files, which are rather big. However, in case of a hardcore
|
||||
debug session you might want to comment this out.
|
||||
|
||||
</details>
|
||||
|
||||
|
||||
### Binary dependency package
|
||||
|
||||
If you design a module with binary dependencies for several platforms, **Addon Tools**
|
||||
would encourage you to abide by the following rules:
|
||||
|
||||
* Your binary directories are:
|
||||
|
||||
* bin-win32
|
||||
* bin-win64
|
||||
* bin-linux64
|
||||
* bin-mac64
|
||||
|
||||
* The following piece of code in your `index.js` without changes. Method `paths()`
|
||||
is described [here](#indexjs).
|
||||
|
||||
```
|
||||
module.exports = require('addon-tools-raub').paths(__dirname);
|
||||
```
|
||||
|
||||
* Your whole **binding.gyp**:
|
||||
|
||||
<details>
|
||||
|
||||
<summary>Show binding.gyp</summary>
|
||||
|
||||
```
|
||||
{
|
||||
'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
|
||||
|
||||
It is easy to build a C++ addon with **Addon Tools**. To have a full picture, you
|
||||
can view the
|
||||
[official example](https://github.com/node-3d/addon-tools-raub/tree/master/examples/addon).
|
||||
|
||||
The main file for an addon is **binding.gyp**. Here's a snippet with most of the features.
|
||||
|
||||
<details>
|
||||
|
||||
<summary>binding.gyp</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` directory.
|
||||
|
||||
```
|
||||
npm i -s addon-tools-raub
|
||||
{
|
||||
'variables': {
|
||||
'rm' : '<!(node -e "require(\'addon-tools-raub\').rm()")',
|
||||
'cp' : '<!(node -e "require(\'addon-tools-raub\').cp()")',
|
||||
'mkdir' : '<!(node -e "require(\'addon-tools-raub\').mkdir()")',
|
||||
'binary' : '<!(node -e "require(\'addon-tools-raub\').bin()")',
|
||||
'EXT_LIB_include' : '<!(node -e "require(\'deps-EXT_LIB\').include()")',
|
||||
'EXT_LIB_bin' : '<!(node -e "require(\'deps-EXT_LIB\').bin()")',
|
||||
},
|
||||
'targets': [
|
||||
{
|
||||
'target_name': 'MY_ADDON',
|
||||
'sources': [
|
||||
'cpp/MY_ADDON.cpp',
|
||||
],
|
||||
'include_dirs': [
|
||||
'<!(node -e "require(\'addon-tools-raub\').include()")',
|
||||
'<(EXT_LIB_include)',
|
||||
'<(module_root_dir)/include',
|
||||
],
|
||||
'library_dirs': [ '<(EXT_LIB_bin)' ],
|
||||
'conditions': [
|
||||
[
|
||||
'OS=="linux"',
|
||||
{
|
||||
'libraries': [
|
||||
'-Wl,-rpath,<(EXT_LIB_bin)',
|
||||
'<(EXT_LIB_bin)/libEXT_LIB.so',
|
||||
],
|
||||
}
|
||||
],
|
||||
[
|
||||
'OS=="mac"',
|
||||
{
|
||||
'libraries': [
|
||||
'-Wl,-rpath,<(EXT_LIB_bin)',
|
||||
'<(EXT_LIB_bin)/EXT_LIB.dylib',
|
||||
],
|
||||
}
|
||||
],
|
||||
[
|
||||
'OS=="win"',
|
||||
{
|
||||
'libraries': [ 'EXT_LIB.lib' ],
|
||||
'defines' : [
|
||||
'WIN32_LEAN_AND_MEAN',
|
||||
'VC_EXTRALEAN'
|
||||
],
|
||||
'msvs_version' : '2013',
|
||||
'msvs_settings' : {
|
||||
'VCCLCompilerTool' : {
|
||||
'AdditionalOptions' : [
|
||||
'/O2','/Oy', # Comment this for debugging
|
||||
# '/Z7', # Unomment this for debugging
|
||||
'/GL','/GF','/Gm-','/EHsc',
|
||||
'/MT','/GS','/Gy','/GR-','/Gd',
|
||||
]
|
||||
},
|
||||
'VCLinkerTool' : {
|
||||
'AdditionalOptions' : ['/OPT:REF','/OPT:ICF','/LTCG']
|
||||
},
|
||||
},
|
||||
}
|
||||
],
|
||||
],
|
||||
},
|
||||
|
||||
{
|
||||
'target_name' : 'make_directory',
|
||||
'type' : 'none',
|
||||
'dependencies' : ['MY_ADDON'],
|
||||
'actions' : [{
|
||||
'action_name' : 'Directory created.',
|
||||
'inputs' : [],
|
||||
'outputs' : ['build'],
|
||||
'action': ['<(mkdir)', '-p', '<(binary)']
|
||||
}],
|
||||
},
|
||||
{
|
||||
'target_name' : 'copy_binary',
|
||||
'type' : 'none',
|
||||
'dependencies' : ['make_directory'],
|
||||
'actions' : [{
|
||||
'action_name' : 'Module copied.',
|
||||
'inputs' : [],
|
||||
'outputs' : ['binary'],
|
||||
'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/*.*'
|
||||
] } ],
|
||||
],
|
||||
}],
|
||||
},
|
||||
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
Then require the built module like this:
|
||||
|
||||
```
|
||||
const { binPath } = require('addon-tools-raub');
|
||||
const core = require(`./${binPath}/MY_ADDON`);
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
---
|
||||
|
||||
|
||||
## include/addon-tools.hpp
|
||||
|
||||
Macro shortcuts for C++ addons using **NAPI**.
|
||||
See [docs inside the folder](/include).
|
||||
|
||||
Example of an addon method definition:
|
||||
There is a C++ header file, `addon-tools.hpp`, shipped with this package. It
|
||||
introduces several useful macros and utilities. Also it includes Nan automatically,
|
||||
so that you can replace:
|
||||
|
||||
```
|
||||
// hpp:
|
||||
#include <addon-tools.hpp>
|
||||
DBG_EXPORT JS_METHOD(doSomething);
|
||||
// cpp:
|
||||
DBG_EXPORT JS_METHOD(doSomething) { NAPI_ENV;
|
||||
LET_INT32_ARG(0, param0);
|
||||
std::cout << "param0: " << param0 << std::endl;
|
||||
RET_UNDEFINED;
|
||||
// #include <v8.h> // already in node.h
|
||||
// #include <node.h> // already in nan.h
|
||||
#include <nan.h>
|
||||
```
|
||||
|
||||
with
|
||||
|
||||
```
|
||||
#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.
|
||||
As it was mentioned above, this can be done automatically. Also an actual path to the
|
||||
directory is exported from the module and is accessible like this:
|
||||
|
||||
```
|
||||
require('addon-tools-raub').include() // implicit console.log()
|
||||
require('addon-tools-raub').includePath // just a string
|
||||
```
|
||||
|
||||
Currently, there are following helpers in **addon-tools.hpp**:
|
||||
|
||||
|
||||
<details>
|
||||
|
||||
<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;
|
||||
...
|
||||
}
|
||||
...
|
||||
glfwSetWindowFocusCallback(window, windowFocusCB);
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
|
||||
<details>
|
||||
|
||||
<summary>Method return</summary>
|
||||
|
||||
* `RET_VALUE(VAL)` - set method return value, where `VAL` is `v8::Local<v8::Value>`.
|
||||
* `RET_UNDEFINED` - set method return value as `undefined`.
|
||||
* `RET_STR(VAL)` - set method return value, where `VAL` is `const char *`.
|
||||
* `RET_UTF8(VAL)` - set method return value, where `VAL` is `const char *`.
|
||||
* `RET_INT(VAL)` - set method return value, where `VAL` is `int32`.
|
||||
* `RET_INT32(VAL)` - set method return value, where `VAL` is `int32`.
|
||||
* `RET_UINT32(VAL)` - set method return value, where `VAL` is `uint32`.
|
||||
* `RET_NUM(VAL)` - set method return value, where `VAL` is `double`.
|
||||
* `RET_OFFS(VAL)` - set method return value, where `VAL` is `size_t`.
|
||||
* `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>
|
||||
|
||||
<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>
|
||||
|
||||
<summary>New JS value</summary>
|
||||
|
||||
* `JS_STR(...)` - create a string value
|
||||
* `JS_UTF8(...)` - same as JS_STR
|
||||
* `JS_INT(val)` - create an integer value
|
||||
* `JS_INT32(val)` - same as `JS_INT`
|
||||
* `JS_UINT32(val)` - same as `JS_INT`
|
||||
* `JS_NUM(val)` - create a numeric 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>
|
||||
|
||||
<summary>Method check</summary>
|
||||
|
||||
These checks throw JS TypeError if not passed. Here `T` is always used as a typename
|
||||
in error messages. `C` is
|
||||
[v8::Value](https://v8docs.nodesource.com/node-0.8/dc/d0a/classv8_1_1_value.html)
|
||||
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()`.
|
||||
|
||||
</details>
|
||||
|
||||
|
||||
<details>
|
||||
|
||||
<summary>Method arguments</summary>
|
||||
|
||||
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.
|
||||
`I` is the index of argument as in `info[I]`,
|
||||
starting from `0`. `VAR` is the name of the variable to be created.
|
||||
|
||||
* `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) {
|
||||
|
||||
REQ_UINT32_ARG(0, width);
|
||||
REQ_UINT32_ARG(1, height);
|
||||
LET_FLOAT_ARG(2, z);
|
||||
// Variables created: unsigned int width, height; float z;
|
||||
...
|
||||
```
|
||||
|
||||
> 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>
|
||||
|
||||
<summary>Set object accessors</summary>
|
||||
|
||||
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(OBJ, NAME)` - add read and write accessors of NAME for OBJ.
|
||||
* `ACCESSOR_R(OBJ, NAME)` - read-only property.
|
||||
|
||||
```
|
||||
void MyClass::init(Handle<Object> target) {
|
||||
...
|
||||
Local<ObjectTemplate> proto = ctor->PrototypeTemplate();
|
||||
ACCESSOR_RW(proto, message);
|
||||
...
|
||||
}
|
||||
NAN_GETTER(MyClass::messageGetter) { ...
|
||||
NAN_SETTER(MyClass::messageSetter) { ...
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
|
||||
<details>
|
||||
|
||||
<summary>Setter argument</summary>
|
||||
|
||||
Useful addition to NAN_SETTER macro. Works similar to method arguments. But there
|
||||
is always only one required argument stored in `v`.
|
||||
|
||||
* `SETTER_UTF8_ARG` - require the value to be a `string`. Stored at `Nan::Utf8String v`.
|
||||
* `SETTER_STR_ARG` - require the value to be a `string`. Stored at `Nan::Utf8String v`.
|
||||
* `SETTER_INT32_ARG` - require the value to be a `number`. Stored at `int v`.
|
||||
* `SETTER_INT_ARG` - require the value to be a `number`. Stored at `int v`.
|
||||
* `SETTER_UINT32_ARG` - require the value to be a `number`. Stored at `unsigned v`.
|
||||
* `SETTER_BOOL_ARG` - require the value to be a `boolean`. Stored at `bool v`.
|
||||
* `SETTER_OFFS_ARG` - require the value to be a `number`. Stored at `size_t v`.
|
||||
* `SETTER_DOUBLE_ARG` - require the value to be a `number`. Stored at `double v`.
|
||||
* `SETTER_FLOAT_ARG` - require the value to be a `number`. Stored at `float v`.
|
||||
* `SETTER_EXT_ARG` - require the value to be an `external`. Stored at `Local<External> v`.
|
||||
* `SETTER_FUN_ARG` - require the value to be a `function`. Stored at `Local<Function> v`.
|
||||
* `SETTER_OBJ_ARG` - require the value to be an `object`. Stored at `Local<Object> v`.
|
||||
* `SETTER_ARRV_ARG` - require the value to be a `TypedArray`. Stored at `Local<ArrayBufferView> v`.
|
||||
|
||||
```
|
||||
NAN_SETTER(MyClass::messageSetter) { SETTER_UTF8_ARG;
|
||||
// Variable created: Nan::Utf8String v;
|
||||
...
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
|
||||
<details>
|
||||
|
||||
<summary>Data retrieval</summary>
|
||||
|
||||
* `T *getArrayData(value, num = NULL)` - extracts TypedArray data of any type from
|
||||
the given JS value. Does not accept Array, checked with `IsArrayBufferView()`.
|
||||
Returns `NULL` 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.
|
||||
|
||||
</details>
|
||||
|
||||
---
|
||||
|
||||
|
||||
## index.js
|
||||
|
||||
JavaScript helpers for Node.js addon development. The short list of helpers:
|
||||
Exports:
|
||||
* `paths(dir)` - function. Returns a set of platform dependent paths depending on
|
||||
input `dir`.
|
||||
* `bin()` - prints platform binary directory absolute path.
|
||||
* `rem()` - prints a space-separated list of binary paths to be cleaned on this platform.
|
||||
* `include()` - prints include directory for this `dir`.
|
||||
* `binPath` - platform binary directory absolute path.
|
||||
* `remPath` - a space-separated list of binary paths to be cleaned on this platform.
|
||||
* `includePath` - include directory for this `dir`.
|
||||
* `root()` - prints where `'addon-tools-raub'` module is situated.
|
||||
* `include()` - prints both `'addon-tools-raub'` and `'nan'` include paths. Use with
|
||||
`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
|
||||
|
||||
On Unix, it will be an actual system `mkdir`, whereas on Windows it will use the
|
||||
**mkdir.bat** file, located at the root of this package. This BAT file behaves
|
||||
as if it was a `mkdir -p ...` call. You can still pass `-p` switch, which is
|
||||
ignored. And the limitation is that you can not create a relative-path **-p**
|
||||
folder. This can possibly be bypassed by supplying `./-p` or something like this.
|
||||
|
||||
|
||||
```
|
||||
'getBin', 'getPlatform', 'getInclude', 'getPaths',
|
||||
'install', 'cpbin', 'download', 'read', 'write', 'copy', 'exists',
|
||||
'mkdir', 'stat', 'isDir', 'isFile', 'dirUp', 'ensuredir', 'copysafe',
|
||||
'readdir', 'subdirs', 'subfiles', 'traverse', 'copyall',
|
||||
'rmdir', 'rm', 'WritableBuffer', 'actionPack',
|
||||
'variables': {
|
||||
'mkdir' : '<!(node -e "require(\'addon-tools-raub\').mkdir()")',
|
||||
},
|
||||
...
|
||||
'action' : ['<(mkdir)', '-p', 'binary'],
|
||||
```
|
||||
|
||||
|
||||
See the [TypeScript definitions](/index.d.ts) with comments.
|
||||
### rm
|
||||
|
||||
|
||||
### Example for an ADDON's **index.js**:
|
||||
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.
|
||||
|
||||
```
|
||||
const { getBin } = require('addon-tools-raub');
|
||||
const core = require(`./${getBin()}/ADDON`); // uses the platform-specific ADDON.node
|
||||
'variables': {
|
||||
'rm' : '<!(node -e "require(\'addon-tools-raub\').rm()")',
|
||||
'rem' : '<!(node -e "require(\'.\').rem()")',
|
||||
},
|
||||
...
|
||||
'action' : ['<(rm)', '-rf', '<@(rem)'],
|
||||
```
|
||||
|
||||
### cp
|
||||
|
||||
### Example for **binding.gyp**:
|
||||
For Windows the `/y` flag was embedded.
|
||||
|
||||
```
|
||||
'variables': {
|
||||
'cp' : '<!(node -e "require(\'addon-tools-raub\').cp()")',
|
||||
},
|
||||
...
|
||||
'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 -p "require(\'addon-tools-raub\').getInclude()")',
|
||||
'<!@(node -e "require(\'addon-tools-raub\').include()")',
|
||||
],
|
||||
```
|
||||
|
||||
> NOTE: the optional `node-addon-api` dependency is used by the `getInclude()` helper. If not found,
|
||||
the **napi.h** include path won't be a part of the returned string.
|
||||
|
||||
|
||||
### Example of `cpbin` in **package.json :: scripts**:
|
||||
Then include the **event-emitter.hpp**, it also includes **addon-tools.hpp**.
|
||||
Inherit from `EventEmitter`, it already inherits from `Nan::ObjectWrap`:
|
||||
|
||||
```
|
||||
"build": "cd src && node-gyp rebuild -j max --silent && node -e \"require('addon-tools-raub').cpbin('segfault')\" && cd ..",
|
||||
"build-only": "cd src && node-gyp build -j max --silent && node -e \"require('addon-tools-raub').cpbin('segfault')\" && cd ..",
|
||||
#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
|
||||
|
||||
In C++ addons, the use of **iostream** is discouraged because **Node.js** has its own
|
||||
perspective on **stdout** behavior.
|
||||
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
|
||||
broken really hard in your addon.
|
||||
|
||||
To overcome this, we can use some V8 `eval` magic to make a real `console.log`
|
||||
call from C++ land. And this is where `consoleLog` comes into play.
|
||||
|
||||
* `inline void consoleLog(int argc, V8_VAR_VAL *argv)` - a generic logger,
|
||||
receives any set of arguments.
|
||||
|
||||
* `inline void consoleLog(const std::string &message)` - an alias to log a single
|
||||
string.
|
||||
|
||||
> Note: Don't do it in GC-accessible code: sometimes it works, sometimes it crashes.
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
for %%x in (%*) do (
|
||||
|
||||
if not %%x=="-p" if not exist %%x md %%x
|
||||
|
||||
)
|
|
@ -0,0 +1,6 @@
|
|||
for %%x in (%*) do (
|
||||
|
||||
if not %%x=="-rf" if not %%x=="-r" if not %%x=="-f" if exist %%~x\ rd /s /q %%x
|
||||
if not %%x=="-rf" if not %%x=="-r" if not %%x=="-f" if not exist %%~x\ if exist %%x del /f /q %%x
|
||||
|
||||
)
|
|
@ -0,0 +1,116 @@
|
|||
{
|
||||
"root": true,
|
||||
"env": {
|
||||
"node" : true,
|
||||
"es6" : true,
|
||||
"mocha" : true
|
||||
},
|
||||
"globals": {
|
||||
"expect" : true,
|
||||
"chai" : true,
|
||||
"sinon" : true
|
||||
},
|
||||
"extends": ["eslint:recommended"],
|
||||
"parserOptions": {
|
||||
"ecmaVersion": 8,
|
||||
"ecmaFeatures": {
|
||||
"experimentalObjectRestSpread": true
|
||||
}
|
||||
},
|
||||
"rules": {
|
||||
"arrow-parens": ["error", "as-needed"],
|
||||
"no-trailing-spaces": [
|
||||
"error",
|
||||
{
|
||||
"skipBlankLines": true
|
||||
}
|
||||
],
|
||||
"indent": [
|
||||
"error",
|
||||
"tab",
|
||||
{
|
||||
"SwitchCase": 1
|
||||
}
|
||||
],
|
||||
"linebreak-style": [
|
||||
"error",
|
||||
"unix"
|
||||
],
|
||||
"max-len": ["error", 110],
|
||||
"quotes": [
|
||||
"error",
|
||||
"single"
|
||||
],
|
||||
"semi": [
|
||||
"error",
|
||||
"always"
|
||||
],
|
||||
"no-multiple-empty-lines": ["error", { "max": 3, "maxEOF": 1, "maxBOF": 1 }],
|
||||
"keyword-spacing": ["error", { "before": true, "after": true }],
|
||||
"space-before-blocks": ["error"],
|
||||
"space-before-function-paren": ["error", {"anonymous": "always", "named": "never", "asyncArrow": "always"}],
|
||||
"space-infix-ops": ["error"],
|
||||
"space-unary-ops": [
|
||||
"error", {
|
||||
"words": true,
|
||||
"nonwords": false,
|
||||
"overrides": {
|
||||
"!": true
|
||||
}
|
||||
}
|
||||
],
|
||||
"spaced-comment": [0],
|
||||
"camelcase": ["error"],
|
||||
"no-tabs": [0],
|
||||
"comma-dangle": [0],
|
||||
"global-require": [0],
|
||||
"func-names": [0],
|
||||
"no-param-reassign": [0],
|
||||
"no-underscore-dangle": [0],
|
||||
"no-restricted-syntax": [
|
||||
"error",
|
||||
{
|
||||
"selector": "LabeledStatement",
|
||||
"message": "Labels are a form of GOTO; using them makes code confusing and hard to maintain and understand."
|
||||
},
|
||||
{
|
||||
"selector": "WithStatement",
|
||||
"message": "`with` is disallowed in strict mode because it makes code impossible to predict and optimize."
|
||||
}
|
||||
],
|
||||
"no-mixed-operators": [0],
|
||||
"no-plusplus": [0],
|
||||
"comma-spacing": [0],
|
||||
"default-case": [0],
|
||||
"no-shadow": [0],
|
||||
"no-console": [0],
|
||||
"key-spacing": [0],
|
||||
"no-return-assign": [0],
|
||||
"consistent-return": [0],
|
||||
"class-methods-use-this": [0],
|
||||
"no-multi-spaces": [
|
||||
"error",
|
||||
{
|
||||
"exceptions": {
|
||||
"VariableDeclarator": true,
|
||||
"Property": true,
|
||||
"ImportDeclaration": true
|
||||
}
|
||||
}
|
||||
],
|
||||
"array-callback-return": [0],
|
||||
"no-use-before-define": [
|
||||
"error",
|
||||
{
|
||||
"functions": false,
|
||||
"classes": true,
|
||||
"variables": true
|
||||
}
|
||||
],
|
||||
"padded-blocks": [0],
|
||||
"space-in-parens": [0],
|
||||
"valid-jsdoc": [0],
|
||||
"no-unused-expressions": [0],
|
||||
"import/no-dynamic-require": [0]
|
||||
}
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
.idea
|
||||
.cproject
|
||||
.project
|
||||
.lock-wscript
|
||||
build*/
|
||||
bin-*/
|
||||
.DS_Store
|
||||
Debug/
|
||||
node_modules/
|
||||
package-lock.json
|
||||
binary/
|
||||
*.log
|
|
@ -0,0 +1,16 @@
|
|||
*.log
|
||||
.cproject
|
||||
.eslintrc
|
||||
.gitignore
|
||||
.idea
|
||||
.lock-wscript
|
||||
.project
|
||||
binary/
|
||||
bin-*/
|
||||
build*/
|
||||
CPPLINT.cfg
|
||||
Debug/
|
||||
examples/
|
||||
package-lock.json
|
||||
test/
|
||||
qt/
|
|
@ -0,0 +1,15 @@
|
|||
set noparent
|
||||
linelength=110
|
||||
filter=-legal/copyright
|
||||
filter=-build/include_order
|
||||
filter=-build/header_guard
|
||||
filter=-build/namespaces
|
||||
filter=-build/include_what_you_use
|
||||
filter=-whitespace/blank_line
|
||||
filter=-whitespace/comments
|
||||
filter=-whitespace/tab
|
||||
filter=-whitespace/end_of_line
|
||||
filter=-whitespace/indent
|
||||
filter=-whitespace/operators
|
||||
filter=-whitespace/parens
|
||||
filter=-readability/todo
|
|
@ -0,0 +1,91 @@
|
|||
{
|
||||
'variables': {
|
||||
'rm' : '<!(node -e "require(\'addon-tools-raub\').rm()")',
|
||||
'cp' : '<!(node -e "require(\'addon-tools-raub\').cp()")',
|
||||
'mkdir' : '<!(node -e "require(\'addon-tools-raub\').mkdir()")',
|
||||
'binary' : '<!(node -e "require(\'addon-tools-raub\').bin()")',
|
||||
},
|
||||
'targets': [
|
||||
{
|
||||
'target_name': 'addon',
|
||||
'sources': [
|
||||
'cpp/bindings.cpp',
|
||||
'cpp/example.cpp',
|
||||
],
|
||||
'include_dirs': [
|
||||
'<!@(node -e "require(\'addon-tools-raub\').include()")',
|
||||
],
|
||||
'conditions' : [
|
||||
[
|
||||
'OS=="win"',
|
||||
{
|
||||
'msvs_settings' : {
|
||||
'VCCLCompilerTool' : {
|
||||
'AdditionalOptions' : [
|
||||
'/O2','/Oy', # Comment this for debugging
|
||||
# '/Z7', # Unomment this for debugging
|
||||
'/GL','/GF','/Gm-', '/Fm-',
|
||||
'/EHsc','/MT','/GS','/Gy','/GR-','/Gd',
|
||||
]
|
||||
},
|
||||
'VCLinkerTool' : {
|
||||
'AdditionalOptions' : ['/RELEASE','/OPT:REF','/OPT:ICF','/LTCG']
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
],
|
||||
},
|
||||
{
|
||||
'target_name' : 'make_directory',
|
||||
'type' : 'none',
|
||||
'dependencies' : ['addon'],
|
||||
'actions' : [{
|
||||
'action_name' : 'Directory created.',
|
||||
'inputs' : [],
|
||||
'outputs' : ['build'],
|
||||
'action': ['<(mkdir)', '-p', '<(binary)']
|
||||
}],
|
||||
},
|
||||
{
|
||||
'target_name' : 'copy_binary',
|
||||
'type' : 'none',
|
||||
'dependencies' : ['make_directory'],
|
||||
'actions' : [{
|
||||
'action_name' : 'Module copied.',
|
||||
'inputs' : [],
|
||||
'outputs' : ['binary'],
|
||||
'action' : ['<(cp)', 'build/Release/addon.node', '<(binary)/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/addon/cpp/bindings.o',
|
||||
'<(module_root_dir)/build/Release/obj.target/addon/cpp/example.o',
|
||||
'<(module_root_dir)/build/Release/addon.node'
|
||||
] } ],
|
||||
[ 'OS=="mac"', { 'action' : [
|
||||
'rm',
|
||||
'<(module_root_dir)/build/Release/obj.target/addon/cpp/bindings.o',
|
||||
'<(module_root_dir)/build/Release/obj.target/addon/cpp/example.o',
|
||||
'<(module_root_dir)/build/Release/addon.node'
|
||||
] } ],
|
||||
[ 'OS=="win"', { 'action' : [
|
||||
'<(rm)',
|
||||
'<(module_root_dir)/build/Release/addon.*',
|
||||
'<(module_root_dir)/build/Release/obj/addon/*.*'
|
||||
] } ],
|
||||
],
|
||||
}],
|
||||
},
|
||||
]
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
'use strict';
|
||||
|
||||
const util = require('util');
|
||||
|
||||
const { binPath } = require('addon-tools-raub');
|
||||
|
||||
const core = require(`./${binPath}/addon`);
|
||||
|
||||
|
||||
const { Example } = core;
|
||||
|
||||
Example.prototype[util.inspect.custom] = function () {
|
||||
return `Example { listeners: [${this.eventNames()}] }`;
|
||||
};
|
||||
|
||||
|
||||
module.exports = core;
|
|
@ -0,0 +1,23 @@
|
|||
#include <cstdlib>
|
||||
|
||||
#include <event-emitter.hpp>
|
||||
|
||||
#include "example.hpp"
|
||||
|
||||
|
||||
extern "C" {
|
||||
|
||||
|
||||
void init(V8_VAR_OBJ target) {
|
||||
|
||||
EventEmitter::init(target);
|
||||
|
||||
Example::init(target);
|
||||
|
||||
}
|
||||
|
||||
|
||||
NODE_MODULE(example, init);
|
||||
|
||||
|
||||
} // extern "C"
|
|
@ -0,0 +1,105 @@
|
|||
#include <cstdlib>
|
||||
|
||||
#include "example.hpp"
|
||||
|
||||
|
||||
using namespace v8;
|
||||
using namespace node;
|
||||
using namespace std;
|
||||
|
||||
|
||||
// ------ Aux macros
|
||||
|
||||
#define THIS_EXAMPLE \
|
||||
Example *example = ObjectWrap::Unwrap<Example>(info.This());
|
||||
|
||||
#define THIS_CHECK \
|
||||
if (example->_isDestroyed) return;
|
||||
|
||||
|
||||
// ------ Constructor and Destructor
|
||||
|
||||
Example::Example() : EventEmitter() {
|
||||
|
||||
_isDestroyed = false;
|
||||
|
||||
}
|
||||
|
||||
|
||||
Example::~Example() {
|
||||
|
||||
_destroy();
|
||||
|
||||
}
|
||||
|
||||
|
||||
NAN_METHOD(Example::cppOn) { THIS_EXAMPLE; THIS_CHECK;
|
||||
|
||||
REQ_STR_ARG(0, name);
|
||||
REQ_FUN_ARG(1, cb);
|
||||
|
||||
example->on(*name, cb);
|
||||
|
||||
}
|
||||
|
||||
|
||||
// ------ System methods and props for ObjectWrap
|
||||
|
||||
V8_STORE_FT Example::_protoExample;
|
||||
V8_STORE_FUNC Example::_ctorExample;
|
||||
|
||||
|
||||
void Example::init(V8_VAR_OBJ target) {
|
||||
|
||||
V8_VAR_FT proto = Nan::New<FunctionTemplate>(newCtor);
|
||||
|
||||
// class AudioBufferSourceNode inherits AudioScheduledSourceNode
|
||||
V8_VAR_FT parent = Nan::New(EventEmitter::_protoEventEmitter);
|
||||
proto->Inherit(parent);
|
||||
|
||||
proto->InstanceTemplate()->SetInternalFieldCount(1);
|
||||
proto->SetClassName(JS_STR("Example"));
|
||||
|
||||
// -------- dynamic
|
||||
Nan::SetPrototypeMethod(proto, "destroy", destroy);
|
||||
Nan::SetPrototypeMethod(proto, "cppOn", cppOn);
|
||||
|
||||
// -------- static
|
||||
V8_VAR_FUNC ctor = Nan::GetFunction(proto).ToLocalChecked();
|
||||
|
||||
_protoExample.Reset(proto);
|
||||
_ctorExample.Reset(ctor);
|
||||
|
||||
Nan::Set(target, JS_STR("Example"), ctor);
|
||||
|
||||
}
|
||||
|
||||
|
||||
NAN_METHOD(Example::newCtor) {
|
||||
|
||||
CTOR_CHECK("EventEmitter");
|
||||
|
||||
Example *example = new Example();
|
||||
example->Wrap(info.This());
|
||||
|
||||
RET_VALUE(info.This());
|
||||
|
||||
}
|
||||
|
||||
|
||||
void Example::_destroy() { DES_CHECK;
|
||||
|
||||
_isDestroyed = true;
|
||||
|
||||
EventEmitter::_destroy();
|
||||
|
||||
}
|
||||
|
||||
|
||||
NAN_METHOD(Example::destroy) { THIS_EXAMPLE; THIS_CHECK;
|
||||
|
||||
example->emit("destroy");
|
||||
|
||||
example->_destroy();
|
||||
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
#ifndef _EXAMPLE_HPP_
|
||||
#define _EXAMPLE_HPP_
|
||||
|
||||
|
||||
#include <event-emitter.hpp>
|
||||
|
||||
|
||||
class Example : public EventEmitter {
|
||||
|
||||
public:
|
||||
|
||||
~Example();
|
||||
|
||||
static void init(V8_VAR_OBJ target);
|
||||
|
||||
|
||||
protected:
|
||||
|
||||
Example();
|
||||
|
||||
void _destroy();
|
||||
|
||||
static V8_STORE_FT _protoExample;
|
||||
static V8_STORE_FUNC _ctorExample;
|
||||
|
||||
bool _isDestroyed;
|
||||
|
||||
|
||||
private:
|
||||
|
||||
static NAN_METHOD(newCtor);
|
||||
static NAN_METHOD(destroy);
|
||||
|
||||
static NAN_METHOD(cppOn);
|
||||
|
||||
};
|
||||
|
||||
|
||||
#endif // _EXAMPLE_HPP_
|
|
@ -0,0 +1,65 @@
|
|||
'use strict';
|
||||
|
||||
const { Example, EventEmitter } = require('./core');
|
||||
|
||||
console.log('Example', Example);
|
||||
|
||||
|
||||
const example = new Example();
|
||||
|
||||
console.log('example 0', example, 'instanceof EventEmitter', example instanceof EventEmitter);
|
||||
|
||||
console.log('static listenerCount', EventEmitter.listenerCount);
|
||||
|
||||
console.log('listenerCount', example.listenerCount);
|
||||
console.log('addListener', example.addListener);
|
||||
console.log('emit', example.emit);
|
||||
console.log('eventNames', example.eventNames);
|
||||
console.log('getMaxListeners', example.getMaxListeners);
|
||||
console.log('listeners', example.listeners);
|
||||
console.log('on', example.on);
|
||||
console.log('once', example.once);
|
||||
console.log('prependListener', example.prependListener);
|
||||
console.log('prependOnceListener', example.prependOnceListener);
|
||||
console.log('removeAllListeners', example.removeAllListeners);
|
||||
console.log('removeListener', example.removeListener);
|
||||
console.log('setMaxListeners', example.setMaxListeners);
|
||||
console.log('rawListeners', example.rawListeners);
|
||||
console.log('destroy', example.destroy);
|
||||
|
||||
|
||||
example.on('evt1', (arg1, arg2) => {
|
||||
console.log('EVT1', arg1, arg2, example.eventNames());
|
||||
});
|
||||
|
||||
example.once('evt2', (arg1, arg2) => {
|
||||
console.log('EVT2', arg1, arg2, example.eventNames());
|
||||
});
|
||||
|
||||
|
||||
example.emit('evt1', 111, '221');
|
||||
example.emit('evt1', 112, '222');
|
||||
|
||||
console.log('example.eventNames 1', example.eventNames());
|
||||
|
||||
example.emit('evt2', 111, '221');
|
||||
|
||||
console.log('example.eventNames 2', example.eventNames());
|
||||
|
||||
example.emit('evt2', 112, '222');
|
||||
|
||||
|
||||
console.log('example 1', example);
|
||||
|
||||
|
||||
example.setMaxListeners(2);
|
||||
example.on('max1', () => {});
|
||||
example.on('max1', () => {});
|
||||
example.on('max1', () => {});
|
||||
|
||||
example.on('cpp-on', (arg1, arg2) => {
|
||||
console.log('CPP_ON', arg1, arg2, example.eventNames());
|
||||
});
|
||||
example.emit('cpp-on', 555, 'abc');
|
||||
|
||||
module.exports = Example;
|
|
@ -0,0 +1,9 @@
|
|||
{
|
||||
"name": "example",
|
||||
"version": "0.0.0",
|
||||
"private": true,
|
||||
"main": "index.js",
|
||||
"dependencies": {
|
||||
"addon-tools-raub": "https://github.com/node-3d/addon-tools-raub.git"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
node_modules
|
||||
*.log
|
||||
build*
|
||||
.DS_Store
|
||||
*.pro.user
|
||||
*.exp
|
||||
*.pdb
|
||||
*.ilk
|
||||
package-lock.json
|
|
@ -0,0 +1,11 @@
|
|||
node_modules
|
||||
*.log
|
||||
build*
|
||||
.DS_Store
|
||||
*.pro.user
|
||||
*.exp
|
||||
*.pdb
|
||||
*.ilk
|
||||
.gitignore
|
||||
package-lock.json
|
||||
test
|
|
@ -0,0 +1,21 @@
|
|||
{
|
||||
'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)'],
|
||||
}
|
||||
]}]],
|
||||
}
|
||||
]
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
'use strict';
|
||||
|
||||
module.exports = require('addon-tools-raub').paths(__dirname);
|
|
@ -0,0 +1,9 @@
|
|||
{
|
||||
"name": "example",
|
||||
"version": "0.0.0",
|
||||
"private": true,
|
||||
"main": "index.js",
|
||||
"dependencies": {
|
||||
"addon-tools-raub": "https://github.com/node-3d/addon-tools-raub.git"
|
||||
}
|
||||
}
|
|
@ -1,240 +0,0 @@
|
|||
# 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**
|
||||
implicitly, so you can replace:
|
||||
|
||||
```
|
||||
#include <napi.h>
|
||||
```
|
||||
with
|
||||
```
|
||||
#include <addon-tools.hpp>
|
||||
```
|
||||
|
||||
In **GYP**, the include directory should be set for your addon.
|
||||
An actual path to the directory is exported from the module
|
||||
and is accessible with:
|
||||
```
|
||||
require('addon-tools-raub').getInclude() // a string
|
||||
```
|
||||
|
||||
For more examples, see [code snippets here](snippets.md).
|
||||
|
||||
|
||||
### ES5 Classes
|
||||
|
||||
The standard class-defining (i.e. exporting a JS class from the C++ side) tools
|
||||
from **NAPI** are a bit repetitive and excessive. So instead, Addon Tools
|
||||
comes with a set of helpers for old-school class definition.
|
||||
Think of it as ES5 classes. Just a function, spawning instances that have their
|
||||
constructor and prototype set accordingly. Such classes may be further extended
|
||||
and/or manipulated from JS-land. See the [class-wrapping doc here](class-wrapping.md).
|
||||
|
||||
|
||||
### Method Helpers
|
||||
|
||||
Usually all the helpers work within the context of a method. In this case we
|
||||
have `Napi::CallbackInfo info` passed as an argument. And we can return `undefined`
|
||||
in case a problem has occured. So most of these macros are only usable
|
||||
within `Napi::Value`-returning functions.
|
||||
|
||||
```
|
||||
#define NAPI_ENV Napi::Env env = info.Env();
|
||||
#define NAPI_HS Napi::HandleScope scope(env);
|
||||
```
|
||||
|
||||
Other global helpers:
|
||||
* `DBG_EXPORT`- set symbol visibility (mainly for callstack traces). On Windows, that is
|
||||
equal to exporting a symbol: `__declspec(dllexport)`. On Unix it does nothing.
|
||||
|
||||
<details>
|
||||
|
||||
<summary><b>Return value</b></summary>
|
||||
|
||||
* `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 of numeric type.
|
||||
* `RET_EXT(VAL)` - return `Napi::External`, expected `VAL` is a pointer.
|
||||
* `RET_BOOL(VAL)` - return `Napi::Boolean`, expected `VAL` is convertible to bool.
|
||||
* `RET_ARRAY_STR(VAL)` - return `Napi::Array`, expected `VAL` is `std::vector<std::string>`.
|
||||
|
||||
</details>
|
||||
|
||||
|
||||
|
||||
<details>
|
||||
|
||||
<summary><b>New JS value</b></summary>
|
||||
|
||||
* `JS_UNDEFINED` - an `undefined` value.
|
||||
* `JS_NULL` - a `null` value.
|
||||
* `JS_STR(VAL)` - create a `Napi::String`, expected `VAL` is `const char *`.
|
||||
* `JS_NUM(VAL)` - create a `Napi::Number`, expected `VAL` is of numeric type.
|
||||
* `JS_EXT(VAL)` - create a `Napi::External`, expected `VAL` is a pointer.
|
||||
* `JS_BOOL(VAL)` - create a `Napi::Boolean`, expected `VAL` is convertible to bool.
|
||||
* `JS_OBJECT` - a new empty `Object` instance.
|
||||
* `JS_ARRAY` - a new empty `Array` instance.
|
||||
|
||||
</details>
|
||||
|
||||
|
||||
<details>
|
||||
|
||||
<summary><b>Method check</b></summary>
|
||||
|
||||
These checks throw JS `TypeError` if not passed. `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)
|
||||
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.
|
||||
* `SETTER_CHECK(C, T)` - check if setter `value` is approved by `C` check.
|
||||
* `DES_CHECK` - for void-returning methods, check if the instance wasn't
|
||||
destroyed by `destroy()`.
|
||||
* `THIS_CHECK` - check if the instance wasn't
|
||||
destroyed by `destroy()`, and then fetch `env`.
|
||||
|
||||
</details>
|
||||
|
||||
|
||||
<details>
|
||||
|
||||
<summary><b>Method arguments</b></summary>
|
||||
|
||||
Following macros convert JS arguments into C++ variables.
|
||||
Three types of argument retrieval are supported:
|
||||
* `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.
|
||||
* `SOFT_` - 2 params, is `LET_` without type and arity checks.
|
||||
|
||||
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:
|
||||
|
||||
| Macro | JS type | C++ type | Default |
|
||||
| :--- | :---: | :---: | :---: |
|
||||
| `REQ_STR_ARG` | `string` | `std::string` | - |
|
||||
| `USE_STR_ARG` | `string` | `std::string` | - |
|
||||
| `LET_STR_ARG` | `string` | `std::string` | `""` |
|
||||
| `REQ_INT32_ARG` | `number` | `int32_t` | - |
|
||||
| `USE_INT32_ARG` | `number` | `int32_t` | - |
|
||||
| `LET_INT32_ARG` | `number` | `int32_t` | `0` |
|
||||
| `REQ_INT_ARG` | `number` | `int32_t` | - |
|
||||
| `USE_INT_ARG` | `number` | `int32_t` | - |
|
||||
| `LET_INT_ARG` | `number` | `int32_t` | `0` |
|
||||
| `REQ_UINT32_ARG` | `number` | `uint32_t` | - |
|
||||
| `USE_UINT32_ARG` | `number` | `uint32_t` | - |
|
||||
| `LET_UINT32_ARG` | `number` | `uint32_t` | `0` |
|
||||
| `REQ_UINT_ARG` | `number` | `uint32_t` | - |
|
||||
| `USE_UINT_ARG` | `number` | `uint32_t` | - |
|
||||
| `LET_UINT_ARG` | `number` | `uint32_t` | `0` |
|
||||
| `REQ_BOOL_ARG` | `Boolean` | `bool` | - |
|
||||
| `USE_BOOL_ARG` | `Boolean` | `bool` | - |
|
||||
| `LET_BOOL_ARG` | `Boolean` | `bool` | `false` |
|
||||
| `SOFT_BOOL_ARG` | `Boolean` | `bool` | `false` |
|
||||
| `REQ_OFFS_ARG` | `number` | `size_t` | - |
|
||||
| `USE_OFFS_ARG` | `number` | `size_t` | - |
|
||||
| `LET_OFFS_ARG` | `number` | `size_t` | `0` |
|
||||
| `REQ_DOUBLE_ARG` | `number` | `double` | - |
|
||||
| `USE_DOUBLE_ARG` | `number` | `double` | - |
|
||||
| `LET_DOUBLE_ARG` | `number` | `double` | `0.0` |
|
||||
| `REQ_FLOAT_ARG` | `number` | `float` | - |
|
||||
| `USE_FLOAT_ARG` | `number` | `float` | - |
|
||||
| `LET_FLOAT_ARG` | `number` | `float` | `0.f` |
|
||||
| `REQ_EXT_ARG` | `native` | `void*` | - |
|
||||
| `USE_EXT_ARG` | `native` | `void*` | - |
|
||||
| `LET_EXT_ARG` | `native` | `void*` | `nullptr` |
|
||||
| `REQ_OBJ_ARG` | `object` | `Napi::Object` | - |
|
||||
| `USE_OBJ_ARG` | `object` | `Napi::Object` | - |
|
||||
| `LET_OBJ_ARG` | `object` | `Napi::Object` | `{}` |
|
||||
| `REQ_ARRAY_ARG` | `object` | `Napi::Array` | - |
|
||||
| `USE_ARRAY_ARG` | `object` | `Napi::Array` | - |
|
||||
| `LET_ARRAY_ARG` | `object` | `Napi::Array` | `[]` |
|
||||
| `LET_ARRAY_STR_ARG` | `object` | `std::vector<std::string>` | `std::vector<std::string>()` |
|
||||
| `REQ_FUN_ARG` | `function` | `Napi::Function` | - |
|
||||
| `REQ_ARRV_ARG` | `ArrayBuffer` | `Napi::ArrayBuffer` | - |
|
||||
| `REQ_BUF_ARG` | `Buffer` | `Napi::Buffer<uint8_t>` | - |
|
||||
|
||||
|
||||
```
|
||||
JS_METHOD(test) {
|
||||
REQ_UINT32_ARG(0, width); // uint32_t width
|
||||
REQ_UINT32_ARG(1, height); // uint32_t height
|
||||
LET_FLOAT_ARG(2, z); // float z
|
||||
// An error is thrown if width or height are not passed as numbers.
|
||||
// Argument z can be undefined, null, or number; error otherwise.
|
||||
...
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
|
||||
<details>
|
||||
|
||||
<summary><b>Setter argument</b></summary>
|
||||
|
||||
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_IMPLEMENT_SETTER(MyClass, x) { THIS_CHECK; SETTER_STR_ARG;
|
||||
// Variable created: std::string v;
|
||||
...
|
||||
```
|
||||
|
||||
See also: [Class Wrapping](class-wrapping.md)
|
||||
|
||||
</details>
|
||||
|
||||
|
||||
<details>
|
||||
|
||||
<summary><b>JS Data to C++ Data</b></summary>
|
||||
|
||||
* `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.
|
||||
|
||||
* `T *getBufferData(value, num = NULL)` - extracts Buffer data from
|
||||
the given JS value. Checks with `IsBuffer()`.
|
||||
Returns `nullptr` for empty JS values. For unacceptable values throws TypeError.
|
||||
|
||||
* `void *getData(value)` - if `value` is a `TypedArray|Buffer`,
|
||||
calls `getArrayData` or `getArrayData` on it. Otherwise, if
|
||||
`value.data` is a `TypedArray|Buffer`,
|
||||
calls `getArrayData` or `getArrayData` on it.
|
||||
Returns `nullptr` in other cases.
|
||||
|
||||
</details>
|
|
@ -1,669 +1,317 @@
|
|||
#ifndef ADDON_TOOLS_HPP
|
||||
#define ADDON_TOOLS_HPP
|
||||
|
||||
#define NODE_ADDON_API_DISABLE_DEPRECATED
|
||||
#define NAPI_DISABLE_CPP_EXCEPTIONS
|
||||
#include <napi.h>
|
||||
#ifndef _ADDON_TOOLS_HPP_
|
||||
#define _ADDON_TOOLS_HPP_
|
||||
|
||||
|
||||
#ifdef _WIN32
|
||||
#define strcasestr(s, t) strstr(strupr(s), strupr(t))
|
||||
#endif
|
||||
#include <nan.h>
|
||||
|
||||
|
||||
#ifdef _WIN32
|
||||
#define DBG_EXPORT __declspec(dllexport)
|
||||
#else
|
||||
#define DBG_EXPORT
|
||||
#endif
|
||||
|
||||
#define NAPI_ENV Napi::Env env = info.Env();
|
||||
#define NAPI_HS Napi::HandleScope scope(env);
|
||||
#define NAN_HS Nan::HandleScope scope;
|
||||
|
||||
|
||||
#define JS_UNDEFINED env.Undefined()
|
||||
#define JS_NULL env.Null()
|
||||
#define JS_STR(VAL) Napi::String::New(env, VAL)
|
||||
#define JS_NUM(VAL) Napi::Number::New(env, static_cast<double>(VAL))
|
||||
#define JS_EXT(VAL) Napi::External<void>::New(env, static_cast<void*>(VAL))
|
||||
#define JS_BOOL(VAL) Napi::Boolean::New(env, static_cast<bool>(VAL))
|
||||
#define JS_OBJECT Napi::Object::New(env)
|
||||
#define JS_ARRAY Napi::Array::New(env)
|
||||
|
||||
#define RET_VALUE(VAL) return VAL;
|
||||
#define RET_UNDEFINED RET_VALUE(JS_UNDEFINED)
|
||||
#define RET_NULL RET_VALUE(JS_NULL)
|
||||
#define RET_STR(VAL) RET_VALUE(JS_STR(VAL))
|
||||
#define RET_NUM(VAL) RET_VALUE(JS_NUM(VAL))
|
||||
#define RET_EXT(VAL) RET_VALUE(JS_EXT(VAL))
|
||||
#define RET_BOOL(VAL) RET_VALUE(JS_BOOL(VAL))
|
||||
#define RET_VALUE(VAL) info.GetReturnValue().Set(VAL);
|
||||
#define RET_UNDEFINED RET_VALUE(Nan::Undefined());
|
||||
|
||||
|
||||
#define JS_THROW(VAL) \
|
||||
Napi::Error::New(env, VAL).ThrowAsJavaScriptException();
|
||||
typedef v8::Local<v8::Value> V8_VAR_VAL;
|
||||
typedef v8::Local<v8::Object> V8_VAR_OBJ;
|
||||
typedef v8::Local<v8::Array> V8_VAR_ARR;
|
||||
typedef v8::Local<v8::ArrayBufferView> V8_VAR_ABV;
|
||||
typedef v8::Local<v8::String> V8_VAR_STR;
|
||||
typedef v8::Local<v8::Function> V8_VAR_FUNC;
|
||||
typedef v8::Local<v8::External> V8_VAR_EXT;
|
||||
typedef v8::Local<v8::FunctionTemplate> V8_VAR_FT;
|
||||
typedef v8::Local<v8::ObjectTemplate> V8_VAR_OT;
|
||||
|
||||
typedef Nan::Persistent<v8::FunctionTemplate> V8_STORE_FT;
|
||||
typedef Nan::Persistent<v8::Function> V8_STORE_FUNC;
|
||||
typedef Nan::Persistent<v8::Object> V8_STORE_OBJ;
|
||||
typedef Nan::Persistent<v8::Value> V8_STORE_VAL;
|
||||
|
||||
|
||||
#define JS_STR(...) Nan::New<v8::String>(__VA_ARGS__).ToLocalChecked()
|
||||
#define JS_UTF8(...) Nan::New<v8::String>(__VA_ARGS__).ToLocalChecked()
|
||||
#define JS_INT(val) Nan::New<v8::Integer>(val)
|
||||
#define JS_INT32(val) Nan::New<v8::Integer>(val)
|
||||
#define JS_UINT32(val) Nan::New<v8::Integer>(val)
|
||||
#define JS_NUM(val) Nan::New<v8::Number>(val)
|
||||
#define JS_OFFS(val) Nan::New<v8::Number>(static_cast<double>(val))
|
||||
#define JS_FLOAT(val) Nan::New<v8::Number>(val)
|
||||
#define JS_DOUBLE(val) Nan::New<v8::Number>(val)
|
||||
#define JS_EXT(val) Nan::New<v8::External>(reinterpret_cast<void*>(val))
|
||||
#define JS_BOOL(val) (val) ? Nan::True() : Nan::False()
|
||||
#define JS_FUN(val) Nan::New<v8::Function>(val)
|
||||
#define JS_OBJ(val) Nan::New<v8::Object>(val)
|
||||
|
||||
|
||||
#define RET_STR(...) RET_VALUE(JS_STR(__VA_ARGS__))
|
||||
#define RET_UTF8(...) RET_VALUE(JS_UTF8(__VA_ARGS__))
|
||||
#define RET_INT(val) RET_VALUE(JS_INT(val))
|
||||
#define RET_INT32(val) RET_VALUE(JS_INT32(val))
|
||||
#define RET_UINT32(val) RET_VALUE(JS_UINT32(val))
|
||||
#define RET_NUM(val) RET_VALUE(JS_NUM(val))
|
||||
#define RET_OFFS(val) RET_VALUE(JS_OFFS(val))
|
||||
#define RET_FLOAT(val) RET_VALUE(JS_FLOAT(val))
|
||||
#define RET_DOUBLE(val) RET_VALUE(JS_DOUBLE(val))
|
||||
#define RET_EXT(val) RET_VALUE(JS_EXT(val))
|
||||
#define RET_BOOL(val) RET_VALUE(JS_BOOL(val))
|
||||
#define RET_FUN(val) RET_VALUE(JS_FUN(val))
|
||||
#define RET_OBJ(val) RET_VALUE(JS_OBJ(val))
|
||||
|
||||
|
||||
#define REQ_ARGS(N) \
|
||||
if (info.Length() < (N)) { \
|
||||
JS_THROW("Expected at least " #N " arguments"); \
|
||||
RET_UNDEFINED; \
|
||||
}
|
||||
if (info.Length() < (N)) \
|
||||
return Nan::ThrowTypeError("Expected at least " #N " arguments");
|
||||
|
||||
|
||||
#define IS_EMPTY(VAL) (VAL.IsNull() || VAL.IsUndefined())
|
||||
#define IS_ARG_EMPTY(I) IS_EMPTY(info[I])
|
||||
|
||||
#define IS_ARG_EMPTY(I) (info[I]->IsNull() || info[I]->IsUndefined())
|
||||
|
||||
#define CHECK_REQ_ARG(I, C, T) \
|
||||
if (info.Length() <= (I) || !info[I].C) { \
|
||||
JS_THROW("Argument " #I " must be of type `" T "`"); \
|
||||
RET_UNDEFINED; \
|
||||
}
|
||||
if (info.Length() <= (I) || ! info[I]->C) \
|
||||
return Nan::ThrowTypeError("Argument " #I " must be " T);
|
||||
|
||||
#define CHECK_LET_ARG(I, C, T) \
|
||||
if (!(IS_ARG_EMPTY(I) || info[I].C)) { \
|
||||
JS_THROW( \
|
||||
"Argument " #I \
|
||||
" must be of type `" T \
|
||||
"` or be `null`/`undefined`" \
|
||||
); \
|
||||
RET_UNDEFINED; \
|
||||
}
|
||||
if ( ! (IS_ARG_EMPTY(I) || info[I]->C) ) \
|
||||
return Nan::ThrowTypeError("Argument " #I " must be " T " or null");
|
||||
|
||||
|
||||
#define REQ_STR_ARG(I, VAR) \
|
||||
CHECK_REQ_ARG(I, IsString(), "String"); \
|
||||
std::string VAR = info[I].ToString().Utf8Value();
|
||||
#define REQ_UTF8_ARG(I, VAR) \
|
||||
CHECK_REQ_ARG(I, IsString(), "string"); \
|
||||
Nan::Utf8String VAR(info[I]);
|
||||
|
||||
#define USE_STR_ARG(I, VAR, DEF) \
|
||||
CHECK_LET_ARG(I, IsString(), "String"); \
|
||||
std::string VAR = IS_ARG_EMPTY(I) ? (DEF) : info[I].ToString().Utf8Value();
|
||||
|
||||
#define LET_STR_ARG(I, VAR) USE_STR_ARG(I, VAR, "")
|
||||
#define LET_UTF8_ARG(I, VAR) \
|
||||
CHECK_LET_ARG(I, IsString(), "string"); \
|
||||
Nan::Utf8String VAR(JS_STR(""));
|
||||
|
||||
#define REQ_STR_ARG(I, VAR) REQ_UTF8_ARG(I, VAR)
|
||||
#define LET_STR_ARG(I, VAR) LET_UTF8_ARG(I, VAR)
|
||||
|
||||
#define REQ_INT32_ARG(I, VAR) \
|
||||
CHECK_REQ_ARG(I, IsNumber(), "Int32"); \
|
||||
int VAR = info[I].ToNumber().Int32Value();
|
||||
|
||||
#define USE_INT32_ARG(I, VAR, DEF) \
|
||||
CHECK_LET_ARG(I, IsNumber(), "Int32"); \
|
||||
int VAR = IS_ARG_EMPTY(I) ? (DEF) : info[I].ToNumber().Int32Value();
|
||||
|
||||
#define LET_INT32_ARG(I, VAR) USE_INT32_ARG(I, VAR, 0)
|
||||
|
||||
#define REQ_INT_ARG(I, VAR) REQ_INT32_ARG(I, VAR)
|
||||
#define USE_INT_ARG(I, VAR, DEF) USE_INT32_ARG(I, VAR, DEF)
|
||||
#define LET_INT_ARG(I, VAR) LET_INT32_ARG(I, VAR)
|
||||
CHECK_REQ_ARG(I, IsInt32(), "int32"); \
|
||||
int VAR = info[I].As<v8::Int32>()->Value();
|
||||
|
||||
#define LET_INT32_ARG(I, VAR) \
|
||||
CHECK_LET_ARG(I, IsInt32(), "int32"); \
|
||||
int VAR = IS_ARG_EMPTY(I) ? 0 : info[I].As<v8::Int32>()->Value();
|
||||
|
||||
#define REQ_UINT32_ARG(I, VAR) \
|
||||
CHECK_REQ_ARG(I, IsNumber(), "Uint32"); \
|
||||
unsigned int VAR = info[I].ToNumber().Uint32Value();
|
||||
CHECK_REQ_ARG(I, IsUint32(), "uint32"); \
|
||||
unsigned int VAR = info[I].As<v8::Uint32>()->Value();
|
||||
|
||||
#define USE_UINT32_ARG(I, VAR, DEF) \
|
||||
CHECK_LET_ARG(I, IsNumber(), "Uint32"); \
|
||||
unsigned int VAR = IS_ARG_EMPTY(I) \
|
||||
? (DEF) \
|
||||
: info[I].ToNumber().Uint32Value();
|
||||
|
||||
#define LET_UINT32_ARG(I, VAR) USE_UINT32_ARG(I, VAR, 0)
|
||||
|
||||
#define REQ_UINT_ARG(I, VAR) REQ_UINT32_ARG(I, VAR)
|
||||
#define USE_UINT_ARG(I, VAR, DEF) USE_UINT32_ARG(I, VAR, DEF)
|
||||
#define LET_UINT_ARG(I, VAR) LET_UINT32_ARG(I, VAR)
|
||||
#define LET_UINT32_ARG(I, VAR) \
|
||||
CHECK_LET_ARG(I, IsUint32(), "uint32"); \
|
||||
unsigned int VAR = IS_ARG_EMPTY(I) ? 0 : info[I].As<v8::Uint32>()->Value();
|
||||
|
||||
#define REQ_INT_ARG(I, VAR) LET_INT32_ARG(I, VAR)
|
||||
#define LET_INT_ARG(I, VAR) REQ_UINT32_ARG(I, VAR)
|
||||
|
||||
#define REQ_BOOL_ARG(I, VAR) \
|
||||
CHECK_REQ_ARG(I, IsBoolean(), "Bool"); \
|
||||
bool VAR = info[I].ToBoolean().Value();
|
||||
|
||||
#define USE_BOOL_ARG(I, VAR, DEF) \
|
||||
CHECK_LET_ARG(I, IsBoolean(), "Bool"); \
|
||||
bool VAR = IS_ARG_EMPTY(I) ? (DEF) : info[I].ToBoolean().Value();
|
||||
|
||||
#define LET_BOOL_ARG(I, VAR) USE_BOOL_ARG(I, VAR, false)
|
||||
|
||||
#define SOFT_BOOL_ARG(I, VAR) \
|
||||
bool VAR = (info.Length() >= (I) && info[I].ToBoolean().Value()) || false;
|
||||
CHECK_REQ_ARG(I, IsBoolean(), "bool"); \
|
||||
bool VAR = info[I].As<v8::Boolean>()->Value();
|
||||
|
||||
#define LET_BOOL_ARG(I, VAR) \
|
||||
CHECK_LET_ARG(I, IsBoolean(), "bool"); \
|
||||
bool VAR = IS_ARG_EMPTY(I) ? false : info[I].As<v8::Boolean>()->Value();
|
||||
|
||||
#define REQ_OFFS_ARG(I, VAR) \
|
||||
CHECK_REQ_ARG(I, IsNumber(), "Number"); \
|
||||
size_t VAR = static_cast<size_t>(info[I].ToNumber().DoubleValue());
|
||||
CHECK_REQ_ARG(I, IsNumber(), "number"); \
|
||||
size_t VAR = static_cast<size_t>(info[I].As<v8::Integer>()->Value());
|
||||
|
||||
#define USE_OFFS_ARG(I, VAR, DEF) \
|
||||
CHECK_LET_ARG(I, IsNumber(), "Number"); \
|
||||
size_t VAR = IS_ARG_EMPTY(I) \
|
||||
? (DEF) \
|
||||
: static_cast<size_t>(info[I].ToNumber().DoubleValue());
|
||||
|
||||
#define LET_OFFS_ARG(I, VAR) USE_OFFS_ARG(I, VAR, 0)
|
||||
#define LET_OFFS_ARG(I, VAR) \
|
||||
CHECK_LET_ARG(I, IsNumber(), "number"); \
|
||||
size_t VAR = IS_ARG_EMPTY(I) ? 0 : static_cast<size_t>( \
|
||||
info[I].As<v8::Integer>()->Value() \
|
||||
);
|
||||
|
||||
|
||||
#define REQ_DOUBLE_ARG(I, VAR) \
|
||||
CHECK_REQ_ARG(I, IsNumber(), "Number"); \
|
||||
double VAR = info[I].ToNumber().DoubleValue();
|
||||
CHECK_REQ_ARG(I, IsNumber(), "number"); \
|
||||
double VAR = info[I].As<v8::Number>()->Value();
|
||||
|
||||
#define USE_DOUBLE_ARG(I, VAR, DEF) \
|
||||
CHECK_LET_ARG(I, IsNumber(), "Number"); \
|
||||
double VAR = IS_ARG_EMPTY(I) ? (DEF) : info[I].ToNumber().DoubleValue();
|
||||
|
||||
#define LET_DOUBLE_ARG(I, VAR) USE_DOUBLE_ARG(I, VAR, 0.0)
|
||||
#define LET_DOUBLE_ARG(I, VAR) \
|
||||
CHECK_LET_ARG(I, IsNumber(), "number"); \
|
||||
double VAR = IS_ARG_EMPTY(I) ? 0.0 : info[I].As<v8::Number>()->Value();
|
||||
|
||||
|
||||
#define REQ_FLOAT_ARG(I, VAR) \
|
||||
CHECK_REQ_ARG(I, IsNumber(), "Number"); \
|
||||
float VAR = info[I].ToNumber().FloatValue();
|
||||
CHECK_REQ_ARG(I, IsNumber(), "number"); \
|
||||
float VAR = static_cast<float>(info[I].As<v8::Number>()->Value());
|
||||
|
||||
#define USE_FLOAT_ARG(I, VAR, DEF) \
|
||||
CHECK_LET_ARG(I, IsNumber(), "Number"); \
|
||||
float VAR = IS_ARG_EMPTY(I) ? (DEF) : info[I].ToNumber().FloatValue();
|
||||
|
||||
#define LET_FLOAT_ARG(I, VAR) USE_FLOAT_ARG(I, VAR, 0.f)
|
||||
#define LET_FLOAT_ARG(I, VAR) \
|
||||
CHECK_LET_ARG(I, IsNumber(), "number"); \
|
||||
float VAR = IS_ARG_EMPTY(I) ? 0.f : static_cast<float>( \
|
||||
info[I].As<v8::Number>()->Value() \
|
||||
);
|
||||
|
||||
|
||||
#define REQ_EXT_ARG(I, VAR) \
|
||||
CHECK_REQ_ARG(I, IsExternal(), "Pointer"); \
|
||||
void *VAR = info[I].As< Napi::External<void> >().Data();
|
||||
CHECK_REQ_ARG(I, IsExternal(), "void*"); \
|
||||
V8_VAR_EXT VAR = V8_VAR_EXT::Cast(info[I]);
|
||||
|
||||
#define USE_EXT_ARG(I, VAR, DEF) \
|
||||
CHECK_LET_ARG(I, IsExternal(), "Pointer"); \
|
||||
void *VAR = IS_ARG_EMPTY(I) ? (DEF) : info[I].As< Napi::External<void> >().Data();
|
||||
|
||||
#define LET_EXT_ARG(I, VAR) USE_EXT_ARG(I, VAR, nullptr)
|
||||
#define LET_EXT_ARG(I, VAR) \
|
||||
CHECK_LET_ARG(I, IsExternal(), "number"); \
|
||||
V8_VAR_EXT VAR = IS_ARG_EMPTY(I) ? JS_EXT(nullptr) : V8_VAR_EXT::Cast(info[I]);
|
||||
|
||||
|
||||
#define REQ_FUN_ARG(I, VAR) \
|
||||
CHECK_REQ_ARG(I, IsFunction(), "Function"); \
|
||||
Napi::Function VAR = info[I].As<Napi::Function>();
|
||||
CHECK_REQ_ARG(I, IsFunction(), "function"); \
|
||||
V8_VAR_FUNC VAR = V8_VAR_FUNC::Cast(info[I]);
|
||||
|
||||
|
||||
#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, Napi::Object::New(env))
|
||||
CHECK_REQ_ARG(I, IsObject(), "object"); \
|
||||
V8_VAR_OBJ VAR = V8_VAR_OBJ::Cast(info[I]);
|
||||
|
||||
|
||||
#define REQ_ARRV_ARG(I, VAR) \
|
||||
CHECK_REQ_ARG(I, IsArrayBuffer(), "ArrayBuffer"); \
|
||||
Napi::ArrayBuffer VAR = info[I].As<Napi::ArrayBuffer>();
|
||||
REQ_OBJ_ARG(I, _obj_##VAR); \
|
||||
if( ! _obj_##VAR->IsArrayBufferView() ) \
|
||||
return Nan::ThrowTypeError("Argument " #I " must be an array buffer");\
|
||||
V8_VAR_ABV VAR = V8_VAR_ABV::Cast(_obj_##VAR);
|
||||
|
||||
|
||||
#define REQ_BUF_ARG(I, VAR) \
|
||||
CHECK_REQ_ARG(I, IsBuffer(), "Buffer"); \
|
||||
Napi::Buffer<uint8_t> VAR = info[I].As< Napi::Buffer<uint8_t> >();
|
||||
#define SET_PROP(OBJ, KEY, VAL) OBJ->Set(JS_STR(KEY), VAL);
|
||||
#define SET_I(ARR, I, VAL) ARR->Set(I, VAL);
|
||||
|
||||
|
||||
#define REQ_ARRAY_ARG(I, VAR) \
|
||||
CHECK_REQ_ARG(I, IsArray(), "Array"); \
|
||||
Napi::Array VAR = info[I].As<Napi::Array>();
|
||||
|
||||
#define USE_ARRAY_ARG(I, VAR, DEF) \
|
||||
CHECK_LET_ARG(I, IsArray(), "Array"); \
|
||||
Napi::Array VAR = IS_ARG_EMPTY(I) ? (DEF) : info[I].As<Napi::Array>();
|
||||
|
||||
#define LET_ARRAY_ARG(I, VAR) USE_ARRAY_ARG(I, VAR, Napi::Array::New(env))
|
||||
|
||||
|
||||
inline std::vector<std::string> arrayStrToVec(const Napi::Array &arr) {
|
||||
uint32_t count = arr.Length();
|
||||
std::vector<std::string> result(count);
|
||||
for (uint32_t i = 0; i < count; i++) {
|
||||
Napi::Value item = arr[i];
|
||||
if (item.IsString()) {
|
||||
result[i] = item.ToString().Utf8Value();
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
inline Napi::Array stringsToArray(Napi::Env env, const char **strings, size_t count) {
|
||||
Napi::Array arr = JS_ARRAY;
|
||||
for (size_t i = 0; i < count; i++) {
|
||||
arr.Set(i, strings[i]);
|
||||
}
|
||||
return arr;
|
||||
}
|
||||
|
||||
|
||||
inline Napi::Array vecStrToArray(Napi::Env env, const std::vector<std::string> &strings) {
|
||||
Napi::Array arr = JS_ARRAY;
|
||||
size_t count = strings.size();
|
||||
for (size_t i = 0; i < count; i++) {
|
||||
arr.Set(i, strings[i]);
|
||||
}
|
||||
return arr;
|
||||
}
|
||||
|
||||
|
||||
#define LET_ARRAY_STR_ARG(I, VAR) \
|
||||
USE_ARRAY_ARG(I, __ARRAY_ ## VAR, Napi::Array::New(env)); \
|
||||
std::vector<std::string> VAR = arrayStrToVec(__ARRAY_ ## VAR);
|
||||
|
||||
|
||||
#define RET_ARRAY_STR(VAL) RET_VALUE(vecStrToArray(env, VAL))
|
||||
|
||||
|
||||
#define REQ_TYPED_ARRAY_ARG(I, VAR) \
|
||||
CHECK_REQ_ARG(I, IsTypedArray(), "TypedArray"); \
|
||||
Napi::TypedArray VAR = info[I].As<Napi::TypedArray>();
|
||||
|
||||
#define CTOR_CHECK(T) \
|
||||
if ( ! info.IsConstructCall() ) \
|
||||
return Nan::ThrowTypeError(T " must be called with the 'new' keyword.");
|
||||
|
||||
#define DES_CHECK \
|
||||
if (_isDestroyed) return;
|
||||
|
||||
#define THIS_CHECK \
|
||||
NAPI_ENV; \
|
||||
if (_isDestroyed) RET_UNDEFINED;
|
||||
|
||||
#define CACHE_CAS(CACHE, V) \
|
||||
if (CACHE == V) { \
|
||||
RET_UNDEFINED; \
|
||||
} \
|
||||
CACHE = V;
|
||||
|
||||
#define SETTER_CHECK(C, T) \
|
||||
if (!value.C) { \
|
||||
JS_THROW("Value must be " T); \
|
||||
RET_UNDEFINED; \
|
||||
}
|
||||
if ( ! value->C ) \
|
||||
return Nan::ThrowTypeError("Value must be " T);
|
||||
|
||||
|
||||
#define JS_METHOD(NAME) Napi::Value NAME(const Napi::CallbackInfo &info)
|
||||
#define ACCESSOR_RW(OBJ, NAME) \
|
||||
Nan::SetAccessor(OBJ, JS_STR(#NAME), NAME ## Getter, NAME ## Setter);
|
||||
|
||||
#define ACCESSOR_RW(CLASS, NAME) \
|
||||
InstanceAccessor(#NAME, &CLASS::NAME ## Getter, &CLASS::NAME ## Setter)
|
||||
#define ACCESSOR_R(OBJ, NAME) \
|
||||
Nan::SetAccessor(OBJ, JS_STR(#NAME), NAME ## Getter);
|
||||
|
||||
#define ACCESSOR_R(CLASS, NAME) \
|
||||
InstanceAccessor(#NAME, &CLASS::NAME ## Getter, nullptr)
|
||||
|
||||
#define ACCESSOR_M(CLASS, NAME) \
|
||||
InstanceMethod(#NAME, &CLASS::NAME)
|
||||
#define SETTER_UTF8_ARG \
|
||||
SETTER_CHECK(IsString(), "string"); \
|
||||
Nan::Utf8String v(value);
|
||||
|
||||
#define THIS_OBJ(VAR) \
|
||||
Napi::Object VAR = info.This().As<Napi::Object>();
|
||||
|
||||
#define SETTER_STR_ARG \
|
||||
SETTER_CHECK(IsString(), "String"); \
|
||||
std::string v = value.ToString().Utf8Value();
|
||||
#define SETTER_STR_ARG SETTER_UTF8_ARG
|
||||
|
||||
#define SETTER_INT32_ARG \
|
||||
SETTER_CHECK(IsNumber(), "Int32"); \
|
||||
int v = value.ToNumber().Int32Value();
|
||||
SETTER_CHECK(IsInt32(), "int32"); \
|
||||
int v = value.As<v8::Int32>()->Value();
|
||||
|
||||
#define SETTER_INT_ARG SETTER_INT32_ARG
|
||||
|
||||
#define SETTER_BOOL_ARG \
|
||||
SETTER_CHECK(IsBoolean(), "Bool"); \
|
||||
bool v = value.ToBoolean().Value();
|
||||
SETTER_CHECK(IsBoolean(), "bool"); \
|
||||
bool v = value.As<v8::Boolean>()->Value();
|
||||
|
||||
#define SETTER_UINT32_ARG \
|
||||
SETTER_CHECK(IsNumber(), "Uint32"); \
|
||||
unsigned int v = value.ToNumber().Uint32Value();
|
||||
|
||||
#define SETTER_UINT_ARG SETTER_UINT32_ARG
|
||||
SETTER_CHECK(IsUint32(), "uint32"); \
|
||||
unsigned int v = value.As<v8::Uint32>()->Value();
|
||||
|
||||
#define SETTER_OFFS_ARG \
|
||||
SETTER_CHECK(IsNumber(), "Number"); \
|
||||
size_t v = static_cast<size_t>(value.ToNumber().DoubleValue());
|
||||
SETTER_CHECK(IsNumber(), "number"); \
|
||||
size_t v = static_cast<size_t>(value.As<v8::Integer>()->Value());
|
||||
|
||||
#define SETTER_DOUBLE_ARG \
|
||||
SETTER_CHECK(IsNumber(), "Number"); \
|
||||
double v = value.ToNumber().DoubleValue();
|
||||
SETTER_CHECK(IsNumber(), "number"); \
|
||||
double v = value.As<v8::Number>()->Value();
|
||||
|
||||
#define SETTER_FLOAT_ARG \
|
||||
SETTER_CHECK(IsNumber(), "Number"); \
|
||||
float v = value.ToNumber().FloatValue();
|
||||
SETTER_CHECK(IsNumber(), "number"); \
|
||||
float v = static_cast<float>(value.As<v8::Number>()->Value());
|
||||
|
||||
#define SETTER_EXT_ARG \
|
||||
SETTER_CHECK(IsExternal(), "Pointer"); \
|
||||
Napi::External v = value.As<Napi::External>();
|
||||
SETTER_CHECK(IsExternal(), "void*"); \
|
||||
V8_VAR_EXT v = V8_VAR_EXT::Cast(value);
|
||||
|
||||
#define SETTER_FUN_ARG \
|
||||
SETTER_CHECK(IsFunction(), "Function"); \
|
||||
Napi::Function v = value.As<Napi::Function>()
|
||||
SETTER_CHECK(IsFunction(), "function"); \
|
||||
V8_VAR_FUNC v = V8_VAR_FUNC::Cast(value);
|
||||
|
||||
#define SETTER_OBJ_ARG \
|
||||
SETTER_CHECK(IsObject(), "Object"); \
|
||||
Napi::Object v = value.As<Napi::Object>()
|
||||
SETTER_CHECK(IsObject(), "object"); \
|
||||
V8_VAR_OBJ v = V8_VAR_OBJ::Cast(value);
|
||||
|
||||
#define SETTER_ARRV_ARG \
|
||||
SETTER_CHECK(IsArrayBuffer(), "TypedArray"); \
|
||||
Napi::ArrayBuffer v = value.As<Napi::ArrayBuffer>();
|
||||
SETTER_CHECK(IsObject(), "object"); \
|
||||
V8_VAR_OBJ _obj_v = V8_VAR_OBJ::Cast(value); \
|
||||
if( ! _obj_v->IsArrayBufferView() ) \
|
||||
return Nan::ThrowTypeError("The value must be an array buffer"); \
|
||||
V8_VAR_ABV v = V8_VAR_ABV::Cast(_obj_v);
|
||||
|
||||
|
||||
#define GET_AND_THROW_LAST_ERROR() \
|
||||
do { \
|
||||
const napi_extended_error_info *error_info; \
|
||||
napi_get_last_error_info((env), &error_info); \
|
||||
bool is_pending; \
|
||||
napi_is_exception_pending((env), &is_pending); \
|
||||
/* If an exception is already pending, don't rethrow it */ \
|
||||
if (!is_pending) { \
|
||||
const char* error_message = error_info->error_message != NULL \
|
||||
? error_info->error_message \
|
||||
: "empty error message"; \
|
||||
JS_THROW(error_message); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define NAPI_CALL(the_call) \
|
||||
do { \
|
||||
if ((the_call) != napi_ok) { \
|
||||
GET_AND_THROW_LAST_ERROR(); \
|
||||
RET_UNDEFINED; \
|
||||
} \
|
||||
} while (0)
|
||||
template<typename Type>
|
||||
inline Type* getArrayData(V8_VAR_OBJ obj, int *num = nullptr) {
|
||||
|
||||
#define JS_RUN(code, VAR) \
|
||||
napi_value __RESULT_ ## VAR; \
|
||||
NAPI_CALL( \
|
||||
napi_run_script(env, napi_value(JS_STR(code)), &__RESULT_ ## VAR) \
|
||||
); \
|
||||
Napi::Value VAR(env, __RESULT_ ## VAR);
|
||||
|
||||
|
||||
template<typename Type = uint8_t>
|
||||
inline Type* getArrayData(
|
||||
Napi::Env env,
|
||||
Napi::Object obj,
|
||||
int *num = nullptr
|
||||
) {
|
||||
Type *out = nullptr;
|
||||
|
||||
if (obj.IsTypedArray()) {
|
||||
Napi::TypedArray ta = obj.As<Napi::TypedArray>();
|
||||
size_t offset = ta.ByteOffset();
|
||||
Napi::ArrayBuffer arr = ta.ArrayBuffer();
|
||||
if (num) {
|
||||
*num = ta.ByteLength() / sizeof(Type);
|
||||
}
|
||||
uint8_t *base = reinterpret_cast<uint8_t *>(arr.Data());
|
||||
out = reinterpret_cast<Type *>(base + offset);
|
||||
} else if (obj.IsArrayBuffer()) {
|
||||
Napi::ArrayBuffer arr = obj.As<Napi::ArrayBuffer>();
|
||||
if (num) {
|
||||
*num = arr.ByteLength() / sizeof(Type);
|
||||
}
|
||||
out = reinterpret_cast<Type *>(arr.Data());
|
||||
} else {
|
||||
if (num) {
|
||||
*num = 0;
|
||||
}
|
||||
JS_THROW("Argument must be of type `TypedArray`.");
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
|
||||
template<typename Type = uint8_t>
|
||||
inline Type* getBufferData(
|
||||
Napi::Env env,
|
||||
Napi::Object obj,
|
||||
int *num = nullptr
|
||||
) {
|
||||
Type *out = nullptr;
|
||||
Type *data = nullptr;
|
||||
|
||||
if (num) {
|
||||
*num = 0;
|
||||
}
|
||||
|
||||
if (!obj.IsBuffer()) {
|
||||
JS_THROW("Argument must be of type `Buffer`.");
|
||||
return out;
|
||||
if ( ! obj->IsArrayBufferView() ) {
|
||||
Nan::ThrowError("Argument must be a TypedArray.");
|
||||
return data;
|
||||
}
|
||||
|
||||
Napi::Buffer<uint8_t> arr = obj.As< Napi::Buffer<uint8_t> >();
|
||||
V8_VAR_ABV arr = V8_VAR_ABV::Cast(obj);
|
||||
if (num) {
|
||||
*num = arr.Length() / sizeof(Type);
|
||||
*num = arr->ByteLength() / sizeof(Type);
|
||||
}
|
||||
out = arr.Data();
|
||||
data = reinterpret_cast<Type*>(arr->Buffer()->GetContents().Data());
|
||||
|
||||
return data;
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
|
||||
inline void *getData(Napi::Env env, Napi::Object obj) {
|
||||
void *out = nullptr;
|
||||
inline void *getData(V8_VAR_OBJ obj) {
|
||||
|
||||
if (obj.IsTypedArray() || obj.IsArrayBuffer()) {
|
||||
out = getArrayData<uint8_t>(env, obj);
|
||||
} else if (obj.IsBuffer()) {
|
||||
out = getBufferData<uint8_t>(env, obj);
|
||||
} else if (obj.Has("data")) {
|
||||
Napi::Object data = obj.Get("data").As<Napi::Object>();
|
||||
if (data.IsTypedArray() || data.IsArrayBuffer()) {
|
||||
out = getArrayData<uint8_t>(env, data);
|
||||
} else if (data.IsBuffer()) {
|
||||
out = getBufferData<uint8_t>(env, data);
|
||||
void *pixels = nullptr;
|
||||
|
||||
if (obj->IsArrayBufferView()) {
|
||||
pixels = getArrayData<unsigned char>(obj);
|
||||
} else if (obj->Has(JS_STR("data"))) {
|
||||
V8_VAR_VAL data = Nan::Get(obj, JS_STR("data")).ToLocalChecked();
|
||||
if ( ! data->IsNullOrUndefined() ) {
|
||||
pixels = node::Buffer::Data(data);
|
||||
}
|
||||
}
|
||||
|
||||
return out;
|
||||
return pixels;
|
||||
|
||||
}
|
||||
|
||||
|
||||
inline Napi::Value consoleLog(
|
||||
Napi::Env env,
|
||||
int argc,
|
||||
const Napi::Value *argv
|
||||
) {
|
||||
JS_RUN("console.log", log);
|
||||
std::vector<napi_value> args;
|
||||
for (int i = 0; i < argc; i++) {
|
||||
args.push_back(napi_value(argv[i]));
|
||||
}
|
||||
log.As<Napi::Function>().Call(args);
|
||||
RET_UNDEFINED;
|
||||
inline void consoleLog(int argc, V8_VAR_VAL *argv) {
|
||||
|
||||
V8_VAR_STR code = JS_STR("((...args) => console.log(...args))");
|
||||
|
||||
v8::Local<v8::Value> log = v8::Script::Compile(
|
||||
Nan::GetCurrentContext(), code
|
||||
).ToLocalChecked()->Run(
|
||||
Nan::GetCurrentContext()
|
||||
).ToLocalChecked();
|
||||
Nan::Callback logCb(Nan::To<v8::Function>(log).ToLocalChecked());
|
||||
|
||||
Nan::AsyncResource async("consoleLog()");
|
||||
|
||||
logCb.Call(argc, argv, &async);
|
||||
|
||||
}
|
||||
|
||||
|
||||
inline Napi::Value consoleLog(Napi::Env env, const std::string &message) {
|
||||
Napi::Value arg = JS_STR(message);
|
||||
consoleLog(env, 1, &arg);
|
||||
RET_UNDEFINED;
|
||||
inline void consoleLog(const std::string &message) {
|
||||
|
||||
V8_VAR_VAL arg = JS_STR(message);
|
||||
consoleLog(1, &arg);
|
||||
|
||||
}
|
||||
|
||||
|
||||
inline void eventEmit(
|
||||
Napi::Object that,
|
||||
const std::string &name,
|
||||
int argc = 0,
|
||||
const Napi::Value *argv = nullptr,
|
||||
napi_async_context context = nullptr
|
||||
) {
|
||||
if (!that.Has("emit")) {
|
||||
return;
|
||||
}
|
||||
|
||||
Napi::Env env = that.Env();
|
||||
|
||||
Napi::String eventName = JS_STR(name);
|
||||
Napi::Function thatEmit = that.Get("emit").As<Napi::Function>();
|
||||
|
||||
std::vector<napi_value> args;
|
||||
args.push_back(napi_value(eventName));
|
||||
for (int i = 0; i < argc; i++) {
|
||||
args.push_back(napi_value(argv[i]));
|
||||
}
|
||||
|
||||
if (context) {
|
||||
thatEmit.MakeCallback(that, args, context);
|
||||
} else {
|
||||
thatEmit.Call(that, args);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
typedef Napi::Value (*Es5MethodCallback)(const Napi::CallbackInfo& info);
|
||||
typedef Napi::Value (*Es5GetterCallback)(const Napi::CallbackInfo& info);
|
||||
typedef void (*Es5SetterCallback)(const Napi::CallbackInfo& info);
|
||||
|
||||
|
||||
#define DECLARE_ES5_CLASS(CLASS, NAME) \
|
||||
public: \
|
||||
inline static CLASS *unwrap(Napi::Object thatObj) { \
|
||||
CLASS *that; \
|
||||
napi_status ns = napi_unwrap( \
|
||||
thatObj.Env(), \
|
||||
thatObj.Get(_nameEs5), \
|
||||
reinterpret_cast<void**>(&that) \
|
||||
); \
|
||||
if (ns != napi_ok) { \
|
||||
return nullptr; \
|
||||
} \
|
||||
return that; \
|
||||
} \
|
||||
private: \
|
||||
static Napi::FunctionReference _ctorEs5; \
|
||||
static const char *_nameEs5; \
|
||||
static void _finalizeEs5(napi_env e, void *dest, void* hint); \
|
||||
static napi_value _createEs5(napi_env e, napi_callback_info i); \
|
||||
inline void super( \
|
||||
const Napi::CallbackInfo& info, \
|
||||
int argc, \
|
||||
const Napi::Value *argv \
|
||||
) { \
|
||||
Napi::Function ctor = _ctorEs5.Value(); \
|
||||
if (ctor.Has("super_")) { \
|
||||
Napi::Function _super = ctor.Get("super_").As<Napi::Function>(); \
|
||||
std::vector<napi_value> args; \
|
||||
for (int i = 0; i < argc; i++) { \
|
||||
args.push_back(argv[i]); \
|
||||
} \
|
||||
_super.Call(info.This(), args); \
|
||||
} \
|
||||
} \
|
||||
inline void super( \
|
||||
const Napi::CallbackInfo& info, \
|
||||
int argc = 0, \
|
||||
const napi_value *argv = nullptr \
|
||||
) { \
|
||||
Napi::Function ctor = _ctorEs5.Value(); \
|
||||
if (ctor.Has("super_")) { \
|
||||
Napi::Function _super = ctor.Get("super_").As<Napi::Function>(); \
|
||||
_super.Call(info.This(), argc, argv); \
|
||||
} \
|
||||
} \
|
||||
inline static Napi::Function wrap(Napi::Env env) { \
|
||||
napi_value __initResult; \
|
||||
napi_create_function( \
|
||||
env, #NAME, 0, _createEs5, nullptr, &__initResult \
|
||||
); \
|
||||
Napi::Function ctor = Napi::Function(env, __initResult); \
|
||||
_ctorEs5 = Napi::Persistent(ctor); \
|
||||
_ctorEs5.SuppressDestruct(); \
|
||||
return ctor; \
|
||||
} \
|
||||
inline static void method( \
|
||||
const char *name, \
|
||||
Es5MethodCallback cb \
|
||||
) { \
|
||||
Napi::Function proto = ( \
|
||||
_ctorEs5.Value().Get("prototype").As<Napi::Function>() \
|
||||
); \
|
||||
proto.DefineProperty( \
|
||||
Napi::PropertyDescriptor::Function( \
|
||||
proto.Env(), proto, name, cb \
|
||||
) \
|
||||
); \
|
||||
} \
|
||||
inline static void accessorR( \
|
||||
const char *name, \
|
||||
Es5GetterCallback getter \
|
||||
) { \
|
||||
Napi::Function proto = ( \
|
||||
_ctorEs5.Value().Get("prototype").As<Napi::Function>() \
|
||||
); \
|
||||
proto.DefineProperty( \
|
||||
Napi::PropertyDescriptor::Accessor( \
|
||||
proto.Env(), proto, name, getter \
|
||||
) \
|
||||
); \
|
||||
} \
|
||||
inline static void accessorRw( \
|
||||
const char *name, \
|
||||
Es5GetterCallback getter, \
|
||||
Es5SetterCallback setter \
|
||||
) { \
|
||||
Napi::Function proto = ( \
|
||||
_ctorEs5.Value().Get("prototype").As<Napi::Function>() \
|
||||
); \
|
||||
proto.DefineProperty( \
|
||||
Napi::PropertyDescriptor::Accessor( \
|
||||
proto.Env(), \
|
||||
proto, \
|
||||
name, \
|
||||
getter, \
|
||||
setter \
|
||||
) \
|
||||
); \
|
||||
}
|
||||
|
||||
|
||||
#define JS_GET_THAT(CLASS) \
|
||||
CLASS *that = CLASS::unwrap(info.This().As<Napi::Object>());
|
||||
|
||||
#define JS_DECLARE_METHOD(CLASS, NAME) \
|
||||
inline static JS_METHOD(__st_##NAME) { \
|
||||
JS_GET_THAT(CLASS); \
|
||||
return that->__i_##NAME(info); \
|
||||
}; \
|
||||
JS_METHOD(__i_##NAME);
|
||||
|
||||
#define JS_DECLARE_GETTER(CLASS, NAME) JS_DECLARE_METHOD(CLASS, NAME##Getter)
|
||||
|
||||
#define JS_DECLARE_SETTER(CLASS, NAME) \
|
||||
inline static void __st_##NAME##Setter( \
|
||||
const Napi::CallbackInfo &info \
|
||||
) { \
|
||||
JS_GET_THAT(CLASS); \
|
||||
that->__i_##NAME##Setter(info, info[0]); \
|
||||
} \
|
||||
Napi::Value __i_##NAME##Setter( \
|
||||
const Napi::CallbackInfo &info, \
|
||||
const Napi::Value &value \
|
||||
);
|
||||
|
||||
#define JS_IMPLEMENT_METHOD(CLASS, NAME) \
|
||||
JS_METHOD(CLASS::__i_##NAME)
|
||||
|
||||
#define JS_IMPLEMENT_GETTER(CLASS, NAME) \
|
||||
JS_IMPLEMENT_METHOD(CLASS, NAME##Getter)
|
||||
|
||||
#define JS_IMPLEMENT_SETTER(CLASS, NAME) \
|
||||
Napi::Value CLASS::__i_##NAME##Setter( \
|
||||
const Napi::CallbackInfo &info, \
|
||||
const Napi::Value &value \
|
||||
)
|
||||
|
||||
#define JS_ASSIGN_METHOD(NAME) method(#NAME, __st_##NAME)
|
||||
#define JS_ASSIGN_GETTER(NAME) accessorR(#NAME, __st_##NAME##Getter)
|
||||
#define JS_ASSIGN_SETTER(NAME) \
|
||||
accessorRw(#NAME, __st_##NAME##Getter, __st_##NAME##Setter)
|
||||
|
||||
#define IMPLEMENT_ES5_CLASS(CLASS) \
|
||||
Napi::FunctionReference CLASS::_ctorEs5; \
|
||||
const char *CLASS::_nameEs5 = #CLASS; \
|
||||
void CLASS::_finalizeEs5(napi_env e, void *dest, void* hint) { \
|
||||
CLASS *instance = reinterpret_cast<CLASS*>(dest); \
|
||||
delete instance; \
|
||||
} \
|
||||
napi_value CLASS::_createEs5(napi_env env, napi_callback_info i) { \
|
||||
Napi::CallbackInfo info(env, i); \
|
||||
CLASS *instance = new CLASS(info); \
|
||||
Napi::Object wrapObj = Napi::Object::New(env); \
|
||||
info.This().As<Napi::Object>().Set(_nameEs5, wrapObj); \
|
||||
napi_wrap(env, wrapObj, instance, _finalizeEs5, nullptr, nullptr); \
|
||||
return info.Env().Undefined(); \
|
||||
}
|
||||
|
||||
#endif // ADDON_TOOLS_HPP
|
||||
#endif // _ADDON_TOOLS_HPP_
|
||||
|
|
|
@ -1,103 +0,0 @@
|
|||
# Es5 class wrapping
|
||||
|
||||
This wrapping implementation diverges from standard ES6 style wrapping.
|
||||
It also uses composition rather than inheritance, so it is easily pluggable.
|
||||
|
||||
* For **NAPI** addons, `super()` can be called from C++ side.
|
||||
* Constructor is callable with `ClassName.call(obj, ...args)`.
|
||||
* Multiple C++ objects can be attached to a single JS object
|
||||
if it is necessary in an inheritance scenario.
|
||||
* On JS side `util.inherits`
|
||||
[is used](https://nodejs.org/api/util.html#util_util_inherits_constructor_superconstructor),
|
||||
and on C++ side there is `inheritEs5` function.
|
||||
|
||||
|
||||
## Class Declaration
|
||||
|
||||
```
|
||||
class ClassName {
|
||||
DECLARE_ES5_CLASS(ClassName, JSClassName);
|
||||
|
||||
public:
|
||||
static void init(Napi::Env env, Napi::Object exports);
|
||||
explicit ClassName(const Napi::CallbackInfo& info);
|
||||
~ClassName();
|
||||
|
||||
void _destroy();
|
||||
|
||||
private:
|
||||
JS_DECLARE_GETTER(ClassName, isDestroyed);
|
||||
|
||||
JS_DECLARE_METHOD(ClassName, ClassName, destroy);
|
||||
|
||||
bool _isDestroyed;
|
||||
};
|
||||
```
|
||||
|
||||
* `DECLARE_ES5_CLASS` - adds utility declarations, the first argument
|
||||
must be this class name, and the second argument will become the
|
||||
name (arbitrary) of this function (constructor) in JS.
|
||||
* `init` - can be used to initialize this class and export it.
|
||||
* `JS_DECLARE_METHOD` - declares a method, the first argument is this class,
|
||||
the second is the name of the method to be created.
|
||||
* `JS_DECLARE_GETTER` - declares a getter, the first argument is this class,
|
||||
the second is the name of the getter to be created.
|
||||
* `JS_DECLARE_SETTER` - declares a setter, the first argument is this class,
|
||||
the second is the name of the setter to be created.
|
||||
|
||||
|
||||
## Class Implementation
|
||||
|
||||
```
|
||||
IMPLEMENT_ES5_CLASS(ClassName);
|
||||
|
||||
// Fill the properties and export the class
|
||||
void ClassName::init(Napi::Env env, Napi::Object exports) {
|
||||
Napi::Function ctor = wrap(env);
|
||||
JS_ASSIGN_METHOD(destroy);
|
||||
JS_ASSIGN_GETTER(isDestroyed);
|
||||
// ...
|
||||
exports.Set("JSClassName", ctor);
|
||||
}
|
||||
|
||||
ClassName::ClassName(const Napi::CallbackInfo &info) { NAPI_ENV;
|
||||
super(info);
|
||||
_isDestroyed = false;
|
||||
|
||||
// ...
|
||||
}
|
||||
|
||||
ClassName::~ClassName() {
|
||||
_destroy();
|
||||
}
|
||||
|
||||
void ClassName::_destroy() { DES_CHECK;
|
||||
// ...
|
||||
_isDestroyed = true;
|
||||
}
|
||||
|
||||
|
||||
JS_IMPLEMENT_METHOD(ClassName, destroy) { THIS_CHECK;
|
||||
emit("destroy");
|
||||
_destroy();
|
||||
RET_UNDEFINED;
|
||||
}
|
||||
|
||||
|
||||
JS_IMPLEMENT_GETTER(ClassName, isDestroyed) { THIS_CHECK;
|
||||
RET_BOOL(_isDestroyed);
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
* `IMPLEMENT_ES5_CLASS` - implements some utility functions for class wrapping.
|
||||
* `JS_ASSIGN_METHOD` - in `init()`, assigns the given method to this class.
|
||||
* `JS_ASSIGN_GETTER` - in `init()`, assigns the given getter to this class.
|
||||
* `JS_ASSIGN_SETTER` - in `init()`, assigns both getter and setter to this class.
|
||||
It also takes only one argument because both have the same name.
|
||||
* `JS_IMPLEMENT_METHOD` - implements a method, the first argument is this class,
|
||||
the second is the name of the method being implemented.
|
||||
* `JS_IMPLEMENT_GETTER` - implements a getter, the first argument is this class,
|
||||
the second is the name of the getter being implemented.
|
||||
* `JS_IMPLEMENT_SETTER` - implements a setter, the first argument is this class,
|
||||
the second is the name of the setter being implemented.
|
|
@ -0,0 +1,723 @@
|
|||
#ifndef _EVENT_EMITTER_
|
||||
#define _EVENT_EMITTER_
|
||||
|
||||
|
||||
#include <addon-tools.hpp>
|
||||
|
||||
#include <string>
|
||||
#include <map>
|
||||
#include <deque>
|
||||
|
||||
|
||||
#define THIS_EVENT_EMITTER \
|
||||
EventEmitter *eventEmitter = ObjectWrap::Unwrap<EventEmitter>(info.This());
|
||||
|
||||
#define EVENT_EMITTER_THIS_CHECK \
|
||||
if (eventEmitter->_isDestroyed) return;
|
||||
|
||||
|
||||
// This template class provides static-member initialization in-header
|
||||
template <typename T>
|
||||
class StaticHolder {
|
||||
protected:
|
||||
static V8_STORE_FT _protoEventEmitter;
|
||||
static V8_STORE_FUNC _ctorEventEmitter;
|
||||
};
|
||||
template <typename T> V8_STORE_FT StaticHolder<T>::_protoEventEmitter;
|
||||
template <typename T> V8_STORE_FUNC StaticHolder<T>::_ctorEventEmitter;
|
||||
|
||||
|
||||
class EventEmitter : public StaticHolder<int>, public Nan::ObjectWrap {
|
||||
|
||||
typedef Nan::CopyablePersistentTraits<v8::Function>::CopyablePersistent FN_TYPE;
|
||||
typedef std::deque<FN_TYPE> VEC_TYPE;
|
||||
typedef std::map<std::string, VEC_TYPE> MAP_TYPE;
|
||||
typedef std::map<int, FN_TYPE> FNMAP_TYPE;
|
||||
typedef VEC_TYPE::iterator IT_TYPE;
|
||||
typedef MAP_TYPE::iterator MAP_IT_TYPE;
|
||||
typedef FNMAP_TYPE::iterator FNMAP_IT_TYPE;
|
||||
|
||||
public:
|
||||
|
||||
// Public V8 init
|
||||
static void init(V8_VAR_OBJ target) {
|
||||
|
||||
V8_VAR_FT proto = Nan::New<v8::FunctionTemplate>(newCtor);
|
||||
|
||||
proto->InstanceTemplate()->SetInternalFieldCount(1);
|
||||
proto->SetClassName(JS_STR("EventEmitter"));
|
||||
|
||||
|
||||
// Accessors
|
||||
V8_VAR_OT obj = proto->PrototypeTemplate();
|
||||
ACCESSOR_R(obj, isDestroyed);
|
||||
|
||||
|
||||
// -------- dynamic
|
||||
|
||||
Nan::SetPrototypeMethod(proto, "listenerCount", jsListenerCount);
|
||||
Nan::SetPrototypeMethod(proto, "addEventListener", jsAddListener);
|
||||
Nan::SetPrototypeMethod(proto, "addListener", jsAddListener);
|
||||
Nan::SetPrototypeMethod(proto, "dispatchEvent", jsDispatchEvent);
|
||||
Nan::SetPrototypeMethod(proto, "emit", jsEmit);
|
||||
Nan::SetPrototypeMethod(proto, "eventNames", jsEventNames);
|
||||
Nan::SetPrototypeMethod(proto, "getMaxListeners", jsGetMaxListeners);
|
||||
Nan::SetPrototypeMethod(proto, "listeners", jsListeners);
|
||||
Nan::SetPrototypeMethod(proto, "off", jsRemoveListener);
|
||||
Nan::SetPrototypeMethod(proto, "on", jsAddListener);
|
||||
Nan::SetPrototypeMethod(proto, "once", jsAddListener);
|
||||
Nan::SetPrototypeMethod(proto, "prependListener", jsPrependListener);
|
||||
Nan::SetPrototypeMethod(proto, "prependOnceListener", jsPrependOnceListener);
|
||||
Nan::SetPrototypeMethod(proto, "removeAllListeners", jsRemoveAllListeners);
|
||||
Nan::SetPrototypeMethod(proto, "removeEventListener", jsRemoveListener);
|
||||
Nan::SetPrototypeMethod(proto, "removeListener", jsRemoveListener);
|
||||
Nan::SetPrototypeMethod(proto, "setMaxListeners", jsSetMaxListeners);
|
||||
Nan::SetPrototypeMethod(proto, "rawListeners", jsRawListeners);
|
||||
|
||||
Nan::SetPrototypeMethod(proto, "destroy", jsDestroy);
|
||||
|
||||
// -------- static
|
||||
|
||||
V8_VAR_FUNC ctor = Nan::GetFunction(proto).ToLocalChecked();
|
||||
|
||||
V8_VAR_OBJ ctorObj = V8_VAR_OBJ::Cast(ctor);
|
||||
|
||||
Nan::SetMethod(ctorObj, "listenerCount", jsStaticListenerCount);
|
||||
|
||||
|
||||
_ctorEventEmitter.Reset(ctor);
|
||||
_protoEventEmitter.Reset(proto);
|
||||
|
||||
Nan::Set(target, JS_STR("EventEmitter"), ctor);
|
||||
|
||||
}
|
||||
|
||||
|
||||
// C++ side emit() method
|
||||
void emit(const std::string &name, int argc = 0, V8_VAR_VAL *argv = NULL) {
|
||||
|
||||
// Important! As actual get map[key] produces a new (empty) map entry
|
||||
if ( _listeners.find(name) == _listeners.end() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// A copy is intended, because handlers can call removeListener (and they DO)
|
||||
VEC_TYPE list = _listeners[name];
|
||||
|
||||
if (list.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (IT_TYPE it = list.begin(); it != list.end(); ++it) {
|
||||
|
||||
Nan::Callback callback(Nan::New(*it));
|
||||
|
||||
if ( ! callback.IsEmpty() ) {
|
||||
Nan::AsyncResource async("EventEmitter::cpp_emit()");
|
||||
callback.Call(handle(), argc, argv, &async);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
// C++ side on() method
|
||||
void on(const std::string &name, V8_VAR_FUNC cb) {
|
||||
|
||||
V8_VAR_OBJ me = handle();
|
||||
|
||||
V8_VAR_VAL onVal = Nan::Get(me, JS_STR("on")).ToLocalChecked();
|
||||
V8_VAR_FUNC onFunc = V8_VAR_FUNC::Cast(onVal);
|
||||
|
||||
Nan::Callback onCb(onFunc);
|
||||
|
||||
V8_VAR_VAL argv[] = { JS_STR(name.c_str()), cb };
|
||||
Nan::AsyncResource async("EventEmitter::cpp_on()");
|
||||
|
||||
onCb.Call(me, 2, argv, &async);
|
||||
|
||||
}
|
||||
|
||||
|
||||
void _destroy() { DES_CHECK;
|
||||
|
||||
_isDestroyed = true;
|
||||
|
||||
_listeners.clear();
|
||||
_raw.clear();
|
||||
_wrappedIds.clear();
|
||||
_rawIds.clear();
|
||||
|
||||
}
|
||||
|
||||
|
||||
~EventEmitter () { _destroy(); }
|
||||
|
||||
|
||||
protected:
|
||||
|
||||
EventEmitter () {
|
||||
_isDestroyed = false;
|
||||
_maxListeners = 0;
|
||||
_freeId = 0;
|
||||
}
|
||||
|
||||
|
||||
private:
|
||||
|
||||
static NAN_METHOD(newCtor) {
|
||||
|
||||
CTOR_CHECK("EventEmitter");
|
||||
|
||||
EventEmitter *eventEmitter = new EventEmitter();
|
||||
eventEmitter->Wrap(info.This());
|
||||
|
||||
RET_VALUE(info.This());
|
||||
|
||||
}
|
||||
|
||||
|
||||
static NAN_GETTER(isDestroyedGetter) { THIS_EVENT_EMITTER; EVENT_EMITTER_THIS_CHECK;
|
||||
|
||||
RET_BOOL(eventEmitter->_isDestroyed);
|
||||
|
||||
}
|
||||
|
||||
|
||||
// Deprecated static method
|
||||
static NAN_METHOD(jsStaticListenerCount) {
|
||||
|
||||
REQ_OBJ_ARG(0, obj);
|
||||
EventEmitter *eventEmitter = ObjectWrap::Unwrap<EventEmitter>(obj);
|
||||
REQ_UTF8_ARG(1, name);
|
||||
|
||||
const VEC_TYPE &list = eventEmitter->_listeners[*name];
|
||||
|
||||
RET_INT(static_cast<int>(list.size()));
|
||||
|
||||
}
|
||||
|
||||
|
||||
static NAN_METHOD(jsAddListener) {
|
||||
|
||||
_wrapListener(info);
|
||||
|
||||
RET_VALUE(info.This());
|
||||
|
||||
}
|
||||
|
||||
|
||||
static NAN_METHOD(jsDispatchEvent) { THIS_EVENT_EMITTER; EVENT_EMITTER_THIS_CHECK;
|
||||
|
||||
REQ_OBJ_ARG(0, event);
|
||||
|
||||
if ( ! event->Has(JS_STR("type")) ) {
|
||||
return Nan::ThrowError("Event must have the `type` property.");
|
||||
}
|
||||
|
||||
Nan::Utf8String name(event->Get(JS_STR("type")));
|
||||
|
||||
V8_VAR_VAL args = event;
|
||||
|
||||
eventEmitter->emit(*name, 1, &args);
|
||||
|
||||
RET_BOOL(true);
|
||||
|
||||
}
|
||||
|
||||
|
||||
static NAN_METHOD(jsEmit) { THIS_EVENT_EMITTER; EVENT_EMITTER_THIS_CHECK;
|
||||
|
||||
REQ_UTF8_ARG(0, name);
|
||||
|
||||
int length = info.Length();
|
||||
|
||||
std::vector< V8_VAR_VAL > args;
|
||||
|
||||
for (int i = 1; i < length; i++) {
|
||||
args.push_back(info[i]);
|
||||
}
|
||||
|
||||
eventEmitter->emit(*name, length - 1, &args[0]);
|
||||
|
||||
if ( eventEmitter->_listeners.find(*name) == eventEmitter->_listeners.end() ) {
|
||||
RET_BOOL(false);
|
||||
} else {
|
||||
RET_BOOL(true);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
static NAN_METHOD(jsEventNames) { THIS_EVENT_EMITTER; EVENT_EMITTER_THIS_CHECK;
|
||||
|
||||
V8_VAR_ARR jsNames = Nan::New<v8::Array>(eventEmitter->_raw.size());
|
||||
|
||||
if (eventEmitter->_raw.empty()) {
|
||||
RET_VALUE(jsNames);
|
||||
return;
|
||||
}
|
||||
|
||||
int i = 0;
|
||||
for (MAP_IT_TYPE it = eventEmitter->_raw.begin(); it != eventEmitter->_raw.end(); ++it, i++) {
|
||||
|
||||
jsNames->Set(JS_INT(i), JS_STR(it->first));
|
||||
|
||||
}
|
||||
|
||||
RET_VALUE(jsNames);
|
||||
|
||||
}
|
||||
|
||||
|
||||
static NAN_METHOD(jsGetMaxListeners) { THIS_EVENT_EMITTER; EVENT_EMITTER_THIS_CHECK;
|
||||
|
||||
RET_INT(eventEmitter->_maxListeners);
|
||||
|
||||
}
|
||||
|
||||
|
||||
static NAN_METHOD(jsListenerCount) { THIS_EVENT_EMITTER; EVENT_EMITTER_THIS_CHECK;
|
||||
|
||||
REQ_UTF8_ARG(0, name);
|
||||
|
||||
const VEC_TYPE &list = eventEmitter->_listeners[*name];
|
||||
|
||||
RET_INT(static_cast<int>(list.size()));
|
||||
|
||||
}
|
||||
|
||||
|
||||
static NAN_METHOD(jsListeners) { THIS_EVENT_EMITTER; EVENT_EMITTER_THIS_CHECK;
|
||||
|
||||
REQ_UTF8_ARG(0, name);
|
||||
|
||||
VEC_TYPE &list = eventEmitter->_listeners[*name];
|
||||
|
||||
V8_VAR_ARR jsListeners = Nan::New<v8::Array>(list.size());
|
||||
|
||||
if (list.empty()) {
|
||||
RET_VALUE(jsListeners);
|
||||
return;
|
||||
}
|
||||
|
||||
int i = 0;
|
||||
for (IT_TYPE it = list.begin(); it != list.end(); ++it, i++) {
|
||||
|
||||
jsListeners->Set(JS_INT(i), Nan::New(*it));
|
||||
|
||||
}
|
||||
|
||||
RET_VALUE(jsListeners);
|
||||
|
||||
}
|
||||
|
||||
|
||||
static inline void _reportListeners(
|
||||
const std::string &name,
|
||||
int numListeners,
|
||||
int maxListeners
|
||||
) {
|
||||
|
||||
std::string msg = "EventEmitter Warning: too many listeners (";
|
||||
msg += std::to_string(numListeners);
|
||||
msg += " > ";
|
||||
msg += std::to_string(maxListeners);
|
||||
msg += ") on '";
|
||||
msg += name;
|
||||
msg += "' event, possible memory leak.\n";
|
||||
|
||||
// Some JS magic to retrieve the call stack
|
||||
V8_VAR_STR code = JS_STR(
|
||||
"(new Error()).stack.split('\\n').slice(2).join('\\n')"
|
||||
);
|
||||
|
||||
v8::Local<v8::Value> stack = v8::Script::Compile(
|
||||
Nan::GetCurrentContext(), code
|
||||
).ToLocalChecked()->Run(
|
||||
Nan::GetCurrentContext()
|
||||
).ToLocalChecked();
|
||||
Nan::Utf8String stackStr(stack);
|
||||
|
||||
msg += *stackStr;
|
||||
|
||||
consoleLog(msg);
|
||||
|
||||
}
|
||||
|
||||
|
||||
static inline void _addListener(
|
||||
const Nan::FunctionCallbackInfo<v8::Value> &info,
|
||||
const std::string &name,
|
||||
V8_STORE_FUNC *cb,
|
||||
bool isFront
|
||||
) { THIS_EVENT_EMITTER; EVENT_EMITTER_THIS_CHECK;
|
||||
|
||||
V8_VAR_VAL args[] = { info[0], info[1] };
|
||||
eventEmitter->emit("newListener", 2, args);
|
||||
|
||||
if (isFront) {
|
||||
eventEmitter->_listeners[name].push_front(*cb);
|
||||
eventEmitter->_raw[name].push_front(*cb);
|
||||
} else {
|
||||
eventEmitter->_listeners[name].push_back(*cb);
|
||||
eventEmitter->_raw[name].push_back(*cb);
|
||||
}
|
||||
|
||||
int count = eventEmitter->_raw[name].size();
|
||||
|
||||
if (eventEmitter->_maxListeners > 0 && count > eventEmitter->_maxListeners) {
|
||||
|
||||
_reportListeners(name, count, eventEmitter->_maxListeners);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
static inline void _wrapListener(
|
||||
const Nan::FunctionCallbackInfo<v8::Value> &info,
|
||||
bool isFront = false
|
||||
) {
|
||||
|
||||
REQ_UTF8_ARG(0, name);
|
||||
REQ_FUN_ARG(1, cb);
|
||||
|
||||
V8_STORE_FUNC persistentCb;
|
||||
persistentCb.Reset(cb);
|
||||
|
||||
_addListener(info, *name, &persistentCb, isFront);
|
||||
|
||||
}
|
||||
|
||||
|
||||
static inline void _addOnceListener(
|
||||
const Nan::FunctionCallbackInfo<v8::Value> &info,
|
||||
const std::string &name,
|
||||
V8_STORE_FUNC *raw,
|
||||
V8_STORE_FUNC *cb,
|
||||
bool isFront
|
||||
) { THIS_EVENT_EMITTER; EVENT_EMITTER_THIS_CHECK;
|
||||
|
||||
V8_VAR_VAL args[] = { info[0], info[1] };
|
||||
eventEmitter->emit("newListener", 2, args);
|
||||
|
||||
if (isFront) {
|
||||
eventEmitter->_listeners[name].push_front(*cb);
|
||||
eventEmitter->_raw[name].push_front(*raw);
|
||||
} else {
|
||||
eventEmitter->_listeners[name].push_back(*cb);
|
||||
eventEmitter->_raw[name].push_back(*raw);
|
||||
}
|
||||
|
||||
int nextId = eventEmitter->_freeId++;
|
||||
eventEmitter->_wrappedIds[nextId] = *cb;
|
||||
eventEmitter->_rawIds[nextId] = *raw;
|
||||
|
||||
int count = eventEmitter->_raw[name].size();
|
||||
|
||||
if (eventEmitter->_maxListeners > 0 && count > eventEmitter->_maxListeners) {
|
||||
|
||||
_reportListeners(name, count, eventEmitter->_maxListeners);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
static inline void _wrapOnceListener(
|
||||
const Nan::FunctionCallbackInfo<v8::Value> &info,
|
||||
bool isFront = false
|
||||
) {
|
||||
|
||||
REQ_UTF8_ARG(0, name);
|
||||
REQ_FUN_ARG(1, raw);
|
||||
|
||||
V8_VAR_STR code = JS_STR(R"(
|
||||
((emitter, name, cb) => (...args) => {
|
||||
cb(...args);
|
||||
emitter.removeListener(name, cb);
|
||||
})
|
||||
)");
|
||||
|
||||
v8::Local<v8::Value> decor = v8::Script::Compile(
|
||||
Nan::GetCurrentContext(), code
|
||||
).ToLocalChecked()->Run(
|
||||
Nan::GetCurrentContext()
|
||||
).ToLocalChecked();
|
||||
Nan::Callback decorCb(Nan::To<v8::Function>(decor).ToLocalChecked());
|
||||
|
||||
V8_VAR_VAL argv[] = { info.This(), info[0], raw };
|
||||
Nan::AsyncResource async("EventEmitter::js_once()");
|
||||
V8_VAR_VAL wrapValue = decorCb.Call(3, argv, &async).ToLocalChecked();
|
||||
V8_VAR_FUNC wrap = V8_VAR_FUNC::Cast(wrapValue);
|
||||
|
||||
V8_STORE_FUNC persistentWrap;
|
||||
persistentWrap.Reset(wrap);
|
||||
|
||||
V8_STORE_FUNC persistentRaw;
|
||||
persistentRaw.Reset(raw);
|
||||
|
||||
_addOnceListener(info, *name, &persistentRaw, &persistentWrap, isFront);
|
||||
|
||||
}
|
||||
|
||||
|
||||
static NAN_METHOD(jsOnce) {
|
||||
|
||||
_wrapOnceListener(info);
|
||||
|
||||
RET_VALUE(info.This());
|
||||
|
||||
}
|
||||
|
||||
static NAN_METHOD(jsPrependListener) {
|
||||
|
||||
_wrapListener(info, true);
|
||||
|
||||
RET_VALUE(info.This());
|
||||
|
||||
}
|
||||
|
||||
static NAN_METHOD(jsPrependOnceListener) {
|
||||
|
||||
_wrapOnceListener(info, true);
|
||||
|
||||
RET_VALUE(info.This());
|
||||
|
||||
}
|
||||
|
||||
|
||||
static NAN_METHOD(jsRemoveAllListeners) { THIS_EVENT_EMITTER; EVENT_EMITTER_THIS_CHECK;
|
||||
|
||||
RET_VALUE(info.This());
|
||||
|
||||
if (info.Length() == 0) {
|
||||
|
||||
MAP_TYPE tmpMap = eventEmitter->_raw;
|
||||
|
||||
eventEmitter->_listeners.clear();
|
||||
eventEmitter->_raw.clear();
|
||||
eventEmitter->_wrappedIds.clear();
|
||||
eventEmitter->_rawIds.clear();
|
||||
|
||||
for (MAP_IT_TYPE itMap = tmpMap.begin(); itMap != tmpMap.end(); ++itMap) {
|
||||
|
||||
const std::string ¤t = itMap->first;
|
||||
VEC_TYPE &list = itMap->second;
|
||||
|
||||
for (IT_TYPE it = list.begin(); it != list.end(); ++it) {
|
||||
|
||||
V8_VAR_VAL args[] = { JS_STR(current.c_str()), Nan::New(*it) };
|
||||
eventEmitter->emit("removeListener", 2, args);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
REQ_UTF8_ARG(0, n);
|
||||
|
||||
std::string name = std::string(*n);
|
||||
VEC_TYPE &list = eventEmitter->_raw[name];
|
||||
|
||||
if (list.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (eventEmitter->_rawIds.size()) {
|
||||
|
||||
std::vector<int> removes;
|
||||
|
||||
for (IT_TYPE it = list.begin(); it != list.end(); ++it) {
|
||||
|
||||
FN_TYPE fn = *it;
|
||||
|
||||
for (FNMAP_IT_TYPE itRaw = eventEmitter->_rawIds.begin(); itRaw != eventEmitter->_rawIds.end(); ++itRaw) {
|
||||
if (fn == itRaw->second) {
|
||||
removes.push_back(itRaw->first);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (removes.size()) {
|
||||
for (std::vector<int>::const_iterator it = removes.begin(); it != removes.end(); ++it) {
|
||||
|
||||
eventEmitter->_wrappedIds.erase(*it);
|
||||
eventEmitter->_rawIds.erase(*it);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
VEC_TYPE tmpVec = eventEmitter->_raw[name];
|
||||
|
||||
eventEmitter->_listeners[name].clear();
|
||||
eventEmitter->_raw[name].clear();
|
||||
|
||||
for (IT_TYPE it = tmpVec.begin(); it != tmpVec.end(); ++it) {
|
||||
|
||||
V8_VAR_VAL args[] = { JS_STR(name.c_str()), Nan::New(*it) };
|
||||
eventEmitter->emit("removeListener", 2, args);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
static NAN_METHOD(jsRemoveListener) { THIS_EVENT_EMITTER; EVENT_EMITTER_THIS_CHECK;
|
||||
|
||||
RET_VALUE(info.This());
|
||||
|
||||
REQ_UTF8_ARG(0, n);
|
||||
REQ_FUN_ARG(1, raw);
|
||||
|
||||
V8_STORE_FUNC persistentRaw;
|
||||
persistentRaw.Reset(raw);
|
||||
|
||||
std::string name = std::string(*n);
|
||||
|
||||
VEC_TYPE &rawList = eventEmitter->_raw[name];
|
||||
|
||||
if (rawList.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
V8_VAR_VAL args[] = { info[0], info[1] };
|
||||
|
||||
for (IT_TYPE it = rawList.begin(); it != rawList.end(); ++it) {
|
||||
|
||||
if (*it == persistentRaw) {
|
||||
rawList.erase(it);
|
||||
if (rawList.empty()) {
|
||||
eventEmitter->_raw.erase(name);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
VEC_TYPE &wrapList = eventEmitter->_listeners[name];
|
||||
|
||||
if (eventEmitter->_wrappedIds.size() == 0) {
|
||||
|
||||
for (IT_TYPE it = wrapList.begin(); it != wrapList.end(); ++it) {
|
||||
|
||||
if (*it == persistentRaw) {
|
||||
wrapList.erase(it);
|
||||
if (wrapList.empty()) {
|
||||
eventEmitter->_listeners.erase(name);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
eventEmitter->emit("removeListener", 2, args);
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
|
||||
for (FNMAP_IT_TYPE itRaw = eventEmitter->_rawIds.begin(); itRaw != eventEmitter->_rawIds.end(); ++itRaw) {
|
||||
|
||||
if (persistentRaw == itRaw->second) {
|
||||
|
||||
FN_TYPE fn = eventEmitter->_wrappedIds[itRaw->first];
|
||||
|
||||
for (IT_TYPE it = wrapList.begin(); it != wrapList.end(); ++it) {
|
||||
|
||||
if (*it == fn) {
|
||||
wrapList.erase(it);
|
||||
if (wrapList.empty()) {
|
||||
eventEmitter->_listeners.erase(name);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
eventEmitter->_wrappedIds.erase(itRaw->first);
|
||||
eventEmitter->_rawIds.erase(itRaw->first);
|
||||
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
eventEmitter->emit("removeListener", 2, args);
|
||||
|
||||
}
|
||||
|
||||
|
||||
static NAN_METHOD(jsSetMaxListeners) { THIS_EVENT_EMITTER; EVENT_EMITTER_THIS_CHECK;
|
||||
|
||||
REQ_INT32_ARG(0, value);
|
||||
|
||||
eventEmitter->_maxListeners = value;
|
||||
|
||||
RET_VALUE(info.This());
|
||||
|
||||
}
|
||||
|
||||
|
||||
static NAN_METHOD(jsRawListeners) { THIS_EVENT_EMITTER; EVENT_EMITTER_THIS_CHECK;
|
||||
|
||||
REQ_UTF8_ARG(0, name);
|
||||
|
||||
VEC_TYPE &list = eventEmitter->_raw[*name];
|
||||
|
||||
V8_VAR_ARR jsListeners = Nan::New<v8::Array>(list.size());
|
||||
|
||||
if (list.empty()) {
|
||||
RET_VALUE(jsListeners);
|
||||
return;
|
||||
}
|
||||
|
||||
int i = 0;
|
||||
for (IT_TYPE it = list.begin(); it != list.end(); ++it, i++) {
|
||||
|
||||
jsListeners->Set(JS_INT(i), Nan::New(*it));
|
||||
|
||||
}
|
||||
|
||||
RET_VALUE(jsListeners);
|
||||
|
||||
}
|
||||
|
||||
|
||||
static NAN_METHOD(jsDestroy) { THIS_EVENT_EMITTER; EVENT_EMITTER_THIS_CHECK;
|
||||
|
||||
eventEmitter->emit("destroy");
|
||||
|
||||
eventEmitter->_destroy();
|
||||
|
||||
}
|
||||
|
||||
|
||||
private:
|
||||
|
||||
bool _isDestroyed;
|
||||
|
||||
int _maxListeners;
|
||||
|
||||
MAP_TYPE _listeners;
|
||||
MAP_TYPE _raw;
|
||||
|
||||
int _freeId;
|
||||
FNMAP_TYPE _wrappedIds;
|
||||
FNMAP_TYPE _rawIds;
|
||||
|
||||
};
|
||||
|
||||
|
||||
#endif // _EVENT_EMITTER_
|
|
@ -1,65 +0,0 @@
|
|||
'use strict';
|
||||
|
||||
const path = require('node:path');
|
||||
|
||||
|
||||
const nameWindows = 'windows';
|
||||
const platformAndArch = `${process.platform}-${process.arch}`;
|
||||
|
||||
const platformNames = {
|
||||
'win32-x64': nameWindows,
|
||||
'linux-x64': 'linux',
|
||||
'darwin-x64': 'osx',
|
||||
'linux-arm64': 'aarch64',
|
||||
};
|
||||
|
||||
const platformName = platformNames[platformAndArch] || platformAndArch;
|
||||
|
||||
const isWindows = platformName === nameWindows;
|
||||
|
||||
|
||||
const getPaths = (dir) => {
|
||||
dir = dir.replace(/\\/g, '/');
|
||||
|
||||
const bin = `${dir}/bin-${platformName}`;
|
||||
const include = `${dir}/include`;
|
||||
|
||||
if (isWindows) {
|
||||
process.env.path = `${bin};${process.env.path ? `${process.env.path}` : ''}`;
|
||||
}
|
||||
|
||||
return { bin, include };
|
||||
};
|
||||
|
||||
|
||||
const getBin = () => {
|
||||
return `bin-${platformName}`;
|
||||
};
|
||||
|
||||
const getPlatform = () => {
|
||||
return platformName;
|
||||
};
|
||||
|
||||
const getInclude = () => {
|
||||
let napi = null;
|
||||
try {
|
||||
napi = require('node-addon-api');
|
||||
} catch (ex) {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
const rootPath = path.resolve(`${__dirname}/..`).replace(/\\/g, '/');
|
||||
const napiInclude = napi ? napi.include_dir.replace(/\\/g, '/') : '';
|
||||
const thisInclude = `${rootPath}/include`;
|
||||
const includePath = `${napiInclude} ${thisInclude}`;
|
||||
|
||||
return includePath;
|
||||
};
|
||||
|
||||
|
||||
module.exports = {
|
||||
getPaths,
|
||||
getBin,
|
||||
getPlatform,
|
||||
getInclude,
|
||||
};
|
|
@ -1,41 +0,0 @@
|
|||
'use strict';
|
||||
|
||||
const assert = require('node:assert').strict;
|
||||
const { describe, it } = require('node:test');
|
||||
|
||||
const tools = require('.');
|
||||
|
||||
|
||||
describe('AT / include', () => {
|
||||
const stringMethods = ['getBin', 'getPlatform', 'getInclude'];
|
||||
|
||||
stringMethods.forEach((name) => {
|
||||
describe(`#${name}()`, () => {
|
||||
it('is a function', () => {
|
||||
assert.strictEqual(typeof tools[name], 'function');
|
||||
});
|
||||
|
||||
it('returns an object', () => {
|
||||
assert.strictEqual(typeof tools[name](), 'string');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('#getPaths()', () => {
|
||||
it('is a function', () => {
|
||||
assert.strictEqual(typeof tools.getPaths, 'function');
|
||||
});
|
||||
|
||||
it('returns an object', () => {
|
||||
assert.strictEqual(typeof tools.getPaths(__dirname), 'object');
|
||||
});
|
||||
|
||||
it('has "include" string', () => {
|
||||
assert.strictEqual(typeof tools.getPaths(__dirname).include, 'string');
|
||||
});
|
||||
|
||||
it('has "bin" string', () => {
|
||||
assert.strictEqual(typeof tools.getPaths(__dirname).include, 'string');
|
||||
});
|
||||
});
|
||||
});
|
|
@ -1,81 +0,0 @@
|
|||
# Snippets
|
||||
|
||||
## C++ Addon building
|
||||
|
||||
### Binary distribution
|
||||
|
||||
In **package.json** use the `"postinstall"` script to download the libraries.
|
||||
For example the following structure might work. Note that **Addon Tools** will
|
||||
append any given URL with `/${getPlatform()}.gz`
|
||||
|
||||
In **package.json**:
|
||||
|
||||
```
|
||||
"scripts": {
|
||||
"postinstall": "node install",
|
||||
},
|
||||
"dependencies": {
|
||||
"addon-tools-raub": "^7.0.0",
|
||||
},
|
||||
"devDependencies": {
|
||||
"node-addon-api": "^5.0.0"
|
||||
}
|
||||
```
|
||||
|
||||
Create the **install.js** file, see `install` in [index.d.ts](/index.d.ts).
|
||||
**Addon Tools** will unpack (using **tar**) the downloaded file into the platform binary
|
||||
directory. E.g. on Windows it will be **bin-windows**.
|
||||
|
||||
* For a dependency package:
|
||||
|
||||
Place the following piece of code into the `index.js` without changes.
|
||||
|
||||
```
|
||||
module.exports = require('addon-tools-raub').getPaths(__dirname);
|
||||
```
|
||||
|
||||
* For a compiled addon:
|
||||
|
||||
Require the `ADDON.node` in your **index.js** from the platform-specific directory.
|
||||
```
|
||||
const { getBin } = require('addon-tools-raub');
|
||||
const core = require(`./${getBin()}/ADDON`);
|
||||
```
|
||||
|
||||
|
||||
Publishing binaries is done by attaching a GZIPped platform folder to a 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
|
||||
**install.js**.
|
||||
|
||||
> NOTE: You can publish your binaries to anywhere, not necessarily GitHub.
|
||||
Just tweak **YOUR install.js** script as appropriate. The only limitation
|
||||
from **Addon Tools** is that it should be a GZIPped set of files/folders.
|
||||
|
||||
|
||||
### GYP Variables
|
||||
|
||||
```
|
||||
'variables': {
|
||||
'bin': '<!(node -p "require(\'addon-tools-raub\').getBin()")',
|
||||
'DEPS_include': '<!(node -p "require(\'DEPS\').getInclude()")',
|
||||
'DEPS_bin': '<!(node -p "require(\'DEPS\').getBin()")',
|
||||
},
|
||||
```
|
||||
|
||||
* `bin` - the name of this platform's binary directory, e.g. *bin-linux*.
|
||||
* `DEPS_include` - the include folder for some dependency package.
|
||||
* `DEPS_bin` - the binary folder for some dependency package.
|
||||
|
||||
|
||||
|
||||
### Include directories
|
||||
|
||||
```
|
||||
'include_dirs' : [
|
||||
'<!@(node -p "require(\'addon-tools-raub\').getInclude()")',
|
||||
'<(DEPS_include)',
|
||||
],
|
||||
```
|
||||
|
||||
See example of a working [**binding.gyp** here](/test-addon/binding.gyp)
|
|
@ -1,361 +0,0 @@
|
|||
declare module "addon-tools-raub" {
|
||||
type Stats = import('node:fs').Stats;
|
||||
type Writable = import('node:stream').Writable;
|
||||
type Readable = import('node:stream').Readable;
|
||||
type WritableOptions = import('node:stream').WritableOptions;
|
||||
|
||||
/**
|
||||
* Get the internal paths for an addon
|
||||
*
|
||||
* Returns a set of platform dependent paths depending on the input dir
|
||||
*/
|
||||
export const getPaths: (dir: string) => Readonly<{
|
||||
/**
|
||||
* Path to binaries
|
||||
*
|
||||
* Platform binary directory absolute path for this `dir`
|
||||
*/
|
||||
bin: string;
|
||||
/**
|
||||
* Path to include
|
||||
*
|
||||
* Include directory for this `dir`
|
||||
*/
|
||||
include: string;
|
||||
}>;
|
||||
|
||||
type TPlatformName = 'windows' | 'linux' | 'osx' | 'aarch64';
|
||||
type TPlatformDir = `bin-${TPlatformName}`;
|
||||
|
||||
/**
|
||||
* Get the platform-specific binary directory name
|
||||
*/
|
||||
export const getBin: () => TPlatformDir;
|
||||
|
||||
/**
|
||||
* Get the platform identifier
|
||||
*/
|
||||
export const getPlatform: () => TPlatformName;
|
||||
|
||||
/**
|
||||
* Get the include directories for **binding.gyp**
|
||||
*
|
||||
* Both 'addon-tools-raub' and 'node-addon-api' include paths.
|
||||
* In binding.gyp: `'<!@(node -p "require(\'addon-tools-raub\').getInclude()")'`
|
||||
*/
|
||||
export const getInclude: () => string;
|
||||
|
||||
/**
|
||||
* Install binaries
|
||||
*
|
||||
* Downloads and unpacks the platform specific binary for the calling package.
|
||||
* To use it, create a new script for your package, which may as well be named
|
||||
* **install.js**, with the following content:
|
||||
*
|
||||
* ```
|
||||
* 'use strict';
|
||||
* const { install } = require('addon-tools-raub');
|
||||
* const prefix = 'https://github.com/USER/ADDON-NAME/releases/download';
|
||||
* const tag = '1.0.0';
|
||||
* install(`${prefix}/${tag}`);
|
||||
* ```
|
||||
*
|
||||
* * `prefix` - the constant base part of the download url.
|
||||
* * `tag` - the version-dependent part of the url.
|
||||
*
|
||||
* ```
|
||||
* "scripts": {
|
||||
* "postinstall": "node install"
|
||||
* },
|
||||
* ```
|
||||
*/
|
||||
export const install: (folder: string) => Promise<boolean>;
|
||||
|
||||
|
||||
/**
|
||||
* Copy binary
|
||||
*
|
||||
* Copies the addon binary from `src/build/Release` to the platform-specific folder.
|
||||
*
|
||||
* ```
|
||||
* "scripts": {
|
||||
* * "build": "node-gyp rebuild && node -e \"require('addon-tools-raub').cpbin('ADDON')\""
|
||||
* },
|
||||
* ```
|
||||
*
|
||||
* Here ADDON should be replaced with the name of your addon, without `.node` extension.
|
||||
*/
|
||||
export const cpbin: (name: string) => Promise<void>;
|
||||
|
||||
|
||||
/**
|
||||
* Packs binaries into GZIP
|
||||
*
|
||||
* Example of `actionPack` usage in **Github Actions**:
|
||||
*
|
||||
* ```
|
||||
* - name: Pack Files
|
||||
* id: pack-files
|
||||
* run: node -e "require('addon-tools-raub').actionPack()" >> $GITHUB_OUTPUT
|
||||
* - name: Store Binaries
|
||||
* uses: softprops/action-gh-release@v1
|
||||
* with:
|
||||
* files: ${{ steps.pack-files.outputs.pack }}
|
||||
* ```
|
||||
*/
|
||||
export const actionPack: () => Promise<void>;
|
||||
|
||||
|
||||
/**
|
||||
* Download to memory
|
||||
*
|
||||
* Accepts an **URL**, and returns an in-memory file Buffer,
|
||||
* when the file is loaded. Use for small files, as the whole
|
||||
* file will be loaded into memory at once.
|
||||
*
|
||||
* ```
|
||||
* download(srcUrl).then(data => useData(data), err => emit('error', err));
|
||||
* ```
|
||||
* or
|
||||
* ```
|
||||
* const data = await download(srcUrl);
|
||||
* useData(data);
|
||||
* ```
|
||||
*/
|
||||
export const download: (url: string) => Promise<Buffer>;
|
||||
|
||||
/**
|
||||
* (async) Read a file
|
||||
*
|
||||
* Reads a whole file to string, NOT A Buffer
|
||||
*/
|
||||
|
||||
|
||||
type ComposeFnParam = (source: any) => void;
|
||||
/**
|
||||
* WritableBuffer
|
||||
*
|
||||
* A [Writable](https://nodejs.org/api/stream.html#stream_writable_streams)
|
||||
* stream buffer, that is stored in-memory and can be fully
|
||||
* obtained when writing was finished. It is equivalent to stream-writing
|
||||
* a temporary file and then reading it into a `Buffer`.
|
||||
*/
|
||||
export class WritableBuffer implements Writable {
|
||||
constructor();
|
||||
/**
|
||||
* Get the downloaded data
|
||||
* Use `stream.get()` to obtain the data when writing was finished
|
||||
*/
|
||||
get(): Buffer;
|
||||
|
||||
// ----------- implements Writable
|
||||
|
||||
readonly writable: boolean;
|
||||
readonly writableEnded: boolean;
|
||||
readonly writableFinished: boolean;
|
||||
readonly writableHighWaterMark: number;
|
||||
readonly writableLength: number;
|
||||
readonly writableObjectMode: boolean;
|
||||
readonly writableCorked: number;
|
||||
destroyed: boolean;
|
||||
readonly closed: boolean;
|
||||
readonly errored: Error | null;
|
||||
readonly writableNeedDrain: boolean;
|
||||
constructor(opts?: WritableOptions);
|
||||
_write(chunk: any, encoding: BufferEncoding, callback: (error?: Error | null) => void): void;
|
||||
_writev?(
|
||||
chunks: Array<{
|
||||
chunk: any;
|
||||
encoding: BufferEncoding;
|
||||
}>,
|
||||
callback: (error?: Error | null) => void,
|
||||
): void;
|
||||
_construct?(callback: (error?: Error | null) => void): void;
|
||||
_destroy(error: Error | null, callback: (error?: Error | null) => void): void;
|
||||
_final(callback: (error?: Error | null) => void): void;
|
||||
write(chunk: any, callback?: (error: Error | null | undefined) => void): boolean;
|
||||
write(chunk: any, encoding: BufferEncoding, callback?: (error: Error | null | undefined) => void): boolean;
|
||||
setDefaultEncoding(encoding: BufferEncoding): this;
|
||||
end(cb?: () => void): this;
|
||||
end(chunk: any, cb?: () => void): this;
|
||||
end(chunk: any, encoding: BufferEncoding, cb?: () => void): this;
|
||||
cork(): void;
|
||||
uncork(): void;
|
||||
destroy(error?: Error): this;
|
||||
addListener(event: "close", listener: () => void): this;
|
||||
addListener(event: "drain", listener: () => void): this;
|
||||
addListener(event: "error", listener: (err: Error) => void): this;
|
||||
addListener(event: "finish", listener: () => void): this;
|
||||
addListener(event: "pipe", listener: (src: Readable) => void): this;
|
||||
addListener(event: "unpipe", listener: (src: Readable) => void): this;
|
||||
addListener(event: string | symbol, listener: (...args: any[]) => void): this;
|
||||
emit(event: "close"): boolean;
|
||||
emit(event: "drain"): boolean;
|
||||
emit(event: "error", err: Error): boolean;
|
||||
emit(event: "finish"): boolean;
|
||||
emit(event: "pipe", src: Readable): boolean;
|
||||
emit(event: "unpipe", src: Readable): boolean;
|
||||
emit(event: string | symbol, ...args: any[]): boolean;
|
||||
on(event: "close", listener: () => void): this;
|
||||
on(event: "drain", listener: () => void): this;
|
||||
on(event: "error", listener: (err: Error) => void): this;
|
||||
on(event: "finish", listener: () => void): this;
|
||||
on(event: "pipe", listener: (src: Readable) => void): this;
|
||||
on(event: "unpipe", listener: (src: Readable) => void): this;
|
||||
on(event: string | symbol, listener: (...args: any[]) => void): this;
|
||||
once(event: "close", listener: () => void): this;
|
||||
once(event: "drain", listener: () => void): this;
|
||||
once(event: "error", listener: (err: Error) => void): this;
|
||||
once(event: "finish", listener: () => void): this;
|
||||
once(event: "pipe", listener: (src: Readable) => void): this;
|
||||
once(event: "unpipe", listener: (src: Readable) => void): this;
|
||||
once(event: string | symbol, listener: (...args: any[]) => void): this;
|
||||
prependListener(event: "close", listener: () => void): this;
|
||||
prependListener(event: "drain", listener: () => void): this;
|
||||
prependListener(event: "error", listener: (err: Error) => void): this;
|
||||
prependListener(event: "finish", listener: () => void): this;
|
||||
prependListener(event: "pipe", listener: (src: Readable) => void): this;
|
||||
prependListener(event: "unpipe", listener: (src: Readable) => void): this;
|
||||
prependListener(event: string | symbol, listener: (...args: any[]) => void): this;
|
||||
prependOnceListener(event: "close", listener: () => void): this;
|
||||
prependOnceListener(event: "drain", listener: () => void): this;
|
||||
prependOnceListener(event: "error", listener: (err: Error) => void): this;
|
||||
prependOnceListener(event: "finish", listener: () => void): this;
|
||||
prependOnceListener(event: "pipe", listener: (src: Readable) => void): this;
|
||||
prependOnceListener(event: "unpipe", listener: (src: Readable) => void): this;
|
||||
prependOnceListener(event: string | symbol, listener: (...args: any[]) => void): this;
|
||||
removeListener(event: "close", listener: () => void): this;
|
||||
removeListener(event: "drain", listener: () => void): this;
|
||||
removeListener(event: "error", listener: (err: Error) => void): this;
|
||||
removeListener(event: "finish", listener: () => void): this;
|
||||
removeListener(event: "pipe", listener: (src: Readable) => void): this;
|
||||
removeListener(event: "unpipe", listener: (src: Readable) => void): this;
|
||||
removeListener(event: string | symbol, listener: (...args: any[]) => void): this;
|
||||
pipe<T extends NodeJS.WritableStream>(
|
||||
destination: T,
|
||||
options?: {
|
||||
end?: boolean | undefined;
|
||||
},
|
||||
): T;
|
||||
compose<T extends NodeJS.ReadableStream>(
|
||||
stream: T | ComposeFnParam | Iterable<T> | AsyncIterable<T>,
|
||||
options?: { signal: AbortSignal },
|
||||
): T;
|
||||
off(eventName: string | symbol, listener: (...args: any[]) => void): this;
|
||||
removeAllListeners(event?: string | symbol): this;
|
||||
setMaxListeners(n: number): this;
|
||||
getMaxListeners(): number;
|
||||
listeners(eventName: string | symbol): Function[];
|
||||
rawListeners(eventName: string | symbol): Function[];
|
||||
emit(eventName: string | symbol, ...args: any[]): boolean;
|
||||
listenerCount(eventName: string | symbol, listener?: Function): number;
|
||||
eventNames(): Array<string | symbol>;
|
||||
}
|
||||
|
||||
|
||||
export const read: (name: string) => Promise<string>;
|
||||
|
||||
/**
|
||||
* (async) Write a file
|
||||
*/
|
||||
export const write: (name: string, text: string) => Promise<void>;
|
||||
|
||||
/**
|
||||
* (async) Copy a file
|
||||
*/
|
||||
export const copy: (src: string, dest: string) => Promise<void>;
|
||||
|
||||
/**
|
||||
* (async) Check if a file/folder exists
|
||||
*/
|
||||
export const exists: (name: string) => Promise<boolean>;
|
||||
|
||||
/**
|
||||
* (async) Create an empty folder
|
||||
*/
|
||||
export const mkdir: (name: string) => Promise<void>;
|
||||
|
||||
/**
|
||||
* (async) Get status on a file
|
||||
*/
|
||||
export const stat: (name: string) => Promise<Stats>;
|
||||
|
||||
/**
|
||||
* (async) Check if the path is a folder
|
||||
*/
|
||||
export const isDir: (name: string) => Promise<boolean>;
|
||||
|
||||
/**
|
||||
* (async) Check if the path is a file
|
||||
*/
|
||||
export const isFile: (name: string) => Promise<boolean>;
|
||||
|
||||
/**
|
||||
* Cut the path one folder up
|
||||
*/
|
||||
export const dirUp: (dir: string) => string;
|
||||
|
||||
/**
|
||||
* (async) Create a directory
|
||||
*
|
||||
* Like `mkdir -p`, makes sure a directory exists
|
||||
*/
|
||||
export const ensuredir: (dir: string) => Promise<void>;
|
||||
|
||||
/**
|
||||
* (async) Copy a file
|
||||
*
|
||||
* Copy a file, `dest` folder is created if needed
|
||||
*/
|
||||
export const copysafe: (src: string, dest: string) => Promise<void>;
|
||||
|
||||
/**
|
||||
* (async) Read a directory
|
||||
*
|
||||
* Get file/folder names of the 1st level
|
||||
*/
|
||||
export const readdir: (src: string, dest: string) => Promise<ReadonlyArray<string>>;
|
||||
|
||||
/**
|
||||
* (async) List subdirectories
|
||||
*
|
||||
* Get folder paths (concatenated with input) of the 1st level
|
||||
*/
|
||||
export const subdirs: (name: string) => Promise<ReadonlyArray<string>>;
|
||||
|
||||
/**
|
||||
* (async) List nested files
|
||||
*
|
||||
* Get file paths (concatenated with input) of the 1st level
|
||||
*/
|
||||
export const subfiles: (name: string) => Promise<ReadonlyArray<string>>;
|
||||
|
||||
/**
|
||||
* (async) Get all nested files recursively
|
||||
*
|
||||
* Folder paths are omitted by default.
|
||||
* Order is: shallow-to-deep, each subdirectory lists dirs-then-files.
|
||||
*/
|
||||
export const traverse: (name: string, showDirs?: boolean) => Promise<ReadonlyArray<string>>;
|
||||
|
||||
/**
|
||||
* (async) Copy a directory
|
||||
*
|
||||
* Copy a folder with all the contained files
|
||||
*/
|
||||
export const copyall: (src: string, dest: string) => Promise<void>;
|
||||
|
||||
/**
|
||||
* (async) Remove a directory
|
||||
*
|
||||
* Like `rm -rf`, removes everything recursively
|
||||
*/
|
||||
export const rmdir: (name: string) => Promise<void>;
|
||||
|
||||
/**
|
||||
* (async) Remove a file
|
||||
*
|
||||
* Must be a file, not a folder. Just `fs.unlink`.
|
||||
*/
|
||||
export const rm: (name: string) => Promise<void>;
|
||||
}
|
94
index.js
94
index.js
|
@ -1,3 +1,95 @@
|
|||
'use strict';
|
||||
|
||||
module.exports = Object.assign({}, require('./include'), require('./utils'));
|
||||
const path = require('path');
|
||||
|
||||
|
||||
const rootPath = __dirname.replace(/\\/g, '/');
|
||||
|
||||
const nanInclude = path.dirname(require.resolve('nan')).replace(/\\/g, '/');
|
||||
const thisInclude = `${rootPath}/include`;
|
||||
|
||||
|
||||
const isWindows = process.platform === 'win32';
|
||||
|
||||
const names = ['win32', 'win64', 'linux32', 'linux64', 'mac64'];
|
||||
|
||||
const prefixName = name => `bin-${name}`;
|
||||
|
||||
const getPlatformDir = platform => {
|
||||
switch (platform) {
|
||||
case 'win32' : return process.arch === 'x64' ? 'win64' : 'win32';
|
||||
case 'linux' :
|
||||
if (process.arch === 'x32') {
|
||||
throw new Error('Linux x32 not supported since 4.0.0.');
|
||||
}
|
||||
return 'linux64';
|
||||
case 'darwin' :
|
||||
if (process.arch === 'x32') {
|
||||
throw new Error('Mac x32 not supported.');
|
||||
}
|
||||
return 'mac64';
|
||||
default : throw new Error(`Platform "${platform}" is not supported.`);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
const currentDir = prefixName(getPlatformDir(process.platform));
|
||||
const remDirs = names.map(prefixName).filter(n => n !== currentDir);
|
||||
|
||||
|
||||
const paths = dir => {
|
||||
|
||||
dir = dir.replace(/\\/g, '/');
|
||||
|
||||
const binPath = `${dir}/${currentDir}`;
|
||||
|
||||
if (isWindows) {
|
||||
process.env.path = `${binPath};${process.env.path ? `${process.env.path}` : ''}`;
|
||||
}
|
||||
|
||||
const remPath = remDirs.map(k => `${dir}/${k}`).join(' ');
|
||||
const includePath = `${dir}/include`;
|
||||
|
||||
return {
|
||||
|
||||
binPath,
|
||||
remPath,
|
||||
includePath,
|
||||
|
||||
bin() { console.log(binPath); },
|
||||
rem() { console.log(remPath); },
|
||||
include() { console.log(includePath); },
|
||||
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
|
||||
const includePath = `${nanInclude} ${thisInclude}`;
|
||||
const binPath = currentDir;
|
||||
|
||||
const mkdirPath = isWindows ? `${rootPath}/_mkdir.bat` : 'mkdir';
|
||||
const rmPath = isWindows ? `${rootPath}/_rm.bat` : 'rm';
|
||||
const cpPath = isWindows ? `${rootPath}/_cp.bat` : 'cp';
|
||||
|
||||
|
||||
module.exports = {
|
||||
|
||||
paths,
|
||||
|
||||
binPath,
|
||||
rootPath,
|
||||
includePath,
|
||||
mkdirPath,
|
||||
rmPath,
|
||||
cpPath,
|
||||
|
||||
bin() { return console.log(binPath); },
|
||||
root() { return console.log(rootPath); },
|
||||
include() { console.log(includePath); },
|
||||
|
||||
mkdir() { return console.log(mkdirPath); },
|
||||
rm() { return console.log(rmPath); },
|
||||
cp() { return console.log(cpPath); },
|
||||
|
||||
};
|
||||
|
|
File diff suppressed because it is too large
Load Diff
49
package.json
49
package.json
|
@ -1,60 +1,39 @@
|
|||
{
|
||||
"author": "Luis Blanco <luisblanco1337@gmail.com>",
|
||||
"name": "addon-tools-raub",
|
||||
"version": "7.4.0",
|
||||
"version": "4.2.0",
|
||||
"description": "Helpers for Node.js addons and dependency packages",
|
||||
"license": "MIT",
|
||||
"main": "index.js",
|
||||
"keywords": [
|
||||
"support",
|
||||
"headers",
|
||||
"include",
|
||||
"eventemitter",
|
||||
"events",
|
||||
"utils",
|
||||
"c++",
|
||||
"addon",
|
||||
"bindings",
|
||||
"native",
|
||||
"napi",
|
||||
"gyp"
|
||||
],
|
||||
"files": [
|
||||
"include",
|
||||
"utils.js",
|
||||
"utils.d.ts",
|
||||
"index.js",
|
||||
"index.d.ts",
|
||||
"utils",
|
||||
"LICENSE",
|
||||
"package.json",
|
||||
"README.md"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18.16.0",
|
||||
"npm": ">=9.5.1"
|
||||
},
|
||||
"scripts": {
|
||||
"eslint": "eslint .",
|
||||
"test": "node --test --watch .",
|
||||
"test-ci": "node --test",
|
||||
"build-test": "cd test-addon && node-gyp rebuild -j max --silent && cd .."
|
||||
"node": ">=10.13.0",
|
||||
"npm": ">=6.4.1"
|
||||
},
|
||||
"maintainers": [
|
||||
{
|
||||
"name": "Luis Blanco",
|
||||
"email": "luisblanco1337@gmail.com",
|
||||
"skype": "rauber666"
|
||||
}
|
||||
],
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/node-3d/addon-tools-raub.git"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"node-addon-api": "^7.0.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"node-addon-api": {
|
||||
"optional": true
|
||||
}
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^20.8.3",
|
||||
"eslint": "^8.51.0",
|
||||
"eslint-plugin-node": "^11.1.0",
|
||||
"node-addon-api": "^7.0.0",
|
||||
"typescript": "^5.2.2"
|
||||
"dependencies": {
|
||||
"nan": "2.12.1"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,37 +0,0 @@
|
|||
{
|
||||
'targets': [{
|
||||
'target_name': 'test',
|
||||
'sources': [
|
||||
'test.cpp',
|
||||
],
|
||||
'cflags_cc': ['-std=c++17', '-fno-exceptions'],
|
||||
'include_dirs': [
|
||||
'<!@(node -p "require(\'..\').getInclude()")',
|
||||
],
|
||||
'conditions': [
|
||||
['OS=="linux"', {
|
||||
'defines': ['__linux__'],
|
||||
}],
|
||||
['OS=="mac"', {
|
||||
'MACOSX_DEPLOYMENT_TARGET': '10.9',
|
||||
'defines': ['__APPLE__'],
|
||||
'CLANG_CXX_LIBRARY': 'libc++',
|
||||
'OTHER_CFLAGS': ['-std=c++17', '-fno-exceptions'],
|
||||
}],
|
||||
['OS=="win"', {
|
||||
'defines' : ['WIN32_LEAN_AND_MEAN', 'VC_EXTRALEAN', '_WIN32', '_HAS_EXCEPTIONS=0'],
|
||||
'msvs_settings' : {
|
||||
'VCCLCompilerTool' : {
|
||||
'AdditionalOptions' : [
|
||||
'/O2','/Oy','/GL','/GF','/Gm-', '/std:c++17',
|
||||
'/EHa-s-c-','/MT','/GS','/Gy','/GR-','/Gd',
|
||||
],
|
||||
},
|
||||
'VCLinkerTool' : {
|
||||
'AdditionalOptions' : ['/DEBUG:NONE', '/LTCG', '/OPT:NOREF'],
|
||||
},
|
||||
},
|
||||
}],
|
||||
],
|
||||
}],
|
||||
}
|
|
@ -1,45 +0,0 @@
|
|||
'use strict';
|
||||
|
||||
const assert = require('node:assert').strict;
|
||||
const { describe, it } = require('node:test');
|
||||
|
||||
const test = require('./build/Release/test.node');
|
||||
|
||||
|
||||
const arrayArgLetMsg = { message: 'Argument 0 must be of type `Array` or be `null`/`undefined`' };
|
||||
|
||||
describe('AT / HPP / LET_ARRAY_ARG', () => {
|
||||
it('exports letArrayStrArg', () => {
|
||||
assert.strictEqual(typeof test.letArrayStrArg, 'function');
|
||||
});
|
||||
it('throws if arg was passed a string', () => {
|
||||
assert.throws(() => test.letArrayStrArg('1'), arrayArgLetMsg);
|
||||
});
|
||||
it('throws if arg was passed a number', () => {
|
||||
assert.throws(() => test.letArrayStrArg(1), arrayArgLetMsg);
|
||||
});
|
||||
it('throws if arg was passed a boolean', () => {
|
||||
assert.throws(() => test.letArrayStrArg(true), arrayArgLetMsg);
|
||||
});
|
||||
it('throws if arg was passed a pointer', () => {
|
||||
assert.throws(() => test.letArrayStrArg(test.retExt()), arrayArgLetMsg);
|
||||
});
|
||||
it('throws if arg was passed an object', () => {
|
||||
assert.throws(() => test.letArrayStrArg({}), arrayArgLetMsg);
|
||||
});
|
||||
it('accepts an empty arg', () => {
|
||||
assert.ok(Array.isArray(test.letArrayStrArg()));
|
||||
});
|
||||
it('accepts undefined', () => {
|
||||
assert.ok(Array.isArray(test.letArrayStrArg(undefined)));
|
||||
});
|
||||
it('accepts null', () => {
|
||||
assert.ok(Array.isArray(test.letArrayStrArg(null)));
|
||||
});
|
||||
it('accepts an array', () => {
|
||||
assert.ok(Array.isArray(test.letArrayStrArg([])));
|
||||
});
|
||||
it('returns same array', () => {
|
||||
assert.deepStrictEqual(test.letArrayStrArg(['a', 'b']),['a', 'b']);
|
||||
});
|
||||
});
|
|
@ -1,109 +0,0 @@
|
|||
'use strict';
|
||||
|
||||
const assert = require('node:assert').strict;
|
||||
const { describe, it } = require('node:test');
|
||||
|
||||
const test = require('./build/Release/test.node');
|
||||
|
||||
|
||||
const arrayArgMsg = { message: 'Argument 0 must be of type `Array`' };
|
||||
const arrayArgLetMsg = { message: 'Argument 0 must be of type `Array` or be `null`/`undefined`' };
|
||||
|
||||
describe('AT / HPP / REQ_ARRAY_ARG', () => {
|
||||
it('exports reqArrayArg', () => {
|
||||
assert.strictEqual(typeof test.reqArrayArg, 'function');
|
||||
});
|
||||
it('throws if arg was not passed', () => {
|
||||
assert.throws(() => test.reqArrayArg(), arrayArgMsg);
|
||||
});
|
||||
it('throws if arg was passed undefined', () => {
|
||||
assert.throws(() => test.reqArrayArg(undefined), arrayArgMsg);
|
||||
});
|
||||
it('throws if arg was passed null', () => {
|
||||
assert.throws(() => test.reqArrayArg(null), arrayArgMsg);
|
||||
});
|
||||
it('throws if arg was passed a string', () => {
|
||||
assert.throws(() => test.reqArrayArg('1'), arrayArgMsg);
|
||||
});
|
||||
it('throws if arg was passed a number', () => {
|
||||
assert.throws(() => test.reqArrayArg(1), arrayArgMsg);
|
||||
});
|
||||
it('throws if arg was passed a boolean', () => {
|
||||
assert.throws(() => test.reqArrayArg(true), arrayArgMsg);
|
||||
});
|
||||
it('throws if arg was passed a pointer', () => {
|
||||
assert.throws(() => test.reqArrayArg(test.retExt()), arrayArgMsg);
|
||||
});
|
||||
it('throws if arg was passed an object', () => {
|
||||
assert.throws(() => test.reqArrayArg({}), arrayArgMsg);
|
||||
});
|
||||
it('accepts an array', () => {
|
||||
assert.ok(Array.isArray(test.reqArrayArg([])));
|
||||
});
|
||||
});
|
||||
|
||||
describe('addon-tools.hpp: LET_ARRAY_ARG', () => {
|
||||
it('exports letArrayArg', () => {
|
||||
assert.strictEqual(typeof test.letArrayArg, 'function');
|
||||
});
|
||||
it('throws if arg was passed a string', () => {
|
||||
assert.throws(() => test.letArrayArg('1'), arrayArgLetMsg);
|
||||
});
|
||||
it('throws if arg was passed a number', () => {
|
||||
assert.throws(() => test.letArrayArg(1), arrayArgLetMsg);
|
||||
});
|
||||
it('throws if arg was passed a boolean', () => {
|
||||
assert.throws(() => test.letArrayArg(true), arrayArgLetMsg);
|
||||
});
|
||||
it('throws if arg was passed a pointer', () => {
|
||||
assert.throws(() => test.letArrayArg(test.retExt()), arrayArgLetMsg);
|
||||
});
|
||||
it('throws if arg was passed an object', () => {
|
||||
assert.throws(() => test.letArrayArg({}), arrayArgLetMsg);
|
||||
});
|
||||
it('accepts an empty arg', () => {
|
||||
assert.ok(Array.isArray(test.letArrayArg()));
|
||||
});
|
||||
it('accepts undefined', () => {
|
||||
assert.ok(Array.isArray(test.letArrayArg(undefined)));
|
||||
});
|
||||
it('accepts null', () => {
|
||||
assert.ok(Array.isArray(test.letArrayArg(null)));
|
||||
});
|
||||
it('accepts an array', () => {
|
||||
assert.ok(Array.isArray(test.letArrayArg([])));
|
||||
});
|
||||
});
|
||||
|
||||
describe('addon-tools.hpp: USE_ARRAY_ARG', () => {
|
||||
it('exports useArrayArg', () => {
|
||||
assert.strictEqual(typeof test.useArrayArg, 'function');
|
||||
});
|
||||
it('throws if arg was passed a string', () => {
|
||||
assert.throws(() => test.useArrayArg('1'), arrayArgLetMsg);
|
||||
});
|
||||
it('throws if arg was passed a number', () => {
|
||||
assert.throws(() => test.useArrayArg(1), arrayArgLetMsg);
|
||||
});
|
||||
it('throws if arg was passed a boolean', () => {
|
||||
assert.throws(() => test.useArrayArg(true), arrayArgLetMsg);
|
||||
});
|
||||
it('throws if arg was passed a pointer', () => {
|
||||
assert.throws(() => test.useArrayArg(test.retExt()), arrayArgLetMsg);
|
||||
});
|
||||
it('throws if arg was passed an object', () => {
|
||||
assert.throws(() => test.useArrayArg({}), arrayArgLetMsg);
|
||||
});
|
||||
it('accepts an empty arg', () => {
|
||||
assert.ok(Array.isArray(test.useArrayArg()));
|
||||
});
|
||||
it('accepts undefined', () => {
|
||||
assert.ok(Array.isArray(test.useArrayArg(undefined)));
|
||||
});
|
||||
it('accepts null', () => {
|
||||
assert.ok(Array.isArray(test.useArrayArg(null)));
|
||||
});
|
||||
it('accepts an array', () => {
|
||||
assert.ok(Array.isArray(test.useArrayArg([])));
|
||||
});
|
||||
});
|
|
@ -1,100 +0,0 @@
|
|||
'use strict';
|
||||
|
||||
const assert = require('node:assert').strict;
|
||||
const { describe, it } = require('node:test');
|
||||
|
||||
const test = require('./build/Release/test.node');
|
||||
|
||||
|
||||
const boolArgMsg = { message: 'Argument 0 must be of type `Bool`' };
|
||||
const boolArgLetMsg = { message: 'Argument 0 must be of type `Bool` or be `null`/`undefined`' };
|
||||
|
||||
describe('AT / HPP / REQ_BOOL_ARG', () => {
|
||||
it('exports reqBoolArg', () => {
|
||||
assert.strictEqual(typeof test.reqBoolArg, 'function');
|
||||
});
|
||||
it('throws if arg was not passed', () => {
|
||||
assert.throws(() => test.reqBoolArg(), boolArgMsg);
|
||||
});
|
||||
it('throws if arg was passed undefined', () => {
|
||||
assert.throws(() => test.reqBoolArg(undefined), boolArgMsg);
|
||||
});
|
||||
it('throws if arg was passed null', () => {
|
||||
assert.throws(() => test.reqBoolArg(null), boolArgMsg);
|
||||
});
|
||||
it('throws if arg was passed a string', () => {
|
||||
assert.throws(() => test.reqBoolArg('1'), boolArgMsg);
|
||||
});
|
||||
it('throws if arg was passed a number', () => {
|
||||
assert.throws(() => test.reqBoolArg(1), boolArgMsg);
|
||||
});
|
||||
it('throws if arg was passed an object', () => {
|
||||
assert.throws(() => test.reqBoolArg({}), boolArgMsg);
|
||||
});
|
||||
it('throws if arg was passed an array', () => {
|
||||
assert.throws(() => test.reqBoolArg([]), boolArgMsg);
|
||||
});
|
||||
it('accepts a boolean', () => {
|
||||
assert.ok(test.reqBoolArg(true));
|
||||
});
|
||||
});
|
||||
|
||||
describe('addon-tools.hpp: LET_BOOL_ARG', () => {
|
||||
it('exports letBoolArg', () => {
|
||||
assert.strictEqual(typeof test.letBoolArg, 'function');
|
||||
});
|
||||
it('throws if arg was passed a string', () => {
|
||||
assert.throws(() => test.letBoolArg('1'), boolArgLetMsg);
|
||||
});
|
||||
it('throws if arg was passed a number', () => {
|
||||
assert.throws(() => test.letBoolArg(1), boolArgLetMsg);
|
||||
});
|
||||
it('throws if arg was passed an object', () => {
|
||||
assert.throws(() => test.letBoolArg({}), boolArgLetMsg);
|
||||
});
|
||||
it('throws if arg was passed an array', () => {
|
||||
assert.throws(() => test.letBoolArg([]), boolArgLetMsg);
|
||||
});
|
||||
it('accepts an empty arg', () => {
|
||||
assert.strictEqual(test.letBoolArg(), false);
|
||||
});
|
||||
it('accepts undefined', () => {
|
||||
assert.strictEqual(test.letBoolArg(undefined), false);
|
||||
});
|
||||
it('accepts null', () => {
|
||||
assert.strictEqual(test.letBoolArg(null), false);
|
||||
});
|
||||
it('accepts a boolean', () => {
|
||||
assert.ok(test.letBoolArg(true));
|
||||
});
|
||||
});
|
||||
|
||||
describe('addon-tools.hpp: USE_BOOL_ARG', () => {
|
||||
it('exports useBoolArg', () => {
|
||||
assert.strictEqual(typeof test.useBoolArg, 'function');
|
||||
});
|
||||
it('throws if arg was passed a string', () => {
|
||||
assert.throws(() => test.useBoolArg('1'), boolArgLetMsg);
|
||||
});
|
||||
it('throws if arg was passed a number', () => {
|
||||
assert.throws(() => test.useBoolArg(1), boolArgLetMsg);
|
||||
});
|
||||
it('throws if arg was passed an object', () => {
|
||||
assert.throws(() => test.useBoolArg({}), boolArgLetMsg);
|
||||
});
|
||||
it('throws if arg was passed an array', () => {
|
||||
assert.throws(() => test.useBoolArg([]), boolArgLetMsg);
|
||||
});
|
||||
it('accepts an empty arg', () => {
|
||||
assert.ok(test.useBoolArg());
|
||||
});
|
||||
it('accepts undefined', () => {
|
||||
assert.ok(test.useBoolArg(undefined));
|
||||
});
|
||||
it('accepts null', () => {
|
||||
assert.ok(test.useBoolArg(null));
|
||||
});
|
||||
it('accepts a boolean', () => {
|
||||
assert.ok(test.useBoolArg(true));
|
||||
});
|
||||
});
|
|
@ -1,100 +0,0 @@
|
|||
'use strict';
|
||||
|
||||
const assert = require('node:assert').strict;
|
||||
const { describe, it } = require('node:test');
|
||||
|
||||
const test = require('./build/Release/test.node');
|
||||
|
||||
|
||||
const numArgMsg = { message: 'Argument 0 must be of type `Number`' };
|
||||
const numArgLetMsg = { message: 'Argument 0 must be of type `Number` or be `null`/`undefined`' };
|
||||
|
||||
describe('AT / HPP / REQ_DOUBLE_ARG', () => {
|
||||
it('exports reqDoubleArg', () => {
|
||||
assert.strictEqual(typeof test.reqDoubleArg, 'function');
|
||||
});
|
||||
it('throws if arg was not passed', () => {
|
||||
assert.throws(() => test.reqDoubleArg(), numArgMsg);
|
||||
});
|
||||
it('throws if arg was passed undefined', () => {
|
||||
assert.throws(() => test.reqDoubleArg(undefined), numArgMsg);
|
||||
});
|
||||
it('throws if arg was passed null', () => {
|
||||
assert.throws(() => test.reqDoubleArg(null), numArgMsg);
|
||||
});
|
||||
it('throws if arg was passed a string', () => {
|
||||
assert.throws(() => test.reqDoubleArg('1'), numArgMsg);
|
||||
});
|
||||
it('throws if arg was passed a boolean', () => {
|
||||
assert.throws(() => test.reqDoubleArg(true), numArgMsg);
|
||||
});
|
||||
it('throws if arg was passed an object', () => {
|
||||
assert.throws(() => test.reqDoubleArg({}), numArgMsg);
|
||||
});
|
||||
it('throws if arg was passed an array', () => {
|
||||
assert.throws(() => test.reqDoubleArg([]), numArgMsg);
|
||||
});
|
||||
it('accepts a number', () => {
|
||||
assert.strictEqual(test.reqDoubleArg(55), 55);
|
||||
});
|
||||
});
|
||||
|
||||
describe('addon-tools.hpp: LET_DOUBLE_ARG', () => {
|
||||
it('exports letDoubleArg', () => {
|
||||
assert.strictEqual(typeof test.letDoubleArg, 'function');
|
||||
});
|
||||
it('throws if arg was passed a string', () => {
|
||||
assert.throws(() => test.letDoubleArg('1'), numArgLetMsg);
|
||||
});
|
||||
it('throws if arg was passed a boolean', () => {
|
||||
assert.throws(() => test.letDoubleArg(true), numArgLetMsg);
|
||||
});
|
||||
it('throws if arg was passed an object', () => {
|
||||
assert.throws(() => test.letDoubleArg({}), numArgLetMsg);
|
||||
});
|
||||
it('throws if arg was passed an array', () => {
|
||||
assert.throws(() => test.letDoubleArg([]), numArgLetMsg);
|
||||
});
|
||||
it('accepts an empty arg', () => {
|
||||
assert.strictEqual(test.letDoubleArg(), 0);
|
||||
});
|
||||
it('accepts undefined', () => {
|
||||
assert.strictEqual(test.letDoubleArg(undefined), 0);
|
||||
});
|
||||
it('accepts null', () => {
|
||||
assert.strictEqual(test.letDoubleArg(null), 0);
|
||||
});
|
||||
it('accepts a number', () => {
|
||||
assert.strictEqual(test.letDoubleArg(55), 55);
|
||||
});
|
||||
});
|
||||
|
||||
describe('addon-tools.hpp: USE_DOUBLE_ARG', () => {
|
||||
it('exports useDoubleArg', () => {
|
||||
assert.strictEqual(typeof test.useDoubleArg, 'function');
|
||||
});
|
||||
it('throws if arg was passed a string', () => {
|
||||
assert.throws(() => test.useDoubleArg('1'), numArgLetMsg);
|
||||
});
|
||||
it('throws if arg was passed a boolean', () => {
|
||||
assert.throws(() => test.useDoubleArg(true), numArgLetMsg);
|
||||
});
|
||||
it('throws if arg was passed an object', () => {
|
||||
assert.throws(() => test.useDoubleArg({}), numArgLetMsg);
|
||||
});
|
||||
it('throws if arg was passed an array', () => {
|
||||
assert.throws(() => test.useDoubleArg([]), numArgLetMsg);
|
||||
});
|
||||
it('accepts an empty arg', () => {
|
||||
assert.strictEqual(test.useDoubleArg(), 10);
|
||||
});
|
||||
it('accepts undefined', () => {
|
||||
assert.strictEqual(test.useDoubleArg(undefined), 10);
|
||||
});
|
||||
it('accepts null', () => {
|
||||
assert.strictEqual(test.useDoubleArg(null), 10);
|
||||
});
|
||||
it('accepts a number', () => {
|
||||
assert.strictEqual(test.useDoubleArg(55), 55);
|
||||
});
|
||||
});
|
|
@ -1,109 +0,0 @@
|
|||
'use strict';
|
||||
|
||||
const assert = require('node:assert').strict;
|
||||
const { describe, it } = require('node:test');
|
||||
|
||||
const test = require('./build/Release/test.node');
|
||||
|
||||
|
||||
const extArgMsg = { message: 'Argument 0 must be of type `Pointer`' };
|
||||
const extArgLetMsg = { message: 'Argument 0 must be of type `Pointer` or be `null`/`undefined`' };
|
||||
|
||||
describe('AT / HPP / REQ_EXT_ARG', () => {
|
||||
it('exports reqExtArg', () => {
|
||||
assert.strictEqual(typeof test.reqExtArg, 'function');
|
||||
});
|
||||
it('throws if arg was not passed', () => {
|
||||
assert.throws(() => test.reqExtArg(), extArgMsg);
|
||||
});
|
||||
it('throws if arg was passed undefined', () => {
|
||||
assert.throws(() => test.reqExtArg(undefined), extArgMsg);
|
||||
});
|
||||
it('throws if arg was passed null', () => {
|
||||
assert.throws(() => test.reqExtArg(null), extArgMsg);
|
||||
});
|
||||
it('throws if arg was passed a string', () => {
|
||||
assert.throws(() => test.reqExtArg('1'), extArgMsg);
|
||||
});
|
||||
it('throws if arg was passed a number', () => {
|
||||
assert.throws(() => test.reqExtArg(1), extArgMsg);
|
||||
});
|
||||
it('throws if arg was passed a boolean', () => {
|
||||
assert.throws(() => test.reqExtArg(true), extArgMsg);
|
||||
});
|
||||
it('throws if arg was passed an object', () => {
|
||||
assert.throws(() => test.reqExtArg({}), extArgMsg);
|
||||
});
|
||||
it('throws if arg was passed an array', () => {
|
||||
assert.throws(() => test.reqExtArg([]), extArgMsg);
|
||||
});
|
||||
it('accepts a pointer', () => {
|
||||
assert.strictEqual(typeof test.reqExtArg(test.retExt()), 'object');
|
||||
});
|
||||
});
|
||||
|
||||
describe('addon-tools.hpp: LET_EXT_ARG', () => {
|
||||
it('exports letExtArg', () => {
|
||||
assert.strictEqual(typeof test.letExtArg, 'function');
|
||||
});
|
||||
it('throws if arg was passed a string', () => {
|
||||
assert.throws(() => test.letExtArg('1'), extArgLetMsg);
|
||||
});
|
||||
it('throws if arg was passed a number', () => {
|
||||
assert.throws(() => test.letExtArg(1), extArgLetMsg);
|
||||
});
|
||||
it('throws if arg was passed a boolean', () => {
|
||||
assert.throws(() => test.letExtArg(true), extArgLetMsg);
|
||||
});
|
||||
it('throws if arg was passed an object', () => {
|
||||
assert.throws(() => test.letExtArg({}), extArgLetMsg);
|
||||
});
|
||||
it('throws if arg was passed an array', () => {
|
||||
assert.throws(() => test.letExtArg([]), extArgLetMsg);
|
||||
});
|
||||
it('accepts an empty arg', () => {
|
||||
assert.strictEqual(typeof test.letExtArg(), 'object');
|
||||
});
|
||||
it('accepts undefined', () => {
|
||||
assert.strictEqual(typeof test.letExtArg(undefined), 'object');
|
||||
});
|
||||
it('accepts null', () => {
|
||||
assert.strictEqual(typeof test.letExtArg(null), 'object');
|
||||
});
|
||||
it('accepts a pointer', () => {
|
||||
assert.strictEqual(typeof test.reqExtArg(test.retExt()), 'object');
|
||||
});
|
||||
});
|
||||
|
||||
describe('addon-tools.hpp: USE_EXT_ARG', () => {
|
||||
it('exports useExtArg', () => {
|
||||
assert.strictEqual(typeof test.useExtArg, 'function');
|
||||
});
|
||||
it('throws if arg was passed a string', () => {
|
||||
assert.throws(() => test.useExtArg('1'), extArgLetMsg);
|
||||
});
|
||||
it('throws if arg was passed a number', () => {
|
||||
assert.throws(() => test.useExtArg(1), extArgLetMsg);
|
||||
});
|
||||
it('throws if arg was passed a boolean', () => {
|
||||
assert.throws(() => test.useExtArg(true), extArgLetMsg);
|
||||
});
|
||||
it('throws if arg was passed an object', () => {
|
||||
assert.throws(() => test.useExtArg({}), extArgLetMsg);
|
||||
});
|
||||
it('throws if arg was passed an array', () => {
|
||||
assert.throws(() => test.useExtArg([]), extArgLetMsg);
|
||||
});
|
||||
it('accepts an empty arg', () => {
|
||||
assert.strictEqual(typeof test.useExtArg(), 'object');
|
||||
});
|
||||
it('accepts undefined', () => {
|
||||
assert.strictEqual(typeof test.useExtArg(undefined), 'object');
|
||||
});
|
||||
it('accepts null', () => {
|
||||
assert.strictEqual(typeof test.useExtArg(null), 'object');
|
||||
});
|
||||
it('accepts a number', () => {
|
||||
assert.strictEqual(typeof test.useExtArg(test.retExt()), 'object');
|
||||
});
|
||||
});
|
|
@ -1,100 +0,0 @@
|
|||
'use strict';
|
||||
|
||||
const assert = require('node:assert').strict;
|
||||
const { describe, it } = require('node:test');
|
||||
|
||||
const test = require('./build/Release/test.node');
|
||||
|
||||
|
||||
const numArgMsg = { message: 'Argument 0 must be of type `Number`' };
|
||||
const numArgLetMsg = { message: 'Argument 0 must be of type `Number` or be `null`/`undefined`' };
|
||||
|
||||
describe('AT / HPP / REQ_FLOAT_ARG', () => {
|
||||
it('exports reqFloatArg', () => {
|
||||
assert.strictEqual(typeof test.reqFloatArg, 'function');
|
||||
});
|
||||
it('throws if arg was not passed', () => {
|
||||
assert.throws(() => test.reqFloatArg(), numArgMsg);
|
||||
});
|
||||
it('throws if arg was passed undefined', () => {
|
||||
assert.throws(() => test.reqFloatArg(undefined), numArgMsg);
|
||||
});
|
||||
it('throws if arg was passed null', () => {
|
||||
assert.throws(() => test.reqFloatArg(null), numArgMsg);
|
||||
});
|
||||
it('throws if arg was passed a string', () => {
|
||||
assert.throws(() => test.reqFloatArg('1'), numArgMsg);
|
||||
});
|
||||
it('throws if arg was passed a boolean', () => {
|
||||
assert.throws(() => test.reqFloatArg(true), numArgMsg);
|
||||
});
|
||||
it('throws if arg was passed an object', () => {
|
||||
assert.throws(() => test.reqFloatArg({}), numArgMsg);
|
||||
});
|
||||
it('throws if arg was passed an array', () => {
|
||||
assert.throws(() => test.reqFloatArg([]), numArgMsg);
|
||||
});
|
||||
it('accepts a number', () => {
|
||||
assert.strictEqual(test.reqFloatArg(55), 55);
|
||||
});
|
||||
});
|
||||
|
||||
describe('addon-tools.hpp: LET_FLOAT_ARG', () => {
|
||||
it('exports letFloatArg', () => {
|
||||
assert.strictEqual(typeof test.letFloatArg, 'function');
|
||||
});
|
||||
it('throws if arg was passed a string', () => {
|
||||
assert.throws(() => test.letFloatArg('1'), numArgLetMsg);
|
||||
});
|
||||
it('throws if arg was passed a boolean', () => {
|
||||
assert.throws(() => test.letFloatArg(true), numArgLetMsg);
|
||||
});
|
||||
it('throws if arg was passed an object', () => {
|
||||
assert.throws(() => test.letFloatArg({}), numArgLetMsg);
|
||||
});
|
||||
it('throws if arg was passed an array', () => {
|
||||
assert.throws(() => test.letFloatArg([]), numArgLetMsg);
|
||||
});
|
||||
it('accepts an empty arg', () => {
|
||||
assert.strictEqual(test.letFloatArg(), 0);
|
||||
});
|
||||
it('accepts undefined', () => {
|
||||
assert.strictEqual(test.letFloatArg(undefined), 0);
|
||||
});
|
||||
it('accepts null', () => {
|
||||
assert.strictEqual(test.letFloatArg(null), 0);
|
||||
});
|
||||
it('accepts a number', () => {
|
||||
assert.strictEqual(test.letFloatArg(55), 55);
|
||||
});
|
||||
});
|
||||
|
||||
describe('addon-tools.hpp: USE_FLOAT_ARG', () => {
|
||||
it('exports useFloatArg', () => {
|
||||
assert.strictEqual(typeof test.useFloatArg, 'function');
|
||||
});
|
||||
it('throws if arg was passed a string', () => {
|
||||
assert.throws(() => test.useFloatArg('1'), numArgLetMsg);
|
||||
});
|
||||
it('throws if arg was passed a boolean', () => {
|
||||
assert.throws(() => test.useFloatArg(true), numArgLetMsg);
|
||||
});
|
||||
it('throws if arg was passed an object', () => {
|
||||
assert.throws(() => test.useFloatArg({}), numArgLetMsg);
|
||||
});
|
||||
it('throws if arg was passed an array', () => {
|
||||
assert.throws(() => test.useFloatArg([]), numArgLetMsg);
|
||||
});
|
||||
it('accepts an empty arg', () => {
|
||||
assert.strictEqual(test.useFloatArg(), 10);
|
||||
});
|
||||
it('accepts undefined', () => {
|
||||
assert.strictEqual(test.useFloatArg(undefined), 10);
|
||||
});
|
||||
it('accepts null', () => {
|
||||
assert.strictEqual(test.useFloatArg(null), 10);
|
||||
});
|
||||
it('accepts a number', () => {
|
||||
assert.strictEqual(test.useFloatArg(55), 55);
|
||||
});
|
||||
});
|
|
@ -1,100 +0,0 @@
|
|||
'use strict';
|
||||
|
||||
const assert = require('node:assert').strict;
|
||||
const { describe, it } = require('node:test');
|
||||
|
||||
const test = require('./build/Release/test.node');
|
||||
|
||||
|
||||
const intArgMsg = { message: 'Argument 0 must be of type `Int32`' };
|
||||
const intArgLetMsg = { message: 'Argument 0 must be of type `Int32` or be `null`/`undefined`' };
|
||||
|
||||
describe('AT / HPP / REQ_INT_ARG, REQ_INT32_ARG', () => {
|
||||
it('exports reqIntArg', () => {
|
||||
assert.strictEqual(typeof test.reqIntArg, 'function');
|
||||
});
|
||||
it('throws if arg was not passed', () => {
|
||||
assert.throws(() => test.reqIntArg(), intArgMsg);
|
||||
});
|
||||
it('throws if arg was passed undefined', () => {
|
||||
assert.throws(() => test.reqIntArg(undefined), intArgMsg);
|
||||
});
|
||||
it('throws if arg was passed null', () => {
|
||||
assert.throws(() => test.reqIntArg(null), intArgMsg);
|
||||
});
|
||||
it('throws if arg was passed a string', () => {
|
||||
assert.throws(() => test.reqIntArg('1'), intArgMsg);
|
||||
});
|
||||
it('throws if arg was passed a boolean', () => {
|
||||
assert.throws(() => test.reqIntArg(true), intArgMsg);
|
||||
});
|
||||
it('throws if arg was passed an object', () => {
|
||||
assert.throws(() => test.reqIntArg({}), intArgMsg);
|
||||
});
|
||||
it('throws if arg was passed an array', () => {
|
||||
assert.throws(() => test.reqIntArg([]), intArgMsg);
|
||||
});
|
||||
it('accepts a number', () => {
|
||||
assert.strictEqual(test.reqIntArg(55), 55);
|
||||
});
|
||||
});
|
||||
|
||||
describe('addon-tools.hpp: LET_INT_ARG / LET_INT32_ARG', () => {
|
||||
it('exports letIntArg', () => {
|
||||
assert.strictEqual(typeof test.letIntArg, 'function');
|
||||
});
|
||||
it('throws if arg was passed a string', () => {
|
||||
assert.throws(() => test.letIntArg('1'), intArgLetMsg);
|
||||
});
|
||||
it('throws if arg was passed a boolean', () => {
|
||||
assert.throws(() => test.letIntArg(true), intArgLetMsg);
|
||||
});
|
||||
it('throws if arg was passed an object', () => {
|
||||
assert.throws(() => test.letIntArg({}), intArgLetMsg);
|
||||
});
|
||||
it('throws if arg was passed an array', () => {
|
||||
assert.throws(() => test.letIntArg([]), intArgLetMsg);
|
||||
});
|
||||
it('accepts an empty arg', () => {
|
||||
assert.strictEqual(test.letIntArg(), 0);
|
||||
});
|
||||
it('accepts undefined', () => {
|
||||
assert.strictEqual(test.letIntArg(undefined), 0);
|
||||
});
|
||||
it('accepts null', () => {
|
||||
assert.strictEqual(test.letIntArg(null), 0);
|
||||
});
|
||||
it('accepts a number', () => {
|
||||
assert.strictEqual(test.letIntArg(55), 55);
|
||||
});
|
||||
});
|
||||
|
||||
describe('addon-tools.hpp: USE_INT_ARG / USE_INT32_ARG', () => {
|
||||
it('exports useIntArg', () => {
|
||||
assert.strictEqual(typeof test.useIntArg, 'function');
|
||||
});
|
||||
it('throws if arg was passed a string', () => {
|
||||
assert.throws(() => test.useIntArg('1'), intArgLetMsg);
|
||||
});
|
||||
it('throws if arg was passed a boolean', () => {
|
||||
assert.throws(() => test.useIntArg(true), intArgLetMsg);
|
||||
});
|
||||
it('throws if arg was passed an object', () => {
|
||||
assert.throws(() => test.useIntArg({}), intArgLetMsg);
|
||||
});
|
||||
it('throws if arg was passed an array', () => {
|
||||
assert.throws(() => test.useIntArg([]), intArgLetMsg);
|
||||
});
|
||||
it('accepts an empty arg', () => {
|
||||
assert.strictEqual(test.useIntArg(), 10);
|
||||
});
|
||||
it('accepts undefined', () => {
|
||||
assert.strictEqual(test.useIntArg(undefined), 10);
|
||||
});
|
||||
it('accepts null', () => {
|
||||
assert.strictEqual(test.useIntArg(null), 10);
|
||||
});
|
||||
it('accepts a number', () => {
|
||||
assert.strictEqual(test.useIntArg(55), 55);
|
||||
});
|
||||
});
|
|
@ -1,109 +0,0 @@
|
|||
'use strict';
|
||||
|
||||
const assert = require('node:assert').strict;
|
||||
const { describe, it } = require('node:test');
|
||||
|
||||
const test = require('./build/Release/test.node');
|
||||
|
||||
|
||||
const objArgMsg = { message: 'Argument 0 must be of type `Object`' };
|
||||
const objArgLetMsg = { message: 'Argument 0 must be of type `Object` or be `null`/`undefined`' };
|
||||
|
||||
describe('AT / HPP / REQ_OBJ_ARG', () => {
|
||||
it('exports reqObjArg', () => {
|
||||
assert.strictEqual(typeof test.reqObjArg, 'function');
|
||||
});
|
||||
it('throws if arg was not passed', () => {
|
||||
assert.throws(() => test.reqObjArg(), objArgMsg);
|
||||
});
|
||||
it('throws if arg was passed undefined', () => {
|
||||
assert.throws(() => test.reqObjArg(undefined), objArgMsg);
|
||||
});
|
||||
it('throws if arg was passed null', () => {
|
||||
assert.throws(() => test.reqObjArg(null), objArgMsg);
|
||||
});
|
||||
it('throws if arg was passed a string', () => {
|
||||
assert.throws(() => test.reqObjArg('1'), objArgMsg);
|
||||
});
|
||||
it('throws if arg was passed a number', () => {
|
||||
assert.throws(() => test.reqObjArg(1), objArgMsg);
|
||||
});
|
||||
it('throws if arg was passed a boolean', () => {
|
||||
assert.throws(() => test.reqObjArg(true), objArgMsg);
|
||||
});
|
||||
it('throws if arg was passed a pointer', () => {
|
||||
assert.throws(() => test.reqObjArg(test.retExt()), objArgMsg);
|
||||
});
|
||||
it('accepts an object', () => {
|
||||
assert.strictEqual(typeof test.reqObjArg({}), 'object');
|
||||
});
|
||||
it('accepts an array', () => {
|
||||
assert.ok(Array.isArray(test.reqObjArg([])));
|
||||
});
|
||||
});
|
||||
|
||||
describe('addon-tools.hpp: LET_OBJ_ARG', () => {
|
||||
it('exports letObjArg', () => {
|
||||
assert.strictEqual(typeof test.letObjArg, 'function');
|
||||
});
|
||||
it('throws if arg was passed a string', () => {
|
||||
assert.throws(() => test.letObjArg('1'), objArgLetMsg);
|
||||
});
|
||||
it('throws if arg was passed a number', () => {
|
||||
assert.throws(() => test.letObjArg(1), objArgLetMsg);
|
||||
});
|
||||
it('throws if arg was passed a boolean', () => {
|
||||
assert.throws(() => test.letObjArg(true), objArgLetMsg);
|
||||
});
|
||||
it('throws if arg was passed a pointer', () => {
|
||||
assert.throws(() => test.letObjArg(test.retExt()), objArgLetMsg);
|
||||
});
|
||||
it('accepts an empty arg', () => {
|
||||
assert.strictEqual(typeof test.letObjArg(), 'object');
|
||||
});
|
||||
it('accepts undefined', () => {
|
||||
assert.strictEqual(typeof test.letObjArg(undefined), 'object');
|
||||
});
|
||||
it('accepts null', () => {
|
||||
assert.strictEqual(typeof test.letObjArg(null), 'object');
|
||||
});
|
||||
it('accepts an object', () => {
|
||||
assert.strictEqual(typeof test.letObjArg({}), 'object');
|
||||
});
|
||||
it('accepts an array', () => {
|
||||
assert.ok(Array.isArray(test.letObjArg([])));
|
||||
});
|
||||
});
|
||||
|
||||
describe('addon-tools.hpp: USE_OBJ_ARG', () => {
|
||||
it('exports useObjArg', () => {
|
||||
assert.strictEqual(typeof test.useObjArg, 'function');
|
||||
});
|
||||
it('throws if arg was passed a string', () => {
|
||||
assert.throws(() => test.useObjArg('1'), objArgLetMsg);
|
||||
});
|
||||
it('throws if arg was passed a number', () => {
|
||||
assert.throws(() => test.useObjArg(1), objArgLetMsg);
|
||||
});
|
||||
it('throws if arg was passed a boolean', () => {
|
||||
assert.throws(() => test.useObjArg(true), objArgLetMsg);
|
||||
});
|
||||
it('throws if arg was passed a pointer', () => {
|
||||
assert.throws(() => test.useObjArg(test.retExt()), objArgLetMsg);
|
||||
});
|
||||
it('accepts an empty arg', () => {
|
||||
assert.strictEqual(typeof test.useObjArg(), 'object');
|
||||
});
|
||||
it('accepts undefined', () => {
|
||||
assert.strictEqual(typeof test.useObjArg(undefined), 'object');
|
||||
});
|
||||
it('accepts null', () => {
|
||||
assert.strictEqual(typeof test.useObjArg(null), 'object');
|
||||
});
|
||||
it('accepts an object', () => {
|
||||
assert.strictEqual(typeof test.useObjArg({}), 'object');
|
||||
});
|
||||
it('accepts an array', () => {
|
||||
assert.ok(Array.isArray(test.useObjArg([])));
|
||||
});
|
||||
});
|
|
@ -1,100 +0,0 @@
|
|||
'use strict';
|
||||
|
||||
const assert = require('node:assert').strict;
|
||||
const { describe, it } = require('node:test');
|
||||
|
||||
const test = require('./build/Release/test.node');
|
||||
|
||||
|
||||
const numArgMsg = { message: 'Argument 0 must be of type `Number`' };
|
||||
const numArgLetMsg = { message: 'Argument 0 must be of type `Number` or be `null`/`undefined`' };
|
||||
|
||||
describe('AT / HPP / REQ_OFFS_ARG', () => {
|
||||
it('exports reqOffsArg', () => {
|
||||
assert.strictEqual(typeof test.reqOffsArg, 'function');
|
||||
});
|
||||
it('throws if arg was not passed', () => {
|
||||
assert.throws(() => test.reqOffsArg(), numArgMsg);
|
||||
});
|
||||
it('throws if arg was passed undefined', () => {
|
||||
assert.throws(() => test.reqOffsArg(undefined), numArgMsg);
|
||||
});
|
||||
it('throws if arg was passed null', () => {
|
||||
assert.throws(() => test.reqOffsArg(null), numArgMsg);
|
||||
});
|
||||
it('throws if arg was passed a string', () => {
|
||||
assert.throws(() => test.reqOffsArg('1'), numArgMsg);
|
||||
});
|
||||
it('throws if arg was passed a boolean', () => {
|
||||
assert.throws(() => test.reqOffsArg(true), numArgMsg);
|
||||
});
|
||||
it('throws if arg was passed an object', () => {
|
||||
assert.throws(() => test.reqOffsArg({}), numArgMsg);
|
||||
});
|
||||
it('throws if arg was passed an array', () => {
|
||||
assert.throws(() => test.reqOffsArg([]), numArgMsg);
|
||||
});
|
||||
it('accepts a number', () => {
|
||||
assert.strictEqual(test.reqOffsArg(55), 55);
|
||||
});
|
||||
});
|
||||
|
||||
describe('addon-tools.hpp: LET_OFFS_ARG', () => {
|
||||
it('exports letOffsArg', () => {
|
||||
assert.strictEqual(typeof test.letOffsArg, 'function');
|
||||
});
|
||||
it('throws if arg was passed a string', () => {
|
||||
assert.throws(() => test.letOffsArg('1'), numArgLetMsg);
|
||||
});
|
||||
it('throws if arg was passed a boolean', () => {
|
||||
assert.throws(() => test.letOffsArg(true), numArgLetMsg);
|
||||
});
|
||||
it('throws if arg was passed an object', () => {
|
||||
assert.throws(() => test.letOffsArg({}), numArgLetMsg);
|
||||
});
|
||||
it('throws if arg was passed an array', () => {
|
||||
assert.throws(() => test.letOffsArg([]), numArgLetMsg);
|
||||
});
|
||||
it('accepts an empty arg', () => {
|
||||
assert.strictEqual(test.letOffsArg(), 0);
|
||||
});
|
||||
it('accepts undefined', () => {
|
||||
assert.strictEqual(test.letOffsArg(undefined), 0);
|
||||
});
|
||||
it('accepts null', () => {
|
||||
assert.strictEqual(test.letOffsArg(null), 0);
|
||||
});
|
||||
it('accepts a number', () => {
|
||||
assert.strictEqual(test.letOffsArg(55), 55);
|
||||
});
|
||||
});
|
||||
|
||||
describe('addon-tools.hpp: USE_OFFS_ARG', () => {
|
||||
it('exports useOffsArg', () => {
|
||||
assert.strictEqual(typeof test.useOffsArg, 'function');
|
||||
});
|
||||
it('throws if arg was passed a string', () => {
|
||||
assert.throws(() => test.useOffsArg('1'), numArgLetMsg);
|
||||
});
|
||||
it('throws if arg was passed a boolean', () => {
|
||||
assert.throws(() => test.useOffsArg(true), numArgLetMsg);
|
||||
});
|
||||
it('throws if arg was passed an object', () => {
|
||||
assert.throws(() => test.useOffsArg({}), numArgLetMsg);
|
||||
});
|
||||
it('throws if arg was passed an array', () => {
|
||||
assert.throws(() => test.useOffsArg([]), numArgLetMsg);
|
||||
});
|
||||
it('accepts an empty arg', () => {
|
||||
assert.strictEqual(test.useOffsArg(), 10);
|
||||
});
|
||||
it('accepts undefined', () => {
|
||||
assert.strictEqual(test.useOffsArg(undefined), 10);
|
||||
});
|
||||
it('accepts null', () => {
|
||||
assert.strictEqual(test.useOffsArg(null), 10);
|
||||
});
|
||||
it('accepts a number', () => {
|
||||
assert.strictEqual(test.useOffsArg(55), 55);
|
||||
});
|
||||
});
|
|
@ -1,100 +0,0 @@
|
|||
'use strict';
|
||||
|
||||
const assert = require('node:assert').strict;
|
||||
const { describe, it } = require('node:test');
|
||||
|
||||
const test = require('./build/Release/test.node');
|
||||
|
||||
|
||||
const strArgMsg = { message: 'Argument 0 must be of type `String`' };
|
||||
const strArgLetMsg = { message: 'Argument 0 must be of type `String` or be `null`/`undefined`' };
|
||||
|
||||
describe('AT / HPP / REQ_STR_ARG', () => {
|
||||
it('exports reqStrArg', () => {
|
||||
assert.strictEqual(typeof test.reqStrArg, 'function');
|
||||
});
|
||||
it('throws if arg was not passed', () => {
|
||||
assert.throws(() => test.reqStrArg(), strArgMsg);
|
||||
});
|
||||
it('throws if arg was passed undefined', () => {
|
||||
assert.throws(() => test.reqStrArg(undefined), strArgMsg);
|
||||
});
|
||||
it('throws if arg was passed null', () => {
|
||||
assert.throws(() => test.reqStrArg(null), strArgMsg);
|
||||
});
|
||||
it('throws if arg was passed a number', () => {
|
||||
assert.throws(() => test.reqStrArg(1), strArgMsg);
|
||||
});
|
||||
it('throws if arg was passed a boolean', () => {
|
||||
assert.throws(() => test.reqStrArg(true), strArgMsg);
|
||||
});
|
||||
it('throws if arg was passed an object', () => {
|
||||
assert.throws(() => test.reqStrArg({}), strArgMsg);
|
||||
});
|
||||
it('throws if arg was passed an array', () => {
|
||||
assert.throws(() => test.reqStrArg([]), strArgMsg);
|
||||
});
|
||||
it('accepts a string', () => {
|
||||
assert.strictEqual(test.reqStrArg('1abc'), '1abc');
|
||||
});
|
||||
});
|
||||
|
||||
describe('addon-tools.hpp: LET_STR_ARG', () => {
|
||||
it('exports letStrArg', () => {
|
||||
assert.strictEqual(typeof test.letStrArg, 'function');
|
||||
});
|
||||
it('throws if arg was passed a number', () => {
|
||||
assert.throws(() => test.letStrArg(1), strArgLetMsg);
|
||||
});
|
||||
it('throws if arg was passed a boolean', () => {
|
||||
assert.throws(() => test.letStrArg(true), strArgLetMsg);
|
||||
});
|
||||
it('throws if arg was passed an object', () => {
|
||||
assert.throws(() => test.letStrArg({}), strArgLetMsg);
|
||||
});
|
||||
it('throws if arg was passed an array', () => {
|
||||
assert.throws(() => test.letStrArg([]), strArgLetMsg);
|
||||
});
|
||||
it('accepts an empty arg', () => {
|
||||
assert.strictEqual(test.letStrArg(), '');
|
||||
});
|
||||
it('accepts undefined', () => {
|
||||
assert.strictEqual(test.letStrArg(undefined), '');
|
||||
});
|
||||
it('accepts null', () => {
|
||||
assert.strictEqual(test.letStrArg(null), '');
|
||||
});
|
||||
it('accepts a string', () => {
|
||||
assert.strictEqual(test.letStrArg('1abc'), '1abc');
|
||||
});
|
||||
});
|
||||
|
||||
describe('addon-tools.hpp: USE_STR_ARG', () => {
|
||||
it('exports useStrArg', () => {
|
||||
assert.strictEqual(typeof test.useStrArg, 'function');
|
||||
});
|
||||
it('throws if arg was passed a number', () => {
|
||||
assert.throws(() => test.useStrArg(1), strArgLetMsg);
|
||||
});
|
||||
it('throws if arg was passed a boolean', () => {
|
||||
assert.throws(() => test.useStrArg(true), strArgLetMsg);
|
||||
});
|
||||
it('throws if arg was passed an object', () => {
|
||||
assert.throws(() => test.useStrArg({}), strArgLetMsg);
|
||||
});
|
||||
it('throws if arg was passed an array', () => {
|
||||
assert.throws(() => test.useStrArg([]), strArgLetMsg);
|
||||
});
|
||||
it('accepts an empty arg', () => {
|
||||
assert.strictEqual(test.useStrArg(), 'default');
|
||||
});
|
||||
it('accepts undefined', () => {
|
||||
assert.strictEqual(test.useStrArg(undefined), 'default');
|
||||
});
|
||||
it('accepts null', () => {
|
||||
assert.strictEqual(test.useStrArg(null), 'default');
|
||||
});
|
||||
it('accepts a string', () => {
|
||||
assert.strictEqual(test.useStrArg('1abc'), '1abc');
|
||||
});
|
||||
});
|
|
@ -1,100 +0,0 @@
|
|||
'use strict';
|
||||
|
||||
const assert = require('node:assert').strict;
|
||||
const { describe, it } = require('node:test');
|
||||
|
||||
const test = require('./build/Release/test.node');
|
||||
|
||||
|
||||
const uintArgMsg = { message: 'Argument 0 must be of type `Uint32`' };
|
||||
const uintArgLetMsg = { message: 'Argument 0 must be of type `Uint32` or be `null`/`undefined`' };
|
||||
|
||||
describe('AT / HPP / REQ_UINT_ARG, REQ_UINT32_ARG', () => {
|
||||
it('exports reqUintArg', () => {
|
||||
assert.strictEqual(typeof test.reqUintArg, 'function');
|
||||
});
|
||||
it('throws if arg was not passed', () => {
|
||||
assert.throws(() => test.reqUintArg(), uintArgMsg);
|
||||
});
|
||||
it('throws if arg was passed undefined', () => {
|
||||
assert.throws(() => test.reqUintArg(undefined), uintArgMsg);
|
||||
});
|
||||
it('throws if arg was passed null', () => {
|
||||
assert.throws(() => test.reqUintArg(null), uintArgMsg);
|
||||
});
|
||||
it('throws if arg was passed a string', () => {
|
||||
assert.throws(() => test.reqUintArg('1'), uintArgMsg);
|
||||
});
|
||||
it('throws if arg was passed a boolean', () => {
|
||||
assert.throws(() => test.reqUintArg(true), uintArgMsg);
|
||||
});
|
||||
it('throws if arg was passed an object', () => {
|
||||
assert.throws(() => test.reqUintArg({}), uintArgMsg);
|
||||
});
|
||||
it('throws if arg was passed an array', () => {
|
||||
assert.throws(() => test.reqUintArg([]), uintArgMsg);
|
||||
});
|
||||
it('accepts a number', () => {
|
||||
assert.strictEqual(test.reqUintArg(55), 55);
|
||||
});
|
||||
});
|
||||
|
||||
describe('addon-tools.hpp: LET_UINT_ARG / LET_UINT32_ARG', () => {
|
||||
it('exports letUintArg', () => {
|
||||
assert.strictEqual(typeof test.letUintArg, 'function');
|
||||
});
|
||||
it('throws if arg was passed a string', () => {
|
||||
assert.throws(() => test.letUintArg('1'), uintArgLetMsg);
|
||||
});
|
||||
it('throws if arg was passed a boolean', () => {
|
||||
assert.throws(() => test.letUintArg(true), uintArgLetMsg);
|
||||
});
|
||||
it('throws if arg was passed an object', () => {
|
||||
assert.throws(() => test.letUintArg({}), uintArgLetMsg);
|
||||
});
|
||||
it('throws if arg was passed an array', () => {
|
||||
assert.throws(() => test.letUintArg([]), uintArgLetMsg);
|
||||
});
|
||||
it('accepts an empty arg', () => {
|
||||
assert.strictEqual(test.letUintArg(), 0);
|
||||
});
|
||||
it('accepts undefined', () => {
|
||||
assert.strictEqual(test.letUintArg(undefined), 0);
|
||||
});
|
||||
it('accepts null', () => {
|
||||
assert.strictEqual(test.letUintArg(null), 0);
|
||||
});
|
||||
it('accepts a number', () => {
|
||||
assert.strictEqual(test.letUintArg(55), 55);
|
||||
});
|
||||
});
|
||||
|
||||
describe('addon-tools.hpp: USE_UINT_ARG / USE_UINT32_ARG', () => {
|
||||
it('exports useUintArg', () => {
|
||||
assert.strictEqual(typeof test.useUintArg, 'function');
|
||||
});
|
||||
it('throws if arg was passed a string', () => {
|
||||
assert.throws(() => test.useUintArg('1'), uintArgLetMsg);
|
||||
});
|
||||
it('throws if arg was passed a boolean', () => {
|
||||
assert.throws(() => test.useUintArg(true), uintArgLetMsg);
|
||||
});
|
||||
it('throws if arg was passed an object', () => {
|
||||
assert.throws(() => test.useUintArg({}), uintArgLetMsg);
|
||||
});
|
||||
it('throws if arg was passed an array', () => {
|
||||
assert.throws(() => test.useUintArg([]), uintArgLetMsg);
|
||||
});
|
||||
it('accepts an empty arg', () => {
|
||||
assert.strictEqual(test.useUintArg(), 10);
|
||||
});
|
||||
it('accepts undefined', () => {
|
||||
assert.strictEqual(test.useUintArg(undefined), 10);
|
||||
});
|
||||
it('accepts null', () => {
|
||||
assert.strictEqual(test.useUintArg(null), 10);
|
||||
});
|
||||
it('accepts a number', () => {
|
||||
assert.strictEqual(test.useUintArg(55), 55);
|
||||
});
|
||||
});
|
|
@ -1,211 +0,0 @@
|
|||
'use strict';
|
||||
|
||||
const assert = require('node:assert').strict;
|
||||
const { describe, it } = require('node:test');
|
||||
|
||||
const test = require('./build/Release/test.node');
|
||||
|
||||
|
||||
describe('AT / HPP / Function arguments', () => {
|
||||
|
||||
const arg3Msg = { message: 'Expected at least 3 arguments' };
|
||||
|
||||
describe('REQ_ARGS', () => {
|
||||
it('exports reqArgs3', () => {
|
||||
assert.strictEqual(typeof test.reqArgs3, 'function');
|
||||
});
|
||||
it('throws if no args passed', () => {
|
||||
assert.throws(() => test.reqArgs3(), arg3Msg);
|
||||
});
|
||||
it('throws if 1 arg passed', () => {
|
||||
assert.throws(() => test.reqArgs3(1), arg3Msg);
|
||||
});
|
||||
it('returns true if 3 args passed', () => {
|
||||
assert.ok(test.reqArgs3(1, 2, 3));
|
||||
});
|
||||
it('returns true if 5 args passed', () => {
|
||||
assert.ok(test.reqArgs3(1, 2, 3, 4, 5));
|
||||
});
|
||||
});
|
||||
|
||||
describe('IS_ARG_EMPTY', () => {
|
||||
it('exports isArg0Empty', () => {
|
||||
assert.strictEqual(typeof test.isArg0Empty, 'function');
|
||||
});
|
||||
it('returns true for absent arg', () => {
|
||||
assert.ok(test.isArg0Empty());
|
||||
});
|
||||
it('returns true for undefined arg', () => {
|
||||
assert.ok(test.isArg0Empty(undefined));
|
||||
});
|
||||
it('returns true for null arg', () => {
|
||||
assert.ok(test.isArg0Empty(null));
|
||||
});
|
||||
it('returns false for non-empty value', () => {
|
||||
assert.strictEqual(test.isArg0Empty(1), false);
|
||||
});
|
||||
});
|
||||
|
||||
// ------------------------------ FUN_ARG
|
||||
|
||||
const funArgMsg = { message: 'Argument 0 must be of type `Function`' };
|
||||
|
||||
describe('REQ_FUN_ARG', () => {
|
||||
it('exports reqFunArg', () => {
|
||||
assert.strictEqual(typeof test.reqFunArg, 'function');
|
||||
});
|
||||
it('throws if arg was not passed', () => {
|
||||
assert.throws(() => test.reqFunArg(), funArgMsg);
|
||||
});
|
||||
it('throws if arg was passed undefined', () => {
|
||||
assert.throws(() => test.reqFunArg(undefined), funArgMsg);
|
||||
});
|
||||
it('throws if arg was passed null', () => {
|
||||
assert.throws(() => test.reqFunArg(null), funArgMsg);
|
||||
});
|
||||
it('throws if arg was passed a string', () => {
|
||||
assert.throws(() => test.reqFunArg('1'), funArgMsg);
|
||||
});
|
||||
it('throws if arg was passed a number', () => {
|
||||
assert.throws(() => test.reqFunArg(1), funArgMsg);
|
||||
});
|
||||
it('throws if arg was passed a boolean', () => {
|
||||
assert.throws(() => test.reqFunArg(true), funArgMsg);
|
||||
});
|
||||
it('throws if arg was passed a pointer', () => {
|
||||
assert.throws(() => test.reqFunArg(test.retExt()), funArgMsg);
|
||||
});
|
||||
it('throws if arg was passed an object', () => {
|
||||
assert.throws(() => test.reqFunArg({}), funArgMsg);
|
||||
});
|
||||
it('throws if arg was passed an array', () => {
|
||||
assert.throws(() => test.reqFunArg([]), funArgMsg);
|
||||
});
|
||||
it('accepts a function', () => {
|
||||
assert.strictEqual(typeof test.reqFunArg(() => {}), 'function');
|
||||
});
|
||||
});
|
||||
|
||||
// ------------------------------ ARRV_ARG
|
||||
|
||||
const arrvArgMsg = { message: 'Argument 0 must be of type `ArrayBuffer`' };
|
||||
|
||||
describe('REQ_ARRV_ARG', () => {
|
||||
it('exports reqArrvArg', () => {
|
||||
assert.strictEqual(typeof test.reqArrvArg, 'function');
|
||||
});
|
||||
it('throws if arg was not passed', () => {
|
||||
assert.throws(() => test.reqArrvArg(), arrvArgMsg);
|
||||
});
|
||||
it('throws if arg was passed undefined', () => {
|
||||
assert.throws(() => test.reqArrvArg(undefined), arrvArgMsg);
|
||||
});
|
||||
it('throws if arg was passed null', () => {
|
||||
assert.throws(() => test.reqArrvArg(null), arrvArgMsg);
|
||||
});
|
||||
it('throws if arg was passed a string', () => {
|
||||
assert.throws(() => test.reqArrvArg('1'), arrvArgMsg);
|
||||
});
|
||||
it('throws if arg was passed a number', () => {
|
||||
assert.throws(() => test.reqArrvArg(1), arrvArgMsg);
|
||||
});
|
||||
it('throws if arg was passed a boolean', () => {
|
||||
assert.throws(() => test.reqArrvArg(true), arrvArgMsg);
|
||||
});
|
||||
it('throws if arg was passed a pointer', () => {
|
||||
assert.throws(() => test.reqArrvArg(test.retExt()), arrvArgMsg);
|
||||
});
|
||||
it('throws if arg was passed an object', () => {
|
||||
assert.throws(() => test.reqArrvArg({}), arrvArgMsg);
|
||||
});
|
||||
it('throws if arg was passed an array', () => {
|
||||
assert.throws(() => test.reqArrvArg([]), arrvArgMsg);
|
||||
});
|
||||
it('accepts an array buffer', () => {
|
||||
const { buffer } = new Uint8Array([1, 2, 3]);
|
||||
assert.strictEqual(test.reqArrvArg(buffer), buffer);
|
||||
});
|
||||
});
|
||||
|
||||
// ------------------------------ BUF_ARG
|
||||
|
||||
const bufArgMsg = { message: 'Argument 0 must be of type `Buffer`' };
|
||||
|
||||
describe('REQ_BUF_ARG', () => {
|
||||
it('exports reqBufArg', () => {
|
||||
assert.strictEqual(typeof test.reqBufArg, 'function');
|
||||
});
|
||||
it('throws if arg was not passed', () => {
|
||||
assert.throws(() => test.reqBufArg(), bufArgMsg);
|
||||
});
|
||||
it('throws if arg was passed undefined', () => {
|
||||
assert.throws(() => test.reqBufArg(undefined), bufArgMsg);
|
||||
});
|
||||
it('throws if arg was passed null', () => {
|
||||
assert.throws(() => test.reqBufArg(null), bufArgMsg);
|
||||
});
|
||||
it('throws if arg was passed a string', () => {
|
||||
assert.throws(() => test.reqBufArg('1'), bufArgMsg);
|
||||
});
|
||||
it('throws if arg was passed a number', () => {
|
||||
assert.throws(() => test.reqBufArg(1), bufArgMsg);
|
||||
});
|
||||
it('throws if arg was passed a boolean', () => {
|
||||
assert.throws(() => test.reqBufArg(true), bufArgMsg);
|
||||
});
|
||||
it('throws if arg was passed a pointer', () => {
|
||||
assert.throws(() => test.reqBufArg(test.retExt()), bufArgMsg);
|
||||
});
|
||||
it('throws if arg was passed an object', () => {
|
||||
assert.throws(() => test.reqBufArg({}), bufArgMsg);
|
||||
});
|
||||
it('throws if arg was passed an array', () => {
|
||||
assert.throws(() => test.reqBufArg([]), bufArgMsg);
|
||||
});
|
||||
it('accepts a buffer', () => {
|
||||
const buffer = Buffer.from([1, 2, 3]);
|
||||
assert.strictEqual(test.reqBufArg(buffer), buffer);
|
||||
});
|
||||
});
|
||||
|
||||
// ------------------------------ TYPED_ARRAY_ARG
|
||||
|
||||
const typedArgMsg = { message: 'Argument 0 must be of type `TypedArray`' };
|
||||
|
||||
describe('REQ_TYPED_ARRAY_ARG', () => {
|
||||
it('exports reqTypedArg', () => {
|
||||
assert.strictEqual(typeof test.reqTypedArg, 'function');
|
||||
});
|
||||
it('throws if arg was not passed', () => {
|
||||
assert.throws(() => test.reqTypedArg(), typedArgMsg);
|
||||
});
|
||||
it('throws if arg was passed undefined', () => {
|
||||
assert.throws(() => test.reqTypedArg(undefined), typedArgMsg);
|
||||
});
|
||||
it('throws if arg was passed null', () => {
|
||||
assert.throws(() => test.reqTypedArg(null), typedArgMsg);
|
||||
});
|
||||
it('throws if arg was passed a string', () => {
|
||||
assert.throws(() => test.reqTypedArg('1'), typedArgMsg);
|
||||
});
|
||||
it('throws if arg was passed a number', () => {
|
||||
assert.throws(() => test.reqTypedArg(1), typedArgMsg);
|
||||
});
|
||||
it('throws if arg was passed a boolean', () => {
|
||||
assert.throws(() => test.reqTypedArg(true), typedArgMsg);
|
||||
});
|
||||
it('throws if arg was passed a pointer', () => {
|
||||
assert.throws(() => test.reqTypedArg(test.retExt()), typedArgMsg);
|
||||
});
|
||||
it('throws if arg was passed an object', () => {
|
||||
assert.throws(() => test.reqTypedArg({}), typedArgMsg);
|
||||
});
|
||||
it('throws if arg was passed an array', () => {
|
||||
assert.throws(() => test.reqTypedArg([]), typedArgMsg);
|
||||
});
|
||||
it('accepts a typed array', () => {
|
||||
const typed = new Uint8Array([1, 2, 3]);
|
||||
assert.strictEqual(test.reqTypedArg(typed), typed);
|
||||
});
|
||||
});
|
||||
});
|
|
@ -1,302 +0,0 @@
|
|||
#include <addon-tools.hpp>
|
||||
|
||||
|
||||
JS_METHOD(empty) { NAPI_ENV;
|
||||
NAPI_HS;
|
||||
RET_UNDEFINED;
|
||||
}
|
||||
|
||||
JS_METHOD(throwing) { NAPI_ENV;
|
||||
JS_THROW("Some error");
|
||||
RET_UNDEFINED;
|
||||
}
|
||||
|
||||
JS_METHOD(retUndefined) { NAPI_ENV;
|
||||
RET_UNDEFINED;
|
||||
}
|
||||
|
||||
JS_METHOD(retNull) { NAPI_ENV;
|
||||
RET_NULL;
|
||||
}
|
||||
|
||||
JS_METHOD(retStr) { NAPI_ENV;
|
||||
RET_STR("abcdef");
|
||||
}
|
||||
|
||||
JS_METHOD(retNum) { NAPI_ENV;
|
||||
RET_NUM(12345);
|
||||
}
|
||||
|
||||
JS_METHOD(retExt) { NAPI_ENV;
|
||||
RET_EXT(nullptr);
|
||||
}
|
||||
|
||||
JS_METHOD(retBool) { NAPI_ENV;
|
||||
RET_BOOL(true);
|
||||
}
|
||||
|
||||
JS_METHOD(retObject) { NAPI_ENV;
|
||||
RET_VALUE(JS_OBJECT);
|
||||
}
|
||||
|
||||
JS_METHOD(retArray) { NAPI_ENV;
|
||||
RET_VALUE(JS_ARRAY);
|
||||
}
|
||||
|
||||
JS_METHOD(reqArgs3) { NAPI_ENV;
|
||||
REQ_ARGS(3);
|
||||
RET_BOOL(true);
|
||||
}
|
||||
|
||||
JS_METHOD(isArg0Empty) { NAPI_ENV;
|
||||
RET_BOOL(IS_ARG_EMPTY(0));
|
||||
}
|
||||
|
||||
JS_METHOD(reqStrArg) { NAPI_ENV;
|
||||
REQ_STR_ARG(0, arg);
|
||||
RET_STR(arg.c_str());
|
||||
}
|
||||
|
||||
JS_METHOD(useStrArg) { NAPI_ENV;
|
||||
USE_STR_ARG(0, arg, "default");
|
||||
RET_STR(arg.c_str());
|
||||
}
|
||||
|
||||
JS_METHOD(letStrArg) { NAPI_ENV;
|
||||
LET_STR_ARG(0, arg);
|
||||
RET_STR(arg.c_str());
|
||||
}
|
||||
|
||||
JS_METHOD(reqIntArg) { NAPI_ENV;
|
||||
REQ_INT_ARG(0, arg);
|
||||
RET_NUM(arg);
|
||||
}
|
||||
|
||||
JS_METHOD(useIntArg) { NAPI_ENV;
|
||||
USE_INT_ARG(0, arg, 10);
|
||||
RET_NUM(arg);
|
||||
}
|
||||
|
||||
JS_METHOD(letIntArg) { NAPI_ENV;
|
||||
LET_INT_ARG(0, arg);
|
||||
RET_NUM(arg);
|
||||
}
|
||||
|
||||
JS_METHOD(reqUintArg) { NAPI_ENV;
|
||||
REQ_UINT_ARG(0, arg);
|
||||
RET_NUM(arg);
|
||||
}
|
||||
|
||||
JS_METHOD(useUintArg) { NAPI_ENV;
|
||||
USE_UINT_ARG(0, arg, 10);
|
||||
RET_NUM(arg);
|
||||
}
|
||||
|
||||
JS_METHOD(letUintArg) { NAPI_ENV;
|
||||
LET_UINT_ARG(0, arg);
|
||||
RET_NUM(arg);
|
||||
}
|
||||
|
||||
JS_METHOD(reqBoolArg) { NAPI_ENV;
|
||||
REQ_BOOL_ARG(0, arg);
|
||||
RET_BOOL(arg);
|
||||
}
|
||||
|
||||
JS_METHOD(useBoolArg) { NAPI_ENV;
|
||||
USE_BOOL_ARG(0, arg, true);
|
||||
RET_BOOL(arg);
|
||||
}
|
||||
|
||||
JS_METHOD(letBoolArg) { NAPI_ENV;
|
||||
LET_BOOL_ARG(0, arg);
|
||||
RET_BOOL(arg);
|
||||
}
|
||||
|
||||
JS_METHOD(reqOffsArg) { NAPI_ENV;
|
||||
REQ_OFFS_ARG(0, arg);
|
||||
RET_NUM(arg);
|
||||
}
|
||||
|
||||
JS_METHOD(useOffsArg) { NAPI_ENV;
|
||||
USE_OFFS_ARG(0, arg, 10);
|
||||
RET_NUM(arg);
|
||||
}
|
||||
|
||||
JS_METHOD(letOffsArg) { NAPI_ENV;
|
||||
LET_OFFS_ARG(0, arg);
|
||||
RET_NUM(arg);
|
||||
}
|
||||
|
||||
JS_METHOD(reqDoubleArg) { NAPI_ENV;
|
||||
REQ_DOUBLE_ARG(0, arg);
|
||||
RET_NUM(arg);
|
||||
}
|
||||
|
||||
JS_METHOD(useDoubleArg) { NAPI_ENV;
|
||||
USE_DOUBLE_ARG(0, arg, 10);
|
||||
RET_NUM(arg);
|
||||
}
|
||||
|
||||
JS_METHOD(letDoubleArg) { NAPI_ENV;
|
||||
LET_DOUBLE_ARG(0, arg);
|
||||
RET_NUM(arg);
|
||||
}
|
||||
|
||||
JS_METHOD(reqFloatArg) { NAPI_ENV;
|
||||
REQ_FLOAT_ARG(0, arg);
|
||||
RET_NUM(arg);
|
||||
}
|
||||
|
||||
JS_METHOD(useFloatArg) { NAPI_ENV;
|
||||
USE_FLOAT_ARG(0, arg, 10);
|
||||
RET_NUM(arg);
|
||||
}
|
||||
|
||||
JS_METHOD(letFloatArg) { NAPI_ENV;
|
||||
LET_FLOAT_ARG(0, arg);
|
||||
RET_NUM(arg);
|
||||
}
|
||||
|
||||
JS_METHOD(reqExtArg) { NAPI_ENV;
|
||||
REQ_EXT_ARG(0, arg);
|
||||
RET_EXT(arg);
|
||||
}
|
||||
|
||||
JS_METHOD(useExtArg) { NAPI_ENV;
|
||||
USE_EXT_ARG(0, arg, nullptr);
|
||||
RET_EXT(arg);
|
||||
}
|
||||
|
||||
JS_METHOD(letExtArg) { NAPI_ENV;
|
||||
LET_EXT_ARG(0, arg);
|
||||
RET_EXT(arg);
|
||||
}
|
||||
|
||||
JS_METHOD(reqObjArg) { NAPI_ENV;
|
||||
REQ_OBJ_ARG(0, arg);
|
||||
RET_VALUE(arg);
|
||||
}
|
||||
|
||||
JS_METHOD(useObjArg) { NAPI_ENV;
|
||||
USE_OBJ_ARG(0, arg, JS_OBJECT);
|
||||
RET_VALUE(arg);
|
||||
}
|
||||
|
||||
JS_METHOD(letObjArg) { NAPI_ENV;
|
||||
LET_OBJ_ARG(0, arg);
|
||||
RET_VALUE(arg);
|
||||
}
|
||||
|
||||
JS_METHOD(reqArrayArg) { NAPI_ENV;
|
||||
REQ_ARRAY_ARG(0, arg);
|
||||
RET_VALUE(arg);
|
||||
}
|
||||
|
||||
JS_METHOD(useArrayArg) { NAPI_ENV;
|
||||
USE_ARRAY_ARG(0, arg, JS_ARRAY);
|
||||
RET_VALUE(arg);
|
||||
}
|
||||
|
||||
JS_METHOD(letArrayArg) { NAPI_ENV;
|
||||
LET_ARRAY_ARG(0, arg);
|
||||
RET_VALUE(arg);
|
||||
}
|
||||
|
||||
JS_METHOD(letArrayStrArg) { NAPI_ENV;
|
||||
LET_ARRAY_STR_ARG(0, arg);
|
||||
RET_ARRAY_STR(arg);
|
||||
}
|
||||
|
||||
JS_METHOD(reqFunArg) { NAPI_ENV;
|
||||
REQ_FUN_ARG(0, arg);
|
||||
RET_VALUE(arg);
|
||||
}
|
||||
|
||||
JS_METHOD(reqArrvArg) { NAPI_ENV;
|
||||
REQ_ARRV_ARG(0, arg);
|
||||
RET_VALUE(arg);
|
||||
}
|
||||
|
||||
JS_METHOD(reqBufArg) { NAPI_ENV;
|
||||
REQ_BUF_ARG(0, arg);
|
||||
RET_VALUE(arg);
|
||||
}
|
||||
|
||||
JS_METHOD(reqTypedArg) { NAPI_ENV;
|
||||
REQ_TYPED_ARRAY_ARG(0, arg);
|
||||
RET_VALUE(arg);
|
||||
}
|
||||
|
||||
|
||||
#define TEST_SET_METHOD(name) \
|
||||
exports.DefineProperty( \
|
||||
Napi::PropertyDescriptor::Function(env, exports, #name, name) \
|
||||
);
|
||||
|
||||
Napi::Object init(Napi::Env env, Napi::Object exports) {
|
||||
TEST_SET_METHOD(empty);
|
||||
TEST_SET_METHOD(throwing);
|
||||
|
||||
TEST_SET_METHOD(retUndefined);
|
||||
TEST_SET_METHOD(retNull);
|
||||
TEST_SET_METHOD(retStr);
|
||||
TEST_SET_METHOD(retNum);
|
||||
TEST_SET_METHOD(retExt);
|
||||
TEST_SET_METHOD(retBool);
|
||||
TEST_SET_METHOD(retObject);
|
||||
TEST_SET_METHOD(retArray);
|
||||
|
||||
TEST_SET_METHOD(reqArgs3);
|
||||
TEST_SET_METHOD(isArg0Empty);
|
||||
|
||||
TEST_SET_METHOD(reqStrArg);
|
||||
TEST_SET_METHOD(useStrArg);
|
||||
TEST_SET_METHOD(letStrArg);
|
||||
|
||||
TEST_SET_METHOD(reqIntArg);
|
||||
TEST_SET_METHOD(useIntArg);
|
||||
TEST_SET_METHOD(letIntArg);
|
||||
|
||||
TEST_SET_METHOD(reqUintArg);
|
||||
TEST_SET_METHOD(useUintArg);
|
||||
TEST_SET_METHOD(letUintArg);
|
||||
|
||||
TEST_SET_METHOD(reqBoolArg);
|
||||
TEST_SET_METHOD(useBoolArg);
|
||||
TEST_SET_METHOD(letBoolArg);
|
||||
|
||||
TEST_SET_METHOD(reqOffsArg);
|
||||
TEST_SET_METHOD(useOffsArg);
|
||||
TEST_SET_METHOD(letOffsArg);
|
||||
|
||||
TEST_SET_METHOD(reqDoubleArg);
|
||||
TEST_SET_METHOD(useDoubleArg);
|
||||
TEST_SET_METHOD(letDoubleArg);
|
||||
|
||||
TEST_SET_METHOD(reqFloatArg);
|
||||
TEST_SET_METHOD(useFloatArg);
|
||||
TEST_SET_METHOD(letFloatArg);
|
||||
|
||||
TEST_SET_METHOD(reqExtArg);
|
||||
TEST_SET_METHOD(useExtArg);
|
||||
TEST_SET_METHOD(letExtArg);
|
||||
|
||||
TEST_SET_METHOD(reqObjArg);
|
||||
TEST_SET_METHOD(useObjArg);
|
||||
TEST_SET_METHOD(letObjArg);
|
||||
|
||||
TEST_SET_METHOD(reqArrayArg);
|
||||
TEST_SET_METHOD(useArrayArg);
|
||||
TEST_SET_METHOD(letArrayArg);
|
||||
|
||||
TEST_SET_METHOD(letArrayStrArg);
|
||||
|
||||
TEST_SET_METHOD(reqFunArg);
|
||||
TEST_SET_METHOD(reqArrvArg);
|
||||
TEST_SET_METHOD(reqBufArg);
|
||||
TEST_SET_METHOD(reqTypedArg);
|
||||
|
||||
return exports;
|
||||
}
|
||||
|
||||
NODE_API_MODULE(test, init)
|
|
@ -0,0 +1,16 @@
|
|||
{
|
||||
"name": "test",
|
||||
"version": "0.0.0",
|
||||
"private": true,
|
||||
"main": "mocha",
|
||||
"scripts": {
|
||||
"test": "mocha",
|
||||
"start": "mocha"
|
||||
},
|
||||
"dependencies": {
|
||||
"addon-tools-raub": "https://github.com/node-3d/addon-tools-raub.git",
|
||||
"chai": "^4.2.0",
|
||||
"mocha": "^5.2.0",
|
||||
"sinon": "^7.1.1"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,128 @@
|
|||
'use strict';
|
||||
|
||||
const path = require('path');
|
||||
const fs = require('fs');
|
||||
|
||||
const { expect } = require('chai');
|
||||
const { stub, spy } = require('sinon');
|
||||
|
||||
const tools = require('addon-tools-raub');
|
||||
|
||||
|
||||
const toolsDir = path.dirname(require.resolve('addon-tools-raub')).replace(/\\/g, '/');
|
||||
const allMethods = ['paths', 'bin', 'root', 'include', 'mkdir', 'rm', 'cp'];
|
||||
const ownMethods = allMethods.slice(1);
|
||||
const cmdMethods = allMethods.slice(3);
|
||||
const pathsMethods = ['bin', 'rem', 'include'];
|
||||
|
||||
|
||||
describe('Tools', () => {
|
||||
|
||||
let log;
|
||||
let stubbed;
|
||||
beforeEach(() => {
|
||||
log = spy();
|
||||
stubbed = stub(console, 'log').callsFake(log);
|
||||
});
|
||||
afterEach(() => stubbed.restore());
|
||||
|
||||
|
||||
describe('Own Methods', () => {
|
||||
|
||||
ownMethods.forEach(
|
||||
m => it(`#${m}() is available`, () => {
|
||||
expect(tools).to.respondTo(m);
|
||||
})
|
||||
);
|
||||
|
||||
|
||||
ownMethods.forEach(
|
||||
m => it(`#${m}() writes stdout`, () => {
|
||||
tools[m]();
|
||||
expect(log.getCall(0), 'called').to.exist;
|
||||
expect(log.getCall(0).args[0], 'has args').to.exist;
|
||||
expect(log.getCall(0).args[0], 'writes string').to.be.a('string');
|
||||
})
|
||||
);
|
||||
|
||||
|
||||
it('#bin() is correct', () => {
|
||||
tools.bin();
|
||||
expect(log.getCall(0).args[0]).to.equal(path.basename(tools.paths(__dirname).binPath));
|
||||
});
|
||||
|
||||
|
||||
it('#root() is correct', () => {
|
||||
tools.root();
|
||||
expect(log.getCall(0).args[0]).to.equal(toolsDir);
|
||||
});
|
||||
|
||||
|
||||
it('#include() is correct', async () => {
|
||||
|
||||
tools.include();
|
||||
const dirs = log.getCall(0).args[0].split(' ');
|
||||
|
||||
const stats = await Promise.all(dirs.map(dir => new Promise(
|
||||
res => fs.stat(
|
||||
dir,
|
||||
(err, stat) => err ? res(false) : res(stat.isDirectory())
|
||||
)
|
||||
)));
|
||||
|
||||
dirs.forEach((dir, i) => expect(stats[i], dir).to.be.true);
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
|
||||
describe('Cmd Methods', () => {
|
||||
|
||||
cmdMethods.forEach(
|
||||
m => it(`#${m}() is available`, () => {
|
||||
expect(tools).to.respondTo(m);
|
||||
})
|
||||
);
|
||||
|
||||
|
||||
cmdMethods.forEach(
|
||||
m => it(`#${m}() writes stdout`, () => {
|
||||
tools[m]();
|
||||
expect(log.getCall(0), 'called').to.exist;
|
||||
expect(log.getCall(0).args[0], 'has args').to.exist;
|
||||
expect(log.getCall(0).args[0], 'writes string').to.be.a('string');
|
||||
})
|
||||
);
|
||||
|
||||
});
|
||||
|
||||
|
||||
describe('Paths', () => {
|
||||
|
||||
it('#paths() returns an object', () => {
|
||||
expect(tools.paths(__dirname)).to.be.an('object');
|
||||
});
|
||||
|
||||
|
||||
pathsMethods.forEach(
|
||||
m => it(`#${m}() is available`, () => {
|
||||
const paths = tools.paths(__dirname);
|
||||
expect(paths).to.respondTo(m);
|
||||
})
|
||||
);
|
||||
|
||||
|
||||
pathsMethods.forEach(
|
||||
m => it(`#${m}() writes stdout`, () => {
|
||||
const paths = tools.paths(__dirname);
|
||||
paths[m]();
|
||||
expect(log.getCall(0), 'called').to.exist;
|
||||
expect(log.getCall(0).args[0], 'has args').to.exist;
|
||||
expect(log.getCall(0).args[0], 'writes string').to.be.a('string');
|
||||
})
|
||||
);
|
||||
|
||||
});
|
||||
|
||||
});
|
|
@ -1,20 +0,0 @@
|
|||
'use strict';
|
||||
|
||||
const util = require('node:util');
|
||||
const exec = util.promisify(require('node:child_process').exec);
|
||||
|
||||
const { getPlatform, getBin } = require('../include');
|
||||
|
||||
|
||||
const actionPack = async () => {
|
||||
try {
|
||||
await exec(`cd ${getBin()} && tar -czf ../${getPlatform()}.gz *`);
|
||||
console.log(`pack=${getPlatform()}.gz`);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
process.exit(-1);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
module.exports = { actionPack };
|
|
@ -1,31 +0,0 @@
|
|||
'use strict';
|
||||
|
||||
const { copy, exists, mkdir, rm } = require('./files');
|
||||
const { getBin } = require('../include');
|
||||
|
||||
|
||||
const cpbin = async (name) => {
|
||||
const srcDir = process.cwd().replace(/\\/g, '/');
|
||||
|
||||
if (!await exists(`${srcDir}/build/Release/${name}.node`) ) {
|
||||
console.error(`Error. File "${srcDir}/build/Release/${name}.node" not found.`);
|
||||
}
|
||||
|
||||
const binAbs = `${srcDir}/../${getBin()}`;
|
||||
|
||||
if (!await exists(binAbs)) {
|
||||
await mkdir(binAbs);
|
||||
}
|
||||
|
||||
const destAbs = `${binAbs}/${name}.node`;
|
||||
|
||||
if (await exists(destAbs)) {
|
||||
await rm(destAbs);
|
||||
}
|
||||
|
||||
await copy(`${srcDir}/build/Release/${name}.node`, destAbs);
|
||||
|
||||
console.log(`The binary "${name}.node" was copied to "${getBin()}".`);
|
||||
};
|
||||
|
||||
module.exports = { cpbin };
|
|
@ -1,24 +0,0 @@
|
|||
'use strict';
|
||||
|
||||
const { copy, exists } = require('./files');
|
||||
|
||||
|
||||
const cpcpplint = async () => {
|
||||
const cpplintDest = `${process.cwd()}/CPPLINT.cfg`.replace(/\\/g, '/');
|
||||
const cpplintSrc = `${__dirname}/CPPLINT.cfg`.replace(/\\/g, '/');
|
||||
|
||||
if (!await exists(cpplintSrc) ) {
|
||||
console.error('Error. File "CPPLINT.cfg" not found.');
|
||||
return;
|
||||
}
|
||||
|
||||
if (await exists(cpplintDest) ) {
|
||||
console.warn('Warning. Dest "CPPLINT.cfg" exists and will be overwritten.');
|
||||
}
|
||||
|
||||
await copy(cpplintSrc, cpplintDest);
|
||||
|
||||
console.log(`"CPPLINT.cfg" was copied to "${cpplintDest}".`);
|
||||
};
|
||||
|
||||
module.exports = { cpcpplint };
|
|
@ -1,46 +0,0 @@
|
|||
'use strict';
|
||||
|
||||
const https = require('node:https');
|
||||
const http = require('node:http');
|
||||
|
||||
const { WritableBuffer } = require('./writable-buffer');
|
||||
|
||||
|
||||
const protocols = { http, https };
|
||||
|
||||
|
||||
const downloadRecursive = async (url, count = 1) => {
|
||||
const stream = new WritableBuffer();
|
||||
const proto = protocols[url.match(/^https?/i)[0].toLowerCase()];
|
||||
|
||||
const response = await new Promise((res, rej) => {
|
||||
const request = proto.get(url, (response) => res(response));
|
||||
request.on('error', (err) => rej(err));
|
||||
});
|
||||
|
||||
// Handle redirects
|
||||
if ([301, 302, 303, 307].includes(response.statusCode)) {
|
||||
if (count < 5) {
|
||||
return downloadRecursive(response.headers.location, count + 1);
|
||||
}
|
||||
console.log(url);
|
||||
throw new Error('Error: Too many redirects.');
|
||||
}
|
||||
|
||||
// Handle bad status
|
||||
if (response.statusCode !== 200) {
|
||||
console.log(url);
|
||||
throw new Error(`Response status was ${response.statusCode}`);
|
||||
}
|
||||
|
||||
response.pipe(stream);
|
||||
|
||||
return new Promise((res, rej) => {
|
||||
response.on('error', (err) => rej(err));
|
||||
response.on('end', () => res(stream.get()));
|
||||
});
|
||||
};
|
||||
|
||||
const download = (url) => downloadRecursive(url);
|
||||
|
||||
module.exports = { download };
|
196
utils/files.js
196
utils/files.js
|
@ -1,196 +0,0 @@
|
|||
'use strict';
|
||||
|
||||
const fs = require('node:fs');
|
||||
|
||||
|
||||
// (async) Reads a whole file to string, NOT A Buffer
|
||||
const read = (name) => new Promise(
|
||||
(res, rej) => fs.readFile(
|
||||
name,
|
||||
(err, data) => (err ? rej(err) : res(data.toString()))
|
||||
)
|
||||
);
|
||||
|
||||
|
||||
// (async) Write a file
|
||||
const write = (name, text) => new Promise(
|
||||
(res, rej) => fs.writeFile(name, text, (err) => (err ? rej(err) : res()))
|
||||
);
|
||||
|
||||
|
||||
// (async) Copy a file
|
||||
const copy = async (src, dest) => {
|
||||
try {
|
||||
await new Promise(
|
||||
(res, rej) => fs.copyFile(src, dest, (err) => (err ? rej(err) : res()))
|
||||
);
|
||||
} catch (e) {
|
||||
if (e.code !== 'EBUSY') {
|
||||
console.warn('WARNING\n', e);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// (async) Check if a file/folder exists
|
||||
const exists = (name) => new Promise(
|
||||
(res) => fs.access(
|
||||
name,
|
||||
fs.constants.F_OK,
|
||||
(err) => res(err ? false : true)
|
||||
)
|
||||
);
|
||||
|
||||
|
||||
// (async) Create an empty folder
|
||||
const mkdir = async (name) => {
|
||||
if (await exists(name)) {
|
||||
return;
|
||||
}
|
||||
return new Promise(
|
||||
(res, rej) => fs.mkdir(name, (err) => (err ? rej(err) : res()))
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
// (async) Get status on a file
|
||||
const stat = (name) => new Promise(
|
||||
(res, rej) => fs.stat(name, (err, stats) => (err ? rej(err) : res(stats)))
|
||||
);
|
||||
|
||||
|
||||
// (async) Check if the path is a folder
|
||||
const isDir = async (name) => (await stat(name)).isDirectory();
|
||||
|
||||
|
||||
// (async) Check if the path is a file
|
||||
const isFile = async (name) => (await stat(name)).isFile();
|
||||
|
||||
|
||||
// Cut the path one folder up
|
||||
const dirUp = (dir) => dir.replace(/\\/g, '/').split('/').slice(0, -1).join('/');
|
||||
|
||||
|
||||
// (async) Like `mkdir -p`, makes sure a directory exists
|
||||
const ensuredir = async (dir) => {
|
||||
if (await exists(dir) && await isDir(dir)) {
|
||||
return;
|
||||
}
|
||||
await ensuredir(dirUp(dir));
|
||||
await mkdir(dir);
|
||||
};
|
||||
|
||||
|
||||
// (async) Copy a file, `dest` folder is created if needed
|
||||
const copysafe = async (src, dest) => {
|
||||
await ensuredir(dirUp(dest));
|
||||
await copy(src, dest);
|
||||
};
|
||||
|
||||
|
||||
// (async) Get file/folder names of the 1st level
|
||||
const readdir = (name) => new Promise(
|
||||
(res, rej) => fs.readdir(
|
||||
name,
|
||||
(err, dirents) => (err ? rej(err) : res(dirents))
|
||||
)
|
||||
);
|
||||
|
||||
|
||||
// (async) Get folder paths (concatenated with input) of the 1st level
|
||||
const subdirs = async (name) => {
|
||||
const all = await readdir(name);
|
||||
const mapped = await Promise.all(all.map((d) => isDir(`${name}/${d}`)));
|
||||
return all.filter((_, i) => mapped[i]);
|
||||
};
|
||||
|
||||
|
||||
// (async) Get file paths (concatenated with input) of the 1st level
|
||||
const subfiles = async (name) => {
|
||||
const all = await readdir(name);
|
||||
const mapped = await Promise.all(all.map((d) => isFile(`${name}/${d}`)));
|
||||
return all.filter((_, i) => mapped[i]).map((f) => `${name}/${f}`);
|
||||
};
|
||||
|
||||
|
||||
// (async) Get all nested files recursively
|
||||
// Folder paths are omitted by default
|
||||
// Order is: shallow-to-deep, each subdirectory lists dirs-then-files.
|
||||
const traverse = async (name, showDirs = false) => {
|
||||
const dirs = [];
|
||||
const stack = [name];
|
||||
while (stack.length) {
|
||||
const dir = stack.pop();
|
||||
dirs.push(dir);
|
||||
(await subdirs(dir)).forEach((d) => stack.push(`${dir}/${d}`));
|
||||
}
|
||||
return (showDirs ? dirs : []).concat(
|
||||
...(await Promise.all(dirs.map(subfiles)))
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
// (async) Copy a folder with all the contained files
|
||||
const copyall = async (src, dest) => {
|
||||
const files = (await traverse(src, true)).reverse();
|
||||
while (files.length) {
|
||||
const target = files.pop();
|
||||
const dir = await isDir(target);
|
||||
if (dir) {
|
||||
await mkdir(target.replace(src, dest));
|
||||
} else {
|
||||
await copy(target, target.replace(src, dest));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// (async) Like `rm -rf`, removes everything recursively
|
||||
const rmdir = async (name) => {
|
||||
if (!await exists(name)) {
|
||||
return;
|
||||
}
|
||||
const paths = await traverse(name, true);
|
||||
while (paths.length) {
|
||||
const target = paths.pop();
|
||||
const dir = await isDir(target);
|
||||
await new Promise(
|
||||
(res, rej) => fs[dir ? 'rmdir' : 'unlink'](
|
||||
target,
|
||||
(err) => (err ? rej(err) : res())
|
||||
)
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// (async) Remove a file. Must be a file, not a folder. Just `fs.unlink`.
|
||||
const rm = async (name) => {
|
||||
if (!await exists(name)) {
|
||||
return;
|
||||
}
|
||||
await new Promise(
|
||||
(res, rej) => fs.unlink(name, (err) => (err ? rej(err) : res()))
|
||||
);
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
read,
|
||||
write,
|
||||
copy,
|
||||
exists,
|
||||
mkdir,
|
||||
stat,
|
||||
isDir,
|
||||
isFile,
|
||||
dirUp,
|
||||
ensuredir,
|
||||
copysafe,
|
||||
readdir,
|
||||
subdirs,
|
||||
subfiles,
|
||||
traverse,
|
||||
copyall,
|
||||
rmdir,
|
||||
rm,
|
||||
};
|
|
@ -1,13 +0,0 @@
|
|||
'use strict';
|
||||
|
||||
|
||||
module.exports = Object.assign(
|
||||
{},
|
||||
require('./action-pack'),
|
||||
require('./cpbin'),
|
||||
require('./cpcpplint'),
|
||||
require('./download'),
|
||||
require('./files'),
|
||||
require('./install'),
|
||||
require('./writable-buffer'),
|
||||
);
|
|
@ -1,72 +0,0 @@
|
|||
'use strict';
|
||||
|
||||
const fs = require('node:fs');
|
||||
const https = require('node:https');
|
||||
const http = require('node:http');
|
||||
const util = require('node:util');
|
||||
const exec = util.promisify(require('node:child_process').exec);
|
||||
|
||||
const { getBin, getPlatform } = require('../include');
|
||||
const { mkdir, rmdir, rm } = require('./files');
|
||||
|
||||
|
||||
const protocols = { http, https };
|
||||
|
||||
|
||||
const installRecursive = async (url, count = 1) => {
|
||||
try {
|
||||
const proto = protocols[url.match(/^https?/)[0]];
|
||||
|
||||
const response = await new Promise((res, rej) => {
|
||||
const request = proto.get(url, (response) => res(response));
|
||||
request.on('error', (err) => rej(err));
|
||||
});
|
||||
response.on('error', (err) => { throw err; });
|
||||
|
||||
// Handle redirects
|
||||
if ([301, 302, 303, 307].includes(response.statusCode)) {
|
||||
if (count < 5) {
|
||||
return installRecursive(response.headers.location, count + 1);
|
||||
}
|
||||
console.warn(url);
|
||||
throw new Error('Error: Too many redirects.');
|
||||
}
|
||||
|
||||
// Handle bad status
|
||||
if (response.statusCode !== 200) {
|
||||
console.warn(url);
|
||||
throw new Error(`Response status was ${response.statusCode}`);
|
||||
}
|
||||
|
||||
await rmdir(getBin());
|
||||
await mkdir(getBin());
|
||||
|
||||
const packPath = `${getBin()}/${getPlatform()}.gz`;
|
||||
|
||||
await new Promise((res, rej) => {
|
||||
const packWriter = fs.createWriteStream(packPath);
|
||||
packWriter.on('error', (err) => rej(err));
|
||||
packWriter.on('finish', () => res());
|
||||
response.pipe(packWriter);
|
||||
});
|
||||
|
||||
const { stderr } = await exec(`tar -xzf ${packPath} --directory ${getBin()}`);
|
||||
if (stderr) {
|
||||
console.warn(stderr);
|
||||
}
|
||||
|
||||
await rm(packPath);
|
||||
return true;
|
||||
} catch (error) {
|
||||
console.error(error.message);
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
const install = async (folder) => {
|
||||
const url = `${folder}/${getPlatform()}.gz`;
|
||||
return installRecursive(url);
|
||||
};
|
||||
|
||||
module.exports = { install };
|
|
@ -1,22 +0,0 @@
|
|||
'use strict';
|
||||
|
||||
const assert = require('node:assert').strict;
|
||||
const { describe, it } = require('node:test');
|
||||
|
||||
const utils = require('.');
|
||||
|
||||
|
||||
describe('AT / utils', () => {
|
||||
const methods = [
|
||||
'install', 'cpbin', 'download', 'read', 'write', 'copy', 'exists',
|
||||
'mkdir', 'stat', 'isDir', 'isFile', 'dirUp', 'ensuredir', 'copysafe',
|
||||
'readdir', 'subdirs', 'subfiles', 'traverse', 'copyall',
|
||||
'rmdir', 'rm', 'WritableBuffer', 'actionPack',
|
||||
];
|
||||
|
||||
methods.forEach((name) => {
|
||||
it(`exports the "${name}" function`, () => {
|
||||
assert.strictEqual(typeof utils[name], 'function');
|
||||
});
|
||||
});
|
||||
});
|
|
@ -1,56 +0,0 @@
|
|||
'use strict';
|
||||
|
||||
const { Writable } = require('node:stream');
|
||||
|
||||
|
||||
const CHUNK_SIZE = 1024;
|
||||
const INITIAL_SIZE = 8 * CHUNK_SIZE;
|
||||
const INCREMENT_SIZE = 8 * CHUNK_SIZE;
|
||||
|
||||
|
||||
class WritableBuffer extends Writable {
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this._buffer = Buffer.alloc(INITIAL_SIZE);
|
||||
this._size = 0;
|
||||
}
|
||||
|
||||
get() {
|
||||
if (!this._size) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const data = Buffer.alloc(this._size);
|
||||
this._buffer.copy(data, 0, 0, this._size);
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
|
||||
_increaseAsNeeded(incomingSize) {
|
||||
if ((this._buffer.length - this._size) >= incomingSize) {
|
||||
return;
|
||||
}
|
||||
|
||||
const freeSpace = this._buffer.length - this._size;
|
||||
const factor = Math.ceil((incomingSize - freeSpace) / INCREMENT_SIZE);
|
||||
|
||||
const newBuffer = Buffer.alloc(this._buffer.length + (INCREMENT_SIZE * factor));
|
||||
this._buffer.copy(newBuffer, 0, 0, this._size);
|
||||
|
||||
this._buffer = newBuffer;
|
||||
}
|
||||
|
||||
|
||||
_write(chunk, encoding, callback) {
|
||||
this._increaseAsNeeded(chunk.length);
|
||||
|
||||
chunk.copy(this._buffer, this._size, 0);
|
||||
this._size += chunk.length;
|
||||
|
||||
callback();
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = { WritableBuffer };
|
Loading…
Reference in New Issue