Skip to content

devhxj/Jazor

Repository files navigation

English | 中文

Today's Verse

Jazor - C# to JavaScript Compiler

.NET License NuGet

Experimental. Public APIs, generated output shape, and tooling are still evolving. The compiler core and ECMAScript module emission are the most stable parts of the project.

Jazor is a Roslyn-based C# to JavaScript compiler centered on IOperation to ECMAScript AST lowering. Annotate C# classes with [ECMAScriptModule], get .mjs files at build time. Includes typed Vue 3 h() render function bindings via ECMAScript.Vue3, Pinia store bindings via ECMAScript.Pinia, and Vue Router bindings via ECMAScript.VueRoute.

Features

  • C# syntax coverage — supports virtually all C# 15 language features: variable declarations, basic types, pattern matching, nullable types, async/await, string interpolation, object and collection initialization, tuples, deconstruction, switch statements/expressions, and loops (for/foreach/while/do-while)
  • Roslyn IOperation lowering — semantic-driven compilation via IOperation → ECMAScript AST, not syntax-level translation
  • Static analysis safetyJazor.Analyzer enforces whitelist boundaries at compile time, diagnosing unsupported types and members before emission
  • CLR runtime modules — common BCL types (string, int, double, List<T>, Dictionary<TKey, TValue>, Task<T>, etc.) are mapped to JavaScript runtime implementations through Jazor.CLR
  • ECMAScript module emission[ECMAScriptModule] annotated classes compile to .mjs files with automatic cross-module import resolution and source maps
  • Vue 3 integrationECMAScript.Vue3 provides typed C# bindings for Vue 3's Composition API and h() render function, enabling component authoring in pure C#
  • Pinia integrationECMAScript.Pinia provides typed C# bindings for Pinia root/store authoring, including createPinia(), defineStore(), storeToRefs(), and common Options API helpers such as mapState() / mapActions()
  • Vue Router integrationECMAScript.VueRoute provides typed C# bindings for Vue Router 4 authoring, including createRouter(), history creators, useRouter(), useRoute(), RouterLink, and RouterView
  • Vuetify integrationECMAScript.Vuetify provides production-grade typed C# bindings for Vuetify 3 component authoring, with strongly typed props, named slots, event bindings, and full runtime export coverage
  • MSBuild integration — emit, bundle, and output path configuration through standard MSBuild properties

Status

TierComponentStatus
WorkingCompiler core (SemanticWalker, AstConverter) — 1,891 testsStable — the most mature part
WorkingECMAScript module emission ([ECMAScriptModule].mjs) — 111 testsStable
WorkingECMAScript.Vue3 bindings (h, ref, reactive, lifecycle, createApp)Stable
WorkingMSBuild integration (JazorEmit, JazorBundle, JazorOutDir)Stable
WorkingJazor.Analyzer (whitelist compile-time validation)Stable
WorkingECMAScript.Vuetify bindings (117 components, typed props/slots/events) — 117 componentsStable — production-grade Vuetify 3 authoring
WorkingCLR runtime modules — 70 testsStable
In progressRazorVue (SFC pipeline, canonical h() model, Vuetify production coverage) — 772 testsProduction-grade Vuetify authoring, pipeline validation, slot/fallthrough support
In progressECMAScript.Pinia + ECMAScript.VueRoute — 205 testsExpanding API coverage, Pinia Testing utilities
In progressSourceMapNarrow lane — module-level .mjs.map, not full coverage yet
In progressDeno bundlingJazorBundle target works for basic cases
In progressDebuggingDesign and milestone code exist, not user-facing yet
Long-termJolt (LSP, HMR, DevServer, debug, build) — 778 testsPhase 1–6 closing, Design

Users today should target the Working tier. 32 projects · ~290K production LoC · ~190K test LoC · ~3,850+ total tests.


Getting Started

Install

dotnet add package Jazor

The package includes runtime (ECMAScript, ECMAScript.Vue3, ECMAScript.Pinia, ECMAScript.VueRoute, ECMAScript.Vuetify), compiler (Jazor.Compiler), static analyzer (Jazor.Analyzer), emit tool (Jazor.Emit), and MSBuild props/targets.

Multi-project Setup

Jazor works best with a multi-project layout where library projects declare modules and a host project emits them.

Library project — declares modules, no emit:

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <TargetFramework>net11.0</TargetFramework>
    <JazorEmit>false</JazorEmit>
  </PropertyGroup>
  <ItemGroup>
    <PackageReference Include="Jazor" Version="*" />
  </ItemGroup>
</Project>

Host project — triggers emit and writes .mjs files:

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net11.0</TargetFramework>
    <JazorEmit>true</JazorEmit>
    <JazorOutDir>$(MSBuildProjectDirectory)\wwwroot\jazor\</JazorOutDir>
  </PropertyGroup>
  <ItemGroup>
    <PackageReference Include="Jazor" Version="*" />
    <ProjectReference Include="..\MyApp.Features\MyApp.Features.csproj" />
  </ItemGroup>
</Project>

The host project scans its own assembly and all referenced assemblies for [ECMAScriptModule]-annotated types and emits .mjs files to JazorOutDir. See the multi-project sample for the baseline emit layout, and ECMAScript.Pinia.Counter for a Vue 3 + Pinia consumption sample.

MSBuild Properties

Property Default Description
JazorCompile true Enables compilation of [ECMAScriptModule] types.
JazorEmit true for Exe, false for Library Emits .mjs files after build.
JazorOutDir $(IntermediateOutputPath)jazor\$(TargetFramework)\modules\ Output directory for emitted .mjs files.
JazorBundle false Bundles all emitted modules into a single JS file (uses bundled Deno runtime).
JazorBundleOut $(OutDir)jazor\app.js Output path for the bundled JS file.
JazorCleanEmit true Removes stale .mjs files from the output directory.
JazorFailOnPathConflict true Fails the build if two modules claim the same output path.

Authoring Modules

Basic Module

using ECMAScript;

namespace MyApp;

[ECMAScriptModule("shared/greetings.mjs")]
public static class GreetingModule
{
    public static string Prefix() => "Hello";
    public static string Compose(string name) => $"{Prefix()}, {name}";
}

Generates shared/greetings.mjs:

export function prefix() {
  return "Hello";
}
export function compose(name) {
  return `${prefix()}, ${name}`;
}

Cross-module imports are resolved automatically — when another module calls GreetingModule.Compose(name), the compiler generates the corresponding import statement.

Vue 3 h() Function Authoring

ECMAScript.Vue3 provides typed C# bindings for Vue 3's Composition API and h() render function:

using ECMAScript;
using static ECMAScript.Vue3;

namespace MyApp;

[ECMAScriptModule("app/counter.mjs")]
public static class CounterModule
{
    public static IVueComponent Counter
        => DefineComponent(new VueComponentOptions
        {
            Setup = () =>
            {
                var count = Ref(0);
                return () => H("div", new VueObject { Class = "counter" },
                [
                    H("p", $"Count: {count.Value}"),
                    H("button", new VueObject
                    {
                        Events = new VueDictionary
                        {
                            ["click"] = (Action)(() => count.Value++)
                        }
                    }, "Increment")
                ]);
            }
        });
}

Compilation Capabilities

The compiler supports variable declarations, basic types, pattern matching, nullable types, async/await, string interpolation, object and collection initialization, tuples, deconstruction, switch statements/expressions, and loops (for/foreach/while/do-while). See Compiler Docs for the full feature set.


ECMAScript Attribute Conventions

  • [ECMAScript("npm:vue@3")] — declares a runtime import dependency. The compiler generates import { ... } from "npm:vue@3".
  • [ECMAScript("jsr:@scope/pkg")] or [ECMAScript("https://...")] — Deno-resolvable import addresses.
  • [ECMAScriptModule("features/todo/index.mjs")] — declares the output module path after emission. Not a package resolution address.
  • [Jazor(...)] — CLR and host mapping producer-side declarations.

Project Structure

Jazor/
├── src/
│   ├── ECMAScript/                  # ECMAScript AST core types and attributes
│   ├── ECMAScript.Contract/         # Minimal contract layer (JazorAttribute, Op)
│   ├── ECMAScript.Pinia/            # Pinia runtime binding surface
│   ├── ECMAScript.Pinia.Test/       # Pinia binding tests
│   ├── ECMAScript.Vue3/             # Vue 3 runtime binding surface
│   ├── ECMAScript.VueRoute/         # Vue Router runtime binding surface
│   ├── ECMAScript.VueRoute.Test/    # Vue Router binding tests
│   ├── ECMAScript.Vuetify/          # Vuetify 3 production-grade component bindings
│   ├── Jazor.Compiler/              # C# → JS compiler core
│   ├── Jazor.Analyzer/              # Static analyzer (whitelist validation)
│   ├── Jazor.CLR/                   # CLR runtime module support
│   ├── Jazor.Emit/                  # Emit pipeline and bundle materialisation
│   ├── Jazor.Common/                # Shared contracts and utilities
│   ├── Jazor/                       # NuGet package (bundles everything above)
│   ├── Jolt/                        # [Long-term] Dev toolchain
│   ├── Wiki/                        # Docs site built with Jazor
│   └── samples/                     # Working host/consumer samples
├── docs/                            # Documentation hub
└── scripts/                         # Build and tooling scripts

Documentation

Audience Entry
New visitors Docs Hub — project overview and navigation
Maintainers Workstream Dashboard — resume work entry point
Architecture Compiler Architecture · ECMAScript.Vue3 Design · ECMAScript.Pinia Design · ECMAScript.VueRoute Design

Docs are organized into five categories: Goals · Plans · Completed · Supplements · Retired


Development

Prerequisites

  • .NET 11 SDK (preview)
  • PowerShell 7+ (for test scripts)
  • Windows, Linux, or macOS

Build Steps

git clone https://github.com/devhxj/Jazor.git
cd Jazor
dotnet restore
dotnet build

# Run all tests
dotnet run --file ./scripts/csharp/test-dotnet.cs

# Run compiler tests only
dotnet run --file ./scripts/csharp/test-dotnet.cs -- --project compiler

# Run Pinia binding tests only
dotnet run --file ./scripts/csharp/test-dotnet.cs -- --project pinia

# Run Vue Router binding tests only
dotnet run --file ./scripts/csharp/test-dotnet.cs -- --project vueroute

# Run a single test class
dotnet test src/Jazor.CompilerTest/Jazor.CompilerTest.csproj --filter "SemanticWalkerPatternTest"

Contributing

Community contributions are welcome. Please review the repository documentation and follow the conventions described in the codebase before submitting a Pull Request.

License

This project is licensed under the MIT License. See LICENSE.txt for details.

Acknowledgements


Security Policy

If you discover a security vulnerability, please report it privately via GitHub Security Advisories. Do not file public issues for security concerns.

Feedback