Tsonic GitHub
Edit on GitHub

Getting Started

Requirements

  • Node.js 22+
  • .NET 10 SDK

Install

npm install -g tsonic

Create a default CLR workspace

mkdir hello-clr
cd hello-clr
tsonic init
tsonic run

Generated sample:

import { Console } from "@tsonic/dotnet/System.js";

export function main(): void {
  Console.WriteLine("Hello from Tsonic.");
}

This is the simplest possible CLR-first workspace:

  • default surface is clr
  • CLR APIs are imported explicitly
  • the default generated project is an executable

Create a JS workspace

mkdir hello-js
cd hello-js
tsonic init --surface @tsonic/js
tsonic run
export function main(): void {
  const value = JSON.parse<{ x: number }>('{"x": 1}');
  console.log(JSON.stringify(value));
}

This switches the workspace ambient world to @tsonic/js.

Add Node modules

tsonic add npm @tsonic/nodejs
import * as fs from "node:fs";
import * as path from "node:path";

export function main(): void {
  const file = path.join("src", "App.ts");
  console.log(file, fs.existsSync(file));
}

This is the current package model in action:

  • ambient world from @tsonic/js
  • Node-style modules from @tsonic/nodejs

Add CLR packages

tsonic add nuget Microsoft.Extensions.Logging 10.0.0
tsonic restore

Then import the generated binding package:

import { ILogger_1 } from "@tsonic/microsoft-extensions/Microsoft.Extensions.Logging.js";

First-party source packages

tsonic init now produces source-package-ready projects by default. Each project gets a tsonic.package.json manifest.

Example:

{
  "schemaVersion": 1,
  "kind": "tsonic-source-package",
  "surfaces": ["@tsonic/js"],
  "source": {
    "exports": {
      ".": "./src/App.ts",
      "./index.js": "./src/App.ts"
    }
  }
}

Installed source packages with that manifest are compiled transitively as part of the same Tsonic program.