@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/content-config.mjs
CHANGED
|
@@ -31,8 +31,6 @@
|
|
|
31
31
|
* packageBuild:
|
|
32
32
|
* assets:
|
|
33
33
|
* - { from: assets/icons, to: assets/icons }
|
|
34
|
-
* publish:
|
|
35
|
-
* site: content
|
|
36
34
|
* ```
|
|
37
35
|
*
|
|
38
36
|
* `defineConfig` is the whole of the contract: it validates the object, fills
|
|
@@ -65,6 +63,7 @@
|
|
|
65
63
|
|
|
66
64
|
import fs from "node:fs";
|
|
67
65
|
import path from "node:path";
|
|
66
|
+
import { fileURLToPath } from "node:url";
|
|
68
67
|
import YAML from "yaml";
|
|
69
68
|
|
|
70
69
|
// Leaves with no local imports of their own, so naming them here cannot close
|
|
@@ -246,59 +245,45 @@ export const DEFAULT_ADDRESS_SCHEME = Object.freeze({
|
|
|
246
245
|
prefix: "",
|
|
247
246
|
});
|
|
248
247
|
|
|
249
|
-
/**
|
|
250
|
-
* How much of a package reaches the web.
|
|
251
|
-
*
|
|
252
|
-
* Every HeroicLands package publishes something: a top-level, human-authored
|
|
253
|
-
* homepage at `https://www.heroiclands.org/<contentPackage>/` saying what the
|
|
254
|
-
* module is, which system it needs and how to install it. So there is no
|
|
255
|
-
* value here meaning *no web presence at all* — homepage-only is the **floor**,
|
|
256
|
-
* and the default.
|
|
257
|
-
*
|
|
258
|
-
* - `homepage` — the authored homepage, and **no other page**. The content tree
|
|
259
|
-
* is not walked for pages, and nothing serves a page for its addresses.
|
|
260
|
-
* - `content` — the homepage *plus* every page the content tree publishes, one
|
|
261
|
-
* per note.
|
|
262
|
-
*
|
|
263
|
-
* **Homepage-only is a first-class mode, not an accommodation.**
|
|
264
|
-
* `sohl-kethira-basic` (unofficial Hârn fan material under Keléstia Productions'
|
|
265
|
-
* Fan Material Guidelines) and `harn-adventures` (HârnFanon under Lythia's
|
|
266
|
-
* terms) must each publish a homepage and nothing beneath it — two packages
|
|
267
|
-
* under two different fan-content licences. The boundary is **published
|
|
268
|
-
* content**: journal text, artwork, item descriptions, compiled notes. A
|
|
269
|
-
* human-authored page announcing the module discloses none of it. Because the
|
|
270
|
-
* failure mode is silent — a `site:` block added later ships licensed content
|
|
271
|
-
* with nobody noticing — the mode fences the content surfaces off rather than
|
|
272
|
-
* trusting a configuration to stay empty.
|
|
273
|
-
*
|
|
274
|
-
* This was a boolean until 5.0.0, and `false` read as "no web presence", which
|
|
275
|
-
* no longer describes any package. Both spellings are refused rather than
|
|
276
|
-
* mapped: a value silently reinterpreted reads to its author as though it still
|
|
277
|
-
* means what it said.
|
|
278
|
-
*
|
|
279
|
-
* @typedef {"homepage" | "content"} SiteMode
|
|
280
|
-
*/
|
|
281
|
-
|
|
282
|
-
/**
|
|
283
|
-
* The publishing modes {@link PublishSwitches.site} may name, floor first.
|
|
284
|
-
*
|
|
285
|
-
* @satisfies {readonly SiteMode[]}
|
|
286
|
-
*/
|
|
287
|
-
export const SITE_MODES = /** @type {const} */ (["homepage", "content"]);
|
|
288
|
-
|
|
289
248
|
/**
|
|
290
249
|
* Whether this package publishes the pages its content tree compiles to.
|
|
291
250
|
*
|
|
292
|
-
*
|
|
293
|
-
*
|
|
294
|
-
*
|
|
295
|
-
*
|
|
251
|
+
* Every package publishes an authored homepage. Other authored notes provide
|
|
252
|
+
* content pages; a tree containing only the homepage publishes only that page.
|
|
253
|
+
*
|
|
254
|
+
* Site, PDF, index, and Foundry address emission use the same source-tree
|
|
255
|
+
* decision. A file without note frontmatter is not a content page.
|
|
296
256
|
*
|
|
297
|
-
* @param {{
|
|
257
|
+
* @param {{paths: {content: string}, skipDirectories: readonly string[]}} config -
|
|
258
|
+
* A resolved configuration with content and skip paths.
|
|
298
259
|
* @returns {boolean} Whether content pages are published.
|
|
299
260
|
*/
|
|
300
261
|
export function publishesContentPages(config) {
|
|
301
|
-
|
|
262
|
+
const root = config.paths.content;
|
|
263
|
+
if (!fs.existsSync(root)) return false;
|
|
264
|
+
const skipped = new Set(config.skipDirectories);
|
|
265
|
+
const stack = [root];
|
|
266
|
+
while (stack.length) {
|
|
267
|
+
const dir = stack.pop();
|
|
268
|
+
for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
|
|
269
|
+
const file = path.join(dir, entry.name);
|
|
270
|
+
if (entry.isDirectory()) {
|
|
271
|
+
if (!skipped.has(entry.name)) stack.push(file);
|
|
272
|
+
continue;
|
|
273
|
+
}
|
|
274
|
+
if (!entry.isFile() || !entry.name.endsWith(".md")) continue;
|
|
275
|
+
const match = fs
|
|
276
|
+
.readFileSync(file, "utf8")
|
|
277
|
+
.match(/^---\r?\n([\s\S]*?)\r?\n---(?:\r?\n|$)/);
|
|
278
|
+
if (!match) continue;
|
|
279
|
+
try {
|
|
280
|
+
if (YAML.parse(match[1])?.type !== "homepage") return true;
|
|
281
|
+
} catch {
|
|
282
|
+
return true;
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
return false;
|
|
302
287
|
}
|
|
303
288
|
|
|
304
289
|
/**
|
|
@@ -447,8 +432,7 @@ export function publishesContentPages(config) {
|
|
|
447
432
|
|
|
448
433
|
/**
|
|
449
434
|
* @typedef {object} PublishSwitches
|
|
450
|
-
* @property {
|
|
451
|
-
* See {@link SITE_MODES}.
|
|
435
|
+
* @property {Readonly<{prefix: string}>} address Where content pages mount within the package.
|
|
452
436
|
*/
|
|
453
437
|
|
|
454
438
|
/**
|
|
@@ -539,13 +523,12 @@ export function publishesContentPages(config) {
|
|
|
539
523
|
*
|
|
540
524
|
* @typedef {object} DocsSpec
|
|
541
525
|
* @property {DocPageSpec} [itemFields] The item-frontmatter reference,
|
|
542
|
-
* rendered by `
|
|
526
|
+
* rendered by `package-build docs
|
|
543
527
|
* item-fields`.
|
|
544
528
|
*/
|
|
545
529
|
|
|
546
530
|
/**
|
|
547
531
|
* @typedef {object} PublishSwitchesInput
|
|
548
|
-
* @property {SiteMode} [site]
|
|
549
532
|
* @property {AddressSchemeInput} [address]
|
|
550
533
|
*/
|
|
551
534
|
|
|
@@ -764,6 +747,7 @@ const DOCS_KEYS = ["itemFields"];
|
|
|
764
747
|
const CHANGELOG_KEYS = ["labels"];
|
|
765
748
|
const SITE_KEYS = [
|
|
766
749
|
"base",
|
|
750
|
+
"title",
|
|
767
751
|
"assets",
|
|
768
752
|
"description",
|
|
769
753
|
"packages",
|
|
@@ -821,7 +805,7 @@ const STATS_KEYS = ["lastModifiedBy"];
|
|
|
821
805
|
* @type {symbol}
|
|
822
806
|
*/
|
|
823
807
|
export const DERIVED_SYSTEM_VERSION = Symbol.for("package-build.derivedSystemVersion");
|
|
824
|
-
const PUBLISH_KEYS = ["
|
|
808
|
+
const PUBLISH_KEYS = ["address"];
|
|
825
809
|
const ADDRESS_KEYS = ["prefix"];
|
|
826
810
|
|
|
827
811
|
/** @param {unknown} value */
|
|
@@ -906,9 +890,10 @@ function requireNonEmptyString(value, field) {
|
|
|
906
890
|
* package's id to *be* its system id, and `sohl-sohl-skill-clmb` is the
|
|
907
891
|
* honest address that results — which is the reason to prevent the ones that
|
|
908
892
|
* are avoidable.
|
|
909
|
-
* 3. _Not
|
|
910
|
-
*
|
|
911
|
-
*
|
|
893
|
+
* 3. _Not reserved for another repository_. `packagebuild` addresses assets
|
|
894
|
+
* and documentation this toolchain ships itself. Only this repository's
|
|
895
|
+
* documentation build can use that namespace — see
|
|
896
|
+
* {@link module:engine/packages}.
|
|
912
897
|
*
|
|
913
898
|
* The type vocabulary rule reaches the **asset** types too: `icon`, `image` and
|
|
914
899
|
* `audio` are types an address names exactly as it names a being, so a package
|
|
@@ -919,9 +904,11 @@ function requireNonEmptyString(value, field) {
|
|
|
919
904
|
* to a documentation entry: the item types plus `macro` and the map types.
|
|
920
905
|
* With {@link PACK_BY_TYPE}, {@link NOTE_VOCABULARY} and the `doc`-prefixed
|
|
921
906
|
* forms, this is the whole type vocabulary an address may write.
|
|
907
|
+
* @param {{rootDir: string, packageKind: string}} scope - The source claiming
|
|
908
|
+
* the namespace; this toolchain owns `packagebuild` for its documentation.
|
|
922
909
|
* @returns {string} The value, unchanged.
|
|
923
910
|
*/
|
|
924
|
-
function requireContentPackage(value, docEntryTypes) {
|
|
911
|
+
function requireContentPackage(value, docEntryTypes, { rootDir, packageKind }) {
|
|
925
912
|
const pkg = requireNonEmptyString(value, "contentPackage");
|
|
926
913
|
if (!isAddressSegment(pkg)) {
|
|
927
914
|
fail(
|
|
@@ -935,7 +922,12 @@ function requireContentPackage(value, docEntryTypes) {
|
|
|
935
922
|
"than merely ugly. `harn-adventures` became `harnadventures`",
|
|
936
923
|
);
|
|
937
924
|
}
|
|
938
|
-
|
|
925
|
+
// This toolchain's own documentation shares the asset namespace it owns.
|
|
926
|
+
// Other repositories cannot claim that namespace.
|
|
927
|
+
const ownDocumentation =
|
|
928
|
+
packageKind === DOCUMENTATION_KIND &&
|
|
929
|
+
path.resolve(rootDir) === path.dirname(fileURLToPath(import.meta.url));
|
|
930
|
+
if (isReservedPackage(pkg) && !ownDocumentation) {
|
|
939
931
|
fail(
|
|
940
932
|
"contentPackage",
|
|
941
933
|
`is \`${pkg}\`, which is a reserved package name. ` +
|
|
@@ -1270,7 +1262,7 @@ function normalizeIcons(value, rootDir) {
|
|
|
1270
1262
|
if (!/^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(name)) {
|
|
1271
1263
|
fail(
|
|
1272
1264
|
`${where}.icons.${name}`,
|
|
1273
|
-
"is not a name a note can write — `:icon
|
|
1265
|
+
"is not a name a note can write — `:icon …:` takes lowercase " +
|
|
1274
1266
|
"letters, digits and hyphens, the charset an address segment uses",
|
|
1275
1267
|
);
|
|
1276
1268
|
}
|
|
@@ -1567,7 +1559,7 @@ function normalizeSiteDescription(value) {
|
|
|
1567
1559
|
*/
|
|
1568
1560
|
export const DERIVED_HUGO_KEYS = Object.freeze({
|
|
1569
1561
|
baseURL: "package.json `homepage`",
|
|
1570
|
-
title: "`packageBuild.manifest.title`",
|
|
1562
|
+
title: "`site.title` or `packageBuild.manifest.title`",
|
|
1571
1563
|
locale: "the organisation's locale, in `engine/site-config.mjs`",
|
|
1572
1564
|
publishDir: "`contentPackage`, under the deployment root `build/site`",
|
|
1573
1565
|
contentDir: "the fixed content mount, `build/hugo/content`",
|
|
@@ -1588,7 +1580,7 @@ export const DERIVED_HUGO_KEYS = Object.freeze({
|
|
|
1588
1580
|
"params.brand": "the organisation's brand links, in `engine/site-config.mjs`",
|
|
1589
1581
|
"params.notfound": "`site.notfound`",
|
|
1590
1582
|
"markup.goldmark.renderer.unsafe": "the toolchain, whose pages carry raw HTML",
|
|
1591
|
-
menu: "the navigation `
|
|
1583
|
+
menu: "the navigation `package-build deps fetch` caches from heroiclands.org",
|
|
1592
1584
|
});
|
|
1593
1585
|
|
|
1594
1586
|
/**
|
|
@@ -1706,7 +1698,7 @@ const SITE_INDEX_MESSAGE =
|
|
|
1706
1698
|
* Everything here is *framing*: which named pass bundle supplies the
|
|
1707
1699
|
* repository's own body rewrites, and the residue of the generated Hugo
|
|
1708
1700
|
* configuration that is genuinely this repository's own. Where the Hugo tree
|
|
1709
|
-
* is written is not a choice: `
|
|
1701
|
+
* is written is not a choice: `package-build site` writes it under
|
|
1710
1702
|
* `build/hugo/`, and a `site.out` is refused by name. How a page gets its
|
|
1711
1703
|
* **address** is deliberately not here either — that is `publish.address`,
|
|
1712
1704
|
* shared with the link manifest so the two cannot disagree about where a
|
|
@@ -1714,7 +1706,7 @@ const SITE_INDEX_MESSAGE =
|
|
|
1714
1706
|
*
|
|
1715
1707
|
* Two switches sit here, both on by default. `search` is whether
|
|
1716
1708
|
* `package-build site-root` writes a search index beside the rendered pages.
|
|
1717
|
-
* `maps` is whether `
|
|
1709
|
+
* `maps` is whether `package-build site` draws the map from each place that
|
|
1718
1710
|
* states a border or a route and writes it into that place's page — a site
|
|
1719
1711
|
* carries both unless it says otherwise.
|
|
1720
1712
|
*
|
|
@@ -1734,6 +1726,7 @@ const SITE_INDEX_MESSAGE =
|
|
|
1734
1726
|
function normalizeSite(value) {
|
|
1735
1727
|
const empty = Object.freeze({
|
|
1736
1728
|
base: "",
|
|
1729
|
+
title: "",
|
|
1737
1730
|
assets: "",
|
|
1738
1731
|
description: "",
|
|
1739
1732
|
packages: Object.freeze([]),
|
|
@@ -1788,6 +1781,7 @@ function normalizeSite(value) {
|
|
|
1788
1781
|
|
|
1789
1782
|
return Object.freeze({
|
|
1790
1783
|
base: input.base === undefined ? "" : requireNonEmptyString(input.base, "site.base"),
|
|
1784
|
+
title: input.title === undefined ? "" : requireNonEmptyString(input.title, "site.title"),
|
|
1791
1785
|
assets: normalizeSiteAssets(input.assets),
|
|
1792
1786
|
description: normalizeSiteDescription(input.description),
|
|
1793
1787
|
packages: Object.freeze(packages),
|
|
@@ -1818,10 +1812,8 @@ function normalizeSite(value) {
|
|
|
1818
1812
|
* choose, which is the whole reason they are configuration: the engine that
|
|
1819
1813
|
* sets the book must be able to set somebody else's book.
|
|
1820
1814
|
*
|
|
1821
|
-
*
|
|
1822
|
-
*
|
|
1823
|
-
* cannot end up with two switches that disagree about whether it publishes its
|
|
1824
|
-
* content tree. See {@link publishesContentPages}.
|
|
1815
|
+
* A PDF is built when the authored tree contains content pages and the `pdf`
|
|
1816
|
+
* block names a document tree. See {@link publishesContentPages}.
|
|
1825
1817
|
*
|
|
1826
1818
|
* @param {unknown} value - The `pdf` block, or `undefined`.
|
|
1827
1819
|
* @param {string} rootDir - The repository root configured paths resolve against.
|
|
@@ -1874,7 +1866,7 @@ function normalizePdf(value, rootDir) {
|
|
|
1874
1866
|
});
|
|
1875
1867
|
}
|
|
1876
1868
|
|
|
1877
|
-
// Family name to the font file carrying its glyphs, for `:icon
|
|
1869
|
+
// Family name to the font file carrying its glyphs, for `:icon …:`. A file
|
|
1878
1870
|
// rather than a codepoint, because the font's own tables are the only
|
|
1879
1871
|
// trustworthy source of which glyph a name resolves to — see
|
|
1880
1872
|
// {@link module:engine/content-icons}, which states the style and the name
|
|
@@ -2103,8 +2095,8 @@ function normalizeRelationships(value) {
|
|
|
2103
2095
|
};
|
|
2104
2096
|
// What the other package's *content* is called, where that
|
|
2105
2097
|
// differs from its Foundry id. A note addresses a file by the
|
|
2106
|
-
// content package that owns it — `
|
|
2107
|
-
// Foundry id (`
|
|
2098
|
+
// content package that owns it — `harnensemble/assets/…` — and the
|
|
2099
|
+
// Foundry id (`harn-ensemble`) appears only in the install
|
|
2108
2100
|
// path this derives. Omitted where the two are the same word,
|
|
2109
2101
|
// which they are for every system.
|
|
2110
2102
|
if (rel.contentPackage !== undefined) {
|
|
@@ -2397,43 +2389,6 @@ function normalizeItemBuilders(value) {
|
|
|
2397
2389
|
};
|
|
2398
2390
|
}
|
|
2399
2391
|
|
|
2400
|
-
/**
|
|
2401
|
-
* The publishing mode, refusing a boolean.
|
|
2402
|
-
*
|
|
2403
|
-
* A boolean is refused rather than mapped onto the nearest mode, because the
|
|
2404
|
-
* reading `false` invited — *this package has no web presence* — is exactly the
|
|
2405
|
-
* belief the change exists to correct, and a value quietly reinterpreted reads
|
|
2406
|
-
* to its author as though it still means what it said. So the message names the
|
|
2407
|
-
* mode to write instead of the value to fix.
|
|
2408
|
-
*
|
|
2409
|
-
* @param {unknown} value - The authored `publish.site`.
|
|
2410
|
-
* @returns {SiteMode} The mode.
|
|
2411
|
-
*/
|
|
2412
|
-
function normalizeSiteMode(value) {
|
|
2413
|
-
if (value === undefined) return "homepage";
|
|
2414
|
-
if (typeof value === "boolean") {
|
|
2415
|
-
fail(
|
|
2416
|
-
"publish.site",
|
|
2417
|
-
`is no longer a boolean — write \`site: ${value ? "content" : "homepage"}\`. ` +
|
|
2418
|
-
`Every package publishes an authored homepage at ` +
|
|
2419
|
-
`/<contentPackage>/, so no value means "no web presence": ` +
|
|
2420
|
-
`\`homepage\` publishes that page and nothing else, and ` +
|
|
2421
|
-
`\`content\` publishes it plus every page the content tree ` +
|
|
2422
|
-
`compiles to`,
|
|
2423
|
-
);
|
|
2424
|
-
}
|
|
2425
|
-
if (
|
|
2426
|
-
typeof value !== "string" ||
|
|
2427
|
-
!(/** @type {readonly string[]} */ (SITE_MODES).includes(value))
|
|
2428
|
-
) {
|
|
2429
|
-
fail(
|
|
2430
|
-
"publish.site",
|
|
2431
|
-
`must be one of ${SITE_MODES.join(", ")} (got ${JSON.stringify(value)})`,
|
|
2432
|
-
);
|
|
2433
|
-
}
|
|
2434
|
-
return /** @type {SiteMode} */ (value);
|
|
2435
|
-
}
|
|
2436
|
-
|
|
2437
2392
|
/**
|
|
2438
2393
|
* @param {unknown} value
|
|
2439
2394
|
* @returns {Readonly<PublishSwitches>}
|
|
@@ -2441,12 +2396,14 @@ function normalizeSiteMode(value) {
|
|
|
2441
2396
|
function normalizePublish(value) {
|
|
2442
2397
|
if (value === undefined) {
|
|
2443
2398
|
return Object.freeze({
|
|
2444
|
-
site: "homepage",
|
|
2445
2399
|
address: Object.freeze({ ...DEFAULT_ADDRESS_SCHEME }),
|
|
2446
2400
|
});
|
|
2447
2401
|
}
|
|
2448
2402
|
if (!isPlainObject(value)) fail("publish", "must be an object");
|
|
2449
2403
|
const publish = /** @type {Record<string, unknown>} */ (value);
|
|
2404
|
+
if (Object.hasOwn(publish, "site")) {
|
|
2405
|
+
fail("publish.site", "is not configured; publishing follows the authored content tree");
|
|
2406
|
+
}
|
|
2450
2407
|
rejectUnknownKeys(publish, PUBLISH_KEYS, "publish.");
|
|
2451
2408
|
|
|
2452
2409
|
const addressInput = publish.address;
|
|
@@ -2482,7 +2439,6 @@ function normalizePublish(value) {
|
|
|
2482
2439
|
}
|
|
2483
2440
|
|
|
2484
2441
|
return Object.freeze({
|
|
2485
|
-
site: normalizeSiteMode(publish.site),
|
|
2486
2442
|
address: Object.freeze({ prefix }),
|
|
2487
2443
|
});
|
|
2488
2444
|
}
|
|
@@ -2534,28 +2490,6 @@ export function defineConfig(config) {
|
|
|
2534
2490
|
if (input[key] === undefined) continue;
|
|
2535
2491
|
fail(key, `is refused in a \`${DOCUMENTATION_KIND}\` package, which ${why}`);
|
|
2536
2492
|
}
|
|
2537
|
-
// Publishing is what a documentation package is *for*, so the floor
|
|
2538
|
-
// every other package may sit at is not available to it: `homepage`
|
|
2539
|
-
// would leave a package that publishes one authored page, builds no
|
|
2540
|
-
// book, and compiles nothing at all.
|
|
2541
|
-
if (!isPlainObject(input.publish)) {
|
|
2542
|
-
fail(
|
|
2543
|
-
"publish",
|
|
2544
|
-
`is required in a \`${DOCUMENTATION_KIND}\` package: publishing ` +
|
|
2545
|
-
"the content tree is the whole of what it does. Write " +
|
|
2546
|
-
"`publish: {site: content}`",
|
|
2547
|
-
);
|
|
2548
|
-
}
|
|
2549
|
-
const mode = /** @type {Record<string, unknown>} */ (input.publish).site;
|
|
2550
|
-
if (mode !== "content") {
|
|
2551
|
-
fail(
|
|
2552
|
-
"publish.site",
|
|
2553
|
-
`must be \`content\` in a \`${DOCUMENTATION_KIND}\` package — ` +
|
|
2554
|
-
"`homepage` fences the content surfaces off, and a package " +
|
|
2555
|
-
"that compiles nothing and publishes nothing from its tree " +
|
|
2556
|
-
"would produce a single authored page and no book",
|
|
2557
|
-
);
|
|
2558
|
-
}
|
|
2559
2493
|
}
|
|
2560
2494
|
|
|
2561
2495
|
// A Foundry package need not compile anything. A module may ship assets and
|
|
@@ -2718,10 +2652,16 @@ export function defineConfig(config) {
|
|
|
2718
2652
|
const docEntryTypes = Object.freeze(
|
|
2719
2653
|
new Set([...itemTypes, ...ACTOR_TYPES, "macro", ...MAP_TYPES]),
|
|
2720
2654
|
);
|
|
2655
|
+
const compatibility = normalizeCompatibility(input.compatibility, "compatibility");
|
|
2656
|
+
if (!documentation && compatibility === null)
|
|
2657
|
+
fail("compatibility.minimum", "is required for a Foundry package");
|
|
2721
2658
|
|
|
2722
2659
|
return Object.freeze({
|
|
2723
2660
|
rootDir,
|
|
2724
|
-
contentPackage: requireContentPackage(input.contentPackage, docEntryTypes
|
|
2661
|
+
contentPackage: requireContentPackage(input.contentPackage, docEntryTypes, {
|
|
2662
|
+
rootDir,
|
|
2663
|
+
packageKind,
|
|
2664
|
+
}),
|
|
2725
2665
|
foundryPackage,
|
|
2726
2666
|
// `package.json`'s own address and byline. `homepage` is checked by
|
|
2727
2667
|
// `checkHomepage` in `config.mjs`.
|
|
@@ -2730,7 +2670,7 @@ export function defineConfig(config) {
|
|
|
2730
2670
|
author: normalizeAuthor(input.author),
|
|
2731
2671
|
packageKind: /** @type {PackageKind} */ (packageKind),
|
|
2732
2672
|
// Foundry serves a package's files from `<kind>/<id>/`, so this is the
|
|
2733
|
-
// one place `systems/sohl` (or `modules/
|
|
2673
|
+
// one place `systems/sohl` (or `modules/harn-ensemble`) is spelled.
|
|
2734
2674
|
//
|
|
2735
2675
|
// **Conditional on the kind.** `documentation` names no directory
|
|
2736
2676
|
// Foundry serves, and there is no package id to put under one either, so
|
|
@@ -2797,7 +2737,7 @@ export function defineConfig(config) {
|
|
|
2797
2737
|
docs: normalizeDocs(input.docs),
|
|
2798
2738
|
site: normalizeSite(input.site),
|
|
2799
2739
|
pdf: normalizePdf(input.pdf, rootDir),
|
|
2800
|
-
compatibility
|
|
2740
|
+
compatibility,
|
|
2801
2741
|
relationships: normalizeRelationships(input.relationships),
|
|
2802
2742
|
systems,
|
|
2803
2743
|
requiresSystem,
|