Ecosyste.ms: Awesome

An open API service indexing awesome lists of open source software.

Awesome Lists | Featured Topics | Projects

https://github.com/isaacvando/rtl

A template language for Roc with compile time validation and tag unions
https://github.com/isaacvando/rtl

html html-template roc-lang template-engine

Last synced: 22 minutes ago
JSON representation

A template language for Roc with compile time validation and tag unions

Awesome Lists containing this project

README

        

# Roc Template Language (RTL)

A template language for Roc with compile time validation and tag unions. RTL can be used with HTML or any other textual content type.

First write a template like `hello.rtl`:

```html

Hello, {{model.name}}!


    {|list number : model.numbers |}
  • {{Num.toStr number}}

  • {|endlist|}

{|if model.isSubscribed |}
Subscription
{|else|}
Sign up
{|endif|}
```

Then run `rtl` in the directory containing `hello.rtl` to generate `Pages.roc`.

Now you can call the generated function

```roc
Pages.hello {
name: "World",
numbers: [1, 2, 3],
isSubscribed: Bool.true,
}
```

to generate your HTML!

```html

Hello, World!


  • 1

  • 2

  • 3

Subscription
```

## Installation

Right now RTL must be built locally. For a quick start, run these commands to build RTL and place it in `/usr/local/bin`.

```bash
wget https://github.com/isaacvando/rtl/archive/refs/heads/main.zip
unzip main.zip
roc build rtl-main/rtl.roc --optimize
sudo mv rtl-main/rtl /usr/local/bin
rm -r rtl-main main.zip
rtl --help
```

## How It Works

Running `rtl` in a directory containing `.rtl` templates generates a file called `Pages.roc` which exposes a roc function for each `.rtl` file. Each function accepts a single argument called `model` which can be any type, but will normally be a record.

RTL supports inserting values, conditionally including content, expanding over lists, and pattern matching with when expressions. These constructs all accept normal Roc expressions so there is no need to learn a different set of primitives.

The generated file, `Pages.roc`, becomes a normal part of your Roc project, so you get type checking right out of the box, for free.

### Inserting Values

To interpolate a value into the document, use double curly brackets:

```
{{ model.firstName }}
```

The value between the brackets must be a `Str`, so conversions may be necessary:

```
{{ 2 |> Num.toStr }}
```

HTML in the interpolated string will be escaped to prevent security issues like XSS.

### Lists

Generate a list of values by specifying a pattern for a list element and the list to be expanded over.

```html
{|list paragraph : model.paragraphs |}

{{ paragraph }}


{|endlist|}
```

The pattern can be any normal Roc pattern so things like this are also valid:

```html
{|list (x,y) : [(1,2),(3,4)] |}

X: {{ x |> Num.toStr }}, Y: {{ y |> Num.toStr }}


{|endlist|}
```

### When-Is

Use when is expressions like this:

```
{|when x |}
{|is Ok y |} The result was ok!
{|is Err _ |} The result was an error!
{|endwhen|}
```

### Conditionals

Conditionally include content like this:

```
{|if model.x < model.y |}
Conditional content here
{|endif|}
```

Or with an else block:

```
{|if model.x < model.y |}
Conditional content here
{|else|}
Other content
{|endif|}
```

### Raw Interpolation

If it is necessary to insert content without escaping HTML, use triple brackets.

```
{{{ model.dynamicHtml }}}
```

This is useful for generating content types other than HTML or combining multiple templates into one final HTML output.

### Imports

You can import a module into the template like this.

```
{|import MyModule |}
```

## Tips

### Hot Reloading

You can achieve a pretty decent "hot reloading" experience with a command like this:

```bash
fswatch -o . -e ".*" -i "\\.rtl$" | xargs -n1 -I{} sh -c 'lsof -ti tcp:8000 | xargs kill -9 && rtl && roc server.roc &'
```

### Syntax Highlighting

If you want to get the syntax highlighting for the ambient content type in all of your `.rtl` files in VS Code, you can create a file association like this in `settings.json`:

```json
"files.associations": {
"*.rtl": "html"
}
```

## Talk

I gave a talk about RTL at LambdaConf 2024 called [_Writing a Type-Safe HTML Template Language for Roc_](https://youtu.be/VXQub6U_BUM?si=8rzBNBRZHo0i5X1O) which explains how RTL takes advantages of Roc features like structural typing and type inference.

## Todo

- [ ] Allow more control for whitespace around RTL tags.
- [ ] Allow RTL tags to be escaped.
- [ ] Look into real hot code reloading.
- [ ] Potentially update the generated code to use buffer passing style to avoid unnecessary copies.
- [ ] Benchmark runtime performance against other template languages.
- [ ] Potentially add error messages for incomplete tags.