Migration Guide¶
Migration from v3¶
v4 introduces a profile-based architecture: each CV variant lives in its own profile_<name>/ directory with a self-contained metadata.toml and content modules. This solves #142 — you can now vary any field per profile ([personal.info], [layout], [inject], the lot), not just the three localized strings v3 supported via [lang.<code>].
Design principles¶
- One profile = one complete CV configuration. Look at a single
profile_<name>/metadata.tomland you see the full effective config for that profile — no merging, no inheritance. - No root
metadata.toml. The v4 template ships only profile directories; there is no shared/base config layer. - DRY is the user's job, not the framework's. If you maintain many profiles and want to share config, you can add your own preprocessor or symlinks; the package stays simple.
Upgrade steps¶
1. Rename your module folder to a profile folder:
2. Move your metadata.toml into the profile folder and flatten the schema:
You also need to flatten the v3 [lang.<code>] structure to top-level fields. v4 panics on detection of any v3-only schema field (consistent with the existing v2→v3 inject_* migration guards) — see "Schema migration guards" below for the exact field mapping.
3. Update your cv.typ:
// Before (v3)
#import "@preview/brilliant-cv:4.0.1": cv
#let metadata = toml("./metadata.toml")
#let cv-language = sys.inputs.at("language", default: none)
#let metadata = if cv-language != none {
metadata + (language: cv-language)
} else {
metadata
}
#let import-modules(modules, lang: metadata.language) = {
for module in modules {
include { "modules_" + lang + "/" + module + ".typ" }
}
}
// After (v4) — profile-based, no merge
#import "@preview/brilliant-cv:4.0.1": cv
#let profile = sys.inputs.at("profile", default: "en")
#let metadata = toml("profile_" + profile + "/metadata.toml")
#let import-modules(modules) = {
for module in modules {
include { "profile_" + profile + "/" + module + ".typ" }
}
}
4. Update letter.typ — same preamble pattern.
5. Update CLI commands:
6. Adding more profiles. Copy profile_en/ to profile_<new>/ and edit the fields that differ. Each profile is independent — there's no DRY mechanism inside the package, by design.
See Recipes → Switching Profiles for compile-time examples.
Schema migration guards (panic on v3 fields)¶
v4 follows the same pattern the package already used for the v2→v3 inject_ai_prompt / inject_keywords migration: when a removed v3 schema field is detected at compile time, the package panics with a migration message rather than silently falling back. This avoids the "silent fallback" anti-pattern that hides behavior changes.
The guarded fields and their replacements:
| v3 field | Panic? | v4 replacement |
|---|---|---|
language |
✅ | Set typography explicitly: [layout.fonts] regular_fonts, [layout.fonts] header_font, [layout.section] title_highlight, [personal] display_name, [layout] date_width |
non_latin_font |
✅ | List both fonts in [layout.fonts] regular_fonts = ["Source Sans 3", "Heiti SC"] and set [layout.fonts] header_font. typst's codepoint-level fallback handles mixed scripts — verified empirically (pdffonts shows identical embedded subsets to the v3 trigger). |
non_latin_name |
✅ | [personal] display_name — overrides the Latin first/last split with a single styled string |
[lang.<code>] table |
✅ | Set header_quote, cv_footer, letter_footer as top-level fields in profile_<name>/metadata.toml |
[lang.non_latin] table |
✅ (covered by [lang.*] panic above) |
Use [personal] display_name and [layout.fonts] |
v3 (language=zh) → v4 example¶
Before:
language = "zh"
non_latin_font = "Heiti SC"
non_latin_name = "王道尔"
[lang.zh]
header_quote = "具有丰富经验的数据分析师,随时可入职"
cv_footer = "简历"
letter_footer = "申请信"
After:
header_quote = "具有丰富经验的数据分析师,随时可入职"
cv_footer = "简历"
letter_footer = "申请信"
[layout]
date_width = "4.7cm"
[layout.fonts]
regular_fonts = ["Source Sans 3", "Heiti SC"]
header_font = "Heiti SC"
[layout.section]
title_highlight = "full"
[personal]
display_name = "王道尔"
The _is-non-latin() whitelist and _default-date-width() lookup table are gone from src/utils/lang.typ (file deleted). Adding a new non-Latin script (Arabic, Hebrew, Thai, Devanagari, …) no longer requires a framework change — users configure typography directly.
Why panic instead of silent fallback?¶
The package supports three backward-compat patterns. v4 deliberately picks panic-with-migration-message for schema changes:
| Pattern | When used | Example |
|---|---|---|
| Panic with migration message | Removed metadata schema fields | inject_ai_prompt, v3 language / non_latin_* / [lang.*] |
| Fully removed | Renamed function/parameter aliases | cvEntry, profilePhoto, awesomeColors (typst gives a generic "unknown parameter" error) |
| Silent fallback | ❌ Avoided in v4 — was used briefly for v3 metadata fields and removed because hidden behavior changes broke the "explicit > implicit" design |
Removed in v4 (no longer panic — fully removed)¶
The following parameter aliases and function aliases have panicked since v3 and are now removed entirely in v4. Code still using them will fail with a generic "unknown parameter" / "unknown function" error rather than the v3 deprecation panic.
Parameter aliases (now removed):
| Removed name | Use instead |
|---|---|
profilePhoto (in cv()) |
profile-photo |
myAddress (in letter()) |
sender-address |
recipientName (in letter()) |
recipient-name |
recipientAddress (in letter()) |
recipient-address |
awesomeColors (in entry/section/honor) |
awesome-colors |
refStyle (in cv-publication) |
ref-style |
refFull (in cv-publication) |
ref-full |
keyList (in cv-publication) |
key-list |
Function aliases (now removed):
cvEntry, cvEntryStart, cvEntryContinued, cvSection, cvSkill, cvSkillWithLevel, cvSkillTag, cvHonor, cvPublication, hBar — use the kebab-case equivalents.
Schema migration guards retained: inject_ai_prompt and inject_keywords still panic with a clear upgrade message if found in metadata.toml. These are kept because silently ignoring an unknown metadata key would be confusing — a user who sees their ATS keywords disappear should know why.
Migration from v2¶
v3 introduces a new directory structure, kebab-case naming, and removes several deprecated features. If you are upgrading from v2, follow these steps.
1. Update Imports¶
The package entry point is unchanged, but you should update any version-pinned imports:
// Before (v2)
#import "@preview/brilliant-cv:4.0.1": *
// After (v3)
#import "@preview/brilliant-cv:4.0.1": *
2. Parameter Renaming (now panics)¶
In v3, all camelCase parameter aliases panic at compile time instead of silently mapping to the new names. Update all call sites:
| Old (v2, camelCase) | New (v3, kebab-case) | Function |
|---|---|---|
profilePhoto |
profile-photo |
cv() |
myAddress |
sender-address |
letter() |
recipientName |
recipient-name |
letter() |
recipientAddress |
recipient-address |
letter() |
awesomeColors |
awesome-colors |
cv-section, cv-entry, cv-honor, etc. |
refStyle |
ref-style |
cv-publication |
refFull |
ref-full |
cv-publication |
keyList |
key-list |
cv-publication |
3. Removed Function Aliases (now panic)¶
The old camelCase function names now panic immediately. Rename all usages:
| Old (v2) | New (v3) |
|---|---|
cvEntry |
cv-entry |
cvEntryStart |
cv-entry-start |
cvEntryContinued |
cv-entry-continued |
cvSection |
cv-section |
cvSkill |
cv-skill |
cvSkillWithLevel |
cv-skill-with-level |
cvSkillTag |
cv-skill-tag |
cvHonor |
cv-honor |
cvPublication |
cv-publication |
hBar |
h-bar |
4. Removed [inject] Keys (now panic)¶
The old injection keys have been removed. If your metadata.toml still contains them, the CV will panic:
# Before (v2) — these now cause panics
[inject]
inject_ai_prompt = true
inject_keywords = true
injected_keywords_list = ["Python", "SQL"]
# After (v3) — just use the list directly; remove the boolean flags
[inject]
injected_keywords_list = ["Python", "SQL"]
# custom_ai_prompt_text = "Optional custom prompt"
inject_keywordshas been removed — ifinjected_keywords_listis present, keywords are injected automaticallyinject_ai_prompthas been removed — usecustom_ai_prompt_textinstead
5. Template Updates¶
If you are using the template, update your cv.typ and letter.typ to use the new parameter names. See the API Reference for the current signatures.
Migration from v1¶
Note
The version v1 is now deprecated, due to the compliance to Typst Packages standard. However, if you want to continue to develop on the older version, please refer to the v1-legacy branch.
With an existing CV project using the v1 version of the template, a migration is needed, including replacing some files and some content in certain files.
-
Delete old submodule — Remove the
brilliant-CVfolder and.gitmodules. Future package management is handled directly by Typst. -
Migrate metadata — Migrate all the config from
metadata.typby creating a newmetadata.toml. Follow the example TOML file in the repo — it is rather straightforward to migrate. -
Update entry points — For
cv.typandletter.typ, copy the new files from the repo, and adapt the modules you have in your project. -
Update module files in
modules_*/:- Delete the old import
#import "../brilliant-CV/template.typ": *, and replace it with the import statements from the new template files. - Due to the Typst path handling mechanism, one cannot directly pass the path string to some functions anymore. This concerns, for example, the
logoargument incv-entry, but alsocv-publicationas well. Some parameter names were changed, but most importantly, you should pass a function instead of a string (i.e.image("logo.png")instead of"logo.png"). Refer to the new template files for reference.
- Delete the old import
-
Install fonts — You might need to install
FontAwesome 6,RobotoandSource Sans Proon your local system now, as new Typst package guidelines discourage including these large files. -
Compile — Run
typst compile cv.typwithout passing thefont-pathflag. All should be good now — congrats!
Tip
Feel free to raise an issue for more assistance should you encounter a problem that you cannot solve on your own.