@heroiclands/package-build 22.4.6 → 22.4.7
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 +10 -0
- package/CONTENT.md +109 -0
- package/bin/content-build.mjs +10 -2
- package/content-config.mjs +8 -3
- package/docs/api.md +50 -14
- package/docs/configuration.md +20 -1
- package/docs/content-format.md +71 -0
- package/engine/frontmatter-lint.mjs +2 -0
- package/engine/holdings.mjs +391 -0
- package/engine/index.mjs +9 -0
- package/engine/map-graphviz.mjs +12 -3
- package/engine/map-places.mjs +33 -3
- package/engine/market-class.mjs +178 -0
- package/engine/metadata-index.mjs +7 -4
- package/engine/note-vocabulary.mjs +19 -0
- package/engine/site-build.mjs +88 -10
- package/engine/site-maps.mjs +165 -0
- package/package.json +1 -1
- package/types/engine/holdings.d.mts +146 -0
- package/types/engine/index.d.mts +3 -0
- package/types/engine/map-graphviz.d.mts +8 -1
- package/types/engine/map-places.d.mts +26 -0
- package/types/engine/market-class.d.mts +62 -0
- package/types/engine/note-vocabulary.d.mts +10 -0
- package/types/engine/site-build.d.mts +37 -6
- package/types/engine/site-maps.d.mts +81 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
# @heroiclands/package-build
|
|
2
2
|
|
|
3
|
+
## 22.4.7
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
Every place page that states a border or a route, or is named in one, carries the map from that place: `from-<shortcode>.svg` beside the page and `map:` naming it in the front matter, with every place name on it a link to that place's page. GraphViz draws it; when GraphViz is not installed the site build says so once and writes every page without a map, and `site.maps: false` draws none.
|
|
8
|
+
|
|
9
|
+
A place's page carries what lies within it and who holds it, and an affiliation's page carries what it holds, read from the `parents` and `domains` the notes already state — a dependency's places and affiliations included. `content-build lint` warns of unheld land: a settlement, site or structure that no affiliation's `domains` names.
|
|
10
|
+
|
|
11
|
+
A `place` note can state a settlement's market class: `data.market`, a whole number from 1 to 6 beside `population`. `1` is a hamlet, with no market beyond what neighbours trade among themselves; `2` a village and its weekly market; `3` a town with several trades working full time; `4` a market town, its chartered fair and its moneylender; `5` a city, where anything ordinary can be had in quantity; `6` a great city that banks, imports as a matter of course, and is the market from which the other markets buy. The key is optional, and a value off the scale is reported at the line that states it rather than rounded onto a class nobody wrote.
|
|
12
|
+
|
|
3
13
|
## 22.4.6
|
|
4
14
|
|
|
5
15
|
### Patch Changes
|
package/CONTENT.md
CHANGED
|
@@ -1183,6 +1183,25 @@ parent or child, or a pair listed twice by one mode. Each finding lands on the
|
|
|
1183
1183
|
entry that states it. The keys, the sets and the days scale are specified under
|
|
1184
1184
|
`type: place` in `docs/content-format.md`.
|
|
1185
1185
|
|
|
1186
|
+
**A settlement is held by someone.** An affiliation's `domains` names the
|
|
1187
|
+
places it holds, and a `place` of subType `settlement`, `site` or `structure`
|
|
1188
|
+
that no affiliation's `domains` names — in this tree or in a fetched index —
|
|
1189
|
+
is a warning, `unheld land`, at the note's `type:` line, so a gap in tenure
|
|
1190
|
+
shows. A region is held through its polity's `domains` and a feature by
|
|
1191
|
+
nobody, so neither is checked.
|
|
1192
|
+
|
|
1193
|
+
**A settlement's market class is one of six.** `data.market` states what trade
|
|
1194
|
+
a settlement supports — `1` a hamlet, with no market beyond what neighbours
|
|
1195
|
+
trade among themselves; `2` a village and its weekly market; `3` a town with
|
|
1196
|
+
several trades working full time; `4` a market town, its chartered fair and its
|
|
1197
|
+
moneylender; `5` a city, where anything ordinary can be had in quantity; `6` a
|
|
1198
|
+
great city that banks, imports as a matter of course, and is the market from
|
|
1199
|
+
which the other markets buy. The numbers order the steps and do nothing else, so a value off the
|
|
1200
|
+
scale is an error at the value that states it rather than a number rounded onto
|
|
1201
|
+
a class the author did not write. The key is optional and the lint checks no
|
|
1202
|
+
`subType` condition, as it checks none for `population`. The scale is stated
|
|
1203
|
+
under `type: place` in `docs/content-format.md`.
|
|
1204
|
+
|
|
1186
1205
|
Nothing here writes. A check reports and an author fixes.
|
|
1187
1206
|
|
|
1188
1207
|
**`content-build map` draws what those keys state.** `--tree` is the author's
|
|
@@ -2409,6 +2428,96 @@ The theme's `related.html` partial reads both lists and renders them as one
|
|
|
2409
2428
|
`related` is derived, never authored: what links to a page is a fact about
|
|
2410
2429
|
every other page in the tree. A note that writes one has it replaced.
|
|
2411
2430
|
|
|
2431
|
+
### A place page says what lies within it and who holds it; an affiliation page says what it holds
|
|
2432
|
+
|
|
2433
|
+
Two keys the notes already carry answer three questions a reader asks of a
|
|
2434
|
+
page. A place's `data.parents` is geography — what it sits within — and an
|
|
2435
|
+
affiliation's `data.domains` is tenure — what it holds. The build reads both
|
|
2436
|
+
across the tree and every fetched index, inverts them, and writes each page
|
|
2437
|
+
the lists that concern it, shaped like `related`:
|
|
2438
|
+
|
|
2439
|
+
```yaml
|
|
2440
|
+
# on a place
|
|
2441
|
+
contains: # every place whose `parents` names this one
|
|
2442
|
+
- title: Khaset-Mehtet
|
|
2443
|
+
url: /thalorna/place-khasetmehtet/
|
|
2444
|
+
type: place
|
|
2445
|
+
subType: settlement
|
|
2446
|
+
held_by: # every affiliation whose `domains` names this one
|
|
2447
|
+
- title: The Nome of Ankhsetun
|
|
2448
|
+
url: /thalorna/affiliation-nomenkhstn/
|
|
2449
|
+
type: affiliation
|
|
2450
|
+
subType: polity
|
|
2451
|
+
|
|
2452
|
+
# on an affiliation
|
|
2453
|
+
holdings: # every place its `domains` names
|
|
2454
|
+
- title: Ankhsetun
|
|
2455
|
+
url: /thalorna/place-ankhsetunnome/
|
|
2456
|
+
type: place
|
|
2457
|
+
subType: region
|
|
2458
|
+
```
|
|
2459
|
+
|
|
2460
|
+
- **`contains` and `holdings` are sorted by `subType`, then `title`**;
|
|
2461
|
+
`held_by` by `title`. An entry carries `subType` where the note declares
|
|
2462
|
+
one.
|
|
2463
|
+
- **A key is present only when it has an entry.** A place nobody holds and
|
|
2464
|
+
nothing sits within carries neither key; an affiliation whose `domains` is
|
|
2465
|
+
empty carries no `holdings`. The theme's silent-disappear convention, kept
|
|
2466
|
+
at the source.
|
|
2467
|
+
- **`domains` names what an affiliation holds directly, and is never
|
|
2468
|
+
expanded.** A polity whose `domains` names a region holds the region; the
|
|
2469
|
+
settlements within it are reached through the region's `contains`, and are
|
|
2470
|
+
not repeated in the polity's `holdings`. Tenure runs through
|
|
2471
|
+
`affiliation.parents` — a house of its earl, an earl of the crown — and
|
|
2472
|
+
geography through `place.parents`. A subinfeudated manor sits in one region
|
|
2473
|
+
by the second and under a lord of another polity by the first, and both
|
|
2474
|
+
pages say so.
|
|
2475
|
+
- **A dependency's places and affiliations take part.** A fetched index entry
|
|
2476
|
+
carries the `parents` and `domains` its record stated, so a place another
|
|
2477
|
+
package publishes is listed within a local region, and a house another
|
|
2478
|
+
package publishes is named on the local manor it holds. A local note
|
|
2479
|
+
declaring a shortcode shadows a fetched entry declaring the same one.
|
|
2480
|
+
- **`url` composes `<base><slug>/`**, as every `related` entry does.
|
|
2481
|
+
|
|
2482
|
+
All three are derived, never authored: a note that writes one has it
|
|
2483
|
+
replaced. The theme renders each list as a table on the page.
|
|
2484
|
+
|
|
2485
|
+
### A place page carries the map from that place
|
|
2486
|
+
|
|
2487
|
+
A place that states a border or a route, or is named in one, has a map from
|
|
2488
|
+
it — the drawing `content-build map --from` makes, with the place at the
|
|
2489
|
+
centre and each neighbour at its bearing. The site build draws that map for
|
|
2490
|
+
every such place and writes it with the page: the page is a leaf bundle,
|
|
2491
|
+
`place-<shortcode>/index.md`, the drawing sits beside it as
|
|
2492
|
+
`from-<shortcode>.svg`, and the front matter names it:
|
|
2493
|
+
|
|
2494
|
+
```yaml
|
|
2495
|
+
map: from-kthrmr.svg
|
|
2496
|
+
```
|
|
2497
|
+
|
|
2498
|
+
The theme's "From here" panel reads the key, inlines the file as a page
|
|
2499
|
+
resource, and so every place name on the map is a link to that place's page.
|
|
2500
|
+
What the build guarantees:
|
|
2501
|
+
|
|
2502
|
+
- **The address does not move.** A bundled page states the same `url` a flat
|
|
2503
|
+
one does, `/<slug>/`, so `place-kthrmr/index.md` publishes exactly where
|
|
2504
|
+
`place-kthrmr.md` would.
|
|
2505
|
+
- **Every name links through the site base.** The map is drawn with the base
|
|
2506
|
+
the pages are served under, so each `href` composes `<base><slug>/` the way
|
|
2507
|
+
every other href the build renders does.
|
|
2508
|
+
- **It is written for inlining.** No XML prologue, no DOCTYPE, no comment, no
|
|
2509
|
+
reference to anything outside the file; the root carries a `viewBox` and no
|
|
2510
|
+
fixed width or height, so the theme sizes it to the column.
|
|
2511
|
+
- **A place with no relation carries nothing**: no file beside it, no `map`
|
|
2512
|
+
key, and the page stays a flat file. So does every page that is not a place.
|
|
2513
|
+
- **A site never fails for want of a map.** GraphViz draws; when it is not
|
|
2514
|
+
installed the build says so once, as a warning naming what to install, and
|
|
2515
|
+
every page is written as it would be without maps. `site.maps: false` in
|
|
2516
|
+
`package-build.config.yaml` draws nothing and asks nothing of GraphViz.
|
|
2517
|
+
|
|
2518
|
+
`map` is derived, never authored: a note that writes one has it replaced when
|
|
2519
|
+
the build draws a map, and dropped when there is none to draw.
|
|
2520
|
+
|
|
2412
2521
|
### Why the output location is fixed
|
|
2413
2522
|
|
|
2414
2523
|
The content mount is a build artifact and is **deleted on every run**, so that a
|
package/bin/content-build.mjs
CHANGED
|
@@ -1515,7 +1515,8 @@ function pdfCommand() {
|
|
|
1515
1515
|
* wikilink resolution, code-fence protection and the foreign-manifest merge —
|
|
1516
1516
|
* happens here, from configuration. So does the Hugo configuration: the whole
|
|
1517
1517
|
* source tree Hugo reads lands under `build/hugo/`, and the consumer's script
|
|
1518
|
-
* runs Hugo over it.
|
|
1518
|
+
* runs Hugo over it. With `site.maps` on and GraphViz installed, every place
|
|
1519
|
+
* page with a border or a route carries the map from that place.
|
|
1519
1520
|
*
|
|
1520
1521
|
* **The Hugo configuration is generated before anything is written.** Its
|
|
1521
1522
|
* sources — `package.json`'s `homepage`, the cached navigation, the installed
|
|
@@ -1653,10 +1654,17 @@ function siteCommand() {
|
|
|
1653
1654
|
return;
|
|
1654
1655
|
}
|
|
1655
1656
|
|
|
1657
|
+
// What drawing the maps found: warnings, every one. A site
|
|
1658
|
+
// never fails for want of a map, so none of these touches the
|
|
1659
|
+
// exit code — GraphViz absent is one line naming what to
|
|
1660
|
+
// install, and every page is written as it would be without.
|
|
1661
|
+
for (const f of result.mapFindings) emitDiagnostic(f);
|
|
1662
|
+
|
|
1656
1663
|
const s = result.stats;
|
|
1657
1664
|
log.info(
|
|
1658
1665
|
`wrote ${s.homepages ?? 0} homepage(s) + ` +
|
|
1659
|
-
`${s.content ?? 0} content page(s)
|
|
1666
|
+
`${s.content ?? 0} content page(s), ` +
|
|
1667
|
+
`${s.maps ?? 0} with a map, to ` +
|
|
1660
1668
|
`${path.relative(process.cwd(), s.out)}`,
|
|
1661
1669
|
);
|
|
1662
1670
|
const { file } = writeHugoConfig(config, hugo);
|
package/content-config.mjs
CHANGED
|
@@ -772,6 +772,7 @@ const SITE_KEYS = [
|
|
|
772
772
|
"notfound",
|
|
773
773
|
"hugo",
|
|
774
774
|
"search",
|
|
775
|
+
"maps",
|
|
775
776
|
];
|
|
776
777
|
const SITE_NOTFOUND_KEYS = ["tagline", "sitenoun", "heroimage", "links"];
|
|
777
778
|
const SITE_NOTFOUND_LINK_KEYS = ["title", "url", "text"];
|
|
@@ -1711,9 +1712,11 @@ const SITE_INDEX_MESSAGE =
|
|
|
1711
1712
|
* shared with the link manifest so the two cannot disagree about where a
|
|
1712
1713
|
* page is.
|
|
1713
1714
|
*
|
|
1714
|
-
*
|
|
1715
|
-
* search index beside the rendered pages.
|
|
1716
|
-
*
|
|
1715
|
+
* Two switches sit here, both on by default. `search` is whether
|
|
1716
|
+
* `package-build site-root` writes a search index beside the rendered pages.
|
|
1717
|
+
* `maps` is whether `content-build site` draws the map from each place that
|
|
1718
|
+
* states a border or a route and writes it into that place's page — a site
|
|
1719
|
+
* carries both unless it says otherwise.
|
|
1717
1720
|
*
|
|
1718
1721
|
* **What the site publishes is the homepage and the content tree, and nothing
|
|
1719
1722
|
* beside them.** A page of documentation is a note — `type: doc`, addressed
|
|
@@ -1739,6 +1742,7 @@ function normalizeSite(value) {
|
|
|
1739
1742
|
notfound: null,
|
|
1740
1743
|
hugo: Object.freeze({}),
|
|
1741
1744
|
search: true,
|
|
1745
|
+
maps: true,
|
|
1742
1746
|
});
|
|
1743
1747
|
if (value === undefined) return empty;
|
|
1744
1748
|
if (!isPlainObject(value)) fail("site", "must be a mapping");
|
|
@@ -1795,6 +1799,7 @@ function normalizeSite(value) {
|
|
|
1795
1799
|
notfound: normalizeSiteNotfound(input.notfound),
|
|
1796
1800
|
hugo: normalizeSiteHugo(input.hugo),
|
|
1797
1801
|
search: optionalBoolean(input.search, "site.search", true),
|
|
1802
|
+
maps: optionalBoolean(input.maps, "site.maps", true),
|
|
1798
1803
|
});
|
|
1799
1804
|
}
|
|
1800
1805
|
|
package/docs/api.md
CHANGED
|
@@ -429,6 +429,30 @@ What a place is next to (`data.borders`) and reachable from (`data.routes`): the
|
|
|
429
429
|
| `checkBorders` | `checkBorders(note, { index })` | `object[]` | linting a place's `borders` — targets, values, once per pair, no parent or child, and reciprocity with the other end |
|
|
430
430
|
| `checkRoutes` | `checkRoutes(note, { index })` | `object[]` | linting a place's `routes` — as `checkBorders`, plus `mode`, `days` and `terrain`, once per pair per mode |
|
|
431
431
|
|
|
432
|
+
### `engine.holdings`
|
|
433
|
+
|
|
434
|
+
What lies within a place, who holds it, and what an affiliation holds — read off `data.parents` and `data.domains` across this package and every fetched index, inverted into the `contains`, `held_by` and `holdings` lists the site build writes — and the tenure check `content-build lint` runs on a place.
|
|
435
|
+
|
|
436
|
+
| Export | Signature | Returns | Use it when |
|
|
437
|
+
| ---------------------- | ------------------------------------ | ----------------------- | ---------------------------------------------------------------------------------------------------------------------------- |
|
|
438
|
+
| `HOLDINGS_KEYS` | `const HOLDINGS_KEYS` | `readonly string[]` | the three derived keys — `contains`, `held_by`, `holdings` — which a note cannot author |
|
|
439
|
+
| `HELD_SUBTYPES` | `const HELD_SUBTYPES` | `readonly string[]` | the place subTypes tenure is checked on — `settlement`, `site`, `structure` |
|
|
440
|
+
| `holdingsNode` | `holdingsNode(fm, { title, url })` | `HoldingsNode\|null` | reading a local note as a node of the derivation, or `null` for one that is neither a place nor an affiliation |
|
|
441
|
+
| `foreignHoldingsNodes` | `foreignHoldingsNodes(foreignIndex)` | `HoldingsNode[]` | reading a fetched index's places and affiliations as nodes |
|
|
442
|
+
| `holdingsPages` | `holdingsPages(nodes)` | `Map<string, Holdings>` | inverting `parents` and `domains` into each page's sorted lists, keyed by URL, for every page with at least one entry |
|
|
443
|
+
| `checkHeld` | `checkHeld(note, { index })` | `object[]` | linting a place's tenure — a settlement, site or structure no affiliation's `domains` names is a warning at its `type:` line |
|
|
444
|
+
|
|
445
|
+
### `engine.marketClass`
|
|
446
|
+
|
|
447
|
+
What trade a settlement supports (`data.market`): the six-step scale, stated once for the lint, the author-facing reference and the specification, and the check that holds a note's value to it.
|
|
448
|
+
|
|
449
|
+
| Export | Signature | Returns | Use it when |
|
|
450
|
+
| ---------------- | ---------------------- | ------------------- | ------------------------------------------------------------------------------------------------- |
|
|
451
|
+
| `MARKET_CLASSES` | `const MARKET_CLASSES` | `readonly object[]` | the scale in order, each step its `value`, the `name` a settlement of it goes by, and its `trade` |
|
|
452
|
+
| `isMarketClass` | `isMarketClass(value)` | `boolean` | asking whether an authored value names a class, a quoted number included |
|
|
453
|
+
| `marketClass` | `marketClass(value)` | `object\|undefined` | the class a value names, for rendering what a number means |
|
|
454
|
+
| `checkMarket` | `checkMarket(note)` | `object[]` | linting a place's `market` — a value off the scale, located at the value that states it |
|
|
455
|
+
|
|
432
456
|
### `engine.mapPlaces`
|
|
433
457
|
|
|
434
458
|
The places a map is drawn from — this package's index records and every fetched dependency's entries, read through one shape — and what their `parents` say: the world, the continents, each place's continent, and everything the containment tree can get wrong, reported as findings located at the entry that states it.
|
|
@@ -437,6 +461,7 @@ The places a map is drawn from — this package's index records and every fetche
|
|
|
437
461
|
| --------------------- | ---------------------------------------------------------------- | ------------- | ------------------------------------------------------------------------------------------------------------------------ |
|
|
438
462
|
| `placesFromRecords` | `placesFromRecords(records, { contentBase, base })` | `MapWorld` | reading this package's places and polities out of its index records, with a page URL each where a site base is given |
|
|
439
463
|
| `loadMapWorld` | `loadMapWorld({ config, contentBase, base, problems })` | `MapWorld` | loading the world a map is drawn from — this package's index and every declared dependency's |
|
|
464
|
+
| `mapWorld` | `mapWorld({ records, foreignIndex, contentBase, base, config })` | `MapWorld` | the same world out of records and indexes a caller already holds, as the site build does |
|
|
440
465
|
| `analyzeContainment` | `analyzeContainment(places, polities)` | `Containment` | finding the world, the continents, each place's continent and the anomalies of `parents` |
|
|
441
466
|
| `subtreeOf` | `subtreeOf(analysis, root)` | `Set<string>` | every place beneath one, by following `parents` downward |
|
|
442
467
|
| `containmentFindings` | `containmentFindings(analysis, places, { contentBase, config })` | `object[]` | the anomalies as `file:line:column: severity: message` findings — no parent, an unresolved parent and a cycle are errors |
|
|
@@ -485,6 +510,7 @@ GraphViz, as the map command reaches it: a build-time tool of that command alone
|
|
|
485
510
|
| Export | Signature | Returns | Use it when |
|
|
486
511
|
| ------------------------ | ------------------------------------------------------- | ------------------- | ------------------------------------------------------------------------------------------ |
|
|
487
512
|
| `GRAPHVIZ_ENGINES` | `const GRAPHVIZ_ENGINES` | `readonly string[]` | the engines the map command may ask for — `dot`, `twopi`, `neato` |
|
|
513
|
+
| `GRAPHVIZ_INSTALL` | `const GRAPHVIZ_INSTALL` | `string` | what installs GraphViz, as the clause every "not installed" message ends with |
|
|
488
514
|
| `findGraphviz` | `findGraphviz(engine, { env, fallbacks })` | `string\|undefined` | finding an engine's binary on `PATH` or in the directories a package manager installs into |
|
|
489
515
|
| `graphvizMissingMessage` | `graphvizMissingMessage(engine)` | `string` | saying an engine is not installed, and what installs it |
|
|
490
516
|
| `renderDot` | `renderDot(dotPath, outPath, { binary, format, args })` | `{warnings}` | rendering a `.dot` file, the engine's warnings returned and a non-zero exit thrown |
|
|
@@ -500,6 +526,16 @@ GraphViz, as the map command reaches it: a build-time tool of that command alone
|
|
|
500
526
|
| `relatedPlaces` | `relatedPlaces(places)` | `string[]` | every place that states, or is named in, a border or a route |
|
|
501
527
|
| `buildMaps` | `buildMaps({ world, outDir, tree, root, from, chart, horizon, travel, engine, rankdir, nodesep, ranksep, scale, polities, locate, requireGraphviz })` | `{written, findings, skipped}` | drawing any of the four from a loaded world |
|
|
502
528
|
|
|
529
|
+
### `engine.siteMaps`
|
|
530
|
+
|
|
531
|
+
The map from each place, drawn for the site: `content-build site` draws the map from every place of this package that states, or is named in, a border or a route — with the site's base, so every name links through it — and the page writer lays each drawing beside its page as `from-<shortcode>.svg` and names it in the front matter as `map`. GraphViz absent is one warning and no map; `site.maps: false` draws none.
|
|
532
|
+
|
|
533
|
+
| Export | Signature | Returns | Use it when |
|
|
534
|
+
| -------------- | --------------------------------------------------------------- | ------------------ | --------------------------------------------------------------------------------------------------------------- |
|
|
535
|
+
| `SITE_MAP_DIR` | `const SITE_MAP_DIR` | `string` | where the site build draws below a package's root, `build/map/site` |
|
|
536
|
+
| `inlineSvg` | `inlineSvg(svg)` | `string` | rewriting a GraphViz SVG for inlining — no prologue, no comment, no `xlink:`, the root sized by `viewBox` alone |
|
|
537
|
+
| `drawSiteMaps` | `drawSiteMaps({ records, foreignIndex, config, base, locate })` | `{maps, findings}` | drawing the map from every related place of this package, keyed by the URL of the page that carries it |
|
|
538
|
+
|
|
503
539
|
### `engine.systems`
|
|
504
540
|
|
|
505
541
|
The closed registry of system ids, and the `none` that stands for no system at all. An unknown system value is an error; adding a system is a data change to this registry rather than a hardcoded set scattered through the pipeline.
|
|
@@ -694,20 +730,20 @@ The toolchain's own content index — the files it ships, addressed. Every other
|
|
|
694
730
|
|
|
695
731
|
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.
|
|
696
732
|
|
|
697
|
-
| Export | Signature | Returns | Use it when
|
|
698
|
-
| --------------------- | ------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------- |
|
|
699
|
-
| `collectContentPages` | `function collectContentPages(contentBase, ctx)` | {{pages: object[], addressFindings: object[], fmLinkFindings: object[]}} | The content tree's pages, and what could not be addressed.
|
|
700
|
-
| `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>/`.
|
|
701
|
-
| `writeHomepages` | `function writeHomepages(outRoot, pages, config)` | {number} How many pages were written. | Writes each homepage as the package root's `_index.md`.
|
|
702
|
-
| `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.
|
|
703
|
-
| `emptyGates` | `function emptyGates()` | {object} An all-clear gate result. | The gate result of a build that ran none of them.
|
|
704
|
-
| `gatesFailed` | `function gatesFailed(gates)` | — | Whether any gate produced a finding.
|
|
705
|
-
| `tableUniverse` | `function tableUniverse(pages)` | {Map<string, object[]>} Package → the notes it may tabulate. | The universe a generated table searches, grouped by package.
|
|
706
|
-
| `pageFrontmatter` | `function pageFrontmatter(page,` | {object} The frontmatter to write. | The frontmatter a page publishes with.
|
|
707
|
-
| `pageDestination` | `function pageDestination(page)`
|
|
708
|
-
| `renderPages` | `function renderPages(pages, options)` | {{written: number, byKind: Record<string, number>, tableErrors: object[], wikiErrors: object[]}} | Renders and writes every page.
|
|
709
|
-
| `resolveSitePass` | `function resolveSitePass(name, options)` | {{beforeLinks?: Function}} The bundle. | Resolves `site.pass` to its bundle.
|
|
710
|
-
| `buildSite` | `buildSite({ config, sqlTables })`
|
|
733
|
+
| Export | Signature | Returns | Use it when |
|
|
734
|
+
| --------------------- | ------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------- |
|
|
735
|
+
| `collectContentPages` | `function collectContentPages(contentBase, ctx)` | {{pages: object[], addressFindings: object[], fmLinkFindings: object[]}} | The content tree's pages, and what could not be addressed. |
|
|
736
|
+
| `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>/`. |
|
|
737
|
+
| `writeHomepages` | `function writeHomepages(outRoot, pages, config)` | {number} How many pages were written. | Writes each homepage as the package root's `_index.md`. |
|
|
738
|
+
| `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. |
|
|
739
|
+
| `emptyGates` | `function emptyGates()` | {object} An all-clear gate result. | The gate result of a build that ran none of them. |
|
|
740
|
+
| `gatesFailed` | `function gatesFailed(gates)` | — | Whether any gate produced a finding. |
|
|
741
|
+
| `tableUniverse` | `function tableUniverse(pages)` | {Map<string, object[]>} Package → the notes it may tabulate. | The universe a generated table searches, grouped by package. |
|
|
742
|
+
| `pageFrontmatter` | `function pageFrontmatter(page,` | {object} The frontmatter to write. | The frontmatter a page publishes with. |
|
|
743
|
+
| `pageDestination` | `function pageDestination(page, { bundle })` | — | Where a page is written, relative to the output root: flat, or as a leaf bundle when it carries a file beside itself. |
|
|
744
|
+
| `renderPages` | `function renderPages(pages, options)` | {{written: number, byKind: Record<string, number>, tableErrors: object[], wikiErrors: object[]}} | Renders and writes every page. |
|
|
745
|
+
| `resolveSitePass` | `function resolveSitePass(name, options)` | {{beforeLinks?: Function}} The bundle. | Resolves `site.pass` to its bundle. |
|
|
746
|
+
| `buildSite` | `buildSite({ config, sqlTables, locate })` | {{gates: object, stats: object\|null, tableErrors: object[], wikiErrors: object[], mapFindings: object[], manifests: object\|null}} | Builds a Hugo content tree from a content tree, and reports what it found. |
|
|
711
747
|
|
|
712
748
|
### `engine.contentLint`
|
|
713
749
|
|
package/docs/configuration.md
CHANGED
|
@@ -770,6 +770,7 @@ Any other key under `docs.itemFields` is refused:
|
|
|
770
770
|
| `site.notfound` | object | `null` |
|
|
771
771
|
| `site.hugo` | object | `{}` |
|
|
772
772
|
| `site.search` | boolean | `true` |
|
|
773
|
+
| `site.maps` | boolean | `true` |
|
|
773
774
|
|
|
774
775
|
How much of a package reaches the web at all is **not** here — it is
|
|
775
776
|
[`publish.site`](#publish). `site` is framing: which named pass bundle
|
|
@@ -819,7 +820,7 @@ published. A `site.out` is refused by name:
|
|
|
819
820
|
|
|
820
821
|
> ``package-build config: `site` must be a mapping.``
|
|
821
822
|
|
|
822
|
-
> ``package-build config: `site.<key>` is not a recognized option (expected one of: base, assets, description, packages, pass, passOptions, notfound, hugo, search).``
|
|
823
|
+
> ``package-build config: `site.<key>` is not a recognized option (expected one of: base, assets, description, packages, pass, passOptions, notfound, hugo, search, maps).``
|
|
823
824
|
|
|
824
825
|
`site.assets` is the host every package's imagery is served from, and it is
|
|
825
826
|
the one address in this file that is not this repository's own. A note names
|
|
@@ -950,6 +951,24 @@ site:
|
|
|
950
951
|
|
|
951
952
|
> ``package-build config: `site.search` must be a boolean.``
|
|
952
953
|
|
|
954
|
+
`site.maps` is whether every place page carries the map from that place. On
|
|
955
|
+
by default: `content-build site` draws the map `content-build map --from`
|
|
956
|
+
draws — the place's borders and routes, each neighbour at its bearing — for
|
|
957
|
+
every place that states a border or a route or is named in one, lays it beside
|
|
958
|
+
the page as `from-<shortcode>.svg`, and names the file in the page's front
|
|
959
|
+
matter as `map`, which the theme inlines so every place name on it is a link
|
|
960
|
+
to that place's page. A place with no relation carries no map and no key.
|
|
961
|
+
GraphViz draws; when it is not installed the build says so once, as a
|
|
962
|
+
warning, and writes every page as it would without maps — a site never fails
|
|
963
|
+
for want of one. `false` draws nothing and asks nothing of GraphViz.
|
|
964
|
+
|
|
965
|
+
```yaml
|
|
966
|
+
site:
|
|
967
|
+
maps: false
|
|
968
|
+
```
|
|
969
|
+
|
|
970
|
+
> ``package-build config: `site.maps` must be a boolean.``
|
|
971
|
+
|
|
953
972
|
### The generated Hugo configuration
|
|
954
973
|
|
|
955
974
|
`content-build site` writes `build/hugo/hugo.toml` on every run. Every value
|
package/docs/content-format.md
CHANGED
|
@@ -2440,6 +2440,23 @@ subType `deity` covers a god's nature, domains, epithets and aspects in prose.
|
|
|
2440
2440
|
That frees the name for the territorial sense, which earns it: every affiliation
|
|
2441
2441
|
subType holds sway somewhere, while the divine sense applies to one.
|
|
2442
2442
|
|
|
2443
|
+
**`domains` names what the affiliation holds directly**, and nothing beneath
|
|
2444
|
+
it. A polity whose `domains` names a region holds the region; the settlements
|
|
2445
|
+
within it are the region's, reached through its `contains`, and are not
|
|
2446
|
+
repeated here. A house holds its manors and names each; the crown holds the
|
|
2447
|
+
realm and names the realm. What lies below a holder is answered by the two
|
|
2448
|
+
hierarchies together — `place.parents` for where a place sits, and
|
|
2449
|
+
`affiliation.parents` for whom a holder answers to — so a subinfeudated manor
|
|
2450
|
+
sits in one region by the first and under a lord of another polity by the
|
|
2451
|
+
second, and both pages say so.
|
|
2452
|
+
|
|
2453
|
+
The website reads `domains` across the package and every fetched index and
|
|
2454
|
+
writes the affiliation's page a `holdings` list — every place its `domains`
|
|
2455
|
+
names, each entry `{ title, url, type, subType }`, sorted by `subType` then
|
|
2456
|
+
`title`, and absent when `domains` names nothing. Every place it names carries
|
|
2457
|
+
the affiliation back in `held_by`. Both are derived; a note that writes one has
|
|
2458
|
+
it replaced.
|
|
2459
|
+
|
|
2443
2460
|
If `sohl` is present, this becomes an `affiliation` item.
|
|
2444
2461
|
|
|
2445
2462
|
| shared source | → sohl | → hm3 |
|
|
@@ -3087,9 +3104,38 @@ three maps, and none of those maps needs to know it is a keep.
|
|
|
3087
3104
|
| `lore` | `WikiLink[]` | Lore concerning this place — its peoples, its law, its calendar, its history |
|
|
3088
3105
|
| `parents` | `WikiLink[]` | Enclosing places within which this place is located |
|
|
3089
3106
|
| `population` | `number` | Approximate population (precision 2 significant digits) |
|
|
3107
|
+
| `market` | `number` | Market class, 1 to 6 — what trade a settlement supports |
|
|
3090
3108
|
| `borders` | `{ to, bearing }[]` | Places sharing a frontier with this one, and where each lies from here |
|
|
3091
3109
|
| `routes` | `{ to, bearing, mode, days, terrain?, leagues? }[]` | Journeys from this place's centre to another place |
|
|
3092
3110
|
|
|
3111
|
+
#### What a settlement's market supports
|
|
3112
|
+
|
|
3113
|
+
`population` states how many people a settlement holds; `market` states what a
|
|
3114
|
+
traveller can buy there. Those are different questions — two settlements of
|
|
3115
|
+
eight hundred are a different proposition when one holds a weekly market and
|
|
3116
|
+
the other a chartered fair with a moneylender — so a note states both, either,
|
|
3117
|
+
or neither.
|
|
3118
|
+
|
|
3119
|
+
The value is a **class**, not a measurement. The numbers order six steps and do
|
|
3120
|
+
nothing else: a `5` is not a market two fifths larger than a `3`, it is a city
|
|
3121
|
+
rather than a town. `content-build lint` refuses a value off the scale instead
|
|
3122
|
+
of rounding it onto one, so a reader can trust that a number means the same
|
|
3123
|
+
thing on every page.
|
|
3124
|
+
|
|
3125
|
+
| `market` value | the settlement is | what can be had there |
|
|
3126
|
+
| -------------- | ----------------- | ------------------------------------------------------------------------------------------------- |
|
|
3127
|
+
| `1` | a hamlet | no market — what neighbours trade among themselves |
|
|
3128
|
+
| `2` | a village | a weekly market: staples, a smith, what a household cannot make |
|
|
3129
|
+
| `3` | a town | a regular market, most common goods, several trades working full time |
|
|
3130
|
+
| `4` | a market town | a chartered fair, goods carried from a week away, a moneylender |
|
|
3131
|
+
| `5` | a city | anything ordinary, in quantity; foreign goods; a guild structure |
|
|
3132
|
+
| `6` | a great city | the rare and the imported as a matter of course; banking; the market from which other markets buy |
|
|
3133
|
+
|
|
3134
|
+
The key is meaningful on a settlement, and the lint checks no `subType`
|
|
3135
|
+
condition — as it checks none for `population`. Which places a fact says
|
|
3136
|
+
something about is the author's judgement, and the `data:` container is closed
|
|
3137
|
+
per type rather than per subType.
|
|
3138
|
+
|
|
3093
3139
|
#### Where a place is, in relation to other places
|
|
3094
3140
|
|
|
3095
3141
|
`parents` states what a place is **within**. Two more properties state what
|
|
@@ -3187,6 +3233,31 @@ closed set, and each finding is located at the entry that states it.
|
|
|
3187
3233
|
| `river` | boat |
|
|
3188
3234
|
| `lake` | boat |
|
|
3189
3235
|
|
|
3236
|
+
#### What lies within a place, and who holds it
|
|
3237
|
+
|
|
3238
|
+
`parents` states what a place is within; an affiliation's `domains` states
|
|
3239
|
+
what it holds. The website reads both across the package and every fetched
|
|
3240
|
+
index and writes each place's page two lists, each entry
|
|
3241
|
+
`{ title, url, type, subType }` and each absent when empty:
|
|
3242
|
+
|
|
3243
|
+
- `contains` — every place whose `parents` names this one, sorted by
|
|
3244
|
+
`subType` then `title`. A region lists its settlements, sites, structures
|
|
3245
|
+
and features; a settlement lists the structures within it.
|
|
3246
|
+
- `held_by` — every affiliation whose `domains` names this one, sorted by
|
|
3247
|
+
`title`.
|
|
3248
|
+
|
|
3249
|
+
Both are derived; a note that writes one has it replaced. **`domains` is
|
|
3250
|
+
never expanded**: a region held by a polity lists the polity in `held_by`, and
|
|
3251
|
+
a settlement within that region lists only the house or order whose `domains`
|
|
3252
|
+
names it — its lord's lord is reached through `affiliation.parents`.
|
|
3253
|
+
|
|
3254
|
+
**A settlement, a site or a structure is held by someone.** One of those that
|
|
3255
|
+
no affiliation's `domains` names — in this package or in a fetched index — is
|
|
3256
|
+
a warning from `content-build lint`, `unheld land`, located at the note's
|
|
3257
|
+
`type:` line, so a gap in tenure shows while a note is written. A `world`, a
|
|
3258
|
+
`region` and a `feature` are exempt: a region is held through its polity's
|
|
3259
|
+
`domains`, and a river by nobody.
|
|
3260
|
+
|
|
3190
3261
|
**A place declares only what is true of ground.** Four properties were removed
|
|
3191
3262
|
because they were true of something else, and each removal has a home to go to.
|
|
3192
3263
|
|
|
@@ -1424,6 +1424,8 @@ export function lintNote(
|
|
|
1424
1424
|
if (typeof field.check !== "function") continue;
|
|
1425
1425
|
findings.push(...field.check(note, { index }));
|
|
1426
1426
|
}
|
|
1427
|
+
// A check the type declares of the whole note, with the same index.
|
|
1428
|
+
if (typeof entry.check === "function") findings.push(...entry.check(note, { index }));
|
|
1427
1429
|
}
|
|
1428
1430
|
|
|
1429
1431
|
const fields = authoredFields(schema);
|