UNPKG

kriti-lang

Version:

A TypeScript implementation of the Kriti templating language

313 lines (247 loc) 6.87 kB
# Kriti Lang TypeScript A TypeScript implementation of the **Kriti templating language** - a minimal JSON templating language inspired by Go's template language. Kriti templates are a superset of JSON with path lookups, if/then/else expressions, loops, and some basic predicate and conditional operators. > **Note**: This is a TypeScript port of the original [Kriti Lang](https://github.com/hasura/kriti-lang) implementation by Hasura, which is written in Haskell. ## Installation ```bash npm install kriti-lang ``` ## Quick Start ```typescript import { evaluate } from 'kriti-lang'; const template = `{ "author": { "name": {{$.event.name}}, "age": {{$.event.age}}, "articles": [ {{ range _, x := $.event.author.articles }} { "id": {{x.id}}, "title": {{x.title}} } {{ end }} ] } }`; const data = { "event": { "name": "Freddie Jones", "age": 27, "author": { "articles": [ { "id": 0, "title": "The Elements", "length": 150, "published": true}, { "id": 1, "title": "ARRL Handbook", "length": 1000, "published": true}, { "id": 2, "title": "The Mars Trilogy", "length": 500, "published": false} ] } } }; const result = evaluate(template, { $: data }); console.log(result); // Output: Transformed JSON with author info and filtered articles // { // "author": { // "name": "Freddie Jones", // "age": 27, // "articles": [ // {"id": 0, "title": "The Elements"}, // {"id": 1, "title": "ARRL Handbook"}, // {"id": 2, "title": "The Mars Trilogy"} // ] // } // } ``` ## Kriti Expressions ### Path Accessors Values can be looked up in bound json expressions using the standard path lookup syntax: ``` {{ $body.foo.bar[0]['my key'] }} ``` - `.` is used to look up object fields - `[x]` is used to lookup array indices - `['a b c']` is used to lookup string literal object fields If a variable is unbound, the kriti template fail and throw an exception. To prevent such failures, we provide an "optional" lookup operator: ``` {{ $body?.foo }} ``` This example will return a `null` if `foo` is not bound in `$body`. Optional lookups will immediately shortcircuit with `null` so that the following will not attempt any lookups past the unbound `foo`: ``` {{ $body?.foo.bar.baz }} ``` - `foo?` is used to optionally look up a variable. - `?.` is used to optionally look up object fields - `?[x]` is used to optionally lookup array indices - `?['a b c']` is used to optionally lookup string literal object fields ### Defaulting Operator The defaulting operator `??` can be used to replace a `null` value with any other value. The expression `null ?? true` will evaluate to `true`. This is especially useful when used with path lookups: ``` $foo?.bar ?? true ``` ### Loops The `range` identifier is used to declare for loops: ``` {{ range i, x := $.event.author.articles }} {{ x.title }} {{ end }} ``` `i` and `x` above are binders for the index and value of the array element from `$.event.author.articles`. The index can be omitted by using an underscore in its place. ### If Statements Kriti supports if statements and `>` `<` `==` `||` and `&&` operators. ``` {{ if x.published && (x.post_id > 100) }} { "id": {{x.id}}, "title": {{x.title}} } {{ else }} null {{ end }} ``` Use `elif` for multiple conditionals. ``` {{ if x.published && (x.post_id > 100) }} { "id": {{x.id}}, "title": {{x.title}} } {{ elif x.published && (x.post_id <= 100) }} { "id": {{x.id}}, "title": {{x.title}}, "content": {{x.content}} } {{ else }} null {{ end }} ``` ### String Interpolation Bound variables, booleans, integers, object/array lookups, and functions can be interpolated: ``` "http://www.{{$.domain}}.com/{{$.path}}" ``` ``` "http://www.{{$.domain}}.com/{{ $?[1000] }}" ``` ## API Reference ### Main Functions #### `evaluate(template, variables?, customFunctions?)` Evaluates a Kriti template with the provided data. ```typescript function evaluate( input: string, variables?: RuntimeObject, customFunctions?: Map<string, CustomFunction> ): any ``` **Parameters:** - `input` - The Kriti template string - `variables` - Data object (typically passed as `{ $: yourData }`) - `customFunctions` - Map of custom function implementations ### Low-Level API For advanced use cases, you can use the individual components: ```typescript import { tokenize, parseTokens, evaluateAST } from 'kriti-lang'; // Tokenization const tokens = tokenize(template); // Parsing const ast = parseTokens(tokens); // Evaluation const result = evaluateAST(ast, variables, customFunctions); ``` ### Custom Functions Extend Kriti with your own functions: ```typescript import { evaluate } from 'kriti-lang'; const customFunctions = new Map(); customFunctions.set('upperCase', (args) => { return args[0]?.toString().toUpperCase(); }); const template = '{{ upperCase($.name) }}'; const result = evaluate(template, { $: { name: "alice" } }, customFunctions); // Output: "ALICE" ``` ## Transformation Examples JSON Input: ```json { "event": { "name": "Freddie Jones", "age": 27, "author": { "articles": [ { "id": 0, "title": "The Elements", "length": 150, "published": true}, { "id": 1, "title": "ARRL Handbook", "length": 1000, "published": true}, { "id": 2, "title": "The Mars Trilogy", "length": 500, "published": false} ] } } } ``` Template Example: ```kriti { "author": { "name": {{$.event.name}}, "age": {{$.event.age}}, "articles": [ {{ range _, x := $.event.author.articles }} { "id": {{x.id}}, "title": {{x.title}} } {{ end }} ] } } ``` JSON Output: ```json { "author": { "name": "Freddie Jones", "age": 27, "articles": [ {"id": 0, "title": "The Elements"}, {"id": 1, "title": "ARRL Handbook"}, {"id": 2, "title": "The Mars Trilogy"} ] } } ``` Template Example 2: ```kriti { "author": { "name": {{$.event.name}}, "age": {{$.event.age}}, "articles": [ {{ range _, x := $.event.author.articles }} {{ if x.published }} { "id": {{x.id}}, "title": {{x.title}} } {{ else }} null {{ end }} {{ end }} ] } } ``` JSON Output 2: ```json { "author": { "name": "Freddie Jones", "age": 27, "articles": [ {"id": 0, "title": "The Elements"}, {"id": 1, "title": "ARRL Handbook"}, null ] } } ``` ## Original Implementation This TypeScript implementation is based on the original [Kriti Lang](https://github.com/hasura/kriti-lang) by Hasura, written in Haskell. For more information about the language specification and the original implementation, visit the [original repository](https://github.com/hasura/kriti-lang).