All Projects β†’ scala-thera β†’ thera

scala-thera / thera

Licence: other
A template engine for Scala

Programming Languages

scala
5932 projects
shell
77523 projects

Projects that are alternatives of or similar to thera

Hydrogen
🎈 Hydrogen. Voted (by me) the world's lightest static-site generator built with TypeScript ❀ It uses πŸ”₯ lit-html inspired templating for super duper performant template generation.
Stars: ✭ 80 (+63.27%)
Mutual labels:  static-site-generator, template-engine
Docpad
Empower your website frontends with layouts, meta-data, pre-processors (markdown, jade, coffeescript, etc.), partials, skeletons, file watching, querying, and an amazing plugin system. DocPad will streamline your web development process allowing you to craft powerful static sites quicker than ever before.
Stars: ✭ 3,035 (+6093.88%)
Mutual labels:  static-site-generator, template-engine
Metalsmith React Templates
A metalsmith plugin to render files using React / Preact / JSX based templates.
Stars: ✭ 90 (+83.67%)
Mutual labels:  static-site-generator, template-engine
Hepek
Web content generators in Scala. Intuitive, scalable, powerful.
Stars: ✭ 69 (+40.82%)
Mutual labels:  static-site-generator, template-engine
voldemort
A simple static site generator using Jinja2 and Markdown templates.
Stars: ✭ 48 (-2.04%)
Mutual labels:  static-site-generator, template-engine
moustachu
Mustache templating for Nim
Stars: ✭ 58 (+18.37%)
Mutual labels:  template-engine
hakyll-nix-template
Hakyll + Nix starter template
Stars: ✭ 20 (-59.18%)
Mutual labels:  static-site-generator
tinystatic
A tiny static website generator which is flexible and easy to use
Stars: ✭ 36 (-26.53%)
Mutual labels:  static-site-generator
awesome-gridsome
A curated list of awesome things related to Gridsome
Stars: ✭ 66 (+34.69%)
Mutual labels:  static-site-generator
katapult
Kickstart Rails development!
Stars: ✭ 21 (-57.14%)
Mutual labels:  template-engine
frontman
πŸ’Ž A Ruby-based static website generator
Stars: ✭ 103 (+110.2%)
Mutual labels:  static-site-generator
hulk-template
δΈΊ CodeIgniter ζ‘†ζžΆε’žεŠ θ§†ε›Ύη»§ζ‰ΏεŠŸθƒ½οΌŒδΈζ”Ήε˜εŽŸζœ‰θ§†ε›ΎηΌ–ε†™ζ–ΉεΌοΌŒζ— ηΌε’žεŠ θ§†ε›Ύη»§ζ‰ΏεŠŸθƒ½γ€‚
Stars: ✭ 17 (-65.31%)
Mutual labels:  template-engine
kirby-blade
Enable Laravel Blade Template Engine for Kirby 3
Stars: ✭ 20 (-59.18%)
Mutual labels:  template-engine
TechFusionFM
Static site for tech podcast built using Hexo.io with deployment script, XML escaper and iTunes rank tracking Telegram bot.
Stars: ✭ 20 (-59.18%)
Mutual labels:  static-site-generator
jigsaw-blog-template
Starter template for a blog, using Jigsaw by Tighten
Stars: ✭ 75 (+53.06%)
Mutual labels:  static-site-generator
tonic
Super fast and powerful PHP template engine
Stars: ✭ 48 (-2.04%)
Mutual labels:  template-engine
sbt-hepek
Sbt plugin for rendering Scala objects to files. And more!
Stars: ✭ 17 (-65.31%)
Mutual labels:  static-site-generator
stacy
Website generator that combines content from Contentful CMS with Handlebars templates and publishes the website in Amazon S3.
Stars: ✭ 24 (-51.02%)
Mutual labels:  static-site-generator
github-flavored-markdown-to-html
Convert markdown to HTML using the GitHub API and some additional tweaks with Python. Comes with full formula support and image compression.
Stars: ✭ 44 (-10.2%)
Mutual labels:  static-site-generator
yatpl
Yet Another Template Engine πŸš€
Stars: ✭ 14 (-71.43%)
Mutual labels:  template-engine

Thera - the templating engine for Scala

CI Gitter

import thera._

val template =
"""
---
system:
  name: Solar System
  centralBody: Sun
  planets:
    - { name: "Mercury", mass: "3.30 * 10^23" }
    - { name: "Mars", mass: " 6.42 * 10^23" }
    - { name: "Venus", mass: "4.87 * 10^24" }
    - { name: "Earth", mass: "5.97 * 10^24" }
    - { name: "Uranus", mass: " 8.68 * 10^25" }
    - { name: "Neptune", mass: "1.02 * 10^26" }
    - { name: "Saturn", mass: " 5.68 * 10^26" }
    - { name: "Jupiter", mass: "1.90 * 10^27" }
---
Hello! We are located at the ${system.name}!
The central body here is ${system.centralBody}.
The planets and their masses are as follows:

${foreach: ${system.planets}, ${planet => \
  - ${planet.name} - ${planet.mass}
}}
"""

println(Thera(template).mkString)

// Hello! We are located at the Solar System!
// The central body here is Sun.
// The planets and their masses are as follows:

//   - Mercury - 3.30 * 10^23
//   - Mars -  6.42 * 10^23
//   - Venus - 4.87 * 10^24
//   - Earth - 5.97 * 10^24
//   - Uranus -  8.68 * 10^25
//   - Neptune - 1.02 * 10^26
//   - Saturn -  5.68 * 10^26
//   - Jupiter - 1.90 * 10^27

Thera is a template engine for Scala. It is intended to help people build static websites (such as ones deployed to GitHub Pages) in Scala.

Table of contents generated with markdown-toc

Getting started

Requires Scala 2.13. Add the following dependency to your SBT project:

libraryDependencies += "com.akmetiuk" %% "thera" % "0.2.0-M3"

Or in Mill:

def ivyDeps = Agg(ivy"com.akmetiuk::thera:0.2.0-M3")

Or in Ammonite:

import $ivy.`com.akmetiuk::thera:0.2.0-M3`

Templates

A template consists of two parts – header and body. They are delimited by ---. A header is formatted as Yaml and defines the variables accessible to the template body. The template body can access these variables via ${path.to.variable} syntax. You can process the template via Thera(templateString).mkString syntax.

val person =
"""
---
person:
  name: Tom
  age: 40
---
${person.name} is aged ${person.age}
"""

println(Thera(person).mkString)  // Tom is aged 40

You can also create a template from a file.

val personFile = new File("person-template")

println(Thera(personFile).mkString)  // Tom is aged 40

ValueHierarchy

A template context is the hierarchy of variables accessible to the template when it is processed. Yaml header is parsed to such a hierarchy.

Internally the hierarchy is represented as a ValueHierarchy. Given h: ValueHierarchy, you can query the member variables programmatically from Scala via h("path.to.variable") syntax. This call will return a Value. A Value can be one of the following:

  • Str(value: String) – a String
  • Arr(value: List[Value]) - a collection of values
  • Function(f: (List[Value]) => Str) - a function – such as foreach function in the example at the beginning of this document
  • ValueHierarchy – a nested value hierarchy
  • Throws a RuntimeException - if the queried path doesn't point to a variable

Creating and using ValueHierarchies in templates

You can create a ValueHierarchy from Yaml, or a Scala Map using methods defined in its companion object. If you defined a value hierarchy as an implicit value, the mkString method of a template will implicitly pick it up and add to the template context:

val book =
"""
---
books:
  masteringScala:
    title: Mastering Scala
---
${books.masteringScala.title} costs \$${books.masteringScala.price}.
It was released in ${books.masteringScala.year}
"""

implicit val ctx = ValueHierarchy.names(
  "books" -> ValueHierarchy.names(
    "masteringScala" -> ValueHierarchy.names(
      "price" -> Str("20"),
      "year" -> Str("2015")
    )
  )
)

println(Thera(book).mkString)

// Mastering Scala costs $20.
// It was released in 2015

Functions

You can define functions, put them in the template context and call them from a template. You can do so via methods in the Function companion object. For example:

val hiTml =
"""
${sayHi: World}
"""

implicit val ctx = ValueHierarchy.names(
  "sayHi" -> Function.function[Str] { name: Str =>
    Str(s"Hello ${name.value}")
  }
)

println(Thera(hiTml).mkString)

// Hello World

Templates can also be functions. The argument are specified in square brackets at the top of the header. You can use them as ordinary Scala functions as follows:

val hiTml =
"""
---
[title, name]
city: Lausanne
---
Welcome to $city, $title $name!
"""

val hi: List[Value] => String = Thera(hiTml).mkFunction
println(hi(Str("Mr") :: Str("Jack") :: Nil))

// Welcome to Lausanne, Mr Jack!

Or you can make a Thera Function out of them and pass them to other templates:

val hiTml =
"""
---
[title, name]
city: Lausanne
---
Welcome to $city, $title $name!
"""

val wrapperTml =
"""
${greetingsFun: Mr, Jack}
"""

val hi: Function = Thera(hiTml).mkValue.asFunction
implicit val ctx = ValueHierarchy.names(
  "greetingsFun" -> hi
)

println(Thera(wrapperTml).mkString)

// Welcome to Lausanne, Mr Jack!

Predefined functions

Currently the following functions are available out of the box in Thera:

  • id: Str => Str – identity, evaluates to its input.
  • foreachSep: (arr: Arr, sep: Str, f: Function) => Str - applies f to every element of arr. Then concatenates the results while separating them with sep.
  • foreach: (arr: Arr, f: Function) => Str – like foreachSep where sep is an empty string.
  • if: (cond: Str, ifTrue: Str, ifFalse: Str) => Str - if cond is true, evaluates to ifTrue, otherwise – to ifFalse.
  • outdent: (size: Str, text: Str) => Str – outdents every line of text by size. Useful when working with lambdas.

Lambdas

If a function you are calling accepts another function as an argument, you can define this other function inline using a lambda syntax: ${ arg1, arg2, ... => body }. For example:

val article =
"""
---
tags: [scala, functional, programming]
---
Tags are: ${foreachSep: $tags, \, , ${x => Tag $x}}
"""

println(Thera(article).mkString)

// Tags are: Tag scala, Tag functional, Tag programming

Syntactic rules

Escapes

Symbols $, { and } are significant symbols for the template. If you want to use them as plain text, you need to escape them with \, e.g. \$.

Whitespace parsing

In function calls and lambdas, we need to decide when to parse the whitespaces and when to drop them for ergonomics reasons. For example: ${foreachSep: $tags, \, , ${x => Tag $x}} – here, the whitespace before $tags and \, is for convenience of reading rather than for the output. Hence, in arguments to the function calls, we always drop initial whitespaces and start the argument parsing from the first non-whitespace character.

You can modify this behavior by escaping the whitespace: ${foreachSep: $tags,\ \, , ${x => Tag $x}} – here, the separator will be " , " instead of ", ".

In the bodies of lambdas, the story is similar. To start parsing the whitespaces, you need to escape them, e.g.:

${foreach: ${system.planets}, ${planet => \
  - ${planet.name} - ${planet.mass}
}}

Philosophy

This project started as a static website generator because there wasn't one for Scala and I needed one to generate my blog. Since then, however, I realised that Scala doesn't need a static website generator. It has a powerful enough ecosystem for a user to effortlessly unroll their own logic for generating a website using existing libraries. For instance, my blog uses Ammonite and os-lib in conjunction with Pandoc, a Docker image that defines the environment with Pandoc in it and GitHub Actions that runs the Docker and deploys the website to GitHub Pages. You can have a look at the sources of the blog here.

The only missing piece in the ecosystem is a good templating engine. Thera attempts to provide such an engine for Scala. It doesn't aim to be a markdown processor or a website generator since these tasks can already be easily done using other tools.

Tutorial

There is a tutorial explaining how to build a blog powered by Thera and published on GitHub Pages. You can also use it as a basis to start developing your own website.

Contributions

If you would like to collaborate on this project, do not hesitate to contact me about it!

Note that the project description data, including the texts, logos, images, and/or trademarks, for each open source project belongs to its rightful owner. If you wish to add or remove any projects, please contact us at [email protected].