Skip to content

Template Runtime

Pardon’s JSON templates are transformed and evaluated as javascript expressions. This is a synchronous evaluation to form a template object, which is then processed into a schema via merge operations.

In pardon templates, “pure” JSON represents itself (with {{interpolations}}). Pardon uses any JS syntax beyond JSON for adding more structure.

For example, shorthand-property-assignement for a variable is not JSON:

{ "a": "{{a}}" }
{ a }

A value of a is provided as a reference template object.

Reference template objects support various (chainable) operations, such as variable hinting

HintDescription
ref.$hint(hint)adds hint(s) to the reference, e.g., '?' for optional
ref.$optionalsame as `ref.$hint(’?’) — allow this value to be missing when rendering
ref.$secretsame as `ref.$hint(’@’) — redacts this variable in non-secret contexts
ref.$requiredsame as `ref.$hint(’!’) — ensures this value is present when matching
ref.$exportsame as `ref.$hint(’+’) — signals this variable for further use
ref.$noexportsame as `ref.$hint(’-’) — does not export this variable
ref.$distinctsame as `ref.$hint(’~’) — distinguishes a ref in keyed/multivalue contexts

References also support typing hints

TypeDescription
ref.$numbertreat this reference as a number
ref.$biginttreat this reference as a bigint
ref.$stringtreat this reference as a string
ref.$booleantreat this reference as a boolean
ref.$boolsame as above, but shorter
ref.$nullablealso allow null values, renders as null when undefined
ref.$nullsame as above, but shorter

Typing hints affect matching and rendering: when matching the value must be of the same or similar type, while when rendering a value is cast to the type when possible.

References support binding mechanisms, they can be bound to to-be-evaluated scripts.

ActionDescription
ref.$expr(script)declares a default value for ref as the evaluation of script (as a string)

References support pathing, ref.x is a reference to "ref.x". Since @value and @key are special subpaths for scoped references, and @ is not a valid identifier, there are two path references as well:

PathingDescription
ref.$valueis a reference to "ref.@value"
ref.$keyis a reference to "ref.@key"

For reference variable names that include hyphens, an alternate syntax is supported: $`a-b`.

Some of the reference API is designed to be used via syntax transformations, for instance here’s some syntax that is usually nice than using the API directly.

SyntaxTransformation
template1 = template2$merged(template2, template1)
ref = (expr)ref.$expr("expr")
(expr)$.$expr("expr")

For references that match simple values (strings, numbers, booleans) a regex can be specified. This transforms the ref to an interpolation in the runtime, so regex can only be used in these two forms.

SyntaxTransformation
ref % /regex/"{{ ref % /regex/ }}"
ref = (expr) % /regex/"{{ ref = $$expr("expr") % /regex/ }}"

Hints and types can be applied via as type typescript casts.

SyntaxTransformation
ref as optionalref.$optional
ref as flow | boolref.$optional.$boolean

Other syntax transformations are available to support representing structures and scoping with minimal syntax.

| Syntax | Transformation | notes | | --- | --- | | f(...) | $f(...) | | a.b.f(...) | a.b.$f(...) | | (expr) | $.$expr("expr") | | [...template] | $elements(template) | | ![...template] | $itemOrArray(template) | | { key } * [...item] | $keyed({ key }, $elements(item)) | | { key } ** [...item] | $keyed$mv({ key }, $elements(item)) | | { ...template } | $scoped(template) | | ref = template | $merged(template, ref) | | 123 | $$number('123') | | ref! | $required(ref) | | -ref | $noexport(ref) | | +ref | $export(ref) | | ~ref | $distinct(ref) |

In addition, / can be used to apply template decoration functions.

| hidden / template | $hidden(template) | | match / template | $match(template) |

match mode mimics response processing, and this could be useful for providing literal JSON that might contain template-like values ("{{...}}") that should not be treated as interpolations.

merge is an inline merge of two templates, we’ve seen this before with reference = template pattern but it can be used for any binding. In most cases, merging should be distributive, e.g.,

{ a: b } = { a: 10 }

would result in the same effective schema as { a: b = 10 }.

$$number is a wrapper applied to numbers in JSON, it produces a new Number() (object) with a source field equal to the original value. This can be a little annoying, as $$number(123) == 123 and yet $$number(123) !== 123, but it’s a tradeoff to allow very big numbers and arbitrary precision decimals to not get mangled by the String(Number(original)) conversion implied by the template runtime.

Loading Template Playground...