In the development of Typst packages, I found that every time when I am creating a new package, some procedures, like creating a typst.toml config file, a src directory, a README.md documentation and LICENSE.txt file, are always the same. Also, every time I want to publish my package to Typst Universe, I need to select the files that goes to the Typst packages repository. Doing these works manually can be tiresome, so I decide to write a simple Python script typacker to simplify such process.
This script currently has 4 sub-commands: new, export, doc and copy.
Running typacker new <package-name> will create a package directory in the current directory, with a structure displayed below:
- [dir]
<package-name>- [dir]
src- [dir]
_impl lib.typ
- [dir]
typst.tomlREADME.orig.md.gitignore
- [dir]
The newly-created package directory will be automatically initialized by git if git is installed on your device to prepare for version control and GitHub submission.
By default when importing a .typ file as a module, all functions & variables (referred to as "items" below) inside the file will be exposed to the user, including those intended for internal use only. Typst currently does not have visibility control keywords like pub to expose only public items, so some developers choose to prepend underscores before private items to distinguish them from public ones. This strategy, however, still cannot prevent private items from being accessible to the user.
So in package development, I take on a strategy by separating implementation files and export files. Below is an example file structure under this strategy.
- [dir]
example-package- [dir]
src- [dir]
_implgreeter.typ
greeter.typlib.typ
- [dir]
- (other files are omitted)
- [dir]
// src/_impl/greeter.typ
// implementation file
#let greet(target) = {
"Hello, " + target + "!"
} // intended as private
#let say-hello() = {
greet("world")
} // intended as public// src/greeter.typ
// export file
// can have a documentation
/// Some basic greetings.
// expose only public functions
#import "_impl/greeter.typ": say-hello// src/lib.typ
// package entry point
// expose sub-modules
#import "greeter.typ"In this way, only the public function is exposed to user.
#import "@preview/example-package:x.y.z": greeter
#greeter.say-hello()
//#greeter.greet("Alice") // failsTo simplify this process so that you don't need to manually update the export file each time a new public function is written, you can use the export command of typacker. To make this command work, you have to write your implementation files in the following way.
- Before each public function, write
#let /*pub*/ <function-name>()instead of#let <function-name>(). The/*pub*/comment will be recognized by the script so that it can be exported. - If you want the exported function to have a different name, change
/*pub*/to/*pub as <exported-name>*/.