String interpolation in files, without `substituteAll`

So here’s my use case. I have this in my HomeManager config:

  home.file.".config/i3/config".source = ../dotfiles/i3;

… where dotfiles/i3 is ~300 lines of i3 config.

Now inside this i3 config I’d like to reference nix packages, and other configurations (e.g. home dir, wallpaper, …). But since it’s 300+ lines I don’t want to inline the whole config into the nix file.

I currently use substituteAll, but I dislike it a lot, I think it’s hacky and messy, it has a lot of weird foot gun behavior, and I dislike that I have to declare every single substitution by hand (e.g. I can’t just pass in pkgs and then reference pkgs.feh, instead I have to declare every single package). (Apparently others think so too).

Are there any alternatives to this?

substituteAll is just an in-house templating framework for Nixpkgs. You could theoretically substitute it (heh) for any other generic templating framework (of which there are thousands I’m sure) and wrap all of that in a runCommand.
The interesting bit is how you pass the data to the templating framework. They must accept a serialised format for the mapping we can easily generate such as JSON.

The higher effort but better and (IMHO) cooler option would be to write a pure generator which turns pure Nix data into a i3 config syntax string. See Nixpkgs 23.11 manual | Nix & NixOS for more info.
Though i3 config syntax is quite different from most; more resembling a DSL than a data serialisation format.