Published
Towards Deterministic Typst Compilation
Typst is really nice. Being able to compose a PDF in a markup language that doesn't enforce tight coupling between the style and content (like Word or Google Docs do) enables me to fearlessly edit my PDF documents without the mental overload of constantly switching back and forth between adding content and manually fixing the style of the content I added.
And yet, in terms of dependency management, there's still something to be desired. Some features, such as fonts, images, and data loading, depend on external files being available on your system. This isn't something handled automatically as is the case with tools such as npm: as a Typst user, you have to manage the dependencies yourself. And while keeping the necessary files co-located with your Typst source files is always an option, this prevents more advanced usage such as keeping your project in sync with an upstream dependency that updates periodically, whether it's a font, an icon library, or critical data.
There is also the problem of reproducibility: when we run typst compile
, the
result can be different depending on the machine on which it runs. In other
words, its output depends on external state. What we need is
a way to provide a hermetically sealed environment that, given a list of
dependencies, runs typst compile
to always produce the correct result.
Luckily, a solution to both of these issues already exists in the form of Nix.
The Nix Package Manager
Nix is a purely functional package manager. This means packages are built in a manner analogous to how pure functions work: producing the desired output from nothing more than a set of inputs you describe. The beauty of this approach is in the reliability it enables given a well-crafted specification written in the Nix language.
Nix flakes enable a user experience more akin to that of npm or Cargo compared to the base Nix experience:
- A standardized, uniform schema
- Convenient dependency management with lockfiles, programmatic updates, and flake references
- A command-line suite to build and run packages
However, actually using Nix dependencies ("flake inputs") in
a Typst project is non-trivial. You can install them to the Nix
store with nix flake archive
, but without
using derivations you'd have to reference the dependencies
with absolute paths, which is hardly reproducible or user-friendly.
Typix
Typix solves the issue of making Nix dependencies available in Typst projects. Here's what it looks like:
{
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
typix.url = "github:loqusion/typix";
font-awesome = {
url = "github:FortAwesome/Font-Awesome";
flake = false;
};
};
outputs = {
nixpkgs,
typix,
font-awesome,
...
}: let
system = "x86_64-linux";
pkgs = nixpkgs.legacyPackages.${system};
typixLib = typix.lib.${system};
in {
packages.${system}.default = typixLib.buildTypstProject {
src = typixLib.cleanTypstSource ./.;
typstSource = "main.typ";
fontPaths = [
"${pkgs.roboto}/share/fonts/truetype"
];
virtualPaths = [
{
dest = "icons";
src = "${font-awesome}/svgs/regular";
}
];
};
};
}
Let's break it down a bit:
typixLib.buildTypstProject
— Produces a derivation which runstypst compile
in a sandboxed environment where dependencies are automatically made available from the attributes you provide, namelyfontPaths
andvirtualPaths
(and technicallysrc
).src
— Represents a subset of the working directory provided as input totypst compile
.typixLib.cleanTypstSource
— Filters out all files not tracked by version control, as well as all files besides Typst source files (*.typ
).
typstSource
— Specifies a relative path to the entrypoint of your Typst project.fontPaths
— List of paths to fonts.virtualPaths
— List of paths; eachsrc
is made virtually available atdest
.
Running nix build
will produce a PDF whose symlink is available at ./result
.
However, more often than not you'll want the actual PDF file:
buildTypstProjectLocal
is a convenience
wrapper built for that purpose.
Typix also has a derivation builder for watching the input file and recompiling
on changes (via typst watch
): watchTypstProject
.
Using this in tandem with a PDF viewer that updates in real time (such as
Zathura) is an insane quality of life enhancement comparable with the
official Typst web app.
More information can be found on GitHub.
Limitations
Typst packages are currently unsupported. This is because the
Typst package manager requires an internet connection during the invocation of
typst compile
, which is unavailable in the Nix sandbox. Support may be added
in the future, but for now there is a guide for getting
them to work today.