@heroiclands/package-build 20.3.1 → 20.5.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 (195) hide show
  1. package/CHANGELOG.md +351 -0
  2. package/CONTENT.md +322 -110
  3. package/README.md +21 -10
  4. package/bin/content-build.mjs +204 -92
  5. package/bin/package-build.mjs +48 -15
  6. package/config.mjs +6 -8
  7. package/content-config.mjs +518 -151
  8. package/docs/api.md +1352 -0
  9. package/docs/commands.md +1609 -0
  10. package/docs/configuration.md +1432 -0
  11. package/docs/content-format.md +73 -74
  12. package/docs/diagnostics.md +356 -0
  13. package/docs/getting-started.md +813 -0
  14. package/docs/project-setup.md +469 -0
  15. package/e2e.mjs +4 -4
  16. package/engine/actor-compiler.mjs +64 -61
  17. package/engine/address-charset.mjs +6 -6
  18. package/engine/address-diff.mjs +57 -52
  19. package/engine/anchored-sections.mjs +1 -1
  20. package/engine/anchors.mjs +1 -1
  21. package/engine/base-compiler.mjs +42 -38
  22. package/engine/bundle-notes.mjs +4 -4
  23. package/engine/bundles.mjs +17 -8
  24. package/engine/code-fences.mjs +3 -3
  25. package/engine/compendiums.mjs +8 -8
  26. package/engine/compile-corpus.mjs +4 -5
  27. package/engine/content-address.mjs +24 -24
  28. package/engine/content-charset.mjs +1 -1
  29. package/engine/content-format-check.mjs +24 -25
  30. package/engine/content-format.mjs +8 -8
  31. package/engine/content-html.mjs +202 -0
  32. package/engine/content-icons.mjs +196 -165
  33. package/engine/content-index.mjs +61 -40
  34. package/engine/content-links.mjs +74 -41
  35. package/engine/content-lint.mjs +64 -34
  36. package/engine/content-package.mjs +7 -9
  37. package/engine/content-slug.mjs +2 -2
  38. package/engine/content-tables.mjs +38 -34
  39. package/engine/content-tree.mjs +1 -1
  40. package/engine/derived-fields.mjs +174 -0
  41. package/engine/diagnostics.mjs +3 -4
  42. package/engine/document-subtypes.mjs +6 -7
  43. package/engine/field-reference.mjs +8 -8
  44. package/engine/field-spec.mjs +29 -30
  45. package/engine/folder-notes.mjs +18 -16
  46. package/engine/foreign-catalog.mjs +8 -9
  47. package/engine/foundry-entries.mjs +19 -20
  48. package/engine/frontmatter-lint.mjs +95 -103
  49. package/engine/frontmatter.mjs +11 -11
  50. package/engine/generate.mjs +44 -39
  51. package/engine/helpers.mjs +147 -84
  52. package/engine/homepage.mjs +20 -22
  53. package/engine/ids.mjs +15 -15
  54. package/engine/index-records.mjs +4 -4
  55. package/engine/index.mjs +26 -10
  56. package/engine/item-compiler.mjs +54 -29
  57. package/engine/item-docs.mjs +11 -13
  58. package/engine/item-registry.mjs +13 -13
  59. package/engine/journals.mjs +24 -15
  60. package/engine/macros.mjs +3 -3
  61. package/engine/map-notes.mjs +6 -6
  62. package/engine/metadata-index.mjs +5 -5
  63. package/engine/note-claims.mjs +51 -41
  64. package/engine/note-ids.mjs +5 -5
  65. package/engine/note-package.mjs +4 -4
  66. package/engine/note-renames.mjs +3 -3
  67. package/engine/note-schemas.mjs +3 -8
  68. package/engine/note-vocabulary.mjs +56 -54
  69. package/engine/pack-config.mjs +39 -25
  70. package/engine/pack-router.mjs +0 -0
  71. package/engine/pdf-build.mjs +464 -0
  72. package/engine/pdf-fonts.mjs +420 -0
  73. package/engine/pdf-render.mjs +798 -0
  74. package/engine/pdf-toc.mjs +525 -0
  75. package/engine/prose-config.mjs +9 -9
  76. package/engine/prose-lint.mjs +12 -13
  77. package/engine/region-events.mjs +2 -2
  78. package/engine/retired-fields.mjs +24 -22
  79. package/engine/runtime-only-fields.mjs +1 -1
  80. package/engine/scene-levels.mjs +1 -1
  81. package/engine/scenes.mjs +33 -25
  82. package/engine/schema-check.mjs +21 -21
  83. package/engine/schema-extract.mjs +1 -1
  84. package/engine/site-build.mjs +66 -50
  85. package/engine/site-index.mjs +17 -18
  86. package/engine/sql-tables.mjs +21 -13
  87. package/engine/subtype-registry.mjs +5 -4
  88. package/engine/system-block.mjs +18 -19
  89. package/engine/systems.mjs +2 -2
  90. package/engine/web-wikilinks.mjs +41 -38
  91. package/engine/wikilink-syntax.mjs +16 -16
  92. package/engine/wikilinks.mjs +40 -42
  93. package/hm3/actors.mjs +30 -22
  94. package/hm3/document-subtypes.mjs +5 -5
  95. package/hm3/index.mjs +2 -2
  96. package/hm3/item-builders.mjs +1 -1
  97. package/hm3/item-fields.mjs +3 -3
  98. package/hm3/items.mjs +28 -8
  99. package/hm3/template-priority.mjs +2 -2
  100. package/lang.mjs +3 -3
  101. package/manifest.mjs +17 -20
  102. package/package.json +1 -2
  103. package/release.mjs +66 -6
  104. package/sohl/actors.mjs +36 -28
  105. package/sohl/affiliation-standings.mjs +2 -2
  106. package/sohl/being-info.mjs +5 -5
  107. package/sohl/default-item-art.mjs +5 -5
  108. package/sohl/document-subtypes.mjs +5 -5
  109. package/sohl/index.mjs +3 -3
  110. package/sohl/item-builders.mjs +7 -7
  111. package/sohl/item-fields.mjs +8 -9
  112. package/sohl/items.mjs +28 -6
  113. package/sohl/kb-passes.mjs +5 -5
  114. package/sohl/note-schemas.mjs +12 -12
  115. package/sohl/skill-base.mjs +1 -1
  116. package/types/content-config.d.mts +91 -39
  117. package/types/e2e.d.mts +3 -3
  118. package/types/engine/actor-compiler.d.mts +45 -40
  119. package/types/engine/address-charset.d.mts +6 -6
  120. package/types/engine/address-diff.d.mts +60 -6
  121. package/types/engine/base-compiler.d.mts +27 -21
  122. package/types/engine/bundle-notes.d.mts +3 -3
  123. package/types/engine/bundles.d.mts +10 -1
  124. package/types/engine/code-fences.d.mts +3 -3
  125. package/types/engine/compendiums.d.mts +3 -3
  126. package/types/engine/compile-corpus.d.mts +1 -1
  127. package/types/engine/content-address.d.mts +20 -20
  128. package/types/engine/content-format-check.d.mts +6 -6
  129. package/types/engine/content-format.d.mts +2 -2
  130. package/types/engine/content-html.d.mts +78 -0
  131. package/types/engine/content-icons.d.mts +139 -92
  132. package/types/engine/content-index.d.mts +59 -15
  133. package/types/engine/content-links.d.mts +8 -8
  134. package/types/engine/content-lint.d.mts +8 -6
  135. package/types/engine/content-package.d.mts +6 -8
  136. package/types/engine/content-tables.d.mts +49 -18
  137. package/types/engine/derived-fields.d.mts +101 -0
  138. package/types/engine/diagnostics.d.mts +2 -2
  139. package/types/engine/document-subtypes.d.mts +3 -3
  140. package/types/engine/field-spec.d.mts +37 -39
  141. package/types/engine/folder-notes.d.mts +4 -5
  142. package/types/engine/foreign-catalog.d.mts +3 -3
  143. package/types/engine/foundry-entries.d.mts +6 -7
  144. package/types/engine/frontmatter-lint.d.mts +10 -9
  145. package/types/engine/frontmatter.d.mts +7 -7
  146. package/types/engine/generate.d.mts +7 -7
  147. package/types/engine/helpers.d.mts +128 -56
  148. package/types/engine/homepage.d.mts +16 -18
  149. package/types/engine/ids.d.mts +13 -13
  150. package/types/engine/index-records.d.mts +3 -3
  151. package/types/engine/index.d.mts +6 -0
  152. package/types/engine/item-compiler.d.mts +21 -5
  153. package/types/engine/item-docs.d.mts +2 -2
  154. package/types/engine/item-registry.d.mts +6 -6
  155. package/types/engine/journals.d.mts +12 -3
  156. package/types/engine/map-notes.d.mts +2 -2
  157. package/types/engine/metadata-index.d.mts +4 -4
  158. package/types/engine/note-claims.d.mts +30 -22
  159. package/types/engine/note-ids.d.mts +4 -4
  160. package/types/engine/note-package.d.mts +1 -1
  161. package/types/engine/note-renames.d.mts +3 -3
  162. package/types/engine/note-vocabulary.d.mts +31 -203
  163. package/types/engine/pack-config.d.mts +7 -7
  164. package/types/engine/pack-router.d.mts +1 -1
  165. package/types/engine/pdf-build.d.mts +42 -0
  166. package/types/engine/pdf-fonts.d.mts +30 -0
  167. package/types/engine/pdf-render.d.mts +144 -0
  168. package/types/engine/pdf-toc.d.mts +114 -0
  169. package/types/engine/prose-config.d.mts +9 -9
  170. package/types/engine/prose-lint.d.mts +3 -4
  171. package/types/engine/region-events.d.mts +2 -2
  172. package/types/engine/retired-fields.d.mts +10 -9
  173. package/types/engine/scenes.d.mts +10 -1
  174. package/types/engine/schema-check.d.mts +13 -13
  175. package/types/engine/site-build.d.mts +52 -25
  176. package/types/engine/site-index.d.mts +3 -4
  177. package/types/engine/sql-tables.d.mts +11 -5
  178. package/types/engine/subtype-registry.d.mts +3 -3
  179. package/types/engine/system-block.d.mts +3 -3
  180. package/types/engine/web-wikilinks.d.mts +7 -7
  181. package/types/engine/wikilink-syntax.d.mts +17 -17
  182. package/types/engine/wikilinks.d.mts +14 -14
  183. package/types/hm3/actors.d.mts +9 -1
  184. package/types/hm3/document-subtypes.d.mts +1 -1
  185. package/types/hm3/items.d.mts +9 -2
  186. package/types/hm3/template-priority.d.mts +1 -1
  187. package/types/manifest.d.mts +8 -8
  188. package/types/release.d.mts +15 -4
  189. package/types/sohl/actors.d.mts +12 -4
  190. package/types/sohl/affiliation-standings.d.mts +2 -2
  191. package/types/sohl/being-info.d.mts +2 -2
  192. package/types/sohl/document-subtypes.d.mts +1 -1
  193. package/types/sohl/items.d.mts +9 -2
  194. package/types/sohl/note-schemas.d.mts +1 -1
  195. package/MIGRATING.md +0 -608
@@ -17,8 +17,8 @@
17
17
  * This is the authority for what an item note may write, and — because
18
18
  * {@link buildFromFields} turns each list into the builder that runs — it is
19
19
  * the same authority the compiler obeys. A field that is not here is not
20
- * emitted; a description here is a description of the thing that executes
21
- * (#22). The per-type authoring reference on the knowledgebase is generated
20
+ * emitted; a description here is a description of the thing that executes.
21
+ * The per-type authoring reference on the knowledgebase is generated
22
22
  * from this file.
23
23
  *
24
24
  * **SoHL type vocabulary, so it lives in `sohl/`.** `engine/` holds the
@@ -197,7 +197,7 @@ const STRIKE_MODES = Object.freeze({
197
197
  * depends on it.
198
198
  *
199
199
  * Authored `impact.die`, stored `impactBase.die` — the one re-reading field
200
- * whose two positions are spelled differently, so both are named (#126).
200
+ * whose two positions are spelled differently, so both are named.
201
201
  *
202
202
  * @param {object} fm - The note's frontmatter.
203
203
  * @returns {number} The die size, `0` when the projectile declares none.
@@ -340,8 +340,7 @@ export const ITEM_FIELDS = Object.freeze({
340
340
  },
341
341
  {
342
342
  // Plural because the field holds a map of many standings, one per
343
- // affiliation — the singular was a misnomer every author read past
344
- // (SoHL#1781).
343
+ // affiliation — the singular reads as a misnomer.
345
344
  name: "relations",
346
345
  to: "relations",
347
346
  ...RELATION,
@@ -448,7 +447,7 @@ export const ITEM_FIELDS = Object.freeze({
448
447
  describe:
449
448
  "What running the course to the end does to the host: `death`, or the benign default `cured`.",
450
449
  },
451
- // The two **authored** thirds of each timed-phase triplet (#329). Every
450
+ // The two **authored** thirds of each timed-phase triplet. Every
452
451
  // one omits its key when the note does not carry it, because the value
453
452
  // that stands otherwise is the DataModel's `initial: null` — writing a
454
453
  // compile-time `null` over it would say "this phase takes no time",
@@ -513,7 +512,7 @@ export const ITEM_FIELDS = Object.freeze({
513
512
  "The resolution interval in seconds, standing in for a roll of the formula. Omitted when unset.",
514
513
  },
515
514
  // The `…Date` third of every timed-phase triplet, declared as **runtime
516
- // state** (#330). A phase is authored as its `…DurationFormula`; the
515
+ // state**. A phase is authored as its `…DurationFormula`; the
517
516
  // `…DurationBase` records what that rolled to; the date records *when
518
517
  // it happened*, and only play can know that. All four are
519
518
  // `worldTimeDateField()`s in SoHL's schema — nullable rather than
@@ -949,7 +948,7 @@ export const ITEM_FIELDS = Object.freeze({
949
948
  default: null,
950
949
  describe: "Shortcode of the body location injured. Unset on a descriptive condition.",
951
950
  },
952
- // A trauma's three timed phases (#329). Its healing check and course
951
+ // A trauma's three timed phases. Its healing check and course
953
952
  // both fall back to a **world setting** when the trauma sets neither
954
953
  // half, which is the sharper reason these omit rather than default: a
955
954
  // compile-time `null` written here is still a value, and the seeding in
@@ -1007,7 +1006,7 @@ export const ITEM_FIELDS = Object.freeze({
1007
1006
  describe:
1008
1007
  "The course-test interval in seconds, standing in for a roll of the formula. Omitted when unset.",
1009
1008
  },
1010
- // Runtime state, for the reason `affliction`'s four are (#330): a
1009
+ // Runtime state, for the reason `affliction`'s four are: a
1011
1010
  // trauma's dates are crystallized when the phase fires, and world time
1012
1011
  // does not exist while content is compiled.
1013
1012
  {
package/sohl/items.mjs CHANGED
@@ -19,14 +19,14 @@
19
19
  * note, looking its subtype up in the system's map, dispatching to the
20
20
  * consumer's registry, merging the authored `sohl.system` block, checking what
21
21
  * was emitted against the receiving schema, and writing the envelope. That was
22
- * all here until a second system needed it (#139), and it reached its
22
+ * all here until a second system needed it, and it reached its
23
23
  * system-specific facts through one constant read off SoHL's own map — which is
24
24
  * why lifting it cost a subclass rather than a rewrite.
25
25
  *
26
26
  * What stays:
27
27
  *
28
28
  * - **The map**, declared in `document-subtypes.mjs` and named here, which
29
- * decides which notes this pass claims and what each one becomes (#79).
29
+ * decides which notes this pass claims and what each one becomes.
30
30
  * - **`commonSystem`** — `shortcode`, `templatePriority`, `actionDefs`, `notes` and
31
31
  * `docHtml`, which SoHL's compiler writes on every item of every type and no
32
32
  * field declaration states.
@@ -41,16 +41,38 @@ import { systemTemplatePriority } from "../engine/helpers.mjs";
41
41
  import { SystemItemCompiler } from "../engine/item-compiler.mjs";
42
42
  import { SOHL_DOCUMENT_SUBTYPES } from "./document-subtypes.mjs";
43
43
 
44
+ /**
45
+ * SoHL's Item compile pass.
46
+ *
47
+ * Declares SoHL's note-type → document-subtype map, which decides the notes
48
+ * this pass claims and what each becomes, and the `system` keys SoHL writes on
49
+ * every item whatever its type. Everything else is
50
+ * {@link module:engine/item-compiler}'s.
51
+ */
44
52
  export class Items extends SystemItemCompiler {
45
53
  /**
46
54
  * SoHL's note-type → document-subtype map — the one declaration that says
47
- * which block this pass reads, which notes it claims, and what each becomes
48
- * (#58/#79).
55
+ * which block this pass reads, which notes it claims, and what each becomes.
49
56
  *
50
57
  * @type {import("../engine/document-subtypes.mjs").DocumentSubtypeMap}
51
58
  */
52
59
  static documentSubtypes = SOHL_DOCUMENT_SUBTYPES;
53
60
 
61
+ /**
62
+ * The `system` keys this pass derives from the note.
63
+ *
64
+ * `docHtml` holds the `@UUID` of the JournalEntry the note's prose compiled
65
+ * into, so a note writing prose there ships a string where every reader
66
+ * expects a pointer. `notes` is a play-time scratchpad the compiler writes
67
+ * empty, so an authored one ships a note's content as a GM's jottings.
68
+ *
69
+ * @type {readonly {key: string, from: string}[]}
70
+ */
71
+ static derivedSystemKeys = Object.freeze([
72
+ { key: "docHtml", from: "the note's own body" },
73
+ { key: "notes", from: "the note's own body" },
74
+ ]);
75
+
54
76
  /**
55
77
  * The `system.*` fields SoHL writes on every item, whatever its type:
56
78
  * shortcode, templatePriority, actionDefs, notes, docHtml.
@@ -65,8 +87,8 @@ export class Items extends SystemItemCompiler {
65
87
  return {
66
88
  shortcode: fm.shortcode,
67
89
  // Required nullable number: a priority, or `null` for a document
68
- // that is not a template (#126 / archetype contract #604). The
69
- // field was `system.archetype` until #266 / sohl#1836; the receiving
90
+ // that is not a template (the archetype contract).
91
+ // `system.archetype` is the legacy spelling; the receiving
70
92
  // schema declares only the new name, so the emitted key moves with
71
93
  // it — an undeclared `system` key is discarded at construction
72
94
  // without a warning.
@@ -12,12 +12,12 @@
12
12
  */
13
13
 
14
14
  /**
15
- * The `sohl` knowledgebase's own body passes (#63).
15
+ * The `sohl` knowledgebase's own body passes.
16
16
  *
17
17
  * `content-build site` publishes a content tree as a website, and almost all of
18
18
  * that job is the same for every package. These two rewrites are not: they are
19
19
  * driven by a TypeDoc symbol map and a repository layout only this package has,
20
- * and #63 rules them explicitly per-consumer.
20
+ * and they are ruled explicitly per-consumer.
21
21
  *
22
22
  * They live here rather than in a script in the consuming repository for the
23
23
  * same reason `sohl/item-builders.mjs` and `sohl/being-info.mjs` do: a
@@ -33,7 +33,7 @@
33
33
  * broken link or a failed build for a syntax example in prose would not be.
34
34
  *
35
35
  * Building the bundle is a different matter: a `symbolMap` that is configured
36
- * and cannot be used fails, loudly, before a page is rendered (#75). Degrading
36
+ * and cannot be used fails, loudly, before a page is rendered. Degrading
37
37
  * a tag the map does not know is a judgement about *prose*; degrading every tag
38
38
  * on the site because the map was never read is a build that lied.
39
39
  *
@@ -58,12 +58,12 @@ import log from "loglevel";
58
58
  * with no symbols: a bare `catch` returned `{}` for all five. Nothing then
59
59
  * compares an emitted page against what its source asked for, so the first
60
60
  * observer of a broken map was a reader who clicked nothing, because every
61
- * `{@link}` on the published site had quietly become a code span (#75).
61
+ * `{@link}` on the published site had quietly become a code span.
62
62
  *
63
63
  * The path is resolved against the **repository root**, never the process cwd.
64
64
  * `site.passOptions.symbolMap` is authored repo-relative, so a cwd-relative
65
65
  * read misses the moment `content-build site` is driven from anywhere but the
66
- * repository root — which is exactly how #51's end-to-end verification, running
66
+ * repository root — which is exactly how the end-to-end verification, running
67
67
  * through `PACKAGE_BUILD_CONFIG` from outside the tree, found this.
68
68
  *
69
69
  * @param {string|undefined} file - Path to the map, if configured.
@@ -13,7 +13,7 @@
13
13
 
14
14
  /**
15
15
  * What every SoHL content type may write under `sohl:` — the vocabulary the
16
- * frontmatter linter checks a note against (#19).
16
+ * frontmatter linter checks a note against.
17
17
  *
18
18
  * **Item types need no declaration here.** {@link ITEM_FIELDS} already is one,
19
19
  * and it is the same list the compiler obeys, so an item's schema and its
@@ -26,7 +26,7 @@
26
26
  * nothing here builds anything, and claiming an emitted path they do not
27
27
  * produce would be a lie in the one place a reader would trust it. When one of
28
28
  * those compilers becomes declarative, its entry here becomes the builder, the
29
- * way `ITEM_FIELDS` did (#22).
29
+ * way `ITEM_FIELDS` did.
30
30
  *
31
31
  * **Every consumer loads all of it.** An adventure module authors the full
32
32
  * vocabulary — a specific skill, a magic sword, an NPC, a custom beast is the
@@ -82,11 +82,6 @@ const MACRO_FIELDS = Object.freeze([
82
82
  },
83
83
  ]);
84
84
 
85
- /**
86
- * A `being` note — an actor, with its body, movement and embedded items.
87
- *
88
- * @type {readonly import("../engine/field-spec.mjs").FieldSpec[]}
89
- */
90
85
  /**
91
86
  * A `vehicle` note — a conveyance that carries goods and people.
92
87
  *
@@ -94,7 +89,7 @@ const MACRO_FIELDS = Object.freeze([
94
89
  * vehicle two `data` properties and no `sohl:` field of its own, and the
95
90
  * closed `data:` container is where `engine/note-vocabulary.mjs` holds them.
96
91
  * Declaring the type with no fields distinguishes a type with no vocabulary
97
- * from a type that is unknown, which are different findings (#231).
92
+ * from a type that is unknown, which are different findings.
98
93
  *
99
94
  * It is here rather than in the engine because a vehicle is a SoHL actor —
100
95
  * the specification maps it to `sohl` and marks it NA for hm3.
@@ -103,6 +98,11 @@ const MACRO_FIELDS = Object.freeze([
103
98
  */
104
99
  const VEHICLE_FIELDS = Object.freeze([]);
105
100
 
101
+ /**
102
+ * A `being` note — an actor, with its body, movement and embedded items.
103
+ *
104
+ * @type {readonly import("../engine/field-spec.mjs").FieldSpec[]}
105
+ */
106
106
  const BEING_FIELDS = Object.freeze([
107
107
  {
108
108
  name: "body",
@@ -139,7 +139,7 @@ const BEING_FIELDS = Object.freeze([
139
139
 
140
140
  /**
141
141
  * A map note — one type whose `battlemap` / `localmap` / `regionalmap`
142
- * subType decides the derived canvas (#174). Compiled into a
142
+ * subType decides the derived canvas. Compiled into a
143
143
  * Foundry Scene.
144
144
  *
145
145
  * The three differ only in derived canvas defaults, which is the map compiler's
@@ -148,8 +148,8 @@ const BEING_FIELDS = Object.freeze([
148
148
  *
149
149
  * `img` is the one required field — the compiler refuses a map note without it,
150
150
  * since a scene with no background is not a map. It was spelled `image` and
151
- * read from the `sohl:` block until #142, which read both while the trees were
152
- * swept; that retirement has since completed and `image` is gone (#149).
151
+ * read from the `sohl:` block; both were read while the trees were
152
+ * swept; that retirement has since completed and `image` is gone.
153
153
  *
154
154
  * @type {readonly import("../engine/field-spec.mjs").FieldSpec[]}
155
155
  */
@@ -332,7 +332,7 @@ const PRESENTATION_FIELDS = Object.freeze({
332
332
  * one vocabulary rather than two. They are declared there rather than here
333
333
  * because they are note-format knowledge — a `homepage` carries no `system`
334
334
  * block and would mean the same thing for a game system that is not SoHL — and
335
- * because a package declaring no `itemBuilders` never reaches this file (#51).
335
+ * because a package declaring no `itemBuilders` never reaches this file.
336
336
  *
337
337
  * @type {Readonly<Record<string, readonly import("../engine/field-spec.mjs").FieldSpec[]>>}
338
338
  */
@@ -13,7 +13,7 @@
13
13
 
14
14
  /**
15
15
  * Build-time Skill Base evaluation — the small part of SoHL's `SafeExpression`
16
- * needed to compile a skill's opening mastery level into the pack (#46).
16
+ * needed to compile a skill's opening mastery level into the pack.
17
17
  *
18
18
  * A skill's `skillBaseFormula` is a `SafeExpression` in the `skill.base` scope:
19
19
  * an expression over one binding, `attr` (attribute scores by shortcode), with
@@ -1,3 +1,16 @@
1
+ /**
2
+ * Whether this package compiles Foundry documents at all.
3
+ *
4
+ * The one question every Foundry-side reader asks — the manifest writer, to
5
+ * decide whether there is a package for Foundry to install, and the pack
6
+ * compilers, to decide whether there is anything to compile.
7
+ *
8
+ * @param {{packageKind: string}} config - A resolved configuration.
9
+ * @returns {boolean} Whether the package compiles Foundry documents.
10
+ */
11
+ export function compilesFoundryDocuments(config: {
12
+ packageKind: string;
13
+ }): boolean;
1
14
  /**
2
15
  * Whether this package publishes the pages its content tree compiles to.
3
16
  *
@@ -30,13 +43,27 @@ export function publishesContentPages(config: {
30
43
  */
31
44
  export function defineConfig(config: ContentBuildConfigInput): ContentBuildConfig;
32
45
  /**
33
- * The two kinds of Foundry package a content module can be built into. The
34
- * value is also the directory Foundry installs the package under, which is why
35
- * it is plural.
46
+ * What kind of package this is.
47
+ *
48
+ * `systems` and `modules` are the two Foundry answers, and the value is also
49
+ * the directory Foundry installs the package under, which is why they are
50
+ * plural. `documentation` is the answer "not a Foundry package at all": it
51
+ * publishes a site and a book from its notes, installs into no Foundry data
52
+ * directory and compiles no compendium.
36
53
  *
37
54
  * @satisfies {readonly PackageKind[]}
38
55
  */
39
- export const PACKAGE_KINDS: readonly ["systems", "modules"];
56
+ export const PACKAGE_KINDS: readonly ["systems", "modules", "documentation"];
57
+ /**
58
+ * The kind that compiles no Foundry documents.
59
+ *
60
+ * Spelled once and read wherever a pass asks whether it applies, so the
61
+ * validator, the CLI and the compile passes cannot come to disagree about what
62
+ * the value means.
63
+ *
64
+ * @type {string}
65
+ */
66
+ export const DOCUMENTATION_KIND: string;
40
67
  export namespace DEFAULT_PATHS {
41
68
  let content: "assets/content";
42
69
  let contentIndex: "build/content-index";
@@ -49,7 +76,7 @@ export namespace DEFAULT_PATHS {
49
76
  /**
50
77
  * The Foundry document types a compendium pack may hold. This is the set the
51
78
  * toolchain is able to compile a pack of; a document type Foundry supports but
52
- * this toolchain does not compile is deliberately absent (see #1501 — playlists
79
+ * this toolchain does not compile is deliberately absent (playlists
53
80
  * and roll tables are out of scope).
54
81
  *
55
82
  * @satisfies {readonly PackDocumentType[]}
@@ -68,15 +95,11 @@ export const PACK_DOCUMENT_TYPES: readonly ["Actor", "Adventure", "Item", "Journ
68
95
  * key is **refused**, at the line it was written on, with a message that says
69
96
  * the mechanism is gone rather than naming a value to correct.
70
97
  *
71
- * **What `landing` did (#204).** It named which note addressed a whole section
98
+ * **`landing` is one such key.** It named which note addressed a whole section
72
99
  * rather than a page within one — a *landing page*, which therefore had no slug
73
- * of its own. #203 retired the second of its two rules and #204 retired the
74
- * concept both rules chose between: a section is a Hugo content directory that
75
- * the note format does not carry, a page's address names no directory, and so
76
- * no note lands anything. The key outlived its mechanism by one release only
77
- * because both publishing consumers still declared the then-true
78
- * `landing: readme`, and neither breaking them over a correct statement nor
79
- * accepting the key in silence was acceptable. Neither declares it now.
100
+ * of its own. There are no sections to address: a section is a Hugo content
101
+ * directory the note format does not carry, a page's address names no
102
+ * directory, and so no note lands anything.
80
103
  *
81
104
  * @type {Readonly<Record<string, string>>}
82
105
  */
@@ -89,7 +112,7 @@ export const RETIRED_ADDRESS_KEYS: Readonly<Record<string, string>>;
89
112
  * `thalorna`, whose site is nothing but its content. It is not the package's
90
113
  * own mount point: where the package itself is served is the consuming build's
91
114
  * knowledge, held in `PACKAGE_BASE` (`engine/content-address.mjs`) and prefixed at
92
- * resolve time, so it is never recorded here (#1465).
115
+ * resolve time, so it is never recorded here.
93
116
  *
94
117
  * It is the whole scheme: `landing`, the key that named which note addressed a
95
118
  * whole section, is retired with the sections themselves — see
@@ -103,7 +126,7 @@ export const DEFAULT_ADDRESS_SCHEME: Readonly<{
103
126
  *
104
127
  * Every HeroicLands package publishes something: a top-level, human-authored
105
128
  * homepage at `https://www.heroiclands.org/<contentPackage>/` saying what the
106
- * module is, which system it needs and how to install it (#50). So there is no
129
+ * module is, which system it needs and how to install it. So there is no
107
130
  * value here meaning *no web presence at all* — homepage-only is the **floor**,
108
131
  * and the default.
109
132
  *
@@ -141,7 +164,7 @@ export const SITE_MODES: readonly ["homepage", "content"];
141
164
  * How the loader hands {@link defineConfig} the system version it resolved.
142
165
  *
143
166
  * A **Symbol**, deliberately. `stats.systemVersion` is refused from an authored
144
- * configuration (#48), but the value still has to reach here from the loader —
167
+ * configuration, but the value still has to reach here from the loader —
145
168
  * which is the half that may do I/O, and which reads a system package's version
146
169
  * out of the adjacent `package.json`. A string key would be a second spelling of
147
170
  * the refused one, forgeable from YAML and reachable by `rejectUnknownKeys`; a
@@ -156,7 +179,7 @@ export const DERIVED_SYSTEM_VERSION: symbol;
156
179
  *
157
180
  * Every HeroicLands package publishes something: a top-level, human-authored
158
181
  * homepage at `https://www.heroiclands.org/<contentPackage>/` saying what the
159
- * module is, which system it needs and how to install it (#50). So there is no
182
+ * module is, which system it needs and how to install it. So there is no
160
183
  * value here meaning *no web presence at all* — homepage-only is the **floor**,
161
184
  * and the default.
162
185
  *
@@ -183,7 +206,7 @@ export const DERIVED_SYSTEM_VERSION: symbol;
183
206
  * means what it said.
184
207
  */
185
208
  export type SiteMode = "homepage" | "content";
186
- export type PackageKind = "systems" | "modules";
209
+ export type PackageKind = "systems" | "modules" | "documentation";
187
210
  export type PackDocumentType = "Actor" | "Adventure" | "Item" | "JournalEntry" | "Macro" | "Scene";
188
211
  /**
189
212
  * One compendium pack the build compiles, named exactly as it is declared in
@@ -194,7 +217,7 @@ export type PackDocumentType = "Actor" | "Adventure" | "Item" | "JournalEntry" |
194
217
  * type** receives its document. The two are orthogonal, and both are needed
195
218
  * once a repository groups same-type documents editorially — which it may have
196
219
  * to, since a compendium UUID carries its pack name and collapsing such a
197
- * layout breaks every stored reference (#1566).
220
+ * layout breaks every stored reference.
198
221
  */
199
222
  export type PackSpec = {
200
223
  /**
@@ -304,7 +327,7 @@ export type PathsInput = {
304
327
  foreignCache?: string | undefined;
305
328
  /**
306
329
  * Where a dependency's published content
307
- * index is fetched to (#239). Inbound,
330
+ * index is fetched to. Inbound,
308
331
  * for *every* declared dependency, not
309
332
  * only those supplying a catalogue.
310
333
  */
@@ -503,7 +526,7 @@ export type AddressSchemeInput = {
503
526
  * `fields` is what makes the type documentable: a builder function says
504
527
  * nothing about the vocabulary it consumes, so a consumer that declares its
505
528
  * fields can generate its own authoring reference and check its own notes,
506
- * while one that does not is simply undocumented rather than broken (#22).
529
+ * while one that does not is simply undocumented rather than broken.
507
530
  */
508
531
  export type ItemBuilderEntry = ((fm: object) => object) | {
509
532
  system: (fm: object) => object;
@@ -511,7 +534,7 @@ export type ItemBuilderEntry = ((fm: object) => object) | {
511
534
  fields?: readonly object[];
512
535
  };
513
536
  /**
514
- * One **registry** of a declared set, and the system it belongs to (#58).
537
+ * One **registry** of a declared set, and the system it belongs to.
515
538
  *
516
539
  * A repository shipping content for two systems declares one of these per
517
540
  * system: the accepted type vocabulary is their union, and a type both declare
@@ -546,17 +569,27 @@ export type ContentBuildConfigInput = {
546
569
  contentPackage: string;
547
570
  /**
548
571
  * Foundry package id, as it appears in
549
- * `system.json` / `module.json`.
572
+ * `system.json` / `module.json`.
573
+ * Refused by a `documentation`
574
+ * package, which ships no Foundry
575
+ * package.
550
576
  */
551
- foundryPackage: string;
577
+ foundryPackage?: string | undefined;
552
578
  /**
553
- * Whether the package is a system or a module.
579
+ * Whether the package is a system, a
580
+ * module, or documentation — the kind
581
+ * that publishes a site and a book
582
+ * while compiling nothing.
554
583
  */
555
584
  packageKind: PackageKind;
556
585
  /**
557
- * Identity stamped into every document's `_stats`.
586
+ * Identity stamped into every
587
+ * document's `_stats`. Required of a
588
+ * package that compiles documents, and
589
+ * refused by a `documentation` one,
590
+ * which compiles none.
558
591
  */
559
- stats: StatsSpec;
592
+ stats?: StatsSpec | undefined;
560
593
  /**
561
594
  * The consumer's
562
595
  * item-type registry: each content `type`
@@ -570,7 +603,7 @@ export type ContentBuildConfigInput = {
570
603
  * two systems declares a **list** of
571
604
  * `{ system, builders }` registries
572
605
  * instead, and the accepted type
573
- * vocabulary is their union (#58).
606
+ * vocabulary is their union.
574
607
  */
575
608
  itemBuilders?: Record<string, ItemBuilderEntry> | readonly ItemRegistrySpec[] | undefined;
576
609
  /**
@@ -579,8 +612,7 @@ export type ContentBuildConfigInput = {
579
612
  * the pack it belongs in with its
580
613
  * `pack:` frontmatter, and one pack of
581
614
  * the type is marked `default: true` to
582
- * receive the notes that name none
583
- * (#1566).
615
+ * receive the notes that name none.
584
616
  */
585
617
  packs: PackSpec[];
586
618
  /**
@@ -631,15 +663,29 @@ export type ContentBuildConfigInput = {
631
663
  export type ContentBuildConfig = {
632
664
  rootDir: string;
633
665
  contentPackage: string;
634
- foundryPackage: string;
666
+ /**
667
+ * `null` for a `documentation`
668
+ * package, which ships no Foundry package.
669
+ */
670
+ foundryPackage: string | null;
635
671
  packageKind: PackageKind;
636
672
  /**
637
- * Derived: the served Foundry asset root,
638
- * `<packageKind>/<foundryPackage>/assets`.
673
+ * Derived, and **conditional**: the served
674
+ * Foundry asset root,
675
+ * `<packageKind>/<foundryPackage>/assets`,
676
+ * for a package Foundry installs — and
677
+ * `null` for a `documentation` package,
678
+ * which Foundry serves no files for. See
679
+ * {@link module :engine/helpers.resolveImg},
680
+ * the one reader of it.
639
681
  */
640
- assetRoot: string;
682
+ assetRoot: string | null;
641
683
  paths: Readonly<ResolvedPaths>;
642
- stats: Readonly<StatsSpec>;
684
+ /**
685
+ * `null` for a `documentation`
686
+ * package, which stamps no `_stats`.
687
+ */
688
+ stats: Readonly<StatsSpec> | null;
643
689
  /**
644
690
  * Derived: the
645
691
  * `system` builder of each entry, whichever
@@ -658,14 +704,13 @@ export type ContentBuildConfig = {
658
704
  * the frontmatter fields each entry
659
705
  * declared. Sparse, like `itemArt` — a type
660
706
  * absent here compiles normally and is
661
- * simply undocumented (#22).
707
+ * simply undocumented.
662
708
  */
663
709
  itemFields: Readonly<Record<string, readonly object[]>>;
664
710
  /**
665
711
  * Derived: the same builders, kept per
666
712
  * declaring system. `{}` for the single
667
- * registry form, which names no system
668
- * (#58).
713
+ * registry form, which names no system.
669
714
  */
670
715
  itemBuildersBySystem: Readonly<Record<string, Readonly<Record<string, Function>>>>;
671
716
  /**
@@ -688,7 +733,7 @@ export type ContentBuildConfig = {
688
733
  * {@link ContentBuildConfigInput.itemBuilders},
689
734
  * unioned across every declared registry, so
690
735
  * the accepted item types and the builder
691
- * tables are one list (#1504).
736
+ * tables are one list.
692
737
  */
693
738
  itemTypes: ReadonlySet<string>;
694
739
  /**
@@ -700,6 +745,13 @@ export type ContentBuildConfig = {
700
745
  */
701
746
  docEntryTypes: ReadonlySet<string>;
702
747
  skipDirectories: readonly string[];
748
+ /**
749
+ * The
750
+ * fonts this package ships and the names it
751
+ * draws from them; empty when it declares
752
+ * none.
753
+ */
754
+ icons: import("./engine/content-icons.mjs").IconRegistry;
703
755
  packs: readonly Readonly<ResolvedPackSpec>[];
704
756
  /**
705
757
  * Derived: every pack directory
package/types/e2e.d.mts CHANGED
@@ -220,7 +220,7 @@ export function findExecutable(name: string, { cwd, env }?: {
220
220
  *
221
221
  * Asked twice per run, and the second asking is the point: an install running
222
222
  * alongside the suite can take the runner out from under it mid-flight, which
223
- * is precisely the failure that reported itself as green (#153).
223
+ * is precisely the failure that reported itself as green.
224
224
  *
225
225
  * @param {object} opts
226
226
  * @param {readonly string[]} opts.command - The program and its arguments.
@@ -268,7 +268,7 @@ export function freshResults({ paths, since, cwd }: {
268
268
  * exit status on its own cannot call a run green, because every way of stopping
269
269
  * a runner before it starts — a corrupt install, a missing browser, a killed
270
270
  * process, the concurrent `npm ci` that surfaced this — produces a run that
271
- * executed nothing, and nothing is not a pass (#153).
271
+ * executed nothing, and nothing is not a pass.
272
272
  *
273
273
  * This can only ever make a verdict worse. A suite that failed keeps its own
274
274
  * status; a suite that passed on no evidence loses the claim. Never the other
@@ -297,7 +297,7 @@ export function suiteVerdict({ status, vanished, declared, fresh }: {
297
297
  * `MODULE_NOT_FOUND` naming nothing relevant.
298
298
  *
299
299
  * The suite is bracketed by checks rather than trusted on its exit status,
300
- * because a run that never started used to report as green (#153):
300
+ * because a run that never started used to report as green:
301
301
  *
302
302
  * - **Before.** Every executable the command needs is resolved, and a missing
303
303
  * one is an error naming it — rather than a container stood up, a world