Compare commits
No commits in common. "master" and "4.0.0" have entirely different histories.
|
@ -0,0 +1,116 @@
|
||||||
|
{
|
||||||
|
"root": true,
|
||||||
|
"env": {
|
||||||
|
"node" : true,
|
||||||
|
"es6" : true,
|
||||||
|
"mocha" : true
|
||||||
|
},
|
||||||
|
"globals": {
|
||||||
|
"expect" : true,
|
||||||
|
"chai" : true,
|
||||||
|
"sinon" : true
|
||||||
|
},
|
||||||
|
"extends": ["eslint:recommended"],
|
||||||
|
"parserOptions": {
|
||||||
|
"ecmaVersion": 8,
|
||||||
|
"ecmaFeatures": {
|
||||||
|
"experimentalObjectRestSpread": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"rules": {
|
||||||
|
"arrow-parens": ["error", "as-needed"],
|
||||||
|
"no-trailing-spaces": [
|
||||||
|
"error",
|
||||||
|
{
|
||||||
|
"skipBlankLines": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"indent": [
|
||||||
|
"error",
|
||||||
|
"tab",
|
||||||
|
{
|
||||||
|
"SwitchCase": 1
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"linebreak-style": [
|
||||||
|
"error",
|
||||||
|
"unix"
|
||||||
|
],
|
||||||
|
"max-len": ["error", 110],
|
||||||
|
"quotes": [
|
||||||
|
"error",
|
||||||
|
"single"
|
||||||
|
],
|
||||||
|
"semi": [
|
||||||
|
"error",
|
||||||
|
"always"
|
||||||
|
],
|
||||||
|
"no-multiple-empty-lines": ["error", { "max": 3, "maxEOF": 1, "maxBOF": 1 }],
|
||||||
|
"keyword-spacing": ["error", { "before": true, "after": true }],
|
||||||
|
"space-before-blocks": ["error"],
|
||||||
|
"space-before-function-paren": ["error", {"anonymous": "always", "named": "never", "asyncArrow": "always"}],
|
||||||
|
"space-infix-ops": ["error"],
|
||||||
|
"space-unary-ops": [
|
||||||
|
"error", {
|
||||||
|
"words": true,
|
||||||
|
"nonwords": false,
|
||||||
|
"overrides": {
|
||||||
|
"!": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"spaced-comment": [0],
|
||||||
|
"camelcase": ["error"],
|
||||||
|
"no-tabs": [0],
|
||||||
|
"comma-dangle": [0],
|
||||||
|
"global-require": [0],
|
||||||
|
"func-names": [0],
|
||||||
|
"no-param-reassign": [0],
|
||||||
|
"no-underscore-dangle": [0],
|
||||||
|
"no-restricted-syntax": [
|
||||||
|
"error",
|
||||||
|
{
|
||||||
|
"selector": "LabeledStatement",
|
||||||
|
"message": "Labels are a form of GOTO; using them makes code confusing and hard to maintain and understand."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"selector": "WithStatement",
|
||||||
|
"message": "`with` is disallowed in strict mode because it makes code impossible to predict and optimize."
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"no-mixed-operators": [0],
|
||||||
|
"no-plusplus": [0],
|
||||||
|
"comma-spacing": [0],
|
||||||
|
"default-case": [0],
|
||||||
|
"no-shadow": [0],
|
||||||
|
"no-console": [0],
|
||||||
|
"key-spacing": [0],
|
||||||
|
"no-return-assign": [0],
|
||||||
|
"consistent-return": [0],
|
||||||
|
"class-methods-use-this": [0],
|
||||||
|
"no-multi-spaces": [
|
||||||
|
"error",
|
||||||
|
{
|
||||||
|
"exceptions": {
|
||||||
|
"VariableDeclarator": true,
|
||||||
|
"Property": true,
|
||||||
|
"ImportDeclaration": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"array-callback-return": [0],
|
||||||
|
"no-use-before-define": [
|
||||||
|
"error",
|
||||||
|
{
|
||||||
|
"functions": false,
|
||||||
|
"classes": true,
|
||||||
|
"variables": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"padded-blocks": [0],
|
||||||
|
"space-in-parens": [0],
|
||||||
|
"valid-jsdoc": [0],
|
||||||
|
"no-unused-expressions": [0],
|
||||||
|
"import/no-dynamic-require": [0]
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,75 +0,0 @@
|
||||||
{
|
|
||||||
"ignorePatterns": [
|
|
||||||
"src/**"
|
|
||||||
],
|
|
||||||
"extends": [
|
|
||||||
"eslint:recommended",
|
|
||||||
"plugin:node/recommended"
|
|
||||||
],
|
|
||||||
"parserOptions": {
|
|
||||||
"ecmaVersion": 2022
|
|
||||||
},
|
|
||||||
"env": {
|
|
||||||
"node": true,
|
|
||||||
"es6": true
|
|
||||||
},
|
|
||||||
"rules": {
|
|
||||||
"arrow-parens": ["error", "always"],
|
|
||||||
"no-trailing-spaces": [
|
|
||||||
"error",
|
|
||||||
{
|
|
||||||
"skipBlankLines": true
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"indent": [
|
|
||||||
"error",
|
|
||||||
"tab",
|
|
||||||
{
|
|
||||||
"SwitchCase": 1
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"operator-linebreak": [
|
|
||||||
"error",
|
|
||||||
"after",
|
|
||||||
{
|
|
||||||
"overrides": {
|
|
||||||
"?": "before",
|
|
||||||
":": "before"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"max-len": ["error", 110],
|
|
||||||
"quotes": [
|
|
||||||
"error",
|
|
||||||
"single"
|
|
||||||
],
|
|
||||||
"semi": [
|
|
||||||
"error",
|
|
||||||
"always"
|
|
||||||
],
|
|
||||||
"no-multiple-empty-lines": ["error", { "max": 3, "maxEOF": 1, "maxBOF": 1 }],
|
|
||||||
"keyword-spacing": ["error", { "before": true, "after": true }],
|
|
||||||
"space-before-blocks": ["error"],
|
|
||||||
"space-before-function-paren": ["error", {"anonymous": "always", "named": "never", "asyncArrow": "always"}],
|
|
||||||
"camelcase": ["error"],
|
|
||||||
"no-tabs": [0],
|
|
||||||
"global-require": [0],
|
|
||||||
"no-underscore-dangle": [0],
|
|
||||||
"no-plusplus": [0],
|
|
||||||
"no-shadow": [0],
|
|
||||||
"node/no-unpublished-require": [0],
|
|
||||||
"no-process-exit": [0],
|
|
||||||
"linebreak-style": [0],
|
|
||||||
"node/no-missing-require": [0],
|
|
||||||
"no-console": [0],
|
|
||||||
"node/no-unsupported-features/es-builtins": 0,
|
|
||||||
"node/no-unsupported-features/node-builtins": 0,
|
|
||||||
"func-names": [
|
|
||||||
"error",
|
|
||||||
"never",
|
|
||||||
{
|
|
||||||
"generators": "never"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1 +0,0 @@
|
||||||
test-addon/binding.gyp linguist-vendored
|
|
|
@ -1,47 +0,0 @@
|
||||||
name: Cpplint
|
|
||||||
defaults:
|
|
||||||
run:
|
|
||||||
shell: bash
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches:
|
|
||||||
- master
|
|
||||||
pull_request:
|
|
||||||
branches:
|
|
||||||
- master
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
eslint:
|
|
||||||
name: Cpplint
|
|
||||||
runs-on: ubuntu-20.04
|
|
||||||
|
|
||||||
steps:
|
|
||||||
|
|
||||||
- name: Fetch Repository
|
|
||||||
uses: actions/checkout@v3
|
|
||||||
with:
|
|
||||||
persist-credentials: false
|
|
||||||
|
|
||||||
- name: Install Node.js
|
|
||||||
uses: actions/setup-node@v3
|
|
||||||
with:
|
|
||||||
node-version: 18.16.0
|
|
||||||
cache: 'npm'
|
|
||||||
|
|
||||||
- name: Install Modules
|
|
||||||
run: npm ci
|
|
||||||
|
|
||||||
- name: Install Python
|
|
||||||
uses: actions/setup-python@v4
|
|
||||||
with:
|
|
||||||
python-version: '3.12'
|
|
||||||
|
|
||||||
- name: Install Cpplint
|
|
||||||
run: pip install cpplint
|
|
||||||
|
|
||||||
- name: Run Cpplint
|
|
||||||
run: |
|
|
||||||
node -e "require('.').cpcpplint()"
|
|
||||||
cpplint --recursive ./test-addon
|
|
||||||
cpplint --recursive ./include
|
|
|
@ -1,36 +0,0 @@
|
||||||
name: ESLint
|
|
||||||
defaults:
|
|
||||||
run:
|
|
||||||
shell: bash
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches:
|
|
||||||
- master
|
|
||||||
pull_request:
|
|
||||||
branches:
|
|
||||||
- master
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
eslint:
|
|
||||||
name: ESLint
|
|
||||||
runs-on: ubuntu-20.04
|
|
||||||
|
|
||||||
steps:
|
|
||||||
|
|
||||||
- name: Fetch Repository
|
|
||||||
uses: actions/checkout@v3
|
|
||||||
with:
|
|
||||||
persist-credentials: false
|
|
||||||
|
|
||||||
- name: Install Node.js
|
|
||||||
uses: actions/setup-node@v3
|
|
||||||
with:
|
|
||||||
node-version: 18.16.0
|
|
||||||
cache: 'npm'
|
|
||||||
|
|
||||||
- name: Install Modules
|
|
||||||
run: npm ci
|
|
||||||
|
|
||||||
- name: Run ESLint
|
|
||||||
run: npm run eslint
|
|
|
@ -1,46 +0,0 @@
|
||||||
name: Publish to NPM
|
|
||||||
defaults:
|
|
||||||
run:
|
|
||||||
shell: bash
|
|
||||||
|
|
||||||
on:
|
|
||||||
workflow_dispatch
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
Publish:
|
|
||||||
if: contains('["raub"]', github.actor)
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
|
|
||||||
steps:
|
|
||||||
|
|
||||||
- name: Fetch Repository
|
|
||||||
uses: actions/checkout@v3
|
|
||||||
with:
|
|
||||||
persist-credentials: false
|
|
||||||
|
|
||||||
- name: Install Node.js
|
|
||||||
uses: actions/setup-node@v3
|
|
||||||
with:
|
|
||||||
node-version: 18.16.0
|
|
||||||
cache: 'npm'
|
|
||||||
|
|
||||||
- name: Get Package Version
|
|
||||||
id: package-version
|
|
||||||
run: node -p "'version='+require('./package').version" >> $GITHUB_OUTPUT
|
|
||||||
|
|
||||||
- name: Publish
|
|
||||||
run: |
|
|
||||||
npm config set //registry.npmjs.org/:_authToken ${NPM_TOKEN}
|
|
||||||
npm publish --ignore-scripts
|
|
||||||
env:
|
|
||||||
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
|
|
||||||
|
|
||||||
- name: Create Release
|
|
||||||
id: create_release
|
|
||||||
uses: softprops/action-gh-release@v1
|
|
||||||
env:
|
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
with:
|
|
||||||
tag_name: ${{ steps.package-version.outputs.version }}
|
|
||||||
name: Release ${{ steps.package-version.outputs.version }}
|
|
||||||
body: Published at ${{ github.sha }}
|
|
|
@ -1,43 +0,0 @@
|
||||||
name: Test
|
|
||||||
defaults:
|
|
||||||
run:
|
|
||||||
shell: bash
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches:
|
|
||||||
- master
|
|
||||||
pull_request:
|
|
||||||
branches:
|
|
||||||
- master
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
unit-tests:
|
|
||||||
name: Unit Tests
|
|
||||||
strategy:
|
|
||||||
matrix:
|
|
||||||
os: [ubuntu-20.04, windows-2022, macos-11, [self-hosted, linux, ARM64]]
|
|
||||||
|
|
||||||
runs-on: ${{ matrix.os }}
|
|
||||||
|
|
||||||
steps:
|
|
||||||
|
|
||||||
- name: Fetch Repository
|
|
||||||
uses: actions/checkout@v3
|
|
||||||
with:
|
|
||||||
persist-credentials: false
|
|
||||||
|
|
||||||
- name: Install Node.js
|
|
||||||
uses: actions/setup-node@v3
|
|
||||||
with:
|
|
||||||
node-version: 18.16.0
|
|
||||||
cache: 'npm'
|
|
||||||
|
|
||||||
- name: Install Modules
|
|
||||||
run: npm ci
|
|
||||||
|
|
||||||
- name: Build Sample Addon
|
|
||||||
run: npm run build-test
|
|
||||||
|
|
||||||
- name: Run Unit Tests
|
|
||||||
run: npm run test-ci
|
|
|
@ -1,9 +1,11 @@
|
||||||
.cproject
|
|
||||||
.idea
|
.idea
|
||||||
.lock-wscript
|
.cproject
|
||||||
.DS_Store
|
|
||||||
.project
|
.project
|
||||||
|
.lock-wscript
|
||||||
|
build*/
|
||||||
.DS_Store
|
.DS_Store
|
||||||
|
Debug/
|
||||||
node_modules/
|
node_modules/
|
||||||
test-addon/build/
|
package-lock.json
|
||||||
|
binary/
|
||||||
*.log
|
*.log
|
||||||
|
|
|
@ -0,0 +1,15 @@
|
||||||
|
*.log
|
||||||
|
.cproject
|
||||||
|
.eslintrc
|
||||||
|
.gitignore
|
||||||
|
.idea
|
||||||
|
.lock-wscript
|
||||||
|
.project
|
||||||
|
binary/
|
||||||
|
build*/
|
||||||
|
CPPLINT.cfg
|
||||||
|
Debug/
|
||||||
|
examples/
|
||||||
|
package-lock.json
|
||||||
|
test/
|
||||||
|
qt/
|
|
@ -0,0 +1,40 @@
|
||||||
|
sudo: true
|
||||||
|
dist: trusty
|
||||||
|
|
||||||
|
language: node_js
|
||||||
|
|
||||||
|
node_js:
|
||||||
|
- "8.11.4"
|
||||||
|
|
||||||
|
|
||||||
|
addons:
|
||||||
|
apt:
|
||||||
|
sources:
|
||||||
|
- ubuntu-toolchain-r-test
|
||||||
|
- llvm-toolchain-precise-3.5
|
||||||
|
packages:
|
||||||
|
- clang-3.5
|
||||||
|
|
||||||
|
|
||||||
|
before_install:
|
||||||
|
- if [[ $(uname -s) == 'Linux' ]]; then
|
||||||
|
export CXX="clang++-3.5";
|
||||||
|
export CC="clang-3.5";
|
||||||
|
fi;
|
||||||
|
- npm install -g npm@6.4.0
|
||||||
|
|
||||||
|
|
||||||
|
install:
|
||||||
|
- cd examples/addon && npm i --clang=1
|
||||||
|
- cd ../deps && npm i --clang=1
|
||||||
|
- cd ../.. && npm i --clang=1
|
||||||
|
- cd test && npm i --clang=1
|
||||||
|
- cd ..
|
||||||
|
|
||||||
|
|
||||||
|
script:
|
||||||
|
- cd examples/addon && node .
|
||||||
|
- cd ../deps && node .
|
||||||
|
- cd ../.. && node .
|
||||||
|
- cd test && npm test
|
||||||
|
- cd ..
|
|
@ -1,17 +0,0 @@
|
||||||
{
|
|
||||||
"configurations": [
|
|
||||||
{
|
|
||||||
"name": "Win32",
|
|
||||||
"includePath": [
|
|
||||||
"${workspaceFolder}/**",
|
|
||||||
"${LocalAppData}/node-gyp/Cache/16.17.0/include/node",
|
|
||||||
"${LocalAppData}/node-gyp/Cache/18.16.0/include/node"
|
|
||||||
],
|
|
||||||
"windowsSdkVersion": "10.0.19041.0",
|
|
||||||
"cStandard": "c17",
|
|
||||||
"cppStandard": "c++17",
|
|
||||||
"intelliSenseMode": "windows-msvc-x64"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"version": 4
|
|
||||||
}
|
|
|
@ -1,18 +1,15 @@
|
||||||
set noparent
|
set noparent
|
||||||
linelength=110
|
linelength=110
|
||||||
filter=-build/header_guard
|
|
||||||
filter=-build/include
|
|
||||||
filter=-build/include_order
|
|
||||||
filter=-build/include_what_you_use
|
|
||||||
filter=-build/namespaces
|
|
||||||
filter=-legal/copyright
|
filter=-legal/copyright
|
||||||
filter=-readability/todo
|
filter=-build/include_order
|
||||||
filter=-runtime/indentation_namespace
|
filter=-build/header_guard
|
||||||
|
filter=-build/namespaces
|
||||||
|
filter=-build/include_what_you_use
|
||||||
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=-whitespace/tab
|
filter=-readability/todo
|
2
LICENSE
2
LICENSE
|
@ -1,6 +1,6 @@
|
||||||
MIT License
|
MIT License
|
||||||
|
|
||||||
Copyright (c) 2023 Luis Blanco
|
Copyright (c) 2018 Luis Blanco
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
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
|
||||||
|
|
800
README.md
800
README.md
|
@ -2,74 +2,800 @@
|
||||||
|
|
||||||
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)
|

|
||||||
[](https://github.com/node-3d/addon-tools-raub/actions/workflows/eslint.yml)
|
|
||||||
[](https://github.com/node-3d/addon-tools-raub/actions/workflows/test.yml)
|

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