Mal.OnyxTemplate 1.0.0-alpha

This is a prerelease version of Mal.OnyxTemplate.
There is a newer version of this package available.
See the version list below for details.
dotnet add package Mal.OnyxTemplate --version 1.0.0-alpha                
NuGet\Install-Package Mal.OnyxTemplate -Version 1.0.0-alpha                
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="Mal.OnyxTemplate" Version="1.0.0-alpha" />                
For projects that support PackageReference, copy this XML node into the project file to reference the package.
paket add Mal.OnyxTemplate --version 1.0.0-alpha                
#r "nuget: Mal.OnyxTemplate, 1.0.0-alpha"                
#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.
// Install Mal.OnyxTemplate as a Cake Addin
#addin nuget:?package=Mal.OnyxTemplate&version=1.0.0-alpha&prerelease

// Install Mal.OnyxTemplate as a Cake Tool
#tool nuget:?package=Mal.OnyxTemplate&version=1.0.0-alpha&prerelease                

Onyx Template

Onyx Template is a very simple Source Generator powered runtime text template system.

  • Note: Only really tested with modern project format + modern nuget. No guarantees of
    proper function in old .NET Framework projects etc.

Usage:

  1. Add the nuget package.
  2. Add a file with an .onyx extension.
    (It should be added automatically as AdditionalFiles. If not - change it to that).

The macros are fairly simple.

{{ YourMacroName }}

Let's create our first .onyx file. Let's call it WelcomeText.onyx.

Hello, {{ UserName }}, welcome to the show.

The source generator will have generated a base class for you to derive from. Create a new .cs file in the same folder, named WelcomeText.onyx.cs (or whatever you want, it's just useful to group them together).

class WelcomeText: WelcomeTextBase
{
    protected override string GetUserName()
    {
        return "Batman";
    }
}

Every macro you make in your .onyx file will get its own Get method you can override to provide the replacement string for that macro.


... But I need it to be public / $template

There is a special configuration macro you can add to the very top of your .onyx file to configure it. Let's mke our WelcomeText macro generate a public base class instead of the default internal.

{{ $template public }}
Hello, {{ UserName }}, welcome to the show.

That's all it takes.


Repeating data / $foreach

To generate lists of data you need to utilize the special $foreach macro:

Animals:
{{ $foreach animal in animals }}   
   {{ animal }}
{{ $next }}

Providing we give it the following data provider:

class AnimalList: AnimalListBase
{
    protected override IEnumerable<string> GetAnimals()
    {
        yield return "Dog";
        yield return "Cat";
        yield return "Tiger";
        yield return "Bear";
    }
}

The result will be

Animals:
   Dog
   Cat
   Tiger
   Bear

Dealing with multiline macros

But what if the animal macro returned multiple lines? Let's try it:

class AnimalList: AnimalListBase
{
    protected override IEnumerable<string> GetAnimals()
    {
        yield return "Dog\n- Canidae";
        yield return "Cat\n- Felidae";
        yield return "Tiger\n- Felidae";
        yield return "Bear\n- Ursidae";
    }
}

Result:

Animals:
   Dog
- Canidae
   Cat
- Felidae
   Tiger
- Felidae
   Bear
- Ursidae

So yes, that worked, but... it's not exactly what we're after, is it. We'd prefer it if the new lines were aligned with the first macro.

There's two ways we can solve this problem. We can set an indentation flag in the template header

{{ $template indented }}

or we can tell the generator that only this particular use of the macro needs to be indented:

   {{ animal:indent }}

Result:

Animals:
   Dog
   - Canidae
   Cat
   - Felidae
   Tiger
   - Felidae
   Bear
   - Ursidae

That's better!


Complex macros

We could make it even better though. Rather than formatting the whole thing in the data provider itself we could do this:

Animals:

{{ $foreach animal in animals }}   
   Animal Name:   {{ name }}
   Animal Family: {{ type }}

{{ $next }}

Now this is detected to be a complex macro. This means we'll have to change our data provider again:

class AnimalList: AnimalListBase
{
    class Animal : AnimalsItemBase
    {
        readonly string _name;
        readonly string _type;

        public Animal(string name, string type)
        {
            _name = name;
            _type = type;
        }

        public override string GetName() => _name;
        public override string GetType() => _type;
    }
    
    protected override IEnumerable<AnimalsItemBase> GetAnimals()
    {
        yield return new Animal("Dog", "Canidae");
        yield return new Animal("Cat", "Felidae");
        yield return new Animal("Tiger", "Felidae");
        yield return new Animal("Bear", "Felidae");
    }
}

The generator has produced an item base class we can derive from to produce data for the complex macro. So we make an Animal class, which derives from the generated AnimalsItemBase class, and our GetAnimals() class is changed to return a set of those instead of a simple string.

Now, our results are:

Animals:
   
   Animal Name:   Dog
   Animal Family: Canidae
   
   Animal Name:   Cat
   Animal Family: Felidae
   
   Animal Name:   Tiger
   Animal Family: Felidae
   
   Animal Name:   Bear
   Animal Family: Felidae


And that's it

This library was primarily designed 1. to help me get better at making source generators and 2. because I needed something simple to make my other source generators cleaner, and without all the StringBuilder shenanigans all over the place.

Known Issue with Jetbrains Rider

There seems to be some kind of caching going on with Rider which prevents it from detecting changes in the .onyx files now and again. I have yet to find a solution for it. A Rebuild All forces the issue.

Watch your whitespace

If you have whitespace before or after the {{ $template }}, {{ $foreach item in source }} and {{ $next }} macros, you might get newlines you don't want.

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. 
.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.
  • .NETStandard 2.0

    • No dependencies.

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.3.6-alpha 498 8/5/2024
1.3.5-alpha 62 8/5/2024
1.3.4-alpha 67 8/5/2024
1.3.3-alpha 68 8/4/2024
1.3.1-alpha 73 8/4/2024
1.3.0-alpha 63 7/31/2024
1.2.1 4,008 11/19/2023
1.2.1-alpha 1,105 10/10/2023
1.2.0-alpha 97 10/10/2023
1.1.7 142 11/5/2023 1.1.7 is deprecated.
1.1.7-alpha 108 10/9/2023
1.1.6-alpha 97 10/9/2023
1.1.5-alpha 108 10/9/2023
1.1.3-alpha 111 10/9/2023
1.1.2-alpha 96 10/9/2023
1.1.1-alpha 119 9/17/2023
1.1.0-alpha 99 9/17/2023
1.0.0-alpha 110 9/17/2023