@heroiclands/package-build 22.20.0 → 22.22.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-content-build.md +0 -8
- package/CHANGELOG.md +204 -0
- package/assets/styles/content-base.css +162 -0
- package/bin/content-commands.mjs +26 -3
- package/bin/package-build.mjs +15 -2
- package/ci/content-format-reference.mjs +68 -1
- package/config.mjs +26 -0
- package/content-config.mjs +73 -4
- package/docs/api.md +46 -28
- package/docs/authoring/dates-and-calendars.md +4 -2
- package/docs/authoring/frontmatter.md +2 -2
- package/docs/authoring/links-and-markup.md +237 -28
- package/docs/configuration.md +27 -1
- package/docs/content-format.md +3 -1
- package/docs/guides/reference-pages.md +15 -0
- package/docs/reference/format-details.md +265 -42
- package/docs/reference/note-types.md +45 -28
- package/engine/actor-compiler.mjs +23 -0
- package/engine/anchored-sections.mjs +35 -4
- package/engine/anchors.mjs +10 -20
- package/engine/base-compiler.mjs +86 -21
- package/engine/base-styles.mjs +149 -0
- package/engine/bundles.mjs +0 -1
- package/engine/compendiums.mjs +8 -1
- package/engine/content-blocks.mjs +410 -0
- package/engine/content-captions.mjs +57 -8
- package/engine/content-footnotes.mjs +189 -0
- package/engine/content-format-check.mjs +15 -12
- package/engine/content-index.mjs +2 -2
- package/engine/content-links.mjs +14 -6
- package/engine/content-tables.mjs +45 -4
- package/engine/content-tasklists.mjs +189 -0
- package/engine/docs-checks.mjs +4 -4
- package/engine/extension-attributes.mjs +48 -2
- package/engine/frontmatter-lint.mjs +62 -66
- package/engine/frontmatter.mjs +3 -3
- package/engine/heading-attributes.mjs +308 -0
- package/engine/helpers.mjs +19 -13
- package/engine/homepage.mjs +8 -8
- package/engine/index.mjs +3 -0
- package/engine/infobox-render.mjs +21 -4
- package/engine/infobox.mjs +167 -7
- package/engine/journals.mjs +36 -17
- package/engine/macros.mjs +1 -1
- package/engine/markdown-expressions.mjs +178 -53
- package/engine/note-addresses.mjs +17 -8
- package/engine/note-dates.mjs +49 -0
- package/engine/note-frontmatter.mjs +7 -16
- package/engine/note-vocabulary.mjs +216 -42
- package/engine/page-lists.mjs +358 -0
- package/engine/pdf-build.mjs +76 -42
- package/engine/pdf-render.mjs +422 -49
- package/engine/rank-ladder.mjs +198 -0
- package/engine/reckoning-markers.mjs +1 -1
- package/engine/reference-targets.mjs +13 -1
- package/engine/retired-fields.mjs +8 -13
- package/engine/scenes.mjs +10 -3
- package/engine/site-build.mjs +41 -12
- package/engine/site-config.mjs +10 -1
- package/engine/site-index.mjs +39 -4
- package/engine/site-preview.mjs +18 -7
- package/engine/social-ties.mjs +52 -14
- package/engine/sql-tables.mjs +66 -9
- package/engine/standing-terms.mjs +19 -0
- package/engine/standings.mjs +538 -0
- package/engine/svg-theme.mjs +15 -4
- package/engine/system-block.mjs +12 -14
- package/engine/web-wikilinks.mjs +48 -1
- package/engine/wikilinks.mjs +3 -1
- package/hm3/actors.mjs +6 -3
- package/manifest.mjs +13 -0
- package/package.json +1 -1
- package/sohl/actors.mjs +82 -103
- package/sohl/item-fields.mjs +4 -5
- package/sohl/items.mjs +1 -1
- package/sohl/note-schemas.mjs +11 -21
- package/types/config.d.mts +9 -0
- package/types/engine/actor-compiler.d.mts +11 -0
- package/types/engine/anchored-sections.d.mts +24 -0
- package/types/engine/base-compiler.d.mts +19 -0
- package/types/engine/base-styles.d.mts +93 -0
- package/types/engine/content-blocks.d.mts +87 -0
- package/types/engine/content-captions.d.mts +14 -1
- package/types/engine/content-footnotes.d.mts +73 -0
- package/types/engine/content-format-check.d.mts +8 -6
- package/types/engine/content-tables.d.mts +12 -4
- package/types/engine/content-tasklists.d.mts +72 -0
- package/types/engine/extension-attributes.d.mts +22 -1
- package/types/engine/frontmatter.d.mts +1 -1
- package/types/engine/heading-attributes.d.mts +174 -0
- package/types/engine/helpers.d.mts +7 -4
- package/types/engine/homepage.d.mts +7 -7
- package/types/engine/index.d.mts +1 -0
- package/types/engine/infobox-render.d.mts +6 -1
- package/types/engine/journals.d.mts +14 -7
- package/types/engine/markdown-expressions.d.mts +23 -0
- package/types/engine/note-frontmatter.d.mts +1 -4
- package/types/engine/note-vocabulary.d.mts +98 -8
- package/types/engine/page-lists.d.mts +126 -0
- package/types/engine/pdf-render.d.mts +72 -2
- package/types/engine/rank-ladder.d.mts +32 -0
- package/types/engine/retired-fields.d.mts +4 -6
- package/types/engine/site-build.d.mts +13 -1
- package/types/engine/site-config.d.mts +9 -0
- package/types/engine/site-index.d.mts +13 -1
- package/types/engine/site-preview.d.mts +1 -1
- package/types/engine/sql-tables.d.mts +31 -7
- package/types/engine/standing-terms.d.mts +16 -0
- package/types/engine/standings.d.mts +147 -0
- package/types/engine/system-block.d.mts +11 -12
- package/types/engine/web-wikilinks.d.mts +3 -1
- package/types/engine/wikilinks.d.mts +2 -1
- package/types/hm3/actors.d.mts +3 -3
- package/types/sohl/actors.d.mts +25 -7
- package/engine/content-admonitions.mjs +0 -114
- package/engine/content-secrets.mjs +0 -112
- package/types/engine/content-admonitions.d.mts +0 -24
- package/types/engine/content-secrets.d.mts +0 -17
|
@@ -119,7 +119,7 @@ export function checkHomepageCount(found: ReadonlyArray<{
|
|
|
119
119
|
* `packageBuild.manifest.title` — the name Foundry already shows for the
|
|
120
120
|
* package — so a homepage that adds nothing to it need not restate it. An
|
|
121
121
|
* authored `name.full` wins, because a front page may greet a reader
|
|
122
|
-
* differently from a package browser.
|
|
122
|
+
* differently from a package browser.
|
|
123
123
|
*
|
|
124
124
|
* Falls back to `contentPackage` last, so a package that has no manifest of its
|
|
125
125
|
* own still yields a titled page rather than a blank heading.
|
|
@@ -244,12 +244,12 @@ export const HOMEPAGE_DESTINATION: string;
|
|
|
244
244
|
* `name` and `shortcode` are permitted: the shortcode is what a link is
|
|
245
245
|
* written with, and `name` titles the page like every other note's.
|
|
246
246
|
*
|
|
247
|
-
* **A named class, not an allow-list, and that boundary is the decision.**
|
|
248
|
-
*
|
|
249
|
-
*
|
|
250
|
-
*
|
|
251
|
-
*
|
|
252
|
-
*
|
|
247
|
+
* **A named class, not an allow-list, and that boundary is the decision.**
|
|
248
|
+
* What this class answers is a key the top-level vocabulary *accepts* and a
|
|
249
|
+
* homepage still cannot use, which is a different question from an unknown
|
|
250
|
+
* key: the frontmatter lint refuses those for every note alike, naming the
|
|
251
|
+
* closed vocabulary, so repeating that judgement per type would be a second
|
|
252
|
+
* answer to one question.
|
|
253
253
|
*
|
|
254
254
|
* `aliases` is deliberately not in the class: it is a **retired** field, refused
|
|
255
255
|
* on every note whatever its type, so it is answered there rather than
|
package/types/engine/index.d.mts
CHANGED
|
@@ -61,6 +61,7 @@ export * as contentCharset from "./content-charset.mjs";
|
|
|
61
61
|
export * as contentIcons from "./content-icons.mjs";
|
|
62
62
|
export * as contentHtml from "./content-html.mjs";
|
|
63
63
|
export * as contentImages from "./content-images.mjs";
|
|
64
|
+
export * as contentTaskLists from "./content-tasklists.mjs";
|
|
64
65
|
export * as contentEmbeds from "./content-embeds.mjs";
|
|
65
66
|
export * as pathnames from "./pathnames.mjs";
|
|
66
67
|
export * as contentLinks from "./content-links.mjs";
|
|
@@ -54,12 +54,17 @@ export function sectionHasContent(section: object): boolean;
|
|
|
54
54
|
* reference, so the link works on paper as a cross-reference and in a PDF
|
|
55
55
|
* viewer as a jump. Everything else is set as its own words.
|
|
56
56
|
*
|
|
57
|
+
* A panel's rows address pages the same way prose does, so a page the book does
|
|
58
|
+
* not print is set as the absolute URL a reader can follow — see
|
|
59
|
+
* {@link module:engine/pdf-render.absolutePageUrl}.
|
|
60
|
+
*
|
|
57
61
|
* @param {object} value - A `link` value.
|
|
58
62
|
* @param {Map<string, string>} links - Address slug → the book's anchor.
|
|
59
63
|
* @param {(anchor: string) => string} labelFor - The anchor's Typst label.
|
|
64
|
+
* @param {string} [site] - The absolute address this book's pages are served at.
|
|
60
65
|
* @returns {string} Typst markup.
|
|
61
66
|
*/
|
|
62
|
-
export function linkToTypst(value: object, links: Map<string, string>, labelFor: (anchor: string) => string): string;
|
|
67
|
+
export function linkToTypst(value: object, links: Map<string, string>, labelFor: (anchor: string) => string, site?: string): string;
|
|
63
68
|
/**
|
|
64
69
|
* Every box as one Typst panel each, breaking between sections.
|
|
65
70
|
*
|
|
@@ -4,7 +4,8 @@
|
|
|
4
4
|
* a split. Content before the first H1 (if non-empty) becomes a leading
|
|
5
5
|
* page. Each H1 yields a page whose name is the heading
|
|
6
6
|
* text (with any `{#anchor-id}` suffix stripped out and surfaced as
|
|
7
|
-
* `anchorSlug`).
|
|
7
|
+
* `anchorSlug`). The classes the heading declares come along as `classes`, so
|
|
8
|
+
* a surface that honours one has it without reading the suffix again.
|
|
8
9
|
*
|
|
9
10
|
* `leadName` names that leading page. A journal note's is "Introduction",
|
|
10
11
|
* because it introduces the pages that follow. An item doc's is the item — a
|
|
@@ -15,12 +16,13 @@
|
|
|
15
16
|
* @param {string} body - Markdown body to split.
|
|
16
17
|
* @param {string} [leadName] - Name of the page before the first heading.
|
|
17
18
|
* @returns {Array<{name: string, anchorSlug: string|null, level: number,
|
|
18
|
-
* markdown: string}>} Pages in document order.
|
|
19
|
+
* classes: string[], markdown: string}>} Pages in document order.
|
|
19
20
|
*/
|
|
20
21
|
export function splitPages(body: string, leadName?: string): Array<{
|
|
21
22
|
name: string;
|
|
22
23
|
anchorSlug: string | null;
|
|
23
24
|
level: number;
|
|
25
|
+
classes: string[];
|
|
24
26
|
markdown: string;
|
|
25
27
|
}>;
|
|
26
28
|
/**
|
|
@@ -97,10 +99,14 @@ export function journalPageId(entryId: string, page: {
|
|
|
97
99
|
* @param {Array<object>} rawPages - From {@link splitPages}.
|
|
98
100
|
* @param {string} entryId - The owning JournalEntry's `_id`.
|
|
99
101
|
* @param {string} noteName - The note, for error messages.
|
|
102
|
+
* A page whose heading carries `.secret` is given to the GM alone, by the one
|
|
103
|
+
* ownership value Foundry reads as "nobody but a GM". Every other page states
|
|
104
|
+
* no ownership and inherits the journal's.
|
|
105
|
+
*
|
|
100
106
|
* @returns {Array<{_id: string, name: string, type: string,
|
|
101
107
|
* title: {show: boolean, level: number},
|
|
102
|
-
* text: {format: number, content: string}, _key: string
|
|
103
|
-
* documents, in order.
|
|
108
|
+
* text: {format: number, content: string}, _key: string,
|
|
109
|
+
* ownership?: {default: number}}>} The page documents, in order.
|
|
104
110
|
* @throws {Error} When the note has no content at all, or repeats an anchor.
|
|
105
111
|
*/
|
|
106
112
|
export function buildPages(rawPages: Array<object>, entryId: string, noteName: string, captions: any): Array<{
|
|
@@ -116,6 +122,9 @@ export function buildPages(rawPages: Array<object>, entryId: string, noteName: s
|
|
|
116
122
|
content: string;
|
|
117
123
|
};
|
|
118
124
|
_key: string;
|
|
125
|
+
ownership?: {
|
|
126
|
+
default: number;
|
|
127
|
+
};
|
|
119
128
|
}>;
|
|
120
129
|
/**
|
|
121
130
|
* Assemble one JournalEntry document from a note's converted markdown.
|
|
@@ -133,7 +142,6 @@ export function buildPages(rawPages: Array<object>, entryId: string, noteName: s
|
|
|
133
142
|
* @param {string} [params.leadName] - Name for the page before the first
|
|
134
143
|
* heading; see {@link splitPages}.
|
|
135
144
|
* @param {string|null} [params.folder] - The folder id, or `null`.
|
|
136
|
-
* @param {object} [params.flags] - Document flags.
|
|
137
145
|
* @param {object} [params.stats] - The `_stats` block to stamp. Passed by the
|
|
138
146
|
* caller because it is a property of the *pack* being written, not of the
|
|
139
147
|
* entry: a module may ship the same content for two systems, and each pack's
|
|
@@ -150,13 +158,12 @@ export function buildPages(rawPages: Array<object>, entryId: string, noteName: s
|
|
|
150
158
|
* before they read it rather than after.
|
|
151
159
|
* @returns {object} The JournalEntry document, keyed for the pack.
|
|
152
160
|
*/
|
|
153
|
-
export function buildJournalEntry({ id, name, markdown, leadName, folder,
|
|
161
|
+
export function buildJournalEntry({ id, name, markdown, leadName, folder, stats, infoboxes, notice, }: {
|
|
154
162
|
id: string;
|
|
155
163
|
name: string;
|
|
156
164
|
markdown: string;
|
|
157
165
|
leadName?: string | undefined;
|
|
158
166
|
folder?: string | null | undefined;
|
|
159
|
-
flags?: object | undefined;
|
|
160
167
|
stats?: object | undefined;
|
|
161
168
|
infoboxes?: {
|
|
162
169
|
id: string;
|
|
@@ -15,3 +15,26 @@ export function renderMarkdownExpressions(body: string, { fm, dates, sqlResults,
|
|
|
15
15
|
markdown: string;
|
|
16
16
|
findings: any[];
|
|
17
17
|
};
|
|
18
|
+
/**
|
|
19
|
+
* An inline expression, as an author writes one.
|
|
20
|
+
*
|
|
21
|
+
* Hugo shortcodes begin `{{<` or `{{%` and stay in the source for Hugo.
|
|
22
|
+
*
|
|
23
|
+
* Exported because a renderer that meets one has met a body this pass never ran
|
|
24
|
+
* over, and has to be able to say so.
|
|
25
|
+
*
|
|
26
|
+
* @type {RegExp}
|
|
27
|
+
*/
|
|
28
|
+
export const EXPRESSION: RegExp;
|
|
29
|
+
/**
|
|
30
|
+
* Each helper's parameters and what it does, keyed by name.
|
|
31
|
+
*
|
|
32
|
+
* The authoring document describes this surface, and a test holds the two
|
|
33
|
+
* together. A consumer reads it to list what an expression may call.
|
|
34
|
+
*
|
|
35
|
+
* @type {Readonly<Record<string, Readonly<{params: string, summary: string}>>>}
|
|
36
|
+
*/
|
|
37
|
+
export const EXPRESSION_HELPERS: Readonly<Record<string, Readonly<{
|
|
38
|
+
params: string;
|
|
39
|
+
summary: string;
|
|
40
|
+
}>>>;
|
|
@@ -1,10 +1,7 @@
|
|
|
1
1
|
/** Return authored keys from an opening YAML frontmatter map. */
|
|
2
2
|
export function authoredNoteKeys(markdown: any): string[];
|
|
3
|
-
/** The complete ordered top-level vocabulary for addressed content notes. */
|
|
4
|
-
export const NOTE_TOP_LEVEL_KEYS: readonly string[];
|
|
5
|
-
/** Membership test for the top-level vocabulary. */
|
|
6
|
-
export const NOTE_TOP_LEVEL_KEY_SET: Set<string>;
|
|
7
3
|
/** The name properties every note accepts, in display order. */
|
|
8
4
|
export const NOTE_NAME_KEYS: readonly string[];
|
|
9
5
|
/** Additional name components accepted by an individual character. */
|
|
10
6
|
export const CHARACTER_NAME_KEYS: readonly string[];
|
|
7
|
+
export { NOTE_TOP_LEVEL_FIELDS, NOTE_TOP_LEVEL_KEYS, NOTE_TOP_LEVEL_KEY_SET } from "./note-vocabulary.mjs";
|
|
@@ -42,8 +42,6 @@ export function exclusiveTagGroups(type: string, groups?: object): {
|
|
|
42
42
|
* The spelling of the tag *itself* still is — a near miss is a near miss, and
|
|
43
43
|
* the frontmatter lint is what reports it; nothing here guesses.
|
|
44
44
|
*
|
|
45
|
-
* Reads `tags` and the singular `tag` spelling.
|
|
46
|
-
*
|
|
47
45
|
* @param {object|null|undefined} fm - Parsed frontmatter.
|
|
48
46
|
* @param {string} tag - The tag to look for, in its declared spelling.
|
|
49
47
|
* @returns {boolean} Whether the note carries it.
|
|
@@ -135,6 +133,59 @@ export function dataFields(type: string, vocabulary?: Readonly<Record<string, Ty
|
|
|
135
133
|
* it has no `subType` at all — see {@link TypeVocabulary}.
|
|
136
134
|
*/
|
|
137
135
|
export function subTypes(type: string, vocabulary?: Readonly<Record<string, TypeVocabulary>>): readonly string[] | null | undefined;
|
|
136
|
+
/**
|
|
137
|
+
* One top-level frontmatter key.
|
|
138
|
+
*
|
|
139
|
+
* Narrower than a {@link DataFieldSpec}: the top level is a fixed list rather
|
|
140
|
+
* than a per-type vocabulary, nothing there is keyed by pack or resolved as an
|
|
141
|
+
* Address, and the value's shape is checked by whatever owns the region the key
|
|
142
|
+
* opens — `name`, `tags` and each system block each have their own check.
|
|
143
|
+
*
|
|
144
|
+
* @typedef {object} TopLevelFieldSpec
|
|
145
|
+
* @property {string} name - The key, as a note writes it.
|
|
146
|
+
* @property {boolean} [system] - The key opens a game system's block, so it is
|
|
147
|
+
* one of {@link module:engine/systems.SYSTEM_IDS} rather than a fixed key.
|
|
148
|
+
* @property {string} describe - One line, for the author-facing reference.
|
|
149
|
+
*/
|
|
150
|
+
/**
|
|
151
|
+
* **The top-level vocabulary**, in the order a formatted note writes it.
|
|
152
|
+
*
|
|
153
|
+
* The region describes the note as a published artefact: what it is called,
|
|
154
|
+
* what kind of thing it is about, how it is classified, and which of the other
|
|
155
|
+
* two regions it opens. Everything else a note states is a fact about the
|
|
156
|
+
* subject, which belongs under `data:`, or a fact about one system's document,
|
|
157
|
+
* which belongs inside that system's block.
|
|
158
|
+
*
|
|
159
|
+
* **Closed, like the other two regions.** A key this list does not name is a
|
|
160
|
+
* finding at its own line, because a region that passed one through would turn
|
|
161
|
+
* a misspelling into a value nothing reads — `title` beside `name`, or `pack`
|
|
162
|
+
* where `data.pack` was meant.
|
|
163
|
+
*
|
|
164
|
+
* **The system blocks are derived from the system registry**, so recognising a
|
|
165
|
+
* further system is a change to {@link module:engine/systems.SYSTEM_IDS} and to
|
|
166
|
+
* nothing else. They sort by name after the fixed keys: a block is the largest
|
|
167
|
+
* thing a note writes, every one of them belongs at the end, and a rule a
|
|
168
|
+
* reader can restate in one sentence is worth more than a hand-kept order.
|
|
169
|
+
*
|
|
170
|
+
* Five readers derive from this one list — the frontmatter lint's accepted set,
|
|
171
|
+
* the message it prints, the formatter's key order, the content-format check,
|
|
172
|
+
* and the author-facing reference — so none of them can fall behind the others.
|
|
173
|
+
*
|
|
174
|
+
* @type {readonly TopLevelFieldSpec[]}
|
|
175
|
+
*/
|
|
176
|
+
export const NOTE_TOP_LEVEL_FIELDS: readonly TopLevelFieldSpec[];
|
|
177
|
+
/**
|
|
178
|
+
* The top-level keys, in formatted order.
|
|
179
|
+
*
|
|
180
|
+
* @type {readonly string[]}
|
|
181
|
+
*/
|
|
182
|
+
export const NOTE_TOP_LEVEL_KEYS: readonly string[];
|
|
183
|
+
/**
|
|
184
|
+
* Membership test for the top-level vocabulary.
|
|
185
|
+
*
|
|
186
|
+
* @type {ReadonlySet<string>}
|
|
187
|
+
*/
|
|
188
|
+
export const NOTE_TOP_LEVEL_KEY_SET: ReadonlySet<string>;
|
|
138
189
|
/**
|
|
139
190
|
* The `data:` keys **every** note type accepts, whatever it is.
|
|
140
191
|
*
|
|
@@ -174,15 +225,17 @@ export const GM_TAG: "gm";
|
|
|
174
225
|
/**
|
|
175
226
|
* The tags that **classify** a note, grouped by what they classify.
|
|
176
227
|
*
|
|
177
|
-
* `tags:`
|
|
178
|
-
* region, a working state is the author's own and this build has no
|
|
179
|
-
* about it. A classifying tag is different, because something queries
|
|
228
|
+
* `tags:` holds an open vocabulary of *values*, and most tags belong there: a
|
|
229
|
+
* theme, a region, a working state is the author's own and this build has no
|
|
230
|
+
* opinion about it. A classifying tag is different, because something queries
|
|
231
|
+
* it — a
|
|
180
232
|
* settlement tagged `village` appears in the list of villages and an untagged
|
|
181
233
|
* one does not, so `vilage` does not merely look wrong, it removes the note from
|
|
182
234
|
* an index while the index still renders a table that looks complete.
|
|
183
235
|
*
|
|
184
236
|
* **This list is not a closed set.** An unrecognised tag is legal, because the
|
|
185
|
-
*
|
|
237
|
+
* values are the author's; what is reported is a **near miss** — a tag close
|
|
238
|
+
* enough to a
|
|
186
239
|
* declared one to be a typo of it.
|
|
187
240
|
*
|
|
188
241
|
* **Each group names the types it applies to**, and that scope is what makes the
|
|
@@ -296,15 +349,29 @@ export type DataFieldSpec = {
|
|
|
296
349
|
* value — which is the honest answer wherever the specification's stated
|
|
297
350
|
* shape and the shape notes are authored in today disagree.
|
|
298
351
|
*/
|
|
299
|
-
kind?: "string" | "number" | "boolean" | "map" | "address" | "list" | "shortcode" | "scalar-or-map" | undefined;
|
|
352
|
+
kind?: "string" | "number" | "boolean" | "map" | "address" | "list" | "shortcode" | "list-or-map" | "scalar-or-map" | undefined;
|
|
300
353
|
/**
|
|
301
354
|
* - Type of each list or pack-map value.
|
|
302
355
|
*/
|
|
303
356
|
entryKind?: "address" | "shortcode" | undefined;
|
|
304
357
|
/**
|
|
305
|
-
* - Type of each map key.
|
|
358
|
+
* - Type of each map key. On a
|
|
359
|
+
* `list-or-map` field it describes the map form alone, and `entryKind` the
|
|
360
|
+
* list form alone: reading both of one value would check a map's entries as
|
|
361
|
+
* if they were its keys.
|
|
306
362
|
*/
|
|
307
363
|
keyKind?: "address" | "shortcode" | undefined;
|
|
364
|
+
/**
|
|
365
|
+
* - The map's entries are standings held in the
|
|
366
|
+
* body each is keyed by, so a row names the two together — see
|
|
367
|
+
* {@link module :engine/standings}.
|
|
368
|
+
*/
|
|
369
|
+
standings?: boolean | undefined;
|
|
370
|
+
/**
|
|
371
|
+
* - The map names posts, each with a description,
|
|
372
|
+
* so each takes a row labelled and anchored by the post's own name.
|
|
373
|
+
*/
|
|
374
|
+
roster?: boolean | undefined;
|
|
308
375
|
/**
|
|
309
376
|
* - An alternative `subType:<skill-subtype>` key.
|
|
310
377
|
*/
|
|
@@ -404,3 +471,26 @@ export type TypeVocabulary = {
|
|
|
404
471
|
index?: object;
|
|
405
472
|
}) => object[]) | undefined;
|
|
406
473
|
};
|
|
474
|
+
/**
|
|
475
|
+
* One top-level frontmatter key.
|
|
476
|
+
*
|
|
477
|
+
* Narrower than a {@link DataFieldSpec}: the top level is a fixed list rather
|
|
478
|
+
* than a per-type vocabulary, nothing there is keyed by pack or resolved as an
|
|
479
|
+
* Address, and the value's shape is checked by whatever owns the region the key
|
|
480
|
+
* opens — `name`, `tags` and each system block each have their own check.
|
|
481
|
+
*/
|
|
482
|
+
export type TopLevelFieldSpec = {
|
|
483
|
+
/**
|
|
484
|
+
* - The key, as a note writes it.
|
|
485
|
+
*/
|
|
486
|
+
name: string;
|
|
487
|
+
/**
|
|
488
|
+
* - The key opens a game system's block, so it is
|
|
489
|
+
* one of {@link module :engine/systems.SYSTEM_IDS} rather than a fixed key.
|
|
490
|
+
*/
|
|
491
|
+
system?: boolean | undefined;
|
|
492
|
+
/**
|
|
493
|
+
* - One line, for the author-facing reference.
|
|
494
|
+
*/
|
|
495
|
+
describe: string;
|
|
496
|
+
};
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Every `pagelist` fence in a markdown body, with the position each occupies.
|
|
3
|
+
*
|
|
4
|
+
* Positions are 0-based lines into the body as given, which is what a
|
|
5
|
+
* diagnostic about a directive needs and what the expander uses to splice
|
|
6
|
+
* results back in.
|
|
7
|
+
*
|
|
8
|
+
* **A malformed directive is still a directive.** It is returned carrying its
|
|
9
|
+
* problems rather than skipped, so the expander finds a prepared answer at its
|
|
10
|
+
* ordinal and reports the fault where the fence sits. That includes a fence
|
|
11
|
+
* nobody closed, which the author meant as a directive and which would
|
|
12
|
+
* otherwise swallow the rest of the note into a code block.
|
|
13
|
+
*
|
|
14
|
+
* @param {string} markdown - The note body, frontmatter already stripped.
|
|
15
|
+
* @returns {Array<{line: number, close: number, indent: string, tag: string,
|
|
16
|
+
* type: string, sort: string, descriptions: boolean, allowEmpty: boolean,
|
|
17
|
+
* problems: string[], block: string}>} One entry per fence, in document order.
|
|
18
|
+
*/
|
|
19
|
+
export function findPageListBlocks(markdown: string): Array<{
|
|
20
|
+
line: number;
|
|
21
|
+
close: number;
|
|
22
|
+
indent: string;
|
|
23
|
+
tag: string;
|
|
24
|
+
type: string;
|
|
25
|
+
sort: string;
|
|
26
|
+
descriptions: boolean;
|
|
27
|
+
allowEmpty: boolean;
|
|
28
|
+
problems: string[];
|
|
29
|
+
block: string;
|
|
30
|
+
}>;
|
|
31
|
+
/**
|
|
32
|
+
* The pages a directive selects, as index records.
|
|
33
|
+
*
|
|
34
|
+
* A **stub** is left out: an empty body publishes no page, which the index says
|
|
35
|
+
* by holding no address, so there is nothing for the list to link to. A
|
|
36
|
+
* documentation journal and an asset are not pages an author tagged, which
|
|
37
|
+
* {@link module:engine/index-records.isNoteRecord} already answers.
|
|
38
|
+
*
|
|
39
|
+
* @param {object[]} records - Content-index records.
|
|
40
|
+
* @param {{tag: string, type: string}} directive - From {@link findPageListBlocks}.
|
|
41
|
+
* @returns {object[]} The selected records, unordered.
|
|
42
|
+
*/
|
|
43
|
+
export function pagesForList(records: object[], { tag, type }: {
|
|
44
|
+
tag: string;
|
|
45
|
+
type: string;
|
|
46
|
+
}): object[];
|
|
47
|
+
/**
|
|
48
|
+
* Render one directive's answer as markdown.
|
|
49
|
+
*
|
|
50
|
+
* **The emitted link is the page's `address.slug`** — the `type-shortcode`
|
|
51
|
+
* form a wikilink resolves — which is exactly what a table's `_ref` cell
|
|
52
|
+
* writes. A short form names the package the link is written in, so it is
|
|
53
|
+
* unambiguous for a list of this package's own pages, and it resolves to the
|
|
54
|
+
* note's **page** on each surface: a compendium journal in Foundry, a URL on
|
|
55
|
+
* the web and in the book. An item note's page is its documentation journal,
|
|
56
|
+
* and the short form reaches it the way prose does.
|
|
57
|
+
*
|
|
58
|
+
* @param {object[]} records - Content-index records.
|
|
59
|
+
* @param {object} directive - From {@link findPageListBlocks}.
|
|
60
|
+
* @returns {{markdown: string, pages: number}} The list, and how many pages
|
|
61
|
+
* are in it.
|
|
62
|
+
*/
|
|
63
|
+
export function renderPageList(records: object[], directive: object): {
|
|
64
|
+
markdown: string;
|
|
65
|
+
pages: number;
|
|
66
|
+
};
|
|
67
|
+
/**
|
|
68
|
+
* Answer every `pagelist` directive in a set of note bodies, ahead of expansion.
|
|
69
|
+
*
|
|
70
|
+
* **Why a separate pass.** The same reason the SQL tables have one:
|
|
71
|
+
* {@link module:engine/content-tables.expandContentTables} is synchronous and
|
|
72
|
+
* exported, and the corpus is enumerated once by whoever already holds it
|
|
73
|
+
* rather than derived again inside a renderer. A page list needs no database —
|
|
74
|
+
* it is a filter over the records — so preparing one costs a walk of what the
|
|
75
|
+
* caller already has.
|
|
76
|
+
*
|
|
77
|
+
* **Keyed by note, then by the directive's ordinal within it**, not by its
|
|
78
|
+
* line: the passes do not agree on what a body is, and trim it differently,
|
|
79
|
+
* but a directive's position in the sequence of fences is the same in each.
|
|
80
|
+
*
|
|
81
|
+
* A malformed directive is recorded rather than thrown, so one bad fence costs
|
|
82
|
+
* its own list and not the whole build's report.
|
|
83
|
+
*
|
|
84
|
+
* @param {object[]} records - Content-index records, already narrowed to the
|
|
85
|
+
* audience the surface publishes to.
|
|
86
|
+
* @param {Array<{source: string, markdown: string}>} sources - The bodies to scan.
|
|
87
|
+
* @returns {Map<string, Array<{markdown?: string, pages?: number, empty?: string,
|
|
88
|
+
* reason?: string, allowEmpty: boolean}>>} Note to results, in document order.
|
|
89
|
+
*/
|
|
90
|
+
export function preparePageLists(records: object[], sources: Array<{
|
|
91
|
+
source: string;
|
|
92
|
+
markdown: string;
|
|
93
|
+
}>): Map<string, Array<{
|
|
94
|
+
markdown?: string;
|
|
95
|
+
pages?: number;
|
|
96
|
+
empty?: string;
|
|
97
|
+
reason?: string;
|
|
98
|
+
allowEmpty: boolean;
|
|
99
|
+
}>>;
|
|
100
|
+
/** The fence language that opens a page list. */
|
|
101
|
+
export const PAGE_LIST_LANGUAGE: "pagelist";
|
|
102
|
+
/**
|
|
103
|
+
* The orders a page list may come out in.
|
|
104
|
+
*
|
|
105
|
+
* Ordering is about output rather than about the pages, so it is an attribute
|
|
106
|
+
* rather than something read off a note. `name` is alphabetical, case and
|
|
107
|
+
* accents folded; `type` groups the note types and orders by name inside each.
|
|
108
|
+
*
|
|
109
|
+
* @type {readonly string[]}
|
|
110
|
+
*/
|
|
111
|
+
export const PAGE_LIST_SORTS: readonly string[];
|
|
112
|
+
/**
|
|
113
|
+
* The attributes a page-list fence takes, with what each one is for.
|
|
114
|
+
*
|
|
115
|
+
* **This is the one list.** The fence is validated against it, the authoring
|
|
116
|
+
* document's table is generated from it, and the reference is checked against
|
|
117
|
+
* it — so an attribute added here is one both documents describe, and anything
|
|
118
|
+
* else an author writes is reported by name.
|
|
119
|
+
*
|
|
120
|
+
* @type {Readonly<Record<string, Readonly<{value: string, default: string, summary: string}>>>}
|
|
121
|
+
*/
|
|
122
|
+
export const PAGE_LIST_ATTRIBUTES: Readonly<Record<string, Readonly<{
|
|
123
|
+
value: string;
|
|
124
|
+
default: string;
|
|
125
|
+
summary: string;
|
|
126
|
+
}>>>;
|
|
@@ -37,6 +37,24 @@ export function labelFor(anchor: string): string;
|
|
|
37
37
|
* @returns {object} A markdown-it instance.
|
|
38
38
|
*/
|
|
39
39
|
export function createParser(registry?: object): object;
|
|
40
|
+
/**
|
|
41
|
+
* A markdown body with its HTML comments taken out.
|
|
42
|
+
*
|
|
43
|
+
* A comment is an author's aside to the next author — a `markdownlint` pragma, a
|
|
44
|
+
* note recording why a notice is quoted verbatim — and no surface shows one: the
|
|
45
|
+
* packs and the website emit it into HTML, where it is a comment still. Typst
|
|
46
|
+
* has nowhere to put it, so markdown-it hands the delimiters over as prose and
|
|
47
|
+
* the aside is typeset. It comes out here instead, and a comment inside a fence
|
|
48
|
+
* or a code span stays, because there it is an example of a comment.
|
|
49
|
+
*
|
|
50
|
+
* **The line count is preserved.** Every finding this module reports is located
|
|
51
|
+
* in the markdown it was handed, so a pass that removed a line would move every
|
|
52
|
+
* position after it.
|
|
53
|
+
*
|
|
54
|
+
* @param {string} markdown - The body, as authored.
|
|
55
|
+
* @returns {string} The same body, its comments replaced by their own newlines.
|
|
56
|
+
*/
|
|
57
|
+
export function stripHtmlComments(markdown: string): string;
|
|
40
58
|
/**
|
|
41
59
|
* Render markdown as Typst content.
|
|
42
60
|
*
|
|
@@ -55,8 +73,26 @@ export function createParser(registry?: object): object;
|
|
|
55
73
|
* note's own `##` nests beneath the entry heading the book gave it.
|
|
56
74
|
* @param {string} [opts.anchorPrefix] - The entry's anchor, which namespaces
|
|
57
75
|
* every `{#slug}` the body declares.
|
|
58
|
-
*
|
|
59
|
-
*
|
|
76
|
+
* @param {object[]} [opts.findings] - Collected here rather than thrown. Every
|
|
77
|
+
* construct this pass cannot render is reported into it — see
|
|
78
|
+
* {@link reportUnrenderable} — and a caller that passes none renders the same
|
|
79
|
+
* Typst and is told nothing.
|
|
80
|
+
* @param {string} [opts.file] - The file the markdown came from, named in every
|
|
81
|
+
* finding.
|
|
82
|
+
* @param {number} [opts.bodyLine] - The 1-based file line the body starts on, so
|
|
83
|
+
* a finding's line is the file's rather than the body's.
|
|
84
|
+
* @param {number} [opts.bodyColumn] - The 1-based column the body starts at.
|
|
85
|
+
* @param {Array<{line: number, generated: boolean}>} [opts.lineMap] - From
|
|
86
|
+
* table expansion, mapping each line handed in back to the line an author
|
|
87
|
+
* wrote.
|
|
88
|
+
* @param {boolean} [opts.prepared] - Whether this body has been through the
|
|
89
|
+
* passes that resolve a wikilink, an embed and an inline expression. A note's
|
|
90
|
+
* has; a front-matter file's and a prose file's have not, and markup of theirs
|
|
91
|
+
* left in one is reported rather than typeset in silence.
|
|
92
|
+
* @param {string} [opts.url] - The absolute address this book's pages are served
|
|
93
|
+
* at. Every link the book sets as a URL is resolved against it, because a
|
|
94
|
+
* reader holding a PDF has no page to resolve a path against — see
|
|
95
|
+
* {@link renderLink}.
|
|
60
96
|
* @returns {string} Typst markup.
|
|
61
97
|
*/
|
|
62
98
|
export function markdownToTypst(markdown: string, opts?: {
|
|
@@ -67,7 +103,41 @@ export function markdownToTypst(markdown: string, opts?: {
|
|
|
67
103
|
images?: Map<string, string> | undefined;
|
|
68
104
|
headingOffset?: number | undefined;
|
|
69
105
|
anchorPrefix?: string | undefined;
|
|
106
|
+
findings?: object[] | undefined;
|
|
107
|
+
file?: string | undefined;
|
|
108
|
+
bodyLine?: number | undefined;
|
|
109
|
+
bodyColumn?: number | undefined;
|
|
110
|
+
lineMap?: {
|
|
111
|
+
line: number;
|
|
112
|
+
generated: boolean;
|
|
113
|
+
}[] | undefined;
|
|
114
|
+
prepared?: boolean | undefined;
|
|
115
|
+
url?: string | undefined;
|
|
70
116
|
}): string;
|
|
117
|
+
/**
|
|
118
|
+
* A page address as a reader of the book can follow it.
|
|
119
|
+
*
|
|
120
|
+
* **Every link the book sets as a URL is absolute.** A page's address is written
|
|
121
|
+
* from the root of the site that serves it — `/sohl/skill-guil/` — because on the
|
|
122
|
+
* website that is the shortest form that is right wherever the page is read
|
|
123
|
+
* from. A PDF is read from nowhere: a viewer handed a path has no document to
|
|
124
|
+
* resolve it against, so it either does nothing or looks for a file on the
|
|
125
|
+
* reader's own disk, and a book of a thousand pages carries a thousand links
|
|
126
|
+
* that do neither.
|
|
127
|
+
*
|
|
128
|
+
* So a root-relative address is resolved against the site's own, which is the
|
|
129
|
+
* one absolute address a build already holds. A **dependency's** page keeps its
|
|
130
|
+
* own package prefix, from the registry in
|
|
131
|
+
* {@link module:engine/content-address.PACKAGE_BASE}, so it resolves to the host
|
|
132
|
+
* serving that package — and where the registry names a host of its own the
|
|
133
|
+
* address is already absolute and is left exactly as it is.
|
|
134
|
+
*
|
|
135
|
+
* @param {string} url - The address, as the link pass resolved it.
|
|
136
|
+
* @param {string} [site] - The absolute address this book's pages are served at.
|
|
137
|
+
* @returns {string} An absolute URL, or the address unchanged when there is no
|
|
138
|
+
* site to resolve it against or it is already absolute.
|
|
139
|
+
*/
|
|
140
|
+
export function absolutePageUrl(url: string, site?: string): string;
|
|
71
141
|
/**
|
|
72
142
|
* The Typst definitions the book's page furniture is drawn with.
|
|
73
143
|
*
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Check one affiliation's `data.governance.ranks` against the rung contract.
|
|
3
|
+
*
|
|
4
|
+
* Each problem is its own finding, so a rung missing all three of its required
|
|
5
|
+
* keys reports three rather than the first: a reader fixing a ladder wants the
|
|
6
|
+
* whole of it in one pass.
|
|
7
|
+
*
|
|
8
|
+
* **A finding is located by the node it is about**, through the frontmatter's
|
|
9
|
+
* own YAML tree — a present-but-wrong value points at that value, and an absent
|
|
10
|
+
* key points at the rung that should have carried it, since a key that was
|
|
11
|
+
* never written has no position of its own. Where even the rung cannot be
|
|
12
|
+
* located the position is dropped rather than guessed, and the finding names
|
|
13
|
+
* the file alone.
|
|
14
|
+
*
|
|
15
|
+
* @param {object} note - The note being checked.
|
|
16
|
+
* @param {string} note.file - Its path, as a diagnostic spells it.
|
|
17
|
+
* @param {string} [note.raw] - Its source, for the position.
|
|
18
|
+
* @param {object} [note.fm] - Its parsed frontmatter.
|
|
19
|
+
* @returns {Array<{file: string, line?: number, column?: number,
|
|
20
|
+
* severity: string, message: string}>} The findings, in authored order.
|
|
21
|
+
*/
|
|
22
|
+
export function checkRankLadder(note: {
|
|
23
|
+
file: string;
|
|
24
|
+
raw?: string | undefined;
|
|
25
|
+
fm?: object | undefined;
|
|
26
|
+
}): Array<{
|
|
27
|
+
file: string;
|
|
28
|
+
line?: number;
|
|
29
|
+
column?: number;
|
|
30
|
+
severity: string;
|
|
31
|
+
message: string;
|
|
32
|
+
}>;
|
|
@@ -256,12 +256,10 @@ export function legacyKeyMessage(block: string, field: {
|
|
|
256
256
|
* What a note writing a field at the **top-level key `data:` gathered it off**
|
|
257
257
|
* is told.
|
|
258
258
|
*
|
|
259
|
-
* {@link legacyKeyMessage}'s counterpart for the other retiring position.
|
|
260
|
-
*
|
|
261
|
-
*
|
|
262
|
-
*
|
|
263
|
-
* of `data.portrait`, and both are read for the same reason both in-block
|
|
264
|
-
* spellings are: a package moves its corpus when it is ready, not on a flag day.
|
|
259
|
+
* {@link legacyKeyMessage}'s counterpart for the other retiring position.
|
|
260
|
+
* `portrait:` beside `img:` is the bare spelling of `data.portrait`, and both
|
|
261
|
+
* are read for the same reason both in-block spellings are: a package moves its
|
|
262
|
+
* corpus when it is ready, not on a flag day.
|
|
265
263
|
*
|
|
266
264
|
* It says nothing about which value is emitted, because that is not what an
|
|
267
265
|
* author needs from it. The value is the same either way; what the finding
|
|
@@ -249,7 +249,7 @@ export function renderSitePage(page: any, { index, foreign, universe, pass, deco
|
|
|
249
249
|
artIndex: any;
|
|
250
250
|
}): {
|
|
251
251
|
page: any;
|
|
252
|
-
body:
|
|
252
|
+
body: string;
|
|
253
253
|
data: object;
|
|
254
254
|
resolved: any[];
|
|
255
255
|
tableErrors: object[];
|
|
@@ -266,6 +266,18 @@ export function renderSitePage(page: any, { index, foreign, universe, pass, deco
|
|
|
266
266
|
column: number;
|
|
267
267
|
message: string;
|
|
268
268
|
}[];
|
|
269
|
+
headingErrors: {
|
|
270
|
+
line: number;
|
|
271
|
+
column: number;
|
|
272
|
+
message: string;
|
|
273
|
+
file: any;
|
|
274
|
+
}[];
|
|
275
|
+
footnoteErrors: {
|
|
276
|
+
file: any;
|
|
277
|
+
line: number;
|
|
278
|
+
column: number;
|
|
279
|
+
message: string;
|
|
280
|
+
}[];
|
|
269
281
|
wikiErrors: any[];
|
|
270
282
|
imageErrors: any[];
|
|
271
283
|
};
|
|
@@ -178,12 +178,21 @@ export const DISABLE_KINDS: readonly string[];
|
|
|
178
178
|
* `<span>` marking an unresolved link — and Goldmark drops raw HTML unless
|
|
179
179
|
* told otherwise. A theme cannot supply this: Hugo does not merge a theme's
|
|
180
180
|
* `markup` block.
|
|
181
|
+
*
|
|
182
|
+
* `extensions.taskList` is off because the format has no checkbox: Goldmark
|
|
183
|
+
* enables it by default, which is the only reason a list item opened with
|
|
184
|
+
* `[ ]` renders a `<input type="checkbox">` here and the same two characters as
|
|
185
|
+
* plain text in the pack and the book. Off, a list item's text begins right
|
|
186
|
+
* after its marker on every surface.
|
|
181
187
|
*/
|
|
182
188
|
export const MARKUP: Readonly<{
|
|
183
189
|
goldmark: Readonly<{
|
|
184
190
|
renderer: Readonly<{
|
|
185
191
|
unsafe: true;
|
|
186
192
|
}>;
|
|
193
|
+
extensions: Readonly<{
|
|
194
|
+
taskList: false;
|
|
195
|
+
}>;
|
|
187
196
|
}>;
|
|
188
197
|
}>;
|
|
189
198
|
/** Where the navigation is published. */
|