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 [[Bitrot|bitrow]]. A good example is the Typewriter Press[1] and it's associated reflected websites:
Broken - Fantasy including contemporary
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.
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[2]. 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]].
2: https://mfgames.com/mfgames-project-setup-flake/
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[3] 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.
3: //d.moonfire.us/garden/project-layout/
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.
Metadata
Categories:
Tags:
Footer
Below are various useful links within this site and to related sites (not all have been converted over to Gemini).
Source