@heroiclands/package-build 22.9.2 → 22.11.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 +56 -0
- package/CONTENT.md +54 -0
- package/bin/content-build.mjs +3 -1
- package/docs/api.md +11 -6
- package/docs/commands.md +1 -1
- package/docs/content-format.md +411 -201
- package/engine/actor-compiler.mjs +20 -7
- package/engine/address-fields.mjs +426 -0
- package/engine/address-values.mjs +80 -0
- package/engine/address.mjs +769 -0
- package/engine/art-fields.mjs +112 -97
- package/engine/art-slots.mjs +81 -0
- package/engine/base-compiler.mjs +61 -7
- package/engine/bundle-notes.mjs +5 -2
- package/engine/bundles.mjs +17 -10
- package/engine/content-address.mjs +23 -186
- package/engine/content-embeds.mjs +18 -4
- package/engine/content-format-check.mjs +50 -22
- package/engine/content-index.mjs +85 -43
- package/engine/content-links.mjs +105 -104
- package/engine/content-lint.mjs +11 -1
- package/engine/content-tables.mjs +9 -1
- package/engine/field-spec.mjs +21 -4
- package/engine/folder-notes.mjs +44 -15
- package/engine/foundry-entries.mjs +3 -0
- package/engine/frontmatter-lint.mjs +150 -47
- package/engine/frontmatter.mjs +55 -10
- package/engine/generate.mjs +7 -1
- package/engine/helpers.mjs +37 -9
- package/engine/holdings.mjs +41 -12
- package/engine/index-records.mjs +2 -0
- package/engine/infobox-render.mjs +2 -1
- package/engine/infobox.mjs +26 -6
- package/engine/item-compiler.mjs +5 -0
- package/engine/item-docs.mjs +7 -44
- package/engine/map-dot.mjs +2 -0
- package/engine/map-notes.mjs +7 -2
- package/engine/map-places.mjs +42 -10
- package/engine/metadata-index.mjs +60 -5
- package/engine/note-addresses.mjs +325 -0
- package/engine/note-vocabulary.mjs +140 -28
- package/engine/pdf-build.mjs +1 -1
- package/engine/pdf-toc.mjs +17 -2
- package/engine/place-relations.mjs +160 -50
- package/engine/populations.mjs +17 -9
- package/engine/reference-targets.mjs +54 -0
- package/engine/scenes.mjs +5 -2
- package/engine/shortcode-references.mjs +68 -0
- package/engine/site-build.mjs +39 -15
- package/engine/site-index.mjs +57 -75
- package/engine/sql-tables.mjs +43 -9
- package/engine/system-block.mjs +21 -0
- package/engine/system-vocabulary.mjs +46 -0
- package/engine/web-wikilinks.mjs +11 -9
- package/engine/wikilink-syntax.mjs +21 -0
- package/engine/wikilinks.mjs +92 -306
- package/hm3/actors.mjs +8 -24
- package/hm3/item-fields.mjs +15 -5
- package/package.json +1 -1
- package/sohl/infobox.mjs +111 -17
- package/sohl/item-fields.mjs +131 -41
- package/types/engine/address-fields.d.mts +95 -0
- package/types/engine/address-values.d.mts +34 -0
- package/types/engine/address.d.mts +452 -0
- package/types/engine/art-fields.d.mts +59 -53
- package/types/engine/art-slots.d.mts +40 -0
- package/types/engine/base-compiler.d.mts +24 -2
- package/types/engine/bundle-notes.d.mts +2 -2
- package/types/engine/content-address.d.mts +3 -139
- package/types/engine/content-embeds.d.mts +1 -1
- package/types/engine/content-format-check.d.mts +25 -0
- package/types/engine/content-index.d.mts +2 -2
- package/types/engine/content-links.d.mts +6 -5
- package/types/engine/field-spec.d.mts +46 -9
- package/types/engine/folder-notes.d.mts +11 -10
- package/types/engine/frontmatter-lint.d.mts +7 -97
- package/types/engine/frontmatter.d.mts +16 -1
- package/types/engine/helpers.d.mts +2 -2
- package/types/engine/infobox.d.mts +6 -0
- package/types/engine/item-docs.d.mts +1 -35
- package/types/engine/map-notes.d.mts +2 -2
- package/types/engine/note-addresses.d.mts +41 -0
- package/types/engine/note-vocabulary.d.mts +20 -3
- package/types/engine/place-relations.d.mts +12 -1
- package/types/engine/populations.d.mts +17 -2
- package/types/engine/reference-targets.d.mts +9 -0
- package/types/engine/shortcode-references.d.mts +14 -0
- package/types/engine/site-build.d.mts +3 -3
- package/types/engine/site-index.d.mts +9 -30
- package/types/engine/sql-tables.d.mts +2 -2
- package/types/engine/system-block.d.mts +17 -0
- package/types/engine/system-vocabulary.d.mts +16 -0
- package/types/engine/wikilink-syntax.d.mts +7 -0
- package/types/engine/wikilinks.d.mts +6 -109
- package/types/hm3/item-fields.d.mts +5 -0
- package/types/sohl/infobox.d.mts +6 -5
- package/types/sohl/item-fields.d.mts +8 -0
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
/** @typedef {import("./art-slots.mjs").ArtSlot} ArtSlot */
|
|
1
2
|
/**
|
|
2
3
|
* The art slot one key names, or `undefined`.
|
|
3
4
|
*
|
|
@@ -6,13 +7,30 @@
|
|
|
6
7
|
*/
|
|
7
8
|
export function artSlot(key: unknown): ArtSlot | undefined;
|
|
8
9
|
/**
|
|
9
|
-
* The
|
|
10
|
+
* The Address an authored art value names, checked against what this position
|
|
11
|
+
* accepts.
|
|
10
12
|
*
|
|
11
|
-
* @
|
|
12
|
-
*
|
|
13
|
-
*
|
|
13
|
+
* The two questions {@link module:engine/address} keeps separate: does the
|
|
14
|
+
* value parse, and is what it names acceptable *here*. {@link parseAddress}
|
|
15
|
+
* answers the first, taking the slot's own `type` as the default an omitted
|
|
16
|
+
* segment fills in; {@link acceptsType} answers the second, against the set
|
|
17
|
+
* the position declares rather than against that same default.
|
|
18
|
+
*
|
|
19
|
+
* @param {unknown} value - The value as authored.
|
|
20
|
+
* @param {string} defaultType - The type a bare value takes.
|
|
21
|
+
* @param {Iterable<string>|undefined} accepts - The types this position
|
|
22
|
+
* accepts, or `undefined` to accept whatever parses.
|
|
23
|
+
* @param {import("./address.mjs").AddressDefaults} [vocabulary] - The
|
|
24
|
+
* position's remaining defaults and the tree's vocabularies.
|
|
25
|
+
* @returns {import("./address.mjs").AddressTuple
|
|
26
|
+
* |import("./address.mjs").AddressProblem
|
|
27
|
+
* |{reason: "not-accepted", type: string}} The Address; a reason it does
|
|
28
|
+
* not parse; or `not-accepted` when it parses to a type outside `accepts`.
|
|
14
29
|
*/
|
|
15
|
-
export function artTarget(value:
|
|
30
|
+
export function artTarget(value: unknown, defaultType: string, accepts: Iterable<string> | undefined, vocabulary?: import("./address.mjs").AddressDefaults): import("./address.mjs").AddressTuple | import("./address.mjs").AddressProblem | {
|
|
31
|
+
reason: "not-accepted";
|
|
32
|
+
type: string;
|
|
33
|
+
};
|
|
16
34
|
/**
|
|
17
35
|
* The address space an asset reference resolves against, from a corpus.
|
|
18
36
|
*
|
|
@@ -60,12 +78,17 @@ export function assetAddressIndex(records?: readonly object[], { config, foreign
|
|
|
60
78
|
* or the equivalent the site and the book build.
|
|
61
79
|
* @param {unknown} value - The value as authored.
|
|
62
80
|
* @param {string} defaultType - The type a bare value takes.
|
|
81
|
+
* @param {Iterable<string>} [accepts] - The types this position accepts, for a
|
|
82
|
+
* position that declares a set narrower than "every asset type" — an art
|
|
83
|
+
* slot's. Omitted, any asset type is taken, which is an embed's rule: it
|
|
84
|
+
* draws a file, and every asset type is a file.
|
|
63
85
|
* @returns {{record: {package: string, asset: {path: string}}, pathname: string}
|
|
64
86
|
* |{record: null, reason: string, type?: string}} The asset and the pathname
|
|
65
87
|
* it is at, or a reason from
|
|
66
|
-
* {@link module:engine/wikilink-syntax.LINK_FINDING_REASONS}
|
|
88
|
+
* {@link module:engine/wikilink-syntax.LINK_FINDING_REASONS}, or `not-accepted`
|
|
89
|
+
* when `accepts` is given and the parsed type falls outside it.
|
|
67
90
|
*/
|
|
68
|
-
export function readAssetAddress(index: object, value: unknown, defaultType: string): {
|
|
91
|
+
export function readAssetAddress(index: object, value: unknown, defaultType: string, accepts?: Iterable<string>): {
|
|
69
92
|
record: {
|
|
70
93
|
package: string;
|
|
71
94
|
asset: {
|
|
@@ -84,9 +107,10 @@ export function readAssetAddress(index: object, value: unknown, defaultType: str
|
|
|
84
107
|
* @param {object} index - From {@link module:engine/wikilinks.buildWikilinkIndex}.
|
|
85
108
|
* @param {unknown} value - The value as authored.
|
|
86
109
|
* @param {string} defaultType - The type the field declares.
|
|
110
|
+
* @param {Iterable<string>} [accepts] - The types this position accepts.
|
|
87
111
|
* @returns {{package: string, asset: {path: string}}|null} The record.
|
|
88
112
|
*/
|
|
89
|
-
export function resolveArtRecord(index: object, value: unknown, defaultType: string): {
|
|
113
|
+
export function resolveArtRecord(index: object, value: unknown, defaultType: string, accepts?: Iterable<string>): {
|
|
90
114
|
package: string;
|
|
91
115
|
asset: {
|
|
92
116
|
path: string;
|
|
@@ -103,13 +127,19 @@ export function resolveArtRecord(index: object, value: unknown, defaultType: str
|
|
|
103
127
|
* @param {object} index - From {@link module:engine/wikilinks.buildWikilinkIndex}.
|
|
104
128
|
* @param {unknown} value - The value as authored.
|
|
105
129
|
* @param {string} defaultType - The type the field declares.
|
|
106
|
-
* @
|
|
107
|
-
*
|
|
108
|
-
*
|
|
130
|
+
* @param {Iterable<string>} [accepts] - The types this position accepts.
|
|
131
|
+
* @returns {{pathname: string|null, resolved: boolean, reason?: string,
|
|
132
|
+
* type?: string}} The pathname, and whether an address was actually
|
|
133
|
+
* answered — which tells a caller applying a default apart from one whose
|
|
134
|
+
* address named nothing. `reason` and `type` carry why, unresolved — in
|
|
135
|
+
* particular `not-accepted`, which the caller reports as an error rather
|
|
136
|
+
* than falling back silently.
|
|
109
137
|
*/
|
|
110
|
-
export function artPathname(index: object, value: unknown, defaultType: string): {
|
|
138
|
+
export function artPathname(index: object, value: unknown, defaultType: string, accepts?: Iterable<string>): {
|
|
111
139
|
pathname: string | null;
|
|
112
140
|
resolved: boolean;
|
|
141
|
+
reason?: string;
|
|
142
|
+
type?: string;
|
|
113
143
|
};
|
|
114
144
|
/**
|
|
115
145
|
* The default art address a being's own tags choose, or `null`.
|
|
@@ -126,34 +156,30 @@ export function beingDefaultArt(fm: object): string | null;
|
|
|
126
156
|
* What an unresolved art address is reported as.
|
|
127
157
|
*
|
|
128
158
|
* One wording, so the four compilers that can meet the case do not each invent
|
|
129
|
-
* their own.
|
|
159
|
+
* their own. A **warning**: nothing in this package or a fetched index answers
|
|
160
|
+
* the address, so the document falls back to its default art rather than
|
|
161
|
+
* shipping a path that installs nowhere.
|
|
130
162
|
*
|
|
131
163
|
* @param {string} key - The key that was authored.
|
|
132
|
-
* @param {unknown} value - The value it carried.
|
|
133
|
-
* @param {string} defaultType - The type the field declares.
|
|
164
|
+
* @param {unknown} value - The value it carried, named exactly as written.
|
|
134
165
|
* @returns {string} The message.
|
|
135
166
|
*/
|
|
136
|
-
export function unresolvedArtMessage(key: string, value: unknown
|
|
167
|
+
export function unresolvedArtMessage(key: string, value: unknown): string;
|
|
137
168
|
/**
|
|
138
|
-
*
|
|
169
|
+
* What an art address naming a type this slot does not accept is reported as.
|
|
139
170
|
*
|
|
140
|
-
*
|
|
141
|
-
*
|
|
142
|
-
*
|
|
143
|
-
* @property {boolean} document - Whether the slot reaches a compiled document.
|
|
144
|
-
* @property {string} describe - One line, for the author-facing reference.
|
|
145
|
-
*/
|
|
146
|
-
/**
|
|
147
|
-
* The four art slots, in the order the specification tabulates them.
|
|
148
|
-
*
|
|
149
|
-
* `banner` is the one that reaches no compiled document: it is the page's hero
|
|
150
|
-
* image, read by the site and by the book's section plates and by nothing else.
|
|
151
|
-
* That is what `document: false` states, and it is why the inert-art check
|
|
152
|
-
* skips it — a key that is *meant* to reach no document is not an inert key.
|
|
171
|
+
* An **error**, not a warning: unlike an address nothing answers, this one
|
|
172
|
+
* names a real type, and no default art repairs an author asking for a sound
|
|
173
|
+
* where the slot takes an icon or an image.
|
|
153
174
|
*
|
|
154
|
-
* @
|
|
175
|
+
* @param {string} key - The key that was authored.
|
|
176
|
+
* @param {unknown} value - The value it carried, named exactly as written.
|
|
177
|
+
* @param {string} type - The type the address named.
|
|
178
|
+
* @param {Iterable<string>} accepts - The types this slot accepts.
|
|
179
|
+
* @returns {string} The message.
|
|
155
180
|
*/
|
|
156
|
-
export
|
|
181
|
+
export function unacceptedArtMessage(key: string, value: unknown, type: string, accepts: Iterable<string>): string;
|
|
182
|
+
export { ART_SLOTS } from "./art-slots.mjs";
|
|
157
183
|
/**
|
|
158
184
|
* The art a being falls back to, by the kind it is tagged.
|
|
159
185
|
*
|
|
@@ -169,24 +195,4 @@ export const ART_SLOTS: readonly ArtSlot[];
|
|
|
169
195
|
* @type {Readonly<Record<string, string>>}
|
|
170
196
|
*/
|
|
171
197
|
export const BEING_DEFAULT_ART: Readonly<Record<string, string>>;
|
|
172
|
-
|
|
173
|
-
* One art slot: the key a note authors, and the type a bare value takes.
|
|
174
|
-
*/
|
|
175
|
-
export type ArtSlot = {
|
|
176
|
-
/**
|
|
177
|
-
* - The key under `data:`.
|
|
178
|
-
*/
|
|
179
|
-
key: string;
|
|
180
|
-
/**
|
|
181
|
-
* - The asset type a bare shortcode defaults to.
|
|
182
|
-
*/
|
|
183
|
-
type: string;
|
|
184
|
-
/**
|
|
185
|
-
* - Whether the slot reaches a compiled document.
|
|
186
|
-
*/
|
|
187
|
-
document: boolean;
|
|
188
|
-
/**
|
|
189
|
-
* - One line, for the author-facing reference.
|
|
190
|
-
*/
|
|
191
|
-
describe: string;
|
|
192
|
-
};
|
|
198
|
+
export type ArtSlot = import("./art-slots.mjs").ArtSlot;
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The four art slots, in the order the specification tabulates them.
|
|
3
|
+
*
|
|
4
|
+
* `banner` is the one that reaches no compiled document: it is the page's hero
|
|
5
|
+
* image, read by the site and by the book's section plates and by nothing else.
|
|
6
|
+
* That is what `document: false` states, and it is why the inert-art check
|
|
7
|
+
* skips it — a key that is *meant* to reach no document is not an inert key.
|
|
8
|
+
*
|
|
9
|
+
* @type {readonly ArtSlot[]}
|
|
10
|
+
*/
|
|
11
|
+
export const ART_SLOTS: readonly ArtSlot[];
|
|
12
|
+
/**
|
|
13
|
+
* One art slot: the key a note authors, and the type a bare value takes.
|
|
14
|
+
*/
|
|
15
|
+
export type ArtSlot = {
|
|
16
|
+
/**
|
|
17
|
+
* - The key under `data:`.
|
|
18
|
+
*/
|
|
19
|
+
key: string;
|
|
20
|
+
/**
|
|
21
|
+
* - The asset type a bare shortcode defaults to.
|
|
22
|
+
*/
|
|
23
|
+
type: string;
|
|
24
|
+
/**
|
|
25
|
+
* - The types an authored value may
|
|
26
|
+
* name. **Not the default** — a faith tradition's profile art is a full
|
|
27
|
+
* illustration rather than a game icon, so an `icon` slot's default stays
|
|
28
|
+
* `icon` while its accepted set also takes `image`. A value naming a type
|
|
29
|
+
* outside it is an error, against this set rather than against `type`.
|
|
30
|
+
*/
|
|
31
|
+
accepts: readonly string[];
|
|
32
|
+
/**
|
|
33
|
+
* - Whether the slot reaches a compiled document.
|
|
34
|
+
*/
|
|
35
|
+
document: boolean;
|
|
36
|
+
/**
|
|
37
|
+
* - One line, for the author-facing reference.
|
|
38
|
+
*/
|
|
39
|
+
describe: string;
|
|
40
|
+
};
|
|
@@ -403,15 +403,37 @@ export class BasePackCompiler {
|
|
|
403
403
|
*
|
|
404
404
|
* An address nothing answers is reported against the note and treated as
|
|
405
405
|
* unnamed, so the document takes its default rather than shipping a path
|
|
406
|
-
* that installs nowhere.
|
|
406
|
+
* that installs nowhere. **A type this slot does not accept is different**:
|
|
407
|
+
* the address is real, so no default repairs it, and it is an error rather
|
|
408
|
+
* than a fallback.
|
|
407
409
|
*
|
|
408
410
|
* @param {unknown} value - The value as authored.
|
|
409
411
|
* @param {string} key - The key it was authored at, for the message.
|
|
410
412
|
* @param {string} type - The asset type a bare value takes.
|
|
413
|
+
* @param {Iterable<string>} [accepts] - The types this slot accepts.
|
|
411
414
|
* @returns {string|null} The Foundry-relative path, `""` for a deliberate
|
|
412
415
|
* blank, or `null` where the note names none.
|
|
413
416
|
*/
|
|
414
|
-
artPathOf(value: unknown, key: string, type: string): string | null;
|
|
417
|
+
artPathOf(value: unknown, key: string, type: string, accepts?: Iterable<string>): string | null;
|
|
418
|
+
/**
|
|
419
|
+
* Reduce every Address an assembled `system` block holds to the Shortcode
|
|
420
|
+
* the document reads.
|
|
421
|
+
*
|
|
422
|
+
* The positions come from the type's own field declarations, so a system
|
|
423
|
+
* adding a field that names a note gets the reduction by declaring it; see
|
|
424
|
+
* {@link module:engine/address-fields}.
|
|
425
|
+
*
|
|
426
|
+
* Called **after** the builder and the authored merge have both written, so
|
|
427
|
+
* one step reduces a value the field read and a value authored straight at
|
|
428
|
+
* the destination alike. Each finding is located at the value inside the
|
|
429
|
+
* note, which for a list or a map is the entry that is wrong rather than the
|
|
430
|
+
* key that introduces it.
|
|
431
|
+
*
|
|
432
|
+
* @param {object} emitted - The `system` block, assembled.
|
|
433
|
+
* @param {readonly object[]} [fields] - The type's field declaration.
|
|
434
|
+
* @returns {number} How many findings were reported.
|
|
435
|
+
*/
|
|
436
|
+
reduceEmittedAddresses(emitted: object, fields?: readonly object[]): number;
|
|
415
437
|
/**
|
|
416
438
|
* The Foundry path one art slot of a note names, or `null`.
|
|
417
439
|
*
|
|
@@ -18,8 +18,8 @@ export function stripAdventureKeys(value: any): any;
|
|
|
18
18
|
/**
|
|
19
19
|
* An authored address with any wikilink brackets and label stripped.
|
|
20
20
|
*
|
|
21
|
-
* The specification types `contents` as
|
|
22
|
-
*
|
|
21
|
+
* The specification types `contents` as an `Address[]`, written bare — but
|
|
22
|
+
* `[[address]]` is what an author reaches
|
|
23
23
|
* for, and it is what a folder note's `parent` already accepts. Accepting both
|
|
24
24
|
* costs one regex and removes a failure whose message would have to explain the
|
|
25
25
|
* difference.
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* The single path segment a note is addressed by: `type-shortcode`.
|
|
3
3
|
*
|
|
4
4
|
* Lowercased and hyphen-joined by the same rule as the note's canonical key
|
|
5
|
-
* ({@link canonicalKey}
|
|
5
|
+
* ({@link canonicalKey} lowercases too), so it is that
|
|
6
6
|
* key's **last two segments** — which is what makes a manifest entry's `path`
|
|
7
7
|
* derivable from the key it is filed under rather than transported beside it.
|
|
8
8
|
*
|
|
@@ -64,55 +64,6 @@ export function addressSlug(fm: object): string;
|
|
|
64
64
|
* dead entry in the manifest.
|
|
65
65
|
*/
|
|
66
66
|
export function packageAddress(fm: object): string;
|
|
67
|
-
/**
|
|
68
|
-
* The **canonical** address of a note: fully qualified, one spelling per
|
|
69
|
-
* document, and globally unique.
|
|
70
|
-
*
|
|
71
|
-
* The written form of a link is a **partial** address: it may omit leading
|
|
72
|
-
* segments, and each one it omits is filled in by rule rather than left
|
|
73
|
-
* unconstrained. An omitted package (`[[skill-lang]]`) defaults to the citing
|
|
74
|
-
* note's own, so an unqualified link resolves locally and only locally, and a
|
|
75
|
-
* link into another package must name it. An omitted **system** is a
|
|
76
|
-
* *wildcard*, not a default — most links target items, which belong to a
|
|
77
|
-
* system — and the resolver requires exactly one match: none is a dead link,
|
|
78
|
-
* more than one is an ambiguity reported with every candidate named.
|
|
79
|
-
* Everything internal — index keys, cache keys, every lookup — uses this fully
|
|
80
|
-
* qualified form instead, so no consumer has to know what a short form
|
|
81
|
-
* defaulted to or matched.
|
|
82
|
-
*
|
|
83
|
-
* Global uniqueness is what lets a dependency's index merge straight into a
|
|
84
|
-
* local one: the keys cannot collide by accident, so a key already present on
|
|
85
|
-
* merge is a real conflict rather than an artefact of two packages sharing a
|
|
86
|
-
* namespace. `(type, shortcode)` alone is unique only *within* a package, and
|
|
87
|
-
* two independently authored packages reaching for the same short string is a
|
|
88
|
-
* matter of time.
|
|
89
|
-
*
|
|
90
|
-
* **The system segment.** A package may ship content for more than one
|
|
91
|
-
* system, and one note then compiles into a document per system — an actor in
|
|
92
|
-
* `actors-sohl` *and* an actor in `actors-hm3`. Without a system segment both
|
|
93
|
-
* land on one key, so the address cannot name either of them. `harn-ensemble`
|
|
94
|
-
* carries 2,497 such notes.
|
|
95
|
-
*
|
|
96
|
-
* The value is a system id, or the literal **`none`** for a document no game
|
|
97
|
-
* system defines: a journal, a macro, a scene, and an item's documentation
|
|
98
|
-
* journal — which is `none` however many systems the item itself declares,
|
|
99
|
-
* because it is one journal.
|
|
100
|
-
*
|
|
101
|
-
* `none` rather than `any`: every segment of an address is an exact literal,
|
|
102
|
-
* and `any` reads as a wildcard — "matches under any system" — which is not
|
|
103
|
-
* what it does. A resolver written to that misreading would fail silently,
|
|
104
|
-
* since a lookup miss already returns nothing rather than erroring. And not
|
|
105
|
-
* `null` or `~`, both of which are YAML nulls that parse to an absent value and
|
|
106
|
-
* drop the segment entirely.
|
|
107
|
-
*
|
|
108
|
-
* @param {string} pkg - The owning **content** package (`sohl`, `thalorna`) —
|
|
109
|
-
* not the Foundry package, which varies per compilation target.
|
|
110
|
-
* @param {string} system - The system whose document this addresses, or `none`.
|
|
111
|
-
* @param {string} type - The note's `type`.
|
|
112
|
-
* @param {string} shortcode - The note's `shortcode`.
|
|
113
|
-
* @returns {string} `package-system-type-shortcode`, lowercased.
|
|
114
|
-
*/
|
|
115
|
-
export function canonicalKey(pkg: string, system: string, type: string, shortcode: string): string;
|
|
116
67
|
/**
|
|
117
68
|
* Which system a frontmatter key path is written under.
|
|
118
69
|
*
|
|
@@ -122,7 +73,7 @@ export function canonicalKey(pkg: string, system: string, type: string, shortcod
|
|
|
122
73
|
* top-level frontmatter, the shared `data:` container, and body prose, which has
|
|
123
74
|
* no key path at all and passes `undefined`.
|
|
124
75
|
*
|
|
125
|
-
* It is the block rather than the field, so
|
|
76
|
+
* It is the block rather than the field, so an `Address` field needs no opinion
|
|
126
77
|
* about systems and no per-field table has to be kept in step with the schema.
|
|
127
78
|
*
|
|
128
79
|
* The first segment must **be** a declared system, not merely look like one:
|
|
@@ -134,83 +85,6 @@ export function canonicalKey(pkg: string, system: string, type: string, shortcod
|
|
|
134
85
|
* @returns {string} The system id, or `none`.
|
|
135
86
|
*/
|
|
136
87
|
export function blockSystem(keyPath?: string): string;
|
|
137
|
-
/**
|
|
138
|
-
* Expand a written address to the one canonical address it names.
|
|
139
|
-
*
|
|
140
|
-
* **An omitted segment defaults from where the link is written** — it is
|
|
141
|
-
* not a wildcard, and resolution is not a search. Package omitted means the
|
|
142
|
-
* citing note's own; system omitted means {@link blockSystem} of the key path it
|
|
143
|
-
* was written under. So every short form has exactly one expansion, computed
|
|
144
|
-
* before anything is looked up, and there is no candidate set to disambiguate.
|
|
145
|
-
*
|
|
146
|
-
* **Under `none`, a system-bearing type addresses its documentation journal.**
|
|
147
|
-
* A note's `none` address *is* its `doc<type>` entry — the Item is the one with
|
|
148
|
-
* a system — so a prose `[[affiliation-sirvadar|…]]` names the page, which is
|
|
149
|
-
* almost always what prose means. A link that means the Item states the system
|
|
150
|
-
* and gets it. This is the defaulting rule applied, not an exception carved out
|
|
151
|
-
* of it.
|
|
152
|
-
*
|
|
153
|
-
* **Only a type whose own document carries a system is redirected.** A `macro`
|
|
154
|
-
* and the map types have documentation journals too, but their own documents
|
|
155
|
-
* are core ones and already live at `none` — so `<pkg>-none-macro-x` names the
|
|
156
|
-
* Macro and `<pkg>-none-docmacro-x` its journal, two live addresses that the
|
|
157
|
-
* redirect would collapse into one. The test is the note type's own system,
|
|
158
|
-
* not merely whether it has a doc entry.
|
|
159
|
-
*
|
|
160
|
-
* A `doc<type>` written explicitly is `none` **wherever** it appears, even
|
|
161
|
-
* inside a system block: no game system defines a JournalEntry, so there is no
|
|
162
|
-
* other system for one to belong to.
|
|
163
|
-
*
|
|
164
|
-
* @param {{type: string, shortcode: string, package?: string, system?: string,
|
|
165
|
-
* itemDoc?: boolean}} read - A qualifier, as `readQualifier` returns one.
|
|
166
|
-
* @param {{package: string, system?: string}} where - The citing context: the
|
|
167
|
-
* tree's own content package, and the system of the block the link sits in.
|
|
168
|
-
* @returns {string} The canonical `package-system-type-shortcode`.
|
|
169
|
-
*/
|
|
170
|
-
export function expandAddress(read: {
|
|
171
|
-
type: string;
|
|
172
|
-
shortcode: string;
|
|
173
|
-
package?: string;
|
|
174
|
-
system?: string;
|
|
175
|
-
itemDoc?: boolean;
|
|
176
|
-
}, where: {
|
|
177
|
-
package: string;
|
|
178
|
-
system?: string;
|
|
179
|
-
}): string;
|
|
180
|
-
/**
|
|
181
|
-
* Reads a canonical key back into its parts.
|
|
182
|
-
*
|
|
183
|
-
* Parsing is plain positional counting: split on the separator, require
|
|
184
|
-
* {@link CANONICAL_KEY_SEGMENTS} of them, and assign each position its field.
|
|
185
|
-
* **The charset rule is what makes that sound** — every segment matches
|
|
186
|
-
* `ADDRESS_SEGMENT_PATTERN` (`engine/address-charset.mjs`), so the hyphen is
|
|
187
|
-
* purely a separator and the count alone determines every field. That is
|
|
188
|
-
* enforced at each of the three sources rather than assumed of
|
|
189
|
-
* the data: shortcodes by `content-lint.mjs`, `contentPackage` by
|
|
190
|
-
* `defineConfig`, and types are bare words. Were any of them free to
|
|
191
|
-
* carry a hyphen, no amount of counting would recover the fields and the reader
|
|
192
|
-
* would need a vocabulary to match against instead.
|
|
193
|
-
*
|
|
194
|
-
* **Nothing to read and nothing readable are different answers.** A key that
|
|
195
|
-
* cannot be canonical — `harn-adventures-sohl-skill-melee`, five segments,
|
|
196
|
-
* because the package name carries the separator — yields `null`, while an
|
|
197
|
-
* absent or blank input yields `undefined`. Both are falsy, so
|
|
198
|
-
* every call site (all of which test the result for truthiness) is unaffected;
|
|
199
|
-
* the distinction is there so a caller reporting "this key is unreadable" can
|
|
200
|
-
* tell that it has a key to report about.
|
|
201
|
-
*
|
|
202
|
-
* @param {unknown} key - A canonical key, or nothing.
|
|
203
|
-
* @returns {{package: string, system: string, type: string, shortcode: string}
|
|
204
|
-
* |null|undefined}
|
|
205
|
-
* The parts; `null` when there is a string that is not in canonical form;
|
|
206
|
-
* `undefined` when there is no key at all.
|
|
207
|
-
*/
|
|
208
|
-
export function readCanonicalKey(key: unknown): {
|
|
209
|
-
package: string;
|
|
210
|
-
system: string;
|
|
211
|
-
type: string;
|
|
212
|
-
shortcode: string;
|
|
213
|
-
} | null | undefined;
|
|
214
88
|
/**
|
|
215
89
|
* Asserts a base is usable as a prefix and returns it.
|
|
216
90
|
*
|
|
@@ -280,17 +154,6 @@ export function resolvePackageUrl(rel: string, base: string): string;
|
|
|
280
154
|
export function documentId(pkg: string, system: string, type: string, shortcode: string): string;
|
|
281
155
|
/** The knowledgebase's mount within this package's site. */
|
|
282
156
|
export const KB_PREFIX: "kb/";
|
|
283
|
-
/**
|
|
284
|
-
* How many segments a canonical key has, and therefore how many the reader
|
|
285
|
-
* below counts.
|
|
286
|
-
*
|
|
287
|
-
* Named rather than written as a literal because it is the *grammar*, not an
|
|
288
|
-
* implementation detail of one function: it is the number a change to the
|
|
289
|
-
* address form would move, and the thing a reader of that change has to find.
|
|
290
|
-
*
|
|
291
|
-
* @type {number}
|
|
292
|
-
*/
|
|
293
|
-
export const CANONICAL_KEY_SEGMENTS: number;
|
|
294
157
|
/**
|
|
295
158
|
* Where this build serves each package, keyed by package name.
|
|
296
159
|
*
|
|
@@ -324,3 +187,4 @@ export const DOCUMENT_ID_NAMESPACE: string;
|
|
|
324
187
|
import { DEFAULT_ADDRESS_SCHEME } from "../content-config.mjs";
|
|
325
188
|
import { NO_SYSTEM } from "./systems.mjs";
|
|
326
189
|
export { DEFAULT_ADDRESS_SCHEME, NO_SYSTEM };
|
|
190
|
+
export { canonicalKey, expandAddress, readCanonicalKey, CANONICAL_KEY_SEGMENTS } from "./address.mjs";
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
* @param {string} body - The note's markdown, without its frontmatter.
|
|
25
25
|
* @returns {ParsedEmbed[]} One entry per embed.
|
|
26
26
|
*/
|
|
27
|
-
export function embedsIn(body: string): ParsedEmbed[];
|
|
27
|
+
export function embedsIn(body: string, context: any): ParsedEmbed[];
|
|
28
28
|
/**
|
|
29
29
|
* What one embed resolves to, or why it does not.
|
|
30
30
|
*
|
|
@@ -105,6 +105,31 @@ export function measureCorpus(notes: Iterable<object>, format: import("./content
|
|
|
105
105
|
* @returns {string} The message.
|
|
106
106
|
*/
|
|
107
107
|
export function fieldDriftMessage({ noteType, source, target, name, to }: object): string;
|
|
108
|
+
/**
|
|
109
|
+
* The declaration that compiles one mapping row, and what is left over.
|
|
110
|
+
*
|
|
111
|
+
* The longest declared name the row's source sits under: a field declared
|
|
112
|
+
* `impact.die` claims `data.impact.die` ahead of any field declared `impact`,
|
|
113
|
+
* and a field declared `charges` claims `data.charges.max` with `.max` left
|
|
114
|
+
* over. Compared on the normalized spelling, so a field naming its source under
|
|
115
|
+
* `data:` matches at the same length a bare one does.
|
|
116
|
+
*
|
|
117
|
+
* Exported because two readers ask it and must agree: {@link
|
|
118
|
+
* checkDeclaredFields}, which holds the row's target to the declaration's, and
|
|
119
|
+
* the guard that holds the declaration's *source* to the row's — a second copy
|
|
120
|
+
* of the match would let the two disagree about which declaration a row is even
|
|
121
|
+
* about.
|
|
122
|
+
*
|
|
123
|
+
* @param {string} source - The row's shared source, as either side spells it.
|
|
124
|
+
* @param {readonly object[]} fields - The type's authored declarations.
|
|
125
|
+
* @returns {{field: object, rest: string}|undefined} The declaration and the
|
|
126
|
+
* remainder of the source beneath its name, or nothing where no declaration
|
|
127
|
+
* names it.
|
|
128
|
+
*/
|
|
129
|
+
export function declarationFor(source: string, fields: readonly object[]): {
|
|
130
|
+
field: object;
|
|
131
|
+
rest: string;
|
|
132
|
+
} | undefined;
|
|
108
133
|
/**
|
|
109
134
|
* Check the specification's per-type tables against the field declarations that
|
|
110
135
|
* compile them.
|
|
@@ -112,7 +112,7 @@ export function asciiAliases(aliases: unknown): Array<string>;
|
|
|
112
112
|
* otherwise be overwritten without a word. `file` and, where the file was
|
|
113
113
|
* read, `position` ride on the error.
|
|
114
114
|
*/
|
|
115
|
-
export function buildIndexRecord({ frontmatter, relPath, absPath, contentPackage, body, bodyLine, manifest, }: {
|
|
115
|
+
export function buildIndexRecord({ frontmatter, relPath, absPath, contentPackage, body, bodyLine, manifest, addressContext, }: {
|
|
116
116
|
frontmatter: Record<string, any>;
|
|
117
117
|
relPath: string;
|
|
118
118
|
absPath?: string | undefined;
|
|
@@ -152,7 +152,7 @@ export function buildIndexRecord({ frontmatter, relPath, absPath, contentPackage
|
|
|
152
152
|
* @returns {Array<Record<string, any>>} The records, in a total order that does
|
|
153
153
|
* not depend on directory-read order.
|
|
154
154
|
*/
|
|
155
|
-
export function collectContentIndex(contentBase: string, { contentPackage, skipDirectories, assetsBase, manifest, problems }: {
|
|
155
|
+
export function collectContentIndex(contentBase: string, { contentPackage, skipDirectories, assetsBase, manifest, problems, addressContext }: {
|
|
156
156
|
contentPackage: string;
|
|
157
157
|
skipDirectories: readonly string[];
|
|
158
158
|
assetsBase?: string | undefined;
|
|
@@ -131,11 +131,12 @@ export function auditHomepageLinks(index: ReturnType<typeof buildLinkIndex>): Ar
|
|
|
131
131
|
* unlabelledLinks: object[], frontmatterLinks: object[],
|
|
132
132
|
* homepageLinks: object[], usedManifest: Set<string>}} The findings, and
|
|
133
133
|
* which addresses a foreign manifest answered. Each `deadAddresses` entry
|
|
134
|
-
* carries a `reason` from {@link LINK_FINDING_REASONS} —
|
|
135
|
-
* `"
|
|
136
|
-
* `
|
|
137
|
-
*
|
|
138
|
-
*
|
|
134
|
+
* carries a `reason` from {@link LINK_FINDING_REASONS} — `"not-an-address"`,
|
|
135
|
+
* `"unknown-type"`, `"no-content-index"`, `"stub"` (with the stub's path) or
|
|
136
|
+
* `"unresolved"`, never `"ambiguous"`: every target here expands to one
|
|
137
|
+
* canonical address before anything is looked up, so there is no candidate
|
|
138
|
+
* set for two packages to claim — and every reason is an **error**: the
|
|
139
|
+
* three resolvers agree on severity for every class.
|
|
139
140
|
*/
|
|
140
141
|
export function auditLinks(index: ReturnType<typeof buildLinkIndex>): {
|
|
141
142
|
deadAnchors: object[];
|
|
@@ -267,10 +267,27 @@ export function runtimeOnlyFields(fields: readonly FieldSpec[]): FieldSpec[];
|
|
|
267
267
|
* several do — `weight` is coerced leniently but is still a number, and
|
|
268
268
|
* `weight: heavy` is an authoring mistake worth reporting where it was made.
|
|
269
269
|
* Absent means the lint makes no claim about the value.
|
|
270
|
-
* @property {string} [
|
|
271
|
-
* for the linter's dead-reference check.
|
|
272
|
-
*
|
|
273
|
-
*
|
|
270
|
+
* @property {string} [code] - The content type a value names by **Shortcode**,
|
|
271
|
+
* for the linter's dead-reference check. The authored value is one segment —
|
|
272
|
+
* held to {@link module:engine/address-charset.ADDRESS_SEGMENT_PATTERN} —
|
|
273
|
+
* persisted verbatim into the emitted field and resolved at runtime among
|
|
274
|
+
* the items embedded on one actor, where packages do not exist; the linter
|
|
275
|
+
* still forms the `type-shortcode` pair to check it against the index. Only
|
|
276
|
+
* for references to a **note**: `bodyLocationCode` names a part inside a
|
|
277
|
+
* being's own body structure, not a note, so it declares none. Distinct
|
|
278
|
+
* from a `DataFieldSpec`'s `ref` ({@link module:engine/note-vocabulary}),
|
|
279
|
+
* which names an **Address** instead.
|
|
280
|
+
* @property {import("./address-fields.mjs").AddressPosition} [address] - The
|
|
281
|
+
* authored value is an Address. The emitted value is its Shortcode, or the
|
|
282
|
+
* exact published Item UUID when `emit: "uuid"` is declared. The
|
|
283
|
+
* statement carries the default type an omitted `<type>` segment takes, the
|
|
284
|
+
* set of types the position accepts — two separate things, which coincide at
|
|
285
|
+
* most positions — and where the Addresses sit: the value itself, every
|
|
286
|
+
* element of a list, or every key of a map.
|
|
287
|
+
* {@link module:engine/address-fields.reduceAddressFields} performs the
|
|
288
|
+
* reduction once the block is assembled, so a value authored at the
|
|
289
|
+
* destination is reduced along with one this field read. Distinct from `code`,
|
|
290
|
+
* which is a Shortcode at both ends and resolves nothing.
|
|
274
291
|
* @property {any|((fm: object) => any)} [value] - For a field with no `name`:
|
|
275
292
|
* the constant, or a function deriving it from the frontmatter.
|
|
276
293
|
* @property {boolean} [omitWhenAbsent] - **The key is left out entirely when
|
|
@@ -524,12 +541,32 @@ export type FieldSpec = {
|
|
|
524
541
|
*/
|
|
525
542
|
kind?: "string" | "number" | "boolean" | "map" | "list" | undefined;
|
|
526
543
|
/**
|
|
527
|
-
* - The content type a value
|
|
528
|
-
* for the linter's dead-reference check.
|
|
529
|
-
*
|
|
530
|
-
*
|
|
544
|
+
* - The content type a value names by **Shortcode**,
|
|
545
|
+
* for the linter's dead-reference check. The authored value is one segment —
|
|
546
|
+
* held to {@link module :engine/address-charset.ADDRESS_SEGMENT_PATTERN} —
|
|
547
|
+
* persisted verbatim into the emitted field and resolved at runtime among
|
|
548
|
+
* the items embedded on one actor, where packages do not exist; the linter
|
|
549
|
+
* still forms the `type-shortcode` pair to check it against the index. Only
|
|
550
|
+
* for references to a **note**: `bodyLocationCode` names a part inside a
|
|
551
|
+
* being's own body structure, not a note, so it declares none. Distinct
|
|
552
|
+
* from a `DataFieldSpec`'s `ref` ({@link module :engine/note-vocabulary}),
|
|
553
|
+
* which names an **Address** instead.
|
|
531
554
|
*/
|
|
532
|
-
|
|
555
|
+
code?: string | undefined;
|
|
556
|
+
/**
|
|
557
|
+
* - The
|
|
558
|
+
* authored value is an Address. The emitted value is its Shortcode, or the
|
|
559
|
+
* exact published Item UUID when `emit: "uuid"` is declared. The
|
|
560
|
+
* statement carries the default type an omitted `<type>` segment takes, the
|
|
561
|
+
* set of types the position accepts — two separate things, which coincide at
|
|
562
|
+
* most positions — and where the Addresses sit: the value itself, every
|
|
563
|
+
* element of a list, or every key of a map.
|
|
564
|
+
* {@link module :engine/address-fields.reduceAddressFields} performs the
|
|
565
|
+
* reduction once the block is assembled, so a value authored at the
|
|
566
|
+
* destination is reduced along with one this field read. Distinct from `code`,
|
|
567
|
+
* which is a Shortcode at both ends and resolves nothing.
|
|
568
|
+
*/
|
|
569
|
+
address?: import("./address-fields.mjs").AddressPosition | undefined;
|
|
533
570
|
/**
|
|
534
571
|
* - For a field with no `name`:
|
|
535
572
|
* the constant, or a function deriving it from the frontmatter.
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* An authored `parent` with any wikilink brackets and label stripped.
|
|
3
3
|
*
|
|
4
|
-
* The specification types `parent` as
|
|
5
|
-
*
|
|
4
|
+
* The specification types `parent` as an `Address`, written bare — but
|
|
5
|
+
* `[[address]]` is what an author reaches for,
|
|
6
6
|
* and Obsidian wrote that form for years. Accepting both costs one regex and
|
|
7
7
|
* removes a failure whose message would have to explain the difference.
|
|
8
8
|
*
|
|
@@ -56,14 +56,15 @@ export function collectFolderNotes(notes: Iterable<{
|
|
|
56
56
|
absPath: string;
|
|
57
57
|
}>, pkg: string): FolderNote[];
|
|
58
58
|
/**
|
|
59
|
-
* Index folder notes by
|
|
60
|
-
*
|
|
61
|
-
*
|
|
62
|
-
*
|
|
63
|
-
*
|
|
64
|
-
*
|
|
65
|
-
*
|
|
66
|
-
*
|
|
59
|
+
* Index folder notes by their canonical address, and check the invariants
|
|
60
|
+
* that make the index sound.
|
|
61
|
+
*
|
|
62
|
+
* A folder reference is resolved by {@link parseAddress}, taking `folder` as
|
|
63
|
+
* the type an omitted segment fills in — the same suffix ladder every other
|
|
64
|
+
* position climbs, so `cooking`, `folder-cooking` and the full address all
|
|
65
|
+
* expand to the one key this index holds. A `packFolder` or `parent` field
|
|
66
|
+
* supplies no type of its own; the position's default is what makes a bare
|
|
67
|
+
* shortcode a complete address there.
|
|
67
68
|
*
|
|
68
69
|
* @param {FolderNote[]} folders - From {@link collectFolderNotes}.
|
|
69
70
|
* @returns {{byKey: Map<string, FolderNote>, folders: FolderNote[],
|