@heroiclands/package-build 21.1.0 → 22.0.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 +245 -0
- package/CONTENT.md +107 -3
- package/assets/fonts/libertinus/LibertinusMono-Regular.otf +0 -0
- package/assets/fonts/libertinus/LibertinusSans-Bold.otf +0 -0
- package/assets/fonts/libertinus/LibertinusSans-Italic.otf +0 -0
- package/assets/fonts/libertinus/LibertinusSans-Regular.otf +0 -0
- package/assets/fonts/libertinus/OFL.txt +94 -0
- package/assets/fonts/libertinus/provenance.yaml +11 -0
- package/assets/images/banners/afflictionbnr.webp +0 -0
- package/assets/images/banners/armorclothingbnr.webp +0 -0
- package/assets/images/banners/attributebnr.webp +0 -0
- package/assets/images/banners/containerbnr.webp +0 -0
- package/assets/images/banners/devdocsbnr.webp +0 -0
- package/assets/images/banners/miscgearbnr.webp +0 -0
- package/assets/images/banners/mysticalabilitybnr.webp +0 -0
- package/assets/images/banners/projectilebnr.webp +0 -0
- package/assets/images/banners/rulesbnr.webp +0 -0
- package/assets/images/banners/skillbnr.webp +0 -0
- package/assets/images/banners/traumabnr.webp +0 -0
- package/assets/images/banners/userguidebnr.webp +0 -0
- package/assets/images/banners/weapongearbnr.webp +0 -0
- package/assets/images/provenance.yaml +4 -0
- package/bin/content-build.mjs +19 -5
- package/bin/package-build.mjs +6 -5
- package/ci/emit-asset-index.mjs +32 -0
- package/content-config.mjs +30 -1
- package/docs/api.md +103 -27
- package/docs/commands.md +13 -11
- package/docs/configuration.md +16 -7
- package/docs/content-format.md +523 -111
- package/engine/actor-compiler.mjs +58 -9
- package/engine/art-fields.mjs +292 -0
- package/engine/asset-index.mjs +397 -0
- package/engine/asset-types.mjs +192 -0
- package/engine/base-compiler.mjs +69 -1
- package/engine/bundles.mjs +5 -5
- package/engine/compile-corpus.mjs +4 -0
- package/engine/content-address.mjs +14 -2
- package/engine/content-charset.mjs +5 -1
- package/engine/content-embeds.mjs +314 -0
- package/engine/content-html.mjs +5 -1
- package/engine/content-icons.mjs +5 -1
- package/engine/content-images.mjs +26 -5
- package/engine/content-index.mjs +68 -33
- package/engine/content-links.mjs +164 -24
- package/engine/dependency-bump.mjs +14 -16
- package/engine/frontmatter-lint.mjs +36 -23
- package/engine/helpers.mjs +83 -31
- package/engine/index-records.mjs +63 -4
- package/engine/index.mjs +15 -0
- package/engine/infobox-registry.mjs +50 -3
- package/engine/infobox.mjs +12 -8
- package/engine/item-compiler.mjs +10 -10
- package/engine/journals.mjs +3 -0
- package/engine/macros.mjs +16 -13
- package/engine/map-notes.mjs +29 -22
- package/engine/metadata-index.mjs +44 -36
- package/engine/note-vocabulary.mjs +71 -29
- package/engine/pack-router.mjs +14 -0
- package/engine/packagebuild-index.mjs +124 -0
- package/engine/packages.mjs +83 -0
- package/engine/pathnames.mjs +74 -0
- package/engine/pdf-build.mjs +133 -8
- package/engine/pdf-render.mjs +49 -12
- package/engine/scenes.mjs +12 -9
- package/engine/site-build.mjs +43 -16
- package/engine/site-index.mjs +10 -1
- package/engine/subtype-registry.mjs +21 -0
- package/engine/web-wikilinks.mjs +46 -12
- package/engine/wikilink-syntax.mjs +20 -2
- package/engine/wikilinks.mjs +15 -2
- package/hm3/actors.mjs +11 -38
- package/manifest.mjs +67 -13
- package/package.json +5 -2
- package/packagebuild-metadata.jsonl +13 -0
- package/sohl/actors.mjs +12 -86
- package/sohl/note-schemas.mjs +4 -16
- package/types/content-config.d.mts +8 -0
- package/types/engine/actor-compiler.d.mts +29 -0
- package/types/engine/art-fields.d.mts +192 -0
- package/types/engine/asset-index.d.mts +92 -0
- package/types/engine/asset-types.d.mts +110 -0
- package/types/engine/base-compiler.d.mts +42 -1
- package/types/engine/compile-corpus.d.mts +4 -1
- package/types/engine/content-charset.d.mts +4 -0
- package/types/engine/content-embeds.d.mts +218 -0
- package/types/engine/content-html.d.mts +4 -0
- package/types/engine/content-icons.d.mts +4 -0
- package/types/engine/content-images.d.mts +15 -2
- package/types/engine/content-index.d.mts +27 -15
- package/types/engine/dependency-bump.d.mts +2 -2
- package/types/engine/frontmatter-lint.d.mts +11 -14
- package/types/engine/helpers.d.mts +7 -2
- package/types/engine/index-records.d.mts +44 -3
- package/types/engine/index.d.mts +5 -0
- package/types/engine/infobox-registry.d.mts +36 -1
- package/types/engine/infobox.d.mts +6 -4
- package/types/engine/macros.d.mts +9 -5
- package/types/engine/map-notes.d.mts +10 -6
- package/types/engine/metadata-index.d.mts +2 -17
- package/types/engine/note-vocabulary.d.mts +30 -0
- package/types/engine/packagebuild-index.d.mts +56 -0
- package/types/engine/packages.d.mts +62 -0
- package/types/engine/pathnames.d.mts +41 -81
- package/types/engine/pdf-build.d.mts +58 -1
- package/types/engine/pdf-render.d.mts +3 -1
- package/types/engine/scenes.d.mts +2 -1
- package/types/engine/site-build.d.mts +5 -1
- package/types/engine/site-index.d.mts +4 -1
- package/types/engine/subtype-registry.d.mts +20 -0
- package/types/engine/web-wikilinks.d.mts +14 -4
- package/types/engine/wikilink-syntax.d.mts +13 -1
- package/types/engine/wikilinks.d.mts +7 -1
- package/types/manifest.d.mts +42 -1
- package/types/sohl/actors.d.mts +0 -1
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* One embed, parsed.
|
|
3
|
+
*
|
|
4
|
+
* @typedef {object} ParsedEmbed
|
|
5
|
+
* @property {string} all - The embed exactly as authored, directive included.
|
|
6
|
+
* @property {string} inner - The whole interior of the brackets.
|
|
7
|
+
* @property {string} written - The link part as authored, anchor included. An
|
|
8
|
+
* embed names a file and a file has no sections, so an anchor is carried into
|
|
9
|
+
* the address rather than stripped off it — where it fails the address charset
|
|
10
|
+
* and is reported as what it is.
|
|
11
|
+
* @property {string|null} display - The text after `|`, `null` when unlabelled.
|
|
12
|
+
* @property {boolean} labelled - Whether a `|` was present at all.
|
|
13
|
+
* @property {string} directive - The `{…}` as written, or `""`.
|
|
14
|
+
* @property {number} index - Where the embed begins in the body.
|
|
15
|
+
* @property {number} length - How much of the body it occupies.
|
|
16
|
+
* @property {boolean} block - Whether it stands alone in its own paragraph.
|
|
17
|
+
*/
|
|
18
|
+
/**
|
|
19
|
+
* Every embed in one body, in source order.
|
|
20
|
+
*
|
|
21
|
+
* Code is skipped, because an embed shown as an example is prose *about* an
|
|
22
|
+
* embed — which is what makes this module's own documentation writable.
|
|
23
|
+
*
|
|
24
|
+
* @param {string} body - The note's markdown, without its frontmatter.
|
|
25
|
+
* @returns {ParsedEmbed[]} One entry per embed.
|
|
26
|
+
*/
|
|
27
|
+
export function embedsIn(body: string): ParsedEmbed[];
|
|
28
|
+
/**
|
|
29
|
+
* What one embed resolves to, or why it does not.
|
|
30
|
+
*
|
|
31
|
+
* The reasons are {@link module:engine/wikilink-syntax.LINK_FINDING_REASONS},
|
|
32
|
+
* the vocabulary every resolver shares, so an author meets one wording for one
|
|
33
|
+
* mistake whichever build they ran first.
|
|
34
|
+
*
|
|
35
|
+
* @param {object} index - From {@link module:engine/wikilinks.buildWikilinkIndex},
|
|
36
|
+
* or the equivalent the site and the book build.
|
|
37
|
+
* @param {ParsedEmbed} embed - The embed.
|
|
38
|
+
* @returns {{pathname: string}|{reason: string, target: string, type?: string}}
|
|
39
|
+
* The authored pathname the picture is at, or the finding.
|
|
40
|
+
*/
|
|
41
|
+
export function resolveEmbed(index: object, embed: ParsedEmbed): {
|
|
42
|
+
pathname: string;
|
|
43
|
+
} | {
|
|
44
|
+
reason: string;
|
|
45
|
+
target: string;
|
|
46
|
+
type?: string;
|
|
47
|
+
};
|
|
48
|
+
/**
|
|
49
|
+
* What is wrong with one embed's directive or placement, if anything.
|
|
50
|
+
*
|
|
51
|
+
* Separate from the link findings beside it because the two speak different
|
|
52
|
+
* vocabularies: a link finding is a `reason` from the closed set every resolver
|
|
53
|
+
* shares, and this is an image's own complaint about a brace or a paragraph.
|
|
54
|
+
* Collapsing them would put a sentence where a `reason` belongs.
|
|
55
|
+
*
|
|
56
|
+
* @param {ParsedEmbed} embed - The embed.
|
|
57
|
+
* @returns {Array<{link: string, offset: number, message: string}>} One entry
|
|
58
|
+
* per defect. `link` is the embed exactly as authored, which is what lets a
|
|
59
|
+
* caller with no offsets locate it by searching the note for the literal.
|
|
60
|
+
*/
|
|
61
|
+
export function embedProblems(embed: ParsedEmbed): Array<{
|
|
62
|
+
link: string;
|
|
63
|
+
offset: number;
|
|
64
|
+
message: string;
|
|
65
|
+
}>;
|
|
66
|
+
/**
|
|
67
|
+
* Rewrite every embed in one body into the image each surface already renders.
|
|
68
|
+
*
|
|
69
|
+
* An embed that does not resolve is left **exactly as authored** and reported,
|
|
70
|
+
* so a missing picture degrades to visible literal text rather than to a broken
|
|
71
|
+
* `src` or a swallowed paragraph. So is one whose directive does not parse: a
|
|
72
|
+
* directive holding a problem is not honoured at all, and the braces reaching
|
|
73
|
+
* the page as their own literal text is how the mistake is visible without a
|
|
74
|
+
* log.
|
|
75
|
+
*
|
|
76
|
+
* Call this **before** wikilink resolution. The rewrite consumes the `!` along
|
|
77
|
+
* with the brackets, which is what stops the link pass reading an embed's
|
|
78
|
+
* interior as an ordinary link to a note that does not exist.
|
|
79
|
+
*
|
|
80
|
+
* @param {string} body - The note's markdown, tables already expanded.
|
|
81
|
+
* @param {object} ctx
|
|
82
|
+
* @param {object} ctx.index - The address index assets resolve through.
|
|
83
|
+
* @returns {{markdown: string, unresolved: Array<{link: string, target: string,
|
|
84
|
+
* offset: number, reason: string, type?: string}>,
|
|
85
|
+
* problems: Array<{link: string, offset: number, message: string}>,
|
|
86
|
+
* images: Array<{link: string, offset: number, pathname: string}>}} The body,
|
|
87
|
+
* the embeds that named nothing, the directives that could not be honoured,
|
|
88
|
+
* and the pathname each embed that did resolve now names. Every `offset` is
|
|
89
|
+
* 0-based in `body`, which is what lets a caller report a line and a column
|
|
90
|
+
* and tell two identical embeds apart.
|
|
91
|
+
*
|
|
92
|
+
* **`images` is how a surface holds an embed to its own rule.** The rewrite
|
|
93
|
+
* is surface-agnostic — a file the website serves and the book stages is not
|
|
94
|
+
* always one a Foundry install carries — so a caller that cares asks about
|
|
95
|
+
* the pathname while it still knows which embed produced it, rather than
|
|
96
|
+
* searching a rewritten body for a literal the note never wrote.
|
|
97
|
+
*/
|
|
98
|
+
export function resolveEmbeds(body: string, { index }: {
|
|
99
|
+
index: object;
|
|
100
|
+
}): {
|
|
101
|
+
markdown: string;
|
|
102
|
+
unresolved: Array<{
|
|
103
|
+
link: string;
|
|
104
|
+
target: string;
|
|
105
|
+
offset: number;
|
|
106
|
+
reason: string;
|
|
107
|
+
type?: string;
|
|
108
|
+
}>;
|
|
109
|
+
problems: Array<{
|
|
110
|
+
link: string;
|
|
111
|
+
offset: number;
|
|
112
|
+
message: string;
|
|
113
|
+
}>;
|
|
114
|
+
images: Array<{
|
|
115
|
+
link: string;
|
|
116
|
+
offset: number;
|
|
117
|
+
pathname: string;
|
|
118
|
+
}>;
|
|
119
|
+
};
|
|
120
|
+
/**
|
|
121
|
+
* Every defect in one note's embeds, located.
|
|
122
|
+
*
|
|
123
|
+
* {@link resolveEmbeds} with its two lists turned into the one shape a finding
|
|
124
|
+
* takes, for a caller that wants the report and not the rewrite.
|
|
125
|
+
*
|
|
126
|
+
* **Errors, not warnings**, for the reason an image's are: `reportFindings`
|
|
127
|
+
* fails on an error and not on a warning, so a refusal reported as advisory
|
|
128
|
+
* publishes anyway, looking exactly like a directive that worked.
|
|
129
|
+
*
|
|
130
|
+
* @param {string} body - The note's markdown, without its frontmatter.
|
|
131
|
+
* @param {string} file - The note's path, for the finding.
|
|
132
|
+
* @param {object} [opts]
|
|
133
|
+
* @param {number} [opts.bodyLine=1] - The 1-based file line the body starts on.
|
|
134
|
+
* @param {number} [opts.bodyColumn=1] - The 1-based file column it starts at.
|
|
135
|
+
* @param {object} [opts.index] - The address index assets resolve through.
|
|
136
|
+
* @returns {Array<{file: string, line: number, column: number|undefined,
|
|
137
|
+
* severity: "error", message: string}>} One finding per defect, in source
|
|
138
|
+
* order.
|
|
139
|
+
*/
|
|
140
|
+
export function checkEmbeds(body: string, file: string, { bodyLine, bodyColumn, index }?: {
|
|
141
|
+
bodyLine?: number | undefined;
|
|
142
|
+
bodyColumn?: number | undefined;
|
|
143
|
+
index?: object | undefined;
|
|
144
|
+
}): Array<{
|
|
145
|
+
file: string;
|
|
146
|
+
line: number;
|
|
147
|
+
column: number | undefined;
|
|
148
|
+
severity: "error";
|
|
149
|
+
message: string;
|
|
150
|
+
}>;
|
|
151
|
+
/**
|
|
152
|
+
* The type a bare shortcode takes, supplied by the syntax itself.
|
|
153
|
+
*
|
|
154
|
+
* A picture is what `!` asks for, so `image` is what an unqualified embed names.
|
|
155
|
+
* An icon is reached by stating it — `![[icon-anvil|An anvil]]` — because the two
|
|
156
|
+
* have separate shortcode namespaces and neither is derivable from the other.
|
|
157
|
+
*
|
|
158
|
+
* @type {string}
|
|
159
|
+
*/
|
|
160
|
+
export const EMBED_DEFAULT_TYPE: string;
|
|
161
|
+
/**
|
|
162
|
+
* An embed, as authored, with the directive it may carry.
|
|
163
|
+
*
|
|
164
|
+
* The interior admits no `]` or newline, exactly as a wikilink's does: an embed
|
|
165
|
+
* is written on one line, and an unclosed `![[` is a typo rather than licence to
|
|
166
|
+
* consume the rest of the document looking for a closer.
|
|
167
|
+
*
|
|
168
|
+
* The directive is `{…}` immediately after the closing `]]`, holding no newline,
|
|
169
|
+
* for the reason {@link module:engine/content-images.IMAGE_PATTERN} gives: a
|
|
170
|
+
* brace that opens and never closes on its line is prose.
|
|
171
|
+
*
|
|
172
|
+
* @type {RegExp}
|
|
173
|
+
*/
|
|
174
|
+
export const EMBED_PATTERN: RegExp;
|
|
175
|
+
/**
|
|
176
|
+
* One embed, parsed.
|
|
177
|
+
*/
|
|
178
|
+
export type ParsedEmbed = {
|
|
179
|
+
/**
|
|
180
|
+
* - The embed exactly as authored, directive included.
|
|
181
|
+
*/
|
|
182
|
+
all: string;
|
|
183
|
+
/**
|
|
184
|
+
* - The whole interior of the brackets.
|
|
185
|
+
*/
|
|
186
|
+
inner: string;
|
|
187
|
+
/**
|
|
188
|
+
* - The link part as authored, anchor included. An
|
|
189
|
+
* embed names a file and a file has no sections, so an anchor is carried into
|
|
190
|
+
* the address rather than stripped off it — where it fails the address charset
|
|
191
|
+
* and is reported as what it is.
|
|
192
|
+
*/
|
|
193
|
+
written: string;
|
|
194
|
+
/**
|
|
195
|
+
* - The text after `|`, `null` when unlabelled.
|
|
196
|
+
*/
|
|
197
|
+
display: string | null;
|
|
198
|
+
/**
|
|
199
|
+
* - Whether a `|` was present at all.
|
|
200
|
+
*/
|
|
201
|
+
labelled: boolean;
|
|
202
|
+
/**
|
|
203
|
+
* - The `{…}` as written, or `""`.
|
|
204
|
+
*/
|
|
205
|
+
directive: string;
|
|
206
|
+
/**
|
|
207
|
+
* - Where the embed begins in the body.
|
|
208
|
+
*/
|
|
209
|
+
index: number;
|
|
210
|
+
/**
|
|
211
|
+
* - How much of the body it occupies.
|
|
212
|
+
*/
|
|
213
|
+
length: number;
|
|
214
|
+
/**
|
|
215
|
+
* - Whether it stands alone in its own paragraph.
|
|
216
|
+
*/
|
|
217
|
+
block: boolean;
|
|
218
|
+
};
|
|
@@ -39,6 +39,10 @@ export function checkHtml(body: string, file: string, { bodyLine, bodyColumn }?:
|
|
|
39
39
|
* whole: it is not a note, but a stray `.md` in the tree carrying markup is the
|
|
40
40
|
* same problem for the same reason.
|
|
41
41
|
*
|
|
42
|
+
* A finding names its file **relative to the working directory**, which is
|
|
43
|
+
* where a reader is standing and what `formatDiagnostic` emits. The content
|
|
44
|
+
* root is where the walk starts, not what a path is measured from.
|
|
45
|
+
*
|
|
42
46
|
* @param {string} contentBase - Root of the content tree.
|
|
43
47
|
* @param {object} [opts]
|
|
44
48
|
* @param {readonly string[]} [opts.skipDirectories] - Directory names to ignore
|
|
@@ -111,6 +111,10 @@ export function checkIconRegistry(registry: {
|
|
|
111
111
|
* with nothing imported between them. The cost is one extra pass over the tree,
|
|
112
112
|
* which is the cheaper half of a lint that already parses every note.
|
|
113
113
|
*
|
|
114
|
+
* A finding names its file **relative to the working directory**, which is
|
|
115
|
+
* where a reader is standing and what `formatDiagnostic` emits. The content
|
|
116
|
+
* root is where the walk starts, not what a path is measured from.
|
|
117
|
+
*
|
|
114
118
|
* @param {string} contentBase - Root of the content tree.
|
|
115
119
|
* @param {object} [opts]
|
|
116
120
|
* @param {readonly string[]} [opts.skipDirectories] - Directory names to ignore.
|
|
@@ -123,13 +123,19 @@ export function imageSourcesIn(body: string): string[];
|
|
|
123
123
|
* @param {object} [opts]
|
|
124
124
|
* @param {number} [opts.bodyLine=1] - The 1-based file line the body starts on.
|
|
125
125
|
* @param {number} [opts.bodyColumn=1] - The 1-based file column it starts at.
|
|
126
|
+
* @param {object} [opts.config] - The resolved build configuration. Supplied,
|
|
127
|
+
* an address is also held to the one surface a pathname can be dead on
|
|
128
|
+
* without any other pass noticing — see the Foundry address below. Omitted,
|
|
129
|
+
* the config-free checks run alone, which is what lets a caller with no
|
|
130
|
+
* repository to resolve still read a body.
|
|
126
131
|
* @returns {Array<{file: string, line: number, column: number|undefined,
|
|
127
132
|
* severity: "error", message: string}>} One finding per defect, in source
|
|
128
133
|
* order.
|
|
129
134
|
*/
|
|
130
|
-
export function checkImages(body: string, file: string, { bodyLine, bodyColumn }?: {
|
|
135
|
+
export function checkImages(body: string, file: string, { bodyLine, bodyColumn, config }?: {
|
|
131
136
|
bodyLine?: number | undefined;
|
|
132
137
|
bodyColumn?: number | undefined;
|
|
138
|
+
config?: object | undefined;
|
|
133
139
|
}): Array<{
|
|
134
140
|
file: string;
|
|
135
141
|
line: number;
|
|
@@ -143,16 +149,23 @@ export function checkImages(body: string, file: string, { bodyLine, bodyColumn }
|
|
|
143
149
|
* Its own walk, like the icon and HTML checks beside it, so all three stay
|
|
144
150
|
* leaves with nothing imported between them.
|
|
145
151
|
*
|
|
152
|
+
* A finding names its file **relative to the working directory**, which is
|
|
153
|
+
* where a reader is standing and what `formatDiagnostic` emits. The content
|
|
154
|
+
* root is where the walk starts, not what a path is measured from.
|
|
155
|
+
*
|
|
146
156
|
* @param {string} contentBase - Root of the content tree.
|
|
147
157
|
* @param {object} [opts]
|
|
148
158
|
* @param {readonly string[]} [opts.skipDirectories] - Directory names to ignore
|
|
149
159
|
* in addition to the dot-directories always skipped.
|
|
160
|
+
* @param {object} [opts.config] - The resolved build configuration, passed to
|
|
161
|
+
* {@link checkImages} so an address is held to the Foundry surface too.
|
|
150
162
|
* @returns {{findings: Array<{file: string, line: number, column: number|undefined,
|
|
151
163
|
* severity: "error", message: string}>, files: number}} The findings, and how
|
|
152
164
|
* many files were read.
|
|
153
165
|
*/
|
|
154
|
-
export function lintContentImages(contentBase: string, { skipDirectories }?: {
|
|
166
|
+
export function lintContentImages(contentBase: string, { skipDirectories, config }?: {
|
|
155
167
|
skipDirectories?: readonly string[] | undefined;
|
|
168
|
+
config?: object | undefined;
|
|
156
169
|
}): {
|
|
157
170
|
findings: Array<{
|
|
158
171
|
file: string;
|
|
@@ -31,16 +31,6 @@ export function noteAddress(frontmatter: Record<string, any>, contentPackage: st
|
|
|
31
31
|
slug: string;
|
|
32
32
|
canonical: string;
|
|
33
33
|
} | null;
|
|
34
|
-
/**
|
|
35
|
-
* Recursively sort an object's keys, so serialization is order-independent.
|
|
36
|
-
*
|
|
37
|
-
* Arrays keep their order — it is authored — but every object inside one is
|
|
38
|
-
* sorted too. Anything that is not a plain object is returned as it is.
|
|
39
|
-
*
|
|
40
|
-
* @param {unknown} value - The value to normalize.
|
|
41
|
-
* @returns {unknown} The value with every plain object's keys in sorted order.
|
|
42
|
-
*/
|
|
43
|
-
export function sortKeysDeep(value: unknown): unknown;
|
|
44
34
|
/**
|
|
45
35
|
* A note's display name reduced to printable 7-bit ASCII.
|
|
46
36
|
*
|
|
@@ -129,12 +119,22 @@ export function buildIndexRecord({ frontmatter, relPath, absPath, contentPackage
|
|
|
129
119
|
* An item note yields two records — the item, and the documentation journal
|
|
130
120
|
* that is a document in its own right.
|
|
131
121
|
*
|
|
122
|
+
* **The asset roots are walked in the same pass.** A package's addressable files
|
|
123
|
+
* sit beside `content/` rather than inside it, and they publish into the same
|
|
124
|
+
* index under the same address grammar — so there is no second walk, no second
|
|
125
|
+
* artifact, and no notion of an "art module" anywhere in the toolchain. A
|
|
126
|
+
* package whose tree holds only assets is one by consequence.
|
|
127
|
+
*
|
|
132
128
|
* @param {string} contentBase - The content tree to walk.
|
|
133
129
|
* @param {object} options - Options.
|
|
134
130
|
* @param {string} options.contentPackage - The package the tree compiles as.
|
|
135
131
|
* @param {readonly string[]} options.skipDirectories - The walk's scope, stated
|
|
136
132
|
* by the caller. An absent one is the caller's omission, and
|
|
137
133
|
* {@link module:engine/helpers.walkMarkdownTree} throws on it.
|
|
134
|
+
* @param {string} [options.assetsBase] - The package's asset directory, holding
|
|
135
|
+
* the three asset roots. Omitted, no asset is indexed — which is what a caller
|
|
136
|
+
* walking a bare content fixture wants, and what an asset-free package gets
|
|
137
|
+
* anyway.
|
|
138
138
|
* @param {object} [options.manifest] - The package manifest, which the Foundry
|
|
139
139
|
* entries are derived against.
|
|
140
140
|
* @param {object[]} [options.problems] - Supplied by a **reader**: a note that
|
|
@@ -144,9 +144,10 @@ export function buildIndexRecord({ frontmatter, relPath, absPath, contentPackage
|
|
|
144
144
|
* @returns {Array<Record<string, any>>} The records, in a total order that does
|
|
145
145
|
* not depend on directory-read order.
|
|
146
146
|
*/
|
|
147
|
-
export function collectContentIndex(contentBase: string, { contentPackage, skipDirectories, manifest, problems }: {
|
|
147
|
+
export function collectContentIndex(contentBase: string, { contentPackage, skipDirectories, assetsBase, manifest, problems }: {
|
|
148
148
|
contentPackage: string;
|
|
149
149
|
skipDirectories: readonly string[];
|
|
150
|
+
assetsBase?: string | undefined;
|
|
150
151
|
manifest?: object | undefined;
|
|
151
152
|
problems?: object[] | undefined;
|
|
152
153
|
}): Array<Record<string, any>>;
|
|
@@ -170,6 +171,10 @@ export function serializeContentIndex(records: Array<Record<string, any>>): stri
|
|
|
170
171
|
*
|
|
171
172
|
* @param {object} [opts]
|
|
172
173
|
* @param {string} [opts.contentBase] - The tree, defaulting to the configured one.
|
|
174
|
+
* @param {string} [opts.assetsBase] - The asset roots' parent, defaulting to
|
|
175
|
+
* the configured one. Stated separately from `contentBase` because the two
|
|
176
|
+
* move independently — a caller walking an assembled fixture tree says where
|
|
177
|
+
* that fixture's files are.
|
|
173
178
|
* @param {object} [opts.config] - Resolved configuration, defaulting to ambient.
|
|
174
179
|
* @param {readonly string[]} [opts.skipDirectories] - The walk's scope, for a
|
|
175
180
|
* caller that resolved one of its own; defaults to the resolved
|
|
@@ -183,8 +188,9 @@ export function serializeContentIndex(records: Array<Record<string, any>>): stri
|
|
|
183
188
|
* exist.
|
|
184
189
|
* @returns {object[]} One record per note, plus one per documentation entry.
|
|
185
190
|
*/
|
|
186
|
-
export function indexRecordsFor({ contentBase, config, skipDirectories, problems }?: {
|
|
191
|
+
export function indexRecordsFor({ contentBase, assetsBase, config, skipDirectories, problems, }?: {
|
|
187
192
|
contentBase?: string | undefined;
|
|
193
|
+
assetsBase?: string | undefined;
|
|
188
194
|
config?: object | undefined;
|
|
189
195
|
skipDirectories?: readonly string[] | undefined;
|
|
190
196
|
problems?: object[] | undefined;
|
|
@@ -198,8 +204,9 @@ export function indexRecordsFor({ contentBase, config, skipDirectories, problems
|
|
|
198
204
|
* @param {string} [options.outDir] - Where to write; defaults to the configured
|
|
199
205
|
* `paths.contentIndex`.
|
|
200
206
|
* @param {object} [options.config] - A resolved configuration; loaded when omitted.
|
|
201
|
-
* @returns {{file: string, notes: number,
|
|
202
|
-
*
|
|
207
|
+
* @returns {{file: string, notes: number, assets: number, records: number,
|
|
208
|
+
* bytes: number}} Where it was written, how many notes and how many assets it
|
|
209
|
+
* holds, how many records that is in all, and its size.
|
|
203
210
|
* @throws {Error} When the content tree is absent, or when it yields no note at
|
|
204
211
|
* all — an empty index is indistinguishable from a mis-pointed tree, and a
|
|
205
212
|
* reader would take it as the authoritative statement that this package has
|
|
@@ -212,11 +219,16 @@ export function emitContentIndex({ contentBase, outDir, config }?: {
|
|
|
212
219
|
}): {
|
|
213
220
|
file: string;
|
|
214
221
|
notes: number;
|
|
222
|
+
assets: number;
|
|
223
|
+
records: number;
|
|
215
224
|
bytes: number;
|
|
216
225
|
};
|
|
217
226
|
import { collectAnchors } from "./anchors.mjs";
|
|
218
227
|
import { authoredFrontmatter } from "./index-records.mjs";
|
|
219
228
|
import { DERIVED_KEYS } from "./index-records.mjs";
|
|
229
|
+
import { isAssetRecord } from "./index-records.mjs";
|
|
220
230
|
import { isNoteRecord } from "./index-records.mjs";
|
|
221
231
|
import { noteFile } from "./index-records.mjs";
|
|
222
|
-
|
|
232
|
+
import { recordPath } from "./index-records.mjs";
|
|
233
|
+
import { sortKeysDeep } from "./index-records.mjs";
|
|
234
|
+
export { collectAnchors, authoredFrontmatter, DERIVED_KEYS, isAssetRecord, isNoteRecord, noteFile, recordPath, sortKeysDeep };
|
|
@@ -57,8 +57,8 @@ export function lockedVersion(lock: object, name: string): string | undefined;
|
|
|
57
57
|
* Take the newest published version of one or more dependencies.
|
|
58
58
|
*
|
|
59
59
|
* npm performs the resolution — so a bump that changes the dependency set is
|
|
60
|
-
* as correct as one that moves three lines — and both JSON files are
|
|
61
|
-
*
|
|
60
|
+
* as correct as one that moves three lines — and both JSON files are written
|
|
61
|
+
* back with the indentation they already used.
|
|
62
62
|
*
|
|
63
63
|
* @param {object} options - Options.
|
|
64
64
|
* @param {string} options.rootDir - The repository root holding `package.json`.
|
|
@@ -240,27 +240,24 @@ export const UNIVERSAL_KEYS: ReadonlySet<string>;
|
|
|
240
240
|
*/
|
|
241
241
|
export const DEFAULT_SYSTEM_BLOCKS: Readonly<Record<string, SystemBlockSpec>>;
|
|
242
242
|
/**
|
|
243
|
-
* The frontmatter fields that name artwork
|
|
244
|
-
* {@link module:engine/helpers.resolveImg}.
|
|
243
|
+
* The frontmatter fields that name artwork.
|
|
245
244
|
*
|
|
246
|
-
*
|
|
247
|
-
*
|
|
248
|
-
*
|
|
249
|
-
*
|
|
250
|
-
* note in any tree writes `img: ""` on a being; a check keyed on `img` alone
|
|
251
|
-
* would have called that tree clean.
|
|
245
|
+
* {@link module:engine/art-fields.ART_SLOTS} is the declaration; this is that
|
|
246
|
+
* list in the shape the checks below read, so the linter states no art key of
|
|
247
|
+
* its own and a slot added there is checked here with no second edit. All four
|
|
248
|
+
* are authored under `data:`, which is what `inData` records.
|
|
252
249
|
*
|
|
253
|
-
*
|
|
254
|
-
*
|
|
255
|
-
*
|
|
256
|
-
*
|
|
257
|
-
* `data.portrait: ""` it could not see.
|
|
250
|
+
* `document` separates the three that reach a compiled document from `banner`,
|
|
251
|
+
* which reaches none by design. The inert-art check reads it: a key that is
|
|
252
|
+
* *meant* to reach no document is not an inert key, and reporting every note
|
|
253
|
+
* that names a hero image would bury the finding that matters.
|
|
258
254
|
*
|
|
259
|
-
* @type {readonly {key: string, inData: boolean}[]}
|
|
255
|
+
* @type {readonly {key: string, inData: boolean, document: boolean}[]}
|
|
260
256
|
*/
|
|
261
257
|
export const ART_FIELDS: readonly {
|
|
262
258
|
key: string;
|
|
263
259
|
inData: boolean;
|
|
260
|
+
document: boolean;
|
|
264
261
|
}[];
|
|
265
262
|
/**
|
|
266
263
|
* What one system block accepts beyond the keys every block carries.
|
|
@@ -385,6 +385,9 @@ export function buildContentLinkIndex(contentBase: string, router?: object, { sk
|
|
|
385
385
|
* in, addressing a self-link the same way.
|
|
386
386
|
* @param {object} ctx.index - The address index every link resolves through.
|
|
387
387
|
* @param {string} ctx.name - The note, for the message.
|
|
388
|
+
* @param {object} [ctx.config] - The resolved build configuration, which is
|
|
389
|
+
* what says which packages this build can address a file in. Loaded when
|
|
390
|
+
* omitted, exactly as an art field's resolution loads it.
|
|
388
391
|
* @param {string} [ctx.file] - The note's file, so a report names it.
|
|
389
392
|
* @param {number} [ctx.bodyLine] - 1-based file line of the body's first line.
|
|
390
393
|
* @param {number} [ctx.bodyColumn] - 1-based file column of the same character.
|
|
@@ -392,17 +395,19 @@ export function buildContentLinkIndex(contentBase: string, router?: object, { sk
|
|
|
392
395
|
* authored line each body line came from, from {@link expandNoteTables}.
|
|
393
396
|
* @returns {{markdown: string, unresolved: Array<object>}}
|
|
394
397
|
* @throws {Error} On any link that does not resolve — an unlabelled one, a
|
|
395
|
-
* target that is not an address, or an address nothing publishes
|
|
398
|
+
* target that is not an address, or an address nothing publishes — and on a
|
|
399
|
+
* body image whose pathname names no file a Foundry install serves. The error
|
|
396
400
|
* carries `file` and `position`, so a caller reports it in the same form
|
|
397
401
|
* rather than re-deriving one.
|
|
398
402
|
*/
|
|
399
|
-
export function convertNoteWikilinks(body: string, { type, id, pack, docPack, index, name, file, bodyLine, bodyColumn, lineMap }: {
|
|
403
|
+
export function convertNoteWikilinks(body: string, { type, id, pack, docPack, index, name, file, bodyLine, bodyColumn, lineMap, config }: {
|
|
400
404
|
type: string;
|
|
401
405
|
id: string;
|
|
402
406
|
pack: string;
|
|
403
407
|
docPack: string;
|
|
404
408
|
index: object;
|
|
405
409
|
name: string;
|
|
410
|
+
config?: object | undefined;
|
|
406
411
|
file?: string | undefined;
|
|
407
412
|
bodyLine?: number | undefined;
|
|
408
413
|
bodyColumn?: number | undefined;
|
|
@@ -1,3 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Recursively sort an object's keys, so serialization is order-independent.
|
|
3
|
+
*
|
|
4
|
+
* Arrays keep their order — it is authored — but every object inside one is
|
|
5
|
+
* sorted too. Anything that is not a plain object is returned as it is.
|
|
6
|
+
*
|
|
7
|
+
* @param {unknown} value - The value to normalize.
|
|
8
|
+
* @returns {unknown} The value with every plain object's keys in sorted order.
|
|
9
|
+
*/
|
|
10
|
+
export function sortKeysDeep(value: unknown): unknown;
|
|
1
11
|
/**
|
|
2
12
|
* The file a record was read from, as an absolute path.
|
|
3
13
|
*
|
|
@@ -36,19 +46,50 @@ export function noteFile(contentBase: string, record: object): string;
|
|
|
36
46
|
*/
|
|
37
47
|
export function authoredFrontmatter(record: Record<string, any>): Record<string, any>;
|
|
38
48
|
/**
|
|
39
|
-
* Whether a record
|
|
49
|
+
* Whether a record addresses a **file** rather than a note.
|
|
50
|
+
*
|
|
51
|
+
* The index holds two record shapes in one file, and this is how a reader tells
|
|
52
|
+
* them apart. An asset record carries no frontmatter, no anchors and no
|
|
53
|
+
* `foundry` block — a `.webp` declares nothing about itself — so every pass that
|
|
54
|
+
* reads a note's fields has to skip it, and the `asset` block is what marks it.
|
|
55
|
+
*
|
|
56
|
+
* Asked of the block rather than of `type`, so a fourth asset type needs no
|
|
57
|
+
* edit here: what makes a record an asset's is that it describes a file, and the
|
|
58
|
+
* block is the description.
|
|
59
|
+
*
|
|
60
|
+
* @param {Record<string, any>} record - An index record.
|
|
61
|
+
* @returns {boolean} True for an asset's record.
|
|
62
|
+
*/
|
|
63
|
+
export function isAssetRecord(record: Record<string, any>): boolean;
|
|
64
|
+
/**
|
|
65
|
+
* Whether a record is a note's, rather than a documentation journal's or an
|
|
66
|
+
* asset's.
|
|
40
67
|
*
|
|
41
68
|
* An item note yields two records — itself and the JournalEntry its prose
|
|
42
69
|
* compiles into — and the second is a document, not a note: it has no file of
|
|
43
70
|
* its own to read, no frontmatter an author wrote, and its `type` is the
|
|
44
71
|
* virtual `doc<type>` that `readQualifier` resolves rather than a type any tree
|
|
45
|
-
* declares.
|
|
46
|
-
*
|
|
72
|
+
* declares. An asset's record is not a note either, for the stronger reason that
|
|
73
|
+
* nobody authored it at all. A reader enumerating the corpus wants the notes;
|
|
74
|
+
* one resolving an address wants all three.
|
|
47
75
|
*
|
|
48
76
|
* @param {Record<string, any>} record - An index record.
|
|
49
77
|
* @returns {boolean} True for a note's own record.
|
|
50
78
|
*/
|
|
51
79
|
export function isNoteRecord(record: Record<string, any>): boolean;
|
|
80
|
+
/**
|
|
81
|
+
* The path a record names inside its package, whichever shape it is.
|
|
82
|
+
*
|
|
83
|
+
* The two shapes state it differently and honestly: a note names the `.md` it
|
|
84
|
+
* was parsed from, relative to the content root, while an asset names the file
|
|
85
|
+
* it *is*, relative to the package's asset directory. Both are paths within one
|
|
86
|
+
* package, so one total order covers the whole index — which is what keeps the
|
|
87
|
+
* artifact byte-stable across a rebuild.
|
|
88
|
+
*
|
|
89
|
+
* @param {Record<string, any>} record - An index record.
|
|
90
|
+
* @returns {string} The path, or `""` for a record naming neither.
|
|
91
|
+
*/
|
|
92
|
+
export function recordPath(record: Record<string, any>): string;
|
|
52
93
|
/**
|
|
53
94
|
* The keys the content index adds to a record, which a note therefore may not
|
|
54
95
|
* carry itself.
|
package/types/engine/index.d.mts
CHANGED
|
@@ -27,12 +27,17 @@ export * as noteRenames from "./note-renames.mjs";
|
|
|
27
27
|
export * as metadataIndex from "./metadata-index.mjs";
|
|
28
28
|
export * as foundryEntries from "./foundry-entries.mjs";
|
|
29
29
|
export * as contentIndex from "./content-index.mjs";
|
|
30
|
+
export * as assetTypes from "./asset-types.mjs";
|
|
31
|
+
export * as assetIndex from "./asset-index.mjs";
|
|
32
|
+
export * as packages from "./packages.mjs";
|
|
33
|
+
export * as packagebuildIndex from "./packagebuild-index.mjs";
|
|
30
34
|
export * as siteBuild from "./site-build.mjs";
|
|
31
35
|
export * as contentLint from "./content-lint.mjs";
|
|
32
36
|
export * as contentCharset from "./content-charset.mjs";
|
|
33
37
|
export * as contentIcons from "./content-icons.mjs";
|
|
34
38
|
export * as contentHtml from "./content-html.mjs";
|
|
35
39
|
export * as contentImages from "./content-images.mjs";
|
|
40
|
+
export * as contentEmbeds from "./content-embeds.mjs";
|
|
36
41
|
export * as pathnames from "./pathnames.mjs";
|
|
37
42
|
export * as contentLinks from "./content-links.mjs";
|
|
38
43
|
export * as webWikilinks from "./web-wikilinks.mjs";
|
|
@@ -6,6 +6,38 @@
|
|
|
6
6
|
* toolchain ships none for it.
|
|
7
7
|
*/
|
|
8
8
|
export function infoboxFor(system: string | undefined): object | undefined;
|
|
9
|
+
/**
|
|
10
|
+
* Whether one system compiles a document for one note.
|
|
11
|
+
*
|
|
12
|
+
* This is what a system box's _available_ asserts, and it is the compile's own
|
|
13
|
+
* question rather than a reading of the frontmatter. A note carrying no block
|
|
14
|
+
* is not a note a system has nothing for: where the pack compiling its document
|
|
15
|
+
* declares no `system:`, the document is built from `data:` and the field
|
|
16
|
+
* defaults and ships exactly like any other.
|
|
17
|
+
*
|
|
18
|
+
* Three statements answer it, and they are the three the compile itself
|
|
19
|
+
* follows:
|
|
20
|
+
*
|
|
21
|
+
* 1. **The map** says which document class this system makes of the note's
|
|
22
|
+
* type. No row, no document — and no box either, which is why a caller
|
|
23
|
+
* reaching here already has one.
|
|
24
|
+
* 2. **The router** says which pack that document goes to, read from the pack
|
|
25
|
+
* list this build is driven by.
|
|
26
|
+
* 3. **That pack's `system:`** decides the rest. Declaring one, it writes that
|
|
27
|
+
* system's data and takes only notes that say something about it — the rule
|
|
28
|
+
* {@link module:engine/base-compiler.BasePackCompiler#eligibleFor} applies,
|
|
29
|
+
* asked here from outside. Declaring none, it is compiled by the fallback
|
|
30
|
+
* pass, which needs no block and answers for
|
|
31
|
+
* {@link module:engine/subtype-registry.DEFAULT_DOCUMENT_SUBTYPES} alone —
|
|
32
|
+
* so a tree with no HM3 pack ships no HM3 document however a note is
|
|
33
|
+
* written.
|
|
34
|
+
*
|
|
35
|
+
* @param {object} fm - The note's frontmatter.
|
|
36
|
+
* @param {object} map - The system's note-type → document-subtype map.
|
|
37
|
+
* @param {object} router - The pack router this build is driven by.
|
|
38
|
+
* @returns {boolean} True when this system compiles a document for this note.
|
|
39
|
+
*/
|
|
40
|
+
export function compilesSystemDocument(fm: object, map: object, router: object): boolean;
|
|
9
41
|
/**
|
|
10
42
|
* Every box one note carries, wired to the registries this toolchain ships.
|
|
11
43
|
*
|
|
@@ -17,12 +49,15 @@ export function infoboxFor(system: string | undefined): object | undefined;
|
|
|
17
49
|
* @param {object} [options] - Options.
|
|
18
50
|
* @param {(ref: unknown, hint?: object) => object|undefined} [options.resolve] -
|
|
19
51
|
* Resolves a reference to `{name, url?, uuid?, address?, subType?}`.
|
|
52
|
+
* @param {object} [options.router] - The pack router deciding which system
|
|
53
|
+
* compiles a document for this note. Defaults to the consuming repository's.
|
|
20
54
|
* @returns {object[]} The boxes, in the order every medium renders them.
|
|
21
55
|
* @throws {Error} When the built set disagrees with what the note's type maps
|
|
22
56
|
* to — see {@link module:engine/infobox.assertInfoboxSet}.
|
|
23
57
|
*/
|
|
24
|
-
export function noteInfoboxes(fm: object, { resolve }?: {
|
|
58
|
+
export function noteInfoboxes(fm: object, { resolve, router }?: {
|
|
25
59
|
resolve?: ((ref: unknown, hint?: object) => object | undefined) | undefined;
|
|
60
|
+
router?: object | undefined;
|
|
26
61
|
}): object[];
|
|
27
62
|
/**
|
|
28
63
|
* Every system's infobox declaration, in the order a page shows them.
|
|
@@ -246,9 +246,11 @@ export function systemRowsSection(fm: object, fields: readonly object[], { block
|
|
|
246
246
|
* build ships, which decide the box set.
|
|
247
247
|
* @param {readonly object[]} [options.providers] - The systems' infobox
|
|
248
248
|
* declarations, keyed by `system`.
|
|
249
|
-
* @param {(fm: object,
|
|
250
|
-
* Whether
|
|
251
|
-
* {@link NOT_AVAILABLE}.
|
|
249
|
+
* @param {(fm: object, map: object) => boolean} options.compilesDocument -
|
|
250
|
+
* Whether that system compiles a document for this note, which decides
|
|
251
|
+
* {@link NOT_AVAILABLE}. Asked of the routing and the passes rather than of
|
|
252
|
+
* the frontmatter: a pack declaring no `system:` compiles a note from `data:`
|
|
253
|
+
* and the field defaults, so the block's presence is not the question.
|
|
252
254
|
* @param {(field: object, fm: object, opts: object) => object} options.resolveField -
|
|
253
255
|
* Resolves one declared field against the note.
|
|
254
256
|
* @param {(ref: unknown) => object|undefined} [options.resolve] - Resolves a
|
|
@@ -259,7 +261,7 @@ export function systemRowsSection(fm: object, fields: readonly object[], { block
|
|
|
259
261
|
export function buildInfoboxes(fm: object, options: {
|
|
260
262
|
maps: readonly object[];
|
|
261
263
|
providers?: readonly object[] | undefined;
|
|
262
|
-
|
|
264
|
+
compilesDocument: (fm: object, map: object) => boolean;
|
|
263
265
|
resolveField: (field: object, fm: object, opts: object) => object;
|
|
264
266
|
resolve?: ((ref: unknown) => object | undefined) | undefined;
|
|
265
267
|
vocabulary?: object | undefined;
|