Getting Started
Requirements
- Node.js 22+
- .NET 10 SDK
- NativeAOT platform toolchain:
- Linux:
clangorgcconPATH - macOS: Xcode Command Line Tools
- Windows: Visual Studio C++ build tools
- Linux:
Source checkout for contributors
For Tsonic compiler work, the supported developer layout is a sibling checkout under one parent directory:
~/repos/tsoniclang/
tsonic/
runtime/
core/
dotnet/
globals/
js/
nodejs/
aspnetcore/
efcore/
efcore-sqlite/
microsoft-extensions/
runtime is the only required sibling for the compiler's runtime DLL sync:
cd ~/repos/tsoniclang/runtime
dotnet build -c Release
cd ../tsonic
npm ci
npm run build
./test/scripts/run-all.sh
The full compiler gate builds NativeAOT outputs. If the platform linker is missing, the gate fails the NativeAOT preflight with the linker prerequisite instead of reporting compiler correctness failures.
The other siblings are used by tests and local package-wave development when
present. Resolution is deterministic: a sibling package is used only if its
expected package.json exists; otherwise installed npm packages are used.
The repo-local tsonic command is the workspace package in npm/tsonic, which
forwards to @tsonic/cli. There should not be another root workspace package
named tsonic.
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 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 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.