English | 中文
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.
- 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
IOperationlowering — semantic-driven compilation viaIOperation→ ECMAScript AST, not syntax-level translation - Static analysis safety —
Jazor.Analyzerenforces 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 throughJazor.CLR - ECMAScript module emission —
[ECMAScriptModule]annotated classes compile to.mjsfiles with automatic cross-module import resolution and source maps - Vue 3 integration —
ECMAScript.Vue3provides typed C# bindings for Vue 3's Composition API andh()render function, enabling component authoring in pure C# - Pinia integration —
ECMAScript.Piniaprovides typed C# bindings for Pinia root/store authoring, includingcreatePinia(),defineStore(),storeToRefs(), and common Options API helpers such asmapState()/mapActions() - Vue Router integration —
ECMAScript.VueRouteprovides typed C# bindings for Vue Router 4 authoring, includingcreateRouter(), history creators,useRouter(),useRoute(),RouterLink, andRouterView - Vuetify integration —
ECMAScript.Vuetifyprovides 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
| Tier | Component | Status |
|---|---|---|
| Working | Compiler core (SemanticWalker, AstConverter) — 1,891 tests | Stable — the most mature part |
| Working | ECMAScript module emission ([ECMAScriptModule] → .mjs) — 111 tests | Stable |
| Working | ECMAScript.Vue3 bindings (h, ref, reactive, lifecycle, createApp) | Stable |
| Working | MSBuild integration (JazorEmit, JazorBundle, JazorOutDir) | Stable |
| Working | Jazor.Analyzer (whitelist compile-time validation) | Stable |
| Working | ECMAScript.Vuetify bindings (117 components, typed props/slots/events) — 117 components | Stable — production-grade Vuetify 3 authoring |
| Working | CLR runtime modules — 70 tests | Stable |
| In progress | RazorVue (SFC pipeline, canonical h() model, Vuetify production coverage) — 772 tests | Production-grade Vuetify authoring, pipeline validation, slot/fallthrough support |
| In progress | ECMAScript.Pinia + ECMAScript.VueRoute — 205 tests | Expanding API coverage, Pinia Testing utilities |
| In progress | SourceMap | Narrow lane — module-level .mjs.map, not full coverage yet |
| In progress | Deno bundling | JazorBundle target works for basic cases |
| In progress | Debugging | Design and milestone code exist, not user-facing yet |
| Long-term | Jolt (LSP, HMR, DevServer, debug, build) — 778 tests | Phase 1–6 closing, Design |
Users today should target the Working tier. 32 projects · ~290K production LoC · ~190K test LoC · ~3,850+ total tests.
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.
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.
| 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. |
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.
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")
]);
}
});
}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("npm:vue@3")]— declares a runtime import dependency. The compiler generatesimport { ... } 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.
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
| 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
- .NET 11 SDK (preview)
- PowerShell 7+ (for test scripts)
- Windows, Linux, or macOS
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"Community contributions are welcome. Please review the repository documentation and follow the conventions described in the codebase before submitting a Pull Request.
This project is licensed under the MIT License. See LICENSE.txt for details.
- Roslyn — C# compiler platform
- Acornima — JavaScript parser and AST library
- WebRef — Web specification references
- WootzJs · h5 · SharpKit — C# to JavaScript compilers
- DenoHost — Deno runtime host for .NET
- CSharpToJavaScript — C# to JavaScript transpiler
If you discover a security vulnerability, please report it privately via GitHub Security Advisories. Do not file public issues for security concerns.