@heroiclands/package-build 23.12.6 → 23.12.8

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 (94) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/assets/styles/content-base.css +25 -0
  3. package/bin/content-commands.mjs +264 -3
  4. package/bin/package-build.mjs +25 -0
  5. package/content-config.mjs +72 -0
  6. package/docs/api.md +10 -9
  7. package/docs/authoring/assets.md +1 -1
  8. package/docs/authoring/links-and-markup.md +439 -47
  9. package/docs/commands.md +137 -3
  10. package/docs/configuration.md +73 -3
  11. package/docs/content-format.md +3 -1
  12. package/docs/guides/book.md +11 -0
  13. package/docs/guides/module-project.md +16 -0
  14. package/docs/guides/site.md +19 -0
  15. package/docs/reference/format-details.md +378 -6
  16. package/docs/reference/note-types.md +36 -11
  17. package/e2e.mjs +1 -1
  18. package/engine/asset-shipping.mjs +22 -8
  19. package/engine/base-compiler.mjs +2 -0
  20. package/engine/bundles.mjs +7 -2
  21. package/engine/compile-corpus.mjs +3 -1
  22. package/engine/content-charset.mjs +21 -2
  23. package/engine/content-format.yaml +1 -0
  24. package/engine/content-index.mjs +12 -0
  25. package/engine/content-poetry.mjs +22 -4
  26. package/engine/content-scripts.mjs +1219 -0
  27. package/engine/content-session.mjs +2 -0
  28. package/engine/data-keys.mjs +5 -1
  29. package/engine/dictionary-layout.mjs +18 -12
  30. package/engine/dictionary-rows.mjs +11 -2
  31. package/engine/helpers.mjs +8 -2
  32. package/engine/index-records.mjs +1 -0
  33. package/engine/journals.mjs +14 -3
  34. package/engine/language-data.mjs +137 -1
  35. package/engine/language-tag.mjs +103 -0
  36. package/engine/language-terms.mjs +16 -4
  37. package/engine/map-lexicon.mjs +762 -0
  38. package/engine/markup-validation.mjs +17 -3
  39. package/engine/names.mjs +1 -1
  40. package/engine/note-vocabulary.mjs +113 -0
  41. package/engine/pdf-build.mjs +28 -10
  42. package/engine/pdf-render.mjs +116 -11
  43. package/engine/run-rows.mjs +119 -0
  44. package/engine/run-words.mjs +266 -0
  45. package/engine/scenes.mjs +1 -0
  46. package/engine/script-font-delivery.mjs +321 -0
  47. package/engine/script-fonts.mjs +172 -0
  48. package/engine/site-build.mjs +10 -3
  49. package/engine/site-config.mjs +5 -0
  50. package/engine/site-root.mjs +19 -5
  51. package/engine/sql-tables.mjs +111 -1
  52. package/engine/text-maps.mjs +607 -0
  53. package/engine/verb-pattern.mjs +99 -0
  54. package/engine/word-pattern.mjs +27 -0
  55. package/engine/word-rows.mjs +8 -2
  56. package/engine/word-terms.mjs +44 -0
  57. package/hugo-theme/layouts/_default/baseof.html +3 -0
  58. package/hugo-theme/static/css/style.css +10 -0
  59. package/manifest.mjs +9 -1
  60. package/package.json +2 -2
  61. package/types/content-config.d.mts +26 -0
  62. package/types/e2e.d.mts +2 -2
  63. package/types/engine/address-fields.d.mts +1 -1
  64. package/types/engine/address-values.d.mts +1 -1
  65. package/types/engine/asset-shipping.d.mts +5 -0
  66. package/types/engine/compile-corpus.d.mts +2 -1
  67. package/types/engine/content-format-check.d.mts +1 -1
  68. package/types/engine/content-index.d.mts +11 -3
  69. package/types/engine/content-poetry.d.mts +7 -0
  70. package/types/engine/content-scripts.d.mts +226 -0
  71. package/types/engine/data-keys.d.mts +2 -2
  72. package/types/engine/dictionary-rows.d.mts +15 -0
  73. package/types/engine/helpers.d.mts +5 -1
  74. package/types/engine/journals.d.mts +7 -2
  75. package/types/engine/language-data.d.mts +2 -0
  76. package/types/engine/language-tag.d.mts +8 -0
  77. package/types/engine/language-terms.d.mts +2 -1
  78. package/types/engine/map-lexicon.d.mts +274 -0
  79. package/types/engine/markup-validation.d.mts +7 -2
  80. package/types/engine/note-vocabulary.d.mts +1 -1
  81. package/types/engine/pdf-build.d.mts +26 -3
  82. package/types/engine/pdf-coverage.d.mts +1 -1
  83. package/types/engine/pdf-render.d.mts +17 -2
  84. package/types/engine/run-rows.d.mts +89 -0
  85. package/types/engine/run-words.d.mts +86 -0
  86. package/types/engine/script-font-delivery.d.mts +134 -0
  87. package/types/engine/script-fonts.d.mts +168 -0
  88. package/types/engine/site-build.d.mts +2 -1
  89. package/types/engine/site-root.d.mts +11 -3
  90. package/types/engine/text-maps.d.mts +175 -0
  91. package/types/engine/verb-pattern.d.mts +65 -0
  92. package/types/engine/word-pattern.d.mts +13 -0
  93. package/types/engine/word-terms.d.mts +6 -0
  94. package/types/release.d.mts +1 -1
@@ -0,0 +1,99 @@
1
+ /*
2
+ * This file is part of the Song of Heroic Lands (SoHL) system for Foundry VTT.
3
+ * Copyright (c) 2024-2026 Tom Rodriguez ("Toasty") — <toasty@heroiclands.org>
4
+ *
5
+ * This work is licensed under the GNU General Public License v3.0 (GPLv3).
6
+ * You may copy, modify, and distribute it under the terms of that license.
7
+ *
8
+ * For full terms, see the LICENSE.md file in the project root or visit:
9
+ * https://www.gnu.org/licenses/gpl-3.0.html
10
+ *
11
+ * SPDX-License-Identifier: GPL-3.0-or-later
12
+ */
13
+
14
+ /**
15
+ * **A language's one regular verb pattern, read once for every reader.** A
16
+ * language's `data.grammar.verbs` is `{ invariant: true }` or six `persons`
17
+ * endings, optionally with `replaces`. A present form is the citation form
18
+ * with the ending added; where the pattern states `replaces`, a citation form
19
+ * closing on those letters has them swapped for the ending, and one closing
20
+ * otherwise takes the ending whole, as a suffix's `replaces` does.
21
+ *
22
+ * The dictionary's verb table and a mapped run's lexicon check both read the
23
+ * pattern through {@link verbPattern} and build forms with {@link verbForm}.
24
+ *
25
+ * @module
26
+ */
27
+
28
+ import { VERB_PERSON_FIELDS } from "./language-terms.mjs";
29
+
30
+ /** Whether a value is a plain map. */
31
+ const isMap = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
32
+
33
+ /**
34
+ * A stated verb pattern, as its readers use it.
35
+ *
36
+ * @typedef {object} VerbPattern
37
+ * @property {boolean} invariant - Whether verbs keep the citation form for
38
+ * every person.
39
+ * @property {string|undefined} replaces - The letters each ending takes the
40
+ * place of, where the pattern states them.
41
+ * @property {Array<{name: string, label: string, ending: string}>} endings -
42
+ * The six endings in presentation order; empty for an invariant pattern.
43
+ */
44
+
45
+ /**
46
+ * Read a language's verb pattern.
47
+ *
48
+ * @param {unknown} verbs - The language's `data.grammar.verbs`, as authored or
49
+ * as a SQL row carries it (absent fields `null`).
50
+ * @returns {VerbPattern|undefined} The pattern, or `undefined` where none is
51
+ * stated in full.
52
+ */
53
+ export function verbPattern(verbs) {
54
+ if (!isMap(verbs)) return undefined;
55
+ const replaces =
56
+ typeof verbs.replaces === "string" && verbs.replaces.trim() ? verbs.replaces : undefined;
57
+ if (verbs.invariant === true) return { invariant: true, replaces, endings: [] };
58
+ const persons = isMap(verbs.persons) ? verbs.persons : undefined;
59
+ if (!persons || !VERB_PERSON_FIELDS.every(({ name }) => typeof persons[name] === "string"))
60
+ return undefined;
61
+ return {
62
+ invariant: false,
63
+ replaces,
64
+ endings: VERB_PERSON_FIELDS.map(({ name, label }) => ({
65
+ name,
66
+ label,
67
+ ending: persons[name],
68
+ })),
69
+ };
70
+ }
71
+
72
+ /**
73
+ * A stem with its closing letters swapped for an ending, or with the ending
74
+ * added where the stem does not close on them.
75
+ *
76
+ * @param {string} stem - The stem.
77
+ * @param {string} ending - The ending.
78
+ * @param {string|undefined} letters - The closing letters the ending takes
79
+ * the place of; none adds the ending.
80
+ * @returns {string} The form.
81
+ */
82
+ export function replaceClosing(stem, ending, letters) {
83
+ return letters && stem.endsWith(letters) ?
84
+ stem.slice(0, -letters.length) + ending
85
+ : stem + ending;
86
+ }
87
+
88
+ /**
89
+ * The present form of a verb for one ending.
90
+ *
91
+ * @param {string} citation - The citation form.
92
+ * @param {string} ending - The person's ending; empty leaves the citation
93
+ * form, less any closing `replaces`.
94
+ * @param {string} [replaces] - The pattern's `replaces`.
95
+ * @returns {string} The form.
96
+ */
97
+ export function verbForm(citation, ending, replaces) {
98
+ return replaceClosing(citation, ending, replaces);
99
+ }
@@ -0,0 +1,27 @@
1
+ /*
2
+ * This file is part of the Song of Heroic Lands (SoHL) system for Foundry VTT.
3
+ * Copyright (c) 2024-2026 Tom Rodriguez ("Toasty") — <toasty@heroiclands.org>
4
+ *
5
+ * This work is licensed under the GNU General Public License v3.0 (GPLv3).
6
+ * You may copy, modify, and distribute it under the terms of that license.
7
+ *
8
+ * For full terms, see the LICENSE.md file in the project root or visit:
9
+ * https://www.gnu.org/licenses/gpl-3.0.html
10
+ *
11
+ * SPDX-License-Identifier: GPL-3.0-or-later
12
+ */
13
+
14
+ /**
15
+ * **What a word is in running text.** A word is a maximal run of letters,
16
+ * combining marks and digits, and an apostrophe (`'` or `’`), a hyphen or an
17
+ * en dash joins two such runs into one word: `k'aralzon`, `Arzen-Thalor`.
18
+ * Anything else, whitespace and punctuation included, sits between words.
19
+ *
20
+ * The name catalog reads a label's last word by it, and a mapped run reads
21
+ * each word it looks up in a lexicon by it.
22
+ *
23
+ * @module
24
+ */
25
+
26
+ /** One word; global, so `matchAll` walks every word of a text. */
27
+ export const WORD = /[\p{L}\p{M}\p{N}]+(?:[’'–-][\p{L}\p{M}\p{N}]+)*/gu;
@@ -47,6 +47,12 @@ import {
47
47
  glossText,
48
48
  } from "./word-terms.mjs";
49
49
 
50
+ /**
51
+ * The word's own keys a word row carries: all but `native`, whose entries are
52
+ * rows of the `natives` relation.
53
+ */
54
+ const WORD_ROW_KEYS = Object.freeze(WORD_KEYS.filter((key) => key !== "native"));
55
+
50
56
  /** The columns a word row carries before the word's own keys. */
51
57
  const WORD_IDENTITY_COLUMNS = Object.freeze(["language", "position"]);
52
58
 
@@ -56,7 +62,7 @@ const WORD_DERIVED_COLUMNS = Object.freeze(["built_text", "built_parts", "senses
56
62
  /** Every column of the `words` relation, in order. */
57
63
  export const WORD_COLUMNS = Object.freeze([
58
64
  ...WORD_IDENTITY_COLUMNS,
59
- ...WORD_KEYS,
65
+ ...WORD_ROW_KEYS,
60
66
  ...WORD_DERIVED_COLUMNS,
61
67
  ]);
62
68
 
@@ -260,7 +266,7 @@ export function wordRows(records, all = records) {
260
266
  words.forEach((entry, position) => {
261
267
  if (!isMap(entry)) return;
262
268
  const row = { language, position };
263
- for (const key of WORD_KEYS) row[key] = entry[key] ?? null;
269
+ for (const key of WORD_ROW_KEYS) row[key] = entry[key] ?? null;
264
270
  if (row.name_sort === null) row.name_sort = row.form;
265
271
  const built = Array.isArray(entry.built) ? entry.built.map(String) : [];
266
272
  row.built_text = built.length ? built.join(" + ") : null;
@@ -99,6 +99,37 @@ export const WORD_SOURCE_FIELDS = Object.freeze([
99
99
  }),
100
100
  ]);
101
101
 
102
+ /**
103
+ * The keys of one entry of a word's `native`.
104
+ *
105
+ * @type {readonly import("./data-keys.mjs").InnerKeySpec[]}
106
+ */
107
+ export const WORD_NATIVE_FIELDS = Object.freeze([
108
+ Object.freeze({
109
+ name: "map",
110
+ kind: "string",
111
+ required: true,
112
+ shape: "a mapping note's `data.map.name`",
113
+ describe: "The map the native form is written for.",
114
+ }),
115
+ Object.freeze({
116
+ name: "font",
117
+ kind: "string",
118
+ shape: "a face the map lists",
119
+ describe:
120
+ "The one face the native form is for; unstated, it is for every face the map lists, " +
121
+ "and a form stated for a face wins over it there.",
122
+ }),
123
+ Object.freeze({
124
+ name: "text",
125
+ kind: "string",
126
+ required: true,
127
+ shape: "the word in the script, as the map's characters",
128
+ describe:
129
+ "The word as the script writes it, where the map's rows would write it otherwise.",
130
+ }),
131
+ ]);
132
+
102
133
  /**
103
134
  * The keys one word of a language note's `data.words` carries.
104
135
  *
@@ -209,6 +240,19 @@ export const WORD_FIELDS = Object.freeze([
209
240
  shape: "a frame of the language's `sounds.frames`",
210
241
  describe: "The frame a root is set in, named in the language's `sounds.frames`.",
211
242
  }),
243
+ Object.freeze({
244
+ name: "native",
245
+ kind: "list",
246
+ shape: "a list of `{ map, font?, text }`",
247
+ entries: Object.freeze({
248
+ kind: "map",
249
+ shape: "a map — `{ map, font?, text }`",
250
+ fields: WORD_NATIVE_FIELDS,
251
+ }),
252
+ describe:
253
+ "The word's native form under a map, stated only where the map's rows spell it " +
254
+ "differently. Its derived forms are spelled by the rows.",
255
+ }),
212
256
  ]);
213
257
 
214
258
  /** The keys of {@link WORD_FIELDS}, in declared order. */
@@ -9,6 +9,9 @@
9
9
  <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
10
10
  <link href="https://fonts.googleapis.com/css2?family=Cinzel:wght@400;500;600;700&family=JetBrains+Mono:wght@400;500&family=Lora:ital,wght@0,400;0,500;0,600;0,700;1,400;1,500;1,600&display=swap" rel="stylesheet">
11
11
  <link rel="stylesheet" href="{{ "css/style.css" | relURL }}">
12
+ {{/* Script fonts: the site build sets params.scriptFonts only when it has
13
+ copied them. */}}
14
+ {{ with .Site.Params.scriptFonts }}<link rel="stylesheet" href="{{ . | relURL }}">{{ end }}
12
15
  {{/* Icon glyphs. SoHL documentation writes icons as <i class="fa-solid …">
13
16
  or <i class="ginf-…">, the same markup the Foundry client renders, so a
14
17
  page reads identically in a journal and here. Font Awesome's FREE set is
@@ -3022,3 +3022,13 @@ summary.info-sidebar-title::after {
3022
3022
  .single-body .dictionary > div > dd > :is(p, ul, ol, dl, blockquote, pre, table, div) { text-indent:0; }
3023
3023
  .single-body .dictionary > div > dd > p:first-child { display:inline; }
3024
3024
  @media (max-width:600px) { .single-body .dictionary { --indent:100px; } }
3025
+
3026
+ /* ---- Text in one of the faces ----
3027
+ The font family and features come from the face's own class. A
3028
+ right-to-left block or verse reads from the right. `font=serif`, `sans`
3029
+ and `mono` set the site's own serif and mono, and the reader's sans. */
3030
+ .script-block { margin:1rem 0; line-height:1.7; }
3031
+ .script-block[dir="rtl"], .poetry[dir="rtl"] { text-align:start; }
3032
+ .font-serif { font-family:var(--font-body); }
3033
+ .font-sans { font-family:sans-serif; }
3034
+ .font-mono { font-family:var(--font-mono); }
package/manifest.mjs CHANGED
@@ -54,6 +54,7 @@ import fsSync from "node:fs";
54
54
  import path from "node:path";
55
55
 
56
56
  import { withBaseStyle } from "./engine/base-styles.mjs";
57
+ import { scriptFontFiles, withScriptFontStyle } from "./engine/script-font-delivery.mjs";
57
58
  import { emitDiagnostic, formatDiagnostic, positionOfYamlPath } from "./engine/diagnostics.mjs";
58
59
  import { metadataFileName } from "./engine/metadata-index.mjs";
59
60
 
@@ -571,11 +572,18 @@ export function buildManifest({ config, packageJson, artifact, flags }) {
571
572
  // the five that do have no stylesheet of their own to name it from. See
572
573
  // `engine/base-styles.mjs` for the layer it loads into and why that is what
573
574
  // lets a package override it.
574
- const styles = withBaseStyle({
575
+ const withBase = withBaseStyle({
575
576
  declared: declared.styles,
576
577
  artifact: /** @type {"system"|"module"} */ (artifact),
577
578
  enabled: config.packageBuild?.baseStyles !== false,
578
579
  });
580
+ // The installed script fonts' stylesheet follows it, when the module
581
+ // carries them: see `engine/script-font-delivery.mjs`.
582
+ const styles = withScriptFontStyle({
583
+ styles: withBase,
584
+ artifact: /** @type {"system"|"module"} */ (artifact),
585
+ delivered: scriptFontFiles(config, ["woff2"]) !== undefined,
586
+ });
579
587
  if (styles !== undefined) merged.styles = styles;
580
588
 
581
589
  // The index every consumer resolves this package's addresses through.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@heroiclands/package-build",
3
- "version": "23.12.6",
3
+ "version": "23.12.8",
4
4
  "description": "Shared toolchain for building and shipping a HeroicLands Foundry VTT package — content compilation, manifest, localization, staging, bundle, release and deployment.",
5
5
  "license": "GPL-3.0-or-later",
6
6
  "type": "module",
@@ -160,7 +160,7 @@
160
160
  "markdown-it": "^15.0.0",
161
161
  "markdown-it-deflist": "^4.0.0",
162
162
  "markdown-it-footnote": "^4.0.0",
163
- "markdownlint": "^0.41.1",
163
+ "markdownlint": "^0.42.0",
164
164
  "minimatch": "^10.2.6",
165
165
  "pagefind": "^1.5.2",
166
166
  "prettier": "^3.9.6",
@@ -703,6 +703,12 @@ export type ContentBuildConfigInput = {
703
703
  * vocabulary is their union.
704
704
  */
705
705
  itemBuilders?: Record<string, ItemBuilderEntry> | readonly ItemRegistrySpec[] | undefined;
706
+ /**
707
+ * The languages a
708
+ * note may tag text with, by short
709
+ * name. Default `{}`.
710
+ */
711
+ languages?: Record<string, LanguageSpec> | undefined;
706
712
  /**
707
713
  * Packs to compile. More than one entry
708
714
  * may share a `type`: a note then names
@@ -891,6 +897,10 @@ export type ContentBuildConfig = {
891
897
  * none.
892
898
  */
893
899
  icons: import("./engine/content-icons.mjs").IconRegistry;
900
+ /**
901
+ * Frozen; `{}` when absent.
902
+ */
903
+ languages: Readonly<Record<string, Readonly<LanguageSpec>>>;
894
904
  packs: readonly Readonly<ResolvedPackSpec>[];
895
905
  /**
896
906
  * Derived: every pack directory
@@ -918,3 +928,19 @@ export type ContentBuildConfig = {
918
928
  relationships: Readonly<Relationships>;
919
929
  publish: Readonly<PublishSwitches>;
920
930
  };
931
+ /**
932
+ * One entry of `languages`.
933
+ */
934
+ export type LanguageSpec = {
935
+ /**
936
+ * Its BCP 47 tag. A constructed language takes
937
+ * `art-x-<name>`.
938
+ */
939
+ tag: string;
940
+ /**
941
+ * The Address of its language note, whose
942
+ * `data.words` is the lexicon a mapped run in the
943
+ * language reads first.
944
+ */
945
+ note?: string | undefined;
946
+ };
package/types/e2e.d.mts CHANGED
@@ -419,7 +419,7 @@ export function suiteCommand(config: object, mode: "run" | "open"): string[];
419
419
  export function e2eRun({ config, packageJson, mode, suiteArgs, env, log, }: {
420
420
  config: object;
421
421
  packageJson: object;
422
- mode?: "open" | "run" | undefined;
422
+ mode?: "run" | "open" | undefined;
423
423
  suiteArgs?: string[] | undefined;
424
424
  env?: NodeJS.ProcessEnv | undefined;
425
425
  log?: ((message: string) => void) | undefined;
@@ -494,7 +494,7 @@ export const E2E_SCENE_ID: "heroiclandsScene";
494
494
  /** How the suite may be run. */
495
495
  export const E2E_MODES: readonly string[];
496
496
  /** The Chrome for Testing build shared by local and CI Cypress runs. */
497
- export const CHROME_BUILD_ID: "155.0.8059.84";
497
+ export const CHROME_BUILD_ID: "158.0.8110.0";
498
498
  /**
499
499
  * The disposable world's identity and credentials.
500
500
  */
@@ -69,7 +69,7 @@ export type AddressPosition = {
69
69
  * - Where the Addresses are.
70
70
  * Defaults to `value`.
71
71
  */
72
- holds?: "keys" | "value" | "items" | undefined;
72
+ holds?: "keys" | "items" | "value" | undefined;
73
73
  /**
74
74
  * - Resolve each exact target to its published Item UUID.
75
75
  */
@@ -75,7 +75,7 @@ export class AddressLink {
75
75
  constructor(target: object, anchor: string, kind?: "prose" | "event" | null);
76
76
  target: object;
77
77
  anchor: string;
78
- kind: "event" | "prose" | null;
78
+ kind: "prose" | "event" | null;
79
79
  }
80
80
  /** The keys of an Address as the published index writes it, and no others. */
81
81
  export const PUBLISHED_ADDRESS_KEYS: readonly string[];
@@ -110,6 +110,11 @@ export function assetsManifest(staged: readonly object[], stageDir: string): Ass
110
110
  * stage before a file is copied: a package whose provenance cannot be read is
111
111
  * not one whose shipping can be decided.
112
112
  *
113
+ * The pictures are written beside whatever a `packageBuild.assets` row already
114
+ * put under `icons/` and `images/`; nothing there is removed. A picture whose
115
+ * deployed path is already a file in the stage is a finding, and no picture is
116
+ * written. Clearing an earlier run's pictures is the stage reset's work.
117
+ *
113
118
  * @param {object} opts - Options.
114
119
  * @param {string} opts.assetsBase - The package's asset directory, the roots'
115
120
  * parent.
@@ -16,7 +16,7 @@
16
16
  * the index refuses is a note the compile must still *report*, exactly as the
17
17
  * compile loop reported it when the loop was the first to see it.
18
18
  * @param {import("./content-session.mjs").ContentSession} [opts.session] - Captured context shared with other operations.
19
- * @returns {Promise<{currentDate: string, records: object[], parsed: Map<string, object>, linkIndex: object, sqlTables: Map<string, object[]>|undefined, problems: object[], syntax?: import("./content-session.mjs").ContentSyntax}>} The corpus,
19
+ * @returns {Promise<{currentDate: string, runs: import("./content-scripts.mjs").RunContext, records: object[], parsed: Map<string, object>, linkIndex: object, sqlTables: Map<string, object[]>|undefined, problems: object[], syntax?: import("./content-session.mjs").ContentSyntax}>} The corpus,
20
20
  * its indexes, and the notes it could not record.
21
21
  */
22
22
  export function buildCompileCorpus({ contentBase, assetsBase, skipDirectories, router, config, problems, session, }: {
@@ -29,6 +29,7 @@ export function buildCompileCorpus({ contentBase, assetsBase, skipDirectories, r
29
29
  session?: import("./content-session.mjs").ContentSession | undefined;
30
30
  }): Promise<{
31
31
  currentDate: string;
32
+ runs: import("./content-scripts.mjs").RunContext;
32
33
  records: object[];
33
34
  parsed: Map<string, object>;
34
35
  linkIndex: object;
@@ -117,7 +117,7 @@ export function checkDeclaredFields({ format, itemFields, system, severity }: {
117
117
  format: import("./content-format.mjs").ContentFormat;
118
118
  itemFields: Record<string, readonly object[]>;
119
119
  system: string;
120
- severity?: "error" | "warning" | undefined;
120
+ severity?: "warning" | "error" | undefined;
121
121
  }): {
122
122
  findings: object[];
123
123
  coverage: object[];
@@ -81,6 +81,8 @@ export function asciiAliases(aliases: unknown): Array<string>;
81
81
  * `name` reads `Map 1` rather than `Figure 1` where it is due — see
82
82
  * {@link module:engine/content-figures.scanFigures}.
83
83
  * @param {import("./content-session.mjs").ContentSyntax} [options.syntax] - Session scanner projections.
84
+ * @param {import("./content-scripts.mjs").RunContext} [options.runContext] -
85
+ * The configured languages, which give each run's `lang=` its tag.
84
86
  * @returns {Record<string, any>} The record, keys sorted at every depth. A
85
87
  * **stub** — a note with an empty body, on a type an empty body suppresses —
86
88
  * carries `address` and `anchors` as `null`: it publishes no page, so it
@@ -92,7 +94,7 @@ export function asciiAliases(aliases: unknown): Array<string>;
92
94
  * otherwise be overwritten without a word. `file` and, where the file was
93
95
  * read, `position` ride on the error.
94
96
  */
95
- export function buildIndexRecord({ frontmatter, relPath, absPath, raw, contentPackage, body, bodyLine, bodyColumn, nameTypes, manifest, addressContext, dateContext, resolveRole, syntax, }: {
97
+ export function buildIndexRecord({ frontmatter, relPath, absPath, raw, contentPackage, body, bodyLine, bodyColumn, nameTypes, manifest, addressContext, dateContext, resolveRole, syntax, runContext, }: {
96
98
  frontmatter: Record<string, any>;
97
99
  relPath: string;
98
100
  absPath?: string | undefined;
@@ -107,12 +109,13 @@ export function buildIndexRecord({ frontmatter, relPath, absPath, raw, contentPa
107
109
  dateContext?: object | undefined;
108
110
  resolveRole?: ((address: string) => string | undefined) | undefined;
109
111
  syntax?: import("./content-session.mjs").ContentSyntax | undefined;
112
+ runContext?: import("./content-scripts.mjs").RunContext | undefined;
110
113
  }): Record<string, any>;
111
114
  /**
112
115
  * Derive every index record belonging to one parsed note.
113
116
  * @param {Parameters<typeof buildIndexRecord>[0]} options - Authored note and its context.
114
117
  */
115
- export function indexRecordsForNote({ frontmatter, body, bodyLine, bodyColumn, nameTypes, relPath, absPath, raw, contentPackage, manifest, addressContext, dateContext, resolveRole, syntax, }: Parameters<typeof buildIndexRecord>[0]): object[];
118
+ export function indexRecordsForNote({ frontmatter, body, bodyLine, bodyColumn, nameTypes, relPath, absPath, raw, contentPackage, manifest, addressContext, dateContext, resolveRole, syntax, runContext, }: Parameters<typeof buildIndexRecord>[0]): object[];
116
119
  /**
117
120
  * Write each note's resolved culture into its record, as `data.culture`.
118
121
  *
@@ -209,6 +212,8 @@ export function resolveCultures(records: Array<Record<string, any>>, { contentPa
209
212
  * calendar note's shortcode; see {@link module:engine/note-calendars}.
210
213
  * @param {string} [options.configFile] - The configuration file, where an
211
214
  * unknown `defaultCalendar` is reported.
215
+ * @param {Readonly<Record<string, {tag: string}>>} [options.languages] - The
216
+ * configured languages, which give each run's `lang=` its tag.
212
217
  * @param {(key: string) => ({culture?: unknown}|undefined)} [options.foreignRecord] -
213
218
  * Another package's entry for a canonical address, read for a parent that
214
219
  * package publishes; see {@link resolveCultures}.
@@ -221,7 +226,7 @@ export function resolveCultures(records: Array<Record<string, any>>, { contentPa
221
226
  * @returns {Array<Record<string, any>>} The records, in a total order that does
222
227
  * not depend on directory-read order.
223
228
  */
224
- export function collectContentIndex(contentBase: string, { contentPackage, currentDate, defaultCalendar, configFile, skipDirectories, assetsBase, manifest, problems, addressContext, foreignRecord, parsed, sources, syntax, nameTypes, }: {
229
+ export function collectContentIndex(contentBase: string, { contentPackage, currentDate, defaultCalendar, configFile, languages, skipDirectories, assetsBase, manifest, problems, addressContext, foreignRecord, parsed, sources, syntax, nameTypes, }: {
225
230
  contentPackage: string;
226
231
  skipDirectories: readonly string[];
227
232
  assetsBase?: string | undefined;
@@ -231,6 +236,9 @@ export function collectContentIndex(contentBase: string, { contentPackage, curre
231
236
  currentDate?: string | undefined;
232
237
  defaultCalendar?: string | undefined;
233
238
  configFile?: string | undefined;
239
+ languages?: Readonly<Record<string, {
240
+ tag: string;
241
+ }>> | undefined;
234
242
  foreignRecord?: ((key: string) => ({
235
243
  culture?: unknown;
236
244
  } | undefined)) | undefined;
@@ -49,3 +49,10 @@ export function renderPoetry(source: string): {
49
49
  message: string;
50
50
  }[];
51
51
  };
52
+ /**
53
+ * The keys a poetry fence's attribute block takes. `lang`, `font` and `map`
54
+ * are read by `scanRuns` in content-scripts.mjs; `dir`, and on the site
55
+ * `data-pagefind-ignore`, are written by its rewrite of a fence set in one of
56
+ * the faces, and reach the element as they are written.
57
+ */
58
+ export const POETRY_KEYS: readonly string[];