Reflections on Nitride
This week's goal was to get mfgames.com migrated over to MfGames.Nitride. It was a lofty goal, mainly because I've been making small incremental changes over the last year but never got something pushed over the finish line.
I succeeded.
Migration
This is my third “big” site for Nitride, one that follows the common pattern of pulling in multiple Git repositories from my forge, integrating the data, and then creating a website out of it. In other words, one of the main reasons I created Nitride.
It isn't exactly a fancy site, but I find that I'm not really creating flashy sites anymore. Part of this is because I still enjoy Gemini, which is a text-only format, but also because the chrome is distracting to me. I want something simple but not the black on gray of Netscape Navigator.
Overall, I'm pretty happy with the results. It creates landing pages for projects, pulls in the ./docs/
folder for each project and formats them (for example, MfGames.Gallium), and merges news posts from the projects (while decorating them) and creates a single unified feed.
There are more things I want to do, such as links to issue tracking, licenses, and other stuff. I also ended up losing some features like the culture sandbox (which I needed to rewrite anyways), the slippery maps, and a few other minor things.
Still happy with it.
News
I was working on debugging creating news posts in individual project repositories anews posts from individual projects into mfgames.com as my “little thing” for today and I came up with the exact period of time I decided to write my latest static site generator (SSG):
$ git log --pretty="%ai %s"
...
2021-05-04 02:27:28 -0500 feat: implementing my own
2021-04-07 22:13:20 -0500 chore: updating Statiq libraries
...
2014-05-07 14:41:27 -0700 Initial commit
It all started a year before, when I migrated from CobblestoneJS (my SSG from the early 2010s) to GatsbyJS. Which was pretty and gorgeous and so difficult to debug. It was also fragile for me because I don't update my libraries to follow the curve and every time I tried to add a new feature to the site, I spent a few days just trying to figure out the node packages to get everything building again first.
When I got into Gemini for the first time, I also realized that GatsbyJS wasn't going to work there because that shiny site was not text friendly. It was also tied into my frustrating that going to a page the first time downloaded the content in one manner, but navigation was done via SPA and pulled it from another; and the cache was killing me there.
(But it looked so pretty.)
So I decided to look for a new SSG. I didn't want to write my own because… I really don't like writing utility programs. But I wanted to do something Gatsby didn't do, which is generate a site that didn't require Javascript.
I figured I was good at C# (this was before Rust, but I'm still best at C#), so I wanted to find a C# static site generator in hopes that the NuGet package management wasn't as excruciating as Node's.
So I found Statiq which seemed like a perfect match. But going from GatsbyJS to Statiq quickly hit a point where I realized I was, once again, forcing a program to do things it wasn't supposed to be doing. The patterns in Statiq were nice but they were too rigid for the complexity of my way of creating sites:
- The fragility of pulling in 80+ Git repositories into a CI pipeline
- The use of multiple dynamic lists on a single page
- The desire to make a Gemini-friendly site
After that month of thinking, I started to write my own. This came when I was looking at Statiq's document type realizing that my desire to create a new type basically meant I had to rebuild the entire framework to support a slightly different enumeration.
Gallium
Enter Entity Component Systems (ECS) which I learned about while pondering writing a game using Bevy. I had never heard of an ECS before that, but the idea was great. Why not write a static site generator that didn't need a single enum to drive processing, where I could add arbitrary components to pages such as nested lists or metadata, and then easily be able to use them.
Of course, then I had to look at existing ECS systems in C#, but I eventually decided to be more idiomatic to C# (another frustration of Statiq) and focus on something that let me use LINQ-like method calls (such as .Select()
and .Where()
).
List<Entity> newsList = fileList
.WhereEntity<UPath>(this.OnlyNewsFiles)
.Run(this.setInstantFromPath, cancellationToken)
.SelectEntity<UPath, Instant>(this.pathService.SetPathFromInstant)
.Select(entity => entity.Set(IsPost.Instance))
.Select(entity => entity.Set(IncludeInFeed.Instance))
.Where(entity => entity.Has<IsMarkdown>())
.OrderBy(entity => entity.Get<PageModel>().Title)
.ToList();
I'm specifically not saying Gallium is a “high performance” because it isn't. It is an ECS for relatively small systems that builds on existing libraries for patterns. I don't need high performance for sites with only a few thousand pages, I want something easy to understand.
Next Steps
There are quite a few steps going forward. I still want to make Nitride easier to use, Gallium to track LINQ patterns, and mfgames.com to present more data. But this is a big step and I'm really happy that I'm proud of (I'm sure there will be tweaks). More importantly, I have something that hasn't given me any major trouble updating since I started three years ago.
Metadata
Categories:
Tags: