@heroiclands/package-build 20.7.0 → 21.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (63) hide show
  1. package/CHANGELOG.md +159 -0
  2. package/CONTENT.md +134 -44
  3. package/bin/content-build.mjs +37 -5
  4. package/bin/package-build.mjs +77 -0
  5. package/content-config.mjs +59 -1
  6. package/docs/api.md +149 -19
  7. package/docs/commands.md +75 -0
  8. package/docs/configuration.md +37 -10
  9. package/docs/content-format.md +450 -49
  10. package/engine/content-format.mjs +52 -3
  11. package/engine/content-images.mjs +699 -0
  12. package/engine/dependency-bump.mjs +218 -0
  13. package/engine/frontmatter-lint.mjs +89 -2
  14. package/engine/helpers.mjs +81 -142
  15. package/engine/index.mjs +15 -0
  16. package/engine/infobox-registry.mjs +81 -0
  17. package/engine/infobox-render.mjs +381 -0
  18. package/engine/infobox.mjs +963 -0
  19. package/engine/item-registry.mjs +5 -5
  20. package/engine/journals.mjs +22 -1
  21. package/engine/map-notes.mjs +11 -5
  22. package/engine/metadata-index.mjs +5 -0
  23. package/engine/note-vocabulary.mjs +57 -2
  24. package/engine/pathnames.mjs +374 -0
  25. package/engine/pdf-build.mjs +208 -9
  26. package/engine/pdf-render.mjs +461 -21
  27. package/engine/pdf-toc.mjs +77 -5
  28. package/engine/scenes.mjs +2 -1
  29. package/engine/site-build.mjs +106 -7
  30. package/engine/site-index.mjs +93 -4
  31. package/engine/wikilinks.mjs +93 -0
  32. package/hm3/default-item-art.mjs +14 -15
  33. package/hm3/index.mjs +3 -0
  34. package/hm3/infobox.mjs +64 -0
  35. package/package.json +1 -1
  36. package/sohl/default-item-art.mjs +18 -16
  37. package/sohl/index.mjs +3 -0
  38. package/sohl/infobox.mjs +499 -0
  39. package/types/content-config.d.mts +7 -0
  40. package/types/engine/content-format.d.mts +36 -0
  41. package/types/engine/content-images.d.mts +281 -0
  42. package/types/engine/dependency-bump.d.mts +89 -0
  43. package/types/engine/frontmatter-lint.d.mts +23 -0
  44. package/types/engine/helpers.d.mts +30 -72
  45. package/types/engine/index.d.mts +5 -0
  46. package/types/engine/infobox-registry.d.mts +36 -0
  47. package/types/engine/infobox-render.d.mts +87 -0
  48. package/types/engine/infobox.d.mts +443 -0
  49. package/types/engine/item-registry.d.mts +5 -5
  50. package/types/engine/journals.d.mts +9 -1
  51. package/types/engine/note-vocabulary.d.mts +51 -0
  52. package/types/engine/pathnames.d.mts +189 -0
  53. package/types/engine/pdf-build.d.mts +46 -0
  54. package/types/engine/pdf-render.d.mts +99 -1
  55. package/types/engine/pdf-toc.d.mts +10 -5
  56. package/types/engine/site-build.d.mts +11 -3
  57. package/types/engine/site-index.d.mts +35 -3
  58. package/types/engine/wikilinks.d.mts +22 -0
  59. package/types/hm3/default-item-art.d.mts +5 -6
  60. package/types/hm3/index.d.mts +1 -0
  61. package/types/hm3/infobox.d.mts +22 -0
  62. package/types/sohl/index.d.mts +1 -0
  63. package/types/sohl/infobox.d.mts +145 -0
@@ -36,16 +36,23 @@
36
36
  * nothing about their contents: no type name, no field name and no system name
37
37
  * is written here. Editing the document changes what the checks assert.
38
38
  *
39
- * **Two table shapes carry everything.**
39
+ * **Three table shapes carry everything.**
40
40
  *
41
41
  * | table | recognised by its first header cell | yields |
42
42
  * | --- | --- | --- |
43
43
  * | the per-type vocabulary | `` `data` property `` | the keys that type's `data:` block may carry |
44
44
  * | the per-type mapping | `shared source` | one claim per `system.*` cell |
45
+ * | a closed vocabulary | `` `<name>` value `` | the values `<name>` admits |
45
46
  *
46
47
  * A mapping table's remaining header cells name the systems (`→ sohl`,
47
48
  * `→ hm3`), so the system vocabulary comes from the document too.
48
49
  *
50
+ * **A closed vocabulary is a table because it is a list with consequences.**
51
+ * Where the format admits a fixed set of names and refuses the rest, each name
52
+ * takes a row beside what it means, which is the only place a reader can
53
+ * compare them. The header names the vocabulary rather than the parser, so a
54
+ * second one costs a table and nothing here.
55
+ *
49
56
  * **The other half of a type's vocabulary is a bullet list, not a table.** A
50
57
  * type's `subType` values are stated as `**subType**:` followed by one bullet
51
58
  * per value, `- <value>` or `- <value>: <definition>`, and that is read here
@@ -125,6 +132,17 @@ export const CONTENT_FORMAT_PATH = path.join(
125
132
  * @property {number} column - 1-based column of the cell's first character.
126
133
  */
127
134
 
135
+ /**
136
+ * One closed vocabulary the specification states as a table.
137
+ *
138
+ * @typedef {object} VocabularySpec
139
+ * @property {string} name - The key the header names, without its backticks.
140
+ * @property {number} line - 1-based line of the header row.
141
+ * @property {string[]} values - The values, in document order. A row whose
142
+ * first cell is not a single inline-code span states no value and is skipped,
143
+ * which is how a table says "no marker" in a row of its own.
144
+ */
145
+
128
146
  /**
129
147
  * The specification, as data.
130
148
  *
@@ -132,6 +150,8 @@ export const CONTENT_FORMAT_PATH = path.join(
132
150
  * @property {string} file - Where it was read from, for diagnostics.
133
151
  * @property {Map<string, TypeSpec>} types - Note type → what its section declares.
134
152
  * @property {MappingClaim[]} claims - Every `system.*` target, in document order.
153
+ * @property {Map<string, VocabularySpec>} vocabularies - Key → the values it
154
+ * admits, for every closed vocabulary the document states as a table.
135
155
  */
136
156
 
137
157
  /**
@@ -280,11 +300,13 @@ export function parseContentFormat(text, { file = CONTENT_FORMAT_PATH } = {}) {
280
300
  const types = new Map();
281
301
  /** @type {MappingClaim[]} */
282
302
  const claims = [];
303
+ /** @type {Map<string, VocabularySpec>} */
304
+ const vocabularies = new Map();
283
305
 
284
306
  const lines = String(text ?? "").split("\n");
285
307
  /** @type {TypeSpec|undefined} */
286
308
  let current;
287
- /** @type {{kind: "data"|"mapping", systems: string[], shared?: boolean}|undefined} */
309
+ /** @type {{kind: "data"|"mapping"|"vocabulary", systems: string[], shared?: boolean, vocabulary?: VocabularySpec}|undefined} */
288
310
  let table;
289
311
 
290
312
  for (let i = 0; i < lines.length; i += 1) {
@@ -343,8 +365,35 @@ export function parseContentFormat(text, { file = CONTENT_FORMAT_PATH } = {}) {
343
365
  };
344
366
  continue;
345
367
  }
368
+ // `` `<name>` value `` — a closed vocabulary, named by its own header.
369
+ const vocabularyHeader = /^`([A-Za-z][\w-]*)`\s+value$/.exec(cells[0]);
370
+ if (vocabularyHeader) {
371
+ const name = vocabularyHeader[1];
372
+ if (vocabularies.has(name)) {
373
+ throw specError(
374
+ file,
375
+ i + 1,
376
+ `\`${name}\` already has a vocabulary table at line ` +
377
+ `${/** @type {VocabularySpec} */ (vocabularies.get(name)).line}. ` +
378
+ "A vocabulary is stated once, or a reader has two closed sets to " +
379
+ "reconcile and no rule for which is closed.",
380
+ );
381
+ }
382
+ const vocabulary = { name, line: i + 1, values: /** @type {string[]} */ ([]) };
383
+ vocabularies.set(name, vocabulary);
384
+ table = { kind: "vocabulary", systems: [], vocabulary };
385
+ continue;
386
+ }
346
387
  if (!table) continue;
347
388
 
389
+ if (table.kind === "vocabulary") {
390
+ const value = code(cells[0]);
391
+ // A row stating no value states the *absence* of a marker, which is
392
+ // a real row of such a table and not a value it admits.
393
+ if (value) /** @type {VocabularySpec} */ (table.vocabulary).values.push(value);
394
+ continue;
395
+ }
396
+
348
397
  if (table.kind === "data") {
349
398
  if (!current) continue;
350
399
  const declared = code(cells[0]);
@@ -373,7 +422,7 @@ export function parseContentFormat(text, { file = CONTENT_FORMAT_PATH } = {}) {
373
422
  }
374
423
  }
375
424
 
376
- return { file, types, claims };
425
+ return { file, types, claims, vocabularies };
377
426
  }
378
427
 
379
428
  /**