Repository: loqusion/typix Branch: main Commit: f3dd958107de Files: 104 Total size: 120.5 KB Directory structure: gitextract_u9nhbyq8/ ├── .editorconfig ├── .envrc ├── .github/ │ ├── FUNDING.yml │ ├── ISSUE_TEMPLATE/ │ │ ├── 1-bug_report.md │ │ ├── 2-feature_request.md │ │ └── 3-typst_packages.md │ └── workflows/ │ ├── pages.yml │ ├── publish.yml │ ├── test.yml │ └── update-lockfile.yml ├── .gitignore ├── .markdownlint-cli2.jsonc ├── .markdownlint.json ├── .prettierignore ├── CHANGELOG.md ├── LICENSE ├── README.md ├── checks/ │ ├── .gitignore │ ├── build-local.nix │ ├── check-emojis.py │ ├── clean/ │ │ ├── bad.c │ │ ├── lib.typ │ │ ├── metadata.toml │ │ ├── sub/ │ │ │ ├── bad.py │ │ │ ├── sub.typ │ │ │ └── typst.toml │ │ ├── typst.toml │ │ └── works.bib │ ├── clean-expected/ │ │ ├── lib.typ │ │ ├── metadata.toml │ │ ├── sub/ │ │ │ ├── sub.typ │ │ │ └── typst.toml │ │ ├── typst.toml │ │ └── works.bib │ ├── date/ │ │ └── main.typ │ ├── default.nix │ ├── emoji/ │ │ └── main.typ │ ├── overlapping-virtual-paths/ │ │ └── main.typ │ ├── simple/ │ │ └── main.typ │ ├── simple-with-fonts/ │ │ └── main.typ │ ├── simple-with-multiple-parameters/ │ │ └── main.typ │ ├── simple-with-virtual-paths/ │ │ └── main.typ │ ├── typst-packages/ │ │ └── main.typ │ ├── virtual-paths.nix │ └── watch.nix ├── docs/ │ ├── .gitignore │ ├── SUMMARY.md │ ├── api/ │ │ ├── derivations/ │ │ │ ├── build-typst-project-local.md │ │ │ ├── build-typst-project.md │ │ │ ├── common/ │ │ │ │ ├── emoji-font.md │ │ │ │ ├── font-paths-example.md │ │ │ │ ├── font-paths.md │ │ │ │ ├── install-phase-command.md │ │ │ │ ├── script-name.md │ │ │ │ ├── src.md │ │ │ │ ├── typst-compile-command.md │ │ │ │ ├── typst-opts-example.md │ │ │ │ ├── typst-opts.md │ │ │ │ ├── typst-packages.md │ │ │ │ ├── typst-project-output.md │ │ │ │ ├── typst-project-source.md │ │ │ │ ├── virtual-paths-example.md │ │ │ │ └── virtual-paths.md │ │ │ ├── dev-shell.md │ │ │ ├── mk-typst-derivation.md │ │ │ └── watch-typst-project.md │ │ ├── derivations.md │ │ ├── utilities/ │ │ │ └── clean-typst-source.md │ │ └── utilities.md │ ├── book.toml │ ├── getting-started.md │ └── recipes/ │ ├── adding-dependencies.md │ ├── declaring-a-shell-environment.md │ ├── specifying-sources.md │ └── using-typst-packages.md ├── examples/ │ ├── .gitignore │ ├── quick-start/ │ │ ├── .gitignore │ │ ├── flake.nix │ │ └── main.typ │ ├── typst-packages/ │ │ ├── flake.nix │ │ └── main.typ │ └── typst-packages-unpublished/ │ ├── flake.nix │ └── main.typ ├── flake.nix ├── lib/ │ ├── buildTypstProject.nix │ ├── buildTypstProjectLocal.nix │ ├── cleanTypstSource.nix │ ├── coerceVirtualPathAttr.nix │ ├── default.nix │ ├── devShell.nix │ ├── emojiFontPathFromString.nix │ ├── fetchTypstPackages.nix │ ├── inferTypstProjectOutput.nix │ ├── linkVirtualPaths.nix │ ├── mkTypstDerivation.nix │ ├── setupHooks/ │ │ ├── copyVirtualPaths.nix │ │ ├── copyVirtualPathsHook.sh │ │ ├── unsetSourceDateEpoch.nix │ │ ├── unsetSourceDateEpochHook.sh │ │ └── unsetSourceDateEpochScript.sh │ ├── typstOptsFromArgs.nix │ └── watchTypstProject.nix ├── pkgs.nix └── release.nix ================================================ FILE CONTENTS ================================================ ================================================ FILE: .editorconfig ================================================ root = true [*] charset = utf-8 env_of_line = lf indent_style = space trim_trailing_whitespace = true insert_final_newline = true [*.nix] indent_size = 2 ================================================ FILE: .envrc ================================================ use flake . --impure ================================================ FILE: .github/FUNDING.yml ================================================ ko_fi: loqusion ================================================ FILE: .github/ISSUE_TEMPLATE/1-bug_report.md ================================================ --- name: Bug report about: Create a report to help us improve title: '' labels: bug assignees: '' --- **Describe the bug** A clear and concise description of what the bug is. **To Reproduce** Steps to reproduce the behavior: 1. Go to '...' 2. Click on '....' 3. Scroll down to '....' 4. See error **Expected behavior** A clear and concise description of what you expected to happen. **Versions** - OS: **Additional context** Add any other context about the problem here. ================================================ FILE: .github/ISSUE_TEMPLATE/2-feature_request.md ================================================ --- name: Feature request about: Suggest an idea for this project title: '' labels: '' assignees: '' --- **Is your feature request related to a problem? Please describe.** A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] **Describe the solution you'd like** A clear and concise description of what you want to happen. **Describe alternatives you've considered** A clear and concise description of any alternative solutions or features you've considered. **Additional context** Add any other context or screenshots about the feature request here. ================================================ FILE: .github/ISSUE_TEMPLATE/3-typst_packages.md ================================================ --- name: Typst packages about: For issues related to Typst packages title: "[Typst packages]: " labels: typst packages assignees: '' --- ================================================ FILE: .github/workflows/pages.yml ================================================ name: Build and deploy documentation on: push: branches: - main workflow_dispatch: permissions: contents: read pages: write id-token: write concurrency: group: pages cancel-in-progress: false jobs: build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 with: fetch-depth: 0 - uses: cachix/install-nix-action@v31 - uses: cachix/cachix-action@v16 with: name: typst-nix authToken: ${{ secrets.CACHIX_AUTH_TOKEN }} - name: Setup Pages id: pages uses: actions/configure-pages@v5 - name: Build with mdBook run: | nix build --accept-flake-config .#docs --out-link result --print-build-logs cp -RLT result book - name: Upload artifact uses: actions/upload-pages-artifact@v4 with: path: ./book deploy: environment: name: github-pages url: ${{ steps.deployment.outputs.page_url }} runs-on: ubuntu-latest needs: build steps: - name: Deploy to GitHub Pages id: deployment uses: actions/deploy-pages@v4 ================================================ FILE: .github/workflows/publish.yml ================================================ name: Publish flake to FlakeHub on: release: types: [published] workflow_dispatch: inputs: tag: description: "The existing tag to publish to FlakeHub" type: "string" required: true jobs: publish-flakehub: runs-on: ubuntu-latest permissions: id-token: write contents: read steps: - name: Get git tag id: git-tag run: | if [ -z "$TAG" ]; then echo "::error::Could not determine git tag" exit 1 fi echo "tag=$TAG" >>"$GITHUB_OUTPUT" env: TAG: ${{ inputs.tag || github.ref_name }} - uses: actions/checkout@v4 with: ref: ${{ format('refs/tags/{0}', steps.git-tag.outputs.tag) }} - uses: cachix/install-nix-action@v31 - uses: DeterminateSystems/flakehub-push@v6 with: visibility: public name: loqusion/typix tag: ${{ steps.git-tag.outputs.tag }} ================================================ FILE: .github/workflows/test.yml ================================================ name: Run flake checks on: pull_request: types: [opened, reopened, synchronize] push: branches: - main - "ci*" permissions: contents: read jobs: checks: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: cachix/install-nix-action@v31 - name: flake check run: nix flake check examples: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: cachix/install-nix-action@v31 - name: check examples run: | set -euo pipefail for f in $(find ./examples -maxdepth 1 -mindepth 1 -type d | sort -u); do nix flake check --no-write-lock-file "$f" done ================================================ FILE: .github/workflows/update-lockfile.yml ================================================ name: Update flake lockfile on: workflow_dispatch: schedule: - cron: "39 8 7,22 * *" permissions: contents: read jobs: update: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: cachix/install-nix-action@v31 - uses: cachix/cachix-action@v16 with: name: typst-nix authToken: ${{ secrets.CACHIX_AUTH_TOKEN }} - name: Update flake.lock id: update uses: DeterminateSystems/update-flake-lock@v27 with: token: ${{ secrets.GH_TOKEN_FOR_UPDATES }} commit-msg: "chore: update flake.lock" pr-title: "chore: update flake.lock" pr-body: | Automated changes by the [update-flake-lock](https://github.com/DeterminateSystems/update-flake-lock) GitHub Action. ``` {{ env.GIT_COMMIT_MESSAGE }} ``` pr-labels: | dependencies automated nix-options: --accept-flake-config - name: Merge PR run: | gh pr merge --squash --delete-branch --auto "$PR_NUMBER" env: GH_TOKEN: ${{ secrets.GH_TOKEN_FOR_UPDATES }} PR_NUMBER: ${{ steps.update.outputs.pull-request-number }} ================================================ FILE: .gitignore ================================================ /result .direnv/ *.pdf ================================================ FILE: .markdownlint-cli2.jsonc ================================================ { "ignores": ["CHANGELOG.md"], } ================================================ FILE: .markdownlint.json ================================================ { "no-inline-html": false } ================================================ FILE: .prettierignore ================================================ docs/book docs/theme flake.lock ================================================ FILE: CHANGELOG.md ================================================ # [0.3.2] (June 27, 2025) ### Added - `typstOpts` repeatable flags ([#59]) [#59]: https://github.com/loqusion/typix/pull/59 # [0.3.1] (April 02, 2025) ### Added - Typst packages support (unstable) ([#51]) - `with-typst-packages-unpublished` template ([#51]) ### Changed - Use `unstable_typstPackages` in `with-typst-packages` template ([#51]) [#51]: https://github.com/loqusion/typix/pull/51 # [0.3.0] (January 27, 2025) ### Added - **BREAKING CHANGE:** Include an emoji font in all derivations by default ([#44]) - Add `emojiFont` attribute in all derivations ([#44]) [#44]: https://github.com/loqusion/typix/pull/44 # [0.2.0] (January 3, 2025) ### Fixed - **BREAKING CHANGE:** Unset `SOURCE_DATE_EPOCH` environment variable in all derivations ([#40]) [#40]: https://github.com/loqusion/typix/pull/40 # [0.1.7] (December 29, 2024) ### Changed - Improve build times ([#38]) [#38]: https://github.com/loqusion/typix/pull/38 # [0.1.6] (October 30, 2024) ### Fixed - Include `metadata.toml` in `cleanTypstSource` ([#29]) [#29]: https://github.com/loqusion/typix/pull/29 # [0.1.5] (July 13, 2024) ### Fixed - Include `typst.toml` in `cleanTypstSource` ([#20]) [#20]: https://github.com/loqusion/typix/pull/20 # [0.1.4] (May 2, 2024) ### Fixed - Handle `src` attribute correctly in `virtualPaths` ([#12]) [#12]: https://github.com/loqusion/typix/pull/12 # [0.1.3] (March 26, 2024) ### Fixed - Include missing dependencies in `buildTypstProjectLocal` and `watchTypstProject` # [0.1.2] (February 29, 2024) - Test release # [0.1.1] (February 29, 2024) - Add release script - Add `description` attribute to `flake.nix` # [0.1.0] (February 29, 2024) - First release [0.3.2]: https://github.com/loqusion/typix/compare/0.3.1...0.3.2 [0.3.1]: https://github.com/loqusion/typix/compare/0.3.0...0.3.1 [0.3.0]: https://github.com/loqusion/typix/compare/0.2.0...0.3.0 [0.2.0]: https://github.com/loqusion/typix/compare/0.1.7...0.2.0 [0.1.7]: https://github.com/loqusion/typix/compare/0.1.6...0.1.7 [0.1.6]: https://github.com/loqusion/typix/compare/0.1.5...0.1.6 [0.1.5]: https://github.com/loqusion/typix/compare/0.1.4...0.1.5 [0.1.4]: https://github.com/loqusion/typix/compare/0.1.3...0.1.4 [0.1.3]: https://github.com/loqusion/typix/compare/0.1.2...0.1.3 [0.1.2]: https://github.com/loqusion/typix/compare/0.1.1...0.1.2 [0.1.1]: https://github.com/loqusion/typix/compare/0.1.0...0.1.1 [0.1.0]: https://github.com/loqusion/typix/commits/0.1.0-1/ ================================================ FILE: LICENSE ================================================ MIT License Copyright (c) 2023 loqusion Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: README.md ================================================

Typix Logo
Typix

FlakeHub version

Typix aims to make it easier to use [Nix](https://nixos.org/) in [Typst](https://github.com/typst/typst) projects. - **Dependency management**: supports arbitrary dependencies including fonts, images, and data - **Reproducible**: via a hermetically sealed build environment - **Extensible**: full support for [Typst packages](https://loqusion.github.io/typix/recipes/using-typst-packages.html) ## Features - Automatically fetch dependencies and compile in a single command (`nix run .#build`) - Watch input files and recompile on changes (`nix run .#watch`) - Set up a shell environment with all dependencies made available via environment variables and symlinks - Extensible via [`mkTypstDerivation`](https://loqusion.github.io/typix/api/derivations/mk-typst-derivation.html) - Support for dependencies such as [fonts](https://typst.app/docs/reference/text/text/#parameters-font), [images](https://typst.app/docs/reference/visualize/image/), and [data](https://typst.app/docs/reference/data-loading/) - [Typst packages](https://loqusion.github.io/typix/recipes/using-typst-packages.html) fetched from the official Typst packages CDN ## Getting Started After [installing Nix](https://github.com/DeterminateSystems/nix-installer) and [enabling flakes](https://nixos.wiki/wiki/Flakes#Enable_flakes_permanently_in_NixOS), you can initialize a flake from the default template: ```bash nix flake init --refresh -t github:loqusion/typix ``` > Alternatively, you can use a template demonstrating [Typst packages](https://loqusion.github.io/typix/recipes/using-typst-packages.html) > usage: > > ```bash > nix flake init --refresh -t 'github:loqusion/typix#with-typst-packages' > ``` Here are some commands you can run from any template: - `nix run .#watch` — watch the input files and recompile on changes - `nix run .#build` — compile and copy the output to the current directory --- For more information, check out [the docs](https://loqusion.github.io/typix/). ================================================ FILE: checks/.gitignore ================================================ *.pdf ================================================ FILE: checks/build-local.nix ================================================ { lib, pkgs, buildTypstProjectLocal, }: runCommandDrvAttr: args: let build-local-drv = buildTypstProjectLocal ({ name = "build-local-check"; } // args // runCommandDrvAttr); in pkgs.runCommand "build-local" (runCommandDrvAttr // { nativeBuildInputs = (runCommandDrvAttr.nativeBuildInputs or []) ++ [ build-local-drv ]; }) '' ${lib.getExe build-local-drv} "$out" '' ================================================ FILE: checks/check-emojis.py ================================================ """ Checks if emoji characters in a PDF file are rendered with an appropriate font face. This script is dumb, and only checks if the font face name for text with an emoji matches one of a set of patterns given on the command line. This assumption is naive, and may not hold if the font names change, or if a font name not supporting emoji happens to match any of the patterns. """ import re import sys from collections.abc import Iterator from dataclasses import dataclass from typing import Final import emoji import pdfplumber @dataclass class TextElement: text: str font: str class InvalidEmojiFontException(Exception): def __init__(self, text_element: TextElement, font_patterns: list[re.Pattern]): self.text_element = text_element self.font_patterns = font_patterns self.message = ( f'Detected invalid font for text containing emoji character: "{text_element.text}"\n' f'Font: "{text_element.font}"\n' f"Font did not match any of the following patterns: {', '.join((f"'{pat.pattern}'" for pat in font_patterns))}" ) super().__init__(self.message) class PDFChecker: pdf_path: str def __init__(self, pdf_path: str): self.pdf_path = pdf_path def check_emojis(self, font_patterns: list[re.Pattern]): text_elements = self._extract_text_elements() for text_element in text_elements: if emoji.emoji_count(text_element.text) == 0: continue if not any(pat.search(text_element.font) for pat in font_patterns): raise InvalidEmojiFontException(text_element, font_patterns) def _extract_text_elements(self) -> Iterator[TextElement]: with pdfplumber.open(self.pdf_path) as pdf: for page in pdf.pages: words = page.extract_words( keep_blank_chars=True, use_text_flow=True, extra_attrs=["fontname"], ) for word in words: yield TextElement( text=word["text"], font=word["fontname"], ) class InvalidArgumentException(Exception): USAGE: Final = f"usage: {sys.argv[0]} [...]" def __init__(self, message: str): self.message = message + "\n" + InvalidArgumentException.USAGE super().__init__(self.message) def main(): pdf_path = sys.argv[1] checker = PDFChecker(pdf_path) font_patterns = list(map(lambda pat: re.compile(pat, re.IGNORECASE), sys.argv[2:])) if len(font_patterns) == 0: raise InvalidArgumentException( "expected one or more patterns given as arguments" ) checker.check_emojis(font_patterns) if __name__ == "__main__": try: main() except InvalidEmojiFontException as err: print(f"test failed: {err}", file=sys.stderr) sys.exit(1) except InvalidArgumentException as err: print(f"error: {err}", file=sys.stderr) sys.exit(2) except Exception: raise ================================================ FILE: checks/clean/bad.c ================================================ #include int main(void) { printf("Hello, World!\n"); return 0; } ================================================ FILE: checks/clean/lib.typ ================================================ #bibliography("works.bib") ================================================ FILE: checks/clean/metadata.toml ================================================ #:schema https://raw.githubusercontent.com/mintyfrankie/brilliant-CV/main/metadata.toml.schema.json # Set the output language # INFO: value must matches folder suffix; i.e "zh" -> "./modules_zh" language = "en" [layout] # Optional values: skyblue, red, nephritis, concrete, darknight # You can also use a custom color by hex code i.e. "#1E90FF" awesome_color = "skyblue" # Skips are for controlling the spacing between sections and entries before_section_skip = "1pt" before_entry_skip = "1pt" before_entry_description_skip = "1pt" [layout.fonts] regular_fonts = ["Source Sans Pro", "Source Sans 3"] header_font = "Roboto" [layout.header] # Optional values: left, center, right header_align = "left" # Decide if you want to display profile photo or not display_profile_photo = true [layout.entry] # Decide if you want to put your company in bold or your position in bold display_entry_society_first = true # Decide if you want to display organisation logo or not display_logo = true [inject] # Decide if you want to inject AI prompt or not inject_ai_prompt = false # Decide if you want to inject keywords or not inject_keywords = true injected_keywords_list = ["Data Analyst", "GCP", "Python", "SQL", "Tableau"] [personal] first_name = "John" last_name = "Doe" # The order of this section will affect how the entries are displayed # The custom value is for any additional information you want to add, name it as custom-1, custom-2, etc. [personal.info] github = "mintyfrankie" phone = "+33 6 12 34 56 78" email = "john.doe@me.org" linkedin = "johndoe" # gitlab = "mintyfrankie" # homepage = "jd.me.org" # orcid = "0000-0000-0000-0000" # researchgate = "John-Doe" # extraInfo = "I am a cool kid" [personal.info.custom-1] # image = "" # Example: image("./path/to/image.png") awesomeIcon = "graduation-cap" # Example: "graduation-cap" see https://typst.app/universe/package/fontawesome/ text = "PhD" link = "https://www.example.com" # add a new section if you want to include the language of your choice # i.e. [[lang.ru]] # each section must contains the following fields [lang.en] header_quote = "Experienced Data Analyst looking for a full time job starting from now" cv_footer = "Curriculum vitae" letter_footer = "Cover letter" [lang.fr] header_quote = "Analyste de données expérimenté à la recherche d'un emploi à temps plein disponible dès maintenant" cv_footer = "Résumé" letter_footer = "Lettre de motivation" [lang.zh] header_quote = "具有丰富经验的数据分析师,随时可入职" cv_footer = "简历" letter_footer = "申请信" # For languages that are not written in Latin script # Currently supported non-latin language codes: ("zh", "ja", "ko", "ru") [lang.non_latin] name = "王道尔" font = "Heiti SC" ================================================ FILE: checks/clean/sub/bad.py ================================================ if __name__ == "__main__": print("Hello, World!") ================================================ FILE: checks/clean/sub/sub.typ ================================================ ================================================ FILE: checks/clean/sub/typst.toml ================================================ [package] name = "sub" version = "0.1.0" entrypoint = "sub.typ" ================================================ FILE: checks/clean/typst.toml ================================================ [package] name = "clean" version = "0.1.0" entrypoint = "lib.typ" ================================================ FILE: checks/clean/works.bib ================================================ \begin{thebibliography}{9} \bibitem{texbook} Donald E. Knuth (1986) \emph{The \TeX{} Book}, Addison-Wesley Professional. \bibitem{lamport94} Leslie Lamport (1994) \emph{\LaTeX: a document preparation system}, Addison Wesley, Massachusetts, 2nd ed. \end{thebibliography} ================================================ FILE: checks/clean-expected/lib.typ ================================================ #bibliography("works.bib") ================================================ FILE: checks/clean-expected/metadata.toml ================================================ #:schema https://raw.githubusercontent.com/mintyfrankie/brilliant-CV/main/metadata.toml.schema.json # Set the output language # INFO: value must matches folder suffix; i.e "zh" -> "./modules_zh" language = "en" [layout] # Optional values: skyblue, red, nephritis, concrete, darknight # You can also use a custom color by hex code i.e. "#1E90FF" awesome_color = "skyblue" # Skips are for controlling the spacing between sections and entries before_section_skip = "1pt" before_entry_skip = "1pt" before_entry_description_skip = "1pt" [layout.fonts] regular_fonts = ["Source Sans Pro", "Source Sans 3"] header_font = "Roboto" [layout.header] # Optional values: left, center, right header_align = "left" # Decide if you want to display profile photo or not display_profile_photo = true [layout.entry] # Decide if you want to put your company in bold or your position in bold display_entry_society_first = true # Decide if you want to display organisation logo or not display_logo = true [inject] # Decide if you want to inject AI prompt or not inject_ai_prompt = false # Decide if you want to inject keywords or not inject_keywords = true injected_keywords_list = ["Data Analyst", "GCP", "Python", "SQL", "Tableau"] [personal] first_name = "John" last_name = "Doe" # The order of this section will affect how the entries are displayed # The custom value is for any additional information you want to add, name it as custom-1, custom-2, etc. [personal.info] github = "mintyfrankie" phone = "+33 6 12 34 56 78" email = "john.doe@me.org" linkedin = "johndoe" # gitlab = "mintyfrankie" # homepage = "jd.me.org" # orcid = "0000-0000-0000-0000" # researchgate = "John-Doe" # extraInfo = "I am a cool kid" [personal.info.custom-1] # image = "" # Example: image("./path/to/image.png") awesomeIcon = "graduation-cap" # Example: "graduation-cap" see https://typst.app/universe/package/fontawesome/ text = "PhD" link = "https://www.example.com" # add a new section if you want to include the language of your choice # i.e. [[lang.ru]] # each section must contains the following fields [lang.en] header_quote = "Experienced Data Analyst looking for a full time job starting from now" cv_footer = "Curriculum vitae" letter_footer = "Cover letter" [lang.fr] header_quote = "Analyste de données expérimenté à la recherche d'un emploi à temps plein disponible dès maintenant" cv_footer = "Résumé" letter_footer = "Lettre de motivation" [lang.zh] header_quote = "具有丰富经验的数据分析师,随时可入职" cv_footer = "简历" letter_footer = "申请信" # For languages that are not written in Latin script # Currently supported non-latin language codes: ("zh", "ja", "ko", "ru") [lang.non_latin] name = "王道尔" font = "Heiti SC" ================================================ FILE: checks/clean-expected/sub/sub.typ ================================================ ================================================ FILE: checks/clean-expected/sub/typst.toml ================================================ [package] name = "sub" version = "0.1.0" entrypoint = "sub.typ" ================================================ FILE: checks/clean-expected/typst.toml ================================================ [package] name = "clean" version = "0.1.0" entrypoint = "lib.typ" ================================================ FILE: checks/clean-expected/works.bib ================================================ \begin{thebibliography}{9} \bibitem{texbook} Donald E. Knuth (1986) \emph{The \TeX{} Book}, Addison-Wesley Professional. \bibitem{lamport94} Leslie Lamport (1994) \emph{\LaTeX: a document preparation system}, Addison Wesley, Massachusetts, 2nd ed. \end{thebibliography} ================================================ FILE: checks/date/main.typ ================================================ #let UNIX_EPOCH = datetime( year: 1980, month: 1, day: 1, ) #assert.ne( datetime.today(), UNIX_EPOCH, ) ================================================ FILE: checks/default.nix ================================================ { pkgs, myLib, }: let inherit (pkgs) lib; inherit (lib.strings) escapeShellArg concatMapStringsSep; onlyDrvs = lib.filterAttrs (_: lib.isDerivation); in onlyDrvs (lib.makeScope myLib.newScope (self: let callPackage = self.newScope {}; typstSource = "main.typ"; fontPaths = [ "${pkgs.roboto}/share/fonts/truetype" ]; virtualPaths = [ { src = ./fixtures/icons; dest = "icons"; } ]; unstable_typstPackages = [ { name = "example"; version = "0.1.0"; hash = "sha256-R18Xv3AoZsTtMycRasczTsje5yIfiURIxtDICQ4mvho="; } { name = "cetz"; version = "0.3.4"; hash = "sha256-5w3UYRUSdi4hCvAjrp9HslzrUw7BhgDdeCiDRHGvqd4="; } { name = "polylux"; version = "0.4.0"; hash = "sha256-4owP2KiyaaASS1YZ0Hs58k6UEVAqsRR3YdGF26ikosk="; } # Required by cetz { name = "oxifmt"; version = "0.2.1"; hash = "sha256-8PNPa9TGFybMZ1uuJwb5ET0WGIInmIgg8h24BmdfxlU="; } ]; in rec { buildLocal = callPackage ./build-local.nix {}; buildLocalSimple = buildLocal {} { inherit typstSource; src = myLib.cleanTypstSource ./simple; }; buildLocalSimpleWithFonts = buildLocal {} { inherit fontPaths typstSource; src = myLib.cleanTypstSource ./simple-with-fonts; }; buildLocalSimpleWithVirtualPaths = buildLocal {} { inherit virtualPaths typstSource; src = myLib.cleanTypstSource ./simple-with-virtual-paths; }; buildLocalWithMultipleParameters = buildLocal {} { inherit typstSource; src = myLib.cleanTypstSource ./simple-with-multiple-parameters; typstOpts = { input = ["key1=value1" "key2=value2" "key3= --spaces-are-properly-escaped" "key4='quotes-are-properly-escaped\""]; }; }; buildLocalWithTypstPackages = buildLocal {} { inherit typstSource; src = myLib.cleanTypstSource ./typst-packages; inherit unstable_typstPackages; }; clean = myLib.mkTypstDerivation { src = myLib.cleanTypstSource ./clean; EXPECTED_SRC = ./clean-expected; buildPhaseTypstCommand = '' diff -r "$src" "$EXPECTED_SRC" touch $out ''; }; date = myLib.buildTypstProject { inherit typstSource; src = myLib.cleanTypstSource ./date; }; dateWatch = watch {} { inherit typstSource; src = myLib.cleanTypstSource ./date; }; devShell = myLib.devShell { inherit virtualPaths; checks = { simple = myLib.buildTypstProject { inherit virtualPaths typstSource; src = myLib.cleanTypstSource ./simple; }; }; }; checkEmojiScript = { inputs = (with pkgs; [ python312 python312Packages.emoji ]) ++ ( with pkgs.python312Packages; [ # Referencing `emoji` doesn't work here because an attribute of that # name already exists in a recursive attribute set (`rec {...}`) pdfplumber ] ); script = patterns: '' python3.12 ${./check-emojis.py} "$out" ${concatMapStringsSep " " escapeShellArg patterns} ''; }; emoji = { emojiFont, fontPaths ? [], patterns, }: (myLib.buildTypstProject ({ inherit typstSource fontPaths; src = myLib.cleanTypstSource ./emoji; doCheck = true; nativeCheckInputs = checkEmojiScript.inputs; checkPhase = checkEmojiScript.script patterns; } // lib.optionalAttrs (emojiFont != "__OMIT__") { inherit emojiFont; })); emojiOmit = emoji { emojiFont = "__OMIT__"; patterns = ["emoji"]; }; emojiTwemoji = emoji { emojiFont = "twemoji"; patterns = ["emoji"]; }; emojiTwemojiCbdt = emoji { emojiFont = "twemoji-cbdt"; patterns = ["emoji"]; }; emojiNoto = emoji { emojiFont = "noto"; patterns = ["emoji"]; }; emojiNotoMonochrome = emoji { emojiFont = "noto-monochrome"; patterns = ["emoji"]; }; # FIXME: https://github.com/loqusion/typix/issues/79 # emojiEmojiOne = emoji { # emojiFont = "emojione"; # patterns = ["emoji"]; # }; emojiFontOverride = emoji { emojiFont = null; fontPaths = ["${pkgs.noto-fonts-color-emoji}/share/fonts/noto"]; patterns = ["emoji"]; }; emojiWatch = { emojiFont, fontPaths ? [], patterns, }: (watch { nativeBuildInputs = checkEmojiScript.inputs; postBuild = checkEmojiScript.script patterns; } ({ inherit typstSource fontPaths; src = myLib.cleanTypstSource ./emoji; } // lib.optionalAttrs (emojiFont != "__OMIT__") { inherit emojiFont; })); emojiWatchOmit = emojiWatch { emojiFont = "__OMIT__"; patterns = ["emoji"]; }; emojiWatchTwemoji = emojiWatch { emojiFont = "twemoji"; patterns = ["emoji"]; }; emojiWatchTwemojiCbdt = emojiWatch { emojiFont = "twemoji-cbdt"; patterns = ["emoji"]; }; emojiWatchNoto = emojiWatch { emojiFont = "noto"; patterns = ["emoji"]; }; emojiWatchNotoMonochrome = emojiWatch { emojiFont = "noto-monochrome"; patterns = ["emoji"]; }; # FIXME: https://github.com/loqusion/typix/issues/79 # emojiWatchEmojiOne = emojiWatch { # emojiFont = "emojione"; # patterns = ["emoji"]; # }; emojiWatchFontOverride = emojiWatch { emojiFont = null; fontPaths = ["${pkgs.noto-fonts-color-emoji}/share/fonts/noto"]; patterns = ["emoji"]; }; overlappingVirtualPaths = isInvariant: util: file: util (let op = if isInvariant then "!=" else "="; errorMsg = if isInvariant then ''$FILE_TO_CHECK was overwritten\; it should stay the same when forceVirtualPaths is false'' else ''$FILE_TO_CHECK was not overwritten\; it should be overwritten when forceVirtualPaths is true''; in { FILE_TO_CHECK = file; preBuild = '' if [ ! -e "$FILE_TO_CHECK" ]; then echo "$FILE_TO_CHECK does not exist; unable to run check" exit 1 fi hash=$(sha256sum "$FILE_TO_CHECK" | awk '{ print $1 }') if [ -z "$hash" ]; then echo "unable to obtain hash for $FILE_TO_CHECK" exit 1 fi ''; postBuild = '' hash=''${hash:?not defined} new_hash=$(sha256sum "$FILE_TO_CHECK" | awk '{ print $1 }') if [ -z "$new_hash" ]; then echo "unable to obtain hash for $FILE_TO_CHECK" exit 1 fi if [ "$hash" ${op} "$new_hash" ]; then echo ${errorMsg} echo echo "old hash: $hash" echo "new hash: $new_hash" exit 1 fi ''; }) { inherit virtualPaths typstSource; src = ./overlapping-virtual-paths; forceVirtualPaths = !isInvariant; }; overlappingVirtualPathsInvariant = overlappingVirtualPaths true; overlappingVirtualPathsForce = overlappingVirtualPaths false; simple = myLib.buildTypstProject { inherit typstSource; src = myLib.cleanTypstSource ./simple; }; simpleWithFonts = myLib.buildTypstProject { inherit fontPaths typstSource; src = myLib.cleanTypstSource ./simple-with-fonts; }; simpleWithMultipleParameters = myLib.buildTypstProject { inherit typstSource; src = myLib.cleanTypstSource ./simple-with-multiple-parameters; typstOpts = { input = ["key1=value1" "key2=value2" "key3= --spaces-are-properly-escaped" "key4='quotes-are-properly-escaped\""]; }; }; simpleWithVirtualPaths = myLib.buildTypstProject { inherit virtualPaths typstSource; src = myLib.cleanTypstSource ./simple-with-virtual-paths; }; withTypstPackages = myLib.buildTypstProject { inherit typstSource; src = myLib.cleanTypstSource ./typst-packages; inherit unstable_typstPackages; }; virtualPathsChecks = callPackage ./virtual-paths.nix {}; watch = callPackage ./watch.nix {}; watchSimple = watch {} { inherit typstSource; src = myLib.cleanTypstSource ./simple; }; watchSimpleWithFonts = watch {} { inherit fontPaths typstSource; src = myLib.cleanTypstSource ./simple-with-fonts; }; watchWithMultipleParameters = watch {} { inherit typstSource; src = myLib.cleanTypstSource ./simple-with-multiple-parameters; typstOpts = { input = ["key1=value1" "key2=value2" "key3= --spaces-are-properly-escaped" "key4='quotes-are-properly-escaped\""]; }; }; # TODO: see above # watchSimpleWithTypstPackages = watch {} { # inherit typstSource; # src = myLib.cleanTypstSource ./simple-with-typst-packages; # }; watchSimpleWithVirtualPaths = watch {} { inherit virtualPaths typstSource; src = myLib.cleanTypstSource ./simple-with-virtual-paths; }; watchOverlappingVirtualPaths = overlappingVirtualPathsInvariant watch "icons/link.svg"; watchOverlappingVirtualPathsForce = overlappingVirtualPathsForce watch "icons/link.svg"; })) ================================================ FILE: checks/emoji/main.typ ================================================ #(emoji.tangerine)hell#(emoji.o) w#(emoji.o)rld#(emoji.face) ================================================ FILE: checks/overlapping-virtual-paths/main.typ ================================================ #lorem(100) #image("icons/link.svg") ================================================ FILE: checks/simple/main.typ ================================================ #lorem(100) ================================================ FILE: checks/simple-with-fonts/main.typ ================================================ #set text(font: "Roboto") #lorem(100) ================================================ FILE: checks/simple-with-multiple-parameters/main.typ ================================================ #sys.inputs.key1 #sys.inputs.key2 ================================================ FILE: checks/simple-with-virtual-paths/main.typ ================================================ #lorem(100) #image("icons/link.svg") #image("icons/paper-plane-tilt.svg") ================================================ FILE: checks/typst-packages/main.typ ================================================ #import "@preview/example:0.1.0": add #import "@preview/cetz:0.3.4" #import "@preview/polylux:0.4.0": slide, uncover // example 2 + 2 = #[add(2 + 2)] // cetz #cetz.canvas({ import cetz.draw: * circle((0, 0)) line((0, 0), (2, 1)) }) // polylux // Make the paper dimensions fit for a presentation and the text larger #set page(paper: "presentation-16-9") #set text(size: 25pt) // Use #slide to create a slide and style it using your favourite Typst functions #slide[ #set align(horizon) = Very minimalist slides A lazy author July 23, 2023 ] #slide[ == First slide Some static text on this slide. ] #slide[ == This slide changes! You can always see this. // Make use of features like #uncover, #only, and others to create dynamic content #uncover(2)[But this appears later!] ] ================================================ FILE: checks/virtual-paths.nix ================================================ { cleanTypstSource, copyVirtualPathsHook, lib, linkFarmFromDrvs, linkVirtualPaths, pkgs, }: let inherit (lib.attrsets) filterAttrs mapAttrsToList; inherit (lib.lists) all; inherit (lib.strings) toShellVars; cleanArgs = args: builtins.removeAttrs args [ "virtualPaths" "assertionCommand" "linkAssertionCommnad" ]; copyVirtualPathsHookAssertion = args @ { virtualPaths, assertionCommand, ... }: pkgs.stdenvNoCC.mkDerivation ((cleanArgs args) // { src = cleanTypstSource ./simple; nativeBuildInputs = [ (copyVirtualPathsHook virtualPaths) ]; buildPhase = '' runHook preBuild runHook postBuild ''; postBuild = '' set -euo pipefail assertionCommand() { ${assertionCommand} } if ! assertionCommand; then ${toShellVars {assertionText = assertionCommand;}} echo "assertion \`$assertionText\` failed" exit 1 fi touch "$out" ''; }); linkVirtualPathsAssertion = args @ { virtualPaths, assertionCommand, linkAssertionCommand ? "true", ... }: pkgs.stdenvNoCC.mkDerivation ((cleanArgs args) // { src = cleanTypstSource ./simple; buildPhase = '' ${linkVirtualPaths {inherit virtualPaths;}} runHook postBuild ''; postBuild = '' set -euo pipefail assertionCommand() { ${assertionCommand} } linkAssertionCommand() { ${linkAssertionCommand} } if ! assertionCommand; then ${toShellVars {assertionText = assertionCommand;}} echo "assertion \`$assertionText\` failed" exit 1 fi if ! linkAssertionCommand; then ${toShellVars {assertionText = linkAssertionCommand;}} echo "assertion \`$assertionText\` failed" exit 1 fi touch "$out" ''; }); virtualPathsTestAttrs = { fileSource = { virtualPaths = ["${./fixtures/icons}/link.svg"]; assertionCommand = ''[ -f ./link.svg ]''; linkAssertionCommand = ''[ -L ./link.svg ]''; }; directorySource = { virtualPaths = [./fixtures/icons]; assertionCommand = ''[ -f ./main.typ ] && [ -f ./link.svg ]''; linkAssertionCommand = ''[ -L ./link.svg ]''; }; fileSourceWithDest = { virtualPaths = [ { src = ./fixtures/icons/link.svg; dest = "link.svg"; } ]; assertionCommand = ''[ -f ./link.svg ]''; linkAssertionCommand = ''[ -L ./link.svg ]''; }; directorySourceWithDest = { virtualPaths = [ { src = ./fixtures/icons; dest = "icons"; } ]; assertionCommand = ''[ -d ./icons ] && [ -f ./icons/link.svg ]''; linkAssertionCommand = ''[ ! -L ./icons ] && [ -L ./icons/link.svg ]''; }; fileSourceWithDeepDest = { virtualPaths = [ { src = ./fixtures/icons/link.svg; dest = "assets/icons/link.svg"; } ]; assertionCommand = ''[ -d ./assets/icons ] && [ -f ./assets/icons/link.svg ]''; linkAssertionCommand = ''[ ! -L ./assets ] && [ ! -L ./assets/icons ] && [ -L ./assets/icons/link.svg ]''; }; directorySourceWithDeepDest = { virtualPaths = [ { src = ./fixtures/icons; dest = "assets/icons"; } ]; assertionCommand = ''[ -d ./assets/icons ] && [ -f ./assets/icons/link.svg ]''; linkAssertionCommand = ''[ ! -L ./assets ] && [ ! -L ./assets/icons ] && [ -L ./assets/icons/link.svg ]''; }; mergedSources = { virtualPaths = [ { src = ./fixtures/icons; dest = "icons"; } { src = ./fixtures/more-icons; dest = "icons"; } ]; assertionCommand = ''[ -d ./icons ] && [ -f ./icons/link.svg ] && [ -f ./icons/another-link.svg ]''; linkAssertionCommand = ''[ ! -L ./icons ] && [ -L ./icons/link.svg ] && [ -L ./icons/another-link.svg ]''; }; }; skip = { copyVirtualPathsHook = []; linkVirtualPaths = []; }; filterSkip = skipNames: filterAttrs (name: _: all (skipName: name != skipName) skipNames); mapTestAttrs = f: skipAttrs: testGroupName: mapAttrsToList (name: attrs: f (attrs // {name = "${testGroupName}-${name}";})) (filterSkip skipAttrs virtualPathsTestAttrs); in linkFarmFromDrvs "virtualPathsTests" ( (mapTestAttrs copyVirtualPathsHookAssertion skip.copyVirtualPathsHook "copyVirtualPathsHook") ++ (mapTestAttrs linkVirtualPathsAssertion skip.linkVirtualPaths "linkVirtualPaths") ) ================================================ FILE: checks/watch.nix ================================================ { lib, pkgs, watchTypstProject, }: runCommandDrvAttr: args: let cleanedArgs = builtins.removeAttrs args [ "src" ]; watch-drv = watchTypstProject ({ name = "watch-check"; # `typst watch` will never exit itself, but we can still check the exit status of `typst compile` for errors typstWatchCommand = "typst compile"; } // cleanedArgs); in pkgs.runCommand "watch" (runCommandDrvAttr // { nativeBuildInputs = (runCommandDrvAttr.nativeBuildInputs or []) ++ [ watch-drv ]; }) '' # This is needed to imitate a user's project directory cp -RLT --no-preserve=mode ${args.src} . runHook preBuild ${lib.getExe watch-drv} "$out" runHook postBuild '' ================================================ FILE: docs/.gitignore ================================================ book ================================================ FILE: docs/SUMMARY.md ================================================ # Summary - [Introduction](README.md) - [Getting Started](getting-started.md) # Recipes - [Adding dependencies](recipes/adding-dependencies.md) - [Specifying sources](recipes/specifying-sources.md) - [Declaring a shell environment](recipes/declaring-a-shell-environment.md) - [Using Typst packages](recipes/using-typst-packages.md) # API Reference - [Derivations](api/derivations.md) - [buildTypstProject](api/derivations/build-typst-project.md) - [buildTypstProjectLocal](api/derivations/build-typst-project-local.md) - [devShell](api/derivations/dev-shell.md) - [mkTypstDerivation](api/derivations/mk-typst-derivation.md) - [watchTypstProject](api/derivations/watch-typst-project.md) - [Utilities](api/utilities.md) - [cleanTypstSource](api/utilities/clean-typst-source.md) ================================================ FILE: docs/api/derivations/build-typst-project-local.md ================================================ # buildTypstProjectLocal Returns a derivation for compiling a Typst project and copying the output to the current directory. This is essentially a script which wraps [`buildTypstProject`](build-typst-project.md).
Using this derivation requires [`allow-import-from-derivation`](https://nixos.org/manual/nix/stable/command-ref/conf-file#conf-allow-import-from-derivation) to be `true` (which is the default at the time of writing). More info:
Invoking the script produced by this derivation directly is currently unsupported. Instead, use `nix run`. See [this issue](https://github.com/loqusion/typix/issues/2) for more information.
## Parameters All parameters accepted by [`buildTypstProject`](build-typst-project.md#parameters) are also accepted by `buildTypstProjectLocal`. They are repeated below for convenience. ### `src` {{#include common/src.md}} ### `emojiFont` optional { #emojifont } {{#include common/emoji-font.md}} ### `fontPaths` optional { #fontpaths } {{#include common/font-paths.md}} #### Example { #fontpaths-example } {{#include common/font-paths-example.md:buildtypstprojectlocal_example}} ### `installPhaseCommand` optional { #installphasecommand } {{#include common/install-phase-command.md}} ### `scriptName` optional { #scriptname } {{#include common/script-name.md}} Default is `typst-build`. ### `typstCompileCommand` optional { #typstcompilecommand } {{#include common/typst-compile-command.md}} Default is `typst compile`. ### `typstOpts` optional { #typstopts } {{#include common/typst-opts.md:head}} These are in addition to any options you manually pass in [`typstCompileCommand`](#typstcompilecommand). {{#include common/typst-opts.md:tail}} #### Example { #typstopts-example } {{#include common/typst-opts-example.md:buildtypstprojectlocal}} {{#include common/typst-opts-example.md:typstcompile}} ### `typstOutput` optional { #typstoutput } {{#include common/typst-project-output.md:head}} {{#include common/typst-project-output.md:buildtypstprojectlocal}} ### `typstSource` optional { #typstsource } {{#include common/typst-project-source.md}} Default is `main.typ`. ### `unstable_typstPackages` optional { #typstpackages } {{#include common/typst-packages.md:body}} #### Example { #typstpackages-example } {{#include common/typst-packages.md:example_buildtypstprojectlocal}} {{#include common/typst-packages.md:example_typst}} ### `virtualPaths` optional { #virtualpaths } {{#include common/virtual-paths.md}} #### Example { #virtualpaths-example } {{#include common/virtual-paths-example.md:head}} {{#include common/virtual-paths-example.md:buildtypstprojectlocal_example}} {{#include common/virtual-paths-example.md:tail}} ## Source - [`buildTypstProjectLocal`](https://github.com/loqusion/typix/blob/main/lib/buildTypstProjectLocal.nix) ================================================ FILE: docs/api/derivations/build-typst-project.md ================================================ # buildTypstProject Returns a derivation for compiling a Typst project. If you want to build to the project directory, use [`buildTypstProjectLocal`](build-typst-project-local.md) instead. ## Parameters All parameters accepted by [`mkTypstDerivation`](mk-typst-derivation.md#parameters) are also accepted by `buildTypstProject`. They are repeated below for convenience. ### `src` {{#include common/src.md}} ### `emojiFont` optional { #emojifont } {{#include common/emoji-font.md}} ### `fontPaths` optional { #fontpaths } {{#include common/font-paths.md}} #### Example { #fontpaths-example } {{#include common/font-paths-example.md:buildtypstproject_example}} ### `installPhaseCommand` optional { #installphasecommand } {{#include common/install-phase-command.md}} ### `typstCompileCommand` optional { #typstcompilecommand } {{#include common/typst-compile-command.md}} Default is `typst compile`. ### `typstOpts` optional { #typstopts } {{#include common/typst-opts.md:head}} These are in addition to any options you manually pass in [`typstCompileCommand`](#typstcompilecommand). {{#include common/typst-opts.md:tail}} #### Example { #typstopts-example } {{#include common/typst-opts-example.md:buildtypstproject}} {{#include common/typst-opts-example.md:typstcompile}} ### `typstSource` optional { #typstsource } {{#include common/typst-project-source.md}} Default is `main.typ`. ### `unstable_typstPackages` optional { #typstpackages } {{#include common/typst-packages.md:body}} #### Example { #typstpackages-example } {{#include common/typst-packages.md:example_buildtypstproject}} {{#include common/typst-packages.md:example_typst}} ### `virtualPaths` optional { #virtualpaths } {{#include common/virtual-paths.md}} #### Example { #virtualpaths-example } {{#include common/virtual-paths-example.md:head}} {{#include common/virtual-paths-example.md:buildtypstproject_example}} {{#include common/virtual-paths-example.md:tail}} ## Source - [`buildTypstProject`](https://github.com/loqusion/typix/blob/main/lib/buildTypstProject.nix) ================================================ FILE: docs/api/derivations/common/emoji-font.md ================================================ Specify which emoji font to use. If `emojiFont` is `null`, no emoji font will be included. May be any of the following: - [`"twemoji"`](https://search.nixos.org/packages?channel=unstable&show=twemoji-color-font) (default) - [`"twemoji-cbdt"`](https://search.nixos.org/packages?channel=unstable&show=twitter-color-emoji) - [`"noto"`](https://search.nixos.org/packages?channel=unstable&show=noto-fonts-color-emoji) - [`"noto-monochrome"`](https://search.nixos.org/packages?channel=unstable&show=noto-fonts-monochrome-emoji) - [`"emojione"`](https://search.nixos.org/packages?channel=unstable&show=emojione) - `null` — Don't include any emoji font (e.g. so you can include your own)
Note about difference between "twemoji" and "twemoji-cbdt" The default Twemoji font displays color emojis using the SVG [font format], which may not be supported by some systems. If emojis aren't displaying properly, using `"twemoji-cbdt"` may fix it. [font format]: https://www.colorfonts.wtf/
================================================ FILE: docs/api/derivations/common/font-paths-example.md ================================================ ```nix { outputs = { nixpkgs, typix }: let system = "x86_64-linux"; pkgs = nixpkgs.legacyPackages.${system}; inherit (pkgs) lib; build-script = typix.lib.${system}.buildTypstProjectLocal { fontPaths = [ "${pkgs.roboto}/share/fonts/truetype" ]; }; in { apps.${system}.default = { type = "app"; program = lib.getExe build-script; }; }; } ``` ```nix { outputs = { nixpkgs, typix }: let system = "x86_64-linux"; pkgs = nixpkgs.legacyPackages.${system}; in { packages.${system}.default = typix.lib.${system}.buildTypstProject { fontPaths = [ "${pkgs.roboto}/share/fonts/truetype" ]; }; }; } ``` ```nix { outputs = { nixpkgs, typix }: let system = "x86_64-linux"; pkgs = nixpkgs.legacyPackages.${system}; in { devShells.${system}.default = typix.lib.${system}.devShell { fontPaths = [ "${pkgs.roboto}/share/fonts/truetype" ]; }; }; } ``` ```nix { outputs = { nixpkgs, typix }: let system = "x86_64-linux"; pkgs = nixpkgs.legacyPackages.${system}; in { packages.${system}.default = typix.lib.${system}.mkTypstDerivation { fontPaths = [ "${pkgs.roboto}/share/fonts/truetype" ]; }; }; } ``` ```nix { outputs = { nixpkgs, typix }: let system = "x86_64-linux"; pkgs = nixpkgs.legacyPackages.${system}; inherit (pkgs) lib; watch-script = typix.lib.${system}.watchTypstProject { fontPaths = [ "${pkgs.roboto}/share/fonts/truetype" ]; }; in { apps.${system}.default = { type = "app"; program = lib.getExe watch-script; }; }; } ``` ================================================ FILE: docs/api/derivations/common/font-paths.md ================================================ List of sources specifying paths to font files that will be made available to your Typst project. With this, you can compile Typst projects even when the fonts it uses are not available on your system. Used for setting `TYPST_FONT_PATHS` (see [`text#font`][typst-ref-text--font]). [typst-ref-text--font]: https://typst.app/docs/reference/text/text/#parameters-font ================================================ FILE: docs/api/derivations/common/install-phase-command.md ================================================ Command (or commands) to run during [`installPhase`][nixpkgs-installphase]. [nixpkgs-installphase]: https://nixos.org/manual/nixpkgs/stable/#ssec-install-phase ================================================ FILE: docs/api/derivations/common/script-name.md ================================================ Name of the script that will be added to the Nix store. You can use this name after entering a development shell to invoke the script. ================================================ FILE: docs/api/derivations/common/src.md ================================================ [Source](../../recipes/specifying-sources.md) containing all local files needed in your Typst project. ================================================ FILE: docs/api/derivations/common/typst-compile-command.md ================================================ Base Typst command to run to compile the project. Other arguments will be appended based on the parameters you supply. ================================================ FILE: docs/api/derivations/common/typst-opts-example.md ================================================ ```nix { format = "png"; ppi = 300; input = ["key1=value1" "key2=value2"]; } ``` ...will result in a command like: ```nix { outputs = { typix }: let system = "x86_64-linux"; in { packages.${system}.default = typix.lib.${system}.buildTypstProject { typstOpts = { format = "png"; ppi = 300; input = ["key1=value1" "key2=value2"]; }; }; }; } ``` ...will result in a command like: ```nix { outputs = { typix }: let system = "x86_64-linux"; inherit (nixpkgs) lib; build-script = typix.lib.${system}.buildTypstProjectLocal { typstOpts = { format = "png"; ppi = 300; input = ["key1=value1" "key2=value2"]; }; }; in { apps.${system}.default = { type = "app"; program = lib.getExe build-script; }; }; } ``` ...will result in a command like: ```nix { outputs = { typix }: let system = "x86_64-linux"; inherit (nixpkgs) lib; watch-script = typix.lib.${system}.watchTypstProject { typstOpts = { format = "png"; ppi = 300; input = ["key1=value1" "key2=value2"]; }; }; in { apps.${system}.default = { type = "app"; program = lib.getExe watch-script; }; }; } ``` ...will result in a command like: ```sh typst compile --format png --ppi 300 --input key1=value1 --input key2=value2 ``` ```sh typst watch --format png --ppi 300 --input key1=value1 --input key2=value2 ``` ================================================ FILE: docs/api/derivations/common/typst-opts.md ================================================ Attrset specifying command-line options to pass to the `typst` command. Default: ```nix { format = "pdf"; } ``` ================================================ FILE: docs/api/derivations/common/typst-packages.md ================================================
This is an **unstable** feature. Its API does not follow Semantic Versioning, and derivations relying on this feature may break at any time, even if you don't update Typst or Typix. When this feature reaches stability, it will be renamed to `typstPackages`. (The old name will be kept as an undocumented alias, to avoid unnecessary breakage.)
List of [Typst packages] to use in your Typst project, fetched from the official Typst packages CDN at `https://packages.typst.org`. Each element of the list is an attribute set with the following keys: - `name`: the package's identifier in its namespace - `version`: the package's version as a full major-minor-patch triple - `namespace` _(optional)_: the package's namespace (defaults to `preview`) - `hash`: hash of the downloaded package tarball `hash` must be manually updated to match the hash of the package tarball downloaded from the registry, using the "fake hash method". See [Updating source hashes][fetchers-hash] for how to do this. Sometimes, you might encounter a Typst compilation error that looks like this: ```text > help: error occurred while importing this module > ┌─ @preview/cetz:0.3.4/src/canvas.typ:3:8 > │ > 3 │ #import "util.typ" > │ ^^^^^^^^^^ > help: error occurred while importing this module > ┌─ @preview/cetz:0.3.4/src/lib.typ:3:8 > │ > 3 │ #import "canvas.typ": canvas > │ ^^^^^^^^^^^^ > help: error occurred while importing this module > ┌─ main.typ:1:8 > │ > 1 │ #import "@preview/cetz:0.3.4" > │ ^^^^^^^^^^^^^^^^^^^^^ > For full logs, run 'nix log /nix/store/6jhjxbl7glmy4adpr5wzfgn9jvsyyipf-typst.drv'. ``` In this example, Nix is hiding too much output for us to diagnose the issue. Run the command again with the [`-L` flag] (in our case, `nix run .#build -L`): [`-L` flag]: https://nix.dev/manual/nix/2.18/command-ref/new-cli/nix3-run#opt-print-build-logs ```text > downloading @preview/oxifmt:0.2.1 > error: failed to download package (Network Error: OpenSSL error) > ┌─ @preview/cetz:0.3.4/src/deps.typ:1:8 > │ > 1 │ #import "@preview/oxifmt:0.2.1" > │ ^^^^^^^^^^^^^^^^^^^^^^^ > help: error occurred while importing this module > ┌─ @preview/cetz:0.3.4/src/util.typ:1:8 > │ > 1 │ #import "deps.typ" > │ ^^^^^^^^^^ > help: error occurred while importing this module > ┌─ @preview/cetz:0.3.4/src/canvas.typ:3:8 > │ > 3 │ #import "util.typ" > │ ^^^^^^^^^^ > help: error occurred while importing this module > ┌─ @preview/cetz:0.3.4/src/lib.typ:3:8 > │ > 3 │ #import "canvas.typ": canvas > │ ^^^^^^^^^^^^ > help: error occurred while importing this module > ┌─ main.typ:1:8 > │ > 1 │ #import "@preview/cetz:0.3.4" > │ ^^^^^^^^^^^^^^^^^^^^^ ``` We can see that `cetz` is trying to import `oxifmt` 0.2.1, but Typst can't download it because Nix derivations are (by design) run in an environment which does not support networking. To fix this, add `oxifmt` 0.2.1 to `unstable_typstPackages` alongside your direct dependencies. (Make sure to match the exact version shown in your error message!!) There is currently no official support for [unpublished Typst packages]. However, there is a [workaround]. [Typst packages]: https://github.com/typst/packages [fetchers-hash]: https://nixos.org/manual/nixpkgs/stable/#sec-pkgs-fetchers-updating-source-hashes [unpublished Typst packages]: https://github.com/typst/packages#local-packages [workaround]: ../../recipes/using-typst-packages.md#unpublished-typst-packages ```nix { outputs = { typix }: let system = "x86_64-linux"; in { packages.${system}.default = typix.lib.${system}.buildTypstProject { unstable_typstPackages = [ { name = "cetz"; version = "0.3.4"; hash = "sha256-5w3UYRUSdi4hCvAjrp9HslzrUw7BhgDdeCiDRHGvqd4="; } # Transitive dependencies must be manually specified # `oxifmt` is required by `cetz` { name = "oxifmt"; version = "0.2.1"; hash = "sha256-8PNPa9TGFybMZ1uuJwb5ET0WGIInmIgg8h24BmdfxlU="; } ]; }; }; } ``` ```nix { outputs = { nixpkgs, typix }: let inherit (nixpkgs) lib; system = "x86_64-linux"; build-script = typix.lib.${system}.buildTypstProjectLocal { unstable_typstPackages = [ { name = "cetz"; version = "0.3.4"; hash = "sha256-5w3UYRUSdi4hCvAjrp9HslzrUw7BhgDdeCiDRHGvqd4="; } # Transitive dependencies must be manually specified # `oxifmt` is required by `cetz` { name = "oxifmt"; version = "0.2.1"; hash = "sha256-8PNPa9TGFybMZ1uuJwb5ET0WGIInmIgg8h24BmdfxlU="; } ]; }; in { apps.${system}.default = { type = "app"; program = lib.getExe build-script; }; }; } ``` ```nix { outputs = { typix }: let system = "x86_64-linux"; in { packages.${system}.default = typix.lib.${system}.mkTypstDerivation { unstable_typstPackages = [ { name = "cetz"; version = "0.3.4"; hash = "sha256-5w3UYRUSdi4hCvAjrp9HslzrUw7BhgDdeCiDRHGvqd4="; } # Transitive dependencies must be manually specified # `oxifmt` is required by `cetz` { name = "oxifmt"; version = "0.2.1"; hash = "sha256-8PNPa9TGFybMZ1uuJwb5ET0WGIInmIgg8h24BmdfxlU="; } ]; }; }; } ``` Then you can import and use the package in Typst: ```typst #import "@preview/cetz:0.3.4" #cetz.canvas({ import cetz.draw: * circle((0, 0)) line((0, 0), (2, 1)) }) ``` ================================================ FILE: docs/api/derivations/common/typst-project-output.md ================================================ Destination path for Typst output. If omitted, it will be inferred from [`typstSource`](#typstsource) — for example, `page.typ` will become `page.pdf` for PDF output. If omitted, it will be inferred from [`typstSource`](#typstsource) — for example, `page.typ` will become `page.pdf` for PDF output. ================================================ FILE: docs/api/derivations/common/typst-project-source.md ================================================ Typst input file to compile. ================================================ FILE: docs/api/derivations/common/virtual-paths-example.md ================================================ You can specify dependencies in your flake input, and then use them in your project with something like: ```nix { inputs = { font-awesome = { url = "github:FortAwesome/Font-Awesome"; flake = false; }; }; outputs = { nixpkgs, typix, font-awesome }: let system = "x86_64-linux"; inherit (nixpkgs) lib; build-script = typix.lib.${system}.buildTypstProjectLocal { virtualPaths = [ { dest = "icons"; src = "${font-awesome}/svgs/regular"; } ]; }; in { apps.${system}.default = { type = "app"; program = lib.getExe build-script; }; }; } ``` ```nix { inputs = { font-awesome = { url = "github:FortAwesome/Font-Awesome"; flake = false; }; }; outputs = { typix, font-awesome }: let system = "x86_64-linux"; in { packages.${system}.default = typix.lib.${system}.buildTypstProject { virtualPaths = [ { dest = "icons"; src = "${font-awesome}/svgs/regular"; } ]; }; }; } ``` ```nix { inputs = { font-awesome = { url = "github:FortAwesome/Font-Awesome"; flake = false; }; }; outputs = { typix, font-awesome }: let system = "x86_64-linux"; in { devShells.${system}.default = typix.lib.${system}.devShell { virtualPaths = [ { dest = "icons"; src = "${font-awesome}/svgs/regular"; } ]; }; }; } ``` ```nix { inputs = { font-awesome = { url = "github:FortAwesome/Font-Awesome"; flake = false; }; }; outputs = { typix, font-awesome }: let system = "x86_64-linux"; in { packages.${system}.default = typix.lib.${system}.mkTypstDerivation { virtualPaths = [ { dest = "icons"; src = "${font-awesome}/svgs/regular"; } ]; }; }; } ``` ```nix { inputs = { font-awesome = { url = "github:FortAwesome/Font-Awesome"; flake = false; }; }; outputs = { nixpkgs, typix, font-awesome }: let system = "x86_64-linux"; inherit (nixpkgs) lib; watch-script = typix.lib.${system}.watchTypstProject { virtualPaths = [ { dest = "icons"; src = "${font-awesome}/svgs/regular"; } ]; }; in { apps.${system}.default = { type = "app"; program = lib.getExe watch-script; }; }; } ``` ```typst #image("icons/heart.svg") ``` Then, reference the files in Typst: ```typst #image("icons/heart.svg") ``` ================================================ FILE: docs/api/derivations/common/virtual-paths.md ================================================ List of sources that will be made virtually available to your Typst project. Useful for projects which rely on remote resources, such as [images][typst-ref-image] or [data][typst-ref-data-loading]. Each element of the list is an attribute set with the following keys: - `src`: path to source file or directory - `dest` _(optional)_: path where file(s) will be made available (defaults to `.`) - If `src` is a directory, `dest` will be a directory containing the files in that directory. - Specifying the same `dest` for multiple `src` directories will merge them. - If `src` is a file, `dest` will be a copy of that file. Instead of an attrset, you may use a path which will be interpreted the same as if you had specified an attrset with just `src`. [typst-ref-data-loading]: https://typst.app/docs/reference/data-loading/ [typst-ref-image]: https://typst.app/docs/reference/visualize/image/ ================================================ FILE: docs/api/derivations/dev-shell.md ================================================ # devShell Sets up a shell environment that activates with [`nix develop`][nix-ref-develop] or [`direnv`][direnv]. ## Parameters **Note:** All parameters for [`mkShell`][nixpkgs-mkshell] are also supported. ### `emojiFont` optional { #emojifont } {{#include common/emoji-font.md}} ### extraShellHook optional { #extrashellhook } Bash statements added to the [`shellHook`][nixpkgs-mkshell-attributes] attribute. ### `fontPaths` optional { #fontpaths } {{#include common/font-paths.md}} #### Example { #fontpaths-example } {{#include common/font-paths-example.md:devshell_example}} ### `forceVirtualPaths` optional { #forcevirtualpaths } If there are any conflicts between [`virtualPaths`](#virtualpaths) and files in your project directory, they will not be overwritten unless `forceVirtualPaths` is `true`. Default is `false`. ### `virtualPaths` optional { #virtualpaths } {{#include common/virtual-paths.md}} **NOTE:** Any paths specified here will not overwrite files in your project directory, unless you set [`forceVirtualPaths`](#forcevirtualpaths) to `true`. #### Example { #virtualpaths-example } {{#include common/virtual-paths-example.md:head}} {{#include common/virtual-paths-example.md:devshell_example}} {{#include common/virtual-paths-example.md:tail}} ## Source - [`devShell`](https://github.com/loqusion/typix/blob/main/lib/devShell.nix) [direnv]: https://direnv.net/ [nix-ref-develop]: https://nixos.org/manual/nix/stable/command-ref/new-cli/nix3-develop [nixpkgs-mkshell-attributes]: https://nixos.org/manual/nixpkgs/stable/#sec-pkgs-mkShell-attributes [nixpkgs-mkshell]: https://nixos.org/manual/nixpkgs/stable/#sec-pkgs-mkShell ================================================ FILE: docs/api/derivations/mk-typst-derivation.md ================================================ # mkTypstDerivation A generic derivation constructor for running Typst commands. ## Parameters **Note:** All parameters for `stdenv.mkDerivation`[^stdenv] are also available. ### `buildPhaseTypstCommand` Command (or commands) to run during [`buildPhase`][nixpkgs-buildphase]. Any output should typically be written to `$out`, e.g. `typst compile "$out"`. See also: [Typst CLI Usage][typst-cli-usage] ### `src` {{#include common/src.md}} ### `emojiFont` optional { #emojifont } {{#include common/emoji-font.md}} ### `fontPaths` optional { #fontpaths } {{#include common/font-paths.md}} #### Example { #fontpaths-example } {{#include common/font-paths-example.md:mktypstderivation_example}} ### `installPhaseCommand` optional { #installphasecommand } {{#include common/install-phase-command.md}} ### `unstable_typstPackages` optional { #typstpackages } {{#include common/typst-packages.md:body}} #### Example { #typstpackages-example } {{#include common/typst-packages.md:example_mktypstderivation}} {{#include common/typst-packages.md:example_typst}} ### `virtualPaths` optional { #virtualpaths } {{#include common/virtual-paths.md}} #### Example { #virtualpaths-example } {{#include common/virtual-paths-example.md:head}} {{#include common/virtual-paths-example.md:mktypstderivation_example}} {{#include common/virtual-paths-example.md:tail}} ## Source - [`mkTypstDerivation`](https://github.com/loqusion/typix/blob/main/lib/mkTypstDerivation.nix) ## Footnotes [^stdenv]: [`stdenv`][nixpkgs-stdenv] (not for the faint of heart) [nixpkgs-buildphase]: https://nixos.org/manual/nixpkgs/stable/#build-phase [nixpkgs-stdenv]: https://nixos.org/manual/nixpkgs/stable/#chap-stdenv [typst-cli-usage]: https://github.com/typst/typst#usage ================================================ FILE: docs/api/derivations/watch-typst-project.md ================================================ # watchTypstProject Returns a derivation for a script that watches an input file and recompiles on changes. ## Parameters **Note:** All parameters for [`writeShellApplication`][nixpkgs-writeshellapplication] are also supported (besides `text`). ### `emojiFont` optional { #emojifont } {{#include common/emoji-font.md}} ### `fontPaths` optional { #fontpaths } {{#include common/font-paths.md}} #### Example { #fontpaths-example } {{#include common/font-paths-example.md:watchtypstproject_example}} ### `forceVirtualPaths` optional { #forcevirtualpaths } If there are any conflicts between [`virtualPaths`](#virtualpaths) and files in your project directory, they will not be overwritten unless `forceVirtualPaths` is `true`. Default is `false`. ### `scriptName` optional { #scriptname } {{#include common/script-name.md}} Default is `typst-watch`. ### `typstOpts` optional { #typstopts } {{#include common/typst-opts.md:head}} These are in addition to any options you manually pass in [`typstWatchCommand`](#typstwatchcommand). {{#include common/typst-opts.md:tail}} #### Example { #typstopts-example } {{#include common/typst-opts-example.md:watchtypstproject}} {{#include common/typst-opts-example.md:typstwatch}} ### `typstOutput` optional { #typstoutput } {{#include common/typst-project-output.md:head}} {{#include common/typst-project-output.md:watchtypstproject}} ### `typstSource` optional { #typstsource } {{#include common/typst-project-source.md}} Default is `main.typ`. ### `typstWatchCommand` optional { #typstwatchcommand } Base Typst command to run to watch the project. Other arguments will be appended based on the other parameters you supply. Default is `typst watch`. ### `virtualPaths` optional { #virtualpaths } {{#include common/virtual-paths.md}} **NOTE:** Any paths specified here will not overwrite files in your project directory, unless you set [`forceVirtualPaths`](#forcevirtualpaths) to `true`. #### Example { #virtualpaths-example } {{#include common/virtual-paths-example.md:head}} {{#include common/virtual-paths-example.md:watchtypstproject_example}} {{#include common/virtual-paths-example.md:tail}} ## Source - [`watchTypstProject`](https://github.com/loqusion/typix/blob/main/lib/watchTypstProject.nix) [nixpkgs-writeshellapplication]: https://nixos.org/manual/nixpkgs/stable/#trivial-builder-writeShellApplication ================================================ FILE: docs/api/derivations.md ================================================ # Derivations As paraphrased from [the Nix Reference Manual][nix-ref-derivations]: > A derivation is a specification for running an executable on precisely defined > input files to repeatably produce output files at uniquely determined file > system paths. The derivation constructors defined in Typix extend this behavior by running `typst compile`/`typst watch` in a context where all the dependencies of your Typst project (fonts, images, etc.) will be made available to the Typst compiler, regardless of if they're already present on the system it runs on. - [`buildTypstProjectLocal`](derivations/build-typst-project-local.md) — Returns a derivation for compiling a Typst project and copying the output to the current directory. - [`buildTypstProject`](derivations/build-typst-project.md) — Returns a derivation for compiling a Typst project. - [`devShell`](derivations/dev-shell.md) — Sets up a shell environment that activates with [`nix develop`][nix-ref-develop] or [`direnv`][direnv]. - [`mkTypstDerivation`](derivations/mk-typst-derivation.md) — A generic derivation constructor for running Typst commands. - [`watchTypstDerivation`](derivations/watch-typst-project.md) — Returns a derivation for a script that watches an input file and recompiles on changes. [direnv]: https://direnv.net/ [nix-ref-derivations]: https://nixos.org/manual/nix/stable/language/derivations.html [nix-ref-develop]: https://nixos.org/manual/nix/stable/command-ref/new-cli/nix3-develop ================================================ FILE: docs/api/utilities/clean-typst-source.md ================================================ # cleanTypstSource Filters a source tree to only contain files that are usually pertinent to a Typst project. #### Example ```nix { outputs = { nixpkgs, typix, }: let system = "x86_64-linux"; typixLib = typix.lib.${system}; in { packages.${system}.default = typixLib.mkTypstDerivation { src = typixLib.cleanTypstSource ./.; }; }; } ``` ================================================ FILE: docs/api/utilities.md ================================================ # Utilities - [`cleanTypstSource`](./utilities/clean-typst-source.md) ================================================ FILE: docs/book.toml ================================================ [book] authors = ["loqusion"] language = "en" src = "." title = "Typix" [output.html] default-theme = "ayu" preferred-dark-theme = "ayu" git-repository-url = "https://github.com/loqusion/typix" edit-url-template = "https://github.com/loqusion/typix/edit/main/docs/{path}" ================================================ FILE: docs/getting-started.md ================================================ # Getting Started First, [install Nix][install-nix]: [install-nix]: https://github.com/DeterminateSystems/nix-installer ```bash curl --proto '=https' --tlsv1.2 -sSf -L https://install.determinate.systems/nix | sh -s -- install ``` Make sure `nix-command` and `flakes` are enabled: `~/.config/nix/nix.conf` ```ini experimental-features = nix-command flakes ``` Finally, you can initialize a flake from the default template: ```bash nix flake init --refresh -t github:loqusion/typix ``` > Alternatively, you can use a template demonstrating [Typst packages] usage: > > ```bash > nix flake init --refresh -t 'github:loqusion/typix#with-typst-packages' > ``` > > [Typst packages]: ./recipes/using-typst-packages.md --- Here are some commands you can run from any template: - `nix run .#watch` — watch the input files and recompile on changes - `nix run .#build` — compile and copy the output to the current directory For more info, see [`nix run --help`](https://nixos.org/manual/nix/stable/command-ref/new-cli/nix3-run). ================================================ FILE: docs/recipes/adding-dependencies.md ================================================ # Adding dependencies You can add dependencies to your [flake inputs][nix-ref-flake-inputs][^zero-to-nix-flakes] so that Typst compilation does not depend on the transient state of the local system: instead, any dependencies are automatically fetched and made available in a sandboxed environment. Examples of dependencies you might want to add: - [Font files][typst-ref-text--font] — [`fontPaths`](../api/derivations/mk-typst-derivation.md#fontpaths) - [Image files][typst-ref-image] — [`virtualPaths`](../api/derivations/mk-typst-derivation.md#virtualpaths) - [Data files][typst-ref-data-loading] (e.g. [JSON][typst-ref-data-json], [TOML][typst-ref-data-toml], [XML][typst-ref-data-xml]) — [`virtualPaths`](../api/derivations/mk-typst-derivation.md#virtualpaths) For a more complete description of how to specify flake inputs, see the Nix Reference Manual [section on flakerefs][nix-ref-flake-references]. [^zero-to-nix-flakes]: See also: ## nixpkgs Many popular fonts are available as packages to [nixpkgs][nixpkgs], so if you're wanting to add a font it's good to try that before anything else. To determine the path(s) to the files you wish to include, first run the following command (which creates a symbolic link named `result` in the current directory): ```bash nix-build '' --out-link result --attr PACKAGE_NAME ``` Then explore the `result` with whichever commands you like — for instance, using [`unix-tree`][unix-tree]: ```bash tree ./result ``` ```text result └── share └── fonts └── truetype ├── Roboto-BlackItalic.ttf ├── Roboto-Black.ttf ├── Roboto-BoldItalic.ttf ├── Roboto-Bold.ttf ├── RobotoCondensed-BoldItalic.ttf ├── RobotoCondensed-Bold.ttf ├── RobotoCondensed-Italic.ttf ├── RobotoCondensed-LightItalic.ttf ├── RobotoCondensed-Light.ttf ├── RobotoCondensed-MediumItalic.ttf ├── RobotoCondensed-Medium.ttf ├── RobotoCondensed-Regular.ttf ├── Roboto-Italic.ttf ├── Roboto-LightItalic.ttf ├── Roboto-Light.ttf ├── Roboto-MediumItalic.ttf ├── Roboto-Medium.ttf ├── Roboto-Regular.ttf ├── Roboto-ThinItalic.ttf └── Roboto-Thin.ttf ``` Here, we can see that the relative path should be `share/fonts/truetype`, so in `flake.nix` we use that information like so: {{#include ../api/derivations/common/font-paths-example.md:mktypstderivation_example}} ## GitHub [GitHub](https://github.com) hosts a great deal of fonts and icon libraries, and Nix makes it easy to add GitHub repositories as flake inputs with [URL-like syntax][nix-ref-flake-url]. Here's an example of specifying a GitHub URL as a flake input and adding it to [`virtualPaths`](../api/derivations/mk-typst-derivation.md#virtualpaths), specifying that we want the `svgs/regular` directory to be accessible from `icons`: {{#include ../api/derivations/common/virtual-paths-example.md:mktypstderivation_example}} With this, we can use it in Typst as if it were any other local path: {{#include ../api/derivations/common/virtual-paths-example.md:typst_example}} ## Using local files If all else fails, you can always manually download what you need and move it to your Typst project directory. Here's what you need to know: - The Typst compiler invoked by [`buildTypstProject`](../api/derivations/build-typst-project.md), [`buildTypstProjectLocal`](../api/derivations/build-typst-project-local.md), etc. won't see the files you've added unless they're present in one of the source tree parameters — in practice, these are [`src`](../api/derivations/mk-typst-derivation.md#src), [`fontPaths`](../api/derivations/mk-typst-derivation.md#fontpaths), and [`virtualPaths`](../api/derivations/mk-typst-derivation.md#virtualpaths). (This doesn't apply to [`watchTypstProject`](../api/derivations/watch-typst-project.md).) - Paths to font files must still be passed in [`fontPaths`](../api/derivations/mk-typst-derivation.md#fontpaths) or otherwise made known to the Typst compiler (e.g. via [`--font-path`][typst-man-compile--font-path]). See ["Specifying sources"](./specifying-sources.md#expanding-a-source-tree) for information on how to expand a source tree to include the files you need. [nix-ref-flake-inputs]: https://nixos.org/manual/nix/stable/command-ref/new-cli/nix3-flake#flake-inputs [nix-ref-flake-references]: https://nixos.org/manual/nix/stable/command-ref/new-cli/nix3-flake#flake-references [nix-ref-flake-url]: https://nixos.org/manual/nix/stable/command-ref/new-cli/nix3-flake#url-like-syntax [nixpkgs]: https://search.nixos.org/packages [typst-man-compile--font-path]: https://man.archlinux.org/man/typst-compile.1.en#font [typst-ref-data-json]: https://typst.app/docs/reference/data-loading/json/ [typst-ref-data-loading]: https://typst.app/docs/reference/data-loading/ [typst-ref-data-toml]: https://typst.app/docs/reference/data-loading/toml/ [typst-ref-data-xml]: https://typst.app/docs/reference/data-loading/xml/ [typst-ref-image]: https://typst.app/docs/reference/visualize/image/ [typst-ref-text--font]: https://typst.app/docs/reference/text/text/#parameters-font [unix-tree]: https://gitlab.com/OldManProgrammer/unix-tree ================================================ FILE: docs/recipes/declaring-a-shell-environment.md ================================================ # Declaring a shell environment You can automatically pull your project's dependencies into your shell by declaring a [shell environment][nix-dev-declarative-shell] and then activating it with [`nix develop`][nix-ref-develop] or [`direnv`][direnv]. Here's an example in a flake using Typix's [`devShell`](../api/derivations/dev-shell.md): ```nix { outputs = { typix }: let system = "x86_64-linux"; typixLib = typix.lib.${system}; watch-script = typixLib.watchTypstProject {/* ... */}; in { # packages, apps, etc. omitted devShells.${system}.default = typixLib.devShell { fontPaths = [/* ... */]; virtualPaths = [/* ... */]; packages = [ watch-script ]; }; }; } ``` What this example does: - Fonts added to [`fontPaths`](../api/derivations/dev-shell.md#fontpaths) will be made available to `typst` commands via the `TYPST_FONT_PATHS` environment variable. - Files in [`virtualPaths`](../api/derivations/dev-shell.md#virtualpaths) will be recursively symlinked to the current directory (only overwriting existing files when [`forceVirtualPaths`](../api/derivations/dev-shell.md#forcevirtualpaths) is `true`). - For convenience, the [`typst-watch`](../api/derivations/watch-typst-project.md#scriptname) script is added, which will run [`watchTypstProject`](../api/derivations/watch-typst-project.md). [direnv]: https://direnv.net/ [nix-dev-declarative-shell]: https://nix.dev/tutorials/first-steps/declarative-shell [nix-ref-develop]: https://nixos.org/manual/nix/stable/command-ref/new-cli/nix3-develop ================================================ FILE: docs/recipes/specifying-sources.md ================================================ # Specifying sources A number of derivations in Typix accept source trees as parameters, such as [`src`](../api/derivations/mk-typst-derivation.md#src), [`fontPaths`](../api/derivations/mk-typst-derivation.md#fontpaths), and [`virtualPaths`](../api/derivations/mk-typst-derivation.md#virtualpaths). Specifying these is usually as simple as [`cleanTypstSource`](../api/utilities/clean-typst-source.md) in the case of `src` and string interpolation (via `${...}`) in the case of `fontPaths` and `virtualPaths`, but there are situations where more is required or desirable. ## Expanding a source tree > TL;DR: you can use [`lib.sources.cleanSource`][nixpkgs-sources-cleansource], > but the problem with this approach is that every change to a file tracked by > git will invalidate the cache and trigger a rebuild. To include more _local files_[^fileset-note] in a source tree, you can use a combination of different functions in [`lib.fileset`][nixpkgs-fileset] such as [`lib.fileset.unions`][nixpkgs-fileset-unions], [`lib.fileset.fromSource`][nixpkgs-fileset-fromsource], and [`lib.fileset.toSource`][nixpkgs-fileset-tosource], like so: ```nix { outputs = { nixpkgs, typix }: let system = "x86_64-linux"; pkgs = nixpkgs.legacyPackages.${system}; inherit (pkgs) lib; typixLib = typix.lib.${system}; myTypstSource = typixLib.cleanTypstSource ./.; in { packages.${system}.default = typixLib.mkTypstDerivation { src = lib.fileset.toSource { root = ./.; fileset = lib.fileset.unions [ (lib.fileset.fromSource myTypstSource) ./path.svg ./other/path.svg ./another ]; }; }; }; } ``` This will create a source tree that looks something like: ```text /nix/store/... ├── another │ ├── path1.svg │ ├── path2.svg │ └── path3.svg ├── path.svg ├── other │ └── path.svg └── ... ``` [^fileset-note]: `lib.fileset` functions can only be used with local files, not e.g. flake inputs, which is what [`virtualPaths`](../api/derivations/mk-typst-derivation.md#virtualpaths) is for. ## Source filtering You can do source filtering primarily using [`builtins.filterSource`][nix-ref-builtins-filtersource] and functions in [`lib.sources`][nixpkgs-sources] such as [`lib.sources.cleanSourceWith`][nixpkgs-sources-cleansourcewith]. A more detailed explanation can be found in the Nix discussion: ["Filtering Source Trees with Nix and Nixpkgs"][nix-discussion-source-filtering]. Here's an example which picks specific files by name: ```nix { outputs = { nixpkgs, typix, font-awesome }: let system = "x86_64-linux"; pkgs = nixpkgs.legacyPackages.${system}; fontAwesomeSubset = let icons = [ "gem.svg" "heart.svg" "lightbulb.svg" ]; in lib.sources.cleanSourceWith { src = "${font-awesome}/svgs/regular"; filter = path: type: builtins.any (icon: builtins.baseNameOf path == icon) icons; }; in { packages.${system}.default = typix.lib.${system}.mkTypstDerivation { virtualPaths = [ fontAwesomeSubset ]; }; }; } ``` [nix-discussion-source-filtering]: https://discourse.nixos.org/t/filtering-source-trees-with-nix-and-nixpkgs/19148 [nix-ref-builtins-filtersource]: https://nixos.org/manual/nix/stable/language/builtins.html#builtins-filterSource [nixpkgs-fileset-fromsource]: https://nixos.org/manual/nixpkgs/stable/#function-library-lib.fileset.fromSource [nixpkgs-fileset-tosource]: https://nixos.org/manual/nixpkgs/stable/#function-library-lib.fileset.toSource [nixpkgs-fileset-unions]: https://nixos.org/manual/nixpkgs/stable/#function-library-lib.fileset.unions [nixpkgs-fileset]: https://nixos.org/manual/nixpkgs/stable/#sec-functions-library-fileset [nixpkgs-sources-cleansource]: https://nixos.org/manual/nixpkgs/stable/#function-library-lib.sources.cleanSource [nixpkgs-sources-cleansourcewith]: https://nixos.org/manual/nixpkgs/stable/#function-library-lib.sources.cleanSourceWith [nixpkgs-sources]: https://nixos.org/manual/nixpkgs/stable/#sec-functions-library-sources ================================================ FILE: docs/recipes/using-typst-packages.md ================================================ # Using Typst packages > TL;DR: Use [this example][published-example] as a template: > > ```bash > nix flake init --refresh -t 'github:loqusion/typix#with-typst-packages' > ``` > > [published-example]: https://github.com/loqusion/typix/blob/main/examples/typst-packages/flake.nix
Typst packages are considered experimental at the time of writing, and the methods documented here may become outdated at any point if breaking changes are made upstream. If you experience any unexpected errors or bugs, and there are no [open issues] related to your problem, feel free to [open an issue]. [open issues]: https://github.com/loqusion/typix/issues?q=is%3Aissue+is%3Aopen+sort%3Aupdated-desc+label%3A%22typst+packages%22 [open an issue]: https://github.com/loqusion/typix/issues/new?assignees=&labels=typst+packages&projects=&template=3-typst_packages.md&title=%5BTypst+packages%5D%3A+
There are two types of [Typst packages][typst-packages]: _published_ and _unpublished_: - [_Published_ packages](#published-typst-packages) are those submitted to the [Typst Packages repository][typst-packages] and can be browsed on [Typst Universe]. - [_Unpublished_ packages](#unpublished-typst-packages) are packages that are only available from unofficial sources. They can be stored locally on your system, on a GitHub repository, or elsewhere. ("Unpublished" can also refer to unpublished versions of a package which has published versions.) [Typst Universe]: https://typst.app/universe/ [Transitive dependencies] — that is, a _dependency's_ dependencies — must be added explicitly, in addition to any direct dependencies you have. For instance, `cetz` 0.3.4 depends on `oxifmt` 0.2.1, so if you use `cetz` 0.3.4 you must also ensure `oxifmt` 0.2.1 is provided. This applies recursively: if you depend on `A` which depends on `B` and `B` depends on `C`, you must explicitly specify both `B` _and_ `C`, in addition to `A`. Also, the precise version is important: if different versions of the same package are imported, _both versions_ must be specified. [Transitive dependencies]: https://en.wikipedia.org/wiki/Transitive_dependency The method to add Typst packages differs depending on whether they are published or unpublished. ## Published Typst packages Published Typst packages work out of the box for for [`watchTypstProject`] and commands executed while [`devShell`] is active. For [`buildTypstProject`], [`buildTypstProjectLocal`], and [`mkTypstDerivation`], there are two methods: - [`unstable_typstPackages`](#the-typstpackages-attribute) _(recommended)_ - [`TYPST_PACKAGE_CACHE_PATH`](#the-typst_package_cache_path-environment-variable) It is recommended to use `unstable_typstPackages`, as it is faster and consumes less disk space. ### The `unstable_typstPackages` attribute { #the-typstpackages-attribute } The `unstable_typstPackages` attribute is used to fetch packages from the official Typst packages CDN at `https://packages.typst.org`. For more information, see the respective documentation for the attribute on [`buildTypstProject`], [`buildTypstProjectLocal`], and [`mkTypstDerivation`]. ```nix { outputs = { nixpkgs, typix, }: let inherit (nixpkgs) lib; system = "x86_64-linux"; unstable_typstPackages = [ { name = "cetz"; version = "0.3.4"; hash = "sha256-5w3UYRUSdi4hCvAjrp9HslzrUw7BhgDdeCiDRHGvqd4="; } # Transitive dependencies must be manually specified # `oxifmt` is required by `cetz` { name = "oxifmt"; version = "0.2.1"; hash = "sha256-8PNPa9TGFybMZ1uuJwb5ET0WGIInmIgg8h24BmdfxlU="; } ]; build-drv = typix.lib.${system}.buildTypstProject { inherit unstable_typstPackages; # ... }; build-script = typix.lib.${system}.buildTypstProjectLocal { inherit unstable_typstPackages; # ... }; in { packages.${system}.default = build-drv; apps.${system}.default = { type = "app"; program = lib.getExe build-script; }; }; } ``` ### The `TYPST_PACKAGE_CACHE_PATH` environment variable This method downloads the _entire_ contents of the [Typst Packages repository][typst-packages], making all packages available in your Typst project. First, add the repository to flake inputs: ```nix { inputs.typst-packages = { url = "github:typst/packages"; flake = false; }; } ``` Then, use it in flake outputs: ```nix { outputs = { nixpkgs, typix, typst-packages, }: let inherit (nixpkgs) lib; system = "x86_64-linux"; build-drv = typix.lib.${system}.buildTypstProject { TYPST_PACKAGE_CACHE_PATH = "${typst-packages}/packages"; # ... }; build-script = typix.lib.${system}.buildTypstProjectLocal { TYPST_PACKAGE_CACHE_PATH = "${typst-packages}/packages"; # ... }; in { packages.${system}.default = build-drv; apps.${system}.default = { type = "app"; program = lib.getExe build-script; }; }; } ``` ## Unpublished Typst packages If the Typst package you want to use is stored locally — in the same repository as your flake — all you have to do is directly [import] the entrypoint module: [import]: https://typst.app/docs/reference/foundations/module/ ```typst #import "my-typst-package/src/lib.typ": my-item #my-item ``` You may want to [expand the source tree] or [filter certain files]. [expand the source tree]: ../recipes/specifying-sources.md#expanding-a-source-tree [filter certain files]: ../recipes/specifying-sources.md#source-filtering If the imported module imports any packages, those packages must be specified using one of the methods documented in this chapter. If you need to fetch an unpublished Typst package from a GitHub repository instead, see below. ### Fetching from a GitHub repository > You can use [this example][unpublished-example] as a template: > > ```bash > nix flake init --refresh -t 'github:loqusion/typix#with-typst-packages-unpublished' > ``` > > [unpublished-example]: https://github.com/loqusion/typix/blob/main/examples/typst-packages-unpublished/flake.nix Add the GitHub repository containing the unpublished Typst package to [flake inputs]: [flake inputs]: https://nix.dev/manual/nix/2.24/command-ref/new-cli/nix3-flake#flake-references ```nix { inputs = { my-typst-package = { url = "github:loqusion/my-typst-package"; flake = false; }; }; } ``` Then, create a derivation containing the inputs and pass it to Typst with the `TYPST_PACKAGE_PATH` environment variable: ```nix { outputs = { nixpkgs, typix, my-typst-package, }: let system = "x86_64-linux"; pkgs = nixpkgs.legacyPackages.${system}; inherit (pkgs) lib; inherit (lib.strings) escapeShellArg; mkTypstPackagesDrv = name: entries: let linkFarmEntries = lib.foldl (set: { name, version, namespace, input, }: set // { "${namespace}/${name}/${version}" = input; }) {} entries; in pkgs.linkFarm name linkFarmEntries; unpublishedTypstPackages = mkTypstPackagesDrv "unpublished-typst-packages" [ # Unpublished packages can be added here { name = "my-typst-package"; version = "0.1.0"; namespace = "local"; input = my-typst-package; } ]; # Any transitive dependencies on published packages must be added here unstable_typstPackages = [ { name = "oxifmt"; version = "0.2.1"; hash = "sha256-8PNPa9TGFybMZ1uuJwb5ET0WGIInmIgg8h24BmdfxlU="; } ]; build-drv = typix.lib.${system}.buildTypstProject { inherit unstable_typstPackages; TYPST_PACKAGE_PATH = unpublishedTypstPackages; # ... }; build-script = typix.lib.${system}.buildTypstProjectLocal { inherit unstable_typstPackages; TYPST_PACKAGE_PATH = unpublishedTypstPackages; # ... }; watch-script = typix.lib.${system}.watchTypstProject { # `watchTypstProject` can already access published packages, so # `unstable_typstPackages` is not needed here typstWatchCommand = "TYPST_PACKAGE_PATH=${escapeShellArg unpublishedTypstPackages} typst watch"; # ... }; in { packages.${system}.default = build-drv; apps.${system}.default = { type = "app"; program = lib.getExe build-script; }; apps.${system}.watch = { type = "app"; program = lib.getExe watch-script; }; }; } ``` Finally, you can use the package in a Typst file: ```typst #import "@local/my-typst-package:0.1.0": * #nothing ``` [typst-packages]: https://github.com/typst/packages [`buildTypstProjectLocal`]: ../api/derivations/build-typst-project-local.md#typstpackages [`buildTypstProject`]: ../api/derivations/build-typst-project.md#typstpackages [`devShell`]: ../api/derivations/dev-shell.md [`mkTypstDerivation`]: ../api/derivations/mk-typst-derivation.md#typstpackages [`watchTypstProject`]: ../api/derivations/watch-typst-project.md ================================================ FILE: examples/.gitignore ================================================ /*/flake.lock /*/result ================================================ FILE: examples/quick-start/.gitignore ================================================ result* ================================================ FILE: examples/quick-start/flake.nix ================================================ { description = "A Typst project"; inputs = { nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; typix = { url = "github:loqusion/typix"; inputs.nixpkgs.follows = "nixpkgs"; }; flake-utils.url = "github:numtide/flake-utils"; # Example of downloading icons from a non-flake source # font-awesome = { # url = "github:FortAwesome/Font-Awesome"; # flake = false; # }; }; outputs = inputs @ { nixpkgs, typix, flake-utils, ... }: flake-utils.lib.eachDefaultSystem (system: let pkgs = nixpkgs.legacyPackages.${system}; inherit (pkgs) lib; typixLib = typix.lib.${system}; src = typixLib.cleanTypstSource ./.; commonArgs = { typstSource = "main.typ"; fontPaths = [ # Add paths to fonts here # "${pkgs.roboto}/share/fonts/truetype" ]; virtualPaths = [ # Add paths that must be locally accessible to typst here # { # dest = "icons"; # src = "${inputs.font-awesome}/svgs/regular"; # } ]; }; # Compile a Typst project, *without* copying the result # to the current directory build-drv = typixLib.buildTypstProject (commonArgs // { inherit src; }); # Compile a Typst project, and then copy the result # to the current directory build-script = typixLib.buildTypstProjectLocal (commonArgs // { inherit src; }); # Watch a project and recompile on changes watch-script = typixLib.watchTypstProject commonArgs; in { checks = { inherit build-drv build-script watch-script; }; packages.default = build-drv; apps = rec { default = watch; build = flake-utils.lib.mkApp { drv = build-script; }; watch = flake-utils.lib.mkApp { drv = watch-script; }; }; devShells.default = typixLib.devShell { inherit (commonArgs) fontPaths virtualPaths; packages = [ # WARNING: Don't run `typst-build` directly, instead use `nix run .#build` # See https://github.com/loqusion/typix/issues/2 # build-script watch-script # More packages can be added here, like typstfmt # pkgs.typstfmt ]; }; }); } ================================================ FILE: examples/quick-start/main.typ ================================================ = Lorem ipsum #lorem(30) ================================================ FILE: examples/typst-packages/flake.nix ================================================ { description = "A Typst project that uses Typst packages"; inputs = { nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; typix = { url = "github:loqusion/typix"; inputs.nixpkgs.follows = "nixpkgs"; }; flake-utils.url = "github:numtide/flake-utils"; # Example of downloading icons from a non-flake source # font-awesome = { # url = "github:FortAwesome/Font-Awesome"; # flake = false; # }; }; outputs = inputs @ { nixpkgs, typix, flake-utils, ... }: flake-utils.lib.eachDefaultSystem (system: let pkgs = nixpkgs.legacyPackages.${system}; inherit (pkgs) lib; typixLib = typix.lib.${system}; src = typixLib.cleanTypstSource ./.; commonArgs = { typstSource = "main.typ"; fontPaths = [ # Add paths to fonts here # "${pkgs.roboto}/share/fonts/truetype" ]; virtualPaths = [ # Add paths that must be locally accessible to typst here # { # dest = "icons"; # src = "${inputs.font-awesome}/svgs/regular"; # } ]; }; unstable_typstPackages = [ { name = "cetz"; version = "0.3.4"; hash = "sha256-5w3UYRUSdi4hCvAjrp9HslzrUw7BhgDdeCiDRHGvqd4="; } # Required by cetz { name = "oxifmt"; version = "0.2.1"; hash = "sha256-8PNPa9TGFybMZ1uuJwb5ET0WGIInmIgg8h24BmdfxlU="; } ]; # Compile a Typst project, *without* copying the result # to the current directory build-drv = typixLib.buildTypstProject (commonArgs // { inherit src unstable_typstPackages; }); # Compile a Typst project, and then copy the result # to the current directory build-script = typixLib.buildTypstProjectLocal (commonArgs // { inherit src unstable_typstPackages; }); # Watch a project and recompile on changes watch-script = typixLib.watchTypstProject commonArgs; in { checks = { inherit build-drv build-script watch-script; }; packages.default = build-drv; apps = rec { default = watch; build = flake-utils.lib.mkApp { drv = build-script; }; watch = flake-utils.lib.mkApp { drv = watch-script; }; }; devShells.default = typixLib.devShell { inherit (commonArgs) fontPaths virtualPaths; packages = [ # WARNING: Don't run `typst-build` directly, instead use `nix run .#build` # See https://github.com/loqusion/typix/issues/2 # build-script watch-script # More packages can be added here, like typstfmt # pkgs.typstfmt ]; }; }); } ================================================ FILE: examples/typst-packages/main.typ ================================================ #import "@preview/cetz:0.3.4" #cetz.canvas({ import cetz.draw: * circle((0, 0)) line((0, 0), (2, 1)) }) ================================================ FILE: examples/typst-packages-unpublished/flake.nix ================================================ { description = "A Typst project that uses unpublished Typst packages"; inputs = { nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; typix = { url = "github:loqusion/typix"; inputs.nixpkgs.follows = "nixpkgs"; }; flake-utils.url = "github:numtide/flake-utils"; # TODO: Change this and the list in `unpublishedTypstPackages` my-typst-package = { url = "github:loqusion/my-typst-package"; flake = false; }; # Example of downloading icons from a non-flake source # font-awesome = { # url = "github:FortAwesome/Font-Awesome"; # flake = false; # }; }; outputs = inputs @ { nixpkgs, typix, flake-utils, ... }: flake-utils.lib.eachDefaultSystem (system: let pkgs = nixpkgs.legacyPackages.${system}; inherit (pkgs) lib; inherit (lib.strings) escapeShellArg; typixLib = typix.lib.${system}; src = typixLib.cleanTypstSource ./.; commonArgs = { typstSource = "main.typ"; fontPaths = [ # Add paths to fonts here # "${pkgs.roboto}/share/fonts/truetype" ]; virtualPaths = [ # Add paths that must be locally accessible to typst here # { # dest = "icons"; # src = "${inputs.font-awesome}/svgs/regular"; # } ]; }; mkTypstPackagesDrv = name: entries: let linkFarmEntries = lib.foldl (set: { name, version, namespace, input, }: set // { "${namespace}/${name}/${version}" = input; }) {} entries; in pkgs.linkFarm name linkFarmEntries; unpublishedTypstPackages = mkTypstPackagesDrv "unpublished-typst-packages" [ { name = "my-typst-package"; version = "0.1.0"; namespace = "local"; input = inputs.my-typst-package; } ]; # Any transitive dependencies must be added here # See https://loqusion.github.io/typix/recipes/using-typst-packages.html#the-typstpackages-attribute unstable_typstPackages = [ { name = "oxifmt"; version = "0.2.1"; hash = "sha256-8PNPa9TGFybMZ1uuJwb5ET0WGIInmIgg8h24BmdfxlU="; } ]; # Compile a Typst project, *without* copying the result # to the current directory build-drv = typixLib.buildTypstProject (commonArgs // { inherit src; inherit unstable_typstPackages; TYPST_PACKAGE_PATH = unpublishedTypstPackages; }); # Compile a Typst project, and then copy the result # to the current directory build-script = typixLib.buildTypstProjectLocal (commonArgs // { inherit src; inherit unstable_typstPackages; TYPST_PACKAGE_PATH = unpublishedTypstPackages; }); # Watch a project and recompile on changes watch-script = typixLib.watchTypstProject (commonArgs // { typstWatchCommand = "TYPST_PACKAGE_PATH=${escapeShellArg unpublishedTypstPackages} typst watch"; }); in { checks = { inherit build-drv build-script watch-script; }; packages.default = build-drv; apps = rec { default = watch; build = flake-utils.lib.mkApp { drv = build-script; }; watch = flake-utils.lib.mkApp { drv = watch-script; }; }; devShells.default = typixLib.devShell { inherit (commonArgs) fontPaths virtualPaths; packages = [ # WARNING: Don't run `typst-build` directly, instead use `nix run .#build` # See https://github.com/loqusion/typix/issues/2 # build-script watch-script # More packages can be added here, like typstfmt # pkgs.typstfmt ]; }; }); } ================================================ FILE: examples/typst-packages-unpublished/main.typ ================================================ #import "@local/my-typst-package:0.1.0": * #nothing ================================================ FILE: flake.nix ================================================ { description = "Deterministic Typst compilation with Nix"; nixConfig = { extra-substituters = ["https://typst-nix.cachix.org"]; extra-trusted-public-keys = ["typst-nix.cachix.org-1:OzDUMt0nd4wlI1AHucBPnchl4utWXeFTtUFt8XZ3DbA="]; }; inputs = { nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable"; }; outputs = {nixpkgs, ...}: let inherit (nixpkgs) lib; linux32BitSystems = ["i686-linux"]; linux64BitSystems = ["x86_64-linux" "aarch64-linux"]; linuxSystems = linux32BitSystems ++ linux64BitSystems; darwinSystems = ["x86_64-darwin" "aarch64-darwin"]; systems = linuxSystems ++ darwinSystems; forAllSystems = lib.genAttrs systems; pkgsFor = lib.genAttrs systems (system: nixpkgs.legacyPackages.${system}); mkLib = pkgs: import ./lib { inherit (pkgs) lib newScope; }; mkReleaseScript = pkgs: import ./release.nix { inherit pkgs; }; in { inherit mkLib; overlays.default = _final: _prev: {}; templates = rec { default = quick-start; quick-start = { description = "A Typst project"; path = ./examples/quick-start; }; with-typst-packages = { description = "A Typst project (with packages)"; path = ./examples/typst-packages; }; with-typst-packages-unpublished = { description = "A Typst project (with unpublished packages)"; path = ./examples/typst-packages-unpublished; }; }; checks = forAllSystems (system: let pkgs = pkgsFor.${system}; in (pkgs.callPackages ./checks { inherit pkgs; myLib = mkLib pkgs; })); lib = forAllSystems (system: mkLib pkgsFor.${system}); packages = forAllSystems (system: ( import ./pkgs.nix {pkgs = pkgsFor.${system};} )); apps = forAllSystems (system: { release = { type = "app"; program = lib.getExe (mkReleaseScript pkgsFor.${system}); }; }); formatter = forAllSystems (system: pkgsFor.${system}.alejandra); devShells = forAllSystems (system: let pkgs = pkgsFor.${system}; in { default = pkgs.mkShellNoCC { nativeBuildInputs = with pkgs; [ alejandra markdownlint-cli mdbook nodePackages.prettier ]; }; }); }; } ================================================ FILE: lib/buildTypstProject.nix ================================================ { lib, mkTypstDerivation, typstOptsFromArgs, }: args @ { typstCompileCommand ? "typst compile", typstSource ? "main.typ", ... }: let inherit (builtins) removeAttrs; inherit (lib.strings) escapeShellArg; typstOptsString = args.typstOptsString or (typstOptsFromArgs args); cleanedArgs = removeAttrs args [ "typstCompileCommand" "typstOpts" "typstOptsString" "typstOutput" "typstSource" ]; in mkTypstDerivation (cleanedArgs // { buildPhaseTypstCommand = args.buildPhaseTypstCommand or '' ${typstCompileCommand} ${typstOptsString} ${escapeShellArg typstSource} "$out" ''; }) ================================================ FILE: lib/buildTypstProjectLocal.nix ================================================ { buildTypstProject, inferTypstProjectOutput, lib, pkgs, typstOptsFromArgs, }: args @ {typstSource ? "main.typ", ...}: let inherit (builtins) removeAttrs; inherit (lib.strings) toShellVars; typstOptsString = args.typstOptsString or (typstOptsFromArgs args); typstOutput = args.typstOutput or (inferTypstProjectOutput ( {inherit typstSource;} // args )); buildTypstProjectDerivation = buildTypstProject ( cleanedArgs // {inherit typstOptsString typstSource;} ); buildTypstProjectImport = builtins.path {path = buildTypstProjectDerivation;}; cleanedArgs = removeAttrs args [ "scriptName" ]; in pkgs.writeShellApplication { name = args.scriptName or "typst-build"; runtimeInputs = [ pkgs.coreutils ]; text = '' ${toShellVars {inherit typstOutput;}} out=''${1:-''${typstOutput:?not defined}} mkdir -p "$(dirname "$out")" cp -LT --no-preserve=mode ${buildTypstProjectImport} "$out" ''; } ================================================ FILE: lib/cleanTypstSource.nix ================================================ {lib}: src: lib.cleanSourceWith { src = lib.cleanSource src; filter = path: type: let hasAcceptedSuffix = builtins.any (lib.flip lib.hasSuffix path) [ ".typ" # Typst files ".bib" # BibLaTeX files ]; isSpecialFile = builtins.elem (builtins.baseNameOf path) [ "typst.toml" "metadata.toml" ]; in builtins.any lib.id [ (type == "directory") hasAcceptedSuffix isSpecialFile ]; } ================================================ FILE: lib/coerceVirtualPathAttr.nix ================================================ {lib}: let inherit (builtins) isAttrs; inherit (lib.strings) isStringLike; in virtualPath: if isStringLike virtualPath then { src = "${virtualPath}"; dest = "."; } else if isAttrs virtualPath then { src = "${virtualPath.src}"; dest = virtualPath.dest or "."; } else throw "Invalid virtualPath: must be string or attrset" ================================================ FILE: lib/default.nix ================================================ { lib, newScope, }: lib.makeScope newScope (self: let inherit (self) callPackage; in { buildTypstProjectLocal = callPackage ./buildTypstProjectLocal.nix {}; buildTypstProject = callPackage ./buildTypstProject.nix {}; cleanTypstSource = callPackage ./cleanTypstSource.nix {}; coerceVirtualPathAttr = callPackage ./coerceVirtualPathAttr.nix {}; copyVirtualPathsHook = callPackage ./setupHooks/copyVirtualPaths.nix {}; devShell = callPackage ./devShell.nix {}; emojiFontPathFromString = callPackage ./emojiFontPathFromString.nix {}; inferTypstProjectOutput = callPackage ./inferTypstProjectOutput.nix {}; linkVirtualPaths = callPackage ./linkVirtualPaths.nix {}; mkTypstDerivation = callPackage ./mkTypstDerivation.nix {}; typstOptsFromArgs = callPackage ./typstOptsFromArgs.nix {}; unsetSourceDateEpochHook = callPackage ./setupHooks/unsetSourceDateEpoch.nix {}; watchTypstProject = callPackage ./watchTypstProject.nix {}; fetchTypstPackages = callPackage ./fetchTypstPackages.nix {}; }) ================================================ FILE: lib/devShell.nix ================================================ { emojiFontPathFromString, lib, linkVirtualPaths, mkShellNoCC, typst, }: args @ { checks ? {}, emojiFont ? "default", extraShellHook ? "", fontPaths ? [], forceVirtualPaths ? false, inputsFrom ? [], packages ? [], virtualPaths ? [], ... }: let inherit (builtins) isNull removeAttrs; inherit (lib) lists optionalAttrs optionalString; inherit (lib.strings) concatStringsSep; emojiFontPath = emojiFontPathFromString emojiFont; allFontPaths = fontPaths ++ lists.optional (!isNull emojiFontPath) emojiFontPath; unsetSourceDateEpochScript = builtins.readFile ./setupHooks/unsetSourceDateEpochScript.sh; cleanedArgs = removeAttrs args [ "checks" "emojiFont" "extraShellHook" "fontPaths" "forceVirtualPaths" "inputsFrom" "virtualPaths" ]; in mkShellNoCC (cleanedArgs // optionalAttrs (allFontPaths != []) { TYPST_FONT_PATHS = concatStringsSep ":" allFontPaths; } // { inputsFrom = builtins.attrValues checks ++ inputsFrom; packages = [ typst ] ++ packages; shellHook = args.shellHook or (optionalString (virtualPaths != []) (linkVirtualPaths { inherit virtualPaths forceVirtualPaths; })) + '' ${unsetSourceDateEpochScript} '' + optionalString (extraShellHook != "") '' ${extraShellHook} ''; }) ================================================ FILE: lib/emojiFontPathFromString.nix ================================================ { lib, pkgs, }: emojiFont: let inherit (builtins) isNull isString typeOf; in if isString emojiFont then ( if emojiFont == "default" then "${pkgs.twemoji-color-font}/share/fonts/truetype" else if emojiFont == "twemoji" then "${pkgs.twemoji-color-font}/share/fonts/truetype" else if emojiFont == "twemoji-cbdt" then "${pkgs.twitter-color-emoji}/share/fonts/truetype" else if emojiFont == "noto" then "${pkgs.noto-fonts-color-emoji}/share/fonts/noto" else if emojiFont == "noto-monochrome" then "${pkgs.noto-fonts-monochrome-emoji}/share/fonts/noto" else if emojiFont == "emojione" then "${pkgs.emojione}/share/fonts/truetype" else throw ''invalid value for `emojiFont`: "${emojiFont}". Must be one of: "twemoji", "twemoji-cbdt", "noto", "noto-monochrome", "emojione", null.'' ) else if isNull emojiFont then null else throw ''invalid type for `emojiFont`: ${typeOf emojiFont}. Must be string or null.'' ================================================ FILE: lib/fetchTypstPackages.nix ================================================ { fetchurl, runCommand, lib, symlinkJoin, stdenvNoCC, }: let inherit (lib.lists) forEach; typstPackageRegistry = "https://packages.typst.org"; # Downloads a tarball from the Typst package registry fetchTypstTarball = { namespace ? "preview", name, version, hash, } @ args: fetchurl { url = "${typstPackageRegistry}/${namespace}/${name}-${version}.tar.gz"; inherit hash; downloadToTemp = true; # The Typst package registry does not produce reproducible tarballs # We modify the tarball *before* Nix computes the output hash, # per instructions at https://reproducible-builds.org/docs/archives/ postFetch = '' pushd $(mktemp -d) mkdir extracted tar -xzf $downloadedFile -C extracted tar --owner=0 --group=0 --numeric-owner --format=gnu \ --sort=name --mtime="@1" \ -czf extracted.tar.gz -C extracted . mv ./extracted.tar.gz $out popd ''; }; # Downloads a Typst package from the package registry # Places the downloaded package to $out/${namespace}/${name}/${version} fetchTypstPackage = { namespace ? "preview", name, version, ... } @ args: let tarball = fetchTypstTarball args; packageSubdir = "${namespace}/${name}/${version}"; in runCommand "fetch-typst-package-${namespace}-${name}-${version}" {} '' tarball=${tarball} mkdir -p $out/${packageSubdir} tar -xzf $tarball -C $out/${packageSubdir} ''; in typstPackages: symlinkJoin { name = "typst-packages"; paths = forEach typstPackages fetchTypstPackage; } ================================================ FILE: lib/inferTypstProjectOutput.nix ================================================ {lib}: args_: let args = if builtins.isString args_ then {typstSource = args_;} else args_; supportedFormats = ["pdf" "svg" "png" "html"]; format = ({format = "pdf";} // args.typstOpts or {}).format; name = lib.strings.removeSuffix ".typ" (builtins.baseNameOf args.typstSource); extension = if builtins.elem format supportedFormats then format else lib.flip lib.trivial.warn "" '' typix could not infer the typst output extension since ${format} is unsupported to silence this warning consider one of the following: - set the format option to one of: pdf, svg, png, html - set `typstOutput` explicitly - if ${format} is supported by typst but not by typix, please open a PR at https://github.com/loqusion/typix ''; in name + lib.optionalString (extension != "") ".${extension}" ================================================ FILE: lib/linkVirtualPaths.nix ================================================ { coerceVirtualPathAttr, lib, pkgs, }: let inherit (builtins) toString; inherit (pkgs) symlinkJoin; inherit (lib) optionalString; inherit (lib.filesystem) pathIsDirectory; inherit (lib.strings) concatMapStringsSep toShellVars; shellUtils = '' _same_path() { [ "$(realpath "$1")" = "$(realpath "$2")" ] } _ensure_parent_exists() { mkdir -p "$(dirname "$1")" } _cleanup_parent() { local parent parent=$(dirname "$1") if ! _same_path "." "$1" && ! _same_path "." "$parent"; then rmdir -p --ignore-fail-on-non-empty "$parent" fi } ''; in { forceVirtualPaths ? false, virtualPaths, }: shellUtils + "\n" + (concatMapStringsSep "\n" (virtualPath_: let virtualPath = coerceVirtualPathAttr virtualPath_; source = if !pathIsDirectory (toString virtualPath.src) then virtualPath.src else (symlinkJoin { name = "symlink" + optionalString (virtualPath ? dest) "-${virtualPath.dest}"; paths = [virtualPath.src]; }); lnAdditionalOpts = optionalString forceVirtualPaths "--force"; cpAdditionalOpts = if forceVirtualPaths then "--force" else "--no-clobber"; in # We don't want a refusal to overwrite existing files to cause nix to fail, so we add `|| true` # to the commands this applies to '' ${toShellVars { virtualPathSrc = source; virtualPathDest = virtualPath.dest; }} _ensure_parent_exists "$virtualPathDest" if [ -f "$virtualPathSrc" ]; then echo "typix: linking ${virtualPath.src} to $virtualPathDest" if _same_path "." "$virtualPathDest"; then ln ${lnAdditionalOpts} -sv "$virtualPathSrc" "$virtualPathDest" || true else ln ${lnAdditionalOpts} -sTv "$virtualPathSrc" "$virtualPathDest" || true fi else echo "typix: linking ${virtualPath.src} to $virtualPathDest recursively" cp ${cpAdditionalOpts} -RTv --no-dereference --no-preserve=mode "$virtualPathSrc" "$virtualPathDest" || true fi _cleanup_parent "$virtualPathDest" '') virtualPaths) ================================================ FILE: lib/mkTypstDerivation.nix ================================================ { copyVirtualPathsHook, emojiFontPathFromString, lib, stdenvNoCC, typst, unsetSourceDateEpochHook, fetchTypstPackages, }: args @ { buildPhaseTypstCommand, emojiFont ? "default", fontPaths ? [], installPhaseCommand ? "", virtualPaths ? [], unstable_typstPackages ? [], ... }: assert lib.assertMsg (!(builtins.hasAttr "unstableTypstPackages" args)) '' `unstableTypstPackages` has been renamed to `unstable_typstPackages`. ''; let inherit (builtins) baseNameOf getEnv isNull removeAttrs; inherit (lib) lists optionalAttrs; inherit (lib.strings) concatStringsSep; emojiFontPath = emojiFontPathFromString emojiFont; allFontPaths = fontPaths ++ lists.optional (!isNull emojiFontPath) emojiFontPath; cleanedArgs = removeAttrs args [ "buildPhaseTypstCommand" "emojiFont" "fontPaths" "installPhaseCommand" "virtualPaths" "unstable_typstPackages" ]; name = args.name or (let pwd = getEnv "PWD"; in if pwd != "" then baseNameOf pwd else "typst"); nameArgs = if args ? version then { pname = args.pname or name; inherit (args) version; } else {inherit name;}; in stdenvNoCC.mkDerivation (cleanedArgs // nameArgs // optionalAttrs (allFontPaths != []) { TYPST_FONT_PATHS = concatStringsSep ":" allFontPaths; } // optionalAttrs (unstable_typstPackages != []) { TYPST_PACKAGE_CACHE_PATH = fetchTypstPackages unstable_typstPackages; } // { nativeBuildInputs = (args.nativeBuildInputs or []) ++ [ typst (copyVirtualPathsHook virtualPaths) unsetSourceDateEpochHook ]; buildPhase = args.buildPhase or '' runHook preBuild ${buildPhaseTypstCommand} runHook postBuild ''; installPhase = args.installPhase or '' runHook preInstall ${installPhaseCommand} runHook postInstall ''; }) ================================================ FILE: lib/setupHooks/copyVirtualPaths.nix ================================================ { coerceVirtualPathAttr, lib, makeSetupHook, }: virtualPaths: let inherit (lib.strings) concatMapStringsSep toShellVars; copyAllVirtualPaths = concatMapStringsSep "\n" (virtualPath_: let virtualPath = coerceVirtualPathAttr virtualPath_; in '' ${toShellVars { virtualPathSrc = virtualPath.src; virtualPathDest = virtualPath.dest; }} echo "Copying $virtualPathSrc to $virtualPathDest" _ensure_parent_exists "$virtualPathDest" if [ -f "$virtualPathSrc" ] && _same_path "." "$virtualPathDest"; then cp -Lv --reflink=auto --no-preserve=mode "$virtualPathSrc" "$virtualPathDest" else cp -LTRv --reflink=auto --no-preserve=mode "$virtualPathSrc" "$virtualPathDest" fi _cleanup_parent "$virtualPathDest" '') virtualPaths; in makeSetupHook { name = "copyVirtualPaths"; substitutions = { inherit copyAllVirtualPaths; }; } ./copyVirtualPathsHook.sh ================================================ FILE: lib/setupHooks/copyVirtualPathsHook.sh ================================================ _same_path() { [ "$(realpath "$1")" = "$(realpath "$2")" ] } _ensure_parent_exists() { mkdir -p "$(dirname "$1")" } _cleanup_parent() { local parent parent=$(dirname "$1") if ! _same_path "." "$1" && ! _same_path "." "$parent"; then rmdir -p --ignore-fail-on-non-empty "$parent" fi } copyVirtualPaths() { : @copyAllVirtualPaths@ } preBuildHooks+=(copyVirtualPaths) ================================================ FILE: lib/setupHooks/unsetSourceDateEpoch.nix ================================================ {makeSetupHook}: let unsetSourceDateEpochScript = builtins.readFile ./unsetSourceDateEpochScript.sh; in makeSetupHook { name = "unsetSourceDateEpoch"; substitutions = { inherit unsetSourceDateEpochScript; }; } ./unsetSourceDateEpochHook.sh ================================================ FILE: lib/setupHooks/unsetSourceDateEpochHook.sh ================================================ unsetSourceDateEpoch() { @unsetSourceDateEpochScript@ } preBuildHooks+=(unsetSourceDateEpoch) ================================================ FILE: lib/setupHooks/unsetSourceDateEpochScript.sh ================================================ unset SOURCE_DATE_EPOCH ================================================ FILE: lib/typstOptsFromArgs.nix ================================================ {lib}: origArgs: let inherit (builtins) elem isBool isNull tail; inherit (lib) optionalAttrs optionalString; inherit (lib.attrsets) filterAttrs mapAttrsToList recursiveUpdate; inherit (lib.lists) last; inherit (lib.strings) concatMapStringsSep concatStringsSep escapeShellArg splitString; pathExtension = path: let splitPathTail = tail (splitString "." path); in if (splitPathTail != []) then last splitPathTail else null; inferredFormatFromTypstProjectOutput = if ((origArgs ? typstOpts.format) || (! origArgs ? typstOutput)) then null else let inherit (origArgs) typstOutput; supportedExtensions = ["pdf" "svg" "png" "html"]; extension = pathExtension typstOutput; in if (elem extension supportedExtensions) then extension else null; inferredTypstOpts = optionalAttrs (!isNull inferredFormatFromTypstProjectOutput) { typstOpts.format = inferredFormatFromTypstProjectOutput; }; defaultArgs = optionalAttrs (! origArgs ? typstOutput) { typstOpts.format = "pdf"; }; args = recursiveUpdate ( recursiveUpdate defaultArgs inferredTypstOpts ) origArgs; parametersFrom = opt: value: "--${opt}" + optionalString (!isBool value) " ${escapeShellArg value}"; in concatStringsSep " " ( mapAttrsToList ( opt: value: if builtins.isList value then (concatMapStringsSep " " (parametersFrom opt) value) else (parametersFrom opt value) ) (filterAttrs (_: v: v != false && !isNull v) args.typstOpts) ) ================================================ FILE: lib/watchTypstProject.nix ================================================ { emojiFontPathFromString, inferTypstProjectOutput, lib, linkVirtualPaths, pkgs, typst, typstOptsFromArgs, }: args @ { emojiFont ? "default", fontPaths ? [], forceVirtualPaths ? false, typstSource ? "main.typ", typstWatchCommand ? "typst watch", virtualPaths ? [], ... }: let inherit (builtins) isNull removeAttrs; inherit (lib) lists optionalString; inherit (lib.strings) concatStringsSep toShellVars; emojiFontPath = emojiFontPathFromString emojiFont; allFontPaths = fontPaths ++ lists.optional (!isNull emojiFontPath) emojiFontPath; typstOptsString = args.typstOptsString or (typstOptsFromArgs args); typstOutput = args.typstOutput or (inferTypstProjectOutput ( {inherit typstSource;} // args )); unsetSourceDateEpochScript = builtins.readFile ./setupHooks/unsetSourceDateEpochScript.sh; cleanedArgs = removeAttrs args [ "emojiFont" "fontPaths" "forceVirtualPaths" "scriptName" "text" "typstOpts" "typstOptsString" "typstOutput" "typstSource" "typstWatchCommand" "virtualPaths" ]; in pkgs.writeShellApplication (cleanedArgs // { name = args.scriptName or args.name or "typst-watch"; runtimeInputs = (args.runtimeInputs or []) ++ [ pkgs.coreutils typst ]; text = optionalString (allFontPaths != []) '' export TYPST_FONT_PATHS=${concatStringsSep ":" allFontPaths} '' + optionalString (virtualPaths != []) (linkVirtualPaths { inherit virtualPaths forceVirtualPaths; }) + '' ${toShellVars {inherit typstOutput typstSource;}} out=''${1:-''${typstOutput:?not defined}} mkdir -p "$(dirname "$out")" ${unsetSourceDateEpochScript} ${typstWatchCommand} ${typstOptsString} "$typstSource" "$out" ''; }) ================================================ FILE: pkgs.nix ================================================ {pkgs}: { docs = let inherit (pkgs) lib; root = ./.; rootPrefix = builtins.toString root; src = lib.sources.cleanSourceWith { src = root; filter = path: _: let relativePath = lib.strings.removePrefix rootPrefix path; in builtins.any ((lib.flip lib.strings.hasPrefix) relativePath) [ "/docs" "/README.md" ]; }; in pkgs.stdenvNoCC.mkDerivation { name = "typix-mdbook"; inherit src; nativeBuildInputs = with pkgs; [mdbook]; buildPhase = '' mdbook build docs --dest-dir "$out" ''; }; } ================================================ FILE: release.nix ================================================ {pkgs}: pkgs.writeShellApplication { name = "github-release"; runtimeInputs = with pkgs; [ gh git nodePackages.semver ]; text = '' VALID_INCREMENT_TYPES="major minor patch premajor preminor prepatch prerelease" increment=''${1:-patch} # shellcheck disable=SC2076 if [[ ! " $VALID_INCREMENT_TYPES " =~ " $increment " ]]; then echo "Invalid increment type: $increment" >&2 echo "Expected one of: ''${VALID_INCREMENT_TYPES// /, }" >&2 exit 1 fi current_version=$(git describe --tags --abbrev=0) next_version=$(semver --increment "$increment" "$current_version") gh release create --generate-notes "$next_version" ''; }