Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
77 changes: 6 additions & 71 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<p align="center">
<a href="https://sharp.elringus.com" target="_blank" rel="noopener noreferrer">
<a href="https://bootsharp.com" target="_blank" rel="noopener noreferrer">
<img width="200" src="https://raw.githubusercontent.com/elringus/bootsharp/main/docs/public/favicon.svg" alt="Bootsharp">
</a>
</p>
Expand All @@ -14,10 +14,12 @@

# Use C# in web apps with comfort

Bootsharp streamlines consuming .NET C# apps and libraries in web projects. It's ideal for building web applications, where domain (backend) is authored in .NET C#, while the UI (frontend) is a standalone TypeScript or JavaScript project. Think of it as [Embind](https://emscripten.org/docs/porting/connecting_cpp_and_javascript/embind.html) for C++ or [wasm-bindgen](https://github.com/rustwasm/wasm-bindgen) for Rust.
Bootsharp streamlines the integration of .NET C# apps and libraries into web projects. It's ideal for building applications where the domain (backend) is authored in .NET C#, while the UI (frontend) is a standalone TypeScript or JavaScript project. Think of it as [Embind](https://emscripten.org/docs/porting/connecting_cpp_and_javascript/embind.html) for C++ or [wasm-bindgen](https://github.com/rustwasm/wasm-bindgen) for Rust.

![](https://raw.githubusercontent.com/elringus/bootsharp/main/docs/public/img/banner.png)

Facilitating high-level interoperation between C# and TypeScript, Bootsharp lets you build the UI layer within its natural ecosystem using industry-standard tooling and frameworks, such as [React](https://react.dev) and [Svelte](https://svelte.dev). The project can then be published to the web or bundled as a native desktop or mobile application with [Electron](https://electronjs.org) or [Tauri](https://tauri.app).

## Features

✨ High-level C# <-> TypeScript interop
Expand All @@ -32,75 +34,8 @@ Bootsharp streamlines consuming .NET C# apps and libraries in web projects. It's

🛠️ Allows customizing emitted bindings

🔥 Supports WASM multi-threading, AOT, trimming

🔥 Supports multi-threading, NativeAOT-LLVM, trimming

## 🎬 Get Started

https://sharp.elringus.com/guide/getting-started

### Why not Blazor?

In contrast to solutions like Blazor, which attempt to bring the entire web platform inside .NET, Bootsharp facilitates high-level interoperation between C# and TypeScript, allowing to build the UI layer under its natural ecosystem using industry-standard tooling and frameworks, such as [React](https://react.dev) and [Svelte](https://svelte.dev).

### Why not `System.JavaScript`?

Bootsharp itself is built on top of [System.Runtime.InteropServices.JavaScript](https://learn.microsoft.com/en-us/aspnet/core/blazor/javascript-interoperability/import-export-interop?view=aspnetcore-8.0) introduced in .NET 7.

If you need to expose a simple library API to JavaScript and don't require type declarations, Bootsharp is probably overkill. However, .NET's interop is low-level, lacks support for passing custom types by value, and requires extensive boilerplate to define bindings, making it impractical for large API surfaces.

With Bootsharp, you can simply provide your domain-specific interfaces and use them seamlessly on the other side, as if they were originally authored in TypeScript (and vice versa). This ensures a clear separation of concerns: your domain codebase won't be aware of the JavaScript environment—no need to annotate methods for interop or specify marshalling hints for arguments.

For example, consider the following abstract domain code:

```cs
public record Data (string Info, IReadOnlyList<Item> Items);
public record Result (View Header, View Content);
public interface IProvider { Data GetData (); }
public interface IGenerator { Result Generate (); }

public class Generator (IProvider provider) : IGenerator
{
public Result Generate ()
{
var data = provider.GetData();
// Process the data and generate result.
return result;
}
}
```
— the code doesn't use any JavaScript-specific APIs, making it fully testable and reusable. To expose it to JavaScript, all we need to do is add the following to `Program.cs` in a separate project for the WASM target:

```cs
using Bootsharp;
using Bootsharp.Inject;
using Microsoft.Extensions.DependencyInjection;

[assembly: JSImport(typeof(IProvider))]
[assembly: JSExport(typeof(IGenerator))]

// Bootsharp auto-injects implementation for 'IProvider'
// from JS and exposes 'Generator' APIs to JS.
new ServiceCollection()
.AddBootsharp()
.AddSingleton<IGenerator, Generator>()
.BuildServiceProvider()
.RunBootsharp();
```

— we can now provide implementation for `IProvider` and use `Generator` in JavaScript/TypeScript:

```ts
import bootsharp, { Provider, Generator } from "bootsharp";

// Implement 'IProvider'.
Provider.getData = () => ({
info: "...",
items: []
});

await bootsharp.boot();

// Use 'Generator'.
const result = Generator.generate();
```
https://bootsharp.com/guide
2 changes: 1 addition & 1 deletion docs/.vitepress/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,5 +75,5 @@ export default defineConfig({
"/api/": [{ text: "Reference", items: (await import("./../api/typedoc-sidebar.json")).default }]
}
},
sitemap: { hostname: "https://sharp.elringus.com" }
sitemap: { hostname: "https://bootsharp.com" }
});
4 changes: 4 additions & 0 deletions docs/guide/getting-started.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,10 @@ public static partial class Program
}
```

::: info NOTE
Authoring interop via static methods is impractical for large API surfaces—it's shown here only as a simple way to get started. For real projects, consider using [interop interfaces](/guide/interop-interfaces) instead.
:::

## Compile ES Module

Run following command under the solution root:
Expand Down
14 changes: 7 additions & 7 deletions docs/guide/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,25 +2,25 @@

## What?

Bootsharp is a solution for building web applications, where domain is authored in .NET C# and is consumed by a standalone JavaScript or TypeScript project.
Bootsharp is a solution for building web applications where the domain logic is authored in .NET C# and consumed by a standalone JavaScript or TypeScript project.

## Why?

C# is a popular language for building maintainable software with complex domain logic, such as enterprise and financial applications. However, its frontend capabilities are lacking, especially compared to the web ecosystem.
C# is a popular language for building maintainable software with complex domain logic, such as enterprise and financial applications. However, its frontend capabilities are lackingespecially when compared to the web ecosystem.

Web platform is the industry-standard for building modern user interfaces. It has best in class tooling and frameworks, such as [React](https://react.dev) and [Svelte](https://svelte.dev) — allowing to build better frontends faster, compared to any other language/platform ecosystem.
The web platform is the industry standard for building modern user interfaces. It offers best-in-class tooling and frameworks, such as [React](https://react.dev) and [Svelte](https://svelte.dev), enabling developers to build better frontends faster than with any other language or platform.

In contrast to solutions like [Blazor](https://dotnet.microsoft.com/en-us/apps/aspnet/web-apps/blazor), which attempt to bring the entire web platform inside .NET (effectively reversing natural workflow), Bootsharp facilitates high-level interoperation between C# and TypeScript, allowing to build domain and UI layers under their natural ecosystems.
In contrast to solutions like [Blazor](https://dotnet.microsoft.com/en-us/apps/aspnet/web-apps/blazor), which attempt to bring the entire web platform into .NET (effectively reversing the natural workflow), Bootsharp facilitates high-level interoperation between C# and TypeScript. This allows you to build the domain and UI layers within their natural ecosystems. The project can then be published to the web or bundled as a native desktop or mobile application with [Electron](https://electronjs.org) or [Tauri](https://tauri.app).

## How?

Bootsharp installs as a [NuGet package](https://www.nuget.org/packages/Bootsharp) to the C# project dedicated for building the solution for web. It's specifically designed to not "leak" the dependency outside entry assembly of the web target, which is essential to keep the domain clean from any platform-specific details.
Bootsharp is installed as a [NuGet package](https://www.nuget.org/packages/Bootsharp) into the C# project dedicated to building the solution for the web. It is specifically designed not to "leak" the dependency outside the entry assembly of the web targetessential for keeping the domain clean of any platform-specific details.

While it's possible to author both export (C# -> JS) and import (C# <- JS) bindings via static methods, complex solutions will benefit from interface-based interop: simply feed Bootsharp C# interfaces describing export and import API surfaces, and it will automatically generate associated bindings and type declarations.
While it's possible to author both export (C# JS) and import (C# JS) bindings via static methods, complex solutions benefit from interface-based interop. Simply provide Bootsharp with C# interfaces describing the export and import API surfaces, and it will automatically generate the associated bindings and type declarations.

![](/img/banner.png)

Bootsharp will automatically build and bundle JavaScript package when publishing C# solution, as well as generate `package.json`, so that you can reference the whole C# solution as any other ES module in your web project.
Bootsharp will automatically build and bundle the JavaScript package when publishing the C# solution, and generate a `package.json`, allowing you to reference the entire C# solution as any other ES module in your web project.

::: code-group
```jsonc [package.json]
Expand Down
31 changes: 9 additions & 22 deletions docs/guide/interop-interfaces.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
# Interop Interfaces

Instead of manually authoring a binding for each method, make Bootsharp generate them automatically with `[JSImport]` and `[JSExport]` assembly attributes.
Instead of manually authoring a binding for each method, let Bootsharp generate them automatically using the `[JSImport]` and `[JSExport]` assembly attributes.

For example, say we have a JavaScript UI (frontend), which needs to be notified when a data is mutated on the C# domain layer (backend), so it can render the updated state; additionally, our frontend may have a setting (eg, stored in browser cache) to temporary mute notifications, which needs to be retrieved by the backend. Create the following interface in C# to describe the expected frontend APIs:
For example, say we have a JavaScript UI (frontend) that needs to be notified when data is mutated in the C# domain layer (backend), so it can render the updated state. Additionally, the frontend may have a setting (e.g., stored in the browser cache) to temporarily mute notifications, which the backend needs to retrieve. You can create the following interface in C# to describe the expected frontend APIs:

```csharp
interface IFrontend
Expand All @@ -12,28 +12,15 @@ interface IFrontend
}
```

Now add the interface type to the JS import list:
Now, add the interface type to the JS import list:

```csharp
[assembly: JSImport([
typeof(IFrontend)
])]
```

Bootsharp will generate following C# implementation:

```csharp
public static partial class JSFrontend : IFrontend
{
[JSFunction] public static partial void NotifyDataChanged (Data data);
[JSFunction] public static partial bool IsMuted ();

void IFrontend.NotifyDataChanged (Data data) => NotifyDataChanged(data);
bool IFrontend.IsMuted () => IsMuted();
}
```

— which you can use in C# to interop with the frontend and following TypeScript spec to be implemented on the frontend:
Bootsharp will automatically implement the interface in C#, wiring it to JavaScript, while also providing you with a TypeScript spec to implement on the frontend:

```ts
export namespace Frontend {
Expand All @@ -42,7 +29,7 @@ export namespace Frontend {
}
```

Now say we want to provide an API for frontend to request mutation of the data:
Now, say we want to provide an API for the frontend to request a mutation of the data:

```csharp
interface IBackend
Expand All @@ -59,7 +46,7 @@ Export the interface to JavaScript:
])]
```

Get the following implementation auto-generated:
This will generate the following implementation:

```csharp
public class JSBackend
Expand All @@ -76,16 +63,16 @@ public class JSBackend
}
```

— which will produce following spec to be consumed on JavaScript side:
— which will produce the following spec to be consumed on the JavaScript side:

```ts
export namespace Backend {
export function addData(data: Data): void;
}
```

To make Bootsharp automatically inject and inititliaize generate interop implementations, use [dependency injection](/guide/extensions/dependency-injection) extension.
To make Bootsharp automatically inject and initialize the generated interop implementations, use the [dependency injection](/guide/extensions/dependency-injection) extension.

::: tip Example
Find example on using interop interfaces in the [React sample](https://github.com/elringus/bootsharp/tree/main/samples/react).
Find an example of using interop interfaces in the [React sample](https://github.com/elringus/bootsharp/tree/main/samples/react).
:::
16 changes: 8 additions & 8 deletions docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ titleTemplate: Bootsharp • :title
hero:
name: Bootsharp
text: Use C# in web apps with comfort
tagline: Author domain in C#, while taking full advantage of the modern JavaScript frontend ecosystem.
tagline: Author the domain in C#, while fully leveraging the modern JavaScript frontend ecosystem.
actions:
- theme: brand
text: Get Started
Expand All @@ -31,7 +31,7 @@ hero:
<div class="icon">✨</div>
<h2 class="title">High-level Interoperation</h2>
</div>
<p class="details">Generates JavaScript bindings and type declarations for your C# APIs facilitating seamless interop between the domain and UI.</p></article>
<p class="details">Generates JavaScript bindings and type declarations for your C# APIs, enabling seamless interop between domain and UI.</p></article>
</div>
</div>
<div class="grid-3 item">
Expand All @@ -41,7 +41,7 @@ hero:
<div class="icon">📦</div>
<h2 class="title">Embed or Sideload</h2>
</div>
<p class="details">Choose between embedding all the C# binaries into single-file ES module for portability or sideload for best performance and build size.</p></article>
<p class="details">Choose between embedding all C# binaries into a single-file ES module for portability or sideloading for performance and size.</p></article>
</div>
</div>
<div class="grid-3 item">
Expand All @@ -51,7 +51,7 @@ hero:
<div class="icon">🗺️</div>
<h2 class="title">Runs Everywhere</h2>
</div>
<p class="details">Node, Deno, Bun, web browsers and even constrained environments, such as VS Code extensionsyour app will work everywhere.</p></article>
<p class="details">Node, Deno, Bun, web browserseven constrained environments like VS Code extensionsyour app runs everywhere.</p></article>
</div>
</div>
</div>
Expand All @@ -63,7 +63,7 @@ hero:
<div class="icon">⚡</div>
<h2 class="title">Interop Interfaces</h2>
</div>
<p class="details">Manually author interop APIs via static C# methods or simply feed Bootsharp your domain-specific interfacesit'll figure the rest.</p></article>
<p class="details">Manually author interop APIs via static C# methods or feed Bootsharp your domain-specific interfacesit'll handle the rest.</p></article>
</div>
</div>
<div class="grid-4 item">
Expand All @@ -73,7 +73,7 @@ hero:
<div class="icon">🏷️</div>
<h2 class="title">Instance Bindings</h2>
</div>
<p class="details">When an interface value is specified in interop API, instance binding is generated allowing to interoperate on stateful objects.</p></article>
<p class="details">When an interface value is used in interop, instance binding is generated to interoperate with stateful objects.</p></article>
</div>
</div>
<div class="grid-4 item">
Expand All @@ -83,7 +83,7 @@ hero:
<div class="icon">🛠️</div>
<h2 class="title">Customizable</h2>
</div>
<p class="details">Configure namespaces for emitted bindings, function and event names, C# -> TypeScript type mappings and more.</p></article>
<p class="details">Configure namespaces for emitted bindings, function and event names, C# -> TypeScript type mappings, and more.</p></article>
</div>
</div>
<div class="grid-4 item">
Expand All @@ -93,7 +93,7 @@ hero:
<div class="icon">🔥</div>
<h2 class="title">Modern .NET</h2>
</div>
<p class="details">Supports latest runtime features: WASM multi-threading, AOT compilation, assembly trimming, streaming module instantiation.</p></article>
<p class="details">Supports latest runtime features: WASM multi-threading, assembly trimming, NativeAOT-LLVM, streaming instantiation.</p></article>
</div>
</div>
</div>
Expand Down
Binary file modified docs/public/img/llvm-bench.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Loading