Command-line tool
editMeziantou.Framework.Templating.Tool renders template files without writing code: use it in scripts and builds to generate source code or configuration files.
The command reference lists every option of the tool.
Install the tool
Install it as a global tool:
dotnet tool install --global Meziantou.Framework.Templating.ToolRender a template
--input is the template file. The text is written to the standard output:
Meziantou.Framework.Templating.Tool --input template.txtAdd --output to write it to a file instead. The folder of the file is created if needed:
Meziantou.Framework.Templating.Tool --input template.txt --output generated/result.txtThe template uses the syntax of the library. --root-directory sets the folder that relative input paths are read from; by default, the current folder.
Generate a file next to the template
A template can name the extension of the file it generates. Here, the outputextension directive asks for a .g.cs file:
<%@ outputextension .g.cs %>
// <auto-generated />
namespace Sample;
public static class Weekdays
{
<% foreach (var day in Enum.GetValues<DayOfWeek>()) { %>
public const string <%= day %> = "<%= day.ToString().ToLowerInvariant() %>";
<% } %>
}Meziantou.Framework.Templating.Tool --input Weekdays.templateThe tool writes Weekdays.g.cs in the folder of the template. Its name is the name of the template with the extension replaced:
// <auto-generated />
namespace Sample;
public static class Weekdays
{
public const string Sunday = "sunday";
public const string Monday = "monday";
public const string Tuesday = "tuesday";
public const string Wednesday = "wednesday";
public const string Thursday = "thursday";
public const string Friday = "friday";
public const string Saturday = "saturday";
}The lines that only hold a directive or a statement block leave no empty line; see Line breaks. --output takes precedence over the directive.
Pass arguments
The template receives values through arguments. --arg name=value passes a string; repeat it for each argument. --arguments-file reads the arguments from the properties of a JSON object:
{
"Product": "Contoso",
"Version": 2,
"Features": [ "Search", "Export" ]
}The template uses the arguments as variables:
<%@ outputextension .txt %>
<%= Product %> <%= Version %> (<%= Channel %>)
<% foreach (var feature in Features.EnumerateArray()) { %>
- <%= feature.GetString() %>
<% } %>Meziantou.Framework.Templating.Tool --input Release.template --arguments-file Release.json --arg Channel=previewThe tool writes Release.txt:
Contoso 2 (preview)
- Search
- ExportThe type of an argument comes from its JSON value:
| JSON value | C# type |
|---|---|
| string | string |
true, false | bool |
| integer | long |
| other number | decimal, or double when the value does not fit in a decimal |
null | object |
| object, array | System.Text.Json.JsonElement |
--arg values come after the file: an --arg replaces the property of the same name, and the last --arg of a name wins. A name must be a valid C# identifier and not a C# keyword, because it becomes the name of a parameter. The JSON file can contain comments and trailing commas.
Every template of the run receives every argument, and ignores the ones it does not use.
Render several templates
Repeat --input, or pass a glob pattern, to render several templates in one run. This is faster than one run per template, and the templates are compiled in parallel:
Meziantou.Framework.Templating.Tool --input "templates/**/*.tt" --output-directory generated --output-extension .txtThe patterns use the syntax of MSBuild: * and ? match a part of a name, ** matches any number of folders, and both / and \ separate folders. A value that names an existing file is that file, even when it contains * or ?. A template matched by several values is rendered once.
Each template generates its own file, so the standard output cannot be used. The name of the file is the name of the template with the extension of its output directive, or --output-extension when it has none. A template without a directive and without --output-extension is an error. The file is written:
- in
--output-directory, when you set it. A template matched by a glob pattern keeps its path relative to the part of the pattern before the first wildcard: withtemplates/**/*.tt,templates/emails/welcome.ttgeneratesgenerated/emails/welcome.txt. A template passed by its path is written at the root of the folder. - next to the template otherwise.
A glob pattern always follows these rules, even when it matches a single template. --output names the file of a single template, so it cannot be used with several templates.
Before it renders anything, the tool checks the inputs: a pattern that matches no file, a template whose output file is unknown, two templates that generate the same file, and a generated file that would replace its template are reported, and no file is written. A template that fails to compile or to run does not stop the others: the tool renders them and exits with code 1.
Use T4-style templates
The tool also reads templates written with the delimiters of T4, <# and #>, and the output directive of T4. It detects them when the file contains a complete <# … #> block and no <%:
<#@ output extension=".txt" #>
<# foreach (var name in new[] { "Ada", "Grace" }) { #>
Hello <#= name #>!
<# } #>Rendering Greeting.tt writes Greeting.txt:
Hello Ada!
Hello Grace!Only the delimiters and the extension of the output directive come from T4: the code blocks and the other directives follow the syntax of the library. The T4 import, assembly and template directives are ignored; use <#@ using Namespace #> and <#@ reference path #> instead. include adds a C# file, not a template.
Choose other delimiters
--start-code-block-delimiter and --end-code-block-delimiter set the delimiters:
Meziantou.Framework.Templating.Tool --input template.txt --start-code-block-delimiter "{{" --end-code-block-delimiter "}}"Pass both, unless you choose one of the known pairs: --start-code-block-delimiter "<#" alone implies #>, and <% implies %>.
Options
| Option | Description |
|---|---|
--input <path> | A template file, or a glob pattern that matches template files. Required; can be repeated. |
--output <path> | The file to write, for a single template. When omitted, the file named by the output directive or --output-extension, or the standard output. |
--output-directory <path> | The folder of the generated files. When omitted, each file is written next to its template. |
--output-extension <extension> | The extension of the generated file of a template that has no output directive. |
--root-directory <path> | The folder that relative input paths and glob patterns are read from. By default, the current folder. |
--arg <name=value> | A string argument passed to the templates. Can be repeated. |
--arguments-file <path> | A JSON file whose root object holds the arguments passed to the templates. |
--start-code-block-delimiter <text> | The start delimiter of code blocks. When omitted, inferred from a known end delimiter or detected from the template. |
--end-code-block-delimiter <text> | The end delimiter of code blocks. When omitted, inferred from a known start delimiter or detected from the template. |
--output-encoding <name> | The encoding of the output file: utf8 (the default, without byte order mark), utf8-bom, or a .NET encoding name such as utf-16, unicode or utf-32. |
--line-ending <LF\|CRLF\|CR> | Converts the line breaks of the output. By default, the output keeps the line breaks of the template. |
-?, -h, --help | Shows the help. |
--version | Shows the version of the tool. |
Errors
The tool exits with code 0 when it succeeds, and 1 when it fails, with a message on the standard error:
- a missing input file, an empty delimiter, a single unknown delimiter, an unknown line ending or encoding;
- an invalid argument or arguments file, or a problem with the inputs of a run of several templates (see Render several templates);
- a compilation error of the template, reported with the path of the template, the line and the column of the error;
- an exception thrown by the code of the template, reported as
path(line,column): error: message, at the position of the code that threw it.