@heroiclands/package-build 22.3.1 → 22.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -758,36 +758,54 @@ Any other key under `docs.itemFields` is refused:
758
758
 
759
759
  **Type:** object · **Optional** · every key defaults to nothing published:
760
760
 
761
- | Key | Type | Default |
762
- | ----------------------- | -------- | ------------------------------------------- |
763
- | `site.base` | string | `""` |
764
- | `site.assets` | string | `""`, but required for `content-build site` |
765
- | `site.description` | string | `""`, but required for `content-build site` |
766
- | `site.packages` | string[] | `[]` |
767
- | `site.sections` | object | `{}` |
768
- | `site.landing` | object | `null` |
769
- | `site.pass` | string | `""` |
770
- | `site.passOptions` | object | `{}` |
771
- | `site.backfillSections` | boolean | `false` |
772
- | `site.list` | object | `{shortcodes: false}` |
773
- | `site.notfound` | object | `null` |
774
- | `site.hugo` | object | `{}` |
761
+ | Key | Type | Default |
762
+ | ------------------ | -------- | ------------------------------------------- |
763
+ | `site.base` | string | `""` |
764
+ | `site.assets` | string | `""`, but required for `content-build site` |
765
+ | `site.description` | string | `""`, but required for `content-build site` |
766
+ | `site.packages` | string[] | `[]` |
767
+ | `site.pass` | string | `""` |
768
+ | `site.passOptions` | object | `{}` |
769
+ | `site.notfound` | object | `null` |
770
+ | `site.hugo` | object | `{}` |
775
771
 
776
772
  How much of a package reaches the web at all is **not** here — it is
777
- [`publish.site`](#publish). `site` is framing: what a section is called,
778
- which named pass bundle supplies the repository's own body rewrites, and the
779
- residue of the [generated Hugo configuration](#the-generated-hugo-configuration)
780
- that is genuinely this repository's own.
773
+ [`publish.site`](#publish). `site` is framing: which named pass bundle
774
+ supplies the repository's own body rewrites, and the residue of the
775
+ [generated Hugo configuration](#the-generated-hugo-configuration) that is
776
+ genuinely this repository's own.
781
777
 
782
- What the site publishes is the content tree, and nothing beside it. A page of
783
- documentation is a note `type: doc`, addressed by its shortcode, and
784
- `pack: none` where it compiles into no Foundry document — so there is no
785
- second mechanism for mounting a directory of markdown, and a configuration
786
- that names one is refused with a message saying where the page goes instead:
778
+ **A site is its homepage and its pages.** The `type: homepage` note is the
779
+ package's front page at `/<contentPackage>/`, and every other note is one page
780
+ at `/<contentPackage>/<type>-<shortcode>/`. Nothing is generated between
781
+ them: no section directory, no listing of a type, no tag page. Every
782
+ structure above the pages which notes belong together, in what order,
783
+ under which headings — is authored, as a `doc` note carrying a content table
784
+ over the content index, and linked from the homepage like any other page.
787
785
 
788
- > ``package-build config: `site.trees` is retired — a page is a note in the content tree. Give each page `type: doc`, a `shortcode` and `pack: none`, file it under `assets/content/`, and declare the section that lists it under `site.sections`.``
786
+ So a page of documentation is a note `type: doc`, addressed by its
787
+ shortcode, and `pack: none` where it compiles into no Foundry document — and
788
+ there is no second mechanism for mounting a directory of markdown. A
789
+ configuration that names one is refused with a message saying where the page
790
+ goes instead:
789
791
 
790
- > ``package-build config: `site.readmeSections` is retired — a page is a note in the content tree. Give each page `type: doc`, a `shortcode` and `pack: none`, file it under `assets/content/`, and declare the section that lists it under `site.sections`.``
792
+ > ``package-build config: `site.trees` is retired — a page is a note in the content tree. Give each page `type: doc`, a `shortcode` and `pack: none`, file it under `assets/content/`, and link it from the homepage or from a `doc` note that indexes it.``
793
+
794
+ > ``package-build config: `site.readmeSections` is retired — a page is a note in the content tree. Give each page `type: doc`, a `shortcode` and `pack: none`, file it under `assets/content/`, and link it from the homepage or from a `doc` note that indexes it.``
795
+
796
+ A configuration that asks the build to generate an index between the
797
+ homepage and the pages is refused the same way, by name and with one message
798
+ — `site.sections` (and the `listType` / `listSubType` an entry carried),
799
+ `site.landing`, `site.backfillSections` and `site.list` alike, since how a
800
+ listing renders is a content table's to say:
801
+
802
+ > ``package-build config: `site.sections` is retired — a site is its homepage and its pages, and any index between them is a `doc` note: write one with `type: doc`, a `shortcode` and `pack: none`, carrying a content table over the notes it lists, and link it from the homepage. Nothing is generated between the homepage and the pages, so delete the key.``
803
+
804
+ > ``package-build config: `site.landing` is retired — a site is its homepage and its pages, and any index between them is a `doc` note: write one with `type: doc`, a `shortcode` and `pack: none`, carrying a content table over the notes it lists, and link it from the homepage. Nothing is generated between the homepage and the pages, so delete the key.``
805
+
806
+ > ``package-build config: `site.backfillSections` is retired — a site is its homepage and its pages, and any index between them is a `doc` note: write one with `type: doc`, a `shortcode` and `pack: none`, carrying a content table over the notes it lists, and link it from the homepage. Nothing is generated between the homepage and the pages, so delete the key.``
807
+
808
+ > ``package-build config: `site.list` is retired — a site is its homepage and its pages, and any index between them is a `doc` note: write one with `type: doc`, a `shortcode` and `pack: none`, carrying a content table over the notes it lists, and link it from the homepage. Nothing is generated between the homepage and the pages, so delete the key.``
791
809
 
792
810
  Where the Hugo tree is written is not a choice. `content-build site` writes
793
811
  the whole Hugo source tree under `build/hugo/` — the generated `hugo.toml`,
@@ -799,7 +817,7 @@ published. A `site.out` is refused by name:
799
817
 
800
818
  > ``package-build config: `site` must be a mapping.``
801
819
 
802
- > ``package-build config: `site.<key>` is not a recognized option (expected one of: base, assets, description, packages, sections, landing, pass, passOptions, backfillSections, list, notfound, hugo).``
820
+ > ``package-build config: `site.<key>` is not a recognized option (expected one of: base, assets, description, packages, pass, passOptions, notfound, hugo).``
803
821
 
804
822
  `site.assets` is the host every package's imagery is served from, and it is
805
823
  the one address in this file that is not this repository's own. A note names
@@ -845,62 +863,8 @@ names a registry):
845
863
 
846
864
  > ``package-build config: `site.packages[<index>]` must be a non-empty string.``
847
865
 
848
- `site.sections` is a closed vocabularya section's _only_ place to speak,
849
- since it exists solely as the generated `_index.md` this build writes for it:
850
-
851
- | Key (under `site.sections.<name>`) | Type | Required | Default |
852
- | ---------------------------------- | -------------------------- | -------- | ------- |
853
- | `site.sections.<name>.title` | string | yes | — |
854
- | `site.sections.<name>.banner` | string | no | none |
855
- | `site.sections.<name>.description` | string | no | none |
856
- | `site.sections.<name>.listType` | string, an address segment | no | none |
857
- | `site.sections.<name>.listSubType` | string, an address segment | no | none |
858
-
859
- > ``package-build config: `site.sections.<name>` must be a mapping.``
860
-
861
- > ``package-build config: `site.sections.<name>.title` must be a non-empty string.``
862
-
863
- `listType` / `listSubType` say what the section **lists** — a content type
864
- and subType, which are address segments and therefore checked against
865
- `^[a-z0-9]+$`, deliberately distinct from the section's own name (a URL
866
- this site chose; `user-guide` is the section, `userguide` the subType):
867
-
868
- > ``package-build config: `site.sections.<name>.listType` is `Not Ok`, which is not lowercase alphanumeric. It names a content type or subType, and those are address segments (^[a-z0-9]+$) — not the section's own name, which is a URL this site chose and need not match (`user-guide` is the section, `userguide` the subType). A value no page carries selects nothing and leaves the landing empty.``
869
-
870
- `listSubType` alone, without `listType`, names no query — a subType only
871
- distinguishes _within_ a type:
872
-
873
- > ``package-build config: `site.sections.<name>.listSubType` is declared without a `listType`. A subType tells pages apart only within a type — `rules`, `userguide` and `reference` are all `doc` — so on its own it names no query for a layout to run.``
874
-
875
- Any other key on a section entry is refused:
876
-
877
- > ``package-build config: `site.sections.<name>.<key>` is not a recognized option (expected one of: title, banner, description, listType, listSubType).``
878
-
879
- `site.landing` is different from a section entry — it is the mount's own
880
- landing page frontmatter, passed through verbatim to Hugo rather than
881
- validated field by field, since its vocabulary is the theme's and not this
882
- package's:
883
-
884
- > ``package-build config: `site.landing` must be a mapping.``
885
-
886
- `site.passOptions` and `site.backfillSections` have no further shape of
887
- their own — `passOptions` is passed to the resolved `site.pass` bundle
888
- unchanged, and `backfillSections` is a plain boolean:
889
-
890
- > ``package-build config: `site.backfillSections` must be a boolean.``
891
-
892
- `site.list` is how a listing page renders, written into the generated Hugo
893
- configuration as `params.list`:
894
-
895
- | Key | Type | Required | Default |
896
- | ---------------------- | ------- | -------- | ------- |
897
- | `site.list.shortcodes` | boolean | no | `false` |
898
-
899
- > ``package-build config: `site.list` must be a mapping.``
900
-
901
- > ``package-build config: `site.list.shortcodes` must be a boolean.``
902
-
903
- > ``package-build config: `site.list.<key>` is not a recognized option (expected one of: shortcodes).``
866
+ `site.passOptions` has no further shape of its own it is passed to the
867
+ resolved `site.pass` bundle unchanged.
904
868
 
905
869
  `site.notfound` is the wording of the "page not found" page, written into
906
870
  the generated Hugo configuration as `params.notfound`. The theme renders
@@ -972,26 +936,25 @@ the block cannot grow into a second configuration file:
972
936
  `content-build site` writes `build/hugo/hugo.toml` on every run. Every value
973
937
  in it has one source, and that source is where it is edited:
974
938
 
975
- | Key | Derived from |
976
- | --------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------- |
977
- | `baseURL` | `package.json` `homepage`, checked by `checkHomepage` — an absolute URL ending `/<contentPackage>/` |
978
- | `title` | `packageBuild.manifest.title`, which is required |
979
- | `locale` | the organisation's locale, `en-us`, in `engine/site-config.mjs` |
980
- | `publishDir` | `contentPackage`, under the deployment root `build/site` — written relative to `build/hugo/`, so `../site/<contentPackage>` |
981
- | `contentDir` | the fixed content mount, `build/hugo/content` — written as `content` |
982
- | `themesDir` | where `@heroiclands/hugo-theme` is installed, resolved the way Node resolves a package and written relative to `build/hugo/` |
983
- | `theme` | the installed `@heroiclands/hugo-theme`, so `hugo-theme` |
984
- | `disableKinds` | whether any note in the tree carries `tags:`, which the site walk discovers: `["taxonomy", "term", "RSS"]` when none does, `["RSS"]` when at least one does |
985
- | `taxonomies` | the same fact — written as `{ tag = "tags" }` when at least one note carries `tags:`, absent otherwise |
986
- | `outputs` | the same fact — written as `{ taxonomy = ["HTML"], term = ["HTML"] }` when at least one note carries `tags:`, absent otherwise |
987
- | `params.description` | `site.description`, which is required |
988
- | `params.author` | `package.json` `author`, its `name`; absent when the package declares none |
989
- | `params.cdnBaseURL` | `site.assets`, which is required |
990
- | `params.brand` | the organisation's brand links — `logo`, `licenseURL`, `discordURL` — in `engine/site-config.mjs` |
991
- | `params.list` | `site.list` |
992
- | `params.notfound` | `site.notfound`; absent when undeclared |
993
- | `markup.goldmark.renderer.unsafe` | the toolchain, whose pages carry raw HTML a `<figure>` for every image, a `<span>` marking an unresolved link |
994
- | `menu` | the navigation `content-build deps fetch` caches from `https://www.heroiclands.org/nav.json`, entry for entry, a dropdown's entries as `parent` entries |
939
+ | Key | Derived from |
940
+ | --------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------- |
941
+ | `baseURL` | `package.json` `homepage`, checked by `checkHomepage` — an absolute URL ending `/<contentPackage>/` |
942
+ | `title` | `packageBuild.manifest.title`, which is required |
943
+ | `locale` | the organisation's locale, `en-us`, in `engine/site-config.mjs` |
944
+ | `publishDir` | `contentPackage`, under the deployment root `build/site` — written relative to `build/hugo/`, so `../site/<contentPackage>` |
945
+ | `contentDir` | the fixed content mount, `build/hugo/content` — written as `content` |
946
+ | `themesDir` | where `@heroiclands/hugo-theme` is installed, resolved the way Node resolves a package and written relative to `build/hugo/` |
947
+ | `theme` | the installed `@heroiclands/hugo-theme`, so `hugo-theme` |
948
+ | `disableKinds` | the toolchain, which renders a site as its homepage and its pages: `["section", "taxonomy", "term", "RSS"]` on every site, whatever its notes carry |
949
+ | `taxonomies` | the toolchainnever written, because `taxonomy` and `term` are disabled kinds |
950
+ | `outputs` | the toolchainnever written, because every listing kind is disabled |
951
+ | `params.description` | `site.description`, which is required |
952
+ | `params.author` | `package.json` `author`, its `name`; absent when the package declares none |
953
+ | `params.cdnBaseURL` | `site.assets`, which is required |
954
+ | `params.brand` | the organisation's brand links — `logo`, `licenseURL`, `discordURL` — in `engine/site-config.mjs` |
955
+ | `params.notfound` | `site.notfound`; absent when undeclared |
956
+ | `markup.goldmark.renderer.unsafe` | the toolchain, whose pages carry raw HTML — a `<figure>` for every image, a `<span>` marking an unresolved link |
957
+ | `menu` | the navigation `content-build deps fetch` caches from `https://www.heroiclands.org/nav.json`, entry for entry, a dropdown's entries as `parent` entries |
995
958
 
996
959
  The site build reads the navigation from the cache only. A cold cache is an
997
960
  error naming the command that fills it:
@@ -1018,13 +981,13 @@ build the same way:
1018
981
 
1019
982
  > ``package-build config: `site.assets` is not declared, and a site build needs one — it is the host every package's imagery is served from, and the theme resolves every relative asset against it.``
1020
983
 
1021
- Nothing else is emitted. A site whose notes carry no `tags:` publishes no
1022
- taxonomy pages `[taxonomies]` and `[outputs]` go unwritten, and Hugo's
1023
- defaults never apply because `taxonomy` and `term` are disabled kinds. A site
1024
- with at least one tagged note publishes `/tags/` and a page per tag: Hugo's
1025
- own default taxonomy pair also declares `category`, so `[taxonomies]` names
1026
- only `tag`, and `[outputs]` restricts both to `HTML` so neither produces a
1027
- feed. Every other key is `site.hugo`'s to add.
984
+ Nothing else is emitted. `home` and `page` are the only kinds a site
985
+ renders: the homepage is the mount's `_index.md`, every note is a page, and
986
+ `section`, `taxonomy`, `term` and `RSS` are disabled on every site — so no
987
+ `[taxonomies]` or `[outputs]` is written, Hugo's default taxonomy pair never
988
+ applies, and a note's `tags:` reach the page's front matter and nothing
989
+ else. A tag is a field a content table filters on, not a page of its own.
990
+ Every other key is `site.hugo`'s to add.
1028
991
 
1029
992
  ### `pdf`
1030
993
 
@@ -1332,9 +1295,8 @@ its content tree's addresses mount inside the package.
1332
1295
  Every HeroicLands package publishes at least an authored homepage at
1333
1296
  `https://www.heroiclands.org/<contentPackage>/` — there is no value meaning
1334
1297
  _no web presence at all_. `homepage` is the floor: the authored homepage
1335
- and nothing else, no content-tree walk, no `site.sections` / `site.landing`
1336
- output. `content` is the homepage plus every page the content tree
1337
- publishes. `publishesContentPages(config)`, exported from
1298
+ and nothing else, no content-tree walk. `content` is the homepage plus every
1299
+ page the content tree publishes. `publishesContentPages(config)`, exported from
1338
1300
  `content-config.mjs` alongside [`compilesFoundryDocuments`](#packagekind),
1339
1301
  answers the one question every reader of the mode actually asks — the site
1340
1302
  build, to decide whether to walk the tree at all, and the content index, to
@@ -1706,17 +1668,19 @@ the source directory:
1706
1668
 
1707
1669
  ## Every retired or forbidden key, in one place
1708
1670
 
1709
- | Key | Why |
1710
- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ |
1711
- | `publish.address.landing` | Retired — named a whole-section landing, and there are no sections to address. |
1712
- | `packs[].folders` | Retired — a folder is a note (`type: folder`), materialised by the pack whose documents reference it. |
1713
- | `rootDir` | Forbidden in a YAML configuration — always the file's own directory. |
1714
- | `foundryPackage` | Forbidden in a YAML configuration — always the adjacent `package.json` `name`. |
1715
- | `homepage`, `author` | Forbidden in a YAML configuration — always the adjacent `package.json`'s own `homepage` and `author`. |
1716
- | `stats.systemId` | Forbidden in every configuration — derived from `packageKind`, `requiresSystem` or a lone declared system. |
1717
- | `stats.systemVersion` | Forbidden in every configuration — derived from `package.json` (a system) or `systems:` / `relationships.systems` (a module). |
1718
- | `packageBuild.manifest.id`, `.version`, `.description`, `.url`, `.bugs`, `.manifest`, `.download`, `.compatibility`, `.relationships`, `.packs` | Forbidden — each is derived from `package.json` or the top level of `package-build.config.yaml`; see [`packageBuild.manifest`](#packagebuildmanifest). |
1719
- | `site.out` | Retired — the site build writes its content mount at `build/hugo/content`, beside the generated `hugo.toml`. |
1720
- | `site.hugo.baseURL`, `.title`, `.locale`, `.publishDir`, `.contentDir`, `.themesDir`, `.theme`, `.disableKinds`, `.taxonomies`, `.outputs`, `.params.description`, `.params.author`, `.params.cdnBaseURL`, `.params.brand`, `.params.list`, `.params.notfound`, `.markup.goldmark.renderer.unsafe`, `.menu` | Forbiddeneach is written by the site build from a source it names; see [the generated Hugo configuration](#the-generated-hugo-configuration). |
1721
- | `publish.site: true` / `publish.site: false` | Refused rather than mapped write `homepage` or `content`. |
1722
- | `packs`, `itemBuilders`, `docs`, `compatibility`, `relationships`, `systems`, `requiresSystem`, `stats`, `foundryPackage` | Forbidden in a `documentation` package — each describes a Foundry package this kind is not; see the key's own section for its located refusal message. |
1671
+ | Key | Why |
1672
+ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ |
1673
+ | `publish.address.landing` | Retired — named a whole-section landing, and there are no sections to address. |
1674
+ | `packs[].folders` | Retired — a folder is a note (`type: folder`), materialised by the pack whose documents reference it. |
1675
+ | `rootDir` | Forbidden in a YAML configuration — always the file's own directory. |
1676
+ | `foundryPackage` | Forbidden in a YAML configuration — always the adjacent `package.json` `name`. |
1677
+ | `homepage`, `author` | Forbidden in a YAML configuration — always the adjacent `package.json`'s own `homepage` and `author`. |
1678
+ | `stats.systemId` | Forbidden in every configuration — derived from `packageKind`, `requiresSystem` or a lone declared system. |
1679
+ | `stats.systemVersion` | Forbidden in every configuration — derived from `package.json` (a system) or `systems:` / `relationships.systems` (a module). |
1680
+ | `packageBuild.manifest.id`, `.version`, `.description`, `.url`, `.bugs`, `.manifest`, `.download`, `.compatibility`, `.relationships`, `.packs` | Forbidden — each is derived from `package.json` or the top level of `package-build.config.yaml`; see [`packageBuild.manifest`](#packagebuildmanifest). |
1681
+ | `site.out` | Retired — the site build writes its content mount at `build/hugo/content`, beside the generated `hugo.toml`. |
1682
+ | `site.trees`, `site.readmeSections` | Retireda page is a note in the content tree. |
1683
+ | `site.sections`, `site.landing`, `site.backfillSections`, `site.list` | Retired a site is its homepage and its pages, and any index between them is a `doc` note. |
1684
+ | `site.hugo.baseURL`, `.title`, `.locale`, `.publishDir`, `.contentDir`, `.themesDir`, `.theme`, `.disableKinds`, `.taxonomies`, `.outputs`, `.params.description`, `.params.author`, `.params.cdnBaseURL`, `.params.brand`, `.params.notfound`, `.markup.goldmark.renderer.unsafe`, `.menu` | Forbidden — each is written by the site build from a source it names; see [the generated Hugo configuration](#the-generated-hugo-configuration). |
1685
+ | `publish.site: true` / `publish.site: false` | Refused rather than mapped — write `homepage` or `content`. |
1686
+ | `packs`, `itemBuilders`, `docs`, `compatibility`, `relationships`, `systems`, `requiresSystem`, `stats`, `foundryPackage` | Forbidden in a `documentation` package — each describes a Foundry package this kind is not; see the key's own section for its located refusal message. |
@@ -2249,11 +2249,26 @@ A SoHL "being" document will be created, as will an "HM3" document.
2249
2249
 
2250
2250
  ### type: homepage
2251
2251
 
2252
- A homepage for the module will be created.
2253
-
2254
- The contents will be available in a page at `https://www.heroiclands.org/<package>/`.
2255
-
2256
- It will also generate a single JournalEntry located at the top level of the "journals" compendium named "\_Introduction".
2252
+ The package's front page, published at `https://www.heroiclands.org/<package>/`.
2253
+ Exactly one note in a content tree declares this type, and it compiles into
2254
+ no Foundry document: it is a page and nothing else.
2255
+
2256
+ It declares a `shortcode` conventionally `root` because that is what a
2257
+ link is written with: `[[homepage-root|Text]]` is an ordinary wikilink, and it
2258
+ resolves to `/<package>/`. The shortcode names the page in links; its address
2259
+ is the package root. `title` defaults to `packageBuild.manifest.title`; a
2260
+ `description` and a `banner` are read by the theme's hero; any other
2261
+ top-level key is passed through to the page as a theme parameter.
2262
+
2263
+ The page is its body, published verbatim: no wikilink is resolved on it and
2264
+ no table expanded, so its links are markdown links, package-relative
2265
+ (`kb/rules/`) or external. An index of what the package publishes is not
2266
+ written here and is not generated anywhere — it is a `doc` note carrying a
2267
+ content table, linked from this page like any other.
2268
+
2269
+ Two top-level keys are refused, because neither decides anything on a page:
2270
+ `id`, the Foundry document id a compendium UUID is built from, and `landing`,
2271
+ a card block — the homepage is a page with a body, rendered as one.
2257
2272
 
2258
2273
  ### type: vehicle
2259
2274
 
@@ -3137,21 +3152,16 @@ any other, and the audience is the section it sits in.
3137
3152
  Write `type: doc`, `subType: reference`, `shortcode: <type>` — so the
3138
3153
  affiliations introduction is `doc-affiliation`, addressed and linked like
3139
3154
  anything else, and typically carrying a generated table of what it introduces.
3140
- It has no build path of its own; the package's own front page already works this
3141
- way (`homepage-root`).
3155
+ It has no build path of its own, and it is the only kind of index there is: a
3156
+ site is its homepage and its pages, and nothing is generated between them.
3142
3157
 
3143
- There is no landing page and no section. A `README.md` used to _be_ its
3144
- section's landing, and a `subType: collection` note with a top-level `section:`
3145
- key was a second way to say the same thing. All of it is retired, because a
3146
- section appears in **no address**: a
3158
+ There is no landing page and no section. A section appears in **no address**: a
3147
3159
  page publishes at `/<package>/<type>-<shortcode>/`, which names no directory. A
3148
3160
  section is what Hugo calls a content directory, and the note format does not
3149
3161
  carry one.
3150
3162
 
3151
- So a `doc`'s `subType` is a **genre** and nothing else, closed to the three
3152
- values above. It briefly had to accept a content type as well, because a
3153
- landing's `subType` named the section it addressed; with no landings, one field
3154
- has one reading again.
3163
+ So a `doc`'s `subType` is a **genre** and nothing else, closed to the values
3164
+ above.
3155
3165
 
3156
3166
  ### type: macro
3157
3167
 
@@ -780,10 +780,11 @@ values, and the content mount — for `hugo --source build/hugo` to render
780
780
  into `build/site/<contentPackage>/`. There is no Hugo configuration to
781
781
  write: the file is generated on every run, and what is genuinely the
782
782
  package's own — the wording of its "page not found" page — goes in the
783
- `site:` block as `site.notfound`. Set `publish.site: content` and name the
784
- sections under `site.sections`, and the same command publishes the content
785
- tree's every page beside the homepage. [`project-setup.md`](project-setup.md)
786
- gives the npm scripts.
783
+ `site:` block as `site.notfound`. Set `publish.site: content`, and the same
784
+ command publishes the content tree's every page beside the homepage — one
785
+ page per note, and nothing generated between them; an index of what the
786
+ package publishes is a `doc` note carrying a content table.
787
+ [`project-setup.md`](project-setup.md) gives the npm scripts.
787
788
 
788
789
  **Another package's content.** Declare a dependency under `relationships`, and
789
790
  `content-build deps fetch` caches that release's published content index so
@@ -254,7 +254,7 @@ package carries this group:
254
254
  `build:site-content` writes the whole Hugo source tree under `build/hugo/` —
255
255
  the generated `hugo.toml` and the content mount — with `content-build site`;
256
256
  `build:site-html` runs Hugo over it, rendering into `build/site/<contentPackage>/`;
257
- `build:site-root` writes the deployment's `_headers` and `_redirects` beside
257
+ `build:site-root` writes the deployment's `_headers` beside
258
258
  that; and `serve:site` does the first and then `hugo server` for a live
259
259
  preview. The repository carries no Hugo configuration of its own: `hugo.toml`
260
260
  is generated on every run from `package.json`, `package-build.config.yaml`,
@@ -621,21 +621,18 @@ export function buildLinkIndex(
621
621
  const SITE_HOST = /^(?:[a-z0-9-]+\.)*heroiclands\.org$/i;
622
622
 
623
623
  /**
624
- * Every package landing this build can name, as `package` → base.
624
+ * Every package front page this build can name, as `package` → base.
625
625
  *
626
- * **A landing needs no manifest, and that is what makes it work.** The link
627
- * manifest indexes content notes, and a homepage is deliberately not one — it
628
- * compiles to no document and is entered in no manifest. The reading that
629
- * follows from this, and that left a hardcoded URL as the only authored form,
630
- * is that a landing therefore cannot be addressed. It does not follow: a
631
- * landing's address is not a *note's* address but the **package's**, and
632
- * {@link PACKAGE_BASE} already records where each package is served. That is a
633
- * frozen constant compiled into every build, so consulting it walks no
634
- * tree, reads no manifest and builds no index — which is precisely why the
635
- * mechanism survives `homepage` mode, where the licensing fence means none of
636
- * those exist.
626
+ * **A front page needs no manifest, and that is what makes it work.** A
627
+ * homepage compiles to no document and is entered in no manifest, but its
628
+ * address is not a *note's* address but the **package's** it is the mount's
629
+ * `_index.md`, published at `/<package>/` and {@link PACKAGE_BASE} already
630
+ * records where each package is served. That is a frozen constant compiled
631
+ * into every build, so consulting it walks no tree, reads no manifest and
632
+ * builds no index which is precisely why the mechanism survives `homepage`
633
+ * mode, where the licensing fence means none of those exist.
637
634
  *
638
- * The roster is consulted **for landings only**. Widening the package set the
635
+ * The roster is consulted **for front pages only**. Widening the package set the
639
636
  * other rules read would make them offer manifest-based advice about packages
640
637
  * no index has been fetched for.
641
638
  *
@@ -738,41 +735,30 @@ function readAddress(url, packages) {
738
735
  * not and cannot: it is published *verbatim* by every publishing mode, including
739
736
  * the homepage-only mode two fan-licensed packages ship under, where the content
740
737
  * tree is never walked and there is no index for a wikilink to resolve against.
741
- * So a landing addresses the web the way the web does — markdown links and
742
- * `url:` fields — and nothing was looking at those. SoHL's landing pointed at
743
- * `kb/creature/` and `kb/character/` from the day those types merged into
744
- * `being`: two 404s on the package's front page, through every build.
738
+ * So a homepage addresses the web the way the web does — markdown links in its
739
+ * body — and this is what looks at those. A dead link on the page a reader
740
+ * arrives at is the one nothing else would report.
745
741
  *
746
742
  * **What is checkable, stated plainly.** Only an address into this site is, and
747
743
  * only against facts this build already holds:
748
744
  *
749
745
  * - A **retired content type** in the path. The engine knows the retired names
750
- * and what replaced it, so this is a fact rather than a guess — and it is
751
- * exactly the SoHL defect.
746
+ * and what replaced it, so this is a fact rather than a guess.
752
747
  * - A **hardcoded absolute URL** into this package's own prefix, or into one a
753
- * a fetched index names. Every one of them has a better form to write, which
748
+ * fetched index names. Every one of them has a better form to write, which
754
749
  * is why every one is reported — including a bare `/<package>/`, which names
755
- * another package's landing.
756
- *
757
- * That last case was exempt until the better form was identified, on the
758
- * reasoning that a landing is in no link manifest so nothing could resolve it.
759
- * True, and beside the point: it does not need resolving. A landing's address
760
- * *is* its package prefix, so `/<package>/` is the absolute URL with the host
761
- * struck off — host-free, emitted verbatim, and needing no index, which is
762
- * what lets it hold in homepage-only mode where the tree is never walked. The
763
- * form was already accepted here; nothing had ever named it as the one to use.
764
- * - A **root-relative `url:`**, which the theme's `relURL` prefixes a second
765
- * time. `href:` means "already resolved, use verbatim", so the same leading
766
- * slash is correct there and is not reported.
750
+ * another package's front page. A front page's address *is* its package
751
+ * prefix, so `/<package>/` is the absolute URL with the host struck off —
752
+ * host-free, emitted verbatim, and needing no index, which is what lets it
753
+ * hold in homepage-only mode where the tree is never walked.
767
754
  * - A **wikilink**, which nothing on this page will ever resolve.
768
755
  *
769
756
  * **What is not checkable, and is not attempted.** Whether an external URL
770
757
  * answers — there is no network at build time, and a build must not fail because
771
758
  * a third party is down. And whether a live in-site address names a page that
772
- * exists: several of the surfaces a landing routes to are produced by other
773
- * tools entirely (generated API documentation, hand-authored Hugo sections), so
774
- * this build does not hold the set of published pages and would report a working
775
- * link as dead.
759
+ * exists: several of the surfaces a homepage routes to are produced by other
760
+ * tools entirely (generated API documentation, say), so this build does not
761
+ * hold the set of published pages and would report a working link as dead.
776
762
  *
777
763
  * @param {ReturnType<typeof buildLinkIndex>} index - The built index.
778
764
  * @returns {Array<{note: object, field: string, url: string, text: string,
@@ -814,7 +800,7 @@ export function auditHomepageLinks(index) {
814
800
  );
815
801
  }
816
802
 
817
- for (const { field, url, kind } of homepageAddresses(note.fm, note.body)) {
803
+ for (const { field, url } of homepageAddresses(note.body)) {
818
804
  // Counted for every address, checked or not, so the count is
819
805
  // the literal's nth appearance in the file rather than the nth
820
806
  // *finding* about it — two rules can fire on one address.
@@ -853,34 +839,13 @@ export function auditHomepageLinks(index) {
853
839
  prefix === index.contentPackage ?
854
840
  `hardcoded absolute URL into this package's own ` +
855
841
  `address — write the package-relative ` +
856
- `"${rest}/", which the landing resolves ` +
857
- `against the site so the page follows the mount`
842
+ `"${rest}/", which a browser resolves against ` +
843
+ `the homepage's own address, the package root`
858
844
  : `hardcoded absolute URL into package "${prefix}" ` +
859
845
  `— resolve it through that package's link ` +
860
846
  `manifest, whose entries carry the address, so a ` +
861
847
  `relocation does not leave this page behind`,
862
848
  );
863
- } else if (shape === "rooted" && kind === "url") {
864
- const rest = prefix ? segments.slice(1).join("/") : segments.join("/");
865
- report(
866
- field,
867
- url,
868
- url,
869
- occurrence,
870
- // A `url:` is package-relative by construction, so it
871
- // cannot address anything outside this package at all —
872
- // there is no relative spelling of another package's root.
873
- // `href:` is the field for an address already resolved.
874
- !rest ?
875
- `url "${url}" addresses ` +
876
- (prefix ? `package "${prefix}"'s landing` : `the site root`) +
877
- `, but a landing's url: is package-relative and ` +
878
- `cannot leave this package — write ` +
879
- `href: "${url}", which is used verbatim`
880
- : `url "${url}" is root-relative, but a landing's url: ` +
881
- `is resolved against the site — write "${rest}/", ` +
882
- `or href: for an address that is already resolved`,
883
- );
884
849
  }
885
850
 
886
851
  // The retired-type rule reads the path *inside* the package, so an
@@ -297,7 +297,7 @@ export function positionOfLiteral(text, needle, occurrence = 1) {
297
297
  *
298
298
  * **`key: true` addresses the declaration rather than the value.** A finding
299
299
  * about a *value* — this pack name is not in `packs[]` — belongs on the value,
300
- * which is the default. A finding that names a **field** — `\`site.sections.x\`
300
+ * which is the default. A finding that names a **field** — `\`site.notfound.x\`
301
301
  * is not a recognized option` — sends the reader to look for that field, so the
302
302
  * position should be the field's own, and in a flow mapping
303
303
  * (`{ title: X, banner: Y }`) the two are different columns on one line. The
@@ -347,7 +347,7 @@ export function positionOfYamlPath(text, keyPath, { key = false } = {}) {
347
347
  * The YAML key path a **dotted field path** addresses.
348
348
  *
349
349
  * Configuration checks report the offending key as the path a reader would
350
- * write it — `packs[1].name`, `site.sections.affliction.title` — because that
350
+ * write it — `packs[1].name`, `site.notfound.links[0].url` — because that
351
351
  * is what the message has to say. {@link positionOfYamlPath} addresses a node
352
352
  * by segments instead, so this is the one translation between them: `.`
353
353
  * separates map keys, and a bracketed suffix is a sequence index.
@@ -442,5 +442,11 @@ export function renderItemFieldsPage(body, { title, destination, contentRoot, fr
442
442
  shortcode: shortcodeFromBasename(destination),
443
443
  frontmatter,
444
444
  });
445
- return matter.stringify(body, envelope);
445
+ const page = matter.stringify(body, envelope);
446
+ // `matter.stringify` closes the frontmatter fence directly onto the
447
+ // body's first line; Prettier's markdown printer requires a blank line
448
+ // between them, so a page written without one fails a consumer's
449
+ // `lint:format` the moment it is committed. Insert it here rather than
450
+ // let the generator and the formatter rewrite the file back and forth.
451
+ return page.replace(/^(---\n[\s\S]*?\n---\n)/, "$1\n");
446
452
  }