Compare commits

..

No commits in common. "master" and "v5.3.0" have entirely different histories.

70 changed files with 6518 additions and 3816 deletions

116
.eslintrc Normal file
View File

@ -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]
}
}

View File

@ -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
.gitattributes vendored
View File

@ -1 +0,0 @@
test-addon/binding.gyp linguist-vendored

View File

@ -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

View File

@ -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

View File

@ -1,46 +1,43 @@
name: Publish to NPM name: Publish
defaults:
run:
shell: bash
on: on:
workflow_dispatch workflow_dispatch:
inputs:
name:
description: 'Release name'
required: true
default: 'Minor Update'
text:
description: 'Patch notes'
required: true
default: Fixed minor issues.
jobs: jobs:
Publish: publish:
if: contains('["raub"]', github.actor) if: contains('["raub"]', github.actor)
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v2
- name: Fetch Repository
uses: actions/checkout@v3
with:
persist-credentials: false
- name: Install Node.js - name: Install Node.js
uses: actions/setup-node@v3 uses: actions/setup-node@v1
with: with:
node-version: 18.16.0 node-version: 14.16
cache: 'npm' - name: Get Npm Version
- name: Get Package Version
id: package-version id: package-version
run: node -p "'version='+require('./package').version" >> $GITHUB_OUTPUT uses: martinbeentjes/npm-get-version-action@master
- name: Publish - name: Publish
run: | run: |
npm config set //registry.npmjs.org/:_authToken ${NPM_TOKEN} npm config set //registry.npmjs.org/:_authToken ${NPM_TOKEN}
npm publish --ignore-scripts npm publish --ignore-scripts
env: env:
NPM_TOKEN: ${{ secrets.NPM_TOKEN }} NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
- name: GitHub Release
- name: Create Release uses: actions/create-release@v1
id: create_release
uses: softprops/action-gh-release@v1
env: env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with: with:
tag_name: ${{ steps.package-version.outputs.version }} tag_name: v${{ steps.package-version.outputs.current-version}}
name: Release ${{ steps.package-version.outputs.version }} release_name: ${{ github.event.inputs.name }}
body: Published at ${{ github.sha }} body: ${{ github.event.inputs.text }}

View File

@ -1,7 +1,4 @@
name: Test name: Test
defaults:
run:
shell: bash
on: on:
push: push:
@ -12,32 +9,12 @@ on:
- master - master
jobs: jobs:
unit-tests: test:
name: Unit Tests runs-on: ubuntu-latest
strategy:
matrix:
os: [ubuntu-20.04, windows-2022, macos-11, [self-hosted, linux, ARM64]]
runs-on: ${{ matrix.os }}
steps: steps:
- uses: actions/checkout@v2
- name: Fetch Repository - uses: actions/setup-node@v1
uses: actions/checkout@v3
with: with:
persist-credentials: false node-version: 14.16.0
- run: npm run test-install
- name: Install Node.js - run: npm run test-ci
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

5
.gitignore vendored
View File

@ -4,6 +4,9 @@
.DS_Store .DS_Store
.project .project
.DS_Store .DS_Store
.vscode
.eslintrc
node_modules/ node_modules/
test-addon/build/ test/build/
doc/jest/
*.log *.log

20
.travis.yml Normal file
View File

@ -0,0 +1,20 @@
language: node_js
node_js:
- "12.13.0"
matrix:
include:
- name: "Linux"
os: linux
dist: xenial
sudo: false
- name: "MacOS"
os: osx
- name: "Windows"
os: windows
install:
- cd test
- npm ci

View File

@ -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
}

View File

@ -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

View File

@ -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

208
README.md
View File

@ -2,74 +2,202 @@
This is a part of [Node3D](https://github.com/node-3d) project. This is a part of [Node3D](https://github.com/node-3d) project.
[![NPM](https://badge.fury.io/js/addon-tools-raub.svg)](https://badge.fury.io/js/addon-tools-raub) [![NPM](https://nodei.co/npm/addon-tools-raub.png?compact=true)](https://www.npmjs.com/package/addon-tools-raub)
[![ESLint](https://github.com/node-3d/addon-tools-raub/actions/workflows/eslint.yml/badge.svg)](https://github.com/node-3d/addon-tools-raub/actions/workflows/eslint.yml) [![CodeFactor](https://www.codefactor.io/repository/github/node-3d/addon-tools-raub/badge)](https://www.codefactor.io/repository/github/node-3d/addon-tools-raub)
[![Test](https://github.com/node-3d/addon-tools-raub/actions/workflows/test.yml/badge.svg)](https://github.com/node-3d/addon-tools-raub/actions/workflows/test.yml)
[![Cpplint](https://github.com/node-3d/addon-tools-raub/actions/workflows/cpplint.yml/badge.svg)](https://github.com/node-3d/addon-tools-raub/actions/workflows/cpplint.yml)
``` ```
npm i -s addon-tools-raub npm i addon-tools-raub
``` ```
## include/addon-tools.hpp ## Synopsis
Macro shortcuts for C++ addons using **NAPI**. This module contains numerous helpers for Node.js **NAPI**
See [docs inside the folder](/include). addons and dependency packages. On this page, helper scripts
are described. For details on **addon-tools.hpp** and some
additional snippets follow the links below.
Example of an addon method definition: **Go to**:
``` * [include/addon-tools.hpp](doc/addon-tools.md)
// hpp:
#include <addon-tools.hpp> Macro shortcuts for C++ addons using **NAPI**.
DBG_EXPORT JS_METHOD(doSomething); * [Es5 Class Wrapping](doc/class-wrapping.md)
// cpp:
DBG_EXPORT JS_METHOD(doSomething) { NAPI_ENV; An alternative, lightweight native class-defining mechanism for addons.
LET_INT32_ARG(0, param0); * [Snippets](doc/snippets.md)
std::cout << "param0: " << param0 << std::endl;
RET_UNDEFINED; Some repetitive bits of code for addons.
}
```
## index.js ## index.js
JavaScript helpers for Node.js addon development. The short list of helpers: Main exports for cross-platform addon configuration.
``` ```
'getBin', 'getPlatform', 'getInclude', 'getPaths', export const paths: (dir: string) => Readonly<{
'install', 'cpbin', 'download', 'read', 'write', 'copy', 'exists', bin: string;
'mkdir', 'stat', 'isDir', 'isFile', 'dirUp', 'ensuredir', 'copysafe', include: string;
'readdir', 'subdirs', 'subfiles', 'traverse', 'copyall', }>;
'rmdir', 'rm', 'WritableBuffer', 'actionPack', export const bin: string;
export const platform: string;
export const include: string;
```
* `paths` - Returns a set of platform dependent paths depending on
input `dir`.
* `bin` - platform binary directory absolute path.
* `include` - include directory for this `dir`.
* `bin` - platform-dependent binary directory name.
* `platform` - platform name: `'windows', 'linux', 'osx'`.
* `include` - both `'addon-tools-raub'` and `'node-addon-api'` include paths.
Use with `node -p` through list context command expansion `<!@(...)`.
## download.js
Downloads a file into the memory, **HTTP** or **HTTPS**.
```
declare const download: (url: string) => Promise<Buffer>;
export default download;
```
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.
Example:
```
download(srcUrl).then(
data => useData(data),
err => emit('error', err)
);
// or
const data = await download(srcUrl);
useData(data);
``` ```
See the [TypeScript definitions](/index.d.ts) with comments. ## cpbin.js
Copies the addon binary from `src/build/Release` to the platform folder.
### Example for an ADDON's **index.js**:
``` ```
const { getBin } = require('addon-tools-raub'); declare const cpbin: (name: string) => Promise<void>;
const core = require(`./${getBin()}/ADDON`); // uses the platform-specific ADDON.node export default cpbin;
```
It is useful for development builds. Use it in your **src/package.json**:
```
"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.
## install.js
Downloads and unzips the platform specific binary for the calling package.
```
declare const install: (folder: string) => void;
export default install;
```
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/install');
const prefix = 'https://github.com/USER/ADDON-NAME/releases/download';
const tag = process.env.npm_package_config_install;
install(`${prefix}/${tag}`);
```
* `prefix` - the constant base part of the download url.
* `tag` - the version-dependent part of the url,
here `process.env.npm_package_config_install` is taken
([automatically](https://docs.npmjs.com/misc/config#per-package-config-settings))
from **package.json**:
```
"config": {
"install": "v2.0.0"
},
"scripts": {
"postinstall": "node install"
},
``` ```
### Example for **binding.gyp**: ## writable-buffer.js
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`.
``` ```
'include_dirs': [ import type { Writable } from 'stream';
'<!@(node -p "require(\'addon-tools-raub\').getInclude()")', export class WritableBuffer extends Writable {
], constructor();
get(): Buffer;
};
``` ```
> NOTE: the optional `node-addon-api` dependency is used by the `getInclude()` helper. If not found, Use `stream.get()` to obtain the data when writing was finished:
the **napi.h** include path won't be a part of the returned string. ```
const stream = new WritableBuffer();
// ...
sourceStream.pipe(stream);
sourceStream.on('end', () => useData(stream.get()));
```
### Example of `cpbin` in **package.json :: scripts**: ## utils.js
Async `fs` based helpers for common addon-related file operations.
``` ```
"build": "cd src && node-gyp rebuild -j max --silent && node -e \"require('addon-tools-raub').cpbin('segfault')\" && cd ..", import type { Stats } from 'fs';
"build-only": "cd src && node-gyp build -j max --silent && node -e \"require('addon-tools-raub').cpbin('segfault')\" && cd ..", export const read: (name: string) => Promise<string>;
export const write: (name: string, text: string) => Promise<void>;
export const copy: (src: string, dest: string) => Promise<void>;
export const exists: (name: string) => Promise<boolean>;
export const mkdir: (name: string) => Promise<void>;
export const stat: (name: string) => Promise<Stats>;
export const isDir: (name: string) => Promise<boolean>;
export const isFile: (name: string) => Promise<boolean>;
export const dirUp: (dir: string) => string;
export const ensuredir: (dir: string) => Promise<void>;
export const copysafe: (src: string, dest: string) => Promise<void>;
export const readdir: (src: string, dest: string) => Promise<ReadonlyArray<string>>;
export const subdirs: (name: string) => Promise<ReadonlyArray<string>>;
export const subfiles: (name: string) => Promise<ReadonlyArray<string>>;
export const traverse: (name: string, showDirs?: boolean) => Promise<ReadonlyArray<string>>;
export const copyall: (src: string, dest: string) => Promise<void>;
export const rmdir: (name: string) => Promise<void>;
export const rm: (name: string) => Promise<void>;
``` ```
* `read` - (async) Reads a whole file to string, NOT A Buffer.
* `write` - (async) Write a file.
* `copy` - (async) Copy a file.
* `exists` - (async) Check if a file/folder exists.
* `mkdir` - (async) Create an empty folder.
* `stat` - (async) Get status on a file.
* `isDir` - (async) Check if the path is a folder.
* `isFile` - (async) Check if the path is a file.
* `dirUp` - Cut the path one folder up.
* `ensuredir` - (async) Like `mkdir -p`, makes sure a directory exists.
* `copysafe` - (async) Copy a file, `dest` folder is created if needed.
* `readdir` - (async) Get file/folder names of the 1st level.
* `subdirs` - (async) Get folder paths (concatenated with input) of the 1st level.
* `subfiles` - (async) Get file paths (concatenated with input) of the 1st level.
* `traverse` - (async) Get all nested files recursively.
* `copyall` - (async) Copy a folder with all the contained files.
* `rmdir` - (async) Like `rm -rf`, removes everything recursively.
* `rm` - (async) Remove a file. Must be a file, not a folder. Just `fs.unlink`.

2
cpbin.d.ts vendored Normal file
View File

@ -0,0 +1,2 @@
declare const cpbin: (name: string) => Promise<void>;
export default cpbin;

View File

@ -1,19 +1,19 @@
'use strict'; 'use strict';
const { copy, exists, mkdir, rm } = require('./files'); const { copy, exists, mkdir, rm } = require('./utils');
const { getBin } = require('../include'); const { bin } = require('.');
const cpbin = async (name) => { module.exports = async name => {
const srcDir = process.cwd().replace(/\\/g, '/'); const srcDir = process.cwd().replace(/\\/g, '/');
if (!await exists(`${srcDir}/build/Release/${name}.node`) ) { if (!await exists(`${srcDir}/build/Release/${name}.node`) ) {
console.error(`Error. File "${srcDir}/build/Release/${name}.node" not found.`); console.error(`Error. File "${srcDir}/build/Release/${name}.node" not found.`);
} }
const binAbs = `${srcDir}/../${getBin()}`; const binAbs = `${srcDir}/../${bin}`;
if (!await exists(binAbs)) { if ( ! await exists(binAbs) ) {
await mkdir(binAbs); await mkdir(binAbs);
} }
@ -25,7 +25,5 @@ const cpbin = async (name) => {
await copy(`${srcDir}/build/Release/${name}.node`, destAbs); await copy(`${srcDir}/build/Release/${name}.node`, destAbs);
console.log(`The binary "${name}.node" was copied to "${getBin()}".`); console.log(`The binary "${name}.node" was copied to "${bin}".`);
}; };
module.exports = { cpbin };

View File

@ -1,9 +1,8 @@
# include/addon-tools.hpp # include/addon-tools.hpp
There is a C++ header file, `addon-tools.hpp`, shipped with this package. It There is a C++ header file, `addon-tools.hpp`, shipped with this package. It
introduces several useful macros and utilities. Also it includes **NAPI** introduces several useful macros and utilities. Also it includes **Napi**
implicitly, so you can replace: implicitly, so you can replace:
``` ```
#include <napi.h> #include <napi.h>
``` ```
@ -11,31 +10,18 @@ with
``` ```
#include <addon-tools.hpp> #include <addon-tools.hpp>
``` ```
In **GYP**, the include directory should be set for your addon. In **GYP**, the include directory should be set for your addon.
An actual path to the directory is exported from the module An actual path to the directory is exported from the module
and is accessible with: and is accessible like this:
``` ```
require('addon-tools-raub').getInclude() // a string require('addon-tools-raub').include // a string
``` ```
For more examples, see [code snippets here](snippets.md).
### Helpers in **addon-tools.hpp**:
### 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 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` have `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 in case a problem has occured. So most of these macros are only usable
within `Napi::Value`-returning functions. within `Napi::Value`-returning functions.
@ -44,10 +30,6 @@ within `Napi::Value`-returning functions.
#define NAPI_HS Napi::HandleScope scope(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> <details>
<summary><b>Return value</b></summary> <summary><b>Return value</b></summary>
@ -59,7 +41,6 @@ Other global helpers:
* `RET_NUM(VAL)` - return `Napi::Number`, expected `VAL` is of numeric type. * `RET_NUM(VAL)` - return `Napi::Number`, expected `VAL` is of numeric type.
* `RET_EXT(VAL)` - return `Napi::External`, expected `VAL` is a pointer. * `RET_EXT(VAL)` - return `Napi::External`, expected `VAL` is a pointer.
* `RET_BOOL(VAL)` - return `Napi::Boolean`, expected `VAL` is convertible to bool. * `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>
@ -168,7 +149,6 @@ That extrapolates well to all the helpers below:
| `REQ_ARRAY_ARG` | `object` | `Napi::Array` | - | | `REQ_ARRAY_ARG` | `object` | `Napi::Array` | - |
| `USE_ARRAY_ARG` | `object` | `Napi::Array` | - | | `USE_ARRAY_ARG` | `object` | `Napi::Array` | - |
| `LET_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_FUN_ARG` | `function` | `Napi::Function` | - |
| `REQ_ARRV_ARG` | `ArrayBuffer` | `Napi::ArrayBuffer` | - | | `REQ_ARRV_ARG` | `ArrayBuffer` | `Napi::ArrayBuffer` | - |
| `REQ_BUF_ARG` | `Buffer` | `Napi::Buffer<uint8_t>` | - | | `REQ_BUF_ARG` | `Buffer` | `Napi::Buffer<uint8_t>` | - |

View File

@ -31,6 +31,7 @@ public:
JS_DECLARE_METHOD(ClassName, ClassName, destroy); JS_DECLARE_METHOD(ClassName, ClassName, destroy);
bool _isDestroyed; bool _isDestroyed;
}; };
``` ```
@ -61,10 +62,13 @@ void ClassName::init(Napi::Env env, Napi::Object exports) {
} }
ClassName::ClassName(const Napi::CallbackInfo &info) { NAPI_ENV; ClassName::ClassName(const Napi::CallbackInfo &info) { NAPI_ENV;
super(info); super(info);
_isDestroyed = false; _isDestroyed = false;
// ... // ...
} }
ClassName::~ClassName() { ClassName::~ClassName() {

197
doc/snippets.md Normal file
View File

@ -0,0 +1,197 @@
# Snippets
## C++ Addon building
**NAPI** addons are built separately from the installation, so we shouldn't
put the file **binding.gyp** to the module root anymore. It is better to have a
separate folder with a separate **package.json**, **binding.gyp** and the sources.
A snippet for **src/package.json**:
```
{
"name": "build",
"version": "0.0.0",
"private": true,
"scripts": {
"build": "node-gyp rebuild && node -e \"require('addon-tools-raub/cpbin')('ADDON')\""
},
"dependencies": {
"addon-tools-raub": "5.0.0",
"DEPS": "1.0.0"
}
}
```
* `ADDON` - the name of this addon (and subsequently of its binary).
* `DEPS` - dependency package(s).
### 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 `/${platform}.zip`
```
"config" : {
"install" : "v1.0.0"
},
"scripts": {
"postinstall": "install",
},
```
Here `config.install` is the tag name to download the binaries from.
To use it, create the *install.js* file in your addon:
```
const install = require('addon-tools-raub/install');
const prefix = 'https://github.com/USER/ADDON/releases/download';
const tag = process.env.npm_package_config_install;
install(`${prefix}/${tag}`);
```
**Addon Tools** will unzip the downloaded file into the platform binary
directory. E.g. on Windows it will be **bin-windows**.
* For a dependency package:
Place the following piece of code in the `index.js` without changes. Method `paths()`
is described [here](../README.md).
```
module.exports = require('addon-tools-raub').paths(__dirname);
```
* For a compiled addon:
Require it in your **index.js** from the platform-specific directory.
```
const { bin } = require('addon-tools-raub');
const core = require(`./${bin}/ADDON`);
```
Publishing binaries is done by attaching a zipped platform folder to the Github
release. Zip file must NOT contain platform folder as a subfolder, but rather
contain the final binaries. The tag of the release should be the same as in
`npm_package_config_install` - that is the way installer will find it.
> 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 zipped set of files/folders.
### GYP Variables
```
'variables': {
'bin' : '<!(node -p "require(\'addon-tools-raub\').bin")',
'DEPS_include' : '<!(node -p "require(\'DEPS\').include")',
'DEPS_bin' : '<!(node -p "require(\'DEPS\').bin")',
},
```
* `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\').include")',
'<(DEPS_include)',
],
```
The former contains both the path to include **Addon Tools** and the one for
**Napi** (which is preinstalled with Addon Tools). The latter can be any other
dependency include path(s).
<details>
<summary><b>See a snipped for src/binding.gyp here</b></summary>
* Assume `DEPS` is the name of an Addon Tools compliant dependency module.
* Assume `ADDON` is the name of this addon's resulting binary.
* Assume C++ code goes to `cpp` subdirectory.
```
{
'variables': {
'bin' : '<!(node -p "require(\'addon-tools-raub\').bin")',
'DEPS_include' : '<!(node -p "require(\'DEPS\').include")',
'DEPS_bin' : '<!(node -p "require(\'DEPS\').bin")',
},
'targets': [
{
'target_name' : 'bullet',
'sources' : [
'cpp/addon.cpp',
],
'include_dirs' : [
'<!@(node -p "require(\'addon-tools-raub\').include")',
'<(DEPS_include)',
],
'library_dirs' : [ '<(DEPS_bin)' ],
'libraries' : [ '-lDEPS' ],
'cflags!': ['-fno-exceptions'],
'cflags_cc!': ['-fno-exceptions'],
'conditions': [
[
'OS=="linux"',
{
'libraries': [
"-Wl,-rpath,'$$ORIGIN'",
"-Wl,-rpath,'$$ORIGIN/../node_modules/DEPS/<(bin)'",
"-Wl,-rpath,'$$ORIGIN/../../DEPS/<(bin)'",
],
'defines': ['__linux__'],
}
],
[
'OS=="mac"',
{
'libraries': [
'-Wl,-rpath,@loader_path',
'-Wl,-rpath,@loader_path/../node_modules/DEPS/<(bin)',
'-Wl,-rpath,@loader_path/../../DEPS/<(bin)',
],
'defines': ['__APPLE__'],
}
],
[
'OS=="win"',
{
'defines' : [
'WIN32_LEAN_AND_MEAN',
'VC_EXTRALEAN',
'_WIN32',
],
'msvs_settings' : {
'VCCLCompilerTool' : {
'AdditionalOptions' : [
'/GL', '/GF', '/EHsc', '/GS', '/Gy', '/GR-',
]
},
'VCLinkerTool' : {
'AdditionalOptions' : ['/RELEASE','/OPT:REF','/OPT:ICF','/LTCG'],
},
},
},
],
],
},
]
}
```
</details>

2
download.d.ts vendored Normal file
View File

@ -0,0 +1,2 @@
declare const download: (url: string) => Promise<Buffer>;
export default download;

28
download.js Normal file
View File

@ -0,0 +1,28 @@
'use strict';
const https = require('https');
const http = require('http');
const WritableBuffer = require('./writable-buffer');
const protocols = { http, https };
module.exports = url => new Promise((res, rej) => {
url = url.toLowerCase();
const stream = new WritableBuffer();
const proto = protocols[url.match(/^https?/)[0]];
proto.get(url, response => {
response.pipe(stream);
response.on('end', () => res(stream.get()));
response.on('error', err => rej(err));
});
});

View File

@ -11,12 +11,6 @@
#endif #endif
#ifdef _WIN32
#define DBG_EXPORT __declspec(dllexport)
#else
#define DBG_EXPORT
#endif
#define NAPI_ENV Napi::Env env = info.Env(); #define NAPI_ENV Napi::Env env = info.Env();
#define NAPI_HS Napi::HandleScope scope(env); #define NAPI_HS Napi::HandleScope scope(env);
@ -55,13 +49,13 @@
#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 "`"); \ JS_THROW("Argument " #I " must be of type `" T "`"); \
RET_UNDEFINED; \ 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( \ JS_THROW( \
"Argument " #I \ "Argument " #I \
" must be of type `" T \ " must be of type `" T \
@ -191,7 +185,7 @@
#define REQ_ARRV_ARG(I, VAR) \ #define REQ_ARRV_ARG(I, VAR) \
CHECK_REQ_ARG(I, IsArrayBuffer(), "ArrayBuffer"); \ CHECK_REQ_ARG(I, IsArrayBuffer(), "ArrayBuffer"); \
Napi::ArrayBuffer VAR = info[I].As<Napi::ArrayBuffer>(); Napi::ArrayBuffer VAR = info[I].As<Napi::ArrayBuffer>();
@ -200,57 +194,17 @@
Napi::Buffer<uint8_t> VAR = info[I].As< Napi::Buffer<uint8_t> >(); Napi::Buffer<uint8_t> VAR = info[I].As< Napi::Buffer<uint8_t> >();
#define REQ_ARRAY_ARG(I, VAR) \ #define REQ_ARRAY_ARG(I, VAR) \
CHECK_REQ_ARG(I, IsArray(), "Array"); \ CHECK_REQ_ARG(I, IsArray(), "Array"); \
Napi::Array VAR = info[I].As<Napi::Array>(); Napi::Array VAR = info[I].As<Napi::Array>();
#define USE_ARRAY_ARG(I, VAR, DEF) \ #define USE_ARRAY_ARG(I, VAR, DEF) \
CHECK_LET_ARG(I, IsArray(), "Array"); \ CHECK_LET_ARG(I, IsArray(), "Array"); \
Napi::Array VAR = IS_ARG_EMPTY(I) ? (DEF) : info[I].As<Napi::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)) #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) \ #define REQ_TYPED_ARRAY_ARG(I, VAR) \
CHECK_REQ_ARG(I, IsTypedArray(), "TypedArray"); \ CHECK_REQ_ARG(I, IsTypedArray(), "TypedArray"); \
Napi::TypedArray VAR = info[I].As<Napi::TypedArray>(); Napi::TypedArray VAR = info[I].As<Napi::TypedArray>();
@ -270,7 +224,7 @@ inline Napi::Array vecStrToArray(Napi::Env env, const std::vector<std::string> &
CACHE = V; CACHE = V;
#define SETTER_CHECK(C, T) \ #define SETTER_CHECK(C, T) \
if (!value.C) { \ if ( ! value.C ) { \
JS_THROW("Value must be " T); \ JS_THROW("Value must be " T); \
RET_UNDEFINED; \ RET_UNDEFINED; \
} }
@ -376,6 +330,7 @@ inline Type* getArrayData(
Napi::Object obj, Napi::Object obj,
int *num = nullptr int *num = nullptr
) { ) {
Type *out = nullptr; Type *out = nullptr;
if (obj.IsTypedArray()) { if (obj.IsTypedArray()) {
@ -401,22 +356,23 @@ inline Type* getArrayData(
} }
return out; return out;
} }
template<typename Type = uint8_t> template<typename Type = uint8_t>
inline Type* getBufferData( inline Type* getBufferData(
Napi::Env env, Napi::Env env,
Napi::Object obj, Napi::Object obj,
int *num = nullptr int *num = nullptr
) { ) {
Type *out = nullptr; Type *out = nullptr;
if (num) { if (num) {
*num = 0; *num = 0;
} }
if (!obj.IsBuffer()) { if ( ! obj.IsBuffer() ) {
JS_THROW("Argument must be of type `Buffer`."); JS_THROW("Argument must be of type `Buffer`.");
return out; return out;
} }
@ -428,10 +384,12 @@ inline Type* getBufferData(
out = arr.Data(); out = arr.Data();
return out; return out;
} }
inline void *getData(Napi::Env env, Napi::Object obj) { inline void *getData(Napi::Env env, Napi::Object obj) {
void *out = nullptr; void *out = nullptr;
if (obj.IsTypedArray() || obj.IsArrayBuffer()) { if (obj.IsTypedArray() || obj.IsArrayBuffer()) {
@ -448,6 +406,7 @@ inline void *getData(Napi::Env env, Napi::Object obj) {
} }
return out; return out;
} }
@ -477,10 +436,10 @@ inline void eventEmit(
Napi::Object that, Napi::Object that,
const std::string &name, const std::string &name,
int argc = 0, int argc = 0,
const Napi::Value *argv = nullptr, const Napi::Value *argv = nullptr
napi_async_context context = nullptr
) { ) {
if (!that.Has("emit")) {
if ( ! that.Has("emit") ) {
return; return;
} }
@ -495,11 +454,36 @@ inline void eventEmit(
args.push_back(napi_value(argv[i])); args.push_back(napi_value(argv[i]));
} }
if (context) { thatEmit.Call(that, args);
thatEmit.MakeCallback(that, args, context);
} else { }
thatEmit.Call(that, args);
inline void eventEmitAsync(
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]));
}
thatEmit.MakeCallback(that, args, context);
} }

View File

@ -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,
};

View File

@ -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');
});
});
});

View File

@ -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)

368
index.d.ts vendored
View File

@ -1,361 +1,7 @@
declare module "addon-tools-raub" { export const paths: (dir: string) => Readonly<{
type Stats = import('node:fs').Stats; bin: string;
type Writable = import('node:stream').Writable; include: string;
type Readable = import('node:stream').Readable; }>;
type WritableOptions = import('node:stream').WritableOptions; export const bin: string;
export const platform: string;
/** export const include: string;
* 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>;
}

View File

@ -1,3 +1,46 @@
'use strict'; 'use strict';
module.exports = Object.assign({}, require('./include'), require('./utils')); const napi = require('node-addon-api');
const platformNames = {
win32 : 'windows',
linux : 'linux',
darwin : 'osx',
};
const platformName = platformNames[process.platform];
const isWindows = process.platform === 'win32';
if ( ! platformName ) {
console.log(`Error: UNKNOWN PLATFORM "${process.platform}"`);
}
const rootPath = __dirname.replace(/\\/g, '/');
const napiInclude = napi.include.replace(/\\/g, '/');
const thisInclude = `${rootPath}/include`;
const includePath = `${napiInclude} ${thisInclude}`;
const paths = 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 };
};
module.exports = {
paths,
bin : `bin-${platformName}`,
platform : platformName,
include : includePath,
};

2
install.d.ts vendored Normal file
View File

@ -0,0 +1,2 @@
declare const install: (folder: string) => void;
export default install;

74
install.js Normal file
View File

@ -0,0 +1,74 @@
'use strict';
const https = require('https');
const http = require('http');
const fs = require('fs');
const AdmZip = require('adm-zip');
const { bin, platform } = require('.');
const { mkdir, rm } = require('./utils');
const protocols = { http, https };
const onError = msg => {
console.error(msg);
process.exit(-1);
};
const zipPath = `${bin}/${bin}.zip`;
const install = 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 install(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}`);
}
await mkdir(bin);
await new Promise((res, rej) => {
const zipWriter = fs.createWriteStream(zipPath);
zipWriter.on('error', err => rej(err));
zipWriter.on('finish', () => res());
response.pipe(zipWriter);
});
const zip = new AdmZip(zipPath);
zip.extractAllTo(bin, true);
await rm(zipPath);
} catch (ex) {
onError(ex.message);
}
};
module.exports = folder => {
const url = `${folder}/${platform}.zip`;
install(url).then();
};

1311
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,7 +1,7 @@
{ {
"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": "5.3.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",
@ -19,42 +19,38 @@
], ],
"files": [ "files": [
"include", "include",
"utils.js", "cpbin.js",
"utils.d.ts", "cpbin.d.ts",
"download.js",
"download.d.ts",
"index.js", "index.js",
"index.d.ts", "index.d.ts",
"utils", "install.js",
"install.d.ts",
"utils.js",
"utils.d.ts",
"writable-buffer.js",
"writable-buffer.d.ts",
"LICENSE", "LICENSE",
"package.json", "package.json",
"README.md" "README.md"
], ],
"engines": { "engines": {
"node": ">=18.16.0", "node": ">=14.16.0",
"npm": ">=9.5.1" "npm": ">=6.14.1"
}, },
"scripts": { "scripts": {
"eslint": "eslint .", "test": "cd test && npm run test && cd ..",
"test": "node --test --watch .", "test-ci": "cd test && npm run test-ci && cd ..",
"test-ci": "node --test", "test-install": "cd test && npm ci && cd ..",
"build-test": "cd test-addon && node-gyp rebuild -j max --silent && cd .." "test-coverage": "cd test && npm run coverage && cd .."
}, },
"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" "adm-zip": "0.5.4",
}, "node-addon-api": "3.1.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"
} }
} }

View File

@ -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'],
},
},
}],
],
}],
}

View File

@ -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']);
});
});

View File

@ -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([])));
});
});

View File

@ -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));
});
});

View File

@ -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);
});
});

View File

@ -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');
});
});

View File

@ -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);
});
});

View File

@ -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);
});
});

View File

@ -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([])));
});
});

View File

@ -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);
});
});

View File

@ -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');
});
});

View File

@ -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);
});
});

View File

@ -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);
});
});
});

0
test/.npmignore Normal file
View File

50
test/binding.gyp Normal file
View File

@ -0,0 +1,50 @@
{
'targets': [
{
'target_name': 'test',
'sources': [
'test.cpp',
],
'cflags!': ['-fno-exceptions'],
'cflags_cc!': ['-fno-exceptions'],
'include_dirs': [
'<!@(node -p "require(\'..\').include")',
],
'conditions': [
[
'OS=="linux"',
{
'defines': ['__linux__'],
}
],
[
'OS=="mac"',
{
'defines': ['__APPLE__'],
}
],
[
'OS=="win"',
{
'defines' : [
'WIN32_LEAN_AND_MEAN',
'VC_EXTRALEAN',
'_WIN32',
],
'msvs_settings' : {
'VCCLCompilerTool' : {
'AdditionalOptions' : [
'/O2','/Oy','/GL','/GF','/Gm-',
'/EHsc','/MT','/GS','/Gy','/GR-','/Gd',
]
},
'VCLinkerTool' : {
'AdditionalOptions' : ['/OPT:REF','/OPT:ICF','/LTCG']
},
},
},
],
],
},
],
}

105
test/hpp-arg-array.test.js Normal file
View File

@ -0,0 +1,105 @@
'use strict';
const test = require('./build/Release/test.node');
const arrayArgMsg = 'Argument 0 must be of type `Array`';
describe('addon-tools.hpp: REQ_ARRAY_ARG', () => {
it('exports reqArrayArg', () => {
expect(typeof test.reqArrayArg).toBe('function');
});
it('throws if arg was not passed', () => {
expect(() => test.reqArrayArg()).toThrow(arrayArgMsg);
});
it('throws if arg was passed undefined', () => {
expect(() => test.reqArrayArg(undefined)).toThrow(arrayArgMsg);
});
it('throws if arg was passed null', () => {
expect(() => test.reqArrayArg(null)).toThrow(arrayArgMsg);
});
it('throws if arg was passed a string', () => {
expect(() => test.reqArrayArg('1')).toThrow(arrayArgMsg);
});
it('throws if arg was passed a number', () => {
expect(() => test.reqArrayArg(1)).toThrow(arrayArgMsg);
});
it('throws if arg was passed a boolean', () => {
expect(() => test.reqArrayArg(true)).toThrow(arrayArgMsg);
});
it('throws if arg was passed a pointer', () => {
expect(() => test.reqArrayArg(test.retExt())).toThrow(arrayArgMsg);
});
it('throws if arg was passed an object', () => {
expect(() => test.reqArrayArg({})).toThrow(arrayArgMsg);
});
it('accepts an array', () => {
expect(Array.isArray(test.reqArrayArg([]))).toBe(true);
});
});
describe('addon-tools.hpp: LET_ARRAY_ARG', () => {
it('exports letArrayArg', () => {
expect(typeof test.letArrayArg).toBe('function');
});
it('throws if arg was passed a string', () => {
expect(() => test.letArrayArg('1')).toThrow(arrayArgMsg);
});
it('throws if arg was passed a number', () => {
expect(() => test.letArrayArg(1)).toThrow(arrayArgMsg);
});
it('throws if arg was passed a boolean', () => {
expect(() => test.letArrayArg(true)).toThrow(arrayArgMsg);
});
it('throws if arg was passed a pointer', () => {
expect(() => test.letArrayArg(test.retExt())).toThrow(arrayArgMsg);
});
it('throws if arg was passed an object', () => {
expect(() => test.letArrayArg({})).toThrow(arrayArgMsg);
});
it('accepts an empty arg', () => {
expect(Array.isArray(test.letArrayArg())).toBe(true);
});
it('accepts undefined', () => {
expect(Array.isArray(test.letArrayArg(undefined))).toBe(true);
});
it('accepts null', () => {
expect(Array.isArray(test.letArrayArg(null))).toBe(true);
});
it('accepts an array', () => {
expect(Array.isArray(test.letArrayArg([]))).toBe(true);
});
});
describe('addon-tools.hpp: USE_ARRAY_ARG', () => {
it('exports useArrayArg', () => {
expect(typeof test.useArrayArg).toBe('function');
});
it('throws if arg was passed a string', () => {
expect(() => test.useArrayArg('1')).toThrow(arrayArgMsg);
});
it('throws if arg was passed a number', () => {
expect(() => test.useArrayArg(1)).toThrow(arrayArgMsg);
});
it('throws if arg was passed a boolean', () => {
expect(() => test.useArrayArg(true)).toThrow(arrayArgMsg);
});
it('throws if arg was passed a pointer', () => {
expect(() => test.useArrayArg(test.retExt())).toThrow(arrayArgMsg);
});
it('throws if arg was passed an object', () => {
expect(() => test.useArrayArg({})).toThrow(arrayArgMsg);
});
it('accepts an empty arg', () => {
expect(Array.isArray(test.useArrayArg())).toBe(true);
});
it('accepts undefined', () => {
expect(Array.isArray(test.useArrayArg(undefined))).toBe(true);
});
it('accepts null', () => {
expect(Array.isArray(test.useArrayArg(null))).toBe(true);
});
it('accepts an array', () => {
expect(Array.isArray(test.useArrayArg([]))).toBe(true);
});
});

96
test/hpp-arg-bool.test.js Normal file
View File

@ -0,0 +1,96 @@
'use strict';
const test = require('./build/Release/test.node');
const boolArgMsg = 'Argument 0 must be of type `Bool`';
describe('addon-tools.hpp: REQ_BOOL_ARG', () => {
it('exports reqBoolArg', () => {
expect(typeof test.reqBoolArg).toBe('function');
});
it('throws if arg was not passed', () => {
expect(() => test.reqBoolArg()).toThrow(boolArgMsg);
});
it('throws if arg was passed undefined', () => {
expect(() => test.reqBoolArg(undefined)).toThrow(boolArgMsg);
});
it('throws if arg was passed null', () => {
expect(() => test.reqBoolArg(null)).toThrow(boolArgMsg);
});
it('throws if arg was passed a string', () => {
expect(() => test.reqBoolArg('1')).toThrow(boolArgMsg);
});
it('throws if arg was passed a number', () => {
expect(() => test.reqBoolArg(1)).toThrow(boolArgMsg);
});
it('throws if arg was passed an object', () => {
expect(() => test.reqBoolArg({})).toThrow(boolArgMsg);
});
it('throws if arg was passed an array', () => {
expect(() => test.reqBoolArg([])).toThrow(boolArgMsg);
});
it('accepts a boolean', () => {
expect(test.reqBoolArg(true)).toEqual(true);
});
});
describe('addon-tools.hpp: LET_BOOL_ARG', () => {
it('exports letBoolArg', () => {
expect(typeof test.letBoolArg).toBe('function');
});
it('throws if arg was passed a string', () => {
expect(() => test.letBoolArg('1')).toThrow(boolArgMsg);
});
it('throws if arg was passed a number', () => {
expect(() => test.letBoolArg(1)).toThrow(boolArgMsg);
});
it('throws if arg was passed an object', () => {
expect(() => test.letBoolArg({})).toThrow(boolArgMsg);
});
it('throws if arg was passed an array', () => {
expect(() => test.letBoolArg([])).toThrow(boolArgMsg);
});
it('accepts an empty arg', () => {
expect(test.letBoolArg()).toEqual(false);
});
it('accepts undefined', () => {
expect(test.letBoolArg(undefined)).toEqual(false);
});
it('accepts null', () => {
expect(test.letBoolArg(null)).toEqual(false);
});
it('accepts a boolean', () => {
expect(test.letBoolArg(true)).toEqual(true);
});
});
describe('addon-tools.hpp: USE_BOOL_ARG', () => {
it('exports useBoolArg', () => {
expect(typeof test.useBoolArg).toBe('function');
});
it('throws if arg was passed a string', () => {
expect(() => test.useBoolArg('1')).toThrow(boolArgMsg);
});
it('throws if arg was passed a number', () => {
expect(() => test.useBoolArg(1)).toThrow(boolArgMsg);
});
it('throws if arg was passed an object', () => {
expect(() => test.useBoolArg({})).toThrow(boolArgMsg);
});
it('throws if arg was passed an array', () => {
expect(() => test.useBoolArg([])).toThrow(boolArgMsg);
});
it('accepts an empty arg', () => {
expect(test.useBoolArg()).toEqual(true);
});
it('accepts undefined', () => {
expect(test.useBoolArg(undefined)).toEqual(true);
});
it('accepts null', () => {
expect(test.useBoolArg(null)).toEqual(true);
});
it('accepts a boolean', () => {
expect(test.useBoolArg(true)).toEqual(true);
});
});

View File

@ -0,0 +1,96 @@
'use strict';
const test = require('./build/Release/test.node');
const numArgMsg = 'Argument 0 must be of type `Number`';
describe('addon-tools.hpp: REQ_DOUBLE_ARG', () => {
it('exports reqDoubleArg', () => {
expect(typeof test.reqDoubleArg).toBe('function');
});
it('throws if arg was not passed', () => {
expect(() => test.reqDoubleArg()).toThrow(numArgMsg);
});
it('throws if arg was passed undefined', () => {
expect(() => test.reqDoubleArg(undefined)).toThrow(numArgMsg);
});
it('throws if arg was passed null', () => {
expect(() => test.reqDoubleArg(null)).toThrow(numArgMsg);
});
it('throws if arg was passed a string', () => {
expect(() => test.reqDoubleArg('1')).toThrow(numArgMsg);
});
it('throws if arg was passed a boolean', () => {
expect(() => test.reqDoubleArg(true)).toThrow(numArgMsg);
});
it('throws if arg was passed an object', () => {
expect(() => test.reqDoubleArg({})).toThrow(numArgMsg);
});
it('throws if arg was passed an array', () => {
expect(() => test.reqDoubleArg([])).toThrow(numArgMsg);
});
it('accepts a number', () => {
expect(test.reqDoubleArg(55)).toEqual(55);
});
});
describe('addon-tools.hpp: LET_DOUBLE_ARG', () => {
it('exports letDoubleArg', () => {
expect(typeof test.letDoubleArg).toBe('function');
});
it('throws if arg was passed a string', () => {
expect(() => test.letDoubleArg('1')).toThrow(numArgMsg);
});
it('throws if arg was passed a boolean', () => {
expect(() => test.letDoubleArg(true)).toThrow(numArgMsg);
});
it('throws if arg was passed an object', () => {
expect(() => test.letDoubleArg({})).toThrow(numArgMsg);
});
it('throws if arg was passed an array', () => {
expect(() => test.letDoubleArg([])).toThrow(numArgMsg);
});
it('accepts an empty arg', () => {
expect(test.letDoubleArg()).toEqual(0);
});
it('accepts undefined', () => {
expect(test.letDoubleArg(undefined)).toEqual(0);
});
it('accepts null', () => {
expect(test.letDoubleArg(null)).toEqual(0);
});
it('accepts a number', () => {
expect(test.letDoubleArg(55)).toEqual(55);
});
});
describe('addon-tools.hpp: USE_DOUBLE_ARG', () => {
it('exports useDoubleArg', () => {
expect(typeof test.useDoubleArg).toBe('function');
});
it('throws if arg was passed a string', () => {
expect(() => test.useDoubleArg('1')).toThrow(numArgMsg);
});
it('throws if arg was passed a boolean', () => {
expect(() => test.useDoubleArg(true)).toThrow(numArgMsg);
});
it('throws if arg was passed an object', () => {
expect(() => test.useDoubleArg({})).toThrow(numArgMsg);
});
it('throws if arg was passed an array', () => {
expect(() => test.useDoubleArg([])).toThrow(numArgMsg);
});
it('accepts an empty arg', () => {
expect(test.useDoubleArg()).toEqual(10);
});
it('accepts undefined', () => {
expect(test.useDoubleArg(undefined)).toEqual(10);
});
it('accepts null', () => {
expect(test.useDoubleArg(null)).toEqual(10);
});
it('accepts a number', () => {
expect(test.useDoubleArg(55)).toEqual(55);
});
});

105
test/hpp-arg-ext.test.js Normal file
View File

@ -0,0 +1,105 @@
'use strict';
const test = require('./build/Release/test.node');
const extArgMsg = 'Argument 0 must be of type `Pointer`';
describe('addon-tools.hpp: REQ_EXT_ARG', () => {
it('exports reqExtArg', () => {
expect(typeof test.reqExtArg).toBe('function');
});
it('throws if arg was not passed', () => {
expect(() => test.reqExtArg()).toThrow(extArgMsg);
});
it('throws if arg was passed undefined', () => {
expect(() => test.reqExtArg(undefined)).toThrow(extArgMsg);
});
it('throws if arg was passed null', () => {
expect(() => test.reqExtArg(null)).toThrow(extArgMsg);
});
it('throws if arg was passed a string', () => {
expect(() => test.reqExtArg('1')).toThrow(extArgMsg);
});
it('throws if arg was passed a number', () => {
expect(() => test.reqExtArg(1)).toThrow(extArgMsg);
});
it('throws if arg was passed a boolean', () => {
expect(() => test.reqExtArg(true)).toThrow(extArgMsg);
});
it('throws if arg was passed an object', () => {
expect(() => test.reqExtArg({})).toThrow(extArgMsg);
});
it('throws if arg was passed an array', () => {
expect(() => test.reqExtArg([])).toThrow(extArgMsg);
});
it('accepts a pointer', () => {
expect(typeof test.reqExtArg(test.retExt())).toBe('object');
});
});
describe('addon-tools.hpp: LET_EXT_ARG', () => {
it('exports letExtArg', () => {
expect(typeof test.letExtArg).toBe('function');
});
it('throws if arg was passed a string', () => {
expect(() => test.letExtArg('1')).toThrow(extArgMsg);
});
it('throws if arg was passed a number', () => {
expect(() => test.letExtArg(1)).toThrow(extArgMsg);
});
it('throws if arg was passed a boolean', () => {
expect(() => test.letExtArg(true)).toThrow(extArgMsg);
});
it('throws if arg was passed an object', () => {
expect(() => test.letExtArg({})).toThrow(extArgMsg);
});
it('throws if arg was passed an array', () => {
expect(() => test.letExtArg([])).toThrow(extArgMsg);
});
it('accepts an empty arg', () => {
expect(typeof test.letExtArg()).toBe('object');
});
it('accepts undefined', () => {
expect(typeof test.letExtArg(undefined)).toBe('object');
});
it('accepts null', () => {
expect(typeof test.letExtArg(null)).toBe('object');
});
it('accepts a pointer', () => {
expect(typeof test.reqExtArg(test.retExt())).toBe('object');
});
});
describe('addon-tools.hpp: USE_EXT_ARG', () => {
it('exports useExtArg', () => {
expect(typeof test.useExtArg).toBe('function');
});
it('throws if arg was passed a string', () => {
expect(() => test.useExtArg('1')).toThrow(extArgMsg);
});
it('throws if arg was passed a number', () => {
expect(() => test.useExtArg(1)).toThrow(extArgMsg);
});
it('throws if arg was passed a boolean', () => {
expect(() => test.useExtArg(true)).toThrow(extArgMsg);
});
it('throws if arg was passed an object', () => {
expect(() => test.useExtArg({})).toThrow(extArgMsg);
});
it('throws if arg was passed an array', () => {
expect(() => test.useExtArg([])).toThrow(extArgMsg);
});
it('accepts an empty arg', () => {
expect(typeof test.useExtArg()).toBe('object');
});
it('accepts undefined', () => {
expect(typeof test.useExtArg(undefined)).toBe('object');
});
it('accepts null', () => {
expect(typeof test.useExtArg(null)).toBe('object');
});
it('accepts a number', () => {
expect(typeof test.useExtArg(test.retExt())).toBe('object');
});
});

View File

@ -0,0 +1,96 @@
'use strict';
const test = require('./build/Release/test.node');
const numArgMsg = 'Argument 0 must be of type `Number`';
describe('addon-tools.hpp: REQ_FLOAT_ARG', () => {
it('exports reqFloatArg', () => {
expect(typeof test.reqFloatArg).toBe('function');
});
it('throws if arg was not passed', () => {
expect(() => test.reqFloatArg()).toThrow(numArgMsg);
});
it('throws if arg was passed undefined', () => {
expect(() => test.reqFloatArg(undefined)).toThrow(numArgMsg);
});
it('throws if arg was passed null', () => {
expect(() => test.reqFloatArg(null)).toThrow(numArgMsg);
});
it('throws if arg was passed a string', () => {
expect(() => test.reqFloatArg('1')).toThrow(numArgMsg);
});
it('throws if arg was passed a boolean', () => {
expect(() => test.reqFloatArg(true)).toThrow(numArgMsg);
});
it('throws if arg was passed an object', () => {
expect(() => test.reqFloatArg({})).toThrow(numArgMsg);
});
it('throws if arg was passed an array', () => {
expect(() => test.reqFloatArg([])).toThrow(numArgMsg);
});
it('accepts a number', () => {
expect(test.reqFloatArg(55)).toEqual(55);
});
});
describe('addon-tools.hpp: LET_FLOAT_ARG', () => {
it('exports letFloatArg', () => {
expect(typeof test.letFloatArg).toBe('function');
});
it('throws if arg was passed a string', () => {
expect(() => test.letFloatArg('1')).toThrow(numArgMsg);
});
it('throws if arg was passed a boolean', () => {
expect(() => test.letFloatArg(true)).toThrow(numArgMsg);
});
it('throws if arg was passed an object', () => {
expect(() => test.letFloatArg({})).toThrow(numArgMsg);
});
it('throws if arg was passed an array', () => {
expect(() => test.letFloatArg([])).toThrow(numArgMsg);
});
it('accepts an empty arg', () => {
expect(test.letFloatArg()).toEqual(0);
});
it('accepts undefined', () => {
expect(test.letFloatArg(undefined)).toEqual(0);
});
it('accepts null', () => {
expect(test.letFloatArg(null)).toEqual(0);
});
it('accepts a number', () => {
expect(test.letFloatArg(55)).toEqual(55);
});
});
describe('addon-tools.hpp: USE_FLOAT_ARG', () => {
it('exports useFloatArg', () => {
expect(typeof test.useFloatArg).toBe('function');
});
it('throws if arg was passed a string', () => {
expect(() => test.useFloatArg('1')).toThrow(numArgMsg);
});
it('throws if arg was passed a boolean', () => {
expect(() => test.useFloatArg(true)).toThrow(numArgMsg);
});
it('throws if arg was passed an object', () => {
expect(() => test.useFloatArg({})).toThrow(numArgMsg);
});
it('throws if arg was passed an array', () => {
expect(() => test.useFloatArg([])).toThrow(numArgMsg);
});
it('accepts an empty arg', () => {
expect(test.useFloatArg()).toEqual(10);
});
it('accepts undefined', () => {
expect(test.useFloatArg(undefined)).toEqual(10);
});
it('accepts null', () => {
expect(test.useFloatArg(null)).toEqual(10);
});
it('accepts a number', () => {
expect(test.useFloatArg(55)).toEqual(55);
});
});

96
test/hpp-arg-int.test.js Normal file
View File

@ -0,0 +1,96 @@
'use strict';
const test = require('./build/Release/test.node');
const intArgMsg = 'Argument 0 must be of type `Int32`';
describe('addon-tools.hpp: REQ_INT_ARG / REQ_INT32_ARG', () => {
it('exports reqIntArg', () => {
expect(typeof test.reqIntArg).toBe('function');
});
it('throws if arg was not passed', () => {
expect(() => test.reqIntArg()).toThrow(intArgMsg);
});
it('throws if arg was passed undefined', () => {
expect(() => test.reqIntArg(undefined)).toThrow(intArgMsg);
});
it('throws if arg was passed null', () => {
expect(() => test.reqIntArg(null)).toThrow(intArgMsg);
});
it('throws if arg was passed a string', () => {
expect(() => test.reqIntArg('1')).toThrow(intArgMsg);
});
it('throws if arg was passed a boolean', () => {
expect(() => test.reqIntArg(true)).toThrow(intArgMsg);
});
it('throws if arg was passed an object', () => {
expect(() => test.reqIntArg({})).toThrow(intArgMsg);
});
it('throws if arg was passed an array', () => {
expect(() => test.reqIntArg([])).toThrow(intArgMsg);
});
it('accepts a number', () => {
expect(test.reqIntArg(55)).toEqual(55);
});
});
describe('addon-tools.hpp: LET_INT_ARG / LET_INT32_ARG', () => {
it('exports letIntArg', () => {
expect(typeof test.letIntArg).toBe('function');
});
it('throws if arg was passed a string', () => {
expect(() => test.letIntArg('1')).toThrow(intArgMsg);
});
it('throws if arg was passed a boolean', () => {
expect(() => test.letIntArg(true)).toThrow(intArgMsg);
});
it('throws if arg was passed an object', () => {
expect(() => test.letIntArg({})).toThrow(intArgMsg);
});
it('throws if arg was passed an array', () => {
expect(() => test.letIntArg([])).toThrow(intArgMsg);
});
it('accepts an empty arg', () => {
expect(test.letIntArg()).toEqual(0);
});
it('accepts undefined', () => {
expect(test.letIntArg(undefined)).toEqual(0);
});
it('accepts null', () => {
expect(test.letIntArg(null)).toEqual(0);
});
it('accepts a number', () => {
expect(test.letIntArg(55)).toEqual(55);
});
});
describe('addon-tools.hpp: USE_INT_ARG / USE_INT32_ARG', () => {
it('exports useIntArg', () => {
expect(typeof test.useIntArg).toBe('function');
});
it('throws if arg was passed a string', () => {
expect(() => test.useIntArg('1')).toThrow(intArgMsg);
});
it('throws if arg was passed a boolean', () => {
expect(() => test.useIntArg(true)).toThrow(intArgMsg);
});
it('throws if arg was passed an object', () => {
expect(() => test.useIntArg({})).toThrow(intArgMsg);
});
it('throws if arg was passed an array', () => {
expect(() => test.useIntArg([])).toThrow(intArgMsg);
});
it('accepts an empty arg', () => {
expect(test.useIntArg()).toEqual(10);
});
it('accepts undefined', () => {
expect(test.useIntArg(undefined)).toEqual(10);
});
it('accepts null', () => {
expect(test.useIntArg(null)).toEqual(10);
});
it('accepts a number', () => {
expect(test.useIntArg(55)).toEqual(55);
});
});

105
test/hpp-arg-obj.test.js Normal file
View File

@ -0,0 +1,105 @@
'use strict';
const test = require('./build/Release/test.node');
const objArgMsg = 'Argument 0 must be of type `Object`';
describe('addon-tools.hpp: REQ_OBJ_ARG', () => {
it('exports reqObjArg', () => {
expect(typeof test.reqObjArg).toBe('function');
});
it('throws if arg was not passed', () => {
expect(() => test.reqObjArg()).toThrow(objArgMsg);
});
it('throws if arg was passed undefined', () => {
expect(() => test.reqObjArg(undefined)).toThrow(objArgMsg);
});
it('throws if arg was passed null', () => {
expect(() => test.reqObjArg(null)).toThrow(objArgMsg);
});
it('throws if arg was passed a string', () => {
expect(() => test.reqObjArg('1')).toThrow(objArgMsg);
});
it('throws if arg was passed a number', () => {
expect(() => test.reqObjArg(1)).toThrow(objArgMsg);
});
it('throws if arg was passed a boolean', () => {
expect(() => test.reqObjArg(true)).toThrow(objArgMsg);
});
it('throws if arg was passed a pointer', () => {
expect(() => test.reqObjArg(test.retExt())).toThrow(objArgMsg);
});
it('accepts an object', () => {
expect(typeof test.reqObjArg({})).toBe('object');
});
it('accepts an array', () => {
expect(Array.isArray(test.reqObjArg([]))).toBe(true);
});
});
describe('addon-tools.hpp: LET_OBJ_ARG', () => {
it('exports letObjArg', () => {
expect(typeof test.letObjArg).toBe('function');
});
it('throws if arg was passed a string', () => {
expect(() => test.letObjArg('1')).toThrow(objArgMsg);
});
it('throws if arg was passed a number', () => {
expect(() => test.letObjArg(1)).toThrow(objArgMsg);
});
it('throws if arg was passed a boolean', () => {
expect(() => test.letObjArg(true)).toThrow(objArgMsg);
});
it('throws if arg was passed a pointer', () => {
expect(() => test.letObjArg(test.retExt())).toThrow(objArgMsg);
});
it('accepts an empty arg', () => {
expect(typeof test.letObjArg()).toBe('object');
});
it('accepts undefined', () => {
expect(typeof test.letObjArg(undefined)).toBe('object');
});
it('accepts null', () => {
expect(typeof test.letObjArg(null)).toBe('object');
});
it('accepts an object', () => {
expect(typeof test.letObjArg({})).toBe('object');
});
it('accepts an array', () => {
expect(Array.isArray(test.letObjArg([]))).toBe(true);
});
});
describe('addon-tools.hpp: USE_OBJ_ARG', () => {
it('exports useObjArg', () => {
expect(typeof test.useObjArg).toBe('function');
});
it('throws if arg was passed a string', () => {
expect(() => test.useObjArg('1')).toThrow(objArgMsg);
});
it('throws if arg was passed a number', () => {
expect(() => test.useObjArg(1)).toThrow(objArgMsg);
});
it('throws if arg was passed a boolean', () => {
expect(() => test.useObjArg(true)).toThrow(objArgMsg);
});
it('throws if arg was passed a pointer', () => {
expect(() => test.useObjArg(test.retExt())).toThrow(objArgMsg);
});
it('accepts an empty arg', () => {
expect(typeof test.useObjArg()).toBe('object');
});
it('accepts undefined', () => {
expect(typeof test.useObjArg(undefined)).toBe('object');
});
it('accepts null', () => {
expect(typeof test.useObjArg(null)).toBe('object');
});
it('accepts an object', () => {
expect(typeof test.useObjArg({})).toBe('object');
});
it('accepts an array', () => {
expect(Array.isArray(test.useObjArg([]))).toBe(true);
});
});

96
test/hpp-arg-offs.test.js Normal file
View File

@ -0,0 +1,96 @@
'use strict';
const test = require('./build/Release/test.node');
const numArgMsg = 'Argument 0 must be of type `Number`';
describe('addon-tools.hpp: REQ_OFFS_ARG', () => {
it('exports reqOffsArg', () => {
expect(typeof test.reqOffsArg).toBe('function');
});
it('throws if arg was not passed', () => {
expect(() => test.reqOffsArg()).toThrow(numArgMsg);
});
it('throws if arg was passed undefined', () => {
expect(() => test.reqOffsArg(undefined)).toThrow(numArgMsg);
});
it('throws if arg was passed null', () => {
expect(() => test.reqOffsArg(null)).toThrow(numArgMsg);
});
it('throws if arg was passed a string', () => {
expect(() => test.reqOffsArg('1')).toThrow(numArgMsg);
});
it('throws if arg was passed a boolean', () => {
expect(() => test.reqOffsArg(true)).toThrow(numArgMsg);
});
it('throws if arg was passed an object', () => {
expect(() => test.reqOffsArg({})).toThrow(numArgMsg);
});
it('throws if arg was passed an array', () => {
expect(() => test.reqOffsArg([])).toThrow(numArgMsg);
});
it('accepts a number', () => {
expect(test.reqOffsArg(55)).toEqual(55);
});
});
describe('addon-tools.hpp: LET_OFFS_ARG', () => {
it('exports letOffsArg', () => {
expect(typeof test.letOffsArg).toBe('function');
});
it('throws if arg was passed a string', () => {
expect(() => test.letOffsArg('1')).toThrow(numArgMsg);
});
it('throws if arg was passed a boolean', () => {
expect(() => test.letOffsArg(true)).toThrow(numArgMsg);
});
it('throws if arg was passed an object', () => {
expect(() => test.letOffsArg({})).toThrow(numArgMsg);
});
it('throws if arg was passed an array', () => {
expect(() => test.letOffsArg([])).toThrow(numArgMsg);
});
it('accepts an empty arg', () => {
expect(test.letOffsArg()).toEqual(0);
});
it('accepts undefined', () => {
expect(test.letOffsArg(undefined)).toEqual(0);
});
it('accepts null', () => {
expect(test.letOffsArg(null)).toEqual(0);
});
it('accepts a number', () => {
expect(test.letOffsArg(55)).toEqual(55);
});
});
describe('addon-tools.hpp: USE_OFFS_ARG', () => {
it('exports useOffsArg', () => {
expect(typeof test.useOffsArg).toBe('function');
});
it('throws if arg was passed a string', () => {
expect(() => test.useOffsArg('1')).toThrow(numArgMsg);
});
it('throws if arg was passed a boolean', () => {
expect(() => test.useOffsArg(true)).toThrow(numArgMsg);
});
it('throws if arg was passed an object', () => {
expect(() => test.useOffsArg({})).toThrow(numArgMsg);
});
it('throws if arg was passed an array', () => {
expect(() => test.useOffsArg([])).toThrow(numArgMsg);
});
it('accepts an empty arg', () => {
expect(test.useOffsArg()).toEqual(10);
});
it('accepts undefined', () => {
expect(test.useOffsArg(undefined)).toEqual(10);
});
it('accepts null', () => {
expect(test.useOffsArg(null)).toEqual(10);
});
it('accepts a number', () => {
expect(test.useOffsArg(55)).toEqual(55);
});
});

96
test/hpp-arg-str.test.js Normal file
View File

@ -0,0 +1,96 @@
'use strict';
const test = require('./build/Release/test.node');
const strArgMsg = 'Argument 0 must be of type `String`';
describe('addon-tools.hpp: REQ_STR_ARG', () => {
it('exports reqStrArg', () => {
expect(typeof test.reqStrArg).toBe('function');
});
it('throws if arg was not passed', () => {
expect(() => test.reqStrArg()).toThrow(strArgMsg);
});
it('throws if arg was passed undefined', () => {
expect(() => test.reqStrArg(undefined)).toThrow(strArgMsg);
});
it('throws if arg was passed null', () => {
expect(() => test.reqStrArg(null)).toThrow(strArgMsg);
});
it('throws if arg was passed a number', () => {
expect(() => test.reqStrArg(1)).toThrow(strArgMsg);
});
it('throws if arg was passed a boolean', () => {
expect(() => test.reqStrArg(true)).toThrow(strArgMsg);
});
it('throws if arg was passed an object', () => {
expect(() => test.reqStrArg({})).toThrow(strArgMsg);
});
it('throws if arg was passed an array', () => {
expect(() => test.reqStrArg([])).toThrow(strArgMsg);
});
it('accepts a string', () => {
expect(test.reqStrArg('1abc')).toEqual('1abc');
});
});
describe('addon-tools.hpp: LET_STR_ARG', () => {
it('exports letStrArg', () => {
expect(typeof test.letStrArg).toBe('function');
});
it('throws if arg was passed a number', () => {
expect(() => test.letStrArg(1)).toThrow(strArgMsg);
});
it('throws if arg was passed a boolean', () => {
expect(() => test.letStrArg(true)).toThrow(strArgMsg);
});
it('throws if arg was passed an object', () => {
expect(() => test.letStrArg({})).toThrow(strArgMsg);
});
it('throws if arg was passed an array', () => {
expect(() => test.letStrArg([])).toThrow(strArgMsg);
});
it('accepts an empty arg', () => {
expect(test.letStrArg()).toEqual('');
});
it('accepts undefined', () => {
expect(test.letStrArg(undefined)).toEqual('');
});
it('accepts null', () => {
expect(test.letStrArg(null)).toEqual('');
});
it('accepts a string', () => {
expect(test.letStrArg('1abc')).toEqual('1abc');
});
});
describe('addon-tools.hpp: USE_STR_ARG', () => {
it('exports useStrArg', () => {
expect(typeof test.useStrArg).toBe('function');
});
it('throws if arg was passed a number', () => {
expect(() => test.useStrArg(1)).toThrow(strArgMsg);
});
it('throws if arg was passed a boolean', () => {
expect(() => test.useStrArg(true)).toThrow(strArgMsg);
});
it('throws if arg was passed an object', () => {
expect(() => test.useStrArg({})).toThrow(strArgMsg);
});
it('throws if arg was passed an array', () => {
expect(() => test.useStrArg([])).toThrow(strArgMsg);
});
it('accepts an empty arg', () => {
expect(test.useStrArg()).toEqual('default');
});
it('accepts undefined', () => {
expect(test.useStrArg(undefined)).toEqual('default');
});
it('accepts null', () => {
expect(test.useStrArg(null)).toEqual('default');
});
it('accepts a string', () => {
expect(test.useStrArg('1abc')).toEqual('1abc');
});
});

96
test/hpp-arg-uint.test.js Normal file
View File

@ -0,0 +1,96 @@
'use strict';
const test = require('./build/Release/test.node');
const uintArgMsg = 'Argument 0 must be of type `Uint32`';
describe('addon-tools.hpp: REQ_UINT_ARG / REQ_UINT32_ARG', () => {
it('exports reqUintArg', () => {
expect(typeof test.reqUintArg).toBe('function');
});
it('throws if arg was not passed', () => {
expect(() => test.reqUintArg()).toThrow(uintArgMsg);
});
it('throws if arg was passed undefined', () => {
expect(() => test.reqUintArg(undefined)).toThrow(uintArgMsg);
});
it('throws if arg was passed null', () => {
expect(() => test.reqUintArg(null)).toThrow(uintArgMsg);
});
it('throws if arg was passed a string', () => {
expect(() => test.reqUintArg('1')).toThrow(uintArgMsg);
});
it('throws if arg was passed a boolean', () => {
expect(() => test.reqUintArg(true)).toThrow(uintArgMsg);
});
it('throws if arg was passed an object', () => {
expect(() => test.reqUintArg({})).toThrow(uintArgMsg);
});
it('throws if arg was passed an array', () => {
expect(() => test.reqUintArg([])).toThrow(uintArgMsg);
});
it('accepts a number', () => {
expect(test.reqUintArg(55)).toEqual(55);
});
});
describe('addon-tools.hpp: LET_UINT_ARG / LET_UINT32_ARG', () => {
it('exports letUintArg', () => {
expect(typeof test.letUintArg).toBe('function');
});
it('throws if arg was passed a string', () => {
expect(() => test.letUintArg('1')).toThrow(uintArgMsg);
});
it('throws if arg was passed a boolean', () => {
expect(() => test.letUintArg(true)).toThrow(uintArgMsg);
});
it('throws if arg was passed an object', () => {
expect(() => test.letUintArg({})).toThrow(uintArgMsg);
});
it('throws if arg was passed an array', () => {
expect(() => test.letUintArg([])).toThrow(uintArgMsg);
});
it('accepts an empty arg', () => {
expect(test.letUintArg()).toEqual(0);
});
it('accepts undefined', () => {
expect(test.letUintArg(undefined)).toEqual(0);
});
it('accepts null', () => {
expect(test.letUintArg(null)).toEqual(0);
});
it('accepts a number', () => {
expect(test.letUintArg(55)).toEqual(55);
});
});
describe('addon-tools.hpp: USE_UINT_ARG / USE_UINT32_ARG', () => {
it('exports useUintArg', () => {
expect(typeof test.useUintArg).toBe('function');
});
it('throws if arg was passed a string', () => {
expect(() => test.useUintArg('1')).toThrow(uintArgMsg);
});
it('throws if arg was passed a boolean', () => {
expect(() => test.useUintArg(true)).toThrow(uintArgMsg);
});
it('throws if arg was passed an object', () => {
expect(() => test.useUintArg({})).toThrow(uintArgMsg);
});
it('throws if arg was passed an array', () => {
expect(() => test.useUintArg([])).toThrow(uintArgMsg);
});
it('accepts an empty arg', () => {
expect(test.useUintArg()).toEqual(10);
});
it('accepts undefined', () => {
expect(test.useUintArg(undefined)).toEqual(10);
});
it('accepts null', () => {
expect(test.useUintArg(null)).toEqual(10);
});
it('accepts a number', () => {
expect(test.useUintArg(55)).toEqual(55);
});
});

207
test/hpp-arg.test.js Normal file
View File

@ -0,0 +1,207 @@
'use strict';
const test = require('./build/Release/test.node');
describe('addon-tools.hpp: Function arguments', () => {
describe('REQ_ARGS', () => {
it('exports reqArgs3', () => {
expect(typeof test.reqArgs3).toBe('function');
});
it('throws if no args passed', () => {
expect(() => test.reqArgs3()).toThrow('Expected at least 3 arguments');
});
it('throws if 1 arg passed', () => {
expect(() => test.reqArgs3(1)).toThrow('Expected at least 3 arguments');
});
it('returns true if 3 args passed', () => {
expect(test.reqArgs3(1, 2, 3)).toEqual(true);
});
it('returns true if 5 args passed', () => {
expect(test.reqArgs3(1, 2, 3, 4, 5)).toEqual(true);
});
});
describe('IS_ARG_EMPTY', () => {
it('exports isArg0Empty', () => {
expect(typeof test.isArg0Empty).toBe('function');
});
it('returns true for absent arg', () => {
expect(test.isArg0Empty()).toEqual(true);
});
it('returns true for undefined arg', () => {
expect(test.isArg0Empty(undefined)).toEqual(true);
});
it('returns true for null arg', () => {
expect(test.isArg0Empty(null)).toEqual(true);
});
it('returns false for non-empty value', () => {
expect(test.isArg0Empty(1)).toEqual(false);
});
});
// ------------------------------ FUN_ARG
const funArgMsg = 'Argument 0 must be of type `Function`';
describe('REQ_FUN_ARG', () => {
it('exports reqFunArg', () => {
expect(typeof test.reqFunArg).toBe('function');
});
it('throws if arg was not passed', () => {
expect(() => test.reqFunArg()).toThrow(funArgMsg);
});
it('throws if arg was passed undefined', () => {
expect(() => test.reqFunArg(undefined)).toThrow(funArgMsg);
});
it('throws if arg was passed null', () => {
expect(() => test.reqFunArg(null)).toThrow(funArgMsg);
});
it('throws if arg was passed a string', () => {
expect(() => test.reqFunArg('1')).toThrow(funArgMsg);
});
it('throws if arg was passed a number', () => {
expect(() => test.reqFunArg(1)).toThrow(funArgMsg);
});
it('throws if arg was passed a boolean', () => {
expect(() => test.reqFunArg(true)).toThrow(funArgMsg);
});
it('throws if arg was passed a pointer', () => {
expect(() => test.reqFunArg(test.retExt())).toThrow(funArgMsg);
});
it('throws if arg was passed an object', () => {
expect(() => test.reqFunArg({})).toThrow(funArgMsg);
});
it('throws if arg was passed an array', () => {
expect(() => test.reqFunArg([])).toThrow(funArgMsg);
});
it('accepts a function', () => {
expect(typeof test.reqFunArg(() => {})).toBe('function');
});
});
// ------------------------------ ARRV_ARG
const arrvArgMsg = 'Argument 0 must be of type `ArrayBuffer`';
describe('REQ_ARRV_ARG', () => {
it('exports reqArrvArg', () => {
expect(typeof test.reqArrvArg).toBe('function');
});
it('throws if arg was not passed', () => {
expect(() => test.reqArrvArg()).toThrow(arrvArgMsg);
});
it('throws if arg was passed undefined', () => {
expect(() => test.reqArrvArg(undefined)).toThrow(arrvArgMsg);
});
it('throws if arg was passed null', () => {
expect(() => test.reqArrvArg(null)).toThrow(arrvArgMsg);
});
it('throws if arg was passed a string', () => {
expect(() => test.reqArrvArg('1')).toThrow(arrvArgMsg);
});
it('throws if arg was passed a number', () => {
expect(() => test.reqArrvArg(1)).toThrow(arrvArgMsg);
});
it('throws if arg was passed a boolean', () => {
expect(() => test.reqArrvArg(true)).toThrow(arrvArgMsg);
});
it('throws if arg was passed a pointer', () => {
expect(() => test.reqArrvArg(test.retExt())).toThrow(arrvArgMsg);
});
it('throws if arg was passed an object', () => {
expect(() => test.reqArrvArg({})).toThrow(arrvArgMsg);
});
it('throws if arg was passed an array', () => {
expect(() => test.reqArrvArg([])).toThrow(arrvArgMsg);
});
it('accepts an array buffer', () => {
const { buffer } = new Uint8Array([1, 2, 3]);
expect(test.reqArrvArg(buffer)).toEqual(buffer);
});
});
// ------------------------------ BUF_ARG
const bufArgMsg = 'Argument 0 must be of type `Buffer`';
describe('REQ_BUF_ARG', () => {
it('exports reqBufArg', () => {
expect(typeof test.reqBufArg).toBe('function');
});
it('throws if arg was not passed', () => {
expect(() => test.reqBufArg()).toThrow(bufArgMsg);
});
it('throws if arg was passed undefined', () => {
expect(() => test.reqBufArg(undefined)).toThrow(bufArgMsg);
});
it('throws if arg was passed null', () => {
expect(() => test.reqBufArg(null)).toThrow(bufArgMsg);
});
it('throws if arg was passed a string', () => {
expect(() => test.reqBufArg('1')).toThrow(bufArgMsg);
});
it('throws if arg was passed a number', () => {
expect(() => test.reqBufArg(1)).toThrow(bufArgMsg);
});
it('throws if arg was passed a boolean', () => {
expect(() => test.reqBufArg(true)).toThrow(bufArgMsg);
});
it('throws if arg was passed a pointer', () => {
expect(() => test.reqBufArg(test.retExt())).toThrow(bufArgMsg);
});
it('throws if arg was passed an object', () => {
expect(() => test.reqBufArg({})).toThrow(bufArgMsg);
});
it('throws if arg was passed an array', () => {
expect(() => test.reqBufArg([])).toThrow(bufArgMsg);
});
it('accepts a buffer', () => {
const buffer = Buffer.from([1, 2, 3]);
expect(test.reqBufArg(buffer)).toEqual(buffer);
});
});
// ------------------------------ TYPED_ARRAY_ARG
const typedArgMsg = 'Argument 0 must be of type `TypedArray`';
describe('REQ_TYPED_ARRAY_ARG', () => {
it('exports reqTypedArg', () => {
expect(typeof test.reqTypedArg).toBe('function');
});
it('throws if arg was not passed', () => {
expect(() => test.reqTypedArg()).toThrow(typedArgMsg);
});
it('throws if arg was passed undefined', () => {
expect(() => test.reqTypedArg(undefined)).toThrow(typedArgMsg);
});
it('throws if arg was passed null', () => {
expect(() => test.reqTypedArg(null)).toThrow(typedArgMsg);
});
it('throws if arg was passed a string', () => {
expect(() => test.reqTypedArg('1')).toThrow(typedArgMsg);
});
it('throws if arg was passed a number', () => {
expect(() => test.reqTypedArg(1)).toThrow(typedArgMsg);
});
it('throws if arg was passed a boolean', () => {
expect(() => test.reqTypedArg(true)).toThrow(typedArgMsg);
});
it('throws if arg was passed a pointer', () => {
expect(() => test.reqTypedArg(test.retExt())).toThrow(typedArgMsg);
});
it('throws if arg was passed an object', () => {
expect(() => test.reqTypedArg({})).toThrow(typedArgMsg);
});
it('throws if arg was passed an array', () => {
expect(() => test.reqTypedArg([])).toThrow(typedArgMsg);
});
it('accepts a typed array', () => {
const typed = new Uint8Array([1, 2, 3]);
expect(test.reqTypedArg(typed)).toEqual(typed);
});
});
});

33
test/index.test.js Normal file
View File

@ -0,0 +1,33 @@
'use strict';
const tools = require('..');
describe('index.js', () => {
describe(
'Properties',
() => ['bin', 'platform', 'include'].forEach(
m => it(`#${m} is a string`, () => {
expect(typeof tools[m]).toBe('string');
})
)
);
describe('#paths()', () => {
it('is a function', () => {
expect(typeof tools.paths).toBe('function');
});
it('returns an object', () => {
expect(typeof tools.paths(__dirname)).toBe('object');
});
it('has "include" string', () => {
expect(typeof tools.paths(__dirname).include).toBe('string');
});
it('has "bin" string', () => {
expect(typeof tools.paths(__dirname).include).toBe('string');
});
});
});

4287
test/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

31
test/package.json Normal file
View File

@ -0,0 +1,31 @@
{
"name": "test",
"version": "0.0.0",
"private": true,
"jest": {
"moduleFileExtensions": [
"js"
],
"testMatch": [
"./**/*.test.js"
],
"coverageDirectory": "../doc/jest",
"coverageReporters": [
"lcov"
],
"collectCoverageFrom": [
"../**/*.js",
"!../**/*.test.js"
]
},
"scripts": {
"test": "jest --coverage=false --watch",
"test-ci": "jest --coverage=false --verbose",
"coverage": "rm -rf ../doc/jest && jest --coverage --silent",
"build": "node-gyp rebuild",
"preinstall": "cd .. && npm ci"
},
"dependencies": {
"jest": "26.6.3"
}
}

View File

@ -3,12 +3,12 @@
JS_METHOD(empty) { NAPI_ENV; JS_METHOD(empty) { NAPI_ENV;
NAPI_HS; NAPI_HS;
RET_UNDEFINED; return env.Undefined();
} }
JS_METHOD(throwing) { NAPI_ENV; JS_METHOD(throwing) { NAPI_ENV;
JS_THROW("Some error"); JS_THROW("Some error");
RET_UNDEFINED; return env.Undefined();
} }
JS_METHOD(retUndefined) { NAPI_ENV; JS_METHOD(retUndefined) { NAPI_ENV;
@ -202,11 +202,6 @@ JS_METHOD(letArrayArg) { NAPI_ENV;
RET_VALUE(arg); RET_VALUE(arg);
} }
JS_METHOD(letArrayStrArg) { NAPI_ENV;
LET_ARRAY_STR_ARG(0, arg);
RET_ARRAY_STR(arg);
}
JS_METHOD(reqFunArg) { NAPI_ENV; JS_METHOD(reqFunArg) { NAPI_ENV;
REQ_FUN_ARG(0, arg); REQ_FUN_ARG(0, arg);
RET_VALUE(arg); RET_VALUE(arg);
@ -234,6 +229,7 @@ JS_METHOD(reqTypedArg) { NAPI_ENV;
); );
Napi::Object init(Napi::Env env, Napi::Object exports) { Napi::Object init(Napi::Env env, Napi::Object exports) {
TEST_SET_METHOD(empty); TEST_SET_METHOD(empty);
TEST_SET_METHOD(throwing); TEST_SET_METHOD(throwing);
@ -289,14 +285,13 @@ Napi::Object init(Napi::Env env, Napi::Object exports) {
TEST_SET_METHOD(useArrayArg); TEST_SET_METHOD(useArrayArg);
TEST_SET_METHOD(letArrayArg); TEST_SET_METHOD(letArrayArg);
TEST_SET_METHOD(letArrayStrArg);
TEST_SET_METHOD(reqFunArg); TEST_SET_METHOD(reqFunArg);
TEST_SET_METHOD(reqArrvArg); TEST_SET_METHOD(reqArrvArg);
TEST_SET_METHOD(reqBufArg); TEST_SET_METHOD(reqBufArg);
TEST_SET_METHOD(reqTypedArg); TEST_SET_METHOD(reqTypedArg);
return exports; return exports;
} }
NODE_API_MODULE(test, init) NODE_API_MODULE(test, init)

75
utils.d.ts vendored Normal file
View File

@ -0,0 +1,75 @@
import type { Stats } from 'fs';
// (async) Reads a whole file to string, NOT A Buffer
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) Like `mkdir -p`, makes sure a directory exists
export const ensuredir: (dir: string) => Promise<void>;
// (async) Copy a file, `dest` folder is created if needed
export const copysafe: (src: string, dest: string) => Promise<void>;
// (async) Get file/folder names of the 1st level
export const readdir: (src: string, dest: string) => Promise<ReadonlyArray<string>>;
// (async) Get folder paths (concatenated with input) of the 1st level
export const subdirs: (name: string) => Promise<ReadonlyArray<string>>;
// (async) 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 folder with all the contained files
export const copyall: (src: string, dest: string) => Promise<void>;
// (async) 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>;

View File

@ -1,10 +1,9 @@
'use strict'; 'use strict';
const fs = require('node:fs'); const fs = require('fs');
// (async) Reads a whole file to string, NOT A Buffer // (async) Reads a whole file to string, NOT A Buffer
const read = (name) => new Promise( const read = name => new Promise(
(res, rej) => fs.readFile( (res, rej) => fs.readFile(
name, name,
(err, data) => (err ? rej(err) : res(data.toString())) (err, data) => (err ? rej(err) : res(data.toString()))
@ -14,7 +13,7 @@ const read = (name) => new Promise(
// (async) Write a file // (async) Write a file
const write = (name, text) => new Promise( const write = (name, text) => new Promise(
(res, rej) => fs.writeFile(name, text, (err) => (err ? rej(err) : res())) (res, rej) => fs.writeFile(name, text, err => (err ? rej(err) : res()))
); );
@ -22,7 +21,7 @@ const write = (name, text) => new Promise(
const copy = async (src, dest) => { const copy = async (src, dest) => {
try { try {
await new Promise( await new Promise(
(res, rej) => fs.copyFile(src, dest, (err) => (err ? rej(err) : res())) (res, rej) => fs.copyFile(src, dest, err => (err ? rej(err) : res()))
); );
} catch (e) { } catch (e) {
if (e.code !== 'EBUSY') { if (e.code !== 'EBUSY') {
@ -33,46 +32,46 @@ const copy = async (src, dest) => {
// (async) Check if a file/folder exists // (async) Check if a file/folder exists
const exists = (name) => new Promise( const exists = name => new Promise(
(res) => fs.access( res => fs.access(
name, name,
fs.constants.F_OK, fs.constants.F_OK,
(err) => res(err ? false : true) err => res(err ? false : true)
) )
); );
// (async) Create an empty folder // (async) Create an empty folder
const mkdir = async (name) => { const mkdir = async name => {
if (await exists(name)) { if (await exists(name)) {
return; return;
} }
return new Promise( return new Promise(
(res, rej) => fs.mkdir(name, (err) => (err ? rej(err) : res())) (res, rej) => fs.mkdir(name, err => (err ? rej(err) : res()))
); );
}; };
// (async) Get status on a file // (async) Get status on a file
const stat = (name) => new Promise( const stat = name => new Promise(
(res, rej) => fs.stat(name, (err, stats) => (err ? rej(err) : res(stats))) (res, rej) => fs.stat(name, (err, stats) => (err ? rej(err) : res(stats)))
); );
// (async) Check if the path is a folder // (async) Check if the path is a folder
const isDir = async (name) => (await stat(name)).isDirectory(); const isDir = async name => (await stat(name)).isDirectory();
// (async) Check if the path is a file // (async) Check if the path is a file
const isFile = async (name) => (await stat(name)).isFile(); const isFile = async name => (await stat(name)).isFile();
// Cut the path one folder up // Cut the path one folder up
const dirUp = (dir) => dir.replace(/\\/g, '/').split('/').slice(0, -1).join('/'); const dirUp = dir => dir.replace(/\\/g, '/').split('/').slice(0, -1).join('/');
// (async) Like `mkdir -p`, makes sure a directory exists // (async) Like `mkdir -p`, makes sure a directory exists
const ensuredir = async (dir) => { const ensuredir = async dir => {
if (await exists(dir) && await isDir(dir)) { if (await exists(dir) && await isDir(dir)) {
return; return;
} }
@ -89,7 +88,7 @@ const copysafe = async (src, dest) => {
// (async) Get file/folder names of the 1st level // (async) Get file/folder names of the 1st level
const readdir = (name) => new Promise( const readdir = name => new Promise(
(res, rej) => fs.readdir( (res, rej) => fs.readdir(
name, name,
(err, dirents) => (err ? rej(err) : res(dirents)) (err, dirents) => (err ? rej(err) : res(dirents))
@ -98,18 +97,18 @@ const readdir = (name) => new Promise(
// (async) Get folder paths (concatenated with input) of the 1st level // (async) Get folder paths (concatenated with input) of the 1st level
const subdirs = async (name) => { const subdirs = async name => {
const all = await readdir(name); const all = await readdir(name);
const mapped = await Promise.all(all.map((d) => isDir(`${name}/${d}`))); const mapped = await Promise.all(all.map(d => isDir(`${name}/${d}`)));
return all.filter((_, i) => mapped[i]); return all.filter((_, i) => mapped[i]);
}; };
// (async) Get file paths (concatenated with input) of the 1st level // (async) Get file paths (concatenated with input) of the 1st level
const subfiles = async (name) => { const subfiles = async name => {
const all = await readdir(name); const all = await readdir(name);
const mapped = await Promise.all(all.map((d) => isFile(`${name}/${d}`))); const mapped = await Promise.all(all.map(d => isFile(`${name}/${d}`)));
return all.filter((_, i) => mapped[i]).map((f) => `${name}/${f}`); return all.filter((_, i) => mapped[i]).map(f => `${name}/${f}`);
}; };
@ -122,7 +121,7 @@ const traverse = async (name, showDirs = false) => {
while (stack.length) { while (stack.length) {
const dir = stack.pop(); const dir = stack.pop();
dirs.push(dir); dirs.push(dir);
(await subdirs(dir)).forEach((d) => stack.push(`${dir}/${d}`)); (await subdirs(dir)).forEach(d => stack.push(`${dir}/${d}`));
} }
return (showDirs ? dirs : []).concat( return (showDirs ? dirs : []).concat(
...(await Promise.all(dirs.map(subfiles))) ...(await Promise.all(dirs.map(subfiles)))
@ -146,8 +145,8 @@ const copyall = async (src, dest) => {
// (async) Like `rm -rf`, removes everything recursively // (async) Like `rm -rf`, removes everything recursively
const rmdir = async (name) => { const rmdir = async name => {
if (!await exists(name)) { if ( ! await exists(name) ) {
return; return;
} }
const paths = await traverse(name, true); const paths = await traverse(name, true);
@ -157,7 +156,7 @@ const rmdir = async (name) => {
await new Promise( await new Promise(
(res, rej) => fs[dir ? 'rmdir' : 'unlink']( (res, rej) => fs[dir ? 'rmdir' : 'unlink'](
target, target,
(err) => (err ? rej(err) : res()) err => (err ? rej(err) : res())
) )
); );
} }
@ -165,12 +164,12 @@ const rmdir = async (name) => {
// (async) Remove a file. Must be a file, not a folder. Just `fs.unlink`. // (async) Remove a file. Must be a file, not a folder. Just `fs.unlink`.
const rm = async (name) => { const rm = async name => {
if (!await exists(name)) { if ( ! await exists(name) ) {
return; return;
} }
await new Promise( await new Promise(
(res, rej) => fs.unlink(name, (err) => (err ? rej(err) : res())) (res, rej) => fs.unlink(name, err => (err ? rej(err) : res()))
); );
}; };

View File

@ -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 };

View File

@ -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 };

View File

@ -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 };

View File

@ -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'),
);

View File

@ -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 };

View File

@ -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');
});
});
});

6
writable-buffer.d.ts vendored Normal file
View File

@ -0,0 +1,6 @@
import type { Writable } from 'stream';
export class WritableBuffer extends Writable {
constructor();
get(): Buffer;
};

View File

@ -1,7 +1,6 @@
'use strict'; 'use strict';
const { Writable } = require('node:stream'); const { Writable } = require('stream');
const CHUNK_SIZE = 1024; const CHUNK_SIZE = 1024;
const INITIAL_SIZE = 8 * CHUNK_SIZE; const INITIAL_SIZE = 8 * CHUNK_SIZE;
@ -9,48 +8,58 @@ const INCREMENT_SIZE = 8 * CHUNK_SIZE;
class WritableBuffer extends Writable { class WritableBuffer extends Writable {
constructor() { constructor() {
super(); super();
this._buffer = Buffer.alloc(INITIAL_SIZE); this._buffer = new Buffer(INITIAL_SIZE);
this._size = 0; this._size = 0;
} }
get() { get() {
if (!this._size) {
if ( ! this._size ) {
return null; return null;
} }
const data = Buffer.alloc(this._size); const data = new Buffer(this._size);
this._buffer.copy(data, 0, 0, this._size); this._buffer.copy(data, 0, 0, this._size);
return data; return data;
} }
_increaseAsNeeded(incomingSize) { _increaseAsNeeded(incomingSize) {
if ((this._buffer.length - this._size) >= incomingSize) {
if ( (this._buffer.length - this._size) >= incomingSize ) {
return; return;
} }
const freeSpace = this._buffer.length - this._size; const freeSpace = this._buffer.length - this._size;
const factor = Math.ceil((incomingSize - freeSpace) / INCREMENT_SIZE); const factor = Math.ceil((incomingSize - freeSpace) / INCREMENT_SIZE);
const newBuffer = Buffer.alloc(this._buffer.length + (INCREMENT_SIZE * factor)); const newBuffer = new Buffer(this._buffer.length + (INCREMENT_SIZE * factor));
this._buffer.copy(newBuffer, 0, 0, this._size); this._buffer.copy(newBuffer, 0, 0, this._size);
this._buffer = newBuffer; this._buffer = newBuffer;
} }
_write(chunk, encoding, callback) { _write(chunk, encoding, callback) {
this._increaseAsNeeded(chunk.length); this._increaseAsNeeded(chunk.length);
chunk.copy(this._buffer, this._size, 0); chunk.copy(this._buffer, this._size, 0);
this._size += chunk.length; this._size += chunk.length;
callback(); callback();
} }
} }
module.exports = { WritableBuffer }; module.exports = WritableBuffer;