@heroiclands/package-build 23.1.0 → 23.2.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 (248) hide show
  1. package/CHANGELOG.md +71 -0
  2. package/bin/content-commands.mjs +68 -149
  3. package/bin/package-build.mjs +56 -10
  4. package/ci/check-banners.mjs +42 -6
  5. package/ci/confirm-npm-version.mjs +135 -0
  6. package/ci/content-format-reference.mjs +14 -5
  7. package/config.mjs +1 -0
  8. package/container.mjs +185 -4
  9. package/content-config.mjs +333 -210
  10. package/deploy.mjs +116 -10
  11. package/docs/api.md +238 -246
  12. package/docs/authoring/assets.md +11 -5
  13. package/docs/authoring/frontmatter.md +1 -1
  14. package/docs/authoring/links-and-markup.md +87 -0
  15. package/docs/commands.md +138 -99
  16. package/docs/configuration.md +277 -125
  17. package/docs/content-format.md +2 -0
  18. package/docs/diagnostics.md +48 -13
  19. package/docs/guides/book.md +341 -21
  20. package/docs/guides/e2e.md +13 -2
  21. package/docs/guides/icons.md +14 -4
  22. package/docs/guides/module-project.md +18 -8
  23. package/docs/guides/release-and-deploy.md +54 -6
  24. package/docs/guides/site.md +5 -2
  25. package/docs/project-setup.md +30 -10
  26. package/docs/reference/format-details.md +373 -170
  27. package/docs/reference/note-types.md +128 -109
  28. package/docs/reference/theme-pages.md +31 -6
  29. package/e2e.mjs +140 -28
  30. package/engine/actor-compiler.mjs +22 -31
  31. package/engine/address-charset.mjs +4 -17
  32. package/engine/address-fields.mjs +1 -1
  33. package/engine/address-render.mjs +12 -1
  34. package/engine/address-values.mjs +6 -0
  35. package/engine/address.mjs +46 -33
  36. package/engine/art-fields.mjs +30 -5
  37. package/engine/art-slots.mjs +10 -1
  38. package/engine/asset-bindings.mjs +12 -1
  39. package/engine/base-compiler.mjs +34 -65
  40. package/engine/being-age.mjs +2 -7
  41. package/engine/being-measurements.mjs +12 -1
  42. package/engine/calendar-artifacts.mjs +6 -0
  43. package/engine/calendar-choice.mjs +12 -1
  44. package/engine/calendar-format.mjs +12 -1
  45. package/engine/calendar-human.mjs +12 -1
  46. package/engine/compile-corpus.mjs +3 -1
  47. package/engine/content-address.mjs +9 -12
  48. package/engine/content-alerts.mjs +13 -1
  49. package/engine/content-blocks.mjs +14 -3
  50. package/engine/content-embeds.mjs +11 -3
  51. package/engine/content-figures.mjs +9 -0
  52. package/engine/content-footnotes.mjs +12 -1
  53. package/engine/content-format-check.mjs +11 -262
  54. package/engine/content-format.yaml +5 -2
  55. package/engine/content-images.mjs +3 -0
  56. package/engine/content-index.mjs +443 -20
  57. package/engine/content-links.mjs +29 -35
  58. package/engine/content-lint.mjs +2 -3
  59. package/engine/content-poetry.mjs +12 -1
  60. package/engine/content-slug.mjs +2 -14
  61. package/engine/content-spans.mjs +13 -1
  62. package/engine/content-tables.mjs +12 -1
  63. package/engine/culture-choice.mjs +210 -13
  64. package/engine/data-keys.mjs +19 -16
  65. package/engine/date-conversion.mjs +12 -1
  66. package/engine/declaration-check.mjs +9 -0
  67. package/engine/derived-fields.mjs +6 -7
  68. package/engine/diagnostics.mjs +34 -4
  69. package/engine/docs-checks.mjs +9 -0
  70. package/engine/document-subtypes.mjs +47 -34
  71. package/engine/event-views.mjs +139 -14
  72. package/engine/exported-scene.mjs +92 -28
  73. package/engine/extension-attributes.mjs +12 -1
  74. package/engine/field-spec.mjs +11 -102
  75. package/engine/folder-notes.mjs +0 -64
  76. package/engine/foreign-catalog.mjs +2 -1
  77. package/engine/format-generated.mjs +9 -0
  78. package/engine/foundry-entries.mjs +10 -28
  79. package/engine/frontmatter-lint.mjs +83 -392
  80. package/engine/frontmatter.mjs +20 -70
  81. package/engine/generate.mjs +10 -15
  82. package/engine/heading-attributes.mjs +12 -1
  83. package/engine/helpers.mjs +41 -102
  84. package/engine/holdings.mjs +1 -1
  85. package/engine/homepage.mjs +3 -10
  86. package/engine/ids.mjs +1 -167
  87. package/engine/index-records.mjs +6 -4
  88. package/engine/index.mjs +0 -3
  89. package/engine/infobox-render.mjs +15 -3
  90. package/engine/infobox.mjs +13 -14
  91. package/engine/item-compiler.mjs +4 -10
  92. package/engine/item-docs.mjs +3 -5
  93. package/engine/item-registry.mjs +2 -10
  94. package/engine/journals.mjs +1 -5
  95. package/engine/literature-notes.mjs +4 -12
  96. package/engine/macros.mjs +1 -2
  97. package/engine/map-graphviz.mjs +12 -1
  98. package/engine/markdown-expressions.mjs +12 -1
  99. package/engine/markdownlint-run.mjs +567 -0
  100. package/engine/metadata-index.mjs +42 -12
  101. package/engine/note-addresses.mjs +143 -35
  102. package/engine/note-claims.mjs +14 -28
  103. package/engine/note-dates.mjs +3 -3
  104. package/engine/note-frontmatter.mjs +6 -0
  105. package/engine/note-ids.mjs +8 -14
  106. package/engine/note-package.mjs +8 -25
  107. package/engine/note-state.mjs +1 -3
  108. package/engine/note-vocabulary.mjs +148 -84
  109. package/engine/number-words.mjs +12 -1
  110. package/engine/pack-config.mjs +15 -25
  111. package/engine/pack-router.mjs +10 -16
  112. package/engine/pathnames.mjs +8 -22
  113. package/engine/pdf-build.mjs +773 -121
  114. package/engine/pdf-coverage.mjs +188 -0
  115. package/engine/pdf-index.mjs +54 -7
  116. package/engine/pdf-pages.mjs +247 -0
  117. package/engine/pdf-render.mjs +114 -4
  118. package/engine/pdf-toc.mjs +29 -32
  119. package/engine/project-init.mjs +46 -6
  120. package/engine/prose-config.mjs +1 -1
  121. package/engine/prose-lint.mjs +52 -88
  122. package/engine/readability-lint.mjs +9 -0
  123. package/engine/reckoning-markers.mjs +12 -1
  124. package/engine/reference-targets.mjs +7 -0
  125. package/engine/runtime-only-fields.mjs +5 -8
  126. package/engine/scene-levels.mjs +2 -2
  127. package/engine/scenes.mjs +1 -1
  128. package/engine/shortcode-references.mjs +7 -0
  129. package/engine/site-build.mjs +30 -16
  130. package/engine/site-index.mjs +10 -16
  131. package/engine/site-preview.mjs +6 -0
  132. package/engine/site-root.mjs +8 -1
  133. package/engine/social-tie-terms.mjs +12 -1
  134. package/engine/social-ties.mjs +3 -0
  135. package/engine/sql-tables.mjs +56 -20
  136. package/engine/standing-terms.mjs +11 -1
  137. package/engine/standings.mjs +11 -27
  138. package/engine/system-block.mjs +39 -141
  139. package/engine/system-vocabulary.mjs +6 -0
  140. package/engine/walk-files.mjs +69 -0
  141. package/engine/web-wikilinks.mjs +23 -3
  142. package/engine/wikilink-syntax.mjs +32 -21
  143. package/engine/wikilinks.mjs +27 -16
  144. package/engine/yaml-lint.mjs +7 -4
  145. package/githooks/attribution.mjs +8 -0
  146. package/githooks/commit-message.mjs +9 -0
  147. package/hm3/actors.mjs +9 -48
  148. package/hm3/default-item-art.mjs +9 -9
  149. package/hm3/item-fields.mjs +2 -9
  150. package/hm3/template-priority.mjs +6 -7
  151. package/hugo-theme/data/banner-kinds.yaml +78 -0
  152. package/hugo-theme/data/banners.yaml +2 -3
  153. package/hugo-theme/layouts/404.html +3 -3
  154. package/hugo-theme/layouts/partials/banner-url.html +31 -7
  155. package/hugo-theme/layouts/partials/hero-banner.html +13 -14
  156. package/hugo-theme/layouts/partials/related.html +1 -1
  157. package/hugo-theme/layouts/partials/warn-missing-banner.html +1 -1
  158. package/hugo-theme/static/css/style.css +1 -1
  159. package/manifest.mjs +11 -4
  160. package/markdownlint-config.mjs +1 -1
  161. package/package.json +6 -3
  162. package/release.mjs +83 -18
  163. package/sohl/actors.mjs +16 -27
  164. package/sohl/default-item-art.mjs +3 -3
  165. package/sohl/document-subtypes.mjs +1 -16
  166. package/sohl/infobox.mjs +2 -3
  167. package/sohl/item-builders.mjs +5 -8
  168. package/sohl/item-fields.mjs +5 -35
  169. package/sohl/items.mjs +0 -4
  170. package/sohl/kb-passes.mjs +3 -3
  171. package/types/container.d.mts +116 -5
  172. package/types/content-config.d.mts +83 -35
  173. package/types/deploy.d.mts +42 -6
  174. package/types/e2e.d.mts +35 -3
  175. package/types/engine/address-charset.d.mts +4 -17
  176. package/types/engine/address.d.mts +17 -8
  177. package/types/engine/art-fields.d.mts +7 -4
  178. package/types/engine/art-slots.d.mts +6 -0
  179. package/types/engine/base-compiler.d.mts +15 -17
  180. package/types/engine/content-address.d.mts +9 -12
  181. package/types/engine/content-embeds.d.mts +4 -2
  182. package/types/engine/content-format-check.d.mts +0 -64
  183. package/types/engine/content-index.d.mts +82 -3
  184. package/types/engine/content-links.d.mts +2 -4
  185. package/types/engine/culture-choice.d.mts +51 -8
  186. package/types/engine/data-keys.d.mts +2 -2
  187. package/types/engine/derived-fields.d.mts +1 -2
  188. package/types/engine/diagnostics.d.mts +27 -4
  189. package/types/engine/document-subtypes.d.mts +12 -31
  190. package/types/engine/event-views.d.mts +14 -2
  191. package/types/engine/exported-scene.d.mts +3 -2
  192. package/types/engine/field-spec.d.mts +13 -122
  193. package/types/engine/folder-notes.d.mts +0 -39
  194. package/types/engine/foreign-catalog.d.mts +2 -1
  195. package/types/engine/foundry-entries.d.mts +4 -5
  196. package/types/engine/frontmatter-lint.d.mts +2 -2
  197. package/types/engine/frontmatter.d.mts +19 -45
  198. package/types/engine/generate.d.mts +1 -1
  199. package/types/engine/helpers.d.mts +25 -42
  200. package/types/engine/homepage.d.mts +3 -10
  201. package/types/engine/ids.d.mts +0 -114
  202. package/types/engine/index-records.d.mts +6 -4
  203. package/types/engine/index.d.mts +0 -1
  204. package/types/engine/infobox-render.d.mts +7 -1
  205. package/types/engine/infobox.d.mts +3 -2
  206. package/types/engine/item-docs.d.mts +1 -1
  207. package/types/engine/item-registry.d.mts +2 -4
  208. package/types/engine/literature-notes.d.mts +1 -1
  209. package/types/engine/markdownlint-run.d.mts +40 -0
  210. package/types/engine/metadata-index.d.mts +9 -1
  211. package/types/engine/note-addresses.d.mts +27 -2
  212. package/types/engine/note-claims.d.mts +3 -5
  213. package/types/engine/note-ids.d.mts +2 -4
  214. package/types/engine/note-package.d.mts +1 -1
  215. package/types/engine/note-state.d.mts +1 -3
  216. package/types/engine/note-vocabulary.d.mts +28 -21
  217. package/types/engine/pack-config.d.mts +2 -6
  218. package/types/engine/pack-router.d.mts +4 -4
  219. package/types/engine/pathnames.d.mts +3 -3
  220. package/types/engine/pdf-build.d.mts +98 -12
  221. package/types/engine/pdf-coverage.d.mts +60 -0
  222. package/types/engine/pdf-index.d.mts +34 -5
  223. package/types/engine/pdf-pages.d.mts +143 -0
  224. package/types/engine/pdf-render.d.mts +45 -1
  225. package/types/engine/pdf-toc.d.mts +12 -14
  226. package/types/engine/prose-config.d.mts +1 -1
  227. package/types/engine/prose-lint.d.mts +11 -10
  228. package/types/engine/runtime-only-fields.d.mts +2 -2
  229. package/types/engine/site-build.d.mts +9 -5
  230. package/types/engine/site-index.d.mts +4 -5
  231. package/types/engine/site-root.d.mts +1 -1
  232. package/types/engine/sql-tables.d.mts +31 -2
  233. package/types/engine/standing-terms.d.mts +2 -1
  234. package/types/engine/standings.d.mts +7 -9
  235. package/types/engine/system-block.d.mts +10 -44
  236. package/types/engine/walk-files.d.mts +15 -0
  237. package/types/engine/wikilink-syntax.d.mts +18 -19
  238. package/types/engine/yaml-lint.d.mts +5 -4
  239. package/types/hm3/actors.d.mts +1 -2
  240. package/types/hm3/item-fields.d.mts +2 -4
  241. package/types/hm3/template-priority.d.mts +6 -7
  242. package/types/manifest.d.mts +3 -2
  243. package/types/release.d.mts +12 -7
  244. package/types/sohl/actors.d.mts +2 -1
  245. package/types/sohl/document-subtypes.d.mts +1 -2
  246. package/types/sohl/item-fields.d.mts +3 -7
  247. package/engine/retired-fields.mjs +0 -631
  248. package/types/engine/retired-fields.d.mts +0 -354
@@ -2,7 +2,7 @@
2
2
  * The file on disk an authored image pathname names, or `null`.
3
3
  *
4
4
  * Two of the four forms {@link module:engine/pathnames.resolvePathname}
5
- * derives, used together: `local` is the file in this repository's own tree,
5
+ * derives, taken together: `local` is the file in this repository's own tree,
6
6
  * and `pdf` is where the book stages a copy of it.
7
7
  *
8
8
  * **Typst decides where that copy goes.** It resolves a path against its root —
@@ -57,45 +57,123 @@ export function stagedImagePath(src: string, config: object, opts?: {
57
57
  * @param {object} config - The resolved configuration.
58
58
  * @param {string} outDir - Where the book is written.
59
59
  * @param {object[]} findings - Collected here rather than thrown.
60
+ * @param {string} treeFile - The file the book's tree was read from — its
61
+ * `document`, or the configuration for a tree written inline — which is
62
+ * where a banner is declared.
63
+ * @param {string} [treeText] - That file's source, so a finding is located at
64
+ * the `banner:` key that declared the value.
60
65
  * @returns {Map<string, string>} Declared path → the staged file's path,
61
66
  * relative to the `.typ`.
62
67
  */
63
- export function stageBanners(entries: object[], config: object, outDir: string, findings?: object[]): Map<string, string>;
68
+ export function stageBanners(entries: object[], config: object, outDir: string, findings: object[], treeFile: string, treeText?: string): Map<string, string>;
64
69
  /**
65
70
  * The file name a downloaded book identifies itself by.
66
71
  *
67
72
  * The zip and the manifest take their names from the manifest, so an asset's
68
73
  * name and its advertised URL cannot disagree. A PDF has no advertised URL, so
69
- * its name is a free choice — which is exactly why it is fixed here rather than
70
- * left for each of six consumers to invent.
74
+ * its name is the book's own file stem — its `out`, default
75
+ * `<contentPackage>-<id>` — with the version appended.
71
76
  *
72
- * @param {string} artifact - The package's artifact name.
77
+ * @param {string} stem - The book's file stem.
73
78
  * @param {string} version - The version being released.
74
- * @returns {string} `<artifact>-<version>.pdf`, or `<artifact>.pdf` unversioned.
79
+ * @returns {string} `<stem>-<version>.pdf`, or `<stem>.pdf` unversioned.
75
80
  */
76
- export function pdfFileName(artifact: string, version: string): string;
81
+ export function pdfFileName(stem: string, version: string): string;
77
82
  /**
78
- * Build the book.
83
+ * The release assets the declared books produce, in configuration order.
84
+ *
85
+ * Builds nothing and reads no note: each entry is the file {@link buildPdf}
86
+ * writes for that book at `version`, as a path relative to the working
87
+ * directory inside `out` (default the configured `pdf.out`, else
88
+ * `build/dist`). A package that publishes no book — no `pdf:` block, or a
89
+ * content tree holding only a homepage — lists none.
90
+ *
91
+ * @param {object} [opts] - Options.
92
+ * @param {object} [opts.config] - A resolved configuration; loaded when absent.
93
+ * @param {string} [opts.version] - The version stamped in each file name.
94
+ * @param {string} [opts.out] - Where the books are written, overriding `pdf.out`.
95
+ * @returns {Array<{id: string, title: string, file: string}>} One entry per book.
96
+ */
97
+ export function listBookAssets({ config, version, out }?: {
98
+ config?: object | undefined;
99
+ version?: string | undefined;
100
+ out?: string | undefined;
101
+ }): Array<{
102
+ id: string;
103
+ title: string;
104
+ file: string;
105
+ }>;
106
+ /**
107
+ * Build the books the `pdf:` block declares — every one, or the one `book`
108
+ * names.
109
+ *
110
+ * Every declared book's filters run against one corpus and one database first,
111
+ * so coverage and each note's home book are computed over the whole set
112
+ * whichever books are requested. A book whose filters select no note is an
113
+ * error at its `id` and is not built; a section that selects none is left out.
114
+ *
115
+ * A finding about a configured key — an unknown `--book`, a `coverImage`,
116
+ * `document` or `front` file that does not exist — names the configuration
117
+ * file and, for a YAML one, the line and column of that key. A book with a
118
+ * missing file is not built; the others still are.
79
119
  *
80
120
  * @param {object} [opts] - Options.
81
121
  * @param {object} [opts.config] - A resolved configuration; loaded when absent.
122
+ * @param {string} [opts.configPath] - The file `config` was read from, for
123
+ * findings at a configured key. Defaults to the file the loader read, or to
124
+ * the configuration file in `config.rootDir`.
125
+ * @param {string} [opts.book] - The `id` of the one book to build; every book
126
+ * when absent.
82
127
  * @param {string} [opts.out] - Where to write, overriding `pdf.out`.
83
128
  * @param {string} [opts.version] - Stamped on the title page and the file name.
84
129
  * @param {boolean} [opts.compile] - Whether to run Typst. False leaves the
85
- * `.typ` source, which is what the unit tests read.
130
+ * `.typ` source, which is what the unit tests read, and runs a single pass:
131
+ * a reference to a note another book is home to prints `p. ?`. True renders
132
+ * every book, measures the page each entry starts on, writes `pages.json`
133
+ * into the output directory, and renders again with those pages — see
134
+ * {@link module:engine/pdf-pages}.
135
+ * @param {Function} [opts.readPages] - {@link
136
+ * module:engine/pdf-pages.readEntryPages}, for tests.
86
137
  * @param {Function} [opts.resolveReplacement] - {@link
87
138
  * module:engine/asset-replacement.resolveAssetReplacement}, for tests.
88
139
  * Loaded from that module otherwise, and only when a relationship declares
89
140
  * `assetReplacement: true` — a build with none never imports it.
90
- * @returns {Promise<object>} `{ built, reason, findings, typ, pdf, stats }`.
141
+ * @returns {Promise<{built: boolean, reason: string|null, findings: object[],
142
+ * imageReports: object[], books: Array<{id: string, title: string,
143
+ * built: boolean, typ: string, pdf: string|null, stats: object}>,
144
+ * homes: Map<string, string>, summary: object|null}>}
145
+ * `built` is true when every requested book was written (and compiled, when
146
+ * asked); `books` holds each book whose source was written. `homes` maps a
147
+ * selected note's canonical address to the id of the first book that selects
148
+ * it, and `summary` is {@link module:engine/pdf-coverage.coverage}'s, over
149
+ * every declared book whichever was requested; both are empty when a book's
150
+ * selection could not be read.
91
151
  */
92
- export function buildPdf({ config, out, version, compile, resolveReplacement, }?: {
152
+ export function buildPdf({ config, configPath, book, out, version, compile, readPages, resolveReplacement, }?: {
93
153
  config?: object | undefined;
154
+ configPath?: string | undefined;
155
+ book?: string | undefined;
94
156
  out?: string | undefined;
95
157
  version?: string | undefined;
96
158
  compile?: boolean | undefined;
159
+ readPages?: Function | undefined;
97
160
  resolveReplacement?: Function | undefined;
98
- }): Promise<object>;
161
+ }): Promise<{
162
+ built: boolean;
163
+ reason: string | null;
164
+ findings: object[];
165
+ imageReports: object[];
166
+ books: Array<{
167
+ id: string;
168
+ title: string;
169
+ built: boolean;
170
+ typ: string;
171
+ pdf: string | null;
172
+ stats: object;
173
+ }>;
174
+ homes: Map<string, string>;
175
+ summary: object | null;
176
+ }>;
99
177
  /**
100
178
  * The command line the compile runs, as data.
101
179
  *
@@ -109,6 +187,14 @@ export function buildPdf({ config, out, version, compile, resolveReplacement, }?
109
187
  * @returns {string[]} The arguments, in order.
110
188
  */
111
189
  export function typstArgs(typPath: string, pdfPath: string, pdf?: object): string[];
190
+ /**
191
+ * The flags that decide which faces a layout uses, shared by the compile and by
192
+ * the page measurement so both lay the book out in the same metrics.
193
+ *
194
+ * @param {object} [pdf] - The resolved `pdf:` block.
195
+ * @returns {string[]} The arguments, in order.
196
+ */
197
+ export function typstFontArgs(pdf?: object): string[];
112
198
  /**
113
199
  * The compiler's own warnings, as findings.
114
200
  *
@@ -0,0 +1,60 @@
1
+ /**
2
+ * Whether a record is a note a book can print.
3
+ *
4
+ * @param {object} record - An index record.
5
+ * @returns {boolean} False for an asset, a documentation row, a stub and a `gm` note.
6
+ */
7
+ export function isPrintable(record: object): boolean;
8
+ /**
9
+ * The key a note is counted by: its canonical address as text. An index record
10
+ * holds the address as a tuple and a row of the notes database as a string;
11
+ * both give the same key.
12
+ *
13
+ * @param {object} record - A printable record.
14
+ * @returns {string} The key.
15
+ */
16
+ export function recordKey(record: object): string;
17
+ /**
18
+ * Compute coverage, homes and the summary.
19
+ *
20
+ * @param {object} input - Options.
21
+ * @param {Array<{id: string, records: object[]}>} input.books - Each book's
22
+ * selected records, in configuration order; a record selected twice appears
23
+ * twice.
24
+ * @param {object[]} input.corpus - Every record in the index.
25
+ * @param {object[]} [input.unprinted] - The records `pdf.unprinted` selects.
26
+ * @param {"warning"|"error"|"off"} [input.mode] - `pdf.coverage`.
27
+ * @param {(record: object) => {file?: string, line?: number, column?: number}} [input.locate]
28
+ * Where a note's `type:` line sits; a field it cannot supply is dropped.
29
+ * @returns {{findings: object[], homes: Map<string, string>, summary: object}}
30
+ * The findings in corpus order, each note's home book id by address, and the
31
+ * summary: per-book counts, totals and every note several books select.
32
+ */
33
+ export function coverage({ books, corpus, unprinted, mode, locate }: {
34
+ books: Array<{
35
+ id: string;
36
+ records: object[];
37
+ }>;
38
+ corpus: object[];
39
+ unprinted?: object[] | undefined;
40
+ mode?: "error" | "off" | "warning" | undefined;
41
+ locate?: ((record: object) => {
42
+ file?: string;
43
+ line?: number;
44
+ column?: number;
45
+ }) | undefined;
46
+ }): {
47
+ findings: object[];
48
+ homes: Map<string, string>;
49
+ summary: object;
50
+ };
51
+ /**
52
+ * The summary as the lines the command prints.
53
+ *
54
+ * @param {object} summary - From {@link coverage}.
55
+ * @returns {string[]} One line per book, one for the totals, one per note
56
+ * several books select.
57
+ */
58
+ export function summaryLines(summary: object): string[];
59
+ /** The message a note in no book carries. */
60
+ export const UNCOVERED_MESSAGE: "no book selects this note; add it to a book's query or to `pdf.unprinted`";
@@ -9,27 +9,56 @@ export function indexMarker(slug: string, main?: boolean): string;
9
9
  /**
10
10
  * Every term the book's index lists, in index order.
11
11
  *
12
+ * A book lists the notes it is home to. An entry `owns` rejects is a reprint of
13
+ * another book's note: it prints there, and its term belongs to its home
14
+ * book's index.
15
+ *
12
16
  * @param {object[]} entries - The plan's entries.
13
- * @returns {Array<{letter: string, name: string, slug: string, see?: string}>}
14
- * Terms with `see` set for a cross-reference to the main name it points at.
17
+ * @param {object} [opts] - Options.
18
+ * @param {(entry: object) => boolean} [opts.owns] - Whether this book is the
19
+ * entry's home. Every entry when absent.
20
+ * @param {Array<{slug: string, name: string, title: string, page: string|number}>} [opts.foreign] -
21
+ * Notes another book prints, listed as `Name: Book title, p. N` once this
22
+ * book mentions them.
23
+ * @returns {Array<{letter: string, name: string, slug: string, see?: string,
24
+ * foreign?: {title: string, page: string}}>} Terms with `see` set for a
25
+ * cross-reference to the main name it points at.
15
26
  */
16
- export function indexTerms(entries: object[]): Array<{
27
+ export function indexTerms(entries: object[], { owns, foreign }?: {
28
+ owns?: ((entry: object) => boolean) | undefined;
29
+ foreign?: {
30
+ slug: string;
31
+ name: string;
32
+ title: string;
33
+ page: string | number;
34
+ }[] | undefined;
35
+ }): Array<{
17
36
  letter: string;
18
37
  name: string;
19
38
  slug: string;
20
39
  see?: string;
40
+ foreign?: {
41
+ title: string;
42
+ page: string;
43
+ };
21
44
  }>;
22
45
  /**
23
46
  * The Typst function definitions the markers and the chapter rely on.
24
47
  *
48
+ * @param {object} [opts] - Options.
49
+ * @param {boolean} [opts.seeAlso] - Whether the chapter may list notes another
50
+ * book prints.
25
51
  * @returns {string} Typst source.
26
52
  */
27
- export function indexPreamble(): string;
53
+ export function indexPreamble({ seeAlso }?: {
54
+ seeAlso?: boolean | undefined;
55
+ }): string;
28
56
  /**
29
57
  * The index chapter, ready to follow the last entry.
30
58
  *
31
59
  * @param {object[]} entries - The plan's entries.
32
60
  * @param {string} title - The book's title, which kicks the chapter.
61
+ * @param {object} [opts] - As {@link indexTerms}.
33
62
  * @returns {string} Typst source, or "" when the book has nothing to index.
34
63
  */
35
- export function indexChapter(entries: object[], title: string): string;
64
+ export function indexChapter(entries: object[], title: string, opts?: object): string;
@@ -0,0 +1,143 @@
1
+ /**
2
+ * The arguments that measure a book, as data.
3
+ *
4
+ * Fonts are the compile's, because a face with other metrics lays out other
5
+ * pages.
6
+ *
7
+ * @param {string} typPath - The `.typ` file.
8
+ * @param {string[]} fontArgs - `--ignore-system-fonts --font-path …`, as the
9
+ * compile passes them.
10
+ * @returns {string[]} The arguments, in order.
11
+ */
12
+ export function evalArgs(typPath: string, fontArgs: string[]): string[];
13
+ /**
14
+ * Measure the page each labelled entry of a book starts on.
15
+ *
16
+ * @param {string} typPath - The `.typ` file.
17
+ * @param {string[]} fontArgs - The compile's font arguments.
18
+ * @param {string} [binary] - The Typst binary.
19
+ * @returns {{ok: boolean, message: string, pages: Map<string, number>}} The
20
+ * printed page of every labelled heading, keyed by label.
21
+ */
22
+ export function readEntryPages(typPath: string, fontArgs: string[], binary?: string): {
23
+ ok: boolean;
24
+ message: string;
25
+ pages: Map<string, number>;
26
+ };
27
+ /**
28
+ * Every note that has a home book, with the anchor its entry carries there.
29
+ *
30
+ * **A note a book prints twice has one page**: the first entry, which is the
31
+ * one the book's links reach. A note a book reprints from another book's home
32
+ * keeps its home book's page here, because the home is the first book in
33
+ * configuration order that selects it.
34
+ *
35
+ * @param {object} input - Options.
36
+ * @param {Array<{id: string, title: string, plan: {entries: object[]}}>} input.books -
37
+ * The books that render, in configuration order.
38
+ * @param {Map<string, string>} input.homes - Each note's key → its home book's
39
+ * id, from {@link module:engine/pdf-coverage.coverage}.
40
+ * @returns {Array<{key: string, book: string, title: string, anchor: string,
41
+ * record: object, slug: string, aliases: string[]}>} One per homed note, in book order and
42
+ * then entry order.
43
+ */
44
+ export function homeEntries({ books, homes }: {
45
+ books: Array<{
46
+ id: string;
47
+ title: string;
48
+ plan: {
49
+ entries: object[];
50
+ };
51
+ }>;
52
+ homes: Map<string, string>;
53
+ }): Array<{
54
+ key: string;
55
+ book: string;
56
+ title: string;
57
+ anchor: string;
58
+ record: object;
59
+ slug: string;
60
+ aliases: string[];
61
+ }>;
62
+ /**
63
+ * The table written as `pages.json`.
64
+ *
65
+ * @param {ReturnType<typeof homeEntries>} entries - From {@link homeEntries}.
66
+ * @param {Map<string, Map<string, number>>} measured - Book id → label → page.
67
+ * @returns {Record<string, {book: string, page: number}>} Keyed by the note's
68
+ * key, in sorted key order. A note whose entry was not measured is absent.
69
+ */
70
+ export function pageTable(entries: ReturnType<typeof homeEntries>, measured: Map<string, Map<string, number>>): Record<string, {
71
+ book: string;
72
+ page: number;
73
+ }>;
74
+ /**
75
+ * The notes whose page differs between two tables.
76
+ *
77
+ * @param {Record<string, {book: string, page: number}>} before - A table.
78
+ * @param {Record<string, {book: string, page: number}>} after - A later one.
79
+ * @returns {Array<{key: string, book: string, before: number|undefined,
80
+ * after: number|undefined}>} In sorted key order.
81
+ */
82
+ export function movedPages(before: Record<string, {
83
+ book: string;
84
+ page: number;
85
+ }>, after: Record<string, {
86
+ book: string;
87
+ page: number;
88
+ }>): Array<{
89
+ key: string;
90
+ book: string;
91
+ before: number | undefined;
92
+ after: number | undefined;
93
+ }>;
94
+ /**
95
+ * The lookup one book renders its cross-book references from.
96
+ *
97
+ * A note the book prints (a reprint included) is never in it: its link stays an
98
+ * internal destination. Keys are the three forms the book's own link map
99
+ * answers to — slug, canonical address and documentation address.
100
+ *
101
+ * `get` counts the references it answers, which is how the build knows whether
102
+ * a measured pass can change anything.
103
+ *
104
+ * @param {object} input - Options.
105
+ * @param {ReturnType<typeof homeEntries>} input.entries - From {@link homeEntries}.
106
+ * @param {string} input.book - The id of the book that renders.
107
+ * @param {Map<string, string>} input.printed - The book's own link map.
108
+ * @param {Record<string, {book: string, page: number}>} [input.table] - The
109
+ * measured pages; every page is {@link UNMEASURED_PAGE} when absent.
110
+ * @param {boolean} [input.seeAlso] - Whether the book's index lists the notes
111
+ * it mentions here (`pdf.indexSeeAlso`). `listed` is empty when not.
112
+ * @returns {{used: number, seeAlso: boolean,
113
+ * listed: Array<{slug: string, name: string, title: string, page: string|number}>,
114
+ * get: (key: string) => ({slug: string, title: string, page: string|number}|undefined)}}
115
+ * The lookup. `listed` is every note another book prints, for the index to
116
+ * show those this book mentions.
117
+ */
118
+ export function elsewhereFor({ entries, book, printed, table, seeAlso }: {
119
+ entries: ReturnType<typeof homeEntries>;
120
+ book: string;
121
+ printed: Map<string, string>;
122
+ table?: Record<string, {
123
+ book: string;
124
+ page: number;
125
+ }> | undefined;
126
+ seeAlso?: boolean | undefined;
127
+ }): {
128
+ used: number;
129
+ seeAlso: boolean;
130
+ listed: Array<{
131
+ slug: string;
132
+ name: string;
133
+ title: string;
134
+ page: string | number;
135
+ }>;
136
+ get: (key: string) => ({
137
+ slug: string;
138
+ title: string;
139
+ page: string | number;
140
+ } | undefined);
141
+ };
142
+ /** What a reference prints for a page that has not been measured. */
143
+ export const UNMEASURED_PAGE: "?";
@@ -99,6 +99,9 @@ export function stripHtmlComments(markdown: string): string;
99
99
  * at. Every link the book sets as a URL is resolved against it, because a
100
100
  * reader holding a PDF has no page to resolve a path against — see
101
101
  * {@link renderLink}.
102
+ * @param {{get: (key: string) => ({title: string, page: string|number}|undefined)}} [opts.elsewhere] -
103
+ * The notes another book is home to, by the slug and addresses a link reaches
104
+ * them through — see {@link crossBookReference}.
102
105
  * @param {boolean} [opts.generated] - Whether the Markdown is a note's
103
106
  * generated sections rather than its author's text — see
104
107
  * {@link module:engine/generated-sections}. Their links follow the rules
@@ -129,6 +132,12 @@ export function markdownToTypst(markdown: string, opts?: {
129
132
  }[] | undefined;
130
133
  prepared?: boolean | undefined;
131
134
  url?: string | undefined;
135
+ elsewhere?: {
136
+ get: (key: string) => ({
137
+ title: string;
138
+ page: string | number;
139
+ } | undefined);
140
+ } | undefined;
132
141
  generated?: boolean | undefined;
133
142
  }): string;
134
143
  /**
@@ -155,6 +164,22 @@ export function markdownToTypst(markdown: string, opts?: {
155
164
  * site to resolve it against or it is already absolute.
156
165
  */
157
166
  export function absolutePageUrl(url: string, site?: string): string;
167
+ /**
168
+ * A reference to a note another book prints, as Typst: the words the link
169
+ * carried, then the book and the page, `Name (_Book title_, p. 12)`.
170
+ *
171
+ * The space before the page number does not break, so a number never starts a
172
+ * line of its own.
173
+ *
174
+ * @param {string} inner - The already-rendered link text.
175
+ * @param {{title: string, page: string|number}} away - The home book's title
176
+ * and the page the entry starts on.
177
+ * @returns {string} Typst markup.
178
+ */
179
+ export function crossBookReference(inner: string, away: {
180
+ title: string;
181
+ page: string | number;
182
+ }): string;
158
183
  /**
159
184
  * The Typst definitions the book's page furniture is drawn with.
160
185
  *
@@ -298,9 +323,20 @@ export function bookTypstPreamble(): string;
298
323
  * plate draws without a picture.
299
324
  * @param {Map<string, {path: string, title: string}>} [opts.maps] - An entry
300
325
  * anchor to its SVG path and page heading. Each map takes a page after its entry.
326
+ * @param {string} [opts.coverImage] - The staged cover picture, relative to the
327
+ * `.typ`. When given the book opens on a full-page cover, page 1, with the
328
+ * title and subtitle over it; without one the book opens on the title page.
329
+ * @param {Array<{name: string, role?: string}>} [opts.authors] - Printed on the
330
+ * title page one per line, as `Name` or `Name, Role`.
331
+ * @param {string} [opts.copyright] - The title page foot's first line.
332
+ * @param {string} [opts.license] - The title page foot's remaining lines; a
333
+ * line break in it starts a new line.
334
+ * @param {object} [opts.index] - As {@link module:engine/pdf-index.indexTerms}:
335
+ * `owns` limits the index to the notes this book is home to, and `foreign`
336
+ * (present only under `pdf.indexSeeAlso`) lists notes another book prints.
301
337
  * @returns {string} A complete `.typ` document.
302
338
  */
303
- export function renderBook({ plan, bodies, title, subtitle, front, fonts, version, preamble, banners, maps, }?: {
339
+ export function renderBook({ plan, bodies, title, subtitle, front, fonts, version, preamble, banners, maps, coverImage, authors, copyright, license, index, }?: {
304
340
  plan: object;
305
341
  bodies: Map<string, string>;
306
342
  title: string;
@@ -314,6 +350,14 @@ export function renderBook({ plan, bodies, title, subtitle, front, fonts, versio
314
350
  path: string;
315
351
  title: string;
316
352
  }> | undefined;
353
+ coverImage?: string | undefined;
354
+ authors?: {
355
+ name: string;
356
+ role?: string;
357
+ }[] | undefined;
358
+ copyright?: string | undefined;
359
+ license?: string | undefined;
360
+ index?: object | undefined;
317
361
  }): string;
318
362
  /**
319
363
  * Point every internal link at a label the document actually declares.
@@ -14,11 +14,16 @@
14
14
  * @param {object} [opts] - Options.
15
15
  * @param {string} [opts.text] - The file's source, for finding positions. Every
16
16
  * finding without one is still reported, just without a locator.
17
+ * @param {Array<string|number>} [opts.at] - Where `raw` sits in `text`: `[]`
18
+ * for a tree file, `["pdf", "books", i]` for a tree written inline in the
19
+ * configuration. Every key path — and so every position, here and in
20
+ * {@link runTreeFilters} — starts with it.
17
21
  * @returns {{nodes: object[], findings: object[]}} The flattened tree and what
18
22
  * was wrong with it.
19
23
  */
20
- export function parseDocumentTree(raw: unknown, { text }?: {
24
+ export function parseDocumentTree(raw: unknown, { text, at }?: {
21
25
  text?: string | undefined;
26
+ at?: (string | number)[] | undefined;
22
27
  }): {
23
28
  nodes: object[];
24
29
  findings: object[];
@@ -33,14 +38,10 @@ export function parseDocumentTree(raw: unknown, { text }?: {
33
38
  * something that is not a note, and unable to pick up the `doc<type>`
34
39
  * documentation rows that ride the same index as the notes they document.
35
40
  *
36
- * **A filter that selects nothing is an error**, and the distinction that makes
37
- * that consistent is worth stating. A *note* no clause selects is expected: the
38
- * book is a selection and a project decides what its own volume carries. A
39
- * *clause* that selects no note is not the same thing — a filter is a deliberate
40
- * act, so one matching nothing is either wrong or left over from a structure
41
- * that has moved on, and in both cases the tree should not carry it. Reported
42
- * with the section's name and the filter's position, so the choice between
43
- * fixing it and deleting it is the author's.
41
+ * **A filter that selects nothing is not a finding.** A section whose clauses
42
+ * select nothing is left out of the plan, and a book whose clauses select
43
+ * nothing at all is refused by the build. Notes that a mistyped clause fails to
44
+ * take are reported by the coverage check, which names each one.
44
45
  *
45
46
  * @param {object[]} nodes - From {@link parseDocumentTree}.
46
47
  * @param {{query: (sql: string) => Promise<{rows: object[]}>}} db - An open
@@ -78,11 +79,8 @@ export function runTreeFilters(nodes: object[], db: {
78
79
  *
79
80
  * **A section with nothing in it does not print**, and emptiness is judged after
80
81
  * its descendants are: a section holding only sections that all resolved to
81
- * nothing is itself empty. In a correct tree this never fires — a filter that
82
- * selects nothing is reported by {@link runTreeFilters} as the error it is — so
83
- * this is the graceful half of that failure rather than a feature: a build whose
84
- * filters are broken still produces a readable document to look at while they
85
- * are fixed. A section holding only prose is not empty; it is a preface.
82
+ * nothing is itself empty. The omission is silent. A section holding only prose
83
+ * is not empty; it is a preface.
86
84
  *
87
85
  * @param {object[]} nodes - From {@link parseDocumentTree}.
88
86
  * @param {object} [opts] - Options.
@@ -64,7 +64,7 @@ export function sharedPrettierDivergence(resolved: object | null | undefined, fi
64
64
  * invokes it.
65
65
  *
66
66
  * **Neither of these is an override.** A consumer that declares its own
67
- * Prettier config or its own `.markdownlint-cli2.jsonc` wins; what ships here
67
+ * Prettier config or its own `.markdownlint-cli2.*` options file wins; what ships here
68
68
  * is the default a repository gets for declaring nothing. Repository *layout*
69
69
  * knowledge — which paths to skip — stays with the repository that has the
70
70
  * layout, in its own ignore files.
@@ -73,10 +73,10 @@ export function checkPrettierConventions(root: string, opts?: {
73
73
  /**
74
74
  * Lint a repository's markdown against the shared rule set.
75
75
  *
76
- * {@link MARKDOWNLINT_CONFIG} is passed as markdownlint's `optionsDefault`,
77
- * which is precisely the "shipped default, consumer overrides" behaviour the
78
- * command promises: a repository with no configuration of its own gets these
79
- * rules, and a `.markdownlint-cli2.jsonc` found in the tree overrides them.
76
+ * {@link MARKDOWNLINT_CONFIG} is the default rule set, which is precisely the
77
+ * "shipped default, consumer overrides" behaviour the command promises: a
78
+ * repository with no configuration of its own gets these rules, and a
79
+ * `.markdownlint-cli2.*` options file found in the tree overrides them.
80
80
  *
81
81
  * The override is **key by key, and each key wholesale** — which is not the same
82
82
  * as "replaces it", and the difference is the one worth stating. A consumer file
@@ -84,23 +84,24 @@ export function checkPrettierConventions(root: string, opts?: {
84
84
  * and every per-rule option; but its `ignores` *replaces*
85
85
  * {@link MARKDOWN_IGNORES} rather than extending it, so such a file must restate
86
86
  * every shared entry it still wants. Omitting `CHANGELOG.md` there silently
87
- * starts linting a generated file. `node_modules` stays excluded by the
88
- * command's glob even when a consumer replaces `ignores`.
87
+ * starts linting a generated file. `node_modules` stays excluded even when a
88
+ * consumer replaces `ignores`.
89
+ *
90
+ * A configuration file the contract refuses (an unknown option, a key that
91
+ * cannot mean anything where it is written, a syntax error) is returned as a
92
+ * finding located in that file, and nothing is linted.
89
93
  *
90
94
  * @param {string} root - Repository to lint.
91
95
  * @param {object} [opts]
92
96
  * @param {readonly string[]} [opts.paths] - Globs to lint instead of every
93
97
  * markdown file.
94
98
  * @param {boolean} [opts.fix=false] - Apply the fixes markdownlint can make.
95
- * @param {(params: object) => Promise<number>} [opts.run] - The
96
- * markdownlint-cli2 entry point, for tests.
97
99
  * @returns {Promise<{findings: object[], exitCode: number}>} The findings, and
98
- * markdownlint's own exit code.
100
+ * an exit code that is 1 when there are any.
99
101
  */
100
102
  export function lintMarkdown(root: string, opts?: {
101
103
  paths?: readonly string[] | undefined;
102
104
  fix?: boolean | undefined;
103
- run?: ((params: object) => Promise<number>) | undefined;
104
105
  }): Promise<{
105
106
  findings: object[];
106
107
  exitCode: number;
@@ -26,7 +26,7 @@ export function runtimeOnlyMessage(key: string, field: import("./field-spec.mjs"
26
26
  /**
27
27
  * The runtime-only fields a note actually writes, in declaration order.
28
28
  *
29
- * **Presence is the whole test**, as it is for every retired field: an authored
29
+ * **Presence is the whole test**: an authored
30
30
  * `onsetDate: null` is as much a claim about play state as a number is, and it
31
31
  * is exactly the belief the message exists to correct. So the question is
32
32
  * whether the path resolves to anything at all, never whether the value is a
@@ -34,7 +34,7 @@ export function runtimeOnlyMessage(key: string, field: import("./field-spec.mjs"
34
34
  *
35
35
  * Only `<block>.system.<to>` is searched, because it is the only position a
36
36
  * runtime-only field is reachable at. Such a declaration carries no `name`, so
37
- * it has neither a legacy in-block key nor a shared top-level source — and a
37
+ * it has neither an in-block key nor a shared top-level source — and a
38
38
  * bare `<block>.onsetDate` is an unrecognized block key, which
39
39
  * {@link module:engine/system-block.unknownBlockKeys} already reports as an
40
40
  * error naming the note and the line.