@heroiclands/package-build 23.7.1 → 23.8.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 +14 -0
- package/README.md +32 -0
- package/bin/content-commands.mjs +20 -6
- package/ci/check-banners.mjs +37 -19
- package/container.mjs +48 -10
- package/docs/api.md +87 -43
- package/docs/authoring/frontmatter.md +30 -1
- package/docs/authoring/links-and-markup.md +14 -0
- package/docs/commands.md +12 -2
- package/docs/content-format.md +23 -0
- package/docs/design.md +31 -0
- package/docs/guides/e2e.md +37 -1
- package/docs/project-setup.md +30 -15
- package/docs/reference/format-details.md +44 -9
- package/docs/reference/note-types.md +7 -4
- package/e2e.mjs +89 -2
- package/engine/base-compiler.mjs +11 -18
- package/engine/compile-corpus.mjs +2 -1
- package/engine/content-alerts.mjs +4 -2
- package/engine/content-blocks.mjs +18 -36
- package/engine/content-index.mjs +35 -22
- package/engine/content-poetry.mjs +9 -0
- package/engine/diagnostics.mjs +24 -11
- package/engine/frontmatter-lint.mjs +1 -1
- package/engine/generate.mjs +12 -22
- package/engine/helpers.mjs +8 -6
- package/engine/infobox.mjs +3 -0
- package/engine/journals.mjs +11 -1
- package/engine/markdownlint-run.mjs +12 -2
- package/engine/markup-validation.mjs +34 -0
- package/engine/note-addresses.mjs +1 -0
- package/engine/note-claims.mjs +19 -10
- package/engine/note-package.mjs +3 -5
- package/engine/note-vocabulary.mjs +29 -1
- package/engine/pdf-build.mjs +8 -1
- package/engine/prose-lint.mjs +12 -6
- package/engine/related-pages.mjs +22 -3
- package/engine/see-also.mjs +150 -0
- package/engine/site-build.mjs +5 -1
- package/engine/site-preview.mjs +4 -2
- package/engine/sql-tables.mjs +25 -10
- package/engine/standings.mjs +14 -12
- package/hugo-theme/layouts/partials/related.html +25 -8
- package/package.json +2 -1
- package/types/container.d.mts +4 -3
- package/types/e2e.d.mts +28 -0
- package/types/engine/base-compiler.d.mts +2 -2
- package/types/engine/compile-corpus.d.mts +2 -1
- package/types/engine/content-index.d.mts +20 -7
- package/types/engine/diagnostics.d.mts +7 -6
- package/types/engine/helpers.d.mts +7 -4
- package/types/engine/markdownlint-run.d.mts +3 -1
- package/types/engine/markup-validation.d.mts +14 -0
- package/types/engine/note-addresses.d.mts +10 -0
- package/types/engine/note-claims.d.mts +7 -1
- package/types/engine/note-package.d.mts +3 -1
- package/types/engine/note-vocabulary.d.mts +1 -0
- package/types/engine/related-pages.d.mts +10 -1
- package/types/engine/see-also.d.mts +55 -0
- package/types/engine/sql-tables.d.mts +7 -3
|
@@ -150,12 +150,14 @@ export function positionInFrontmatter(raw: string, key: string, value?: string,
|
|
|
150
150
|
* {@link positionInFrontmatter}.
|
|
151
151
|
* @param {boolean} [options.topLevel=false] - Require the key at column 1, so
|
|
152
152
|
* an identically named nested key cannot answer for it.
|
|
153
|
+
* @param {string} [options.raw] - Source text already read by the caller.
|
|
153
154
|
* @returns {{line?: number, column?: number}|undefined} Spreadable position
|
|
154
155
|
* fields, dropped rather than guessed when the file cannot be read or the key
|
|
155
156
|
* cannot be found — as `formatDiagnostic` requires.
|
|
156
157
|
*/
|
|
157
|
-
export function locateFrontmatterKey(absPath: string | undefined, key: string, value?: string, { topLevel }?: {
|
|
158
|
+
export function locateFrontmatterKey(absPath: string | undefined, key: string, value?: string, { topLevel, raw }?: {
|
|
158
159
|
topLevel?: boolean | undefined;
|
|
160
|
+
raw?: string | undefined;
|
|
159
161
|
}): {
|
|
160
162
|
line?: number;
|
|
161
163
|
column?: number;
|
|
@@ -195,11 +197,10 @@ export function positionOfLiteral(text: string, needle: string, occurrence?: num
|
|
|
195
197
|
* first occurrence is routinely not the one the finding is about — which is the
|
|
196
198
|
* one thing the located form exists to prevent.
|
|
197
199
|
*
|
|
198
|
-
* A path resolves the exact node
|
|
199
|
-
*
|
|
200
|
-
*
|
|
201
|
-
*
|
|
202
|
-
* representation of the same file to keep in step.
|
|
200
|
+
* A path resolves the exact node. The most recently located text retains its
|
|
201
|
+
* document and line counter, so several findings in one note share a parse.
|
|
202
|
+
* A different text replaces that entry, including an edited copy of the same
|
|
203
|
+
* file. The loader returns plain data without source ranges.
|
|
203
204
|
*
|
|
204
205
|
* Every failure — unparseable text, an `.mjs` configuration, a path that
|
|
205
206
|
* resolves to nothing — yields `{}`, so a caller spreads the result and the
|
|
@@ -19,17 +19,18 @@ export function renderFoundryMarkdown(body: string, figures?: Array<{
|
|
|
19
19
|
/**
|
|
20
20
|
* Parses a markdown file with YAML frontmatter.
|
|
21
21
|
*
|
|
22
|
-
* Returns `{ frontmatter, body, description, bodyLine, bodyColumn }` where
|
|
22
|
+
* Returns `{ frontmatter, body, description, bodyLine, bodyColumn, raw }` where
|
|
23
23
|
* `body` is the trimmed raw markdown after the frontmatter block, and
|
|
24
|
-
* `description` is `body` rendered to HTML.
|
|
24
|
+
* `description` is `body` rendered to HTML. The full source is retained as
|
|
25
|
+
* `raw` for diagnostic locations. `bodyLine` / `bodyColumn` are the
|
|
25
26
|
* 1-based **file** position of the body's first character, which is what turns
|
|
26
27
|
* an offset within `body` into a position a diagnostic can name — see
|
|
27
28
|
* {@link positionInBody}. If the file has no frontmatter block, returns
|
|
28
|
-
* `{ frontmatter: null, body: "", description: "" }`
|
|
29
|
+
* `{ frontmatter: null, body: "", description: "", raw }` and no
|
|
29
30
|
* position: there is no body to have one.
|
|
30
31
|
* @param {string} filePath - Markdown file to read.
|
|
31
32
|
* @param {{addressContext?: object}} [opts] - Address defaults for decoded fields.
|
|
32
|
-
* @returns {{frontmatter: object|null, body: string, description: string,
|
|
33
|
+
* @returns {{frontmatter: object|null, body: string, description: string, raw: string,
|
|
33
34
|
* bodyLine?: number, bodyColumn?: number, parseError?: {message: string,
|
|
34
35
|
* line?: number, column?: number}}} Parsed note and body position.
|
|
35
36
|
*/
|
|
@@ -39,6 +40,7 @@ export function parseMarkdownFile(filePath: string, { addressContext }?: {
|
|
|
39
40
|
frontmatter: object | null;
|
|
40
41
|
body: string;
|
|
41
42
|
description: string;
|
|
43
|
+
raw: string;
|
|
42
44
|
bodyLine?: number;
|
|
43
45
|
bodyColumn?: number;
|
|
44
46
|
parseError?: {
|
|
@@ -118,6 +120,7 @@ export function walkMarkdownTree(rootDir: string, { skipDirectories, addressCont
|
|
|
118
120
|
frontmatter: object | null;
|
|
119
121
|
body: string;
|
|
120
122
|
description: string;
|
|
123
|
+
raw: string;
|
|
121
124
|
bodyLine?: number;
|
|
122
125
|
bodyColumn?: number;
|
|
123
126
|
parseError?: {
|
|
@@ -9,8 +9,9 @@
|
|
|
9
9
|
* `config`, `ignores`, `gitignore` and `globs`.
|
|
10
10
|
* @param {boolean} [params.fix=false] - Repair what markdownlint can, then
|
|
11
11
|
* report what remains.
|
|
12
|
-
* @returns {Promise<{results: object[]}>} markdownlint results, each with its
|
|
12
|
+
* @returns {Promise<{results: object[], markup: object[]}>} markdownlint results, each with its
|
|
13
13
|
* `fileName` relative to `directory`, ordered by file, line and rule.
|
|
14
|
+
* `markup` carries block syntax diagnostics with absolute filenames.
|
|
14
15
|
* @throws {MarkdownConfigError} A configuration file the contract refuses.
|
|
15
16
|
*/
|
|
16
17
|
export function runMarkdownlint(params: {
|
|
@@ -20,6 +21,7 @@ export function runMarkdownlint(params: {
|
|
|
20
21
|
fix?: boolean | undefined;
|
|
21
22
|
}): Promise<{
|
|
22
23
|
results: object[];
|
|
24
|
+
markup: object[];
|
|
23
25
|
}>;
|
|
24
26
|
/** Keys an options file may declare at the repository root. */
|
|
25
27
|
export const ROOT_OPTION_KEYS: readonly string[];
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Validate authored block syntax with positions in the original file.
|
|
3
|
+
* Frontmatter is masked so literal YAML values cannot become body markup.
|
|
4
|
+
* @param {string} source - Complete Markdown source, optionally with frontmatter.
|
|
5
|
+
* @param {string} file - Diagnostic filename.
|
|
6
|
+
* @returns {Array<{file: string, line: number, column: number, severity: string, message: string}>} Syntax errors.
|
|
7
|
+
*/
|
|
8
|
+
export function markupFindings(source: string, file: string): Array<{
|
|
9
|
+
file: string;
|
|
10
|
+
line: number;
|
|
11
|
+
column: number;
|
|
12
|
+
severity: string;
|
|
13
|
+
message: string;
|
|
14
|
+
}>;
|
|
@@ -71,12 +71,21 @@ export const STRUCTURED_ADDRESSES: readonly ({
|
|
|
71
71
|
noteType: string;
|
|
72
72
|
path: (string | string[] | undefined)[];
|
|
73
73
|
shape: string;
|
|
74
|
+
} | {
|
|
75
|
+
path: string[];
|
|
76
|
+
shape: string;
|
|
77
|
+
lenient: boolean;
|
|
78
|
+
type?: undefined;
|
|
79
|
+
accepts?: undefined;
|
|
80
|
+
system?: undefined;
|
|
81
|
+
noteType?: undefined;
|
|
74
82
|
} | {
|
|
75
83
|
path: string[];
|
|
76
84
|
shape: string;
|
|
77
85
|
type: string;
|
|
78
86
|
accepts: string[];
|
|
79
87
|
system: string;
|
|
88
|
+
lenient?: undefined;
|
|
80
89
|
noteType?: undefined;
|
|
81
90
|
} | {
|
|
82
91
|
noteType: string;
|
|
@@ -84,6 +93,7 @@ export const STRUCTURED_ADDRESSES: readonly ({
|
|
|
84
93
|
shape: string;
|
|
85
94
|
type: string;
|
|
86
95
|
accepts: string[];
|
|
96
|
+
lenient?: undefined;
|
|
87
97
|
system?: undefined;
|
|
88
98
|
})[];
|
|
89
99
|
export { AddressEntries, encodeAddresses, cloneAddressState, publishAddresses, readPublishedAddresses, flattenPublishedAddresses } from "./address-values.mjs";
|
|
@@ -120,11 +120,17 @@ export function noteTypeVocabulary(sources?: ClaimSources): ReadonlySet<string>;
|
|
|
120
120
|
* @param {object} [opts] - Options.
|
|
121
121
|
* @param {readonly object[]} [opts.records] - The corpus, derived once by the
|
|
122
122
|
* compile and handed in — required, for the reason above.
|
|
123
|
+
* @param {Map<string, {body: string, raw: string}>} [opts.parsed] - Already parsed
|
|
124
|
+
* notes, keyed by file, for body checks and finding positions.
|
|
123
125
|
* @returns {Array<{file: string, line?: number, column?: number,
|
|
124
126
|
* severity: "error", message: string, type: string}>} One finding per note.
|
|
125
127
|
*/
|
|
126
|
-
export function unclaimedNoteFindings(config?: object, sources?: ClaimSources, { records }?: {
|
|
128
|
+
export function unclaimedNoteFindings(config?: object, sources?: ClaimSources, { records, parsed }?: {
|
|
127
129
|
records?: readonly object[] | undefined;
|
|
130
|
+
parsed?: Map<string, {
|
|
131
|
+
body: string;
|
|
132
|
+
raw: string;
|
|
133
|
+
}> | undefined;
|
|
128
134
|
}): Array<{
|
|
129
135
|
file: string;
|
|
130
136
|
line?: number;
|
|
@@ -21,13 +21,15 @@ export function searchableFrontmatter(fm: object | null | undefined, configured?
|
|
|
21
21
|
* @param {string} [options.absPath] - The note's file on disk, read only on the
|
|
22
22
|
* failing path to locate the offending line and column. The position rides on
|
|
23
23
|
* the thrown error as `position`, for a caller that emits a diagnostic.
|
|
24
|
+
* @param {string} [options.raw] - Source text already read by the caller.
|
|
24
25
|
* @param {string} [options.configured] - The package this build compiles.
|
|
25
26
|
* Defaults to the configured `contentPackage`.
|
|
26
27
|
* @returns {void}
|
|
27
28
|
* @throws {Error} When the note declares the field.
|
|
28
29
|
*/
|
|
29
|
-
export function assertNoDeclaredPackage(fm: object | null | undefined, { file, absPath, configured }?: {
|
|
30
|
+
export function assertNoDeclaredPackage(fm: object | null | undefined, { file, absPath, configured, raw }?: {
|
|
30
31
|
file?: string | undefined;
|
|
31
32
|
absPath?: string | undefined;
|
|
33
|
+
raw?: string | undefined;
|
|
32
34
|
configured?: string | undefined;
|
|
33
35
|
}): void;
|
|
@@ -14,10 +14,11 @@
|
|
|
14
14
|
* the source page's URL and the target page's.
|
|
15
15
|
* @param {ReadonlyMap<string, RelatedEntry>} entries - Every page of this
|
|
16
16
|
* site, keyed by URL.
|
|
17
|
+
* @param {ReadonlyMap<string, RelatedEntry[]>} [deliberate] Authored references, including foreign destinations.
|
|
17
18
|
* @returns {Map<string, Related>} URL → the page's `related` block, both lists
|
|
18
19
|
* sorted, for every page with at least one entry in either.
|
|
19
20
|
*/
|
|
20
|
-
export function relatedPages(edges: Iterable<readonly [string, string]>, entries: ReadonlyMap<string, RelatedEntry>): Map<string, Related>;
|
|
21
|
+
export function relatedPages(edges: Iterable<readonly [string, string]>, entries: ReadonlyMap<string, RelatedEntry>, deliberate?: ReadonlyMap<string, RelatedEntry[]>): Map<string, Related>;
|
|
21
22
|
/**
|
|
22
23
|
* The pages a homepage's body links to, as URLs.
|
|
23
24
|
*
|
|
@@ -50,6 +51,10 @@ export type RelatedEntry = {
|
|
|
50
51
|
* site-root-relative form the page states for itself.
|
|
51
52
|
*/
|
|
52
53
|
url: string;
|
|
54
|
+
/**
|
|
55
|
+
* Why the author recommends this page.
|
|
56
|
+
*/
|
|
57
|
+
gloss?: string | undefined;
|
|
53
58
|
/**
|
|
54
59
|
* The note's `type`, which the theme groups by.
|
|
55
60
|
*/
|
|
@@ -59,6 +64,10 @@ export type RelatedEntry = {
|
|
|
59
64
|
* The `related` block one page publishes.
|
|
60
65
|
*/
|
|
61
66
|
export type Related = {
|
|
67
|
+
/**
|
|
68
|
+
* Deliberate references in authored order.
|
|
69
|
+
*/
|
|
70
|
+
seeAlso?: RelatedEntry[] | undefined;
|
|
62
71
|
/**
|
|
63
72
|
* Pages that link to this one.
|
|
64
73
|
*/
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Validate note references, refusing unresolved, repeated and self destinations.
|
|
3
|
+
* @param {object} note - The authored note, including its raw source.
|
|
4
|
+
* @param {object} [options] - The local and declared dependency context.
|
|
5
|
+
* @param {object} [options.index] - The local and declared dependency index.
|
|
6
|
+
* @param {string} [options.index.contentPackage] - The package owning the note.
|
|
7
|
+
* @param {Set<string>} [options.index.types] - The indexed note types.
|
|
8
|
+
* @param {Set<string>} [options.index.packages] - The indexed packages.
|
|
9
|
+
* @param {(key: string) => {url?: string, uuid?: string, file?: string}|undefined}
|
|
10
|
+
* [options.index.addressHit] - Resolve a canonical Address to its destination.
|
|
11
|
+
* @param {import("./address.mjs").AddressDefaults} [options.addressContext]
|
|
12
|
+
* - The Address defaults and vocabularies at the authored reference.
|
|
13
|
+
* @returns {object[]} Findings located at the offending address.
|
|
14
|
+
*/
|
|
15
|
+
export function checkSeeAlso(note: object, { index, addressContext }?: {
|
|
16
|
+
index?: {
|
|
17
|
+
contentPackage?: string | undefined;
|
|
18
|
+
types?: Set<string> | undefined;
|
|
19
|
+
packages?: Set<string> | undefined;
|
|
20
|
+
addressHit?: ((key: string) => {
|
|
21
|
+
url?: string;
|
|
22
|
+
uuid?: string;
|
|
23
|
+
file?: string;
|
|
24
|
+
} | undefined) | undefined;
|
|
25
|
+
} | undefined;
|
|
26
|
+
addressContext?: import("./address.mjs").AddressDefaults | undefined;
|
|
27
|
+
}): object[];
|
|
28
|
+
/**
|
|
29
|
+
* Resolve deliberate references in authored order using the surface's index.
|
|
30
|
+
* @param {object} fm - The note frontmatter.
|
|
31
|
+
* @param {(address: unknown) => object|undefined} resolve - The surface's reference resolver.
|
|
32
|
+
* @returns {object[]} Display names, destinations and optional glosses.
|
|
33
|
+
*/
|
|
34
|
+
export function seeAlsoEntries(fm: object, resolve: (address: unknown) => object | undefined): object[];
|
|
35
|
+
/**
|
|
36
|
+
* A generated section; an empty list emits nothing.
|
|
37
|
+
* @param {object[]} entries - Resolved references in authored order.
|
|
38
|
+
* @param {(entry: object) => string} link - The medium's link markup.
|
|
39
|
+
* @param {number} [level=2] - Heading level; a journal uses one for a separate page.
|
|
40
|
+
* @returns {string} Markdown for the section.
|
|
41
|
+
*/
|
|
42
|
+
export function seeAlsoMarkdown(entries: object[], link: (entry: object) => string, level?: number): string;
|
|
43
|
+
/**
|
|
44
|
+
* A reference rendered as an ordinary Markdown link for the book pipeline.
|
|
45
|
+
* @param {object} entry - A resolved name and URL.
|
|
46
|
+
* @returns {string} Markdown.
|
|
47
|
+
*/
|
|
48
|
+
export function seeAlsoWebLink(entry: object): string;
|
|
49
|
+
/**
|
|
50
|
+
* Deliberate site connections for a set of pages, including dependency pages.
|
|
51
|
+
* @param {object[]} pages - Pages carrying frontmatter and their published URL.
|
|
52
|
+
* @param {(address: unknown) => object|undefined} resolve - The site's reference resolver.
|
|
53
|
+
* @returns {Map<string, import("./related-pages.mjs").RelatedEntry[]>} Entries keyed by source URL.
|
|
54
|
+
*/
|
|
55
|
+
export function seeAlsoPages(pages: object[], resolve: (address: unknown) => object | undefined): Map<string, import("./related-pages.mjs").RelatedEntry[]>;
|
|
@@ -139,8 +139,9 @@ export function renderSqlTable(result: {
|
|
|
139
139
|
* its own table and not the whole build's report.
|
|
140
140
|
*
|
|
141
141
|
* @param {object} db - From {@link openNotesDatabase}.
|
|
142
|
-
* @param {Array<{source: string, markdown: string}>} sources -
|
|
143
|
-
* scan.
|
|
142
|
+
* @param {Array<{source: string, markdown: string, frontmatter?: object, authoredSqlBlocks?: number}>} sources -
|
|
143
|
+
* The bodies to scan. `joinGeneratedSections` records the number of authored
|
|
144
|
+
* fences; following generated event fences need no stub comparison.
|
|
144
145
|
* @param {object} [opts]
|
|
145
146
|
* @param {(ref: string) => boolean} [opts.linkable] - Passed to
|
|
146
147
|
* {@link renderSqlTable}.
|
|
@@ -163,6 +164,8 @@ export function renderSqlTable(result: {
|
|
|
163
164
|
export function prepareSqlTables(db: object, sources: Array<{
|
|
164
165
|
source: string;
|
|
165
166
|
markdown: string;
|
|
167
|
+
frontmatter?: object;
|
|
168
|
+
authoredSqlBlocks?: number;
|
|
166
169
|
}>, { linkable, records, pageLanguage }?: {
|
|
167
170
|
linkable?: ((ref: string) => boolean) | undefined;
|
|
168
171
|
pageLanguage?: ((source: string, frontmatter?: object) => string | undefined) | undefined;
|
|
@@ -216,7 +219,7 @@ export function generatedSectionsFor(db: object | undefined, records: object[],
|
|
|
216
219
|
*
|
|
217
220
|
* @param {{sections: Map<string, string>, views: Set<string>}} generated -
|
|
218
221
|
* From {@link generatedSectionsFor}.
|
|
219
|
-
* @param {Array<{source: string, markdown: string, frontmatter?: object}>} sources -
|
|
222
|
+
* @param {Array<{source: string, markdown: string, frontmatter?: object, authoredSqlBlocks?: number}>} sources -
|
|
220
223
|
* The bodies to answer, extended in place.
|
|
221
224
|
* @param {Map<string, {body: string, frontmatter: object|null}>} [parsed] -
|
|
222
225
|
* Notes already parsed, keyed by file; a note absent from it is read.
|
|
@@ -229,6 +232,7 @@ export function joinGeneratedSections(generated: {
|
|
|
229
232
|
source: string;
|
|
230
233
|
markdown: string;
|
|
231
234
|
frontmatter?: object;
|
|
235
|
+
authoredSqlBlocks?: number;
|
|
232
236
|
}>, parsed?: Map<string, {
|
|
233
237
|
body: string;
|
|
234
238
|
frontmatter: object | null;
|