How to generate dependencies when they don't exist

I’m creating a cmake project to generate a number of typst PDFs. Some of these include images which are also generated by cmake. If I generate the images first I can use typst compile --deps to create a make format dependency file that works. The problem I have is I can’t initially generate that file if the images haven’t been generated yet.

That means I can’t generate the deps file during cmake configure and generating it during the normal cmake compile only works if the image happens to be first in the build order.

Is there a way I can get typst to only generate the deps file (no compilation to PDF) and add dep targets even if the files don’t exist? If not, is there another way to solve this issue that doesn’t involve a custom script to extract dependencies?

In Typst, reads and imports are dynamic; they could for example depend on data in a file that’s read from disk, information in --inputs, or the dimensions of some content. So there isn’t really a way to statically evaluate dependencies without the final project structure and external compiler inputs in place. There is some discussion about this for imports at least in Restrict import paths to static string literals · Issue #8377 · typst/typst · GitHub and Add support for packages hosted in Git repositories · Issue #4040 · typst/typst · GitHub

As a hacky workaround, you could write temporary dummy files such that compilation succeeds, or inject a show rule that disables images for example (see also Try & Catch - Typst Examples Book, if you need more elaborate fallback logic). How that would best be done depends on your specific setup and what you’re most comfortable with (pre-processing, external tooling, building a custom version of the compiler, etc.)

disabling the images wouldn’t work because those are the dependencies I need to create. The pdf is the top-level dependency and the build system needs to know that image is a dependency of the pdf so it can trigger the rules to build the image.
My hack for now is a script that looks for image(path" and extracts the path to create the dependencies for that image. The documents are pretty boilerplate so I’ll just enforce always having them match the regexp I’m using.

Typst should still be generating a depfile when the compiler fails, see the closed & completed issue Also output a depfile with `--make-deps` when the typst compiler fails · Issue #5886 · typst/typst · GitHub

It will, however, only contain the dependencies up to and including the import that failed, so you’ll have to keep compiling & rebuilding the dependencies until you stabilise. The reason for this behavior is exactly as @cady said, imports are dynamic.

Another hacky workaround is to use statefully keep track of all the paths that you try to import, and export a dependency file with the new bundle export:

#let deps = state("deps", ())
#show: doc => context {  
  if target() != "bundle" {
    return doc
  }

  document("main.pdf", doc)
  asset("deps.json", json.encode(deps.final()))
}

#let image(path, ..args) = {
  deps.update(arr => arr + (path,))
  if "deps" in sys.inputs.keys() and sys.inputs.deps == "true" {
    return rect()
  }
  std.image(path, ..args)
}

You can compile the document normally to produce filename.pdf, or with
typst c depstest.typ --format bundle --features bundle --input deps=true
to produce the files <filename>/main.pdf with placeholder images and <filename>/deps.json with all the attempted imports

1 Like