@heroiclands/package-build 22.2.0 → 22.3.1
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 +20 -0
- package/CONTENT.md +27 -32
- package/bin/content-build.mjs +25 -6
- package/content-config.mjs +54 -41
- package/docs/api.md +25 -25
- package/docs/commands.md +23 -0
- package/docs/configuration.md +31 -35
- package/docs/content-format.md +36 -6
- package/engine/field-reference.mjs +135 -0
- package/engine/foundry-entries.mjs +12 -1
- package/engine/frontmatter-lint.mjs +2 -3
- package/engine/helpers.mjs +4 -1
- package/engine/ids.mjs +12 -0
- package/engine/note-claims.mjs +21 -0
- package/engine/pack-router.mjs +82 -5
- package/engine/pdf-build.mjs +2 -2
- package/engine/retired-fields.mjs +2 -3
- package/engine/site-build.mjs +59 -218
- package/engine/site-index.mjs +18 -62
- package/engine/web-wikilinks.mjs +2 -16
- package/engine/wikilinks.mjs +35 -14
- package/package.json +1 -1
- package/sohl/kb-passes.mjs +14 -85
- package/types/content-config.d.mts +14 -6
- package/types/engine/field-reference.d.mts +78 -0
- package/types/engine/ids.d.mts +11 -0
- package/types/engine/pack-router.d.mts +20 -2
- package/types/engine/site-build.d.mts +12 -62
- package/types/engine/site-index.d.mts +3 -28
- package/types/engine/web-wikilinks.d.mts +1 -1
- package/types/engine/wikilinks.d.mts +6 -3
- package/types/sohl/kb-passes.d.mts +5 -36
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,25 @@
|
|
|
1
1
|
# @heroiclands/package-build
|
|
2
2
|
|
|
3
|
+
## 22.3.1
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- b2bb2b2: **`content-build docs item-fields`.** A destination under the content tree
|
|
8
|
+
(`assets/content/`) now writes a complete note — `type: doc`,
|
|
9
|
+
`subType: reference`, a derived `shortcode`, `name.full` and `pack: none` —
|
|
10
|
+
rather than a typeless page the content walk silently drops. A consumer
|
|
11
|
+
declares further note frontmatter under `docs.itemFields.frontmatter`,
|
|
12
|
+
deep-merged over the generated envelope; `--check` compares the whole file.
|
|
13
|
+
A destination outside the content tree is unaffected.
|
|
14
|
+
|
|
15
|
+
## 22.3.0
|
|
16
|
+
|
|
17
|
+
### Minor Changes
|
|
18
|
+
|
|
19
|
+
- a79b33a: **A note may declare `pack: none`.** The universal `pack` key accepts one more value: `none` says the note publishes to the site and compiles into no Foundry document. Such a note is walked, published as a page, present in the content index with an address and no UUID, and addressable by wikilink like any other — on the web the link reaches the page, and in a compiled journal the reader gets the link's text with no document to open. Every pack compiler passes over it without a finding. It is accepted only on a type whose sole document is the JournalEntry its prose becomes (`doc`, `place`, `lore`, `scenario`) and refused by name on a type that compiles an Item, an Actor, a Macro, a Scene or an Adventure, naming the document it would drop. It is read per system like any other `pack:`, and a configured pack may not be called `none`.
|
|
20
|
+
|
|
21
|
+
**`site.trees` and `site.readmeSections` are refused.** A page is a note in the content tree, and there is no second mechanism for mounting a directory of markdown beside it. A configuration declaring either key fails with a message saying so. A package that mounted a directory of pages moves them into `assets/content/` as `doc` notes — each with a `shortcode` and `pack: none` — rewrites their relative links as wikilinks, and declares the section that lists them under `site.sections`. The `sohlKb` site pass no longer rewrites repository-relative links, and its `blob` option has no reader.
|
|
22
|
+
|
|
3
23
|
## 22.2.0
|
|
4
24
|
|
|
5
25
|
### Minor Changes
|
package/CONTENT.md
CHANGED
|
@@ -488,6 +488,17 @@ pack: mysteries
|
|
|
488
488
|
type one system maps and another does not stays silent for the system that
|
|
489
489
|
declines it.
|
|
490
490
|
|
|
491
|
+
- **`pack: none` compiles the note into no document, on purpose.** The note is
|
|
492
|
+
walked, published as a page, present in the content index with an address
|
|
493
|
+
and no UUID, and addressable by wikilink; every pass passes over it without
|
|
494
|
+
a finding, and the finding above says nothing about it. It is accepted only
|
|
495
|
+
on a type whose sole document is the JournalEntry its prose becomes — a
|
|
496
|
+
`doc`, say — and refused by name on a type that compiles an Item, an Actor,
|
|
497
|
+
a Macro, a Scene or an Adventure, where it would drop that document. A
|
|
498
|
+
configured pack may not be called `none`. This is how a page of developer
|
|
499
|
+
documentation lives in the content tree beside everything else: it is a
|
|
500
|
+
note like any other, and it reaches no compendium.
|
|
501
|
+
|
|
491
502
|
**The configuration is found by walking up from the working directory, and from
|
|
492
503
|
the installed package only if that finds nothing.** `engine/pack-config.mjs`
|
|
493
504
|
climbs from `process.cwd()` first, so a build reads the tree it was run in —
|
|
@@ -2256,10 +2267,10 @@ note-format knowledge against game-system knowledge, and a homepage carries no
|
|
|
2256
2267
|
|
|
2257
2268
|
`publish.site` then says how much _else_ is published:
|
|
2258
2269
|
|
|
2259
|
-
| Mode | What is published
|
|
2260
|
-
| ---------- |
|
|
2261
|
-
| `homepage` | The authored homepage, and no other page. **The default, and the floor.**
|
|
2262
|
-
| `content` | The homepage plus every page the content tree compiles to
|
|
2270
|
+
| Mode | What is published |
|
|
2271
|
+
| ---------- | ------------------------------------------------------------------------- |
|
|
2272
|
+
| `homepage` | The authored homepage, and no other page. **The default, and the floor.** |
|
|
2273
|
+
| `content` | The homepage plus every page the content tree compiles to. |
|
|
2263
2274
|
|
|
2264
2275
|
There is no value meaning "no web presence": every package publishes its
|
|
2265
2276
|
homepage. A boolean is refused, with a message naming the mode to write
|
|
@@ -2273,8 +2284,8 @@ content_ — journal text, artwork, item descriptions, compiled notes — and a
|
|
|
2273
2284
|
announcing the module discloses none of it. Because the failure mode is silent,
|
|
2274
2285
|
the mode **fences the content surfaces off** rather than trusting a
|
|
2275
2286
|
configuration to stay empty: in `homepage` mode the tree is never walked for
|
|
2276
|
-
pages, and `sections`, `
|
|
2277
|
-
|
|
2287
|
+
pages, and `sections`, `landing` and `backfillSections` emit nothing even when
|
|
2288
|
+
they are declared.
|
|
2278
2289
|
|
|
2279
2290
|
That is separate from the **dependency** edge, which such a module also
|
|
2280
2291
|
declines: being cited by another package is what would stop it being
|
|
@@ -2293,9 +2304,12 @@ does not reach it.
|
|
|
2293
2304
|
**What it does not do is decide addresses.** Those come from `publish.address`,
|
|
2294
2305
|
the same setting the content index reads, so a page and its index record cannot
|
|
2295
2306
|
disagree about where the page is. Everything under `site:` is _framing_ —
|
|
2296
|
-
what a section is called,
|
|
2297
|
-
|
|
2298
|
-
|
|
2307
|
+
what a section is called, and the residue of the generated Hugo configuration
|
|
2308
|
+
that is this repository's own. What the site publishes is the content tree and
|
|
2309
|
+
nothing beside it: a page of documentation is a note (`type: doc`, with
|
|
2310
|
+
`pack: none` where it compiles into no document), so there is no second
|
|
2311
|
+
mechanism for mounting a directory of markdown, and a `trees:` or
|
|
2312
|
+
`readmeSections:` key is refused with a message saying so.
|
|
2299
2313
|
|
|
2300
2314
|
```yaml
|
|
2301
2315
|
site:
|
|
@@ -2307,16 +2321,12 @@ site:
|
|
|
2307
2321
|
passOptions:
|
|
2308
2322
|
apiBase: /sohl/api/
|
|
2309
2323
|
symbolMap: kb/data/api-symbols.json
|
|
2310
|
-
blob: https://github.com/HeroicLands/…/blob/main/
|
|
2311
|
-
trees:
|
|
2312
|
-
- { from: kb/dev-docs, section: dev-docs }
|
|
2313
2324
|
sections:
|
|
2314
2325
|
being:
|
|
2315
2326
|
title: Beings
|
|
2316
2327
|
banner: banners/creature.webp
|
|
2317
2328
|
description: Folk, animals and the things that walk the world.
|
|
2318
|
-
|
|
2319
|
-
dev-docs: { title: Developer Documentation, banner: banners/dev-docs.webp }
|
|
2329
|
+
reference: { title: Reference, listType: doc, listSubType: reference }
|
|
2320
2330
|
list: { shortcodes: true }
|
|
2321
2331
|
notfound:
|
|
2322
2332
|
tagline: Song of Heroic Lands has no page at
|
|
@@ -2330,10 +2340,8 @@ site:
|
|
|
2330
2340
|
| `base` | Where the package is served: the prefix on every rendered `href`, and what a manifest `path` is measured against. It reaches no page's own `url:` — see [A page's URL is its address](#a-pages-url-is-its-address). Defaults to `/<contentPackage>/`. |
|
|
2331
2341
|
| `packages` | Which content packages this site renders. Defaults to its own. |
|
|
2332
2342
|
| `sections` | The Hugo sections this site declares, and what each says about itself — see below. |
|
|
2333
|
-
| `readmeSections` | The same, for a `trees` entry, whose landing comes from its own `README`. |
|
|
2334
2343
|
| `landing` | Frontmatter for the mount's own `_index.md`. Passed through — the vocabulary is the theme's. |
|
|
2335
2344
|
| `backfillSections` | Write a bare `_index.md` for any other directory directly under the mount. |
|
|
2336
|
-
| `trees` | Extra source trees published beside the content, preserving their source layout below a section. |
|
|
2337
2345
|
| `pass` | A named bundle of this repository's own body rewrites. |
|
|
2338
2346
|
| `passOptions` | That bundle's options. |
|
|
2339
2347
|
| `assets` | The host every package's imagery is served from; the generated `params.cdnBaseURL`. |
|
|
@@ -2377,12 +2385,6 @@ Whatever the entry may carry is the whole of what the section can say.
|
|
|
2377
2385
|
| `listType` | no | The content **type** whose pages this section lists. |
|
|
2378
2386
|
| `listSubType` | no | Narrows that to one **subType**. Only with a `listType`. |
|
|
2379
2387
|
|
|
2380
|
-
`readmeSections` takes the same keys, for a **`trees`** entry: those pages keep
|
|
2381
|
-
their source layout below a named section, so the tree's own `README.md` is that
|
|
2382
|
-
section's landing. What the section declares wins over what the `README` happens
|
|
2383
|
-
to carry — the landing has to match the card that links to it. No content note
|
|
2384
|
-
reaches this map any more.
|
|
2385
|
-
|
|
2386
2388
|
#### Saying what a section lists
|
|
2387
2389
|
|
|
2388
2390
|
A generic list layout has nothing to render, because the membership a section
|
|
@@ -2477,15 +2479,9 @@ a site publishes dead `{@link}` tags at exit 0. A map that is read reports
|
|
|
2477
2479
|
its symbol count at info level, which is the only way to tell a map that loaded
|
|
2478
2480
|
from one that loaded empty without reading the emitted HTML.
|
|
2479
2481
|
|
|
2480
|
-
A bundle supplies
|
|
2481
|
-
|
|
2482
|
-
|
|
2483
|
-
1. `beforeLinks`, on every page, before wikilinks resolve — a `{@link}` tag may
|
|
2484
|
-
sit in prose a wikilink also touches.
|
|
2485
|
-
2. `afterLinks`, on pages from an extra tree only — repository-relative links are
|
|
2486
|
-
a property of how those pages are authored, not of content notes.
|
|
2487
|
-
|
|
2488
|
-
Both run inside code-fence protection, so neither can rewrite a fenced example.
|
|
2482
|
+
A bundle supplies one hook, `beforeLinks`, which runs on every page before
|
|
2483
|
+
wikilinks resolve — a `{@link}` tag may sit in prose a wikilink also touches.
|
|
2484
|
+
It runs inside code-fence protection, so it cannot rewrite a fenced example.
|
|
2489
2485
|
|
|
2490
2486
|
### The gates
|
|
2491
2487
|
|
|
@@ -2499,7 +2495,6 @@ after the links that failed because of it reads as a pile of broken notes.
|
|
|
2499
2495
|
| Addresses | A note with no shortcode to be addressed by, or no section to be filed under. |
|
|
2500
2496
|
| Unusable manifest | A vendored manifest this build cannot read. |
|
|
2501
2497
|
| Unaddressable manifest | One it can read but cannot look anything up in. |
|
|
2502
|
-
| Package conflicts | One address claimed by two packages. |
|
|
2503
2498
|
| Tables and wikilinks | A table directive that cannot be honoured, or a link that lands nowhere. |
|
|
2504
2499
|
|
|
2505
2500
|
None of them exits the process from inside the library; the command decides. That
|
package/bin/content-build.mjs
CHANGED
|
@@ -80,7 +80,7 @@ import {
|
|
|
80
80
|
formatUnaddressableFinding,
|
|
81
81
|
} from "../engine/metadata-index.mjs";
|
|
82
82
|
import { fetchNavigation, generateHugoConfig, writeHugoConfig } from "../engine/site-config.mjs";
|
|
83
|
-
import { renderItemFieldReference } from "../engine/field-reference.mjs";
|
|
83
|
+
import { renderItemFieldReference, renderItemFieldsPage } from "../engine/field-reference.mjs";
|
|
84
84
|
import { lintContentTree } from "../engine/content-lint.mjs";
|
|
85
85
|
import { lintContentCharset } from "../engine/content-charset.mjs";
|
|
86
86
|
import { lintContentHtml } from "../engine/content-html.mjs";
|
|
@@ -284,6 +284,14 @@ const argv = yargs(hideBin(process.argv))
|
|
|
284
284
|
* second implementation of the comparison. Staleness is a property of the whole
|
|
285
285
|
* generated file, so there is no line to name.
|
|
286
286
|
*
|
|
287
|
+
* **A destination under the content tree gets a note envelope**, so the walk
|
|
288
|
+
* that collects every note by its `type:` picks this one up too, rather than
|
|
289
|
+
* silently dropping it — `type: doc`, `subType: reference`, a `shortcode`
|
|
290
|
+
* derived from the destination's basename, `name.full` from the title, and
|
|
291
|
+
* `pack: none`; `docs.itemFields.frontmatter` deep-merges over it. `--check`
|
|
292
|
+
* compares the whole file, envelope included. A destination outside the
|
|
293
|
+
* content tree gets the page body alone, as before.
|
|
294
|
+
*
|
|
287
295
|
* `--out` and `--title` still override, for a one-off render.
|
|
288
296
|
*
|
|
289
297
|
* @returns {object} The yargs command module.
|
|
@@ -332,13 +340,27 @@ function docsCommand() {
|
|
|
332
340
|
const spec = config.docs?.itemFields ?? {};
|
|
333
341
|
const destination =
|
|
334
342
|
argv.out ?? (spec.out ? path.resolve(config.rootDir, spec.out) : null);
|
|
343
|
+
const pageTitle = title ?? spec.title ?? "Item Note Frontmatter";
|
|
335
344
|
|
|
336
|
-
const
|
|
337
|
-
|
|
345
|
+
const body = `${renderItemFieldReference({
|
|
346
|
+
title: pageTitle,
|
|
338
347
|
...(spec.preamble ? { preamble: spec.preamble } : {}),
|
|
339
348
|
generatedBy: "`content-build docs item-fields`",
|
|
340
349
|
config,
|
|
341
350
|
})}\n`;
|
|
351
|
+
// A page filed under the content tree is walked for its
|
|
352
|
+
// `type:` like any other note, so it needs the envelope; one
|
|
353
|
+
// filed anywhere else — a repository's own `docs/` — is not,
|
|
354
|
+
// and gets exactly the body it always did.
|
|
355
|
+
const page =
|
|
356
|
+
destination ?
|
|
357
|
+
renderItemFieldsPage(body, {
|
|
358
|
+
title: pageTitle,
|
|
359
|
+
destination,
|
|
360
|
+
contentRoot: config.paths.content,
|
|
361
|
+
frontmatter: spec.frontmatter,
|
|
362
|
+
})
|
|
363
|
+
: body;
|
|
342
364
|
|
|
343
365
|
if (check) {
|
|
344
366
|
if (!destination) {
|
|
@@ -1561,9 +1583,6 @@ function siteCommand() {
|
|
|
1561
1583
|
for (const f of gates.unaddressable) {
|
|
1562
1584
|
console.error(formatUnaddressable(f, loadPackConfig()));
|
|
1563
1585
|
}
|
|
1564
|
-
for (const c of gates.conflicts) {
|
|
1565
|
-
log.error(`address ${c.key} is also published by ${c.package}`);
|
|
1566
|
-
}
|
|
1567
1586
|
if (gatesFailed(gates)) {
|
|
1568
1587
|
process.exitCode = 1;
|
|
1569
1588
|
return;
|
package/content-config.mjs
CHANGED
|
@@ -73,7 +73,7 @@ import { ADDRESS_SEGMENT_PATTERN, isAddressSegment } from "./engine/address-char
|
|
|
73
73
|
import { ASSET_TYPE_NAMES } from "./engine/asset-types.mjs";
|
|
74
74
|
import { isReservedPackage } from "./engine/packages.mjs";
|
|
75
75
|
import { EMPTY_ICON_REGISTRY, checkIconRegistry } from "./engine/content-icons.mjs";
|
|
76
|
-
import { MAP_TYPES, PACK_BY_TYPE } from "./engine/ids.mjs";
|
|
76
|
+
import { MAP_TYPES, NO_PACK, PACK_BY_TYPE } from "./engine/ids.mjs";
|
|
77
77
|
import { ACTOR_TYPES } from "./engine/subtype-registry.mjs";
|
|
78
78
|
import { NOTE_VOCABULARY } from "./engine/note-vocabulary.mjs";
|
|
79
79
|
|
|
@@ -256,10 +256,10 @@ export const DEFAULT_ADDRESS_SCHEME = Object.freeze({
|
|
|
256
256
|
* and the default.
|
|
257
257
|
*
|
|
258
258
|
* - `homepage` — the authored homepage, and **no other page**. The content tree
|
|
259
|
-
* is not walked for pages, `site.sections` / `site.
|
|
260
|
-
*
|
|
259
|
+
* is not walked for pages, `site.sections` / `site.landing` emit nothing,
|
|
260
|
+
* and nothing serves a page for its addresses.
|
|
261
261
|
* - `content` — the homepage *plus* every page the content tree publishes: the
|
|
262
|
-
* knowledgebase
|
|
262
|
+
* knowledgebase and the section landings.
|
|
263
263
|
*
|
|
264
264
|
* **Homepage-only is a first-class mode, not an accommodation.**
|
|
265
265
|
* `sohl-kethira-basic` (unofficial Hârn fan material under Keléstia Productions'
|
|
@@ -528,6 +528,11 @@ export function publishesContentPages(config) {
|
|
|
528
528
|
* Without it the page goes to stdout.
|
|
529
529
|
* @property {string[]} [preamble] Lines between the generated banner and the
|
|
530
530
|
* first table. Markdown, emitted verbatim.
|
|
531
|
+
* @property {Record<string, unknown>} [frontmatter] Further note frontmatter,
|
|
532
|
+
* deep-merged over the generated envelope
|
|
533
|
+
* (`type: doc`, `subType: reference`,
|
|
534
|
+
* `shortcode`, `name.full`, `pack: none`)
|
|
535
|
+
* when `out` is under the content tree.
|
|
531
536
|
*/
|
|
532
537
|
|
|
533
538
|
/**
|
|
@@ -762,9 +767,7 @@ const SITE_KEYS = [
|
|
|
762
767
|
"description",
|
|
763
768
|
"packages",
|
|
764
769
|
"sections",
|
|
765
|
-
"readmeSections",
|
|
766
770
|
"landing",
|
|
767
|
-
"trees",
|
|
768
771
|
"pass",
|
|
769
772
|
"passOptions",
|
|
770
773
|
"backfillSections",
|
|
@@ -772,7 +775,6 @@ const SITE_KEYS = [
|
|
|
772
775
|
"notfound",
|
|
773
776
|
"hugo",
|
|
774
777
|
];
|
|
775
|
-
const SITE_TREE_KEYS = ["from", "section"];
|
|
776
778
|
const SITE_LIST_KEYS = ["shortcodes"];
|
|
777
779
|
const SITE_NOTFOUND_KEYS = ["tagline", "sitenoun", "heroimage", "links"];
|
|
778
780
|
const SITE_NOTFOUND_LINK_KEYS = ["title", "url", "text"];
|
|
@@ -780,7 +782,7 @@ const PDF_KEYS = ["title", "subtitle", "document", "out", "front", "fonts", "ico
|
|
|
780
782
|
const PDF_FONT_KEYS = ["serif", "sans", "mono", "path"];
|
|
781
783
|
const EMPTY_PDF_FONTS = Object.freeze({ serif: "", sans: "", mono: "", path: "" });
|
|
782
784
|
const SECTION_META_KEYS = ["title", "banner", "description", "listType", "listSubType"];
|
|
783
|
-
const DOC_PAGE_KEYS = ["title", "out", "preamble"];
|
|
785
|
+
const DOC_PAGE_KEYS = ["title", "out", "preamble", "frontmatter"];
|
|
784
786
|
const RELATIONSHIP_KINDS = ["systems", "requires", "recommends", "conflicts"];
|
|
785
787
|
const RELATIONSHIP_KEYS = [
|
|
786
788
|
"id",
|
|
@@ -1079,6 +1081,15 @@ function normalizePack(value, where, nested = false) {
|
|
|
1079
1081
|
rejectUnknownKeys(pack, PACK_KEYS, `${where}.`);
|
|
1080
1082
|
|
|
1081
1083
|
const name = requireNonEmptyString(pack.name, `${where}.name`);
|
|
1084
|
+
// A note writes `pack: none` to compile into no document, so a pack of
|
|
1085
|
+
// that name could never be addressed by one.
|
|
1086
|
+
if (name === NO_PACK) {
|
|
1087
|
+
fail(
|
|
1088
|
+
`${where}.name`,
|
|
1089
|
+
`may not be \`${NO_PACK}\` — a note declares \`pack: ${NO_PACK}\` to ` +
|
|
1090
|
+
`compile into no document, so no pack may answer to the name`,
|
|
1091
|
+
);
|
|
1092
|
+
}
|
|
1082
1093
|
const type = pack.type;
|
|
1083
1094
|
if (
|
|
1084
1095
|
typeof type !== "string" ||
|
|
@@ -1429,6 +1440,12 @@ function normalizeDocPage(value, where) {
|
|
|
1429
1440
|
}),
|
|
1430
1441
|
);
|
|
1431
1442
|
}
|
|
1443
|
+
if (input.frontmatter !== undefined) {
|
|
1444
|
+
if (!isPlainObject(input.frontmatter)) {
|
|
1445
|
+
fail(`${where}.frontmatter`, "must be a mapping");
|
|
1446
|
+
}
|
|
1447
|
+
out.frontmatter = deepFreeze({ .../** @type {object} */ (input.frontmatter) });
|
|
1448
|
+
}
|
|
1432
1449
|
return Object.freeze(out);
|
|
1433
1450
|
}
|
|
1434
1451
|
|
|
@@ -1750,15 +1767,22 @@ function normalizeSiteHugo(value) {
|
|
|
1750
1767
|
/**
|
|
1751
1768
|
* The `site` section — how this repository frames the website it publishes.
|
|
1752
1769
|
*
|
|
1753
|
-
* Everything here is *framing*: what a section is called, which
|
|
1754
|
-
*
|
|
1755
|
-
*
|
|
1756
|
-
*
|
|
1757
|
-
*
|
|
1758
|
-
*
|
|
1759
|
-
*
|
|
1760
|
-
*
|
|
1761
|
-
*
|
|
1770
|
+
* Everything here is *framing*: what a section is called, which named pass
|
|
1771
|
+
* bundle supplies the repository's own body rewrites, and the residue of the
|
|
1772
|
+
* generated Hugo configuration that is genuinely this repository's own. Where
|
|
1773
|
+
* the Hugo tree is written is not a choice: `content-build site` writes it
|
|
1774
|
+
* under `build/hugo/`, and a `site.out` is refused by name. How a page gets
|
|
1775
|
+
* its **address** is deliberately not here either — that is
|
|
1776
|
+
* `publish.address`, shared with the link manifest so the two cannot disagree
|
|
1777
|
+
* about where a page is.
|
|
1778
|
+
*
|
|
1779
|
+
* **What the site publishes is the content tree, and nothing beside it.** A
|
|
1780
|
+
* page of documentation is a note — `type: doc`, addressed by its shortcode,
|
|
1781
|
+
* compiling into no document where it says `pack: none` — so there is no
|
|
1782
|
+
* second mechanism for mounting a directory of markdown, and a configuration
|
|
1783
|
+
* that names one (`site.trees`, and the `site.readmeSections` that titled
|
|
1784
|
+
* such a tree's landing) is refused with a message saying where the page
|
|
1785
|
+
* goes instead.
|
|
1762
1786
|
*
|
|
1763
1787
|
* @param {unknown} value - The `site` block, or `undefined`.
|
|
1764
1788
|
* @returns {Readonly<object>} It, frozen, with every default filled.
|
|
@@ -1770,9 +1794,7 @@ function normalizeSite(value) {
|
|
|
1770
1794
|
description: "",
|
|
1771
1795
|
packages: Object.freeze([]),
|
|
1772
1796
|
sections: Object.freeze({}),
|
|
1773
|
-
readmeSections: Object.freeze({}),
|
|
1774
1797
|
landing: null,
|
|
1775
|
-
trees: Object.freeze([]),
|
|
1776
1798
|
pass: "",
|
|
1777
1799
|
passOptions: Object.freeze({}),
|
|
1778
1800
|
backfillSections: false,
|
|
@@ -1793,27 +1815,20 @@ function normalizeSite(value) {
|
|
|
1793
1815
|
"the location is not configurable. Remove the key",
|
|
1794
1816
|
);
|
|
1795
1817
|
}
|
|
1796
|
-
|
|
1797
|
-
|
|
1798
|
-
|
|
1799
|
-
|
|
1800
|
-
if (
|
|
1801
|
-
|
|
1802
|
-
|
|
1803
|
-
|
|
1804
|
-
|
|
1805
|
-
|
|
1806
|
-
|
|
1807
|
-
|
|
1808
|
-
from: requireNonEmptyString(tree.from, `${where}.from`),
|
|
1809
|
-
section: requireNonEmptyString(tree.section, `${where}.section`),
|
|
1810
|
-
// The tree's own path, POSIX-separated — what a
|
|
1811
|
-
// repository-relative link inside it is resolved against.
|
|
1812
|
-
rel: String(tree.from).split(path.sep).join("/"),
|
|
1813
|
-
}),
|
|
1814
|
-
);
|
|
1815
|
-
});
|
|
1818
|
+
// Refused by name too, and ahead of the vocabulary check for the same
|
|
1819
|
+
// reason: a page is a note in the content tree, and the useful thing to
|
|
1820
|
+
// say is where it goes rather than that the key is unknown.
|
|
1821
|
+
for (const key of ["trees", "readmeSections"]) {
|
|
1822
|
+
if (input[key] === undefined) continue;
|
|
1823
|
+
fail(
|
|
1824
|
+
`site.${key}`,
|
|
1825
|
+
"is retired — a page is a note in the content tree. Give each page " +
|
|
1826
|
+
"`type: doc`, a `shortcode` and `pack: none`, file it under " +
|
|
1827
|
+
"`assets/content/`, and declare the section that lists it under " +
|
|
1828
|
+
"`site.sections`",
|
|
1829
|
+
);
|
|
1816
1830
|
}
|
|
1831
|
+
rejectUnknownKeys(input, SITE_KEYS, "site.");
|
|
1817
1832
|
|
|
1818
1833
|
let packages = [];
|
|
1819
1834
|
if (input.packages !== undefined) {
|
|
@@ -1839,9 +1854,7 @@ function normalizeSite(value) {
|
|
|
1839
1854
|
description: normalizeSiteDescription(input.description),
|
|
1840
1855
|
packages: Object.freeze(packages),
|
|
1841
1856
|
sections: normalizeSectionMap(input.sections, "site.sections"),
|
|
1842
|
-
readmeSections: normalizeSectionMap(input.readmeSections, "site.readmeSections"),
|
|
1843
1857
|
landing,
|
|
1844
|
-
trees: Object.freeze(trees),
|
|
1845
1858
|
pass: input.pass === undefined ? "" : requireNonEmptyString(input.pass, "site.pass"),
|
|
1846
1859
|
passOptions:
|
|
1847
1860
|
input.passOptions === undefined ?
|
package/docs/api.md
CHANGED
|
@@ -103,6 +103,7 @@ Deterministic document ids, derived by hashing rather than stored, so compile pa
|
|
|
103
103
|
| `MAP_TYPES` | `const MAP_TYPES` | — | enumerating the content types that compile into a Foundry `Scene` |
|
|
104
104
|
| `MAP_SUBTYPES` | `const MAP_SUBTYPES` | — | reading the map subTypes, which differ only in derived canvas defaults |
|
|
105
105
|
| `JOURNAL_TYPES` | `const JOURNAL_TYPES` | — | enumerating content types whose whole document _is_ a JournalEntry |
|
|
106
|
+
| `NO_PACK` | `const NO_PACK` | — | spelling the `pack:` value (`none`) that routes a note's document into no compendium |
|
|
106
107
|
| `PACK_BY_TYPE` | `const PACK_BY_TYPE` | — | looking up the conventional pack name and document type for a content type |
|
|
107
108
|
| `RETIRED_TYPES` | `const RETIRED_TYPES` | — | looking up what a retired content type was replaced by |
|
|
108
109
|
| `assertTypeNotRetired` | `assertTypeNotRetired(type, where)` | throws | refusing a note whose type has been retired outright |
|
|
@@ -200,6 +201,8 @@ Which pack a note's document lands in, when a document type has more than one co
|
|
|
200
201
|
| ------------------ | -------------------------------------- | -------------------------- | ------------------------------------------------------------------------------------------------- |
|
|
201
202
|
| `PackRoutingError` | `class PackRoutingError extends Error` | — | catching a note that cannot be routed to any configured pack |
|
|
202
203
|
| `PACK_FIELD` | `const PACK_FIELD` | — | naming the frontmatter field a note declares its pack in |
|
|
204
|
+
| `NO_PACK` | `const NO_PACK` | — | spelling the `pack:` value (`none`) that routes a note's document into no compendium |
|
|
205
|
+
| `declaresNoPack` | `declaresNoPack(fm, system)` | `boolean` | asking, without a router, whether a note declares `pack: none` for one system's document |
|
|
203
206
|
| `createPackRouter` | `createPackRouter(packs)` | `{resolve, resolveOrNull}` | building a router for one configured pack list, purely, for testing without a config file on disk |
|
|
204
207
|
| `routerFor` | `routerFor(config)` | the router | getting the router for a resolved configuration, built once per configuration |
|
|
205
208
|
| `packRouter` | `packRouter()` | the router | asking, as every module that emits a UUID does, where a note's document lives |
|
|
@@ -605,25 +608,23 @@ The toolchain's own content index — the files it ships, addressed. Every other
|
|
|
605
608
|
|
|
606
609
|
Publishing a content tree as a website. Compiling a content tree into compendium packs is `content-build package compile`. Publishing the _same tree_ as a website was a script each consumer wrote for itself — 473 code lines in `sohl` and 462 in `sohl-thalorna`, 87 of them identical — and the copies drifted in ways neither repository could see. `sohl-thalorna` reimplemented four things this package already exported, not because it needed different behaviour but because its script predates the extraction. That is the failure a command removes: a consumer cannot accidentally reimplement one.
|
|
607
610
|
|
|
608
|
-
| Export | Signature | Returns | Use it when
|
|
609
|
-
| ---------------------- | ------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------- |
|
|
610
|
-
| `
|
|
611
|
-
| `
|
|
612
|
-
| `
|
|
613
|
-
| `
|
|
614
|
-
| `
|
|
615
|
-
| `
|
|
616
|
-
| `
|
|
617
|
-
| `
|
|
618
|
-
| `
|
|
619
|
-
| `
|
|
620
|
-
| `
|
|
621
|
-
| `
|
|
622
|
-
| `
|
|
623
|
-
| `
|
|
624
|
-
| `
|
|
625
|
-
| `resolveSitePass` | `function resolveSitePass(name, options)` | {{beforeLinks?: Function, afterLinks?: Function}} The bundle. | Resolves `site.pass` to its bundle. |
|
|
626
|
-
| `buildSite` | `buildSite({ config, sqlTables })` | {{gates: object, stats: object\|null, tableErrors: object[], wikiErrors: object[], manifests: object\|null}} | Builds a Hugo content tree from a content tree, and reports what it found. |
|
|
611
|
+
| Export | Signature | Returns | Use it when |
|
|
612
|
+
| ---------------------- | ------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------- |
|
|
613
|
+
| `collectContentPages` | `function collectContentPages(contentBase, ctx)` | {{pages: object[], addressFindings: object[], fmLinkFindings: object[]}} | The content tree's pages, and what could not be addressed. |
|
|
614
|
+
| `collectHomepages` | `function collectHomepages(contentBase, ctx)` | {{pages: object[], addressFindings: object[]}} The homepage notes, in walk order, and the ones among them that could not be addressed. | The package's homepage notes — the authored page at `/<contentPackage>/`. |
|
|
615
|
+
| `writeHomepages` | `function writeHomepages(outRoot, pages, config)` | {number} How many pages were written. | Writes each homepage at its address, below the package's own root. |
|
|
616
|
+
| `siteGates` | `function siteGates(pages, findings,` | {object} The gate results and, when they pass, the built index. | The integrity gates a site build runs before it writes anything. |
|
|
617
|
+
| `emptyGates` | `function emptyGates()` | {object} An all-clear gate result. | The gate result of a build that ran none of them. |
|
|
618
|
+
| `gatesFailed` | `function gatesFailed(gates)` | — | Whether any gate produced a finding. |
|
|
619
|
+
| `tableUniverse` | `function tableUniverse(pages)` | {Map<string, object[]>} Package → the notes it may tabulate. | The universe a generated table searches, grouped by package. |
|
|
620
|
+
| `sectionFrontmatter` | `function sectionFrontmatter(meta)` | {object} Its front matter, `title` first. | The front matter a section's landing states about itself. |
|
|
621
|
+
| `pageFrontmatter` | `function pageFrontmatter(page,` | {object} The frontmatter to write. | The frontmatter a page publishes with. |
|
|
622
|
+
| `pageDestination` | `function pageDestination(page)` | — | Where a page is written, relative to the output root. |
|
|
623
|
+
| `renderPages` | `function renderPages(pages, options)` | {{written: number, byKind: Record<string, number>, tableErrors: object[], wikiErrors: object[]}} | Renders and writes every page. |
|
|
624
|
+
| `writeSectionLandings` | `function writeSectionLandings(outRoot,` | {number} How many landings were written. | Writes the Hugo sections a published tree declares. |
|
|
625
|
+
| `pluralTitle` | `function pluralTitle(name)` | {string} The display title. | A section landing's title, from its directory name — `macro` → `Macros`. |
|
|
626
|
+
| `resolveSitePass` | `function resolveSitePass(name, options)` | {{beforeLinks?: Function}} The bundle. | Resolves `site.pass` to its bundle. |
|
|
627
|
+
| `buildSite` | `buildSite({ config, sqlTables })` | {{gates: object, stats: object\|null, tableErrors: object[], wikiErrors: object[], manifests: object\|null}} | Builds a Hugo content tree from a content tree, and reports what it found. |
|
|
627
628
|
|
|
628
629
|
### `engine.contentLint`
|
|
629
630
|
|
|
@@ -1138,13 +1139,12 @@ Which of SoHL's facts a note's summary panel carries, and how they group. Read o
|
|
|
1138
1139
|
|
|
1139
1140
|
### `sohl.kbPasses`
|
|
1140
1141
|
|
|
1141
|
-
The `sohl` knowledgebase's own body
|
|
1142
|
+
The `sohl` knowledgebase's own body pass: a rewrite driven by a TypeDoc symbol map only this package has, named from `site.passOptions` the same way an asset transform is named from configuration. The rewrite never fails a build — an unresolved `{@link}` degrades to a code span — but building the bundle from a misconfigured `symbolMap` fails loudly before any page renders.
|
|
1142
1143
|
|
|
1143
|
-
| Export
|
|
1144
|
-
|
|
|
1145
|
-
| `resolveApiLinks`
|
|
1146
|
-
| `
|
|
1147
|
-
| `sohlKbPass` | `sohlKbPass(options)` | `{beforeLinks: Function, afterLinks: Function}` | building the `sohl` knowledgebase pass bundle the site renderer calls around wikilink resolution |
|
|
1144
|
+
| Export | Signature | Returns | Use it when |
|
|
1145
|
+
| ----------------- | ----------------------------------------- | ------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------- |
|
|
1146
|
+
| `resolveApiLinks` | `resolveApiLinks(body, symbols, apiBase)` | `string` — the body with every tag resolved | resolving inline TypeDoc `{@link}` / `{@linkcode}` / `{@linkplain}` tags in a markdown body against the API symbol map |
|
|
1147
|
+
| `sohlKbPass` | `sohlKbPass(options)` | `{beforeLinks: Function}` | building the `sohl` knowledgebase pass bundle the site renderer calls before wikilink resolution |
|
|
1148
1148
|
|
|
1149
1149
|
### Flat exports (not under a namespace)
|
|
1150
1150
|
|
package/docs/commands.md
CHANGED
|
@@ -1064,6 +1064,29 @@ named. Reads the configured `itemBuilders` registries; writes (or checks)
|
|
|
1064
1064
|
the destination file, or prints to stdout when none is configured and
|
|
1065
1065
|
`--out` is not given.
|
|
1066
1066
|
|
|
1067
|
+
**A destination under `paths.content` gets a complete note, not a typeless
|
|
1068
|
+
page.** The content-tree walk collects every published page by its `type:`,
|
|
1069
|
+
so a generated page filed there needs the envelope every other note carries
|
|
1070
|
+
or the walk drops it silently — no build failure, no page, every wikilink
|
|
1071
|
+
into it dead. So when `--out` (or `docs.itemFields.out`) resolves inside the
|
|
1072
|
+
content tree, the written file carries:
|
|
1073
|
+
|
|
1074
|
+
- `type: doc`, `subType: reference` — out-of-world lookup material, like
|
|
1075
|
+
every other generated reference page;
|
|
1076
|
+
- `shortcode`, derived from the destination's basename — lowercase
|
|
1077
|
+
alphanumerics only, so `item-frontmatter.md` derives `itemfrontmatter` —
|
|
1078
|
+
unless `docs.itemFields.frontmatter.shortcode` gives one;
|
|
1079
|
+
- `name.full`, from the page's title;
|
|
1080
|
+
- `pack: none` — the page publishes to the website and compiles into no
|
|
1081
|
+
compendium document.
|
|
1082
|
+
|
|
1083
|
+
`docs.itemFields.frontmatter` is deep-merged over that envelope, so a
|
|
1084
|
+
consumer may add keys (`description`, `tags`) or override any of the derived
|
|
1085
|
+
ones. `--check` then compares the **whole** file, envelope included — a page
|
|
1086
|
+
committed with a hand-written or stale envelope reads as out of date exactly
|
|
1087
|
+
as a stale body does. A destination outside the content tree gets the page
|
|
1088
|
+
body alone, with no frontmatter, exactly as before.
|
|
1089
|
+
|
|
1067
1090
|
**OPTIONS**
|
|
1068
1091
|
|
|
1069
1092
|
| Positional | Type | Default | Description |
|