new website powered by quartz and nix

a screenshot of the new website layout for https://www.exodrifter.space

I’ve finally gotten around to converting my notes into a new website. There, you can find all sorts of things like my blog posts, information about games I’ve made, music albums I’ve composed, and notes I’ve written.

You can see it here: https://www.exodrifter.space

I’m still migrating a lot of stuff over, but hopefully over time all of my written content will eventually live there now. There’s also an rss feed!

This website is a bit different from the usual because it doesn’t host just a blog; it is also the successor to both my logbook and my old website. In fact, the original logbook repository is now being used to make the website with Quartz and Nix, which I’ll explain briefly below.


Quartz has what I would consider a less-than-ideal setup process. Here’s how it works in summary:

  1. Clone the Quartz repository
  2. Put your content in the content/ folder
  3. Configure, customize, and extend Quartz by modifying the source code accordingly
  4. Whenever you want to generate your site, you build the entire project

I don’t think this is a very good idea, as there’s no encapsulation between the application and the user. I won’t spend time explaining my position here though.

Because of Quartz’s setup process, I had a few specific concerns:

  • I don’t want to have all of the Quartz source code in my repository
  • I want to make sure my changes to Quartz continue to be modular and easy to maintain
  • I don’t want to install Quartz’s build dependencies on my system

So, I turned to Nix to address these issues. Now, I’ve never used Nix before but I managed to wade through the terrible documentation on stream to arrive to what I think is a pretty good solution (and I also got some help from Gabby @fullmoon):

{
  description = "A flake for building exodrifter's website";
  inputs = {
    utils.url = "github:numtide/flake-utils/v1.0.0";
    nixpkgs.url = "github:NixOS/nixpkgs/24.05";
  };
 
  outputs = { self, nixpkgs, utils }:
    utils.lib.eachDefaultSystem(
      system:
        let
          pkgs = nixpkgs.legacyPackages.${system};
        in {
          apps.default = {
            type = "app";
 
            program =
              let
                caddyfile = pkgs.writeText "Caddyfile" ''
                  :8080 {
                      root * ${self.packages."${system}".default}/public/
                      file_server
                      try_files {path} {path}.html {path}/ =404
                  }
                '';
 
                formattedCaddyfile = pkgs.runCommand "Caddyfile"
                  { nativeBuildInputs = [ pkgs.caddy ]; }
                  ''(caddy fmt ${caddyfile} || :) > "$out"'';
 
                script =
                  pkgs.writeShellApplication {
                    name = "logbook";
 
                    runtimeInputs = [ pkgs.caddy ];
 
                    text =
                      "caddy run --config ${formattedCaddyfile} --adapter caddyfile";
                  };
 
              in
                "${pkgs.lib.getExe script}";
          };
 
          devShells.default = pkgs.mkShell {
            packages = with pkgs; [
              pkgs.nodejs_22
            ];
          };
          packages = rec {
            default = pkgs.buildNpmPackage rec {
              pname = "quartz";
              version = "4.3.1";
 
              src = pkgs.fetchFromGitHub {
                owner = "jackyzha0";
                repo = "quartz";
                rev = "v${version}";
                hash = "sha256-kID0R/n3ij5uvZ/CXjiLa3oqjghX2U4Zu82huejG6/Q=";
              };
 
              dontNpmBuild = true;
              makeCacheWritable = true;
 
              npmDepsHash = "sha256-qgAzMTtFTShj3xUut73DBCbkt7yTwVjthL8hEgRFdIo=";
 
              installPhase = ''
                runHook preInstall
                npmInstallHook
 
                cd $out/lib/node_modules/@jackyzha0/quartz
 
                # Copy our website content
                rm -r ./content
                mkdir content
                cp -r ${./content}/* ./content
 
                # Override quartz source files
                mv ./quartz/components/index.ts ./quartz/components/index-original.ts
                mv ./quartz/plugins/emitters/index.ts ./quartz/plugins/emitters/index-original.ts
                mv ./quartz/plugins/transformers/index.ts ./quartz/plugins/transformers/index-original.ts
                cp -r ${./quartz}/* ./
 
                $out/bin/quartz build
                mv public/ $out/public/
 
                runHook postInstall
              '';
            };
          };
        }
    );
}

(you can also view this on GitHub)

This flake does essentially every step in the Quartz setup instructions, except without actually pulling the history of Quartz into your repository and by copying my own content to wherever Quartz expects it when it builds. You might notice that some of the re-export modules were moved; that’s so I can add new files that need to be re-exported, but without needing to write code that knows how to edit typescript. Instead, all I do is re-export everything the original index re-exports.

Along with some changes to add developer tools, now all I have to do to build and test my website is nix run!