Transformers
You can apply a chain of multiple Stream transformations to transform output using Transformers which are just functions that accept an Input Stream and return a modified Output Stream. The MarkdownPageFormat's TransformToHtml shows an example of a Transformer which converts Markdown Input and returns a Stream of HTML output:
public class MarkdownPageFormat : PageFormat
{
private static readonly MarkdownDeep.Markdown markdown = new MarkdownDeep.Markdown();
public MarkdownPageFormat()
{
Extension = "md";
ContentType = MimeTypes.MarkdownText;
}
public static async Task<Stream> TransformToHtml(Stream markdownStream)
{
using (var reader = new StreamReader(markdownStream))
{
var md = await reader.ReadToEndAsync();
var html = markdown.Transform(md);
return MemoryStreamFactory.GetStream(html.ToUtf8Bytes());
}
}
}
Output Transformers
You can transform the entire output of a Script with Output Transformers which you would do if both your _layout and page both contain markdown, e.g:
var context = new ScriptContext {
PageFormats = { new MarkdownPageFormat() }
}.Init();
context.VirtualFiles.WriteFile("_layout.md", @"
The Header
{{ page }}");
context.VirtualFiles.WriteFile("page.md", @"
## {{ title }}
The Content");
PageResult with Output Transformer
var result = new PageResult(context.GetPage("page"))
{
Args = { {"title", "The Title"} },
ContentType = MimeTypes.Html,
OutputTransformers = { MarkdownPageFormat.TransformToHtml },
};
var html = await result.RenderToStringAsync();
After the Script is evaluated it's entire output gets passed into the chain of OutputTransformers defined, which in this case will send a MemoryStream of the generated Markdown Output into the MarkdownPageFormat.TransformToHtml transformer which returns a Stream of converted HTML which is what's written to the OutputStream.
Page Transformers
You can also apply Transformations to only the Page's output using Page Transformers which you would do if only the page was in Markdown and the _layout was already in HTML, e.g:
var context = new ScriptContext {
PageFormats = { new MarkdownPageFormat() }
}.Init();
context.VirtualFiles.WriteFile("_layout.html", @"
<html>
<title>{{ title }}</title>
</head>
<body>
{{ page }}
</body>");
context.VirtualFiles.WriteFile("page.md", @"
## Transformers
The Content");
PageResult with Page Transformer
var result = new PageResult(context.GetPage("page"))
{
Args = { {"title", "The Title"} },
ContentType = MimeTypes.Html,
PageTransformers = { MarkdownPageFormat.TransformToHtml },
};
var html = await result.RenderToStringAsync();
Filter Transformers
Filter Transformers are used to apply Stream Transformations to Block Methods which you could use if you only wanted to convert an embedded Markdown file inside a Page to HTML. You can register Filter Transformers in either the ScriptContext's or PageResult's FilterTransformers Dictionary by assigning it the name you want it available in your Scripts under, e.g:
var context = new ScriptContext
{
ScriptMethods = { new ProtectedScripts() },
FilterTransformers =
{
["markdown"] = MarkdownPageFormat.TransformToHtml
}
}.Init();
context.VirtualFiles.WriteFile("doc.md", "## The Heading\nThe Content");
context.VirtualFiles.WriteFile("page.html",
"<div id="content">{{ 'doc.md' |> includeFile |> markdown }}</div>");
PageResult with Filter Transformer
var html = new PageResult(context.GetPage("page")).Result;
htmlencode
The htmlencode Filter Transformer is pre-registered in ScriptContext which lets you encode Block Method outputs which is useful when you want to HTML Encode a text file before embedding it in the page, e.g:
{{ "page.txt" |> includeFile |> htmlencode }}
Preprocessor Code Transformations
Preprocessors allows you to perform source-code transformations before #Script
evaluates it which is useful when you want to
convert any placeholder markers into HTML or #Script
code at runtime, e.g. this feature could be used to replace translated text with the
language that the App's configured with to use on Startup.
With this feature we can also change how #Script's Language Block Feature is implemented where
we could instead use a simple TransformCodeBlocks PreProcessor
to process any code
bocks by registering in the Preprocessors
list:
var context = new ScriptContext {
Preprocessors = { ScriptPreprocessors.TransformCodeBlocks }
}.Init();
There by overriding the existing implementation with the code transformation below:
Code Transformation
This preprocessor performs a basic transformation that assumes every statement is an expression and wraps them in an {{...}}
expression block, the exception are expressions which are already within an expression block which are ignored and you still
need to use to wrap multi-line expressions in code
blocks.
The other transformation code
blocks perform is collapsing new lines and trimming each line, this is so scripts which are primarily
structured and indented for readability aren't reflected in its text output.
We can see an example of how code
blocks work from the example below:
<!--
title: The title
-->
# {{title}}
{{ 1 + 1 }}
```code
* Odd numbers < 5 *
#each i in range(1,5)
#if i.isOdd()
`${i} is odd`
else
`${i} is even`
/if
/each
```
```code
1 + 2 * 3
```
```code
{{ 1 + 1 }}
{{#if debug}}
{{ range(1,5)
|> where => it.isOdd()
|> map => it * it
|> join(',')
}}
{{/if}}
```
Before this script is run it's processed by the
TransformCodeBlocks PreProcessor
which transforms it to the normal #Script
code:
The resulting output when executed is:
# The title
2
1 is odd
2 is even
3 is odd
4 is even
5 is odd
7
2
1,9,25