#Script Code

Code statements is an alternative language dialect which allows us to invert #Script from "Template Mode" where all text is emitted as-is with only Template Expressions {{ ... }} being evaluated and changed it to "Code Mode" where all code statements are evaluated as JS Expression statements.

This is akin to Razor's statement blocks which inverts Razor's mode of emitting text to treating text inside statement blocks as code, e.g:

    var a = 1;
    var b = 2;
Basic Calc
a + b = @(a + b)

The equivalent in #Script using code language blocks:

var a = 1
var b = 2
Basic Calc
a + b = {{a + b}}

Which is useful in reducing boilerplate when you need to evaluate code blocks with 2+ or more lines without the distracting boilerplate of wrapping each expression within a {{ ... }} Template Expression.


Effectively you can think of Code Statements as #Script without template expressions since the code within a Template Expressions {{ ... }} are both evaluated as JS Expressions.

One difference between them is that each line in a code statement block must be a complete expression, any expressions that spans multiple lines need to be wrapped withing Template Expressions where-by the source code for both Template and Code blocks remain exactly the same, e.g:

{{ products
    |> groupBy => it.Category
    |> map => { Category: it.Key, MostExpensivePrice: it.max(p => p.UnitPrice) }
    |> htmlDump }}

The difference between them is that whitespace and indentation in code statement blocks are inert whereas they're emitted in Templates so these examples are equivalent:

{{#if test.isEven() }}
{{test}} is even
{{test}} is odd
#if test.isEven()
    `${test} is even`
    `${test} is odd`

Whilst code statements can effectively be thought of as "#Script without Template Expressions", you can view the LINQ examples in each to see how they compare:

Executing #Script Code in .NET

All #Script languages are executed within a ScriptContext that defines all functionality available to them, i.e:

var context = new ScriptContext {
    Args = { ... },              // Global Arguments available to all Scripts, Pages, Partials, etc
    ScriptMethods = { ... },     // Additional Methods
    ScriptBlocks = { ... },      // Additional Script Blocks 
    FilterTransformers = { .. }, // Additional Stream Transformers
    PageFormats = { ... },       // Additional Text Document Formats
    Plugins = { ... },           // Encapsulated Features e.g. Markdown, Protected or ServiceStack Features

    ScanTypes = { ... },         // Auto register Methods, Blocks and Code Page Types
    ScanAssemblies = { ... },    // Auto register all Methods, Blocks and Code Page Types in Assembly

Where you can customize the pure sandboxed ScriptContext your Script is executed within by extending it with:

But to render code statements you'd use RenderCode instead of RenderScript, e.g:

// render code statements
var output = context.RenderCode("now |> dateFormat('HH:mm:ss')"); 

// async
var output = await context.RenderCodeAsync("now |> dateFormat('HH:mm:ss')"); 

These APIs match the high-level APIs for rendering normal #Script:

var output = context.RenderScript("{{ now |> dateFormat('HH:mm:ss') }}"); 
var output = await context.RenderScriptAsync("{{ now |> dateFormat('HH:mm:ss') }}"); 

Finer grained control

The high-level APIs above wraps the finer-grained functionality below which works by rendering a SharpPage configured with the code language in a PageResult that all languages use:

var context = new ScriptContext().Init();
var dynamicPage = context.CodeSharpPage("now |> dateFormat('HH:mm:ss')");           // render code
//var dynamicPage = context.SharpScriptPage("{{ now |> dateFormat('HH:mm:ss') }}"); // render #Script
var output = new PageResult(dynamicPage).RenderScript();

var output = await new PageResult(dynamicPage).RenderScriptAsync();

If you need the return value instead you can access it from:

var result = new PageResult(dynamicPage).EvaluateResult(out var returnValue)
    ? ScriptLanguage.UnwrapValue(returnValue)
    : null;

If your script source code doesn't change you can re-use dynamicPage which lets you re-evaluate your source code's cached AST.

Evaluating Script Results

If you instead wanted to access Script return values instead of the code rendered output you would use the EvaluateCode() APIs:

var result = context.EvaluateCode("return ( 1 + 1 )"); //= 2

The generic overloads below utilizes ServiceStack's Auto Mapping utils to convert the return value into your preferred type, e.g:

double result = context.EvaluateCode<double>("return (1 + 1)"); //= 2.0
string result = context.EvaluateCode<string>("return (1 + 1)"); //= "2"

Which can also be used for more powerful conversions like converting an Object Dictionary into your preferred POCO:

var result = context.EvaluateCode<Customer>("`select * from customer where id=@id` |> dbSingle({id}) |>return"
    , new ObjectDictionary {
        ["id"] = 1

Scripting .NET Types

All #Script languages have the same access to Scripting .NET Types.

Code Scripts

The same functionality in Sharp Scripts is also available in #Script Code, except instead of using the *.ss file extension for executing #Script you'd use the *.sc file extension which will allow you to use the x and app dotnet tools to watch or run code scripts:

$ x run code.sc
$ x watch code.sc

Watch code scripts

Here's a quick demo showcasing the same functionality in Sharp Scripts is also available in *.sc scripts which provides instant feedback whilst you develop in real-time:

YouTube: youtu.be/TQPOZ0kVpw4

made with by ServiceStack