Finds imports and their line numbers
finds_imports_with_line_numbers
#import "@preview/cetz:0.1.0": canvas
#import "@preview/tablex:0.0.1"
| Line | Package | Version |
|---|---|---|
| 1 | cetz | 0.1.0 |
| 3 | tablex | 0.0.1 |
Command-line tool · Rust
typst-updater scans a project for @preview package imports, checks them against the
Typst Universe package index, shows you which ones are out of date and
rewrites just the version numbers once you say yes.
$ typst-updater Scanning 2 Typst file(s) in . Fetching package index from https://packages.typst.org/preview/index.json Available updates: chapters/ch1.typ L4 cetz 0.1.0 → 0.10.12 breaking main.typ L1 cetz 0.1.0 → 0.10.12 breaking L3 tablex 0.0.1 → 0.0.9 breaking Apply 3 update(s)? [y/N] y ✔ chapters/ch1.typ line 4: @preview/cetz 0.1.0 → 0.10.12 ✔ main.typ line 1: @preview/cetz 0.1.0 → 0.10.12 line 3: @preview/tablex 0.0.1 → 0.0.9 Updated 3 import(s) across 2 file(s).
Typst package imports always name an exact version, such as "@preview/cetz:0.1.0". Nothing
tells you when a newer release lands, so documents quietly fall behind.
A thesis or a set of templates can pull the same package into dozens of files. Finding and editing each import by hand is tedious and easy to get wrong.
Only the version text inside each import string is replaced. Formatting, comments and everything else in the file are left exactly as they were, and a file you edit while the prompt is open is skipped rather than overwritten.
Run it from the root of a Typst project, or point it somewhere else with --path.
$ typst-updater --help Find Typst package imports and update them to their latest versions Usage: typst-updater [OPTIONS] Options: -p, --path <PATH> Directory to search for *.typ files [default: .] -y, --yes Apply updates without prompting [alias: --force] --include-docs Also update imports in comments and raw blocks, such as the examples in a package's documentation -h, --help Print help -V, --version Print version
.typ files, skipping hidden directories such as .git.packages.typst.org and keep the highest version of each package.@preview import against that index.0.1 to 0.2 or 1.x to
2.0, are marked breaking.--yes.
--yes (or its older name --force) skips the prompt, which suits CI jobs and
scripted upgrades. Commit first so you can review the result with git diff.
Imports inside comments and raw blocks are left alone, since they are usually examples rather than code.
Pass --include-docs to update them too, for instance in a package's own documentation.
Each example below is generated from a unit test in
src/main.rs, so the behaviour shown here is the
behaviour the test suite checks.
finds_imports_with_line_numbers
#import "@preview/cetz:0.1.0": canvas
#import "@preview/tablex:0.0.1"
| Line | Package | Version |
|---|---|---|
| 1 | cetz | 0.1.0 |
| 3 | tablex | 0.0.1 |
finds_multiline_and_code_mode_imports
#import "@preview/headstamp:0.1.0": (
a, b,
)
#{ import "@preview/cetz:0.1.0" }
Both headstamp and cetz are found. The import
list can span several lines, and an import inside a #{ … } code block
counts too.
ignores_non_package_and_non_preview_imports
#import "custom-theme.typ": custom
#import "@local/mine:0.1.0"
#include "@preview/cetz:0.1.0"
#let s = "@preview/cetz:0.1.0"
None of these lines match. Local files, the @local namespace, #include and plain
strings that only look like a package spec are all ignored.
find_updates_only_reports_newer_known_packages
#import "@preview/old:0.1.0"
#import "@preview/current:1.0.0"
#import "@preview/ahead:2.0.0"
#import "@preview/unknown:0.1.0"
| Import | Index | Result |
|---|---|---|
old 0.1.0 |
0.2.0 | update to 0.2.0 |
current 1.0.0 |
1.0.0 | up to date |
ahead 2.0.0 |
1.0.0 | never downgraded |
unknown 0.1.0 |
— | not in index, skipped |
latest_versions_picks_highest_regardless_of_order
[
{ "name": "cetz", "version": "0.2.0" },
{ "name": "cetz", "version": "0.10.0" },
{ "name": "cetz", "version": "0.9.1" },
{ "name": "tablex", "version": "0.0.9" },
{ "name": "cetz", "version": "not-a-version" }
]
The latest cetz is 0.10.0
: as text, "0.9.1" would sort above it, but semver ordering doesn't.
The order of entries in the index doesn't matter, and an entry that isn't a valid version is skipped
(latest_versions_skips_unparseable_versions).
rewrite_changes_only_versions_and_handles_length_changes
#import "@preview/cetz:0.1.0": canvas
// keep this
#import "@preview/tablex:0.0.1"
#import "@preview/cetz:0.10.12": canvas
// keep this
#import "@preview/tablex:0.0.9"
Comments and import lists are untouched.
A version that changes length, like 0.1.0 becoming
0.10.12, doesn't shift any later edits, because edits are applied from the end
of the file backwards.
finds_typ_files_recursively_skipping_hidden_dirs
my-thesis/ ├── .git/ │ └── x.typ skipped: hidden directory ├── chapters/ │ └── ch1.typ scanned ├── main.typ scanned └── notes.txt skipped: not a .typ file
Files are visited in name order, so they are reported as
chapters/ch1.typ then main.typ.
Each release ships archives for Linux (x86_64, aarch64), macOS (Apple
silicon, Intel) and Windows (x86_64). A rolling snapshot prerelease tracks main.
cargo install --locked \
--git https://github.com/talios/typst-updater
mise run install
Runs the tests, builds a release binary and installs it into ~/.cargo/bin.