Waffle.Bakery 1.0.1

dotnet add package Waffle.Bakery --version 1.0.1
                    
NuGet\Install-Package Waffle.Bakery -Version 1.0.1
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Waffle.Bakery" Version="1.0.1" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Waffle.Bakery" Version="1.0.1" />
                    
Directory.Packages.props
<PackageReference Include="Waffle.Bakery" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add Waffle.Bakery --version 1.0.1
                    
#r "nuget: Waffle.Bakery, 1.0.1"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package Waffle.Bakery@1.0.1
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=Waffle.Bakery&version=1.0.1
                    
Install as a Cake Addin
#tool nuget:?package=Waffle.Bakery&version=1.0.1
                    
Install as a Cake Tool

Waffle.Bakery

A batch-execution framework for running multiple Waffle templates and collecting their outputs. It handles the orchestration of template registration, execution, and result retrieval so you can focus on writing templates.

Quick Start

Add the NuGet packages to your project:

<ItemGroup>
    <PackageReference Include="Waffle.Core" Version="1.x"/>
    <PackageReference Include="Waffle.Bakery" Version="1.x"/>
</ItemGroup>

Define Templates

Create classes that inherit from DefaultTemplate. Each template declares a unique OutputId and implements ProcessImpl:

using Waffle;
using static Waffle.WaffleSyntax;

public sealed class VectorTemplate : DefaultTemplate
{
    protected override string OutputId => "vectors";
    protected override void ProcessImpl(DefaultBakeryContext ctx)
    {
        const int N = 10;
        Render(ctx, $"""
            {For(2, N + 1, out var i)}
            public readonly record struct Vector{i}(
                {For(0, i, out var k, out var h)}
                float Value{k + 1}{h.LastOrNot(");", ",")}
                {End}
            {End}
            """);
    }
}

public sealed class HelperTemplate : DefaultTemplate
{
    protected override string OutputId => "helpers";
    protected override void ProcessImpl(DefaultBakeryContext ctx)
    {
        const int N = 10;
        Render(ctx, $$"""
            public static class VectorMath
            {
                {{For(2, N + 1, out var i)}}
                public static float Dot(in Vector{{i}} a, in Vector{{i}} b)
                    => {{For(0, i, out var k, out var h):>>}}
                    a.Value{{k + 1}} * b.Value{{k + 1}}{{h.LastOrNot(";\n", " + "):>>}}
                    {{End}}
                {{End}}
            }
            """);
    }
}

Run the Bakery

Create a DefaultBakery, register templates, execute them, and retrieve results:

using Waffle;

var results = new DefaultBakery()
    .Initialize(new DefaultBakeryContext())
    .Register<VectorTemplate>()
    .Register<HelperTemplate>()
    .Run()
    .GetResults();

// results is a Dictionary<string, string> keyed by OutputId
Console.WriteLine(results["vectors"]);
Console.WriteLine(results["helpers"]);

Architecture

Core Interfaces

Interface Role
IBakery<T, TContext> Orchestrates template registration and batch execution.
IBakeryContext Extends ITemplateInterpreterContext with named output destinations (Open/Close/Clear).
ITemplate<TContext> Defines a single template's execution logic.

Class Hierarchy

IBakery<T, TContext>
└── BakeryBase<T, TContext>        # Shared registration/run logic
    └── DefaultBakery              # Ready-to-use concrete bakery

IBakeryContext
└── BakeryContextBase              # Output buffering, error collection, lifecycle hooks
    └── DefaultBakeryContext       # No-op concrete context (sufficient for most use cases)

ITemplate<TContext>
└── SingleOutputTemplate<TContext> # Opens/closes a single OutputId automatically
    └── DefaultTemplate            # Shorthand for SingleOutputTemplate<DefaultBakeryContext>

Execution Flow

  1. Initializebakery.Initialize(ctx) stores the context and calls OnConfigure.
  2. Register — Templates are added to an internal list.
  3. Run — Iterates through registered templates and calls Process on each.
  4. CollectGetResults() returns a Dictionary<string, string> mapping each OutputId to its rendered content.

Within each SingleOutputTemplate, Process automatically calls ctx.Open(OutputId) before your code and ctx.Close() after, so you only need to implement ProcessImpl.

Template Registration

Manual registration

bakery.Register<MyTemplate>();          // instantiates via parameterless constructor
bakery.Register(new MyTemplate());      // pass a pre-built instance

Registration with a modifier

bakery.Register<MyTemplate>(t => t.SomeProperty = value);

Attribute-based auto-discovery

Scan an assembly for all template classes marked with a specific attribute:

bakery.RegisterAllByAttribute<GenerateAttribute>(typeof(Program).Assembly);

Optionally filter by attribute properties:

bakery.RegisterAllByAttribute<GenerateAttribute>(
    typeof(Program).Assembly,
    attr => attr.Enabled);

Customization

Custom Context

Subclass BakeryContextBase to add shared state, logging, or transformation hooks:

public class MyContext : BakeryContextBase
{
    public string RootNamespace { get; set; } = "MyApp";

    protected override void OnPostOpen(string outputId, bool isNew)
    {
        if (isNew) Console.WriteLine($"Generating: {outputId}");
    }
}

Available hooks in BakeryContextBase:

Hook Timing
OnPreOpen / OnPostOpen Before/after an output destination is opened
OnPreAppend / OnPostAppend Before/after content is written
OnPreClose / OnPostClose Before/after an output destination is closed
OnPreAppendLiteral / OnPostAppendLiteral Before/after a literal string segment is appended
OnPreAppendFormatted / OnPostAppendFormatted Before/after an interpolated expression is appended
OnCleared After all output is discarded

Custom Bakery

Subclass BakeryBase<T, TContext> to add project-specific orchestration:

public class MyBakery : BakeryBase<MyBakery, MyContext>
{
    protected override void OnConfigure(MyContext ctx)
    {
        // Called once during Initialize — set up shared resources here
    }
}

Template Lifecycle Hooks

SingleOutputTemplate<TContext> provides OnPreProcess and OnPostProcess hooks:

public abstract class MyTemplateBase : SingleOutputTemplate<MyContext>
{
    protected override void OnPreProcess(MyContext ctx)
    {
        // e.g., emit a file header
        Render(ctx, $"// Auto-generated — do not edit\n");
    }
}

Error Handling

If ProcessImpl throws an exception, SingleOutputTemplate catches it and records a TemplateError on the context. After execution, retrieve errors with:

var errors = bakery.GetErrors(); // Dictionary<string, IReadOnlyList<TemplateError>>
foreach (var (outputId, errs) in errors)
{
    foreach (var e in errs)
        Console.Error.WriteLine($"[{outputId}] {e.Message}");
}

Dependencies

Product Compatible and additional computed target framework versions.
.NET net5.0 was computed.  net5.0-windows was computed.  net6.0 was computed.  net6.0-android was computed.  net6.0-ios was computed.  net6.0-maccatalyst was computed.  net6.0-macos was computed.  net6.0-tvos was computed.  net6.0-windows was computed.  net7.0 was computed.  net7.0-android was computed.  net7.0-ios was computed.  net7.0-maccatalyst was computed.  net7.0-macos was computed.  net7.0-tvos was computed.  net7.0-windows was computed.  net8.0 was computed.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 was computed.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 was computed.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
.NET Core netcoreapp2.0 was computed.  netcoreapp2.1 was computed.  netcoreapp2.2 was computed.  netcoreapp3.0 was computed.  netcoreapp3.1 was computed. 
.NET Standard netstandard2.0 is compatible.  netstandard2.1 was computed. 
.NET Framework net461 was computed.  net462 was computed.  net463 was computed.  net47 was computed.  net471 was computed.  net472 was computed.  net48 was computed.  net481 was computed. 
MonoAndroid monoandroid was computed. 
MonoMac monomac was computed. 
MonoTouch monotouch was computed. 
Tizen tizen40 was computed.  tizen60 was computed. 
Xamarin.iOS xamarinios was computed. 
Xamarin.Mac xamarinmac was computed. 
Xamarin.TVOS xamarintvos was computed. 
Xamarin.WatchOS xamarinwatchos was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
1.0.1 144 6/10/2026
1.0.0 119 5/27/2026