@heroiclands/package-build 22.0.2 → 22.1.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.
@@ -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
 
@@ -580,7 +644,9 @@ packs:
580
644
 
581
645
  > ``package-build config: `packs` must be an array.``
582
646
 
583
- > ``package-build config: `packs` must declare at least one pack.``
647
+ A package may declare **no** packs. A module that ships assets and compiles
648
+ nothing — alternative art for another package is the case — writes `packs: []`,
649
+ and the manifest carries an empty pack list.
584
650
 
585
651
  Two packs (including companions, anywhere in the tree) may not share a name
586
652
  — two packs both named `x` produce:
@@ -686,36 +752,40 @@ Any other key under `docs.itemFields` is refused:
686
752
 
687
753
  **Type:** object · **Optional** · every key defaults to nothing published:
688
754
 
689
- | Key | Type | Default |
690
- | ----------------------- | -------- | ------- |
691
- | `site.out` | string | `""` |
692
- | `site.base` | string | `""` |
693
- | `site.assets` | string | `""` |
694
- | `site.packages` | string[] | `[]` |
695
- | `site.sections` | object | `{}` |
696
- | `site.readmeSections` | object | `{}` |
697
- | `site.landing` | object | `null` |
698
- | `site.trees` | array | `[]` |
699
- | `site.pass` | string | `""` |
700
- | `site.passOptions` | object | `{}` |
701
- | `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 | `{}` |
702
770
 
703
771
  How much of a package reaches the web at all is **not** here — it is
704
- [`publish.site`](#publish). `site` is framing: where the Hugo tree is
705
- written, what a section is called, which extra trees are published beside
706
- the content, which named pass bundle supplies the repository's own body
707
- 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.
708
777
 
709
- > ``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:
710
783
 
711
- > ``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.``
712
785
 
713
- `site.out` is the output root, resolved by `engine/site-build.mjs`; unset,
714
- it is refused **at build time** rather than by `defineConfig` (an unset
715
- value would otherwise resolve to `rootDir` itself, and the tree the build
716
- wipes on every run would be the working tree):
786
+ > ``package-build config: `site` must be a mapping.``
717
787
 
718
- > `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).``
719
789
 
720
790
  `site.assets` is the host every package's imagery is served from, and it is
721
791
  the one address in this file that is not this repository's own. A note names
@@ -728,11 +798,10 @@ Absolute, and the trailing slash is trimmed:
728
798
 
729
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.``
730
800
 
731
- The consuming Hugo site spells the same host as `params.cdnBaseURL`, which
732
- its theme resolves a relative asset path against. The two are the same
733
- address written for two readers: this one is what the toolchain emits into a
734
- page, that one is what the theme joins onto anything the toolchain left
735
- 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.
736
805
 
737
806
  `site.packages` names which content packages' notes the site walks, beyond
738
807
  this one's own; `site.pass` names a repository's own body-rewrite bundle
@@ -804,6 +873,126 @@ unchanged, and `backfillSections` is a plain boolean:
804
873
 
805
874
  > ``package-build config: `site.backfillSections` must be a boolean.``
806
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` | the toolchain, which renders the same kinds on every site: `["taxonomy", "term", "RSS"]` |
969
+ | `params.description` | `package.json` `description`; absent when the package declares none |
970
+ | `params.author` | `package.json` `author`, its `name`; absent when the package declares none |
971
+ | `params.cdnBaseURL` | `site.assets`; absent when unset |
972
+ | `params.brand` | the organisation's brand links — `logo`, `licenseURL`, `discordURL` — in `engine/site-config.mjs` |
973
+ | `params.list` | `site.list` |
974
+ | `params.notfound` | `site.notfound`; absent when undeclared |
975
+ | `markup.goldmark.renderer.unsafe` | the toolchain, whose pages carry raw HTML — a `<figure>` for every image, a `<span>` marking an unresolved link |
976
+ | `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 |
977
+
978
+ The site build reads the navigation from the cache only. A cold cache is an
979
+ error naming the command that fills it:
980
+
981
+ > `the site navigation has not been fetched. Run `content-build deps fetch` first.`
982
+
983
+ A missing theme names the package to install:
984
+
985
+ > `@heroiclands/hugo-theme is not installed anywhere above <rootDir> — add it to `devDependencies`and run`npm ci``
986
+
987
+ And a site's title reads from the manifest's, so a configuration declaring
988
+ none fails the site build:
989
+
990
+ > ``package-build config: `packageBuild.manifest.title` is not declared, and the site's `title` reads from it.``
991
+
992
+ Nothing else is emitted. `[taxonomies]` and `[outputs]` are Hugo's defaults
993
+ once the taxonomy kinds and RSS are disabled, and every other key is
994
+ `site.hugo`'s to add.
995
+
807
996
  ### `pdf`
808
997
 
809
998
  **Type:** object · **Optional** · default `null` (no book is built).
@@ -1155,7 +1344,7 @@ packageBuild:
1155
1344
  assetTransform: ./utils/svg-theme.mjs
1156
1345
  stageDir: build/stage
1157
1346
  clean:
1158
- extra: [site/content, site/public]
1347
+ extra: [coverage]
1159
1348
  lang:
1160
1349
  sources: lang/*.json
1161
1350
  deploy:
@@ -1230,6 +1419,7 @@ say so.
1230
1419
  | ------------------------------------- | ---------------------------------------------------------------- |
1231
1420
  | `packageBuild.manifest.id` | `foundryPackage`, itself derived from `package.json` `name` |
1232
1421
  | `packageBuild.manifest.version` | `package.json` `version` |
1422
+ | `packageBuild.manifest.description` | `package.json` `description` |
1233
1423
  | `packageBuild.manifest.url` | `package.json` `repository` |
1234
1424
  | `packageBuild.manifest.bugs` | `package.json` `repository` |
1235
1425
  | `packageBuild.manifest.manifest` | `package.json` `repository` and the release tag |
@@ -1240,6 +1430,8 @@ say so.
1240
1430
 
1241
1431
  > ``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.``
1242
1432
 
1433
+ > ``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.``
1434
+
1243
1435
  > ``package-build config: `packageBuild.manifest` must be a mapping.``
1244
1436
 
1245
1437
  ### `packageBuild.schema`
@@ -1455,14 +1647,17 @@ the source directory:
1455
1647
 
1456
1648
  ## Every retired or forbidden key, in one place
1457
1649
 
1458
- | Key | Why |
1459
- | ------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ |
1460
- | `publish.address.landing` | Retired — named a whole-section landing, and there are no sections to address. |
1461
- | `packs[].folders` | Retired — a folder is a note (`type: folder`), materialised by the pack whose documents reference it. |
1462
- | `rootDir` | Forbidden in a YAML configuration — always the file's own directory. |
1463
- | `foundryPackage` | Forbidden in a YAML configuration — always the adjacent `package.json` `name`. |
1464
- | `stats.systemId` | Forbidden in every configuration — derived from `packageKind`, `requiresSystem` or a lone declared system. |
1465
- | `stats.systemVersion` | Forbidden in every configuration — derived from `package.json` (a system) or `systems:` / `relationships.systems` (a module). |
1466
- | `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). |
1467
- | `publish.site: true` / `publish.site: false` | Refused rather than mapped write `homepage` or `content`. |
1468
- | `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. |
1650
+ | Key | Why |
1651
+ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ |
1652
+ | `publish.address.landing` | Retired — named a whole-section landing, and there are no sections to address. |
1653
+ | `packs[].folders` | Retired — a folder is a note (`type: folder`), materialised by the pack whose documents reference it. |
1654
+ | `rootDir` | Forbidden in a YAML configuration — always the file's own directory. |
1655
+ | `foundryPackage` | Forbidden in a YAML configuration — always the adjacent `package.json` `name`. |
1656
+ | `homepage`, `author` | Forbidden in a YAML configuration — always the adjacent `package.json`'s own `homepage` and `author`. |
1657
+ | `stats.systemId` | Forbidden in every configuration — derived from `packageKind`, `requiresSystem` or a lone declared system. |
1658
+ | `stats.systemVersion` | Forbidden in every configuration derived from `package.json` (a system) or `systems:` / `relationships.systems` (a module). |
1659
+ | `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). |
1660
+ | `site.out` | Retiredthe site build writes its content mount at `build/hugo/content`, beside the generated `hugo.toml`. |
1661
+ | `site.hugo.baseURL`, `.title`, `.locale`, `.publishDir`, `.contentDir`, `.themesDir`, `.theme`, `.disableKinds`, `.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). |
1662
+ | `publish.site: true` / `publish.site: false` | Refused rather than mapped — write `homepage` or `content`. |
1663
+ | `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.