node-api-dotnet-generator 0.7.29 → 0.7.30

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -3,7 +3,7 @@
3
3
  This project enables advanced interoperability between .NET and JavaScript in the same process.
4
4
 
5
5
  - Load .NET assemblies and call .NET APIs in-proc from a JavaScript application.
6
- - Load JavaScript packages call JS APIs in-proc from a .NET application.
6
+ - Load JavaScript packages and call JS APIs in-proc from a .NET application.
7
7
 
8
8
  Interop is high-performance and supports TypeScript type-definitions generation, async
9
9
  (tasks/promises), streams, and more. It uses [Node API](https://nodejs.org/api/n-api.html) so
@@ -13,13 +13,16 @@ supports Node API.
13
13
  :warning: _**Status: Public Preview** - Most functionality works well, though there are some known
14
14
  limitations around the edges, and there may still be minor breaking API changes._
15
15
 
16
- [Instructions for getting started are below.](#getting-started)
16
+ ### Documentation
17
+
18
+ Getting-started guidance, feature details, and API reference documentation are published at
19
+ https://microsoft.github.io/node-api-dotnet
17
20
 
18
21
  ### Minimal example - JS calling .NET
19
22
  ```JavaScript
20
23
  // JavaScript
21
24
  const Console = require('node-api-dotnet').System.Console;
22
- Console.WriteLine('Hello from .NET!');
25
+ Console.WriteLine('Hello from .NET!'); // JS writes to the .NET console API
23
26
  ```
24
27
 
25
28
  ### Minimal example - .NET calling JS
@@ -28,288 +31,32 @@ Console.WriteLine('Hello from .NET!');
28
31
  interface IConsole { void Log(string message); }
29
32
 
30
33
  var nodejs = new NodejsPlatform(libnodePath).CreateEnvironment();
31
- nodejs.Run(() => {
32
- var console = nodejs.Import<IConsole>("global", "console");
33
- console.Log("Hello from JS!");
34
- });
35
- ```
36
-
37
- For more examples, see the [examples](./examples/) directory.
38
-
39
- ## Feature Highlights
40
- - [Load and call .NET assemblies from JS](#load-and-call-net-assemblies-from-js)
41
- - [Load and call JavaScript packages from .NET](#load-and-call-javascript-packages-from-net)
42
- - [Generate TS type definitions for .NET APIs](#generate-ts-type-definitions-for-net-apis)
43
- - [Full async support](#full-async-support)
44
- - [Error propagation](#error-propagation)
45
- - [Develop Node.js addons with C#](#develop-nodejs-addons-with-c)
46
- - [Optionally work directly with JS types in C#](#optionally-work-directly-with-js-types-in-c)
47
- - [Automatic efficient marshaling](#automatic-efficient-marshaling)
48
- - [Stream across .NET and JS](#stream-across-net-and-js)
49
- - [Optional .NET native AOT compilation](#optional-net-native-aot-compilation)
50
- - [High performance](#high-performance)
51
- - [Work with .NET generic APIs in JS](#generics)
52
-
53
- ### Load and call .NET assemblies from JS
54
- The `node-api-dotnet` package manages hosting the .NET runtime in the JS process
55
- (if not using AOT - see below). The .NET core library types are available immediately;
56
- additional .NET assemblies can be loaded by file path:
57
- ```JavaScript
58
- // JavaScript
59
- const dotnet = require('node-api-dotnet');
60
- dotnet.load('path/to/ExampleAssembly.dll');
61
- const exampleObj = new dotnet.ExampleNamespace.ExampleClass(...args);
62
- ```
63
-
64
- All .NET namespaces are projected onto the top-level `dotnet` object. When loading multiple .NET
65
- assemblies, types from all assemblies are merged into the same namespace hierarchy.
66
-
67
- ### Load and call JavaScript packages from .NET
68
- Calling JavaScript from .NET requires hosting a JS runtime such as Node.js in the .NET app.
69
- Then JS packages can be imported either directly as JS values or by declaring C# interfaces for
70
- the JS types and using automatic marshalling.
71
-
72
- All interaction with a JavaScript environment must be from its thread, via the
73
- `Run()`, `RunAsync()`, or `Post()` methods on the JS environment object.
74
- ```C#
75
- // C#
76
- interface IExample
34
+ nodejs.Run(() =>
77
35
  {
78
- void ExampleMethod();
79
- }
80
-
81
- var nodejsPlatform = new NodejsPlatform(libnodePath);
82
- var nodejs = nodejsPlatform.CreateEnvironment();
83
-
84
- nodejs.Run(() => {
85
- // Import a module property, then call a function on it.
86
- var example1 = nodejs.Import("example-npm-package", "ExampleObject");
87
- example1.CallMethod("exampleMethod");
88
-
89
- // Import the module property using an interface, and call the same function.
90
- var example2 = nodejs.Import<IExample>("example-npm-package", "ExampleObject");
91
- example2.ExampleMethod();
36
+ var console = nodejs.Import<IConsole>("global", "console");
37
+ console.Log("Hello from JS!"); // C# writes to the JS console API
92
38
  });
93
39
  ```
94
40
 
95
- In the future, it may be possible to automatically generate .NET API definitions from TypeScript
96
- type definitions.
97
-
98
- ### Generate TS type definitions for .NET APIs
99
- If writing TypeScript, or type-checked JavaScript, there is a tool to generate type `.d.ts` type
100
- definitions for .NET APIs. It also generates a small `.js` file that makes it simple to load the
101
- assembly DLL and type-definitions together.
102
- ```bash
103
- $ npm exec node-api-dotnet-generator --assembly ExampleAssembly.dll --typedefs ExampleAssembly.d.ts
104
- ```
105
- ```TypeScript
106
- // TypeScript
107
- import './ExampleAssembly.js'; // TS also loads the adjacent .d.ts file.
108
- dotnet.ExampleNamespace.ExampleClass.ExampleMethod(...args); // This call is type-checked!
109
- ```
110
-
111
- (CommonJS modules must use `require()` instead of `import`.)
112
-
113
- For reference, there is a [list of C# type projections to TypeScript](/docs/typescript.md).
114
-
115
- ### Full async support
116
- JavaScript code can `await` a call to a .NET method that returns a `Task`. The marshaller
117
- automatically sets up a `SynchronizationContext` so that the .NET result is returned back to the
118
- JS thread.
119
- ```TypeScript
120
- // TypeScript
121
- import { ExampleClass } from './ExampleAssembly';
122
- const asyncResult = await ExampleClass.GetSomethingAsync(...args);
123
- ```
124
- .NET `Task`s are seamlessly marshaled to & from JS `Promise`s. So JS code can work naturally with
125
- a `Promise` returned from a .NET async method, and a JS `Promise` passed to .NET becomes a
126
- `JSPromise` that can be `await`ed in the C# code.
127
-
128
- ### Error propagation
129
- Exceptions/errors thrown in .NET or JS are propagated across the boundary with stack traces.
130
- An unhandled .NET exception is thrown back to a JS caller as an `Error` with a stack trace that
131
- includes both .NET and JS frames, and source line numbers if symbols are available. For example:
132
- ```
133
- Error: Test error thrown by JS.
134
- at Microsoft.JavaScript.NodeApi.TestCases.Errors.ThrowDotnetError(String message) in D:\node-api-dotnet\test\TestCases\Errors.cs:line 13
135
- at Microsoft.JavaScript.NodeApi.Generated.Module.Errors_ThrowDotnetError(JSCallbackArgs __args) in napi-dotnet.NodeApi.g.cs:line 357
136
- at Microsoft.JavaScript.NodeApi.JSNativeApi.InvokeCallback[TDescriptor](napi_env env, napi_callback_info callbackInfo, JSValueScopeType scopeType, Func`2 getCallbackDescriptor) in JSNativeApi.cs:line 1070
137
- at catchDotnetError (D:\node-api-dotnet\test\TestCases\errors.js:14:12)
138
- at Object.<anonymous> (D:\node-api-dotnet\test\TestCases\errors.js:41:1)
139
- ```
140
- Similarly, an unhandled JS `Error` is thrown back to a .NET caller as a `JSException` with a
141
- combined stack trace.
142
-
143
- ### Develop Node.js addons with C#
144
- A C# class library project can use the `[JSExport]` attribute to tag (and rename) APIs that are
145
- exported when the library is built as a JavaScript module. A [C# Source Generator](
146
- https://learn.microsoft.com/en-us/dotnet/csharp/roslyn-sdk/source-generators-overview) runs as
147
- part of the compilation and generates code to export the tagged APIs and marshal values between
148
- JavaScript and C#.
149
-
150
- ```C#
151
- // C#
152
- [JSExport] // Export class and all public members to JS.
153
- public class ExampleClass { ... }
154
-
155
- public static class ExampleStaticClass
156
- {
157
- [JSExport("exampleFunction")] // Export as a module-level function.
158
- public static string StaticMethod(ExampleClass obj) { ... }
159
-
160
- // (Other public members in this class are not exported by default.)
161
- }
162
- ```
163
-
164
- The `[JSExport]` source generator enables faster startup time because the marshaling code is
165
- generated at build time rather than dynamically emitted at runtime (as when calling a pre-built
166
- assembly). The source generator also enables building ahead-of-time compiled libraries in C# that
167
- can be called by JavaScript without depending on the .NET Runtime. (More on that below.)
168
-
169
- ### Optionally work directly with JS types in C#
170
- The class library includes an object model for the JavaScript type system. `JSValue` represents a
171
- value of any type, and there are more types like `JSObject`, `JSArray`, `JSMap`, `JSPromise`, etc.
172
- C# code can work directly with those types if desired:
173
-
174
- ```C#
175
- // C#
176
- [JSExport]
177
- public static JSPromise JSAsyncExample(JSValue input)
178
- {
179
- // Example of integration between C# async/await and JS promises.
180
- string greeter = (string)input;
181
- return new JSPromise(async (resolve) =>
182
- {
183
- await Task.Delay(50);
184
- resolve((JSValue)$"Hey {greeter}!");
185
- });
186
- }
187
- ```
188
-
189
- ### Automatic efficient marshaling
190
- There are two ways to get automatic marshaling between C# and JavaScript types:
191
- 1. Compile a C# class library with `[JSExport]` attributes like the examples above. The source
192
- generator produces marshaling code that is compiled with the assembly.
193
-
194
- 2. Load a pre-built .NET assembly, as in the earlier examples. The loader will use reflection to
195
- scan the APIs, then emit marshaling code on-demand for each type that is referenced by JS. The
196
- dynamic marshalling code is derived from the same expression trees that are used for compile-time
197
- source-generation, but is generated and at runtime and compiled with
198
- [`LambdaExpression.Compile()`](https://learn.microsoft.com/en-us/dotnet/api/system.linq.expressions.lambdaexpression.compile).
199
- So there is a small startup cost from that reflection and compilation, but subsequent calls to
200
- the same APIs may be just as fast as the pre-compiled marshaling code (and are just as likely
201
- to be JITted).
202
-
203
- The marshaller uses the strong typing information from the C# API declarations as hints about how to
204
- convert values beteen JavaScript and C#. Here's a general summary of conversions:
205
- - Primitives (numbers, strings, etc.) are passed by value directy.
206
- - C# structs have all properties passed by value (shallow copied).
207
- - C# classes are passed by reference. Any JS call to a C# class or interface property or method
208
- gets proxied over to the C# instance of the class. (Object GC lifetimes are synchronized
209
- accordingly.)
210
- - JS code may implement a C# interface, and pass that implementation back to C# code where it
211
- becomes a proxy that C# code can use.
212
- - C# collections like `IList<T>` and JS collections like `Map<T>` are also passed by reference;
213
- access to collection elements is proxied to whichever side the real instance of the collection
214
- is on.
215
- - JS `TypedArray`s are mapped to C# `Memory<T>` and passed by reference using shared memory
216
- (no proxying is needed).
217
- - Other types like enums, dates, and delegates are automatically marshaled as one would expect.
218
- - Custom marshaling and marshaling hints [may be supported later](
219
- https://github.com/jasongin/napi-dotnet/pull/25).
41
+ ## Packages
220
42
 
221
- ### Stream across .NET and JS
222
- .NET `Stream`s are automatically marshalled to and from Node.js `Duplex` (or `Readable` or
223
- `Writable`) streams. That means JS code can seamlessly read from or write to streams created
224
- by .NET. Or .NET code can read from or write to streams created by JS. Streamed data is
225
- transferred using shared memory (without any additional sockets or pipes), so memory allocation
226
- and copying is minimized.
43
+ Depending on the [scenario](https://microsoft.github.io/node-api-dotnet/scenarios/),
44
+ either NPM or NuGet packages may be used:
45
+ - NPM: [`node-api-dotnet`](https://www.npmjs.com/package/node-api-dotnet)
46
+ - NuGet: [`Microsoft.JavaScript.NodeApi`](https://www.nuget.org/packages/Microsoft.JavaScript.NodeApi/)
227
47
 
228
- ### Optional .NET native AOT compilation
229
- This library supports hosting the .NET Runtime in the same process as the JavaScript runtime.
230
- Alternatively, it also supports building [native ahead-of-time (AOT) compiled C#](
231
- https://learn.microsoft.com/en-us/dotnet/core/deploying/native-aot/) libraries that are
232
- loadable as a JavaScript module _without depending on the .NET Runtime_.
233
-
234
- There are advantages and disadvantages to either approach:
235
- | | .NET Runtime | .NET Native AOT |
236
- |---------------------|--------------|-----------------|
237
- | API compatibility | Broad compatibility with .NET APIs | Limited compatibility with APIs designed to support AOT |
238
- | Ease of deployment | Requires a matching version of .NET to be installed on the target system | A .NET installation is not required (though some platform libs may be required on Linux/Mac)
239
- | Size of deployment | Compact - only IL assemblies need to be deployed | Larger due to bundling necessary runtime code - minimum ~3 MB per platform |
240
- | Performance | Slightly slower startup (JIT) | Slightly faster startup (no JIT) |
241
- | Runtime limitations | Full .NET functionality | Some .NET features like reflection and code-generation aren't supported |
242
-
243
- ### High performance
244
- The project is designed to be as performant as possible when bridging between .NET and JavaScript.
245
- Techniques benefitting performance include:
246
- - Automatic marshaling avoids any use of JSON serialization, and uses generated code to avoid
247
- reflection.
248
- - Automatic marshalling uses shared memory or proxies when possible to minimize the amount of
249
- data transferred across the boundary.
250
- - Simple calls between JS and C# require **_almost_** zero memory allocation. (Maybe it will be
251
- zero eventually.)
252
- - Most JavaScript values are represented in C# as small structs (basically containing just a
253
- handle to the JS value), which helps avoid memory allocation.
254
- - Marshaling code uses modern C# performance features like `Span<T>` and `stackalloc` to minimize
255
- heap allocations and copying.
256
-
257
- Thanks to these design choices, JS to .NET calls are [more than twice as fast](
258
- https://github.com/jasongin/napi-dotnet/pull/23) when compared to `edge-js` using
259
- [that project's benchmark](https://github.com/tjanczuk/edge/wiki/Performance).
260
-
261
- ### Generics
262
- Even though the JavaScript runtime type system lacks generics, it is possible to work with .NET
263
- generic types and methods from JavaScript:
264
-
265
- ```JavaScript
266
- // JavaScript
267
- System.Enum.Parse$(System.DayOfWeek)('Tuesday');
268
- ```
269
-
270
- For details, see [Using .NET Generics in JavaScript](./docs/generics.md).
271
-
272
- ## Getting Started
273
- #### Requirements
274
- - .NET 6 or later
275
- - .NET 8 or later is required for AOT support.
276
- - .NET Framework 4.7.2 or later is supported at runtime,
277
- but .NET 6 SDK is still required for building.
278
- - Node.js v16 or later
279
- - Other JS runtimes may be supported in the future.
280
- - OS: Windows, Mac, or Linux
281
- - It should work on any platform where .NET 6 is supported.
282
-
283
- #### Instructions
284
- For calling .NET from JS, choose between one of the following scenarios:
285
- - [Dynamically invoke .NET APIs from JavaScript](./docs/dynamic-invoke.md)<br/>
286
- Dynamic invocation is simpler to set up: all you need is the `node-api-dotnet` npm package and
287
- the path to a .NET assembly you want to call. But it has some limitations (not all kinds of APIs
288
- are supported), and is not quite as fast as a C# module, because marshalling code must be
289
- generated at runtime.
290
- - [Develop a Node module in C#](./docs/node-module.md)<br/>
291
- A C# Node module is appropriate for an application that has more advanced interop needs. It can
292
- be faster because marshalling code can be generated at compile time, and the shape of the APIs
293
- exposed to JavaScript can be adapted with JS interop in mind.
294
-
295
- For calling JS from .NET, more documentation will be added soon. For now, see the
296
- [`winui-fluid` example code](./examples/winui-fluid/).
297
-
298
- Generated TypeScript type definitions can be utilized with any of these aproaches.
48
+ See [Packages & Releases](https://microsoft.github.io/node-api-dotnet/reference/packages-releases.html)
49
+ for details.
299
50
 
300
51
  ## Development
301
- For information about building, testing, and contributing changes to this project, see
302
- [README-DEV.md](./README-DEV.md).
303
52
 
304
- ## Contributing
53
+ For information about building, testing, and debugging this project, see
54
+ [README-DEV.md](./README-DEV.md).
305
55
 
306
- This project welcomes contributions and suggestions. Most contributions require you to agree to a
307
- Contributor License Agreement (CLA) declaring that you have the right to, and actually do, grant us
308
- the rights to use your contribution. For details, visit https://cla.opensource.microsoft.com.
56
+ Contributions require agreement to the
57
+ [Contributor License Agreement](https://microsoft.github.io/node-api-dotnet/contributing.html#contributor-license-agreement).
309
58
 
310
- When you submit a pull request, a CLA bot will automatically determine whether you need to provide
311
- a CLA and decorate the PR appropriately (e.g., status check, comment). Simply follow the
312
- instructions provided by the bot. You will only need to do this once across all repos using our CLA.
59
+ ## Code of Conduct
313
60
 
314
61
  This project has adopted the
315
62
  [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/).
package/package.json CHANGED
@@ -1,13 +1,13 @@
1
1
  {
2
2
  "name": "node-api-dotnet-generator",
3
- "version": "0.7.29",
3
+ "version": "0.7.30",
4
4
  "description": "Node-API for .Net code generator",
5
5
  "main": "index.js",
6
6
  "bin": "index.js",
7
7
  "license": "MIT",
8
8
  "author": "Microsoft",
9
9
  "dependencies": {
10
- "node-api-dotnet": "0.7.29"
10
+ "node-api-dotnet": "0.7.30"
11
11
  },
12
12
  "keywords": [
13
13
  "Node-API",