@heroiclands/package-build 23.12.8 → 24.0.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 (258) hide show
  1. package/CHANGELOG.md +84 -0
  2. package/README.md +3 -2
  3. package/assets/preview/gitea.ini +12 -7
  4. package/assets/styles/content-base.css +293 -24
  5. package/bin/content-commands.mjs +370 -213
  6. package/bin/package-build-preview.mjs +2 -2
  7. package/bin/package-build.mjs +3 -2
  8. package/ci/check-banners.mjs +7 -7
  9. package/config.mjs +21 -6
  10. package/content-config.mjs +199 -80
  11. package/docs/api.md +110 -89
  12. package/docs/authoring/links-and-markup.md +469 -97
  13. package/docs/commands.md +259 -78
  14. package/docs/configuration.md +211 -103
  15. package/docs/content-format.md +4 -4
  16. package/docs/diagnostics.md +44 -2
  17. package/docs/getting-started.md +1 -1
  18. package/docs/guides/icons.md +1 -1
  19. package/docs/guides/module-project.md +5 -8
  20. package/docs/guides/release-and-deploy.md +2 -2
  21. package/docs/guides/site.md +112 -23
  22. package/docs/guides/system-project.md +1 -1
  23. package/docs/guides/writing-in-invented-scripts.md +1247 -0
  24. package/docs/index.md +1 -0
  25. package/docs/project-setup.md +28 -30
  26. package/docs/reference/format-details.md +677 -319
  27. package/docs/reference/note-types.md +47 -10
  28. package/docs/reference/theme-pages.md +178 -108
  29. package/docs/source-preview.md +16 -2
  30. package/engine/anchors.mjs +2 -2
  31. package/engine/asset-shipping.mjs +22 -10
  32. package/engine/base-compiler.mjs +62 -104
  33. package/engine/changelog-lint.mjs +1 -1
  34. package/engine/code-fences.mjs +3 -3
  35. package/engine/content-address.mjs +1 -1
  36. package/engine/content-alerts.mjs +8 -4
  37. package/engine/content-blocks.mjs +29 -14
  38. package/engine/content-embeds.mjs +1 -1
  39. package/engine/content-expand.mjs +376 -0
  40. package/engine/content-figures.mjs +18 -11
  41. package/engine/content-format.yaml +8 -0
  42. package/engine/content-glyphs.mjs +213 -0
  43. package/engine/content-images.mjs +6 -7
  44. package/engine/content-index.mjs +1 -1
  45. package/engine/content-links.mjs +3 -3
  46. package/engine/content-lint.mjs +1 -1
  47. package/engine/content-render.mjs +453 -0
  48. package/engine/content-scripts.mjs +376 -43
  49. package/engine/content-spans.mjs +6 -3
  50. package/engine/content-styles.mjs +91 -0
  51. package/engine/content-symbols.mjs +186 -0
  52. package/engine/content-verse.mjs +738 -0
  53. package/engine/core-report.mjs +169 -0
  54. package/engine/core-vocabulary.mjs +208 -0
  55. package/engine/dictionary-html.mjs +119 -0
  56. package/engine/dictionary-rows.mjs +15 -4
  57. package/engine/field-reference.mjs +5 -5
  58. package/engine/first-use.mjs +17 -27
  59. package/engine/frontmatter-lint.mjs +1 -1
  60. package/engine/heading-attributes.mjs +89 -0
  61. package/engine/helpers.mjs +61 -101
  62. package/engine/homepage.mjs +17 -29
  63. package/engine/html-minify.mjs +70 -0
  64. package/engine/index.mjs +8 -5
  65. package/engine/key-case-collisions.mjs +136 -0
  66. package/engine/language-data.mjs +203 -6
  67. package/engine/language-rows.mjs +8 -2
  68. package/engine/language-terms.mjs +186 -16
  69. package/engine/language-words.mjs +12 -0
  70. package/engine/logographic.mjs +255 -0
  71. package/engine/map-lexicon.mjs +262 -47
  72. package/engine/map-places.mjs +5 -4
  73. package/engine/markdown-expressions.mjs +0 -2
  74. package/engine/markdownlint-run.mjs +9 -3
  75. package/engine/markup-validation.mjs +21 -0
  76. package/engine/metre-declaration.mjs +134 -0
  77. package/engine/names.mjs +36 -9
  78. package/engine/note-matter.mjs +66 -0
  79. package/engine/note-vocabulary.mjs +187 -3
  80. package/engine/pdf-build.mjs +69 -133
  81. package/engine/pdf-render.mjs +205 -58
  82. package/engine/post-fields.mjs +157 -0
  83. package/engine/preview-index.mjs +130 -0
  84. package/engine/preview-source.mjs +450 -0
  85. package/engine/project-init.mjs +5 -12
  86. package/engine/prose-config.mjs +3 -3
  87. package/engine/read-stdin.mjs +29 -0
  88. package/engine/run-rows.mjs +2 -2
  89. package/engine/run-words.mjs +6 -112
  90. package/engine/script-emphasis-terms.mjs +38 -0
  91. package/engine/script-emphasis.mjs +295 -0
  92. package/engine/script-font-delivery.mjs +4 -4
  93. package/engine/site-build.mjs +260 -315
  94. package/engine/site-config.mjs +26 -238
  95. package/engine/site-files.mjs +175 -0
  96. package/engine/site-index.mjs +9 -4
  97. package/engine/site-preview.mjs +52 -525
  98. package/engine/site-render.mjs +593 -0
  99. package/engine/site-root.mjs +22 -13
  100. package/engine/site-search.mjs +161 -0
  101. package/engine/site-serve.mjs +273 -0
  102. package/engine/site-theme-banner.mjs +154 -0
  103. package/engine/site-theme-urls.mjs +125 -0
  104. package/engine/site-theme.mjs +926 -0
  105. package/engine/sql-tables.mjs +20 -3
  106. package/engine/text-maps.mjs +57 -10
  107. package/engine/typographer.mjs +248 -0
  108. package/engine/verse-metre.mjs +357 -0
  109. package/engine/wikilink-syntax.mjs +29 -47
  110. package/engine/wikilinks.mjs +758 -279
  111. package/engine/word-forms.mjs +364 -0
  112. package/engine/word-rows.mjs +12 -2
  113. package/engine/word-terms.mjs +38 -4
  114. package/manifest.mjs +3 -4
  115. package/package.json +11 -8
  116. package/{hugo-theme → site-theme}/data/banners.yaml +6 -10
  117. package/site-theme/layouts/base.hbs +31 -0
  118. package/site-theme/layouts/blog-list.hbs +30 -0
  119. package/site-theme/layouts/blog-post.hbs +40 -0
  120. package/site-theme/layouts/home.hbs +35 -0
  121. package/site-theme/layouts/list.hbs +20 -0
  122. package/site-theme/layouts/not-found.hbs +31 -0
  123. package/site-theme/layouts/page.hbs +7 -0
  124. package/site-theme/partials/copy-buttons.hbs +37 -0
  125. package/site-theme/partials/draft-notice.hbs +5 -0
  126. package/site-theme/partials/exturl.hbs +2 -0
  127. package/{hugo-theme/layouts/partials/footer.html → site-theme/partials/footer.hbs} +10 -7
  128. package/site-theme/partials/header.hbs +44 -0
  129. package/site-theme/partials/hero-banner.hbs +11 -0
  130. package/site-theme/partials/infobox-entry.hbs +2 -0
  131. package/site-theme/partials/infobox-section.hbs +31 -0
  132. package/site-theme/partials/infobox-value.hbs +2 -0
  133. package/site-theme/partials/infobox.hbs +18 -0
  134. package/site-theme/partials/list-members.hbs +9 -0
  135. package/site-theme/partials/nav-links.hbs +5 -0
  136. package/site-theme/partials/page-body.hbs +48 -0
  137. package/site-theme/partials/page-list.hbs +14 -0
  138. package/site-theme/partials/related.hbs +24 -0
  139. package/site-theme/partials/search-meta.hbs +6 -0
  140. package/site-theme/partials/search-names.hbs +2 -0
  141. package/site-theme/partials/search.hbs +52 -0
  142. package/site-theme/partials/tags.hbs +4 -0
  143. package/site-theme/partials/toc.hbs +7 -0
  144. package/{hugo-theme → site-theme}/static/css/style.css +106 -14
  145. package/stage.mjs +1 -2
  146. package/types/config.d.mts +8 -4
  147. package/types/content-config.d.mts +15 -15
  148. package/types/engine/asset-shipping.d.mts +5 -2
  149. package/types/engine/base-compiler.d.mts +20 -2
  150. package/types/engine/code-fences.d.mts +3 -3
  151. package/types/engine/content-address.d.mts +1 -1
  152. package/types/engine/content-alerts.d.mts +8 -2
  153. package/types/engine/content-blocks.d.mts +11 -4
  154. package/types/engine/content-expand.d.mts +288 -0
  155. package/types/engine/content-figures.d.mts +11 -3
  156. package/types/engine/content-glyphs.d.mts +85 -0
  157. package/types/engine/content-images.d.mts +6 -7
  158. package/types/engine/content-links.d.mts +1 -1
  159. package/types/engine/content-render.d.mts +199 -0
  160. package/types/engine/content-scripts.d.mts +70 -4
  161. package/types/engine/content-spans.d.mts +1 -1
  162. package/types/engine/content-styles.d.mts +33 -0
  163. package/types/engine/content-symbols.d.mts +35 -0
  164. package/types/engine/content-verse.d.mts +240 -0
  165. package/types/engine/core-report.d.mts +45 -0
  166. package/types/engine/core-vocabulary.d.mts +53 -0
  167. package/types/engine/dictionary-html.d.mts +8 -0
  168. package/types/engine/dictionary-rows.d.mts +10 -0
  169. package/types/engine/field-reference.d.mts +2 -2
  170. package/types/engine/heading-attributes.d.mts +20 -0
  171. package/types/engine/helpers.d.mts +3 -10
  172. package/types/engine/homepage.d.mts +11 -24
  173. package/types/engine/html-minify.d.mts +9 -0
  174. package/types/engine/index.d.mts +2 -1
  175. package/types/engine/key-case-collisions.d.mts +28 -0
  176. package/types/engine/language-data.d.mts +0 -2
  177. package/types/engine/language-terms.d.mts +24 -2
  178. package/types/engine/logographic.d.mts +45 -0
  179. package/types/engine/map-lexicon.d.mts +66 -13
  180. package/types/engine/map-places.d.mts +8 -0
  181. package/types/engine/markdown-expressions.d.mts +0 -2
  182. package/types/engine/metre-declaration.d.mts +39 -0
  183. package/types/engine/names.d.mts +34 -3
  184. package/types/engine/note-matter.d.mts +48 -0
  185. package/types/engine/pdf-render.d.mts +6 -2
  186. package/types/engine/post-fields.d.mts +44 -0
  187. package/types/engine/preview-index.d.mts +72 -0
  188. package/types/engine/preview-source.d.mts +51 -0
  189. package/types/engine/read-stdin.d.mts +11 -0
  190. package/types/engine/run-rows.d.mts +2 -2
  191. package/types/engine/run-words.d.mts +0 -15
  192. package/types/engine/script-emphasis-terms.d.mts +21 -0
  193. package/types/engine/script-emphasis.d.mts +84 -0
  194. package/types/engine/script-font-delivery.d.mts +4 -4
  195. package/types/engine/site-build.d.mts +71 -113
  196. package/types/engine/site-config.d.mts +14 -134
  197. package/types/engine/site-files.d.mts +73 -0
  198. package/types/engine/site-index.d.mts +11 -3
  199. package/types/engine/site-preview.d.mts +15 -19
  200. package/types/engine/site-render.d.mts +171 -0
  201. package/types/engine/site-root.d.mts +12 -4
  202. package/types/engine/site-search.d.mts +54 -0
  203. package/types/engine/site-serve.d.mts +44 -0
  204. package/types/engine/site-theme-banner.d.mts +51 -0
  205. package/types/engine/site-theme-urls.d.mts +60 -0
  206. package/types/engine/site-theme.d.mts +127 -0
  207. package/types/engine/sql-tables.d.mts +4 -1
  208. package/types/engine/text-maps.d.mts +17 -3
  209. package/types/engine/typographer.d.mts +11 -0
  210. package/types/engine/verse-metre.d.mts +131 -0
  211. package/types/engine/wikilink-syntax.d.mts +30 -48
  212. package/types/engine/wikilinks.d.mts +330 -14
  213. package/types/engine/word-forms.d.mts +83 -0
  214. package/types/manifest.d.mts +3 -4
  215. package/types/stage.d.mts +1 -2
  216. package/types/vendor/markdownlint.d.mts +8 -0
  217. package/vendor/markdownlint.mjs +20388 -0
  218. package/engine/content-poetry.mjs +0 -240
  219. package/engine/web-wikilinks.mjs +0 -594
  220. package/hugo-theme/layouts/404.html +0 -85
  221. package/hugo-theme/layouts/_default/baseof.html +0 -67
  222. package/hugo-theme/layouts/_default/list.html +0 -139
  223. package/hugo-theme/layouts/_default/single.html +0 -4
  224. package/hugo-theme/layouts/blog/list.html +0 -54
  225. package/hugo-theme/layouts/blog-post/single.html +0 -36
  226. package/hugo-theme/layouts/index.html +0 -75
  227. package/hugo-theme/layouts/partials/banner-url.html +0 -141
  228. package/hugo-theme/layouts/partials/cdn-url.html +0 -36
  229. package/hugo-theme/layouts/partials/dictionary-lists.html +0 -58
  230. package/hugo-theme/layouts/partials/draft-notice.html +0 -41
  231. package/hugo-theme/layouts/partials/draft-state.html +0 -26
  232. package/hugo-theme/layouts/partials/header.html +0 -35
  233. package/hugo-theme/layouts/partials/hero-banner.html +0 -85
  234. package/hugo-theme/layouts/partials/infobox/entry.html +0 -18
  235. package/hugo-theme/layouts/partials/infobox/items.html +0 -26
  236. package/hugo-theme/layouts/partials/infobox/section.html +0 -56
  237. package/hugo-theme/layouts/partials/infobox/value.html +0 -43
  238. package/hugo-theme/layouts/partials/infobox.html +0 -57
  239. package/hugo-theme/layouts/partials/page-body.html +0 -152
  240. package/hugo-theme/layouts/partials/related.html +0 -138
  241. package/hugo-theme/layouts/partials/search.html +0 -84
  242. package/hugo-theme/layouts/partials/site-url.html +0 -32
  243. package/hugo-theme/layouts/partials/tags.html +0 -30
  244. package/hugo-theme/layouts/partials/toc.html +0 -28
  245. package/hugo-theme/layouts/partials/warn-missing-banner.html +0 -3
  246. package/hugo-theme/layouts/shortcodes/cartouche.html +0 -64
  247. package/hugo-theme/layouts/shortcodes/exturl.html +0 -12
  248. package/hugo-theme/layouts/shortcodes/glyph.html +0 -43
  249. package/hugo-theme/layouts/shortcodes/pagelist.html +0 -63
  250. package/hugo-theme/theme.toml +0 -11
  251. package/types/engine/content-poetry.d.mts +0 -58
  252. package/types/engine/web-wikilinks.d.mts +0 -107
  253. /package/{hugo-theme → site-theme}/data/banner-kinds.yaml +0 -0
  254. /package/{hugo-theme → site-theme}/static/css/sohl-icons.css +0 -0
  255. /package/{hugo-theme → site-theme}/static/fonts/game-icons-kb.woff2 +0 -0
  256. /package/{hugo-theme → site-theme}/static/img/banners/takheperu-hieroglyphs.svg +0 -0
  257. /package/{hugo-theme → site-theme}/static/img/cartouches/takheperu.svg +0 -0
  258. /package/{hugo-theme → site-theme}/static/js/inline-spoilers.js +0 -0
@@ -26,11 +26,12 @@
26
26
  * has found a bug in one of them, so the book does not get its own walk, its
27
27
  * own frontmatter reader or its own link resolver. It collects pages with
28
28
  * {@link module:engine/site-build.collectContentPages}, indexes them with
29
- * `siteGates`, expands tables with `expandContentTables` and resolves links
30
- * with `resolveWebWikilinks` — the same four calls, in the same order, that
31
- * `renderPages` makes. What differs is only the last step and the *selection*:
32
- * the site publishes every page, and the book publishes what its document tree
33
- * asked for.
29
+ * `siteGates`, and expands each body with
30
+ * {@link module:engine/content-expand.expandContent} for the `book` target —
31
+ * the same calls the site makes. What differs is how a link is written, the
32
+ * figure numbering, which runs across the whole book, the last step and the
33
+ * *selection*: the site publishes every page, and the book publishes what its
34
+ * document tree asked for.
34
35
  *
35
36
  * The book builds when the authored tree contains content pages. The same
36
37
  * {@link module:content-config.publishesContentPages} decision governs the
@@ -97,20 +98,9 @@ import {
97
98
  } from "./pdf-pages.mjs";
98
99
  import { cachedDependencies, openNotesDatabase, prepareTreeSqlTables } from "./sql-tables.mjs";
99
100
  import { parseDocumentTree, runTreeFilters, planDocument } from "./pdf-toc.mjs";
100
- import {
101
- collectContentPages,
102
- siteGates,
103
- tableUniverse,
104
- gatesFailed,
105
- resolveSitePass,
106
- } from "./site-build.mjs";
107
- import {
108
- figureIndexKeys,
109
- resolveCrossNoteFigures,
110
- resolveInfoboxRef,
111
- wikiContext,
112
- } from "./site-index.mjs";
113
- import { resolveWebWikilinks } from "./web-wikilinks.mjs";
101
+ import { collectContentPages, siteGates, gatesFailed, resolveSitePass } from "./site-build.mjs";
102
+ import { figureIndexKeys, resolveInfoboxRef } from "./site-index.mjs";
103
+ import { expandContent, expandTables } from "./content-expand.mjs";
114
104
  import { linkFindingMessage } from "./wikilink-syntax.mjs";
115
105
  import {
116
106
  positionOfFrontmatterPath,
@@ -120,14 +110,11 @@ import {
120
110
  } from "./diagnostics.mjs";
121
111
  import { artPathname, assetAddressIndex, pathnameRoles } from "./art-fields.mjs";
122
112
  import { embedRole } from "./content-embeds.mjs";
123
- import { expandContentTables } from "./content-tables.mjs";
124
113
  import { seeAlsoEntries, seeAlsoMarkdown, seeAlsoWebLink } from "./see-also.mjs";
125
- import { renderMarkdownExpressions } from "./markdown-expressions.mjs";
126
114
  import { eventNoteIndex } from "./event-fields.mjs";
127
115
  import { numberFigures, scanFigures } from "./content-figures.mjs";
128
- import { collectAnchors } from "./anchors.mjs";
129
- import { protectCode } from "./code-fences.mjs";
130
116
  import { imagesIn, parseImageDirective } from "./content-images.mjs";
117
+ import { symbolImagesIn } from "./content-symbols.mjs";
131
118
  import {
132
119
  bookImageWidthIn,
133
120
  PDF_IMAGE_INCHES,
@@ -159,7 +146,7 @@ import { infoboxTypstPreamble, infoboxesToTypst, linkToTypst } from "./infobox-r
159
146
  import { noteInfoboxes } from "./infobox-registry.mjs";
160
147
  import { resolveIconGlyphs } from "./pdf-fonts.mjs";
161
148
  import { drawMapsFrom } from "./derived-sections.mjs";
162
- import { generatedDrawing, markGenerated, splitGenerated } from "./generated-sections.mjs";
149
+ import { generatedDrawing } from "./generated-sections.mjs";
163
150
  import { resolveAssetReplacement } from "./asset-replacement.mjs";
164
151
  import { typstScriptFontPath } from "./script-font-delivery.mjs";
165
152
 
@@ -980,6 +967,7 @@ async function prepareWorld(resolved, { contentBase, findings, resolveReplacemen
980
967
  dir,
981
968
  dependencies: await cachedDependencies(resolved),
982
969
  audience: "public",
970
+ contentBase,
983
971
  });
984
972
 
985
973
  // Pages, indexed and gated exactly as the site does. A book of a tree whose
@@ -1040,7 +1028,6 @@ async function prepareWorld(resolved, { contentBase, findings, resolveReplacemen
1040
1028
  }
1041
1029
 
1042
1030
  const byFile = new Map(collected.pages.map((page) => [page.file, page]));
1043
- const universe = tableUniverse(collected.pages);
1044
1031
  // The address space an `![[…]]` embed resolves against — the same one the
1045
1032
  // site builds, so one authored picture reaches both surfaces or neither.
1046
1033
  const assets = assetAddressIndex(records, {
@@ -1069,7 +1056,6 @@ async function prepareWorld(resolved, { contentBase, findings, resolveReplacemen
1069
1056
  site,
1070
1057
  gates,
1071
1058
  byFile,
1072
- universe,
1073
1059
  sqlTables,
1074
1060
  assets,
1075
1061
  md,
@@ -1195,8 +1181,7 @@ async function buildBook(book, shared) {
1195
1181
  const staged = stagedImagePath(src, resolved, world.stagingOpts);
1196
1182
  return staged && { ...staged, to: inBook(staged.to) };
1197
1183
  };
1198
- const { records, site, gates, byFile, universe, sqlTables, assets, md, glyphs, stagingOpts } =
1199
- world;
1184
+ const { records, site, gates, byFile, sqlTables, assets, md, glyphs, stagingOpts } = world;
1200
1185
 
1201
1186
  fs.mkdirSync(outDir, { recursive: true });
1202
1187
 
@@ -1243,7 +1228,14 @@ async function buildBook(book, shared) {
1243
1228
  }
1244
1229
  return positionOfLiteral(raw, src, occurrence);
1245
1230
  };
1246
- for (const image of imagesIn(body)) {
1231
+ // A glyph or a cartouche is a picture the package ships, staged like
1232
+ // any other and located in the note by its slug.
1233
+ const symbols = symbolImagesIn(body).map(({ src, locate }) => ({
1234
+ src,
1235
+ locate,
1236
+ directive: "",
1237
+ }));
1238
+ for (const image of [...imagesIn(body), ...symbols]) {
1247
1239
  const src = image.src;
1248
1240
  const occurrence = (seenHere.get(src) ?? 0) + 1;
1249
1241
  seenHere.set(src, occurrence);
@@ -1286,7 +1278,7 @@ async function buildBook(book, shared) {
1286
1278
  if (problem) {
1287
1279
  findings.push({
1288
1280
  file,
1289
- ...where(src, occurrence),
1281
+ ...where(image.locate ?? src, occurrence),
1290
1282
  severity: "error",
1291
1283
  message: problem,
1292
1284
  });
@@ -1298,7 +1290,7 @@ async function buildBook(book, shared) {
1298
1290
  } catch (err) {
1299
1291
  findings.push({
1300
1292
  file,
1301
- ...where(src, occurrence),
1293
+ ...where(image.locate ?? src, occurrence),
1302
1294
  severity: "error",
1303
1295
  message: err.message,
1304
1296
  });
@@ -1307,7 +1299,7 @@ async function buildBook(book, shared) {
1307
1299
  if (!staged) {
1308
1300
  findings.push({
1309
1301
  file,
1310
- ...where(src, occurrence),
1302
+ ...where(image.locate ?? src, occurrence),
1311
1303
  severity: "warning",
1312
1304
  message:
1313
1305
  `\`${src}\` names a file this package does not ship, so the book ` +
@@ -1323,7 +1315,7 @@ async function buildBook(book, shared) {
1323
1315
  } catch (err) {
1324
1316
  findings.push({
1325
1317
  file,
1326
- ...where(src, occurrence),
1318
+ ...where(image.locate ?? src, occurrence),
1327
1319
  severity: "warning",
1328
1320
  message:
1329
1321
  `\`${src}\` cannot be read from ` +
@@ -1388,12 +1380,12 @@ async function buildBook(book, shared) {
1388
1380
  * order, before any entry's expressions render — so a cross-note `ref`
1389
1381
  * finds the number its target carries in the finished book rather than a
1390
1382
  * page-local recount. Walked in the same order {@link bodies} fills
1391
- * below, so `figureCounts` lands on the same final tallies either way;
1392
- * the note's table expansion is cached here too, so the render loop does
1393
- * not redo it.
1383
+ * below, so `figureCounts` lands on the same final tallies either way. A
1384
+ * note's {@link module:engine/content-expand.expandTables} is kept here
1385
+ * with its figures numbered, and the render loop expands the rest from it.
1394
1386
  *
1395
- * @type {Map<string, {markdown?: string, errors?: object[],
1396
- * lineMap?: object[], numberedFigures: object[]}>}
1387
+ * @type {Map<string, {staged?: import("./content-expand.mjs").ExpandedTables,
1388
+ * numberedFigures: object[]}>}
1397
1389
  */
1398
1390
  const prepared = new Map();
1399
1391
  // The map from each selected place given a From here section, drawn before
@@ -1423,25 +1415,18 @@ async function buildBook(book, shared) {
1423
1415
  const file = noteFile(contentBase, entry.record);
1424
1416
  const page = byFile.get(file);
1425
1417
  if (!page) continue; // reported as `missing` by the render loop below
1426
- const src = page.relPath ?? page.base;
1427
- const { markdown, errors, lineMap, generatedFrom } = expandContentTables(page.body, {
1428
- docs: universe.get(page.pkg) ?? [],
1429
- linkable: (d) => Boolean(d.fm.shortcode),
1430
- source: src,
1431
- sqlTables: sqlTables?.get(page.file),
1432
- pageLists: sqlTables?.pageLists?.get(page.file),
1433
- self: { fm: page.fm, path: page.relPath },
1418
+ const staged = expandTables(page.body, {
1419
+ source: page.relPath ?? page.base,
1420
+ prepared: {
1421
+ sqlTables: sqlTables?.get(page.file),
1422
+ pageLists: sqlTables?.pageLists?.get(page.file),
1423
+ },
1424
+ resolveRole,
1434
1425
  });
1435
- const numberedFigures = numberFigures(
1436
- scanFigures(markdown, { resolveRole }).figures,
1437
- figureCounts,
1438
- );
1426
+ const numberedFigures = numberFigures(staged.figures.figures, figureCounts);
1439
1427
  prepared.set(entry.anchor, {
1440
- markdown,
1441
- errors,
1442
- lineMap,
1428
+ staged: { ...staged, figures: { ...staged.figures, figures: numberedFigures } },
1443
1429
  numberedFigures,
1444
- generatedFrom,
1445
1430
  });
1446
1431
  const shortcode = page.fm.shortcode;
1447
1432
  if (typeof shortcode === "string" && shortcode) {
@@ -1481,15 +1466,36 @@ async function buildBook(book, shared) {
1481
1466
  }
1482
1467
 
1483
1468
  const renderPage = (page, headingOffset, anchorPrefix, columns) => {
1484
- const src = page.relPath ?? page.base;
1485
- const wikiErrors = [];
1486
- const { markdown, errors, lineMap, numberedFigures, generatedFrom } =
1487
- prepared.get(anchorPrefix);
1469
+ const { staged, numberedFigures } = prepared.get(anchorPrefix);
1470
+ // The book's own figure numbering, from `prepared`: the book numbers
1471
+ // each kind across the whole book in reading order, so a reference
1472
+ // reads the number the book gives the figure, not the number the note
1473
+ // would give it alone.
1474
+ const expanded = expandContent(page.body, {
1475
+ target: "book",
1476
+ index: gates.index,
1477
+ note: {
1478
+ fm: page.fm,
1479
+ file: page.file,
1480
+ source: page.relPath ?? page.base,
1481
+ bodyLine: page.bodyLine,
1482
+ },
1483
+ prepared: { inline: sqlTables?.inline?.get(page.file) },
1484
+ events,
1485
+ figuresByAddress,
1486
+ tables: staged,
1487
+ links: {
1488
+ foreign: gates.foreign.index,
1489
+ assets,
1490
+ beforeLinks: pass.beforeLinks ? (text) => pass.beforeLinks(text, page) : undefined,
1491
+ },
1492
+ });
1493
+ const { lineMap, generatedFrom } = expanded;
1488
1494
  // A content-table finding states its `reason`, its 0-based line within
1489
1495
  // the body and its column, exactly as the site build reports them.
1490
1496
  // Reading it as `message` yields `[object Object]` and discards the
1491
1497
  // position, which is the one thing a reader needs to find the fence.
1492
- for (const err of errors) {
1498
+ for (const err of expanded.findings.tables) {
1493
1499
  findings.push({
1494
1500
  file: page.file,
1495
1501
  ...(typeof err.line === "number" ?
@@ -1499,77 +1505,7 @@ async function buildBook(book, shared) {
1499
1505
  message: err.reason ?? String(err.message ?? err),
1500
1506
  });
1501
1507
  }
1502
- const linkCtx = wikiContext(gates.index, {
1503
- src,
1504
- file: page.file,
1505
- type: page.fm.type ?? null,
1506
- errors: wikiErrors,
1507
- foreignIndex: gates.foreign.index,
1508
- assets,
1509
- });
1510
- linkCtx.captionLabels = new Map(
1511
- numberedFigures
1512
- .filter((figure) => figure.id)
1513
- .map((figure) => [figure.id, figure.label]),
1514
- );
1515
- // This page's own anchors, so a `[[#slug]]` self-link is checked
1516
- // against what the page actually declares, exactly as the site build
1517
- // checks it.
1518
- linkCtx.anchors = new Set(collectAnchors(markdown).map((anchor) => anchor.slug));
1519
- linkCtx.output = "book";
1520
- // What the `ref` expression helper needs beyond the label — the
1521
- // caption text and whether one was authored — keyed as
1522
- // `engine/site-build.mjs` keys its own, but built from this note's
1523
- // *book-wide* numbering rather than its page-local count: the book
1524
- // numbers each kind across the whole book in reading order, so a
1525
- // reference has to read the number the book gives the figure, not the
1526
- // number the note would give it alone.
1527
- const figuresById = new Map(
1528
- numberedFigures
1529
- .filter((figure) => figure.id)
1530
- .map((figure) => [
1531
- figure.id,
1532
- { label: figure.label, caption: figure.caption, hasCaption: figure.hasCaption },
1533
- ]),
1534
- );
1535
- // Code fences are protected for the same reason every other pass
1536
- // protects them: a wikilink shown as an example is prose about a
1537
- // wikilink, and resolving it would make the example impossible to write.
1538
- // Where the generated sections begin is marked before any pass can move
1539
- // a line, and the body is split there once its links resolve: the
1540
- // sections are set by the rules for generated links — see
1541
- // `renderLink` in `engine/pdf-render.mjs`.
1542
- const expressions = renderMarkdownExpressions(markGenerated(markdown, generatedFrom), {
1543
- fm: page.fm,
1544
- dates: gates.index.dateContext,
1545
- sqlResults: sqlTables?.inline?.get(page.file),
1546
- file: page.file,
1547
- bodyLine: page.bodyLine,
1548
- events,
1549
- figures: {
1550
- get: (id) => figuresById.get(id),
1551
- // A cross-note `ref` resolves the written address exactly as
1552
- // a wikilink does, against every note's figures numbered
1553
- // before any entry's expressions render — see `prepared`
1554
- // above. The default link renders the target's own web
1555
- // address, which `renderLink` in `engine/pdf-render.mjs`
1556
- // resolves to the entry the book gave it, exactly as an
1557
- // ordinary cross-note wikilink does.
1558
- note: (target) =>
1559
- resolveCrossNoteFigures(target, {
1560
- contentTypes: gates.index.contentTypes,
1561
- packages: gates.index.packages,
1562
- noIndexPackages: gates.index.noIndexPackages,
1563
- contentPackage: gates.index.contentPackage,
1564
- siteIndexMap: gates.index.index,
1565
- figuresByAddress,
1566
- }),
1567
- },
1568
- });
1569
- findings.push(...expressions.findings);
1570
- const resolvedBody = protectCode(expressions.markdown, (text) =>
1571
- resolveWebWikilinks(pass.beforeLinks ? pass.beforeLinks(text, page) : text, linkCtx),
1572
- );
1508
+ findings.push(...expanded.findings.expressions);
1573
1509
  // A link finding carries the authored link and which occurrence of it
1574
1510
  // this was, which is exactly what locates it: the position is implicit
1575
1511
  // in the literal the pass matched, so it is recovered by searching the
@@ -1593,7 +1529,7 @@ async function buildBook(book, shared) {
1593
1529
  // The source is written and the compiler still runs, so the finding is a
1594
1530
  // report on a book that was built, exactly as the site writes its pages
1595
1531
  // and refuses the run.
1596
- for (const err of wikiErrors) {
1532
+ for (const err of expanded.findings.links) {
1597
1533
  findings.push({
1598
1534
  file: page.file,
1599
1535
  ...positionOfLiteral(noteSource(), err.link, err.occurrence),
@@ -1604,8 +1540,8 @@ async function buildBook(book, shared) {
1604
1540
  message: err.message ?? (err.reason ? linkFindingMessage(err) : String(err)),
1605
1541
  });
1606
1542
  }
1607
- stageImages(resolvedBody, page.file, columns);
1608
- const { authored, generated } = splitGenerated(resolvedBody);
1543
+ stageImages(expanded.markdown, page.file, columns);
1544
+ const { authored, generated } = expanded;
1609
1545
  const typst = (text, options) =>
1610
1546
  markdownToTypst(text, {
1611
1547
  md,