@heroiclands/package-build 20.3.1 → 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 +75 -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 +196 -165
  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 +139 -92
  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
@@ -13,7 +13,7 @@
13
13
 
14
14
  /**
15
15
  * The note types the **engine** declares — the ones whose vocabulary is a fact
16
- * about the note format rather than about any game system (#51).
16
+ * about the note format rather than about any game system.
17
17
  *
18
18
  * `sohl/note-schemas.mjs` is the other half, and the line between them is the
19
19
  * `engine/` ÷ `sohl/` line everywhere else in this package: note-format
@@ -55,7 +55,7 @@ import { HOMEPAGE_FIELDS, HOMEPAGE_TYPE } from "./homepage.mjs";
55
55
  * all three and nothing implemented them, so a note using one was reported as
56
56
  * having no schema and then *skipped entirely*: `lintNote` returns after that
57
57
  * finding, so the note's `data:`, `subType`, references and system block all
58
- * went unexamined (#231).
58
+ * went unexamined.
59
59
  *
60
60
  * @type {readonly import("./field-spec.mjs").FieldSpec[]}
61
61
  */
@@ -90,7 +90,7 @@ const BUNDLE_FIELDS = Object.freeze([]);
90
90
  * Empty for the same reason a bundle's is: a folder declares no system-block
91
91
  * fields, because a `Folder` is a core Foundry document rather than any
92
92
  * system's. Its two properties — `parent` and `color` — are `data:` keys, held
93
- * closed by `engine/note-vocabulary.mjs` (#256).
93
+ * closed by `engine/note-vocabulary.mjs`.
94
94
  *
95
95
  * @type {readonly import("./field-spec.mjs").FieldSpec[]}
96
96
  */
@@ -13,7 +13,7 @@
13
13
 
14
14
  /**
15
15
  * The **closed** half of a note's frontmatter: the `data:` container, and the
16
- * `subType` each note type declares (#128).
16
+ * `subType` each note type declares.
17
17
  *
18
18
  * A note's frontmatter has three regions, and only one of them is open. The
19
19
  * **top level** describes the note as a published artefact, every key of it is
@@ -42,17 +42,17 @@
42
42
  * transmission — and is true of it whichever system is reading. What each
43
43
  * system makes of that value is declared elsewhere, in that system's own half.
44
44
  *
45
- * **The type names here are the specification's**, since #78 renamed `armor`,
45
+ * **The type names here are the specification's**, which renamed `armor`,
46
46
  * `projectile` and `concoction` off the `…gear` spellings that named a SoHL
47
47
  * document subtype rather than the thing the note is about. `weapon` is the one
48
48
  * the specification and this registry still spell differently: both systems
49
49
  * call that document a `weapongear`, so the name says nothing system-specific
50
- * and #78's table has no row for it. A note left on a renamed spelling still
50
+ * and the table has no row for it. A note left on a renamed spelling still
51
51
  * reaches its entry — every type-keyed lookup normalises through
52
52
  * `RENAMED_TYPES` — and is reported rather than refused until the content trees
53
53
  * have swept.
54
54
  *
55
- * **A type name and a subType value are held to the address charset** (#206), so
55
+ * **A type name and a subType value are held to the address charset**, so
56
56
  * both are `^[A-Za-z0-9]+$` — the charset `engine/address-charset.mjs` states
57
57
  * and the shortcode is already held to. For a type that is literal: it is a
58
58
  * segment of every address — the first of the short form an author writes
@@ -63,7 +63,7 @@
63
63
  * about why.
64
64
  *
65
65
  * A subType reaches no address of its own. It did when this rule was written —
66
- * a `doc`'s was its section, a path segment — and #204 retired sections from
66
+ * a `doc`'s was its section, a path segment — and sections are retired from
67
67
  * the note format one release later. It keeps the rule regardless, and the
68
68
  * reason is not inertia: a subType is a vocabulary term the whole toolchain
69
69
  * keys on, it is one closed set away from being an address again, and a charset
@@ -76,11 +76,11 @@
76
76
  */
77
77
 
78
78
  // The one charset, read rather than restated. A second spelling of the pattern
79
- // is how the three disagreements found in #202/#203 happened.
79
+ // is how a disagreement between the three arises.
80
80
  import { ADDRESS_SEGMENT_PATTERN, isAddressSegment } from "./address-charset.mjs";
81
81
  // The retirement window for a renamed type, read rather than restated: a
82
82
  // vocabulary that answered only to the current spelling would report every key
83
- // of an unswept note as unknown (#78).
83
+ // of an unswept note as unknown.
84
84
  import { currentType } from "./ids.mjs";
85
85
 
86
86
  /**
@@ -88,7 +88,7 @@ import { currentType } from "./ids.mjs";
88
88
  *
89
89
  * A deliberate subset of {@link import("./field-spec.mjs").FieldSpec}: no `to`,
90
90
  * because nothing here builds anything yet. Reading `data.*` through into a
91
- * document's `system` block is the passthrough slice (#126), and claiming an
91
+ * document's `system` block is the passthrough slice, and claiming an
92
92
  * emitted path this does not produce would be a lie in the one place a reader
93
93
  * would trust it.
94
94
  *
@@ -155,9 +155,9 @@ const LINKS = Object.freeze({ shape: "list of wikilinks", kind: "list" });
155
155
  * the scalar cannot, that the answer *differs by pack*. A folder's `parent` is
156
156
  * the case it exists for — a folder's identity is one thing and its hierarchy
157
157
  * another, and both large trees file the same folder under a different parent
158
- * in the items pack and the journals pack (#276).
158
+ * in the items pack and the journals pack.
159
159
  *
160
- * Typing it as a bare {@link LINK} is what #288 was: the compiler read both
160
+ * Typing it as a bare {@link LINK} makes the compiler read both
161
161
  * forms and the lint rejected one of them, so every note using the form the
162
162
  * specification prescribes was a finding and no note using it was not.
163
163
  */
@@ -235,7 +235,7 @@ const CHARGES = Object.freeze([
235
235
  * @type {Readonly<Record<string, TypeVocabulary>>}
236
236
  */
237
237
  /**
238
- * The tags that **classify** a note, grouped by what they classify (#172).
238
+ * The tags that **classify** a note, grouped by what they classify.
239
239
  *
240
240
  * `tags:` lives at the open top level and most tags belong there: a theme, a
241
241
  * region, a working state is the author's own and this build has no opinion
@@ -261,7 +261,7 @@ const CHARGES = Object.freeze([
261
261
  * this replaced had to spell it `Fishing Village` as a value of its own.
262
262
  */
263
263
  /**
264
- * The declared tag that marks a note as **unfinished** (#183).
264
+ * The declared tag that marks a note as **unfinished**.
265
265
  *
266
266
  * Named once and referenced from the declaration below, because a second
267
267
  * spelling is how the two come apart: rename the tag in `DECLARED_TAGS` and a
@@ -399,7 +399,7 @@ export function hasTag(fm, tag) {
399
399
  }
400
400
 
401
401
  /**
402
- * Whether a note is tagged as an unfinished **draft** (#183).
402
+ * Whether a note is tagged as an unfinished **draft**.
403
403
  *
404
404
  * The one reader of {@link DRAFT_TAG}, so both builds ask the same question of
405
405
  * the same field. Presentation only: a draft note is in the packs, in the
@@ -418,7 +418,7 @@ export const NOTE_VOCABULARY = Object.freeze({
418
418
 
419
419
  being: Object.freeze({
420
420
  // Derived from the note's `(type, subType)` by each system's map, which
421
- // lands with #79. Declared open until it does, because inventing the
421
+ // lands with. Declared open until it does, because inventing the
422
422
  // values here would put a second, weaker answer beside the real one.
423
423
  subTypes: null,
424
424
  data: Object.freeze([
@@ -794,10 +794,10 @@ export const NOTE_VOCABULARY = Object.freeze({
794
794
 
795
795
  doc: Object.freeze({
796
796
  // `userguide`, not `user-guide`: a subType is held to the address
797
- // charset, and a segment carries no hyphen (#206). The old spelling was
797
+ // charset, and a segment carries no hyphen. The old spelling was
798
798
  // accepted transitionally for one release so the consumer trees could
799
799
  // sweep; they have, so it is refused by the charset check now, with no
800
- // retirement-specific code left over (#210).
800
+ // retirement-specific code left over.
801
801
  subTypes: Object.freeze(["rules", "userguide", "reference"]),
802
802
  data: Object.freeze([]),
803
803
  }),
@@ -805,12 +805,12 @@ export const NOTE_VOCABULARY = Object.freeze({
805
805
  macro: Object.freeze({ data: Object.freeze([]) }),
806
806
 
807
807
  // Foundry's `Folder`, and the last document this package compiled from
808
- // bespoke configuration rather than from a note (#256). It declares no
808
+ // bespoke configuration rather than from a note. It declares no
809
809
  // system-block fields, like a bundle: a `Folder` is a core Foundry
810
810
  // document, so its address carries the `none` system segment and
811
811
  // everything it says is a `data` property.
812
812
  //
813
- // It carries **no prose**, which is the decision #256 left open: a folder
813
+ // It carries **no prose**: a folder
814
814
  // is structure, not content, so it wants no documentation journal and takes
815
815
  // no part in `docEntryTypes`.
816
816
  folder: Object.freeze({
@@ -836,7 +836,7 @@ export const NOTE_VOCABULARY = Object.freeze({
836
836
  // calls it: a set of documents taken as a unit. The document is an
837
837
  // installer — it carries copies, and importing one creates or updates each
838
838
  // document in the world — which is what separates a bundle from a folder,
839
- // a live grouping that persists in the pack (#259).
839
+ // a live grouping that persists in the pack.
840
840
  //
841
841
  // How many Adventures a bundle makes is decided by its system blocks, as
842
842
  // for every other type, and not by a property: an `Adventure` has no
@@ -940,12 +940,12 @@ export const NOTE_VOCABULARY = Object.freeze({
940
940
 
941
941
  map: Object.freeze({
942
942
  // One type, three subTypes: they differ only in the canvas defaults
943
- // derived for them, which is precisely what a subType decides (#174).
943
+ // derived for them, which is precisely what a subType decides.
944
944
  subTypes: Object.freeze(["battlemap", "localmap", "regionalmap"]),
945
945
  data: Object.freeze([
946
946
  // `img`, as every other note type spells its artwork. A map alone
947
947
  // read `image` out of its `sohl:` block; that spelling is retired
948
- // and gone (#149), so the two names are one again.
948
+ // and gone, so the two names are one again.
949
949
  {
950
950
  name: "img",
951
951
  ...TEXT,
@@ -1001,10 +1001,11 @@ export const NOTE_VOCABULARY = Object.freeze({
1001
1001
  /**
1002
1002
  * What a note carrying a subType outside the address charset is told.
1003
1003
  *
1004
- * **Why the charset holds for a subType, which reaches no address.** #206 said
1004
+ * **Why the charset holds for a subType, which reaches no address.** The rule
1005
+ * said
1005
1006
  * "the hyphen separates the segments of an address", and that was true of a
1006
1007
  * subType when it shipped: `sectionOf` returned a `doc`'s subType, so the value
1007
- * was a URL path segment. #204 retired sections and it is not one now. The rule
1008
+ * was a URL path segment. Sections are retired and it is not one. The rule
1008
1009
  * stays, on its own footing: a subType is a vocabulary term the whole toolchain
1009
1010
  * keys on, and it is one closed set away from being an address segment again —
1010
1011
  * so the reason to spell it in the address charset is that a charset holding
@@ -1054,8 +1055,8 @@ export function typeCharsetMessage(type) {
1054
1055
  *
1055
1056
  * The message states the reason **per key**, as {@link typeCharsetMessage} and
1056
1057
  * {@link subTypeCharsetMessage} do: a type is an address segment, and a subType
1057
- * has not been one since #204 retired sections, so a single claim covering both
1058
- * would be half wrong (#210).
1058
+ * is not one, since sections are retired, so a single claim covering both
1059
+ * would be half wrong.
1059
1060
  *
1060
1061
  * @param {Readonly<Record<string, TypeVocabulary>>} vocabulary - The registry.
1061
1062
  * @param {string} [where] - What declares it, for the message.
@@ -1077,7 +1078,7 @@ export function assertVocabularyCharset(vocabulary, where = "the note vocabulary
1077
1078
  `${where} declares ${bad.join(", ")}, which ${bad.length === 1 ? "is" : "are"} ` +
1078
1079
  `not ${ADDRESS_SEGMENT_PATTERN.source}. A type is an address segment, ` +
1079
1080
  `and the hyphen separates segments rather than occurring inside one. ` +
1080
- `A subType reaches no address since #204 retired sections, and is ` +
1081
+ `A subType reaches no address, and is ` +
1081
1082
  `held to the same charset anyway: it is a vocabulary term the whole ` +
1082
1083
  `toolchain keys on, one closed set away from being a segment again, ` +
1083
1084
  `and a charset holding for every term but that one would be a rule ` +
@@ -17,7 +17,7 @@
17
17
  * One module, one import: everything the compilers used to hard-code — the
18
18
  * content package, the Foundry package and its kind, every path, the `_stats`
19
19
  * identity, the item-type membership, and the pack list — arrives from the
20
- * consuming repository's `package-build.config.yaml` (#1508).
20
+ * consuming repository's `package-build.config.yaml`.
21
21
  *
22
22
  * **The configuration is data, and a repository writes it as data.** Every
23
23
  * value in it is a literal; the three consumers' configs held no logic between
@@ -58,7 +58,7 @@
58
58
  * resolution walks parent directories. `import.meta.dirname` is then inside the
59
59
  * parent, the walk lands on the parent's configuration, and the build compiles
60
60
  * the parent's content tree into the parent's `build/` and exits 0 — saying so
61
- * only in paths that are easy to read past (#364).
61
+ * only in paths that are easy to read past.
62
62
  *
63
63
  * That failure is undetectable on exactly the work most likely to provoke it.
64
64
  * The usual tell is a zero diff where a change was expected; an
@@ -75,7 +75,7 @@
75
75
  * `node_modules`. `PACKAGE_BUILD_CONFIG` names the file explicitly and skips
76
76
  * both walks, which is why it was the workaround.
77
77
  *
78
- * What #1508 removed stays removed. The property it bought was not "resolve
78
+ * What was removed stays removed. The property it bought was not "resolve
79
79
  * from the module"; it was that a build reads one tree however it was launched,
80
80
  * and an upward walk from the working directory keeps that — every directory
81
81
  * inside a repository resolves that repository's single configuration.
@@ -179,7 +179,7 @@ export function findConfigFile(from) {
179
179
  *
180
180
  * Kept separate from {@link loadPackConfig} because the *choice* is worth being
181
181
  * able to ask about without loading anything: the two walks disagreeing is the
182
- * observable form of #364, and a caller that wants to report it — or a test
182
+ * observable form of that, and a caller that wants to report it — or a test
183
183
  * that wants to describe it — should not have to reproduce the resolution and
184
184
  * risk disagreeing with the loader about it. It performs I/O, and is named for
185
185
  * it, like the {@link findConfigFile} it calls twice.
@@ -244,7 +244,7 @@ const ITEM_BUILDER_REGISTRIES = Object.freeze({
244
244
  *
245
245
  * `stats.systemVersion` is stamped into every compiled document, and a
246
246
  * transcribed copy of it froze at `0.6.0` for four releases before anyone
247
- * noticed (#1548). `package.json` is the file Changesets bumps, so reading it
247
+ * noticed. `package.json` is the file Changesets bumps, so reading it
248
248
  * is what keeps the stamp equal to the version that did the compiling.
249
249
  *
250
250
  * The read happens *here*, in the loader, rather than in `defineConfig`:
@@ -309,7 +309,7 @@ function foundryPackageId(rootDir) {
309
309
  * the whole point:
310
310
  *
311
311
  * - A **system** ships itself, so its own `package.json` version *is* the
312
- * system version. That is the read #1548 introduced after a transcribed copy
312
+ * system version. That is the read to make, because a transcribed copy
313
313
  * froze at `0.6.0` for four releases.
314
314
  * - A **module** ships content *for* someone else's system. Its own version is
315
315
  * the module's — `sohl-thalorna` sits at `0.0.1` — so deriving from it would
@@ -343,7 +343,7 @@ function shippedSystemVersion(rootDir, input) {
343
343
  const declaredSystems = /** @type {Record<string, unknown>} */ (input.relationships ?? {})
344
344
  .systems;
345
345
 
346
- // The `systems:` block declares without requiring (#48), so it is consulted
346
+ // The `systems:` block declares without requiring, so it is consulted
347
347
  // first: a package that has adopted it needs no relationship, and one that
348
348
  // ships for two systems could not express itself through a relationship at
349
349
  // all. `requiresSystem` names the package-wide default when there is one;
@@ -373,7 +373,7 @@ function shippedSystemVersion(rootDir, input) {
373
373
  // system data, and it installs under any system. There is no version to
374
374
  // stamp, and inventing one would be the very thing the throw below guards
375
375
  // against. The two signals together are what separate this from a module
376
- // that simply forgot to declare its system (#43).
376
+ // that simply forgot to declare its system.
377
377
  if (
378
378
  (systemId === undefined || systemId === null) &&
379
379
  !(Array.isArray(declaredSystems) && declaredSystems.length)
@@ -453,7 +453,7 @@ function positionInConfig(configPath, field) {
453
453
  * worse than locating none — a reader would learn that some configuration
454
454
  * errors carry a position and could not predict which — so the path rides on
455
455
  * the error and every one of them is located here, at the boundary that knows
456
- * which file was read (#95).
456
+ * which file was read.
457
457
  *
458
458
  * The message keeps its body and gains the `file:line:column: error: ` prefix
459
459
  * every other finding in this build already uses, so nothing a reader has today
@@ -500,7 +500,7 @@ export function locateConfigError(err, configPath) {
500
500
  * cannot write `import.meta.dirname`, and any absolute path it wrote instead
501
501
  * would be one machine's — so authoring it is rejected rather than honoured.
502
502
  * - **`itemBuilders`** is a *name* (`sohl`) — or a list of names, for a tree
503
- * feeding more than one system (#58) — resolved against the built-in
503
+ * feeding more than one system — resolved against the built-in
504
504
  * registries. A registry's name is the system it belongs to. A registry of a
505
505
  * consumer's own is code, and code goes in an `.mjs` configuration.
506
506
  * - **`stats.systemVersion`** is derived from the adjacent `package.json` when
@@ -539,7 +539,7 @@ export function configFromData(data, configPath) {
539
539
 
540
540
  // Transcribed from `package.json`, and therefore free to disagree with it.
541
541
  // Every consumer's copy matched exactly, which is what a transcription
542
- // looks like right up until it does not (#1548 froze one at `0.6.0` for
542
+ // looks like right up until it does not (a transcribed one freezes
543
543
  // four releases while nothing said so).
544
544
  if (input.foundryPackage !== undefined) {
545
545
  throw new Error(
@@ -554,7 +554,7 @@ export function configFromData(data, configPath) {
554
554
  const declared = input.itemBuilders;
555
555
  const known = Object.keys(ITEM_BUILDER_REGISTRIES).join(", ");
556
556
  // One name or several. A repository feeding two systems needs both
557
- // vocabularies, and one registry can only carry one (#58); the scalar
557
+ // vocabularies, and one registry can only carry one; the scalar
558
558
  // form every existing configuration uses is the one-element case and
559
559
  // means exactly what it always did.
560
560
  const names = Array.isArray(declared) ? declared : [declared];
@@ -691,7 +691,7 @@ export function loadPackConfig() {
691
691
  }
692
692
 
693
693
  // Two different files, one of which is about to be ignored. Said out loud
694
- // because the alternative is what #364 was: a build that reads the parent
694
+ // because the alternative is a build that reads the parent
695
695
  // checkout's configuration, compiles the parent's tree, and reports it only
696
696
  // in absolute paths nobody rereads. A warning rather than an error — the
697
697
  // shape is legitimate, and the working directory's answer is the right one
Binary file
@@ -14,14 +14,14 @@
14
14
  /**
15
15
  * The prose conventions every content repository writes to — one Prettier
16
16
  * configuration and one markdownlint rule set, declared here so a note
17
- * formatted in one repository is formatted the same way in the next (#69).
17
+ * formatted in one repository is formatted the same way in the next.
18
18
  *
19
- * These used to exist in exactly one consumer. The SoHL repository carried
19
+ * These belong in no single consumer. The SoHL repository carried
20
20
  * both; `sohl-thalorna` had Prettier but never ran it from `lint`; and
21
21
  * `sohl-kethira-basic` had neither, so the package least likely to have been
22
22
  * proofread was checked for addresses and nothing else. A rule set copied into
23
23
  * three repositories is one rule with three implementations, which is the drift
24
- * #20 exists to remove — so it is declared once, here, and every consumer
24
+ * this exists to remove — so it is declared once, here, and every consumer
25
25
  * invokes it.
26
26
  *
27
27
  * **Neither of these is an override.** A consumer that declares its own
@@ -89,7 +89,7 @@ export const PRETTIER_BASE = Object.freeze({
89
89
  * **Declared apart from the `overrides` block, not derived from it.** Prettier
90
90
  * applies `overrides` only while resolving a config *file*; options handed to
91
91
  * it directly keep the global values, so a consumer with no config of its own
92
- * silently got markdown at 4 (#76). The runner needs the adjustment as data it
92
+ * silently got markdown at 4. The runner needs the adjustment as data it
93
93
  * can apply itself, and {@link PRETTIER_CONFIG} composes the same values into
94
94
  * the shape a config file wants — one source, two presentations.
95
95
  *
@@ -128,7 +128,7 @@ export const PRETTIER_CONFIG = Object.freeze({
128
128
  *
129
129
  * @param {string} file - Path of the file about to be formatted.
130
130
  * @returns {object} Options to hand Prettier directly. Never carries
131
- * `overrides`: passing that inline is what silently did nothing (#76).
131
+ * `overrides`: passing that inline is what silently did nothing.
132
132
  */
133
133
  export function sharedPrettierOptionsFor(file) {
134
134
  return /\.md$/i.test(file) ? { ...PRETTIER_BASE, ...PRETTIER_MARKDOWN } : { ...PRETTIER_BASE };
@@ -140,11 +140,11 @@ export function sharedPrettierOptionsFor(file) {
140
140
  * The runner resolves each file's options as *either* the consumer's own config
141
141
  * or {@link sharedPrettierOptionsFor}, never a merge. That is what bare Prettier
142
142
  * does and it is the contract — but it means the conventions this package exists
143
- * to publish hold by convention alone, and they lapse in two opposite directions
144
- * (#133). A consumer that declares any config of its own gets whatever that
143
+ * to publish hold by convention alone, and they lapse in two opposite directions.
144
+ * A consumer that declares any config of its own gets whatever that
145
145
  * config says: spread {@link PRETTIER_BASE} without the markdown override and
146
- * every note reindents at 4, the reindentation the override was added to prevent
147
- * (#76); write a partial `.prettierrc` such as `{"tabWidth": 2}` and
146
+ * every note reindents at 4, the reindentation the override was added to prevent;
147
+ * write a partial `.prettierrc` such as `{"tabWidth": 2}` and
148
148
  * `printWidth`, `trailingComma`, `experimentalTernaries` and the rest fall back
149
149
  * to Prettier's own defaults. A consumer that declares *nothing* formats one way
150
150
  * under this command and another under a bare `npx prettier`.
@@ -13,13 +13,13 @@
13
13
 
14
14
  /**
15
15
  * Running the shared prose conventions over a repository — Prettier for
16
- * formatting, markdownlint for the structure Prettier is indifferent to (#69).
16
+ * formatting, markdownlint for the structure Prettier is indifferent to.
17
17
  *
18
18
  * Both are thin: the rules live in `./prose-config.mjs` and the tools are
19
19
  * Prettier and markdownlint themselves. What this module adds is the two things
20
20
  * a consumer would otherwise have to get right on its own — supplying the
21
21
  * shared configuration *as a default a local one overrides*, and reporting
22
- * findings in the one parseable form every check in this package emits (#17).
22
+ * findings in the one parseable form every check in this package emits.
23
23
  *
24
24
  * **Neither tool's own file discovery is reimplemented.** Prettier decides what
25
25
  * it formats and what an ignore file excludes, through `getFileInfo`;
@@ -68,7 +68,7 @@ const IGNORE_FILES = Object.freeze([".gitignore", ".prettierignore"]);
68
68
  * `format` is *assumed* idempotent and is not guaranteed to be: a single pass
69
69
  * can leave text the next pass would still change, and a `--write` run that
70
70
  * takes one pass then reports success has called such a file formatted while
71
- * `prettier --check` still rejects it (#125). Formatting to a fixpoint removes
71
+ * `prettier --check` still rejects it. Formatting to a fixpoint removes
72
72
  * the assumption — the file lands on the value repeated formatting converges
73
73
  * to, whatever it took to get there.
74
74
  *
@@ -86,7 +86,7 @@ const MAX_FORMAT_PASSES = 3;
86
86
  * configured to do means asking about a file. These are the two answers that
87
87
  * differ: markdown carries the shared `tabWidth` override and everything else
88
88
  * does not, so a single probe would check half the conventions and miss the one
89
- * most worth checking (#133).
89
+ * most worth checking.
90
90
  *
91
91
  * Ordinary names at the repository root, and neither has to exist —
92
92
  * `resolveConfig` reads the path to walk up from it and to match `overrides`
@@ -147,8 +147,7 @@ function walkFiles(root) {
147
147
  * @param {boolean} [opts.write=false] - Rewrite unformatted files in place
148
148
  * rather than reporting them. Each file is formatted to a fixpoint (up to
149
149
  * {@link MAX_FORMAT_PASSES} passes), so a written tree is one a second run
150
- * leaves alone; a file that will not converge is reported and left unchanged
151
- * (#125).
150
+ * leaves alone; a file that will not converge is reported and left unchanged.
152
151
  * @param {object} [opts.prettier] - The Prettier module, for tests.
153
152
  * @returns {Promise<{findings: Array<{file: string, severity: string,
154
153
  * message: string}>, checked: number, written: string[]}>} The findings, how
@@ -185,7 +184,7 @@ export async function checkFormatting(root, opts = {}) {
185
184
  });
186
185
  // `resolveConfig` has already applied any `overrides` the consumer's
187
186
  // own config declares. The shared fallback has to apply its own, since
188
- // Prettier ignores an `overrides` block passed inline (#76).
187
+ // Prettier ignores an `overrides` block passed inline.
189
188
  const options = {
190
189
  ...(local ?? sharedPrettierOptionsFor(file)),
191
190
  filepath: file,
@@ -199,7 +198,7 @@ export async function checkFormatting(root, opts = {}) {
199
198
  try {
200
199
  if (write) {
201
200
  // Format to a fixpoint rather than once, so what lands on disk
202
- // is what a second run would have produced (#125).
201
+ // is what a second run would have produced.
203
202
  let formatted = source;
204
203
  let converged = false;
205
204
  for (let pass = 0; pass < MAX_FORMAT_PASSES; pass += 1) {
@@ -221,7 +220,7 @@ export async function checkFormatting(root, opts = {}) {
221
220
  file,
222
221
  severity: "error",
223
222
  // No line or column: the verdict is about the whole
224
- // file, and #17's rule is to drop a field rather than
223
+ // file, and the rule is to drop a field rather than
225
224
  // invent one.
226
225
  message:
227
226
  `did not converge after ${MAX_FORMAT_PASSES} formatting passes; ` +
@@ -242,7 +241,7 @@ export async function checkFormatting(root, opts = {}) {
242
241
  file,
243
242
  severity: "error",
244
243
  // No line or column: Prettier's answer is about the whole
245
- // file, and #17's rule is to drop a field rather than
244
+ // file, and the rule is to drop a field rather than
246
245
  // invent one.
247
246
  message: "is not formatted; run `content-build format --write` to fix it",
248
247
  });
@@ -272,7 +271,7 @@ export async function checkFormatting(root, opts = {}) {
272
271
  *
273
272
  * The two cases read differently on purpose. A key set to something else is a
274
273
  * choice someone made and can defend; a key that is simply absent is the
275
- * silent half of #133 the consumer did not choose Prettier's default, it
274
+ * silent half — a consumer does not choose Prettier's default, it
276
275
  * arrived because declaring one option discards every option not restated.
277
276
  *
278
277
  * @param {{key: string, shared: unknown, local: unknown}} divergence - From
@@ -291,7 +290,7 @@ function divergenceMessage({ key, shared, local }, scope = "") {
291
290
 
292
291
  /**
293
292
  * Report where a repository's own Prettier configuration parts from the shared
294
- * one — or that it has none at all (#133).
293
+ * one — or that it has none at all.
295
294
  *
296
295
  * **Warnings, every one of them.** A consumer's config wins by design and this
297
296
  * does not change that; it only refuses to let the divergence be silent, which
@@ -312,7 +311,7 @@ function divergenceMessage({ key, shared, local }, scope = "") {
312
311
  * @returns {Promise<{findings: Array<{file?: string, severity: string,
313
312
  * message: string}>, configFile: string|null}>} The findings and the config
314
313
  * file they are about, which is `null` when the repository declares none. A
315
- * finding about a missing file carries no `file`: #17's rule is to drop a
314
+ * finding about a missing file carries no `file`: the rule is to drop a
316
315
  * field rather than invent one.
317
316
  */
318
317
  export async function checkPrettierConventions(root, opts = {}) {
@@ -22,10 +22,10 @@
22
22
  * runtime bridge from drifting apart.
23
23
  *
24
24
  * It sits in this package rather than in the system's `src/` tree because the
25
- * map-note compiler that reads it is installed as a dependency (#1501), and a
25
+ * map-note compiler that reads it is installed as a dependency, and a
26
26
  * relative path out of the package would resolve to garbage from
27
27
  * `node_modules`. The runtime reaches it back through the package's
28
- * `./engine/region-events` entry point (#1510). It is engine-side, not
28
+ * `./engine/region-events` entry point. It is engine-side, not
29
29
  * SoHL-side, because any content module that authors a scene region — an
30
30
  * adventure module included — needs this vocabulary.
31
31
  *