App Initialization

Just like in a .NET console application, a Transpose application's entry point is its static Main method. Transpose emits the entry point into the generated bundle and the runtime invokes it once the page is ready.

The Main method

Define a class with a static Main:

using System;
using Transpose;

namespace MyProject
{
    public class Program
    {
        public static void Main()
        {
            Console.WriteLine("Hello from Transpose!");
        }
    }
}

Roslyn picks the entry point the same way it does for a console app, so Main(string[] args), Main(), and the async Task Main forms all work. If the entry point returns a Task, the runtime awaits it and surfaces a faulted result rather than silently dropping it.

Transpose emits the entry point as the type's main member; the runtime registers it to run when the document is ready — immediately if the document has already finished loading, which is what makes a lazily fetched bundle start on load too. Static initializers of the types in the bundle run before it.

The [Ready] attribute

[Ready] marks a static method to run when the DOM is ready. Transpose emits a registration for each one, after every type in the bundle has been defined, so the method can safely touch other types.

using System;
using Transpose;

namespace MyProject
{
    public class App
    {
        [Ready]
        public static void OnReady()
        {
            // runs on DOMContentLoaded, or immediately if the document is already loaded
            Console.WriteLine("DOM is ready!");
        }
    }
}

You can have several [Ready] methods.

Don't depend on the order

Main and every [Ready] method are scheduled through the same runtime hook, so their relative order is an implementation detail. Put anything order-sensitive in one place — call the dependent steps from Main explicitly — rather than splitting it across several [Ready] methods.

Running code before Main

The h5 [Init] attribute, which emitted a static method's body at a chosen position in the file, is not implemented in Transpose. Use the standard .NET [ModuleInitializer] attribute instead: Transpose collects those methods and calls them at the top of the entry point, before the Main body runs.

using System;
using System.Runtime.CompilerServices;

namespace MyProject
{
    public static class Config
    {
        [ModuleInitializer]
        internal static void Initialize()
        {
            // runs before the Main body
            Console.WriteLine("Initializing configuration…");
        }
    }
}

Note that unlike on .NET, where a module initializer runs at module load, Transpose sequences these calls immediately ahead of Main — so they still run before your application logic, but not before the bundle's type definitions.

A static constructor on a type is the other option, and it behaves as you would expect: the runtime runs a type's static initializer before the type is first used.

Execution order:

  1. Type definitions are registered and static initializers run.
  2. [ModuleInitializer] methods, then the Main body — both once the document is ready.
  3. [Ready] methods, also once the document is ready (order relative to Main unspecified).

Controlling the generated page

The generated index.html is what loads the runtime and your bundle and thereby triggers the sequence above. Configure it through the html section of tps.json:

tps.json
{
  "output": "$(OutDir)tps/",
  "html": {
    "disabled": false,
    "title": "My Transpose App",
    "body": "<div id=\"root\"></div>"
  }
}

Set "disabled": true when the page is supplied by something else — a library, or an application whose HTML is served by a back-end. You then load tps.js and your bundle with your own <script> tags, in that order, and the same initialization sequence runs.

© 2026 Curiosity. All rights reserved.