Skip to content

Setup

Each Client Builder module (e.g., WebApiModule, TranslationsModule) inherits from the base ScaffoldModule. A module’s job is to describe:

  • What folders to create
  • What files to generate
  • What data to inject into templates

The heart of every module is the SetupAsync method. This method runs when the Client Builder is executed (typically triggered from the /_cb UI).

What does SetupAsync do?

SetupAsync defines the exact files and folders the module should scaffold. Inside SetupAsync, you:

  1. Fetch any metadata your files need (e.g., controllers, classes, enums, translations).
  2. Define one or more folders using AddFolder.
  3. Define one or more files using AddFile and connect each file to a template.
  4. Supply context data for the templates — this is what makes your files dynamic.

Example 1: WebApiModule

What it does:

  • Fetches all MVC controllers and actions.

  • Extracts related classes and enums.

  • Generates:

    • services.ts for calling the API
    • enums.ts for enum definitions
    • types.ts for strongly-typed DTOs and contracts

How it works:

csharp
public override async Task SetupAsync()
{
    // Gather metadata using extractors
    var controllerActions = mvcDescriptionExtractor.FetchControllerActions();
    var classes = mvcDescriptionExtractor.FetchActionsClasses(controllerActions).ToList();
    var enums = descriptionExtractor.ExtractUniqueEnumsFromClasses(classes).ToList();

    // Add a folder where files will be created
    var rootFolder = Path.Combine("ClientApp", "src");
    this.AddFolder(new ScaffoldModuleFolder
    {
        Name = "api",
        RelativePath = rootFolder,
    });

    // Add each file, attach its template, and pass the context data
    this.AddFile(new ScaffoldModuleFile
    {
        Name = "services.ts",
        RelativePath = Path.Combine(rootFolder, "api"),
        Template = new HandlebarsFileTemplate("..."),
        ContextData = new { classes, serviceContexts },
    });

    this.AddFile(new ScaffoldModuleFile
    {
        Name = "enums.ts",
        RelativePath = Path.Combine(rootFolder, "api"),
        Template = new HandlebarsFileTemplate("..."),
        ContextData = new { enums, enumsHelpers },
    });

    this.AddFile(new ScaffoldModuleFile
    {
        Name = "types.ts",
        RelativePath = Path.Combine(rootFolder, "api"),
        Template = new HandlebarsFileTemplate("..."),
        ContextData = new { enums, classes },
    });
}

Result: When you trigger the module, these .ts files are rendered using the Handlebars templates, filled with real data about your API.

Example 2: TranslationsModule

What it does:

  • Loads all translations from a .resx resource file.
  • Exports them to a single translations.json in your client app’s config folder.

How it works:

csharp
public override Task SetupAsync()
{
    // Load all translations from resource files
    var translations = new Dictionary<string, string>();
    var resourceSet = T.ResourceManager.GetResourceSet(CultureInfo.CurrentUICulture, true, true);
    foreach (var entry in resourceSet)
    {
        var dictionaryEntry = (DictionaryEntry)entry;
        translations[dictionaryEntry.Key.ToString()] = dictionaryEntry.Value?.ToString();
    }

    // Add the JSON file
    this.AddFile(new ScaffoldModuleFile
    {
        Name = "translations.json",
        RelativePath = Path.Combine("ClientApp", "src", "config"),
        Template = new JsonFileTemplate(),
        ContextData = translations,
    });

    return Task.CompletedTask;
}

Result: When you trigger this module, a fresh translations.json with all your keys/values appears in your front-end project.

Key building blocks

MethodWhat it does
AddFolderDeclares a folder where generated files will live.
AddFileDeclares a file and connects it to a template + data context.
TemplateDefines how the file will be rendered (Handlebars, JSON, etc.).
ContextDataThe data model passed to the template for rendering dynamic content.

Typical pattern for your own modules

Every new module you create will usually follow this pattern:

csharp
public override async Task SetupAsync()
{
    // Gather metadata
    var data = GetSomeData();

    // Add folders if needed
    this.AddFolder(new ScaffoldModuleFolder { Name = "...", RelativePath = "..." });

    // Add files and link them to templates
    this.AddFile(new ScaffoldModuleFile
    {
        Name = "...",
        RelativePath = "...",
        Template = new MyTemplate(),
        ContextData = new { ... },
    });
}

Released under the MIT License.