Edit source

C# projects and output

Generated project

Without projectFile, Tsonic emits a complete SDK-style project:

out/csharp/
├── TsonicGenerated.csproj
├── src/
│   └── <source-owned C# files>
└── generated/
    └── <compiler-owned helpers and startup>

The generated project always uses Microsoft.NET.Sdk. Choose a user-owned project when another SDK is required.

The assembly name controls the project filename. Generated source paths are stable content/owner identities rather than source basenames that can collide.

{
  "targets": [{
    "id": "csharp",
    "options": {
      "assemblyName": "Example.App",
      "namespace": "Example.Generated",
      "outputType": "Exe",
      "targetFramework": "net10.0",
      "publishAot": true
    }
  }]
}

outputType: "Exe" creates TsonicEntrypoint.Main. It initializes imported modules in ESM order and runs the TypeScript entry module. Put startup work at top level:

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

function run(): void {
  Console.WriteLine("ready");
}

run();

outputType: "Library" emits public native declarations for supported TypeScript exports. Synchronous top-level initialization uses a CLR module initializer. A library with top-level await is rejected because CLR module initializers are synchronous.

References

Generated projects can declare project, NuGet package, framework, and assembly references:

{
  "references": {
    "projects": ["../Shared/Shared.csproj"],
    "packages": [{
      "include": "Microsoft.Extensions.Logging.Abstractions",
      "version": "10.0.0"
    }],
    "frameworks": ["Microsoft.AspNetCore.App"],
    "assemblies": [{
      "include": "Acme.Native",
      "hintPath": "lib/Acme.Native.dll"
    }]
  }
}

providerReferences controls assemblies available to declaration reflection. It does not silently add a build reference. If source both imports an assembly and emitted C# links it, declare the appropriate build reference as well.

User-owned project

{
  "targets": [{
    "id": "csharp",
    "options": {
      "projectFile": "native/Example.csproj",
      "providerReferences": {
        "directories": ["native/bin/Debug/net10.0"]
      }
    }
  }]
}

The .csproj must exist outside generated output. Tsonic emits sources only and does not mutate it. The project owns package restore, framework references, RID, target triple, signing, trimming, NativeAOT, deployment, and inclusion of generated files.

For a project at native/Example.csproj and generated files under out/csharp, a minimal project can be:

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <TargetFramework>net10.0</TargetFramework>
    <Nullable>enable</Nullable>
  </PropertyGroup>
  <ItemGroup>
    <Compile Include="../out/csharp/**/*.cs" LinkBase="Generated" />
  </ItemGroup>
</Project>

Adjust the relative path to match the project layout. Use a user-owned project for a different SDK, such as Microsoft.NET.Sdk.Web, a test SDK, desktop UI, MAUI, or a custom build pipeline.

What each setting controls

NeedPut it here
C# output kind, namespace, nullable mode, language rulesdedicated C# target option
Generated project package, framework, project, or assembly referencereferences
Assembly used only for source declaration reflectionproviderReferences
Open MSBuild scalar such as RuntimeIdentifierproperties
Custom SDK, item graph, build targets, signing, packaginguser-owned .csproj

Dedicated target settings cannot be overridden through properties. This keeps the checked source contract and generated project in agreement.

Native build

Tsonic’s toolchain stage records generated artifacts; the .NET SDK performs the actual compile or publish:

npx --no-install tsonic build -p tsonic.json
dotnet build out/csharp/Example.App.csproj
dotnet publish out/csharp/Example.App.csproj -c Release

In user-owned mode, build the configured project instead:

dotnet build native/Example.csproj