@heroiclands/package-build 22.28.0 → 23.1.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 +72 -0
- package/README.md +1 -1
- package/bin/content-commands.mjs +1 -1
- package/ci/content-format-reference.mjs +46 -0
- package/docs/api.md +125 -142
- package/docs/authoring/assets.md +1 -1
- package/docs/authoring/dates-and-calendars.md +12 -6
- package/docs/authoring/first-note.md +1 -1
- package/docs/authoring/frontmatter.md +2 -2
- package/docs/authoring/links-and-markup.md +145 -29
- package/docs/commands.md +8 -10
- package/docs/configuration.md +13 -11
- package/docs/content-format.md +3 -3
- package/docs/guides/book.md +59 -4
- package/docs/guides/icons.md +4 -5
- package/docs/guides/packs.md +6 -0
- package/docs/guides/reference-pages.md +21 -1
- package/docs/guides/release-and-deploy.md +17 -1
- package/docs/guides/site.md +17 -18
- package/docs/reference/format-details.md +904 -275
- package/docs/reference/note-types.md +282 -91
- package/docs/reference/theme-pages.md +58 -66
- package/engine/address-values.mjs +124 -4
- package/engine/address.mjs +44 -3
- package/engine/anchors.mjs +103 -3
- package/engine/art-slots.mjs +2 -9
- package/engine/base-compiler.mjs +25 -6
- package/engine/calendar-artifacts.mjs +4 -1
- package/engine/calendar-notes.mjs +166 -102
- package/engine/content-format-check.mjs +2 -3
- package/engine/content-format.mjs +44 -1
- package/engine/content-format.yaml +84 -16
- package/engine/content-images.mjs +10 -1
- package/engine/content-index.mjs +37 -9
- package/engine/content-links.mjs +24 -5
- package/engine/content-tables.mjs +24 -3
- package/engine/data-keys.mjs +347 -0
- package/engine/derived-sections.mjs +322 -0
- package/engine/diagnostics.mjs +28 -0
- package/engine/event-fields.mjs +187 -0
- package/engine/event-rows.mjs +126 -0
- package/engine/event-views.mjs +305 -0
- package/engine/exported-scene.mjs +29 -53
- package/engine/frontmatter-lint.mjs +103 -77
- package/engine/frontmatter.mjs +7 -2
- package/engine/generate.mjs +53 -0
- package/engine/generated-sections.mjs +223 -0
- package/engine/heading-attributes.mjs +4 -2
- package/engine/helpers.mjs +12 -1
- package/engine/holdings.mjs +12 -33
- package/engine/ids.mjs +17 -11
- package/engine/index.mjs +4 -4
- package/engine/infobox-render.mjs +21 -6
- package/engine/infobox.mjs +217 -99
- package/engine/journals.mjs +104 -47
- package/engine/literature-notes.mjs +136 -0
- package/engine/literature-works.mjs +161 -0
- package/engine/map-graphviz.mjs +4 -18
- package/engine/markdown-expressions.mjs +116 -11
- package/engine/metadata-index.mjs +34 -0
- package/engine/near-miss.mjs +90 -0
- package/engine/note-addresses.mjs +75 -19
- package/engine/note-dates.mjs +5 -3
- package/engine/note-event-terms.mjs +364 -0
- package/engine/note-events.mjs +823 -0
- package/engine/note-vocabulary.mjs +195 -92
- package/engine/office-holders.mjs +20 -54
- package/engine/packagebuild-index.mjs +16 -1
- package/engine/pdf-build.mjs +97 -60
- package/engine/pdf-index.mjs +263 -0
- package/engine/pdf-render.mjs +47 -5
- package/engine/place-relation-terms.mjs +174 -0
- package/engine/place-relations.mjs +72 -160
- package/engine/rank-ladder.mjs +14 -41
- package/engine/reckoning-markers.mjs +2 -0
- package/engine/region-events.mjs +6 -12
- package/engine/scenes.mjs +73 -468
- package/engine/site-build.mjs +74 -85
- package/engine/site-index.mjs +3 -1
- package/engine/site-maps.mjs +43 -29
- package/engine/site-preview.mjs +40 -31
- package/engine/social-ties.mjs +2 -1
- package/engine/sql-tables.mjs +212 -9
- package/engine/standing-terms.mjs +145 -6
- package/engine/standings.mjs +37 -49
- package/engine/stub-lint.mjs +6 -0
- package/engine/web-wikilinks.mjs +21 -1
- package/engine/wikilink-syntax.mjs +34 -4
- package/engine/wikilinks.mjs +27 -2
- package/hugo-theme/layouts/partials/infobox/entry.html +4 -1
- package/hugo-theme/layouts/partials/page-body.html +0 -2
- package/hugo-theme/static/css/style.css +25 -55
- package/package.json +1 -2
- package/packagebuild-metadata.jsonl +13 -13
- package/sohl/default-item-art.mjs +6 -6
- package/sohl/note-schemas.mjs +8 -73
- package/types/engine/address-values.d.mts +50 -3
- package/types/engine/address.d.mts +21 -0
- package/types/engine/anchors.d.mts +49 -2
- package/types/engine/art-slots.d.mts +1 -1
- package/types/engine/base-compiler.d.mts +13 -4
- package/types/engine/calendar-notes.d.mts +7 -0
- package/types/engine/content-format.d.mts +52 -0
- package/types/engine/content-images.d.mts +5 -1
- package/types/engine/content-tables.d.mts +9 -1
- package/types/engine/data-keys.d.mts +145 -0
- package/types/engine/derived-sections.d.mts +73 -0
- package/types/engine/diagnostics.d.mts +15 -0
- package/types/engine/event-fields.d.mts +99 -0
- package/types/engine/event-rows.d.mts +12 -0
- package/types/engine/event-views.d.mts +67 -0
- package/types/engine/exported-scene.d.mts +5 -2
- package/types/engine/frontmatter-lint.d.mts +3 -3
- package/types/engine/generated-sections.d.mts +143 -0
- package/types/engine/holdings.d.mts +6 -20
- package/types/engine/ids.d.mts +15 -12
- package/types/engine/index.d.mts +1 -1
- package/types/engine/infobox.d.mts +13 -3
- package/types/engine/journals.d.mts +24 -11
- package/types/engine/literature-notes.d.mts +29 -0
- package/types/engine/literature-works.d.mts +67 -0
- package/types/engine/map-graphviz.d.mts +2 -10
- package/types/engine/markdown-expressions.d.mts +12 -3
- package/types/engine/near-miss.d.mts +42 -0
- package/types/engine/note-addresses.d.mts +7 -1
- package/types/engine/note-event-terms.d.mts +68 -0
- package/types/engine/note-events.d.mts +71 -0
- package/types/engine/note-vocabulary.d.mts +37 -8
- package/types/engine/office-holders.d.mts +3 -0
- package/types/engine/pdf-index.d.mts +35 -0
- package/types/engine/pdf-render.d.mts +8 -2
- package/types/engine/place-relation-terms.d.mts +83 -0
- package/types/engine/place-relations.d.mts +11 -66
- package/types/engine/rank-ladder.d.mts +2 -0
- package/types/engine/region-events.d.mts +6 -12
- package/types/engine/scenes.d.mts +6 -32
- package/types/engine/site-build.d.mts +8 -22
- package/types/engine/site-maps.d.mts +15 -7
- package/types/engine/sql-tables.d.mts +77 -2
- package/types/engine/standing-terms.d.mts +37 -4
- package/types/engine/standings.d.mts +14 -9
- package/types/engine/wikilink-syntax.d.mts +20 -0
- package/types/engine/wikilinks.d.mts +3 -2
- package/docs/guides/government-migration.md +0 -52
- package/engine/itinerary-scenes.mjs +0 -166
- package/engine/lore-events.mjs +0 -197
- package/engine/map-notes.mjs +0 -1309
- package/engine/map-raster.mjs +0 -51
- package/hugo-theme/layouts/partials/holdings/entry.html +0 -21
- package/hugo-theme/layouts/partials/holdings/kind-groups.html +0 -71
- package/hugo-theme/layouts/partials/holdings.html +0 -61
- package/hugo-theme/layouts/partials/place-map.html +0 -9
- package/types/engine/itinerary-scenes.d.mts +0 -29
- package/types/engine/lore-events.d.mts +0 -14
- package/types/engine/map-notes.d.mts +0 -297
- package/types/engine/map-raster.d.mts +0 -8
package/engine/site-build.mjs
CHANGED
|
@@ -36,9 +36,9 @@
|
|
|
36
36
|
* `/<package>/<type>-<shortcode>/`. Nothing is generated between them — no
|
|
37
37
|
* section directory, no listing, no `_index.md` but the root's. An index of
|
|
38
38
|
* what the package publishes is a `doc` note carrying a content table, and it
|
|
39
|
-
* is authored where every other page is.
|
|
40
|
-
*
|
|
41
|
-
*
|
|
39
|
+
* is authored where every other page is. Every page is one file; the map from a
|
|
40
|
+
* place is set inline in its **From here** section — see
|
|
41
|
+
* `engine/site-maps.mjs`.
|
|
42
42
|
*
|
|
43
43
|
* **Every gate reports; none exits.** The integrity checks a site build needs —
|
|
44
44
|
* a wikilink authored in frontmatter, a name that yields no slug, an unusable
|
|
@@ -62,13 +62,14 @@ import matter from "gray-matter";
|
|
|
62
62
|
import { addressSlug } from "./content-address.mjs";
|
|
63
63
|
import { protectCode } from "./code-fences.mjs";
|
|
64
64
|
import { renderMarkdownExpressions } from "./markdown-expressions.mjs";
|
|
65
|
+
import { eventNoteIndex } from "./event-fields.mjs";
|
|
65
66
|
import { expandContentTables } from "./content-tables.mjs";
|
|
66
67
|
import { renderSpans, scanSpans } from "./content-spans.mjs";
|
|
67
68
|
import { renderBlocks, renderWithheldSections, scanBlocks } from "./content-blocks.mjs";
|
|
68
69
|
import { scanHeadingAttributes, withheldSections } from "./heading-attributes.mjs";
|
|
69
70
|
import { renderFigureBlocks, scanFigures } from "./content-figures.mjs";
|
|
70
71
|
import { footnoteFindings } from "./content-footnotes.mjs";
|
|
71
|
-
import { collectAnchors, markupAnchorFindings } from "./anchors.mjs";
|
|
72
|
+
import { collectAnchors, eventAnchors, markupAnchorFindings } from "./anchors.mjs";
|
|
72
73
|
import { checkImages, renderImageFigures } from "./content-images.mjs";
|
|
73
74
|
import { assetPathnameKey, pathnameProblem, resolvePathname } from "./pathnames.mjs";
|
|
74
75
|
import {
|
|
@@ -108,8 +109,9 @@ import {
|
|
|
108
109
|
import { publishesContentPages } from "../content-config.mjs";
|
|
109
110
|
import { HUGO_CONTENT } from "./site-config.mjs";
|
|
110
111
|
import { homepageLinkTargets, relatedPages } from "./related-pages.mjs";
|
|
111
|
-
import {
|
|
112
|
-
import {
|
|
112
|
+
import { drawSiteMaps, inlineDrawingFigure } from "./site-maps.mjs";
|
|
113
|
+
import { generatedDrawing, markGenerated, splitGenerated } from "./generated-sections.mjs";
|
|
114
|
+
import { IMAGE_PATTERN } from "./content-images.mjs";
|
|
113
115
|
|
|
114
116
|
const require = createRequire(import.meta.url);
|
|
115
117
|
|
|
@@ -565,10 +567,7 @@ export function tableUniverse(pages) {
|
|
|
565
567
|
* about every other page, known only once the whole tree has resolved — see
|
|
566
568
|
* {@link module:engine/related-pages} — so an authored value is dropped the
|
|
567
569
|
* way `aliases` is, and {@link renderPages} writes the derived block once it
|
|
568
|
-
* holds the graph.
|
|
569
|
-
* same reason — see {@link module:engine/holdings}. **So is `map`**: the
|
|
570
|
-
* file a place page names is the one the build drew beside it — see
|
|
571
|
-
* {@link module:engine/site-maps} — and a page with no drawing names none.
|
|
570
|
+
* holds the graph.
|
|
572
571
|
*
|
|
573
572
|
* @param {object} page - The page.
|
|
574
573
|
* @param {object} options
|
|
@@ -603,11 +602,8 @@ export function pageFrontmatter(page, { decorate, webSrc, artSrc }) {
|
|
|
603
602
|
kbfolder: page.folder,
|
|
604
603
|
};
|
|
605
604
|
if (decorate) decorate(data, page);
|
|
606
|
-
if (data.type === "affiliation" && data.data) delete data.data.domains;
|
|
607
605
|
delete data.aliases;
|
|
608
606
|
delete data.related;
|
|
609
|
-
for (const key of HOLDINGS_KEYS) delete data[key];
|
|
610
|
-
delete data.map;
|
|
611
607
|
if (webSrc && artSrc) resolveArtFields(data, webSrc, artSrc);
|
|
612
608
|
return data;
|
|
613
609
|
}
|
|
@@ -680,20 +676,11 @@ function isPlainObject(value) {
|
|
|
680
676
|
* `doc` note's `subType` may be spelled the same as another note's `type`, and
|
|
681
677
|
* `doc-gear.md` and `weapongear-gear.md` are distinct.
|
|
682
678
|
*
|
|
683
|
-
* **A page that carries a file beside itself is a leaf bundle**, `<slug>/index.md`,
|
|
684
|
-
* because that is the one shape under which Hugo hands a page its own
|
|
685
|
-
* resources — the map from a place, inlined by the theme through
|
|
686
|
-
* `.Resources.Get`. The directory is the page, not a section: it holds no
|
|
687
|
-
* `_index.md`, and the page's stated `url` keeps its address exactly where the
|
|
688
|
-
* flat file's was.
|
|
689
|
-
*
|
|
690
679
|
* @param {{slug: string}} page - The page.
|
|
691
|
-
* @param {object} [opts]
|
|
692
|
-
* @param {boolean} [opts.bundle=false] - Whether the page carries a resource.
|
|
693
680
|
* @returns {string} The file, relative to the mount.
|
|
694
681
|
*/
|
|
695
|
-
export function pageDestination(page
|
|
696
|
-
return
|
|
682
|
+
export function pageDestination(page) {
|
|
683
|
+
return `${page.slug}.md`;
|
|
697
684
|
}
|
|
698
685
|
|
|
699
686
|
/** Transform one content page without writing the site mount. */
|
|
@@ -710,6 +697,8 @@ export function renderSitePage(
|
|
|
710
697
|
config,
|
|
711
698
|
artIndex,
|
|
712
699
|
figuresByAddress = new Map(),
|
|
700
|
+
events,
|
|
701
|
+
drawings = new Map(),
|
|
713
702
|
},
|
|
714
703
|
) {
|
|
715
704
|
const tableErrors = [];
|
|
@@ -786,10 +775,21 @@ export function renderSitePage(
|
|
|
786
775
|
const resolve = (text) => {
|
|
787
776
|
let transformed = pass.beforeLinks ? pass.beforeLinks(text, page) : text;
|
|
788
777
|
transformed = resolveWebWikilinks(transformed, ctx);
|
|
789
|
-
return renderImageFigures(transformed, webSrcWithRole, lookupAsset);
|
|
778
|
+
return renderImageFigures(inlineDrawings(transformed), webSrcWithRole, lookupAsset);
|
|
790
779
|
};
|
|
780
|
+
// A generated **From here** section names the drawing the build made, which
|
|
781
|
+
// the asset host does not serve: it is set inline, so its place names stay
|
|
782
|
+
// links. A drawing this build did not make is left for the image pass,
|
|
783
|
+
// which reports it.
|
|
784
|
+
const inlineDrawings = (text) =>
|
|
785
|
+
text.replace(new RegExp(IMAGE_PATTERN.source, "g"), (whole, alt, src) => {
|
|
786
|
+
const name = generatedDrawing(src);
|
|
787
|
+
const drawing = name ? drawings.get(name) : undefined;
|
|
788
|
+
if (!drawing) return whole;
|
|
789
|
+
return inlineDrawingFigure(fs.readFileSync(drawing.file, "utf8"), alt);
|
|
790
|
+
});
|
|
791
791
|
|
|
792
|
-
const { markdown, errors, lineMap } = expandContentTables(page.body, {
|
|
792
|
+
const { markdown, errors, lineMap, generatedFrom } = expandContentTables(page.body, {
|
|
793
793
|
docs: universe.get(page.pkg) ?? [],
|
|
794
794
|
linkable,
|
|
795
795
|
source: src,
|
|
@@ -816,12 +816,14 @@ export function renderSitePage(
|
|
|
816
816
|
// This page's own anchors, so a `[[#slug]]` self-link is checked against
|
|
817
817
|
// what the page actually declares rather than trusted unconditionally.
|
|
818
818
|
ctx.anchors = new Set(collectAnchors(markdown).map((anchor) => anchor.slug));
|
|
819
|
-
|
|
819
|
+
ctx.eventAnchors = new Set(eventAnchors(page.fm).map((anchor) => anchor.slug));
|
|
820
|
+
const expressions = renderMarkdownExpressions(markGenerated(markdown, generatedFrom), {
|
|
820
821
|
fm: page.fm,
|
|
821
822
|
dates: index.dateContext,
|
|
822
823
|
file: page.file,
|
|
823
824
|
bodyLine: page.bodyLine,
|
|
824
825
|
sqlResults: sqlTables?.inline?.get(page.file),
|
|
826
|
+
events,
|
|
825
827
|
figures: {
|
|
826
828
|
get: (id) => figuresById.get(id),
|
|
827
829
|
// A cross-note `ref` resolves the written address exactly as a
|
|
@@ -840,14 +842,17 @@ export function renderSitePage(
|
|
|
840
842
|
});
|
|
841
843
|
expressionErrors.push(...expressions.findings);
|
|
842
844
|
const data = pageFrontmatter(page, { decorate, webSrc, artSrc });
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
845
|
+
// The author's text and the generated sections are resolved apart, so the
|
|
846
|
+
// links a generated section makes join no page's Related card: that card is
|
|
847
|
+
// what the authors link, and the sections already list what they derive.
|
|
848
|
+
const { authored, generated } = splitGenerated(expressions.markdown);
|
|
849
|
+
const authoredBody = protectCode(authored, resolve);
|
|
850
|
+
const authoredLinks = resolved.length;
|
|
851
|
+
const resolvedBody =
|
|
852
|
+
generated ? `${authoredBody}\n${protectCode(generated, resolve)}` : authoredBody;
|
|
853
|
+
const figured = renderFigureBlocks(resolvedBody, undefined, undefined, {
|
|
854
|
+
resolveRole: (address) => roleByWebSrc.get(address),
|
|
855
|
+
});
|
|
851
856
|
for (const error of figureScan.errors)
|
|
852
857
|
captionErrors.push({
|
|
853
858
|
file: page.file,
|
|
@@ -894,7 +899,7 @@ export function renderSitePage(
|
|
|
894
899
|
.markdown,
|
|
895
900
|
),
|
|
896
901
|
data,
|
|
897
|
-
resolved,
|
|
902
|
+
resolved: resolved.slice(0, authoredLinks),
|
|
898
903
|
tableErrors,
|
|
899
904
|
expressionErrors,
|
|
900
905
|
secretErrors,
|
|
@@ -929,25 +934,23 @@ export function renderSitePage(
|
|
|
929
934
|
* point the resolver answers, and inverted once between them. Nothing is
|
|
930
935
|
* resolved twice.
|
|
931
936
|
*
|
|
932
|
-
* **A map is
|
|
937
|
+
* **A map is set inline in its page.** The drawing from a place is made before
|
|
933
938
|
* this render — see {@link module:engine/site-maps} — and handed in keyed by
|
|
934
|
-
*
|
|
935
|
-
* copied beside it, and the front matter names it as `map`. Every other page
|
|
936
|
-
* is written flat, and carries no such key.
|
|
939
|
+
* the file name the page's **From here** section names.
|
|
937
940
|
*
|
|
938
941
|
* @param {object[]} pages - Every page.
|
|
939
942
|
* @param {object} options - Everything the render needs. `homepages` is the
|
|
940
943
|
* homepage as the index knows it — `{ fm, body, url, title }` — which takes
|
|
941
944
|
* no part in the render and every part in the graph: a content page links
|
|
942
945
|
* it by wikilink, and its own markdown links name content pages. `maps` is
|
|
943
|
-
* each drawing by the
|
|
946
|
+
* each drawing by the file name a **From here** section names.
|
|
944
947
|
* @returns {{written: number, byKind: Record<string, number>, tableErrors: object[],
|
|
945
948
|
* wikiErrors: object[], imageErrors: object[], embedErrors: object[],
|
|
946
949
|
* related: Map<string, import("./related-pages.mjs").Related>,
|
|
947
950
|
* maps: number}} `related`
|
|
948
951
|
* is keyed by page URL, and holds the homepage's block beside every content
|
|
949
952
|
* page's, so the caller can write it on the page this render does not.
|
|
950
|
-
* `maps` counts the pages
|
|
953
|
+
* `maps` counts the pages carrying a map.
|
|
951
954
|
*/
|
|
952
955
|
export function renderPages(pages, options) {
|
|
953
956
|
const {
|
|
@@ -1007,6 +1010,14 @@ export function renderPages(pages, options) {
|
|
|
1007
1010
|
figuresByAddress.set(key, byId);
|
|
1008
1011
|
}
|
|
1009
1012
|
|
|
1013
|
+
// Every note's events by Address, for an inline reference to an event's
|
|
1014
|
+
// field — the same lookup the Foundry compile and the book build.
|
|
1015
|
+
const events = eventNoteIndex(records, {
|
|
1016
|
+
contentPackage: index?.contentPackage,
|
|
1017
|
+
foreignIndex: foreign?.index,
|
|
1018
|
+
dates: index?.dateContext,
|
|
1019
|
+
});
|
|
1020
|
+
|
|
1010
1021
|
const tableErrors = [];
|
|
1011
1022
|
const expressionErrors = [];
|
|
1012
1023
|
const secretErrors = [];
|
|
@@ -1053,6 +1064,8 @@ export function renderPages(pages, options) {
|
|
|
1053
1064
|
config,
|
|
1054
1065
|
artIndex,
|
|
1055
1066
|
figuresByAddress,
|
|
1067
|
+
events,
|
|
1068
|
+
drawings: maps,
|
|
1056
1069
|
});
|
|
1057
1070
|
tableErrors.push(...result.tableErrors);
|
|
1058
1071
|
expressionErrors.push(...result.expressionErrors);
|
|
@@ -1068,42 +1081,16 @@ export function renderPages(pages, options) {
|
|
|
1068
1081
|
}
|
|
1069
1082
|
|
|
1070
1083
|
const related = relatedPages(edges, entries);
|
|
1071
|
-
// Geography follows parents; governing-body lists follow explicit government
|
|
1072
|
-
// references across this package and every fetched index.
|
|
1073
|
-
const holdings = holdingsPages(
|
|
1074
|
-
[
|
|
1075
|
-
...pages.map((page) =>
|
|
1076
|
-
holdingsNode(page.fm, { title: pageTitle(page), url: page.url, package: page.pkg }),
|
|
1077
|
-
),
|
|
1078
|
-
...foreignHoldingsNodes(foreign?.index),
|
|
1079
|
-
],
|
|
1080
|
-
{
|
|
1081
|
-
governmentNodes: records
|
|
1082
|
-
.filter((record) => !isGmNote(record))
|
|
1083
|
-
.map((record) =>
|
|
1084
|
-
holdingsNode(record, {
|
|
1085
|
-
title: record.name?.full ?? record.shortcode,
|
|
1086
|
-
package: config?.contentPackage ?? pages[0]?.pkg,
|
|
1087
|
-
}),
|
|
1088
|
-
),
|
|
1089
|
-
},
|
|
1090
|
-
);
|
|
1091
1084
|
|
|
1092
1085
|
let withMap = 0;
|
|
1093
1086
|
const outputs = capture === true ? new Map() : null;
|
|
1094
1087
|
for (const { page, body, data } of rendered) {
|
|
1095
1088
|
const block = related.get(page.url);
|
|
1096
1089
|
if (block) data.related = block;
|
|
1097
|
-
|
|
1098
|
-
const map = maps.get(page.url);
|
|
1099
|
-
if (map) {
|
|
1100
|
-
data.map = map.name;
|
|
1101
|
-
withMap += 1;
|
|
1102
|
-
}
|
|
1090
|
+
if (/\{#fromhere\}/.test(sqlTables?.get(page.file)?.generated ?? "")) withMap += 1;
|
|
1103
1091
|
if (write) {
|
|
1104
|
-
const dest = path.join(outRoot, pageDestination(page
|
|
1092
|
+
const dest = path.join(outRoot, pageDestination(page));
|
|
1105
1093
|
fs.mkdirSync(path.dirname(dest), { recursive: true });
|
|
1106
|
-
if (map) fs.copyFileSync(map.file, path.join(path.dirname(dest), map.name));
|
|
1107
1094
|
fs.writeFileSync(dest, matter.stringify(body, encodeAddresses(data)));
|
|
1108
1095
|
}
|
|
1109
1096
|
if (outputs) outputs.set(page.file, { markdown: body, frontmatter: data });
|
|
@@ -1166,7 +1153,11 @@ export function resolveSitePass(name, options) {
|
|
|
1166
1153
|
export function sitePageDecorator(config, index) {
|
|
1167
1154
|
const router = routerFor(config);
|
|
1168
1155
|
return (data, page) => {
|
|
1169
|
-
|
|
1156
|
+
// A being's dates reach the page; a note's events reach no page.
|
|
1157
|
+
const { events: _events, ...resolvedDates } = resolvedDateFields(
|
|
1158
|
+
page.fm,
|
|
1159
|
+
index.dateContext,
|
|
1160
|
+
);
|
|
1170
1161
|
if (Object.keys(resolvedDates).length) data.resolvedDates = resolvedDates;
|
|
1171
1162
|
data.infoboxes = noteInfoboxes(page.fm, {
|
|
1172
1163
|
resolve: (ref, hint) => resolveInfoboxRef(index, ref, hint),
|
|
@@ -1381,20 +1372,18 @@ export function buildSite({ config, sqlTables } = {}) {
|
|
|
1381
1372
|
config: resolved,
|
|
1382
1373
|
});
|
|
1383
1374
|
|
|
1384
|
-
// The map from each
|
|
1385
|
-
// be addressable and before any is written, so the page
|
|
1386
|
-
// drawing
|
|
1387
|
-
// in a drawing links through — the same `<base><slug>/`
|
|
1388
|
-
// composes.
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
})
|
|
1397
|
-
: { maps: new Map(), findings: [] };
|
|
1375
|
+
// The map from each place given a From here section, drawn now that every
|
|
1376
|
+
// page is known to be addressable and before any is written, so the page
|
|
1377
|
+
// writer sets each drawing inline in its section. Drawn with `base`, which
|
|
1378
|
+
// is what every name in a drawing links through — the same `<base><slug>/`
|
|
1379
|
+
// every other href composes. With `site.maps: false` no place is given the
|
|
1380
|
+
// section, nothing is drawn and nothing is asked of GraphViz.
|
|
1381
|
+
const drawn = drawSiteMaps({
|
|
1382
|
+
records: ctx.records,
|
|
1383
|
+
foreignIndex: gates.foreign.index,
|
|
1384
|
+
config: resolved,
|
|
1385
|
+
base,
|
|
1386
|
+
});
|
|
1398
1387
|
|
|
1399
1388
|
const rendered = renderPages(pages, {
|
|
1400
1389
|
outRoot: out,
|
package/engine/site-index.mjs
CHANGED
|
@@ -53,7 +53,7 @@ import { buildReferenceTargets } from "./reference-targets.mjs";
|
|
|
53
53
|
import { standingsDigest } from "./standings.mjs";
|
|
54
54
|
// The leaf reader, shared with the content index and the link checker, so a
|
|
55
55
|
// page's anchors are read once rather than re-derived a third way here.
|
|
56
|
-
import { collectAnchors } from "./anchors.mjs";
|
|
56
|
+
import { collectAnchors, eventAnchors } from "./anchors.mjs";
|
|
57
57
|
|
|
58
58
|
import { resolveShortcodeReference } from "./shortcode-references.mjs";
|
|
59
59
|
|
|
@@ -247,6 +247,8 @@ export function buildSiteIndex(
|
|
|
247
247
|
// URL unverified. A Set even when empty: a page that declares no
|
|
248
248
|
// anchor still has a definite answer for one an author names.
|
|
249
249
|
anchors: new Set(collectAnchors(e.body ?? "").map((anchor) => anchor.slug)),
|
|
250
|
+
// The note's event ids, which a wikilink may not name.
|
|
251
|
+
eventAnchors: new Set(eventAnchors(e.fm).map((anchor) => anchor.slug)),
|
|
250
252
|
};
|
|
251
253
|
|
|
252
254
|
const shortcode = e.fm.shortcode;
|
package/engine/site-maps.mjs
CHANGED
|
@@ -12,24 +12,22 @@
|
|
|
12
12
|
*/
|
|
13
13
|
|
|
14
14
|
/**
|
|
15
|
-
* The maps a site build
|
|
15
|
+
* The maps a site build draws for its place pages.
|
|
16
16
|
*
|
|
17
|
-
* A place that states, or is named in, a border or a route
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
* the build renders does.
|
|
17
|
+
* A place that states, or is named in, a border or a route is given a
|
|
18
|
+
* **From here** section — see {@link module:engine/derived-sections} — naming
|
|
19
|
+
* the drawing `package-build map --from` makes. The site build draws that map
|
|
20
|
+
* for every such place and sets it inline in the section, so its place names
|
|
21
|
+
* are links to their pages: every drawing is made with the site's base, and
|
|
22
|
+
* every `href` in it composes `<base><slug>/` the way every other href the
|
|
23
|
+
* build renders does.
|
|
25
24
|
*
|
|
26
|
-
* The npm Graphviz runtime draws each map. `site.maps: false`
|
|
27
|
-
*
|
|
25
|
+
* The npm Graphviz runtime draws each map. `site.maps: false` gives no place
|
|
26
|
+
* the section, and nothing is drawn.
|
|
28
27
|
*
|
|
29
28
|
* The drawings land under `build/map/site/` — beside the author's own
|
|
30
29
|
* drawings under `build/map/`, and apart from them, because the site's
|
|
31
|
-
* carry links and the author's may not
|
|
32
|
-
* content mount. A build never mutates its inputs.
|
|
30
|
+
* carry links and the author's may not. A build never mutates its inputs.
|
|
33
31
|
*
|
|
34
32
|
* @module
|
|
35
33
|
*/
|
|
@@ -37,7 +35,9 @@
|
|
|
37
35
|
import fs from "node:fs";
|
|
38
36
|
import path from "node:path";
|
|
39
37
|
|
|
40
|
-
import {
|
|
38
|
+
import { placesWithMaps } from "./derived-sections.mjs";
|
|
39
|
+
import { fromHereFile } from "./generated-sections.mjs";
|
|
40
|
+
import { buildMaps } from "./map-build.mjs";
|
|
41
41
|
import { mapWorld } from "./map-places.mjs";
|
|
42
42
|
|
|
43
43
|
/**
|
|
@@ -51,8 +51,7 @@ export const SITE_MAP_DIR = "build/map/site";
|
|
|
51
51
|
* A drawing as the page writer receives it.
|
|
52
52
|
*
|
|
53
53
|
* @typedef {object} SiteMap
|
|
54
|
-
* @property {string} name - The file's name
|
|
55
|
-
* value of the page's `map` key: `from-<shortcode>.svg`.
|
|
54
|
+
* @property {string} name - The file's name: `from-<shortcode>.svg`.
|
|
56
55
|
* @property {string} file - Where the drawing is, absolute.
|
|
57
56
|
*/
|
|
58
57
|
|
|
@@ -86,7 +85,7 @@ export function inlineSvg(svg) {
|
|
|
86
85
|
}
|
|
87
86
|
|
|
88
87
|
/**
|
|
89
|
-
* Draw the map from every
|
|
88
|
+
* Draw the map from every place of this package that is given one.
|
|
90
89
|
*
|
|
91
90
|
* @param {object} opts
|
|
92
91
|
* @param {Array<Record<string, any>>} opts.records - The package's index
|
|
@@ -98,8 +97,8 @@ export function inlineSvg(svg) {
|
|
|
98
97
|
* ending in a slash: what every name in a drawing links through.
|
|
99
98
|
* @returns {{maps: Map<string, SiteMap>, findings: Array<{file: string,
|
|
100
99
|
* line?: number, column?: number, severity: "error"|"warning",
|
|
101
|
-
* message: string}>}} The drawings, keyed by the
|
|
102
|
-
*
|
|
100
|
+
* message: string}>}} The drawings, keyed by the file name a **From here**
|
|
101
|
+
* section names, and warnings for relations that name no place.
|
|
103
102
|
*/
|
|
104
103
|
export function drawSiteMaps({ records, foreignIndex, config, base }) {
|
|
105
104
|
/** @type {Map<string, SiteMap>} */
|
|
@@ -109,6 +108,8 @@ export function drawSiteMaps({ records, foreignIndex, config, base }) {
|
|
|
109
108
|
// the map an earlier run drew.
|
|
110
109
|
fs.rmSync(outDir, { recursive: true, force: true });
|
|
111
110
|
|
|
111
|
+
const centres = placesWithMaps(records, { foreignIndex, config });
|
|
112
|
+
if (centres.length === 0) return { maps, findings: [] };
|
|
112
113
|
const world = mapWorld({
|
|
113
114
|
records,
|
|
114
115
|
foreignIndex,
|
|
@@ -116,20 +117,33 @@ export function drawSiteMaps({ records, foreignIndex, config, base }) {
|
|
|
116
117
|
base,
|
|
117
118
|
config,
|
|
118
119
|
});
|
|
119
|
-
// This package's related places, which are the ones with a page here to
|
|
120
|
-
// carry a map. A dependency's place is drawn on those maps and gets none
|
|
121
|
-
// of its own from this build.
|
|
122
|
-
const centres = relatedPlaces(world.places).filter((s) => world.places.get(s)?.local);
|
|
123
|
-
if (centres.length === 0) return { maps, findings: [] };
|
|
124
|
-
|
|
125
120
|
const { findings } = buildMaps({ world, outDir, from: centres });
|
|
126
121
|
for (const centre of centres) {
|
|
127
|
-
const
|
|
128
|
-
if (!place?.url) continue;
|
|
129
|
-
const name = `from-${centre}.svg`;
|
|
122
|
+
const name = fromHereFile(centre);
|
|
130
123
|
const file = path.join(outDir, name);
|
|
131
124
|
fs.writeFileSync(file, inlineSvg(fs.readFileSync(file, "utf8")));
|
|
132
|
-
maps.set(
|
|
125
|
+
maps.set(name, { name, file });
|
|
133
126
|
}
|
|
134
127
|
return { maps, findings };
|
|
135
128
|
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* The figure the website sets a drawing in: the SVG inline, so its place names
|
|
132
|
+
* stay links, with no blank line inside it — a blank line would end the HTML
|
|
133
|
+
* block and hand the rest of the drawing to the Markdown renderer.
|
|
134
|
+
*
|
|
135
|
+
* @param {string} svg - The drawing, as {@link inlineSvg} wrote it.
|
|
136
|
+
* @param {string} alt - What the drawing shows, for a screen reader.
|
|
137
|
+
* @returns {string} One HTML block.
|
|
138
|
+
*/
|
|
139
|
+
export function inlineDrawingFigure(svg, alt) {
|
|
140
|
+
const label = String(alt ?? "").replace(/[&<>"]/g, (c) => `&#${c.charCodeAt(0)};`);
|
|
141
|
+
const body = String(svg)
|
|
142
|
+
.split("\n")
|
|
143
|
+
.filter((line) => line.trim() !== "")
|
|
144
|
+
.join("\n");
|
|
145
|
+
return (
|
|
146
|
+
`<figure class="note-image note-image-full-width" role="img" ` +
|
|
147
|
+
`aria-label="${label}">\n${body}\n</figure>`
|
|
148
|
+
);
|
|
149
|
+
}
|
package/engine/site-preview.mjs
CHANGED
|
@@ -17,17 +17,25 @@ import { applyComputedBeingAge, presentAmongRecords } from "./being-age.mjs";
|
|
|
17
17
|
import { reckoningContext } from "./reckoning-markers.mjs";
|
|
18
18
|
import { cachedMetadataIndexes, noContentIndexPackages } from "./metadata-index.mjs";
|
|
19
19
|
import { buildSiteIndex } from "./site-index.mjs";
|
|
20
|
-
import {
|
|
20
|
+
import {
|
|
21
|
+
attachGeneratedSections,
|
|
22
|
+
findSqlBlocks,
|
|
23
|
+
generatedSectionsFor,
|
|
24
|
+
joinGeneratedSections,
|
|
25
|
+
openNotesDatabase,
|
|
26
|
+
prepareSqlTables,
|
|
27
|
+
} from "./sql-tables.mjs";
|
|
21
28
|
import { findPageListBlocks } from "./page-lists.mjs";
|
|
22
29
|
import { isGmNote } from "./note-vocabulary.mjs";
|
|
23
30
|
import { relatedPages } from "./related-pages.mjs";
|
|
24
|
-
import { holdingsNode, holdingsPages, foreignHoldingsNodes } from "./holdings.mjs";
|
|
25
31
|
import { assetAddressIndex } from "./art-fields.mjs";
|
|
32
|
+
import { eventNoteIndex } from "./event-fields.mjs";
|
|
26
33
|
import { frontmatterWikilinks } from "./web-wikilinks.mjs";
|
|
27
34
|
import { positionOfLiteral } from "./diagnostics.mjs";
|
|
28
35
|
import { addressSlug } from "./content-address.mjs";
|
|
29
36
|
import { homepageTitle } from "./homepage.mjs";
|
|
30
37
|
import { loadPackConfig } from "./pack-config.mjs";
|
|
38
|
+
import { drawSiteMaps } from "./site-maps.mjs";
|
|
31
39
|
import {
|
|
32
40
|
collectContentPages,
|
|
33
41
|
collectHomepages,
|
|
@@ -97,7 +105,19 @@ export async function prepareSitePreview({ config = loadPackConfig() } = {}) {
|
|
|
97
105
|
)
|
|
98
106
|
.map((page) => ({ source: page.file, markdown: page.body }));
|
|
99
107
|
const publicRecords = records.filter((record) => !isGmNote(record));
|
|
108
|
+
const generated = await generatedSectionsFor(db, publicRecords, contentBase, {
|
|
109
|
+
foreignIndex: gates.foreign.index,
|
|
110
|
+
config,
|
|
111
|
+
});
|
|
112
|
+
joinGeneratedSections(generated, sources);
|
|
100
113
|
const sqlTables = await prepareSqlTables(db, sources, { records: publicRecords });
|
|
114
|
+
attachGeneratedSections(sqlTables, generated);
|
|
115
|
+
const drawings = drawSiteMaps({
|
|
116
|
+
records,
|
|
117
|
+
foreignIndex: gates.foreign.index,
|
|
118
|
+
config,
|
|
119
|
+
base,
|
|
120
|
+
}).maps;
|
|
101
121
|
const rendered = renderPages(pages, {
|
|
102
122
|
index: gates.index,
|
|
103
123
|
foreign: gates.foreign,
|
|
@@ -108,10 +128,11 @@ export async function prepareSitePreview({ config = loadPackConfig() } = {}) {
|
|
|
108
128
|
config,
|
|
109
129
|
records,
|
|
110
130
|
homepages: homeEntries,
|
|
131
|
+
maps: drawings,
|
|
111
132
|
write: false,
|
|
112
133
|
capture: "graph",
|
|
113
134
|
});
|
|
114
|
-
return { records, pages, homeEntries, gates, db, rendered };
|
|
135
|
+
return { records, pages, homeEntries, gates, db, rendered, drawings };
|
|
115
136
|
} catch (error) {
|
|
116
137
|
await db.close();
|
|
117
138
|
throw error;
|
|
@@ -219,11 +240,16 @@ export async function prepareSitePreview({ config = loadPackConfig() } = {}) {
|
|
|
219
240
|
},
|
|
220
241
|
);
|
|
221
242
|
}
|
|
222
|
-
const
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
243
|
+
const publicRecords = records.filter((record) => !isGmNote(record));
|
|
244
|
+
const sources = [{ source: absolute, markdown: body }];
|
|
245
|
+
const generated = await generatedSectionsFor(db, publicRecords, contentBase, {
|
|
246
|
+
only: absolute,
|
|
247
|
+
foreignIndex: snapshot.gates.foreign.index,
|
|
248
|
+
config,
|
|
249
|
+
});
|
|
250
|
+
joinGeneratedSections(generated, sources);
|
|
251
|
+
const sqlTables = await prepareSqlTables(db, sources, { records: publicRecords });
|
|
252
|
+
attachGeneratedSections(sqlTables, generated);
|
|
227
253
|
const allPages = snapshot.pages.map((item) =>
|
|
228
254
|
item.file === absolute ? page : item,
|
|
229
255
|
);
|
|
@@ -240,6 +266,12 @@ export async function prepareSitePreview({ config = loadPackConfig() } = {}) {
|
|
|
240
266
|
foreign: snapshot.gates.foreign,
|
|
241
267
|
types: index.contentTypes,
|
|
242
268
|
}),
|
|
269
|
+
events: eventNoteIndex(records, {
|
|
270
|
+
contentPackage: index.contentPackage,
|
|
271
|
+
foreignIndex: snapshot.gates.foreign?.index,
|
|
272
|
+
dates: index.dateContext,
|
|
273
|
+
}),
|
|
274
|
+
drawings: snapshot.drawings,
|
|
243
275
|
});
|
|
244
276
|
for (const error of result.tableErrors)
|
|
245
277
|
findings.push({
|
|
@@ -284,29 +316,6 @@ export async function prepareSitePreview({ config = loadPackConfig() } = {}) {
|
|
|
284
316
|
});
|
|
285
317
|
const related = relatedPages(edges, entries).get(page.url);
|
|
286
318
|
if (related) result.data.related = related;
|
|
287
|
-
const holdings = holdingsPages(
|
|
288
|
-
[
|
|
289
|
-
...allPages.map((item) =>
|
|
290
|
-
holdingsNode(item.fm, {
|
|
291
|
-
title: item.name,
|
|
292
|
-
url: item.url,
|
|
293
|
-
package: item.pkg ?? config.contentPackage,
|
|
294
|
-
}),
|
|
295
|
-
),
|
|
296
|
-
...foreignHoldingsNodes(snapshot.gates.foreign.index),
|
|
297
|
-
],
|
|
298
|
-
{
|
|
299
|
-
governmentNodes: records
|
|
300
|
-
.filter((record) => !isGmNote(record))
|
|
301
|
-
.map((record) =>
|
|
302
|
-
holdingsNode(record, {
|
|
303
|
-
title: record.name?.full ?? record.shortcode,
|
|
304
|
-
package: config.contentPackage,
|
|
305
|
-
}),
|
|
306
|
-
),
|
|
307
|
-
},
|
|
308
|
-
);
|
|
309
|
-
Object.assign(result.data, holdings.get(page.url));
|
|
310
319
|
return {
|
|
311
320
|
ok: true,
|
|
312
321
|
markdown: result.body,
|
package/engine/social-ties.mjs
CHANGED
|
@@ -127,7 +127,8 @@ export function checkSocialTies(note, { index } = {}) {
|
|
|
127
127
|
});
|
|
128
128
|
}
|
|
129
129
|
}
|
|
130
|
-
|
|
130
|
+
// A term that is not text is the inner-key check's finding.
|
|
131
|
+
if ((typeof term === "string" || term === null || term === undefined) && !TERMS.has(term))
|
|
131
132
|
findings.push({
|
|
132
133
|
...at(sourceKey),
|
|
133
134
|
message: `data.socialTies value ${JSON.stringify(term)} must be one of ${SOCIAL_TIES.map(({ term: value }) => value).join(", ")}`,
|