pauldeen79.TemplateFramework.Console 2.0.1

dotnet tool install --global pauldeen79.TemplateFramework.Console --version 2.0.1                
This package contains a .NET tool you can call from the shell/command line.
dotnet new tool-manifest # if you are setting up this repo
dotnet tool install --local pauldeen79.TemplateFramework.Console --version 2.0.1                
This package contains a .NET tool you can call from the shell/command line.
#tool dotnet:?package=pauldeen79.TemplateFramework.Console&version=2.0.1                
nuke :add-package pauldeen79.TemplateFramework.Console --version 2.0.1                

TemplateFramework

Template and code generation framework for C#

If you want to create templates in any .NET language (C#, VB.Net, F#) and run them using a dotnet global tool, this framework is for you!

We currently target .NET 9.0, but the code can easily be ported back to older .NET versions.

Difference between a template and code generation provider

A code generation provider is a class that provides a template instance, along with optional model and additional parameters. This is typically the level you want to use, if you want to scaffold multiple files using a single command.

If you want to use the template abstraction level, then you have to make sure the template class has a public parameterless constructor.

Features

  • Runs templates or code generation providers from .NET assemblies from command line
  • Supports generating single or multiple files from one template
  • Supports custom hosting of the template engine or code generation engine, if you want to
  • Writes output to either console, clipboard or file system
  • Supports child templates, if you want to split your template into multiple logical templates
  • Battle tested
  • Fully async
  • Extensible using dependency injection (write new implementations, and register them in your DI container)

Packages

  • TemplateFramework.Abstractions: Interfaces for templates, code generation providers and generation environments
  • TemplateFramework.Core: Template engine and code generation engine, and all needed implementations for abstractions
  • TemplateFramework.Console: Dotnet tool that can be launched from command line (using tf command)
  • TemplateFramework.Runtime: Run-time infrastructure, to load assemblies
  • TemplateFramework.TemplateProviders.ChildTemplateProvider: Adds support for child templates
  • TemplateFramework.TemplateProviders.CompiledTemplateProvider: Adds support for compiled templates
  • TemplateFramework.TemplateProviders.StringTemplateProvider: Adds support for text-based templates with formattable strings or expression strings

How to create a template

You have to write a class in a .NET 8.0 project (class library project is good enough), and compile this project. Then you can either use the command line tool 'tf' (Template Framework) or write your own host and reference the Core and TemplateProviders.CompiledTemplateProvider packages.

There are multiple types of templates supported out of the box:

  • StringBuilder template, which appends to a single output using a StringBuilder which is passed as an argument
  • Text Transform template, which has one method with a return type of string, that is called to run the template
  • Multiple Content Builder template, which allows to create more output files
  • POCO template. If the class is not of a supported type, then the ToString method will be called on the template instance

Important: If you are not using a POCO template, make sure you reference the same package version of TemplateFramework.Abstractions as the host! So if you install version x.y of TemplateFramework.Console, then also reference version x.y of the TemplateFramework.Abstractions package from your template or code generation assembly.

To create a StringBuilder template, implement this interface from the TemplateFramework.Abstractions package:

public interface IStringBuilderTemplate
{
    Task Render(StringBuilder builder, CancellationToken cancellationToken);
}

To create a Text Transform template, implement this interface from the TemplateFramework.Abstractions package:

public interface ITextTransformTemplate
{
    Task<string> TransformText(CancellationToken cancellationToken);
}

To create a Multiple Content Builder template, implement this interface from the TemplateFramework.Abstractions package:

public interface IMultipleContentBuilderTemplate
{
    Task Render(IMultipleContentBuilder builder, CancellationToken cancellationToken);
}

How to add package references to your template assembly, when using the (Console) command tool

The template assembly is loaded by the command tool. If you want to add external references to your template assembly, then you have to take some additional steps. There are more options to choose from.

The first option is to write a custom host. Add the references to the Core and TemplateProviders.CompiledTemplateProvider packages.

The second option is to add the following property to your template assembly, so your build output directory contains all referenced assemblies from package references:

  <PropertyGroup>
    ...
    <CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
    ...
  </PropertyGroup>

The third option is to publish your template assembly, and use the publishing output directory.

Note that the following assemblies will be loaded from the host (Console) command tool, so make sure you use the same versions referenced from there:

  • TemplateFramework.Abstractions
  • TemplateFramework.Console
  • TemplateFramework.Core
  • TemplateFramework.Core.CodeGeneration
  • TemplateFramework.Runtime
  • TemplateFramework.TemplateProviders.ChildTemplateProvider
  • TemplateFramework.TemplateProviders.CompiledTemplateProvider
  • TemplateFramework.TemplateProviders.StringTemplateProvider
  • CrossCutting.Common (3.13.0)
  • CrossCutting.Utilities.Parsers (6.2.0)
  • Microsoft.Extensions.DependencyInjection (9.0.0)
  • Microsoft.Extensions.DependencyInjection.Abstractions (9.0.0)

Right now, the all TemplateFramework assemblies are built in one build pipeline within one GitHub repository, so all version numbers of the TemplateFramework assemblies are the same. This means, that if you install version x.y of TemplateFramework.Console, then your template assemblies should also use version x.y of TemplateFramework package references. (most likely TemplateFramework.Abstractions)

How to call child templates from your main template

If you want to render child templates from your main (root) template, then you have to implement this interfaces from the TemplateFramework.Abstractions package: ITemplateContextContainer.

public interface ITemplateContextContainer
{
    ITemplateContext Context { get; set; }
}

Then, in your template, call the Render method on the TemplateEngine instance. (Engine property of the Context) As context, create a child context using the CreateChildContext method on the TemplateContext instance.

There is also an integration test in the TemplateFramework.TemplateProviders.ChildTemplateProvider test project to demonstrate this.

How to register child templates to be used from a template

In order to register child templates, so that they can be resolved from the (root) template that's being rendered, you have to create a class that implements the following interface, from the TemplateFramework.Abstractions package:

public interface ITemplateProviderPlugin
{
    Task Initialize(ITemplateProvider provider, CancellationToken cancellationToken);
}

Then, from the command line, you have to specify the class name of this class, using the --templateproviderplugin or -t argument. Note that the current version expects this class to be in the same assembly as the template assembly.

How to register child templates to be used from a code generation provider

If you use one or more code generation providers, then each code generation provider (ICodeGenerationProvider implementation) also can implement this ITemplateProviderPlugin interface, to register additional child templates. Note that if you don't supply a filter on the command line, then all code generation providers will be checked for this interace. If you have conflicting child template names or model types within the same assembly, you have to use a filter to run just one code generation provider instead of all types from the assembly.

How to register custom placeholder processors or function result parsers

If you are using text-based templates, you can register custom components to process placeholders or function results.

FormattableStringTemplates: CrossCutting.Utilities.Parsers.Contracts.IPlaceholderProcessor (from CrossCutting.Utilities.Parsers package) ExpressionStringTemplate: CrossCutting.Utilities.Parsers.Contracts.IPlaceholderProcessor (from CrossCutting.Utilities.Parsers package)

To register this dynamically, you need to create a class that implements this interface, from TemplateFramework.Abstractions:

public interface ITemplateComponentRegistryPlugin
{
    Task Initialize(ITemplateComponentRegistry registry, CancellationToken cancellationToken);
}

Create a constructor to get the ComponentRegistrationContext instance. Then, in the Initialize method, register instances.

public sealed class MyTemplateComponentRegistryPlugin : ITemplateComponentRegistryPlugin
{
    public ComponentRegistrationContext ComponentRegistrationContext { get; }

    public TestTemplateComponentRegistryPlugin(ComponentRegistrationContext componentRegistrationContext)
    {
        Guard.IsNotNull(componentRegistrationContext);

        ComponentRegistrationContext = componentRegistrationContext;
    }

    public Task Initialize(ITemplateComponentRegistry registry, CancellationToken cancellationToken)
    {
        var processorProcessor = new MyPlaceholderProcessor();
        var functionResultParser = new MyFunctionResultParser();

        ComponentRegistrationContext.PlaceholderProcessors.Add(processorProcessor);
        ComponentRegistrationContext.FunctionResultParsers.Add(functionResultParser);

        return Task.CompletedTask;
    }
}

In this example, the MyPlaceholderProcessor and MyFunctionResultParser classes are the implementations that you need to provide. If you need additional dependencies, you need to add those to the constructor or your TemplateComponentRegistryPlugin class, and then use them on construction of your placeholder processors or function result parsers.

Finally, on the command line, use the assembly name and class name (and probably also the directory name where the assembly is stored) to this class. Probably something like this:

tf template --formattablestring template.txt --dryrun --default myfile.txt --interactive --templateproviderplugin MyAssembly.MyTemplateComponentRegistryPlugin --assembly MyAssembly --directory D:\\somewhere\\MyAssembly\\bin\\debug\\net8.0

There is also an example in launchSettings.json of the TemplateFramework.Console project, that uses a template provider plug-in of a unit test project.

Upgrading from 1.x to 2.0

In version 2.0, there are three breaking changes:

//ICodeGenerationProvider:
Task<object?> CreateAdditionalParameters();
Task<object?> CreateModel();

//ISessionAwareComponent:
Task StartSession(CancellationToken cancellationToken);

has changed to

//ICodeGenerationProvider:
Task<Result<object?>> CreateAdditionalParameters(CancellationToken cancellationToken);
Task<Result<object?>> CreateModel(CancellationToken cancellationToken);

//ISessionAwareComponent:
Task<Result> StartSession(CancellationToken cancellationToken);

This enables you to return error messages from model creation, instead of throwing exceptions. And while we are already breaking compatibility, the CancellationToken argument is also added to CreateAdditionalParameters and CreateModel.

Product Compatible and additional computed target framework versions.
.NET net9.0 is compatible. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

This package has no dependencies.

Version Downloads Last updated
2.0.1 40 12/27/2024
2.0.0 82 12/20/2024
1.1.2 82 12/18/2024
1.1.1 231 11/24/2024
1.1.0 91 11/17/2024
1.0.3 99 10/30/2024
1.0.2 235 10/18/2024
1.0.1 96 9/21/2024
1.0.0 124 9/16/2024
0.11.1 351 5/18/2024
0.11.0 121 5/17/2024
0.10.2 271 5/9/2024
0.10.1 81 5/3/2024
0.10.0 118 4/19/2024
0.9.3 191 4/3/2024
0.9.2 290 3/22/2024
0.9.1 299 3/15/2024
0.9.0 452 3/3/2024
0.8.7 1,608 1/5/2024
0.8.6 882 12/7/2023
0.8.5 442 11/29/2023
0.8.4 240 11/27/2023
0.8.3 195 11/27/2023
0.8.1 234 11/24/2023
0.8.0 309 11/19/2023
0.7.4 354 9/13/2023
0.7.3 332 9/10/2023
0.7.1 349 9/9/2023
0.7.0 301 9/8/2023
0.6.0 411 9/7/2023
0.5.1 273 9/4/2023
0.5.0 345 9/4/2023
0.4.0 349 8/23/2023
0.3.2 361 8/14/2023
0.3.1 403 8/13/2023
0.3.0 355 8/13/2023
0.2.0 332 8/11/2023
0.1.0 258 7/23/2023