@heroiclands/package-build 25.1.2 → 25.2.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 (86) hide show
  1. package/CHANGELOG.md +36 -0
  2. package/assets/styles/content-base.css +0 -9
  3. package/bin/content-commands.mjs +22 -16
  4. package/bin/package-build.mjs +24 -25
  5. package/content-config.mjs +7 -34
  6. package/docs/api.md +38 -21
  7. package/docs/authoring/first-note.md +3 -3
  8. package/docs/authoring/frontmatter.md +30 -2
  9. package/docs/authoring/links-and-markup.md +92 -31
  10. package/docs/commands.md +103 -59
  11. package/docs/configuration.md +10 -45
  12. package/docs/content-format.md +7 -5
  13. package/docs/diagnostics.md +75 -0
  14. package/docs/encoding-core.md +11 -0
  15. package/docs/guides/book.md +3 -3
  16. package/docs/guides/module-project.md +1 -1
  17. package/docs/guides/reference-pages.md +7 -3
  18. package/docs/project-setup.md +3 -0
  19. package/docs/reference/format-details.md +329 -187
  20. package/docs/reference/note-types.md +274 -274
  21. package/engine/changelog-group.mjs +84 -173
  22. package/engine/changelog-labels.mjs +73 -0
  23. package/engine/changelog-lint.mjs +168 -44
  24. package/engine/content-expand.mjs +58 -38
  25. package/engine/content-format.yaml +0 -2
  26. package/engine/content-index.mjs +49 -23
  27. package/engine/content-links.mjs +4 -0
  28. package/engine/content-lint.mjs +2 -3
  29. package/engine/content-render.mjs +6 -2
  30. package/engine/culture-parents.mjs +1 -1
  31. package/engine/culture-reach.mjs +10 -1
  32. package/engine/diagnostics.mjs +3 -2
  33. package/engine/dictionary-rows.mjs +3 -1
  34. package/engine/encoding-lexicon.mjs +12 -2
  35. package/engine/first-use.mjs +301 -87
  36. package/engine/frontmatter-lint.mjs +40 -14
  37. package/engine/glossary-entries.mjs +5 -5
  38. package/engine/glossary-language.mjs +21 -44
  39. package/engine/homepage.mjs +0 -4
  40. package/engine/index-records.mjs +3 -1
  41. package/engine/infobox.mjs +6 -3
  42. package/engine/language-names.mjs +70 -10
  43. package/engine/language-rows.mjs +6 -7
  44. package/engine/lexical-sections.mjs +3 -1
  45. package/engine/literature-notes.mjs +4 -76
  46. package/engine/metadata-index.mjs +15 -8
  47. package/engine/name-forms.mjs +99 -0
  48. package/engine/name-refs.mjs +143 -0
  49. package/engine/name-rows.mjs +11 -1
  50. package/engine/names.mjs +27 -22
  51. package/engine/note-addresses.mjs +0 -3
  52. package/engine/note-frontmatter.mjs +3 -5
  53. package/engine/note-language.mjs +348 -0
  54. package/engine/note-vocabulary.mjs +35 -13
  55. package/engine/pdf-index.mjs +3 -2
  56. package/engine/pdf-render.mjs +2 -4
  57. package/engine/settlement-tables.mjs +4 -3
  58. package/engine/site-build.mjs +2 -7
  59. package/engine/site-search.mjs +3 -5
  60. package/engine/site-theme.mjs +1 -1
  61. package/engine/sql-tables.mjs +29 -14
  62. package/engine/wikilink-syntax.mjs +2 -0
  63. package/engine/word-citations.mjs +3 -3
  64. package/engine/word-rows.mjs +3 -1
  65. package/engine/word-terms.mjs +2 -2
  66. package/package.json +1 -1
  67. package/site-theme/static/css/style.css +0 -4
  68. package/types/engine/changelog-group.d.mts +6 -10
  69. package/types/engine/changelog-labels.d.mts +10 -0
  70. package/types/engine/changelog-lint.d.mts +11 -6
  71. package/types/engine/content-index.d.mts +16 -16
  72. package/types/engine/diagnostics.d.mts +1 -1
  73. package/types/engine/first-use.d.mts +97 -1
  74. package/types/engine/frontmatter-lint.d.mts +9 -2
  75. package/types/engine/glossary-language.d.mts +13 -6
  76. package/types/engine/homepage.d.mts +0 -3
  77. package/types/engine/language-names.d.mts +3 -0
  78. package/types/engine/literature-notes.d.mts +2 -27
  79. package/types/engine/name-forms.d.mts +61 -0
  80. package/types/engine/name-refs.d.mts +51 -0
  81. package/types/engine/name-rows.d.mts +4 -0
  82. package/types/engine/names.d.mts +12 -92
  83. package/types/engine/note-frontmatter.d.mts +1 -4
  84. package/types/engine/note-language.d.mts +47 -0
  85. package/types/engine/settlement-tables.d.mts +1 -1
  86. package/types/engine/site-build.d.mts +2 -6
@@ -0,0 +1,61 @@
1
+ /**
2
+ * A note's names, by language.
3
+ *
4
+ * `name` is `{full, <code>: [forms]}`. `full` is the English name a page,
5
+ * a compendium entry and an index print. Every other name the subject goes by
6
+ * is listed under the code of its language: a code `languages:` declares, or
7
+ * `en`, which needs no declaration. The first form of a list is that
8
+ * language's standard form, and `full` counts as an English form.
9
+ *
10
+ * `sort`, and a character's `given` and `clan`, are single strings and no
11
+ * language's forms, so a configured language may not take one of those names.
12
+ *
13
+ * A leaf module, importing nothing.
14
+ *
15
+ * @module
16
+ */
17
+ /**
18
+ * A whole visible label as names are matched: NFC, straight apostrophes,
19
+ * single spaces, trimmed, and case folded. Matching ignores case; a name is
20
+ * displayed as it was written, never in this form.
21
+ *
22
+ * @param {string} text - A visible name, `full` or a form.
23
+ * @returns {string} Its matching key.
24
+ */
25
+ export function normalizeName(text: string): string;
26
+ /**
27
+ * A whole visible label as it is displayed: NFC, straight apostrophes, single
28
+ * spaces and trimmed, its case as written.
29
+ *
30
+ * @param {string} text - A visible name, `full` or a form.
31
+ * @returns {string} The label for display.
32
+ */
33
+ export function displayName(text: string): string;
34
+ /**
35
+ * A note's form lists, in authored order: each key of `name` that is not one
36
+ * of {@link NAME_STRING_KEYS} and holds a list, with its string forms.
37
+ *
38
+ * Read without the configuration, so a reader of an index gets every list a
39
+ * note states; which codes a note may state is the frontmatter lint's
40
+ * question, not a reader's.
41
+ *
42
+ * @param {unknown} name - A note's `name`.
43
+ * @returns {Array<[string, string[]]>} `[code, forms]` pairs; a form that is
44
+ * not a nonempty string is left out, and so is a list left empty by that.
45
+ */
46
+ export function nameFormLists(name: unknown): Array<[string, string[]]>;
47
+ /**
48
+ * Every form a note's name lists, in authored order, without `full`.
49
+ *
50
+ * @param {unknown} name - A note's `name`.
51
+ * @returns {string[]} The forms; empty where it lists none.
52
+ */
53
+ export function nameFormsOf(name: unknown): string[];
54
+ /** The language every note falls back to, and a code no configuration need declare. */
55
+ export const FALLBACK_LANGUAGE: "en";
56
+ /** The single-string name keys every note accepts, in display order. */
57
+ export const NOTE_NAME_KEYS: readonly string[];
58
+ /** Additional name components accepted by an individual character. */
59
+ export const CHARACTER_NAME_KEYS: readonly string[];
60
+ /** Every `name` key that is not a language's list of forms. */
61
+ export const NAME_STRING_KEYS: readonly string[];
@@ -0,0 +1,51 @@
1
+ /**
2
+ * The `ref` a span states.
3
+ * @param {{attrs: ReadonlyArray<readonly [string, string]>}} span - A parsed span.
4
+ * @returns {string|null} The value as written, or null when the span has none.
5
+ */
6
+ export function readNameRef(span: {
7
+ attrs: ReadonlyArray<readonly [string, string]>;
8
+ }): string | null;
9
+ /**
10
+ * The `ref=` findings that depend on the span alone.
11
+ * @param {ReadonlyArray<import("./names.mjs").ParsedNameSpan>} spans - Every parsed span, at file coordinates.
12
+ * @param {{file?: string}} [options] - Authored source file.
13
+ * @returns {Array<{file?: string, line?: number, column?: number, severity: "error", message: string}>} One error per defect.
14
+ */
15
+ export function nameRefFindings(spans: ReadonlyArray<import("./names.mjs").ParsedNameSpan>, { file }?: {
16
+ file?: string;
17
+ }): Array<{
18
+ file?: string;
19
+ line?: number;
20
+ column?: number;
21
+ severity: "error";
22
+ message: string;
23
+ }>;
24
+ /**
25
+ * Create the check that every note `ref=` of a note names a note.
26
+ * @param {object} options - The lint's prepared inputs.
27
+ * @param {import("./names.mjs").NameCatalog} options.catalog - Resolves an address to its note.
28
+ * @param {Map<string, {raw: string, body: string, bodyLine?: number, bodyColumn?: number}>} [options.parsed] - Captured authored bodies.
29
+ * @param {import("./content-session.mjs").ContentSyntax} [options.syntax] - Exact-text scanner projections.
30
+ * @returns {(note: {file: string, raw: string}) => Array<{file: string, line?: number, column?: number, severity: "error", message: string}>} One error per unresolved address.
31
+ */
32
+ export function createNameRefChecker({ catalog, parsed, syntax }: {
33
+ catalog: import("./names.mjs").NameCatalog;
34
+ parsed?: Map<string, {
35
+ raw: string;
36
+ body: string;
37
+ bodyLine?: number;
38
+ bodyColumn?: number;
39
+ }> | undefined;
40
+ syntax?: import("./content-session.mjs").ContentSyntax | undefined;
41
+ }): (note: {
42
+ file: string;
43
+ raw: string;
44
+ }) => Array<{
45
+ file: string;
46
+ line?: number;
47
+ column?: number;
48
+ severity: "error";
49
+ message: string;
50
+ }>;
51
+ export function isLocalRef(ref: string): boolean;
@@ -24,6 +24,10 @@ export type NameRow = {
24
24
  * - Supplied subtype, or null.
25
25
  */
26
26
  subType: string | null;
27
+ /**
28
+ * - The `ref=` as written, a note address or a page-local `#key`, or null.
29
+ */
30
+ ref: string | null;
27
31
  /**
28
32
  * - Original 1-based source line at the opening bracket.
29
33
  */
@@ -1,97 +1,12 @@
1
1
  /**
2
- * An indexed whole-label match. Ambiguous labels retain every candidate.
3
- * @typedef {object} NameMatch
4
- * @property {string} key - Canonical page identity, or the ambiguous label identity.
5
- * @property {string} full - Declared full name.
6
- * @property {true} indexed - The identity belongs to the indexed catalog.
7
- * @property {boolean} [fullUse] - The label is a full-name form rather than an alias.
8
- * @property {string} [type] - The note's type, absent on an ambiguous label identity.
9
- * @property {string|null} [subType] - The note's subtype, or null.
10
- * @property {NameMatch[]} [candidates] - Indexed identities sharing the label.
11
- * @property {string} [destination] - Canonical Address used by a resolved link.
12
- */
13
- /**
14
- * The prepared name and declared-alias lookup.
15
- * @typedef {object} NameCatalog
16
- * @property {(label: string, preferred?: {key: string}, kind?: NameKind) => NameMatch|undefined} recognize - Match a whole visible label, among notes of `kind` when it supplies a type.
17
- * @property {(target: string) => NameMatch|undefined} resolve - Resolve an Address target without its anchor to its indexed identity.
18
- */
19
- /**
20
- * What a typed `.name` span says the named thing is. A null type matches any note;
21
- * a type matches only notes of that type, and a subtype only notes of that subtype.
22
- * @typedef {object} NameKind
23
- * @property {string|null} type - Supplied type, or null.
24
- * @property {string|null} subType - Supplied subtype, or null.
25
- */
26
- /**
27
- * One captured marked-name occurrence.
28
- * @typedef {object} MarkedName
29
- * @property {string} name - Rendered visible text.
30
- * @property {string|null} type - Supplied valid type, or null.
31
- * @property {string|null} subType - Supplied valid subtype, or null.
32
- * @property {number} line - Original 1-based file line.
33
- * @property {number} column - Original 1-based UTF-16 file column.
34
- */
35
- /**
36
- * One reported source use.
37
- * @typedef {object} NameUse
38
- * @property {string|null} owner - Canonical NOTE owner, or null for an unaddressed record.
39
- * @property {string|undefined} file - Stored path relative to the content root.
40
- * @property {number} line - Original 1-based file line.
41
- * @property {number} column - Original 1-based UTF-16 file column.
42
- */
43
- /**
44
- * Unwritten names sharing one supplied kind.
45
- * @typedef {object} UnwrittenNameGroup
46
- * @property {string|null} type - Supplied type, or null.
47
- * @property {string|null} subType - Supplied subtype, or null.
48
- * @property {Array<{name: string, uses: NameUse[]}>} names - Normalized labels and every source use.
49
- */
50
- /**
51
- * A parsed span with its original source sites and visible text.
52
- * @typedef {object} ParsedNameSpan
53
- * @property {string} visible - Text from the actual parsed children.
54
- * @property {ReadonlyArray<readonly [string, string]>} attrs - Retained permitted attributes.
55
- * @property {number} line - Original 1-based file line of the opening bracket.
56
- * @property {number} column - Original 1-based UTF-16 file column of the bracket.
57
- * @property {Readonly<Record<string, {key: {line: number, column: number}}>>} [attributePositions] - Actual attribute-key sites.
58
- */
59
- /**
60
- * One invalid supplied name kind.
61
- * @typedef {object} NameKindFinding
62
- * @property {string} [file] - Authored source file.
63
- * @property {number} [line] - Actual attribute-key line when supplied by the parser.
64
- * @property {number} [column] - Actual attribute-key UTF-16 column when known.
65
- * @property {"error"} severity - Invalid supplied values fail validation.
66
- * @property {string} message - The invalid value and the declaration it must follow.
67
- */
68
- /**
69
- * A marked name whose supplied kinds disagree.
70
- * @typedef {object} NameConflict
71
- * @property {string} name - Normalized visible label.
72
- * @property {Array<{type: string, subType: string|null, uses: NameUse[]}>} kinds - Every supplied typed use; untyped uses are excluded.
73
- */
74
- /**
75
- * The current package's unwritten names and incompatible supplied facts.
76
- * @typedef {object} NamesReport
77
- * @property {string} package - Owning content package.
78
- * @property {UnwrittenNameGroup[]} unwritten - Unresolved labels grouped by supplied kind.
79
- * @property {NameConflict[]} conflicts - Labels with incompatible supplied kinds.
80
- */
81
- /**
82
- * Normalize a whole visible label without folding its case.
83
- * @param {string} text - Visible name or declared alias.
84
- * @returns {string} NFC text with straight apostrophes and single spaces.
85
- */
86
- export function normalizeName(text: string): string;
87
- /**
88
- * Whole-label possessives and regular plurals; other forms are declared aliases.
89
- * @param {string} text - Full name or declared alias.
90
- * @returns {string[]} Supported whole-label forms.
2
+ * Whole-label possessives and regular plurals, as matching keys; any other
3
+ * form is one a note's name lists.
4
+ * @param {string} text - Full name or a listed form.
5
+ * @returns {string[]} Supported whole-label forms, case folded.
91
6
  */
92
7
  export function nameForms(text: string): string[];
93
8
  /**
94
- * Prepare full names, declared aliases and link identities from captured records.
9
+ * Prepare full names, every listed form and link identities from captured records.
95
10
  * @param {object} options - The invocation's existing corpus and link index.
96
11
  * @param {readonly object[]} options.records - Captured local records.
97
12
  * @param {ReturnType<typeof import("./content-links.mjs").buildLinkIndex>} options.index - Captured address space.
@@ -181,7 +96,7 @@ export type NameMatch = {
181
96
  */
182
97
  indexed: true;
183
98
  /**
184
- * - The label is a full-name form rather than an alias.
99
+ * - The label is a form of the full name rather than a listed form.
185
100
  */
186
101
  fullUse?: boolean | undefined;
187
102
  /**
@@ -202,7 +117,7 @@ export type NameMatch = {
202
117
  destination?: string | undefined;
203
118
  };
204
119
  /**
205
- * The prepared name and declared-alias lookup.
120
+ * The prepared lookup of full names and listed forms.
206
121
  */
207
122
  export type NameCatalog = {
208
123
  /**
@@ -246,6 +161,10 @@ export type MarkedName = {
246
161
  * - Supplied valid subtype, or null.
247
162
  */
248
163
  subType: string | null;
164
+ /**
165
+ * - The `ref=` as written, a note address or a `#key`, or null.
166
+ */
167
+ ref: string | null;
249
168
  /**
250
169
  * - Original 1-based file line.
251
170
  */
@@ -385,3 +304,4 @@ export type NamesReport = {
385
304
  */
386
305
  conflicts: NameConflict[];
387
306
  };
307
+ export { displayName, normalizeName } from "./name-forms.mjs";
@@ -1,7 +1,4 @@
1
1
  /** Return authored keys from an opening YAML frontmatter map. */
2
2
  export function authoredNoteKeys(markdown: any): string[];
3
- /** The name properties every note accepts, in display order. */
4
- export const NOTE_NAME_KEYS: readonly string[];
5
- /** Additional name components accepted by an individual character. */
6
- export const CHARACTER_NAME_KEYS: readonly string[];
7
3
  export { NOTE_TOP_LEVEL_FIELDS, NOTE_TOP_LEVEL_KEYS, NOTE_TOP_LEVEL_KEY_SET } from "./note-vocabulary.mjs";
4
+ export { CHARACTER_NAME_KEYS, NOTE_NAME_KEYS } from "./name-forms.mjs";
@@ -0,0 +1,47 @@
1
+ /**
2
+ * Write each note's language onto its record as `lang`, and each configured
3
+ * language note's code onto its record as `languageCode`.
4
+ *
5
+ * Reads each note's authored `data.lang`, `data.culture` and `data.parents`,
6
+ * so it runs before the index writes a resolved culture over `data.culture`.
7
+ *
8
+ * @param {Array<Record<string, any>>} records - Index records, every Address a
9
+ * tuple. Records that are not notes are passed over.
10
+ * @param {object} options - Options.
11
+ * @param {string} options.contentPackage - The package the records belong to.
12
+ * @param {Readonly<Record<string, {note?: string}>>} [options.languages] - The
13
+ * configured languages, by code.
14
+ * @param {string} [options.contentBase] - The content tree, for opening a note
15
+ * to locate a finding.
16
+ * @param {(key: string) => Record<string, any>|undefined} [options.foreignRecord] -
17
+ * Another package's entry for a canonical address.
18
+ * @param {object[]} [options.problems] - Receives each finding. Omitted, the
19
+ * findings are collected and one Error lists them all.
20
+ * @param {Map<string, {raw: string}>} [options.parsed] - Notes already read, keyed by file.
21
+ * @returns {Array<Record<string, any>>} The same records.
22
+ * @throws {Error} When `problems` is omitted and anything is reported.
23
+ */
24
+ export function resolveNoteLanguages(records: Array<Record<string, any>>, { contentPackage, languages, contentBase, foreignRecord, problems, parsed }: {
25
+ contentPackage: string;
26
+ languages?: Readonly<Record<string, {
27
+ note?: string;
28
+ }>> | undefined;
29
+ contentBase?: string | undefined;
30
+ foreignRecord?: ((key: string) => Record<string, any> | undefined) | undefined;
31
+ problems?: object[] | undefined;
32
+ parsed?: Map<string, {
33
+ raw: string;
34
+ }> | undefined;
35
+ }): Array<Record<string, any>>;
36
+ /**
37
+ * Each language code's note, read from records alone: the Address of every
38
+ * record carrying a `languageCode`.
39
+ *
40
+ * @param {Iterable<Record<string, any>>} records - Index records, local and
41
+ * any dependency's, or link-index notes carrying them.
42
+ * @param {(record: Record<string, any>) => string|undefined} addressOf - A
43
+ * record's Address, as the caller keys notes.
44
+ * @returns {Map<string, string>} Code to the language note's Address; the first
45
+ * record naming a code wins.
46
+ */
47
+ export function languageNotesByCode(records: Iterable<Record<string, any>>, addressOf: (record: Record<string, any>) => string | undefined): Map<string, string>;
@@ -127,7 +127,7 @@ export type PlaceNode = {
127
127
  subType: string;
128
128
  title: string;
129
129
  /**
130
- * - Its name and aliases, lower case.
130
+ * - Its name and listed forms, lower case.
131
131
  */
132
132
  names: Set<string>;
133
133
  /**
@@ -149,9 +149,6 @@ export function tableUniverse(pages: object[]): Map<string, object[]>;
149
149
  /**
150
150
  * The frontmatter a page publishes with.
151
151
  *
152
- * An authored `aliases` is a list of names a reader might call the note. It is
153
- * dropped: this build emits no redirects.
154
- *
155
152
  * A content page carries the package the build **derived**. No note
156
153
  * declares one, so the note's frontmatter alone would publish a page that does
157
154
  * not say which package it belongs to. Writing the derived value keeps a page
@@ -160,9 +157,8 @@ export function tableUniverse(pages: object[]): Map<string, object[]>;
160
157
  *
161
158
  * **`related` is derived, never authored.** What links to a page is a fact
162
159
  * about every other page, known only once the whole tree has resolved — see
163
- * {@link module:engine/related-pages} — so an authored value is dropped the
164
- * way `aliases` is, and {@link renderPages} sets the derived block once it
165
- * holds the graph.
160
+ * {@link module:engine/related-pages} — so an authored value is dropped, and
161
+ * {@link renderPages} sets the derived block once it holds the graph.
166
162
  *
167
163
  * @param {object} page - The page.
168
164
  * @param {object} options