Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
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
- Host: GitHub
- URL: https://github.com/isaacvando/rtl
- Owner: isaacvando
- License: upl-1.0
- Created: 2024-02-28T01:35:15.000Z (9 months ago)
- Default Branch: main
- Last Pushed: 2024-09-18T02:46:32.000Z (about 2 months ago)
- Last Synced: 2024-09-18T05:16:01.275Z (about 2 months ago)
- Topics: html, html-template, roc-lang, template-engine
- Language: Roc
- Homepage:
- Size: 184 KB
- Stars: 15
- Watchers: 3
- Forks: 1
- Open Issues: 2
-
Metadata Files:
- Readme: README.md
- License: LICENSE.md
- Codeowners: CODEOWNERS
Awesome Lists containing this project
- roc-awesome - isaacvando/rtl
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}}!
- {{Num.toStr number}}
{|list number : model.numbers |}
{|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.