Skip to content

s97712/BlazorTS

Repository files navigation

BlazorTS

πŸš€ A tool for seamless Blazor-TypeScript integration


BlazorTS is a source generator library that uses Tree-sitter syntax tree parsing to analyze TypeScript code, automatically generating C# wrapper code that enables you to call TypeScript functions directly in Blazor applications without manually writing JavaScript interop code.

✨ Features

  • πŸ”„ Auto Generation: Uses Tree-sitter syntax tree parsing to analyze TypeScript code and automatically generates C# wrapper code
  • 🎯 Type Safety: Complete type mapping and compile-time checking
  • πŸš€ Zero Configuration: Minimal configuration, works out of the box
  • πŸ”§ Smart Dependencies: Automatic service resolution and registration
  • 🌳 Precise Parsing: Uses Tree-sitter for accurate TypeScript syntax structure parsing

πŸ“¦ Installation

Core Libraries

NuGet Package NuGet Version Description
BlazorTS NuGet Core runtime library
BlazorTS.SourceGenerator NuGet Source generator library
BlazorTS.Assets NuGet Asset management with dynamic URLs
Microsoft.TypeScript.MSBuild NuGet TypeScript compilation support

Installation and Configuration

1. Install NuGet Packages

Install via .NET CLI

dotnet add package BlazorTS
dotnet add package BlazorTS.SourceGenerator
dotnet add package BlazorTS.Assets # (optional)
dotnet add package Microsoft.TypeScript.MSBuild # (optional)

Install via Package Manager Console

Install-Package BlazorTS
Install-Package BlazorTS.SourceGenerator
Install-Package BlazorTS.Assets # (optional)
Install-Package Microsoft.TypeScript.MSBuild # (optional)

Alternative: If you prefer not to use Microsoft.TypeScript.MSBuild, you can manually add a Target to your .csproj file to invoke the TypeScript compiler (npx tsc).

<Target Name="CompileTypeScript" BeforeTargets="Build">
  <Exec Command="npx tsc" />
</Target>

2. Configure Project File

Add the following to your .csproj file to ensure TypeScript files are processed correctly:

<!-- Add .razor.ts and .entry.ts files as additional files -->
<ItemGroup>
  <AdditionalFiles Include="**/*.razor.ts" Exclude="**/node_modules/**" />
  <AdditionalFiles Include="**/*.entry.ts" Exclude="**/node_modules/**" />
</ItemGroup>

πŸš€ File Naming Conventions

BlazorTS supports two types of TypeScript files to provide a flexible modularization scheme:

1. Razor Component Scripts (.razor.ts)

These files are bound to a specific Razor component for component-level script logic.

  • Naming Convention: MyComponent.razor.ts must be paired with MyComponent.razor.
  • Generated Output: Automatically generates a partial class for MyComponent and injects a TSInterop instance named Scripts.
  • Usage: Directly call TypeScript functions via the @injected Scripts property within the component.

Example:

Components/Pages/Counter.razor.ts

// Dedicated module for the Counter.razor component
export function increment(count: number): number {
    console.log("Incrementing count from TypeScript module!");
    return count + 1;
}

Components/Pages/Counter.razor

@page "/counter"
@rendermode InteractiveServer

@code {
    private int currentCount = 0;

    private async Task HandleClick()
    {
        // Directly call the `Scripts` property injected by BlazorTS
        currentCount = await Scripts.increment(currentCount);
    }
}

2. Standalone Feature Modules (.entry.ts)

These files are used to define common TypeScript modules that can be shared across multiple components or services.

  • Naming Convention: my-utils.entry.ts or api.entry.ts.
  • Generated Output: Generates a standard C# class (e.g., MyUtils or Api) that needs to be manually registered and injected.
  • Usage: Register the service in Program.cs and use it where needed via dependency injection.

Example:

Services/Formatter.entry.ts

export function formatCurrency(amount: number): string {
    return `$${amount.toFixed(2)}`;
}

Program.cs

// Automatically finds and registers all services generated from .entry.ts files
builder.Services.AddBlazorTSScripts();

MyComponent.razor

@inject TestApp.Services.Formatter Formatter

<p>@Formatter.formatCurrency(123.45)</p>

2. Configure tsconfig.json

To enable Blazor to find the compiled JS file, we need to configure tsconfig.json to preserve the directory structure.

{
  "compilerOptions": {
    "noImplicitAny": false,
    "noEmitOnError": true,
    "removeComments": false,
    "target": "es2015",
    "rootDir": ".",
    "outDir": "wwwroot/js"
  },
  "include": [
    "**/*.razor.ts",
    "**/*.entry.ts"
  ]
}

With this configuration, Components/Pages/Counter.razor.ts will be compiled to wwwroot/js/Components/Pages/Counter.js.

3. Register Services

Register the BlazorTS services in Program.cs.

// Program.cs
using BlazorTS.SourceGenerator.Extensions;
using Microsoft.Extensions.DependencyInjection;

// Register BlazorTS core services (includes default path resolver)
builder.Services.AddBlazorTS();
// Automatically finds and registers all services generated from .entry.ts files
builder.Services.AddBlazorTSScripts();

4. Run and See the Result

Now, run your Blazor application. When you click the button:

  1. The HandleClick method in Counter.razor is called.
  2. It directly accesses the Scripts property, which is automatically generated by BlazorTS.
  3. The Scripts.increment call executes the corresponding function in Counter.razor.ts.

Behind the scenes, BlazorTS generates the following partial class code for you and merges it with your Counter.razor.cs:

// Code automatically generated by BlazorTS (conceptual)
public partial class Counter
{
    // Automatically injects the TSInterop instance
    [Inject]
    public TSInterop Scripts { get; set; } = null!;

    // Wrapper class responsible for JS interop
    public class TSInterop(ScriptBridge invoker)
    {
        // ... implementation details ...
        public async Task<double> increment(double count)
        {
            // ... calls JS ...
        }
    }
}

In this way, BlazorTS perfectly integrates the TypeScript development experience with the Blazor component model, achieving true modularity.

πŸ› οΈ Custom Path Resolution

BlazorTS maps MyApp.Components.Counter to /js/Components/Counter.js by default.

To customize paths, specify when registering services:

// Using custom function
builder.Services.AddBlazorTS((type, suffix) =>
{
    var path = type.FullName!.Replace('.', '/');
    return $"/scripts/{path}{suffix}.js";
});

// Using custom resolver class
public class CustomResolver : INSResolver
{
    public string ResolveNS(Type tsType, string suffix)
    {
        var path = tsType.FullName!.Replace('.', '/');
        return $"/lib/{path}{suffix}.js";
    }
}
builder.Services.AddBlazorTS<CustomResolver>();

Suffix Parameter

The ResolveNS method now includes a suffix parameter to distinguish between different module types:

  • Razor Components (.razor.ts files): Use suffix ".razor"
    • Component.razor.ts β†’ /js/Component.razor.js
  • Entry Modules (.entry.ts files): Use suffix ".entry"
    • Module.entry.ts β†’ /js/Module.entry.js
  • Custom Suffixes: Any string can be used as suffix

Examples:

// The default resolver automatically handles suffixes
var resolver = new DefaultNSResolver();
resolver.ResolveNS(typeof(MyComponent), ".razor");  // "/js/MyComponent.razor.js"
resolver.ResolveNS(typeof(MyModule), ".entry");     // "/js/MyModule.entry.js"
resolver.ResolveNS(typeof(MyClass), "");            // "/js/MyClass.js"

πŸ”§ Supported Types

TypeScript C# Parameter Return Type
string string Task<string>
number double Task<double>
boolean bool Task<bool>
any object? Task<object?>
void - Task
Promise<T> - Task<T>

πŸ“– Documentation

🀝 Contributing

Issues and Pull Requests are welcome!

πŸ“„ License

MIT License

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Packages

No packages published

Languages