@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
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The cascade layer the sheet loads into, per package kind.
|
|
3
|
+
*
|
|
4
|
+
* Foundry declares its layer order in `css/foundry2.css`, ending
|
|
5
|
+
* `… layouts, system, modules, exceptions` — the eighth and ninth being, by its
|
|
6
|
+
* own comments, default game system styles and default module styles. That is
|
|
7
|
+
* what this sheet is, so it takes the layer its kind is given and is therefore
|
|
8
|
+
* the weakest thing a package can load:
|
|
9
|
+
*
|
|
10
|
+
* - **An unlayered package sheet beats it**, because an unlayered rule beats
|
|
11
|
+
* every layered one whatever its specificity.
|
|
12
|
+
* - **A package sheet naming any other layer beats it too**, because a layer
|
|
13
|
+
* Foundry's order does not mention is appended after the ones it does.
|
|
14
|
+
*
|
|
15
|
+
* So a package overrides the base sheet from its own, whichever way it declares
|
|
16
|
+
* one, and the override needs no `!important` and no specificity contest.
|
|
17
|
+
*
|
|
18
|
+
* @param {"system"|"module"} artifact - Which artifact is shipped.
|
|
19
|
+
* @returns {string} The layer name.
|
|
20
|
+
*/
|
|
21
|
+
export function baseStyleLayer(artifact: "system" | "module"): string;
|
|
22
|
+
/**
|
|
23
|
+
* The manifest `styles` entry for the base sheet.
|
|
24
|
+
*
|
|
25
|
+
* Foundry's `styles` is an array of `{layer?, src}`, and a bare string is
|
|
26
|
+
* migrated into that shape on read, so the object form is written directly.
|
|
27
|
+
*
|
|
28
|
+
* @param {"system"|"module"} artifact - Which artifact is shipped.
|
|
29
|
+
* @returns {{src: string, layer: string}} The entry.
|
|
30
|
+
*/
|
|
31
|
+
export function baseStyleEntry(artifact: "system" | "module"): {
|
|
32
|
+
src: string;
|
|
33
|
+
layer: string;
|
|
34
|
+
};
|
|
35
|
+
/**
|
|
36
|
+
* The absolute path of the sheet as installed, so the stage can copy it.
|
|
37
|
+
*
|
|
38
|
+
* Resolved against this module rather than against the consumer's working
|
|
39
|
+
* directory, because the file belongs to this package and arrives through
|
|
40
|
+
* `node_modules`.
|
|
41
|
+
*
|
|
42
|
+
* @returns {string} Absolute path of the shipped stylesheet.
|
|
43
|
+
*/
|
|
44
|
+
export function baseStylesheetSource(): string;
|
|
45
|
+
/**
|
|
46
|
+
* The `[source, destination]` pair that stages the sheet.
|
|
47
|
+
*
|
|
48
|
+
* The destination is relative, in the same spelling the repository's own asset
|
|
49
|
+
* table uses, so the caller resolves both against the repository root exactly
|
|
50
|
+
* as it resolves every other entry.
|
|
51
|
+
*
|
|
52
|
+
* @param {string} stageDir - The configured stage directory, relative to the
|
|
53
|
+
* repository root.
|
|
54
|
+
* @returns {[string, string]} The pair, for `stageAssets`.
|
|
55
|
+
*/
|
|
56
|
+
export function baseStyleStageEntry(stageDir: string): [string, string];
|
|
57
|
+
/**
|
|
58
|
+
* The manifest's `styles`, with the base sheet named first.
|
|
59
|
+
*
|
|
60
|
+
* **First rather than last**, because `styles` is load order: a package's own
|
|
61
|
+
* sheet has to be read after the base one for an override written at equal
|
|
62
|
+
* specificity in an equal layer to take. The cascade layer makes the override
|
|
63
|
+
* work anyway, and the order makes it work for the right reason.
|
|
64
|
+
*
|
|
65
|
+
* A declared entry addressing the same file is not duplicated — a repository
|
|
66
|
+
* that named the staged path itself gets one entry, keeping whatever layer it
|
|
67
|
+
* chose, so declaring it is a way of placing it rather than an error.
|
|
68
|
+
*
|
|
69
|
+
* @param {object} options
|
|
70
|
+
* @param {unknown} [options.declared] - The declared `styles`, whatever shape
|
|
71
|
+
* it was written in; anything that is not a list is returned untouched.
|
|
72
|
+
* @param {"system"|"module"} options.artifact - Which artifact is shipped.
|
|
73
|
+
* @param {boolean} [options.enabled] - Whether the package takes the base
|
|
74
|
+
* sheet. `false` returns the declared list unchanged.
|
|
75
|
+
* @returns {unknown} The `styles` to emit, or `undefined` when there is
|
|
76
|
+
* nothing to emit at all.
|
|
77
|
+
*/
|
|
78
|
+
export function withBaseStyle({ declared, artifact, enabled }: {
|
|
79
|
+
declared?: unknown;
|
|
80
|
+
artifact: "system" | "module";
|
|
81
|
+
enabled?: boolean | undefined;
|
|
82
|
+
}): unknown;
|
|
83
|
+
/**
|
|
84
|
+
* Where the shipped sheet lands inside the staged package.
|
|
85
|
+
*
|
|
86
|
+
* Under `styles/` beside a package's own sheet, and named for the toolchain so
|
|
87
|
+
* nothing a repository writes can collide with it. The path is relative to the
|
|
88
|
+
* staged package root, which is what both the stage entry and the manifest's
|
|
89
|
+
* `styles` are addressed in.
|
|
90
|
+
*
|
|
91
|
+
* @type {string}
|
|
92
|
+
*/
|
|
93
|
+
export const BASE_STYLESHEET_DEST: string;
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Find named blocks, leaving code examples alone.
|
|
3
|
+
*
|
|
4
|
+
* A block reaches `blocks` only when it is well formed and closed; everything
|
|
5
|
+
* else is a finding, and the surrounding blocks are unaffected.
|
|
6
|
+
*
|
|
7
|
+
* @param {string} source - Markdown that may contain named blocks.
|
|
8
|
+
* @returns {{blocks: Array<{name: string, title: string, id: string, classes: string[], attributes: Record<string, string>, start: number, end: number, body: string}>, errors: Array<{line: number, column: number, message: string}>}}
|
|
9
|
+
*/
|
|
10
|
+
export function scanBlocks(source: string): {
|
|
11
|
+
blocks: Array<{
|
|
12
|
+
name: string;
|
|
13
|
+
title: string;
|
|
14
|
+
id: string;
|
|
15
|
+
classes: string[];
|
|
16
|
+
attributes: Record<string, string>;
|
|
17
|
+
start: number;
|
|
18
|
+
end: number;
|
|
19
|
+
body: string;
|
|
20
|
+
}>;
|
|
21
|
+
errors: Array<{
|
|
22
|
+
line: number;
|
|
23
|
+
column: number;
|
|
24
|
+
message: string;
|
|
25
|
+
}>;
|
|
26
|
+
};
|
|
27
|
+
/**
|
|
28
|
+
* Render named blocks for one publishing surface.
|
|
29
|
+
*
|
|
30
|
+
* `foundry` emits a `<section>` whose heading is its first line; `web` emits a
|
|
31
|
+
* `<details>` the reader opens. Neither carries an inline `style`.
|
|
32
|
+
*
|
|
33
|
+
* The body is left as Markdown between the wrapper's tags, each separated from
|
|
34
|
+
* its tag by a blank line — see the module docs for why that is what lets the
|
|
35
|
+
* surrounding render treat the body as real content rather than an opaque
|
|
36
|
+
* fragment.
|
|
37
|
+
*
|
|
38
|
+
* A finding does not stop the well-formed blocks around it from rendering.
|
|
39
|
+
*
|
|
40
|
+
* @param {string} source - Markdown containing named blocks.
|
|
41
|
+
* @param {"foundry"|"web"} target - Publishing surface.
|
|
42
|
+
* @returns {{markdown: string, errors: Array<{line: number, column: number, message: string}>}}
|
|
43
|
+
*/
|
|
44
|
+
export function renderBlocks(source: string, target: "foundry" | "web"): {
|
|
45
|
+
markdown: string;
|
|
46
|
+
errors: Array<{
|
|
47
|
+
line: number;
|
|
48
|
+
column: number;
|
|
49
|
+
message: string;
|
|
50
|
+
}>;
|
|
51
|
+
};
|
|
52
|
+
/**
|
|
53
|
+
* Wrap each withheld section in the disclosure a `secret` block renders as.
|
|
54
|
+
*
|
|
55
|
+
* The section's own Markdown is left as Markdown, with a blank line either side
|
|
56
|
+
* of it, so the page's renderer still reads it: the heading keeps its id, the
|
|
57
|
+
* prose keeps its links, and only the disclosure around them is written as HTML.
|
|
58
|
+
*
|
|
59
|
+
* **A spoiler, not access control.** The section is in the published page, so a
|
|
60
|
+
* reader who opens the element or reads the source reads it. Foundry is the one
|
|
61
|
+
* surface that withholds anything.
|
|
62
|
+
*
|
|
63
|
+
* @param {string} source - A page's Markdown, headings intact.
|
|
64
|
+
* @returns {string} The same Markdown, each withheld section inside a
|
|
65
|
+
* `<details>`.
|
|
66
|
+
*/
|
|
67
|
+
export function renderWithheldSections(source: string): string;
|
|
68
|
+
/** The blocks an author may open, with the heading each takes by default. */
|
|
69
|
+
export const BLOCK_NAMES: Readonly<{
|
|
70
|
+
info: "Info";
|
|
71
|
+
secret: "Secret";
|
|
72
|
+
warn: "Warn";
|
|
73
|
+
}>;
|
|
74
|
+
/**
|
|
75
|
+
* Blocks that may hold another named block.
|
|
76
|
+
*
|
|
77
|
+
* A box inside a GM-only section is a real thing to write — the section is a
|
|
78
|
+
* container for whatever the GM reads, boxes included — so a `secret` holds one
|
|
79
|
+
* and the box stays inside it. A box inside a box is noise, so `info` and `warn`
|
|
80
|
+
* hold no named block and a second opener inside one is a finding.
|
|
81
|
+
*
|
|
82
|
+
* Exported because a renderer has to know it: the body of a container is read
|
|
83
|
+
* for blocks of its own, and the body of a box is not.
|
|
84
|
+
*
|
|
85
|
+
* @type {readonly string[]}
|
|
86
|
+
*/
|
|
87
|
+
export const BLOCK_CONTAINERS: readonly string[];
|
|
@@ -27,7 +27,20 @@ export function scanCaptions(source: string): {
|
|
|
27
27
|
message: string;
|
|
28
28
|
}>;
|
|
29
29
|
};
|
|
30
|
-
/**
|
|
30
|
+
/**
|
|
31
|
+
* Render captioned blocks as HTML, leaving other Markdown untouched.
|
|
32
|
+
*
|
|
33
|
+
* A code, table or prose caption's block is left as Markdown inside the
|
|
34
|
+
* wrapper, blank-line separated from its tags exactly as
|
|
35
|
+
* {@link module:engine/content-blocks.renderBlocks} leaves a named block's
|
|
36
|
+
* body — so the surrounding render sees it as part of its own document and a
|
|
37
|
+
* footnote reference inside it resolves against the note's own definitions.
|
|
38
|
+
*
|
|
39
|
+
* A figure's block is still rendered here, because its `<figcaption>` has to
|
|
40
|
+
* be stripped from HTML this pass controls directly; the surrounding render
|
|
41
|
+
* never sees it as Markdown, so a footnote reference inside a figure caption
|
|
42
|
+
* is not resolved, the same gap a block's own title carries.
|
|
43
|
+
*/
|
|
31
44
|
export function renderCaptionBlocks(source: any, renderMarkdown: ((src: string, env?: import("markdown-it").Env) => string) | undefined, numbers: any): {
|
|
32
45
|
markdown: any;
|
|
33
46
|
errors: {
|
|
@@ -8,3 +8,76 @@ export function separateFootnotes(source: string): {
|
|
|
8
8
|
markdown: string;
|
|
9
9
|
definitions: string;
|
|
10
10
|
};
|
|
11
|
+
/**
|
|
12
|
+
* A footnote definition written inside a list item or a block quote, where
|
|
13
|
+
* the web and Foundry can resolve it but the book drops it silently.
|
|
14
|
+
*
|
|
15
|
+
* A definition inside a table cell is not reported here: a table cell is
|
|
16
|
+
* inline content, so `[^id]:` there is never recognised as a definition on
|
|
17
|
+
* any surface and simply prints as written — the reference it was meant to
|
|
18
|
+
* answer is what {@link unresolvedFootnoteReferences} reports.
|
|
19
|
+
*
|
|
20
|
+
* @param {string} source - A note's markdown body.
|
|
21
|
+
* @returns {Array<{line: number, column: number, label: string, message: string}>}
|
|
22
|
+
*/
|
|
23
|
+
export function misplacedFootnoteDefinitions(source: string): Array<{
|
|
24
|
+
line: number;
|
|
25
|
+
column: number;
|
|
26
|
+
label: string;
|
|
27
|
+
message: string;
|
|
28
|
+
}>;
|
|
29
|
+
/**
|
|
30
|
+
* A footnote reference with no top-level definition to resolve against.
|
|
31
|
+
*
|
|
32
|
+
* Covers both commoner slips alike: a label never defined at all, and one
|
|
33
|
+
* "defined" only inside a table cell, which is inline content and never
|
|
34
|
+
* registers as a definition on any surface. A label misplaced in a list or a
|
|
35
|
+
* block quote is excluded — {@link misplacedFootnoteDefinitions} already
|
|
36
|
+
* reports that mistake at the definition's own line, and reporting it again
|
|
37
|
+
* at the reference would say the same thing twice.
|
|
38
|
+
*
|
|
39
|
+
* @param {string} source - A note's markdown body.
|
|
40
|
+
* @param {string} [definitions] - Its top-level definitions, when a caller
|
|
41
|
+
* already extracted them with {@link separateFootnotes}. Derived from
|
|
42
|
+
* `source` when omitted; a caller already holding both passes its own
|
|
43
|
+
* extraction instead, so the two cannot disagree about what is defined.
|
|
44
|
+
* @returns {Array<{line: number, column: number, message: string}>}
|
|
45
|
+
*/
|
|
46
|
+
export function unresolvedFootnoteReferences(source: string, definitions?: string): Array<{
|
|
47
|
+
line: number;
|
|
48
|
+
column: number;
|
|
49
|
+
message: string;
|
|
50
|
+
}>;
|
|
51
|
+
/**
|
|
52
|
+
* A top-level footnote definition no reference anywhere in the note uses.
|
|
53
|
+
*
|
|
54
|
+
* `separateFootnotes` lifts it out correctly, nothing resolves against it,
|
|
55
|
+
* and the paragraph it held vanishes from every surface with the build
|
|
56
|
+
* exiting 0 — a dropped paragraph is not something a reader can notice is
|
|
57
|
+
* missing, so it is an error here rather than a warning, the same severity
|
|
58
|
+
* every other silent loss this pass catches already carries. A note mid-edit,
|
|
59
|
+
* with the reference already deleted and the definition not yet followed, is
|
|
60
|
+
* exactly the case this refuses — which is the point: the alternative is
|
|
61
|
+
* shipping it.
|
|
62
|
+
*
|
|
63
|
+
* @param {string} source - A note's markdown body.
|
|
64
|
+
* @returns {Array<{line: number, column: number, message: string}>}
|
|
65
|
+
*/
|
|
66
|
+
export function unusedFootnoteDefinitions(source: string): Array<{
|
|
67
|
+
line: number;
|
|
68
|
+
column: number;
|
|
69
|
+
message: string;
|
|
70
|
+
}>;
|
|
71
|
+
/**
|
|
72
|
+
* Every footnote placement finding for one note body — the union
|
|
73
|
+
* {@link misplacedFootnoteDefinitions}, {@link unresolvedFootnoteReferences}
|
|
74
|
+
* and {@link unusedFootnoteDefinitions} report, in document order.
|
|
75
|
+
*
|
|
76
|
+
* @param {string} source - A note's markdown body.
|
|
77
|
+
* @returns {Array<{line: number, column: number, message: string}>}
|
|
78
|
+
*/
|
|
79
|
+
export function footnoteFindings(source: string): Array<{
|
|
80
|
+
line: number;
|
|
81
|
+
column: number;
|
|
82
|
+
message: string;
|
|
83
|
+
}>;
|
|
@@ -180,12 +180,14 @@ export function checkDeclaredFields({ format, itemFields, system, severity }: {
|
|
|
180
180
|
/**
|
|
181
181
|
* Top-level keys the format names as the note's own, whatever its type.
|
|
182
182
|
*
|
|
183
|
-
*
|
|
184
|
-
*
|
|
185
|
-
*
|
|
186
|
-
*
|
|
187
|
-
*
|
|
188
|
-
*
|
|
183
|
+
* The top-level vocabulary itself, read rather than restated: this set exists
|
|
184
|
+
* to keep a note's own identity fields from being mistaken for a misplaced
|
|
185
|
+
* `data` property where the two share a name, and the keys that are a note's
|
|
186
|
+
* own are exactly the ones the region declares. `type` and `subType` are the
|
|
187
|
+
* pair that matters — several mapping tables name `subType` as a shared source,
|
|
188
|
+
* and it is authored at top level by design. A key the region does *not*
|
|
189
|
+
* declare, such as `id` beside `data.id`, is reported as the misplaced `data`
|
|
190
|
+
* property it is.
|
|
189
191
|
*
|
|
190
192
|
* @type {ReadonlySet<string>}
|
|
191
193
|
*/
|
|
@@ -1,13 +1,21 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Replace prepared SQL fences with Markdown
|
|
3
|
-
*
|
|
2
|
+
* Replace prepared SQL fences and page lists with Markdown while retaining
|
|
3
|
+
* source lines.
|
|
4
|
+
*
|
|
5
|
+
* Both directives read the content index, so both are answered before this
|
|
6
|
+
* pass runs — query execution in the asynchronous SQL preparation pass, page
|
|
7
|
+
* lists in {@link module:engine/page-lists.preparePageLists} — and each is
|
|
8
|
+
* looked up by the ordinal of its own kind of fence within the body.
|
|
9
|
+
*
|
|
4
10
|
* @param {string} markdown - The authored body.
|
|
5
|
-
* @param {{source?: string, sqlTables?: object[]}} [context] -
|
|
11
|
+
* @param {{source?: string, sqlTables?: object[], pageLists?: object[]}} [context] -
|
|
12
|
+
* Prepared results.
|
|
6
13
|
* @returns {{markdown: string, errors: object[], warnings: object[], lineMap: object[]}}
|
|
7
14
|
*/
|
|
8
|
-
export function expandContentTables(markdown: string, { source, sqlTables }?: {
|
|
15
|
+
export function expandContentTables(markdown: string, { source, sqlTables, pageLists }?: {
|
|
9
16
|
source?: string;
|
|
10
17
|
sqlTables?: object[];
|
|
18
|
+
pageLists?: object[];
|
|
11
19
|
}): {
|
|
12
20
|
markdown: string;
|
|
13
21
|
errors: object[];
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What a note opening a list item with `[ ]` or `[x]` is told.
|
|
3
|
+
*
|
|
4
|
+
* @param {string} marker - The matched marker, as written.
|
|
5
|
+
* @returns {string} The message, unpunctuated at the end as a finding is.
|
|
6
|
+
*/
|
|
7
|
+
export function taskListMessage(marker: string): string;
|
|
8
|
+
/**
|
|
9
|
+
* Every list item opened with `[ ]` or `[x]` in one note's body.
|
|
10
|
+
*
|
|
11
|
+
* @param {string} body - The note's markdown, without its frontmatter.
|
|
12
|
+
* @param {string} file - The note's path, for the finding.
|
|
13
|
+
* @param {object} [opts]
|
|
14
|
+
* @param {number} [opts.bodyLine=1] - The 1-based file line the body starts on.
|
|
15
|
+
* @param {number} [opts.bodyColumn=1] - The 1-based file column it starts at.
|
|
16
|
+
* @returns {Array<{file: string, line: number, column: number,
|
|
17
|
+
* severity: "warning", message: string}>} One finding per item, in source
|
|
18
|
+
* order.
|
|
19
|
+
*/
|
|
20
|
+
export function checkTaskLists(body: string, file: string, { bodyLine, bodyColumn }?: {
|
|
21
|
+
bodyLine?: number | undefined;
|
|
22
|
+
bodyColumn?: number | undefined;
|
|
23
|
+
}): Array<{
|
|
24
|
+
file: string;
|
|
25
|
+
line: number;
|
|
26
|
+
column: number;
|
|
27
|
+
severity: "warning";
|
|
28
|
+
message: string;
|
|
29
|
+
}>;
|
|
30
|
+
/**
|
|
31
|
+
* Walk a content tree and report every list item opened with `[ ]` or `[x]`.
|
|
32
|
+
*
|
|
33
|
+
* The frontmatter fence is taken off first, so what is scanned is the body and
|
|
34
|
+
* the positions are still the file's. A file with no frontmatter is scanned
|
|
35
|
+
* whole: it is not a note, but a stray `.md` in the tree carrying the marker is
|
|
36
|
+
* the same problem for the same reason.
|
|
37
|
+
*
|
|
38
|
+
* A finding names its file **relative to the working directory**, which is
|
|
39
|
+
* where a reader is standing and what `formatDiagnostic` emits. The content
|
|
40
|
+
* root is where the walk starts, not what a path is measured from.
|
|
41
|
+
*
|
|
42
|
+
* @param {string} contentBase - Root of the content tree.
|
|
43
|
+
* @param {object} [opts]
|
|
44
|
+
* @param {readonly string[]} [opts.skipDirectories] - Directory names to ignore
|
|
45
|
+
* in addition to the dot-directories always skipped.
|
|
46
|
+
* @returns {{findings: Array<{file: string, line: number, column: number,
|
|
47
|
+
* severity: "warning", message: string}>, files: number}} The findings, and
|
|
48
|
+
* how many files were read.
|
|
49
|
+
*/
|
|
50
|
+
export function lintContentTaskLists(contentBase: string, { skipDirectories }?: {
|
|
51
|
+
skipDirectories?: readonly string[] | undefined;
|
|
52
|
+
}): {
|
|
53
|
+
findings: Array<{
|
|
54
|
+
file: string;
|
|
55
|
+
line: number;
|
|
56
|
+
column: number;
|
|
57
|
+
severity: "warning";
|
|
58
|
+
message: string;
|
|
59
|
+
}>;
|
|
60
|
+
files: number;
|
|
61
|
+
};
|
|
62
|
+
/**
|
|
63
|
+
* A list item's marker, immediately followed by `[ ]` or `[x]` and a space —
|
|
64
|
+
* bulleted (`-`, `*`, `+`) or ordered (`1.` or `1)`), at the start of a line.
|
|
65
|
+
*
|
|
66
|
+
* The leading indentation is a lookbehind rather than part of the match, so a
|
|
67
|
+
* nested item's finding points at the marker itself, not at the line's first
|
|
68
|
+
* column.
|
|
69
|
+
*
|
|
70
|
+
* @type {RegExp}
|
|
71
|
+
*/
|
|
72
|
+
export const TASK_LIST_MARKER: RegExp;
|
|
@@ -2,9 +2,15 @@
|
|
|
2
2
|
* Read the shared braced attribute grammar used by body extensions.
|
|
3
3
|
* Callers validate which names and classes their extension understands.
|
|
4
4
|
* @param {string} source - Text inside the braces.
|
|
5
|
+
* @param {object} [options={}]
|
|
6
|
+
* @param {RegExp} [options.idPattern=NAME] - What an `#id` may be named. A
|
|
7
|
+
* construct whose id is published as an HTML `id` admits a digit first and
|
|
8
|
+
* passes a looser pattern; the default is the pattern every other name takes.
|
|
5
9
|
* @returns {{id: string, classes: string[], values: Record<string, string>, problems: string[]}}
|
|
6
10
|
*/
|
|
7
|
-
export function parseExtensionAttributes(source: string
|
|
11
|
+
export function parseExtensionAttributes(source: string, { idPattern }?: {
|
|
12
|
+
idPattern?: RegExp | undefined;
|
|
13
|
+
}): {
|
|
8
14
|
id: string;
|
|
9
15
|
classes: string[];
|
|
10
16
|
values: Record<string, string>;
|
|
@@ -12,3 +18,18 @@ export function parseExtensionAttributes(source: string): {
|
|
|
12
18
|
};
|
|
13
19
|
/** A Boolean attribute is spelled exactly `true` or `false`. */
|
|
14
20
|
export function booleanAttribute(value: any, key: any): boolean;
|
|
21
|
+
/**
|
|
22
|
+
* The attribute keys no construct writes onto an element.
|
|
23
|
+
*
|
|
24
|
+
* `id=` and `class=` are refused in favour of `#id` and `.class`, which the
|
|
25
|
+
* grammar already states; a key beginning `on` is an event handler, and nothing
|
|
26
|
+
* authored in a note becomes code on a published page.
|
|
27
|
+
*
|
|
28
|
+
* Shared by every construct that writes an author's attributes onto an element,
|
|
29
|
+
* because a refusal that held on one and not the next would be the hole the
|
|
30
|
+
* other construct is checked for.
|
|
31
|
+
*
|
|
32
|
+
* @param {Record<string, string>} values - The `key=value` attributes parsed.
|
|
33
|
+
* @returns {string[]} One message per refused key, in the order written.
|
|
34
|
+
*/
|
|
35
|
+
export function refusedAttributes(values: Record<string, string>): string[];
|
|
@@ -137,7 +137,7 @@ export function resolveRelation(fm: object, ctx?: string): Record<string, string
|
|
|
137
137
|
*
|
|
138
138
|
* @param {object} fm - The item frontmatter.
|
|
139
139
|
* @param {string} [ctx] - Optional label for the error (defaults to the item's
|
|
140
|
-
*
|
|
140
|
+
* display name, else "item").
|
|
141
141
|
* @returns {string} The declared subType.
|
|
142
142
|
* @throws {Error} When `subType` is missing or blank.
|
|
143
143
|
*/
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Read a heading's text and the attribute block it may end with.
|
|
3
|
+
*
|
|
4
|
+
* A suffix that is not an attribute block — a set written in braces, a die
|
|
5
|
+
* expression — is left in the text and reported, so no surface silently prints
|
|
6
|
+
* a heading with words missing and no author is told to fix nothing.
|
|
7
|
+
*
|
|
8
|
+
* **One mistake draws one finding.** Braces holding nothing the grammar
|
|
9
|
+
* recognises are reported once, naming the braces: an author who wrote
|
|
10
|
+
* `{a, b}` meant no attribute block at all, and a complaint per fragment inside
|
|
11
|
+
* it says the same thing twice in words that name neither the heading nor the
|
|
12
|
+
* fix. Braces that *are* an attribute block, carrying one bad attribute beside
|
|
13
|
+
* good ones, relay the grammar's own complaint about that attribute.
|
|
14
|
+
*
|
|
15
|
+
* @param {string} heading - A heading's text, hashes already removed.
|
|
16
|
+
* @returns {{text: string, id: string, classes: string[],
|
|
17
|
+
* values: Record<string, string>, problems: string[], braces: boolean}}
|
|
18
|
+
* `text` is what a surface sets, `braces` whether a suffix was there at all.
|
|
19
|
+
*/
|
|
20
|
+
export function splitHeadingAttributes(heading: string): {
|
|
21
|
+
text: string;
|
|
22
|
+
id: string;
|
|
23
|
+
classes: string[];
|
|
24
|
+
values: Record<string, string>;
|
|
25
|
+
problems: string[];
|
|
26
|
+
braces: boolean;
|
|
27
|
+
};
|
|
28
|
+
/**
|
|
29
|
+
* Whether a heading opens a journal page.
|
|
30
|
+
*
|
|
31
|
+
* A Foundry UUID addresses a page and nothing smaller, so a section a link can
|
|
32
|
+
* reach has to be one: a top-level heading starts a page, and so does any
|
|
33
|
+
* heading that names an anchor. {@link module:engine/journals.splitPages}
|
|
34
|
+
* decides *where* a page begins — a captioned heading and a heading inside a
|
|
35
|
+
* fence or a named block are its business — and asks this which headings are
|
|
36
|
+
* eligible, so no caller re-decides it.
|
|
37
|
+
*
|
|
38
|
+
* @param {{level: number, id: string}} heading - From
|
|
39
|
+
* {@link scanHeadingAttributes}, or any `{level, id}` pair.
|
|
40
|
+
* @returns {boolean} Whether the heading opens a page.
|
|
41
|
+
*/
|
|
42
|
+
export function opensPage({ level, id }: {
|
|
43
|
+
level: number;
|
|
44
|
+
id: string;
|
|
45
|
+
}): boolean;
|
|
46
|
+
/**
|
|
47
|
+
* Every heading in a body, with what its attribute block declares.
|
|
48
|
+
*
|
|
49
|
+
* Fenced code is skipped: `#` opens a comment in most of what gets fenced, and
|
|
50
|
+
* an example heading in a document is not a heading of it.
|
|
51
|
+
*
|
|
52
|
+
* @param {string} source - A note's markdown body.
|
|
53
|
+
* @param {number} [bodyLine=1] - The 1-based file line the body starts on, so a
|
|
54
|
+
* finding lands where an editor can open it.
|
|
55
|
+
* @returns {{headings: Array<{level: number, line: number, text: string,
|
|
56
|
+
* id: string, classes: string[], values: Record<string, string>}>,
|
|
57
|
+
* errors: Array<{line: number, column: number, message: string}>}}
|
|
58
|
+
*/
|
|
59
|
+
export function scanHeadingAttributes(source: string, bodyLine?: number): {
|
|
60
|
+
headings: Array<{
|
|
61
|
+
level: number;
|
|
62
|
+
line: number;
|
|
63
|
+
text: string;
|
|
64
|
+
id: string;
|
|
65
|
+
classes: string[];
|
|
66
|
+
values: Record<string, string>;
|
|
67
|
+
}>;
|
|
68
|
+
errors: Array<{
|
|
69
|
+
line: number;
|
|
70
|
+
column: number;
|
|
71
|
+
message: string;
|
|
72
|
+
}>;
|
|
73
|
+
};
|
|
74
|
+
/**
|
|
75
|
+
* A markdown-it plugin writing a heading's attribute block onto its element.
|
|
76
|
+
*
|
|
77
|
+
* The website's renderer reads the block itself, so the surfaces that use
|
|
78
|
+
* markdown-it read it here and publish the same `id`, the same classes and the
|
|
79
|
+
* same attributes — rather than typesetting the braces an author wrote.
|
|
80
|
+
*
|
|
81
|
+
* @param {object} md - A markdown-it instance.
|
|
82
|
+
* @returns {void}
|
|
83
|
+
*/
|
|
84
|
+
export function headingAttributesPlugin(md: object): void;
|
|
85
|
+
/**
|
|
86
|
+
* Parse one line as a heading, and say whether it starts a journal page.
|
|
87
|
+
*
|
|
88
|
+
* The single reading of that rule. {@link module:engine/journals.splitPages}
|
|
89
|
+
* builds its pages from it; `scanBlocks` and `scanCaptions` refuse a heading
|
|
90
|
+
* that starts a page written where it cannot become one. All three ask this, so
|
|
91
|
+
* none of them can disagree about what a heading line means or about what starts
|
|
92
|
+
* a page — and the attribute block comes off here, so a class beside an anchor
|
|
93
|
+
* is not read as part of the anchor by any of them.
|
|
94
|
+
*
|
|
95
|
+
* @param {string} line - One line of a note's body.
|
|
96
|
+
* @returns {{level: number, text: string, id: string, classes: string[],
|
|
97
|
+
* values: Record<string, string>, problems: string[], startsPage: boolean}|null}
|
|
98
|
+
* `null` when the line is not a heading.
|
|
99
|
+
*/
|
|
100
|
+
export function parseHeadingLine(line: string): {
|
|
101
|
+
level: number;
|
|
102
|
+
text: string;
|
|
103
|
+
id: string;
|
|
104
|
+
classes: string[];
|
|
105
|
+
values: Record<string, string>;
|
|
106
|
+
problems: string[];
|
|
107
|
+
startsPage: boolean;
|
|
108
|
+
} | null;
|
|
109
|
+
/**
|
|
110
|
+
* Every heading that opens a journal page, by the line it sits on.
|
|
111
|
+
*
|
|
112
|
+
* The walk that decides it: fenced code holds no headings, a named block holds
|
|
113
|
+
* no page — the block is one passage of the page it is written in — and what is
|
|
114
|
+
* left is read by {@link opensPage}. {@link module:engine/journals.splitPages}
|
|
115
|
+
* builds the pages from this, and the passes that honour a class on a
|
|
116
|
+
* page-opening heading ask the same question of the same answer.
|
|
117
|
+
*
|
|
118
|
+
* A caption also opens a page, which is `splitPages`' business: a caption is
|
|
119
|
+
* found by the caption pass, and a heading it captions is listed here and
|
|
120
|
+
* dropped there.
|
|
121
|
+
*
|
|
122
|
+
* @param {string} source - A note's markdown body.
|
|
123
|
+
* @returns {Map<number, {level: number, line: number, text: string, id: string,
|
|
124
|
+
* classes: string[], values: Record<string, string>}>} Keyed by 0-based line.
|
|
125
|
+
*/
|
|
126
|
+
export function pageOpenings(source: string): Map<number, {
|
|
127
|
+
level: number;
|
|
128
|
+
line: number;
|
|
129
|
+
text: string;
|
|
130
|
+
id: string;
|
|
131
|
+
classes: string[];
|
|
132
|
+
values: Record<string, string>;
|
|
133
|
+
}>;
|
|
134
|
+
/**
|
|
135
|
+
* The spans a `.secret` heading withholds, and the classes written where no
|
|
136
|
+
* surface can honour them.
|
|
137
|
+
*
|
|
138
|
+
* A span runs from its heading to the line before the next heading that opens a
|
|
139
|
+
* page, which is the stretch Foundry makes one page of. A `.secret` on a heading
|
|
140
|
+
* that opens no page covers no page, so no surface can withhold it: that is a
|
|
141
|
+
* finding at the heading's own line rather than a class quietly ignored.
|
|
142
|
+
*
|
|
143
|
+
* @param {string} source - A note's markdown body.
|
|
144
|
+
* @param {number} [bodyLine=1] - The 1-based file line the body starts on.
|
|
145
|
+
* @returns {{sections: Array<{start: number, end: number, heading: object}>,
|
|
146
|
+
* errors: Array<{line: number, column: number, message: string}>}} `start` and
|
|
147
|
+
* `end` are 0-based line indexes, `end` exclusive.
|
|
148
|
+
*/
|
|
149
|
+
export function withheldSections(source: string, bodyLine?: number): {
|
|
150
|
+
sections: Array<{
|
|
151
|
+
start: number;
|
|
152
|
+
end: number;
|
|
153
|
+
heading: object;
|
|
154
|
+
}>;
|
|
155
|
+
errors: Array<{
|
|
156
|
+
line: number;
|
|
157
|
+
column: number;
|
|
158
|
+
message: string;
|
|
159
|
+
}>;
|
|
160
|
+
};
|
|
161
|
+
/**
|
|
162
|
+
* A heading line: its hashes and the text after them.
|
|
163
|
+
*
|
|
164
|
+
* Shared with every pass that has to find a heading before reading what it
|
|
165
|
+
* declares, so the passes cannot disagree about which lines are headings.
|
|
166
|
+
*/
|
|
167
|
+
export const HEADING_LINE: RegExp;
|
|
168
|
+
/**
|
|
169
|
+
* The class that withholds a section from players.
|
|
170
|
+
*
|
|
171
|
+
* Written on the heading that opens the section, so the section stays linkable:
|
|
172
|
+
* secrecy is a property of the section, not of the name a link resolves to.
|
|
173
|
+
*/
|
|
174
|
+
export const WITHHELD_CLASS: "secret";
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/** Render a note body with Foundry's
|
|
1
|
+
/** Render a note body with Foundry's named-block markup. */
|
|
2
2
|
export function renderFoundryMarkdown(body: any, captions: any, footnoteNumbers: any, docId: any): string;
|
|
3
3
|
/**
|
|
4
4
|
* Parses a markdown file with YAML frontmatter.
|
|
@@ -426,18 +426,21 @@ export function convertNoteWikilinks(body: string, { type, id, pack, docPack, in
|
|
|
426
426
|
unresolved: Array<object>;
|
|
427
427
|
};
|
|
428
428
|
/**
|
|
429
|
-
* Expand prepared SQL tables before resolving wikilinks in
|
|
429
|
+
* Expand prepared SQL tables and page lists before resolving wikilinks in the
|
|
430
|
+
* links they generate.
|
|
430
431
|
* @param {string} body - The note body.
|
|
431
|
-
* @param {object} ctx - Source and prepared
|
|
432
|
+
* @param {object} ctx - Source and prepared results.
|
|
432
433
|
* @param {string} ctx.name - Name used in diagnostics.
|
|
433
434
|
* @param {number} [ctx.bodyLine] - The first body line in the source file.
|
|
434
435
|
* @param {object[]} [ctx.sqlTables] - Prepared SQL results in document order.
|
|
436
|
+
* @param {object[]} [ctx.pageLists] - Prepared page lists in document order.
|
|
435
437
|
* @returns {{markdown: string, lineMap: Array<{line: number, generated: boolean}>}}
|
|
436
438
|
*/
|
|
437
|
-
export function expandNoteTables(body: string, { name, bodyLine, sqlTables }: {
|
|
439
|
+
export function expandNoteTables(body: string, { name, bodyLine, sqlTables, pageLists }: {
|
|
438
440
|
name: string;
|
|
439
441
|
bodyLine?: number | undefined;
|
|
440
442
|
sqlTables?: object[] | undefined;
|
|
443
|
+
pageLists?: object[] | undefined;
|
|
441
444
|
}): {
|
|
442
445
|
markdown: string;
|
|
443
446
|
lineMap: Array<{
|