@heroiclands/package-build 25.6.3 → 25.7.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.
Files changed (55) hide show
  1. package/CHANGELOG.md +17 -0
  2. package/README.md +19 -1
  3. package/bin/content-commands.mjs +14 -70
  4. package/bin/package-build.mjs +25 -10
  5. package/ci/wip-workflows.test.mjs +252 -0
  6. package/content-config.mjs +40 -12
  7. package/docs/api.md +237 -48
  8. package/docs/commands.md +73 -36
  9. package/docs/configuration.md +92 -48
  10. package/docs/guides/release-and-deploy.md +26 -14
  11. package/docs/guides/site.md +29 -6
  12. package/engine/cdn-publish.mjs +164 -6
  13. package/engine/content-index.mjs +71 -19
  14. package/engine/content-spans.mjs +18 -1
  15. package/engine/content-styles.mjs +85 -0
  16. package/engine/derived-sections.mjs +4 -3
  17. package/engine/event-views.mjs +6 -4
  18. package/engine/helpers.mjs +33 -9
  19. package/engine/index.mjs +1 -0
  20. package/engine/note-cache.mjs +1107 -0
  21. package/engine/note-page.mjs +424 -0
  22. package/engine/pack-config.mjs +47 -4
  23. package/engine/pathnames.mjs +22 -7
  24. package/engine/prepared-only.mjs +32 -0
  25. package/engine/preview-index.mjs +55 -13
  26. package/engine/site-build.mjs +34 -448
  27. package/engine/site-config.mjs +33 -4
  28. package/engine/site-maps.mjs +16 -24
  29. package/engine/site-origins.mjs +23 -0
  30. package/engine/site-page.mjs +648 -0
  31. package/engine/site-render.mjs +142 -10
  32. package/engine/site-serve.mjs +1 -1
  33. package/engine/sql-tables.mjs +12 -3
  34. package/package.json +6 -1
  35. package/site-theme/static/css/style.css +14 -0
  36. package/types/content-config.d.mts +24 -4
  37. package/types/engine/cdn-publish.d.mts +22 -0
  38. package/types/engine/content-index.d.mts +36 -2
  39. package/types/engine/content-styles.d.mts +20 -0
  40. package/types/engine/derived-sections.d.mts +3 -3
  41. package/types/engine/event-views.d.mts +4 -3
  42. package/types/engine/helpers.d.mts +18 -1
  43. package/types/engine/index.d.mts +1 -0
  44. package/types/engine/note-cache.d.mts +149 -0
  45. package/types/engine/note-page.d.mts +92 -0
  46. package/types/engine/pack-config.d.mts +30 -0
  47. package/types/engine/prepared-only.d.mts +14 -0
  48. package/types/engine/preview-index.d.mts +35 -0
  49. package/types/engine/site-build.d.mts +5 -100
  50. package/types/engine/site-config.d.mts +3 -2
  51. package/types/engine/site-maps.d.mts +7 -11
  52. package/types/engine/site-origins.d.mts +9 -0
  53. package/types/engine/site-page.d.mts +261 -0
  54. package/types/engine/site-render.d.mts +20 -1
  55. package/types/engine/sql-tables.d.mts +8 -4
@@ -0,0 +1,261 @@
1
+ /**
2
+ * A note's frontmatter and body, from its source text.
3
+ *
4
+ * @param {string} source - The note as authored.
5
+ * @param {string} file - Where it is, for a finding.
6
+ * @param {object} ctx - `{ config, contentPackage }`, which decode its
7
+ * addresses.
8
+ * @returns {{fm: object, body: string, bodyLine: number}|null} `null` for text
9
+ * that is not front-mattered markdown.
10
+ * @throws {Error} When an address in its frontmatter cannot be decoded; the
11
+ * error carries `file` and `position`.
12
+ */
13
+ export function parseNote(source: string, file: string, ctx: object): {
14
+ fm: object;
15
+ body: string;
16
+ bodyLine: number;
17
+ } | null;
18
+ /**
19
+ * The page a note publishes as, or why it publishes none.
20
+ *
21
+ * @param {{fm: object, body: string, bodyLine: number}} note - From {@link parseNote}.
22
+ * @param {object} ctx - `{ file, fileBase, relPath, folder, base, packages,
23
+ * contentPackage, sectionPages, present, dates }`: the note's path, its
24
+ * file name, its path below the content root and its folder's name; the
25
+ * package's site address; the packages the site publishes and the one the
26
+ * note belongs to; the pages `site.sections` names; and the present and
27
+ * reckoning a being's age is computed in.
28
+ * @returns {{page?: object, fmLinks: object[], addressFinding?: object}|null}
29
+ * `null` for a note with no page: a homepage, a stub, a GM note, or one
30
+ * outside the published packages. Otherwise the page, or the finding that
31
+ * says why it has no address, with any wikilink authored in frontmatter.
32
+ */
33
+ export function contentPage(note: {
34
+ fm: object;
35
+ body: string;
36
+ bodyLine: number;
37
+ }, ctx: object): {
38
+ page?: object;
39
+ fmLinks: object[];
40
+ addressFinding?: object;
41
+ } | null;
42
+ /**
43
+ * The figure the website sets a drawing in: the SVG inline, so its place names
44
+ * stay links, with no blank line inside it — a blank line would end the HTML
45
+ * block and hand the rest of the drawing to the Markdown renderer.
46
+ *
47
+ * @param {string} svg - The drawing, as {@link module:engine/site-maps.inlineSvg} wrote it.
48
+ * @param {string} alt - What the drawing shows, for a screen reader.
49
+ * @returns {string} One HTML block.
50
+ */
51
+ export function inlineDrawingFigure(svg: string, alt: string): string;
52
+ /**
53
+ * The frontmatter a page publishes with.
54
+ *
55
+ * A content page carries the package the build **derived**. No note
56
+ * declares one, so the note's frontmatter alone would publish a page that does
57
+ * not say which package it belongs to. Writing the derived value keeps a page
58
+ * self-describing and makes sweeping the field out of a content tree
59
+ * output-preserving for a site as it already is for the packs.
60
+ *
61
+ * **`related` is derived, never authored.** What links to a page is a fact
62
+ * about every other page, known only once the whole tree has resolved — see
63
+ * {@link module:engine/related-pages} — so an authored value is dropped, and
64
+ * {@link module:engine/site-build.renderPages} sets the derived block once it holds the graph.
65
+ *
66
+ * @param {object} page - The page.
67
+ * @param {object} options
68
+ * @param {(data: object, page: object) => void} [options.decorate] - Called
69
+ * with each page's frontmatter, for whatever a consumer's own pass adds.
70
+ * @param {(value: unknown, type: string, accepts?: Iterable<string>) => string|null} [options.artSrc] -
71
+ * Translates an art address into the pathname the website resolver takes, or
72
+ * `null` where nothing answers it.
73
+ * @param {(src: string) => string} [options.webSrc] - Translates an authored
74
+ * pathname into the address the website serves. Every artwork field goes
75
+ * through it, so a page's `img:` and its body images name the same file the
76
+ * same way.
77
+ * @returns {object} The frontmatter.
78
+ */
79
+ export function pageFrontmatter(page: object, { decorate, webSrc, artSrc }: {
80
+ decorate?: ((data: object, page: object) => void) | undefined;
81
+ artSrc?: ((value: unknown, type: string, accepts?: Iterable<string>) => string | null) | undefined;
82
+ webSrc?: ((src: string) => string) | undefined;
83
+ }): object;
84
+ /**
85
+ * The title a content page publishes under — the note's name.
86
+ *
87
+ * One rule, read by the page's own front matter and by every `related` entry
88
+ * that names the page, so a card lists a page by exactly the title its heading
89
+ * shows.
90
+ *
91
+ * @param {object} page - The page, from {@link module:engine/site-build.collectContentPages}.
92
+ * @returns {string} The title.
93
+ */
94
+ export function pageTitle(page: object): string;
95
+ /**
96
+ * Rewrite a page's artwork addresses into the URLs the website serves.
97
+ *
98
+ * The slots are read from {@link module:engine/art-fields.ART_SLOTS}, so a slot
99
+ * added to the vocabulary reaches the page without anyone remembering this
100
+ * function exists. Only an authored **string** is touched: `null` is a note
101
+ * naming no art and `""` is one naming none on purpose, and neither is an
102
+ * address to resolve. A slot's declining word (`banner: none`) is emitted as
103
+ * a blank, the value a theme reads as "draw no image".
104
+ *
105
+ * **An address nothing answers is dropped**, not emitted as written. The theme
106
+ * renders nothing where a value is absent — its silent-disappear convention —
107
+ * and a raw address left in place would reach the reader as a broken image
108
+ * source with nothing saying why.
109
+ *
110
+ * @param {object} data - The frontmatter being emitted, rewritten in place.
111
+ * @param {(src: string) => string} webSrc - The website's pathname resolver.
112
+ * @param {(value: unknown, type: string, accepts?: Iterable<string>) => string|null} artSrc -
113
+ * The address resolver, yielding the pathname `webSrc` takes.
114
+ * @returns {void}
115
+ */
116
+ export function resolveArtFields(data: object, webSrc: (src: string) => string, artSrc: (value: unknown, type: string, accepts?: Iterable<string>) => string | null): void;
117
+ /**
118
+ * @param {unknown} value - Anything.
119
+ * @returns {boolean} Whether it is a mapping a field may be read out of.
120
+ */
121
+ export function isPlainObject(value: unknown): boolean;
122
+ /**
123
+ * Transform one content page.
124
+ *
125
+ * `page` is from {@link contentPage}; the options are what it resolves
126
+ * against: the site `index`, the `foreign` indexes, the consumer's `pass`, the
127
+ * frontmatter `decorate`, the prepared `sqlTables`, the `config`, the asset
128
+ * `artIndex`, other pages' `figuresByAddress`, the `events` index, `drawing`
129
+ * and the marked-text `runs`. The result holds the expanded `markdown`, its
130
+ * `figures`, the page's frontmatter as `data`, the links it `resolved`, and
131
+ * each kind of finding.
132
+ *
133
+ * Reads no file. A drawing a generated **From here** section names is asked
134
+ * of `drawing`, by its file name, for its SVG text, and set inline when it
135
+ * answers.
136
+ */
137
+ export function renderSitePage(page: any, { index, foreign, pass, decorate, sqlTables, config, artIndex, figuresByAddress, events, drawing, runs, }: {
138
+ index: any;
139
+ foreign: any;
140
+ pass?: {} | undefined;
141
+ decorate: any;
142
+ sqlTables: any;
143
+ config: any;
144
+ artIndex: any;
145
+ figuresByAddress?: Map<any, any> | undefined;
146
+ events: any;
147
+ drawing?: (() => undefined) | undefined;
148
+ runs?: import("./content-scripts.mjs").RunContext | undefined;
149
+ }): {
150
+ page: any;
151
+ markdown: string;
152
+ figures: object[];
153
+ data: object;
154
+ resolved: object[];
155
+ tableErrors: object[];
156
+ expressionErrors: object[];
157
+ secretErrors: {
158
+ file: any;
159
+ line: number;
160
+ column: number;
161
+ message: string;
162
+ }[];
163
+ captionErrors: {
164
+ file: any;
165
+ line: any;
166
+ column: any;
167
+ message: any;
168
+ }[];
169
+ headingErrors: {
170
+ line: number;
171
+ column: number;
172
+ message: string;
173
+ file: any;
174
+ }[];
175
+ footnoteErrors: {
176
+ file: any;
177
+ line: number;
178
+ column: number;
179
+ message: string;
180
+ }[];
181
+ wikiErrors: object[];
182
+ imageErrors: any[];
183
+ embedErrors: {
184
+ file: string;
185
+ line: number;
186
+ column: number | undefined;
187
+ severity: "error";
188
+ message: string;
189
+ }[];
190
+ };
191
+ /**
192
+ * Resolves `site.pass` to its bundle.
193
+ *
194
+ * @param {string|undefined} name - The configured name.
195
+ * @param {object} options - The configured options, plus `repoRoot`.
196
+ * @returns {{beforeLinks?: Function}} The bundle.
197
+ */
198
+ export function resolveSitePass(name: string | undefined, options: object): {
199
+ beforeLinks?: Function;
200
+ };
201
+ /** The site frontmatter decoration shared by the site writer and page preview. */
202
+ export function sitePageDecorator(config: any, index: any): (data: any, page: any) => void;
203
+ /**
204
+ * How a site renders a page body from the markdown its build expanded.
205
+ *
206
+ * @param {object} config - The resolved configuration.
207
+ * @returns {(markdown: string, options?: {runs?: object, figures?: object[]}) => string}
208
+ * The body HTML as the site writer receives it.
209
+ */
210
+ export function siteBodyRenderer(config: object): (markdown: string, options?: {
211
+ runs?: object;
212
+ figures?: object[];
213
+ }) => string;
214
+ /**
215
+ * Each finding of a page render as a diagnostic, kind by kind.
216
+ *
217
+ * @param {object} result - From {@link renderSitePage}, or the site build's
218
+ * gathered result, which carries the same lists.
219
+ * @param {(file: string) => string} rawOf - A note's source text, which
220
+ * locates a link or an image by the literal it was handed.
221
+ * @returns {Array<{file?: string, line?: number, column?: number,
222
+ * severity: string, message: string}>} The diagnostics.
223
+ */
224
+ export function pageDiagnostics(result: object, rawOf: (file: string) => string): Array<{
225
+ file?: string;
226
+ line?: number;
227
+ column?: number;
228
+ severity: string;
229
+ message: string;
230
+ }>;
231
+ /**
232
+ * A wikilink authored in a note's frontmatter, as a diagnostic.
233
+ *
234
+ * @param {{file: string, link: string, path: string}} finding - From
235
+ * {@link contentPage}'s `fmLinks`.
236
+ * @returns {{file: string, severity: "error", message: string}} The diagnostic.
237
+ */
238
+ export function frontmatterLinkDiagnostic(finding: {
239
+ file: string;
240
+ link: string;
241
+ path: string;
242
+ }): {
243
+ file: string;
244
+ severity: "error";
245
+ message: string;
246
+ };
247
+ /**
248
+ * A note whose page has no address, as a diagnostic.
249
+ *
250
+ * @param {{file: string, reason: string}} finding - From {@link contentPage}'s
251
+ * `addressFinding`.
252
+ * @returns {{file: string, severity: "error", message: string}} The diagnostic.
253
+ */
254
+ export function addressDiagnostic(finding: {
255
+ file: string;
256
+ reason: string;
257
+ }): {
258
+ file: string;
259
+ severity: "error";
260
+ message: string;
261
+ };
@@ -155,7 +155,13 @@ export function deliverSiteFonts(config: object, out: string): {
155
155
  * What the build found, and, when it found nothing that stops it, what was
156
156
  * written, the pages it was written from and how a note rendered again
157
157
  * becomes its page. `written` is `null` when a gate fired.
158
- * @throws {Error} When the site's settings or its cached menu cannot be read.
158
+ * With `site.inputs`, the local default-exported provider receives `{config}`
159
+ * and supplies the navigation plus text artifacts. All artifact paths are checked
160
+ * before replacing output; artifacts are then written after the renderer clears
161
+ * and writes the site, and included in `written.files`.
162
+ *
163
+ * @throws {Error} When the site's settings, cached menu or input provider fail,
164
+ * or a provider artifact is unsafe or collides with builder-owned output.
159
165
  */
160
166
  export function buildPackageSite({ config, out, year, progress }: {
161
167
  config: object;
@@ -175,3 +181,16 @@ export function buildPackageSite({ config, out, year, progress }: {
175
181
  * icon stylesheets, the spoiler script, the icon font and the theme's images.
176
182
  */
177
183
  export const SITE_STATIC_DIR: string;
184
+ export type SiteInputs = {
185
+ /**
186
+ * The selected menu.
187
+ */
188
+ navigation: import("./site-config.mjs").NavigationEntry[];
189
+ /**
190
+ * Text artifacts relative to the site's output.
191
+ */
192
+ files: Record<string, string>;
193
+ };
194
+ export type SiteInputsProvider = (options: {
195
+ config: import("../content-config.mjs").ContentBuildConfig;
196
+ }) => SiteInputs | Promise<SiteInputs>;
@@ -214,8 +214,8 @@ export function prepareInlineSqlExpressions(db: any, sources: any): Promise<Map<
214
214
  * surface publishes to.
215
215
  * @param {string} contentBase - Root of the content tree.
216
216
  * @param {object} [opts]
217
- * @param {string} [opts.only] - One note file to give sections to; absent,
218
- * every note.
217
+ * @param {string|Set<string>} [opts.only] - The note file, or files, to give
218
+ * sections to; absent, every note.
219
219
  * @param {Map<string, object>} [opts.foreignIndex] - The fetched indexes, whose
220
220
  * places, affiliations and works the lists take in.
221
221
  * @param {object} [opts.config] - The resolved configuration.
@@ -233,7 +233,7 @@ export function prepareInlineSqlExpressions(db: any, sources: any): Promise<Map<
233
233
  * the derived lists alone.
234
234
  */
235
235
  export function generatedSectionsFor(db: object | undefined, records: object[], contentBase: string, { only, foreignIndex, config, derived: given, all, parsed }?: {
236
- only?: string | undefined;
236
+ only?: string | Set<string> | undefined;
237
237
  foreignIndex?: Map<string, object> | undefined;
238
238
  config?: object | undefined;
239
239
  derived?: Map<string, Map<string, string>> | undefined;
@@ -351,11 +351,14 @@ export function cachedDependencies(config?: object): Promise<Array<{
351
351
  * generated sections take in, where the caller holds them already; absent,
352
352
  * they are read from the cache.
353
353
  * @param {import("./content-session.mjs").ContentSyntax} [opts.syntax] - Exact-text scanner projections.
354
+ * @param {string|Iterable<string>} [opts.only] - One note's file, or several:
355
+ * answer their directives and give them their generated sections, and no
356
+ * other note's. The queries still read the whole corpus.
354
357
  * @returns {Promise<Map<string, object[]>|undefined>} Results by note path,
355
358
  * carrying `inline` and `pageLists` beside them, or nothing when the tree
356
359
  * holds no such directive and no note is given a generated section.
357
360
  */
358
- export function prepareTreeSqlTables(contentBase: string, { config, skipDirectories, records, parsed, database, audience, foreignIndex, syntax, }?: {
361
+ export function prepareTreeSqlTables(contentBase: string, { config, skipDirectories, records, parsed, database, audience, foreignIndex, syntax, only, }?: {
359
362
  config?: object | undefined;
360
363
  skipDirectories?: readonly string[] | undefined;
361
364
  records?: object[] | undefined;
@@ -367,6 +370,7 @@ export function prepareTreeSqlTables(contentBase: string, { config, skipDirector
367
370
  audience?: "all" | "public" | undefined;
368
371
  foreignIndex?: Map<string, object> | undefined;
369
372
  syntax?: import("./content-session.mjs").ContentSyntax | undefined;
373
+ only?: string | Iterable<string> | undefined;
370
374
  }): Promise<Map<string, object[]> | undefined>;
371
375
  /**
372
376
  * Prepare authored link-check tables in each source note's audience. Public