@heroiclands/package-build 20.4.0 → 20.6.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 +288 -0
- package/CONTENT.md +213 -20
- package/README.md +19 -1
- package/bin/content-build.mjs +135 -32
- package/bin/package-build.mjs +46 -13
- package/content-config.mjs +345 -101
- package/docs/api.md +1352 -0
- package/docs/commands.md +1609 -0
- package/docs/configuration.md +1432 -0
- package/docs/content-format.md +16 -6
- package/docs/diagnostics.md +356 -0
- package/docs/getting-started.md +813 -0
- package/docs/project-setup.md +469 -0
- package/engine/actor-compiler.mjs +30 -27
- package/engine/address-diff.mjs +45 -41
- package/engine/base-compiler.mjs +6 -0
- package/engine/bundles.mjs +9 -0
- package/engine/content-address.mjs +9 -9
- package/engine/content-index.mjs +44 -23
- package/engine/content-links.mjs +44 -11
- package/engine/content-lint.mjs +44 -10
- package/engine/content-tables.mjs +32 -27
- package/engine/folder-notes.mjs +4 -2
- package/engine/frontmatter-lint.mjs +35 -38
- package/engine/generate.mjs +5 -0
- package/engine/helpers.mjs +86 -32
- package/engine/index.mjs +12 -2
- package/engine/journals.mjs +9 -0
- package/engine/note-claims.mjs +18 -10
- package/engine/note-schemas.mjs +0 -5
- package/engine/note-vocabulary.mjs +32 -31
- package/engine/pack-config.mjs +26 -12
- package/engine/pack-router.mjs +0 -0
- package/engine/pdf-build.mjs +464 -0
- package/engine/pdf-fonts.mjs +420 -0
- package/engine/pdf-render.mjs +876 -0
- package/engine/pdf-toc.mjs +525 -0
- package/engine/scenes.mjs +14 -5
- package/engine/schema-check.mjs +1 -1
- package/engine/site-build.mjs +21 -3
- package/engine/web-wikilinks.mjs +6 -3
- package/engine/wikilinks.mjs +2 -4
- package/hm3/actors.mjs +8 -0
- package/hm3/items.mjs +8 -0
- package/package.json +1 -1
- package/release.mjs +63 -3
- package/sohl/actors.mjs +8 -0
- package/sohl/items.mjs +8 -0
- package/sohl/note-schemas.mjs +5 -5
- package/types/content-config.d.mts +66 -15
- package/types/engine/actor-compiler.d.mts +34 -30
- package/types/engine/address-diff.d.mts +57 -3
- package/types/engine/base-compiler.d.mts +10 -2
- package/types/engine/bundles.d.mts +9 -0
- package/types/engine/content-address.d.mts +9 -9
- package/types/engine/content-index.d.mts +57 -13
- package/types/engine/content-lint.d.mts +6 -4
- package/types/engine/content-tables.d.mts +49 -18
- package/types/engine/frontmatter-lint.d.mts +3 -2
- package/types/engine/helpers.d.mts +105 -31
- package/types/engine/index.d.mts +4 -0
- package/types/engine/journals.d.mts +9 -0
- package/types/engine/note-claims.d.mts +17 -10
- package/types/engine/note-vocabulary.d.mts +23 -196
- package/types/engine/pack-config.d.mts +4 -4
- package/types/engine/pdf-build.d.mts +42 -0
- package/types/engine/pdf-fonts.d.mts +30 -0
- package/types/engine/pdf-render.d.mts +156 -0
- package/types/engine/pdf-toc.d.mts +114 -0
- package/types/engine/scenes.d.mts +10 -1
- package/types/engine/schema-check.d.mts +2 -2
- package/types/engine/site-build.d.mts +34 -6
- package/types/engine/wikilinks.d.mts +2 -3
- package/types/hm3/actors.d.mts +8 -0
- package/types/hm3/items.d.mts +8 -0
- package/types/release.d.mts +15 -4
- package/types/sohl/actors.d.mts +10 -2
- package/types/sohl/items.d.mts +8 -0
|
@@ -95,11 +95,11 @@ export function locateConfigError(err: unknown, configPath?: string): unknown;
|
|
|
95
95
|
*
|
|
96
96
|
* @param {unknown} data - The parsed configuration document.
|
|
97
97
|
* @param {string} configPath - Absolute path of the file it was parsed from.
|
|
98
|
-
* @returns {import("../config.mjs").ContentBuildConfig} The frozen configuration.
|
|
98
|
+
* @returns {import("../content-config.mjs").ContentBuildConfig} The frozen configuration.
|
|
99
99
|
* @throws {Error} When the document is not a mapping, declares `rootDir`, or
|
|
100
100
|
* names an item-builder registry this package does not ship.
|
|
101
101
|
*/
|
|
102
|
-
export function configFromData(data: unknown, configPath: string): import("../config.mjs").ContentBuildConfig;
|
|
102
|
+
export function configFromData(data: unknown, configPath: string): import("../content-config.mjs").ContentBuildConfig;
|
|
103
103
|
/**
|
|
104
104
|
* The consuming repository's resolved, frozen configuration.
|
|
105
105
|
*
|
|
@@ -108,12 +108,12 @@ export function configFromData(data: unknown, configPath: string): import("../co
|
|
|
108
108
|
* configuration (#2). The result is memoised, so calling it in a default
|
|
109
109
|
* parameter — the usual spelling here — costs one property read per call.
|
|
110
110
|
*
|
|
111
|
-
* @returns {import("../config.mjs").ContentBuildConfig} The frozen configuration.
|
|
111
|
+
* @returns {import("../content-config.mjs").ContentBuildConfig} The frozen configuration.
|
|
112
112
|
* @throws {Error} When no configuration file can be found, or the one named
|
|
113
113
|
* cannot be loaded. Absence is a defect, not a fallback: without it the
|
|
114
114
|
* compilers know neither what to compile nor where to put it.
|
|
115
115
|
*/
|
|
116
|
-
export function loadPackConfig(): import("../config.mjs").ContentBuildConfig;
|
|
116
|
+
export function loadPackConfig(): import("../content-config.mjs").ContentBuildConfig;
|
|
117
117
|
/**
|
|
118
118
|
* The file {@link loadPackConfig} resolved the configuration from.
|
|
119
119
|
*
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The file name a downloaded book identifies itself by.
|
|
3
|
+
*
|
|
4
|
+
* The zip and the manifest take their names from the manifest, so an asset's
|
|
5
|
+
* name and its advertised URL cannot disagree. A PDF has no advertised URL, so
|
|
6
|
+
* its name is a free choice — which is exactly why it is fixed here rather than
|
|
7
|
+
* left for each of six consumers to invent.
|
|
8
|
+
*
|
|
9
|
+
* @param {string} artifact - The package's artifact name.
|
|
10
|
+
* @param {string} version - The version being released.
|
|
11
|
+
* @returns {string} `<artifact>-<version>.pdf`, or `<artifact>.pdf` unversioned.
|
|
12
|
+
*/
|
|
13
|
+
export function pdfFileName(artifact: string, version: string): string;
|
|
14
|
+
/**
|
|
15
|
+
* Build the book.
|
|
16
|
+
*
|
|
17
|
+
* @param {object} [opts] - Options.
|
|
18
|
+
* @param {object} [opts.config] - A resolved configuration; loaded when absent.
|
|
19
|
+
* @param {string} [opts.out] - Where to write, overriding `pdf.out`.
|
|
20
|
+
* @param {string} [opts.version] - Stamped on the title page and the file name.
|
|
21
|
+
* @param {boolean} [opts.compile] - Whether to run Typst. False leaves the
|
|
22
|
+
* `.typ` source, which is what the unit tests read.
|
|
23
|
+
* @returns {Promise<object>} `{ built, reason, findings, typ, pdf, stats }`.
|
|
24
|
+
*/
|
|
25
|
+
export function buildPdf({ config, out, version, compile }?: {
|
|
26
|
+
config?: object | undefined;
|
|
27
|
+
out?: string | undefined;
|
|
28
|
+
version?: string | undefined;
|
|
29
|
+
compile?: boolean | undefined;
|
|
30
|
+
}): Promise<object>;
|
|
31
|
+
/**
|
|
32
|
+
* Run Typst over the emitted source.
|
|
33
|
+
*
|
|
34
|
+
* @param {string} typPath - The `.typ` file.
|
|
35
|
+
* @param {string} pdfPath - Where the PDF goes.
|
|
36
|
+
* @param {object} pdf - The resolved `pdf:` block.
|
|
37
|
+
* @returns {{ok: boolean, message: string}} What happened.
|
|
38
|
+
*/
|
|
39
|
+
export function compileTypst(typPath: string, pdfPath: string, pdf?: object): {
|
|
40
|
+
ok: boolean;
|
|
41
|
+
message: string;
|
|
42
|
+
};
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Every glyph name a font carries, with the codepoint that reaches it.
|
|
3
|
+
*
|
|
4
|
+
* @param {string} file - Path to a `.ttf`/`.otf`.
|
|
5
|
+
* @returns {Map<string, number>} Name → codepoint.
|
|
6
|
+
*/
|
|
7
|
+
export function glyphTable(file: string): Map<string, number>;
|
|
8
|
+
/**
|
|
9
|
+
* The family name a font file announces, for Typst's `text(font: …)`.
|
|
10
|
+
*
|
|
11
|
+
* Read from the `name` table rather than from the file name, because the file
|
|
12
|
+
* is what a consumer happened to call it and the family is what the font stack
|
|
13
|
+
* will match on.
|
|
14
|
+
*
|
|
15
|
+
* @param {string} file - Path to a `.ttf`/`.otf`.
|
|
16
|
+
* @returns {string} The family name, or "" when the table cannot be read.
|
|
17
|
+
*/
|
|
18
|
+
export function familyName(file: string): string;
|
|
19
|
+
/**
|
|
20
|
+
* Resolve every icon in a registry against the fonts a consumer named.
|
|
21
|
+
*
|
|
22
|
+
* @param {object} registry - The resolved `icons:` registry.
|
|
23
|
+
* @param {Record<string, string>} iconFonts - Family name → font file.
|
|
24
|
+
* @param {object[]} [findings] - Collected here rather than thrown.
|
|
25
|
+
* @returns {Map<string, {font: string, codepoint: number}>} Icon name → glyph.
|
|
26
|
+
*/
|
|
27
|
+
export function resolveIconGlyphs(registry: object, iconFonts?: Record<string, string>, findings?: object[]): Map<string, {
|
|
28
|
+
font: string;
|
|
29
|
+
codepoint: number;
|
|
30
|
+
}>;
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Escape literal text for Typst markup.
|
|
3
|
+
*
|
|
4
|
+
* @param {string} text - Text as the author wrote it.
|
|
5
|
+
* @returns {string} The same text, inert.
|
|
6
|
+
*/
|
|
7
|
+
export function escapeTypst(text: string): string;
|
|
8
|
+
/**
|
|
9
|
+
* Escape a string going inside Typst string quotes, as a `#link` URL does.
|
|
10
|
+
*
|
|
11
|
+
* @param {string} text - The raw value.
|
|
12
|
+
* @returns {string} The same value, quotable.
|
|
13
|
+
*/
|
|
14
|
+
export function escapeTypstString(text: string): string;
|
|
15
|
+
/**
|
|
16
|
+
* A Typst label, from a plan anchor.
|
|
17
|
+
*
|
|
18
|
+
* Typst labels admit a narrower charset than an anchor does, so anything else
|
|
19
|
+
* folds to a hyphen. The plan already guarantees anchors are unique, and a fold
|
|
20
|
+
* that merged two of them would silently give one destination two meanings —
|
|
21
|
+
* so the fold is injective by construction: only characters Typst rejects move,
|
|
22
|
+
* and they move to a character the slugifier never emits twice in a row.
|
|
23
|
+
*
|
|
24
|
+
* @param {string} anchor - The plan's anchor.
|
|
25
|
+
* @returns {string} A Typst label name.
|
|
26
|
+
*/
|
|
27
|
+
export function labelFor(anchor: string): string;
|
|
28
|
+
/**
|
|
29
|
+
* A markdown-it configured to parse, not to render.
|
|
30
|
+
*
|
|
31
|
+
* `html: false` is the load-bearing setting: raw HTML in a note has no route to
|
|
32
|
+
* Typst at all, which is why {@link module:engine/content-html} reports it. With
|
|
33
|
+
* HTML disabled markdown-it emits the tag as text, so it arrives in the book
|
|
34
|
+
* visibly wrong rather than invisibly missing.
|
|
35
|
+
*
|
|
36
|
+
* @param {object} [registry] - The icon registry.
|
|
37
|
+
* @returns {object} A markdown-it instance.
|
|
38
|
+
*/
|
|
39
|
+
export function createParser(registry?: object): object;
|
|
40
|
+
/**
|
|
41
|
+
* Render markdown as Typst content.
|
|
42
|
+
*
|
|
43
|
+
* @param {string} markdown - The note's body, tables expanded and links resolved.
|
|
44
|
+
* @param {object} [opts] - Options.
|
|
45
|
+
* @param {object} [opts.md] - A parser from {@link createParser}, reused across
|
|
46
|
+
* a whole book rather than rebuilt for each of 2,500 notes.
|
|
47
|
+
* @param {object} [opts.registry] - The icon registry, when no parser is passed.
|
|
48
|
+
* @param {Map<string, string>} [opts.links] - Address slug → plan anchor.
|
|
49
|
+
* @param {Map<string, string>} [opts.glyphs] - Icon name → `{font, char}`.
|
|
50
|
+
* @param {number} [opts.headingOffset] - Added to every heading level, so a
|
|
51
|
+
* note's own `##` nests beneath the entry heading the book gave it.
|
|
52
|
+
* @param {string} [opts.anchorPrefix] - The entry's anchor, which namespaces
|
|
53
|
+
* every `{#slug}` the body declares.
|
|
54
|
+
* @returns {string} Typst markup.
|
|
55
|
+
*/
|
|
56
|
+
export function markdownToTypst(markdown: string, opts?: {
|
|
57
|
+
md?: object | undefined;
|
|
58
|
+
registry?: object | undefined;
|
|
59
|
+
links?: Map<string, string> | undefined;
|
|
60
|
+
glyphs?: Map<string, string> | undefined;
|
|
61
|
+
headingOffset?: number | undefined;
|
|
62
|
+
anchorPrefix?: string | undefined;
|
|
63
|
+
}): string;
|
|
64
|
+
/**
|
|
65
|
+
* The whole book, as one Typst document.
|
|
66
|
+
*
|
|
67
|
+
* **Pure, and that is the point.** Everything a reviewer of #316 has to check
|
|
68
|
+
* about structure — the outline's shape, the anchors, which links went inward,
|
|
69
|
+
* the order entries print in — is decided here from a plan and a map of bodies,
|
|
70
|
+
* with no filesystem and no compiler. {@link module:engine/pdf-build} supplies
|
|
71
|
+
* both and runs Typst over the result.
|
|
72
|
+
*
|
|
73
|
+
* ## Three surfaces, one heading tree
|
|
74
|
+
*
|
|
75
|
+
* A roster of 2,500 entries wants every entry reachable from a viewer's
|
|
76
|
+
* sidebar, and emphatically does not want all 2,500 printed in the front
|
|
77
|
+
* matter: that is forty pages of contents before the book starts. It also
|
|
78
|
+
* wants every heading in a note's own body to keep working as a link target,
|
|
79
|
+
* without appearing on either surface — the anchor an author writes for
|
|
80
|
+
* `[[note#appearance]]` is structure, not a destination either outline offers
|
|
81
|
+
* on its own.
|
|
82
|
+
*
|
|
83
|
+
* `heading` carries `outlined` and `bookmarked` independently, so the three
|
|
84
|
+
* wants are three settings rather than three passes:
|
|
85
|
+
*
|
|
86
|
+
* - A **section** — `outlined: true, bookmarked: true` — prints in the paper
|
|
87
|
+
* contents and the PDF sidebar alike.
|
|
88
|
+
* - A **note leaf**, titled from `name.full`, is `outlined: false,
|
|
89
|
+
* bookmarked: true`: reachable from the sidebar, absent from the printed
|
|
90
|
+
* contents.
|
|
91
|
+
* - A **body heading**, inside a note's own markdown, is `outlined: false,
|
|
92
|
+
* bookmarked: false`: a real heading with a label, so it still supplies a
|
|
93
|
+
* link target, a running head and a page break, but neither outline lists
|
|
94
|
+
* it. {@link markdownToTypst} emits these.
|
|
95
|
+
*
|
|
96
|
+
* `#outline()` needs no depth limit under this model: what prints is decided
|
|
97
|
+
* per heading, not by how deep the tree happens to go.
|
|
98
|
+
*
|
|
99
|
+
* ## Headings carry the structure, so nothing else has to
|
|
100
|
+
*
|
|
101
|
+
* Every section, every prose file and every entry is a real Typst heading at
|
|
102
|
+
* its plan depth. That single decision supplies both outlines, the running
|
|
103
|
+
* heads and the page breaks at once — where drawing titles as styled text
|
|
104
|
+
* would have meant building all four by hand and keeping them agreeing with
|
|
105
|
+
* each other.
|
|
106
|
+
*
|
|
107
|
+
* @param {object} opts - Options.
|
|
108
|
+
* @param {object} opts.plan - From {@link module:engine/pdf-toc.planDocument}.
|
|
109
|
+
* @param {Map<string, string>} opts.bodies - Anchor → the entry's rendered
|
|
110
|
+
* Typst body. An entry with no body prints its heading alone.
|
|
111
|
+
* @param {string} opts.title - The document's title.
|
|
112
|
+
* @param {string} [opts.subtitle] - Shown under it on the title page.
|
|
113
|
+
* @param {string[]} [opts.front] - Rendered Typst for each front-matter file.
|
|
114
|
+
* @param {object} [opts.fonts] - `{ serif, sans, mono }` family names.
|
|
115
|
+
* @param {string} [opts.version] - Stamped on the title page when given.
|
|
116
|
+
* @returns {string} A complete `.typ` document.
|
|
117
|
+
*/
|
|
118
|
+
export function renderBook({ plan, bodies, title, subtitle, front, fonts, version, }?: {
|
|
119
|
+
plan: object;
|
|
120
|
+
bodies: Map<string, string>;
|
|
121
|
+
title: string;
|
|
122
|
+
subtitle?: string | undefined;
|
|
123
|
+
front?: string[] | undefined;
|
|
124
|
+
fonts?: object | undefined;
|
|
125
|
+
version?: string | undefined;
|
|
126
|
+
}): string;
|
|
127
|
+
/**
|
|
128
|
+
* Point every internal link at a label the document actually declares.
|
|
129
|
+
*
|
|
130
|
+
* **Typst refuses to compile a reference to a label that is not there.** That
|
|
131
|
+
* makes one mistyped `[[note#appearance]]`, or an anchor written inside a code
|
|
132
|
+
* fence where no heading is emitted, fatal to a 1,200-page book — and fatal at
|
|
133
|
+
* the very end, after everything else has succeeded. A reference book cannot
|
|
134
|
+
* have that failure mode: the link is the least important thing on the page and
|
|
135
|
+
* would be taking the other two thousand entries down with it.
|
|
136
|
+
*
|
|
137
|
+
* So references are reconciled against declarations before the source is
|
|
138
|
+
* written. A link to a section that does not exist falls back to the **entry**
|
|
139
|
+
* that would have contained it, which is where a reader wants to end up anyway;
|
|
140
|
+
* a link with no entry to fall back to becomes plain text. Both are reported.
|
|
141
|
+
*
|
|
142
|
+
* A declaration is a label not preceded by `#link(` — the only two places a
|
|
143
|
+
* label appears are the heading that declares one and the link that uses one.
|
|
144
|
+
*
|
|
145
|
+
* @param {string} source - The assembled Typst document.
|
|
146
|
+
* @param {object[]} [findings] - Collected here rather than thrown.
|
|
147
|
+
* @returns {string} The same document, with no reference left dangling.
|
|
148
|
+
*/
|
|
149
|
+
export function resolveDanglingLabels(source: string, findings?: object[]): string;
|
|
150
|
+
/**
|
|
151
|
+
* Every icon name a body uses, so a build can resolve them once.
|
|
152
|
+
*
|
|
153
|
+
* @param {string} markdown - A note body.
|
|
154
|
+
* @returns {string[]} The names, in order of appearance, with repeats.
|
|
155
|
+
*/
|
|
156
|
+
export function iconNamesIn(markdown: string): string[];
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Validate the raw tree and flatten it into nodes.
|
|
3
|
+
*
|
|
4
|
+
* The format has no chapter/section distinction: every node is a section, and
|
|
5
|
+
* the leaves of the *document* are the notes and prose its contents resolve to.
|
|
6
|
+
* Depth is whatever the tree says, so nothing here caps or normalises it.
|
|
7
|
+
*
|
|
8
|
+
* `contents` is an **ordered, heterogeneous** list — prose, filters and child
|
|
9
|
+
* sections interleave in the sequence written, and that sequence is preserved
|
|
10
|
+
* exactly. A section that opens with a `file:` and then lists its entries is
|
|
11
|
+
* saying something different from one that does the reverse.
|
|
12
|
+
*
|
|
13
|
+
* @param {unknown} raw - The parsed document, as YAML returns it.
|
|
14
|
+
* @param {object} [opts] - Options.
|
|
15
|
+
* @param {string} [opts.text] - The file's source, for finding positions. Every
|
|
16
|
+
* finding without one is still reported, just without a locator.
|
|
17
|
+
* @returns {{nodes: object[], findings: object[]}} The flattened tree and what
|
|
18
|
+
* was wrong with it.
|
|
19
|
+
*/
|
|
20
|
+
export function parseDocumentTree(raw: unknown, { text }?: {
|
|
21
|
+
text?: string | undefined;
|
|
22
|
+
}): {
|
|
23
|
+
nodes: object[];
|
|
24
|
+
findings: object[];
|
|
25
|
+
};
|
|
26
|
+
/**
|
|
27
|
+
* Run every filter, and report the ones that would not run.
|
|
28
|
+
*
|
|
29
|
+
* The I/O half, kept apart from the planner for the reason the whole engine
|
|
30
|
+
* keeps them apart: the plan is then assertable without a database. The build
|
|
31
|
+
* owns the statement — `SELECT * FROM notes WHERE <clause>` — which is what
|
|
32
|
+
* makes a filter unable to reach another package's schema, unable to project
|
|
33
|
+
* something that is not a note, and unable to pick up the `doc<type>`
|
|
34
|
+
* documentation rows that ride the same index as the notes they document.
|
|
35
|
+
*
|
|
36
|
+
* **A filter that selects nothing is an error**, and the distinction that makes
|
|
37
|
+
* that consistent is worth stating. A *note* no clause selects is expected: the
|
|
38
|
+
* book is a selection and a project decides what its own volume carries. A
|
|
39
|
+
* *clause* that selects no note is not the same thing — a filter is a deliberate
|
|
40
|
+
* act, so one matching nothing is either wrong or left over from a structure
|
|
41
|
+
* that has moved on, and in both cases the tree should not carry it. Reported
|
|
42
|
+
* with the section's name and the filter's position, so the choice between
|
|
43
|
+
* fixing it and deleting it is the author's.
|
|
44
|
+
*
|
|
45
|
+
* @param {object[]} nodes - From {@link parseDocumentTree}.
|
|
46
|
+
* @param {{query: (sql: string) => Promise<{rows: object[]}>}} db - An open
|
|
47
|
+
* database, from {@link module:engine/sql-tables.openNotesDatabase}.
|
|
48
|
+
* @param {object} [opts] - Options.
|
|
49
|
+
* @param {(record: object) => boolean} [opts.keep] - Which rows are notes.
|
|
50
|
+
* @param {string} [opts.text] - The document's source, for finding positions.
|
|
51
|
+
* @returns {Promise<{selections: Map<string, object[]>, findings: object[]}>}
|
|
52
|
+
*/
|
|
53
|
+
export function runTreeFilters(nodes: object[], db: {
|
|
54
|
+
query: (sql: string) => Promise<{
|
|
55
|
+
rows: object[];
|
|
56
|
+
}>;
|
|
57
|
+
}, { keep, text }?: {
|
|
58
|
+
keep?: ((record: object) => boolean) | undefined;
|
|
59
|
+
text?: string | undefined;
|
|
60
|
+
}): Promise<{
|
|
61
|
+
selections: Map<string, object[]>;
|
|
62
|
+
findings: object[];
|
|
63
|
+
}>;
|
|
64
|
+
/**
|
|
65
|
+
* Resolve the flattened tree into the document plan.
|
|
66
|
+
*
|
|
67
|
+
* The plan is an ordered list of entries — `section`, `prose`, `note` — and it
|
|
68
|
+
* is the artifact worth having. Order, depth, the outline, the table of
|
|
69
|
+
* contents, anchor uniqueness and every link destination are all readable from
|
|
70
|
+
* it, so nearly the whole of what #316 asks for can be asserted here, on data,
|
|
71
|
+
* without a renderer or a PDF. Only how the result *looks* needs eyes.
|
|
72
|
+
*
|
|
73
|
+
* **Notes are sorted, prose is not.** A section's entries come out in
|
|
74
|
+
* `nameAscii` order — the ASCII fold `buildIndexRecord` already derives, so a
|
|
75
|
+
* circumflex sorts with its letter instead of after `Z` as a raw codepoint
|
|
76
|
+
* comparison would put it. Prose keeps the position it was written in, because
|
|
77
|
+
* its place in the sequence is the author's statement.
|
|
78
|
+
*
|
|
79
|
+
* **A section with nothing in it does not print**, and emptiness is judged after
|
|
80
|
+
* its descendants are: a section holding only sections that all resolved to
|
|
81
|
+
* nothing is itself empty. In a correct tree this never fires — a filter that
|
|
82
|
+
* selects nothing is reported by {@link runTreeFilters} as the error it is — so
|
|
83
|
+
* this is the graceful half of that failure rather than a feature: a build whose
|
|
84
|
+
* filters are broken still produces a readable document to look at while they
|
|
85
|
+
* are fixed. A section holding only prose is not empty; it is a preface.
|
|
86
|
+
*
|
|
87
|
+
* @param {object[]} nodes - From {@link parseDocumentTree}.
|
|
88
|
+
* @param {object} [opts] - Options.
|
|
89
|
+
* @param {Map<string, object[]>} [opts.selections] - Records each filter
|
|
90
|
+
* selected, keyed by item id. Run by the caller: DuckDB is async and this is
|
|
91
|
+
* not.
|
|
92
|
+
* @returns {{entries: object[], links: Map<string, string>, stats: object}}
|
|
93
|
+
* The plan, the address→anchor map inbound wikilinks resolve through, and
|
|
94
|
+
* what the selection came to.
|
|
95
|
+
*/
|
|
96
|
+
export function planDocument(nodes: object[], { selections }?: {
|
|
97
|
+
selections?: Map<string, object[]> | undefined;
|
|
98
|
+
}): {
|
|
99
|
+
entries: object[];
|
|
100
|
+
links: Map<string, string>;
|
|
101
|
+
stats: object;
|
|
102
|
+
};
|
|
103
|
+
/**
|
|
104
|
+
* Presentation a node may declare, and that its descendants inherit.
|
|
105
|
+
*
|
|
106
|
+
* Reserved now although the first release renders none of them, because the
|
|
107
|
+
* shape of the file is the thing consumers commit to: a book that has to be
|
|
108
|
+
* restructured to gain a running head has the wrong format, not the wrong
|
|
109
|
+
* renderer. Inheritance is what makes them worth declaring at all — `Gear` says
|
|
110
|
+
* once which infobox its entries use, and nine sections beneath it agree.
|
|
111
|
+
*
|
|
112
|
+
* @type {readonly string[]}
|
|
113
|
+
*/
|
|
114
|
+
export const PRESENTATION_KEYS: readonly string[];
|
|
@@ -13,6 +13,15 @@
|
|
|
13
13
|
* @returns {Set<string>} The known action names.
|
|
14
14
|
*/
|
|
15
15
|
export function collectKnownActionNames(repoRoot: string): Set<string>;
|
|
16
|
+
/**
|
|
17
|
+
* Scenes pack compiler.
|
|
18
|
+
*
|
|
19
|
+
* Walks the content tree and compiles every map note into one Scene, resolving
|
|
20
|
+
* what one note says about another through an index built before any scene is
|
|
21
|
+
* written. It also writes one Adventure per place, bundling those scenes with
|
|
22
|
+
* the JournalEntries their prose compiled into, which is what makes a pinned
|
|
23
|
+
* scene's id-based references resolve on import.
|
|
24
|
+
*/
|
|
16
25
|
export class Scenes extends BasePackCompiler {
|
|
17
26
|
constructor({ contentBase, dest, skipDirectories, companionDests, folderResolver, repoRoot, }: {
|
|
18
27
|
contentBase: any;
|
|
@@ -31,7 +40,7 @@ export class Scenes extends BasePackCompiler {
|
|
|
31
40
|
*/
|
|
32
41
|
adventureCount: number;
|
|
33
42
|
index: Map<string, object> | undefined;
|
|
34
|
-
effectsByAddress: Map<
|
|
43
|
+
effectsByAddress: Map<string, object> | undefined;
|
|
35
44
|
knownActions: Set<string> | undefined;
|
|
36
45
|
/** place key → `{name, img, scenes: [], journal: []}` */
|
|
37
46
|
places: Map<any, any> | undefined;
|
|
@@ -291,7 +291,7 @@ export function checkAuthoredSystemData(fm: object, { block, documentType, subTy
|
|
|
291
291
|
* existed, or a subtype the artifact does not name, produces no findings.
|
|
292
292
|
* `content-build lint` is where a missing artifact is said out loud, once.
|
|
293
293
|
*
|
|
294
|
-
* @param {object}
|
|
294
|
+
* @param {object} emitted - The `system` block the compiler produced.
|
|
295
295
|
* @param {object} opts
|
|
296
296
|
* @param {object} opts.fm - The note's frontmatter, for the authored paths this
|
|
297
297
|
* check leaves to {@link checkAuthoredSystemData}.
|
|
@@ -307,7 +307,7 @@ export function checkAuthoredSystemData(fm: object, { block, documentType, subTy
|
|
|
307
307
|
* @param {object} [opts.config] - The resolved build configuration.
|
|
308
308
|
* @returns {(EmissionFinding & {message: string})[]} One per undeclared path.
|
|
309
309
|
*/
|
|
310
|
-
export function checkEmittedSystemData(emitted:
|
|
310
|
+
export function checkEmittedSystemData(emitted: object, { fm, block, documentType, subType, type, fields, system, config, }: {
|
|
311
311
|
fm: object;
|
|
312
312
|
block: string;
|
|
313
313
|
documentType: string;
|
|
@@ -126,10 +126,13 @@ export function writeHomepages(outRoot: string, pages: readonly object[], config
|
|
|
126
126
|
* @param {object[]} pages - Every page, from both walks.
|
|
127
127
|
* @param {object} findings - `{ addressFindings, fmLinkFindings }` from
|
|
128
128
|
* collection.
|
|
129
|
-
* @param {object} options
|
|
129
|
+
* @param {object} options
|
|
130
|
+
* @param {object} options.config - The resolved build configuration.
|
|
130
131
|
* @returns {object} The gate results and, when they pass, the built index.
|
|
131
132
|
*/
|
|
132
|
-
export function siteGates(pages: object[], findings: object, { config }:
|
|
133
|
+
export function siteGates(pages: object[], findings: object, { config }: {
|
|
134
|
+
config: object;
|
|
135
|
+
}): object;
|
|
133
136
|
/**
|
|
134
137
|
* The gate result of a build that ran none of them.
|
|
135
138
|
*
|
|
@@ -220,10 +223,18 @@ export function sectionFrontmatter(meta: object): object;
|
|
|
220
223
|
* section's landing and takes the title and hero the section declares.
|
|
221
224
|
*
|
|
222
225
|
* @param {object} page - The page.
|
|
223
|
-
* @param {object} options
|
|
226
|
+
* @param {object} options
|
|
227
|
+
* @param {Record<string, object>} [options.readmeSections] - The sections a
|
|
228
|
+
* published tree declares, which a tree page's own `README` is the landing
|
|
229
|
+
* for.
|
|
230
|
+
* @param {(data: object, page: object) => void} [options.decorate] - Called
|
|
231
|
+
* with each page's frontmatter, for whatever a consumer's own pass adds.
|
|
224
232
|
* @returns {object} The frontmatter to write.
|
|
225
233
|
*/
|
|
226
|
-
export function pageFrontmatter(page: object, { readmeSections, decorate }:
|
|
234
|
+
export function pageFrontmatter(page: object, { readmeSections, decorate }: {
|
|
235
|
+
readmeSections?: Record<string, object> | undefined;
|
|
236
|
+
decorate?: ((data: object, page: object) => void) | undefined;
|
|
237
|
+
}): object;
|
|
227
238
|
/**
|
|
228
239
|
* Where a page is written, relative to the output root.
|
|
229
240
|
*
|
|
@@ -310,10 +321,21 @@ export function renderPages(pages: object[], options: object): {
|
|
|
310
321
|
* navigation of every page inside it.
|
|
311
322
|
*
|
|
312
323
|
* @param {string} outRoot - The mount directory.
|
|
313
|
-
* @param {object} options
|
|
324
|
+
* @param {object} options
|
|
325
|
+
* @param {Record<string, object>} [options.sections] - The declared sections,
|
|
326
|
+
* each written as a titled `_index.md` carrying its own frontmatter.
|
|
327
|
+
* @param {object} [options.landing] - The mount's own landing frontmatter.
|
|
328
|
+
* Omitted, the mount gets no `_index.md` of its own.
|
|
329
|
+
* @param {((name: string) => string)|null} [options.sectionTitle] - Titles a
|
|
330
|
+
* directory below the mount that declared no section. `null` leaves such a
|
|
331
|
+
* directory without an `_index.md`.
|
|
314
332
|
* @returns {number} How many landings were written.
|
|
315
333
|
*/
|
|
316
|
-
export function writeSectionLandings(outRoot: string, { sections, landing, sectionTitle }:
|
|
334
|
+
export function writeSectionLandings(outRoot: string, { sections, landing, sectionTitle }: {
|
|
335
|
+
sections?: Record<string, object> | undefined;
|
|
336
|
+
landing?: object | undefined;
|
|
337
|
+
sectionTitle?: ((name: string) => string) | null | undefined;
|
|
338
|
+
}): number;
|
|
317
339
|
/**
|
|
318
340
|
* A section landing's title, from its directory name — `macro` → `Macros`.
|
|
319
341
|
*
|
|
@@ -370,12 +392,18 @@ export function resolveOutputRoot(rootDir: string, out: string): string;
|
|
|
370
392
|
* @param {object} [options.config] - A resolved configuration; loaded when
|
|
371
393
|
* omitted.
|
|
372
394
|
* @param {string} [options.outRoot] - Override the configured output mount.
|
|
395
|
+
* @param {Map<string, object[]>} [options.sqlTables] - Prepared `sql` results,
|
|
396
|
+
* keyed by the note's absolute file, from
|
|
397
|
+
* {@link module:engine/sql-tables.prepareSqlTables}. A page authoring an
|
|
398
|
+
* `sql` directive with none prepared is a table error: nothing here runs a
|
|
399
|
+
* query.
|
|
373
400
|
* @returns {{gates: object, stats: object|null, tableErrors: object[],
|
|
374
401
|
* wikiErrors: object[], manifests: object|null}}
|
|
375
402
|
*/
|
|
376
403
|
export function buildSite({ config, outRoot, sqlTables }?: {
|
|
377
404
|
config?: object | undefined;
|
|
378
405
|
outRoot?: string | undefined;
|
|
406
|
+
sqlTables?: Map<string, object[]> | undefined;
|
|
379
407
|
}): {
|
|
380
408
|
gates: object;
|
|
381
409
|
stats: object | null;
|
|
@@ -55,8 +55,8 @@ export function resolveItemDocType(qualifier: string, types: Set<string>): strin
|
|
|
55
55
|
*
|
|
56
56
|
* **Parsing is plain positional counting**, the same rule
|
|
57
57
|
* {@link readCanonicalKey} follows, and it is sound for the same reason: every
|
|
58
|
-
* segment
|
|
59
|
-
*
|
|
58
|
+
* segment matches `ADDRESS_SEGMENT_PATTERN` (enforced on shortcodes by
|
|
59
|
+
* `content-lint.mjs`), so the hyphen is purely a
|
|
60
60
|
* separator and the count alone determines every field. Verified across the
|
|
61
61
|
* four content trees: 138,204 authored shortcodes, none carrying a separator.
|
|
62
62
|
*
|
|
@@ -144,7 +144,6 @@ export function buildWikilinkIndex(docs: Array<{
|
|
|
144
144
|
byShortcode: Map<string, object>;
|
|
145
145
|
types: Set<string>;
|
|
146
146
|
};
|
|
147
|
-
/** Matches a whole wikilink, capturing its inner text. */
|
|
148
147
|
/**
|
|
149
148
|
* Rewrites every wikilink in a markdown body as a Foundry UUID enricher.
|
|
150
149
|
*
|
package/types/hm3/actors.d.mts
CHANGED
|
@@ -1,3 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* HM3's Actor compile pass.
|
|
3
|
+
*
|
|
4
|
+
* Declares HM3's note-type → document-subtype map and builds a `being` note's
|
|
5
|
+
* document: the subtype the note authors in `hm3.type`, the mapped fields and
|
|
6
|
+
* the two anchored prose sections, and the embedded items `hm3.items` names.
|
|
7
|
+
* Everything else is {@link module:engine/actor-compiler}'s.
|
|
8
|
+
*/
|
|
1
9
|
export class Hm3Actors extends SystemActorCompiler {
|
|
2
10
|
/**
|
|
3
11
|
* HM3's note-type → document-subtype map — the one declaration that says
|
package/types/hm3/items.d.mts
CHANGED
|
@@ -1,3 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* HM3's Item compile pass.
|
|
3
|
+
*
|
|
4
|
+
* Declares HM3's note-type → document-subtype map, which decides the notes this
|
|
5
|
+
* pass claims and what each becomes, the one `system` key HM3 writes from prose,
|
|
6
|
+
* and the template-priority flag HM3's data model has no field for. Everything
|
|
7
|
+
* else is {@link module:engine/item-compiler}'s.
|
|
8
|
+
*/
|
|
1
9
|
export class Hm3Items extends SystemItemCompiler {
|
|
2
10
|
/**
|
|
3
11
|
* HM3's note-type → document-subtype map — the one declaration that says
|
package/types/release.d.mts
CHANGED
|
@@ -15,22 +15,33 @@
|
|
|
15
15
|
* Determines both asset names.
|
|
16
16
|
* @param {string} [opts.metadataDir] - Where the build writes its content
|
|
17
17
|
* index, consulted when the advertised file was not staged.
|
|
18
|
+
* @param {boolean} [opts.pdf] - Whether to build the book that ships beside the
|
|
19
|
+
* archive. `true` by default; `false` skips the build and reports the skip.
|
|
18
20
|
* @returns {Promise<{zip: string, manifest: string, metadata?: string,
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
21
|
+
* pdf?: string, pdfFindings: object[], pdfSkipped: string|null,
|
|
22
|
+
* bytes: number, version: string}>} The paths written, what the book build
|
|
23
|
+
* found, the archive's size, and the version the manifest declares.
|
|
24
|
+
* `metadata` is absent when the manifest advertises no content index, and
|
|
25
|
+
* `pdf` is absent when no book was written. `pdfSkipped` is `null` when a
|
|
26
|
+
* book was built, and otherwise the reason none was — itself `null` when the
|
|
27
|
+
* book builder could not be loaded, which is reported through
|
|
28
|
+
* `pdfFindings`.
|
|
22
29
|
* @throws {Error} When the stage has no manifest — there is nothing to release,
|
|
23
30
|
* and an archive without one installs as nothing.
|
|
24
31
|
*/
|
|
25
|
-
export function packRelease({ stageDir, outDir, artifact, metadataDir, }?: {
|
|
32
|
+
export function packRelease({ stageDir, outDir, artifact, metadataDir, pdf, }?: {
|
|
26
33
|
stageDir?: string | undefined;
|
|
27
34
|
outDir?: string | undefined;
|
|
28
35
|
artifact?: "module" | "system" | undefined;
|
|
29
36
|
metadataDir?: string | undefined;
|
|
37
|
+
pdf?: boolean | undefined;
|
|
30
38
|
}): Promise<{
|
|
31
39
|
zip: string;
|
|
32
40
|
manifest: string;
|
|
33
41
|
metadata?: string;
|
|
42
|
+
pdf?: string;
|
|
43
|
+
pdfFindings: object[];
|
|
44
|
+
pdfSkipped: string | null;
|
|
34
45
|
bytes: number;
|
|
35
46
|
version: string;
|
|
36
47
|
}>;
|
package/types/sohl/actors.d.mts
CHANGED
|
@@ -1,3 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SoHL's Actor compile pass.
|
|
3
|
+
*
|
|
4
|
+
* Declares SoHL's note-type → document-subtype map and builds a `being` note's
|
|
5
|
+
* document: the body structure and its movement profiles, the embedded
|
|
6
|
+
* attribute and item documents the frontmatter names, and the `system` block.
|
|
7
|
+
* Everything else is {@link module:engine/actor-compiler}'s.
|
|
8
|
+
*/
|
|
1
9
|
export class Actors extends SystemActorCompiler {
|
|
2
10
|
/**
|
|
3
11
|
* SoHL's note-type → document-subtype map — the one declaration that says
|
|
@@ -12,7 +20,7 @@ export class Actors extends SystemActorCompiler {
|
|
|
12
20
|
* Build all embedded items for an actor: one per `sohl.attributes`
|
|
13
21
|
* entry plus one per `sohl.items` entry. `sohl.skills` is ignored.
|
|
14
22
|
*/
|
|
15
|
-
buildEmbeddedItems(itemsMap: any, actorId: any, fm: any, ctx: any):
|
|
23
|
+
buildEmbeddedItems(itemsMap: any, actorId: any, fm: any, ctx: any): object[];
|
|
16
24
|
/**
|
|
17
25
|
* Bake each unopened skill's opening mastery level into the document.
|
|
18
26
|
*
|
|
@@ -52,7 +60,7 @@ export class Actors extends SystemActorCompiler {
|
|
|
52
60
|
appearance: string;
|
|
53
61
|
dossier: string;
|
|
54
62
|
};
|
|
55
|
-
items:
|
|
63
|
+
items: object[];
|
|
56
64
|
prototypeToken: {
|
|
57
65
|
name: any;
|
|
58
66
|
displayName: number;
|
package/types/sohl/items.d.mts
CHANGED
|
@@ -1,3 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SoHL's Item compile pass.
|
|
3
|
+
*
|
|
4
|
+
* Declares SoHL's note-type → document-subtype map, which decides the notes
|
|
5
|
+
* this pass claims and what each becomes, and the `system` keys SoHL writes on
|
|
6
|
+
* every item whatever its type. Everything else is
|
|
7
|
+
* {@link module:engine/item-compiler}'s.
|
|
8
|
+
*/
|
|
1
9
|
export class Items extends SystemItemCompiler {
|
|
2
10
|
/**
|
|
3
11
|
* SoHL's note-type → document-subtype map — the one declaration that says
|