Skip to content

nathanjhood/esnative

Repository files navigation

esnative

Native meets modern.


Quickstart

Install NodeJS dependencies:

yarn install

Build the Native module:

yarn cmake:build

Build the NodeJS module:

yarn pkg:build

Start the application:

yarn start

Successful output:

$ yarn start
yarn run v1.22.22
$ node ./dist/cli.mjs
addon.node is online!
Node Addon API 8
Done in <time>s.

Contents



Motivations

esnative intends to create a simple, clean, and well-defined entry point for projects targeting native platforms that combines modern ECMAscript tooling, features, and workflow, with the power of CMake-driven, system-level languages such as C and C++, and bindings provided by Node-API.

This repository currently represents a project template. Once built (see Quickstart), the application exposes two components - a library, and a command-line interface - as entry points. The command-line interface's functionality is derived from the library.

  • The library consists of two distinct parts; one is written in c++ (or optionally c or rust), then compiled to a system-native binary file with the file extension .node; the other part is written in Typescript, then transpiled to Javascript, and which imports the functions and variables from the binary file (on disk), binds them to corresponding ECMAscript, and then exports the bindings as an ECMA module.

  • The command-line interface is purely - or, almost exclusively - written in modern Typescript, then compiled into a module-friendly, npm-ready bundle of it's own. It imports and consumes the library - via the bindings - , and is intended to be executed by the node runtime.

The particularly interesting part of this project, is using NodeJS as an entry point to system-level languages and their paradigms. Usually, in system-application development, a main() function is written in the lower-level language and defines what the application should do when it is executed. The main() call is necessarily compiled-in to the binary, and thus any changes by the developer (such as bug fixes, enhancements, etc) require re-configuring and rebuilding. In the case of a Node Addon, our application entrypoint is written in ECMAscript languages like Javascript and Typescript; of which, even the latter now offers near-instant transpilation times, and much more rapid iteration over development cycles in comparison to native coding languages.

In short, I'm trying to make the commands shown below a repeatable, accessible entry point for new project templates:


Commands

{
    /** 'PUBLIC' COMMANDS INTENDED FOR CONSUMERS OF THE TEST MODULE */

    "start":              "node ./dist/cli.mjs",
    "dev":                "tsx  ./src/cli.ts",
    "build":              "tsc --noEmit && cmake-js --build && pkgroll --build",

    /** 'PRIVATE' COMMANDS INTENDED FOR DEVELOPERS OF THE TEST MODULE */

    // ECMAscript commands
    "pkg:build":          "pkgroll --build",
    "pkg:minify":         "pkgroll --minify",
    "pkg:sourcemap":      "pkgroll --sourcemap",
    "pkg:watch":          "pkgroll --watch",
    "pkg:clean":          "pkgroll --clean-dist",

    // Native Addon commands
    "cmake:install":      "cmake-js install",
    "cmake:postinstall":  "cmake-js compile",
    "cmake:configure":    "cmake-js configure",
    "cmake:reconfigure":  "cmake-js reconfigure",
    "cmake:build":        "cmake-js build",
    "cmake:rebuild":      "cmake-js rebuild",
    "cmake:clean":        "cmake-js clean",
    "cmake:wipe":         "cmake-js clean && rm -rvf ./node_modules",

    // maintenance
    "tsc:check":          "tsc --noEmit",

    "lint":               "eslint .",
    "lint:fix":           "eslint . --fix",

    "format":             "prettier --check ./**/*.{js,jsx,ts,tsx,css,md,json} --config ./prettier.config.mjs",
    "format:fix":         "prettier --write ./**/*.{js,jsx,ts,tsx,css,md,json} --config ./prettier.config.mjs"

    // tests (tbc...)
},

The template should build and run successfully using the commands above (and/or the others in package.json). The built project, and commands above which interact with it, represent what I intend for this project to facilitate for others to do with it, eventually.

If these early concepts pass the validation stage, then the template project will be moved into a sub-directory (probably /packages/template), and the main codebase of esnative shall take the form of the required tooling for scaffolding and developing new projects which resemble the template; think of a command like npx create-native-app@latest myApp --template typescript...


Notes

This project uses my fork of cmake-js; I drafted an API to provide the CMake side of consuming projects with some wrapper functions, based on some of my general requirements. See my CMakeLists.txt for an example of how the .node binary creation is configured; more documentation is available in the forked repo.

Both Node-API and cmake-js offer an unshakeable level of compatibility; try building the test project against a variety of NodeJS versions using nvm, and note how cmake-js automatically fetches the matching set of headers and other developer files for that NodeJS version. Node-API's well-documented ABI stability further guarantees that the resulting binary is widely stable across NodeJs versions, system platforms and architectures, and so forth.

The additional functionalities of tsx and pkgroll, both seperately, and together, are worth looking at closer, if you're hacking on this project. They provided the impetus that inspired the creation of the esnative project. The combined workflows of tsx and pkgroll alongside cmake-js in the creation of Node-API modules feels smooth, fast, and lightweight; no particular library or framework is required, other than NodeJS and it's Node-API and your native compiler toolchain (GCC/MSVC/xCode/LLVM, etc...).

I expect to experiment with some front-end libraries and frameworks in addition to the current template; if, and once, the two are integrating with absolute ease, this project will likely change form as proposed.

For the time being, it is a show of final intentions, and one I thought was interesting enough to make public.

Thanks for reading, let me know if any interest, curiousity, or otherwise.

Nathan J. Hood


About

Native meets modern.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published