For most of my projects, I use hugo as a static site generator. It's fast, it's mature, it's great and I'm pretty happy with it.

However, I really like the Dart programming language and wanted to see how much of my personal website I could write in Dart. I can't really invoke Dart compilers from hugo, so I started to look for other options. There are static site generators written in Dart, but I couldn't find one that allows me to compile Dart without much hassle.

After stumbling across "Why I Built My Own Shitty Static Site Generator", I decided to give it a go myself and see what I could come up with. I have better things to do than coming up with a fast system with support for incremental builds and everything else I expect from a site generator, so I decided to offload as much work as possible to Dart's standard build system, simply called build. With that system, you write a set of deterministic functions transforming inputs into outputs, called builders.

In my case, the inputs are a bunch of .md files and html templates, and the output is the generated website you're seeing. I tried to write a set of builders for this transformation. After some days of experimenting, I'm very happy with the result - the build package turned out to be an excellent fit. I'll compare it to hugo in the aspects I care about:

Speed

Thanks to incremental rebuilds, editing and rebuilding the site feels super quick. Subjectively, it rebuilds just as as fast and reliable as hugo. Dart's build system also cares about deterministic builds a lot, so the output of any incremental build is just what I'd get in a full build. With hugo, I sometimes ran into situations where editing some files (e.g. parts of the config) would just be ignored. I don't have those problems with Dart's build system at all.

Running a full build for deployment is noticably slower, but I don't really care about that. I deploy like twice a week and firebase deployments are slow anyway.

Tooling

For my Dart website, I run dart run webdev serve --auto reload to start a local server that reloads pages as they change. In my opinion, that comes close to the hugo serve from Hugo.

I care a lot about having a single system to run all build steps for me, I don't want to run a chain of commands to build my website. Both hugo and my build setup work great here.

Language integration

I strongly prefer Dart's sound type system over JavaScript or TypeScript, so being able to write my website in Dart is a big plus. The build_web_compilers package compiles my Dart code to JavaScript. It also uses Dart's build system, so my site generator and the compilers work neatly together.

I don't really know CSS so I stole most of my website's design, but at least I could simplify it a bit by using Sass. As you might know, the reference implementation of Sass is written in Dart, and of course there are builders to transpile Sass to CSS. Very awesome - integrating Sass only requires one dev_dependency in my pubspec.

Since I'll probably need to highlight a lot of Dart code around here, I wrote a custom syntax highlighter using the analyzer package which gives me better results than highlight.js.

Summary

Overall, I really like how well Dart's build system can handle this task that it hasn't been really designed for. It's plenty fast and integrates with all the tools I like, so I'm going to use it to build websites now.

I was also pleasantly suprised to see that Dart's ecosystem has grown a lot, there were a lot of cool packages I could use:

  • The front_matter package to parse a block of yaml containing metadata for a page.
  • The markdown package to parse and render markdown.
  • For Dart, I use my own syntax highlighter based on the analyzer package. For other languages, I use the highlight package.
  • I like Shopify's Liquid template system and wanted to use that for my website. Easy to do thanks to the liquid_engine package.