Using Nitride - Introduction
One drawback of having a lot of irons in the fire is occasionally projects that should not have been forgotten are and they bitrow. A good example is the Typewriter Press and it's associated reflected websites:
Eventually, when I start publishing my sci-fi stories, they would go under “Electric”. I call them “reflected” sites because they are just filtered versions of the main Typewriter site with some slightly different branding. That way, if I wrote more romance, I could put them there and folks could go to the romance-only site or jump over to the main one to see that I have no single genre (much like I don't have a single genre for my writing).
However, it's been years since I've updated those sites and they are beginning to creak underneath their age. Not to mention, I haven't included the last two books I published on them and that is humiliating for me.
So, I decided to switch the sites over to MfGames.Nitride and document the process.
Series
This is going to be broken into multiple posts, mostly because I know I'm going to get distracted around the fifteenth of the month when I switch to writing, but also to keep these topic-focused.
2025-06-07 Using Nitride - Introduction - I'm going to start a long, erratic journey to update my publisher website. Along the way, I'm going to document how to create a MfGames.Nitride website from the ground up, along with a lot of little asides about reasoning or purpose.
2025-06-08 Using Nitride - Pipelines - The first major concept of Nitride is the idea of a "pipeline" which are the largest units of working in the system. This shows a relatively complex setup of pipelines that will be used to generate the website.
2025-06-09 Using Nitride - Entities - The continued adventures of creating the Typewriter website using MfGames.Nitride, a C# static site generator. The topic of today is the Entity class and how Nitride uses an ECS (Entity-Component-System) to generate pages.
2025-06-10 Using Nitride - Markdown - Examples and explanations of converting Markdown to HTML using MfGames.Nitride and MarkDig.
2025-06-12 Using Nitride - Front Matter - How to use Nitride to take the front matter from pages and add them as a component into the Entity class.
Conventions
My usual conventions when giving file names is to use //
for the Git repository root. So, //flake.nix
means the flake.nix
in the root level of the project. Also, I'll add a trailing /
when I'm talking specifically about a directory, such as //node_modules/
.
NixOS Flakes
Since I'm going to be creating a new site instead of converting the existing, I can easily start from the beginning. That usually means starting with a NixOS flake which is my preferred way of writing.
# //flake.nix
{
inputs = {
nixpkgs.url = "nixpkgs/nixos-25.05";
flake-utils.url = "github:numtide/flake-utils";
mfgames-project-setup.url = "git+https://src.mfgames.com/nixos-contrib/mfgames-project-setup-flake.git";
};
outputs = inputs @ { self, nixpkgs, flake-utils, mfgames-project-setup }:
flake-utils.lib.eachDefaultSystem (system:
let
pkgs = nixpkgs.legacyPackages.${system};
project-config = mfgames-project-setup.lib.mkConfig {
inherit system;
pkgs = nixpkgs.legacyPackages.${system};
dotnet.enable = true;
prettier.proseWrap = "never";
};
in
{
devShell = pkgs.mkShell {
packages = [
# Development
pkgs.dotnet-sdk
pkgs.nodejs_20
# Local Serving
pkgs.miniserve
pkgs.agate
]
++ project-config.packages;
shellHook = project-config.shellHook;
};
});
}
There really isn't much to this other than my use of mfgames-project-setup-flake. This is a flake that sets up a lot of the common elements I use: a consistent .editorconfig
, treefmt
for formatting various parts of the system, lefthook
for setting up conventional commits.
And naturally, I have to set up direnv because I want to set up my environment as soon as I change into the directory.
# //.envrc
use flake || use nix
dotenv_if_exists .env
PATH_add $PWD/node_modules/.bin
I'm adding the node_modules
path because I know I'm going to be using webpack
as part of this site.
Once those two files are in, then I can get my basic setup.
$ git add flake.nix .envrc
$ direnv allow
direnv: nix-direnv: Renewed cache
nixago: updating repository files
nixago: '.conform.yaml' link updated
nixago: '/.conform.yaml' added to .gitignore
nixago: '.editorconfig' copy created
nixago: 'lefthook.yml' link updated
Error: unknown shorthand flag: 'a' in -a
Error: unknown shorthand flag: 'a' in -a
nixago: '/lefthook.yml' added to .gitignore
nixago: '.prettierrc.json' link updated
nixago: '/.prettierrc.json' added to .gitignore
nixago: 'treefmt.toml' link updated
nixago: '/treefmt.toml' added to .gitignore
mfgames-project-setup: '/.direnv/' added to .gitignore
sync hooks: ✔️ (commit-msg, pre-commit)
direnv: export +AR +AS +AWS_ACCESS_KEY_ID +AWS_BUCKET +AWS_ENDPOINT +AWS_REGION +AWS_SECRET_ACCESS_KEY +CC +CONFIG_SHELL +CXX +DOTNET_CLI_TELEMETRY_OPTOUT +DOTNET_NOLOGO +DOTNET_SKIP_FIRST_TIME_EXPERIENCE +DOTNET_SKIP_WORKLOAD_INTEGRITY_CHECK +HOST_PATH +IN_NIX_SHELL +LD +MSBUILDALWAYSOVERWRITEREADONLYFILES +MSBUILDTERMINALLOGGER +NIX_BINTOOLS +NIX_BINTOOLS_WRAPPER_TARGET_HOST_x86_64_unknown_linux_gnu +NIX_BUILD_CORES +NIX_CC +NIX_CC_WRAPPER_TARGET_HOST_x86_64_unknown_linux_gnu +NIX_CFLAGS_COMPILE +NIX_ENFORCE_NO_NATIVE +NIX_HARDENING_ENABLE +NIX_LDFLAGS +NIX_STORE +NM +NODE_PATH +OBJCOPY +OBJDUMP +RANLIB +READELF +SIZE +SOURCE_DATE_EPOCH +STRINGS +STRIP +__structuredAttrs +buildInputs +buildPhase +builder +cmakeFlags +configureFlags +depsBuildBuild +depsBuildBuildPropagated +depsBuildTarget +depsBuildTargetPropagated +depsHostHost +depsHostHostPropagated +depsTargetTarget +depsTargetTargetPropagated +doCheck +doInstallCheck +dontAddDisableDepTrack +mesonFlags +name +nativeBuildInputs +out +outputs +patches +phases +preferLocalBuild +propagatedBuildInputs +propagatedNativeBuildInputs +shell +shellHook +stdenv +strictDeps +system ~PATH ~XDG_DATA_DIRS
$ l
total 64K
drwxr-xr-x 5 dmoonfire users 4.0K Jun 7 11:10 .
drwxrwxr-x 18 dmoonfire users 4.0K Dec 26 08:08 ..
lrwxrwxrwx 1 dmoonfire users 56 Jun 7 11:10 .conform.yaml -> /nix/store/pckycri07q2ah90swk6hf21ilsi59a4s-conform.yaml
drwxr-xr-x 4 dmoonfire users 4.0K Jun 7 11:10 .direnv
-rw-r--r-- 1 dmoonfire users 5.2K Jun 7 11:10 .editorconfig
-rw------- 1 dmoonfire users 259 Jun 7 10:40 .env
-rw-r--r-- 1 dmoonfire users 13K Jun 7 11:10 flake.lock
-rw-r--r-- 1 dmoonfire users 983 Jun 7 11:03 flake.nix
drwxrwxr-x 8 dmoonfire users 4.0K Jun 7 11:10 .git
-rw-r--r-- 1 dmoonfire users 139 Jun 7 11:10 .gitignore
lrwxrwxrwx 1 dmoonfire users 56 Jun 7 11:10 lefthook.yml -> /nix/store/ihdb9vnc6jp535m43ndas79s4p9viyjn-lefthook.yml
lrwxrwxrwx 1 dmoonfire users 59 Jun 7 11:10 .prettierrc.json -> /nix/store/mjdm21r27r4n43aq92qkvmdcvy0l2qrc-prettierrc.json
-rw-r--r-- 1 dmoonfire users 1.2K Jun 7 10:40 README.md
lrwxrwxrwx 1 dmoonfire users 56 Jun 7 11:10 treefmt.toml -> /nix/store/4lk6rmb2khs5l3yn91rah9y4c6zmxybf-treefmt.toml
Probably one of my favorite things is that it also sets up the initial .gitignore
file:
$ cat .gitignore
# nixago: ignore-linked-files
/treefmt.toml
/.prettierrc.json
/lefthook.yml
/.conform.yaml
# mfgames-project-setup: ignore-files
/.direnv/
Overall, using that just cuts out that first hour of starting up a new project. And it lets me evolve my changes to style and different tools by simply running nix flake update
.
Directory Layout
Related to using a flake to setup a lot of the boilerplate, I have written my thoughts on setting up polyglot projects such as this in a consistent manner. I think it is a relatively short digital garden plot, but there are a couple applicable things.
Source code goes into //src/
and the output is going into //build
(which has an entry in .gitignore
). One of the artifacts of my WordPress days is that I have static pages, which I put into //src/pages/
, and blog posts which go into //src/posts/
. There is some magic that goes on with posts, but that's a different post.
$ find src
src
src/pages
src/pages/typewriter
src/pages/typewriter/index.md
src/posts
src/posts/2025-07-01-website-redesign.md
I put an extra layer of directories under //src/pages/
for a site “slug” to identify which site the page will go on. In this case, I'm going to have typewriter
for the main site, sassy
, dusty
, broken
, and electric
. There will also be a common
for pages that are shared across all pages such as the contact page.
For output, those same slugs are going to be used. I'm intending to have this output both HTML and Gemtext pages for HTTPS and Gemini respectively. I know not a lot of people use Gemini these days, but I still think it is useful and there is a certain minimalism to it that appeals to me.
find build
build
build/typewriter
build/typewriter/html
build/typewriter/gemtext
Just
Another part of my standard project layout is using Just to handle the build system. There are a number of reasons, but I'm going to be dealing with Node programs, such as webpack
, and also .NET projects for the build. There is also CLI executables for hosting locally (miniserv
and agate
).
I made a conscious decision not to write wrappers for Node and the other CLIs into Nitride. There is the ability to execute, but I don't want to tightly tie the site generator to a specific tool. Not to mention, most of the time, I want to set them up in different tabs to watch them so I want a more “generic” task runner than the ones built into Node or .NET which have some biases I've struggled with.
Just is something that is self-contained, doesn't make a lot of assumptions, and doesn't drag an entire ecosystem in with it.
# //Justfile
set dotenv-load
_default:
just --choose
# Cleans up the project without removing the local .env
clean:
git clean -xfd --exclude .env
# Builds all the websites
build: build-typewriter
# Builds the typewriter.press website
build-typewriter:
echo I did something
# Serves the Typewriter website
serve-typewriter-html:
miniserve --index index.html build/typewriter/html
# Serves the Typewriter Gemini pod
serve-typewriter-gemtext:
mkdir -p .cache/agate
agate --content build/typewriter/gemtext --certs .cache/agate --hostname localhost
Naturally, .cache/
has to be added to the .gitignore
file. I use .cache
from the project layout plot.
What's Next
Now that we have the basic boilerplate for a new project, time to start adding .NET into the mix.
2025-06-07 Using Nitride - Introduction - I'm going to start a long, erratic journey to update my publisher website. Along the way, I'm going to document how to create a MfGames.Nitride website from the ground up, along with a lot of little asides about reasoning or purpose.
2025-06-08 Using Nitride - Pipelines - The first major concept of Nitride is the idea of a "pipeline" which are the largest units of working in the system. This shows a relatively complex setup of pipelines that will be used to generate the website.
2025-06-09 Using Nitride - Entities - The continued adventures of creating the Typewriter website using MfGames.Nitride, a C# static site generator. The topic of today is the Entity class and how Nitride uses an ECS (Entity-Component-System) to generate pages.
2025-06-10 Using Nitride - Markdown - Examples and explanations of converting Markdown to HTML using MfGames.Nitride and MarkDig.
2025-06-12 Using Nitride - Front Matter - How to use Nitride to take the front matter from pages and add them as a component into the Entity class.
Metadata
Categories:
Tags: