@heroiclands/package-build 22.0.3 → 22.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -28,6 +28,8 @@ two routes, and three keys behave differently depending on which:
28
28
  | Loaded by | `engine/pack-config.mjs`, which parses the YAML and derives three keys before calling `defineConfig` | `require()`, which loads the module and reads its default export — already the result of the file calling `defineConfig` itself |
29
29
  | `rootDir` | Forbidden — always the directory the file sits in | Authored, typically `import.meta.dirname` |
30
30
  | `foundryPackage` | Forbidden — always the adjacent `package.json` `name` | Authored |
31
+ | `homepage` | Forbidden — always the adjacent `package.json` `homepage` | Authored |
32
+ | `author` | Forbidden — always the adjacent `package.json` `author` | Authored |
31
33
  | `itemBuilders` | A **name** (`sohl`, `hm3`) or list of names, resolved against the registries this package ships | The registry object itself — real builder functions, which only code can carry |
32
34
 
33
35
  A file is chosen by its extension: `package-build.config.yaml`, then
@@ -46,15 +48,18 @@ around its own evaluation.
46
48
 
47
49
  ### Quick reference
48
50
 
49
- 18 top-level keys. `rootDir` is not one of them — a data configuration never
51
+ 20 top-level keys. `rootDir` is not one of them — a data configuration never
50
52
  writes it — and is documented under [Derived values](#derived-values) instead,
51
- alongside `foundryPackage` and `itemBuilders`, whose data-configuration
52
- behaviour is also derivation rather than ordinary authoring.
53
+ alongside `foundryPackage`, `homepage`, `author` and `itemBuilders`, whose
54
+ data-configuration behaviour is also derivation rather than ordinary
55
+ authoring.
53
56
 
54
57
  | Key | Type | Required | Default |
55
58
  | ------------------------------------------- | -------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------- | ------------------------------------------- |
56
59
  | [`contentPackage`](#contentpackage) | string | yes | — |
57
60
  | [`foundryPackage`](#foundrypackage) | string | yes (`.mjs` only — derived in YAML); refused in a `documentation` package | — |
61
+ | [`homepage`](#homepage) | string | no (`.mjs` only — derived in YAML) | `null` |
62
+ | [`author`](#author) | string, or `{name, email?, url?}` | no (`.mjs` only — derived in YAML) | `null` |
58
63
  | [`packageKind`](#packagekind) | `"systems"` \| `"modules"` \| `"documentation"` | yes | — |
59
64
  | [`stats`](#stats) | object | yes; refused in a `documentation` package | — |
60
65
  | [`itemBuilders`](#itembuilders) | object, or list of `{system, builders}` (or a name/list of names, in YAML) | no; refused in a `documentation` package | `{}` |
@@ -74,7 +79,7 @@ behaviour is also derivation rather than ordinary authoring.
74
79
 
75
80
  Any key outside this list is refused:
76
81
 
77
- > `` `<key>` is not a recognized option (expected one of: rootDir, contentPackage, foundryPackage, packageKind, stats, itemBuilders, paths, skipDirectories, icons, packs, docs, site, pdf, compatibility, relationships, systems, requiresSystem, packageBuild, publish). ``
82
+ > `` `<key>` is not a recognized option (expected one of: rootDir, contentPackage, foundryPackage, homepage, author, packageKind, stats, itemBuilders, paths, skipDirectories, icons, packs, docs, site, pdf, compatibility, relationships, systems, requiresSystem, packageBuild, publish). ``
78
83
 
79
84
  (`rootDir` appears in that list because it is a key `defineConfig` itself
80
85
  accepts — an `.mjs` configuration authors it directly. A YAML configuration
@@ -84,11 +89,11 @@ refuses an authored `rootDir` earlier, with its own message — see
84
89
 
85
90
  ### Derived values
86
91
 
87
- Five values in the resolved configuration are never transcribed by an author
92
+ Seven values in the resolved configuration are never transcribed by an author
88
93
  — they are computed from where the file sits, from the adjacent
89
94
  `package.json`, from the package kind, or from a name naming a table this
90
- package already ships. Authoring `rootDir`, `foundryPackage` or
91
- `stats.systemVersion` yourself is an **error**, not an override: a
95
+ package already ships. Authoring `rootDir`, `foundryPackage`, `homepage`,
96
+ `author` or `stats.systemVersion` yourself is an **error**, not an override: a
92
97
  transcribed copy is free to drift from what it copied, which is exactly how
93
98
  `stats.systemVersion` once sat at a stale version for four releases while
94
99
  nothing said so. `assetRoot` has no author-facing spelling to refuse in the
@@ -130,7 +135,7 @@ which applies before either loader form gets a chance to derive anything.
130
135
 
131
136
  If the adjacent `package.json` cannot be read, or declares no `name`:
132
137
 
133
- > `package-build: <package.json path> could not be read, and the configuration derives both the Foundry package id and the system version from it.`
138
+ > `package-build: <package.json path> could not be read, and the configuration derives its Foundry package id, system version, homepage and author from it.`
134
139
 
135
140
  > ``package-build: <package.json path> declares no `name`, which is what the Foundry package id is derived from.``
136
141
 
@@ -143,6 +148,54 @@ which applies before either loader form gets a chance to derive anything.
143
148
 
144
149
  > ``package-build config: `foundryPackage` must be a non-empty string.``
145
150
 
151
+ #### `homepage`
152
+
153
+ The address a package's site is served at — read by the generated Hugo
154
+ configuration for `baseURL`, independently of the Foundry manifest's own
155
+ `url`, which derives from `contentPackage` instead (see
156
+ [`packageBuild.manifest`](#packagebuildmanifest)). `null` when the package
157
+ declares none.
158
+
159
+ - In a **YAML** configuration, writing `homepage:` is refused; the loader
160
+ reads it from the adjacent `package.json` `homepage` instead, verbatim:
161
+
162
+ > ``package-build: <config file> declares `homepage`, which a data configuration may not: it is `package.json`'s own `homepage`. Remove the key.``
163
+
164
+ - In an **`.mjs`** configuration, `homepage` is an ordinary optional key — a
165
+ non-empty string when declared:
166
+
167
+ > ``package-build config: `homepage` must be a non-empty string.``
168
+
169
+ `homepage` is not itself checked against `contentPackage` by `defineConfig`.
170
+ `checkHomepage` in `config.mjs` is the check: `homepage` is required
171
+ unconditionally — every package publishes a site — and must be an absolute
172
+ URL whose path ends `/<contentPackage>/`. `content-build site` makes it
173
+ before the generated `baseURL` is written, so a missing or mismatched
174
+ `homepage` is a finding on every site build:
175
+
176
+ > ``package-build config: `homepage` is not declared in `package.json`, and every package needs one to build its site's `baseURL` from. Add `https://www.heroiclands.org/<contentPackage>/`.``
177
+
178
+ > ``package-build config: `homepage` is `https://www.heroiclands.org/harn-ensemble`, but `contentPackage` is `harnensemble` — a package's site is served at `https://www.heroiclands.org/harnensemble/`, so `package.json`'s `homepage` must end `/harnensemble/`.``
179
+
180
+ #### `author`
181
+
182
+ The package's byline, normalised from either of npm's `author` forms — a
183
+ string (`"Name <email> (url)"`, with the email and the URL both optional) or
184
+ an object (`{name, email?, url?}`) — to the object form. `null` when the
185
+ package declares none.
186
+
187
+ - In a **YAML** configuration, writing `author:` is refused; the loader reads
188
+ it from the adjacent `package.json` `author` instead:
189
+
190
+ > ``package-build: <config file> declares `author`, which a data configuration may not: it is `package.json`'s own `author`. Remove the key.``
191
+
192
+ - In an **`.mjs`** configuration, `author` is an ordinary optional key, in
193
+ either form:
194
+
195
+ > ``package-build config: `author` must be `"Name"`, `"Name <email>"`, `"Name (url)"` or `"Name <email> (url)"` — npm's own `author` forms.``
196
+
197
+ > ``package-build config: `author` must be a string or an object with `name`, `email` and `url`.``
198
+
146
199
  #### `assetRoot`
147
200
 
148
201
  The served Foundry asset root a compiled document's `img:` is resolved
@@ -236,7 +289,7 @@ Unlike the first three, authoring `itemBuilders` is not an error — it is
236
289
 
237
290
  ---
238
291
 
239
- ## The 18 keys
292
+ ## The 20 keys
240
293
 
241
294
  ### `contentPackage`
242
295
 
@@ -282,6 +335,16 @@ package id:
282
335
 
283
336
  > ``package-build config: `foundryPackage` is refused in a `documentation` package, which is not a Foundry package, so it has no Foundry package id.``
284
337
 
338
+ ### `homepage`
339
+
340
+ See [Derived values](#derived-values) — forbidden in a YAML configuration,
341
+ optional (a non-empty string) in an `.mjs` one.
342
+
343
+ ### `author`
344
+
345
+ See [Derived values](#derived-values) — forbidden in a YAML configuration,
346
+ optional (either of npm's forms) in an `.mjs` one.
347
+
285
348
  ### `packageKind`
286
349
 
287
350
  **Type:** `"systems"` \| `"modules"` \| `"documentation"` · **Required** · no default.
@@ -436,16 +499,17 @@ name form resolves before reaching here.
436
499
  **Type:** object · **Optional** · every key defaults to the conventional
437
500
  HeroicLands layout, resolved against `rootDir`:
438
501
 
439
- | Key | Default | What it is |
440
- | --------------------- | ---------------------- | ------------------------------------------------------------------------------------------ |
441
- | `paths.content` | `assets/content` | The content tree root. |
442
- | `paths.assets` | `assets` | The asset roots' parent, holding `icons/`, `images/` and `audio/`. |
443
- | `paths.contentIndex` | `build/content-index` | Where `content-index` writes this package's note index. Derived and disposable. |
444
- | `paths.packJson` | `build/packs-json` | Build-only per-entry JSON intermediate. |
445
- | `paths.stage` | `build/stage/packs` | Compiled LevelDB packs. |
446
- | `paths.unpack` | `build/tmp/packs` | Where `unpack` extracts JSON back to. |
447
- | `paths.foreignCache` | `build/cache/foreign` | Where a dependency declaring `itemCatalog: true` is unpacked. |
448
- | `paths.metadataCache` | `build/cache/metadata` | Where a dependency's published content index is fetched to, for every declared dependency. |
502
+ | Key | Default | What it is |
503
+ | ----------------------- | ------------------------ | ----------------------------------------------------------------------------------------------- |
504
+ | `paths.content` | `assets/content` | The content tree root. |
505
+ | `paths.assets` | `assets` | The asset roots' parent, holding `icons/`, `images/` and `audio/`. |
506
+ | `paths.contentIndex` | `build/content-index` | Where `content-index` writes this package's note index. Derived and disposable. |
507
+ | `paths.packJson` | `build/packs-json` | Build-only per-entry JSON intermediate. |
508
+ | `paths.stage` | `build/stage/packs` | Compiled LevelDB packs. |
509
+ | `paths.unpack` | `build/tmp/packs` | Where `unpack` extracts JSON back to. |
510
+ | `paths.foreignCache` | `build/cache/foreign` | Where a dependency declaring `itemCatalog: true` is unpacked. |
511
+ | `paths.metadataCache` | `build/cache/metadata` | Where a dependency's published content index is fetched to, for every declared dependency. |
512
+ | `paths.navigationCache` | `build/cache/navigation` | Where the site navigation heroiclands.org publishes is fetched to, for the generated Hugo menu. |
449
513
 
450
514
  Every configured path must be **relative** — an absolute one would escape
451
515
  the repository the config anchors:
@@ -458,7 +522,7 @@ An empty value is refused generically:
458
522
 
459
523
  Any other key is refused:
460
524
 
461
- > ``package-build config: `paths.<key>` is not a recognized option (expected one of: content, assets, contentIndex, packJson, stage, unpack, foreignCache, metadataCache).``
525
+ > ``package-build config: `paths.<key>` is not a recognized option (expected one of: content, assets, contentIndex, packJson, stage, unpack, foreignCache, metadataCache, navigationCache).``
462
526
 
463
527
  ### `skipDirectories`
464
528
 
@@ -688,36 +752,40 @@ Any other key under `docs.itemFields` is refused:
688
752
 
689
753
  **Type:** object · **Optional** · every key defaults to nothing published:
690
754
 
691
- | Key | Type | Default |
692
- | ----------------------- | -------- | ------- |
693
- | `site.out` | string | `""` |
694
- | `site.base` | string | `""` |
695
- | `site.assets` | string | `""` |
696
- | `site.packages` | string[] | `[]` |
697
- | `site.sections` | object | `{}` |
698
- | `site.readmeSections` | object | `{}` |
699
- | `site.landing` | object | `null` |
700
- | `site.trees` | array | `[]` |
701
- | `site.pass` | string | `""` |
702
- | `site.passOptions` | object | `{}` |
703
- | `site.backfillSections` | boolean | `false` |
755
+ | Key | Type | Default |
756
+ | ----------------------- | -------- | --------------------- |
757
+ | `site.base` | string | `""` |
758
+ | `site.assets` | string | `""` |
759
+ | `site.packages` | string[] | `[]` |
760
+ | `site.sections` | object | `{}` |
761
+ | `site.readmeSections` | object | `{}` |
762
+ | `site.landing` | object | `null` |
763
+ | `site.trees` | array | `[]` |
764
+ | `site.pass` | string | `""` |
765
+ | `site.passOptions` | object | `{}` |
766
+ | `site.backfillSections` | boolean | `false` |
767
+ | `site.list` | object | `{shortcodes: false}` |
768
+ | `site.notfound` | object | `null` |
769
+ | `site.hugo` | object | `{}` |
704
770
 
705
771
  How much of a package reaches the web at all is **not** here — it is
706
- [`publish.site`](#publish). `site` is framing: where the Hugo tree is
707
- written, what a section is called, which extra trees are published beside
708
- the content, which named pass bundle supplies the repository's own body
709
- rewrites.
772
+ [`publish.site`](#publish). `site` is framing: what a section is called,
773
+ which extra trees are published beside the content, 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.
710
777
 
711
- > ``package-build config: `site` must be a mapping.``
778
+ Where the Hugo tree is written is not a choice. `content-build site` writes
779
+ the whole Hugo source tree under `build/hugo/` — the generated `hugo.toml`,
780
+ the content mount at `build/hugo/content/`, Hugo's own cache — as a sibling
781
+ of the deployment root `build/site/`, so nothing Hugo reads lands in what is
782
+ published. A `site.out` is refused by name:
712
783
 
713
- > ``package-build config: `site.<key>` is not a recognized option (expected one of: out, base, assets, packages, sections, readmeSections, landing, trees, pass, passOptions, backfillSections).``
784
+ > ``package-build config: `site.out` is retired the site build writes its content mount at `build/hugo/content`, beside the generated `hugo.toml`, and the location is not configurable. Remove the key.``
714
785
 
715
- `site.out` is the output root, resolved by `engine/site-build.mjs`; unset,
716
- it is refused **at build time** rather than by `defineConfig` (an unset
717
- value would otherwise resolve to `rootDir` itself, and the tree the build
718
- wipes on every run would be the working tree):
786
+ > ``package-build config: `site` must be a mapping.``
719
787
 
720
- > `site.out is not set, so there is nowhere to write the site. Refusing to continue: the output directory is wiped on every run, and an unset one resolves to the repository root.`
788
+ > ``package-build config: `site.<key>` is not a recognized option (expected one of: base, assets, packages, sections, readmeSections, landing, trees, pass, passOptions, backfillSections, list, notfound, hugo).``
721
789
 
722
790
  `site.assets` is the host every package's imagery is served from, and it is
723
791
  the one address in this file that is not this repository's own. A note names
@@ -730,11 +798,10 @@ Absolute, and the trailing slash is trimmed:
730
798
 
731
799
  > ``package-build config: `site.assets` must be an absolute `http://` or `https://` address — it is the host every package's imagery is served from, and a relative value resolves against whichever page happens to carry the image.``
732
800
 
733
- The consuming Hugo site spells the same host as `params.cdnBaseURL`, which
734
- its theme resolves a relative asset path against. The two are the same
735
- address written for two readers: this one is what the toolchain emits into a
736
- page, that one is what the theme joins onto anything the toolchain left
737
- relative.
801
+ The generated Hugo configuration carries the same host as
802
+ `params.cdnBaseURL`, which the theme resolves a relative asset path against.
803
+ The two are one value read by two readers: the toolchain emits it into a
804
+ page, and the theme joins it onto anything the toolchain left relative.
738
805
 
739
806
  `site.packages` names which content packages' notes the site walks, beyond
740
807
  this one's own; `site.pass` names a repository's own body-rewrite bundle
@@ -806,6 +873,132 @@ unchanged, and `backfillSections` is a plain boolean:
806
873
 
807
874
  > ``package-build config: `site.backfillSections` must be a boolean.``
808
875
 
876
+ `site.list` is how a listing page renders, written into the generated Hugo
877
+ configuration as `params.list`:
878
+
879
+ | Key | Type | Required | Default |
880
+ | ---------------------- | ------- | -------- | ------- |
881
+ | `site.list.shortcodes` | boolean | no | `false` |
882
+
883
+ > ``package-build config: `site.list` must be a mapping.``
884
+
885
+ > ``package-build config: `site.list.shortcodes` must be a boolean.``
886
+
887
+ > ``package-build config: `site.list.<key>` is not a recognized option (expected one of: shortcodes).``
888
+
889
+ `site.notfound` is the wording of the "page not found" page, written into
890
+ the generated Hugo configuration as `params.notfound`. The theme renders
891
+ the page for every site; what a repository supplies is the tagline, the
892
+ noun the body prose calls the site, and the routes back. `tagline` and
893
+ `sitenoun` are required once the block is present — a block declaring only
894
+ links would render the theme's generic wording above this site's routes,
895
+ which reads as two sites:
896
+
897
+ | Key | Type | Required | Default |
898
+ | ----------------------------- | ------ | -------- | ----------------------------------------------------------------------- |
899
+ | `site.notfound.tagline` | string | yes | — |
900
+ | `site.notfound.sitenoun` | string | yes | — |
901
+ | `site.notfound.heroimage` | string | no | none — the theme's default banner, resolved against `params.cdnBaseURL` |
902
+ | `site.notfound.links` | array | no | none — no list of routes back |
903
+ | `site.notfound.links[].title` | string | yes | — |
904
+ | `site.notfound.links[].url` | string | yes | — site-relative (`/`, `/polity/`) or absolute |
905
+ | `site.notfound.links[].text` | string | yes | — |
906
+
907
+ ```yaml
908
+ site:
909
+ notfound:
910
+ tagline: This module has one page, and it is not at
911
+ sitenoun: module
912
+ heroimage: images/banners/tapestry-of-dreams.webp
913
+ links:
914
+ - title: Thalorna
915
+ url: https://www.heroiclands.org/thalorna/
916
+ text: The setting whose artwork this module replaces.
917
+ ```
918
+
919
+ > ``package-build config: `site.notfound` must be a mapping.``
920
+
921
+ > ``package-build config: `site.notfound.tagline` must be a non-empty string.``
922
+
923
+ > ``package-build config: `site.notfound.links` must be a list.``
924
+
925
+ > ``package-build config: `site.notfound.links[<index>].url` must be a non-empty string.``
926
+
927
+ > ``package-build config: `site.notfound.<key>` is not a recognized option (expected one of: tagline, sitenoun, heroimage, links).``
928
+
929
+ > ``package-build config: `site.notfound.links[<index>].<key>` is not a recognized option (expected one of: title, url, text).``
930
+
931
+ `site.hugo` is a mapping deep-merged over the generated Hugo configuration,
932
+ last — the escape hatch for the one key nobody anticipated. A repository
933
+ whose homepage reproduces a notice whose bare URLs must stand unedited turns
934
+ Goldmark's autolinker off:
935
+
936
+ ```yaml
937
+ site:
938
+ hugo:
939
+ markup:
940
+ goldmark:
941
+ extensions:
942
+ linkify: false
943
+ ```
944
+
945
+ Objects merge; an array or a scalar replaces what the generator wrote. Every
946
+ key the generator writes is refused here, naming its source — see
947
+ [the generated Hugo configuration](#the-generated-hugo-configuration) — so
948
+ the block cannot grow into a second configuration file:
949
+
950
+ > ``package-build config: `site.hugo` must be a mapping.``
951
+
952
+ > ``package-build config: `site.hugo.baseURL` is derived from package.json `homepage` and must not be declared — it would be overwritten, and the two would disagree with nothing to say so.``
953
+
954
+ ### The generated Hugo configuration
955
+
956
+ `content-build site` writes `build/hugo/hugo.toml` on every run. Every value
957
+ in it has one source, and that source is where it is edited:
958
+
959
+ | Key | Derived from |
960
+ | --------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------- |
961
+ | `baseURL` | `package.json` `homepage`, checked by `checkHomepage` — an absolute URL ending `/<contentPackage>/` |
962
+ | `title` | `packageBuild.manifest.title`, which is required |
963
+ | `locale` | the organisation's locale, `en-us`, in `engine/site-config.mjs` |
964
+ | `publishDir` | `contentPackage`, under the deployment root `build/site` — written relative to `build/hugo/`, so `../site/<contentPackage>` |
965
+ | `contentDir` | the fixed content mount, `build/hugo/content` — written as `content` |
966
+ | `themesDir` | where `@heroiclands/hugo-theme` is installed, resolved the way Node resolves a package and written relative to `build/hugo/` |
967
+ | `theme` | the installed `@heroiclands/hugo-theme`, so `hugo-theme` |
968
+ | `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 |
969
+ | `taxonomies` | the same fact — written as `{ tag = "tags" }` when at least one note carries `tags:`, absent otherwise |
970
+ | `outputs` | the same fact — written as `{ taxonomy = ["HTML"], term = ["HTML"] }` when at least one note carries `tags:`, absent otherwise |
971
+ | `params.description` | `package.json` `description`; absent when the package declares none |
972
+ | `params.author` | `package.json` `author`, its `name`; absent when the package declares none |
973
+ | `params.cdnBaseURL` | `site.assets`; absent when unset |
974
+ | `params.brand` | the organisation's brand links — `logo`, `licenseURL`, `discordURL` — in `engine/site-config.mjs` |
975
+ | `params.list` | `site.list` |
976
+ | `params.notfound` | `site.notfound`; absent when undeclared |
977
+ | `markup.goldmark.renderer.unsafe` | the toolchain, whose pages carry raw HTML — a `<figure>` for every image, a `<span>` marking an unresolved link |
978
+ | `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 |
979
+
980
+ The site build reads the navigation from the cache only. A cold cache is an
981
+ error naming the command that fills it:
982
+
983
+ > `the site navigation has not been fetched. Run `content-build deps fetch` first.`
984
+
985
+ A missing theme names the package to install:
986
+
987
+ > `@heroiclands/hugo-theme is not installed anywhere above <rootDir> — add it to `devDependencies`and run`npm ci``
988
+
989
+ And a site's title reads from the manifest's, so a configuration declaring
990
+ none fails the site build:
991
+
992
+ > ``package-build config: `packageBuild.manifest.title` is not declared, and the site's `title` reads from it.``
993
+
994
+ Nothing else is emitted. A site whose notes carry no `tags:` publishes no
995
+ taxonomy pages — `[taxonomies]` and `[outputs]` go unwritten, and Hugo's
996
+ defaults never apply because `taxonomy` and `term` are disabled kinds. A site
997
+ with at least one tagged note publishes `/tags/` and a page per tag: Hugo's
998
+ own default taxonomy pair also declares `category`, so `[taxonomies]` names
999
+ only `tag`, and `[outputs]` restricts both to `HTML` so neither produces a
1000
+ feed. Every other key is `site.hugo`'s to add.
1001
+
809
1002
  ### `pdf`
810
1003
 
811
1004
  **Type:** object · **Optional** · default `null` (no book is built).
@@ -962,6 +1155,7 @@ Each entry, in any of the four lists:
962
1155
  | `relationships.systems[].manifest` | string | no | none |
963
1156
  | `relationships.systems[].compatibility` | object, `{minimum?, verified?}` | no | none |
964
1157
  | `relationships.systems[].itemCatalog` | boolean | no | `false` |
1158
+ | `relationships.systems[].contentIndex` | boolean | no | `true` |
965
1159
 
966
1160
  (the same keys apply under `requires[]`, `recommends[]` and
967
1161
  `conflicts[]`.)
@@ -970,7 +1164,7 @@ Each entry, in any of the four lists:
970
1164
 
971
1165
  > ``package-build config: `relationships.<kind>[<index>].id` must be a non-empty string.``
972
1166
 
973
- > ``package-build config: `relationships.<kind>[<index>].<key>` is not a recognized option (expected one of: id, contentPackage, type, manifest, compatibility, itemCatalog).``
1167
+ > ``package-build config: `relationships.<kind>[<index>].<key>` is not a recognized option (expected one of: id, contentPackage, type, manifest, compatibility, itemCatalog, contentIndex).``
974
1168
 
975
1169
  `contentPackage` names what the other package's _content_ is called, where
976
1170
  that differs from its Foundry id. A note addresses a file by the content
@@ -989,6 +1183,20 @@ item catalogue at build time. It requires a `manifest`:
989
1183
 
990
1184
  > ``package-build config: `relationships.<kind>[<index>].itemCatalog` needs a `manifest` naming the package to fetch.``
991
1185
 
1186
+ `contentIndex` and `itemCatalog` are the two edges a relationship may declare,
1187
+ and a package may have either without the other. `itemCatalog` says a
1188
+ dependency supplies _items_; `contentIndex`, `true` by default, says
1189
+ `deps fetch` fetches its published note index and this tree may cite its
1190
+ addresses by wikilink. Declaring `contentIndex: false` narrows the
1191
+ relationship to the Foundry manifest only — a dependency Foundry installs but
1192
+ this tree never cites — so `deps fetch` fetches nothing for it and a wikilink
1193
+ into it fails, naming the key, rather than resolving against a stale
1194
+ declaration or an index nobody fetched:
1195
+
1196
+ > ``package-build config: `relationships.<kind>[<index>].contentIndex` must be true or false.``
1197
+
1198
+ > ``package-build config: `relationships.<kind>[<index>].contentIndex` cannot be false together with `itemCatalog: true` — a catalogue is fetched from the same index.``
1199
+
992
1200
  ### `systems`
993
1201
 
994
1202
  **Type:** object (`{id: spec}`) · **Optional** · default `{}`. Refused in a
@@ -1157,7 +1365,7 @@ packageBuild:
1157
1365
  assetTransform: ./utils/svg-theme.mjs
1158
1366
  stageDir: build/stage
1159
1367
  clean:
1160
- extra: [site/content, site/public]
1368
+ extra: [coverage]
1161
1369
  lang:
1162
1370
  sources: lang/*.json
1163
1371
  deploy:
@@ -1232,6 +1440,7 @@ say so.
1232
1440
  | ------------------------------------- | ---------------------------------------------------------------- |
1233
1441
  | `packageBuild.manifest.id` | `foundryPackage`, itself derived from `package.json` `name` |
1234
1442
  | `packageBuild.manifest.version` | `package.json` `version` |
1443
+ | `packageBuild.manifest.description` | `package.json` `description` |
1235
1444
  | `packageBuild.manifest.url` | `package.json` `repository` |
1236
1445
  | `packageBuild.manifest.bugs` | `package.json` `repository` |
1237
1446
  | `packageBuild.manifest.manifest` | `package.json` `repository` and the release tag |
@@ -1242,6 +1451,8 @@ say so.
1242
1451
 
1243
1452
  > ``package-build config: `packageBuild.manifest.version` is derived from package.json `version` and must not be declared — it would be overwritten, and the two would disagree with nothing to say so.``
1244
1453
 
1454
+ > ``package-build config: `packageBuild.manifest.description` is derived from package.json `description` and must not be declared — it would be overwritten, and the two would disagree with nothing to say so.``
1455
+
1245
1456
  > ``package-build config: `packageBuild.manifest` must be a mapping.``
1246
1457
 
1247
1458
  ### `packageBuild.schema`
@@ -1457,14 +1668,17 @@ the source directory:
1457
1668
 
1458
1669
  ## Every retired or forbidden key, in one place
1459
1670
 
1460
- | Key | Why |
1461
- | ------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ |
1462
- | `publish.address.landing` | Retired — named a whole-section landing, and there are no sections to address. |
1463
- | `packs[].folders` | Retired — a folder is a note (`type: folder`), materialised by the pack whose documents reference it. |
1464
- | `rootDir` | Forbidden in a YAML configuration — always the file's own directory. |
1465
- | `foundryPackage` | Forbidden in a YAML configuration — always the adjacent `package.json` `name`. |
1466
- | `stats.systemId` | Forbidden in every configuration — derived from `packageKind`, `requiresSystem` or a lone declared system. |
1467
- | `stats.systemVersion` | Forbidden in every configuration — derived from `package.json` (a system) or `systems:` / `relationships.systems` (a module). |
1468
- | `packageBuild.manifest.id`, `.version`, `.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). |
1469
- | `publish.site: true` / `publish.site: false` | Refused rather than mapped write `homepage` or `content`. |
1470
- | `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` | Retiredthe site build writes its content mount at `build/hugo/content`, beside the generated `hugo.toml`. |
1682
+ | `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` | Forbidden — each is written by the site build from a source it names; see [the generated Hugo configuration](#the-generated-hugo-configuration). |
1683
+ | `publish.site: true` / `publish.site: false` | Refused rather than mapped — write `homepage` or `content`. |
1684
+ | `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. |
@@ -769,10 +769,21 @@ for.
769
769
  Four capabilities are configuration away, and each has its own guide material.
770
770
  None of them is needed to build a package.
771
771
 
772
- **A website.** Set `publish.site: content` and add a `site:` block naming the
773
- sections, and `content-build site` writes a Hugo content tree from the same
774
- notes. The site renders through `@heroiclands/hugo-theme`, and the package
775
- publishes at `https://www.heroiclands.org/<contentPackage>/`.
772
+ **A website.** Every package publishes one at the least, the homepage note
773
+ from step 5 at `https://www.heroiclands.org/<contentPackage>/`. Add that
774
+ address to `package.json` as `homepage`, with a `description` and an `author`
775
+ beside it; add `@heroiclands/hugo-theme` under `devDependencies`; and add a
776
+ `packageBuild.manifest.title`. Then `content-build deps fetch` caches the
777
+ organisation's navigation, and `content-build site` writes the whole Hugo
778
+ source tree under `build/hugo/` — the configuration generated from those
779
+ values, and the content mount — for `hugo --source build/hugo` to render
780
+ into `build/site/<contentPackage>/`. There is no Hugo configuration to
781
+ write: the file is generated on every run, and what is genuinely the
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.
776
787
 
777
788
  **Another package's content.** Declare a dependency under `relationships`, and
778
789
  `content-build deps fetch` caches that release's published content index so
@@ -123,9 +123,10 @@ wrong-case import passes locally and fails there.
123
123
  ```
124
124
 
125
125
  `clean` removes the conventional build directories plus anything named in
126
- `packageBuild.clean.extra` — a site's `content/`, `public/` and `resources/`,
127
- say. `distclean` additionally removes `node_modules`. Both exit 0 whether or not
128
- there was anything to remove.
126
+ `packageBuild.clean.extra` — a coverage directory, say. Everything the site
127
+ build writes is under `build/`, so a site needs no entry. `distclean`
128
+ additionally removes `node_modules`. Both exit 0 whether or not there was
129
+ anything to remove.
129
130
 
130
131
  ### `lint:*` — the checks, one per question
131
132
 
@@ -239,12 +240,36 @@ changesets does not touch the lockfile.
239
240
 
240
241
  ### The site scripts
241
242
 
242
- A package that publishes a website adds a further group. They are outside this
243
- document's scope because they need Hugo and the shared theme submodule, but the
244
- shape is worth knowing so the names are not surprising:
245
- `build:site-content` writes the Hugo content tree with `content-build site`,
246
- `build:site-html` runs Hugo over it, and `serve:site` does the first and then
247
- `hugo server` for a live preview.
243
+ Every package publishes a website at the least, its homepage so every
244
+ package carries this group:
245
+
246
+ ```json
247
+ "build:site": "run-s build:site-content build:site-html build:site-root",
248
+ "build:site-content": "content-build site",
249
+ "build:site-html": "hugo --source build/hugo --minify --gc --cleanDestinationDir",
250
+ "build:site-root": "package-build site-root",
251
+ "serve:site": "npm run build:site-content && hugo server --source build/hugo"
252
+ ```
253
+
254
+ `build:site-content` writes the whole Hugo source tree under `build/hugo/` —
255
+ the generated `hugo.toml` and the content mount — with `content-build site`;
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
258
+ that; and `serve:site` does the first and then `hugo server` for a live
259
+ preview. The repository carries no Hugo configuration of its own: `hugo.toml`
260
+ is generated on every run from `package.json`, `package-build.config.yaml`,
261
+ the installed `@heroiclands/hugo-theme` and the navigation `deps fetch`
262
+ cached, and the only file to add is `@heroiclands/hugo-theme` under
263
+ `devDependencies`. Hugo itself is a separate install — the extended edition,
264
+ on the developer's `PATH` and the runner's.
265
+
266
+ The site build reads the cached navigation, so `build:site` in a package with
267
+ no other dependency still runs `deps fetch` first:
268
+
269
+ ```json
270
+ "build:site": "run-s build:deps build:site-content build:site-html build:site-root",
271
+ "build:deps": "content-build deps fetch"
272
+ ```
248
273
 
249
274
  ### Deployment scripts
250
275
 
@@ -453,17 +478,17 @@ site-deploy workflow, so a fix to any of them reaches every repository at once.
453
478
 
454
479
  A summary, because "where does this come from" is the question that recurs.
455
480
 
456
- | Read | By |
457
- | -------------------------------------------------- | ----------------------------------------------------------- |
458
- | `package.json` | The Foundry package id, the version, the release addresses. |
459
- | `package-build.config.yaml` | Everything else about the build. |
460
- | `assets/content/**/*.md` | Every content command. |
461
- | `.gitignore` | `content-build format`, `content-build markdown`. |
462
- | `.prettierignore` | `content-build format`. |
463
- | `.github/labels.yml`, `.github/ISSUE_REPORTING.md` | `package-build labels check`. |
464
- | `lang/*.json` | `package-build lang`. |
465
- | `.env.local` | `package-build deploy`. |
466
- | `.github/workflows/build.yml` | The `pre-push` hook, for its step list. |
481
+ | Read | By |
482
+ | -------------------------------------------------- | --------------------------------------------------------------------------------------------------------- |
483
+ | `package.json` | The Foundry package id, the version, the release addresses; the site's `baseURL`, description and author. |
484
+ | `package-build.config.yaml` | Everything else about the build. |
485
+ | `assets/content/**/*.md` | Every content command. |
486
+ | `.gitignore` | `content-build format`, `content-build markdown`. |
487
+ | `.prettierignore` | `content-build format`. |
488
+ | `.github/labels.yml`, `.github/ISSUE_REPORTING.md` | `package-build labels check`. |
489
+ | `lang/*.json` | `package-build lang`. |
490
+ | `.env.local` | `package-build deploy`. |
491
+ | `.github/workflows/build.yml` | The `pre-push` hook, for its step list. |
467
492
 
468
493
  Everything written goes under `build/`. Nothing the toolchain generates is
469
494
  committed.