@heroiclands/package-build 22.4.6 → 22.5.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 +33 -0
- package/CONTENT.md +192 -1
- package/bin/content-build.mjs +26 -4
- package/content-config.mjs +8 -3
- package/docs/api.md +99 -26
- package/docs/configuration.md +20 -1
- package/docs/content-format.md +238 -0
- package/docs/diagnostics.md +44 -0
- package/engine/content-index.mjs +63 -21
- package/engine/content-links.mjs +60 -6
- package/engine/content-tables.mjs +29 -2
- package/engine/frontmatter-lint.mjs +2 -0
- package/engine/holdings.mjs +416 -0
- package/engine/index-records.mjs +25 -0
- package/engine/index.mjs +16 -0
- package/engine/map-graphviz.mjs +12 -3
- package/engine/map-places.mjs +33 -3
- package/engine/market-class.mjs +178 -0
- package/engine/metadata-index.mjs +7 -4
- package/engine/note-state.mjs +213 -0
- package/engine/note-vocabulary.mjs +59 -2
- package/engine/pdf-build.mjs +4 -2
- package/engine/populations.mjs +557 -0
- package/engine/site-build.mjs +96 -10
- package/engine/site-maps.mjs +165 -0
- package/engine/sql-tables.mjs +157 -4
- package/engine/stub-lint.mjs +237 -0
- package/engine/wikilink-syntax.mjs +27 -1
- package/package.json +1 -1
- package/types/engine/content-index.d.mts +19 -5
- package/types/engine/content-links.d.mts +3 -3
- package/types/engine/content-tables.d.mts +2 -1
- package/types/engine/holdings.d.mts +157 -0
- package/types/engine/index-records.d.mts +22 -0
- package/types/engine/index.d.mts +6 -0
- package/types/engine/map-graphviz.d.mts +8 -1
- package/types/engine/map-places.d.mts +26 -0
- package/types/engine/market-class.d.mts +62 -0
- package/types/engine/note-state.d.mts +110 -0
- package/types/engine/note-vocabulary.d.mts +25 -0
- package/types/engine/populations.d.mts +121 -0
- package/types/engine/site-build.d.mts +37 -6
- package/types/engine/site-maps.d.mts +81 -0
- package/types/engine/stub-lint.d.mts +51 -0
- package/types/engine/wikilink-syntax.d.mts +19 -1
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Whether a value is one of the classes the scale states.
|
|
3
|
+
*
|
|
4
|
+
* A note may write the number quoted, which YAML hands over as a string, so
|
|
5
|
+
* the value is read as a number before it is compared — the same reading
|
|
6
|
+
* `matchesKind` gives a `number` field.
|
|
7
|
+
*
|
|
8
|
+
* @param {unknown} value - The authored value.
|
|
9
|
+
* @returns {boolean} Whether it names a class.
|
|
10
|
+
*/
|
|
11
|
+
export function isMarketClass(value: unknown): boolean;
|
|
12
|
+
/**
|
|
13
|
+
* The class a value names, or `undefined` for a value that names none.
|
|
14
|
+
*
|
|
15
|
+
* @param {unknown} value - The authored value.
|
|
16
|
+
* @returns {MarketClass|undefined} The class.
|
|
17
|
+
*/
|
|
18
|
+
export function marketClass(value: unknown): MarketClass | undefined;
|
|
19
|
+
/**
|
|
20
|
+
* Check a place note's `data.market`.
|
|
21
|
+
*
|
|
22
|
+
* The value is one of the classes the scale states, or it is a finding located
|
|
23
|
+
* at the key's own line. A value that is not a number at all is left alone:
|
|
24
|
+
* the container's own shape check reports it, and saying so twice would make
|
|
25
|
+
* one slip read as two.
|
|
26
|
+
*
|
|
27
|
+
* @param {object} note - The note, as the link index hands it over.
|
|
28
|
+
* @returns {object[]} Findings.
|
|
29
|
+
*/
|
|
30
|
+
export function checkMarket(note: object): object[];
|
|
31
|
+
/**
|
|
32
|
+
* One step of the market scale.
|
|
33
|
+
*
|
|
34
|
+
* @typedef {object} MarketClass
|
|
35
|
+
* @property {number} value - The integer a note writes.
|
|
36
|
+
* @property {string} name - What a settlement of this class is called.
|
|
37
|
+
* @property {string} trade - What can be had there.
|
|
38
|
+
*/
|
|
39
|
+
/**
|
|
40
|
+
* The market scale, in order, from the settlement with no market to the one
|
|
41
|
+
* other markets buy from.
|
|
42
|
+
*
|
|
43
|
+
* @type {readonly MarketClass[]}
|
|
44
|
+
*/
|
|
45
|
+
export const MARKET_CLASSES: readonly MarketClass[];
|
|
46
|
+
/**
|
|
47
|
+
* One step of the market scale.
|
|
48
|
+
*/
|
|
49
|
+
export type MarketClass = {
|
|
50
|
+
/**
|
|
51
|
+
* - The integer a note writes.
|
|
52
|
+
*/
|
|
53
|
+
value: number;
|
|
54
|
+
/**
|
|
55
|
+
* - What a settlement of this class is called.
|
|
56
|
+
*/
|
|
57
|
+
name: string;
|
|
58
|
+
/**
|
|
59
|
+
* - What can be had there.
|
|
60
|
+
*/
|
|
61
|
+
trade: string;
|
|
62
|
+
};
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Whether a note's body is empty.
|
|
3
|
+
*
|
|
4
|
+
* > **The body is empty when everything after the closing frontmatter fence is
|
|
5
|
+
* > whitespace.** Nothing else is empty.
|
|
6
|
+
*
|
|
7
|
+
* The severity is the point: **the test must be one a person can apply by
|
|
8
|
+
* looking at the file.** Every softer rule makes two files that look different
|
|
9
|
+
* behave the same, and turns "why did my page disappear?" into an
|
|
10
|
+
* investigation. So an HTML comment, a horizontal rule, a lone heading and
|
|
11
|
+
* `_To be written._` are all bodies — each renders to little or nothing, and
|
|
12
|
+
* {@link module:engine/stub-lint} reports them rather than treating them as
|
|
13
|
+
* either state.
|
|
14
|
+
*
|
|
15
|
+
* An HTML comment is deliberately not empty. An invisible marker deciding
|
|
16
|
+
* whether a page exists is the failure the retired `draft:` field was removed
|
|
17
|
+
* for, and reintroducing it in comment form would be worse, because nothing
|
|
18
|
+
* would report it.
|
|
19
|
+
*
|
|
20
|
+
* @param {string|null|undefined} body - The note body, frontmatter stripped.
|
|
21
|
+
* @returns {boolean} Whether the body is empty.
|
|
22
|
+
*/
|
|
23
|
+
export function isEmptyBody(body: string | null | undefined): boolean;
|
|
24
|
+
/**
|
|
25
|
+
* Whether an empty body makes a note of this type a stub.
|
|
26
|
+
*
|
|
27
|
+
* Two types are **structural** and exempt, and the measurement forced the
|
|
28
|
+
* question: a `folder` note's page is a generated section index and the
|
|
29
|
+
* `homepage`'s is the site's front door, so both have no body by design. If an
|
|
30
|
+
* empty body meant "no page" for them, a tree would lose a page per folder plus
|
|
31
|
+
* its own front door, and the build would report nothing.
|
|
32
|
+
*
|
|
33
|
+
* Read from {@link module:engine/note-vocabulary.NOTE_VOCABULARY}, where every
|
|
34
|
+
* type declares `stubbable` — so a type added without answering the question
|
|
35
|
+
* fails the guard rather than being classified by a default nobody chose. A
|
|
36
|
+
* type the registry does not declare at all is stubbable: the rule is about the
|
|
37
|
+
* body, and an unknown type has no exemption to claim.
|
|
38
|
+
*
|
|
39
|
+
* @param {string|null|undefined} type - The note's `type`.
|
|
40
|
+
* @param {Readonly<Record<string, {stubbable?: boolean}>>} [vocabulary] - The
|
|
41
|
+
* registry to read.
|
|
42
|
+
* @returns {boolean} Whether an empty body suppresses this type's page.
|
|
43
|
+
*/
|
|
44
|
+
export function isStubbableType(type: string | null | undefined, vocabulary?: Readonly<Record<string, {
|
|
45
|
+
stubbable?: boolean;
|
|
46
|
+
}>>): boolean;
|
|
47
|
+
/**
|
|
48
|
+
* Whether a note is a stub: an empty body on a type an empty body suppresses.
|
|
49
|
+
*
|
|
50
|
+
* The one place the two halves are put together, so the index, the lint and the
|
|
51
|
+
* guard cannot answer it differently.
|
|
52
|
+
*
|
|
53
|
+
* @param {Record<string, any>|null|undefined} frontmatter - Parsed frontmatter.
|
|
54
|
+
* @param {string|null|undefined} body - The note body.
|
|
55
|
+
* @param {Readonly<Record<string, {stubbable?: boolean}>>} [vocabulary] - The
|
|
56
|
+
* registry to read.
|
|
57
|
+
* @returns {boolean} Whether the note is a stub.
|
|
58
|
+
*/
|
|
59
|
+
export function isStubNote(frontmatter: Record<string, any> | null | undefined, body: string | null | undefined, vocabulary?: Readonly<Record<string, {
|
|
60
|
+
stubbable?: boolean;
|
|
61
|
+
}>>): boolean;
|
|
62
|
+
/**
|
|
63
|
+
* The placeholder phrases a body reduces to, or an empty list.
|
|
64
|
+
*
|
|
65
|
+
* A body is reduced by dropping every line that carries no content of its own —
|
|
66
|
+
* a heading, a thematic break, an HTML comment, a blank line — and then asking
|
|
67
|
+
* whether every line that is left is a placeholder. A body with some real prose
|
|
68
|
+
* beside its unwritten sections is **not** reduced: a `TBD` under a heading in
|
|
69
|
+
* a note that is honestly a draft is not a defect, and this fires only when a
|
|
70
|
+
* body reduces to nothing but such phrases.
|
|
71
|
+
*
|
|
72
|
+
* The phrases are returned with the 1-based offset of the line each sat on
|
|
73
|
+
* within the body, so a finding can be located rather than merely stated.
|
|
74
|
+
*
|
|
75
|
+
* @param {string|null|undefined} body - The note body.
|
|
76
|
+
* @returns {Array<{phrase: string, line: number}>} The phrases, in body order.
|
|
77
|
+
* Empty for a body that is empty, or that carries prose of its own.
|
|
78
|
+
*/
|
|
79
|
+
export function placeholderBody(body: string | null | undefined): Array<{
|
|
80
|
+
phrase: string;
|
|
81
|
+
line: number;
|
|
82
|
+
}>;
|
|
83
|
+
/**
|
|
84
|
+
* How many words a body holds, counting only the prose a reader meets.
|
|
85
|
+
*
|
|
86
|
+
* Fence blocks, headings, thematic breaks and HTML comments are dropped, and a
|
|
87
|
+
* wikilink counts as the one word it renders — `[[affiliation-meivor|Mëivōr]]`
|
|
88
|
+
* is a word, not three. What is left is split on whitespace.
|
|
89
|
+
*
|
|
90
|
+
* @param {string|null|undefined} body - The note body.
|
|
91
|
+
* @returns {number} The word count.
|
|
92
|
+
*/
|
|
93
|
+
export function bodyWordCount(body: string | null | undefined): number;
|
|
94
|
+
/**
|
|
95
|
+
* The three states, in the order they read as a ladder.
|
|
96
|
+
*
|
|
97
|
+
* @type {ReadonlyArray<string>}
|
|
98
|
+
*/
|
|
99
|
+
export const NOTE_STATES: ReadonlyArray<string>;
|
|
100
|
+
/**
|
|
101
|
+
* The phrases that stand in for prose nobody has written.
|
|
102
|
+
*
|
|
103
|
+
* A body consisting of nothing but these is an abandoned draft, not a stub:
|
|
104
|
+
* it looks written, publishes a page, and says nothing. The list is matched
|
|
105
|
+
* whole-line, case-insensitively, after trailing punctuation is dropped — a
|
|
106
|
+
* line reading `TBD.` and one reading `tbd` are the same line.
|
|
107
|
+
*
|
|
108
|
+
* @type {ReadonlyArray<string>}
|
|
109
|
+
*/
|
|
110
|
+
export const PLACEHOLDER_PHRASES: ReadonlyArray<string>;
|
|
@@ -333,6 +333,16 @@ export type DataFieldSpec = {
|
|
|
333
333
|
* - **`null`** — the type has a `subType` whose values the specification does
|
|
334
334
|
* not yet enumerate. Presence is permitted and the value is unchecked.
|
|
335
335
|
* - **a list** — the closed set of values, and anything else is a finding.
|
|
336
|
+
*
|
|
337
|
+
* `stubbable` says whether an empty body suppresses this type's page. Every
|
|
338
|
+
* type declares it, so adding one means answering the question rather than
|
|
339
|
+
* inheriting a default nobody chose — and a guard derives the check from this
|
|
340
|
+
* registry's own key list, so an omission is a red test.
|
|
341
|
+
*
|
|
342
|
+
* Two types answer **false**, and both are structural: a `folder` note's page
|
|
343
|
+
* is a generated section index and a `homepage`'s is the site's front door, so
|
|
344
|
+
* each is complete with no body at all. An empty body on one of them keeps its
|
|
345
|
+
* address and resolves as `full`.
|
|
336
346
|
*/
|
|
337
347
|
export type TypeVocabulary = {
|
|
338
348
|
/**
|
|
@@ -344,4 +354,19 @@ export type TypeVocabulary = {
|
|
|
344
354
|
* values, as above.
|
|
345
355
|
*/
|
|
346
356
|
subTypes?: readonly string[] | null | undefined;
|
|
357
|
+
/**
|
|
358
|
+
* - Whether an empty body makes a note of this
|
|
359
|
+
* type a stub — see {@link module :engine/note-state.isStubbableType}.
|
|
360
|
+
*/
|
|
361
|
+
stubbable: boolean;
|
|
362
|
+
/**
|
|
363
|
+
* - A
|
|
364
|
+
* check of the whole note, run by the frontmatter lint beside the field
|
|
365
|
+
* checks with the same index. For a rule that is about the note rather than
|
|
366
|
+
* one of its fields — a place's tenure is a fact about every affiliation's
|
|
367
|
+
* `domains`, and is located at the note's `type:` line.
|
|
368
|
+
*/
|
|
369
|
+
check?: ((note: object, opts: {
|
|
370
|
+
index?: object;
|
|
371
|
+
}) => object[]) | undefined;
|
|
347
372
|
};
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Check a place's population against the corpus around it.
|
|
3
|
+
*
|
|
4
|
+
* Declared on the `place` vocabulary beside `data.population`, so the lint
|
|
5
|
+
* runs it with the index every reference check resolves through. Without an
|
|
6
|
+
* index the questions cannot be asked and nothing is reported.
|
|
7
|
+
*
|
|
8
|
+
* Three rules, each reported on the note being linted: `over-held land` and
|
|
9
|
+
* `over-full region` on the place whose figure is exceeded, and
|
|
10
|
+
* `oversized settlement` on the settlement that exceeds one.
|
|
11
|
+
*
|
|
12
|
+
* @param {object} note - The note, as the link index hands it over.
|
|
13
|
+
* @param {object} [opts]
|
|
14
|
+
* @param {object} [opts.index] - The link index, holding the whole corpus.
|
|
15
|
+
* @returns {object[]} Findings.
|
|
16
|
+
*/
|
|
17
|
+
export function checkPopulation(note: object, { index }?: {
|
|
18
|
+
index?: object | undefined;
|
|
19
|
+
}): object[];
|
|
20
|
+
/**
|
|
21
|
+
* Check the populations a `doc` note cites against the notes they belong to.
|
|
22
|
+
*
|
|
23
|
+
* A reference page restates figures the place and affiliation notes own, and
|
|
24
|
+
* restated figures drift. Each is located at the figure it wrote, counting
|
|
25
|
+
* repeats so two rows carrying one number land on their own lines.
|
|
26
|
+
*
|
|
27
|
+
* A citation naming a note this package does not hold is skipped: a fetched
|
|
28
|
+
* index carries no figure, so there is nothing to disagree with.
|
|
29
|
+
*
|
|
30
|
+
* Declared as the `doc` vocabulary's type-level check.
|
|
31
|
+
*
|
|
32
|
+
* @param {object} note - The note, as the link index hands it over.
|
|
33
|
+
* @param {object} [opts]
|
|
34
|
+
* @param {object} [opts.index] - The link index, holding the whole corpus.
|
|
35
|
+
* @returns {object[]} Findings.
|
|
36
|
+
*/
|
|
37
|
+
export function checkCitedPopulations(note: object, { index }?: {
|
|
38
|
+
index?: object | undefined;
|
|
39
|
+
}): object[];
|
|
40
|
+
/**
|
|
41
|
+
* How far a parent's figure may fall below the sum of what it contains before
|
|
42
|
+
* that is a finding.
|
|
43
|
+
*
|
|
44
|
+
* A figure written to two significant digits carries a half-unit of error in
|
|
45
|
+
* the second digit, and that error is widest at the bottom of a decade — half
|
|
46
|
+
* a unit on `1.0` is five per cent, against half a unit on `9.9` at a twentieth
|
|
47
|
+
* of that. The widest case is the one a tolerance has to admit, so the whole
|
|
48
|
+
* scale is held to five per cent.
|
|
49
|
+
*
|
|
50
|
+
* @type {number}
|
|
51
|
+
*/
|
|
52
|
+
export const POPULATION_TOLERANCE: number;
|
|
53
|
+
/**
|
|
54
|
+
* The rules this module enforces, each `name` the prefix its findings carry.
|
|
55
|
+
*
|
|
56
|
+
* The list is the module's own statement of what it checks: a reader learns
|
|
57
|
+
* the whole surface from it, and the suite derives a case per entry from it, so
|
|
58
|
+
* a rule cannot be added without evidence that it fires.
|
|
59
|
+
*
|
|
60
|
+
* @type {ReadonlyArray<{name: string, describe: string}>}
|
|
61
|
+
*/
|
|
62
|
+
export const POPULATION_RULES: ReadonlyArray<{
|
|
63
|
+
name: string;
|
|
64
|
+
describe: string;
|
|
65
|
+
}>;
|
|
66
|
+
/**
|
|
67
|
+
* One place or affiliation as the rules read it.
|
|
68
|
+
*/
|
|
69
|
+
export type PopulationNode = {
|
|
70
|
+
/**
|
|
71
|
+
* Lower case.
|
|
72
|
+
*/
|
|
73
|
+
shortcode: string;
|
|
74
|
+
type: "place" | "affiliation";
|
|
75
|
+
subType: string;
|
|
76
|
+
/**
|
|
77
|
+
* What a finding calls it.
|
|
78
|
+
*/
|
|
79
|
+
title: string;
|
|
80
|
+
/**
|
|
81
|
+
* The stated figure, absent where unstated.
|
|
82
|
+
*/
|
|
83
|
+
population?: number | undefined;
|
|
84
|
+
/**
|
|
85
|
+
* A place's enclosing places, an affiliation's
|
|
86
|
+
* superiors.
|
|
87
|
+
*/
|
|
88
|
+
parents: string[];
|
|
89
|
+
/**
|
|
90
|
+
* What an affiliation holds.
|
|
91
|
+
*/
|
|
92
|
+
domains: string[];
|
|
93
|
+
};
|
|
94
|
+
/**
|
|
95
|
+
* The whole tree, indexed the three ways the rules read it.
|
|
96
|
+
*/
|
|
97
|
+
export type Corpus = {
|
|
98
|
+
/**
|
|
99
|
+
* By shortcode.
|
|
100
|
+
*/
|
|
101
|
+
places: Map<string, PopulationNode>;
|
|
102
|
+
/**
|
|
103
|
+
* By shortcode.
|
|
104
|
+
*/
|
|
105
|
+
affiliations: Map<string, PopulationNode>;
|
|
106
|
+
/**
|
|
107
|
+
* Place shortcode → the places
|
|
108
|
+
* whose `parents` name it.
|
|
109
|
+
*/
|
|
110
|
+
childrenOf: Map<string, string[]>;
|
|
111
|
+
/**
|
|
112
|
+
* Place shortcode → the polities
|
|
113
|
+
* whose `domains` name it.
|
|
114
|
+
*/
|
|
115
|
+
holdersOf: Map<string, string[]>;
|
|
116
|
+
/**
|
|
117
|
+
* `type-shortcode` → the node,
|
|
118
|
+
* for the citation rule.
|
|
119
|
+
*/
|
|
120
|
+
byAddress: Map<string, PopulationNode>;
|
|
121
|
+
};
|
|
@@ -176,7 +176,10 @@ export function tableUniverse(pages: object[]): Map<string, object[]>;
|
|
|
176
176
|
* about every other page, known only once the whole tree has resolved — see
|
|
177
177
|
* {@link module:engine/related-pages} — so an authored value is dropped the
|
|
178
178
|
* way `aliases` is, and {@link renderPages} writes the derived block once it
|
|
179
|
-
* holds the graph.
|
|
179
|
+
* holds the graph. `contains`, `held_by` and `holdings` are dropped for the
|
|
180
|
+
* same reason — see {@link module:engine/holdings}. **So is `map`**: the
|
|
181
|
+
* file a place page names is the one the build drew beside it — see
|
|
182
|
+
* {@link module:engine/site-maps} — and a page with no drawing names none.
|
|
180
183
|
*
|
|
181
184
|
* @param {object} page - The page.
|
|
182
185
|
* @param {object} options
|
|
@@ -210,10 +213,21 @@ export function pageFrontmatter(page: object, { decorate, webSrc, artSrc }: {
|
|
|
210
213
|
* `doc` note's `subType` may be spelled the same as another note's `type`, and
|
|
211
214
|
* `doc-gear.md` and `weapongear-gear.md` are distinct.
|
|
212
215
|
*
|
|
216
|
+
* **A page that carries a file beside itself is a leaf bundle**, `<slug>/index.md`,
|
|
217
|
+
* because that is the one shape under which Hugo hands a page its own
|
|
218
|
+
* resources — the map from a place, inlined by the theme through
|
|
219
|
+
* `.Resources.Get`. The directory is the page, not a section: it holds no
|
|
220
|
+
* `_index.md`, and the page's stated `url` keeps its address exactly where the
|
|
221
|
+
* flat file's was.
|
|
222
|
+
*
|
|
213
223
|
* @param {object} page - The page.
|
|
224
|
+
* @param {object} [opts]
|
|
225
|
+
* @param {boolean} [opts.bundle=false] - Whether the page carries a resource.
|
|
214
226
|
* @returns {string} The file, relative to the mount.
|
|
215
227
|
*/
|
|
216
|
-
export function pageDestination(page: object
|
|
228
|
+
export function pageDestination(page: object, { bundle }?: {
|
|
229
|
+
bundle?: boolean | undefined;
|
|
230
|
+
}): string;
|
|
217
231
|
/**
|
|
218
232
|
* Renders and writes every page.
|
|
219
233
|
*
|
|
@@ -236,16 +250,25 @@ export function pageDestination(page: object): string;
|
|
|
236
250
|
* point the resolver answers, and inverted once between them. Nothing is
|
|
237
251
|
* resolved twice.
|
|
238
252
|
*
|
|
253
|
+
* **A map is written with its page.** The drawing from a place is made before
|
|
254
|
+
* this render — see {@link module:engine/site-maps} — and handed in keyed by
|
|
255
|
+
* page URL; the page that owns one is written as a leaf bundle, the file is
|
|
256
|
+
* copied beside it, and the front matter names it as `map`. Every other page
|
|
257
|
+
* is written flat, and carries no such key.
|
|
258
|
+
*
|
|
239
259
|
* @param {object[]} pages - Every page.
|
|
240
260
|
* @param {object} options - Everything the render needs. `homepages` is the
|
|
241
261
|
* homepage as the index knows it — `{ fm, body, url, title }` — which takes
|
|
242
262
|
* no part in the render and every part in the graph: a content page links
|
|
243
|
-
* it by wikilink, and its own markdown links name content pages.
|
|
263
|
+
* it by wikilink, and its own markdown links name content pages. `maps` is
|
|
264
|
+
* each drawing by the URL of the page that carries it.
|
|
244
265
|
* @returns {{written: number, byKind: Record<string, number>, tableErrors: object[],
|
|
245
266
|
* wikiErrors: object[], imageErrors: object[],
|
|
246
|
-
* related: Map<string, import("./related-pages.mjs").Related
|
|
267
|
+
* related: Map<string, import("./related-pages.mjs").Related>,
|
|
268
|
+
* maps: number}} `related`
|
|
247
269
|
* is keyed by page URL, and holds the homepage's block beside every content
|
|
248
270
|
* page's, so the caller can write it on the page this render does not.
|
|
271
|
+
* `maps` counts the pages written with one.
|
|
249
272
|
*/
|
|
250
273
|
export function renderPages(pages: object[], options: object): {
|
|
251
274
|
written: number;
|
|
@@ -254,6 +277,7 @@ export function renderPages(pages: object[], options: object): {
|
|
|
254
277
|
wikiErrors: object[];
|
|
255
278
|
imageErrors: object[];
|
|
256
279
|
related: Map<string, import("./related-pages.mjs").Related>;
|
|
280
|
+
maps: number;
|
|
257
281
|
};
|
|
258
282
|
/**
|
|
259
283
|
* Resolves `site.pass` to its bundle.
|
|
@@ -281,18 +305,25 @@ export function resolveSitePass(name: string | undefined, options: object): {
|
|
|
281
305
|
* {@link module:engine/sql-tables.prepareSqlTables}. A page authoring an
|
|
282
306
|
* `sql` directive with none prepared is a table error: nothing here runs a
|
|
283
307
|
* query.
|
|
308
|
+
* @param {(engine: string) => string|undefined} [options.locate] - How a
|
|
309
|
+
* GraphViz engine's binary is found, for the maps; the real lookup by
|
|
310
|
+
* default.
|
|
284
311
|
* @returns {{gates: object, stats: object|null, tableErrors: object[],
|
|
285
|
-
* wikiErrors: object[], imageErrors: object[],
|
|
312
|
+
* wikiErrors: object[], imageErrors: object[], mapFindings: object[],
|
|
313
|
+
* manifests: object|null}} `mapFindings` is what drawing the maps found —
|
|
314
|
+
* warnings, never a reason to fail the build.
|
|
286
315
|
*/
|
|
287
|
-
export function buildSite({ config, sqlTables }?: {
|
|
316
|
+
export function buildSite({ config, sqlTables, locate }?: {
|
|
288
317
|
config?: object | undefined;
|
|
289
318
|
sqlTables?: Map<string, object[]> | undefined;
|
|
319
|
+
locate?: ((engine: string) => string | undefined) | undefined;
|
|
290
320
|
}): {
|
|
291
321
|
gates: object;
|
|
292
322
|
stats: object | null;
|
|
293
323
|
tableErrors: object[];
|
|
294
324
|
wikiErrors: object[];
|
|
295
325
|
imageErrors: object[];
|
|
326
|
+
mapFindings: object[];
|
|
296
327
|
manifests: object | null;
|
|
297
328
|
};
|
|
298
329
|
export { formatUnaddressableFinding };
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A drawing as the page writer receives it.
|
|
3
|
+
*
|
|
4
|
+
* @typedef {object} SiteMap
|
|
5
|
+
* @property {string} name - The file's name in the page's bundle, and the
|
|
6
|
+
* value of the page's `map` key: `from-<shortcode>.svg`.
|
|
7
|
+
* @property {string} file - Where the drawing is, absolute.
|
|
8
|
+
*/
|
|
9
|
+
/**
|
|
10
|
+
* A GraphViz SVG rewritten for inlining in an HTML page.
|
|
11
|
+
*
|
|
12
|
+
* GraphViz writes a standalone document: an XML declaration, a DOCTYPE naming
|
|
13
|
+
* the SVG 1.1 DTD by URL, generator comments, a root sized in points beside
|
|
14
|
+
* its `viewBox`, and links through the `xlink` namespace. Inline in HTML the
|
|
15
|
+
* prologue is invalid, the DTD reference is the one external reference the
|
|
16
|
+
* drawing would carry, the fixed size fights the column it sits in, and
|
|
17
|
+
* `xlink:` is the pre-SVG-2 spelling. So the prologue and the comments go,
|
|
18
|
+
* the root keeps its `viewBox` and loses `width` and `height`, and
|
|
19
|
+
* `xlink:href` and `xlink:title` become `href` and `title`, with the
|
|
20
|
+
* namespace declaration they needed.
|
|
21
|
+
*
|
|
22
|
+
* @param {string} svg - The SVG as GraphViz wrote it.
|
|
23
|
+
* @returns {string} The SVG to inline, starting at `<svg`.
|
|
24
|
+
*/
|
|
25
|
+
export function inlineSvg(svg: string): string;
|
|
26
|
+
/**
|
|
27
|
+
* Draw the map from every related place of this package.
|
|
28
|
+
*
|
|
29
|
+
* @param {object} opts
|
|
30
|
+
* @param {Array<Record<string, any>>} opts.records - The package's index
|
|
31
|
+
* records, as the site build read them.
|
|
32
|
+
* @param {Map<string, object>} opts.foreignIndex - The fetched indexes the
|
|
33
|
+
* site build resolved links against.
|
|
34
|
+
* @param {object} opts.config - The resolved configuration.
|
|
35
|
+
* @param {string} opts.base - The site base every page is served under,
|
|
36
|
+
* ending in a slash: what every name in a drawing links through.
|
|
37
|
+
* @param {(engine: string) => string|undefined} [opts.locate] - How the
|
|
38
|
+
* engine's binary is found; the real lookup by default.
|
|
39
|
+
* @returns {{maps: Map<string, SiteMap>, findings: Array<{file: string,
|
|
40
|
+
* line?: number, column?: number, severity: "error"|"warning",
|
|
41
|
+
* message: string}>}} The drawings, keyed by the URL of the page each
|
|
42
|
+
* belongs to, and what the drawing found — one warning naming what to
|
|
43
|
+
* install when GraphViz is absent, and a warning for each relation that
|
|
44
|
+
* names no place.
|
|
45
|
+
*/
|
|
46
|
+
export function drawSiteMaps({ records, foreignIndex, config, base, locate }: {
|
|
47
|
+
records: Array<Record<string, any>>;
|
|
48
|
+
foreignIndex: Map<string, object>;
|
|
49
|
+
config: object;
|
|
50
|
+
base: string;
|
|
51
|
+
locate?: ((engine: string) => string | undefined) | undefined;
|
|
52
|
+
}): {
|
|
53
|
+
maps: Map<string, SiteMap>;
|
|
54
|
+
findings: Array<{
|
|
55
|
+
file: string;
|
|
56
|
+
line?: number;
|
|
57
|
+
column?: number;
|
|
58
|
+
severity: "error" | "warning";
|
|
59
|
+
message: string;
|
|
60
|
+
}>;
|
|
61
|
+
};
|
|
62
|
+
/**
|
|
63
|
+
* Where the site build draws, below the package root.
|
|
64
|
+
*
|
|
65
|
+
* @type {string}
|
|
66
|
+
*/
|
|
67
|
+
export const SITE_MAP_DIR: string;
|
|
68
|
+
/**
|
|
69
|
+
* A drawing as the page writer receives it.
|
|
70
|
+
*/
|
|
71
|
+
export type SiteMap = {
|
|
72
|
+
/**
|
|
73
|
+
* - The file's name in the page's bundle, and the
|
|
74
|
+
* value of the page's `map` key: `from-<shortcode>.svg`.
|
|
75
|
+
*/
|
|
76
|
+
name: string;
|
|
77
|
+
/**
|
|
78
|
+
* - Where the drawing is, absolute.
|
|
79
|
+
*/
|
|
80
|
+
file: string;
|
|
81
|
+
};
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The state of every note in a tree, checked and counted.
|
|
3
|
+
*
|
|
4
|
+
* Read from the content index rather than from a walk of this pass's own, like
|
|
5
|
+
* every other lint: the command derives the corpus once and hands it to each
|
|
6
|
+
* pass, so no two findings are drawn from different ideas of which files the
|
|
7
|
+
* content is. The **body** is read from the file, because the index carries
|
|
8
|
+
* what is *about* a note and never the note's text.
|
|
9
|
+
*
|
|
10
|
+
* @param {string} contentBase - Root of the content tree.
|
|
11
|
+
* @param {object} opts
|
|
12
|
+
* @param {readonly object[]} opts.records - Index records the caller derived.
|
|
13
|
+
* @param {string} [opts.contentPackage] - Named in the counts line.
|
|
14
|
+
* @returns {{findings: object[], counts: {full: number, draft: number,
|
|
15
|
+
* stub: number}, byType: Array<{type: string, full: number, draft: number,
|
|
16
|
+
* stub: number}>, oldestDrafts: Array<{file: string, modified: Date}>,
|
|
17
|
+
* summary: string[]}} The findings, the ladder's counts, and the lines a
|
|
18
|
+
* reader is shown as prose rather than as findings.
|
|
19
|
+
*/
|
|
20
|
+
export function lintNoteStates(contentBase: string, { records, contentPackage }?: {
|
|
21
|
+
records: readonly object[];
|
|
22
|
+
contentPackage?: string | undefined;
|
|
23
|
+
}): {
|
|
24
|
+
findings: object[];
|
|
25
|
+
counts: {
|
|
26
|
+
full: number;
|
|
27
|
+
draft: number;
|
|
28
|
+
stub: number;
|
|
29
|
+
};
|
|
30
|
+
byType: Array<{
|
|
31
|
+
type: string;
|
|
32
|
+
full: number;
|
|
33
|
+
draft: number;
|
|
34
|
+
stub: number;
|
|
35
|
+
}>;
|
|
36
|
+
oldestDrafts: Array<{
|
|
37
|
+
file: string;
|
|
38
|
+
modified: Date;
|
|
39
|
+
}>;
|
|
40
|
+
summary: string[];
|
|
41
|
+
};
|
|
42
|
+
/**
|
|
43
|
+
* The word count below which an unmarked body is asked whether it is a draft.
|
|
44
|
+
*
|
|
45
|
+
* A threshold, so it is a value the rule *is* rather than a census: twenty-five
|
|
46
|
+
* words is about two sentences, which is where a note stops reading as a
|
|
47
|
+
* statement and starts reading as a beginning.
|
|
48
|
+
*
|
|
49
|
+
* @type {number}
|
|
50
|
+
*/
|
|
51
|
+
export const SHORT_BODY_WORDS: number;
|
|
@@ -92,6 +92,20 @@ export function unlabelledLinkMessage(target: string): string;
|
|
|
92
92
|
* @returns {string} The message, unpunctuated at the end as a finding is.
|
|
93
93
|
*/
|
|
94
94
|
export function unresolvedAddressMessage(target: string): string;
|
|
95
|
+
/**
|
|
96
|
+
* What an author linking to a **stub** is told.
|
|
97
|
+
*
|
|
98
|
+
* Strictly better than "resolves to no note", and available only because a stub
|
|
99
|
+
* is in the index: the note exists, its file can be named, and the reason the
|
|
100
|
+
* link cannot land is a fact about that file rather than a guess about a
|
|
101
|
+
* shortcode. So the message names the file to open and offers both corrections
|
|
102
|
+
* — the link was either premature or the note is owed a body.
|
|
103
|
+
*
|
|
104
|
+
* @param {string} target - The address as authored.
|
|
105
|
+
* @param {string} [file] - Where the stub is, relative to the content root.
|
|
106
|
+
* @returns {string} The message, unpunctuated at the end as a finding is.
|
|
107
|
+
*/
|
|
108
|
+
export function stubAddressMessage(target: string, file?: string): string;
|
|
95
109
|
/**
|
|
96
110
|
* What an author writing a short address more than one package publishes is
|
|
97
111
|
* told.
|
|
@@ -130,16 +144,18 @@ export function ambiguousAddressMessage(target: string, packages?: Iterable<stri
|
|
|
130
144
|
* @param {string} [finding.anchor] - For `unknown-anchor`, the section named.
|
|
131
145
|
* @param {string} [finding.type] - For `not-an-asset`, the type the address
|
|
132
146
|
* named.
|
|
147
|
+
* @param {string} [finding.stub] - For `stub`, where the stub is.
|
|
133
148
|
* @returns {string} The message.
|
|
134
149
|
* @throws {Error} On a reason outside the closed set — a resolver inventing one
|
|
135
150
|
* would otherwise report a link with no explanation at all.
|
|
136
151
|
*/
|
|
137
|
-
export function linkFindingMessage({ reason, target, packages, anchor, type }: {
|
|
152
|
+
export function linkFindingMessage({ reason, target, packages, anchor, type, stub }: {
|
|
138
153
|
reason: string;
|
|
139
154
|
target: string;
|
|
140
155
|
packages?: Iterable<string> | undefined;
|
|
141
156
|
anchor?: string | undefined;
|
|
142
157
|
type?: string | undefined;
|
|
158
|
+
stub?: string | undefined;
|
|
143
159
|
}): string;
|
|
144
160
|
/**
|
|
145
161
|
* Whether a parsed link addresses a section of the page it is written on.
|
|
@@ -221,6 +237,8 @@ export const WIKILINK: RegExp;
|
|
|
221
237
|
* - `unknown-type` — definitely qualified, but names no type this build knows.
|
|
222
238
|
* - `not-an-asset` — a real address, embedded where only a file can be drawn.
|
|
223
239
|
* - `unresolved` — parses as an address, and nothing publishes it.
|
|
240
|
+
* - `stub` — the address names a note this tree holds whose body is empty, so
|
|
241
|
+
* it publishes no page for the link to reach.
|
|
224
242
|
* - `ambiguous` — more than one package publishes the short address.
|
|
225
243
|
* - `unknown-anchor` — the address resolved, the `#section` it names did not.
|
|
226
244
|
* - `no-content-index` — the address names a package declared
|