CODE.RNR

Learn, code, rock'n'roll

This post is generated by the subject of this post

dotnet core static site generator github pages

The story

Four years ago I wanted to create a professional blog and I was searching for a simple solution without those monolithic features of wordpress. That's how I found Grav and started to use it. I like to write in markdown and liked the concept of database-less design. Unfortunately I didn't have the time to keep on writing so it lived only for four posts that you can now read here.

Years later I discovered Github pages and Gitlab pages, these handy static web serving solutions along with the correspondent static site generators like Jekyll and Hexo and decided that some day I will reboot my blog with one of these tools.

Then I was thinking if there is a product for the job that is created in dotnet core, but I didn't find any, so I decided to write my own! That's how Bloggen.Net was born.

Bloggen.Net

The goal was to learn by experimenting, so I didn't want to copy an existing software, I wanted it to evolve with my blogging needs, and they are quite minimal:

  • need some pagination for the posts, ordered by date
  • need to add tags to the posts and referencing them from each other
  • need some static pages that can be linked from a menu
  • markdown syntax with front matter metadata
  • a simple templating language
  • YAML configuration
  • site generation from command line to support automation

So I created a blank console app project and split the responsibilities in modules with well defined interfaces. The modules are wired together through the default dotnet core DI container.

Configuration

There are two types of configuration:

The YAML provider is dependent on YamlDotNet that is also used by the software.

Source

This module defines an interface through which the software can get all the required sources (index template, layout templates, partials, posts, pages). The concrete implementation is a FileSystemSourceHandler that reads the sources from the filesystem.

Serialization

This module is responsible for the deserialization of front matter headers in posts and pages with their metadata. Uses YamlDotNet.

Model

This module represents the data model and relations between blog objects' metadata. Currently there are three types:

  • IPost: represents a blog post metadata
  • IPage: represents a page metadata
  • ITag: represents a tag metadata

The main interface is IContext (inspired by EF core) that holds the collections of these three types with references to each other. The implementation Context collects all the metadata through serialization and cross-references these objects.

Content

This module parses the markdown from the source post and page files and renders them to html strings. The concrete implementation uses MarkDig.

Template

This module renders templates to the given TextWriter.

You can hardly find a good templating library in the dotnet core ecosystem if you can't use Razor. Razor is tightly coupled to aspnet core and it's a bit hacky to use it in a console application. I had another project where I generate my rock band's static homepage based on social media API-s, and I used Handlebars.Net, a handlebars.js port there. It worked well so I kept it.

Output

This module puts the whole thing together. It has only a Generate() public method and does the work internally. It also holds the concrete implementations of the model interfaces defined in Model. The underlying implementation is FileSystemOutputHandler that renders all the parts to corresponding html pages in their subfolders.

The generation process is something like this:

  1. Source and destination directory path is parsed from command line arguments
  2. Site config is parsed from config YAML file
  3. Context metadata object graph is constructed from source front matter data
  4. Urls of concrete metadata objects are populated (they are output dependent so it is done by output module)
  5. Page and Post contents are parsed from their source markdown and are rendered to html
  6. Paginated post (linked) list is generated and rendered
  7. Assets (like images, js, css, etc.) are copied to output folder

And that's it. The blog is ready to be published! For more details about usage see the project readme

Because I'm writing these lines using this "engine", I'm sure it will evolve in the future as new needs occur. There are a lot of features that would be handy, like RSS, archives, etc. Follow this blog and I'll let you know if new features are added!