@heroiclands/package-build 21.0.0 → 21.2.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 +228 -0
- package/CONTENT.md +109 -7
- 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/ci/emit-asset-index.mjs +32 -0
- package/content-config.mjs +30 -1
- package/docs/api.md +100 -26
- package/docs/configuration.md +16 -7
- package/docs/content-format.md +512 -109
- 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/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-render.mjs +4 -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 -10
- package/engine/pdf-render.mjs +41 -41
- 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/hm3/infobox.mjs +1 -1
- package/package.json +5 -2
- package/packagebuild-metadata.jsonl +13 -0
- package/sohl/actors.mjs +12 -86
- package/sohl/infobox.mjs +1 -1
- 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/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 -3
- 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/sohl/actors.d.mts +0 -1
|
@@ -129,6 +129,11 @@ export class BasePackCompiler {
|
|
|
129
129
|
/**
|
|
130
130
|
* @param {object} options
|
|
131
131
|
* @param {string} options.contentBase - Root of the content tree.
|
|
132
|
+
* @param {string} [options.assetsBase] - The asset roots' parent, whose
|
|
133
|
+
* files the art addresses resolve against. Stated beside `contentBase`
|
|
134
|
+
* because the two move independently: a caller compiling a tree it
|
|
135
|
+
* assembled states where that tree's files are, and one compiling the
|
|
136
|
+
* repository's own leaves it to the configuration.
|
|
132
137
|
* @param {string} options.dest - Where this pass writes its JSON.
|
|
133
138
|
* @param {readonly string[]} options.skipDirectories - Directories the walk
|
|
134
139
|
* never descends into. Required: see {@link assertStatedScope}.
|
|
@@ -148,8 +153,9 @@ export class BasePackCompiler {
|
|
|
148
153
|
* over, derived once and shared by every pass. A pass handed none derives
|
|
149
154
|
* its own in `prepare`, and reports that corpus's problems itself.
|
|
150
155
|
*/
|
|
151
|
-
constructor({ contentBase, dest, skipDirectories, folderResolver, packName, packSystem, docType, router, routingReporter, corpus, }?: {
|
|
156
|
+
constructor({ contentBase, assetsBase, dest, skipDirectories, folderResolver, packName, packSystem, docType, router, routingReporter, corpus, }?: {
|
|
152
157
|
contentBase: string;
|
|
158
|
+
assetsBase?: string | undefined;
|
|
153
159
|
dest: string;
|
|
154
160
|
skipDirectories: readonly string[];
|
|
155
161
|
folderResolver?: ((address: string | null) => string | null) | undefined;
|
|
@@ -164,6 +170,12 @@ export class BasePackCompiler {
|
|
|
164
170
|
});
|
|
165
171
|
/** @type {string} */
|
|
166
172
|
contentBase: string;
|
|
173
|
+
/**
|
|
174
|
+
* The asset roots' parent, whose files the art addresses resolve against.
|
|
175
|
+
*
|
|
176
|
+
* @type {string|undefined}
|
|
177
|
+
*/
|
|
178
|
+
assetsBase: string | undefined;
|
|
167
179
|
/** @type {string} */
|
|
168
180
|
outputDir: string;
|
|
169
181
|
/** @type {(path: string|null) => string|null} */
|
|
@@ -348,6 +360,35 @@ export class BasePackCompiler {
|
|
|
348
360
|
* that does not convert.
|
|
349
361
|
*/
|
|
350
362
|
convertBody(fm: object, body: string): string;
|
|
363
|
+
/**
|
|
364
|
+
* The Foundry path one authored art address names, or `null`.
|
|
365
|
+
*
|
|
366
|
+
* **The two empties survive.** `null` and an absent key mean *no art named*,
|
|
367
|
+
* so the caller's own default applies with nullish coalescing; `""` means
|
|
368
|
+
* *ship blank on purpose* and no default may replace it. That is
|
|
369
|
+
* {@link module:engine/helpers.resolveImg}'s rule, reached through it rather
|
|
370
|
+
* than restated, which is also what puts an art address and a body image
|
|
371
|
+
* through one ownership rule.
|
|
372
|
+
*
|
|
373
|
+
* An address nothing answers is reported against the note and treated as
|
|
374
|
+
* unnamed, so the document takes its default rather than shipping a path
|
|
375
|
+
* that installs nowhere.
|
|
376
|
+
*
|
|
377
|
+
* @param {unknown} value - The value as authored.
|
|
378
|
+
* @param {string} key - The key it was authored at, for the message.
|
|
379
|
+
* @param {string} type - The asset type a bare value takes.
|
|
380
|
+
* @returns {string|null} The Foundry-relative path, `""` for a deliberate
|
|
381
|
+
* blank, or `null` where the note names none.
|
|
382
|
+
*/
|
|
383
|
+
artPathOf(value: unknown, key: string, type: string): string | null;
|
|
384
|
+
/**
|
|
385
|
+
* The Foundry path one art slot of a note names, or `null`.
|
|
386
|
+
*
|
|
387
|
+
* @param {object} fm - The note's frontmatter.
|
|
388
|
+
* @param {string} key - The slot's key under `data:`.
|
|
389
|
+
* @returns {string|null} As {@link BasePackCompiler#artPathOf}.
|
|
390
|
+
*/
|
|
391
|
+
artPath(fm: object, key: string): string | null;
|
|
351
392
|
/**
|
|
352
393
|
* Reports a warning about the note being compiled.
|
|
353
394
|
*
|
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
*
|
|
4
4
|
* @param {object} opts - Options.
|
|
5
5
|
* @param {string} opts.contentBase - Root of the content tree.
|
|
6
|
+
* @param {string} [opts.assetsBase] - The asset roots' parent, whose files the
|
|
7
|
+
* art addresses resolve against. Defaults to the configured one.
|
|
6
8
|
* @param {readonly string[]} opts.skipDirectories - The scope, stated by the
|
|
7
9
|
* caller as every corpus read requires.
|
|
8
10
|
* @param {object} opts.router - The pack router this compile resolved. Shared
|
|
@@ -17,8 +19,9 @@
|
|
|
17
19
|
* sqlTables: Map<string, object[]>|undefined, problems: object[]}>} The corpus,
|
|
18
20
|
* its indexes, and the notes it could not record.
|
|
19
21
|
*/
|
|
20
|
-
export function buildCompileCorpus({ contentBase, skipDirectories, router, config, problems, }: {
|
|
22
|
+
export function buildCompileCorpus({ contentBase, assetsBase, skipDirectories, router, config, problems, }: {
|
|
21
23
|
contentBase: string;
|
|
24
|
+
assetsBase?: string | undefined;
|
|
22
25
|
skipDirectories: readonly string[];
|
|
23
26
|
router: object;
|
|
24
27
|
config?: object | undefined;
|
|
@@ -78,6 +78,10 @@ export function checkText(text: string, file: string): Array<{
|
|
|
78
78
|
* theoretical exclusion — it was the first thing a run over `sohl-thalorna`
|
|
79
79
|
* reported before the skip existed.
|
|
80
80
|
*
|
|
81
|
+
* A finding names its file **relative to the working directory**, which is
|
|
82
|
+
* where a reader is standing and what `formatDiagnostic` emits. The content
|
|
83
|
+
* root is where the walk starts, not what a path is measured from.
|
|
84
|
+
*
|
|
81
85
|
* @param {string} contentBase - Root of the content tree.
|
|
82
86
|
* @param {object} [opts]
|
|
83
87
|
* @param {readonly string[]} [opts.skipDirectories] - Directory names to ignore
|
|
@@ -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 };
|
|
@@ -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";
|