Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Reorganize examples into categorized folders #341

Merged
merged 18 commits into from
Nov 3, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
fd71b77
refactor: create directory structure to organize exmaples
matthewkeil Oct 13, 2023
93c3ac8
test: update test_all to work with new directory structure
matthewkeil Oct 13, 2023
42c6119
fix: bug in test_all if no package.json scripts
matthewkeil Oct 13, 2023
d93a4a8
test: cleanup output of test_all
matthewkeil Oct 13, 2023
2da956d
fix: remove default test script that throws from thread_safe_function…
matthewkeil Oct 13, 2023
0791c92
Merge branch 'main' into mkeil/reorganize
matthewkeil Oct 13, 2023
142407e
fix: remove build_with_cmake from .gitignore
matthewkeil Oct 13, 2023
1328d17
fix: swith to ' from " and remove subdir from getAllTests call
matthewkeil Oct 13, 2023
23fc82c
test: add extra test cases for npm start and just running the pkgJson…
matthewkeil Oct 13, 2023
d2b2da5
refactor: move all examples to src directory
matthewkeil Oct 13, 2023
f15cfc9
refactor: add numbers to categories so they are ordered in the src fo…
matthewkeil Oct 13, 2023
01a25b9
refactor: simplify getAllExamples in test_all.js
matthewkeil Oct 13, 2023
b017bb4
docs: add directory structure to README.md
matthewkeil Oct 13, 2023
fa6c1ec
docs: update directory structure to README.md
matthewkeil Oct 13, 2023
7f34343
docs: update directory structure to README.md
matthewkeil Oct 13, 2023
50a89d5
docs: update directory structure to README.md
matthewkeil Oct 13, 2023
30b21de
docs: update directory structure example in readme
matthewkeil Oct 17, 2023
c6c5496
add back build_with_cmake
legendecas Nov 3, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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.
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.
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
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
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
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.
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
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.
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
Loading