Compare commits
209 Commits
Author | SHA1 | Date |
---|---|---|
|
1ca78906a9 | |
|
9d68a62da4 | |
|
6c19ce79c3 | |
|
fc8b31fe36 | |
|
c82e029e90 | |
|
b501c07de1 | |
|
aeb2eae343 | |
|
747e1f6205 | |
|
c9c391ffd0 | |
|
d5ffbd6ba8 | |
|
1c0b3afac5 | |
|
afd180d7cf | |
|
aa063f9eae | |
|
105964614f | |
|
4b31874a56 | |
|
3f306a4938 | |
|
f9f3ac2a23 | |
|
9852e1e789 | |
|
a56e079de0 | |
|
abc49171eb | |
|
6bdcf9abe2 | |
|
0564af1e96 | |
|
104fad35ae | |
|
9c76216823 | |
|
955e8b61a8 | |
|
05e53641c1 | |
|
ac0c472be9 | |
|
b05666869a | |
|
40e4baeb56 | |
|
3d02cfb49c | |
|
fd165d935d | |
|
b532ca47bf | |
|
da96b71507 | |
|
30f5fce2ec | |
|
6e66f46a4a | |
|
a274cd227e | |
|
d4ed519e71 | |
|
3f52e63257 | |
|
cd8527cdb1 | |
|
12fe6b4569 | |
|
341b5e67a2 | |
|
5d4708dfdf | |
|
eafb17d791 | |
|
edc530bb5e | |
|
018b5aca47 | |
|
68731102df | |
|
b77a3cac96 | |
|
a0a7f6153c | |
|
fc183b9793 | |
|
9857670794 | |
|
a7028c0062 | |
|
4ed13bb471 | |
|
f2f3c440f0 | |
|
f9fe9f0124 | |
|
847a68a2d2 | |
|
c4d42eea40 | |
|
3224521e0c | |
|
cd504c13a3 | |
|
29681a0b46 | |
|
33b4892dfb | |
|
410b704d3d | |
|
cca802d932 | |
|
4b98f5f435 | |
|
498de135cb | |
|
3fea905be4 | |
|
5dd1b40e10 | |
|
5f52a14c85 | |
|
ae9fd3e8ba | |
|
cb8e4841ba | |
|
0a1b289ce4 | |
|
773a356788 | |
|
8a2c0187b2 | |
|
5af58c65d3 | |
|
acddbf669d | |
|
ebdf6d780c | |
|
d1ee793b75 | |
|
0057f6744a | |
|
6aa6dfe35f | |
|
76c70481e4 | |
|
0ca0012ab9 | |
|
2402ee198a | |
|
d68ec8a60f | |
|
f48b78cf20 | |
|
3a7ed91a68 | |
|
dcccb8d4c0 | |
|
9bcce8ede3 | |
|
2c6f74a253 | |
|
8b07e4aab3 | |
|
1ca8dab472 | |
|
360867eb0d | |
|
8e64285e8d | |
|
8f642c6007 | |
|
0ff55d5010 | |
|
10dd5627d4 | |
|
c399f2dfac | |
|
753e39c6b1 | |
|
c6435911f6 | |
|
5434ac9ee8 | |
|
09c43c8801 | |
|
3870468ac1 | |
|
03077d415e | |
|
920be8cbe8 | |
|
63cfadfc0d | |
|
7f4dd4f37d | |
|
31ede2853f | |
|
90a417bedf | |
|
77995f76f6 | |
|
46e82f8ea8 | |
|
99f06682bf | |
|
f351c448cb | |
|
cbae1f5fa7 | |
|
791c1a34d3 | |
|
a570e12166 | |
|
efb180be23 | |
|
7fd868cfde | |
|
e99a1a1033 | |
|
aa800818ed | |
|
b985816b26 | |
|
165b3cfbd3 | |
|
ff5baa1e0a | |
|
a05692ebb2 | |
|
572d89b067 | |
|
fcabcff8cc | |
|
811bcea608 | |
|
1e1edf7611 | |
|
99322630f5 | |
|
b5aee1307f | |
|
98aaf2d4ba | |
|
bb46894d97 | |
|
7e7b4dc3e9 | |
|
96b753542f | |
|
f0680a40de | |
|
449dc12afd | |
|
2600de3f8f | |
|
5654bbc5d5 | |
|
67310fffa0 | |
|
912b12b215 | |
|
ddb2217277 | |
|
d54af88dd2 | |
|
2ae137d034 | |
|
f72cc86fc5 | |
|
670cef5102 | |
|
2fa32d8e5d | |
|
b6f7724558 | |
|
3150f55c86 | |
|
18d727a499 | |
|
301a280342 | |
|
af315ee1fd | |
|
43315b4cf0 | |
|
c7aba01a06 | |
|
d75fb96f6b | |
|
b71173820d | |
|
72073bfe79 | |
|
93121bc92a | |
|
688b7b08a9 | |
|
c548b29ecd | |
|
95173cd89c | |
|
1fb87351a5 | |
|
be5e80f2ec | |
|
9c956b0081 | |
|
04005e1fae | |
|
be27a4226c | |
|
96d1f1c052 | |
|
f3496156fa | |
|
0205554b3b | |
|
6ece43b5fd | |
|
8136b3af00 | |
|
e341dd8d37 | |
|
b33c19e45a | |
|
cca0795e2d | |
|
c4ab0d197c | |
|
47209cb113 | |
|
f09dddceb0 | |
|
88ee8720d5 | |
|
8e7a589bd8 | |
|
003d31356a | |
|
347d2f5311 | |
|
193931995c | |
|
110d80dd59 | |
|
8f568ae4e6 | |
|
db1b6cad42 | |
|
ccdb5a301f | |
|
5824923016 | |
|
b20318a9d2 | |
|
fdba6db005 | |
|
f488086bec | |
|
3bb154afd6 | |
|
fdb07186df | |
|
da9b94d11f | |
|
601ef54410 | |
|
63e78bac9a | |
|
8718958591 | |
|
efcb17a23a | |
|
7323e76ecc | |
|
7fc07c3a71 | |
|
5a18d45cbe | |
|
abcf86a3ba | |
|
4b642f6a2d | |
|
0a8b92893b | |
|
06e148c2d7 | |
|
75d6f468f1 | |
|
d5f1697e2e | |
|
c77f4f30dd | |
|
3a22850637 | |
|
d07c7d747e | |
|
183aba4b6f | |
|
69f2775fc0 | |
|
7baf4be872 | |
|
b6fa42d93f |
116
.eslintrc
116
.eslintrc
|
@ -1,116 +0,0 @@
|
||||||
{
|
|
||||||
"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,75 @@
|
||||||
|
{
|
||||||
|
"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"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1 @@
|
||||||
|
test-addon/binding.gyp linguist-vendored
|
|
@ -0,0 +1,47 @@
|
||||||
|
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
|
|
@ -0,0 +1,36 @@
|
||||||
|
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
|
|
@ -0,0 +1,46 @@
|
||||||
|
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 }}
|
|
@ -0,0 +1,43 @@
|
||||||
|
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,11 +1,9 @@
|
||||||
.idea
|
|
||||||
.cproject
|
.cproject
|
||||||
.project
|
.idea
|
||||||
.lock-wscript
|
.lock-wscript
|
||||||
build*/
|
|
||||||
.DS_Store
|
.DS_Store
|
||||||
Debug/
|
.project
|
||||||
|
.DS_Store
|
||||||
node_modules/
|
node_modules/
|
||||||
package-lock.json
|
test-addon/build/
|
||||||
binary/
|
|
||||||
*.log
|
*.log
|
||||||
|
|
15
.npmignore
15
.npmignore
|
@ -1,15 +0,0 @@
|
||||||
*.log
|
|
||||||
.cproject
|
|
||||||
.eslintrc
|
|
||||||
.gitignore
|
|
||||||
.idea
|
|
||||||
.lock-wscript
|
|
||||||
.project
|
|
||||||
binary/
|
|
||||||
build*/
|
|
||||||
CPPLINT.cfg
|
|
||||||
Debug/
|
|
||||||
examples/
|
|
||||||
package-lock.json
|
|
||||||
test/
|
|
||||||
qt/
|
|
|
@ -0,0 +1,17 @@
|
||||||
|
{
|
||||||
|
"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
|
||||||
|
}
|
15
CPPLINT.cfg
15
CPPLINT.cfg
|
@ -1,15 +0,0 @@
|
||||||
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
|
|
2
LICENSE
2
LICENSE
|
@ -1,6 +1,6 @@
|
||||||
MIT License
|
MIT License
|
||||||
|
|
||||||
Copyright (c) 2018 Luis Blanco
|
Copyright (c) 2023 Luis Blanco
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
|
774
README.md
774
README.md
|
@ -2,774 +2,74 @@
|
||||||
|
|
||||||
This is a part of [Node3D](https://github.com/node-3d) project.
|
This is a part of [Node3D](https://github.com/node-3d) project.
|
||||||
|
|
||||||
|
[](https://badge.fury.io/js/addon-tools-raub)
|
||||||
## Synopsis
|
[](https://github.com/node-3d/addon-tools-raub/actions/workflows/eslint.yml)
|
||||||
|
[](https://github.com/node-3d/addon-tools-raub/actions/workflows/test.yml)
|
||||||
Helpers for Node.js addons and dependency packages:
|
[](https://github.com/node-3d/addon-tools-raub/actions/workflows/cpplint.yml)
|
||||||
|
|
||||||
* `consoleLog()` C++ implementation.
|
|
||||||
* `EventEmitter` C++ implementation.
|
|
||||||
* C++ macros and shortcuts.
|
|
||||||
* Crossplatform commands for GYP: `cp`, `rm`, `mkdir`.
|
|
||||||
* Regarded platforms: win x32/x64, linux x32/x64, mac x64.
|
|
||||||
|
|
||||||
Useful links: [V8 Ref](https://v8docs.nodesource.com/node-0.8/d2/dc3/namespacev8.html),
|
|
||||||
[Nan Docs](https://github.com/nodejs/nan#api),
|
|
||||||
[GYP Docs](https://gyp.gsrc.io/docs/UserDocumentation.md).
|
|
||||||
|
|
||||||
|
|
||||||
## Install
|
|
||||||
|
|
||||||
`npm i -s addon-tools-raub`
|
|
||||||
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 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
|
|
||||||
|
|
||||||
* Crossplatform commands can be put into the variables for later use.
|
|
||||||
|
|
||||||
```
|
```
|
||||||
'variables': {
|
npm i -s addon-tools-raub
|
||||||
'rm' : '<!(node -e "require(\'addon-tools-raub\').rm()")',
|
|
||||||
'cp' : '<!(node -e "require(\'addon-tools-raub\').cp()")',
|
|
||||||
'mkdir' : '<!(node -e "require(\'addon-tools-raub\').mkdir()")',
|
|
||||||
},
|
|
||||||
```
|
```
|
||||||
|
|
||||||
* Include directories for Addon Tools and Nan (which is preinstalled with Addon Tools)
|
|
||||||
are accessible as shown below.
|
|
||||||
|
|
||||||
```
|
|
||||||
'include_dirs': [
|
|
||||||
'<!@(node -e "require(\'addon-tools-raub\').include()")',
|
|
||||||
],
|
|
||||||
```
|
|
||||||
|
|
||||||
* Intermediate build-files can be removed in a separate build-step with `<(rm)`.
|
|
||||||
|
|
||||||
<details>
|
|
||||||
|
|
||||||
<summary>Show Snippet</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/*.*'
|
|
||||||
] } ],
|
|
||||||
```
|
|
||||||
|
|
||||||
</details>
|
|
||||||
|
|
||||||
|
|
||||||
### Binary dependencies
|
|
||||||
|
|
||||||
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-linux32
|
|
||||||
* 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 Snippet</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
|
|
||||||
|
|
||||||
If you always copy your compiled addon to the `binary` directory, it will be easy to
|
|
||||||
`require()` it without any hesitation. For copying, you can use the following snippet:
|
|
||||||
|
|
||||||
<details>
|
|
||||||
|
|
||||||
<summary>Show Snippet</summary>
|
|
||||||
|
|
||||||
```
|
|
||||||
{
|
|
||||||
'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'],
|
|
||||||
}],
|
|
||||||
},
|
|
||||||
```
|
|
||||||
|
|
||||||
</details>
|
|
||||||
|
|
||||||
Here `MY_ADDON` should be replaced by any name you like. Then require like
|
|
||||||
this:
|
|
||||||
|
|
||||||
```
|
|
||||||
module.exports = require('./binary/MY_ADDON');
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Generic addon snippet
|
|
||||||
|
|
||||||
<details>
|
|
||||||
|
|
||||||
<summary>binding.gyp</summary>
|
|
||||||
|
|
||||||
* Assume `EXT_LIB` is the name of an Addon Tools compliant binary dependency module.
|
|
||||||
* Assume `MY_ADDON` is the name of this addon.
|
|
||||||
* Assume C++ code goes to `cpp` directory.
|
|
||||||
|
|
||||||
```
|
|
||||||
{
|
|
||||||
'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()")',
|
|
||||||
'EXT_LIB_include' : '<!(node -e "require(\'node-deps-EXT_LIB-raub\').include()")',
|
|
||||||
'EXT_LIB_bin' : '<!(node -e "require(\'node-deps-EXT_LIB-raub\').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','/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/*.*'
|
|
||||||
] } ],
|
|
||||||
],
|
|
||||||
}],
|
|
||||||
},
|
|
||||||
|
|
||||||
]
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
</details>
|
|
||||||
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## include/addon-tools.hpp
|
## include/addon-tools.hpp
|
||||||
|
|
||||||
There is a C++ header file, `addon-tools.hpp`, shipped with this package. It
|
Macro shortcuts for C++ addons using **NAPI**.
|
||||||
introduces several useful macros and utilities. Also it includes Nan automatically,
|
See [docs inside the folder](/include).
|
||||||
so that you can replace:
|
|
||||||
|
Example of an addon method definition:
|
||||||
|
|
||||||
```
|
```
|
||||||
// #include <v8.h> // node.h includes it
|
// hpp:
|
||||||
// #include <node.h> // nan.h includes it
|
#include <addon-tools.hpp>
|
||||||
#include <nan.h>
|
DBG_EXPORT JS_METHOD(doSomething);
|
||||||
```
|
// cpp:
|
||||||
|
DBG_EXPORT JS_METHOD(doSomething) { NAPI_ENV;
|
||||||
with
|
LET_INT32_ARG(0, param0);
|
||||||
|
std::cout << "param0: " << param0 << std::endl;
|
||||||
```
|
RET_UNDEFINED;
|
||||||
#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
|
|
||||||
* `RET_UNDEFINED` - set method return value as undefined
|
|
||||||
|
|
||||||
</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.
|
|
||||||
* `JS_OBJ(val)` - get an object from persistent.
|
|
||||||
|
|
||||||
</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 *getImageData(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 `NULL` for empty JS values.
|
|
||||||
For unacceptable values throws TypeError.
|
|
||||||
|
|
||||||
</details>
|
|
||||||
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## index.js
|
## index.js
|
||||||
|
|
||||||
Exports:
|
JavaScript helpers for Node.js addon development. The short list of helpers:
|
||||||
* `paths(dir)` - function. Returns a set of platform dependent paths depending on
|
|
||||||
input `dir`.
|
|
||||||
* `bin()` - prints platform binary 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 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.
|
|
||||||
* `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.
|
|
||||||
|
|
||||||
|
|
||||||
```
|
```
|
||||||
'variables': {
|
'getBin', 'getPlatform', 'getInclude', 'getPaths',
|
||||||
'mkdir' : '<!(node -e "require(\'addon-tools-raub\').mkdir()")',
|
'install', 'cpbin', 'download', 'read', 'write', 'copy', 'exists',
|
||||||
},
|
'mkdir', 'stat', 'isDir', 'isFile', 'dirUp', 'ensuredir', 'copysafe',
|
||||||
...
|
'readdir', 'subdirs', 'subfiles', 'traverse', 'copyall',
|
||||||
'action' : ['<(mkdir)', '-p', 'binary'],
|
'rmdir', 'rm', 'WritableBuffer', 'actionPack',
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
### rm
|
See the [TypeScript definitions](/index.d.ts) with comments.
|
||||||
|
|
||||||
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.
|
### Example for an ADDON's **index.js**:
|
||||||
|
|
||||||
```
|
```
|
||||||
'variables': {
|
const { getBin } = require('addon-tools-raub');
|
||||||
'rm' : '<!(node -e "require(\'addon-tools-raub\').rm()")',
|
const core = require(`./${getBin()}/ADDON`); // uses the platform-specific ADDON.node
|
||||||
'rem' : '<!(node -e "require(\'.\').rem()")',
|
|
||||||
},
|
|
||||||
...
|
|
||||||
'action' : ['<(rm)', '-rf', '<@(rem)'],
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### cp
|
|
||||||
|
|
||||||
For Windows the `/y` flag was embedded.
|
### Example for **binding.gyp**:
|
||||||
|
|
||||||
```
|
|
||||||
'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::Local<v8::Value> that, const std::string &method)`
|
|
||||||
subscribes `that[method]` to receive `name` events from this emitter, basically
|
|
||||||
`emitter.on(name, that[method])`.
|
|
||||||
|
|
||||||
* `virtual 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': [
|
'include_dirs': [
|
||||||
'<!@(node -e "require(\'addon-tools-raub\').include()")',
|
'<!@(node -p "require(\'addon-tools-raub\').getInclude()")',
|
||||||
],
|
],
|
||||||
```
|
```
|
||||||
|
|
||||||
Then include the **event-emitter.hpp**, it also includes **addon-tools.hpp**.
|
> NOTE: the optional `node-addon-api` dependency is used by the `getInclude()` helper. If not found,
|
||||||
Inherit from `EventEmitter`, it already inherits from `Nan::ObjectWrap`:
|
the **napi.h** include path won't be a part of the returned string.
|
||||||
|
|
||||||
|
|
||||||
|
### Example of `cpbin` in **package.json :: scripts**:
|
||||||
|
|
||||||
```
|
```
|
||||||
#include <event-emitter.hpp>
|
"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 ..",
|
||||||
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.
|
|
||||||
|
|
|
@ -1,5 +0,0 @@
|
||||||
for %%x in (%*) do (
|
|
||||||
|
|
||||||
if not %%x=="-p" if not exist %%x md %%x
|
|
||||||
|
|
||||||
)
|
|
6
_rm.bat
6
_rm.bat
|
@ -1,6 +0,0 @@
|
||||||
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
|
|
||||||
|
|
||||||
)
|
|
|
@ -1,116 +0,0 @@
|
||||||
{
|
|
||||||
"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,11 +0,0 @@
|
||||||
.idea
|
|
||||||
.cproject
|
|
||||||
.project
|
|
||||||
.lock-wscript
|
|
||||||
build*/
|
|
||||||
.DS_Store
|
|
||||||
Debug/
|
|
||||||
node_modules/
|
|
||||||
package-lock.json
|
|
||||||
binary/
|
|
||||||
*.log
|
|
|
@ -1,15 +0,0 @@
|
||||||
*.log
|
|
||||||
.cproject
|
|
||||||
.eslintrc
|
|
||||||
.gitignore
|
|
||||||
.idea
|
|
||||||
.lock-wscript
|
|
||||||
.project
|
|
||||||
binary/
|
|
||||||
build*/
|
|
||||||
CPPLINT.cfg
|
|
||||||
Debug/
|
|
||||||
examples/
|
|
||||||
package-lock.json
|
|
||||||
test/
|
|
||||||
qt/
|
|
|
@ -1,88 +0,0 @@
|
||||||
{
|
|
||||||
'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()")',
|
|
||||||
},
|
|
||||||
'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','/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/addon.o',
|
|
||||||
'<(module_root_dir)/build/Release/obj.target/addon.node',
|
|
||||||
'<(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/*.*'
|
|
||||||
] } ],
|
|
||||||
],
|
|
||||||
}],
|
|
||||||
},
|
|
||||||
]
|
|
||||||
}
|
|
|
@ -1,15 +0,0 @@
|
||||||
'use strict';
|
|
||||||
|
|
||||||
const util = require('util');
|
|
||||||
|
|
||||||
const core = require('./binary/addon');
|
|
||||||
|
|
||||||
|
|
||||||
const { Example } = core;
|
|
||||||
|
|
||||||
Example.prototype[util.inspect.custom] = function() {
|
|
||||||
return `Example { listeners: [${this.eventNames()}] }`;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
module.exports = core;
|
|
|
@ -1,26 +0,0 @@
|
||||||
#include <cstdlib>
|
|
||||||
|
|
||||||
#include <event-emitter.hpp>
|
|
||||||
|
|
||||||
#include "example.hpp"
|
|
||||||
|
|
||||||
using namespace v8;
|
|
||||||
using namespace node;
|
|
||||||
using namespace std;
|
|
||||||
|
|
||||||
|
|
||||||
extern "C" {
|
|
||||||
|
|
||||||
|
|
||||||
void init(V8_VAR_OBJ target) {
|
|
||||||
|
|
||||||
EventEmitter::init(target);
|
|
||||||
Example::init(target);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
NODE_MODULE(example, init);
|
|
||||||
|
|
||||||
|
|
||||||
} // extern "C"
|
|
|
@ -1,94 +0,0 @@
|
||||||
#include <cstdlib>
|
|
||||||
#include <iostream>
|
|
||||||
|
|
||||||
#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();
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// ------ 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);
|
|
||||||
|
|
||||||
// -------- 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();
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,41 +0,0 @@
|
||||||
#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();
|
|
||||||
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
static NAN_METHOD(newCtor);
|
|
||||||
|
|
||||||
static NAN_METHOD(destroy);
|
|
||||||
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
static V8_STORE_FT _protoExample;
|
|
||||||
static V8_STORE_FUNC _ctorExample;
|
|
||||||
|
|
||||||
bool _isDestroyed;
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
#endif // _EXAMPLE_HPP_
|
|
|
@ -1,60 +0,0 @@
|
||||||
'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', () => {});
|
|
||||||
|
|
||||||
module.exports = Example;
|
|
|
@ -1,9 +0,0 @@
|
||||||
{
|
|
||||||
"name": "example",
|
|
||||||
"version": "0.0.0",
|
|
||||||
"private": true,
|
|
||||||
"main": "index.js",
|
|
||||||
"dependencies": {
|
|
||||||
"addon-tools-raub": "file:../../"
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,9 +0,0 @@
|
||||||
node_modules
|
|
||||||
*.log
|
|
||||||
build*
|
|
||||||
.DS_Store
|
|
||||||
*.pro.user
|
|
||||||
*.exp
|
|
||||||
*.pdb
|
|
||||||
*.ilk
|
|
||||||
package-lock.json
|
|
|
@ -1,11 +0,0 @@
|
||||||
node_modules
|
|
||||||
*.log
|
|
||||||
build*
|
|
||||||
.DS_Store
|
|
||||||
*.pro.user
|
|
||||||
*.exp
|
|
||||||
*.pdb
|
|
||||||
*.ilk
|
|
||||||
.gitignore
|
|
||||||
package-lock.json
|
|
||||||
test
|
|
|
@ -1,21 +0,0 @@
|
||||||
{
|
|
||||||
'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)'],
|
|
||||||
}
|
|
||||||
]}]],
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
|
@ -1,3 +0,0 @@
|
||||||
'use strict';
|
|
||||||
|
|
||||||
module.exports = require('addon-tools-raub').paths(__dirname);
|
|
|
@ -1,9 +0,0 @@
|
||||||
{
|
|
||||||
"name": "example",
|
|
||||||
"version": "0.0.0",
|
|
||||||
"private": true,
|
|
||||||
"main": "index.js",
|
|
||||||
"dependencies": {
|
|
||||||
"addon-tools-raub": "file:../../"
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,240 @@
|
||||||
|
# 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,310 +1,669 @@
|
||||||
#ifndef _ADDON_TOOLS_HPP_
|
#ifndef ADDON_TOOLS_HPP
|
||||||
#define _ADDON_TOOLS_HPP_
|
#define ADDON_TOOLS_HPP
|
||||||
|
|
||||||
|
#define NODE_ADDON_API_DISABLE_DEPRECATED
|
||||||
|
#define NAPI_DISABLE_CPP_EXCEPTIONS
|
||||||
|
#include <napi.h>
|
||||||
|
|
||||||
|
|
||||||
#include <nan.h>
|
#ifdef _WIN32
|
||||||
|
#define strcasestr(s, t) strstr(strupr(s), strupr(t))
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
#define NAN_HS Nan::HandleScope scope;
|
#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 RET_VALUE(VAL) info.GetReturnValue().Set(VAL);
|
#define JS_UNDEFINED env.Undefined()
|
||||||
#define RET_UNDEFINED RET_VALUE(Nan::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))
|
||||||
|
|
||||||
|
|
||||||
typedef v8::Local<v8::Value> V8_VAR_VAL;
|
#define JS_THROW(VAL) \
|
||||||
typedef v8::Local<v8::Object> V8_VAR_OBJ;
|
Napi::Error::New(env, VAL).ThrowAsJavaScriptException();
|
||||||
typedef v8::Local<v8::Array> V8_VAR_ARR;
|
|
||||||
typedef v8::Local<v8::String> V8_VAR_STR;
|
|
||||||
typedef v8::Local<v8::Function> V8_VAR_FUNC;
|
|
||||||
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 REQ_ARGS(N) \
|
#define REQ_ARGS(N) \
|
||||||
if (info.Length() < (N)) \
|
if (info.Length() < (N)) { \
|
||||||
return Nan::ThrowTypeError("Expected at least " #N " arguments");
|
JS_THROW("Expected at least " #N " arguments"); \
|
||||||
|
RET_UNDEFINED; \
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
#define IS_ARG_EMPTY(I) (info[I]->IsNull() || info[I]->IsUndefined())
|
#define IS_EMPTY(VAL) (VAL.IsNull() || VAL.IsUndefined())
|
||||||
|
#define IS_ARG_EMPTY(I) IS_EMPTY(info[I])
|
||||||
|
|
||||||
|
|
||||||
#define CHECK_REQ_ARG(I, C, T) \
|
#define CHECK_REQ_ARG(I, C, T) \
|
||||||
if (info.Length() <= (I) || ! info[I]->C) \
|
if (info.Length() <= (I) || !info[I].C) { \
|
||||||
return Nan::ThrowTypeError("Argument " #I " must be " T);
|
JS_THROW("Argument " #I " must be of type `" T "`"); \
|
||||||
|
RET_UNDEFINED; \
|
||||||
|
}
|
||||||
|
|
||||||
#define CHECK_LET_ARG(I, C, T) \
|
#define CHECK_LET_ARG(I, C, T) \
|
||||||
if ( ! (IS_ARG_EMPTY(I) || info[I]->C) ) \
|
if (!(IS_ARG_EMPTY(I) || info[I].C)) { \
|
||||||
return Nan::ThrowTypeError("Argument " #I " must be " T " or null");
|
JS_THROW( \
|
||||||
|
"Argument " #I \
|
||||||
|
" must be of type `" T \
|
||||||
|
"` or be `null`/`undefined`" \
|
||||||
|
); \
|
||||||
|
RET_UNDEFINED; \
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
#define REQ_UTF8_ARG(I, VAR) \
|
#define REQ_STR_ARG(I, VAR) \
|
||||||
CHECK_REQ_ARG(I, IsString(), "string"); \
|
CHECK_REQ_ARG(I, IsString(), "String"); \
|
||||||
Nan::Utf8String VAR(info[I]);
|
std::string VAR = info[I].ToString().Utf8Value();
|
||||||
|
|
||||||
#define LET_UTF8_ARG(I, VAR) \
|
#define USE_STR_ARG(I, VAR, DEF) \
|
||||||
CHECK_LET_ARG(I, IsString(), "string"); \
|
CHECK_LET_ARG(I, IsString(), "String"); \
|
||||||
Nan::Utf8String VAR(JS_STR(""));
|
std::string VAR = IS_ARG_EMPTY(I) ? (DEF) : info[I].ToString().Utf8Value();
|
||||||
|
|
||||||
|
#define LET_STR_ARG(I, VAR) USE_STR_ARG(I, VAR, "")
|
||||||
|
|
||||||
#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) \
|
#define REQ_INT32_ARG(I, VAR) \
|
||||||
CHECK_REQ_ARG(I, IsInt32(), "int32"); \
|
CHECK_REQ_ARG(I, IsNumber(), "Int32"); \
|
||||||
int VAR = info[I]->Int32Value();
|
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)
|
||||||
|
|
||||||
#define LET_INT32_ARG(I, VAR) \
|
|
||||||
CHECK_LET_ARG(I, IsInt32(), "int32"); \
|
|
||||||
int VAR = IS_ARG_EMPTY(I) ? 0 : info[I]->Int32Value();
|
|
||||||
|
|
||||||
#define REQ_UINT32_ARG(I, VAR) \
|
#define REQ_UINT32_ARG(I, VAR) \
|
||||||
CHECK_REQ_ARG(I, IsUint32(), "uint32"); \
|
CHECK_REQ_ARG(I, IsNumber(), "Uint32"); \
|
||||||
unsigned int VAR = info[I]->Uint32Value();
|
unsigned int VAR = info[I].ToNumber().Uint32Value();
|
||||||
|
|
||||||
#define LET_UINT32_ARG(I, VAR) \
|
#define USE_UINT32_ARG(I, VAR, DEF) \
|
||||||
CHECK_LET_ARG(I, IsUint32(), "uint32"); \
|
CHECK_LET_ARG(I, IsNumber(), "Uint32"); \
|
||||||
unsigned int VAR = IS_ARG_EMPTY(I) ? 0 : info[I]->Uint32Value();
|
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 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) \
|
#define REQ_BOOL_ARG(I, VAR) \
|
||||||
CHECK_REQ_ARG(I, IsBoolean(), "bool"); \
|
CHECK_REQ_ARG(I, IsBoolean(), "Bool"); \
|
||||||
bool VAR = info[I]->BooleanValue();
|
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;
|
||||||
|
|
||||||
#define LET_BOOL_ARG(I, VAR) \
|
|
||||||
CHECK_LET_ARG(I, IsBoolean(), "bool"); \
|
|
||||||
bool VAR = IS_ARG_EMPTY(I) ? false : info[I]->BooleanValue();
|
|
||||||
|
|
||||||
#define REQ_OFFS_ARG(I, VAR) \
|
#define REQ_OFFS_ARG(I, VAR) \
|
||||||
CHECK_REQ_ARG(I, IsNumber(), "number"); \
|
CHECK_REQ_ARG(I, IsNumber(), "Number"); \
|
||||||
size_t VAR = static_cast<size_t>(info[I]->IntegerValue());
|
size_t VAR = static_cast<size_t>(info[I].ToNumber().DoubleValue());
|
||||||
|
|
||||||
#define LET_OFFS_ARG(I, VAR) \
|
#define USE_OFFS_ARG(I, VAR, DEF) \
|
||||||
CHECK_LET_ARG(I, IsNumber(), "number"); \
|
CHECK_LET_ARG(I, IsNumber(), "Number"); \
|
||||||
size_t VAR = IS_ARG_EMPTY(I) ? 0 : static_cast<size_t>(info[I]->IntegerValue());
|
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 REQ_DOUBLE_ARG(I, VAR) \
|
#define REQ_DOUBLE_ARG(I, VAR) \
|
||||||
CHECK_REQ_ARG(I, IsNumber(), "number"); \
|
CHECK_REQ_ARG(I, IsNumber(), "Number"); \
|
||||||
double VAR = info[I]->NumberValue();
|
double VAR = info[I].ToNumber().DoubleValue();
|
||||||
|
|
||||||
#define LET_DOUBLE_ARG(I, VAR) \
|
#define USE_DOUBLE_ARG(I, VAR, DEF) \
|
||||||
CHECK_LET_ARG(I, IsNumber(), "number"); \
|
CHECK_LET_ARG(I, IsNumber(), "Number"); \
|
||||||
double VAR = IS_ARG_EMPTY(I) ? 0.0 : info[I]->NumberValue();
|
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 REQ_FLOAT_ARG(I, VAR) \
|
#define REQ_FLOAT_ARG(I, VAR) \
|
||||||
CHECK_REQ_ARG(I, IsNumber(), "number"); \
|
CHECK_REQ_ARG(I, IsNumber(), "Number"); \
|
||||||
float VAR = static_cast<float>(info[I]->NumberValue());
|
float VAR = info[I].ToNumber().FloatValue();
|
||||||
|
|
||||||
#define LET_FLOAT_ARG(I, VAR) \
|
#define USE_FLOAT_ARG(I, VAR, DEF) \
|
||||||
CHECK_LET_ARG(I, IsNumber(), "number"); \
|
CHECK_LET_ARG(I, IsNumber(), "Number"); \
|
||||||
float VAR = IS_ARG_EMPTY(I) ? 0.f : static_cast<float>(info[I]->NumberValue());
|
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 REQ_EXT_ARG(I, VAR) \
|
#define REQ_EXT_ARG(I, VAR) \
|
||||||
CHECK_REQ_ARG(I, IsExternal(), "void*"); \
|
CHECK_REQ_ARG(I, IsExternal(), "Pointer"); \
|
||||||
v8::Local<v8::External> VAR = v8::Local<v8::External>::Cast(info[I]);
|
void *VAR = info[I].As< Napi::External<void> >().Data();
|
||||||
|
|
||||||
#define LET_EXT_ARG(I, VAR) \
|
#define USE_EXT_ARG(I, VAR, DEF) \
|
||||||
CHECK_LET_ARG(I, IsExternal(), "number"); \
|
CHECK_LET_ARG(I, IsExternal(), "Pointer"); \
|
||||||
v8::Local<v8::External> VAR = IS_ARG_EMPTY(I) ? JS_EXT(NULL) : \
|
void *VAR = IS_ARG_EMPTY(I) ? (DEF) : info[I].As< Napi::External<void> >().Data();
|
||||||
v8::Local<v8::External>::Cast(info[I]);
|
|
||||||
|
#define LET_EXT_ARG(I, VAR) USE_EXT_ARG(I, VAR, nullptr)
|
||||||
|
|
||||||
|
|
||||||
#define REQ_FUN_ARG(I, VAR) \
|
#define REQ_FUN_ARG(I, VAR) \
|
||||||
CHECK_REQ_ARG(I, IsFunction(), "function"); \
|
CHECK_REQ_ARG(I, IsFunction(), "Function"); \
|
||||||
v8::Local<v8::Function> VAR = v8::Local<v8::Function>::Cast(info[I]);
|
Napi::Function VAR = info[I].As<Napi::Function>();
|
||||||
|
|
||||||
|
|
||||||
#define REQ_OBJ_ARG(I, VAR) \
|
#define REQ_OBJ_ARG(I, VAR) \
|
||||||
CHECK_REQ_ARG(I, IsObject(), "object"); \
|
CHECK_REQ_ARG(I, IsObject(), "Object"); \
|
||||||
v8::Local<v8::Object> VAR = v8::Local<v8::Object>::Cast(info[I]);
|
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))
|
||||||
|
|
||||||
|
|
||||||
#define REQ_ARRV_ARG(I, VAR) \
|
#define REQ_ARRV_ARG(I, VAR) \
|
||||||
REQ_OBJ_ARG(I, _obj_##VAR); \
|
CHECK_REQ_ARG(I, IsArrayBuffer(), "ArrayBuffer"); \
|
||||||
if( ! _obj_##VAR->IsArrayBufferView() ) \
|
Napi::ArrayBuffer VAR = info[I].As<Napi::ArrayBuffer>();
|
||||||
return Nan::ThrowTypeError("Argument " #I " must be an array buffer");\
|
|
||||||
v8::Local<v8::ArrayBufferView> VAR = v8::Local<v8::ArrayBufferView>::Cast(_obj_##VAR);
|
|
||||||
|
|
||||||
|
|
||||||
#define SET_PROP(OBJ, KEY, VAL) OBJ->Set(JS_STR(KEY), VAL);
|
#define REQ_BUF_ARG(I, VAR) \
|
||||||
#define SET_I(ARR, I, VAL) ARR->Set(I, VAL);
|
CHECK_REQ_ARG(I, IsBuffer(), "Buffer"); \
|
||||||
|
Napi::Buffer<uint8_t> VAR = info[I].As< Napi::Buffer<uint8_t> >();
|
||||||
|
|
||||||
|
|
||||||
#define CTOR_CHECK(T) \
|
#define REQ_ARRAY_ARG(I, VAR) \
|
||||||
if ( ! info.IsConstructCall() ) \
|
CHECK_REQ_ARG(I, IsArray(), "Array"); \
|
||||||
return Nan::ThrowTypeError(T " must be called with the 'new' keyword.");
|
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 DES_CHECK \
|
#define DES_CHECK \
|
||||||
if (_isDestroyed) return;
|
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) \
|
#define SETTER_CHECK(C, T) \
|
||||||
if ( ! value->C ) \
|
if (!value.C) { \
|
||||||
return Nan::ThrowTypeError("Value must be " T);
|
JS_THROW("Value must be " T); \
|
||||||
|
RET_UNDEFINED; \
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
#define ACCESSOR_RW(OBJ, NAME) \
|
#define JS_METHOD(NAME) Napi::Value NAME(const Napi::CallbackInfo &info)
|
||||||
Nan::SetAccessor(OBJ, JS_STR(#NAME), NAME ## Getter, NAME ## Setter);
|
|
||||||
|
|
||||||
#define ACCESSOR_R(OBJ, NAME) \
|
#define ACCESSOR_RW(CLASS, NAME) \
|
||||||
Nan::SetAccessor(OBJ, JS_STR(#NAME), NAME ## Getter);
|
InstanceAccessor(#NAME, &CLASS::NAME ## Getter, &CLASS::NAME ## Setter)
|
||||||
|
|
||||||
|
#define ACCESSOR_R(CLASS, NAME) \
|
||||||
|
InstanceAccessor(#NAME, &CLASS::NAME ## Getter, nullptr)
|
||||||
|
|
||||||
#define SETTER_UTF8_ARG \
|
#define ACCESSOR_M(CLASS, NAME) \
|
||||||
SETTER_CHECK(IsString(), "string"); \
|
InstanceMethod(#NAME, &CLASS::NAME)
|
||||||
Nan::Utf8String v(value);
|
|
||||||
|
|
||||||
#define SETTER_STR_ARG SETTER_UTF8_ARG
|
#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_INT32_ARG \
|
#define SETTER_INT32_ARG \
|
||||||
SETTER_CHECK(IsInt32(), "int32"); \
|
SETTER_CHECK(IsNumber(), "Int32"); \
|
||||||
int v = value->Int32Value();
|
int v = value.ToNumber().Int32Value();
|
||||||
|
|
||||||
#define SETTER_INT_ARG SETTER_INT32_ARG
|
#define SETTER_INT_ARG SETTER_INT32_ARG
|
||||||
|
|
||||||
#define SETTER_BOOL_ARG \
|
#define SETTER_BOOL_ARG \
|
||||||
SETTER_CHECK(IsBoolean(), "bool"); \
|
SETTER_CHECK(IsBoolean(), "Bool"); \
|
||||||
bool v = value->BooleanValue();
|
bool v = value.ToBoolean().Value();
|
||||||
|
|
||||||
#define SETTER_UINT32_ARG \
|
#define SETTER_UINT32_ARG \
|
||||||
SETTER_CHECK(IsUint32(), "uint32"); \
|
SETTER_CHECK(IsNumber(), "Uint32"); \
|
||||||
unsigned int v = value->Uint32Value();
|
unsigned int v = value.ToNumber().Uint32Value();
|
||||||
|
|
||||||
|
#define SETTER_UINT_ARG SETTER_UINT32_ARG
|
||||||
|
|
||||||
#define SETTER_OFFS_ARG \
|
#define SETTER_OFFS_ARG \
|
||||||
SETTER_CHECK(IsNumber(), "number"); \
|
SETTER_CHECK(IsNumber(), "Number"); \
|
||||||
size_t v = static_cast<size_t>(value->IntegerValue());
|
size_t v = static_cast<size_t>(value.ToNumber().DoubleValue());
|
||||||
|
|
||||||
#define SETTER_DOUBLE_ARG \
|
#define SETTER_DOUBLE_ARG \
|
||||||
SETTER_CHECK(IsNumber(), "number"); \
|
SETTER_CHECK(IsNumber(), "Number"); \
|
||||||
double v = value->NumberValue();
|
double v = value.ToNumber().DoubleValue();
|
||||||
|
|
||||||
#define SETTER_FLOAT_ARG \
|
#define SETTER_FLOAT_ARG \
|
||||||
SETTER_CHECK(IsNumber(), "number"); \
|
SETTER_CHECK(IsNumber(), "Number"); \
|
||||||
float v = static_cast<float>(value->NumberValue());
|
float v = value.ToNumber().FloatValue();
|
||||||
|
|
||||||
#define SETTER_EXT_ARG \
|
#define SETTER_EXT_ARG \
|
||||||
SETTER_CHECK(IsExternal(), "void*"); \
|
SETTER_CHECK(IsExternal(), "Pointer"); \
|
||||||
v8::Local<v8::External> v = v8::Local<v8::External>::Cast(value);
|
Napi::External v = value.As<Napi::External>();
|
||||||
|
|
||||||
#define SETTER_FUN_ARG \
|
#define SETTER_FUN_ARG \
|
||||||
SETTER_CHECK(IsFunction(), "function"); \
|
SETTER_CHECK(IsFunction(), "Function"); \
|
||||||
v8::Local<v8::Function> v = v8::Local<v8::Function>::Cast(value);
|
Napi::Function v = value.As<Napi::Function>()
|
||||||
|
|
||||||
#define SETTER_OBJ_ARG \
|
#define SETTER_OBJ_ARG \
|
||||||
SETTER_CHECK(IsObject(), "object"); \
|
SETTER_CHECK(IsObject(), "Object"); \
|
||||||
v8::Local<v8::Object> v = v8::Local<v8::Object>::Cast(value);
|
Napi::Object v = value.As<Napi::Object>()
|
||||||
|
|
||||||
#define SETTER_ARRV_ARG \
|
#define SETTER_ARRV_ARG \
|
||||||
SETTER_CHECK(IsObject(), "object"); \
|
SETTER_CHECK(IsArrayBuffer(), "TypedArray"); \
|
||||||
v8::Local<v8::Object> _obj_v = v8::Local<v8::Object>::Cast(value); \
|
Napi::ArrayBuffer v = value.As<Napi::ArrayBuffer>();
|
||||||
if( ! _obj_v->IsArrayBufferView() ) \
|
|
||||||
return Nan::ThrowTypeError("The value must be an array buffer"); \
|
|
||||||
v8::Local<v8::ArrayBufferView> v = v8::Local<v8::ArrayBufferView>::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)
|
||||||
|
|
||||||
template<typename Type>
|
#define NAPI_CALL(the_call) \
|
||||||
inline Type* getArrayData(v8::Local<v8::Value> arg, int *num = NULL) {
|
do { \
|
||||||
|
if ((the_call) != napi_ok) { \
|
||||||
|
GET_AND_THROW_LAST_ERROR(); \
|
||||||
|
RET_UNDEFINED; \
|
||||||
|
} \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
Type *data = NULL;
|
#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;
|
||||||
|
|
||||||
if (num) {
|
if (num) {
|
||||||
*num = 0;
|
*num = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (arg->IsNull() || arg->IsUndefined()) {
|
if (!obj.IsBuffer()) {
|
||||||
return data;
|
JS_THROW("Argument must be of type `Buffer`.");
|
||||||
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (arg->IsArray()) {
|
Napi::Buffer<uint8_t> arr = obj.As< Napi::Buffer<uint8_t> >();
|
||||||
Nan::ThrowError("JS Array is not supported here.");
|
|
||||||
return data;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( ! arg->IsArrayBufferView() ) {
|
|
||||||
Nan::ThrowError("Argument must be a TypedArray.");
|
|
||||||
return data;
|
|
||||||
}
|
|
||||||
|
|
||||||
v8::Local<v8::ArrayBufferView> arr = v8::Local<v8::ArrayBufferView>::Cast(arg);
|
|
||||||
if (num) {
|
if (num) {
|
||||||
*num = arr->ByteLength() / sizeof(Type);
|
*num = arr.Length() / sizeof(Type);
|
||||||
}
|
}
|
||||||
data = reinterpret_cast<Type*>(arr->Buffer()->GetContents().Data());
|
out = arr.Data();
|
||||||
|
|
||||||
return data;
|
|
||||||
|
|
||||||
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
inline void *getImageData(v8::Local<v8::Value> arg) {
|
inline void *getData(Napi::Env env, Napi::Object obj) {
|
||||||
|
void *out = nullptr;
|
||||||
|
|
||||||
void *pixels = NULL;
|
if (obj.IsTypedArray() || obj.IsArrayBuffer()) {
|
||||||
|
out = getArrayData<uint8_t>(env, obj);
|
||||||
if (arg->IsNull() || arg->IsUndefined()) {
|
} else if (obj.IsBuffer()) {
|
||||||
return pixels;
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
v8::Local<v8::Object> obj = v8::Local<v8::Object>::Cast(arg);
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
if ( ! obj->IsObject() ) {
|
|
||||||
Nan::ThrowError("Bad Image argument");
|
inline Napi::Value consoleLog(
|
||||||
return pixels;
|
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 Napi::Value consoleLog(Napi::Env env, const std::string &message) {
|
||||||
|
Napi::Value arg = JS_STR(message);
|
||||||
|
consoleLog(env, 1, &arg);
|
||||||
|
RET_UNDEFINED;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (obj->IsArrayBufferView()) {
|
Napi::Env env = that.Env();
|
||||||
pixels = getArrayData<unsigned char>(obj, NULL);
|
|
||||||
} else if (obj->Has(JS_STR("data"))) {
|
Napi::String eventName = JS_STR(name);
|
||||||
pixels = node::Buffer::Data(Nan::Get(obj, JS_STR("data")).ToLocalChecked());
|
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 {
|
} else {
|
||||||
Nan::ThrowError("Bad Image argument");
|
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 \
|
||||||
|
) \
|
||||||
|
); \
|
||||||
}
|
}
|
||||||
|
|
||||||
return pixels;
|
|
||||||
|
|
||||||
}
|
#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);
|
||||||
|
|
||||||
inline void consoleLog(int argc, V8_VAR_VAL *argv) {
|
#define JS_DECLARE_GETTER(CLASS, NAME) JS_DECLARE_METHOD(CLASS, NAME##Getter)
|
||||||
|
|
||||||
V8_VAR_STR code = JS_STR("((...args) => console.log(...args))");
|
#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 \
|
||||||
|
);
|
||||||
|
|
||||||
V8_VAR_FUNC log = V8_VAR_FUNC::Cast(v8::Script::Compile(code)->Run());
|
#define JS_IMPLEMENT_METHOD(CLASS, NAME) \
|
||||||
Nan::Callback logCb(log);
|
JS_METHOD(CLASS::__i_##NAME)
|
||||||
Nan::AsyncResource async("consoleLog()");
|
|
||||||
logCb.Call(argc, argv, &async);
|
|
||||||
|
|
||||||
}
|
#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 \
|
||||||
|
)
|
||||||
|
|
||||||
inline void consoleLog(const std::string &message) {
|
#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)
|
||||||
|
|
||||||
V8_VAR_VAL arg = JS_STR(message);
|
#define IMPLEMENT_ES5_CLASS(CLASS) \
|
||||||
consoleLog(1, &arg);
|
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_
|
|
||||||
|
|
|
@ -0,0 +1,103 @@
|
||||||
|
# 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.
|
|
@ -1,682 +0,0 @@
|
||||||
#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, "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(argc, argv, &async);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// C++ side on() method
|
|
||||||
void on(const std::string &name, V8_VAR_VAL that, const std::string &method) {
|
|
||||||
|
|
||||||
V8_VAR_STR code = JS_STR(
|
|
||||||
"((emitter, name, that, method) => emitter.on(name, that[method]))"
|
|
||||||
);
|
|
||||||
|
|
||||||
V8_VAR_FUNC connector = V8_VAR_FUNC::Cast(v8::Script::Compile(code)->Run());
|
|
||||||
Nan::Callback connectorCb(connector);
|
|
||||||
|
|
||||||
V8_VAR_OBJ emitter = Nan::New<v8::Object>();
|
|
||||||
this->Wrap(emitter);
|
|
||||||
|
|
||||||
V8_VAR_VAL argv[] = { emitter, JS_STR(name.c_str()), that, JS_STR(method.c_str()) };
|
|
||||||
Nan::AsyncResource async("EventEmitter::cpp_on()");
|
|
||||||
connectorCb.Call(4, 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_VALUE(JS_BOOL(eventEmitter->_isDestroyed));
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
NAN_METHOD(destroy) { THIS_EVENT_EMITTER; EVENT_EMITTER_THIS_CHECK;
|
|
||||||
|
|
||||||
eventEmitter->_destroy();
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// 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_VALUE(JS_INT(static_cast<int>(list.size())));
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static NAN_METHOD(jsAddListener) { _wrapListener(info); }
|
|
||||||
|
|
||||||
|
|
||||||
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);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
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]);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
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_VALUE(JS_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_VALUE(JS_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_VAR_STR stack = V8_VAR_STR::Cast(
|
|
||||||
v8::Script::Compile(code)->Run()
|
|
||||||
);
|
|
||||||
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_VAR_FUNC decor = V8_VAR_FUNC::Cast(v8::Script::Compile(code)->Run());
|
|
||||||
Nan::Callback decorCb(decor);
|
|
||||||
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); }
|
|
||||||
|
|
||||||
static NAN_METHOD(jsPrependListener) { _wrapListener(info, true); }
|
|
||||||
|
|
||||||
static NAN_METHOD(jsPrependOnceListener) { _wrapOnceListener(info, true); }
|
|
||||||
|
|
||||||
|
|
||||||
static NAN_METHOD(jsRemoveAllListeners) { THIS_EVENT_EMITTER; EVENT_EMITTER_THIS_CHECK;
|
|
||||||
|
|
||||||
if (info.Length() > 0 && info[0]->IsString()) {
|
|
||||||
|
|
||||||
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;
|
|
||||||
|
|
||||||
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;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
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_
|
|
|
@ -0,0 +1,65 @@
|
||||||
|
'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,
|
||||||
|
};
|
|
@ -0,0 +1,41 @@
|
||||||
|
'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');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,81 @@
|
||||||
|
# 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)
|
|
@ -0,0 +1,361 @@
|
||||||
|
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>;
|
||||||
|
}
|
83
index.js
83
index.js
|
@ -1,84 +1,3 @@
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
const path = require('path');
|
module.exports = Object.assign({}, require('./include'), require('./utils'));
|
||||||
|
|
||||||
|
|
||||||
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' : return process.arch === 'x64' ? 'linux64' : 'linux32';
|
|
||||||
case 'darwin' : 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 mkdirPath = isWindows ? `${rootPath}/_mkdir.bat` : 'mkdir';
|
|
||||||
const rmPath = isWindows ? `${rootPath}/_rm.bat` : 'rm';
|
|
||||||
const cpPath = isWindows ? `${rootPath}/_cp.bat` : 'cp';
|
|
||||||
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
|
|
||||||
paths,
|
|
||||||
|
|
||||||
rootPath,
|
|
||||||
includePath,
|
|
||||||
mkdirPath,
|
|
||||||
rmPath,
|
|
||||||
cpPath,
|
|
||||||
|
|
||||||
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
57
package.json
57
package.json
|
@ -1,33 +1,60 @@
|
||||||
{
|
{
|
||||||
"author": "Luis Blanco <luisblanco1337@gmail.com>",
|
"author": "Luis Blanco <luisblanco1337@gmail.com>",
|
||||||
"name": "addon-tools-raub",
|
"name": "addon-tools-raub",
|
||||||
"version": "1.0.0",
|
"version": "7.4.0",
|
||||||
"description": "Helpers for Node.js addons and dependency packages",
|
"description": "Helpers for Node.js addons and dependency packages",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"support",
|
|
||||||
"headers",
|
"headers",
|
||||||
"include",
|
"include",
|
||||||
"eventemitter",
|
"events",
|
||||||
"utils"
|
"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": {
|
"engines": {
|
||||||
"node": ">=8.11.1",
|
"node": ">=18.16.0",
|
||||||
"npm": ">=5.6.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 .."
|
||||||
},
|
},
|
||||||
"maintainers": [
|
|
||||||
{
|
|
||||||
"name": "Luis Blanco",
|
|
||||||
"email": "luisblanco1337@gmail.com",
|
|
||||||
"skype": "rauber666"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/node-3d/addon-tools-raub.git"
|
"url": "https://github.com/node-3d/addon-tools-raub.git"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"peerDependencies": {
|
||||||
"nan": "~2.10.0"
|
"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"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,37 @@
|
||||||
|
{
|
||||||
|
'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'],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}],
|
||||||
|
],
|
||||||
|
}],
|
||||||
|
}
|
|
@ -0,0 +1,45 @@
|
||||||
|
'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']);
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,109 @@
|
||||||
|
'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([])));
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,100 @@
|
||||||
|
'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));
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,100 @@
|
||||||
|
'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);
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,109 @@
|
||||||
|
'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');
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,100 @@
|
||||||
|
'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);
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,100 @@
|
||||||
|
'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);
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,109 @@
|
||||||
|
'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([])));
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,100 @@
|
||||||
|
'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);
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,100 @@
|
||||||
|
'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');
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,100 @@
|
||||||
|
'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);
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,211 @@
|
||||||
|
'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);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,302 @@
|
||||||
|
#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)
|
|
@ -1,17 +0,0 @@
|
||||||
{
|
|
||||||
"name": "test",
|
|
||||||
"version": "0.0.0",
|
|
||||||
"private": true,
|
|
||||||
"main": "mocha",
|
|
||||||
"scripts": {
|
|
||||||
"test": "mocha",
|
|
||||||
"start": "mocha",
|
|
||||||
"preinstall": "cd .. && npm i"
|
|
||||||
},
|
|
||||||
"dependencies": {
|
|
||||||
"addon-tools-raub": "file:../",
|
|
||||||
"chai": "^4.1.2",
|
|
||||||
"mocha": "^5.0.0",
|
|
||||||
"sinon": "^4.2.2"
|
|
||||||
}
|
|
||||||
}
|
|
122
test/test.js
122
test/test.js
|
@ -1,122 +0,0 @@
|
||||||
'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', '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('#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,15 +1,18 @@
|
||||||
set noparent
|
set noparent
|
||||||
linelength=110
|
linelength=110
|
||||||
filter=-legal/copyright
|
|
||||||
filter=-build/include_order
|
|
||||||
filter=-build/header_guard
|
filter=-build/header_guard
|
||||||
filter=-build/namespaces
|
filter=-build/include
|
||||||
|
filter=-build/include_order
|
||||||
filter=-build/include_what_you_use
|
filter=-build/include_what_you_use
|
||||||
|
filter=-build/namespaces
|
||||||
|
filter=-legal/copyright
|
||||||
|
filter=-readability/todo
|
||||||
|
filter=-runtime/indentation_namespace
|
||||||
filter=-whitespace/blank_line
|
filter=-whitespace/blank_line
|
||||||
|
filter=-whitespace/braces
|
||||||
filter=-whitespace/comments
|
filter=-whitespace/comments
|
||||||
filter=-whitespace/tab
|
|
||||||
filter=-whitespace/end_of_line
|
filter=-whitespace/end_of_line
|
||||||
filter=-whitespace/indent
|
filter=-whitespace/indent
|
||||||
filter=-whitespace/operators
|
filter=-whitespace/operators
|
||||||
filter=-whitespace/parens
|
filter=-whitespace/parens
|
||||||
filter=-readability/todo
|
filter=-whitespace/tab
|
|
@ -0,0 +1,20 @@
|
||||||
|
'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 };
|
|
@ -0,0 +1,31 @@
|
||||||
|
'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 };
|
|
@ -0,0 +1,24 @@
|
||||||
|
'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 };
|
|
@ -0,0 +1,46 @@
|
||||||
|
'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 };
|
|
@ -0,0 +1,196 @@
|
||||||
|
'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,
|
||||||
|
};
|
|
@ -0,0 +1,13 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
|
||||||
|
module.exports = Object.assign(
|
||||||
|
{},
|
||||||
|
require('./action-pack'),
|
||||||
|
require('./cpbin'),
|
||||||
|
require('./cpcpplint'),
|
||||||
|
require('./download'),
|
||||||
|
require('./files'),
|
||||||
|
require('./install'),
|
||||||
|
require('./writable-buffer'),
|
||||||
|
);
|
|
@ -0,0 +1,72 @@
|
||||||
|
'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 };
|
|
@ -0,0 +1,22 @@
|
||||||
|
'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');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,56 @@
|
||||||
|
'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