From 6bf64f6f7501d9961eff549b5c4ba5c0d286cdf1 Mon Sep 17 00:00:00 2001 From: Vladimir Morozov Date: Tue, 4 Mar 2025 17:26:19 -0800 Subject: [PATCH] Update NodeEmbedding docs --- docs/features/js-threading-async.md | 2 +- docs/scenarios/dotnet-js.md | 89 +++++++++++++---------------- 2 files changed, 41 insertions(+), 50 deletions(-) diff --git a/docs/features/js-threading-async.md b/docs/features/js-threading-async.md index 4965f83d..99cacb60 100644 --- a/docs/features/js-threading-async.md +++ b/docs/features/js-threading-async.md @@ -4,7 +4,7 @@ JavaScript engines have a single-threaded execution model. That means all access data or operations must be performed from the JavaScript thread. (It is possible run multiple JavaScript execution threads in a process using [Web workers](https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API) or the -[`NodejsEnvironment`](../reference/dotnet/Microsoft.JavaScript.NodeApi.Runtime/NodejsEnvironment) +[`NodeEmbeddingRuntime`](../reference/dotnet/Microsoft.JavaScript.NodeApi.Runtime/NodeEmbeddingRuntime) class, but the thread affinity rules still apply.) ## Invalid thread access diff --git a/docs/scenarios/dotnet-js.md b/docs/scenarios/dotnet-js.md index d1e4b73f..275e6812 100644 --- a/docs/scenarios/dotnet-js.md +++ b/docs/scenarios/dotnet-js.md @@ -5,61 +5,52 @@ This functionality is still experimental. It works, but the process is not as st should be. ::: -## Building the required `libnode` binary -This project depends on a [PR to Node.js](https://github.com/nodejs/node/pull/43542) that adds -simpler ABI-stable embedding APIs to `libnode`. Until that PR is merged, you'll need to build your -own binary from a branch. And even after it's merged, the Node.js project does not currently and -has no plans to publish official `libnode` binaries. - -1. Install the [prerequisites for building Node.js](https://github.com/nodejs/node/blob/main/BUILDING.md#building-nodejs-on-supported-platforms). -(On Windows this is basically Python 3 and either VS 2022 or the C++ build tools.) - -2. Clone the napi-libnode repo/branch: - -```shell -mkdir libnode -cd libnode -git clone https://github.com/jasongin/nodejs -b napi-libnode-v20.9.0 --single-branch . -``` - -3. Build in shared-library mode: -::: code-group -```shell [Windows] -.\vcbuild.bat x64 release dll openssl-no-asm -``` -```shell [Mac / Linux] -./configure --shared; make -j4 -``` -::: -The build may take an hour or more depending on the speed of your system. - -4. A successful build produces a binary in the `out/Release` directory: - - `libnode.dll` (Windows) - - `libnode.dylib` (Mac) - - `libnode.???.so` (Linux) +## Acquiring the the required `libnode` binary +This project depends on a [PR to Node.js](https://github.com/nodejs/node/pull/54660) that adds +simpler ABI-stable embedding APIs to `libnode`. Until that PR is merged and the Node.js project +starts building shared `libnode`, we offer the +[`Microsoft.JavaScript.LibNode](https://www.nuget.org/packages/Microsoft.JavaScript.LibNode) NuGet +package that installs pre-built `libnode` for Windows, MacOSX, and Linux (Ubuntu). This package +depends on the runtime ID specific NuGet packages which can be used directly if needed. + +Since the PR for the ABI-stable embedding API is still work in progress, the built `libnode` +will have breaking changes between versions. See the `Directory.Packages.props` file in the +root of the `node-api-dotnet` project for the matching version of the `Microsoft.JavaScript.LibNode` +package. ## Importing JS modules into .NET -1. Load `libnode` and initialize a Node.js "platform" and "environment" instance: +1. Load `libnode` and initialize a Node.js "platform" and "runtime" instance: ```C# // Find the path to the libnode binary for the current platform. string baseDir = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location)!; string libnodePath = Path.Combine(baseDir, "libnode.dll"); -NodejsPlatform nodejsPlatform = new(libnodePath); -var nodejs = nodejsPlatform.CreateEnvironment(baseDir); +NodeEmbeddingPlatform nodejsPlatform = new(libnodePath, null); +NodeEmbeddingThreadRuntime nodejsRuntime = nodejsPlatform.CreateThreadRuntime(baseDir, + new NodeEmbeddingRuntimeSettings + { + MainScript = + "globalThis.require = require('module').createRequire(process.execPath);\n" + }); ``` -There can only be one `NodejsPlatform` instance per process, so typically it would be stored -in a static variable. From the platform, multiple `NodejsEnvironment` instances may be created -and disposed. +There can only be one +[`NodeEmbeddingPlatform`](../reference/dotnet/Microsoft.JavaScript.NodeApi.Runtime/NodeEmbeddingPlatform) +instance per process, so typically it would be stored in a static variable. From the platform, +multiple +[`NodeEmbeddingRuntime`](../reference/dotnet/Microsoft.JavaScript.NodeApi.Runtime/NodeEmbeddingRuntime) +or +[`NodeEmbeddingThreadRuntime`](../reference/dotnet/Microsoft.JavaScript.NodeApi.Runtime/NodeEmbeddingThreadRuntime) +instances may be created and disposed. -The directory provided to the environment instance is the base for package resolution. Any packages -or modules imported in the next step must be installed (in a `node_modules` directory) in that base -directory or a parent directory. +The directory provided to the +[`NodeEmbeddingThreadRuntime`](../reference/dotnet/Microsoft.JavaScript.NodeApi.Runtime/NodeEmbeddingThreadRuntime) +instance is the base for package resolution. Any packages or modules imported in the next step must +be installed (in a `node_modules` directory) in that base directory or a parent directory. 2. Invoke a simple script from C#: ```C# -await nodejs.RunAsync(() => +await nodejsRuntime.RunAsync(() => { JSValue.RunScript("console.log('Hello from JS!');"); }); @@ -67,10 +58,10 @@ await nodejs.RunAsync(() => Be aware of JavaScript's single-threaded execution model. **All JavaScript operations must be performed from the JS environment thread**, unless otherwise noted. Use any of the `Run()`, -`RunAsync()`, `Post()`, or `PostAsync()` methods on the JS environment instance to switch to the -JS thread. Also keep in mind any JavaScript values of type `JSValue` (or any of the more specific -JS value struct types) are not valid after switching off the JS thread. A `JSReference` can hold -on to a JS value for future use. See also +`RunAsync()`, `Post()`, or `PostAsync()` methods on the JS `NodeEmbeddingThreadRuntime` instance +to switch to the JS thread. Also keep in mind any JavaScript values of type `JSValue` (or any of +the more specific JS value struct types) are not valid after switching off the JS thread. +A `JSReference` can hold on to a JS value for future use. See also [JS Threading and Async Continuations](../features/js-threading-async) and [JS References](../features/js-references). @@ -78,17 +69,17 @@ on to a JS value for future use. See also ```C# // Import * from the `fluid-framework` module. Items exported from the module will be // available as properties on the returned JS object. -JSValue fluidPackage = nodejs.Import("fluid-framework"); +JSValue fluidPackage = nodejsRuntime.Import("fluid-framework"); // Import just the `SharedString` class from the `fluid-framework` module. -JSValue sharedStringClass = nodejs.Import("fluid-framework", "SharedString"); +JSValue sharedStringClass = nodejsRuntime.Import("fluid-framework", "SharedString"); ``` As explained above, importing modules must be done on the JS thread. ## Debugging the JavaScript code in a .NET process ```C# int pid = Process.GetCurrentProcess().Id; -Uri inspectionUri = nodejs.StartInspector(); +Uri inspectionUri = nodejsRuntime.StartInspector(); Debug.WriteLine($"Node.js ({pid}) inspector listening at {inspectionUri}"); ``` Then attach a JavaScript debugger such as VS Code or Chrome to the inspection URI.