Writing with Typst
This article is the stereotypical ‘I wrote my own blog static site generator’. I just needed an excuse to test my new Typst based pipeline. This article is written in Typst with it’s experimental HTML export.
Prior to this there was a simple markdown version of all my articles, which would start something like this:
--- title_html: Advent of Code 2022 in Rust published: 2022-12-28 pipeline: ['md'] --- # Advent of Code 2022 in Rust ...
The YAML frontmatter provides the metadata for the rest of the generator to do it’s job. Nothing speical. The pipeline dicatates what step are going to transform the text after the frontmatter. This article has a ['typst'] pipeline.
By default these pipelines produce HTML at the end, and there’s a final pipeline step that applies things like syntax highlighting to code blocks.
Since the static site generator is written in Rust I can use Typst as a crate, this is pretty much the whole thing:
struct BlogWorld { source: Source, library: LazyHash<Library>, book: LazyHash<FontBook>, } impl World for BlogWorld { // ... } const PREAMBLE: &str = include_str!("preamble.typ"); pub fn typst_pipeline(content: &str) -> Result<String> { let combined = format!("{}\n{}", PREAMBLE, content); let world = BlogWorld::new(&combined); let warned = typst::compile::<typst_html::HtmlDocument>(&world); let doc = warned.output.map_err(|errors| { let msgs: Vec<_> = errors.iter().map(|e| e.message.as_str()).collect(); anyhow!("typst compilation failed: {}", msgs.join("; ")) })?; let full_html = typst_html::html(&doc, &typst_html::HtmlOptions::default()) .map_err(|errors| anyhow!("typst html export failed: {} errors", errors.len()))?; let dom = kuchiki::parse_html().one(full_html); let body = dom .select_first("body") .ok() .context("typst html output has no body")?; let mut fragment = String::new(); for child in body.as_node().children() { let mut buf = vec![]; child.serialize(&mut buf).unwrap(); fragment.push_str(&String::from_utf8(buf).unwrap()); } Ok(fragment) }
The PREAMBLE is a bit of typst code to make code blocks conform to the HTML I need for the syntax highlighter to work:
#show raw: it => if it.block { html.elem("pre", html.elem("code", attrs: (class: "language-" + it.lang), it.text)) } else { html.elem("code", it.text) }
The hope is that Typst will be more flexible that Markdown and allow me to do more interesting things.
Plus maths: , which I’m sure I’ll find a use for.