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:

<!--
title: The title
-->

# {{title}}

{{ 1 + 1 }}

{{* Odd numbers < 5 *}}
{{#each i in range(1,5)}}
{{#if i.isOdd()}}
{{`${i} is odd`}}
{{else}}
{{`${i} is even`}}
{{/if}}
{{/each}}

{{1 + 2 * 3}}

{{ 1 + 1 }}
{{#if debug}}
{{ range(1,5)
|> where => it.isOdd()
|> map => it * it
|> join(',')
}}
{{/if}}

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

made with by ServiceStack