Skip to content

Commit

Permalink
Reorganize examples into categorized folders (#341)
Browse files Browse the repository at this point in the history
* refactor: create directory structure to organize exmaples

* test: update test_all to work with new directory structure

* fix: bug in test_all if no package.json scripts

* test: cleanup output of test_all

* fix: remove default test script that throws from thread_safe_function_with_object_wrap

* fix: remove build_with_cmake from .gitignore

* fix: swith to ' from " and remove subdir from getAllTests call

* test: add extra test cases for npm start and just running the pkgJson.main with node

* refactor: move all examples to src directory

* refactor: add numbers to categories so they are ordered in the src folder

* refactor: simplify getAllExamples in test_all.js

* docs: add directory structure to README.md

* docs: update directory structure to README.md

* docs: update directory structure to README.md

* docs: update directory structure to README.md

* docs: update directory structure example in readme

* add back build_with_cmake

---------

Co-authored-by: legendecas <legendecas@gmail.com>
  • Loading branch information
matthewkeil and legendecas authored Nov 3, 2023
1 parent e68ad0f commit 7f4a9ed
Show file tree
Hide file tree
Showing 274 changed files with 167 additions and 121 deletions.
1 change: 0 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
build/
build_with_cmake/
node_modules/
Debug/
Release/
Expand Down
44 changes: 26 additions & 18 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,37 +15,45 @@ Implementations against unsupported versions of Node.js are provided for
completeness and historical context. They are not maintained.

The examples are primarily maintained for Node-API and node-addon-api and as outlined in
the Node.js [documentation](https://nodejs.org/dist/latest/docs/api/addons.html),
the Node.js [documentation](https://nodejs.org/dist/latest/docs/api/addons.html),
unless there is a need for direct access to functionality which
is not exposed by Node-API, use Node-API.
is not exposed by Node-API, use Node-API.

The [Node-API Resource](http://nodejs.github.io/node-addon-examples/) offers an
excellent orientation and tips for developers just getting started with Node-API
The [Node-API Resource](http://nodejs.github.io/node-addon-examples/) offers an
excellent orientation and tips for developers just getting started with Node-API
and `node-addon-api`.

## Usage

The directory structure is as follows:

```sh
REPO_ROOT
├── test_all.js
├── package.json
├── README.md
└── src
├── 1-getting-started
│ ├── example1
│ │ ├── nan
│ │ ├── node-addon-api
│ │ └── napi
│ ├── example2
│ └── example3
├── 2-js-to-native-conversion
├── 3-context-awareness
├── 4-references-and-handle-scope
├── 5-async-work
├── 6-threadsafe-function
├── 7-events
└── 8-tooling
```
<name of example>
|
+--- <implementation 1>
| |
| +--- files...
+--- <implementation 2>
. |
. +--- files...
.
```


In each example's implementation subdirectory, run

```text
$ npm install
$ node ./
npm install
node ./
```

to see the example in action.

File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,8 @@
class HelloAddon : public Napi::Addon<HelloAddon> {
public:
HelloAddon(Napi::Env env, Napi::Object exports) {
DefineAddon(exports, {
InstanceMethod("hello", &HelloAddon::Hello, napi_enumerable)
});
DefineAddon(exports,
{InstanceMethod("hello", &HelloAddon::Hello, napi_enumerable)});
}

private:
Expand Down
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,9 @@ void MyObject::Init(Napi::Env env, Napi::Object exports) {

Napi::FunctionReference* constructor = new Napi::FunctionReference();
*constructor = Napi::Persistent(func);
env.SetInstanceData(constructor); //NOTE: this assumes only 1 class is exported
//for multiple exported classes, need a struct or other mechanism
env.SetInstanceData(constructor); // NOTE: this assumes only 1 class is
// exported for multiple exported classes,
// need a struct or other mechanism

exports.Set("MyObject", func);
}
Expand Down
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,9 @@ class NamedInterceptor : public ObjectWrap {
char buf[256];

public:
NamedInterceptor() { std::strncpy(this->buf, "foo", sizeof (this->buf)); }
NamedInterceptor() { std::strncpy(this->buf, "foo", sizeof(this->buf)); }
static NAN_MODULE_INIT(Init);
static v8::Local<v8::Value> NewInstance ();
static v8::Local<v8::Value> NewInstance();
static NAN_METHOD(New);

static NAN_PROPERTY_GETTER(PropertyGetter);
Expand All @@ -35,33 +35,32 @@ NAN_METHOD(CreateNew) {

NAN_MODULE_INIT(NamedInterceptor::Init) {
v8::Local<v8::FunctionTemplate> tpl =
Nan::New<v8::FunctionTemplate>(NamedInterceptor::New);
Nan::New<v8::FunctionTemplate>(NamedInterceptor::New);
namedinterceptors_constructor.Reset(tpl);
tpl->SetClassName(Nan::New("NamedInterceptor").ToLocalChecked());
tpl->InstanceTemplate()->SetInternalFieldCount(1);
v8::Local<v8::ObjectTemplate> inst = tpl->InstanceTemplate();

SetNamedPropertyHandler(
inst
, NamedInterceptor::PropertyGetter
, NamedInterceptor::PropertySetter
, NamedInterceptor::PropertyQuery
, NamedInterceptor::PropertyDeleter
, NamedInterceptor::PropertyEnumerator);
SetNamedPropertyHandler(inst,
NamedInterceptor::PropertyGetter,
NamedInterceptor::PropertySetter,
NamedInterceptor::PropertyQuery,
NamedInterceptor::PropertyDeleter,
NamedInterceptor::PropertyEnumerator);

v8::Local<v8::Function> createnew =
Nan::GetFunction(Nan::New<v8::FunctionTemplate>(CreateNew))
.ToLocalChecked();
Nan::GetFunction(Nan::New<v8::FunctionTemplate>(CreateNew))
.ToLocalChecked();
Set(target, Nan::New("create").ToLocalChecked(), createnew);
}

v8::Local<v8::Value> NamedInterceptor::NewInstance () {
v8::Local<v8::Value> NamedInterceptor::NewInstance() {
EscapableHandleScope scope;
v8::Local<v8::FunctionTemplate> constructorHandle =
Nan::New(namedinterceptors_constructor);
v8::Local<v8::Object> instance =
Nan::NewInstance(GetFunction(constructorHandle).ToLocalChecked())
.ToLocalChecked();
Nan::NewInstance(GetFunction(constructorHandle).ToLocalChecked())
.ToLocalChecked();
return scope.Escape(instance);
}

Expand All @@ -71,10 +70,9 @@ NAN_METHOD(NamedInterceptor::New) {
info.GetReturnValue().Set(info.This());
}


NAN_PROPERTY_GETTER(NamedInterceptor::PropertyGetter) {
NamedInterceptor* interceptor =
ObjectWrap::Unwrap<NamedInterceptor>(info.Holder());
ObjectWrap::Unwrap<NamedInterceptor>(info.Holder());
if (!std::strcmp(*Nan::Utf8String(property), "prop")) {
info.GetReturnValue().Set(Nan::New(interceptor->buf).ToLocalChecked());
} else {
Expand All @@ -84,12 +82,10 @@ NAN_PROPERTY_GETTER(NamedInterceptor::PropertyGetter) {

NAN_PROPERTY_SETTER(NamedInterceptor::PropertySetter) {
NamedInterceptor* interceptor =
ObjectWrap::Unwrap<NamedInterceptor>(info.Holder());
ObjectWrap::Unwrap<NamedInterceptor>(info.Holder());
if (!std::strcmp(*Nan::Utf8String(property), "prop")) {
std::strncpy(
interceptor->buf
, *Nan::Utf8String(value)
, sizeof (interceptor->buf));
interceptor->buf, *Nan::Utf8String(value), sizeof(interceptor->buf));
info.GetReturnValue().Set(info.This());
} else {
info.GetReturnValue().Set(info.This());
Expand All @@ -104,8 +100,8 @@ NAN_PROPERTY_ENUMERATOR(NamedInterceptor::PropertyEnumerator) {

NAN_PROPERTY_DELETER(NamedInterceptor::PropertyDeleter) {
NamedInterceptor* interceptor =
ObjectWrap::Unwrap<NamedInterceptor>(info.Holder());
std::strncpy(interceptor->buf, "goober", sizeof (interceptor->buf));
ObjectWrap::Unwrap<NamedInterceptor>(info.Holder());
std::strncpy(interceptor->buf, "goober", sizeof(interceptor->buf));
info.GetReturnValue().Set(True());
}

Expand Down
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
#include <memory>
#include <cstring>
#include <memory>
#include <string>
#include "proxy-template.h"

Expand Down
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -94,8 +94,9 @@ static Napi::Value CreateByteArray(const Napi::CallbackInfo& info) {
// unique_ptr ownership.
nativeArray.release();

Napi::Uint8Array byteArray = Napi::Uint8Array::New(info.Env(), arrayLength, arrayBuffer, 0);

Napi::Uint8Array byteArray =
Napi::Uint8Array::New(info.Env(), arrayLength, arrayBuffer, 0);

return byteArray;
}

Expand Down
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,14 @@ Napi::Value DoHeavyMath(const Napi::CallbackInfo& info) {
return env.Undefined();
}
uint32_t num_1 = info[0].As<Napi::Number>().Uint32Value();

if (!info[1].IsNumber()) {
Napi::TypeError::New(env, "num2 must be a number")
.ThrowAsJavaScriptException();
return env.Undefined();
}
uint32_t num_2 = info[1].As<Napi::Number>().Uint32Value();

DoHeavyMathWorker* worker = new DoHeavyMathWorker(env, num_1, num_2);
worker->Queue();
return worker->GetPromise();
Expand Down
File renamed without changes.
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,6 @@
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"dependencies": {
"bindings": "*",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,19 +1,19 @@
#include <chrono>
#include <napi.h>
#include <chrono>
#include <thread>

using namespace Napi;

using Context = Reference<Value>;
using DataType = int;
void CallJs(Napi::Env env, Function callback, Context *context, DataType *data);
void CallJs(Napi::Env env, Function callback, Context* context, DataType* data);
using TSFN = TypedThreadSafeFunction<Context, DataType, CallJs>;
using FinalizerDataType = void;

std::thread nativeThread;
TSFN tsfn;

Value Start(const CallbackInfo &info) {
Value Start(const CallbackInfo& info) {
Napi::Env env = info.Env();

if (info.Length() < 2) {
Expand All @@ -28,18 +28,19 @@ Value Start(const CallbackInfo &info) {

// Create a new context set to the the receiver (ie, `this`) of the function
// call
Context *context = new Reference<Value>(Persistent(info.This()));
Context* context = new Reference<Value>(Persistent(info.This()));

// Create a ThreadSafeFunction
tsfn = TSFN::New(
env,
info[0].As<Function>(), // JavaScript function called asynchronously
"Resource Name", // Name
0, // Unlimited queue
1, // Only one thread will use this initially
info[0].As<Function>(), // JavaScript function called asynchronously
"Resource Name", // Name
0, // Unlimited queue
1, // Only one thread will use this initially
context,
[](Napi::Env, FinalizerDataType *,
Context *ctx) { // Finalizer used to clean threads up
[](Napi::Env,
FinalizerDataType*,
Context* ctx) { // Finalizer used to clean threads up
nativeThread.join();
delete ctx;
});
Expand All @@ -48,7 +49,7 @@ Value Start(const CallbackInfo &info) {
nativeThread = std::thread([count] {
for (int i = 0; i < count; i++) {
// Create new data
int *value = new int(clock());
int* value = new int(clock());

// Perform a blocking call
napi_status status = tsfn.BlockingCall(value);
Expand All @@ -69,13 +70,15 @@ Value Start(const CallbackInfo &info) {

// Transform native data into JS data, passing it to the provided
// `callback` -- the TSFN's JavaScript function.
void CallJs(Napi::Env env, Function callback, Context *context,
DataType *data) {
void CallJs(Napi::Env env,
Function callback,
Context* context,
DataType* data) {
// Is the JavaScript environment still available to call into, eg. the TSFN is
// not aborted
if (env != nullptr) {
// On Node-API 5+, the `callback` parameter is optional; however, this example
// does ensure a callback is provided.
// On Node-API 5+, the `callback` parameter is optional; however, this
// example does ensure a callback is provided.
if (callback != nullptr) {
callback.Call(context->Value(), {Number::New(env, *data)});
}
Expand Down
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
#include <napi.h>


static Napi::String Method(const Napi::CallbackInfo& info) {
// Napi::Env is the opaque data structure containing the environment in which the request is being run.
// We will need this env when we want to create any new objects inside of the node.js environment
// Napi::Env is the opaque data structure containing the environment in which
// the request is being run. We will need this env when we want to create any
// new objects inside of the node.js environment
Napi::Env env = info.Env();

// Create a C++ level variable
std::string helloWorld = "Hello, world!";

// Return a new javascript string that we copy-construct inside of the node.js environment

// Return a new javascript string that we copy-construct inside of the node.js
// environment
return Napi::String::New(env, helloWorld);
}

Expand Down
File renamed without changes.
Loading

0 comments on commit 7f4a9ed

Please sign in to comment.