@heroiclands/package-build 23.7.1 → 23.9.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 +41 -0
- package/README.md +32 -0
- package/bin/content-commands.mjs +32 -10
- package/ci/check-banners.mjs +37 -19
- package/container.mjs +48 -10
- package/docs/api.md +402 -58
- package/docs/authoring/first-note.md +1 -1
- package/docs/authoring/frontmatter.md +30 -1
- package/docs/authoring/links-and-markup.md +154 -56
- package/docs/commands.md +31 -12
- package/docs/content-format.md +25 -2
- package/docs/design.md +31 -0
- package/docs/diagnostics.md +1 -1
- package/docs/guides/e2e.md +37 -1
- package/docs/guides/packs.md +2 -1
- package/docs/guides/reference-pages.md +86 -63
- package/docs/project-setup.md +30 -15
- package/docs/reference/format-details.md +313 -132
- package/docs/reference/note-types.md +191 -174
- package/e2e.mjs +89 -2
- package/engine/anchors.mjs +7 -20
- package/engine/base-compiler.mjs +11 -18
- package/engine/compile-corpus.mjs +5 -3
- package/engine/content-alerts.mjs +4 -2
- package/engine/content-blocks.mjs +18 -36
- package/engine/content-format.yaml +1 -0
- package/engine/content-index.mjs +447 -33
- package/engine/content-links.mjs +43 -15
- package/engine/content-poetry.mjs +9 -0
- package/engine/content-tables.mjs +8 -4
- package/engine/culture-reach.mjs +61 -34
- package/engine/data-keys.mjs +3 -1
- package/engine/diagnostics.mjs +24 -11
- package/engine/dictionary-layout.mjs +151 -19
- package/engine/dictionary-rows.mjs +129 -79
- package/engine/foundry-entries.mjs +21 -3
- package/engine/frontmatter-lint.mjs +1 -1
- package/engine/generate.mjs +12 -22
- package/engine/glossary-entries.mjs +11 -11
- package/engine/heading-attributes.mjs +21 -28
- package/engine/helpers.mjs +20 -12
- package/engine/infobox.mjs +3 -0
- package/engine/journals.mjs +15 -2
- package/engine/language-data.mjs +53 -0
- package/engine/language-terms.mjs +86 -0
- package/engine/language-words.mjs +2 -2
- package/engine/lexical-sections.mjs +247 -0
- 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 +21 -10
- package/engine/note-package.mjs +3 -5
- package/engine/note-state.mjs +9 -3
- package/engine/note-vocabulary.mjs +36 -7
- package/engine/pdf-build.mjs +17 -10
- 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 +21 -3
- package/engine/site-preview.mjs +4 -2
- package/engine/sql-tables.mjs +287 -53
- package/engine/standings.mjs +14 -12
- package/engine/stub-lint.mjs +6 -2
- package/engine/web-wikilinks.mjs +8 -6
- package/engine/word-rows.mjs +20 -12
- package/engine/word-terms.mjs +15 -6
- 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 +85 -8
- package/types/engine/content-tables.d.mts +2 -1
- package/types/engine/culture-reach.d.mts +22 -2
- package/types/engine/data-keys.d.mts +7 -0
- package/types/engine/diagnostics.d.mts +7 -6
- package/types/engine/dictionary-layout.d.mts +35 -4
- package/types/engine/dictionary-rows.d.mts +9 -0
- package/types/engine/foundry-entries.d.mts +3 -1
- package/types/engine/heading-attributes.d.mts +2 -2
- package/types/engine/helpers.d.mts +11 -5
- package/types/engine/language-terms.d.mts +7 -0
- package/types/engine/lexical-sections.d.mts +95 -0
- 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-state.d.mts +2 -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/site-build.d.mts +4 -1
- package/types/engine/sql-tables.d.mts +40 -6
- package/types/engine/word-rows.d.mts +9 -7
package/engine/prose-lint.mjs
CHANGED
|
@@ -43,6 +43,7 @@ import {
|
|
|
43
43
|
sharedPrettierDivergence,
|
|
44
44
|
sharedPrettierOptionsFor,
|
|
45
45
|
} from "./prose-config.mjs";
|
|
46
|
+
import { markupFindings } from "./markup-validation.mjs";
|
|
46
47
|
import { MarkdownConfigError, runMarkdownlint } from "./markdownlint-run.mjs";
|
|
47
48
|
import { walkFiles } from "./walk-files.mjs";
|
|
48
49
|
|
|
@@ -152,6 +153,13 @@ export async function checkFormatting(root, opts = {}) {
|
|
|
152
153
|
filepath: file,
|
|
153
154
|
};
|
|
154
155
|
const source = fs.readFileSync(file, "utf8");
|
|
156
|
+
if (["markdown", "mdx"].includes(info.inferredParser)) {
|
|
157
|
+
const syntaxErrors = markupFindings(source, file);
|
|
158
|
+
if (syntaxErrors.length) {
|
|
159
|
+
findings.push(...syntaxErrors);
|
|
160
|
+
continue;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
155
163
|
|
|
156
164
|
// A file Prettier cannot parse is a finding, not a crash. Its own CLI
|
|
157
165
|
// reports the syntax error and carries on to the next file, and a
|
|
@@ -399,9 +407,9 @@ export async function lintMarkdown(root, opts = {}) {
|
|
|
399
407
|
const { paths, fix = false } = opts;
|
|
400
408
|
const directory = path.resolve(root);
|
|
401
409
|
|
|
402
|
-
let results;
|
|
410
|
+
let results, markup;
|
|
403
411
|
try {
|
|
404
|
-
({ results } = await runMarkdownlint({
|
|
412
|
+
({ results, markup } = await runMarkdownlint({
|
|
405
413
|
directory,
|
|
406
414
|
globs: paths,
|
|
407
415
|
fix,
|
|
@@ -430,8 +438,6 @@ export async function lintMarkdown(root, opts = {}) {
|
|
|
430
438
|
};
|
|
431
439
|
}
|
|
432
440
|
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
exitCode: results.length ? 1 : 0,
|
|
436
|
-
};
|
|
441
|
+
const findings = [...results.map((result) => toDiagnostic(directory, result)), ...markup];
|
|
442
|
+
return { findings, exitCode: findings.length ? 1 : 0 };
|
|
437
443
|
}
|
package/engine/related-pages.mjs
CHANGED
|
@@ -27,11 +27,12 @@
|
|
|
27
27
|
* `<type>-<shortcode>` form: the site index keys one page under both, and both
|
|
28
28
|
* answer with one URL.
|
|
29
29
|
*
|
|
30
|
-
* **Only a page of this site is an endpoint.** A link into another package
|
|
30
|
+
* **Only a page of this site is an inferred endpoint.** A link into another package
|
|
31
31
|
* resolves to a URL the build renders but no page it writes, so it appears
|
|
32
32
|
* on neither side: a backlink can only be counted on a page this build emits,
|
|
33
33
|
* and listing a foreign mention beside local ones would make the two lists
|
|
34
|
-
* disagree about what a connection is.
|
|
34
|
+
* disagree about what a connection is. Deliberate see-also references also
|
|
35
|
+
* name pages in declared dependencies.
|
|
35
36
|
*
|
|
36
37
|
* @module
|
|
37
38
|
*/
|
|
@@ -46,6 +47,7 @@ import { homepageAddresses } from "./homepage.mjs";
|
|
|
46
47
|
* @property {string} url `<base><slug>/` — the page's address as every href
|
|
47
48
|
* this build renders composes it, never the
|
|
48
49
|
* site-root-relative form the page states for itself.
|
|
50
|
+
* @property {string} [gloss] Why the author recommends this page.
|
|
49
51
|
* @property {string} type The note's `type`, which the theme groups by.
|
|
50
52
|
*/
|
|
51
53
|
|
|
@@ -53,6 +55,7 @@ import { homepageAddresses } from "./homepage.mjs";
|
|
|
53
55
|
* The `related` block one page publishes.
|
|
54
56
|
*
|
|
55
57
|
* @typedef {object} Related
|
|
58
|
+
* @property {RelatedEntry[]} [seeAlso] Deliberate references in authored order.
|
|
56
59
|
* @property {RelatedEntry[]} backlinks Pages that link to this one.
|
|
57
60
|
* @property {RelatedEntry[]} mentions Pages this one links to.
|
|
58
61
|
*/
|
|
@@ -89,10 +92,11 @@ function byTypeThenTitle(a, b) {
|
|
|
89
92
|
* the source page's URL and the target page's.
|
|
90
93
|
* @param {ReadonlyMap<string, RelatedEntry>} entries - Every page of this
|
|
91
94
|
* site, keyed by URL.
|
|
95
|
+
* @param {ReadonlyMap<string, RelatedEntry[]>} [deliberate] Authored references, including foreign destinations.
|
|
92
96
|
* @returns {Map<string, Related>} URL → the page's `related` block, both lists
|
|
93
97
|
* sorted, for every page with at least one entry in either.
|
|
94
98
|
*/
|
|
95
|
-
export function relatedPages(edges, entries) {
|
|
99
|
+
export function relatedPages(edges, entries, deliberate = new Map()) {
|
|
96
100
|
/** @type {Map<string, {backlinks: Set<string>, mentions: Set<string>}>} */
|
|
97
101
|
const graph = new Map();
|
|
98
102
|
const node = (url) => {
|
|
@@ -120,6 +124,21 @@ export function relatedPages(edges, entries) {
|
|
|
120
124
|
for (const [url, { backlinks, mentions }] of graph) {
|
|
121
125
|
out.set(url, { backlinks: list(backlinks), mentions: list(mentions) });
|
|
122
126
|
}
|
|
127
|
+
for (const [url, refs] of deliberate) {
|
|
128
|
+
const seen = new Set([url]);
|
|
129
|
+
const seeAlso = refs.filter((ref) => {
|
|
130
|
+
if (seen.has(ref.url)) return false;
|
|
131
|
+
seen.add(ref.url);
|
|
132
|
+
return true;
|
|
133
|
+
});
|
|
134
|
+
if (!seeAlso.length) continue;
|
|
135
|
+
const block = out.get(url) ?? { backlinks: [], mentions: [] };
|
|
136
|
+
out.set(url, {
|
|
137
|
+
seeAlso,
|
|
138
|
+
backlinks: block.backlinks.filter((ref) => !seen.has(ref.url)),
|
|
139
|
+
mentions: block.mentions.filter((ref) => !seen.has(ref.url)),
|
|
140
|
+
});
|
|
141
|
+
}
|
|
123
142
|
return out;
|
|
124
143
|
}
|
|
125
144
|
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* This file is part of the Song of Heroic Lands (SoHL) system for Foundry VTT.
|
|
3
|
+
* Copyright (c) 2024-2026 Tom Rodriguez ("Toasty") — <toasty@heroiclands.org>
|
|
4
|
+
*
|
|
5
|
+
* This work is licensed under the GNU General Public License v3.0 (GPLv3).
|
|
6
|
+
* You may copy, modify, and distribute it under the terms of that license.
|
|
7
|
+
*
|
|
8
|
+
* For full terms, see the LICENSE.md file in the project root or visit:
|
|
9
|
+
* https://www.gnu.org/licenses/gpl-3.0.html
|
|
10
|
+
*
|
|
11
|
+
* SPDX-License-Identifier: GPL-3.0-or-later
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import { parseAddress, renderAddress, hasAnchor } from "./address.mjs";
|
|
15
|
+
import { ASSET_TYPE_NAMES } from "./asset-types.mjs";
|
|
16
|
+
import { positionOfFrontmatterPath } from "./diagnostics.mjs";
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Validate note references, refusing unresolved, repeated and self destinations.
|
|
20
|
+
* @param {object} note - The authored note, including its raw source.
|
|
21
|
+
* @param {object} [options] - The local and declared dependency context.
|
|
22
|
+
* @param {object} [options.index] - The local and declared dependency index.
|
|
23
|
+
* @param {string} [options.index.contentPackage] - The package owning the note.
|
|
24
|
+
* @param {Set<string>} [options.index.types] - The indexed note types.
|
|
25
|
+
* @param {Set<string>} [options.index.packages] - The indexed packages.
|
|
26
|
+
* @param {(key: string) => {url?: string, uuid?: string, file?: string}|undefined}
|
|
27
|
+
* [options.index.addressHit] - Resolve a canonical Address to its destination.
|
|
28
|
+
* @param {import("./address.mjs").AddressDefaults} [options.addressContext]
|
|
29
|
+
* - The Address defaults and vocabularies at the authored reference.
|
|
30
|
+
* @returns {object[]} Findings located at the offending address.
|
|
31
|
+
*/
|
|
32
|
+
export function checkSeeAlso(note, { index, addressContext } = {}) {
|
|
33
|
+
const entries = note.fm?.data?.seeAlso;
|
|
34
|
+
if (!Array.isArray(entries)) return [];
|
|
35
|
+
const defaults = {
|
|
36
|
+
package: index?.contentPackage,
|
|
37
|
+
system: "note",
|
|
38
|
+
types: index?.types,
|
|
39
|
+
packages: index?.packages,
|
|
40
|
+
...addressContext,
|
|
41
|
+
type: undefined,
|
|
42
|
+
};
|
|
43
|
+
const own = parseAddress(note.fm.shortcode, { ...defaults, type: note.fm.type });
|
|
44
|
+
const seen = new Set();
|
|
45
|
+
const findings = [];
|
|
46
|
+
entries.forEach((entry, i) => {
|
|
47
|
+
if (entry === null) {
|
|
48
|
+
findings.push({
|
|
49
|
+
file: note.file,
|
|
50
|
+
...positionOfFrontmatterPath(note.raw ?? "", ["data", "seeAlso", i]),
|
|
51
|
+
severity: "error",
|
|
52
|
+
message: `\`data.seeAlso.${i}\` should be a map with an address`,
|
|
53
|
+
});
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
if (!entry || typeof entry !== "object" || entry.address == null) return;
|
|
57
|
+
const tuple = parseAddress(entry.address, defaults, { declared: true });
|
|
58
|
+
const key = tuple.reason ? undefined : renderAddress(tuple);
|
|
59
|
+
const target = key ? index?.addressHit?.(key) : undefined;
|
|
60
|
+
const destination = target?.url ?? target?.uuid ?? target?.file ?? key;
|
|
61
|
+
const self =
|
|
62
|
+
(!own.reason &&
|
|
63
|
+
tuple.package === own.package &&
|
|
64
|
+
tuple.type === own.type &&
|
|
65
|
+
tuple.shortcode === own.shortcode) ||
|
|
66
|
+
(target?.file && target.file === note.file);
|
|
67
|
+
const reason =
|
|
68
|
+
hasAnchor(entry.address) ? "takes no anchor — it names a whole note"
|
|
69
|
+
: tuple.reason || ASSET_TYPE_NAMES.has(tuple.type) ? "should be a note Address"
|
|
70
|
+
: index?.addressHit && !target ?
|
|
71
|
+
"does not resolve in this package or its declared dependencies"
|
|
72
|
+
: self ? "names the note itself"
|
|
73
|
+
: seen.has(destination) ? "is a duplicate destination"
|
|
74
|
+
: undefined;
|
|
75
|
+
seen.add(destination);
|
|
76
|
+
if (reason)
|
|
77
|
+
findings.push({
|
|
78
|
+
file: note.file,
|
|
79
|
+
...positionOfFrontmatterPath(note.raw ?? "", ["data", "seeAlso", i, "address"]),
|
|
80
|
+
severity: "error",
|
|
81
|
+
message: `\`data.seeAlso.${i}.address\` ${reason}`,
|
|
82
|
+
});
|
|
83
|
+
});
|
|
84
|
+
return findings;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Resolve deliberate references in authored order using the surface's index.
|
|
89
|
+
* @param {object} fm - The note frontmatter.
|
|
90
|
+
* @param {(address: unknown) => object|undefined} resolve - The surface's reference resolver.
|
|
91
|
+
* @returns {object[]} Display names, destinations and optional glosses.
|
|
92
|
+
*/
|
|
93
|
+
export function seeAlsoEntries(fm, resolve) {
|
|
94
|
+
const entries = fm?.data?.seeAlso;
|
|
95
|
+
if (!Array.isArray(entries)) return [];
|
|
96
|
+
return entries.flatMap((entry) => {
|
|
97
|
+
const target = entry?.address == null ? undefined : resolve(entry.address);
|
|
98
|
+
if (!target) return [];
|
|
99
|
+
return [{ ...target, text: target.name, ...(entry.gloss ? { gloss: entry.gloss } : {}) }];
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/** Plain text in a generated Markdown paragraph. */
|
|
104
|
+
const plain = (text) =>
|
|
105
|
+
String(text ?? "")
|
|
106
|
+
.replace(/([\\`*_[\]<>{}|#!~])/g, "\\$1")
|
|
107
|
+
.replace(/\r?\n/g, " ");
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* A generated section; an empty list emits nothing.
|
|
111
|
+
* @param {object[]} entries - Resolved references in authored order.
|
|
112
|
+
* @param {(entry: object) => string} link - The medium's link markup.
|
|
113
|
+
* @param {number} [level=2] - Heading level; a journal uses one for a separate page.
|
|
114
|
+
* @returns {string} Markdown for the section.
|
|
115
|
+
*/
|
|
116
|
+
export function seeAlsoMarkdown(entries, link, level = 2) {
|
|
117
|
+
if (!entries.length) return "";
|
|
118
|
+
return `${"#".repeat(level)} See also\n\n${entries.map((entry) => `- ${link(entry)}${entry.gloss ? ` — ${plain(entry.gloss)}` : ""}`).join("\n")}\n`;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* A reference rendered as an ordinary Markdown link for the book pipeline.
|
|
123
|
+
* @param {object} entry - A resolved name and URL.
|
|
124
|
+
* @returns {string} Markdown.
|
|
125
|
+
*/
|
|
126
|
+
export function seeAlsoWebLink(entry) {
|
|
127
|
+
return entry.url ? `[${plain(entry.text)}](<${entry.url}>)` : plain(entry.text);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Deliberate site connections for a set of pages, including dependency pages.
|
|
132
|
+
* @param {object[]} pages - Pages carrying frontmatter and their published URL.
|
|
133
|
+
* @param {(address: unknown) => object|undefined} resolve - The site's reference resolver.
|
|
134
|
+
* @returns {Map<string, import("./related-pages.mjs").RelatedEntry[]>} Entries keyed by source URL.
|
|
135
|
+
*/
|
|
136
|
+
export function seeAlsoPages(pages, resolve) {
|
|
137
|
+
const deliberate = new Map();
|
|
138
|
+
for (const page of pages) {
|
|
139
|
+
const refs = seeAlsoEntries(page.fm, resolve)
|
|
140
|
+
.filter((ref) => ref.url)
|
|
141
|
+
.map((ref) => ({
|
|
142
|
+
title: ref.name,
|
|
143
|
+
url: ref.url,
|
|
144
|
+
type: ref.address.type,
|
|
145
|
+
...(ref.gloss ? { gloss: ref.gloss } : {}),
|
|
146
|
+
}));
|
|
147
|
+
if (refs.length) deliberate.set(page.url, refs);
|
|
148
|
+
}
|
|
149
|
+
return deliberate;
|
|
150
|
+
}
|
package/engine/site-build.mjs
CHANGED
|
@@ -109,6 +109,7 @@ import {
|
|
|
109
109
|
} from "./homepage.mjs";
|
|
110
110
|
import { publishesContentPages } from "../content-config.mjs";
|
|
111
111
|
import { HUGO_CONTENT } from "./site-config.mjs";
|
|
112
|
+
import { seeAlsoPages } from "./see-also.mjs";
|
|
112
113
|
import { homepageLinkTargets, relatedPages } from "./related-pages.mjs";
|
|
113
114
|
import { drawSiteMaps, inlineDrawingFigure } from "./site-maps.mjs";
|
|
114
115
|
import { generatedDrawing, markGenerated, splitGenerated } from "./generated-sections.mjs";
|
|
@@ -429,9 +430,11 @@ export function writeHomepages(outRoot, pages, config, { related, records, forei
|
|
|
429
430
|
* @param {object} options
|
|
430
431
|
* @param {object} options.config - The resolved build configuration.
|
|
431
432
|
* @param {object[]} options.records - Content-index records.
|
|
433
|
+
* @param {Map<string, object[]>} [options.sqlTables] - Prepared directives,
|
|
434
|
+
* whose emitted anchors belong to the pages they expand in.
|
|
432
435
|
* @returns {object} The gate results and, when they pass, the built index.
|
|
433
436
|
*/
|
|
434
|
-
export function siteGates(pages, findings, { config, records }) {
|
|
437
|
+
export function siteGates(pages, findings, { config, records, sqlTables }) {
|
|
435
438
|
const out = {
|
|
436
439
|
// Always empty here: the homepage count is decided in `buildSite`
|
|
437
440
|
// before the content walk, and a failing count returns without ever
|
|
@@ -463,7 +466,18 @@ export function siteGates(pages, findings, { config, records }) {
|
|
|
463
466
|
out.unaddressable = unaddressableForeignPackages(foreign.index);
|
|
464
467
|
if (out.unaddressable.length) return out;
|
|
465
468
|
|
|
466
|
-
|
|
469
|
+
const expanded =
|
|
470
|
+
sqlTables ?
|
|
471
|
+
pages.map((page) => ({
|
|
472
|
+
...page,
|
|
473
|
+
body: expandContentTables(page.body ?? "", {
|
|
474
|
+
source: page.file,
|
|
475
|
+
sqlTables: sqlTables.get(page.file),
|
|
476
|
+
pageLists: sqlTables.pageLists?.get(page.file),
|
|
477
|
+
}).markdown,
|
|
478
|
+
}))
|
|
479
|
+
: pages;
|
|
480
|
+
out.index = buildSiteIndex(expanded, {
|
|
467
481
|
package: config.contentPackage,
|
|
468
482
|
foreignIndex: foreign.index,
|
|
469
483
|
foreignReferences: foreign.references,
|
|
@@ -1095,7 +1109,10 @@ export function renderPages(pages, options) {
|
|
|
1095
1109
|
for (const hit of result.resolved) if (hit.url) edges.push([page.url, hit.url]);
|
|
1096
1110
|
}
|
|
1097
1111
|
|
|
1098
|
-
const
|
|
1112
|
+
const deliberate = seeAlsoPages([...pages, ...homepages], (ref) =>
|
|
1113
|
+
resolveInfoboxRef(index, ref),
|
|
1114
|
+
);
|
|
1115
|
+
const related = relatedPages(edges, entries, deliberate);
|
|
1099
1116
|
|
|
1100
1117
|
let withMap = 0;
|
|
1101
1118
|
const outputs = capture === true ? new Map() : null;
|
|
@@ -1363,6 +1380,7 @@ export function buildSite({ config, sqlTables } = {}) {
|
|
|
1363
1380
|
const gates = siteGates([...pages, ...homepageEntries], content, {
|
|
1364
1381
|
config: resolved,
|
|
1365
1382
|
records: ctx.records,
|
|
1383
|
+
sqlTables,
|
|
1366
1384
|
});
|
|
1367
1385
|
if (gatesFailed(gates)) {
|
|
1368
1386
|
return {
|
package/engine/site-preview.mjs
CHANGED
|
@@ -22,7 +22,8 @@ import { foundryIdentities } from "./foundry-entries.mjs";
|
|
|
22
22
|
import { applyComputedBeingAge, presentAmongRecords } from "./being-age.mjs";
|
|
23
23
|
import { reckoningContext } from "./reckoning-markers.mjs";
|
|
24
24
|
import { cachedMetadataIndexes, noContentIndexPackages } from "./metadata-index.mjs";
|
|
25
|
-
import {
|
|
25
|
+
import { seeAlsoPages } from "./see-also.mjs";
|
|
26
|
+
import { buildSiteIndex, resolveInfoboxRef } from "./site-index.mjs";
|
|
26
27
|
import {
|
|
27
28
|
attachGeneratedSections,
|
|
28
29
|
findSqlBlocks,
|
|
@@ -320,7 +321,8 @@ export async function prepareSitePreview({ config = loadPackConfig() } = {}) {
|
|
|
320
321
|
url: page.url,
|
|
321
322
|
type: String(fm.type),
|
|
322
323
|
});
|
|
323
|
-
const
|
|
324
|
+
const deliberate = seeAlsoPages([page], (ref) => resolveInfoboxRef(index, ref));
|
|
325
|
+
const related = relatedPages(edges, entries, deliberate).get(page.url);
|
|
324
326
|
if (related) result.data.related = related;
|
|
325
327
|
return {
|
|
326
328
|
ok: true,
|