@heroiclands/package-build 20.3.0 → 20.4.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 (178) hide show
  1. package/CHANGELOG.md +97 -0
  2. package/CONTENT.md +111 -92
  3. package/README.md +2 -9
  4. package/bin/content-build.mjs +70 -61
  5. package/bin/package-build.mjs +2 -2
  6. package/config.mjs +6 -8
  7. package/content-config.mjs +187 -64
  8. package/docs/content-format.md +57 -68
  9. package/e2e.mjs +4 -4
  10. package/engine/actor-compiler.mjs +36 -36
  11. package/engine/address-charset.mjs +6 -6
  12. package/engine/address-diff.mjs +12 -11
  13. package/engine/anchored-sections.mjs +1 -1
  14. package/engine/anchors.mjs +1 -1
  15. package/engine/base-compiler.mjs +36 -38
  16. package/engine/bundle-notes.mjs +4 -4
  17. package/engine/bundles.mjs +8 -8
  18. package/engine/code-fences.mjs +3 -3
  19. package/engine/compendiums.mjs +8 -8
  20. package/engine/compile-corpus.mjs +4 -5
  21. package/engine/content-address.mjs +15 -15
  22. package/engine/content-charset.mjs +1 -1
  23. package/engine/content-format-check.mjs +24 -25
  24. package/engine/content-format.mjs +8 -8
  25. package/engine/content-html.mjs +202 -0
  26. package/engine/content-icons.mjs +225 -98
  27. package/engine/content-index.mjs +17 -17
  28. package/engine/content-links.mjs +30 -30
  29. package/engine/content-lint.mjs +20 -24
  30. package/engine/content-package.mjs +7 -9
  31. package/engine/content-slug.mjs +2 -2
  32. package/engine/content-tables.mjs +7 -8
  33. package/engine/content-tree.mjs +1 -1
  34. package/engine/derived-fields.mjs +174 -0
  35. package/engine/diagnostics.mjs +3 -4
  36. package/engine/document-subtypes.mjs +6 -7
  37. package/engine/field-reference.mjs +8 -8
  38. package/engine/field-spec.mjs +29 -30
  39. package/engine/folder-notes.mjs +14 -14
  40. package/engine/foreign-catalog.mjs +8 -9
  41. package/engine/foundry-entries.mjs +19 -20
  42. package/engine/frontmatter-lint.mjs +64 -69
  43. package/engine/frontmatter.mjs +11 -11
  44. package/engine/generate.mjs +39 -39
  45. package/engine/helpers.mjs +63 -54
  46. package/engine/homepage.mjs +20 -22
  47. package/engine/ids.mjs +15 -15
  48. package/engine/index-records.mjs +4 -4
  49. package/engine/index.mjs +14 -8
  50. package/engine/item-compiler.mjs +54 -29
  51. package/engine/item-docs.mjs +11 -13
  52. package/engine/item-registry.mjs +13 -13
  53. package/engine/journals.mjs +15 -15
  54. package/engine/macros.mjs +3 -3
  55. package/engine/map-notes.mjs +6 -6
  56. package/engine/metadata-index.mjs +5 -5
  57. package/engine/note-claims.mjs +36 -34
  58. package/engine/note-ids.mjs +5 -5
  59. package/engine/note-package.mjs +4 -4
  60. package/engine/note-renames.mjs +3 -3
  61. package/engine/note-schemas.mjs +3 -3
  62. package/engine/note-vocabulary.mjs +27 -26
  63. package/engine/pack-config.mjs +13 -13
  64. package/engine/pack-router.mjs +0 -0
  65. package/engine/prose-config.mjs +9 -9
  66. package/engine/prose-lint.mjs +12 -13
  67. package/engine/region-events.mjs +2 -2
  68. package/engine/retired-fields.mjs +24 -22
  69. package/engine/runtime-only-fields.mjs +1 -1
  70. package/engine/scene-levels.mjs +1 -1
  71. package/engine/scenes.mjs +19 -20
  72. package/engine/schema-check.mjs +20 -20
  73. package/engine/schema-extract.mjs +1 -1
  74. package/engine/site-build.mjs +45 -47
  75. package/engine/site-index.mjs +17 -18
  76. package/engine/sql-tables.mjs +21 -13
  77. package/engine/subtype-registry.mjs +5 -4
  78. package/engine/system-block.mjs +18 -19
  79. package/engine/systems.mjs +2 -2
  80. package/engine/web-wikilinks.mjs +35 -35
  81. package/engine/wikilink-syntax.mjs +16 -16
  82. package/engine/wikilinks.mjs +39 -39
  83. package/hm3/actors.mjs +22 -22
  84. package/hm3/document-subtypes.mjs +5 -5
  85. package/hm3/index.mjs +2 -2
  86. package/hm3/item-builders.mjs +1 -1
  87. package/hm3/item-fields.mjs +3 -3
  88. package/hm3/items.mjs +20 -8
  89. package/hm3/template-priority.mjs +2 -2
  90. package/lang.mjs +3 -3
  91. package/manifest.mjs +17 -20
  92. package/package.json +1 -2
  93. package/release.mjs +3 -3
  94. package/sohl/actors.mjs +28 -28
  95. package/sohl/affiliation-standings.mjs +2 -2
  96. package/sohl/being-info.mjs +5 -5
  97. package/sohl/default-item-art.mjs +5 -5
  98. package/sohl/document-subtypes.mjs +5 -5
  99. package/sohl/index.mjs +3 -3
  100. package/sohl/item-builders.mjs +7 -7
  101. package/sohl/item-fields.mjs +8 -9
  102. package/sohl/items.mjs +20 -6
  103. package/sohl/kb-passes.mjs +5 -5
  104. package/sohl/note-schemas.mjs +7 -7
  105. package/sohl/skill-base.mjs +1 -1
  106. package/types/content-config.d.mts +25 -24
  107. package/types/e2e.d.mts +3 -3
  108. package/types/engine/actor-compiler.d.mts +14 -13
  109. package/types/engine/address-charset.d.mts +6 -6
  110. package/types/engine/address-diff.d.mts +3 -3
  111. package/types/engine/base-compiler.d.mts +17 -19
  112. package/types/engine/bundle-notes.d.mts +3 -3
  113. package/types/engine/bundles.d.mts +1 -1
  114. package/types/engine/code-fences.d.mts +3 -3
  115. package/types/engine/compendiums.d.mts +3 -3
  116. package/types/engine/compile-corpus.d.mts +1 -1
  117. package/types/engine/content-address.d.mts +11 -11
  118. package/types/engine/content-format-check.d.mts +6 -6
  119. package/types/engine/content-format.d.mts +2 -2
  120. package/types/engine/content-html.d.mts +78 -0
  121. package/types/engine/content-icons.d.mts +150 -62
  122. package/types/engine/content-index.d.mts +2 -2
  123. package/types/engine/content-links.d.mts +8 -8
  124. package/types/engine/content-lint.d.mts +2 -2
  125. package/types/engine/content-package.d.mts +6 -8
  126. package/types/engine/derived-fields.d.mts +101 -0
  127. package/types/engine/diagnostics.d.mts +2 -2
  128. package/types/engine/document-subtypes.d.mts +3 -3
  129. package/types/engine/field-spec.d.mts +37 -39
  130. package/types/engine/folder-notes.d.mts +4 -5
  131. package/types/engine/foreign-catalog.d.mts +3 -3
  132. package/types/engine/foundry-entries.d.mts +6 -7
  133. package/types/engine/frontmatter-lint.d.mts +7 -7
  134. package/types/engine/frontmatter.d.mts +7 -7
  135. package/types/engine/generate.d.mts +7 -7
  136. package/types/engine/helpers.d.mts +25 -27
  137. package/types/engine/homepage.d.mts +16 -18
  138. package/types/engine/ids.d.mts +13 -13
  139. package/types/engine/index-records.d.mts +3 -3
  140. package/types/engine/index.d.mts +2 -0
  141. package/types/engine/item-compiler.d.mts +21 -5
  142. package/types/engine/item-docs.d.mts +2 -2
  143. package/types/engine/item-registry.d.mts +6 -6
  144. package/types/engine/journals.d.mts +3 -3
  145. package/types/engine/map-notes.d.mts +2 -2
  146. package/types/engine/metadata-index.d.mts +4 -4
  147. package/types/engine/note-claims.d.mts +16 -15
  148. package/types/engine/note-ids.d.mts +4 -4
  149. package/types/engine/note-package.d.mts +1 -1
  150. package/types/engine/note-renames.d.mts +3 -3
  151. package/types/engine/note-vocabulary.d.mts +9 -8
  152. package/types/engine/pack-config.d.mts +3 -3
  153. package/types/engine/pack-router.d.mts +1 -1
  154. package/types/engine/prose-config.d.mts +9 -9
  155. package/types/engine/prose-lint.d.mts +3 -4
  156. package/types/engine/region-events.d.mts +2 -2
  157. package/types/engine/retired-fields.d.mts +10 -9
  158. package/types/engine/schema-check.d.mts +11 -11
  159. package/types/engine/site-build.d.mts +18 -19
  160. package/types/engine/site-index.d.mts +3 -4
  161. package/types/engine/sql-tables.d.mts +11 -5
  162. package/types/engine/subtype-registry.d.mts +3 -3
  163. package/types/engine/system-block.d.mts +3 -3
  164. package/types/engine/web-wikilinks.d.mts +7 -7
  165. package/types/engine/wikilink-syntax.d.mts +17 -17
  166. package/types/engine/wikilinks.d.mts +13 -12
  167. package/types/hm3/actors.d.mts +1 -1
  168. package/types/hm3/document-subtypes.d.mts +1 -1
  169. package/types/hm3/items.d.mts +1 -2
  170. package/types/hm3/template-priority.d.mts +1 -1
  171. package/types/manifest.d.mts +8 -8
  172. package/types/sohl/actors.d.mts +2 -2
  173. package/types/sohl/affiliation-standings.d.mts +2 -2
  174. package/types/sohl/being-info.d.mts +2 -2
  175. package/types/sohl/document-subtypes.d.mts +1 -1
  176. package/types/sohl/items.d.mts +1 -2
  177. package/types/sohl/note-schemas.d.mts +1 -1
  178. package/MIGRATING.md +0 -608
@@ -20,7 +20,7 @@
20
20
  * configuration, directory creation, reading the shipped Foundry package
21
21
  * manifest, and the process exit code. The library itself is import-safe, so a
22
22
  * consuming repository's build — or a test — can call it without any of this
23
- * happening (#1507).
23
+ * happening.
24
24
  *
25
25
  * The side effects that need *configuration* live inside the command handler,
26
26
  * not at module scope, so `--version` and `--help` answer in a directory that
@@ -29,7 +29,7 @@
29
29
  * either is missing.
30
30
  *
31
31
  * Every path and pack name it hands the library comes from the consuming
32
- * repository's `package-build.config.yaml` (#1508), located by
32
+ * repository's `package-build.config.yaml`, located by
33
33
  * `engine/pack-config.mjs`; nothing about any one repository's layout is
34
34
  * written here.
35
35
  *
@@ -80,6 +80,7 @@ import {
80
80
  import { renderItemFieldReference } from "../engine/field-reference.mjs";
81
81
  import { lintContentTree } from "../engine/content-lint.mjs";
82
82
  import { lintContentCharset } from "../engine/content-charset.mjs";
83
+ import { lintContentHtml } from "../engine/content-html.mjs";
83
84
  import { lintContentIcons } from "../engine/content-icons.mjs";
84
85
  import { declaredSystems, lintFrontmatter, systemBlocksFor } from "../engine/frontmatter-lint.mjs";
85
86
  import { loadContentFormat } from "../engine/content-format.mjs";
@@ -96,14 +97,14 @@ import {
96
97
  } from "../engine/schema-check.mjs";
97
98
  // The one vocabulary, loaded whole. Every content project authors the full type
98
99
  // set — an adventure module ships skills, beings and magic swords — so no
99
- // consumer gets a subset (#19, #20).
100
+ // consumer gets a subset.
100
101
  import { NOTE_SCHEMAS } from "../sohl/note-schemas.mjs";
101
102
  // The shipped declarations, so this repository can check its own specification
102
- // against them without standing up a consumer's configuration (#136).
103
+ // against them without standing up a consumer's configuration.
103
104
  import { ITEM_FIELDS } from "../sohl/item-fields.mjs";
104
105
  import { HM3_ITEM_FIELDS } from "../hm3/item-fields.mjs";
105
106
  // The engine's own types, merged under the registry's so the vocabulary stands
106
- // in a package that configures no `itemBuilders` at all (#51).
107
+ // in a package that configures no `itemBuilders` at all.
107
108
  import { ENGINE_NOTE_SCHEMAS } from "../engine/note-schemas.mjs";
108
109
  import { schemaSubtypeOf } from "../engine/subtype-registry.mjs";
109
110
  import { NOTE_VOCABULARY } from "../engine/note-vocabulary.mjs";
@@ -122,7 +123,7 @@ import {
122
123
  } from "../engine/site-build.mjs";
123
124
  import { auditLinks, buildLinkIndex, walkReachability } from "../engine/content-links.mjs";
124
125
  import { prepareTreeSqlTables } from "../engine/sql-tables.mjs";
125
- // The one place a link finding is worded, shared with both builds (#184).
126
+ // The one place a link finding is worded, shared with both builds.
126
127
  import { linkFindingMessage } from "../engine/wikilink-syntax.mjs";
127
128
  import {
128
129
  emitDiagnostic,
@@ -144,7 +145,7 @@ import { emittedArtFor, itemPackJsonDirs } from "../engine/generate.mjs";
144
145
  * The packs `unpack` extracts.
145
146
  *
146
147
  * From the configuration's own pack list, which is where the build already
147
- * knows them. It used to come out of the shipped manifest — a second
148
+ * knows them. It does not come out of the shipped manifest — a second
148
149
  * declaration of the same list, in a second format, with nothing checking that
149
150
  * the two agreed. The manifest is generated from this list now
150
151
  * (package-build#9), so reading it back would be a round trip through an
@@ -166,7 +167,7 @@ function configuredPacks() {
166
167
  * defaults to the *nearest* `package.json` walking up from the working
167
168
  * directory — inside a consuming repository that is the consumer's manifest, so
168
169
  * `content-build --version` reported the consumer's version instead of the
169
- * toolchain's (#1557).
170
+ * toolchain's.
170
171
  *
171
172
  * @returns {string} The `version` field of this package's manifest.
172
173
  */
@@ -191,8 +192,8 @@ prefix.apply(log, {
191
192
  /**
192
193
  * Report a command's failure.
193
194
  *
194
- * A configuration error carries its own `file:line:column: error: ` locator
195
- * (#95), and `loglevel`'s `[timestamp] [ERROR]:` prefix occupies exactly the
195
+ * A configuration error carries its own `file:line:column: error: ` locator,
196
+ * and `loglevel`'s `[timestamp] [ERROR]:` prefix occupies exactly the
196
197
  * position a parser reads the path from — so a located failure is printed
197
198
  * unprefixed, as `emitDiagnostic` prints every other finding. Everything else
198
199
  * is ordinary prose and keeps the log line it always had.
@@ -249,10 +250,10 @@ const argv = yargs(hideBin(process.argv))
249
250
  .version(ownVersion())
250
251
  .help()
251
252
  .alias("help", "h")
252
- // Every invocation this CLI accepts must be one it performs (#57). yargs
253
+ // Every invocation this CLI accepts must be one it performs. yargs
253
254
  // gives neither guarantee by default: without `demandCommand` a bare
254
255
  // `content-build` exits 0 in silence, and without `strict` an unknown
255
- // command or option is ignored rather than reported. Both used to read as
256
+ // command or option is ignored rather than reported. Both would read as
256
257
  // success from a `run-s` chain, so a typo in a build script passed the step
257
258
  // it was meant to run. The sibling toolchain `@heroiclands/package-build`
258
259
  // opts into the same two.
@@ -264,7 +265,7 @@ const argv = yargs(hideBin(process.argv))
264
265
  *
265
266
  * The page is generated from the `fields` each `itemBuilders` entry declares,
266
267
  * so every consuming repository documents *its own* registry with the same
267
- * command (#22).
268
+ * command.
268
269
  *
269
270
  * **The framing comes from configuration**, because the tables are the only
270
271
  * part that is the same everywhere. A repository's `docs.itemFields` says what
@@ -288,10 +289,10 @@ function docsCommand() {
288
289
  command: "docs <action>",
289
290
  describe: "Generate documentation from the configured registries",
290
291
  builder: (yargs) => {
291
- // Required and honoured. It used to be optional and never read:
292
+ // Required and honoured, not optional and unread:
292
293
  // the handler rendered the item-field reference whatever it was
293
294
  // given, so the positional constrained what could be typed and
294
- // selected nothing (#57).
295
+ // selected nothing.
295
296
  yargs.positional("action", {
296
297
  describe: "The document to render.",
297
298
  type: "string",
@@ -380,7 +381,7 @@ function docsCommand() {
380
381
  }
381
382
 
382
383
  /**
383
- * `content-build content-format` — check the format specification itself (#130).
384
+ * `content-build content-format` — check the format specification itself.
384
385
  *
385
386
  * Two checks, because the specification makes claims about two different
386
387
  * worlds, and they fail for different reasons and at different times:
@@ -389,10 +390,9 @@ function docsCommand() {
389
390
  * naming system's published `schema.json`. A failure means the specification
390
391
  * and the system disagree, which is a defect in one of the two.
391
392
  * - `fields` compares the per-type tables against the field declarations that
392
- * compile them, so the hand-written half cannot drift from the generated one
393
- * (#136).
393
+ * compile them, so the hand-written half cannot drift from the generated one.
394
394
  * - `notes` measures a content tree against the vocabulary the document
395
- * declares per type. During #127 it is the migration's progress bar rather
395
+ * declares per type. During the migration it is the progress bar rather
396
396
  * than a gate, so it **reports** by default and `--strict` makes it fatal —
397
397
  * turned on one class at a time as each slice lands.
398
398
  *
@@ -535,7 +535,7 @@ function declarationsFrom(argv) {
535
535
  * The specification hand-writes a `data` table under most of its type sections,
536
536
  * covering ground {@link module:engine/field-reference} already generates from
537
537
  * the `fields` on each `itemBuilders` entry — the duplication that module exists
538
- * to prevent, one document over (#136).
538
+ * to prevent, one document over.
539
539
  *
540
540
  * **Checked, not merged.** The document's vocabulary spans note types that
541
541
  * produce Scenes, Macros and JournalEntries, which no item registry covers, so
@@ -566,7 +566,7 @@ function contentFormatFieldsCommand() {
566
566
  });
567
567
  yargs.option("coverage", {
568
568
  describe:
569
- "List, per type, the fields only one side names. They are not findings — the two vocabularies differ by design until #127 lands.",
569
+ "List, per type, the fields only one side names. They are not findings — the two vocabularies differ by design until the corpus moves.",
570
570
  type: "boolean",
571
571
  default: false,
572
572
  });
@@ -627,8 +627,8 @@ function contentFormatFieldsCommand() {
627
627
  *
628
628
  * **A report, not a gate.** Every authored note predates the format, so a
629
629
  * failing check would be red in every repository from the day it lands and
630
- * would stay red for the length of #127 — which is a check nobody can act on.
631
- * `--strict` raises the findings to errors, and #127 turns it on slice by
630
+ * would stay red for the length of the migration — which is a check nobody
631
+ * can act on. `--strict` raises the findings to errors, turned on slice by
632
632
  * slice as each class of finding reaches zero.
633
633
  *
634
634
  * @returns {object} The yargs command module.
@@ -649,7 +649,7 @@ function contentFormatNotesCommand() {
649
649
  });
650
650
  yargs.option("strict", {
651
651
  describe:
652
- "Fail on the findings instead of reporting them. Turned on per slice of #127, as each class reaches zero.",
652
+ "Fail on the findings instead of reporting them. Turned on per slice, as each class reaches zero.",
653
653
  type: "boolean",
654
654
  default: false,
655
655
  });
@@ -660,7 +660,7 @@ function contentFormatNotesCommand() {
660
660
  const root = argv.root ?? config.paths.content;
661
661
  const format = specFrom(argv);
662
662
 
663
- // The corpus from the index, like every other check (#243).
663
+ // The corpus from the index, like every other check.
664
664
  // A report measuring the tree against the declared vocabulary
665
665
  // has to be looking at the same tree the compile will, or its
666
666
  // counts describe a corpus nobody builds.
@@ -745,14 +745,14 @@ function lintCommand() {
745
745
  const root = argv.root ?? config.paths.content;
746
746
 
747
747
  // The corpus, enumerated once for this command and handed to
748
- // every pass below, rather than derived again by each (#243).
748
+ // every pass below, rather than derived again by each.
749
749
  // The address lint reads it, the `sql` tables select over it
750
750
  // and the link index is built from it, so no two findings this
751
751
  // command reports can be drawn from different ideas of which
752
752
  // files the content is.
753
753
  //
754
754
  // A note the index cannot record is reported like any other
755
- // finding rather than thrown (#243): one malformed note must
755
+ // finding rather than thrown: one malformed note must
756
756
  // not take every other finding in the tree with it, and the
757
757
  // reader needs a line to open, not a stack.
758
758
  const corpusProblems = [];
@@ -768,7 +768,7 @@ function lintCommand() {
768
768
  // would call the tree clean while silently omitting a note.
769
769
  if (corpusProblems.length) process.exitCode = 1;
770
770
 
771
- // The package is passed for the homepage rule (#52), which
771
+ // The package is passed for the homepage rule, which
772
772
  // names the address a tree with no front page fails to serve.
773
773
  const addresses = lintContentTree(root, {
774
774
  contentPackage: config.contentPackage,
@@ -790,7 +790,7 @@ function lintCommand() {
790
790
  }),
791
791
  });
792
792
  // Which system blocks this tree carries, derived from what it
793
- // declares it ships for (#58). Read before the lint so the
793
+ // declares it ships for. Read before the lint so the
794
794
  // systems it will *not* check can be said out loud below.
795
795
  // `schemaSystem` names whose vocabulary the `schemas` below
796
796
  // are. They are `sohl/note-schemas.mjs`, imported here
@@ -823,18 +823,18 @@ function lintCommand() {
823
823
  }
824
824
  const frontmatter = lintFrontmatter(index, {
825
825
  schemas: { ...ENGINE_NOTE_SCHEMAS, ...NOTE_SCHEMAS },
826
- // The closed frontmatter regions (#128). Passed in rather
826
+ // The closed frontmatter regions. Passed in rather
827
827
  // than reached for, so the linter stays a checker of
828
828
  // whatever it is handed and this stays the one place that
829
829
  // decides which vocabulary a tree is held to.
830
830
  vocabulary: NOTE_VOCABULARY,
831
831
  // The pack names, for a `data:` field keyed by pack — a
832
- // folder's `parent` is one (#288). Companions included:
832
+ // folder's `parent` is one. Companions included:
833
833
  // the compile asks the map for whichever pack it is
834
834
  // writing, and a companion is a pack it writes.
835
835
  packs: config.packDirectories,
836
836
  // What art each type actually reaches its document through,
837
- // asked of the passes rather than listed here (#349). This
837
+ // asked of the passes rather than listed here. This
838
838
  // is the one place that decides what a tree is held to, so
839
839
  // it is where the derivation is handed over — the linter
840
840
  // states no list of iconless types of its own.
@@ -850,7 +850,7 @@ function lintCommand() {
850
850
  });
851
851
 
852
852
  // What the builders emit, against what the receiving system
853
- // declares (#60). Reported here rather than at compile: it is
853
+ // declares. Reported here rather than at compile: it is
854
854
  // a property of the *declarations*, not of any one note, so it
855
855
  // is the same answer for every document and belongs where a
856
856
  // reader is already being told about the vocabulary.
@@ -900,7 +900,7 @@ function lintCommand() {
900
900
  artifact: schema.artifact,
901
901
  // A schema is keyed by document subtype and a field
902
902
  // declaration by note type. Those were one string until
903
- // #78 renamed three of them, and joining them by name
903
+ // Three are renamed, and joining them by name
904
904
  // after that would drop `armorgear`'s findings without
905
905
  // saying so — the seam exists for exactly this.
906
906
  subtypeOf: (type) => schemaSubtypeOf(config.stats?.systemId, type),
@@ -924,25 +924,33 @@ function lintCommand() {
924
924
  }
925
925
  }
926
926
 
927
- // The charset the tree is held to, so a book can pick its face
928
- // (#377). Run over the raw files rather than the parsed notes:
927
+ // The charset the tree is held to, so a book can pick its face.
928
+ // Run over the raw files rather than the parsed notes:
929
929
  // the subject is every character authored, including the ones
930
930
  // that stopped a note parsing at all.
931
931
  const charset = lintContentCharset(root, {
932
932
  skipDirectories: config.skipDirectories,
933
933
  });
934
934
 
935
- // The names the charset check leaves room for (#378). An icon
935
+ // The names the charset check leaves room for. An icon
936
936
  // the registry does not declare renders as its own literal
937
937
  // text, which is visible but easy to publish, so it is reported
938
938
  // here rather than left for a reader to notice.
939
939
  //
940
- // The shipped registry, with no per-package override yet: an
941
- // `icons:` configuration key is a change to the configuration
942
- // contract and belongs with its own validation, rather than
943
- // being read here before anything declares it.
940
+ // The configured registry the shipped table with this
941
+ // package's own entries merged over it, which is where a
942
+ // Game-Icons glyph becomes a name a note may write.
944
943
  const icons = lintContentIcons(root, {
945
944
  skipDirectories: config.skipDirectories,
945
+ registry: config.icons,
946
+ });
947
+
948
+ // The markup the charset check cannot see. Class names are
949
+ // ASCII, so a page of `<i class="fa-…">` is allowlist-clean and
950
+ // still unrenderable in a book — the character rule one level
951
+ // up.
952
+ const html = lintContentHtml(root, {
953
+ skipDirectories: config.skipDirectories,
946
954
  });
947
955
 
948
956
  const findings = [
@@ -951,9 +959,10 @@ function lintCommand() {
951
959
  ...schemaFindings,
952
960
  ...charset.findings,
953
961
  ...icons.findings,
962
+ ...html.findings,
954
963
  ];
955
964
  // Only an **error** fails the run. Every finding was an error
956
- // until #142, so this changed nothing on the day it landed
965
+ // by then, so this changes nothing on its own
957
966
  // but a field retired in favour of another is reported while
958
967
  // both spellings still compile, and failing a build over a note
959
968
  // that produces the correct document would red a tree that has
@@ -1002,7 +1011,7 @@ function lintCommand() {
1002
1011
  * reports, as warnings, each shared convention the repository's own
1003
1012
  * configuration resolves differently — or that it declares no configuration at
1004
1013
  * all, which guarantees an editor and a bare `npx prettier` disagree with this
1005
- * command about the same tree (#133). Nothing there fails the run: the point is
1014
+ * command about the same tree. Nothing there fails the run: the point is
1006
1015
  * that a local choice is deliberate rather than silent.
1007
1016
  *
1008
1017
  * @returns {object} The yargs command module.
@@ -1040,7 +1049,7 @@ function formatCommand() {
1040
1049
  const root = process.cwd();
1041
1050
  // Before the per-file report, because it is the context for it:
1042
1051
  // which rules this run applied, and whether anything else in
1043
- // the repository applies the same ones (#133). Warnings only —
1052
+ // the repository applies the same ones. Warnings only —
1044
1053
  // a consumer's config wins by design, so none of this touches
1045
1054
  // the exit code.
1046
1055
  const conventions = await checkPrettierConventions(root);
@@ -1065,7 +1074,7 @@ function formatCommand() {
1065
1074
  // `--write` collects findings too — a file Prettier cannot
1066
1075
  // parse, or one that will not format to a fixpoint — and
1067
1076
  // used to discard them, so a run that had left files
1068
- // unformatted still reported success and exited 0 (#125).
1077
+ // unformatted still reported success and exited 0.
1069
1078
  for (const finding of findings) emitDiagnostic(finding);
1070
1079
  if (findings.length) {
1071
1080
  log.error(
@@ -1166,7 +1175,7 @@ function linksCommand() {
1166
1175
  const contentBase = argv.root ?? config.paths.content;
1167
1176
 
1168
1177
  const scope = { skipDirectories: config.skipDirectories };
1169
- // Enumerated once and shared, as in `lint` (#243), and a note
1178
+ // Enumerated once and shared, as in `lint`, and a note
1170
1179
  // it cannot record is reported rather than thrown.
1171
1180
  const corpusProblems = [];
1172
1181
  const records = indexRecordsFor({
@@ -1238,8 +1247,8 @@ function linksCommand() {
1238
1247
  `heading in ${d.dest.rel} declares`,
1239
1248
  });
1240
1249
  }
1241
- // Every link is an address (#180) and every address must
1242
- // resolve (#184), so all of these are errors — but they read
1250
+ // Every link is an address and every address must
1251
+ // resolve, so all of these are errors — but they read
1243
1252
  // differently because the corrections differ. The wording comes
1244
1253
  // from the shared table, so the checker cannot describe a
1245
1254
  // defect differently from the build that also refuses it.
@@ -1265,7 +1274,7 @@ function linksCommand() {
1265
1274
  // The package homepage. Its addresses are markdown links and
1266
1275
  // `landing:` url/href fields rather than wikilinks — it is
1267
1276
  // published verbatim, so nothing resolves a wikilink on it —
1268
- // and until #54 nothing looked at them at all.
1277
+ // and nothing else looks at them.
1269
1278
  for (const h of homepageLinks) {
1270
1279
  emitDiagnostic({
1271
1280
  file: h.note.file,
@@ -1307,7 +1316,7 @@ function linksCommand() {
1307
1316
  *
1308
1317
  * Every build already walks the tree and parses every note's frontmatter, then
1309
1318
  * throws the result away, so nothing outside a build can ask a question about
1310
- * the content (#224). This publishes that walk as JSON Lines: one record per
1319
+ * the content. This publishes that walk as JSON Lines: one record per
1311
1320
  * note, carrying the whole frontmatter plus the note's place in the tree.
1312
1321
  *
1313
1322
  * It is a command of its own rather than only a build step because the point of
@@ -1358,7 +1367,7 @@ function contentIndexCommand() {
1358
1367
  * `content-build site` — publish the content tree as a website.
1359
1368
  *
1360
1369
  * The sibling of `package compile`: the same tree, rendered as pages instead of
1361
- * compiled into packs (#63). Everything a consumer used to write for itself —
1370
+ * compiled into packs. Everything a consumer would otherwise write for itself —
1362
1371
  * the walk, the address derivation, the address index, table expansion,
1363
1372
  * wikilink resolution, code-fence protection, the foreign-manifest merge and
1364
1373
  * the section-landing backfill — happens here, from configuration.
@@ -1394,7 +1403,7 @@ function siteCommand() {
1394
1403
  // First, because it is decided before the tree is walked and
1395
1404
  // before the output is cleared: a package with no front page,
1396
1405
  // or two competing for it, has nothing to say about its pages
1397
- // yet (#52).
1406
+ // yet.
1398
1407
  for (const f of gates.homepages) emitDiagnostic(f);
1399
1408
  for (const f of gates.frontmatterLinks) {
1400
1409
  emitDiagnostic({
@@ -1440,7 +1449,7 @@ function siteCommand() {
1440
1449
  // was prose with a timestamp where a parser reads the path,
1441
1450
  // which made one authored table produce a machine-readable
1442
1451
  // diagnostic from one build and something ungreppable from the
1443
- // other (#223).
1452
+ // other.
1444
1453
  for (const e of result.tableErrors) {
1445
1454
  emitDiagnostic({
1446
1455
  file: e.source,
@@ -1452,7 +1461,7 @@ function siteCommand() {
1452
1461
  }
1453
1462
  // Reported the way the pack build reports the very same
1454
1463
  // finding: `file:line:column: error: message`, path first, and
1455
- // the message from the shared table (#184). It used to be a
1464
+ // the message from the shared table, not a
1456
1465
  // `log.error` whose timestamp prefix sat where a parser reads
1457
1466
  // the path from, and whose text named a `reason` code rather
1458
1467
  // than saying what to do — so one authored link produced a
@@ -1531,7 +1540,7 @@ function reachabilityCommand() {
1531
1540
  try {
1532
1541
  // Resolved once and passed on, so every pass below runs
1533
1542
  // against the configuration this command resolved rather than
1534
- // whichever one the working directory answers with (#243).
1543
+ // whichever one the working directory answers with.
1535
1544
  const config = loadPackConfig();
1536
1545
  const contentBase = argv.root ?? config.paths.content;
1537
1546
  const dir = String(argv.dir).replace(/\/+$/, "");
@@ -1604,7 +1613,7 @@ function reachabilityCommand() {
1604
1613
  // eslint-disable-next-line
1605
1614
  /**
1606
1615
  * `deps fetch` — fill the caches this build resolves other packages through:
1607
- * the **content index** of every declared dependency (#239), and the **item
1616
+ * the **content index** of every declared dependency, and the **item
1608
1617
  * catalogue** of those additionally declaring `itemCatalog: true`.
1609
1618
  *
1610
1619
  * The two sets differ deliberately. Citing another package's *addresses* and
@@ -1664,7 +1673,7 @@ function depsCommand() {
1664
1673
  command: "deps <action>",
1665
1674
  describe: "Manage build-time dependencies on other packages",
1666
1675
  builder: (yargs) => {
1667
- // Required, for the reason `package <action>` is (#57): an optional
1676
+ // Required, for the reason `package <action>` is: an optional
1668
1677
  // action exits 0 having done nothing.
1669
1678
  yargs.positional("action", {
1670
1679
  describe: "The action to perform.",
@@ -1761,7 +1770,7 @@ async function diffAddresses(config, argv) {
1761
1770
  );
1762
1771
  }
1763
1772
 
1764
- // Stated by the caller, like every other corpus read in this file (#243):
1773
+ // Stated by the caller, like every other corpus read in this file:
1765
1774
  // the two tree reads below must agree with each other and with the compile
1766
1775
  // about which files are the corpus. They now do so by construction — the
1767
1776
  // corpus is derived once, here, and handed to both.
@@ -1823,7 +1832,7 @@ function addressesCommand() {
1823
1832
  command: "addresses <action>",
1824
1833
  describe: "Compare the addresses this build publishes against a release's",
1825
1834
  builder: (yargs) => {
1826
- // Required, for the reason every other action is (#57): an
1835
+ // Required, for the reason every other action is: an
1827
1836
  // optional one exits 0 having compared nothing.
1828
1837
  yargs.positional("action", {
1829
1838
  describe: "The action to perform.",
@@ -1875,7 +1884,7 @@ function packageCommand() {
1875
1884
  builder: (yargs) => {
1876
1885
  // Required, not optional: the action *is* the work, and an
1877
1886
  // optional one meant `content-build package` fell through the
1878
- // switch below and exited 0 having compiled nothing (#57).
1887
+ // switch below and exited 0 having compiled nothing.
1879
1888
  yargs.positional("action", {
1880
1889
  describe: "The action to perform.",
1881
1890
  type: "string",
@@ -1907,7 +1916,7 @@ function packageCommand() {
1907
1916
  switch (action) {
1908
1917
  // Every path and pack list the library needs is defaulted
1909
1918
  // from the resolved configuration, so nothing is restated
1910
- // here (#1508).
1919
+ // here.
1911
1920
  case "compile":
1912
1921
  return await compilePacks({ packName: pack });
1913
1922
  case "clean":
@@ -133,7 +133,7 @@ function die(err) {
133
133
  const message = err instanceof Error ? err.message : String(err);
134
134
  // A located diagnostic already starts with `file:line:column:`, which is
135
135
  // exactly the position a parser reads the path from — prefixing it would
136
- // yield a filename no editor can open (#95).
136
+ // yield a filename no editor can open.
137
137
  console.error(
138
138
  /** @type {{located?: boolean}} */ (err)?.located ? message : `package-build: ${message}`,
139
139
  );
@@ -325,7 +325,7 @@ async function formatGenerated(text, filepath) {
325
325
  *
326
326
  * The producing half of the check `content-build lint` runs: Foundry discards
327
327
  * an unknown `system` key at construction and says nothing, so a content build
328
- * needs to know what a document will actually receive (#60). It cannot ask a
328
+ * needs to know what a document will actually receive. It cannot ask a
329
329
  * running Foundry, and it cannot read `defineSchema()` from a sibling checkout,
330
330
  * so the system publishes the field sets as data — the same shape the link
331
331
  * manifest already uses for addresses.
package/config.mjs CHANGED
@@ -29,10 +29,8 @@
29
29
  * loader checks that the section is a mapping and hands it back frozen;
30
30
  * everything inside it is validated here.
31
31
  *
32
- * **That section used to be a reservation.** Until 3.0.0 these were two
33
- * packages, and `packageBuild:` was a block `@heroiclands/content-build`
34
- * carried on behalf of a toolchain it knew nothing about. One package now owns
35
- * the whole file, so it is an ordinary section — but the validation split is
32
+ * **That section is not a reservation.** One package owns the whole file, so
33
+ * `packageBuild:` is an ordinary section — but the validation split is
36
34
  * kept, because it is what stops a key being checked twice against two
37
35
  * disagreeing ideas of what it means.
38
36
  *
@@ -150,7 +148,7 @@ const ARTIFACT_OF_KIND = Object.freeze({
150
148
  *
151
149
  * The dotted path rides on the error as `field` as well as appearing in the
152
150
  * message, so {@link loadPackageBuildConfig} — the half that knows which file
153
- * was read — can resolve it to a line and column (#95). This half stays pure.
151
+ * was read — can resolve it to a line and column. This half stays pure.
154
152
  *
155
153
  * @param {string} where - Dotted path of the offending key.
156
154
  * @param {string} problem - What is wrong with it.
@@ -802,8 +800,8 @@ export function resolvePackageBuildConfig(shared) {
802
800
  e2eSuite: normalizeE2ESuite(e2eInput.suite),
803
801
  // Declaring nothing keeps the old contract — the suite's exit status is
804
802
  // taken at its word. Declaring where results land is what lets the
805
- // harness tell "the suite ran and passed" from "the suite did not run"
806
- // (#153); a repository that wants that distinction has to say where to
803
+ // harness tell "the suite ran and passed" from "the suite did not run";
804
+ // a repository that wants that distinction has to say where to
807
805
  // look for it, because the harness does not know what the suite is.
808
806
  e2eResults: normalizeGlobs(e2eInput.results, [], "packageBuild.e2e.results"),
809
807
  e2eBuild,
@@ -831,7 +829,7 @@ export function loadPackageBuildConfig() {
831
829
  } catch (err) {
832
830
  // The pure half names the offending key and nothing else; this half
833
831
  // knows the file it was read from, so the position is attached here
834
- // (#95) — the same boundary `configFromData` is for the rest of the
832
+ // — the same boundary `configFromData` is for the rest of the
835
833
  // configuration.
836
834
  throw locateConfigError(err, packConfigPath());
837
835
  }