@heroiclands/package-build 22.4.3 → 22.4.5
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 +28 -18
- package/CONTENT.md +77 -1
- package/README.md +9 -0
- package/bin/content-build.mjs +143 -0
- package/bin/package-build.mjs +18 -4
- package/content-config.mjs +7 -0
- package/docs/api.md +84 -0
- package/docs/commands.md +182 -2
- package/docs/configuration.md +19 -1
- package/docs/content-format.md +105 -6
- package/docs/diagnostics.md +1 -0
- package/docs/project-setup.md +7 -6
- package/engine/changelog-group.mjs +27 -6
- package/engine/changelog-lint.mjs +27 -7
- package/engine/frontmatter-lint.mjs +11 -1
- package/engine/homepage.mjs +9 -1
- package/engine/index.mjs +18 -0
- package/engine/map-build.mjs +240 -0
- package/engine/map-dot.mjs +685 -0
- package/engine/map-graphviz.mjs +138 -0
- package/engine/map-layout.mjs +712 -0
- package/engine/map-places.mjs +561 -0
- package/engine/metadata-index.mjs +7 -0
- package/engine/note-vocabulary.mjs +28 -0
- package/engine/place-relations.mjs +637 -0
- package/engine/related-pages.mjs +161 -0
- package/engine/site-build.mjs +107 -9
- package/engine/site-config.mjs +1 -1
- package/engine/site-index.mjs +5 -1
- package/engine/site-root.mjs +147 -6
- package/engine/web-wikilinks.mjs +13 -2
- package/package.json +3 -2
- package/types/engine/changelog-lint.d.mts +9 -7
- package/types/engine/homepage.d.mts +8 -1
- package/types/engine/index.d.mts +6 -0
- package/types/engine/map-build.d.mts +78 -0
- package/types/engine/map-dot.d.mts +130 -0
- package/types/engine/map-graphviz.d.mts +61 -0
- package/types/engine/map-layout.d.mts +359 -0
- package/types/engine/map-places.d.mts +259 -0
- package/types/engine/note-vocabulary.d.mts +12 -0
- package/types/engine/place-relations.d.mts +95 -0
- package/types/engine/related-pages.d.mts +70 -0
- package/types/engine/site-build.d.mts +36 -3
- package/types/engine/site-config.d.mts +1 -1
- package/types/engine/site-index.d.mts +5 -1
- package/types/engine/site-root.d.mts +59 -7
- package/types/engine/web-wikilinks.d.mts +11 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,45 +1,55 @@
|
|
|
1
1
|
# @heroiclands/package-build
|
|
2
2
|
|
|
3
|
+
## 22.4.5
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
A `place` note may state what it is next to and what it is reachable from, as `data.borders` (`{ to, bearing }`) and `data.routes` (`{ to, bearing, mode, days, terrain?, leagues? }`), with bearings from the eight compass points, modes `land | boat | ship`, days from the marker scale `1 2 3 5 10 20 30 45 60 90 180 360` and terrains from a closed registry. `content-build lint` checks that every `to` is a place, that every value is from its set, and that the other note states the pair back — a missing reciprocal is a warning naming both notes, a contradictory one an error. The content index carries both lists, so a dependency's places take part.
|
|
8
|
+
|
|
9
|
+
**Maps**
|
|
10
|
+
|
|
11
|
+
- A new `content-build map --tree` draws the containment tree from `parents`, clustered by continent, and reports a place with no parent, a parent no place declares, and a cycle.
|
|
12
|
+
- `content-build map --from <shortcode>` draws the map from that place: north up, each neighbour at its bearing and on a ring of its days, places two hops out drawn dimmer.
|
|
13
|
+
- `--from all` draws one map for every place with a border or a route, and `--travel` draws the whole route graph.
|
|
14
|
+
- The drawings land under `build/map/` as SVG beside their DOT source; GraphViz is needed for this command alone, and its absence is reported plainly.
|
|
15
|
+
|
|
16
|
+
**Site**
|
|
17
|
+
|
|
18
|
+
- The rendered site carries a search index, built beside the pages when the site's root files are written and served with them.
|
|
19
|
+
- `site.search: false` in `package-build.config.yaml` turns it off.
|
|
20
|
+
|
|
21
|
+
## 22.4.4
|
|
22
|
+
|
|
23
|
+
### Patch Changes
|
|
24
|
+
|
|
25
|
+
Every page a site build publishes carries a `related` block in its front matter — the pages that link to it and the pages it links to, each named once with its title, address and type — which the theme renders as the Related card at the foot of the page. A page nothing connects to carries no block, and the homepage is counted on both sides like any other page.
|
|
26
|
+
|
|
27
|
+
`changelog group` keeps a hard-wrapped paragraph as one block instead of fragmenting it one block per line, and no longer inserts a blank line under a bold sentence a paragraph continues directly beneath.
|
|
28
|
+
|
|
3
29
|
## 22.4.3
|
|
4
30
|
|
|
5
31
|
### Patch Changes
|
|
6
32
|
|
|
33
|
+
**A new `changelog group` command folds a release's changeset blocks together by their bold label.**
|
|
7
34
|
`@heroiclands/package-build/changelog` writes each changeset's summary into `CHANGELOG.md` as its
|
|
8
|
-
|
|
9
35
|
own block, so three pull requests each touching compendium content leave three separate
|
|
10
|
-
|
|
11
36
|
`**Compendiums**` blocks instead of one. `changelog group` rewrites the newest release section in
|
|
12
|
-
|
|
13
37
|
place: every block sharing one label is merged into a single block, bullets kept in their original
|
|
14
|
-
|
|
15
38
|
order with an exact duplicate kept once, and the merged blocks are ordered by a new
|
|
16
|
-
|
|
17
39
|
`changelog.labels` configuration key — the unlabelled lead paragraph first, then every declared
|
|
18
|
-
|
|
19
40
|
label in order, then any undeclared label last with a warning naming it. `changelog check` warns on
|
|
20
|
-
|
|
21
41
|
a label absent from a declared `changelog.labels` too, so a drifted spelling is caught before a
|
|
22
|
-
|
|
23
42
|
release ships it.
|
|
24
43
|
|
|
44
|
+
**A shipped changelog generator writes a release note without a commit hash.**
|
|
25
45
|
`@heroiclands/package-build/changelog` is a Changesets changelog generator
|
|
26
|
-
|
|
27
46
|
whose `getReleaseLine` renders a changeset's summary as a block, verbatim —
|
|
28
|
-
|
|
29
47
|
no bullet, no indentation, separated from its neighbours by one blank line —
|
|
30
|
-
|
|
31
48
|
and whose `getDependencyReleaseLine` writes nothing. Point
|
|
32
|
-
|
|
33
49
|
`.changeset/config.json` at it
|
|
34
|
-
|
|
35
50
|
(`"changelog": "@heroiclands/package-build/changelog"`) to stop every release
|
|
36
|
-
|
|
37
51
|
note from carrying a commit-hash prefix.
|
|
38
52
|
|
|
39
|
-
**A new `changelog group` command folds a release's changeset blocks together by their bold label.**
|
|
40
|
-
|
|
41
|
-
**A shipped changelog generator writes a release note without a commit hash.**
|
|
42
|
-
|
|
43
53
|
## 22.4.2
|
|
44
54
|
|
|
45
55
|
### Patch Changes
|
package/CONTENT.md
CHANGED
|
@@ -1168,8 +1168,35 @@ builder, so schema and compiler cannot disagree. The hand-written compilers —
|
|
|
1168
1168
|
`being`, `macro`, `doc` and the three map types — declare theirs in
|
|
1169
1169
|
`sohl/note-schemas.mjs`.
|
|
1170
1170
|
|
|
1171
|
+
**A place's `borders` and `routes` are checked from both ends.** A `place`
|
|
1172
|
+
note states what it is next to and what it is reachable from as two `data:`
|
|
1173
|
+
lists — `borders: [{ to, bearing }]` and
|
|
1174
|
+
`routes: [{ to, bearing, mode, days, terrain?, leagues? }]` — and every value
|
|
1175
|
+
is from a closed set: the eight bearings, `land | boat | ship`, a days scale of
|
|
1176
|
+
`1 2 3 5 10 20 30 45 60 90 180 360`, and a terrain registry in which each
|
|
1177
|
+
terrain names the modes that cross it. Every `to` must be the shortcode of a
|
|
1178
|
+
place, here or in a fetched index. The other end must state the pair back —
|
|
1179
|
+
at the opposite bearing, and for a route by the same mode in the same days.
|
|
1180
|
+
A neighbour stating nothing is a warning naming both notes; one stating a
|
|
1181
|
+
different bearing, mode or days is an error, as is a border to the note's own
|
|
1182
|
+
parent or child, or a pair listed twice by one mode. Each finding lands on the
|
|
1183
|
+
entry that states it. The keys, the sets and the days scale are specified under
|
|
1184
|
+
`type: place` in `docs/content-format.md`.
|
|
1185
|
+
|
|
1171
1186
|
Nothing here writes. A check reports and an author fixes.
|
|
1172
1187
|
|
|
1188
|
+
**`content-build map` draws what those keys state.** `--tree` is the author's
|
|
1189
|
+
check of `parents` — the containment tree, clustered by continent, with a
|
|
1190
|
+
place no parent reaches, a parent no place declares and a cycle flagged red
|
|
1191
|
+
and reported as findings. `--from <shortcode>` is the reader's map: the place
|
|
1192
|
+
at the centre, north up, each neighbour at the angle of its bearing and on a
|
|
1193
|
+
log-spaced ring of its days, a second hop drawn dimmer where the two hops
|
|
1194
|
+
agree; `--from all` draws one for every place with a relation. `--travel` is
|
|
1195
|
+
the whole route graph. Every drawing reads the content index, so a
|
|
1196
|
+
dependency's places take part, and lands under `build/map/` as a `.dot`
|
|
1197
|
+
beside its `.svg`; GraphViz draws, and is needed by this command alone. The
|
|
1198
|
+
command and its rings are documented in `docs/commands.md`.
|
|
1199
|
+
|
|
1173
1200
|
### The `data:` container is closed; the top level is not
|
|
1174
1201
|
|
|
1175
1202
|
A note's frontmatter has three regions, and only one of them is open:
|
|
@@ -1409,7 +1436,9 @@ mount's `_index.md` as the `home` kind at `baseURL`, which is
|
|
|
1409
1436
|
since Cloudflare Pages would apply it. Its `_headers` suppress indexing on
|
|
1410
1437
|
every host-assigned address and nothing else: no `Cache-Control` is pinned on
|
|
1411
1438
|
the prefix root, because a lifetime on the homepage would hold a stale copy at
|
|
1412
|
-
the most-linked address after a deploy.
|
|
1439
|
+
the most-linked address after a deploy. The same command then indexes the
|
|
1440
|
+
rendered pages for search into `<package>/pagefind/`, served with the rest of
|
|
1441
|
+
the site; `site.search: false` skips it.
|
|
1413
1442
|
|
|
1414
1443
|
### The homepage's own links
|
|
1415
1444
|
|
|
@@ -2333,6 +2362,53 @@ notes it lists, and link it from the homepage. Nothing is generated between
|
|
|
2333
2362
|
the homepage and the pages, so delete the key.
|
|
2334
2363
|
````
|
|
2335
2364
|
|
|
2365
|
+
### Every page carries what links to it, and what it links to
|
|
2366
|
+
|
|
2367
|
+
The build resolves every wikilink on every page through the address index, so
|
|
2368
|
+
at the moment it writes a page it holds the whole link graph. It writes the
|
|
2369
|
+
part that concerns each page into that page's front matter:
|
|
2370
|
+
|
|
2371
|
+
```yaml
|
|
2372
|
+
related:
|
|
2373
|
+
backlinks: # pages that link to this one
|
|
2374
|
+
- title: Afzandah Parnâzar
|
|
2375
|
+
url: /thalorna/being-afzndhprnzr/
|
|
2376
|
+
type: being
|
|
2377
|
+
mentions: # pages this one links to
|
|
2378
|
+
- title: Kethramír
|
|
2379
|
+
url: /thalorna/place-kthrmr/
|
|
2380
|
+
type: place
|
|
2381
|
+
```
|
|
2382
|
+
|
|
2383
|
+
The theme's `related.html` partial reads both lists and renders them as one
|
|
2384
|
+
"Related" card, grouped by `type`. What the build guarantees about them:
|
|
2385
|
+
|
|
2386
|
+
- **A page appears once per list**, however many times it is linked, and
|
|
2387
|
+
whichever address the link was written to: `docbeing-grod` and `being-grod`
|
|
2388
|
+
are one page, and a page that cites it under both lists it once. A link from
|
|
2389
|
+
a page to itself is not a connection and is dropped.
|
|
2390
|
+
- **Each list is sorted by `type`, then `title`**, so the card's grouping is
|
|
2391
|
+
the same on every build whatever order the notes were walked in.
|
|
2392
|
+
- **Both lists are present whenever either has an entry**, so a theme reads one
|
|
2393
|
+
shape; a page with no links either way carries **no `related` key at all**,
|
|
2394
|
+
which is the partial's silent-disappear convention kept at the source.
|
|
2395
|
+
- **`url` composes `<base><slug>/`**, like every other href this build renders
|
|
2396
|
+
— see [A page's URL is its address](#a-pages-url-is-its-address). The page
|
|
2397
|
+
states its own address as `/<slug>/`; everything pointing _at_ it, this
|
|
2398
|
+
included, carries the base.
|
|
2399
|
+
- **The homepage is a page like any other on both sides.** A content page
|
|
2400
|
+
reaches it through `[[homepage-root|Text]]` and is listed in its backlinks;
|
|
2401
|
+
its own body links are ordinary markdown, resolved against the package root
|
|
2402
|
+
the way a browser resolves them, so `being-grod/`, `/thalorna/being-grod/`
|
|
2403
|
+
and `./being-grod/#top` each count as a mention of that page. A link that
|
|
2404
|
+
names a host leaves the site and counts as nothing.
|
|
2405
|
+
- **Only a page of this site is an endpoint.** A link into another package
|
|
2406
|
+
resolves to an href the build renders but no page it writes, so it is listed
|
|
2407
|
+
on neither side; a backlink can be counted only on a page this build emits.
|
|
2408
|
+
|
|
2409
|
+
`related` is derived, never authored: what links to a page is a fact about
|
|
2410
|
+
every other page in the tree. A note that writes one has it replaced.
|
|
2411
|
+
|
|
2336
2412
|
### Why the output location is fixed
|
|
2337
2413
|
|
|
2338
2414
|
The content mount is a build artifact and is **deleted on every run**, so that a
|
package/README.md
CHANGED
|
@@ -335,6 +335,7 @@ npx package-build lang coverage [--unused]
|
|
|
335
335
|
npx package-build lang hardcoded
|
|
336
336
|
npx package-build bundle check
|
|
337
337
|
npx package-build release [--no-pdf]
|
|
338
|
+
npx package-build site-root [--out <dir>]
|
|
338
339
|
npx package-build deploy <stage>
|
|
339
340
|
npx package-build container <stage> <start|stop|restart|recreate|rm|status|logs|pull>
|
|
340
341
|
npx package-build e2e <seed|run|open|fast|sweep>
|
|
@@ -345,6 +346,14 @@ npx package-build e2e <seed|run|open|fast|sweep>
|
|
|
345
346
|
publishes a content tree — the book that tree compiles to, as
|
|
346
347
|
`<artifact>-<version>.pdf`.
|
|
347
348
|
|
|
349
|
+
`package-build site-root` finishes a site build. Hugo renders into
|
|
350
|
+
`build/site/<contentPackage>/`; this writes the deployment's `_headers` beside
|
|
351
|
+
that, and indexes the rendered pages for search with Pagefind into
|
|
352
|
+
`build/site/<contentPackage>/pagefind/`, deployed with the pages and served at
|
|
353
|
+
`/<contentPackage>/pagefind/`. The dependency is this package's, declared once,
|
|
354
|
+
so every site — and every local build — gets an index without the repository
|
|
355
|
+
adding one; `site.search: false` in `package-build.config.yaml` turns it off.
|
|
356
|
+
|
|
348
357
|
The book is **built by default and never fatal**. A package publishing only a
|
|
349
358
|
homepage, one with no `pdf:` block, and one with no content tree each build
|
|
350
359
|
none and say so; `--no-pdf` is for a release that has a tree and does not want
|
package/bin/content-build.mjs
CHANGED
|
@@ -47,6 +47,7 @@
|
|
|
47
47
|
* npx content-build markdown [paths..] [--fix]
|
|
48
48
|
* npx content-build manifest [root] [--out <dir>]
|
|
49
49
|
* npx content-build site [--out <dir>]
|
|
50
|
+
* npx content-build map [--tree] [--from <shortcode>] [--travel] [--out <dir>]
|
|
50
51
|
* npx content-build reachability <dir> [file] [--index <shortcode>]
|
|
51
52
|
* npx content-build addresses diff --from <zip|dir> [--strict]
|
|
52
53
|
*
|
|
@@ -144,6 +145,9 @@ import {
|
|
|
144
145
|
addressFindingMessage,
|
|
145
146
|
} from "../engine/address-diff.mjs";
|
|
146
147
|
import { emittedArtFor, itemPackJsonDirs } from "../engine/generate.mjs";
|
|
148
|
+
import { buildMaps, FROM_ALL, MAP_DIR } from "../engine/map-build.mjs";
|
|
149
|
+
import { GRAPHVIZ_ENGINES } from "../engine/map-graphviz.mjs";
|
|
150
|
+
import { loadMapWorld } from "../engine/map-places.mjs";
|
|
147
151
|
|
|
148
152
|
/**
|
|
149
153
|
* The packs `unpack` extracts.
|
|
@@ -250,6 +254,7 @@ const argv = yargs(hideBin(process.argv))
|
|
|
250
254
|
.command(contentIndexCommand())
|
|
251
255
|
.command(siteCommand())
|
|
252
256
|
.command(pdfCommand())
|
|
257
|
+
.command(mapCommand())
|
|
253
258
|
.command(reachabilityCommand())
|
|
254
259
|
.command(addressesCommand())
|
|
255
260
|
.version(ownVersion())
|
|
@@ -1663,6 +1668,144 @@ function siteCommand() {
|
|
|
1663
1668
|
};
|
|
1664
1669
|
}
|
|
1665
1670
|
|
|
1671
|
+
/**
|
|
1672
|
+
* `content-build map` — draw what the place notes state.
|
|
1673
|
+
*
|
|
1674
|
+
* Reads the content index — this package's and every declared dependency's —
|
|
1675
|
+
* so a consumer's map draws its dependencies' places beside its own, and
|
|
1676
|
+
* writes under `build/map/`, always the `.dot` beside the `.svg`. Three
|
|
1677
|
+
* drawings: the containment tree from `parents` (`--tree`, the author's
|
|
1678
|
+
* check, whose anomalies are reported as findings), the map from a place
|
|
1679
|
+
* from its `borders` and `routes` (`--from`), and the whole route graph
|
|
1680
|
+
* (`--travel`). GraphViz draws; when it is absent the command says what to
|
|
1681
|
+
* install and stops.
|
|
1682
|
+
*
|
|
1683
|
+
* @returns {object} The yargs command module.
|
|
1684
|
+
*/
|
|
1685
|
+
// eslint-disable-next-line
|
|
1686
|
+
function mapCommand() {
|
|
1687
|
+
return {
|
|
1688
|
+
command: "map",
|
|
1689
|
+
describe: "Draw the containment tree, the map from a place, or the route graph",
|
|
1690
|
+
builder: (yargs) => {
|
|
1691
|
+
yargs
|
|
1692
|
+
.option("tree", {
|
|
1693
|
+
describe:
|
|
1694
|
+
"Draw the containment tree from `parents`: `tree.svg`, and one " +
|
|
1695
|
+
"`tree-<shortcode>.svg` per continent. Reports the anomalies.",
|
|
1696
|
+
type: "boolean",
|
|
1697
|
+
default: false,
|
|
1698
|
+
})
|
|
1699
|
+
.option("root", {
|
|
1700
|
+
describe:
|
|
1701
|
+
"With --tree, draw the subtree beneath this place as " +
|
|
1702
|
+
"`tree-<shortcode>.svg` and report nothing.",
|
|
1703
|
+
type: "string",
|
|
1704
|
+
})
|
|
1705
|
+
.option("from", {
|
|
1706
|
+
describe:
|
|
1707
|
+
"Draw the map from this place as `from-<shortcode>.svg`; repeatable, " +
|
|
1708
|
+
`or \`${FROM_ALL}\` for every place with a border or a route.`,
|
|
1709
|
+
type: "string",
|
|
1710
|
+
array: true,
|
|
1711
|
+
})
|
|
1712
|
+
.option("travel", {
|
|
1713
|
+
describe: "Draw the whole route graph as `travel.svg`.",
|
|
1714
|
+
type: "boolean",
|
|
1715
|
+
default: false,
|
|
1716
|
+
})
|
|
1717
|
+
.option("engine", {
|
|
1718
|
+
describe: "The tree's layout engine. `twopi` is the radial one.",
|
|
1719
|
+
choices: [...GRAPHVIZ_ENGINES],
|
|
1720
|
+
default: "dot",
|
|
1721
|
+
})
|
|
1722
|
+
.option("rankdir", {
|
|
1723
|
+
describe: "The tree's rank direction; `dot` engine only.",
|
|
1724
|
+
choices: ["TB", "LR", "BT", "RL"],
|
|
1725
|
+
default: "TB",
|
|
1726
|
+
})
|
|
1727
|
+
.option("nodesep", {
|
|
1728
|
+
describe: "The tree's GraphViz `nodesep`, in inches.",
|
|
1729
|
+
type: "number",
|
|
1730
|
+
})
|
|
1731
|
+
.option("ranksep", {
|
|
1732
|
+
describe: "The tree's GraphViz `ranksep`, in inches.",
|
|
1733
|
+
type: "number",
|
|
1734
|
+
})
|
|
1735
|
+
.option("scale", {
|
|
1736
|
+
describe: "Multiply every node's size, font and border weight.",
|
|
1737
|
+
type: "number",
|
|
1738
|
+
default: 1,
|
|
1739
|
+
})
|
|
1740
|
+
// Declared positively so yargs derives `--no-polities` from it.
|
|
1741
|
+
.option("polities", {
|
|
1742
|
+
describe:
|
|
1743
|
+
"Draw polities beside their regions in the tree. `--no-polities` " +
|
|
1744
|
+
"drops them.",
|
|
1745
|
+
type: "boolean",
|
|
1746
|
+
default: true,
|
|
1747
|
+
})
|
|
1748
|
+
.option("base", {
|
|
1749
|
+
describe:
|
|
1750
|
+
"The site base this package's pages are served under, ending in " +
|
|
1751
|
+
"a slash; given, every name is a link to its page.",
|
|
1752
|
+
type: "string",
|
|
1753
|
+
})
|
|
1754
|
+
.option("out", {
|
|
1755
|
+
describe: `Directory to write into. Defaults to \`${MAP_DIR}\`.`,
|
|
1756
|
+
type: "string",
|
|
1757
|
+
});
|
|
1758
|
+
},
|
|
1759
|
+
handler: (argv) => {
|
|
1760
|
+
try {
|
|
1761
|
+
const config = loadPackConfig();
|
|
1762
|
+
const outDir = argv.out ?? path.join(config.rootDir, ...MAP_DIR.split("/"));
|
|
1763
|
+
const problems = [];
|
|
1764
|
+
const world = loadMapWorld({ config, base: argv.base, problems });
|
|
1765
|
+
for (const problem of problems) emitDiagnostic(problem);
|
|
1766
|
+
if (problems.length) process.exitCode = 1;
|
|
1767
|
+
for (const s of world.stale) {
|
|
1768
|
+
emitDiagnostic({
|
|
1769
|
+
file: cachedIndexPath(config, s.package),
|
|
1770
|
+
severity: "error",
|
|
1771
|
+
message: `unusable content index: ${s.reason}`,
|
|
1772
|
+
});
|
|
1773
|
+
}
|
|
1774
|
+
if (world.stale.length) {
|
|
1775
|
+
log.error("Re-run `content-build deps fetch`.");
|
|
1776
|
+
process.exitCode = 1;
|
|
1777
|
+
return;
|
|
1778
|
+
}
|
|
1779
|
+
|
|
1780
|
+
const result = buildMaps({
|
|
1781
|
+
world,
|
|
1782
|
+
outDir,
|
|
1783
|
+
tree: argv.tree,
|
|
1784
|
+
root: argv.root,
|
|
1785
|
+
from: argv.from ?? [],
|
|
1786
|
+
travel: argv.travel,
|
|
1787
|
+
engine: argv.engine,
|
|
1788
|
+
rankdir: argv.rankdir,
|
|
1789
|
+
nodesep: argv.nodesep,
|
|
1790
|
+
ranksep: argv.ranksep,
|
|
1791
|
+
scale: argv.scale,
|
|
1792
|
+
polities: argv.polities !== false,
|
|
1793
|
+
});
|
|
1794
|
+
const errors = reportFindings(result.findings, {});
|
|
1795
|
+
const svgs = result.written.filter((f) => f.endsWith(".svg"));
|
|
1796
|
+
log.info(
|
|
1797
|
+
`${world.places.size} place(s) → ${svgs.length} drawing(s) under ` +
|
|
1798
|
+
`${path.relative(process.cwd(), outDir) || "."}`,
|
|
1799
|
+
);
|
|
1800
|
+
if (errors) process.exitCode = 1;
|
|
1801
|
+
} catch (err) {
|
|
1802
|
+
reportFailure(err);
|
|
1803
|
+
process.exitCode = 1;
|
|
1804
|
+
}
|
|
1805
|
+
},
|
|
1806
|
+
};
|
|
1807
|
+
}
|
|
1808
|
+
|
|
1666
1809
|
/**
|
|
1667
1810
|
* `content-build reachability <dir> [file]` — check that a corpus reads through.
|
|
1668
1811
|
*
|
package/bin/package-build.mjs
CHANGED
|
@@ -456,19 +456,21 @@ function manifestCommand() {
|
|
|
456
456
|
}
|
|
457
457
|
|
|
458
458
|
/**
|
|
459
|
-
* `package-build site-root` — the deployment's root files
|
|
459
|
+
* `package-build site-root` — the deployment's root files, and the search
|
|
460
|
+
* index.
|
|
460
461
|
*
|
|
461
462
|
* Hugo owns everything under the `/<package>/` prefix; this owns what sits
|
|
462
463
|
* beside it, which is what Cloudflare Pages reads from the uploaded directory
|
|
463
464
|
* and nowhere else: `_headers`, and no `_redirects`, since the prefix root is
|
|
464
|
-
* the homepage.
|
|
465
|
+
* the homepage. It then indexes the rendered pages for search into
|
|
466
|
+
* `<package>/pagefind/`, unless `site.search` is off.
|
|
465
467
|
*
|
|
466
468
|
* @returns {object} The yargs command.
|
|
467
469
|
*/
|
|
468
470
|
function siteRootCommand() {
|
|
469
471
|
return {
|
|
470
472
|
command: "site-root",
|
|
471
|
-
describe: "Write the deployment's _headers",
|
|
473
|
+
describe: "Write the deployment's _headers and index the site for search",
|
|
472
474
|
builder: (y) =>
|
|
473
475
|
y.option("out", {
|
|
474
476
|
type: "string",
|
|
@@ -479,10 +481,22 @@ function siteRootCommand() {
|
|
|
479
481
|
const shared = loadPackConfig();
|
|
480
482
|
const out = path.resolve(config.rootDir, argv.out ?? DEPLOY_ROOT);
|
|
481
483
|
|
|
482
|
-
const { files } = writeSiteRoot({
|
|
484
|
+
const { files, search } = await writeSiteRoot({
|
|
485
|
+
pkg: shared.contentPackage,
|
|
486
|
+
out,
|
|
487
|
+
search: shared.site.search,
|
|
488
|
+
});
|
|
483
489
|
for (const file of files) {
|
|
484
490
|
console.log(`✅ Wrote ${path.relative(config.rootDir, file)}.`);
|
|
485
491
|
}
|
|
492
|
+
if (search) {
|
|
493
|
+
console.log(
|
|
494
|
+
`✅ Indexed ${search.pages} pages for search into ` +
|
|
495
|
+
`${path.relative(config.rootDir, search.dir)}/.`,
|
|
496
|
+
);
|
|
497
|
+
} else {
|
|
498
|
+
console.log("Search is off (`site.search: false`), so no index was written.");
|
|
499
|
+
}
|
|
486
500
|
}),
|
|
487
501
|
};
|
|
488
502
|
}
|
package/content-config.mjs
CHANGED
|
@@ -771,6 +771,7 @@ const SITE_KEYS = [
|
|
|
771
771
|
"passOptions",
|
|
772
772
|
"notfound",
|
|
773
773
|
"hugo",
|
|
774
|
+
"search",
|
|
774
775
|
];
|
|
775
776
|
const SITE_NOTFOUND_KEYS = ["tagline", "sitenoun", "heroimage", "links"];
|
|
776
777
|
const SITE_NOTFOUND_LINK_KEYS = ["title", "url", "text"];
|
|
@@ -1710,6 +1711,10 @@ const SITE_INDEX_MESSAGE =
|
|
|
1710
1711
|
* shared with the link manifest so the two cannot disagree about where a
|
|
1711
1712
|
* page is.
|
|
1712
1713
|
*
|
|
1714
|
+
* `search` is the one switch here: whether `package-build site-root` writes a
|
|
1715
|
+
* search index beside the rendered pages. On by default — every site carries
|
|
1716
|
+
* one unless it says otherwise.
|
|
1717
|
+
*
|
|
1713
1718
|
* **What the site publishes is the homepage and the content tree, and nothing
|
|
1714
1719
|
* beside them.** A page of documentation is a note — `type: doc`, addressed
|
|
1715
1720
|
* by its shortcode, compiling into no document where it says `pack: none` —
|
|
@@ -1733,6 +1738,7 @@ function normalizeSite(value) {
|
|
|
1733
1738
|
passOptions: Object.freeze({}),
|
|
1734
1739
|
notfound: null,
|
|
1735
1740
|
hugo: Object.freeze({}),
|
|
1741
|
+
search: true,
|
|
1736
1742
|
});
|
|
1737
1743
|
if (value === undefined) return empty;
|
|
1738
1744
|
if (!isPlainObject(value)) fail("site", "must be a mapping");
|
|
@@ -1788,6 +1794,7 @@ function normalizeSite(value) {
|
|
|
1788
1794
|
: Object.freeze({ ...input.passOptions }),
|
|
1789
1795
|
notfound: normalizeSiteNotfound(input.notfound),
|
|
1790
1796
|
hugo: normalizeSiteHugo(input.hugo),
|
|
1797
|
+
search: optionalBoolean(input.search, "site.search", true),
|
|
1791
1798
|
});
|
|
1792
1799
|
}
|
|
1793
1800
|
|
package/docs/api.md
CHANGED
|
@@ -412,6 +412,90 @@ Drawing a declared infobox: HTML for a Foundry Journal Page, Typst for the book.
|
|
|
412
412
|
| `linkToUuid` | `linkToUuid(value)` | `string` | drawing a link value as a Foundry document reference |
|
|
413
413
|
| `sectionHasContent` | `sectionHasContent(section)` | `boolean` | deciding whether a section holds anything worth drawing |
|
|
414
414
|
|
|
415
|
+
### `engine.placeRelations`
|
|
416
|
+
|
|
417
|
+
What a place is next to (`data.borders`) and reachable from (`data.routes`): the closed sets both draw from, stated once for the lint, the specification and a consumer drawing a map, and the checks that hold a note to them and to the note at the other end.
|
|
418
|
+
|
|
419
|
+
| Export | Signature | Returns | Use it when |
|
|
420
|
+
| ----------------- | ------------------------------- | ------------------- | -------------------------------------------------------------------------------------------------------------------- |
|
|
421
|
+
| `BEARINGS` | `const BEARINGS` | `readonly string[]` | the eight compass bearings, clockwise from `N` |
|
|
422
|
+
| `ROUTE_MODES` | `const ROUTE_MODES` | `readonly string[]` | the modes a route is travelled by — `land`, `boat`, `ship` |
|
|
423
|
+
| `TRAVEL_DAYS` | `const TRAVEL_DAYS` | `readonly number[]` | the days markers a route may state, each meaning "about this, under normal conditions" |
|
|
424
|
+
| `TERRAIN_MODES` | `const TERRAIN_MODES` | — | the terrain registry, each terrain naming the modes that cross it |
|
|
425
|
+
| `TERRAINS` | `const TERRAINS` | `readonly string[]` | the terrain names, in registry order |
|
|
426
|
+
| `BORDER_KEYS` | `const BORDER_KEYS` | `readonly string[]` | the keys a `borders` entry carries |
|
|
427
|
+
| `ROUTE_KEYS` | `const ROUTE_KEYS` | `readonly string[]` | the keys a `routes` entry carries |
|
|
428
|
+
| `oppositeBearing` | `oppositeBearing(bearing)` | `string\|undefined` | the bearing the other end of a relation states, or `undefined` for a value that is not a bearing |
|
|
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
|
+
| `checkRoutes` | `checkRoutes(note, { index })` | `object[]` | linting a place's `routes` — as `checkBorders`, plus `mode`, `days` and `terrain`, once per pair per mode |
|
|
431
|
+
|
|
432
|
+
### `engine.mapPlaces`
|
|
433
|
+
|
|
434
|
+
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.
|
|
435
|
+
|
|
436
|
+
| Export | Signature | Returns | Use it when |
|
|
437
|
+
| --------------------- | ---------------------------------------------------------------- | ------------- | ------------------------------------------------------------------------------------------------------------------------ |
|
|
438
|
+
| `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
|
+
| `loadMapWorld` | `loadMapWorld({ config, contentBase, base, problems })` | `MapWorld` | loading the world a map is drawn from — this package's index and every declared dependency's |
|
|
440
|
+
| `analyzeContainment` | `analyzeContainment(places, polities)` | `Containment` | finding the world, the continents, each place's continent and the anomalies of `parents` |
|
|
441
|
+
| `subtreeOf` | `subtreeOf(analysis, root)` | `Set<string>` | every place beneath one, by following `parents` downward |
|
|
442
|
+
| `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 |
|
|
443
|
+
|
|
444
|
+
### `engine.mapLayout`
|
|
445
|
+
|
|
446
|
+
The geometry of the map from a place, as arithmetic over the place records: north up, east right, the angle from the bearing and the radius from the days marker on log-spaced rings, two hops composed into one bearing only where they agree. Nothing here touches a file or GraphViz.
|
|
447
|
+
|
|
448
|
+
| Export | Signature | Returns | Use it when |
|
|
449
|
+
| --------------- | -------------------------------- | ------------------- | ------------------------------------------------------------------------------------------------------------------- |
|
|
450
|
+
| `HORIZON_DAYS` | `const HORIZON_DAYS` | `number` | the horizon of the map from a place, 90 days; beyond it a place is a name at the rim |
|
|
451
|
+
| `RING_DAYS` | `const RING_DAYS` | `readonly number[]` | the days markers that are rings — every marker up to the horizon |
|
|
452
|
+
| `bearingAngle` | `bearingAngle(bearing)` | `number\|undefined` | the angle of a bearing in degrees counter-clockwise from east — `N` is 90, `E` 0 |
|
|
453
|
+
| `bearingVector` | `bearingVector(bearing)` | `{x, y}\|undefined` | the unit vector of a bearing |
|
|
454
|
+
| `bearingSteps` | `bearingSteps(a, b)` | `number\|undefined` | how many steps around the compass separate two bearings, the shorter way |
|
|
455
|
+
| `rimRadius` | `rimRadius()` | `number` | the radius of the rim, one log step beyond the horizon |
|
|
456
|
+
| `ringRadius` | `ringRadius(days)` | `number` | the radius of the ring a days value sits on — the rim for none, or for a value beyond the horizon |
|
|
457
|
+
| `rings` | `rings()` | `object[]` | the rings inside out, each marker with its radius |
|
|
458
|
+
| `daysMarker` | `daysMarker(total)` | `number\|undefined` | the smallest marker of the scale that covers a total of days |
|
|
459
|
+
| `edgeLength` | `edgeLength(days)` | `number` | the length a route asks of `neato` in the route graph, inches, on the rings' log scale |
|
|
460
|
+
| `composeHops` | `composeHops(first, second)` | `object\|undefined` | two hops composed into one bearing and one total where they agree — the same bearing or adjacent — else `undefined` |
|
|
461
|
+
| `relationsOf` | `relationsOf(places, shortcode)` | `object` | every border and route touching one place, read from both ends, plus the targets that resolved to nothing |
|
|
462
|
+
| `layoutFrom` | `layoutFrom(places, centre)` | `object` | laying out the map from a place: every node pinned in points, every edge, the rings and the rim |
|
|
463
|
+
| `travelGraph` | `travelGraph(places)` | `object` | the whole route graph — one edge per pair, however many ends and modes state it, each with a length from its days |
|
|
464
|
+
|
|
465
|
+
### `engine.mapDot`
|
|
466
|
+
|
|
467
|
+
The three drawings of `content-build map` as DOT text, sharing one style table: shape, colour and size by `subType`, an anomaly overriding colour alone, every label a `name.full`, every tooltip an address, and a `URL` where the place carries one so the SVG's names are links.
|
|
468
|
+
|
|
469
|
+
| Export | Signature | Returns | Use it when |
|
|
470
|
+
| ----------- | -------------------------------------------------- | -------- | --------------------------------------------------------------------------------------------------- |
|
|
471
|
+
| `STYLE` | `const STYLE` | — | the shape, colour and size of each `subType`, and of a polity |
|
|
472
|
+
| `dotString` | `dotString(value)` | `string` | quoting text for DOT |
|
|
473
|
+
| `treeDot` | `treeDot({ places, polities, analysis, root, … })` | `string` | the containment tree — the whole world clustered by continent, or the subtree beneath `root` |
|
|
474
|
+
| `fromDot` | `fromDot(layout, places, { scale, title })` | `string` | the map from a place, every node pinned for `neato -n2`, the rings drawn faintly with their markers |
|
|
475
|
+
| `travelDot` | `travelDot(graph, places, { scale, title })` | `string` | the whole route graph for `neato`, each edge asking for a length from its days |
|
|
476
|
+
|
|
477
|
+
### `engine.mapGraphviz`
|
|
478
|
+
|
|
479
|
+
GraphViz, as the map command reaches it: a build-time tool of that command alone, found once, named plainly when absent, and run over a `.dot` file the command keeps beside the rendering.
|
|
480
|
+
|
|
481
|
+
| Export | Signature | Returns | Use it when |
|
|
482
|
+
| ------------------------ | ------------------------------------------------------- | ------------------- | ------------------------------------------------------------------------------------------ |
|
|
483
|
+
| `GRAPHVIZ_ENGINES` | `const GRAPHVIZ_ENGINES` | `readonly string[]` | the engines the map command may ask for — `dot`, `twopi`, `neato` |
|
|
484
|
+
| `findGraphviz` | `findGraphviz(engine, { env, fallbacks })` | `string\|undefined` | finding an engine's binary on `PATH` or in the directories a package manager installs into |
|
|
485
|
+
| `graphvizMissingMessage` | `graphvizMissingMessage(engine)` | `string` | saying an engine is not installed, and what installs it |
|
|
486
|
+
| `renderDot` | `renderDot(dotPath, outPath, { binary, format, args })` | `{warnings}` | rendering a `.dot` file, the engine's warnings returned and a non-zero exit thrown |
|
|
487
|
+
|
|
488
|
+
### `engine.mapBuild`
|
|
489
|
+
|
|
490
|
+
`content-build map` as a function: the drawings written under an output directory, always the `.dot` beside the `.svg`, the tree's anomalies returned as findings, and a missing GraphViz either an error or — for a build that would rather skip — one warning.
|
|
491
|
+
|
|
492
|
+
| Export | Signature | Returns | Use it when |
|
|
493
|
+
| --------------- | ------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------ | ---------------------------------------------------------------- |
|
|
494
|
+
| `MAP_DIR` | `const MAP_DIR` | `string` | where the maps land below a package's root, `build/map` |
|
|
495
|
+
| `FROM_ALL` | `const FROM_ALL` | `string` | the `--from` value that means every place with a relation, `all` |
|
|
496
|
+
| `relatedPlaces` | `relatedPlaces(places)` | `string[]` | every place that states, or is named in, a border or a route |
|
|
497
|
+
| `buildMaps` | `buildMaps({ world, outDir, tree, root, from, travel, engine, rankdir, nodesep, ranksep, scale, polities, locate, requireGraphviz })` | `{written, findings, skipped}` | drawing any of the three from a loaded world |
|
|
498
|
+
|
|
415
499
|
### `engine.systems`
|
|
416
500
|
|
|
417
501
|
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.
|