loading...

Render static site from Scala code

sake_92 profile image Sakib Hadžiavdić ・3 min read

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 to the rescue

So, after some thinking, I decided to write my first sbt plugin just for that (Sbt is a Scala 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 objects 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:


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 traits. 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. Idea is very simple, structure your content inside sections so you can render them 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 alive @ 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 Renderables to be rendered again, but if we change just the content of a page only that page should be rendered. See 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! :)

Posted on by:

sake_92 profile

Sakib Hadžiavdić

@sake_92

In central Bosnia born and raised. On the computer is where I spent most of my days.

Discussion

markdown guide
 

Good stuff. We have implemented something like that in Groovy (using a DSL). But we used it to create dynamic content.
I don't see why you would limit your idea to create a static site, it works the same to create dynamic content, does it?

 

Thanks! :) Well, I was referring to server-side dynamics. Here you run sbt hepek, change a few files and they get rendered to files. You copy them to your server and serve them statically...

Of course you can have your Javascript do whatever you want to. You could even try to use Play, ScalaJS and ScalaCSS and build everything from Scala... :D

Or did I miss the point?

 

Not sure :-)
What we implemented was server-side, too. Groovy is a JVM language like Scala is, and it also supports traits etc. And you can implement your own DSLs, supporting syntax similar to yours:

div(class: "xyz") {
  span("Text goes here")
}

But we used it inside our web application server to create dynamic content, thus accessing context.

And I guess your approach would also be capable to support dynamic content.

Oh, I see now. I forgot to mention here that I'm using Scalatags library (not mine)... Similar stuff to yours. Keep hacking! :D