Compare commits

..

48 Commits

Author SHA1 Message Date
Luis Blanco 1ca78906a9 Use cpcpplint util 2023-10-10 19:07:30 +04:00
Luis Blanco 9d68a62da4 Add cpplint badge 2023-10-09 22:46:58 +04:00
Luis Blanco 6c19ce79c3 Fix python version 2 2023-10-09 22:36:38 +04:00
Luis Blanco fc8b31fe36 Fix python version 2023-10-09 22:27:23 +04:00
Luis Blanco c82e029e90 Fix cpplint cmd 2023-10-09 22:24:11 +04:00
Luis Blanco b501c07de1 Fix GH action 2023-10-09 22:21:43 +04:00
Luis Blanco aeb2eae343 Adjust TS comments 2023-10-09 22:15:37 +04:00
Luis Blanco 747e1f6205 Fix linguist attributes 2023-10-07 19:08:39 +04:00
Luis Blanco c9c391ffd0 Use 18.16 and new badges 2023-10-07 19:06:03 +04:00
Luis Blanco d5ffbd6ba8 Fix action name 2023-10-07 18:34:43 +04:00
Luis Blanco 1c0b3afac5 Update deps and remove jest 2023-10-07 18:11:00 +04:00
Luis Blanco afd180d7cf Fix destructive lowercase 2023-09-26 21:32:57 +04:00
Luis Blanco aa063f9eae Update deps 2023-05-08 19:20:24 +04:00
Luis Blanco 105964614f Make install less strict 2023-01-06 20:00:38 +04:00
Luis Blanco 4b31874a56 Use gz 2023-01-04 19:23:43 +04:00
Luis Blanco 3f306a4938 Fix actionPack description 2023-01-04 17:52:04 +04:00
Luis Blanco f9f3ac2a23 Use GZIP 2023-01-04 17:10:00 +04:00
Luis Blanco 9852e1e789 Fix readme 2 2023-01-04 15:31:42 +04:00
Luis Blanco a56e079de0 Fix readme 2023-01-04 15:19:22 +04:00
Luis Blanco abc49171eb Fix publish workflow 2023-01-04 15:08:14 +04:00
Luis Blanco 6bdcf9abe2 Use object assign 2023-01-04 15:07:41 +04:00
Luis Blanco 0564af1e96 Fix version string 2023-01-04 14:50:40 +04:00
Luis Blanco 104fad35ae Remove actionVersion 2023-01-04 14:43:26 +04:00
Luis Blanco 9c76216823 Fix action script 2023-01-04 14:00:32 +04:00
Luis Blanco 955e8b61a8 Adjust actions 2023-01-04 13:36:32 +04:00
Luis Blanco 05e53641c1 Fix requires 2023-01-04 13:22:46 +04:00
Luis Blanco ac0c472be9 Adjust tests 2023-01-04 12:54:39 +04:00
Luis Blanco b05666869a Fix build 2023-01-03 23:45:14 +04:00
Luis Blanco 40e4baeb56 Fix exports 2023-01-03 23:14:04 +04:00
Luis Blanco 3d02cfb49c Add actionVersion test 2023-01-03 22:55:49 +04:00
Luis Blanco fd165d935d Update module structure 2023-01-03 22:20:39 +04:00
Luis Blanco b532ca47bf Update dependencies 2023-01-02 17:09:33 +04:00
Luis Blanco da96b71507 Fix windows compile flags 2022-12-24 21:59:11 +04:00
Luis Blanco 30f5fce2ec Fix platform names 2022-12-24 21:51:35 +04:00
Luis Blanco 6e66f46a4a Allow unknown platforms 2022-12-24 17:20:48 +04:00
Luis Blanco a274cd227e Add aarch64 2022-12-24 17:10:35 +04:00
Luis Blanco d4ed519e71 Remove eslint action matrix 2022-12-10 12:08:42 +04:00
Luis Blanco 3f52e63257 Adjust eslint action 2022-12-10 12:03:00 +04:00
Luis Blanco cd8527cdb1 Fix inline modifier 2022-12-10 11:57:17 +04:00
Luis Blanco 12fe6b4569 Split github action jobs 2022-12-05 23:02:43 +04:00
Luis Blanco 341b5e67a2 Update workflows 2022-12-05 22:54:03 +04:00
Luis Blanco 5d4708dfdf Update info 2022-12-05 22:44:35 +04:00
Luis Blanco eafb17d791 Fix test 2022-12-05 20:03:13 +04:00
Luis Blanco edc530bb5e Export missing test method 2022-12-05 19:55:29 +04:00
Luis Blanco 018b5aca47 Update package lock 2022-12-05 19:52:23 +04:00
Luis Blanco 68731102df Update header 2022-12-05 19:47:08 +04:00
Luis Blanco b77a3cac96 Fix napi error message 2022-11-28 20:24:17 +04:00
Luis Blanco a0a7f6153c Improve download script 2022-09-28 22:37:31 +04:00
65 changed files with 2581 additions and 9061 deletions

View File

@ -1,35 +1,20 @@
{ {
"ignorePatterns": [
"src/**"
],
"extends": [ "extends": [
"eslint:recommended", "eslint:recommended",
"plugin:node/recommended" "plugin:node/recommended"
], ],
"parserOptions": { "parserOptions": {
"ecmaVersion": 2020 "ecmaVersion": 2022
}, },
"overrides": [
{
"files": [
"**/*.test.js"
],
"env": {
"jest": true
},
"plugins": ["jest"],
"rules": {
"jest/no-disabled-tests": "warn",
"jest/no-focused-tests": "error",
"jest/no-identical-title": "error",
"jest/prefer-to-have-length": "warn",
"jest/valid-expect": "error"
}
}
],
"env": { "env": {
"node": true, "node": true,
"es6": true "es6": true
}, },
"rules": { "rules": {
"arrow-parens": ["error", "as-needed"], "arrow-parens": ["error", "always"],
"no-trailing-spaces": [ "no-trailing-spaces": [
"error", "error",
{ {
@ -43,6 +28,16 @@
"SwitchCase": 1 "SwitchCase": 1
} }
], ],
"operator-linebreak": [
"error",
"after",
{
"overrides": {
"?": "before",
":": "before"
}
}
],
"max-len": ["error", 110], "max-len": ["error", 110],
"quotes": [ "quotes": [
"error", "error",
@ -66,7 +61,15 @@
"no-process-exit": [0], "no-process-exit": [0],
"linebreak-style": [0], "linebreak-style": [0],
"node/no-missing-require": [0], "node/no-missing-require": [0],
"node/no-unsupported-features/node-builtins": [0], "no-console": [0],
"no-console": [0] "node/no-unsupported-features/es-builtins": 0,
"node/no-unsupported-features/node-builtins": 0,
"func-names": [
"error",
"never",
{
"generators": "never"
}
]
} }
} }

2
.gitattributes vendored
View File

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

47
.github/workflows/cpplint.yml vendored Normal file
View File

@ -0,0 +1,47 @@
name: Cpplint
defaults:
run:
shell: bash
on:
push:
branches:
- master
pull_request:
branches:
- master
jobs:
eslint:
name: Cpplint
runs-on: ubuntu-20.04
steps:
- name: Fetch Repository
uses: actions/checkout@v3
with:
persist-credentials: false
- name: Install Node.js
uses: actions/setup-node@v3
with:
node-version: 18.16.0
cache: 'npm'
- name: Install Modules
run: npm ci
- name: Install Python
uses: actions/setup-python@v4
with:
python-version: '3.12'
- name: Install Cpplint
run: pip install cpplint
- name: Run Cpplint
run: |
node -e "require('.').cpcpplint()"
cpplint --recursive ./test-addon
cpplint --recursive ./include

36
.github/workflows/eslint.yml vendored Normal file
View File

@ -0,0 +1,36 @@
name: ESLint
defaults:
run:
shell: bash
on:
push:
branches:
- master
pull_request:
branches:
- master
jobs:
eslint:
name: ESLint
runs-on: ubuntu-20.04
steps:
- name: Fetch Repository
uses: actions/checkout@v3
with:
persist-credentials: false
- name: Install Node.js
uses: actions/setup-node@v3
with:
node-version: 18.16.0
cache: 'npm'
- name: Install Modules
run: npm ci
- name: Run ESLint
run: npm run eslint

View File

@ -1,16 +1,10 @@
name: Publish name: Publish to NPM
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:
@ -20,16 +14,19 @@ jobs:
steps: steps:
- name: Fetch Repository - name: Fetch Repository
uses: actions/checkout@v2 uses: actions/checkout@v3
with:
persist-credentials: false
- name: Install Node.js - name: Install Node.js
uses: actions/setup-node@v1 uses: actions/setup-node@v3
with: with:
node-version: 16.17.0 node-version: 18.16.0
cache: 'npm'
- name: Get Npm Version - name: Get Package Version
id: package-version id: package-version
uses: martinbeentjes/npm-get-version-action@master run: node -p "'version='+require('./package').version" >> $GITHUB_OUTPUT
- name: Publish - name: Publish
run: | run: |
@ -38,11 +35,12 @@ jobs:
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.current-version }} tag_name: ${{ steps.package-version.outputs.version }}
release_name: ${{ github.event.inputs.name }} name: Release ${{ steps.package-version.outputs.version }}
body: ${{ github.event.inputs.text }} body: Published at ${{ github.sha }}

View File

@ -1,4 +1,7 @@
name: Validate name: Test
defaults:
run:
shell: bash
on: on:
push: push:
@ -9,11 +12,11 @@ on:
- master - master
jobs: jobs:
validate: unit-tests:
name: Validate name: Unit Tests
strategy: strategy:
matrix: matrix:
os: [ubuntu-20.04, windows-2022, macos-11] os: [ubuntu-20.04, windows-2022, macos-11, [self-hosted, linux, ARM64]]
runs-on: ${{ matrix.os }} runs-on: ${{ matrix.os }}
@ -27,17 +30,14 @@ jobs:
- name: Install Node.js - name: Install Node.js
uses: actions/setup-node@v3 uses: actions/setup-node@v3
with: with:
node-version: 16.17.0 node-version: 18.16.0
cache: 'npm' cache: 'npm'
- name: Install Modules - name: Install Modules
run: npm ci run: npm ci
- name: Build Sample Addon - name: Build Sample Addon
run: npm run test-build run: npm run build-test
- name: Run Unit Tests - name: Run Unit Tests
run: npm run test-ci run: npm run test-ci
- name: Run ESLint
run: npm run eslint

3
.gitignore vendored
View File

@ -5,6 +5,5 @@
.project .project
.DS_Store .DS_Store
node_modules/ node_modules/
test/build/ test-addon/build/
doc/jest/
*.log *.log

View File

@ -4,13 +4,8 @@
"name": "Win32", "name": "Win32",
"includePath": [ "includePath": [
"${workspaceFolder}/**", "${workspaceFolder}/**",
"${workspaceFolder}/node_modules/node-addon-api", "${LocalAppData}/node-gyp/Cache/16.17.0/include/node",
"${LocalAppData}/node-gyp/Cache/16.17.0/include/node" "${LocalAppData}/node-gyp/Cache/18.16.0/include/node"
],
"defines": [
"_DEBUG",
"UNICODE",
"_UNICODE"
], ],
"windowsSdkVersion": "10.0.19041.0", "windowsSdkVersion": "10.0.19041.0",
"cStandard": "c17", "cStandard": "c17",

View File

@ -1,6 +1,6 @@
MIT License MIT License
Copyright (c) 2022 Luis Blanco Copyright (c) 2023 Luis Blanco
Permission is hereby granted, free of charge, to any person obtaining a copy Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal of this software and associated documentation files (the "Software"), to deal

View File

@ -2,67 +2,74 @@
This is a part of [Node3D](https://github.com/node-3d) project. This is a part of [Node3D](https://github.com/node-3d) project.
[![NPM](https://nodei.co/npm/addon-tools-raub.png?compact=true)](https://www.npmjs.com/package/addon-tools-raub) [![NPM](https://badge.fury.io/js/addon-tools-raub.svg)](https://badge.fury.io/js/addon-tools-raub)
[![CodeFactor](https://www.codefactor.io/repository/github/node-3d/addon-tools-raub/badge)](https://www.codefactor.io/repository/github/node-3d/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)
[![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 addon-tools-raub npm i -s addon-tools-raub
``` ```
This module contains numerous helpers for Node.js **NAPI**
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.
**Docs**: ## include/addon-tools.hpp
* [include/addon-tools.hpp](doc/addon-tools.md)
Macro shortcuts for C++ addons using **NAPI**. Macro shortcuts for C++ addons using **NAPI**.
* [Es5 Class Wrapping](doc/class-wrapping.md) See [docs inside the folder](/include).
An alternative, lightweight native class-defining mechanism for addons. Example of an addon method definition:
* [Snippets](doc/snippets.md)
Some repetitive bits of code for addons. ```
// hpp:
#include <addon-tools.hpp>
DBG_EXPORT JS_METHOD(doSomething);
// cpp:
DBG_EXPORT JS_METHOD(doSomething) { NAPI_ENV;
LET_INT32_ARG(0, param0);
std::cout << "param0: " << param0 << std::endl;
RET_UNDEFINED;
}
```
## index.js ## index.js
> NOTE: peer dependency `node-addon-api` is required for this helper. JavaScript helpers for Node.js addon development. The short list of helpers:
```
Main exports for cross-platform addon configuration. 'getBin', 'getPlatform', 'getInclude', 'getPaths',
See [TypeScript definitions](/index.d.ts) with comments. 'install', 'cpbin', 'download', 'read', 'write', 'copy', 'exists',
'mkdir', 'stat', 'isDir', 'isFile', 'dirUp', 'ensuredir', 'copysafe',
'readdir', 'subdirs', 'subfiles', 'traverse', 'copyall',
'rmdir', 'rm', 'WritableBuffer', 'actionPack',
```
## download.js See the [TypeScript definitions](/index.d.ts) with comments.
Downloads a file into the memory, **HTTP** or **HTTPS**.
See [TypeScript definitions](/download.d.ts) with comments.
## cpbin.js ### Example for an ADDON's **index.js**:
Downloads a file into the memory, **HTTP** or **HTTPS**. ```
See [TypeScript definitions](/cpbin.d.ts) with comments. const { getBin } = require('addon-tools-raub');
const core = require(`./${getBin()}/ADDON`); // uses the platform-specific ADDON.node
```
## install.js ### Example for **binding.gyp**:
> NOTE: peer dependency `adm-zip` is required for this helper. ```
'include_dirs': [
'<!@(node -p "require(\'addon-tools-raub\').getInclude()")',
],
```
Downloads and unzips the platform specific binary for the calling package. > NOTE: the optional `node-addon-api` dependency is used by the `getInclude()` helper. If not found,
See [TypeScript definitions](/install.d.ts) with comments. the **napi.h** include path won't be a part of the returned string.
## writable-buffer.js ### Example of `cpbin` in **package.json :: scripts**:
A [Writable](https://nodejs.org/api/stream.html#stream_writable_streams) ```
stream buffer. "build": "cd src && node-gyp rebuild -j max --silent && node -e \"require('addon-tools-raub').cpbin('segfault')\" && cd ..",
See [TypeScript definitions](/writable-buffer.d.ts) with comments. "build-only": "cd src && node-gyp build -j max --silent && node -e \"require('addon-tools-raub').cpbin('segfault')\" && cd ..",
```
## utils.js
Async `fs` based helpers for common addon-related file operations.
See [TypeScript definitions](/utils.d.ts) with comments.

19
cpbin.d.ts vendored
View File

@ -1,19 +0,0 @@
declare module "addon-tools-raub/cpbin" {
/**
* Copy binary
* Copies the addon binary from `src/build/Release` to the platform folder.
* ```
* declare const cpbin: (name: string) => Promise<void>;
* 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.
*/
const cpbin: (name: string) => Promise<void>;
export = cpbin;
}

View File

@ -1,198 +0,0 @@
# 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": "6.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>

19
download.d.ts vendored
View File

@ -1,19 +0,0 @@
declare module "addon-tools-raub/download" {
/**
* 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);
* ```
*/
const download: (url: string) => Promise<Buffer>;
export = download;
}

View File

@ -1,24 +0,0 @@
'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

@ -1,7 +1,7 @@
# 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:
``` ```
@ -11,18 +11,31 @@ 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 like this: and is accessible with:
``` ```
require('addon-tools-raub').include // a string require('addon-tools-raub').getInclude() // 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 `CallbackInfo info` passed as an argument. And we can return `undefined` have `Napi::CallbackInfo info` passed as an argument. And we can return `undefined`
in case a problem has occured. So most of these macros are only usable in case a problem has occured. So most of these macros are only usable
within `Napi::Value`-returning functions. within `Napi::Value`-returning functions.
@ -31,6 +44,10 @@ 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>
@ -42,6 +59,7 @@ within `Napi::Value`-returning functions.
* `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>
@ -150,6 +168,7 @@ 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

@ -11,6 +11,12 @@
#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);
@ -205,6 +211,46 @@
#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>();
@ -330,7 +376,6 @@ 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()) {
@ -356,16 +401,15 @@ 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) {
@ -384,12 +428,10 @@ 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()) {
@ -406,7 +448,6 @@ inline void *getData(Napi::Env env, Napi::Object obj) {
} }
return out; return out;
} }
@ -433,40 +474,12 @@ inline Napi::Value consoleLog(Napi::Env env, const std::string &message) {
inline void eventEmit( inline void eventEmit(
Napi::Object that,
const std::string &name,
int argc = 0,
const Napi::Value *argv = 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.Call(that, args);
}
inline void eventEmitAsync(
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 napi_async_context context = nullptr
) { ) {
if (!that.Has("emit")) { if (!that.Has("emit")) {
return; return;
} }
@ -482,8 +495,11 @@ inline void eventEmitAsync(
args.push_back(napi_value(argv[i])); args.push_back(napi_value(argv[i]));
} }
if (context) {
thatEmit.MakeCallback(that, args, context); thatEmit.MakeCallback(that, args, context);
} else {
thatEmit.Call(that, args);
}
} }

65
include/index.js Normal file
View File

@ -0,0 +1,65 @@
'use strict';
const path = require('node:path');
const nameWindows = 'windows';
const platformAndArch = `${process.platform}-${process.arch}`;
const platformNames = {
'win32-x64': nameWindows,
'linux-x64': 'linux',
'darwin-x64': 'osx',
'linux-arm64': 'aarch64',
};
const platformName = platformNames[platformAndArch] || platformAndArch;
const isWindows = platformName === nameWindows;
const getPaths = (dir) => {
dir = dir.replace(/\\/g, '/');
const bin = `${dir}/bin-${platformName}`;
const include = `${dir}/include`;
if (isWindows) {
process.env.path = `${bin};${process.env.path ? `${process.env.path}` : ''}`;
}
return { bin, include };
};
const getBin = () => {
return `bin-${platformName}`;
};
const getPlatform = () => {
return platformName;
};
const getInclude = () => {
let napi = null;
try {
napi = require('node-addon-api');
} catch (ex) {
// do nothing
}
const rootPath = path.resolve(`${__dirname}/..`).replace(/\\/g, '/');
const napiInclude = napi ? napi.include_dir.replace(/\\/g, '/') : '';
const thisInclude = `${rootPath}/include`;
const includePath = `${napiInclude} ${thisInclude}`;
return includePath;
};
module.exports = {
getPaths,
getBin,
getPlatform,
getInclude,
};

41
include/index.test.js Normal file
View File

@ -0,0 +1,41 @@
'use strict';
const assert = require('node:assert').strict;
const { describe, it } = require('node:test');
const tools = require('.');
describe('AT / include', () => {
const stringMethods = ['getBin', 'getPlatform', 'getInclude'];
stringMethods.forEach((name) => {
describe(`#${name}()`, () => {
it('is a function', () => {
assert.strictEqual(typeof tools[name], 'function');
});
it('returns an object', () => {
assert.strictEqual(typeof tools[name](), 'string');
});
});
});
describe('#getPaths()', () => {
it('is a function', () => {
assert.strictEqual(typeof tools.getPaths, 'function');
});
it('returns an object', () => {
assert.strictEqual(typeof tools.getPaths(__dirname), 'object');
});
it('has "include" string', () => {
assert.strictEqual(typeof tools.getPaths(__dirname).include, 'string');
});
it('has "bin" string', () => {
assert.strictEqual(typeof tools.getPaths(__dirname).include, 'string');
});
});
});

81
include/snippets.md Normal file
View File

@ -0,0 +1,81 @@
# Snippets
## C++ Addon building
### Binary distribution
In **package.json** use the `"postinstall"` script to download the libraries.
For example the following structure might work. Note that **Addon Tools** will
append any given URL with `/${getPlatform()}.gz`
In **package.json**:
```
"scripts": {
"postinstall": "node install",
},
"dependencies": {
"addon-tools-raub": "^7.0.0",
},
"devDependencies": {
"node-addon-api": "^5.0.0"
}
```
Create the **install.js** file, see `install` in [index.d.ts](/index.d.ts).
**Addon Tools** will unpack (using **tar**) the downloaded file into the platform binary
directory. E.g. on Windows it will be **bin-windows**.
* For a dependency package:
Place the following piece of code into the `index.js` without changes.
```
module.exports = require('addon-tools-raub').getPaths(__dirname);
```
* For a compiled addon:
Require the `ADDON.node` in your **index.js** from the platform-specific directory.
```
const { getBin } = require('addon-tools-raub');
const core = require(`./${getBin()}/ADDON`);
```
Publishing binaries is done by attaching a GZIPped platform folder to a GitHub
release. Zip file must NOT contain platform folder as a subfolder, but rather
contain the final binaries. The tag of the release should be the same as in
**install.js**.
> NOTE: You can publish your binaries to anywhere, not necessarily GitHub.
Just tweak **YOUR install.js** script as appropriate. The only limitation
from **Addon Tools** is that it should be a GZIPped set of files/folders.
### GYP Variables
```
'variables': {
'bin': '<!(node -p "require(\'addon-tools-raub\').getBin()")',
'DEPS_include': '<!(node -p "require(\'DEPS\').getInclude()")',
'DEPS_bin': '<!(node -p "require(\'DEPS\').getBin()")',
},
```
* `bin` - the name of this platform's binary directory, e.g. *bin-linux*.
* `DEPS_include` - the include folder for some dependency package.
* `DEPS_bin` - the binary folder for some dependency package.
### Include directories
```
'include_dirs' : [
'<!@(node -p "require(\'addon-tools-raub\').getInclude()")',
'<(DEPS_include)',
],
```
See example of a working [**binding.gyp** here](/test-addon/binding.gyp)

355
index.d.ts vendored
View File

@ -1,34 +1,361 @@
declare module "addon-tools-raub" { declare module "addon-tools-raub" {
type Stats = import('node:fs').Stats;
type Writable = import('node:stream').Writable;
type Readable = import('node:stream').Readable;
type WritableOptions = import('node:stream').WritableOptions;
/** /**
* Addon paths * Get the internal paths for an addon
* Returns a set of platform dependent paths depending on input dir *
* Returns a set of platform dependent paths depending on the input dir
*/ */
export const paths: (dir: string) => Readonly<{ export const getPaths: (dir: string) => Readonly<{
/** /**
* Path to binaries * Path to binaries
* Platform binary directory absolute path *
* Platform binary directory absolute path for this `dir`
*/ */
bin: string; bin: string;
/** /**
* Path to include * Path to include
* Include directory for this dir *
* Include directory for this `dir`
*/ */
include: string; include: string;
}>; }>;
type TPlatformName = 'windows' | 'linux' | 'osx' | 'aarch64';
type TPlatformDir = `bin-${TPlatformName}`;
/** /**
* Binary folder name * Get the platform-specific binary directory name
* Platform-dependent binary directory name
*/ */
export const bin: string; export const getBin: () => TPlatformDir;
/** /**
* Platform name * Get the platform identifier
* One of: 'windows', 'linux', 'osx'
*/ */
export const platform: string; export const getPlatform: () => TPlatformName;
/** /**
* Main include directories * Get the include directories for **binding.gyp**
*
* Both 'addon-tools-raub' and 'node-addon-api' include paths. * Both 'addon-tools-raub' and 'node-addon-api' include paths.
* Use with node -p through list context command expansion <!@(...). * In binding.gyp: `'<!@(node -p "require(\'addon-tools-raub\').getInclude()")'`
*/ */
export const include: string; 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,52 +1,3 @@
'use strict'; 'use strict';
let napi = null; module.exports = Object.assign({}, require('./include'), require('./utils'));
try {
napi = require('node-addon-api');
} catch (ex) {
console.error('To build addons, `node-addon-api` module is required.');
process.exit(1);
}
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_dir.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,
};

24
install.d.ts vendored
View File

@ -1,24 +0,0 @@
declare module "addon-tools-raub/install" {
/**
* Install binaries
* Downloads and unzips 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/install');
* const prefix = 'https://github.com/USER/ADDON-NAME/releases/download';
* const tag = 'v1.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"
* },
* ```
*/
const install: (folder: string) => void;
export = install;
}

View File

@ -1,77 +0,0 @@
'use strict';
const https = require('https');
const http = require('http');
const fs = require('fs');
let AdmZip = null;
try {
AdmZip = require('adm-zip');
} catch (ex) {
console.error('The `install` script requires `adm-zip` module to be installed.');
process.exit(1);
}
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();
};

7143
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": "6.0.0", "version": "7.4.0",
"description": "Helpers for Node.js addons and dependency packages", "description": "Helpers for Node.js addons and dependency packages",
"license": "MIT", "license": "MIT",
"main": "index.js", "main": "index.js",
@ -19,68 +19,42 @@
], ],
"files": [ "files": [
"include", "include",
"cpbin.js",
"cpbin.d.ts",
"download.js",
"download.d.ts",
"index.js",
"index.d.ts",
"install.js",
"install.d.ts",
"utils.js", "utils.js",
"utils.d.ts", "utils.d.ts",
"writable-buffer.js", "index.js",
"writable-buffer.d.ts", "index.d.ts",
"utils",
"LICENSE", "LICENSE",
"package.json", "package.json",
"README.md" "README.md"
], ],
"engines": { "engines": {
"node": ">=16.17.0", "node": ">=18.16.0",
"npm": ">=8.15.0" "npm": ">=9.5.1"
}, },
"scripts": { "scripts": {
"eslint": "eslint .", "eslint": "eslint .",
"test": "jest --coverage=false --watch", "test": "node --test --watch .",
"test-ci": "jest --coverage=false --verbose", "test-ci": "node --test",
"test-coverage": "rm -rf doc/jest && jest --coverage --silent", "build-test": "cd test-addon && node-gyp rebuild -j max --silent && cd .."
"test-build": "cd test && node-gyp rebuild && cd .."
},
"jest": {
"moduleFileExtensions": [
"js"
],
"testMatch": [
"**/*.test.js"
],
"coverageDirectory": "doc/jest",
"coverageReporters": [
"lcov"
],
"collectCoverageFrom": [
"**/*.js",
"!**/*.test.js"
]
}, },
"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": { "peerDependencies": {
"adm-zip": "^0.5.9", "node-addon-api": "^7.0.0"
"node-addon-api": "^5.0.0"
}, },
"peerDependenciesMeta": { "peerDependenciesMeta": {
"adm-zip": { "optional": true }, "node-addon-api": {
"node-addon-api": { "optional": true } "optional": true
}
}, },
"devDependencies": { "devDependencies": {
"adm-zip": "^0.5.9", "@types/node": "^20.8.3",
"eslint-plugin-jest": "^27.0.4", "eslint": "^8.51.0",
"eslint-plugin-node": "^11.1.0", "eslint-plugin-node": "^11.1.0",
"eslint": "^8.24.0", "node-addon-api": "^7.0.0",
"jest": "^29.0.3", "typescript": "^5.2.2"
"node-addon-api": "^5.0.0",
"typescript": "^4.8.3"
} }
} }

37
test-addon/binding.gyp vendored Normal file
View File

@ -0,0 +1,37 @@
{
'targets': [{
'target_name': 'test',
'sources': [
'test.cpp',
],
'cflags_cc': ['-std=c++17', '-fno-exceptions'],
'include_dirs': [
'<!@(node -p "require(\'..\').getInclude()")',
],
'conditions': [
['OS=="linux"', {
'defines': ['__linux__'],
}],
['OS=="mac"', {
'MACOSX_DEPLOYMENT_TARGET': '10.9',
'defines': ['__APPLE__'],
'CLANG_CXX_LIBRARY': 'libc++',
'OTHER_CFLAGS': ['-std=c++17', '-fno-exceptions'],
}],
['OS=="win"', {
'defines' : ['WIN32_LEAN_AND_MEAN', 'VC_EXTRALEAN', '_WIN32', '_HAS_EXCEPTIONS=0'],
'msvs_settings' : {
'VCCLCompilerTool' : {
'AdditionalOptions' : [
'/O2','/Oy','/GL','/GF','/Gm-', '/std:c++17',
'/EHa-s-c-','/MT','/GS','/Gy','/GR-','/Gd',
],
},
'VCLinkerTool' : {
'AdditionalOptions' : ['/DEBUG:NONE', '/LTCG', '/OPT:NOREF'],
},
},
}],
],
}],
}

View File

@ -0,0 +1,45 @@
'use strict';
const assert = require('node:assert').strict;
const { describe, it } = require('node:test');
const test = require('./build/Release/test.node');
const arrayArgLetMsg = { message: 'Argument 0 must be of type `Array` or be `null`/`undefined`' };
describe('AT / HPP / LET_ARRAY_ARG', () => {
it('exports letArrayStrArg', () => {
assert.strictEqual(typeof test.letArrayStrArg, 'function');
});
it('throws if arg was passed a string', () => {
assert.throws(() => test.letArrayStrArg('1'), arrayArgLetMsg);
});
it('throws if arg was passed a number', () => {
assert.throws(() => test.letArrayStrArg(1), arrayArgLetMsg);
});
it('throws if arg was passed a boolean', () => {
assert.throws(() => test.letArrayStrArg(true), arrayArgLetMsg);
});
it('throws if arg was passed a pointer', () => {
assert.throws(() => test.letArrayStrArg(test.retExt()), arrayArgLetMsg);
});
it('throws if arg was passed an object', () => {
assert.throws(() => test.letArrayStrArg({}), arrayArgLetMsg);
});
it('accepts an empty arg', () => {
assert.ok(Array.isArray(test.letArrayStrArg()));
});
it('accepts undefined', () => {
assert.ok(Array.isArray(test.letArrayStrArg(undefined)));
});
it('accepts null', () => {
assert.ok(Array.isArray(test.letArrayStrArg(null)));
});
it('accepts an array', () => {
assert.ok(Array.isArray(test.letArrayStrArg([])));
});
it('returns same array', () => {
assert.deepStrictEqual(test.letArrayStrArg(['a', 'b']),['a', 'b']);
});
});

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

211
test-addon/hpp-arg.test.js Normal file
View File

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

View File

@ -3,12 +3,12 @@
JS_METHOD(empty) { NAPI_ENV; JS_METHOD(empty) { NAPI_ENV;
NAPI_HS; NAPI_HS;
return env.Undefined(); RET_UNDEFINED;
} }
JS_METHOD(throwing) { NAPI_ENV; JS_METHOD(throwing) { NAPI_ENV;
JS_THROW("Some error"); JS_THROW("Some error");
return env.Undefined(); RET_UNDEFINED;
} }
JS_METHOD(retUndefined) { NAPI_ENV; JS_METHOD(retUndefined) { NAPI_ENV;
@ -202,6 +202,11 @@ 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);
@ -284,6 +289,8 @@ 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);

View File

@ -1,50 +0,0 @@
{
'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']
},
},
},
],
],
},
],
}

View File

@ -1,105 +0,0 @@
'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);
});
});

View File

@ -1,96 +0,0 @@
'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

@ -1,96 +0,0 @@
'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);
});
});

View File

@ -1,105 +0,0 @@
'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

@ -1,96 +0,0 @@
'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);
});
});

View File

@ -1,96 +0,0 @@
'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);
});
});

View File

@ -1,105 +0,0 @@
'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);
});
});

View File

@ -1,96 +0,0 @@
'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);
});
});

View File

@ -1,96 +0,0 @@
'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');
});
});

View File

@ -1,96 +0,0 @@
'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);
});
});

View File

@ -1,207 +0,0 @@
'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);
});
});
});

View File

@ -1,33 +0,0 @@
'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');
});
});
});

105
utils.d.ts vendored
View File

@ -1,105 +0,0 @@
import type { Stats } from 'fs';
declare module "addon-tools-raub/utils" {
/**
* (async) Read a file
* 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) 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,15 +1,18 @@
set noparent set noparent
linelength=110 linelength=110
filter=-legal/copyright
filter=-build/include_order
filter=-build/header_guard filter=-build/header_guard
filter=-build/namespaces filter=-build/include
filter=-build/include_order
filter=-build/include_what_you_use filter=-build/include_what_you_use
filter=-build/namespaces
filter=-legal/copyright
filter=-readability/todo
filter=-runtime/indentation_namespace
filter=-whitespace/blank_line filter=-whitespace/blank_line
filter=-whitespace/braces
filter=-whitespace/comments filter=-whitespace/comments
filter=-whitespace/tab
filter=-whitespace/end_of_line filter=-whitespace/end_of_line
filter=-whitespace/indent filter=-whitespace/indent
filter=-whitespace/operators filter=-whitespace/operators
filter=-whitespace/parens filter=-whitespace/parens
filter=-readability/todo filter=-whitespace/tab

20
utils/action-pack.js Normal file
View File

@ -0,0 +1,20 @@
'use strict';
const util = require('node:util');
const exec = util.promisify(require('node:child_process').exec);
const { getPlatform, getBin } = require('../include');
const actionPack = async () => {
try {
await exec(`cd ${getBin()} && tar -czf ../${getPlatform()}.gz *`);
console.log(`pack=${getPlatform()}.gz`);
} catch (error) {
console.error(error);
process.exit(-1);
}
};
module.exports = { actionPack };

View File

@ -1,17 +1,17 @@
'use strict'; 'use strict';
const { copy, exists, mkdir, rm } = require('./utils'); const { copy, exists, mkdir, rm } = require('./files');
const { bin } = require('.'); const { getBin } = require('../include');
module.exports = async name => { const cpbin = 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}/../${bin}`; const binAbs = `${srcDir}/../${getBin()}`;
if (!await exists(binAbs)) { if (!await exists(binAbs)) {
await mkdir(binAbs); await mkdir(binAbs);
@ -25,5 +25,7 @@ module.exports = 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 "${bin}".`); console.log(`The binary "${name}.node" was copied to "${getBin()}".`);
}; };
module.exports = { cpbin };

24
utils/cpcpplint.js Normal file
View File

@ -0,0 +1,24 @@
'use strict';
const { copy, exists } = require('./files');
const cpcpplint = async () => {
const cpplintDest = `${process.cwd()}/CPPLINT.cfg`.replace(/\\/g, '/');
const cpplintSrc = `${__dirname}/CPPLINT.cfg`.replace(/\\/g, '/');
if (!await exists(cpplintSrc) ) {
console.error('Error. File "CPPLINT.cfg" not found.');
return;
}
if (await exists(cpplintDest) ) {
console.warn('Warning. Dest "CPPLINT.cfg" exists and will be overwritten.');
}
await copy(cpplintSrc, cpplintDest);
console.log(`"CPPLINT.cfg" was copied to "${cpplintDest}".`);
};
module.exports = { cpcpplint };

46
utils/download.js Normal file
View File

@ -0,0 +1,46 @@
'use strict';
const https = require('node:https');
const http = require('node:http');
const { WritableBuffer } = require('./writable-buffer');
const protocols = { http, https };
const downloadRecursive = async (url, count = 1) => {
const stream = new WritableBuffer();
const proto = protocols[url.match(/^https?/i)[0].toLowerCase()];
const response = await new Promise((res, rej) => {
const request = proto.get(url, (response) => res(response));
request.on('error', (err) => rej(err));
});
// Handle redirects
if ([301, 302, 303, 307].includes(response.statusCode)) {
if (count < 5) {
return downloadRecursive(response.headers.location, count + 1);
}
console.log(url);
throw new Error('Error: Too many redirects.');
}
// Handle bad status
if (response.statusCode !== 200) {
console.log(url);
throw new Error(`Response status was ${response.statusCode}`);
}
response.pipe(stream);
return new Promise((res, rej) => {
response.on('error', (err) => rej(err));
response.on('end', () => res(stream.get()));
});
};
const download = (url) => downloadRecursive(url);
module.exports = { download };

View File

@ -1,9 +1,10 @@
'use strict'; 'use strict';
const fs = require('fs'); const fs = require('node: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()))
@ -13,7 +14,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()))
); );
@ -21,7 +22,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') {
@ -32,46 +33,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;
} }
@ -88,7 +89,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))
@ -97,18 +98,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}`);
}; };
@ -121,7 +122,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)))
@ -145,7 +146,7 @@ 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;
} }
@ -156,7 +157,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())
) )
); );
} }
@ -164,12 +165,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()))
); );
}; };

13
utils/index.js Normal file
View File

@ -0,0 +1,13 @@
'use strict';
module.exports = Object.assign(
{},
require('./action-pack'),
require('./cpbin'),
require('./cpcpplint'),
require('./download'),
require('./files'),
require('./install'),
require('./writable-buffer'),
);

72
utils/install.js Normal file
View File

@ -0,0 +1,72 @@
'use strict';
const fs = require('node:fs');
const https = require('node:https');
const http = require('node:http');
const util = require('node:util');
const exec = util.promisify(require('node:child_process').exec);
const { getBin, getPlatform } = require('../include');
const { mkdir, rmdir, rm } = require('./files');
const protocols = { http, https };
const installRecursive = async (url, count = 1) => {
try {
const proto = protocols[url.match(/^https?/)[0]];
const response = await new Promise((res, rej) => {
const request = proto.get(url, (response) => res(response));
request.on('error', (err) => rej(err));
});
response.on('error', (err) => { throw err; });
// Handle redirects
if ([301, 302, 303, 307].includes(response.statusCode)) {
if (count < 5) {
return installRecursive(response.headers.location, count + 1);
}
console.warn(url);
throw new Error('Error: Too many redirects.');
}
// Handle bad status
if (response.statusCode !== 200) {
console.warn(url);
throw new Error(`Response status was ${response.statusCode}`);
}
await rmdir(getBin());
await mkdir(getBin());
const packPath = `${getBin()}/${getPlatform()}.gz`;
await new Promise((res, rej) => {
const packWriter = fs.createWriteStream(packPath);
packWriter.on('error', (err) => rej(err));
packWriter.on('finish', () => res());
response.pipe(packWriter);
});
const { stderr } = await exec(`tar -xzf ${packPath} --directory ${getBin()}`);
if (stderr) {
console.warn(stderr);
}
await rm(packPath);
return true;
} catch (error) {
console.error(error.message);
return false;
}
};
const install = async (folder) => {
const url = `${folder}/${getPlatform()}.gz`;
return installRecursive(url);
};
module.exports = { install };

22
utils/utils.test.js Normal file
View File

@ -0,0 +1,22 @@
'use strict';
const assert = require('node:assert').strict;
const { describe, it } = require('node:test');
const utils = require('.');
describe('AT / utils', () => {
const methods = [
'install', 'cpbin', 'download', 'read', 'write', 'copy', 'exists',
'mkdir', 'stat', 'isDir', 'isFile', 'dirUp', 'ensuredir', 'copysafe',
'readdir', 'subdirs', 'subfiles', 'traverse', 'copyall',
'rmdir', 'rm', 'WritableBuffer', 'actionPack',
];
methods.forEach((name) => {
it(`exports the "${name}" function`, () => {
assert.strictEqual(typeof utils[name], 'function');
});
});
});

View File

@ -1,6 +1,7 @@
'use strict'; 'use strict';
const { Writable } = require('stream'); const { Writable } = require('node:stream');
const CHUNK_SIZE = 1024; const CHUNK_SIZE = 1024;
const INITIAL_SIZE = 8 * CHUNK_SIZE; const INITIAL_SIZE = 8 * CHUNK_SIZE;
@ -8,18 +9,14 @@ 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 = Buffer.alloc(INITIAL_SIZE);
this._size = 0; this._size = 0;
} }
get() { get() {
if (!this._size) { if (!this._size) {
return null; return null;
} }
@ -28,12 +25,10 @@ class WritableBuffer extends Writable {
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;
} }
@ -45,21 +40,17 @@ class WritableBuffer extends Writable {
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;

22
writable-buffer.d.ts vendored
View File

@ -1,22 +0,0 @@
import type { Writable } from 'stream';
declare module "addon-tools-raub/writable-buffer" {
/**
* 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 extends Writable {
constructor();
/**
* Get the downloaded data
* Use `stream.get()` to obtain the data when writing was finished
*/
get(): Buffer;
}
export = WritableBuffer;
}