@heroiclands/package-build 22.15.1 → 22.16.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.
Files changed (221) hide show
  1. package/CHANGELOG.md +95 -0
  2. package/README.md +39 -678
  3. package/bin/{content-build.mjs → content-commands.mjs} +197 -118
  4. package/bin/package-build.mjs +303 -42
  5. package/bin/report.mjs +1 -1
  6. package/ci/ci-docker.mjs +6 -3
  7. package/ci/ci-steps.mjs +6 -4
  8. package/ci/content-format-reference.mjs +106 -0
  9. package/config.mjs +22 -2
  10. package/content-config.mjs +74 -134
  11. package/docs/api.md +294 -234
  12. package/docs/authoring/assets.md +31 -0
  13. package/docs/authoring/dates-and-calendars.md +33 -0
  14. package/docs/authoring/first-note.md +48 -0
  15. package/docs/authoring/frontmatter.md +40 -0
  16. package/docs/authoring/links-and-markup.md +137 -0
  17. package/docs/commands.md +631 -162
  18. package/docs/configuration.md +176 -140
  19. package/docs/content-format.md +50 -4229
  20. package/docs/design.md +32 -0
  21. package/docs/diagnostics.md +29 -22
  22. package/docs/getting-started.md +21 -877
  23. package/docs/guides/book.md +153 -0
  24. package/docs/guides/e2e.md +143 -0
  25. package/docs/guides/icons.md +126 -0
  26. package/docs/guides/localization.md +120 -0
  27. package/docs/guides/module-project.md +915 -0
  28. package/docs/guides/packs.md +167 -0
  29. package/docs/guides/reference-pages.md +148 -0
  30. package/docs/guides/release-and-deploy.md +114 -0
  31. package/docs/guides/site.md +152 -0
  32. package/docs/guides/system-project.md +45 -0
  33. package/docs/index.md +16 -0
  34. package/docs/project-setup.md +54 -35
  35. package/docs/reference/format-details.md +4420 -0
  36. package/docs/reference/note-types.md +531 -0
  37. package/engine/address-render.mjs +26 -0
  38. package/engine/address.mjs +3 -20
  39. package/engine/anchors.mjs +15 -6
  40. package/engine/asset-bindings.mjs +6 -2
  41. package/engine/base-compiler.mjs +47 -11
  42. package/engine/being-measurements.mjs +70 -0
  43. package/engine/calendar-artifacts.mjs +1 -1
  44. package/engine/calendar-choice.mjs +32 -0
  45. package/engine/calendar-human.mjs +71 -0
  46. package/engine/calendar-notes.mjs +41 -3
  47. package/engine/calendars.mjs +8 -1
  48. package/engine/changelog-lint.mjs +2 -2
  49. package/engine/code-fences.mjs +23 -97
  50. package/engine/compile-corpus.mjs +6 -7
  51. package/engine/content-admonitions.mjs +114 -0
  52. package/engine/content-captions.mjs +181 -0
  53. package/engine/content-embeds.mjs +2 -2
  54. package/engine/content-footnotes.mjs +34 -0
  55. package/engine/content-format-check.mjs +2 -2
  56. package/engine/content-format.mjs +127 -73
  57. package/engine/content-format.yaml +745 -0
  58. package/engine/content-html.mjs +1 -1
  59. package/engine/content-icons.mjs +45 -92
  60. package/engine/content-images.mjs +22 -74
  61. package/engine/content-index.mjs +12 -3
  62. package/engine/content-links.mjs +21 -26
  63. package/engine/content-lint.mjs +1 -5
  64. package/engine/content-secrets.mjs +3 -1
  65. package/engine/content-tables.mjs +47 -1257
  66. package/engine/culture-choice.mjs +32 -0
  67. package/engine/date-conversion.mjs +47 -0
  68. package/engine/declaration-check.mjs +87 -0
  69. package/engine/docs-checks.mjs +140 -0
  70. package/engine/documentation-links.mjs +31 -0
  71. package/engine/exported-scene.mjs +1 -1
  72. package/engine/extension-attributes.mjs +74 -0
  73. package/engine/field-reference.mjs +1 -1
  74. package/engine/field-spec.mjs +4 -0
  75. package/engine/foreign-catalog.mjs +5 -5
  76. package/engine/format-generated.mjs +15 -0
  77. package/engine/foundry-entries.mjs +3 -1
  78. package/engine/frontmatter-lint.mjs +5 -1
  79. package/engine/frontmatter.mjs +10 -0
  80. package/engine/generate.mjs +2 -2
  81. package/engine/helpers.mjs +71 -117
  82. package/engine/homepage.mjs +2 -3
  83. package/engine/index-records.mjs +1 -0
  84. package/engine/index.mjs +18 -3
  85. package/engine/infobox-registry.mjs +3 -1
  86. package/engine/infobox.mjs +38 -20
  87. package/engine/item-compiler.mjs +10 -8
  88. package/engine/item-docs.mjs +7 -6
  89. package/engine/journals.mjs +51 -10
  90. package/engine/macros.mjs +1 -1
  91. package/engine/map-build.mjs +1 -1
  92. package/engine/map-dot.mjs +1 -1
  93. package/engine/markdown-expressions.mjs +117 -0
  94. package/engine/metadata-index.mjs +7 -3
  95. package/engine/note-addresses.mjs +1 -0
  96. package/engine/note-dates.mjs +283 -61
  97. package/engine/note-package.mjs +5 -25
  98. package/engine/note-state.mjs +6 -2
  99. package/engine/note-vocabulary.mjs +74 -19
  100. package/engine/number-words.mjs +75 -0
  101. package/engine/office-holders.mjs +188 -0
  102. package/engine/pack-config.mjs +2 -15
  103. package/engine/pack-router.mjs +24 -5
  104. package/engine/packages.mjs +3 -2
  105. package/engine/pathnames.mjs +21 -24
  106. package/engine/pdf-build.mjs +217 -55
  107. package/engine/pdf-fonts.mjs +2 -2
  108. package/engine/pdf-images.mjs +82 -0
  109. package/engine/pdf-render.mjs +174 -22
  110. package/engine/populations.mjs +3 -2
  111. package/engine/project-init.mjs +499 -0
  112. package/engine/prose-config.mjs +1 -1
  113. package/engine/prose-lint.mjs +7 -6
  114. package/engine/readability-lint.mjs +86 -0
  115. package/engine/reckoning-markers.mjs +205 -93
  116. package/engine/scenes.mjs +5 -2
  117. package/engine/schema-check.mjs +3 -3
  118. package/engine/site-build.mjs +87 -37
  119. package/engine/site-config.mjs +8 -8
  120. package/engine/site-index.mjs +4 -0
  121. package/engine/site-maps.mjs +1 -1
  122. package/engine/site-preview.mjs +19 -8
  123. package/engine/social-ties.mjs +5 -1
  124. package/engine/sql-tables.mjs +92 -22
  125. package/engine/web-wikilinks.mjs +15 -31
  126. package/engine/wikilink-syntax.mjs +4 -1
  127. package/engine/wikilinks.mjs +12 -14
  128. package/hm3/item-fields.mjs +1 -1
  129. package/hm3/items.mjs +12 -45
  130. package/index.mjs +2 -14
  131. package/manifest.mjs +31 -4
  132. package/markdownlint-config.mjs +1 -1
  133. package/package.json +32 -11
  134. package/prettier-config.mjs +1 -1
  135. package/sohl/being-info.mjs +3 -145
  136. package/sohl/index.mjs +1 -1
  137. package/sohl/infobox.mjs +2 -1
  138. package/sohl/item-builders.mjs +3 -27
  139. package/sohl/item-fields.mjs +61 -22
  140. package/sohl/kb-passes.mjs +2 -2
  141. package/sohl/note-schemas.mjs +0 -8
  142. package/stage.mjs +19 -0
  143. package/types/config.d.mts +12 -3
  144. package/types/content-config.d.mts +15 -81
  145. package/types/engine/address-render.d.mts +13 -0
  146. package/types/engine/address.d.mts +2 -11
  147. package/types/engine/anchors.d.mts +3 -4
  148. package/types/engine/asset-bindings.d.mts +9 -2
  149. package/types/engine/base-compiler.d.mts +1 -2
  150. package/types/engine/being-measurements.d.mts +16 -0
  151. package/types/engine/calendar-choice.d.mts +14 -0
  152. package/types/engine/calendar-human.d.mts +4 -0
  153. package/types/engine/calendar-notes.d.mts +2 -0
  154. package/types/engine/calendars.d.mts +5 -3
  155. package/types/engine/code-fences.d.mts +6 -72
  156. package/types/engine/compile-corpus.d.mts +1 -3
  157. package/types/engine/content-admonitions.d.mts +24 -0
  158. package/types/engine/content-captions.d.mts +45 -0
  159. package/types/engine/content-footnotes.d.mts +10 -0
  160. package/types/engine/content-format-check.d.mts +17 -4
  161. package/types/engine/content-format.d.mts +24 -11
  162. package/types/engine/content-icons.d.mts +14 -23
  163. package/types/engine/content-images.d.mts +2 -2
  164. package/types/engine/content-index.d.mts +9 -2
  165. package/types/engine/content-links.d.mts +3 -5
  166. package/types/engine/content-tables.d.mts +11 -140
  167. package/types/engine/culture-choice.d.mts +14 -0
  168. package/types/engine/date-conversion.d.mts +4 -0
  169. package/types/engine/declaration-check.d.mts +24 -0
  170. package/types/engine/docs-checks.d.mts +28 -0
  171. package/types/engine/documentation-links.d.mts +7 -0
  172. package/types/engine/exported-scene.d.mts +6 -2
  173. package/types/engine/extension-attributes.d.mts +14 -0
  174. package/types/engine/field-spec.d.mts +9 -0
  175. package/types/engine/format-generated.d.mts +7 -0
  176. package/types/engine/frontmatter.d.mts +14 -4
  177. package/types/engine/generate.d.mts +1 -1
  178. package/types/engine/helpers.d.mts +37 -92
  179. package/types/engine/homepage.d.mts +2 -3
  180. package/types/engine/index.d.mts +5 -0
  181. package/types/engine/infobox-registry.d.mts +3 -1
  182. package/types/engine/infobox.d.mts +17 -6
  183. package/types/engine/item-compiler.d.mts +4 -4
  184. package/types/engine/item-docs.d.mts +4 -3
  185. package/types/engine/journals.d.mts +10 -7
  186. package/types/engine/map-graphviz.d.mts +1 -1
  187. package/types/engine/markdown-expressions.d.mts +17 -0
  188. package/types/engine/note-dates.d.mts +47 -1
  189. package/types/engine/note-package.d.mts +3 -19
  190. package/types/engine/note-state.d.mts +2 -1
  191. package/types/engine/note-vocabulary.d.mts +9 -0
  192. package/types/engine/number-words.d.mts +4 -0
  193. package/types/engine/office-holders.d.mts +14 -0
  194. package/types/engine/pack-config.d.mts +1 -1
  195. package/types/engine/packages.d.mts +3 -2
  196. package/types/engine/pdf-images.d.mts +38 -0
  197. package/types/engine/populations.d.mts +2 -2
  198. package/types/engine/project-init.d.mts +17 -0
  199. package/types/engine/prose-config.d.mts +1 -1
  200. package/types/engine/prose-lint.d.mts +3 -2
  201. package/types/engine/readability-lint.d.mts +22 -0
  202. package/types/engine/reckoning-markers.d.mts +4 -2
  203. package/types/engine/schema-check.d.mts +3 -3
  204. package/types/engine/site-build.d.mts +38 -28
  205. package/types/engine/site-config.d.mts +2 -2
  206. package/types/engine/site-index.d.mts +4 -0
  207. package/types/engine/site-preview.d.mts +1 -1
  208. package/types/engine/social-ties.d.mts +8 -2
  209. package/types/engine/sql-tables.d.mts +13 -2
  210. package/types/engine/web-wikilinks.d.mts +2 -1
  211. package/types/engine/wikilinks.d.mts +7 -1
  212. package/types/hm3/items.d.mts +5 -6
  213. package/types/labels.d.mts +40 -0
  214. package/types/manifest.d.mts +3 -3
  215. package/types/sohl/actors.d.mts +2 -2
  216. package/types/sohl/being-info.d.mts +3 -43
  217. package/types/sohl/index.d.mts +1 -1
  218. package/types/sohl/infobox.d.mts +5 -2
  219. package/types/sohl/item-fields.d.mts +4 -13
  220. package/types/stage.d.mts +9 -0
  221. package/CONTENT.md +0 -3239
@@ -1,891 +1,35 @@
1
- # Getting started
2
-
3
- This is the path from an empty directory to a HeroicLands package that builds:
4
- a repository whose notes compile into Foundry compendium packs, whose manifest
5
- Foundry can install, and whose release archive is ready to attach to a tag.
6
-
7
- It assumes you know the constellation's conventions — `~/dev/HeroicLands`, the
8
- branch and pull request rules, the shared Prettier configuration, the git hooks
9
- — and assumes nothing about this toolchain. Everything specific to
10
- `@heroiclands/package-build` is stated here or linked.
11
-
12
- The worked example builds a **module content package**: a Foundry module whose
13
- whole substance is a content tree. That is the common case, and the shortest
14
- path that exercises every stage of the build — the note format, the pack
15
- compilers, the content index, the manifest and the release archive. A system
16
- package (`packageKind: systems`) differs only in what its manifest is called and
17
- where its version is derived from; a documentation package is a different shape
18
- and is covered at the end.
19
-
20
- You will need Node 24 or newer and npm. Nothing else: no Foundry install, no
21
- credentials, no container.
22
-
23
- ## Where the reference material is
24
-
25
- This document is about **order and motivation** — what to do first, and why the
26
- next step needs the one before it. The detail lives in the references beside it,
27
- and this tutorial links to them rather than repeating them:
28
-
29
- | Document | What it answers |
30
- | ---------------------------------------- | ------------------------------------------------------------------------------- |
31
- | [`commands.md`](commands.md) | What each command does, its options, its exit codes. |
32
- | [`configuration.md`](configuration.md) | Every key of `package-build.config.yaml`, its type, its default, its refusals. |
33
- | [`content-format.md`](content-format.md) | What a note may declare, per note type, and what it compiles into. |
34
- | [`diagnostics.md`](diagnostics.md) | How to read the `file:line:column: severity: message` output. |
35
- | [`project-setup.md`](project-setup.md) | The files a repository carries beyond the configuration, and the script wiring. |
36
- | [`api.md`](api.md) | The programmatic surface, for a repository with a build script of its own. |
37
-
38
- An editor can render one note from its current buffer through the
39
- [`engine.sitePreview` API](api.md#enginesitepreview). The context reads saved
40
- notes and dependency indexes once, while each render overlays the active note's
41
- text and frontmatter. It returns generated Markdown and frontmatter for an HTML
42
- preview, or located findings if the page cannot be rendered. The editor keeps
43
- its own preview artifact and calls `refresh()` after a save.
44
-
45
- ## The shape of what you are building
46
-
47
- Nine files, and a directory the build writes:
48
-
49
- ```text
50
- acme-bestiary/
51
- ├── package.json # identity: the Foundry package id and the release addresses
52
- ├── package-build.config.yaml # the build: what this package is, and what it compiles
53
- ├── prettier.config.js # the shared formatting options
54
- ├── .gitignore # what is generated, so the checks skip it
55
- ├── README.md # shipped into the package
56
- ├── LICENSE.md # shipped into the package
57
- ├── lang/en.json # shipped into the package
58
- ├── assets/content/ # the notes — the only hand-authored content
59
- │ ├── homepage.md
60
- │ └── Bestiary/Marsh_Drake.md
61
- └── build/ # everything the build writes; never committed
62
- ```
63
-
64
- Two facts govern the whole arrangement, and both are worth holding on to before
65
- the first command.
66
-
67
- **`package.json` is the package's identity.** The Foundry package id is its
68
- `name`, verbatim; the manifest, download and bug addresses are derived from its
69
- `repository.url`; a system's stamped version is its `version`. None of those is
70
- transcribed into the build configuration, because a transcribed copy is free to
71
- drift from what it copied.
72
-
73
- **`package-build.config.yaml` is the build's single source.** The pack list, the
74
- compatibility range, the manifest and the site layout are all declared there
75
- once, and the generated `module.json` is derived from it. There is no
76
- hand-authored manifest to keep in step.
77
-
78
- **One thing about the examples.** Every block below is a real file or a real
79
- transcript. Fenced examples render at the two-space indentation markdown itself
80
- uses, which is not the four the shared Prettier configuration gives a `.json` or
81
- `.yaml` file — so copy the content and let `content-build format --write` settle
82
- the whitespace, rather than transcribing it. Step 6 is where that becomes part
83
- of the routine.
84
-
85
- ## Step 1 — `package.json`
86
-
87
- Create the directory and write `package.json` first, because the toolchain reads
88
- it from the moment it is installed.
89
-
90
- ```bash
91
- mkdir acme-bestiary
92
- cd acme-bestiary
93
- ```
94
-
95
- ```json
96
- {
97
- "name": "acme-bestiary",
98
- "private": true,
99
- "version": "0.1.0",
100
- "type": "module",
101
- "license": "GPL-3.0-or-later AND CC-BY-SA-4.0",
102
- "repository": {
103
- "type": "git",
104
- "url": "https://github.com/HeroicLands/acme-bestiary"
105
- },
106
- "engines": {
107
- "node": ">=24.0.0"
108
- },
109
- "scripts": {}
110
- }
111
- ```
112
-
113
- Five of those keys are load-bearing:
114
-
115
- - **`name`** becomes the Foundry package id, read verbatim. It is what appears
116
- as `id` in the generated `module.json`, what a compendium UUID is addressed
117
- through (`Compendium.acme-bestiary.journals.…`), and what the served asset
118
- root is built from. Choose it once; renaming it later invalidates every UUID
119
- anything has stored.
120
- - **`version`** is the release version. It appears in the manifest and in the
121
- pinned `download` address.
122
- - **`repository.url`** is what the manifest's `url`, `bugs`, `manifest` and
123
- `download` addresses are derived from. Without it, `package-build manifest`
124
- refuses to write anything.
125
- - **`type: "module"`** lets `prettier.config.js` be written as an ES module,
126
- which is how the shared configuration is re-exported.
127
- - **`private: true`** because a package built by this toolchain ships as a
128
- Foundry release archive, not to npm.
129
-
130
- Let `content-build format --write` set the indentation once this file exists:
131
- npm matches `package.json`'s existing indentation when it writes
132
- `package-lock.json`, so a formatted `package.json` produces a formatted
133
- lockfile, and neither needs a `.prettierignore` entry for the life of the
134
- repository.
135
-
136
- ## Step 2 — install the toolchain
137
-
138
- ```bash
139
- npm install --save-dev @heroiclands/package-build
140
- ```
141
-
142
- That installs `package-build`, the packaging half, and `content-build`, the
143
- content half, into `node_modules/.bin`. Run them through `npx`, or wire them into npm scripts as
144
- [`project-setup.md`](project-setup.md) describes.
145
-
146
- **How it worked:** `npx package-build --version` prints the installed version.
147
- `--version` and `--help` are the only invocations that do not read a
148
- configuration file. Every other one resolves `package-build.config.yaml` first,
149
- by walking up from the working directory, and fails loudly when it finds none:
150
-
151
- ```console
152
- $ npx content-build package compile
153
- [ERROR]: package-build: no package-build.config.yaml or package-build.config.yml or package-build.config.mjs found at or above /path/to/acme-bestiary, nor at or above /path/to/acme-bestiary/node_modules/@heroiclands/package-build/engine. A consuming repository declares its build in one file at its root; set PACKAGE_BUILD_CONFIG to name it elsewhere.
154
- ```
155
-
156
- That is the next step.
157
-
158
- ## Step 3 — `.gitignore` and `prettier.config.js`
159
-
160
- These come before the first check rather than after it, because the checks read
161
- them.
162
-
163
- `.gitignore` is what keeps generated trees out of the prose checks: both
164
- `content-build format` and `content-build markdown` consult a repository's
165
- ignore files, so a `build/` that is not named here is a build whose own output
166
- gets reported as unformatted.
167
-
168
- ```text
169
- node_modules
170
- /build/
171
- /nogit/
172
- /.env.local
173
- ```
174
-
175
- `node_modules` carries neither a leading nor a trailing slash, for a reason
176
- [`project-setup.md`](project-setup.md) gives in full: a trailing slash matches a
177
- directory only, and a worktree whose `node_modules` is a symlink then goes
178
- unignored.
179
-
180
- `prettier.config.js` re-exports the shared options:
181
-
182
- ```js
183
- /**
184
- * The shared HeroicLands Prettier configuration.
185
- *
186
- * Every route to Prettier — the toolchain, an editor integration, a bare
187
- * `npx prettier` — resolves a config file, so re-exporting the shared options
188
- * here is what keeps those three from formatting the same tree three ways.
189
- *
190
- * @type {import("prettier").Config}
191
- */
192
- export { default } from "@heroiclands/package-build/prettier";
193
- ```
194
-
195
- `content-build format` applies the shared options whether or not this file
196
- exists, so the lint chain is correct without it. Nothing else is: an editor's
197
- format-on-save and a bare `npx prettier --write .` resolve a _config file_, and
198
- finding none they fall back to Prettier's own defaults — a different print width
199
- against the same tree, so the editor and the lint chain take turns rewriting
200
- each other's work. The toolchain says so when the file is missing:
201
-
202
- ```console
203
- $ npx content-build format
204
- warning: this repository declares no Prettier configuration, so `content-build format` applies the shared conventions while an editor and a bare `npx prettier` apply Prettier's own to the same tree; declare them in a prettier.config.mjs — export { default } from "@heroiclands/package-build/prettier";
205
- ```
206
-
207
- Any filename Prettier resolves works, `prettier.config.js` included; this is the
208
- one every repository in the family carries.
209
-
210
- ## Step 4 — `package-build.config.yaml`
211
-
212
- The build's declaration. This is the smallest one that compiles a module content
213
- package:
214
-
215
- ```yaml
216
- contentPackage: bestiary
217
- packageKind: modules
218
-
219
- compatibility:
220
- minimum: "14.359"
221
- verified: "14.364"
222
-
223
- stats:
224
- lastModifiedBy: acmebuilder00000
225
-
226
- packs:
227
- - { name: journals, label: Journals, type: JournalEntry }
228
- ```
229
-
230
- Five keys, and each answers a question the build cannot answer for itself:
231
-
232
- - **`contentPackage`** names the content package this repository single-sources
233
- — `bestiary` here, as `thalorna` and `kethira` name theirs. It is what a note's
234
- canonical address is scoped by and what the published content index is named
235
- for. **A note never declares its own package**; it belongs to this one, and a
236
- `package:` key in a note is a hard error.
237
- - **`packageKind`** is `modules`, `systems` or `documentation`. It decides
238
- whether the manifest is written as `module.json` or `system.json`, where the
239
- served asset root is rooted, and where the stamped system version comes from.
240
- - **`compatibility.minimum`** is the Foundry core version every compiled
241
- document is stamped with, and `verified` is the version the package is tested
242
- against. There is no default: a guessed floor is stamped into every document
243
- in the pack and stays invisible until something migrates on it.
244
- - **`stats.lastModifiedBy`** is the sixteen-character Foundry user id stamped
245
- into every document's `_stats` block.
246
- - **`packs`** is the compendium list, and it is the _only_ place packs are
247
- declared — the manifest's `packs` array is derived from it, so the two cannot
248
- disagree.
249
-
250
- Everything omitted has a default, and the defaults are the conventional
251
- HeroicLands layout: the content tree at `assets/content`, the compiled packs at
252
- `build/stage/packs`, the intermediates and caches elsewhere under `build/`.
253
- [`configuration.md`](configuration.md) documents all eighteen top-level keys,
254
- what each refusal message means, and the five values that are derived rather
255
- than authored.
256
-
257
- **How it worked:** run a content command and watch the complaint move from the
258
- configuration to the content tree.
259
-
260
- ```console
261
- $ npx content-build package compile
262
- [ERROR]: Content tree not found at /path/to/acme-bestiary/assets/content.
263
- [ERROR]: Pack JSON generation reported 1 error(s); refusing to compile packs from incomplete output.
264
- ```
265
-
266
- The configuration validated. There is simply nothing to compile yet.
267
-
268
- ## Step 5 — the content tree
269
-
270
- Two notes: the package homepage, and one piece of content.
271
-
272
- **Every package's tree holds exactly one `type: homepage` note.** It is the
273
- package's front page, authored rather than generated, and `content-build lint`
274
- requires it. It compiles to a page and to no Foundry document.
275
-
276
- `assets/content/homepage.md`:
277
-
278
- ```markdown
279
- ---
280
- shortcode: root
281
- name: { full: The Acme Bestiary }
282
- type: homepage
283
- description: Creatures of the reed flats, their habits and their hides.
284
1
  ---
285
-
286
- # The Acme Bestiary
287
-
288
- What lives in the reed flats, what it eats, and what it is worth to the people
289
- who hunt it.
290
- ```
291
-
292
- `assets/content/Bestiary/Marsh_Drake.md`:
293
-
294
- ```markdown
295
- ---
296
- shortcode: marshdrake
297
- name: { full: Marsh Drake }
298
- type: lore
299
- subType: bestiary
300
- description: A wingless drake of the reed flats, hunted for its hide and feared for its patience.
301
- ---
302
-
303
- # Marsh Drake
304
-
305
- The marsh drake is a wingless reptile of the reed flats, grown to the length of
306
- a river barge. It hunts by stillness: it lies half-submerged for a day at a
307
- time and takes whatever wades within reach.
308
-
309
- ## Habits
310
-
311
- Drakes hold a stretch of water and defend it against their own kind. A stretch
312
- that falls vacant is claimed within a season.
313
- ```
314
-
315
- Four things about that frontmatter are the note format in miniature, and they
316
- are worth reading closely because everything else in the tree is a variation on
317
- them.
318
-
319
- **`type:` routes the note, not its location.** `Bestiary/` is a folder for a
320
- human's benefit. What makes this note a journal is `type: lore`; a note's
321
- directory has no bearing on which pack it lands in. `subType:` narrows it —
322
- `bestiary` is "a kind of creature that is not a people" — and the pair
323
- `(type, subType)` is what each game system maps onto its own document type.
324
-
325
- **`shortcode:` is the note's address.** Cross-references are written
326
- `[[lore-marshdrake|Marsh Drake]]` — the type, shortcode, and readable label — and resolve to whatever
327
- that note compiles into, in whatever system is being compiled. A shortcode is
328
- identity: renaming one breaks every link into it.
329
-
330
- **`name.full` is the document name and the published URL**, derived by one
331
- shared rule. There is no authored slug anywhere in this toolchain.
332
-
333
- **Frontmatter has three closed regions.** Top-level keys identify the note and
334
- describe its publication; `data:` holds shared facts; `sohl:` and `hm3:` hold
335
- system-specific facts. An unrecognised key is an error rather than a site
336
- parameter. The formatter keeps the allowed top-level keys in their required
337
- order.
338
- [`content-format.md`](content-format.md) is the specification.
339
-
340
- **A setting guide is a `doc` that orients a reader to a place or setting.** It
341
- gives a usable overview of areas, peoples, institutions, and daily life, then
342
- links to the detailed notes. Use `subType: settingguide` for that entry point;
343
- `concept` explains one subject, `reference` serves lookup, and `userguide`
344
- explains the Foundry controls. For example, a guide at
345
- `assets/content/Guides/Reed_Flats.md` can begin:
346
-
347
- ```markdown
348
- ---
349
- shortcode: reedflatsguide
350
- name: { full: The Reed Flats Guide }
2
+ shortcode: gettingstarted
3
+ name: { full: "Getting started" }
351
4
  type: doc
352
- subType: settingguide
353
- description: A starting point for adventures in the Reed Flats.
354
- ---
355
-
356
- The river trade links the settlements of the Reed Flats. Their routes and
357
- customs give visitors a way into the region. See [[lore-reedfolk|Reed Folk]]
358
- for the people who guide those journeys.
359
- ```
360
-
361
- The guide can include SQL tables for lists drawn from indexed notes and
362
- `:::secret` blocks for GM passages. Its address is
363
- `doc-reedflatsguide`, following the same rule as every `doc` note.
364
-
365
- An image stands in its own paragraph. Its directive accepts a named size and a
366
- position together:
367
-
368
- ```markdown
369
- ![[icon-anubis|Anubis]]{size: medium, float: top-left}
370
- ```
371
-
372
- The accepted sizes are `auto`, `small`, `medium`, `large`, `xlarge`, and
373
- `full-width`. Omitting `size:` means `auto`, which uses the image's natural
374
- dimensions within the available measure. The other names set maximum display
375
- widths for the website, Foundry, and book. See
376
- [Images](content-format.md#images) for the surface sizes and layout rules.
377
-
378
- ## Step 6 — check the tree
379
-
380
- Four checks, and they are quick enough to run continuously while authoring.
381
-
382
- ```bash
383
- npx content-build lint # addresses and frontmatter
384
- npx content-build links # every wikilink resolves
385
- npx content-build format # the shared Prettier options
386
- npx content-build markdown # the shared markdownlint rule set
387
- ```
388
-
389
- **How it worked:**
390
-
391
- ```console
392
- $ npx content-build lint
393
- [INFO]: Addresses and frontmatter are well-formed (2 address(es) across 2 note(s)).
394
-
395
- $ npx content-build links
396
- [INFO]: 2 notes: every link is a labelled address, every anchor link lands and every address resolves (0 cross-package reference(s) via manifest), no wikilink in frontmatter, every homepage address resolvable.
397
-
398
- $ npx content-build markdown
399
- [INFO]: Markdown is clean.
400
- ```
401
-
402
- `content-build format` reports rather than fixes, which is what a CI gate wants;
403
- `--write` is the fixing form. Transcribe a file's whitespace by hand and the
404
- reporting form names it:
405
-
406
- ```console
407
- $ npx content-build format
408
- package-build.config.yaml: error: is not formatted; run `content-build format --write` to fix it
409
- [ERROR]: 1 of 6 file(s) are not formatted.
410
-
411
- $ npx content-build format --write
412
- [INFO]: Formatted 1 of 6 file(s).
413
-
414
- $ npx content-build format
415
- [INFO]: Formatting is clean (6 file(s)).
416
- ```
417
-
418
- Both forms walk the whole repository, not only the content tree — the
419
- configuration, the scripts and the prose are all held to the same options.
420
-
421
- Every finding these commands emit starts with the path of the file that is
422
- wrong, followed by a line, a column, a severity and a message — the contract
423
- [`diagnostics.md`](diagnostics.md) describes. A field that cannot be known is
424
- dropped rather than guessed, so a finding about a whole file names only the file.
425
-
426
- Forget the homepage and `content-build lint` says so by name:
427
-
428
- ```console
429
- $ npx content-build lint
430
- assets/content: error: holds no `type: homepage` note, so package "bestiary" publishes nothing at its own address /bestiary/ — a package's front page is one authored note in this tree, routed by `type:` rather than by filename
431
- [ERROR]: 1 finding(s) across 1 note(s).
432
- ```
433
-
434
- ## Step 7 — compile the packs
435
-
436
- ```bash
437
- npx content-build package compile
438
- ```
439
-
440
- ```console
441
- [INFO]: Content tree: 2 note(s) at /path/to/acme-bestiary/assets/content
442
- [INFO]: Pack journals: /path/to/acme-bestiary/assets/content → /path/to/acme-bestiary/build/packs-json/journals
443
- [INFO]: Compiled 1 journal entry (0 documentation entries)
444
- [INFO]: Pack journals: compiling to LevelDB at /path/to/acme-bestiary/build/stage/packs/journals
445
- [INFO]: Pack compilation complete.
446
- ```
447
-
448
- Two notes, one journal entry: the homepage compiles to a page and to no Foundry
449
- document, which is why the counts differ.
450
-
451
- The compile runs in two passes, and the intermediate is worth knowing about
452
- because it is where you look when a document is not what you expected.
453
- `build/packs-json/journals/Marsh_Drake_72d497c6a8e57f57.json` is the compiled
454
- document as JSON, before it is written into the LevelDB pack — shown here with
455
- the page's HTML elided, and otherwise entire:
456
-
457
- ```json
458
- {
459
- "name": "Marsh Drake",
460
- "pages": [
461
- {
462
- "_id": "2ecce29fd1c5578f",
463
- "name": "Marsh Drake",
464
- "type": "text",
465
- "title": { "show": true, "level": 1 },
466
- "text": { "format": 1, "content": "…" },
467
- "_key": "!journal.pages!72d497c6a8e57f57.2ecce29fd1c5578f"
468
- }
469
- ],
470
- "folder": null,
471
- "sort": 0,
472
- "ownership": { "default": 0 },
473
- "flags": {},
474
- "_id": "72d497c6a8e57f57",
475
- "_stats": {
476
- "systemId": null,
477
- "systemVersion": null,
478
- "coreVersion": "14.359",
479
- "createdTime": 0,
480
- "modifiedTime": 0,
481
- "lastModifiedBy": "acmebuilder00000"
482
- },
483
- "_key": "!journal!72d497c6a8e57f57"
484
- }
485
- ```
486
-
487
- The body has become one text page, because a journal's pages are cut at its `#`
488
- headings: this note has one, so the whole body is one page named for it. A
489
- second `#` would produce a second page; anything before the first becomes a
490
- leading page named "Introduction". A heading at any level carrying an
491
- `{#anchor}` suffix also starts a page, because a Foundry UUID can address a page
492
- and nothing smaller — which is how a link to a section inside a note resolves at
493
- all. `_key` is the LevelDB key the entry is stored under, which is why the
494
- intermediate carries it.
495
-
496
- `coreVersion` is the `compatibility.minimum` from step 4 and `lastModifiedBy` is
497
- the `stats.lastModifiedBy`. `systemId` and `systemVersion` are `null` because
498
- this module declares no game system: it ships journals, which every system can
499
- read. A module shipping Actors or Items declares the system it ships for, and
500
- those two fields are stamped from it — see
501
- [`systems`](configuration.md#systems).
502
-
503
- `_id` is derived from the note's canonical address, so it is stable across
504
- builds: recompiling does not renumber anything, and a world that imported
505
- yesterday's pack still resolves against today's.
506
-
507
- **A build that exits 0 and produces the wrong output is a bug.** The compile is
508
- the stage where that matters most, so read the counts: "2 note(s)" and "Compiled
509
- 1 journal entry" are the two numbers that say what the tree held and what came
510
- out of it.
511
-
512
- ### Cross-references, and what they compile into
513
-
514
- Add a second note and link to it. `assets/content/Bestiary/Fen_Adder.md`:
515
-
516
- ```markdown
517
- ---
518
- type: lore
519
- subType: bestiary
520
- name:
521
- full: Fen Adder
522
- shortcode: fenadder
523
- description: A small venomous snake of the standing water, more feared than the drake.
5
+ subType: userguide
524
6
  ---
525
7
 
526
- # Fen Adder
527
-
528
- A hand-long snake that lies under the surface scum. Its venom kills slowly and
529
- reliably, which is why the reed-cutters fear it more than the drake.
530
- ```
531
-
532
- Then, in `Marsh_Drake.md`:
533
-
534
- ```markdown
535
- Drakes hold a stretch of water and defend it against their own kind. A stretch
536
- that falls vacant is claimed within a season. Reed-cutters working a drake's
537
- water fear the [[lore-fenadder|fen adder]] more.
538
- ```
539
-
540
- ```console
541
- $ npx content-build links
542
- [INFO]: 3 notes: every link is a labelled address, every anchor link lands and every address resolves (0 cross-package reference(s) via manifest), no wikilink in frontmatter, every homepage address resolvable.
543
-
544
- $ npx content-build package compile
545
- [INFO]: Compiled 2 journal entries (0 documentation entries)
546
- ```
547
-
548
- And in the compiled journal, the wikilink has become a Foundry reference:
549
-
550
- ```text
551
- @UUID[Compendium.acme-bestiary.journals.JournalEntry.c7c3488c3e2a9282]{fen adder}
552
- ```
553
-
554
- That is the whole point of addressing notes by shortcode rather than by
555
- filename or by id: one authored link, resolved at build time into the reference
556
- each surface needs — a `@UUID` in a compendium journal, a relative URL on the
557
- website. A link that resolves to nothing is an error naming the note, never a
558
- blank.
559
-
560
- ## Step 8 — what the package ships
561
-
562
- The packs are compiled but the package is not yet assembled. A Foundry package
563
- is a staged directory — a manifest, the packs, and whatever files the manifest
564
- points at — and the configuration declares both halves.
565
-
566
- Write the three files the manifest will name:
567
-
568
- `README.md`:
569
-
570
- ```markdown
571
- # Acme Bestiary
572
-
573
- Creatures of the reed flats, as compendium journals for Foundry VTT.
574
- ```
575
-
576
- `LICENSE.md`:
577
-
578
- ```markdown
579
- # License
580
-
581
- Code: GPL-3.0-or-later. Content: CC-BY-SA-4.0.
582
- ```
583
-
584
- `lang/en.json`:
585
-
586
- ```json
587
- {
588
- "ACMEBESTIARY": {
589
- "Title": "Acme Bestiary"
590
- }
591
- }
592
- ```
593
-
594
- Then add the `packageBuild:` section to `package-build.config.yaml`. It is the
595
- packaging half's reserved section — validated separately from everything above
596
- it, and documented under
597
- [the `packageBuild` section](configuration.md#the-packagebuild-section):
598
-
599
- ```yaml
600
- packageBuild:
601
- assets:
602
- - { from: README.md, to: README.md }
603
- - { from: LICENSE.md, to: LICENSE.md }
604
- - { from: lang, to: lang }
605
-
606
- manifest:
607
- title: Acme Bestiary
608
- description: Creatures of the reed flats, as compendium journals.
609
- authors:
610
- - name: Acme
611
- license: LICENSE.md
612
- readme: README.md
613
- languages:
614
- - lang: en
615
- name: English
616
- path: lang/en.json
617
- packFolders:
618
- - name: Acme Bestiary
619
- sorting: m
620
- packs:
621
- - journals
622
- ```
623
-
624
- `assets:` is a copy list, from the repository into the stage. `manifest:` is
625
- everything about the manifest that cannot be derived from `package.json` or from
626
- the keys above — the human-facing identity, and the files Foundry links to.
627
- `title` is required by Foundry and has no derivation; supply it.
628
-
629
- `packFolders` may name only packs this package ships, and the build compares the
630
- two lists rather than trusting them to agree:
631
-
632
- ```console
633
- $ npx package-build manifest
634
- package-build.config.yaml:36:21: error: packFolders: folder "Acme Bestiary" names pack "items", which this package does not ship (packs: journals)
635
- package-build: packFolders names 1 pack this package does not ship (reported above). Foundry skips a name it cannot resolve, so the folder would ship missing those packs — correct `packageBuild.manifest.packFolders`.
636
- ```
637
-
638
- That is the shape of the checks throughout: Foundry's own behaviour on bad input
639
- is to skip it silently, so the build refuses to emit input Foundry would skip.
640
-
641
- Stage the files:
642
-
643
- ```console
644
- $ npx package-build assets
645
- ✅ Static assets staged (3 entries, 3 files).
646
- ```
647
-
648
- Name a path that is not there and it says so before copying anything:
649
-
650
- ```console
651
- $ npx package-build assets
652
- package-build: Cannot stage assets — these paths do not exist:
653
- README.md
654
- LICENSE.md
655
- lang
656
- ```
657
-
658
- ## Step 9 — generate the manifest
659
-
660
- ```console
661
- $ npx package-build manifest
662
- ✅ Wrote build/stage/module.json (16 keys, 1 packs).
663
- ```
664
-
665
- `build/stage/module.json` is the file Foundry installs:
666
-
667
- ```json
668
- {
669
- "id": "acme-bestiary",
670
- "title": "Acme Bestiary",
671
- "description": "Creatures of the reed flats, as compendium journals.",
672
- "version": "0.1.0",
673
- "authors": [{ "name": "Acme" }],
674
- "license": "LICENSE.md",
675
- "readme": "README.md",
676
- "flags": {
677
- "metadataUrl": "https://github.com/HeroicLands/acme-bestiary/releases/download/v0.1.0/bestiary-metadata.jsonl"
678
- },
679
- "compatibility": { "minimum": "14.359", "verified": "14.364" },
680
- "languages": [{ "lang": "en", "name": "English", "path": "lang/en.json" }],
681
- "packFolders": [{ "name": "Acme Bestiary", "sorting": "m", "packs": ["journals"] }],
682
- "packs": [
683
- {
684
- "label": "Journals",
685
- "type": "JournalEntry",
686
- "name": "journals",
687
- "path": "packs/journals",
688
- "private": false
689
- }
690
- ],
691
- "url": "https://github.com/HeroicLands/acme-bestiary",
692
- "bugs": "https://github.com/HeroicLands/acme-bestiary/issues",
693
- "manifest": "https://github.com/HeroicLands/acme-bestiary/releases/latest/download/module.json",
694
- "download": "https://github.com/HeroicLands/acme-bestiary/releases/download/v0.1.0/module.zip"
695
- }
696
- ```
697
-
698
- Read it against what you authored, because the derivations are the part worth
699
- checking:
700
-
701
- - `id` is `package.json`'s `name`, verbatim.
702
- - `version` and the pinned `download` address are its `version`.
703
- - `url`, `bugs`, `manifest` and `download` are all built from its
704
- `repository.url`. Omit that and the command refuses rather than writing an
705
- unpublishable manifest:
706
-
707
- ```console
708
- $ npx package-build manifest
709
- package-build: package.json declares no `repository.url`, so the manifest has no release addresses to advertise. Add it.
710
- ```
711
-
712
- - `packs` is the `packs:` list from step 4, with `path` and `private` filled in.
713
- It is never declared twice.
714
- - `manifest` points at `releases/latest`, while `download` and `flags.metadataUrl`
715
- are pinned to this version. That asymmetry is deliberate: the manifest address
716
- must keep resolving as new releases land, and the download must not move under
717
- a world that installed it.
718
-
719
- ## Step 10 — publish the content index
720
-
721
- ```console
722
- $ npx content-build content-index
723
- [INFO]: bestiary → build/content-index/bestiary-metadata.jsonl (3 notes, 1 KiB)
724
- ```
725
-
726
- This is the file `flags.metadataUrl` advertises, and it is how packages address
727
- each other. One JSON object per note, carrying the note's canonical address, its
728
- name, its description and the Foundry UUID it compiles to:
729
-
730
- ```text
731
- {"address":{"canonical":"bestiary-none-lore-marshdrake","slug":"lore-marshdrake"},"aliasesAscii":[],"anchors":[],"description":"A wingless drake of the reed flats, hunted for its hide and feared for its patience.","documentation":null,"file":{"folder":"Bestiary","name":"Marsh_Drake","path":"Bestiary/Marsh_Drake.md"},"foundry":{"none":{"uuid":"Compendium.acme-bestiary.journals.JournalEntry.72d497c6a8e57f57"}},"id":"72d497c6a8e57f57","name":{"full":"Marsh Drake"},"nameAscii":"Marsh Drake","package":"bestiary","shortcode":"marshdrake","subType":"bestiary","type":"lore"}
732
- ```
733
-
734
- A downstream package that declares a dependency on this one fetches this file
735
- for the release it pins, and `[[lore-marshdrake]]` written in _its_ tree resolves
736
- through it to that UUID. The homepage's entry carries `"foundry": null` — it
737
- compiles to a page, so there is no document to address.
738
-
739
- The index is derived and disposable; it is written under `build/` and rebuilt
740
- from the tree every time.
741
-
742
- ### Calendar files for a setting module
743
-
744
- A content package with `lore` notes of `subType: calendar` can emit Foundry
745
- calendar definitions after the content index is built:
746
-
747
- ```console
748
- $ npx package-build calendars --calendaria-version 1.4.2
749
- ✅ 5 calendars emitted (10 files).
750
- ```
751
-
752
- The command writes `build/calendars/<shortcode>.json` and
753
- `build/calendars/<shortcode>.calendaria.json` for each calendar. The first is a
754
- calendar definition; the second wraps the same definition for import through
755
- Calendaria's settings. The version argument names the Calendaria import format
756
- the package targets. The envelope uses a fixed `exportedAt` sentinel because
757
- these files are reproducible build artifacts. A package with no calendars gets
758
- an empty `build/calendars/` directory.
759
-
760
- To ship the files in a Foundry package, add
761
- `{ from: build/calendars, to: calendars }` to `packageBuild.assets` and run the
762
- calendar command before `package-build assets`. Running the command requires
763
- the current content index; regenerate it after changing calendar notes.
764
-
765
- ## Step 11 — the release archive
766
-
767
- ```console
768
- $ npx package-build release
769
- ✅ Packaged 0.1.0 for release: build/dist/module.zip (0.0 MB)
770
- ```
771
-
772
- `build/dist/` then holds the three files a GitHub release needs, and the archive
773
- is named from `packageKind` — a module ships `module.zip`, a system
774
- `system.zip` — so no repository states it a second time:
775
-
776
- ```console
777
- $ ls build/dist
778
- bestiary-metadata.jsonl
779
- module.json
780
- module.zip
781
- ```
8
+ # Getting started
782
9
 
783
- The manifest and the content index sit beside the archive rather than only
784
- inside it, because that is where the addresses in the manifest point: a
785
- consumer reads `module.json` from the release without downloading the zip, and
786
- then fetches the index it names.
10
+ `@heroiclands/package-build` turns an authored content tree and a project configuration into Foundry compendiums, a website, and a book. Choose the path that matches the package you own, then use the shared note guide for its content.
787
11
 
788
- The archive itself is `build/stage/` verbatim — the manifest at the root, the
789
- LevelDB packs under `packs/`, and the files the assets step put there. `unzip
790
- -l build/dist/module.zip` is worth reading once, because it is the only view of
791
- what a player actually installs.
12
+ | You are building | Start here |
13
+ | --------------------------------------------- | ------------------------------------------ |
14
+ | A Foundry system with source code and content | [System project](guides/system-project.md) |
15
+ | A Foundry module, with or without source code | [Module project](guides/module-project.md) |
16
+ | Notes in an existing package | [Your first note](authoring/first-note.md) |
792
17
 
793
- That is the whole build. From an empty directory: identity, configuration,
794
- notes, checks, packs, stage, manifest, index, archive.
18
+ Node 24 or newer and npm are needed for the toolchain. The generated website build uses Hugo Extended; the book build uses Typst. A Foundry installation and deployment credentials are not needed to author or build content locally.
795
19
 
796
- ## Putting it in order
20
+ ## Start a package
797
21
 
798
- The eleven steps above are the order you need them the _first_ time. Thereafter
799
- the build is one chain, and this is the order it runs in:
22
+ Run `init` through npm when the project does not have `package.json`:
800
23
 
801
24
  ```bash
802
- npx content-build format # the prose checks
803
- npx content-build markdown
804
- npx content-build lint # the content checks
805
- npx content-build links
806
- npx content-build content-index # the index the manifest advertises
807
- npx package-build assets # the stage: the shipped files
808
- npx content-build package compile
809
- npx package-build manifest # the stage: the manifest
810
- npx package-build release # the archive
25
+ npm exec --package=@heroiclands/package-build -- package-build init my-package --kind modules --name my-package --title "My Package" --description "A content package." --author "Your Name" --license original
26
+ cd my-package
27
+ npm install
28
+ npm run lint
29
+ npm run build:site
30
+ npm run serve:site
811
31
  ```
812
32
 
813
- Only one of those orderings is enforced, and it is worth knowing which.
814
- `package-build release` refuses to pack an archive whose manifest advertises a
815
- content index the tree never wrote:
816
-
817
- ```console
818
- $ npx package-build release
819
- package-build: the manifest advertises bestiary-metadata.jsonl as `flags.metadataUrl` but no such file exists — looked in …/build/stage/bestiary-metadata.jsonl and …/build/content-index/bestiary-metadata.jsonl. Build the content index before packing the release.
820
- ```
821
-
822
- The rest of the chain is order-independent: `assets`, `compile` and `manifest`
823
- each write their own part of `build/stage/` and none clobbers another's. They
824
- are written in this order because it is the order that reads as an assembly —
825
- the files, then the packs, then the manifest describing both — and because
826
- everything that reads the tree runs before everything that writes the stage.
827
-
828
- [`project-setup.md`](project-setup.md) turns this into npm scripts, and covers
829
- everything a repository carries beyond the build itself: the git hooks, the
830
- changeset directory, the label registry, and what each script in the chain is
831
- for.
832
-
833
- ## What comes next
834
-
835
- Four capabilities are configuration away, and each has its own guide material.
836
- None of them is needed to build a package.
837
-
838
- **A website.** Every package publishes one — at the least, the homepage note
839
- from step 5 — at `https://www.heroiclands.org/<contentPackage>/`. Add that
840
- address to `package.json` as `homepage`, with a `description` and an `author`
841
- beside it; add `@heroiclands/hugo-theme` under `devDependencies`; and add a
842
- `packageBuild.manifest.title`. Then `content-build deps fetch` caches the
843
- organisation's navigation, and `content-build site` writes the whole Hugo
844
- source tree under `build/hugo/` — the configuration generated from those
845
- values, and the content mount — for `hugo --source build/hugo` to render
846
- into `build/site/<contentPackage>/`. There is no Hugo configuration to
847
- write: the file is generated on every run, and what is genuinely the
848
- package's own — the wording of its "page not found" page — goes in the
849
- `site:` block as `site.notfound`. Set `publish.site: content`, and the same
850
- command publishes the content tree's every page beside the homepage — one
851
- page per note, and nothing generated between them; an index of what the
852
- package publishes is a `doc` note carrying a content table.
853
- [`project-setup.md`](project-setup.md) gives the npm scripts.
854
-
855
- **Another package's content.** Declare a dependency under `relationships`, and
856
- `content-build deps fetch` caches that release's published content index so
857
- `[[…]]` links into it resolve. A relationship marked `itemCatalog: true` also
858
- caches the release's Item packs, which is what lets a being embed items by
859
- `(type, shortcode)`. Fetching never happens during a compile: a cold cache
860
- fails naming `deps fetch` rather than reaching the network.
861
-
862
- **A game system's documents.** A module shipping Actors or Items names its
863
- `itemBuilders` registry — `sohl` or `hm3` — and declares the system under
864
- `systems:`. That is what maps a note's `(type, subType)` onto a system's own
865
- document type, and what supplies the `_stats.systemVersion` every document is
866
- stamped with.
867
-
868
- **Deploying and testing.** `package-build deploy <stage>` pushes a staged
869
- package to a Foundry data directory or a remote host; `package-build container`
870
- runs a licensed Foundry in Docker; `package-build e2e` drives the Cypress suite
871
- against it. All three need a Foundry install, credentials, or both — see
872
- [`commands.md`](commands.md).
873
-
874
- ## A documentation package
875
-
876
- `packageKind: documentation` is the third kind, and it is a different shape
877
- rather than a smaller one: a package that publishes a content tree as a website
878
- and a book, and compiles no Foundry documents at all.
879
-
880
- Everything in steps 4 and 8 that exists to describe a Foundry package is
881
- **refused** there rather than ignored, each with a message saying why:
882
- `foundryPackage`, `stats`, `packs`, `itemBuilders`, `compatibility`,
883
- `relationships`, `systems`, `requiresSystem` and `docs`. In exchange, `publish`
884
- becomes required, with `site: content` — publishing the tree is the whole of
885
- what the package does.
33
+ Use `--kind systems` for a system project. `package-build init --check` reports what it would create without writing. The generated project includes a homepage, website and book targets, a `package-build.config.yaml`, npm scripts, and a `.gitignore` for local and generated files. Deployment needs hosting configuration; the local build and server work without it.
886
34
 
887
- So a documentation package's configuration is steps 1 through 6 with a different
888
- `packageKind`, plus a `site:` block, and then `content-build site` in place of
889
- steps 7 through 11. [`configuration.md`](configuration.md) carries the refusal
890
- message for every key; [`commands.md`](commands.md) covers the two commands such
891
- a package lives on, `content-build site` and `content-build pdf`.
35
+ The [command reference](commands.md) gives exact options. [Configuration](configuration.md) explains every key. [Authoring content](content-format.md) is the route into note syntax and the exhaustive [type reference](reference/note-types.md). [Diagnostics](diagnostics.md) explains located errors.