@heroiclands/package-build 22.15.1 → 22.16.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 +95 -0
- package/README.md +39 -678
- package/bin/{content-build.mjs → content-commands.mjs} +197 -118
- package/bin/package-build.mjs +303 -42
- package/bin/report.mjs +1 -1
- package/ci/ci-docker.mjs +6 -3
- package/ci/ci-steps.mjs +6 -4
- package/ci/content-format-reference.mjs +106 -0
- package/config.mjs +22 -2
- package/content-config.mjs +74 -134
- package/docs/api.md +294 -234
- package/docs/authoring/assets.md +31 -0
- package/docs/authoring/dates-and-calendars.md +33 -0
- package/docs/authoring/first-note.md +48 -0
- package/docs/authoring/frontmatter.md +40 -0
- package/docs/authoring/links-and-markup.md +137 -0
- package/docs/commands.md +631 -162
- package/docs/configuration.md +176 -140
- package/docs/content-format.md +50 -4229
- package/docs/design.md +32 -0
- package/docs/diagnostics.md +29 -22
- package/docs/getting-started.md +21 -877
- package/docs/guides/book.md +153 -0
- package/docs/guides/e2e.md +143 -0
- package/docs/guides/icons.md +126 -0
- package/docs/guides/localization.md +120 -0
- package/docs/guides/module-project.md +915 -0
- package/docs/guides/packs.md +167 -0
- package/docs/guides/reference-pages.md +148 -0
- package/docs/guides/release-and-deploy.md +114 -0
- package/docs/guides/site.md +152 -0
- package/docs/guides/system-project.md +45 -0
- package/docs/index.md +16 -0
- package/docs/project-setup.md +54 -35
- package/docs/reference/format-details.md +4420 -0
- package/docs/reference/note-types.md +531 -0
- package/engine/address-render.mjs +26 -0
- package/engine/address.mjs +3 -20
- package/engine/anchors.mjs +15 -6
- package/engine/asset-bindings.mjs +6 -2
- package/engine/base-compiler.mjs +47 -11
- package/engine/being-measurements.mjs +70 -0
- package/engine/calendar-artifacts.mjs +1 -1
- package/engine/calendar-choice.mjs +32 -0
- package/engine/calendar-human.mjs +71 -0
- package/engine/calendar-notes.mjs +41 -3
- package/engine/calendars.mjs +8 -1
- package/engine/changelog-lint.mjs +2 -2
- package/engine/code-fences.mjs +23 -97
- package/engine/compile-corpus.mjs +6 -7
- package/engine/content-admonitions.mjs +114 -0
- package/engine/content-captions.mjs +181 -0
- package/engine/content-embeds.mjs +2 -2
- package/engine/content-footnotes.mjs +34 -0
- package/engine/content-format-check.mjs +2 -2
- package/engine/content-format.mjs +127 -73
- package/engine/content-format.yaml +745 -0
- package/engine/content-html.mjs +1 -1
- package/engine/content-icons.mjs +45 -92
- package/engine/content-images.mjs +22 -74
- package/engine/content-index.mjs +12 -3
- package/engine/content-links.mjs +21 -26
- package/engine/content-lint.mjs +1 -5
- package/engine/content-secrets.mjs +3 -1
- package/engine/content-tables.mjs +47 -1257
- package/engine/culture-choice.mjs +32 -0
- package/engine/date-conversion.mjs +47 -0
- package/engine/declaration-check.mjs +87 -0
- package/engine/docs-checks.mjs +140 -0
- package/engine/documentation-links.mjs +31 -0
- package/engine/exported-scene.mjs +1 -1
- package/engine/extension-attributes.mjs +74 -0
- package/engine/field-reference.mjs +1 -1
- package/engine/field-spec.mjs +4 -0
- package/engine/foreign-catalog.mjs +5 -5
- package/engine/format-generated.mjs +15 -0
- package/engine/foundry-entries.mjs +3 -1
- package/engine/frontmatter-lint.mjs +5 -1
- package/engine/frontmatter.mjs +10 -0
- package/engine/generate.mjs +2 -2
- package/engine/helpers.mjs +71 -117
- package/engine/homepage.mjs +2 -3
- package/engine/index-records.mjs +1 -0
- package/engine/index.mjs +18 -3
- package/engine/infobox-registry.mjs +3 -1
- package/engine/infobox.mjs +38 -20
- package/engine/item-compiler.mjs +10 -8
- package/engine/item-docs.mjs +7 -6
- package/engine/journals.mjs +51 -10
- package/engine/macros.mjs +1 -1
- package/engine/map-build.mjs +1 -1
- package/engine/map-dot.mjs +1 -1
- package/engine/markdown-expressions.mjs +117 -0
- package/engine/metadata-index.mjs +7 -3
- package/engine/note-addresses.mjs +1 -0
- package/engine/note-dates.mjs +283 -61
- package/engine/note-package.mjs +5 -25
- package/engine/note-state.mjs +6 -2
- package/engine/note-vocabulary.mjs +74 -19
- package/engine/number-words.mjs +75 -0
- package/engine/office-holders.mjs +188 -0
- package/engine/pack-config.mjs +2 -15
- package/engine/pack-router.mjs +24 -5
- package/engine/packages.mjs +3 -2
- package/engine/pathnames.mjs +21 -24
- package/engine/pdf-build.mjs +217 -55
- package/engine/pdf-fonts.mjs +2 -2
- package/engine/pdf-images.mjs +82 -0
- package/engine/pdf-render.mjs +174 -22
- package/engine/populations.mjs +3 -2
- package/engine/project-init.mjs +499 -0
- package/engine/prose-config.mjs +1 -1
- package/engine/prose-lint.mjs +7 -6
- package/engine/readability-lint.mjs +86 -0
- package/engine/reckoning-markers.mjs +205 -93
- package/engine/scenes.mjs +5 -2
- package/engine/schema-check.mjs +3 -3
- package/engine/site-build.mjs +87 -37
- package/engine/site-config.mjs +8 -8
- package/engine/site-index.mjs +4 -0
- package/engine/site-maps.mjs +1 -1
- package/engine/site-preview.mjs +19 -8
- package/engine/social-ties.mjs +5 -1
- package/engine/sql-tables.mjs +92 -22
- package/engine/web-wikilinks.mjs +15 -31
- package/engine/wikilink-syntax.mjs +4 -1
- package/engine/wikilinks.mjs +12 -14
- package/hm3/item-fields.mjs +1 -1
- package/hm3/items.mjs +12 -45
- package/index.mjs +2 -14
- package/manifest.mjs +31 -4
- package/markdownlint-config.mjs +1 -1
- package/package.json +32 -11
- package/prettier-config.mjs +1 -1
- package/sohl/being-info.mjs +3 -145
- package/sohl/index.mjs +1 -1
- package/sohl/infobox.mjs +2 -1
- package/sohl/item-builders.mjs +3 -27
- package/sohl/item-fields.mjs +61 -22
- package/sohl/kb-passes.mjs +2 -2
- package/sohl/note-schemas.mjs +0 -8
- package/stage.mjs +19 -0
- package/types/config.d.mts +12 -3
- package/types/content-config.d.mts +15 -81
- package/types/engine/address-render.d.mts +13 -0
- package/types/engine/address.d.mts +2 -11
- package/types/engine/anchors.d.mts +3 -4
- package/types/engine/asset-bindings.d.mts +9 -2
- package/types/engine/base-compiler.d.mts +1 -2
- package/types/engine/being-measurements.d.mts +16 -0
- package/types/engine/calendar-choice.d.mts +14 -0
- package/types/engine/calendar-human.d.mts +4 -0
- package/types/engine/calendar-notes.d.mts +2 -0
- package/types/engine/calendars.d.mts +5 -3
- package/types/engine/code-fences.d.mts +6 -72
- package/types/engine/compile-corpus.d.mts +1 -3
- package/types/engine/content-admonitions.d.mts +24 -0
- package/types/engine/content-captions.d.mts +45 -0
- package/types/engine/content-footnotes.d.mts +10 -0
- package/types/engine/content-format-check.d.mts +17 -4
- package/types/engine/content-format.d.mts +24 -11
- package/types/engine/content-icons.d.mts +14 -23
- package/types/engine/content-images.d.mts +2 -2
- package/types/engine/content-index.d.mts +9 -2
- package/types/engine/content-links.d.mts +3 -5
- package/types/engine/content-tables.d.mts +11 -140
- package/types/engine/culture-choice.d.mts +14 -0
- package/types/engine/date-conversion.d.mts +4 -0
- package/types/engine/declaration-check.d.mts +24 -0
- package/types/engine/docs-checks.d.mts +28 -0
- package/types/engine/documentation-links.d.mts +7 -0
- package/types/engine/exported-scene.d.mts +6 -2
- package/types/engine/extension-attributes.d.mts +14 -0
- package/types/engine/field-spec.d.mts +9 -0
- package/types/engine/format-generated.d.mts +7 -0
- package/types/engine/frontmatter.d.mts +14 -4
- package/types/engine/generate.d.mts +1 -1
- package/types/engine/helpers.d.mts +37 -92
- package/types/engine/homepage.d.mts +2 -3
- package/types/engine/index.d.mts +5 -0
- package/types/engine/infobox-registry.d.mts +3 -1
- package/types/engine/infobox.d.mts +17 -6
- package/types/engine/item-compiler.d.mts +4 -4
- package/types/engine/item-docs.d.mts +4 -3
- package/types/engine/journals.d.mts +10 -7
- package/types/engine/map-graphviz.d.mts +1 -1
- package/types/engine/markdown-expressions.d.mts +17 -0
- package/types/engine/note-dates.d.mts +47 -1
- package/types/engine/note-package.d.mts +3 -19
- package/types/engine/note-state.d.mts +2 -1
- package/types/engine/note-vocabulary.d.mts +9 -0
- package/types/engine/number-words.d.mts +4 -0
- package/types/engine/office-holders.d.mts +14 -0
- package/types/engine/pack-config.d.mts +1 -1
- package/types/engine/packages.d.mts +3 -2
- package/types/engine/pdf-images.d.mts +38 -0
- package/types/engine/populations.d.mts +2 -2
- package/types/engine/project-init.d.mts +17 -0
- package/types/engine/prose-config.d.mts +1 -1
- package/types/engine/prose-lint.d.mts +3 -2
- package/types/engine/readability-lint.d.mts +22 -0
- package/types/engine/reckoning-markers.d.mts +4 -2
- package/types/engine/schema-check.d.mts +3 -3
- package/types/engine/site-build.d.mts +38 -28
- package/types/engine/site-config.d.mts +2 -2
- package/types/engine/site-index.d.mts +4 -0
- package/types/engine/site-preview.d.mts +1 -1
- package/types/engine/social-ties.d.mts +8 -2
- package/types/engine/sql-tables.d.mts +13 -2
- package/types/engine/web-wikilinks.d.mts +2 -1
- package/types/engine/wikilinks.d.mts +7 -1
- package/types/hm3/items.d.mts +5 -6
- package/types/labels.d.mts +40 -0
- package/types/manifest.d.mts +3 -3
- package/types/sohl/actors.d.mts +2 -2
- package/types/sohl/being-info.d.mts +3 -43
- package/types/sohl/index.d.mts +1 -1
- package/types/sohl/infobox.d.mts +5 -2
- package/types/sohl/item-fields.d.mts +4 -13
- package/types/stage.d.mts +9 -0
- package/CONTENT.md +0 -3239
package/engine/site-build.mjs
CHANGED
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
/**
|
|
15
15
|
* Publishing a content tree as a website.
|
|
16
16
|
*
|
|
17
|
-
* Compiling a content tree into compendium packs is `
|
|
17
|
+
* Compiling a content tree into compendium packs is `package-build package
|
|
18
18
|
* compile`. Publishing the *same tree* as a website was a script each consumer
|
|
19
19
|
* wrote for itself — 473 code lines in `sohl` and 462 in `sohl-thalorna`, 87 of
|
|
20
20
|
* them identical — and the copies drifted in ways neither repository could see.
|
|
@@ -60,8 +60,11 @@ import matter from "gray-matter";
|
|
|
60
60
|
|
|
61
61
|
import { addressSlug } from "./content-address.mjs";
|
|
62
62
|
import { protectCode } from "./code-fences.mjs";
|
|
63
|
+
import { renderMarkdownExpressions } from "./markdown-expressions.mjs";
|
|
63
64
|
import { expandContentTables } from "./content-tables.mjs";
|
|
64
65
|
import { renderSecretBlocks } from "./content-secrets.mjs";
|
|
66
|
+
import { renderAdmonitions, scanAdmonitions } from "./content-admonitions.mjs";
|
|
67
|
+
import { renderCaptionBlocks, scanCaptions } from "./content-captions.mjs";
|
|
65
68
|
import { renderImageFigures } from "./content-images.mjs";
|
|
66
69
|
import { pathnameProblem, resolvePathname } from "./pathnames.mjs";
|
|
67
70
|
import { buildSiteIndex, resolveInfoboxRef, wikiContext } from "./site-index.mjs";
|
|
@@ -69,7 +72,6 @@ import { frontmatterWikilinks, resolveWebWikilinks } from "./web-wikilinks.mjs";
|
|
|
69
72
|
import { loadForeignIndexes, noContentIndexPackages } from "./metadata-index.mjs";
|
|
70
73
|
import { noteInfoboxes } from "./infobox-registry.mjs";
|
|
71
74
|
import { formatUnaddressableFinding, unaddressableForeignPackages } from "./metadata-index.mjs";
|
|
72
|
-
import { deriveBeingInfo, isBeing } from "../sohl/being-info.mjs";
|
|
73
75
|
import { loadPackConfig } from "./pack-config.mjs";
|
|
74
76
|
import { routerFor } from "./pack-router.mjs";
|
|
75
77
|
import { searchableFrontmatter } from "./note-package.mjs";
|
|
@@ -79,9 +81,11 @@ import { indexRecordsFor } from "./content-index.mjs";
|
|
|
79
81
|
// reuses `collectContentPages`'s pages, so wiring it here reaches the book too.
|
|
80
82
|
import { applyComputedBeingAge, presentAmongRecords } from "./being-age.mjs";
|
|
81
83
|
import { reckoningContext } from "./reckoning-markers.mjs";
|
|
84
|
+
import { resolvedDateFields } from "./note-dates.mjs";
|
|
82
85
|
import { isNoteRecord, noteFile } from "./index-records.mjs";
|
|
83
86
|
// The one statement of what an empty body means, shared with the index.
|
|
84
87
|
import { isStubNote } from "./note-state.mjs";
|
|
88
|
+
import { isGmNote } from "./note-vocabulary.mjs";
|
|
85
89
|
import { ART_SLOTS, artPathname, assetAddressIndex } from "./art-fields.mjs";
|
|
86
90
|
import {
|
|
87
91
|
HOMEPAGE_DESTINATION,
|
|
@@ -226,7 +230,7 @@ export function collectContentPages(contentBase, ctx) {
|
|
|
226
230
|
// the index apply one rule from one module and a note that cannot be
|
|
227
231
|
// addressed for some *other* reason still reaches the finding below
|
|
228
232
|
// that says so.
|
|
229
|
-
if (isStubNote(fm, body)) continue;
|
|
233
|
+
if (isStubNote(fm, body) || isGmNote(fm)) continue;
|
|
230
234
|
|
|
231
235
|
for (const hit of frontmatterWikilinks(fm)) {
|
|
232
236
|
fmLinkFindings.push({ file, ...hit });
|
|
@@ -286,11 +290,8 @@ export function collectContentPages(contentBase, ctx) {
|
|
|
286
290
|
/**
|
|
287
291
|
* The package's homepage notes — the authored page at `/<contentPackage>/`.
|
|
288
292
|
*
|
|
289
|
-
* A separate
|
|
290
|
-
*
|
|
291
|
-
* content tree is never read for pages at all, so the licensing constraint two
|
|
292
|
-
* packages ship under is a property of the code path rather than of a
|
|
293
|
-
* configuration that happens to be empty.
|
|
293
|
+
* A separate collection from {@link collectContentPages} keeps the homepage
|
|
294
|
+
* at the package root and leaves content pages at their note addresses.
|
|
294
295
|
*
|
|
295
296
|
* Returned as a list rather than as the one note there should be, because the
|
|
296
297
|
* count is what {@link checkHomepageCount} judges — this walk reports
|
|
@@ -298,8 +299,7 @@ export function collectContentPages(contentBase, ctx) {
|
|
|
298
299
|
*
|
|
299
300
|
* A homepage that declares no `shortcode` has no address, and is
|
|
300
301
|
* reported rather than written: it is the same finding a content page's missing
|
|
301
|
-
* shortcode produces, and it
|
|
302
|
-
* no other gate runs.
|
|
302
|
+
* shortcode produces, and it is reported even when the tree has no other notes.
|
|
303
303
|
*
|
|
304
304
|
* **It is still counted.** An unaddressable homepage is a homepage — dropping
|
|
305
305
|
* it from the list would make {@link checkHomepageCount} report a tree with one
|
|
@@ -317,6 +317,10 @@ export function collectHomepages(contentBase, ctx) {
|
|
|
317
317
|
for (const file of siteCorpusFiles(contentBase, ctx)) {
|
|
318
318
|
const note = readNote(file, ctx);
|
|
319
319
|
if (!note || !isHomepage(note.fm)) continue;
|
|
320
|
+
if (isGmNote(note.fm)) {
|
|
321
|
+
addressFindings.push({ file, reason: "a GM-tagged homepage cannot publish publicly" });
|
|
322
|
+
continue;
|
|
323
|
+
}
|
|
320
324
|
try {
|
|
321
325
|
addressSlug(note.fm);
|
|
322
326
|
} catch (err) {
|
|
@@ -332,13 +336,10 @@ export function collectHomepages(contentBase, ctx) {
|
|
|
332
336
|
*
|
|
333
337
|
* Its own writer, deliberately small. A homepage is authored markdown published
|
|
334
338
|
* verbatim — no table expansion and no link resolution — so routing it through
|
|
335
|
-
* {@link renderPages} would
|
|
336
|
-
* make homepage-only mode depend on the index, the foreign manifests and the
|
|
337
|
-
* table universe that mode exists to not build.
|
|
339
|
+
* {@link renderPages} would give it a pipeline it does not use.
|
|
338
340
|
*
|
|
339
341
|
* **Verbatim is the answer, not a gap.** A homepage's links could not be
|
|
340
|
-
* *resolved* here
|
|
341
|
-
* exists to not build, so they are **checked** instead:
|
|
342
|
+
* *resolved* here, so they are **checked** instead:
|
|
342
343
|
* {@link auditHomepageLinks} reads the body's markdown links, and reports a
|
|
343
344
|
* wikilink on the page rather than resolving one.
|
|
344
345
|
*
|
|
@@ -351,8 +352,8 @@ export function collectHomepages(contentBase, ctx) {
|
|
|
351
352
|
* both sides of the link graph: a content page reaches it through
|
|
352
353
|
* `[[homepage-root|Text]]`, and its own markdown links name content pages. So
|
|
353
354
|
* it carries the same `related` block every content page does — handed in by
|
|
354
|
-
* the caller, since the graph is read off the content render and
|
|
355
|
-
*
|
|
355
|
+
* the caller, since the graph is read off the content render and a tree with
|
|
356
|
+
* only a homepage has no such graph.
|
|
356
357
|
*
|
|
357
358
|
* @param {string} outRoot - The package's site root — the content mount's
|
|
358
359
|
* root, `build/hugo/content`, one level above the mount itself.
|
|
@@ -362,7 +363,7 @@ export function collectHomepages(contentBase, ctx) {
|
|
|
362
363
|
* @param {object} [options] - Options.
|
|
363
364
|
* @param {import("./related-pages.mjs").Related} [options.related] - The
|
|
364
365
|
* homepage's backlinks and mentions. Absent where nothing connects to it,
|
|
365
|
-
* and
|
|
366
|
+
* and when the tree contains only a homepage, where no content link resolves.
|
|
366
367
|
* @returns {number} How many pages were written.
|
|
367
368
|
*/
|
|
368
369
|
export function writeHomepages(outRoot, pages, config, { related } = {}) {
|
|
@@ -402,6 +403,7 @@ export function writeHomepages(outRoot, pages, config, { related } = {}) {
|
|
|
402
403
|
* collection.
|
|
403
404
|
* @param {object} options
|
|
404
405
|
* @param {object} options.config - The resolved build configuration.
|
|
406
|
+
* @param {object[]} options.records - Content-index records.
|
|
405
407
|
* @returns {object} The gate results and, when they pass, the built index.
|
|
406
408
|
*/
|
|
407
409
|
export function siteGates(pages, findings, { config, records }) {
|
|
@@ -469,7 +471,13 @@ export function emptyGates() {
|
|
|
469
471
|
};
|
|
470
472
|
}
|
|
471
473
|
|
|
472
|
-
/**
|
|
474
|
+
/**
|
|
475
|
+
* Whether any gate produced a finding.
|
|
476
|
+
* @param {{homepages: unknown[], frontmatterLinks: unknown[],
|
|
477
|
+
* addressErrors: unknown[], staleManifests: unknown[],
|
|
478
|
+
* unaddressable: unknown[]}} gates - Results of the site checks.
|
|
479
|
+
* @returns {boolean} Whether at least one check found an error.
|
|
480
|
+
*/
|
|
473
481
|
export function gatesFailed(gates) {
|
|
474
482
|
return Boolean(
|
|
475
483
|
gates.homepages.length ||
|
|
@@ -668,7 +676,7 @@ function isPlainObject(value) {
|
|
|
668
676
|
* `_index.md`, and the page's stated `url` keeps its address exactly where the
|
|
669
677
|
* flat file's was.
|
|
670
678
|
*
|
|
671
|
-
* @param {
|
|
679
|
+
* @param {{slug: string}} page - The page.
|
|
672
680
|
* @param {object} [opts]
|
|
673
681
|
* @param {boolean} [opts.bundle=false] - Whether the page carries a resource.
|
|
674
682
|
* @returns {string} The file, relative to the mount.
|
|
@@ -693,7 +701,9 @@ export function renderSitePage(
|
|
|
693
701
|
},
|
|
694
702
|
) {
|
|
695
703
|
const tableErrors = [];
|
|
704
|
+
const expressionErrors = [];
|
|
696
705
|
const secretErrors = [];
|
|
706
|
+
const captionErrors = [];
|
|
697
707
|
const wikiErrors = [];
|
|
698
708
|
const imageErrors = [];
|
|
699
709
|
const resolved = [];
|
|
@@ -738,7 +748,7 @@ export function renderSitePage(
|
|
|
738
748
|
return renderImageFigures(transformed, webSrc);
|
|
739
749
|
};
|
|
740
750
|
|
|
741
|
-
const { markdown, errors } = expandContentTables(page.body, {
|
|
751
|
+
const { markdown, errors, lineMap } = expandContentTables(page.body, {
|
|
742
752
|
docs: universe.get(page.pkg) ?? [],
|
|
743
753
|
linkable,
|
|
744
754
|
source: src,
|
|
@@ -746,8 +756,27 @@ export function renderSitePage(
|
|
|
746
756
|
self: { fm: searchableFrontmatter(page.fm, page.pkg), path: page.relPath },
|
|
747
757
|
});
|
|
748
758
|
tableErrors.push(...errors);
|
|
759
|
+
const captionScan = scanCaptions(markdown);
|
|
760
|
+
ctx.captionLabels = new Map(captionScan.captions.map((caption) => [caption.id, caption.label]));
|
|
761
|
+
const expressions = renderMarkdownExpressions(markdown, {
|
|
762
|
+
fm: page.fm,
|
|
763
|
+
dates: index.dateContext,
|
|
764
|
+
file: page.file,
|
|
765
|
+
bodyLine: page.bodyLine,
|
|
766
|
+
sqlResults: sqlTables?.inline?.get(page.file),
|
|
767
|
+
});
|
|
768
|
+
expressionErrors.push(...expressions.findings);
|
|
749
769
|
const data = pageFrontmatter(page, { decorate, webSrc, artSrc });
|
|
750
|
-
const secrets = renderSecretBlocks(protectCode(markdown, resolve), "web");
|
|
770
|
+
const secrets = renderSecretBlocks(protectCode(expressions.markdown, resolve), "web");
|
|
771
|
+
const admonitions = renderAdmonitions(secrets.markdown);
|
|
772
|
+
const captioned = renderCaptionBlocks(admonitions.markdown);
|
|
773
|
+
for (const error of captionScan.errors)
|
|
774
|
+
captionErrors.push({
|
|
775
|
+
file: page.file,
|
|
776
|
+
line: (page.bodyLine ?? 1) + (lineMap[error.line - 1]?.line ?? error.line - 1),
|
|
777
|
+
column: error.column,
|
|
778
|
+
message: error.message,
|
|
779
|
+
});
|
|
751
780
|
for (const error of renderSecretBlocks(page.body, "book").errors)
|
|
752
781
|
secretErrors.push({
|
|
753
782
|
file: page.file,
|
|
@@ -755,13 +784,22 @@ export function renderSitePage(
|
|
|
755
784
|
column: error.column,
|
|
756
785
|
message: error.message,
|
|
757
786
|
});
|
|
787
|
+
for (const error of scanAdmonitions(page.body).errors)
|
|
788
|
+
secretErrors.push({
|
|
789
|
+
file: page.file,
|
|
790
|
+
line: (page.bodyLine ?? 1) + error.line - 1,
|
|
791
|
+
column: error.column,
|
|
792
|
+
message: error.message,
|
|
793
|
+
});
|
|
758
794
|
return {
|
|
759
795
|
page,
|
|
760
|
-
body:
|
|
796
|
+
body: captioned.markdown,
|
|
761
797
|
data,
|
|
762
798
|
resolved,
|
|
763
799
|
tableErrors,
|
|
800
|
+
expressionErrors,
|
|
764
801
|
secretErrors,
|
|
802
|
+
captionErrors,
|
|
765
803
|
wikiErrors,
|
|
766
804
|
imageErrors,
|
|
767
805
|
};
|
|
@@ -774,7 +812,7 @@ export function renderSitePage(
|
|
|
774
812
|
* compilers use:
|
|
775
813
|
*
|
|
776
814
|
* 1. **Tables expand first**, and outside code-fence protection. A table is
|
|
777
|
-
* authored as a fenced `
|
|
815
|
+
* authored as a fenced `sql` block, which `protectCode` would otherwise
|
|
778
816
|
* stash away before the expander saw it. Expanding first leaves an ordinary
|
|
779
817
|
* markdown table to walk, with every other fence still protected.
|
|
780
818
|
* 2. **Then, inside protection**: the consumer's `beforeLinks` pass, then
|
|
@@ -836,7 +874,9 @@ export function renderPages(pages, options) {
|
|
|
836
874
|
});
|
|
837
875
|
|
|
838
876
|
const tableErrors = [];
|
|
877
|
+
const expressionErrors = [];
|
|
839
878
|
const secretErrors = [];
|
|
879
|
+
const captionErrors = [];
|
|
840
880
|
const wikiErrors = [];
|
|
841
881
|
const imageErrors = [];
|
|
842
882
|
const byKind = {};
|
|
@@ -877,7 +917,9 @@ export function renderPages(pages, options) {
|
|
|
877
917
|
artIndex,
|
|
878
918
|
});
|
|
879
919
|
tableErrors.push(...result.tableErrors);
|
|
920
|
+
expressionErrors.push(...result.expressionErrors);
|
|
880
921
|
secretErrors.push(...result.secretErrors);
|
|
922
|
+
captionErrors.push(...result.captionErrors);
|
|
881
923
|
wikiErrors.push(...result.wikiErrors);
|
|
882
924
|
imageErrors.push(...result.imageErrors);
|
|
883
925
|
rendered.push({ page, body: result.body, data: result.data });
|
|
@@ -918,7 +960,9 @@ export function renderPages(pages, options) {
|
|
|
918
960
|
written: pages.length,
|
|
919
961
|
byKind,
|
|
920
962
|
tableErrors,
|
|
963
|
+
expressionErrors,
|
|
921
964
|
secretErrors,
|
|
965
|
+
captionErrors,
|
|
922
966
|
wikiErrors,
|
|
923
967
|
imageErrors,
|
|
924
968
|
related,
|
|
@@ -940,6 +984,7 @@ export function renderPages(pages, options) {
|
|
|
940
984
|
*/
|
|
941
985
|
const SITE_PASSES = Object.freeze({
|
|
942
986
|
sohlKb: () => require("../sohl/kb-passes.mjs").sohlKbPass,
|
|
987
|
+
packageDocs: () => require("./documentation-links.mjs").documentationLinksPass,
|
|
943
988
|
});
|
|
944
989
|
|
|
945
990
|
/**
|
|
@@ -965,10 +1010,12 @@ export function resolveSitePass(name, options) {
|
|
|
965
1010
|
export function sitePageDecorator(config, index) {
|
|
966
1011
|
const router = routerFor(config);
|
|
967
1012
|
return (data, page) => {
|
|
968
|
-
|
|
1013
|
+
const resolvedDates = resolvedDateFields(page.fm, index.dateContext);
|
|
1014
|
+
if (Object.keys(resolvedDates).length) data.resolvedDates = resolvedDates;
|
|
969
1015
|
data.infoboxes = noteInfoboxes(page.fm, {
|
|
970
1016
|
resolve: (ref, hint) => resolveInfoboxRef(index, ref, hint),
|
|
971
1017
|
router,
|
|
1018
|
+
dates: index.dateContext,
|
|
972
1019
|
});
|
|
973
1020
|
};
|
|
974
1021
|
}
|
|
@@ -998,9 +1045,7 @@ export function buildSite({ config, sqlTables } = {}) {
|
|
|
998
1045
|
const resolved = config ?? loadPackConfig();
|
|
999
1046
|
const site = resolved.site;
|
|
1000
1047
|
const scheme = resolved.publish.address;
|
|
1001
|
-
//
|
|
1002
|
-
// so this decides whether the *content* surfaces are published, never
|
|
1003
|
-
// whether anything is.
|
|
1048
|
+
// A tree containing only the homepage has no content pages to publish.
|
|
1004
1049
|
const publishesContent = publishesContentPages(resolved);
|
|
1005
1050
|
|
|
1006
1051
|
// Where the package is served, and where its content mounts inside it. The
|
|
@@ -1023,12 +1068,12 @@ export function buildSite({ config, sqlTables } = {}) {
|
|
|
1023
1068
|
const out =
|
|
1024
1069
|
publishesContent ?
|
|
1025
1070
|
path.join(outBase, scheme.prefix.replace(/\/$/, ""))
|
|
1026
|
-
//
|
|
1071
|
+
// A tree containing only the homepage has no content mount, so its root is
|
|
1027
1072
|
// the output root.
|
|
1028
1073
|
: outBase;
|
|
1029
1074
|
// The homepage publishes at `/<contentPackage>/`, so its file goes at the
|
|
1030
1075
|
// package's own root — one level above the content mount, and the same
|
|
1031
|
-
// directory
|
|
1076
|
+
// directory when the tree contains only a homepage.
|
|
1032
1077
|
const homeRoot = publishesContent ? outBase : out;
|
|
1033
1078
|
|
|
1034
1079
|
const packages = new Set(site.packages.length ? site.packages : [resolved.contentPackage]);
|
|
@@ -1059,12 +1104,8 @@ export function buildSite({ config, sqlTables } = {}) {
|
|
|
1059
1104
|
const collected = collectHomepages(resolved.paths.content, ctx);
|
|
1060
1105
|
const homepages = collected.pages;
|
|
1061
1106
|
|
|
1062
|
-
//
|
|
1063
|
-
//
|
|
1064
|
-
// a gate that fired after it would have destroyed a good site to report a
|
|
1065
|
-
// bad tree. Before the branch, because the requirement does not vary by
|
|
1066
|
-
// mode: `publish.site` chooses whether the *content* surfaces are
|
|
1067
|
-
// published, and the homepage is the floor beneath both.
|
|
1107
|
+
// Check for exactly one homepage before clearing the output tree, so a
|
|
1108
|
+
// malformed tree does not destroy the last good site.
|
|
1068
1109
|
//
|
|
1069
1110
|
// The count is judged first, and alone when it fires: a tree with two
|
|
1070
1111
|
// homepages does not need to be told about each one's address as well, and
|
|
@@ -1074,7 +1115,7 @@ export function buildSite({ config, sqlTables } = {}) {
|
|
|
1074
1115
|
contentPackage: resolved.contentPackage,
|
|
1075
1116
|
});
|
|
1076
1117
|
// A homepage that cannot be addressed is reported in the same place, and
|
|
1077
|
-
//
|
|
1118
|
+
// also applies when the tree contains only a homepage.
|
|
1078
1119
|
const homepageFindings =
|
|
1079
1120
|
counted.length ? counted : (
|
|
1080
1121
|
collected.addressFindings.map((f) => ({
|
|
@@ -1088,7 +1129,9 @@ export function buildSite({ config, sqlTables } = {}) {
|
|
|
1088
1129
|
gates: { ...emptyGates(), homepages: homepageFindings },
|
|
1089
1130
|
manifests: null,
|
|
1090
1131
|
tableErrors: [],
|
|
1132
|
+
expressionErrors: [],
|
|
1091
1133
|
secretErrors: [],
|
|
1134
|
+
captionErrors: [],
|
|
1092
1135
|
wikiErrors: [],
|
|
1093
1136
|
imageErrors: [],
|
|
1094
1137
|
mapFindings: [],
|
|
@@ -1108,7 +1151,9 @@ export function buildSite({ config, sqlTables } = {}) {
|
|
|
1108
1151
|
gates: emptyGates(),
|
|
1109
1152
|
manifests: null,
|
|
1110
1153
|
tableErrors: [],
|
|
1154
|
+
expressionErrors: [],
|
|
1111
1155
|
secretErrors: [],
|
|
1156
|
+
captionErrors: [],
|
|
1112
1157
|
wikiErrors: [],
|
|
1113
1158
|
imageErrors: [],
|
|
1114
1159
|
mapFindings: [],
|
|
@@ -1154,7 +1199,9 @@ export function buildSite({ config, sqlTables } = {}) {
|
|
|
1154
1199
|
gates,
|
|
1155
1200
|
stats: null,
|
|
1156
1201
|
tableErrors: [],
|
|
1202
|
+
expressionErrors: [],
|
|
1157
1203
|
secretErrors: [],
|
|
1204
|
+
captionErrors: [],
|
|
1158
1205
|
wikiErrors: [],
|
|
1159
1206
|
imageErrors: [],
|
|
1160
1207
|
mapFindings: [],
|
|
@@ -1164,6 +1211,7 @@ export function buildSite({ config, sqlTables } = {}) {
|
|
|
1164
1211
|
const pass = resolveSitePass(site.pass, {
|
|
1165
1212
|
...site.passOptions,
|
|
1166
1213
|
repoRoot: resolved.rootDir,
|
|
1214
|
+
config: resolved,
|
|
1167
1215
|
});
|
|
1168
1216
|
|
|
1169
1217
|
// The map from each related place, drawn now that every page is known to
|
|
@@ -1209,7 +1257,9 @@ export function buildSite({ config, sqlTables } = {}) {
|
|
|
1209
1257
|
return {
|
|
1210
1258
|
gates,
|
|
1211
1259
|
tableErrors: rendered.tableErrors,
|
|
1260
|
+
expressionErrors: rendered.expressionErrors,
|
|
1212
1261
|
secretErrors: rendered.secretErrors,
|
|
1262
|
+
captionErrors: rendered.captionErrors,
|
|
1213
1263
|
wikiErrors: rendered.wikiErrors,
|
|
1214
1264
|
imageErrors: rendered.imageErrors,
|
|
1215
1265
|
mapFindings: drawn.findings,
|
package/engine/site-config.mjs
CHANGED
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
/**
|
|
15
15
|
* The Hugo configuration a site build generates.
|
|
16
16
|
*
|
|
17
|
-
* `
|
|
17
|
+
* `package-build site` writes the whole Hugo source tree under `build/hugo/`
|
|
18
18
|
* — `hugo.toml`, the content mount, Hugo's own cache — as a sibling of the
|
|
19
19
|
* deployment root `build/site/`, so nothing Hugo reads lands in what is
|
|
20
20
|
* published. The consumer's script runs Hugo over it; the toolchain never
|
|
@@ -50,7 +50,7 @@ import { slugify } from "./content-slug.mjs";
|
|
|
50
50
|
/** The Hugo source directory, relative to the repository root. */
|
|
51
51
|
export const HUGO_SOURCE = "build/hugo";
|
|
52
52
|
|
|
53
|
-
/** The content mount `
|
|
53
|
+
/** The content mount `package-build site` writes, relative to the repository root. */
|
|
54
54
|
export const HUGO_CONTENT = `${HUGO_SOURCE}/content`;
|
|
55
55
|
|
|
56
56
|
/**
|
|
@@ -255,7 +255,7 @@ export function readCachedNavigation(config) {
|
|
|
255
255
|
const file = path.join(dir, NAVIGATION_FILE);
|
|
256
256
|
if (!fs.existsSync(path.join(dir, STAMP)) || !fs.existsSync(file)) {
|
|
257
257
|
throw new Error(
|
|
258
|
-
"the site navigation has not been fetched. Run `
|
|
258
|
+
"the site navigation has not been fetched. Run `package-build deps fetch` first.",
|
|
259
259
|
);
|
|
260
260
|
}
|
|
261
261
|
return checkNavigation(JSON.parse(fs.readFileSync(file, "utf8")), file);
|
|
@@ -364,17 +364,17 @@ function deepMerge(base, overrides) {
|
|
|
364
364
|
* @param {string} options.themesDir - From {@link resolveThemesDir}.
|
|
365
365
|
* @returns {Record<string, any>} The configuration Hugo reads.
|
|
366
366
|
* @throws {TypeError} When `homepage` fails `checkHomepage`, or the
|
|
367
|
-
* configuration declares no `packageBuild.manifest.title`, no
|
|
367
|
+
* configuration declares no `site.title` or `packageBuild.manifest.title`, no
|
|
368
368
|
* `site.description`, or no `site.assets`.
|
|
369
369
|
*/
|
|
370
370
|
export function hugoConfig({ config, navigation, themesDir }) {
|
|
371
371
|
checkHomepage(config.homepage, config.contentPackage);
|
|
372
372
|
|
|
373
|
-
const title = config.packageBuild?.manifest?.title;
|
|
373
|
+
const title = config.site.title || config.packageBuild?.manifest?.title;
|
|
374
374
|
if (typeof title !== "string" || !title.trim()) {
|
|
375
375
|
throw new TypeError(
|
|
376
|
-
"package-build config: `
|
|
377
|
-
"
|
|
376
|
+
"package-build config: declare `site.title` or " +
|
|
377
|
+
"`packageBuild.manifest.title` for the site's title.",
|
|
378
378
|
);
|
|
379
379
|
}
|
|
380
380
|
if (!config.site.description) {
|
|
@@ -423,7 +423,7 @@ export function hugoConfig({ config, navigation, themesDir }) {
|
|
|
423
423
|
*/
|
|
424
424
|
export function hugoToml(generated) {
|
|
425
425
|
return (
|
|
426
|
-
"# Generated by `
|
|
426
|
+
"# Generated by `package-build site` from package.json and " +
|
|
427
427
|
"package-build.config.yaml.\n# Every value here has a source there; edit " +
|
|
428
428
|
"the source, not this file.\n\n" +
|
|
429
429
|
`${stringifyToml(generated)}\n`
|
package/engine/site-index.mjs
CHANGED
|
@@ -65,6 +65,7 @@ import {
|
|
|
65
65
|
} from "./address.mjs";
|
|
66
66
|
import { NOTE_SYSTEM } from "./systems.mjs";
|
|
67
67
|
import { contentPackage } from "./content-package.mjs";
|
|
68
|
+
import { reckoningContext } from "./reckoning-markers.mjs";
|
|
68
69
|
// The declared tag vocabulary, which is where `draft` is stated.
|
|
69
70
|
import { isDraftNote } from "./note-vocabulary.mjs";
|
|
70
71
|
|
|
@@ -160,6 +161,8 @@ function mergeForeign(index, foreignIndex) {
|
|
|
160
161
|
* `contentIndex: false` — a Foundry dependency only, with no fetched index.
|
|
161
162
|
* A link naming one fails naming the key, rather than reading as prose or an
|
|
162
163
|
* ordinary dead address.
|
|
164
|
+
* @param {object[]} [options.records] - Content-index records.
|
|
165
|
+
* @param {Map<string, object>} [options.foreignReferences] - Foreign reference targets.
|
|
163
166
|
* @returns {SiteIndex} The index, and what could not be addressed unambiguously.
|
|
164
167
|
*/
|
|
165
168
|
export function buildSiteIndex(
|
|
@@ -252,6 +255,7 @@ export function buildSiteIndex(
|
|
|
252
255
|
|
|
253
256
|
return {
|
|
254
257
|
contentPackage: ownPackage,
|
|
258
|
+
dateContext: reckoningContext({ notes: records }),
|
|
255
259
|
referenceTargets: buildReferenceTargets(records, new Map([...foreignReferences, ...index])),
|
|
256
260
|
index,
|
|
257
261
|
ambiguous,
|
package/engine/site-maps.mjs
CHANGED
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
* The maps a site build writes into its place pages.
|
|
16
16
|
*
|
|
17
17
|
* A place that states, or is named in, a border or a route has a map from it
|
|
18
|
-
* — the drawing `
|
|
18
|
+
* — the drawing `package-build map --from` makes — and the site build draws
|
|
19
19
|
* that map for every such place of this package and hands each one to the
|
|
20
20
|
* page writer, which lays it beside the page as `from-<shortcode>.svg` and
|
|
21
21
|
* names it in the front matter as `map`. The theme inlines it, so its place
|
package/engine/site-preview.mjs
CHANGED
|
@@ -18,6 +18,7 @@ import { reckoningContext } from "./reckoning-markers.mjs";
|
|
|
18
18
|
import { cachedMetadataIndexes, noContentIndexPackages } from "./metadata-index.mjs";
|
|
19
19
|
import { buildSiteIndex } from "./site-index.mjs";
|
|
20
20
|
import { openNotesDatabase, prepareSqlTables, findSqlBlocks } from "./sql-tables.mjs";
|
|
21
|
+
import { isGmNote } from "./note-vocabulary.mjs";
|
|
21
22
|
import { relatedPages } from "./related-pages.mjs";
|
|
22
23
|
import { holdingsNode, holdingsPages, foreignHoldingsNodes } from "./holdings.mjs";
|
|
23
24
|
import { assetAddressIndex } from "./art-fields.mjs";
|
|
@@ -79,10 +80,14 @@ export async function prepareSitePreview({ config = loadPackConfig() } = {}) {
|
|
|
79
80
|
const gates = siteGates([...pages, ...homeEntries], findings, { config, records });
|
|
80
81
|
if (gatesFailed(gates))
|
|
81
82
|
throw new Error("site preview cannot prepare: site integrity gates failed");
|
|
82
|
-
const db = await openNotesDatabase(
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
83
|
+
const db = await openNotesDatabase(
|
|
84
|
+
records.filter((record) => !isGmNote(record)),
|
|
85
|
+
{
|
|
86
|
+
dependencies,
|
|
87
|
+
addressContext: noteAddressContext(config),
|
|
88
|
+
audience: "public",
|
|
89
|
+
},
|
|
90
|
+
);
|
|
86
91
|
try {
|
|
87
92
|
const sources = pages
|
|
88
93
|
.filter((page) => findSqlBlocks(page.body).length)
|
|
@@ -200,10 +205,14 @@ export async function prepareSitePreview({ config = loadPackConfig() } = {}) {
|
|
|
200
205
|
records,
|
|
201
206
|
noIndexPackages: noContentIndexPackages(config),
|
|
202
207
|
});
|
|
203
|
-
db = await openNotesDatabase(
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
208
|
+
db = await openNotesDatabase(
|
|
209
|
+
records.filter((record) => !isGmNote(record)),
|
|
210
|
+
{
|
|
211
|
+
dependencies,
|
|
212
|
+
addressContext: noteAddressContext(config),
|
|
213
|
+
audience: "public",
|
|
214
|
+
},
|
|
215
|
+
);
|
|
207
216
|
}
|
|
208
217
|
const sqlTables = await prepareSqlTables(db, [
|
|
209
218
|
{ source: absolute, markdown: body },
|
|
@@ -235,6 +244,8 @@ export async function prepareSitePreview({ config = loadPackConfig() } = {}) {
|
|
|
235
244
|
});
|
|
236
245
|
for (const error of result.secretErrors)
|
|
237
246
|
findings.push({ ...error, severity: "error" });
|
|
247
|
+
for (const error of result.captionErrors)
|
|
248
|
+
findings.push({ ...error, severity: "error" });
|
|
238
249
|
for (const error of result.wikiErrors) {
|
|
239
250
|
const pos = positionOfLiteral(text, error.link, error.occurrence);
|
|
240
251
|
findings.push({
|
package/engine/social-ties.mjs
CHANGED
|
@@ -16,7 +16,11 @@ export { SOCIAL_TIES, SOCIAL_TIE_TARGET_TYPES };
|
|
|
16
16
|
|
|
17
17
|
const TERMS = new Set(SOCIAL_TIES.map(({ term }) => term));
|
|
18
18
|
|
|
19
|
-
/**
|
|
19
|
+
/**
|
|
20
|
+
* Validate a being's map of defining relationships.
|
|
21
|
+
* @param {object} note
|
|
22
|
+
* @param {{index?: object}} [options]
|
|
23
|
+
*/
|
|
20
24
|
export function checkSocialTies(note, { index } = {}) {
|
|
21
25
|
const ties = note.fm?.data?.socialTies;
|
|
22
26
|
if (ties === undefined || ties === null) return [];
|