@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.
- package/CHANGELOG.md +75 -0
- package/CONTENT.md +111 -92
- package/README.md +2 -9
- package/bin/content-build.mjs +70 -61
- package/bin/package-build.mjs +2 -2
- package/config.mjs +6 -8
- package/content-config.mjs +187 -64
- package/docs/content-format.md +57 -68
- package/e2e.mjs +4 -4
- package/engine/actor-compiler.mjs +36 -36
- package/engine/address-charset.mjs +6 -6
- package/engine/address-diff.mjs +12 -11
- package/engine/anchored-sections.mjs +1 -1
- package/engine/anchors.mjs +1 -1
- package/engine/base-compiler.mjs +36 -38
- package/engine/bundle-notes.mjs +4 -4
- package/engine/bundles.mjs +8 -8
- package/engine/code-fences.mjs +3 -3
- package/engine/compendiums.mjs +8 -8
- package/engine/compile-corpus.mjs +4 -5
- package/engine/content-address.mjs +15 -15
- package/engine/content-charset.mjs +1 -1
- package/engine/content-format-check.mjs +24 -25
- package/engine/content-format.mjs +8 -8
- package/engine/content-html.mjs +202 -0
- package/engine/content-icons.mjs +196 -165
- package/engine/content-index.mjs +17 -17
- package/engine/content-links.mjs +30 -30
- package/engine/content-lint.mjs +20 -24
- package/engine/content-package.mjs +7 -9
- package/engine/content-slug.mjs +2 -2
- package/engine/content-tables.mjs +7 -8
- package/engine/content-tree.mjs +1 -1
- package/engine/derived-fields.mjs +174 -0
- package/engine/diagnostics.mjs +3 -4
- package/engine/document-subtypes.mjs +6 -7
- package/engine/field-reference.mjs +8 -8
- package/engine/field-spec.mjs +29 -30
- package/engine/folder-notes.mjs +14 -14
- package/engine/foreign-catalog.mjs +8 -9
- package/engine/foundry-entries.mjs +19 -20
- package/engine/frontmatter-lint.mjs +64 -69
- package/engine/frontmatter.mjs +11 -11
- package/engine/generate.mjs +39 -39
- package/engine/helpers.mjs +63 -54
- package/engine/homepage.mjs +20 -22
- package/engine/ids.mjs +15 -15
- package/engine/index-records.mjs +4 -4
- package/engine/index.mjs +14 -8
- package/engine/item-compiler.mjs +54 -29
- package/engine/item-docs.mjs +11 -13
- package/engine/item-registry.mjs +13 -13
- package/engine/journals.mjs +15 -15
- package/engine/macros.mjs +3 -3
- package/engine/map-notes.mjs +6 -6
- package/engine/metadata-index.mjs +5 -5
- package/engine/note-claims.mjs +36 -34
- package/engine/note-ids.mjs +5 -5
- package/engine/note-package.mjs +4 -4
- package/engine/note-renames.mjs +3 -3
- package/engine/note-schemas.mjs +3 -3
- package/engine/note-vocabulary.mjs +27 -26
- package/engine/pack-config.mjs +13 -13
- package/engine/pack-router.mjs +0 -0
- package/engine/prose-config.mjs +9 -9
- package/engine/prose-lint.mjs +12 -13
- package/engine/region-events.mjs +2 -2
- package/engine/retired-fields.mjs +24 -22
- package/engine/runtime-only-fields.mjs +1 -1
- package/engine/scene-levels.mjs +1 -1
- package/engine/scenes.mjs +19 -20
- package/engine/schema-check.mjs +20 -20
- package/engine/schema-extract.mjs +1 -1
- package/engine/site-build.mjs +45 -47
- package/engine/site-index.mjs +17 -18
- package/engine/sql-tables.mjs +21 -13
- package/engine/subtype-registry.mjs +5 -4
- package/engine/system-block.mjs +18 -19
- package/engine/systems.mjs +2 -2
- package/engine/web-wikilinks.mjs +35 -35
- package/engine/wikilink-syntax.mjs +16 -16
- package/engine/wikilinks.mjs +39 -39
- package/hm3/actors.mjs +22 -22
- package/hm3/document-subtypes.mjs +5 -5
- package/hm3/index.mjs +2 -2
- package/hm3/item-builders.mjs +1 -1
- package/hm3/item-fields.mjs +3 -3
- package/hm3/items.mjs +20 -8
- package/hm3/template-priority.mjs +2 -2
- package/lang.mjs +3 -3
- package/manifest.mjs +17 -20
- package/package.json +1 -2
- package/release.mjs +3 -3
- package/sohl/actors.mjs +28 -28
- package/sohl/affiliation-standings.mjs +2 -2
- package/sohl/being-info.mjs +5 -5
- package/sohl/default-item-art.mjs +5 -5
- package/sohl/document-subtypes.mjs +5 -5
- package/sohl/index.mjs +3 -3
- package/sohl/item-builders.mjs +7 -7
- package/sohl/item-fields.mjs +8 -9
- package/sohl/items.mjs +20 -6
- package/sohl/kb-passes.mjs +5 -5
- package/sohl/note-schemas.mjs +7 -7
- package/sohl/skill-base.mjs +1 -1
- package/types/content-config.d.mts +25 -24
- package/types/e2e.d.mts +3 -3
- package/types/engine/actor-compiler.d.mts +14 -13
- package/types/engine/address-charset.d.mts +6 -6
- package/types/engine/address-diff.d.mts +3 -3
- package/types/engine/base-compiler.d.mts +17 -19
- package/types/engine/bundle-notes.d.mts +3 -3
- package/types/engine/bundles.d.mts +1 -1
- package/types/engine/code-fences.d.mts +3 -3
- package/types/engine/compendiums.d.mts +3 -3
- package/types/engine/compile-corpus.d.mts +1 -1
- package/types/engine/content-address.d.mts +11 -11
- package/types/engine/content-format-check.d.mts +6 -6
- package/types/engine/content-format.d.mts +2 -2
- package/types/engine/content-html.d.mts +78 -0
- package/types/engine/content-icons.d.mts +139 -92
- package/types/engine/content-index.d.mts +2 -2
- package/types/engine/content-links.d.mts +8 -8
- package/types/engine/content-lint.d.mts +2 -2
- package/types/engine/content-package.d.mts +6 -8
- package/types/engine/derived-fields.d.mts +101 -0
- package/types/engine/diagnostics.d.mts +2 -2
- package/types/engine/document-subtypes.d.mts +3 -3
- package/types/engine/field-spec.d.mts +37 -39
- package/types/engine/folder-notes.d.mts +4 -5
- package/types/engine/foreign-catalog.d.mts +3 -3
- package/types/engine/foundry-entries.d.mts +6 -7
- package/types/engine/frontmatter-lint.d.mts +7 -7
- package/types/engine/frontmatter.d.mts +7 -7
- package/types/engine/generate.d.mts +7 -7
- package/types/engine/helpers.d.mts +25 -27
- package/types/engine/homepage.d.mts +16 -18
- package/types/engine/ids.d.mts +13 -13
- package/types/engine/index-records.d.mts +3 -3
- package/types/engine/index.d.mts +2 -0
- package/types/engine/item-compiler.d.mts +21 -5
- package/types/engine/item-docs.d.mts +2 -2
- package/types/engine/item-registry.d.mts +6 -6
- package/types/engine/journals.d.mts +3 -3
- package/types/engine/map-notes.d.mts +2 -2
- package/types/engine/metadata-index.d.mts +4 -4
- package/types/engine/note-claims.d.mts +16 -15
- package/types/engine/note-ids.d.mts +4 -4
- package/types/engine/note-package.d.mts +1 -1
- package/types/engine/note-renames.d.mts +3 -3
- package/types/engine/note-vocabulary.d.mts +9 -8
- package/types/engine/pack-config.d.mts +3 -3
- package/types/engine/pack-router.d.mts +1 -1
- package/types/engine/prose-config.d.mts +9 -9
- package/types/engine/prose-lint.d.mts +3 -4
- package/types/engine/region-events.d.mts +2 -2
- package/types/engine/retired-fields.d.mts +10 -9
- package/types/engine/schema-check.d.mts +11 -11
- package/types/engine/site-build.d.mts +18 -19
- package/types/engine/site-index.d.mts +3 -4
- package/types/engine/sql-tables.d.mts +11 -5
- package/types/engine/subtype-registry.d.mts +3 -3
- package/types/engine/system-block.d.mts +3 -3
- package/types/engine/web-wikilinks.d.mts +7 -7
- package/types/engine/wikilink-syntax.d.mts +17 -17
- package/types/engine/wikilinks.d.mts +13 -12
- package/types/hm3/actors.d.mts +1 -1
- package/types/hm3/document-subtypes.d.mts +1 -1
- package/types/hm3/items.d.mts +1 -2
- package/types/hm3/template-priority.d.mts +1 -1
- package/types/manifest.d.mts +8 -8
- package/types/sohl/actors.d.mts +2 -2
- package/types/sohl/affiliation-standings.d.mts +2 -2
- package/types/sohl/being-info.d.mts +2 -2
- package/types/sohl/document-subtypes.d.mts +1 -1
- package/types/sohl/items.d.mts +1 -2
- package/types/sohl/note-schemas.d.mts +1 -1
- package/MIGRATING.md +0 -608
|
@@ -2,11 +2,13 @@
|
|
|
2
2
|
* The family an entry draws from, named or defaulted.
|
|
3
3
|
*
|
|
4
4
|
* @param {{family?: string}} entry - A registry entry.
|
|
5
|
-
* @
|
|
5
|
+
* @param {IconRegistry} [registry] - The registry it came from.
|
|
6
|
+
* @returns {string|undefined} The family name, or nothing when neither the
|
|
7
|
+
* entry nor the registry says.
|
|
6
8
|
*/
|
|
7
9
|
export function familyOf(entry: {
|
|
8
10
|
family?: string;
|
|
9
|
-
}): string;
|
|
11
|
+
}, registry?: IconRegistry): string | undefined;
|
|
10
12
|
/**
|
|
11
13
|
* Read the brace of an icon token.
|
|
12
14
|
*
|
|
@@ -26,15 +28,11 @@ export function parseIconAttributes(raw?: string): {
|
|
|
26
28
|
* Look one name up.
|
|
27
29
|
*
|
|
28
30
|
* @param {string} name - The name written between the colons, without `icon-`.
|
|
29
|
-
* @param {
|
|
30
|
-
* @returns {
|
|
31
|
-
*
|
|
31
|
+
* @param {IconRegistry} [registry] - The package's registry.
|
|
32
|
+
* @returns {object|null} The entry, or `null` when the registry does not
|
|
33
|
+
* declare it.
|
|
32
34
|
*/
|
|
33
|
-
export function resolveIcon(name: string, registry?:
|
|
34
|
-
style: string;
|
|
35
|
-
icon: string;
|
|
36
|
-
label: string;
|
|
37
|
-
} | null;
|
|
35
|
+
export function resolveIcon(name: string, registry?: IconRegistry): object | null;
|
|
38
36
|
/**
|
|
39
37
|
* The HTML the journals and the website emit — what the system already renders.
|
|
40
38
|
*
|
|
@@ -43,14 +41,13 @@ export function resolveIcon(name: string, registry?: Record<string, object>): {
|
|
|
43
41
|
* an icon dropped into a sentence has no such parent, and "the ☆ toggles it"
|
|
44
42
|
* read aloud as "the toggles it" is a sentence with a hole in it.
|
|
45
43
|
*
|
|
46
|
-
* @param {
|
|
44
|
+
* @param {object} entry - A registry entry.
|
|
45
|
+
* @param {Record<string, string>} [attrs] - The token's attributes.
|
|
46
|
+
* @param {IconRegistry} [registry] - The registry the entry came from, which is
|
|
47
|
+
* what says how its family spells a class.
|
|
47
48
|
* @returns {string} An `<i>` element.
|
|
48
49
|
*/
|
|
49
|
-
export function iconHtml(entry:
|
|
50
|
-
style: string;
|
|
51
|
-
icon: string;
|
|
52
|
-
label: string;
|
|
53
|
-
}, attrs?: {}): string;
|
|
50
|
+
export function iconHtml(entry: object, attrs?: Record<string, string>, registry?: IconRegistry): string;
|
|
54
51
|
/**
|
|
55
52
|
* Every icon a string names, in the order written.
|
|
56
53
|
*
|
|
@@ -71,11 +68,11 @@ export function iconsIn(text: string): Array<{
|
|
|
71
68
|
*
|
|
72
69
|
* @param {string} text - The file's contents.
|
|
73
70
|
* @param {string} file - Path to report.
|
|
74
|
-
* @param {
|
|
71
|
+
* @param {IconRegistry} [registry] - The package's registry.
|
|
75
72
|
* @returns {Array<{file: string, line: number, column: number,
|
|
76
73
|
* severity: "warning", message: string}>} The unknown names.
|
|
77
74
|
*/
|
|
78
|
-
export function lintIcons(text: string, file: string, registry?:
|
|
75
|
+
export function lintIcons(text: string, file: string, registry?: IconRegistry): Array<{
|
|
79
76
|
file: string;
|
|
80
77
|
line: number;
|
|
81
78
|
column: number;
|
|
@@ -83,13 +80,27 @@ export function lintIcons(text: string, file: string, registry?: Record<string,
|
|
|
83
80
|
message: string;
|
|
84
81
|
}>;
|
|
85
82
|
/**
|
|
86
|
-
*
|
|
83
|
+
* What is wrong with a package's declared registry.
|
|
87
84
|
*
|
|
88
|
-
*
|
|
85
|
+
* Both halves are checked, because either alone is unusable: an icon naming a
|
|
86
|
+
* family nothing declares has no class prefix, and a family nothing names is a
|
|
87
|
+
* font declared for no reason.
|
|
88
|
+
*
|
|
89
|
+
* A **style** is checked against the family's own `styles`, not against a list
|
|
90
|
+
* here. Font Awesome Free ships three weights and another font ships none or
|
|
91
|
+
* five, and only the declaration knows which — so a `duotone` entry is refused
|
|
92
|
+
* because the family that entry belongs to does not list `duotone`, which is a
|
|
93
|
+
* statement the consumer made about the font it actually ships.
|
|
94
|
+
*
|
|
95
|
+
* @param {{families?: object, icons?: object}} registry - A package's declared
|
|
96
|
+
* registry, before it is resolved.
|
|
89
97
|
* @param {string} [where="icons"] - Where to say the fault is.
|
|
90
98
|
* @returns {Array<{severity: "warning", message: string}>} What is wrong with it.
|
|
91
99
|
*/
|
|
92
|
-
export function checkIconRegistry(registry:
|
|
100
|
+
export function checkIconRegistry(registry: {
|
|
101
|
+
families?: object;
|
|
102
|
+
icons?: object;
|
|
103
|
+
}, where?: string): Array<{
|
|
93
104
|
severity: "warning";
|
|
94
105
|
message: string;
|
|
95
106
|
}>;
|
|
@@ -103,13 +114,13 @@ export function checkIconRegistry(registry: Record<string, object>, where?: stri
|
|
|
103
114
|
* @param {string} contentBase - Root of the content tree.
|
|
104
115
|
* @param {object} [opts]
|
|
105
116
|
* @param {readonly string[]} [opts.skipDirectories] - Directory names to ignore.
|
|
106
|
-
* @param {
|
|
117
|
+
* @param {IconRegistry} [opts.registry] - The package's registry.
|
|
107
118
|
* @returns {{findings: Array<{file: string, line: number, column: number,
|
|
108
119
|
* severity: "warning", message: string}>, files: number}} What it found.
|
|
109
120
|
*/
|
|
110
121
|
export function lintContentIcons(contentBase: string, { skipDirectories, registry }?: {
|
|
111
122
|
skipDirectories?: readonly string[] | undefined;
|
|
112
|
-
registry?:
|
|
123
|
+
registry?: IconRegistry | undefined;
|
|
113
124
|
}): {
|
|
114
125
|
findings: Array<{
|
|
115
126
|
file: string;
|
|
@@ -127,52 +138,69 @@ export function lintContentIcons(contentBase: string, { skipDirectories, registr
|
|
|
127
138
|
* is reported by {@link lintIcons}, and a rendered page that still shows
|
|
128
139
|
* `:icon-stra:` is how the author finds it without reading a log.
|
|
129
140
|
*
|
|
130
|
-
*
|
|
141
|
+
* **A function is accepted as well as a table**, and resolved per render. The
|
|
142
|
+
* shared markdown-it instance is a module-level constant, so it is built before
|
|
143
|
+
* any configuration is read; a getter lets it draw the package's own registry
|
|
144
|
+
* without the module load order deciding whether that registry exists yet.
|
|
145
|
+
*
|
|
146
|
+
* @param {Record<string, object>|(() => Record<string, object>)} [registry] -
|
|
147
|
+
* The registry, or something that returns it.
|
|
131
148
|
* @returns {(md: object) => void} A markdown-it plugin.
|
|
132
149
|
*/
|
|
133
|
-
export function iconPlugin(registry?: Record<string, object>): (md: object) => void;
|
|
150
|
+
export function iconPlugin(registry?: Record<string, object> | (() => Record<string, object>)): (md: object) => void;
|
|
134
151
|
/**
|
|
135
|
-
*
|
|
136
|
-
*
|
|
137
|
-
*
|
|
138
|
-
*
|
|
139
|
-
*
|
|
140
|
-
*
|
|
141
|
-
*
|
|
152
|
+
* A **family** is an icon font, and a consumer declares the ones it ships.
|
|
153
|
+
*
|
|
154
|
+
* Nothing here names Font Awesome, or Game-Icons, or any other font. A registry
|
|
155
|
+
* entry is a promise that a glyph will render, and only the package that ships
|
|
156
|
+
* the font can keep it: the Game-Icons webfont is built by a consumer from its
|
|
157
|
+
* own templates, and even Font Awesome — which Foundry supplies in-app — is
|
|
158
|
+
* present on neither the knowledgebase nor the page of a book unless somebody
|
|
159
|
+
* puts it there. A toolchain that shipped a table would be promising on a
|
|
160
|
+
* consumer's behalf.
|
|
161
|
+
*
|
|
162
|
+
* The vocabulary is the consumer's for a second reason, independent of fonts. A
|
|
163
|
+
* name like `victory-star-tester` is one game system's concept; another system
|
|
164
|
+
* compiled by this same toolchain has different icons meaning different things.
|
|
165
|
+
* What is shared is the *mechanism* — the syntax, the resolution, the checks —
|
|
166
|
+
* and that is what lives here.
|
|
167
|
+
*
|
|
168
|
+
* A family declares:
|
|
169
|
+
*
|
|
170
|
+
* - `class` — the prefix its stylesheet uses (`fa`, `ginf`, `bi`).
|
|
171
|
+
* - `styles` — the weights it ships, in the spelling its classes use. An empty
|
|
172
|
+
* list means the font has none, and then a `style` on an entry names
|
|
173
|
+
* something that does not exist and is reported rather than rendered.
|
|
174
|
+
* - `describe` — one line, so a finding can say which font it means.
|
|
175
|
+
*
|
|
176
|
+
* @typedef {object} IconFamily
|
|
177
|
+
* @property {string} class - The stylesheet's class prefix.
|
|
178
|
+
* @property {readonly string[]} styles - The weights it ships; empty for none.
|
|
179
|
+
* @property {string} describe - One line, for a finding.
|
|
142
180
|
*/
|
|
143
|
-
export const ICON_STYLES: readonly string[];
|
|
144
181
|
/**
|
|
145
|
-
*
|
|
146
|
-
*
|
|
147
|
-
* **Two, because the interface uses two.** The SoHL icon legend says so in its
|
|
148
|
-
* own prose: Font Awesome for most things, and Game-Icons.net *"for the arms,
|
|
149
|
-
* gear, and condition glyphs that Font Awesome does not cover"* — eighteen of
|
|
150
|
-
* them, `ginf-broadsword` and its kin (#391).
|
|
182
|
+
* A resolved registry: the families a package ships, and the icons it names.
|
|
151
183
|
*
|
|
152
|
-
*
|
|
153
|
-
*
|
|
184
|
+
* `defaultFamily` is what an entry that names none belongs to. It is optional,
|
|
185
|
+
* and where a package declares exactly one family that one is it — so a
|
|
186
|
+
* single-font package writes no `family` anywhere.
|
|
154
187
|
*
|
|
155
|
-
*
|
|
156
|
-
*
|
|
157
|
-
*
|
|
158
|
-
*
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
*
|
|
188
|
+
* @typedef {object} IconRegistry
|
|
189
|
+
* @property {Readonly<Record<string, IconFamily>>} families
|
|
190
|
+
* @property {string|undefined} defaultFamily
|
|
191
|
+
* @property {Readonly<Record<string, object>>} icons
|
|
192
|
+
*/
|
|
193
|
+
/**
|
|
194
|
+
* The registry a package that declares none gets: nothing at all.
|
|
162
195
|
*
|
|
163
|
-
*
|
|
164
|
-
*
|
|
165
|
-
*
|
|
196
|
+
* Empty rather than a starter set, because a starter set is a promise about
|
|
197
|
+
* fonts this package does not ship. A tree with no `icons:` configured names no
|
|
198
|
+
* icons, and `:icon-star:` in one of its notes renders as its own literal text
|
|
199
|
+
* and is reported — which is the visible failure, not a silent one.
|
|
166
200
|
*
|
|
167
|
-
* @type {
|
|
201
|
+
* @type {IconRegistry}
|
|
168
202
|
*/
|
|
169
|
-
export const
|
|
170
|
-
class: string;
|
|
171
|
-
styled: boolean;
|
|
172
|
-
describe: string;
|
|
173
|
-
}>>;
|
|
174
|
-
/** The family an entry that does not name one belongs to. */
|
|
175
|
-
export const DEFAULT_ICON_FAMILY: "fontawesome";
|
|
203
|
+
export const EMPTY_ICON_REGISTRY: IconRegistry;
|
|
176
204
|
/**
|
|
177
205
|
* The sizes a note may ask for, and what each means on a page.
|
|
178
206
|
*
|
|
@@ -208,38 +236,6 @@ export const ICON_ATTRIBUTES: Readonly<Record<string, {
|
|
|
208
236
|
values: readonly string[];
|
|
209
237
|
describe: string;
|
|
210
238
|
}>>;
|
|
211
|
-
/**
|
|
212
|
-
* The icons the user guide already depicts, under the names it should call them.
|
|
213
|
-
*
|
|
214
|
-
* Each entry is read off the interface it describes rather than invented, and
|
|
215
|
-
* that is meant literally — the table was checked against the system's own
|
|
216
|
-
* templates, which is how `delete` came to be `fa-trash` rather than the
|
|
217
|
-
* `fa-trash-can` first written here. The sheets draw `fa-trash` twenty-five
|
|
218
|
-
* times and `fa-trash-can` never, so the first spelling would have printed an
|
|
219
|
-
* icon the reader has never seen on screen. A registry that is not checked
|
|
220
|
-
* against the interface is just a second place to be wrong.
|
|
221
|
-
*
|
|
222
|
-
* The names are what a *writer* would reach for — `delete`, not `trash` —
|
|
223
|
-
* because the writer is the one typing them; the Font Awesome spelling is this
|
|
224
|
-
* table's business, not theirs.
|
|
225
|
-
*
|
|
226
|
-
* **Three entries share `xmark`, and that is the point of naming rather than
|
|
227
|
-
* drawing.** A `✕` in the guide means "not applicable" in a Healing Rate
|
|
228
|
-
* column, "remove this row" on a control, and "close" on a dialog's corner. One
|
|
229
|
-
* glyph, three sentences, three different things for a reader who cannot see
|
|
230
|
-
* it — so they are three names with three labels, and the fact that Font
|
|
231
|
-
* Awesome happens to draw them identically stays in this table.
|
|
232
|
-
*
|
|
233
|
-
* `run` and `expand` are likewise distinct: `▶` runs an action, and its label
|
|
234
|
-
* should say so. `fa-play` is what the sheets use for it.
|
|
235
|
-
*
|
|
236
|
-
* @type {Readonly<Record<string, {style: string, icon: string, label: string}>>}
|
|
237
|
-
*/
|
|
238
|
-
export const DEFAULT_ICONS: Readonly<Record<string, {
|
|
239
|
-
style: string;
|
|
240
|
-
icon: string;
|
|
241
|
-
label: string;
|
|
242
|
-
}>>;
|
|
243
239
|
/**
|
|
244
240
|
* The shape a note writes, and the one this module claims.
|
|
245
241
|
*
|
|
@@ -247,7 +243,7 @@ export const DEFAULT_ICONS: Readonly<Record<string, {
|
|
|
247
243
|
* surface that also renders `:smile:` can tell the two apart without a lookup,
|
|
248
244
|
* and a reader can tell what `:icon-star:` is without knowing this module
|
|
249
245
|
* exists. Names are lowercase, digits and hyphens — the charset an address
|
|
250
|
-
* segment already uses
|
|
246
|
+
* segment already uses, so nothing new has to be explained.
|
|
251
247
|
*
|
|
252
248
|
* Not `:name[content]`. That is remark-directive syntax, and this toolchain
|
|
253
249
|
* parses with markdown-it; a directive would render as its own literal text.
|
|
@@ -270,3 +266,54 @@ export const DEFAULT_ICONS: Readonly<Record<string, {
|
|
|
270
266
|
* @type {RegExp}
|
|
271
267
|
*/
|
|
272
268
|
export const ICON_PATTERN: RegExp;
|
|
269
|
+
/**
|
|
270
|
+
* A **family** is an icon font, and a consumer declares the ones it ships.
|
|
271
|
+
*
|
|
272
|
+
* Nothing here names Font Awesome, or Game-Icons, or any other font. A registry
|
|
273
|
+
* entry is a promise that a glyph will render, and only the package that ships
|
|
274
|
+
* the font can keep it: the Game-Icons webfont is built by a consumer from its
|
|
275
|
+
* own templates, and even Font Awesome — which Foundry supplies in-app — is
|
|
276
|
+
* present on neither the knowledgebase nor the page of a book unless somebody
|
|
277
|
+
* puts it there. A toolchain that shipped a table would be promising on a
|
|
278
|
+
* consumer's behalf.
|
|
279
|
+
*
|
|
280
|
+
* The vocabulary is the consumer's for a second reason, independent of fonts. A
|
|
281
|
+
* name like `victory-star-tester` is one game system's concept; another system
|
|
282
|
+
* compiled by this same toolchain has different icons meaning different things.
|
|
283
|
+
* What is shared is the *mechanism* — the syntax, the resolution, the checks —
|
|
284
|
+
* and that is what lives here.
|
|
285
|
+
*
|
|
286
|
+
* A family declares:
|
|
287
|
+
*
|
|
288
|
+
* - `class` — the prefix its stylesheet uses (`fa`, `ginf`, `bi`).
|
|
289
|
+
* - `styles` — the weights it ships, in the spelling its classes use. An empty
|
|
290
|
+
* list means the font has none, and then a `style` on an entry names
|
|
291
|
+
* something that does not exist and is reported rather than rendered.
|
|
292
|
+
* - `describe` — one line, so a finding can say which font it means.
|
|
293
|
+
*/
|
|
294
|
+
export type IconFamily = {
|
|
295
|
+
/**
|
|
296
|
+
* - The stylesheet's class prefix.
|
|
297
|
+
*/
|
|
298
|
+
class: string;
|
|
299
|
+
/**
|
|
300
|
+
* - The weights it ships; empty for none.
|
|
301
|
+
*/
|
|
302
|
+
styles: readonly string[];
|
|
303
|
+
/**
|
|
304
|
+
* - One line, for a finding.
|
|
305
|
+
*/
|
|
306
|
+
describe: string;
|
|
307
|
+
};
|
|
308
|
+
/**
|
|
309
|
+
* A resolved registry: the families a package ships, and the icons it names.
|
|
310
|
+
*
|
|
311
|
+
* `defaultFamily` is what an entry that names none belongs to. It is optional,
|
|
312
|
+
* and where a package declares exactly one family that one is it — so a
|
|
313
|
+
* single-font package writes no `family` anywhere.
|
|
314
|
+
*/
|
|
315
|
+
export type IconRegistry = {
|
|
316
|
+
families: Readonly<Record<string, IconFamily>>;
|
|
317
|
+
defaultFamily: string | undefined;
|
|
318
|
+
icons: Readonly<Record<string, object>>;
|
|
319
|
+
};
|
|
@@ -120,7 +120,7 @@ export function serializeContentIndex(records: Array<Record<string, any>>): stri
|
|
|
120
120
|
*
|
|
121
121
|
* The half of {@link emitContentIndex} that derives rather than emits, so a
|
|
122
122
|
* pass that needs the corpus in memory — a SQL content table, the link check,
|
|
123
|
-
* and in time every reader
|
|
123
|
+
* and in time every converted reader — builds it the same way the artifact
|
|
124
124
|
* is built, rather than by walking and parsing again with its own idea of the
|
|
125
125
|
* scope.
|
|
126
126
|
*
|
|
@@ -131,7 +131,7 @@ export function serializeContentIndex(records: Array<Record<string, any>>): stri
|
|
|
131
131
|
* caller that resolved one of its own; defaults to the resolved
|
|
132
132
|
* configuration's. Stated separately from `config` because a caller that was
|
|
133
133
|
* *handed* a scope must be able to pass it on rather than have it silently
|
|
134
|
-
* replaced by the one its configuration happens to carry
|
|
134
|
+
* replaced by the one its configuration happens to carry.
|
|
135
135
|
* @param {object[]} [opts.problems] - Supplied by a **reader**: a note that
|
|
136
136
|
* cannot be recorded is pushed here as a diagnostic and skipped, instead of
|
|
137
137
|
* aborting the derivation. Omitted, the note throws — which is the contract
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Every `{#anchor}` a note declares on a heading.
|
|
3
3
|
*
|
|
4
4
|
* **Read from the content index's reader, not a second one.** This module kept
|
|
5
|
-
* its own
|
|
5
|
+
* its own, and the two disagreed: it matched `{#([a-z0-9-]+)}` while
|
|
6
6
|
* {@link module:engine/content-index.collectAnchors} matches `{#([^}]+)}`, so
|
|
7
7
|
* an anchor with a capital in it — `{#CalendarFormat}` — existed for the index
|
|
8
8
|
* and for the compiler and did not exist for the link checker. Nothing links to
|
|
@@ -21,7 +21,7 @@ export function anchorsOf(body: string): Set<string>;
|
|
|
21
21
|
* Read a content tree into the index a link resolves against.
|
|
22
22
|
*
|
|
23
23
|
* **The corpus comes from the content index, not from a walk of this module's
|
|
24
|
-
* own
|
|
24
|
+
* own**. Every pass used to answer "which files are the content?" for
|
|
25
25
|
* itself and throw the answer away; this one now reads
|
|
26
26
|
* {@link module:engine/content-index.indexRecordsFor}, which is the same
|
|
27
27
|
* derivation the published artifact and the compilers are driven from. So a
|
|
@@ -29,7 +29,7 @@ export function anchorsOf(body: string): Set<string>;
|
|
|
29
29
|
* anchors it resolves against are the ones every other pass will emit — rather
|
|
30
30
|
* than a second derivation that agrees with them only by inspection. That was
|
|
31
31
|
* not hypothetical: this module carried its own anchor reader until the anchor
|
|
32
|
-
* half
|
|
32
|
+
* anchor half, and the two disagreed about which anchors existed.
|
|
33
33
|
*
|
|
34
34
|
* **The file is opened for its bytes and nothing else.** The index deliberately
|
|
35
35
|
* carries no note *body*, and a link lives in the body — so each note is read
|
|
@@ -49,7 +49,7 @@ export function anchorsOf(body: string): Set<string>;
|
|
|
49
49
|
* @param {string} contentBase - Root of the content tree.
|
|
50
50
|
* @param {object} [opts]
|
|
51
51
|
* @param {object} [opts.config] - The resolved build configuration, whose
|
|
52
|
-
* fetched dependency indexes foreign addresses resolve through
|
|
52
|
+
* fetched dependency indexes foreign addresses resolve through, and
|
|
53
53
|
* whose `contentPackage` every local address is built from. Omitted, the
|
|
54
54
|
* ambient configuration is resolved and no cross-package address resolves.
|
|
55
55
|
* @param {readonly string[]} [opts.skipDirectories] - The walk's scope, passed
|
|
@@ -87,13 +87,13 @@ export function buildLinkIndex(contentBase: string, { config, skipDirectories, s
|
|
|
87
87
|
* **What is checkable, stated plainly.** Only an address into this site is, and
|
|
88
88
|
* only against facts this build already holds:
|
|
89
89
|
*
|
|
90
|
-
* - A **retired content type** in the path. The engine knows
|
|
90
|
+
* - A **retired content type** in the path. The engine knows the retired names
|
|
91
91
|
* and what replaced it, so this is a fact rather than a guess — and it is
|
|
92
92
|
* exactly the SoHL defect.
|
|
93
93
|
* - A **hardcoded absolute URL** into this package's own prefix, or into one a
|
|
94
94
|
* a fetched index names. Every one of them has a better form to write, which
|
|
95
95
|
* is why every one is reported — including a bare `/<package>/`, which names
|
|
96
|
-
* another package's landing
|
|
96
|
+
* another package's landing.
|
|
97
97
|
*
|
|
98
98
|
* That last case was exempt until the better form was identified, on the
|
|
99
99
|
* reasoning that a landing is in no link manifest so nothing could resolve it.
|
|
@@ -133,7 +133,7 @@ export function auditHomepageLinks(index: ReturnType<typeof buildLinkIndex>): Ar
|
|
|
133
133
|
*
|
|
134
134
|
* **How the link is *written* is a separate finding from where it points**, and
|
|
135
135
|
* the two are kept apart because the corrections differ. An unlabelled link
|
|
136
|
-
*
|
|
136
|
+
* has to become `[[type-shortcode|Text]]`; a labelled one whose target
|
|
137
137
|
* resolves nowhere has a shortcode to fix. Reporting a bare `[[Name]]` as a
|
|
138
138
|
* dead address would send an author hunting for a note that was never named.
|
|
139
139
|
*
|
|
@@ -145,7 +145,7 @@ export function auditHomepageLinks(index: ReturnType<typeof buildLinkIndex>): Ar
|
|
|
145
145
|
* carries a `reason` from {@link LINK_FINDING_REASONS} —
|
|
146
146
|
* `"not-an-address"`, `"unknown-type"`, `"ambiguous"` (with the claiming
|
|
147
147
|
* `packages`), or `"unresolved"` — and every one of them is an **error**:
|
|
148
|
-
* the three resolvers agree on severity for every class
|
|
148
|
+
* the three resolvers agree on severity for every class.
|
|
149
149
|
*/
|
|
150
150
|
export function auditLinks(index: ReturnType<typeof buildLinkIndex>): {
|
|
151
151
|
deadAnchors: object[];
|
|
@@ -21,7 +21,7 @@ export function isValidShortcode(value: unknown): boolean;
|
|
|
21
21
|
* @param {object} [opts.config] - The resolved build configuration, which the
|
|
22
22
|
* corpus is derived against.
|
|
23
23
|
* @param {readonly object[]} [opts.records] - Index records the caller already
|
|
24
|
-
* derived, so a command reads one corpus
|
|
24
|
+
* derived, so a command reads one corpus.
|
|
25
25
|
* @param {object[]} [opts.problems] - Collects the notes the index cannot
|
|
26
26
|
* record, instead of letting one of them silence the lint.
|
|
27
27
|
* @returns {{findings: Array<{file: string, line?: number, column?: number,
|
|
@@ -51,7 +51,7 @@ export function lintContentTree(contentBase: string, { skipDirectories, contentP
|
|
|
51
51
|
* This is {@link ADDRESS_SEGMENT_PATTERN}, not a second copy of it. A shortcode
|
|
52
52
|
* is the last segment of a canonical address, and the rule it is held to is the
|
|
53
53
|
* rule *every* segment is held to — so the two are one constant rather than two
|
|
54
|
-
* free to drift apart
|
|
54
|
+
* free to drift apart. The name survives because this is where the rule
|
|
55
55
|
* is applied to a note.
|
|
56
56
|
*
|
|
57
57
|
* Case is deliberately **not** constrained: hundreds of authored shortcodes are
|
|
@@ -9,13 +9,11 @@
|
|
|
9
9
|
* repository's identity in the address space, not a switch — and never dead
|
|
10
10
|
* configuration, whatever else changes.
|
|
11
11
|
*
|
|
12
|
-
* It
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
* retired and declaring it now fails the build; this value stays, here, where
|
|
18
|
-
* it is declared once.
|
|
12
|
+
* It is not a **selector**. Every content tree is single-package — each is
|
|
13
|
+
* single-sourced in the repository that ships it — so a `package:` field on a
|
|
14
|
+
* note would restate this constant once per note, and a value matching nothing
|
|
15
|
+
* would filter the whole tree out in silence. That field is refused, and this
|
|
16
|
+
* value stays here, declared once.
|
|
19
17
|
*
|
|
20
18
|
* Stable across compilation targets. If this content were ever compiled for a
|
|
21
19
|
* second game system, it would still be published as `sohl` — only the Foundry
|
|
@@ -36,7 +34,7 @@ export function contentPackage(): string;
|
|
|
36
34
|
* here: a note is published under `sohl` and its documents are addressed as
|
|
37
35
|
* `Compendium.sohl.<pack>.<Type>.<id>`. In `sohl-thalorna` the two differ
|
|
38
36
|
* (`thalorna` vs `sohl-thalorna`), which is why they are separate values rather
|
|
39
|
-
* than one — treating them as interchangeable is
|
|
37
|
+
* than one — treating them as interchangeable is the mistake.
|
|
40
38
|
*
|
|
41
39
|
* Configured rather than read from the manifest so the link resolver stays
|
|
42
40
|
* filesystem-free and unit-testable. `assertPackageIdMatchesManifestFile` in
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What a note authoring a derived key is told, in one place.
|
|
3
|
+
*
|
|
4
|
+
* Shared by every caller that can meet one, so an author meets the same
|
|
5
|
+
* sentence whichever runs first. It names the correction rather than only the
|
|
6
|
+
* fault, because "delete this" is not the fix here: the text is wanted, and
|
|
7
|
+
* there is somewhere it belongs.
|
|
8
|
+
*
|
|
9
|
+
* @param {string} key - The **whole key the note wrote**, from the region it
|
|
10
|
+
* sits in down to the field: `sohl.system.docHtml`. Composed by the caller,
|
|
11
|
+
* because only it knows where it found the value.
|
|
12
|
+
* @param {string} from - Where the compiler reads the value from, completing
|
|
13
|
+
* "the compiler writes it from …". The declaration carries it.
|
|
14
|
+
* @param {string} [file] - The note's path, named in the message. Omit it where
|
|
15
|
+
* the caller emits through a diagnostic, whose locator already starts the
|
|
16
|
+
* line — repeating it prints the path twice.
|
|
17
|
+
* @returns {string} The message, unpunctuated at the end as a finding is.
|
|
18
|
+
*/
|
|
19
|
+
export function derivedMessage(key: string, from: string, file?: string): string;
|
|
20
|
+
/**
|
|
21
|
+
* The derived keys a note actually writes, in declaration order.
|
|
22
|
+
*
|
|
23
|
+
* **Presence is the whole test**, as it is for a retired or a runtime-only
|
|
24
|
+
* field: an authored empty string is as much a claim on the key as prose is,
|
|
25
|
+
* and it is the same belief the message exists to correct. So the question is
|
|
26
|
+
* whether the path resolves to anything at all, never whether the value is a
|
|
27
|
+
* usable one.
|
|
28
|
+
*
|
|
29
|
+
* @param {object|null|undefined} fm - Parsed frontmatter.
|
|
30
|
+
* @param {readonly {key: string, from: string}[]} [keys] - What the compiler
|
|
31
|
+
* derives. A pass that derives nothing passes.
|
|
32
|
+
* @param {object} options - Options.
|
|
33
|
+
* @param {string} options.block - The system block to look in.
|
|
34
|
+
* @returns {{key: string, from: string}[]} The offending declarations.
|
|
35
|
+
*/
|
|
36
|
+
export function authoredDerivedKeys(fm: object | null | undefined, keys?: readonly {
|
|
37
|
+
key: string;
|
|
38
|
+
from: string;
|
|
39
|
+
}[], { block }?: {
|
|
40
|
+
block: string;
|
|
41
|
+
}): {
|
|
42
|
+
key: string;
|
|
43
|
+
from: string;
|
|
44
|
+
}[];
|
|
45
|
+
/**
|
|
46
|
+
* The same question asked of a `system` block directly.
|
|
47
|
+
*
|
|
48
|
+
* A note's own block is reached through {@link authoredDerivedKeys}, but it is
|
|
49
|
+
* not the only place an author writes one: an actor note's `items:` entries
|
|
50
|
+
* carry a `system:` overlay that is deep-merged onto the template verbatim,
|
|
51
|
+
* with no field declaration in the path at all. That overlay is a `system`
|
|
52
|
+
* block by every meaning except where it sits, and a `docHtml` written there
|
|
53
|
+
* ships exactly as one written on the item's own note.
|
|
54
|
+
*
|
|
55
|
+
* @param {Record<string, unknown>|null|undefined} data - The authored `system`
|
|
56
|
+
* data.
|
|
57
|
+
* @param {readonly {key: string, from: string}[]} [keys] - What the compiler
|
|
58
|
+
* derives.
|
|
59
|
+
* @returns {{key: string, from: string}[]} The offending declarations.
|
|
60
|
+
*/
|
|
61
|
+
export function derivedIn(data: Record<string, unknown> | null | undefined, keys?: readonly {
|
|
62
|
+
key: string;
|
|
63
|
+
from: string;
|
|
64
|
+
}[]): {
|
|
65
|
+
key: string;
|
|
66
|
+
from: string;
|
|
67
|
+
}[];
|
|
68
|
+
/**
|
|
69
|
+
* Refuse a note that authors any key its compiler derives.
|
|
70
|
+
*
|
|
71
|
+
* Refused rather than reported, for the reason its two siblings are: the note
|
|
72
|
+
* is not compiled, so nothing it would have emitted reaches a pack, and each
|
|
73
|
+
* caller counts the refused note and emits a located diagnostic — a refusal is
|
|
74
|
+
* never a silent skip.
|
|
75
|
+
*
|
|
76
|
+
* The **first** offending key is thrown on. A note authoring two of them is one
|
|
77
|
+
* mistake with one fix, and the build stops on this note either way.
|
|
78
|
+
*
|
|
79
|
+
* @param {object|null|undefined} fm - Parsed frontmatter, or nothing when it
|
|
80
|
+
* could not be parsed.
|
|
81
|
+
* @param {readonly {key: string, from: string}[]} [keys] - What the compiler
|
|
82
|
+
* derives.
|
|
83
|
+
* @param {object} options - Options.
|
|
84
|
+
* @param {string} options.block - The system block to look in.
|
|
85
|
+
* @param {string} [options.file] - The note's path, named in the message. Omit
|
|
86
|
+
* it where the caller emits through a diagnostic, which puts the locator at
|
|
87
|
+
* the start of the line already.
|
|
88
|
+
* @param {string} [options.absPath] - The note's file on disk, read only on the
|
|
89
|
+
* failing path to locate the offending line and column. The position rides on
|
|
90
|
+
* the thrown error as `position`, for a caller that emits a diagnostic.
|
|
91
|
+
* @returns {void}
|
|
92
|
+
* @throws {Error} When the note authors one.
|
|
93
|
+
*/
|
|
94
|
+
export function assertNoDerivedFields(fm: object | null | undefined, keys?: readonly {
|
|
95
|
+
key: string;
|
|
96
|
+
from: string;
|
|
97
|
+
}[], { block, file, absPath }?: {
|
|
98
|
+
block: string;
|
|
99
|
+
file?: string | undefined;
|
|
100
|
+
absPath?: string | undefined;
|
|
101
|
+
}): void;
|
|
@@ -115,7 +115,7 @@ export function positionInBody(body: string, offset: number, { bodyLine, bodyCol
|
|
|
115
115
|
* same name answers when no top-level one is present — which is usually what a
|
|
116
116
|
* reader wants, the key being unique in nearly every note. Pass `topLevel` where
|
|
117
117
|
* it is not: `aliases` is both a retired top-level field and a **permitted**
|
|
118
|
-
* `name.aliases
|
|
118
|
+
* `name.aliases`, and a finding about the first must never open on the
|
|
119
119
|
* second, which would tell an author to delete a field they are allowed to
|
|
120
120
|
* write.
|
|
121
121
|
*
|
|
@@ -232,7 +232,7 @@ export function yamlKeyPath(field: string): Array<string | number>;
|
|
|
232
232
|
* the same name is legal in two regions — `data.weight` and a top-level
|
|
233
233
|
* `weight` are different keys, and a search finds whichever comes first.
|
|
234
234
|
*
|
|
235
|
-
* So this is the located form for the closed regions
|
|
235
|
+
* So this is the located form for the closed regions. It reuses
|
|
236
236
|
* {@link positionOfYamlPath} rather than repeating its parse, which is what
|
|
237
237
|
* keeps a finding about `data.weight` pointing at `data.weight`.
|
|
238
238
|
*
|
|
@@ -58,7 +58,7 @@ export function defineDocumentSubtypes({ system, block, types }?: {
|
|
|
58
58
|
* {@link mapsNoteType}, {@link noteTypesFor}'s consumers and
|
|
59
59
|
* {@link systemOf} all arrive here. A note still spelling `armorgear` finds the
|
|
60
60
|
* `armor` row, compiles into the `armorgear` document it always did, and keeps
|
|
61
|
-
* the address it publishes at; only the *report* tells it to move
|
|
61
|
+
* the address it publishes at; only the *report* tells it to move.
|
|
62
62
|
*
|
|
63
63
|
* @param {DocumentSubtypeMap} map - The system's map.
|
|
64
64
|
* @param {string|undefined} noteType - The note's declared `type`.
|
|
@@ -127,7 +127,7 @@ export function documentSubtype(map: DocumentSubtypeMap, noteType: string | unde
|
|
|
127
127
|
* @property {string} [problem] - Why the reference names no document subtype.
|
|
128
128
|
*/
|
|
129
129
|
/**
|
|
130
|
-
* The document subtype a `(type, shortcode)` **reference** addresses
|
|
130
|
+
* The document subtype a `(type, shortcode)` **reference** addresses.
|
|
131
131
|
*
|
|
132
132
|
* A being's frontmatter names each embedded item by the *note's* type — the
|
|
133
133
|
* vocabulary an author writes — while the predefined items it resolves against
|
|
@@ -164,7 +164,7 @@ export function documentSubtype(map: DocumentSubtypeMap, noteType: string | unde
|
|
|
164
164
|
* retirement exists to stop.
|
|
165
165
|
*
|
|
166
166
|
* A **renamed** spelling is the opposite case and resolves normally, through
|
|
167
|
-
* the same {@link currentType} every other lookup goes through
|
|
167
|
+
* the same {@link currentType} every other lookup goes through. It has to
|
|
168
168
|
* be this side as much as the note's own `type:`: the overwhelming majority of
|
|
169
169
|
* the 31,000 occurrences of the old names are `(type, shortcode)` references
|
|
170
170
|
* inside a being's `items:` list, so a window that resolved notes but not
|