This project is dedicated to providing a user-friendly workflow for consuming .NET C# programs and libraries in any JavaScript environments: be it browsers, node.js or custom restricted spaces, like web extensions for VS Code.
In C# project (.csproj) specify Microsoft.NET.Sdk.BlazorWebAssembly
SDK and import DotNetJS NuGet package:
<Project Sdk="Microsoft.NET.Sdk.BlazorWebAssembly">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<LangVersion>10</LangVersion>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="DotNetJS" Version="0.1.0"/>
</ItemGroup>
</Project>
To invoke a JavaScript function in C# use JS.Invoke(functionName, args)
method. To expose a C# method to JavaScript, use [JSInvokable]
attribute:
using System;
using DotNetJS;
using Microsoft.JSInterop;
namespace HelloWorld;
public static class Program
{
public static void Main ()
{
var hostName = JS.Invoke<string>("getName");
Console.WriteLine($"Hello {hostName}, DotNet here!");
}
[JSInvokable]
public static string GetName () => "DotNet";
}
Publish the project with dotnet publish
. A single-file UMD library containing the dotnet runtime and project assemblies will be produced in the "bin" directory. Namespace of the program will be used for both the library file name and main export object. Consume the library depending on the environment:
<script src="HelloWorld.js"></script>
<script>
// This function is invoked by DotNet.
window.getName = () => "Browser";
window.onload = async function () {
// Booting the DotNet runtime and invoking entry point.
await HelloWorld.boot();
// Invoking 'GetName()' method from DotNet.
const guestName = HelloWorld.invoke("GetName");
console.log(`Welcome, ${guestName}! Enjoy your global space.`);
};
</script>
const HelloWorld = require("HelloWorld");
// This function is invoked by DotNet.
global.getName = () => "Node.js";
(async function () {
// Booting the DotNet runtime and invoking entry point.
await HelloWorld.boot();
// Invoking 'GetName()' method from DotNet.
const guestName = HelloWorld.invoke("GetName");
console.log(`Welcome, ${guestName}! Enjoy your CommonJS module space.`);
})();
You can find the following sample projects in this repository:
- Hello World — Consume DotNetJS-compiled program as a global import in browser, CommonJS or ECMAScript (ES) module in node.
- Web Extension — Consume the library in VS Code web extension, which works with both web and standalone versions of the IDE.
- Runtime Tests — Integration tests featuring various usage scenarios: async invocations, interop with instances, sending raw byte arrays, streaming, etc.
You can specify the following optional properties in .csproj to customize the build:
<Clean>false<Clean>
Do not clean the build output folders<LibraryName>CustomName</LibraryName>
Provide a custom name for the compiled library and export object.
For example, the following configuration will preserve the WebAssembly build artifacts and produce my-dotnet-lib.js
library with my-dotnet-lib
export object:
<Project Sdk="Microsoft.NET.Sdk.BlazorWebAssembly">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<LangVersion>10</LangVersion>
<Clean>false</Clean>
<LibraryName>my-dotnet-lib</LibraryName>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="DotNetJS" Version="0.1.0"/>
</ItemGroup>
</Project>
To compile and test the runtime run the following in order (under Runtime folder):
scripts/install-emsdk.sh
scripts/compile-runtime.sh
scripts/compile-test.sh
npm build
npm test
A memo for the publishing process after modifying JS runtime.
- Bump NPM version on
./Runtime/package.json
and:
npm run build
scripts/publish-package.sh
- Bump NuGet version on
./DotNetJS/DotNetJS.csproj
and:
dotnet pack -c Release --output bin
dotnet nuget push bin/DotNetJS.{VER}.nupkg --api-key {KEY} --source https://api.nuget.org/v3/index.json
- Wait for the package indexing, bump NuGet version on
./Runtime/test/Test.csproj
and:
script/compile-test.sh
- Remind myself that this should be automated.