Render static site from Scala code
Tags: scala, showdev, webdev
Published: 2017-08-17 14:18:46

---
title: "Render static site from Scala code"
published: true
description: 
tags: Scala, showdev, webdev
---

Have you ever thought:
> If I just had a variable in my ugly HTML...
> So I wouldn't have to repeat all those Bootstrap classes and could change them at one place...

# [Sbt-hepek](https://github.com/sake92/sbt-hepek) to the rescue
So, after some thinking, I decided to write my first sbt plugin just for that ([Sbt](http://www.scala-sbt.org/) is a [Scala](http://www.scala-lang.org/) build tool, like Maven or Gradle in Java ecosystem).

Note: _This is **not** a standard template-based, special-language static site generator like Jekyll and others. It is a **completely** different approach, namely, we use Scala `object`s to represent pages._
We can use some of Scala's features to our advantage:

- Scala has singleton object as a built-in feature. Every `object` that extends `Renderable` trait will be rendered to a file. You can specify a default extension etc. 
- Also, Scala/Java packages can be used to *represent* file system structure! For example, if an `object` is in a `package mysite.posts` it could be rendered to a "mysite/posts" folder! Cool, right? :)

The meat of your page could look like this:
```scala

object HelloWorld extends JavaTemplate {

  override def pageTitle = "Hello world!"

  val introSectionContent =
    div(
      p("Java "Hello World!" example:"),
      javaSnippet("""         
            class HelloWorldApp {
                public static void main(String[] args) {       
                    System.out.println("Hello!");      
                }
            }
      """)
    )

  val introSection = Section("Hello world!", introSectionContent)
  override def sections = Seq(introSection)
}
```

# Ideas
Since we use a full-featured programming language, we can do many things that are not possible (or not as nice) with custom-made template languages like Markdown. These are some ideas that are already implemented.

### Templates
This one is obvious, to represent a template you can use `trait`s. Then you make an `object` that extends it and implement(`override`) only the methods that are `abstract`. Scala's trait methods can have default implementation so we can exploit that also.

### Relative paths
Another thing I thought about was error-prone relative paths in HTML, like `../js/something.js`. We can make a method `def relTo(other: RelativePath): String` that will "calculate" it for us. Then a relative link looks like `Index.relTo(MyOtherPage)`, or from `Index` object itself `this.relTo(MyOtherPage)`. Very intuitive IMHO.

### Sections
This one is "stolen" from Li Haoyi's [Scalatex](http://www.lihaoyi.com/Scalatex/#Section). Idea is very simple, structure your content inside [sections](https://github.com/sake92/sake-ba-source/blob/master/core/src/main/scala/hepek/templates/Section.scala#L9) so you can [render them](https://github.com/sake92/sake-ba-source/blob/eea293d665611415b7d6ec30d3cdc5566c16c155/core/src/main/scala/hepek/utils/HTMLUtils.scala#L48) like chapters in a book. Also, you can make a nice Table Of Contents! :)

# Example site

For a full-blown example, please take a look at my [repo](https://github.com/sake92/sake-ba-source) alive @ [blog.sake.ba](https://blog.sake.ba)

It has a `core` project where the templates are (if I decide to make another site, I could reuse it), and `sake-ba-blog` where the content is.

# Conclusion
IMHO, this is a very powerful approach for static sites and all kinds of static stuff, like XML or JSON. Since there is no parsing we can incrementally render files by examining class dependencies. E.g. if we change a template we want all `Renderable`s to be rendered again, but if we change just the content of a page only that page should be rendered. See [hepek-core](https://github.com/sake92/hepek-core) for implementation details.

This approach can be implemented in other languages that have singleton objects as first-class sitizens, like Kotlin and others.
Opinions, suggestions are welcome! :)