@anja.codes/vitakit 0.1.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (70) hide show
  1. package/README.md +52 -131
  2. package/dist/check.mjs +20 -11
  3. package/dist/chunks/authoring.mjs +372 -0
  4. package/dist/{shared/vitakit.CypCF-4Q.mjs → chunks/bibliography.mjs} +74 -237
  5. package/dist/chunks/build.mjs +178 -0
  6. package/dist/chunks/documents.mjs +152 -0
  7. package/dist/chunks/draft.mjs +222 -0
  8. package/dist/chunks/eject.mjs +59 -0
  9. package/dist/chunks/emit.mjs +216 -0
  10. package/dist/chunks/eta.mjs +16 -0
  11. package/dist/chunks/fields.mjs +79 -0
  12. package/dist/chunks/inventory.mjs +201 -0
  13. package/dist/chunks/migrations.mjs +218 -0
  14. package/dist/chunks/plan.mjs +172 -0
  15. package/dist/chunks/prompt.mjs +33 -0
  16. package/dist/chunks/publication-lists.mjs +202 -0
  17. package/dist/chunks/schema-source.mjs +12 -0
  18. package/dist/chunks/schemas.mjs +109 -0
  19. package/dist/chunks/sections.mjs +109 -0
  20. package/dist/chunks/snippets.mjs +199 -0
  21. package/dist/chunks/validate.mjs +133 -0
  22. package/dist/chunks/vue.mjs +153 -0
  23. package/dist/chunks/yaml.mjs +62 -0
  24. package/dist/cli.d.mts +33 -40
  25. package/dist/cli.mjs +7 -224
  26. package/dist/index.d.mts +379 -49
  27. package/dist/index.mjs +15 -7
  28. package/dist/nuxt.mjs +48 -10
  29. package/dist/runtime/components/VitaPublications.vue +21 -22
  30. package/dist/runtime/components/VitaSection.vue +94 -38
  31. package/dist/runtime/composables/useVitakit.ts +14 -3
  32. package/dist/schemas/education.ts +4 -4
  33. package/dist/schemas/employment.ts +3 -3
  34. package/dist/schemas/publications.ts +10 -8
  35. package/dist/schemas/supervision.ts +3 -3
  36. package/dist/schemas/talks.ts +1 -1
  37. package/dist/schemas/teaching.ts +3 -3
  38. package/dist/shared/index.d.mts +60 -9
  39. package/dist/shared/index.mjs +3 -57
  40. package/dist/shared/{vitakit.D8WRg_Xx.d.mts → vitakit.BfR_mUHm.d.mts} +25 -12
  41. package/dist/shared/vitakit.CCTDg7pz.mjs +199 -0
  42. package/dist/shared/vitakit.CfB3hMHl.mjs +60 -0
  43. package/dist/shared/{vitakit.BlU-p01e.mjs → vitakit.CtChKO0I.mjs} +1 -1
  44. package/dist/shared/vitakit.D-DdJaDZ.mjs +20 -0
  45. package/dist/shared/vitakit.D10HyDhF.mjs +19 -0
  46. package/dist/shared/vitakit.DCK90zq4.mjs +67 -0
  47. package/dist/shared/vitakit.DQ6PmGoL.mjs +34 -0
  48. package/dist/shared/vitakit.DcQOdM4I.mjs +57 -0
  49. package/dist/shared/vitakit.DteA9vLy.mjs +135 -0
  50. package/dist/shared/vitakit.DvABWuUc.mjs +121 -0
  51. package/dist/shared/vitakit.DvvUYUTS.mjs +200 -0
  52. package/dist/shared/vitakit.TSMTC5Jl.mjs +6 -0
  53. package/dist/shared/vitakit.UfoXdL7U.mjs +22 -0
  54. package/dist/shared/vitakit.zwr1ssO5.mjs +1850 -0
  55. package/dist/tex/templates/_generic.eta +6 -4
  56. package/dist/tex/templates/education.eta +10 -5
  57. package/dist/tex/templates/employment.eta +12 -5
  58. package/dist/tex/templates/publications.eta +7 -3
  59. package/dist/tex/templates/supervision.eta +10 -2
  60. package/dist/tex/templates/talks.eta +5 -1
  61. package/dist/tex/templates/teaching.eta +6 -4
  62. package/dist/tex/vitakit-cv.cls +36 -10
  63. package/dist/tex.d.mts +80 -11
  64. package/dist/tex.mjs +27 -117
  65. package/package.json +10 -7
  66. package/src/bin/vitakit.mjs +3 -153
  67. package/dist/shared/vitakit.BRhjqpAt.mjs +0 -250
  68. package/dist/shared/vitakit.BxcnlFGP.mjs +0 -325
  69. package/src/bin/vitakit-build-cv.mjs +0 -50
  70. package/src/bin/vitakit-check.mjs +0 -57
@@ -1,6 +1,8 @@
1
1
  <% for (const item of it.entries) { %>
2
- \vitaentry{<%~ it.date(item.date) %>}{<%~ it.rich(it.titleOf(item)) %>}{<%~ it.secondaryOf(item) %>}
3
- <% if (item.note) { %>
4
- \vitanote{<%~ it.rich(item.note) %>}
5
- <% } %>
2
+ <%~ it.vita.entry({
3
+ date: it.date(item.date),
4
+ title: it.generic.title(item),
5
+ context: it.generic.context(item)
6
+ }) %>
7
+ <%~ it.vita.note(item.note) %>
6
8
  <% } %>
@@ -1,9 +1,14 @@
1
1
  <% for (const item of it.entries) { %>
2
- \vitaentry{<%~ it.date(item.date) %>}{<%~ it.rich(item.degree) %>}{<%~ it.inst(item.institution) %>}
2
+ <%~ it.vita.entry({
3
+ date: it.date(item.date),
4
+ title: item.degree,
5
+ context: it.linked(item.institution)
6
+ }) %>
3
7
  <% if (item.thesis) { %>
4
- \vitanote{<%~ it.tex(item.thesis.type ?? 'Thesis') %>: \emph{<%~ it.rich(item.thesis.title) %>}<% if (item.thesis.supervisor) { %>, supervised by <%~ it.linkOrText(item.thesis.supervisor) %><% } %>}
5
- <% } %>
6
- <% if (item.note) { %>
7
- \vitanote{<%~ it.rich(item.note) %>}
8
+ <%~ it.vita.note(it.join([
9
+ it.optional`${item.thesis.type ?? 'Thesis'}: \emph{${item.thesis.title}}`,
10
+ it.optional`, supervised by ${it.linked(item.thesis.supervisor)}`
11
+ ], '')) %>
8
12
  <% } %>
13
+ <%~ it.vita.note(item.note) %>
9
14
  <% } %>
@@ -1,9 +1,16 @@
1
1
  <% for (const item of it.entries) { %>
2
- \vitaentry{<%~ it.date(item.date) %>}{<%~ it.rich(item.position) %>}{<%~ it.inst(item.institution) %>}
2
+ <%~ it.vita.entry({
3
+ date: it.date(item.date),
4
+ title: item.position,
5
+ context: it.linked(item.institution)
6
+ }) %>
3
7
  <% if (item.project) { %>
4
- \vitanote{Project <% if (item.project.url) { %>\href{<%~ it.url(item.project.url) %>}{\emph{<%~ it.rich(item.project.title) %>}}<% } else { %>\emph{<%~ it.rich(item.project.title) %>}<% } %><% if (item.project.funder) { %> (<%~ it.tex(item.project.funder) %><% if (item.project.grant) { %>~<%~ it.tex(item.project.grant) %><% } %>)<% } %>}
5
- <% } %>
6
- <% if (item.note) { %>
7
- \vitanote{<%~ it.rich(item.note) %>}
8
+ <%~ it.vita.note(it.join([
9
+ it.optional`Project \emph{${it.linked(item.project.title, item.project.url)}}`,
10
+ item.project.funder
11
+ ? it.optional` (${it.join([item.project.funder, item.project.grant], it.optional`~`)})`
12
+ : ''
13
+ ], '')) %>
8
14
  <% } %>
15
+ <%~ it.vita.note(item.note) %>
9
16
  <% } %>
@@ -1,7 +1,11 @@
1
+ <% if (it.groups.length) { %>
1
2
  \begin{refsection}
2
- \nocite{*}
3
+ \begin{refcontext}[sorting=none]
4
+ <%~ it.bib.cite() %>
3
5
  <% for (const group of it.groups) { %>
4
- <%~ it.bibgroup(group) %>
5
- \printbibliography[check=vita-<%~ group.id %>, title={<%~ it.tex(group.label) %>}, heading=subbibliography]
6
+ <%~ it.bib.group(group) %>
7
+ \printbibliography[resetnumbers=true, check=vita-<%~ group.id %>, <% if (group.label !== undefined) { %>heading=subbibliography, title={<%~ it.text(group.label) %>}<% } else { %>heading=none<% } %>]
6
8
  <% } %>
9
+ \end{refcontext}
7
10
  \end{refsection}
11
+ <% } %>
@@ -1,4 +1,12 @@
1
1
  <% for (const item of it.entries) { %>
2
- \vitaentry{<%~ it.date(item.date) %><% if (item.ongoing) { %>--<% } %>}{<%~ it.tex(item.student) %>}{<%~ [item.degree && it.tex(item.degree), it.inst(item.institution)].filter(Boolean).join(', ') %>}
3
- \vitanote{\emph{<%~ it.rich(item.title) %>}<% if (item.coSupervisor) { %>, with <%~ it.tex(item.coSupervisor) %><% } %><% if (item.ongoing) { %> (in progress)<% } %>}
2
+ <%~ it.vita.entry({
3
+ date: it.join([it.date(item.date), item.ongoing ? '--' : ''], ''),
4
+ title: item.student,
5
+ context: it.join([item.degree, it.linked(item.institution)])
6
+ }) %>
7
+ <%~ it.vita.note(it.join([
8
+ it.optional`\emph{${item.title}}`,
9
+ it.optional`, with ${item.coSupervisor}`,
10
+ item.ongoing ? ' (in progress)' : ''
11
+ ], '')) %>
4
12
  <% } %>
@@ -1,3 +1,7 @@
1
1
  <% for (const item of it.entries) { %>
2
- \vitaentry{<%~ it.date(item.date) %>}{<%~ it.rich(item.title) %>}{<%~ [item.event && it.tex(item.event), item.location && it.tex(item.location)].filter(Boolean).join(', ') %>}
2
+ <%~ it.vita.entry({
3
+ date: it.date(item.date),
4
+ title: item.title,
5
+ context: it.join([item.event, item.location])
6
+ }) %>
3
7
  <% } %>
@@ -1,6 +1,8 @@
1
1
  <% for (const item of it.entries) { %>
2
- \vitaentry{<%~ it.term(item) %>}{<%~ it.rich(item.title) %>}{<%~ [item.role, it.inst(item.institution)].filter(Boolean).join(', ') %>}
3
- <% if (item.note) { %>
4
- \vitanote{<%~ it.rich(item.note) %>}
5
- <% } %>
2
+ <%~ it.vita.entry({
3
+ date: it.text(item.term) || it.date(item.date),
4
+ title: item.title,
5
+ context: it.join([item.role, it.linked(item.institution)])
6
+ }) %>
7
+ <%~ it.vita.note(item.note) %>
6
8
  <% } %>
@@ -13,6 +13,15 @@
13
13
  \ProcessOptions\relax
14
14
  \LoadClass[11pt,a4paper]{article}
15
15
 
16
+ %% pdfLaTeX's default font encoding (OT1) has no „, ‚, ł, ő and many other
17
+ %% characters of names, places and titles; T1 has them. LuaLaTeX and XeLaTeX
18
+ %% use Unicode fonts instead.
19
+ \RequirePackage{iftex}
20
+ \ifPDFTeX
21
+ \RequirePackage[T1]{fontenc}
22
+ \RequirePackage{lmodern}
23
+ \fi
24
+
16
25
  \RequirePackage[margin=2.5cm]{geometry}
17
26
  \RequirePackage{enumitem}
18
27
  \RequirePackage{xcolor}
@@ -27,10 +36,10 @@
27
36
  \RequirePackage[backend=biber, style=numeric, sorting=ydnt,
28
37
  defernumbers=true, maxbibnames=99, giveninits=true]{biblatex}
29
38
  %% \vitabibgroup{id}{key,key,...} defines the bibliography check `vita-id`,
30
- %% which keeps exactly the listed entries. The generated publications.tex
31
- %% uses one per group: vitakit decides the groups, with the website's rules
32
- %% (a missing `status` is `published`, case does not matter), so the PDF
33
- %% lists what the website lists. Keys are compared as strings.
39
+ %% which keeps exactly the listed entries. A generated list cites its keys
40
+ %% in the list's order and prints one bibliography per group with
41
+ %% sorting=none, so the PDF lists what the website lists, in its order.
42
+ %% Keys are compared as strings.
34
43
  \newcommand{\vitabibgroup}[2]{%
35
44
  \defbibcheck{vita-#1}{%
36
45
  \edef\vita@bibkey{\thefield{entrykey}}%
@@ -105,15 +114,32 @@
105
114
  %% that it is safe after any preceding content, including none.
106
115
  \newcommand{\vitanote}[1]{\par{\small\color{vitamuted}#1}}
107
116
 
108
- %% \vitasection{id} -- heading plus the generated file for this document.
109
- \newcommand{\vitasection}[1]{%
110
- \section{\vita@titlefor{#1}}%
111
- \begin{vitaentries}%
112
- \input{generated/\vita@document/#1.tex}%
113
- \end{vitaentries}}
117
+ %% Generated profile.tex marks sections whose selection is empty.
118
+ \newcommand{\vitaemptysection}[1]{\expandafter\def\csname vita@empty@#1\endcsname{}}
119
+
120
+ %% \vitasection[Heading]{id} -- an optional heading for this invocation only.
121
+ %% Otherwise use the document/preamble label, falling back to the section id.
122
+ \NewDocumentCommand{\vitasection}{o m}{%
123
+ \ifcsname vita@empty@#2\endcsname\else
124
+ \IfNoValueTF{#1}{\section{\vita@titlefor{#2}}}{\section{#1}}%
125
+ \begin{vitaentries}%
126
+ \input{generated/\vita@document/#2.tex}%
127
+ \end{vitaentries}%
128
+ \fi}
114
129
 
115
130
  %% \vitainput{id} -- the generated file without heading or list wrapper,
116
131
  %% for sections that bring their own structure (publications).
117
132
  \newcommand{\vitainput}[1]{\input{generated/\vita@document/#1.tex}}
118
133
 
134
+ %% Generated profile.tex marks publication lists without entries.
135
+ \newcommand{\vitaemptypublications}[1]{\expandafter\def\csname vita@emptylist@#1\endcsname{}}
136
+
137
+ %% \vitapublications[Heading]{list} -- a list from content/publication-lists.yml,
138
+ %% with an optional heading; neither appears when the list has no entries.
139
+ \NewDocumentCommand{\vitapublications}{o m}{%
140
+ \ifcsname vita@emptylist@#2\endcsname\else
141
+ \IfNoValueF{#1}{\section{#1}}%
142
+ \input{generated/\vita@document/publications/#2.tex}%
143
+ \fi}
144
+
119
145
  \endinput
package/dist/tex.d.mts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { z } from 'zod';
2
- import { Entry, PublicationGroup } from './index.mjs';
2
+ import { Entry, PublicationList } from './index.mjs';
3
3
 
4
4
  interface BuildOptions {
5
5
  rootDir: string;
@@ -7,11 +7,52 @@ interface BuildOptions {
7
7
  only?: string[];
8
8
  /** Emit .tex but skip latexmk. */
9
9
  noPdf?: boolean;
10
+ /**
11
+ * What happens to latexmk's output: shown as it runs (`inherit`, the
12
+ * default), or kept back and only its end shown on failure (`capture`).
13
+ */
14
+ latexOutput?: 'inherit' | 'capture';
15
+ /** Told about each finished step, for concise progress output. */
16
+ onProgress?: (event: BuildEvent) => void;
17
+ }
18
+ type BuildEvent = {
19
+ document: string;
20
+ step: 'tex';
21
+ dir: string;
22
+ files: number;
23
+ } | {
24
+ document: string;
25
+ step: 'pdf';
26
+ file: string;
27
+ };
28
+ /** A tool of the TeX distribution is not installed. */
29
+ declare class MissingToolError extends Error {
30
+ readonly tool: string;
31
+ constructor(tool: string);
10
32
  }
11
33
  declare function buildCv(options: BuildOptions): Promise<string[]>;
12
34
  /** Read a generated file, for tests and diagnostics. */
13
35
  declare function readGenerated(rootDir: string, document: string, section: string): Promise<string>;
14
36
 
37
+ interface PdfPlacement {
38
+ section: string;
39
+ document: string;
40
+ /** `cv-tex/cv.tex`. */
41
+ file: string;
42
+ line: number;
43
+ macro: 'vitasection' | 'vitainput';
44
+ /** The optional heading of `\vitasection[Heading]{id}`. */
45
+ heading?: string;
46
+ }
47
+ /** A `\vitapublications[Heading]{list}` line. */
48
+ interface PdfPublicationPlacement {
49
+ list: string;
50
+ document: string;
51
+ file: string;
52
+ line: number;
53
+ heading?: string;
54
+ }
55
+
15
56
  declare const documentSchema: z.ZodObject<{
16
57
  visibility: z.ZodDefault<z.ZodEnum<{
17
58
  public: "public";
@@ -19,15 +60,12 @@ declare const documentSchema: z.ZodObject<{
19
60
  private: "private";
20
61
  }>>;
21
62
  sections: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodObject<{
22
- where: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodUnion<readonly [z.ZodString, z.ZodArray<z.ZodString>]>>>;
63
+ where: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodUnion<readonly [z.ZodUnion<readonly [z.ZodString, z.ZodNumber, z.ZodBoolean]>, z.ZodArray<z.ZodUnion<readonly [z.ZodString, z.ZodNumber, z.ZodBoolean]>>]>>>;
23
64
  limit: z.ZodOptional<z.ZodNumber>;
24
65
  since: z.ZodOptional<z.ZodNumber>;
25
66
  until: z.ZodOptional<z.ZodNumber>;
26
67
  title: z.ZodOptional<z.ZodString>;
27
68
  }, z.core.$strict>>>;
28
- publications: z.ZodDefault<z.ZodObject<{
29
- groups: z.ZodOptional<z.ZodArray<z.ZodString>>;
30
- }, z.core.$strict>>;
31
69
  }, z.core.$strict>;
32
70
  type DocumentOptions = z.infer<typeof documentSchema>;
33
71
  interface DocumentSource {
@@ -35,17 +73,40 @@ interface DocumentSource {
35
73
  name: string;
36
74
  texFile: string;
37
75
  options: DocumentOptions;
76
+ /** Its `\vitasection` and `\vitainput` lines. */
77
+ placements: PdfPlacement[];
78
+ /** Its `\vitapublications` lines. */
79
+ publications: PdfPublicationPlacement[];
38
80
  }
39
- /** Read every `cv-tex/*.tex` and its options. */
81
+ /** Read every `cv-tex/*.tex` and its options. Strict: the first problem throws. */
40
82
  declare function discoverDocuments(cvDir: string): Promise<DocumentSource[]>;
41
83
  /** Select the entries of one section for one document. */
42
84
  declare function selectEntries(entries: Entry[], options: DocumentOptions, sectionId: string): Entry[];
43
85
 
44
86
  /** `tex/` in the installed package: templates and the document class. */
45
87
  declare const bundledTexDir: string;
88
+ /** Where a template came from, in the order they are tried. */
89
+ interface ResolvedTemplate {
90
+ path: string;
91
+ source: string;
92
+ /** `local` is in `cv-tex/templates/`, `bundled` is the package's own. */
93
+ origin: 'local' | 'bundled';
94
+ /** Whether this is `_generic.eta` rather than a template named for the section. */
95
+ generic: boolean;
96
+ }
97
+ /**
98
+ * The template that renders section `id`. A file in the repo always wins over
99
+ * the package default; `_generic.eta` is the fallback for sections without a
100
+ * dedicated template. `exclude` skips one path, so that replacing a local
101
+ * template can start from whatever would be used without it.
102
+ */
103
+ declare function resolveTemplate(id: string, userDir: string, { exclude }?: {
104
+ exclude?: string;
105
+ }): Promise<ResolvedTemplate>;
46
106
  interface EmitOptions {
47
107
  sections: Record<string, Entry[]>;
48
- publications: PublicationGroup[];
108
+ /** The publication lists the document shows, by name. */
109
+ publications: Record<string, PublicationList>;
49
110
  options: DocumentOptions;
50
111
  /** `cv-tex/templates/`, where user overrides live. */
51
112
  templateDir: string;
@@ -56,7 +117,14 @@ interface EmitOptions {
56
117
  /** Path of the .bib file, relative to the generated directory. */
57
118
  bibPath?: string;
58
119
  }
59
- /** Write one `.tex` file per section, plus `publications.tex`. */
120
+ /**
121
+ * Render one section with its effective template, without writing anything.
122
+ * `entries` are all loaded entries; the document's selection is applied here.
123
+ */
124
+ declare function renderSection(id: string, entries: Entry[], options: DocumentOptions, templateDir: string): Promise<string>;
125
+ /** Render one publication list, without writing anything. */
126
+ declare function renderPublications(list: PublicationList, templateDir: string, siteUrl?: string): Promise<string>;
127
+ /** Write one `.tex` file per section, and one per publication list in `publications/`. */
60
128
  declare function emitDocument(emit: EmitOptions): Promise<string[]>;
61
129
 
62
130
  /**
@@ -84,10 +152,11 @@ declare function escapeTex(value: string, { typography, source }?: EscapeOptions
84
152
  * Escape prose while preserving `$...$` and `$$...$$` spans verbatim.
85
153
  *
86
154
  * Used for `richText` fields, where a mathematician writes `Random $k$-ary
87
- * trees` and expects the formula to reach LaTeX untouched.
155
+ * trees` and expects the formula to reach LaTeX untouched. The prose is
156
+ * Markdown, so `\$` is a dollar sign and does not start a formula.
88
157
  */
89
158
  declare function escapeRichTex(value: string): string;
90
159
  declare function escapeUrl(value: string): string;
91
160
 
92
- export { buildCv, bundledTexDir, discoverDocuments, documentSchema, emitDocument, escapeRichTex, escapeTex, escapeUrl, readGenerated, selectEntries };
93
- export type { BuildOptions, DocumentOptions, DocumentSource, EmitOptions, EscapeOptions };
161
+ export { MissingToolError, buildCv, bundledTexDir, discoverDocuments, documentSchema, emitDocument, escapeRichTex, escapeTex, escapeUrl, readGenerated, renderPublications, renderSection, resolveTemplate, selectEntries };
162
+ export type { BuildEvent, BuildOptions, DocumentOptions, DocumentSource, EmitOptions, EscapeOptions, ResolvedTemplate };
package/dist/tex.mjs CHANGED
@@ -1,129 +1,39 @@
1
- import { spawn } from 'node:child_process';
2
- import { mkdir, readFile, writeFile, rm, copyFile, rename } from 'node:fs/promises';
3
- import { existsSync } from 'node:fs';
4
- import process from 'node:process';
5
- import { join, delimiter } from 'node:path';
6
- import { l as loadContent, a as loadPublicationsSchema } from './shared/vitakit.CypCF-4Q.mjs';
7
- import { m as fail, n as isSitePath } from './shared/vitakit.BxcnlFGP.mjs';
8
- import { d as discoverDocuments, e as emitDocument, b as bundledTexDir, a as escapeTex, c as escapeUrl, S as SITE_PATH_HELP } from './shared/vitakit.BRhjqpAt.mjs';
9
- export { f as documentSchema, g as escapeRichTex, s as selectEntries } from './shared/vitakit.BRhjqpAt.mjs';
1
+ export { MissingToolError, buildCv, readGenerated } from './chunks/build.mjs';
2
+ export { bundledTexDir, emitDocument, renderPublications, renderSection, resolveTemplate } from './chunks/emit.mjs';
3
+ export { discoverDocuments, documentSchema, selectEntries } from './chunks/documents.mjs';
4
+ export { e as escapeRichTex, a as escapeTex, b as escapeUrl } from './shared/vitakit.DcQOdM4I.mjs';
5
+ import 'node:child_process';
6
+ import 'node:fs/promises';
7
+ import 'node:fs';
8
+ import 'node:process';
9
+ import 'node:path';
10
+ import './shared/vitakit.DteA9vLy.mjs';
10
11
  import 'gray-matter';
11
12
  import 'yaml';
13
+ import './chunks/bibliography.mjs';
14
+ import './shared/vitakit.DQ6PmGoL.mjs';
15
+ import 'node:url';
16
+ import '@behackl/citation-js-extras';
12
17
  import 'unified';
18
+ import 'rehype-parse';
19
+ import 'rehype-sanitize';
20
+ import 'rehype-stringify';
13
21
  import 'remark-parse';
14
22
  import 'remark-gfm';
15
23
  import 'remark-math';
16
24
  import 'remark-rehype';
17
25
  import 'rehype-katex';
18
26
  import 'rehype-raw';
19
- import 'rehype-stringify';
20
27
  import 'unist-util-visit';
21
- import '@behackl/citation-js-extras';
22
- import 'rehype-parse';
23
- import 'rehype-sanitize';
28
+ import './shared/vitakit.DCK90zq4.mjs';
24
29
  import 'zod';
25
- import 'node:url';
30
+ import './chunks/publication-lists.mjs';
31
+ import './shared/vitakit.D10HyDhF.mjs';
32
+ import './chunks/sections.mjs';
33
+ import './chunks/schemas.mjs';
34
+ import './shared/vitakit.DvABWuUc.mjs';
35
+ import './shared/vitakit.TSMTC5Jl.mjs';
36
+ import './shared/vitakit.DvvUYUTS.mjs';
26
37
  import 'eta';
27
- import './shared/index.mjs';
28
- import './shared/vitakit.BlU-p01e.mjs';
29
-
30
- const CLASS_FILE = "vitakit-cv.cls";
31
- function run(command, args, cwd, env = {}) {
32
- return new Promise((resolve, reject) => {
33
- const child = spawn(command, args, { cwd, stdio: "inherit", env: { ...process.env, ...env } });
34
- child.on("error", (error) => reject(
35
- error.code === "ENOENT" ? new Error(`${command} not found. Install a TeX distribution, or use --no-pdf to only generate .tex files.`) : error
36
- ));
37
- child.on("exit", (code) => code === 0 ? resolve() : reject(new Error(`${command} exited with code ${code}`)));
38
- });
39
- }
40
- function profilePreamble(content) {
41
- const { profile } = content;
42
- const href = (url) => {
43
- if (isSitePath(url)) fail("content/profile.yml", `links: ${url} ${SITE_PATH_HELP}`);
44
- return escapeUrl(url);
45
- };
46
- const affiliation = profile.affiliation?.name ? escapeTex(profile.affiliation.name) : "";
47
- const tagline = [profile.title && escapeTex(profile.title), affiliation].filter(Boolean).join(", ");
48
- const contact = [
49
- profile.email && `\\href{mailto:${escapeUrl(profile.email)}}{${escapeTex(profile.email)}}`,
50
- ...profile.links.map((link) => `\\href{${href(link.url)}}{${escapeTex(link.label)}}`)
51
- ].filter(Boolean).join(" \\quad ");
52
- return [
53
- "% Generated by vitakit. Do not edit; edit content/profile.yml instead.",
54
- `\\vitaname{${escapeTex(profile.name)}}`,
55
- `\\vitatagline{${tagline}}`,
56
- `\\vitacontact{${contact}}`,
57
- ...profile.url ? siteUrlSourcemap(profile.url) : [],
58
- ""
59
- ].join("\n");
60
- }
61
- function siteUrlSourcemap(siteUrl) {
62
- const base = siteUrl.replace(/\/?$/, "/");
63
- if (!/^https?:\/\/[\w.:/-]+$/.test(base)) {
64
- fail("content/profile.yml", `url: ${siteUrl} may only contain letters, digits and - . _ : /`);
65
- }
66
- return [
67
- "\\ifdefined\\DeclareSourcemap",
68
- ` \\DeclareSourcemap{\\maps[datatype=bibtex]{\\map{\\step[fieldsource=url, match=\\regexp{\\A/}, replace={${base}}]}}}`,
69
- "\\fi"
70
- ];
71
- }
72
- async function buildDocument(document, content, options, cvDir) {
73
- const outDir = join(cvDir, "generated", document.name);
74
- await emitDocument({
75
- sections: content.sections,
76
- publications: content.publications,
77
- siteUrl: content.profile.url,
78
- options: document.options,
79
- templateDir: join(cvDir, "templates"),
80
- outDir
81
- });
82
- await writeFile(join(outDir, "profile.tex"), profilePreamble(content));
83
- const generatedClass = join(outDir, CLASS_FILE);
84
- if (existsSync(join(cvDir, CLASS_FILE))) await rm(generatedClass, { force: true });
85
- else await copyFile(join(bundledTexDir, CLASS_FILE), generatedClass);
86
- if (options.noPdf) return;
87
- const relativeOut = join("generated", document.name);
88
- await run("latexmk", [
89
- "-pdf",
90
- "-halt-on-error",
91
- "-interaction=nonstopmode",
92
- `-outdir=${relativeOut}`,
93
- `${document.name}.tex`
94
- ], cvDir, {
95
- // A trailing delimiter keeps the standard search paths.
96
- TEXINPUTS: `.${delimiter}${relativeOut}${delimiter}${process.env.TEXINPUTS ?? ""}`
97
- });
98
- await rename(join(outDir, `${document.name}.pdf`), join(cvDir, `${document.name}.pdf`));
99
- }
100
- async function buildCv(options) {
101
- const cvDir = join(options.rootDir, "cv-tex");
102
- if (!existsSync(cvDir)) {
103
- throw new Error(`no cv-tex/ directory in ${options.rootDir}`);
104
- }
105
- const content = await loadContent(options.rootDir, { strict: true, maxVisibility: "private", target: "pdf" });
106
- const documents = await discoverDocuments(cvDir);
107
- const groups = (await loadPublicationsSchema(options.rootDir)).groups.map((group) => group.id);
108
- for (const document of documents) {
109
- const unknown = (document.options.publications.groups ?? []).filter((id) => !groups.includes(id));
110
- if (unknown.length) {
111
- fail(`cv-tex/${document.name}.yml`, `publications.groups: unknown group ${unknown.join(", ")}; the groups are ${groups.join(", ")} (schemas/publications.ts)`);
112
- }
113
- }
114
- const selected = options.only?.length ? documents.filter((document) => options.only.includes(document.name)) : documents;
115
- if (!selected.length) {
116
- const known = documents.map((document) => document.name).join(", ");
117
- throw new Error(`no document matched ${options.only?.join(", ")}; available: ${known}`);
118
- }
119
- await mkdir(join(cvDir, "generated"), { recursive: true });
120
- for (const document of selected) {
121
- await buildDocument(document, content, options, cvDir);
122
- }
123
- return selected.map((document) => join(cvDir, `${document.name}.pdf`));
124
- }
125
- async function readGenerated(rootDir, document, section) {
126
- return readFile(join(rootDir, "cv-tex", "generated", document, `${section}.tex`), "utf8");
127
- }
128
-
129
- export { buildCv, bundledTexDir, discoverDocuments, emitDocument, escapeTex, escapeUrl, readGenerated };
38
+ import './shared/vitakit.CfB3hMHl.mjs';
39
+ import './shared/vitakit.D-DdJaDZ.mjs';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@anja.codes/vitakit",
3
- "version": "0.1.0",
3
+ "version": "0.3.0",
4
4
  "description": "Structured CV data in, academic website and LaTeX CV out.",
5
5
  "license": "MIT",
6
6
  "keywords": [
@@ -13,7 +13,7 @@
13
13
  "katex",
14
14
  "static-site"
15
15
  ],
16
- "homepage": "https://github.com/anja-codes/vitakit#readme",
16
+ "homepage": "https://vitakit.anja.codes",
17
17
  "bugs": {
18
18
  "url": "https://github.com/anja-codes/vitakit/issues"
19
19
  },
@@ -69,16 +69,20 @@
69
69
  "test": "vitest run",
70
70
  "test:watch": "vitest",
71
71
  "typecheck": "tsc --noEmit",
72
- "check": "pnpm typecheck && pnpm test && pnpm build && node scripts/check-dist.mjs && pnpm example:check",
72
+ "check": "pnpm typecheck && pnpm build && node scripts/check-dist.mjs && pnpm test && pnpm example:check",
73
73
  "check:dist": "node scripts/check-dist.mjs",
74
74
  "prepublishOnly": "pnpm check",
75
75
  "example": "node scripts/generate-example.mjs",
76
76
  "example:check": "node scripts/generate-example.mjs --check",
77
- "check:deps": "node scripts/check-deps.mjs"
77
+ "check:deps": "node scripts/check-deps.mjs",
78
+ "docs:dev": "pnpm --filter ./docs dev",
79
+ "docs:build": "pnpm build && pnpm --filter ./docs build"
78
80
  },
79
81
  "dependencies": {
82
+ "@babel/parser": "^7.29.9",
80
83
  "@behackl/citation-js-extras": "^0.4.0",
81
84
  "@types/hast": "^3.0.5",
85
+ "@vue/compiler-sfc": "^3.5.43",
82
86
  "citation-js": "^0.7.22",
83
87
  "eta": "^4.6.0",
84
88
  "gray-matter": "^4.0.3",
@@ -101,6 +105,7 @@
101
105
  "nuxt": "^4.0.0"
102
106
  },
103
107
  "devDependencies": {
108
+ "@babel/types": "^7.29.8",
104
109
  "@nuxt/kit": "^4.5.2",
105
110
  "@nuxt/schema": "^4.5.2",
106
111
  "@types/node": "^26.4.1",
@@ -114,8 +119,6 @@
114
119
  "access": "public"
115
120
  },
116
121
  "bin": {
117
- "vitakit": "src/bin/vitakit.mjs",
118
- "vitakit-check": "src/bin/vitakit-check.mjs",
119
- "vitakit-build-cv": "src/bin/vitakit-build-cv.mjs"
122
+ "vitakit": "src/bin/vitakit.mjs"
120
123
  }
121
124
  }