@heroiclands/package-build 20.3.1 → 20.5.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 (195) hide show
  1. package/CHANGELOG.md +351 -0
  2. package/CONTENT.md +322 -110
  3. package/README.md +21 -10
  4. package/bin/content-build.mjs +204 -92
  5. package/bin/package-build.mjs +48 -15
  6. package/config.mjs +6 -8
  7. package/content-config.mjs +518 -151
  8. package/docs/api.md +1352 -0
  9. package/docs/commands.md +1609 -0
  10. package/docs/configuration.md +1432 -0
  11. package/docs/content-format.md +73 -74
  12. package/docs/diagnostics.md +356 -0
  13. package/docs/getting-started.md +813 -0
  14. package/docs/project-setup.md +469 -0
  15. package/e2e.mjs +4 -4
  16. package/engine/actor-compiler.mjs +64 -61
  17. package/engine/address-charset.mjs +6 -6
  18. package/engine/address-diff.mjs +57 -52
  19. package/engine/anchored-sections.mjs +1 -1
  20. package/engine/anchors.mjs +1 -1
  21. package/engine/base-compiler.mjs +42 -38
  22. package/engine/bundle-notes.mjs +4 -4
  23. package/engine/bundles.mjs +17 -8
  24. package/engine/code-fences.mjs +3 -3
  25. package/engine/compendiums.mjs +8 -8
  26. package/engine/compile-corpus.mjs +4 -5
  27. package/engine/content-address.mjs +24 -24
  28. package/engine/content-charset.mjs +1 -1
  29. package/engine/content-format-check.mjs +24 -25
  30. package/engine/content-format.mjs +8 -8
  31. package/engine/content-html.mjs +202 -0
  32. package/engine/content-icons.mjs +196 -165
  33. package/engine/content-index.mjs +61 -40
  34. package/engine/content-links.mjs +74 -41
  35. package/engine/content-lint.mjs +64 -34
  36. package/engine/content-package.mjs +7 -9
  37. package/engine/content-slug.mjs +2 -2
  38. package/engine/content-tables.mjs +38 -34
  39. package/engine/content-tree.mjs +1 -1
  40. package/engine/derived-fields.mjs +174 -0
  41. package/engine/diagnostics.mjs +3 -4
  42. package/engine/document-subtypes.mjs +6 -7
  43. package/engine/field-reference.mjs +8 -8
  44. package/engine/field-spec.mjs +29 -30
  45. package/engine/folder-notes.mjs +18 -16
  46. package/engine/foreign-catalog.mjs +8 -9
  47. package/engine/foundry-entries.mjs +19 -20
  48. package/engine/frontmatter-lint.mjs +95 -103
  49. package/engine/frontmatter.mjs +11 -11
  50. package/engine/generate.mjs +44 -39
  51. package/engine/helpers.mjs +147 -84
  52. package/engine/homepage.mjs +20 -22
  53. package/engine/ids.mjs +15 -15
  54. package/engine/index-records.mjs +4 -4
  55. package/engine/index.mjs +26 -10
  56. package/engine/item-compiler.mjs +54 -29
  57. package/engine/item-docs.mjs +11 -13
  58. package/engine/item-registry.mjs +13 -13
  59. package/engine/journals.mjs +24 -15
  60. package/engine/macros.mjs +3 -3
  61. package/engine/map-notes.mjs +6 -6
  62. package/engine/metadata-index.mjs +5 -5
  63. package/engine/note-claims.mjs +51 -41
  64. package/engine/note-ids.mjs +5 -5
  65. package/engine/note-package.mjs +4 -4
  66. package/engine/note-renames.mjs +3 -3
  67. package/engine/note-schemas.mjs +3 -8
  68. package/engine/note-vocabulary.mjs +56 -54
  69. package/engine/pack-config.mjs +39 -25
  70. package/engine/pack-router.mjs +0 -0
  71. package/engine/pdf-build.mjs +464 -0
  72. package/engine/pdf-fonts.mjs +420 -0
  73. package/engine/pdf-render.mjs +798 -0
  74. package/engine/pdf-toc.mjs +525 -0
  75. package/engine/prose-config.mjs +9 -9
  76. package/engine/prose-lint.mjs +12 -13
  77. package/engine/region-events.mjs +2 -2
  78. package/engine/retired-fields.mjs +24 -22
  79. package/engine/runtime-only-fields.mjs +1 -1
  80. package/engine/scene-levels.mjs +1 -1
  81. package/engine/scenes.mjs +33 -25
  82. package/engine/schema-check.mjs +21 -21
  83. package/engine/schema-extract.mjs +1 -1
  84. package/engine/site-build.mjs +66 -50
  85. package/engine/site-index.mjs +17 -18
  86. package/engine/sql-tables.mjs +21 -13
  87. package/engine/subtype-registry.mjs +5 -4
  88. package/engine/system-block.mjs +18 -19
  89. package/engine/systems.mjs +2 -2
  90. package/engine/web-wikilinks.mjs +41 -38
  91. package/engine/wikilink-syntax.mjs +16 -16
  92. package/engine/wikilinks.mjs +40 -42
  93. package/hm3/actors.mjs +30 -22
  94. package/hm3/document-subtypes.mjs +5 -5
  95. package/hm3/index.mjs +2 -2
  96. package/hm3/item-builders.mjs +1 -1
  97. package/hm3/item-fields.mjs +3 -3
  98. package/hm3/items.mjs +28 -8
  99. package/hm3/template-priority.mjs +2 -2
  100. package/lang.mjs +3 -3
  101. package/manifest.mjs +17 -20
  102. package/package.json +1 -2
  103. package/release.mjs +66 -6
  104. package/sohl/actors.mjs +36 -28
  105. package/sohl/affiliation-standings.mjs +2 -2
  106. package/sohl/being-info.mjs +5 -5
  107. package/sohl/default-item-art.mjs +5 -5
  108. package/sohl/document-subtypes.mjs +5 -5
  109. package/sohl/index.mjs +3 -3
  110. package/sohl/item-builders.mjs +7 -7
  111. package/sohl/item-fields.mjs +8 -9
  112. package/sohl/items.mjs +28 -6
  113. package/sohl/kb-passes.mjs +5 -5
  114. package/sohl/note-schemas.mjs +12 -12
  115. package/sohl/skill-base.mjs +1 -1
  116. package/types/content-config.d.mts +91 -39
  117. package/types/e2e.d.mts +3 -3
  118. package/types/engine/actor-compiler.d.mts +45 -40
  119. package/types/engine/address-charset.d.mts +6 -6
  120. package/types/engine/address-diff.d.mts +60 -6
  121. package/types/engine/base-compiler.d.mts +27 -21
  122. package/types/engine/bundle-notes.d.mts +3 -3
  123. package/types/engine/bundles.d.mts +10 -1
  124. package/types/engine/code-fences.d.mts +3 -3
  125. package/types/engine/compendiums.d.mts +3 -3
  126. package/types/engine/compile-corpus.d.mts +1 -1
  127. package/types/engine/content-address.d.mts +20 -20
  128. package/types/engine/content-format-check.d.mts +6 -6
  129. package/types/engine/content-format.d.mts +2 -2
  130. package/types/engine/content-html.d.mts +78 -0
  131. package/types/engine/content-icons.d.mts +139 -92
  132. package/types/engine/content-index.d.mts +59 -15
  133. package/types/engine/content-links.d.mts +8 -8
  134. package/types/engine/content-lint.d.mts +8 -6
  135. package/types/engine/content-package.d.mts +6 -8
  136. package/types/engine/content-tables.d.mts +49 -18
  137. package/types/engine/derived-fields.d.mts +101 -0
  138. package/types/engine/diagnostics.d.mts +2 -2
  139. package/types/engine/document-subtypes.d.mts +3 -3
  140. package/types/engine/field-spec.d.mts +37 -39
  141. package/types/engine/folder-notes.d.mts +4 -5
  142. package/types/engine/foreign-catalog.d.mts +3 -3
  143. package/types/engine/foundry-entries.d.mts +6 -7
  144. package/types/engine/frontmatter-lint.d.mts +10 -9
  145. package/types/engine/frontmatter.d.mts +7 -7
  146. package/types/engine/generate.d.mts +7 -7
  147. package/types/engine/helpers.d.mts +128 -56
  148. package/types/engine/homepage.d.mts +16 -18
  149. package/types/engine/ids.d.mts +13 -13
  150. package/types/engine/index-records.d.mts +3 -3
  151. package/types/engine/index.d.mts +6 -0
  152. package/types/engine/item-compiler.d.mts +21 -5
  153. package/types/engine/item-docs.d.mts +2 -2
  154. package/types/engine/item-registry.d.mts +6 -6
  155. package/types/engine/journals.d.mts +12 -3
  156. package/types/engine/map-notes.d.mts +2 -2
  157. package/types/engine/metadata-index.d.mts +4 -4
  158. package/types/engine/note-claims.d.mts +30 -22
  159. package/types/engine/note-ids.d.mts +4 -4
  160. package/types/engine/note-package.d.mts +1 -1
  161. package/types/engine/note-renames.d.mts +3 -3
  162. package/types/engine/note-vocabulary.d.mts +31 -203
  163. package/types/engine/pack-config.d.mts +7 -7
  164. package/types/engine/pack-router.d.mts +1 -1
  165. package/types/engine/pdf-build.d.mts +42 -0
  166. package/types/engine/pdf-fonts.d.mts +30 -0
  167. package/types/engine/pdf-render.d.mts +144 -0
  168. package/types/engine/pdf-toc.d.mts +114 -0
  169. package/types/engine/prose-config.d.mts +9 -9
  170. package/types/engine/prose-lint.d.mts +3 -4
  171. package/types/engine/region-events.d.mts +2 -2
  172. package/types/engine/retired-fields.d.mts +10 -9
  173. package/types/engine/scenes.d.mts +10 -1
  174. package/types/engine/schema-check.d.mts +13 -13
  175. package/types/engine/site-build.d.mts +52 -25
  176. package/types/engine/site-index.d.mts +3 -4
  177. package/types/engine/sql-tables.d.mts +11 -5
  178. package/types/engine/subtype-registry.d.mts +3 -3
  179. package/types/engine/system-block.d.mts +3 -3
  180. package/types/engine/web-wikilinks.d.mts +7 -7
  181. package/types/engine/wikilink-syntax.d.mts +17 -17
  182. package/types/engine/wikilinks.d.mts +14 -14
  183. package/types/hm3/actors.d.mts +9 -1
  184. package/types/hm3/document-subtypes.d.mts +1 -1
  185. package/types/hm3/items.d.mts +9 -2
  186. package/types/hm3/template-priority.d.mts +1 -1
  187. package/types/manifest.d.mts +8 -8
  188. package/types/release.d.mts +15 -4
  189. package/types/sohl/actors.d.mts +12 -4
  190. package/types/sohl/affiliation-standings.d.mts +2 -2
  191. package/types/sohl/being-info.d.mts +2 -2
  192. package/types/sohl/document-subtypes.d.mts +1 -1
  193. package/types/sohl/items.d.mts +9 -2
  194. package/types/sohl/note-schemas.d.mts +1 -1
  195. package/MIGRATING.md +0 -608
@@ -0,0 +1,1609 @@
1
+ # Command reference
2
+
3
+ `@heroiclands/package-build` ships two binaries. `package-build` is the
4
+ packaging half — clean, stage, check, package, deploy, run a Foundry
5
+ container, drive the end-to-end suite. `content-build` is the content half —
6
+ compile a note tree into compendium packs, check it, publish it as a site or a
7
+ book, and manage the caches a build resolves other packages through.
8
+
9
+ Both read `package-build.config.yaml` from the repository root — see
10
+ [Configuration](configuration.md) for every key. Neither reads it for
11
+ `--version` or `--help`: those two answer in a directory with no
12
+ configuration at all, and every other invocation resolves the configuration
13
+ and fails loudly when it is missing or wrong.
14
+
15
+ Each command below reads like a manual page: **NAME**, **SYNOPSIS**,
16
+ **DESCRIPTION**, **OPTIONS**, **EXIT STATUS**, **EXAMPLES**, **SEE ALSO**, in
17
+ that order, as bold labels rather than headings. A command with no options
18
+ says so under **OPTIONS** rather than omitting the section.
19
+
20
+ ## Conventions
21
+
22
+ **Every finding is `file:line:column: severity: message`**, the path starting
23
+ the line — the diagnostic contract this package's own build tooling emits and
24
+ that both binaries' content-side commands use for anything found in a
25
+ specific file. A field that cannot be known is dropped rather than guessed:
26
+ `file:line:` when the column means nothing, `file:` when only the file is
27
+ known. See [Diagnostics](diagnostics.md) for the full contract.
28
+
29
+ **Exit codes are 0 or 1.** 0 is success — including a command that found
30
+ advisory warnings but no error, and a command that had deliberately nothing to
31
+ do. 1 is failure: a configuration error, a missing prerequisite, or findings
32
+ that the command treats as errors. Each command's own **EXIT STATUS** states
33
+ exactly what makes its run a 1.
34
+
35
+ **`package-build` has one failure path for everything **`.fail()`** does not
36
+ handle specially: every thrown error, from any command, is caught, printed as
37
+ one line — `package-build: <message>`, or the bare diagnostic when the error
38
+ already carries its own `file:line:column:` — and turned into exit code 1. A
39
+ command that additionally emits findings of its own (`lang coverage`, `yaml`,
40
+ `bundle check`, …) is called out below.**
41
+
42
+ **`content-build` has no such umbrella.** Each command catches its own errors,
43
+ logs them (prefixed `[timestamp] [LEVEL]:` for ordinary failures, unprefixed
44
+ for a located diagnostic) and sets `process.exitCode = 1`; a command-line
45
+ parse error — an unknown flag, a missing required option, an action outside
46
+ its `choices` — is reported by yargs' own default handler and also exits 1.
47
+
48
+ **Both binaries are `strict()` and `demandCommand(1, …)`.** An unknown
49
+ command or option is refused rather than ignored, and running either with no
50
+ command prints usage and exits 1 rather than silently doing nothing — the
51
+ failure mode that let a typo in a build script pass the step it was meant to
52
+ run. The one exception is `package-build e2e <action>`, which relaxes to
53
+ `.strict(false)` for everything after the action, because that belongs to the
54
+ suite or the fast loop and this command line must not judge it.
55
+
56
+ **`--help` / `-h` and `--version` are global, not per-command.** Both
57
+ binaries register them once on their own top-level parser
58
+ (`.version(ownVersion())`, `.help()`, `.alias("help", "h")`); no command below
59
+ lists them among its own options.
60
+
61
+ **`PACKAGE_BUILD_CONFIG`** names the configuration file explicitly instead of
62
+ the walk up from the working directory both binaries otherwise do — the only
63
+ environment variable the toolchain itself reads for configuration
64
+ resolution. `deploy`, `container` and `e2e` additionally read `FOUNDRYVTT_*`,
65
+ documented where each applies.
66
+
67
+ An option name recurring across commands is not a recurring meaning —
68
+ `--out`, `--check`, `--from` and `--strict` each carry their own default and
69
+ behaviour where they appear below; read the command, not the name.
70
+
71
+ ---
72
+
73
+ ## `package-build`
74
+
75
+ ### `package-build clean`
76
+
77
+ **NAME**
78
+
79
+ Remove this repository's build artifacts.
80
+
81
+ **SYNOPSIS**
82
+
83
+ ```
84
+ package-build clean [--distclean]
85
+ ```
86
+
87
+ **DESCRIPTION**
88
+
89
+ Removes the conventional directories the library knows about, plus anything
90
+ named in `packageBuild.clean.extra` for a repository that generates more — a
91
+ site's `content/`, `public/` and `resources/`, say. Nothing is read from the
92
+ content tree; nothing is written.
93
+
94
+ **OPTIONS**
95
+
96
+ | Option | Type | Default | Description |
97
+ | ------------- | ------- | ------- | --------------------------- |
98
+ | `--distclean` | boolean | `false` | Also remove `node_modules`. |
99
+
100
+ **EXIT STATUS**
101
+
102
+ 1 on any thrown error (a missing or invalid configuration). Otherwise 0,
103
+ whether or not there was anything to remove.
104
+
105
+ **EXAMPLES**
106
+
107
+ ```
108
+ $ package-build clean
109
+ Nothing to clean.
110
+ ```
111
+
112
+ **SEE ALSO**
113
+
114
+ [Configuration](configuration.md).
115
+
116
+ ### `package-build assets`
117
+
118
+ **NAME**
119
+
120
+ Stage this repository's static files into the package root.
121
+
122
+ **SYNOPSIS**
123
+
124
+ ```
125
+ package-build assets
126
+ ```
127
+
128
+ **DESCRIPTION**
129
+
130
+ Stages every entry in `packageBuild.assets` — each names a `from` and a `to`,
131
+ and `to` is relative to the staged package root, so a table reads `lang`, not
132
+ `build/stage/lang`. A repository that has to transform a file on the way
133
+ names a module in `packageBuild.assetTransform`, exporting
134
+ `transform(sourcePath) -> string | null` — `null` copies the file verbatim,
135
+ anything else replaces its content. Reads every `from` path; writes into
136
+ `packageBuild.stageDir`.
137
+
138
+ **OPTIONS**
139
+
140
+ None.
141
+
142
+ **EXIT STATUS**
143
+
144
+ 1 if `packageBuild.assetTransform` is declared but cannot be loaded, or
145
+ exports no `transform` function. 1 on any other thrown error. Otherwise 0 —
146
+ including when `packageBuild.assets` is empty, which logs and stages nothing
147
+ rather than failing.
148
+
149
+ **EXAMPLES**
150
+
151
+ ```
152
+ $ package-build assets
153
+ ✅ Static assets staged (1 entries, 1 files).
154
+ ```
155
+
156
+ **SEE ALSO**
157
+
158
+ [Configuration](configuration.md).
159
+
160
+ ### `package-build schema`
161
+
162
+ **NAME**
163
+
164
+ Publish this package's DataModel field sets as `schema.json`.
165
+
166
+ **SYNOPSIS**
167
+
168
+ ```
169
+ package-build schema [--check]
170
+ ```
171
+
172
+ **DESCRIPTION**
173
+
174
+ Publishes the registries named in `packageBuild.schema` as `schema.json`, read
175
+ by `content-build content-format schema` and, in a consuming package, by
176
+ `content-build lint`'s emitted-versus-declared check. A repository that
177
+ declares no registries has nothing to publish. `--check` compares the
178
+ committed file against what the source would produce now instead of
179
+ rewriting it, so CI can gate on the file being current. Reads the registries
180
+ `packageBuild.schema` names; writes (or checks) `schema.json` at the
181
+ repository root.
182
+
183
+ **OPTIONS**
184
+
185
+ | Option | Type | Default | Description |
186
+ | --------- | ------- | ------- | ------------------------------------------------------------------------------ |
187
+ | `--check` | boolean | `false` | Fail when the committed `schema.json` is out of date rather than rewriting it. |
188
+
189
+ **EXIT STATUS**
190
+
191
+ 1 when `--check` finds the committed file does not match what the source
192
+ would produce. 1 on any other thrown error. Otherwise 0 — including when
193
+ `packageBuild.schema` is empty, which logs and publishes nothing.
194
+
195
+ **EXAMPLES**
196
+
197
+ ```
198
+ $ package-build schema
199
+ package-build: no `packageBuild.schema` declared; nothing to publish.
200
+ ```
201
+
202
+ **SEE ALSO**
203
+
204
+ `content-build content-format schema`, `content-build lint [root]`,
205
+ [Configuration](configuration.md).
206
+
207
+ ### `package-build manifest`
208
+
209
+ **NAME**
210
+
211
+ Generate the Foundry package manifest into the build stage.
212
+
213
+ **SYNOPSIS**
214
+
215
+ ```
216
+ package-build manifest
217
+ ```
218
+
219
+ **DESCRIPTION**
220
+
221
+ Generates `system.json` or `module.json` into the build stage —
222
+ `system.json` when `packageKind` is `systems`, `module.json` otherwise.
223
+ There is no template: every key comes from `packageBuild.manifest`, from
224
+ configuration this repository already carries (`packageKind`, `packageId`,
225
+ `compatibility`, the `packs` list, `relationships`), or from a module named
226
+ in `packageBuild.manifestFlags` for a flag the repository has to compute
227
+ itself — the compendium address of a document that exists only once the
228
+ content tree has been walked. Reads the shared pack configuration and
229
+ `package.json`; writes the manifest into `packageBuild.stageDir`.
230
+
231
+ **Refused for `packageKind: documentation`.** A manifest is the file Foundry
232
+ reads to install a package, and a documentation package is not one —
233
+ emitting `module.json` for it would advertise an installable package with no
234
+ packs, no compatibility range and no id. Its site is built by
235
+ `content-build site`, and its book by `content-build pdf`.
236
+
237
+ **OPTIONS**
238
+
239
+ None.
240
+
241
+ **EXIT STATUS**
242
+
243
+ 1 for `packageKind: documentation`. 1 if `packageBuild.manifestFlags` is
244
+ declared but cannot be loaded, or exports no `flags` function. 1 on any other
245
+ thrown error (a manifest key the configuration cannot supply —
246
+ `package.json` naming no `repository.url`, for instance). Otherwise 0.
247
+
248
+ **EXAMPLES**
249
+
250
+ ```
251
+ $ package-build manifest
252
+ ✅ Wrote build/stage/module.json (10 keys, 2 packs).
253
+
254
+ $ package-build manifest # packageKind: documentation
255
+ package-build: `packageKind: documentation` ships no Foundry package, so there is no manifest to generate. The site and the book are built by `content-build`.
256
+ ```
257
+
258
+ **SEE ALSO**
259
+
260
+ `content-build site`, `content-build pdf`, `package-build bundle check`,
261
+ [Configuration](configuration.md).
262
+
263
+ `package-build lang <action>` asks three independent questions about this
264
+ repository's localization, each blind to what the others see: `check`,
265
+ `coverage` and `hardcoded`, one section below per action.
266
+
267
+ ### `package-build lang check`
268
+
269
+ **NAME**
270
+
271
+ Check that every localization file survives `foundry.utils.expandObject`.
272
+
273
+ **SYNOPSIS**
274
+
275
+ ```
276
+ package-build lang check
277
+ ```
278
+
279
+ **DESCRIPTION**
280
+
281
+ A dotted-prefix collision (`"a.b": 1` beside `"a.b.c": 2`) makes
282
+ `expandObject` throw, and Foundry drops the whole file silently. Reads every
283
+ file matching `packageBuild.lang.sources`; writes nothing.
284
+
285
+ **OPTIONS**
286
+
287
+ None.
288
+
289
+ **EXIT STATUS**
290
+
291
+ 1 if any file fails `expandObject`, printing `packageBuild.lang.help` first
292
+ when the repository declares one. 1 if `packageBuild.lang.sources` matches no
293
+ file. 1 on any other thrown error. Otherwise 0.
294
+
295
+ **EXAMPLES**
296
+
297
+ ```
298
+ $ package-build lang check
299
+ package-build: 1 localization file(s) are expandObject-safe.
300
+ ```
301
+
302
+ **SEE ALSO**
303
+
304
+ `package-build lang coverage`, `package-build lang hardcoded`,
305
+ [Configuration](configuration.md).
306
+
307
+ ### `package-build lang coverage`
308
+
309
+ **NAME**
310
+
311
+ Check that declared and referenced localization keys agree.
312
+
313
+ **SYNOPSIS**
314
+
315
+ ```
316
+ package-build lang coverage [--unused]
317
+ ```
318
+
319
+ **DESCRIPTION**
320
+
321
+ Does every key the package references exist, and is every key it declares
322
+ referenced? A referenced-but-missing key fails the run — it renders to a
323
+ player as its own raw key string. An unreferenced-but-declared key is
324
+ reported and does not fail, because no scan sees every way a key can be
325
+ reached. Reads `packageBuild.lang.primary`, and every file matching
326
+ `packageBuild.lang.scripts` and `packageBuild.lang.templates`. A repository
327
+ that generates keys by a convention of its own names a module in
328
+ `packageBuild.lang.references`, exporting `references(context) ->
329
+ ReferenceSet`. Writes nothing.
330
+
331
+ **OPTIONS**
332
+
333
+ | Option | Type | Default | Description |
334
+ | ---------- | ------- | ------- | -------------------------------------------------------------- |
335
+ | `--unused` | boolean | `false` | List every unreferenced key instead of a preview capped at 20. |
336
+
337
+ **EXIT STATUS**
338
+
339
+ 1 if any reference is missing (an unreferenced key alone does not fail it).
340
+ 1 if the configured source globs match nothing at all. 1 on any other thrown
341
+ error. Otherwise 0.
342
+
343
+ **EXAMPLES**
344
+
345
+ ```
346
+ $ package-build lang coverage
347
+ package-build: 1 key(s) declared in lang/en.json · 1 referenced · 0 namespace(s) · 0 dynamic shape(s) · 0 missing · 0 unreferenced
348
+ ```
349
+
350
+ **SEE ALSO**
351
+
352
+ `package-build lang check`, `package-build lang hardcoded`,
353
+ [Diagnostics](diagnostics.md), [Configuration](configuration.md).
354
+
355
+ ### `package-build lang hardcoded`
356
+
357
+ **NAME**
358
+
359
+ Check that markup text is localized and that every template compiles.
360
+
361
+ **SYNOPSIS**
362
+
363
+ ```
364
+ package-build lang hardcoded
365
+ ```
366
+
367
+ **DESCRIPTION**
368
+
369
+ Does the markup's user-visible text go through localization, and does each
370
+ template still compile? The reverse of `coverage`, which is blind to a
371
+ template that mentions no key at all. Reads every file matching
372
+ `packageBuild.lang.templates`; writes nothing.
373
+
374
+ **OPTIONS**
375
+
376
+ None.
377
+
378
+ **EXIT STATUS**
379
+
380
+ 1 if any user-visible literal is unlocalized or any template fails to
381
+ compile. 1 if `packageBuild.lang.templates` matches nothing. 1 on any other
382
+ thrown error. Otherwise 0.
383
+
384
+ **EXAMPLES**
385
+
386
+ ```
387
+ $ package-build lang hardcoded
388
+ package-build: 1 template(s) fully localized and compiling.
389
+ ```
390
+
391
+ **SEE ALSO**
392
+
393
+ `package-build lang check`, `package-build lang coverage`,
394
+ [Diagnostics](diagnostics.md), [Configuration](configuration.md).
395
+
396
+ ### `package-build labels check`
397
+
398
+ **NAME**
399
+
400
+ Compare the machine label registry against the documented reference table
401
+ (`check` is the only action).
402
+
403
+ **SYNOPSIS**
404
+
405
+ ```
406
+ package-build labels check [--registry <path>] [--doc <path>]
407
+ ```
408
+
409
+ **DESCRIPTION**
410
+
411
+ Compares the machine label registry synced to GitHub against the documented
412
+ reference table, and reports where they disagree — a label the registry has
413
+ that the table does not, or the reverse. Neither derives from the other, so
414
+ nothing else notices when they drift. Reads both files named below; writes
415
+ nothing.
416
+
417
+ **OPTIONS**
418
+
419
+ | Option | Type | Default | Description |
420
+ | ------------ | ------ | ---------------------------- | -------------------------------------------------------------- |
421
+ | `--registry` | string | `.github/labels.yml` | The machine registry synced to GitHub. |
422
+ | `--doc` | string | `.github/ISSUE_REPORTING.md` | The documented reference whose §3 table lists the same labels. |
423
+
424
+ **EXIT STATUS**
425
+
426
+ 1 if either file does not exist. 1 if the registry and the table disagree on
427
+ any label. Otherwise 0.
428
+
429
+ **EXAMPLES**
430
+
431
+ ```
432
+ $ package-build labels check --registry .github/labels.yml --doc .github/ISSUE_REPORTING.md
433
+ package-build: registry and §3 agree (11 labels).
434
+ ```
435
+
436
+ **SEE ALSO**
437
+
438
+ None.
439
+
440
+ ### `package-build yaml [paths..]`
441
+
442
+ **NAME**
443
+
444
+ Lint note frontmatter and every other YAML file in the repository.
445
+
446
+ **SYNOPSIS**
447
+
448
+ ```
449
+ package-build yaml [paths..]
450
+ ```
451
+
452
+ **DESCRIPTION**
453
+
454
+ The check that exists because a duplicate frontmatter key used to parse to
455
+ nothing and the note it belonged to silently lost every field, with every
456
+ later build pass reporting success. Needs no ESLint configuration of its
457
+ own, so a repository with one for its own source code
458
+ (`Song-of-Heroic-Lands-FoundryVTT`'s `src/`) keeps it untouched. Reads the
459
+ paths given, or every matched file; writes nothing.
460
+
461
+ **OPTIONS**
462
+
463
+ | Positional | Type | Default | Description |
464
+ | ---------- | --------- | ----------------------------------------------------------------------------------------------------- | ----------------------- |
465
+ | `paths` | string(s) | every YAML file and markdown frontmatter git would consider (tracked, plus untracked and not ignored) | Files or globs to lint. |
466
+
467
+ **EXIT STATUS**
468
+
469
+ 1 if any finding is an error. A warning-only run exits 0.
470
+
471
+ **EXAMPLES**
472
+
473
+ ```
474
+ $ package-build yaml
475
+ package-build: 2 file(s) checked · 0 error(s) · 0 warning(s)
476
+ ```
477
+
478
+ **SEE ALSO**
479
+
480
+ [Diagnostics](diagnostics.md).
481
+
482
+ ### `package-build bundle check`
483
+
484
+ **NAME**
485
+
486
+ Check that the staged manifest and code bundle agree (`check` is the only
487
+ action).
488
+
489
+ **SYNOPSIS**
490
+
491
+ ```
492
+ package-build bundle check
493
+ ```
494
+
495
+ **DESCRIPTION**
496
+
497
+ Verifies the generated manifest and the staged code bundle agree — catching
498
+ three ways a package builds successfully and still does not load, all
499
+ invisible to the bundler itself: the entry declared under both `esmodules`
500
+ and `scripts` (Foundry loads it twice), under neither (Foundry never loads
501
+ it), or under `esmodules` while the file only parses as a classic script
502
+ (which fails at runtime with a message about whatever `import` came first,
503
+ naming nothing about the manifest). Both files are read from the stage,
504
+ because the stage is what ships; nothing is written.
505
+
506
+ **OPTIONS**
507
+
508
+ None.
509
+
510
+ **EXIT STATUS**
511
+
512
+ 1 if the staged manifest or bundle is missing — build the stage first. 1 if
513
+ the two disagree. Otherwise 0.
514
+
515
+ **EXAMPLES**
516
+
517
+ ```
518
+ $ package-build bundle check
519
+ package-build: demo-package.mjs is declared under "esmodules" and loads as one.
520
+ ```
521
+
522
+ **SEE ALSO**
523
+
524
+ `package-build manifest`, [Diagnostics](diagnostics.md).
525
+
526
+ ### `package-build release`
527
+
528
+ **NAME**
529
+
530
+ Zip the staged package for a GitHub release.
531
+
532
+ **SYNOPSIS**
533
+
534
+ ```
535
+ package-build release [--no-pdf]
536
+ ```
537
+
538
+ **DESCRIPTION**
539
+
540
+ The artifact name comes from `packageKind` — a system ships as
541
+ `system.json`'s sibling, a module as `module.json`'s — so no repository
542
+ states it a second time. When the package publishes content
543
+ (`publish.site: content`), it also builds the content-tree book (see
544
+ `content-build pdf`) and reports it alongside the archive; `--no-pdf` skips
545
+ that step for a release that has a tree but does not want the book this
546
+ time. A book that fails to build is reported, never fatal — the archive
547
+ above is the release regardless. Reads the staged package and (for the book)
548
+ the content tree; writes `<artifact>.zip` and, unless skipped, the book,
549
+ both under `build/dist`.
550
+
551
+ **OPTIONS**
552
+
553
+ | Option | Type | Default | Description |
554
+ | ---------- | ------- | ------- | --------------------------------------------------------------------------- |
555
+ | `--no-pdf` | boolean | — | Skip the content-tree book even when the package would otherwise build one. |
556
+
557
+ **EXIT STATUS**
558
+
559
+ 1 on any thrown error (an unstaged package, a manifest that advertises a
560
+ metadata file the content index never wrote). Otherwise 0 — book findings
561
+ are reported on stderr but do not fail the release.
562
+
563
+ **EXAMPLES**
564
+
565
+ ```
566
+ $ package-build release
567
+ ✅ Packaged 1.0.0 for release: build/dist/module.zip (0.0 MB)
568
+ No book: `publish.site` is `homepage`, which fences the content surfaces off — the tree is not walked and no book is built. Publish content to build one.
569
+ ```
570
+
571
+ **SEE ALSO**
572
+
573
+ `content-build pdf`, [Configuration](configuration.md).
574
+
575
+ ### `package-build deploy <stage>`
576
+
577
+ **NAME**
578
+
579
+ Push the staged package into a Foundry data directory.
580
+
581
+ **SYNOPSIS**
582
+
583
+ ```
584
+ package-build deploy <stage>
585
+ ```
586
+
587
+ **DESCRIPTION**
588
+
589
+ Pushes the staged package into a Foundry data directory for the named stage.
590
+ `packageKind` and `packageId` come from the shared configuration rather than
591
+ being restated per repository. The destination is read from
592
+ `FOUNDRYVTT_<STAGE>_DATA` (a local filesystem path is copied; a
593
+ `[user@]host:/path` target is uploaded over SFTP, authenticated through the
594
+ running SSH agent unless `FOUNDRYVTT_<STAGE>_KEY` names an explicit key). The
595
+ deploy is staged and swapped rather than written in place, because a running
596
+ Foundry holds its LevelDB packs open and a replacement written underneath it
597
+ corrupts them. `.env.local` and `.env` are loaded first. Reads
598
+ `packageBuild.stageDir`; writes into the stage's data directory.
599
+
600
+ **OPTIONS**
601
+
602
+ | Positional | Type | Default | Description |
603
+ | ---------- | ------ | ------- | ----------------------------------------------- |
604
+ | `stage` | string | — | Target stage, e.g. `dev`, `qa`, `prod`, `test`. |
605
+
606
+ **Environment.** `FOUNDRYVTT_<STAGE>_DATA` (required — the destination).
607
+ `FOUNDRYVTT_<STAGE>_AGENT`, `FOUNDRYVTT_<STAGE>_PORT`,
608
+ `FOUNDRYVTT_<STAGE>_USER`, `FOUNDRYVTT_<STAGE>_KEY` (SFTP destinations
609
+ only).
610
+
611
+ **EXIT STATUS**
612
+
613
+ 1 if `FOUNDRYVTT_<STAGE>_DATA` is unset. 1 on any other thrown error.
614
+ Otherwise 0.
615
+
616
+ **EXAMPLES**
617
+
618
+ ```
619
+ $ package-build deploy dev
620
+ package-build: No destination configured for stage 'dev'. Set FOUNDRYVTT_DEV_DATA — for example FOUNDRYVTT_DEV_DATA="/path/to/foundryvtt/data".
621
+
622
+ $ FOUNDRYVTT_DEV_DATA=/tmp/pb-docs-demo-410-data package-build deploy dev
623
+ Deploying /private/tmp/pb-docs-demo-410/build/stage → /tmp/pb-docs-demo-410-data/Data/modules/demo-package (local copy)
624
+ Deployed stage 'dev' successfully.
625
+ ```
626
+
627
+ **SEE ALSO**
628
+
629
+ `package-build container <stage> <action>`, `package-build e2e <action>`,
630
+ [Configuration](configuration.md).
631
+
632
+ ### `package-build container <stage> <action>`
633
+
634
+ **NAME**
635
+
636
+ Run a stage's Foundry in a Docker container.
637
+
638
+ **SYNOPSIS**
639
+
640
+ ```
641
+ package-build container <stage> <start|stop|restart|recreate|rm|status|logs|pull>
642
+ ```
643
+
644
+ **DESCRIPTION**
645
+
646
+ Runs a stage's Foundry in a Docker container, bind-mounted at the same
647
+ `FOUNDRYVTT_<STAGE>_DATA` directory `deploy <stage>` installs into — nothing
648
+ about the destination is stated twice. `.env.local` and `.env` are loaded
649
+ first.
650
+
651
+ `start` requires the stage's data root to exist and runs the image, passing
652
+ through `FOUNDRYVTT_<STAGE>_VERSION`, `FOUNDRYVTT_<STAGE>_WORLD` and
653
+ `FOUNDRYVTT_<STAGE>_LICENSE_KEY` when set. `stop` stops it. `restart` stops
654
+ it, clears a stale lock left by an unclean shutdown, and starts it again —
655
+ not `docker restart`, which leaves no window to clear the lock. `recreate`
656
+ removes the container first, so a changed world, license or cache
657
+ environment variable actually takes effect, then starts fresh. `rm` removes
658
+ the container. `status` runs `docker ps` filtered to this stage's container
659
+ name. `logs` follows its log. `pull` pulls the configured image
660
+ (`FOUNDRYVTT_CONTAINER_IMAGE`, then `packageBuild.container.image`, then a
661
+ floating `felddy/foundryvtt:release` tag when the package declares no
662
+ `compatibility.minimum`).
663
+
664
+ **OPTIONS**
665
+
666
+ | Positional | Type | Default | Description |
667
+ | ---------- | ------------------------------------------------------------------------------------- | ------- | ----------------------------------------------- |
668
+ | `stage` | string | — | Target stage, e.g. `dev`, `qa`, `prod`, `test`. |
669
+ | `action` | string, one of `start`, `stop`, `restart`, `recreate`, `rm`, `status`, `logs`, `pull` | — | What to do with the stage's container. |
670
+
671
+ **Environment.** `FOUNDRYVTT_<STAGE>_DATA` (`start`, `restart`, `recreate`).
672
+ `FOUNDRYVTT_<STAGE>_PORT`, `FOUNDRYVTT_<STAGE>_VERSION`,
673
+ `FOUNDRYVTT_<STAGE>_WORLD`, `FOUNDRYVTT_<STAGE>_LICENSE_KEY`,
674
+ `FOUNDRYVTT_CONTAINER_IMAGE`, `FOUNDRYVTT_CACHE` (a shared download-cache
675
+ mount).
676
+
677
+ **EXIT STATUS**
678
+
679
+ The command's own status is `docker`'s exit status — `process.exit(status)`
680
+ — so a Docker failure surfaces as this command's own non-zero exit. 1 if
681
+ Docker is not on `PATH`, or (for `start`/`restart`/`recreate`) if the
682
+ stage's data root is unset or does not exist.
683
+
684
+ **EXAMPLES**
685
+
686
+ ```
687
+ $ FOUNDRYVTT_DEV_DATA=/tmp/pb-docs-demo-410-data package-build container dev status
688
+ CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
689
+ ```
690
+
691
+ **SEE ALSO**
692
+
693
+ `package-build deploy <stage>`, `package-build e2e <action>`,
694
+ [Configuration](configuration.md).
695
+
696
+ ### `package-build e2e <action>`
697
+
698
+ **NAME**
699
+
700
+ Drive the repository's end-to-end suite against a served Foundry world.
701
+
702
+ **SYNOPSIS**
703
+
704
+ ```
705
+ package-build e2e seed
706
+ package-build e2e <run|open> [-- <suite args>]
707
+ package-build e2e fast [-- <suite args>]
708
+ package-build e2e sweep [-- <suite args>]
709
+ ```
710
+
711
+ **DESCRIPTION**
712
+
713
+ Stands a disposable Foundry world up against the `test` stage and drives the
714
+ repository's own end-to-end suite against it — the suite itself is never
715
+ this package's; it is named in `packageBuild.e2e.suite`. Everything after
716
+ the action is the suite's own and is passed through untouched — this
717
+ command line deliberately parses none of it (`.strict(false)`; see
718
+ [Conventions](#conventions)). `.env.local` and `.env` are loaded first.
719
+
720
+ | Action | Description |
721
+ | ------- | ---------------------------------------------------------------------------- |
722
+ | `seed` | Write the disposable world. |
723
+ | `run` | Deploy, reseed, recreate the container, wait until _active_, then run. |
724
+ | `open` | The same from-scratch path as `run`, opening the suite's interactive runner. |
725
+ | `fast` | The iteration loop: rebuild and re-run without tearing the world down. |
726
+ | `sweep` | Repeat the full run against a Foundry build the repository does not pin. |
727
+
728
+ `run`, `open` and `sweep` may change the pinned Foundry build; `sweep` exists
729
+ so `compatibility.verified` can be evidence rather than hope.
730
+
731
+ **OPTIONS**
732
+
733
+ No options of its own; every flag after the action belongs to the suite.
734
+
735
+ **Environment.** `FOUNDRYVTT_TEST_DATA` (required for every action —
736
+ end-to-end always targets the `test` stage). `FOUNDRYVTT_TEST_VERSION`,
737
+ `FOUNDRYVTT_TEST_LICENSE_KEY` for `run`/`open`/`fast`/`sweep`.
738
+
739
+ **EXIT STATUS**
740
+
741
+ `seed` — 1 if `FOUNDRYVTT_TEST_DATA` is unset, or on any other thrown error;
742
+ otherwise 0. `run`/`open`/`fast`/`sweep` — the command's own status is the
743
+ suite's own exit status, via `process.exit(status)`; 1 if
744
+ `packageBuild.e2e.suite` names no runner for the action being asked for.
745
+
746
+ **EXAMPLES**
747
+
748
+ ```
749
+ $ FOUNDRYVTT_TEST_DATA=/tmp/pb-docs-demo-410-testdata package-build e2e seed
750
+ Seeded world 'demo-package-e2e' at /tmp/pb-docs-demo-410-testdata/Data/worlds/demo-package-e2e
751
+ GM user: Gamemaster (id heroiclandsE2EGM)
752
+ password: demo-package-e2e
753
+
754
+ $ FOUNDRYVTT_TEST_DATA=/tmp/pb-docs-demo-410-testdata package-build e2e fast
755
+ package-build: This repository declares no end-to-end suite to `run`. Name one under `packageBuild.e2e.suite.run` — for example `run: [npx, cypress, run]`.
756
+ ```
757
+
758
+ **SEE ALSO**
759
+
760
+ `package-build deploy <stage>`, `package-build container <stage> <action>`,
761
+ [Configuration](configuration.md).
762
+
763
+ ---
764
+
765
+ ## `content-build`
766
+
767
+ ### `content-build package <action> [pack] [entry]`
768
+
769
+ **NAME**
770
+
771
+ Compile a content tree into Foundry compendium packs, or reverse the
772
+ process.
773
+
774
+ **SYNOPSIS**
775
+
776
+ ```
777
+ content-build package compile [pack]
778
+ content-build package unpack [pack] [entry]
779
+ content-build package clean [pack] [entry]
780
+ ```
781
+
782
+ **DESCRIPTION**
783
+
784
+ `compile` walks the content tree, writes one intermediate JSON file per
785
+ document per pack, then compiles each declared pack's JSON into LevelDB —
786
+ and refuses to compile _any_ pack when generation reported an error, so a
787
+ broken pack never ships beside packs that built cleanly. `unpack` extracts a
788
+ built LevelDB pack back to JSON, for inspecting or hand-editing an entry.
789
+ `clean` removes a pack's compiled LevelDB (and, with `entry`, a single
790
+ unpacked entry). Named without `pack`, `compile` builds every declared pack;
791
+ named with one, only that pack. Reads the content tree (`compile`) or the
792
+ staged LevelDB packs (`unpack`, `clean`); writes the intermediate JSON and
793
+ the staged LevelDB packs.
794
+
795
+ **Refused for `packageKind: documentation`**, for every action — `compile`,
796
+ `unpack` and `clean` alike. A package of that kind declares no packs by
797
+ rule, so there is nothing to compile, unpack or clean; a run that exited 0
798
+ having done nothing would be the quiet failure this toolchain refuses
799
+ everywhere else. Its site is built by `content-build site`, and its book by
800
+ `content-build pdf`.
801
+
802
+ **OPTIONS**
803
+
804
+ | Positional | Type | Default | Description |
805
+ | ---------- | ------------------------------------------- | ------------------- | ------------------------------------------------------------- |
806
+ | `action` | string, one of `compile`, `unpack`, `clean` | — | The action to perform. |
807
+ | `pack` | string | every declared pack | Name of the pack to act on. |
808
+ | `entry` | string | every entry | Name of an entry within the pack — `unpack` and `clean` only. |
809
+
810
+ **EXIT STATUS**
811
+
812
+ 1 for `packageKind: documentation`, whatever the action. 1 if generation
813
+ reports any error — a pack that compiled zero entries from a non-empty
814
+ content tree, for instance, unless the pack declares `mayBeEmpty`. This
815
+ condition applies to `compile` only; `unpack` and `clean` have no generation
816
+ step to report against. 1 on any other thrown error. Otherwise 0.
817
+
818
+ **EXAMPLES**
819
+
820
+ ```
821
+ $ content-build package compile # packageKind: documentation
822
+ […] ERROR: `packageKind: documentation` compiles no compendium, so there is nothing to compile. Build its site with `content-build site` and its book with `content-build pdf`.
823
+
824
+ $ content-build package compile
825
+ […] Content tree: 4 note(s) at /private/tmp/pb-docs-demo-410/assets/content
826
+ […] Pack items: /private/tmp/pb-docs-demo-410/assets/content → …/build/packs-json/items
827
+ […] Compiled 0 items:
828
+ […] Pack journals: /private/tmp/pb-docs-demo-410/assets/content → …/build/packs-json/journals
829
+ […] Compiled 3 journal entries (0 documentation entries)
830
+ […] Pack items: compiling to LevelDB at …/build/stage/packs/items
831
+ […] Pack journals: compiling to LevelDB at …/build/stage/packs/journals
832
+ […] Pack compilation complete.
833
+
834
+ $ content-build package unpack
835
+ […] Extracting pack items
836
+ […] Extracting pack journals
837
+ Wrote welcome.json
838
+ ```
839
+
840
+ **SEE ALSO**
841
+
842
+ `content-build site`, `content-build pdf`, `content-build addresses diff`,
843
+ [Diagnostics](diagnostics.md), [Configuration](configuration.md).
844
+
845
+ ### `content-build deps fetch`
846
+
847
+ **NAME**
848
+
849
+ Fill the caches a build resolves other packages through.
850
+
851
+ **SYNOPSIS**
852
+
853
+ ```
854
+ content-build deps fetch [--from <zip|dir>] [--id <id>]
855
+ ```
856
+
857
+ **DESCRIPTION**
858
+
859
+ The only action is `fetch`, which fills the **content index** of every
860
+ declared dependency, and the **item catalogue** of those that additionally
861
+ declare `itemCatalog: true`. Its own command rather than a step of
862
+ `package compile`, so a compile never reaches the network — a build that
863
+ downloads silently is not reproducible and hides a dependency's version
864
+ change behind a passing run. `--from` fills the cache from a locally built
865
+ artifact — a package zip, or the directory it was built from — instead of a
866
+ release, which is what makes testing a dependency change against its
867
+ consumers possible before any of it ships; `--id` names which declared
868
+ dependency `--from` supplies, needed only when the repository declares more
869
+ than one. Writes into the configured foreign-cache directory.
870
+
871
+ **OPTIONS**
872
+
873
+ | Option | Type | Default | Description |
874
+ | -------- | ------ | ------- | ---------------------------------------------------------------------------------------- |
875
+ | `--from` | string | — | Fill the cache from a locally built artifact instead of a release. |
876
+ | `--id` | string | — | Which declared dependency `--from` supplies. Only needed when more than one is declared. |
877
+
878
+ **EXIT STATUS**
879
+
880
+ 1 on any thrown error — including `--from` with no `--id` when the
881
+ repository declares anything other than exactly one dependency. Otherwise 0,
882
+ including when the repository declares no dependencies at all.
883
+
884
+ **EXAMPLES**
885
+
886
+ ```
887
+ $ content-build deps fetch
888
+ […] No relationship declares `itemCatalog: true`; nothing to fetch.
889
+ […] This package declares no dependencies.
890
+
891
+ $ content-build deps fetch --from build/dist/module.zip
892
+ […] ERROR: --from needs --id when a package declares several dependencies (declared: none)
893
+ ```
894
+
895
+ **SEE ALSO**
896
+
897
+ `content-build addresses diff`, [Configuration](configuration.md).
898
+
899
+ ### `content-build docs item-fields`
900
+
901
+ **NAME**
902
+
903
+ Render this repository's item-frontmatter reference.
904
+
905
+ **SYNOPSIS**
906
+
907
+ ```
908
+ content-build docs item-fields [--out <path>] [--check] [--title <title>]
909
+ ```
910
+
911
+ **DESCRIPTION**
912
+
913
+ Renders this repository's item-frontmatter reference from the `fields` each
914
+ `itemBuilders` entry declares, so every consuming repository documents its
915
+ own registry with the same command. The framing — where the page is filed,
916
+ its title, its preamble — comes from `docs.itemFields` in configuration;
917
+ `--out` and `--title` override it for a one-off render. `--check` compares
918
+ against the file already on disk instead of writing it, for a CI gate;
919
+ because staleness is a property of the whole generated file, no line is
920
+ named. Reads the configured `itemBuilders` registries; writes (or checks)
921
+ the destination file, or prints to stdout when none is configured and
922
+ `--out` is not given.
923
+
924
+ **OPTIONS**
925
+
926
+ | Positional | Type | Default | Description |
927
+ | ---------- | ---------------------------- | ------- | ----------------------- |
928
+ | `action` | string, one of `item-fields` | — | The document to render. |
929
+
930
+ | Option | Type | Default | Description |
931
+ | --------- | ------- | ----------------------- | ------------------------------------------------------ |
932
+ | `--out` | string | `docs.itemFields.out` | Write to this file instead of the configured location. |
933
+ | `--check` | boolean | `false` | Compare against the file already there; write nothing. |
934
+ | `--title` | string | `docs.itemFields.title` | The page's H1. |
935
+
936
+ **EXIT STATUS**
937
+
938
+ 1 if `--check` is given with no destination to compare against. 1 if
939
+ `--check` finds the file out of date. 1 on any other thrown error. Otherwise 0.
940
+
941
+ **EXAMPLES**
942
+
943
+ ```
944
+ $ content-build docs item-fields --out docs/item-fields.md --title "Demo Item Fields"
945
+ […] Wrote docs/item-fields.md
946
+
947
+ $ content-build docs item-fields --out docs/item-fields.md --title "Demo Item Fields" --check
948
+ […] docs/item-fields.md is up to date.
949
+ ```
950
+
951
+ **SEE ALSO**
952
+
953
+ [Configuration](configuration.md).
954
+
955
+ ### `content-build lint [root]`
956
+
957
+ **NAME**
958
+
959
+ Check a content tree's addresses and frontmatter without compiling it.
960
+
961
+ **SYNOPSIS**
962
+
963
+ ```
964
+ content-build lint [root] [--no-references]
965
+ ```
966
+
967
+ **DESCRIPTION**
968
+
969
+ Checks a content tree's addresses and frontmatter without compiling anything
970
+ — no LevelDB is opened and no Foundry manifest is needed, so it runs in
971
+ under a second and can gate a commit. Beyond address well-formedness, it
972
+ checks frontmatter against the declared vocabulary and each system's `sohl:`
973
+ / `hm3:` block, the package's charset (so a book can pick a font that covers
974
+ it), that an icon a note writes is one the registry declares, and that
975
+ markup inside a note's body does not smuggle in a character the charset
976
+ check cannot see. `--no-references` turns off the check that a frontmatter
977
+ shortcode reference lands, for a tree whose cross-package references it
978
+ cannot see. Reads the content tree named by `root`, defaulting to
979
+ `paths.content`; writes nothing.
980
+
981
+ **OPTIONS**
982
+
983
+ | Positional | Type | Default | Description |
984
+ | ---------- | ------ | ------------------------------ | --------------------- |
985
+ | `root` | string | the configured `paths.content` | Content tree to lint. |
986
+
987
+ | Option | Type | Default | Description |
988
+ | -------------- | ------- | ------- | --------------------------------------------------- |
989
+ | `--references` | boolean | `true` | Check that a frontmatter shortcode reference lands. |
990
+
991
+ **EXIT STATUS**
992
+
993
+ 1 if any finding is an error (a warning-only run — the schema comparison's
994
+ unemitted-field advisories, for instance — exits 0). 1 if the content index
995
+ itself could not be built for any note. Otherwise 0.
996
+
997
+ **EXAMPLES**
998
+
999
+ ```
1000
+ $ content-build lint
1001
+ […] No `itemBuilders` registry declares the vocabulary of `demo`, so that system's block is unchecked — a key inside one is discarded at compile with no warning. Declare `itemBuilders: [demo]`.
1002
+ […] No published schema for demo@1.0.0, so emitted `system` fields are unchecked. A system generates its own; a module gets one from `content-build deps fetch`.
1003
+ […] Addresses and frontmatter are well-formed (4 address(es) across 4 note(s)).
1004
+ ```
1005
+
1006
+ **SEE ALSO**
1007
+
1008
+ `content-build links [root]`, `content-build reachability <dir> [file]`,
1009
+ `content-build content-format schema`, `content-build content-format fields`,
1010
+ `content-build content-format notes`, [Diagnostics](diagnostics.md),
1011
+ [Configuration](configuration.md).
1012
+
1013
+ `content-build content-format <action>` checks `docs/content-format.md` —
1014
+ the content-format specification this package ships — against reality, in
1015
+ three ways that fail for different reasons at different times: `schema`,
1016
+ `fields` and `notes`, one section below per action. All three read the
1017
+ specification named by `--spec`, defaulting to the one this package ships,
1018
+ and write nothing.
1019
+
1020
+ ### `content-build content-format schema`
1021
+
1022
+ **NAME**
1023
+
1024
+ Compare the content-format specification against a published schema.
1025
+
1026
+ **SYNOPSIS**
1027
+
1028
+ ```
1029
+ content-build content-format schema --schema <system>=<path> [--schema <system>=<path> ...] [--spec <path>]
1030
+ ```
1031
+
1032
+ **DESCRIPTION**
1033
+
1034
+ Compares every `system.*` target the specification names against a
1035
+ published `schema.json` (see `package-build schema`). A schema a system has
1036
+ not yet published — HM3 today — is reported as unchecked rather than
1037
+ skipped in silence, because a check that quietly does nothing reads exactly
1038
+ like one that passed.
1039
+
1040
+ **OPTIONS**
1041
+
1042
+ | Option | Type | Default | Description |
1043
+ | ---------- | ---------------------------- | ------------------------------------ | ----------------------------------------- |
1044
+ | `--spec` | string | the shipped `docs/content-format.md` | The specification to read. |
1045
+ | `--schema` | string, repeatable, required | — | A published schema, as `<system>=<path>`. |
1046
+
1047
+ **EXIT STATUS**
1048
+
1049
+ 1 if any checked claim names a field no supplied schema declares, or if a
1050
+ `--schema` entry is not `<system>=<path>`. Otherwise 0.
1051
+
1052
+ **EXAMPLES**
1053
+
1054
+ ```
1055
+ $ content-build content-format schema --schema sohl=schema.json
1056
+ […] 19 claim(s) about hm3 are unchecked — no schema was supplied for it, so nothing here confirms them.
1057
+ […] 76 mapping claim(s) confirmed against the supplied schemas.
1058
+ ```
1059
+
1060
+ **SEE ALSO**
1061
+
1062
+ `content-build content-format fields`, `content-build content-format notes`,
1063
+ `package-build schema`, [Configuration](configuration.md).
1064
+
1065
+ ### `content-build content-format fields`
1066
+
1067
+ **NAME**
1068
+
1069
+ Compare the specification's field tables against the compiled item
1070
+ declarations.
1071
+
1072
+ **SYNOPSIS**
1073
+
1074
+ ```
1075
+ content-build content-format fields [--fields <system>] [--coverage] [--spec <path>]
1076
+ ```
1077
+
1078
+ **DESCRIPTION**
1079
+
1080
+ Compares the specification's per-type field tables against the
1081
+ `itemBuilders` field declarations that actually compile those types —
1082
+ checked, not merged, because the specification's vocabulary spans note
1083
+ types (Scenes, Macros, JournalEntries) no item registry covers. A type only
1084
+ one side describes is named as out of reach rather than silently skipped.
1085
+ `--fields` reads one of this package's own shipped declaration sets
1086
+ (`sohl`, `hm3`) instead of the consuming repository's configured
1087
+ `itemBuilders` — this repository ships the specification and the SoHL
1088
+ declarations together and configures no content tree of its own, so a
1089
+ consumer's arrangement would not apply here. `--coverage` lists, per type,
1090
+ the fields only one side names — advisory, not findings, since the two
1091
+ vocabularies differ by design until a note's data fully moves under `data:`.
1092
+
1093
+ **OPTIONS**
1094
+
1095
+ | Option | Type | Default | Description |
1096
+ | ------------ | ---------------------------- | --------------------------------------------- | --------------------------------------------------- |
1097
+ | `--spec` | string | the shipped `docs/content-format.md` | The specification to read. |
1098
+ | `--fields` | string, one of `sohl`, `hm3` | the consuming repository's own `itemBuilders` | A declaration set this package ships, by system id. |
1099
+ | `--coverage` | boolean | `false` | List, per type, the fields only one side names. |
1100
+
1101
+ **EXIT STATUS**
1102
+
1103
+ 1 if any compared field pair disagrees between the specification and the
1104
+ declaration that compiles it. Otherwise 0.
1105
+
1106
+ **EXAMPLES**
1107
+
1108
+ ```
1109
+ $ content-build content-format fields --fields sohl --coverage
1110
+ […] 13 type(s) compared against sohl's declarations (56 field pair(s)).
1111
+ […] 12 type(s) the format declares are out of reach — no `itemBuilders` entry covers them: being, homepage, vehicle, armorlocation, lore, map, place, scenario, doc, macro, bundle, folder.
1112
+ […] affiliation: format only [commonSkills, demonym, economy, epithet, governance, lore, population, symbol, templatePriority], declaration only [level, office, society, subType, title]
1113
+ ```
1114
+
1115
+ **SEE ALSO**
1116
+
1117
+ `content-build content-format schema`, `content-build content-format notes`,
1118
+ [Configuration](configuration.md).
1119
+
1120
+ ### `content-build content-format notes`
1121
+
1122
+ **NAME**
1123
+
1124
+ Measure a content tree against the specification's vocabulary.
1125
+
1126
+ **SYNOPSIS**
1127
+
1128
+ ```
1129
+ content-build content-format notes [root] [--strict] [--spec <path>]
1130
+ ```
1131
+
1132
+ **DESCRIPTION**
1133
+
1134
+ Measures a content tree against the vocabulary the specification declares
1135
+ per type. A report by default rather than a gate — every authored note
1136
+ predates the specification, so a failing check would be red from the day it
1137
+ landed and stay red for the length of a migration nobody could act on;
1138
+ `--strict` raises its findings to errors, turned on slice by slice as each
1139
+ class of finding reaches zero.
1140
+
1141
+ **OPTIONS**
1142
+
1143
+ | Positional | Type | Default | Description |
1144
+ | ---------- | ------ | ------------------------------ | ------------------------ |
1145
+ | `root` | string | the configured `paths.content` | Content tree to measure. |
1146
+
1147
+ | Option | Type | Default | Description |
1148
+ | ---------- | ------- | ------------------------------------ | ---------------------------------------------------- |
1149
+ | `--spec` | string | the shipped `docs/content-format.md` | The specification to read. |
1150
+ | `--strict` | boolean | `false` | Fail on the findings instead of only reporting them. |
1151
+
1152
+ **EXIT STATUS**
1153
+
1154
+ 1 if the content index could not be built for any note. Without `--strict`,
1155
+ findings never fail the run. With `--strict`, 1 if there are any findings.
1156
+ Otherwise 0.
1157
+
1158
+ **EXAMPLES**
1159
+
1160
+ ```
1161
+ $ content-build content-format notes
1162
+ […] 0 finding(s) across 4 note(s) measured against docs/content-format.md.
1163
+ ```
1164
+
1165
+ **SEE ALSO**
1166
+
1167
+ `content-build content-format schema`, `content-build content-format fields`,
1168
+ `content-build lint [root]`, [Diagnostics](diagnostics.md),
1169
+ [Configuration](configuration.md).
1170
+
1171
+ ### `content-build links [root]`
1172
+
1173
+ **NAME**
1174
+
1175
+ Check that every link in a content tree lands.
1176
+
1177
+ **SYNOPSIS**
1178
+
1179
+ ```
1180
+ content-build links [root]
1181
+ ```
1182
+
1183
+ **DESCRIPTION**
1184
+
1185
+ Checks that every link in a content tree lands: a dead `#anchor`, a dead
1186
+ qualified address, a link with no label (the bare `[[Name]]` alias form is
1187
+ retired — every link is an address), a wikilink authored in frontmatter
1188
+ (which is data and is never resolved), and a package homepage's markdown
1189
+ links and `landing:` addresses, which are published verbatim and use no
1190
+ wikilink at all. Also reports a vendored foreign-package content index that
1191
+ has drifted out of reach, naming `content-build deps fetch` as the fix.
1192
+ Reads the content tree named by `root`, defaulting to `paths.content`, and
1193
+ the cached content indexes of any declared dependency; writes nothing.
1194
+
1195
+ **OPTIONS**
1196
+
1197
+ | Positional | Type | Default | Description |
1198
+ | ---------- | ------ | ------------------------------ | ---------------------- |
1199
+ | `root` | string | the configured `paths.content` | Content tree to check. |
1200
+
1201
+ **EXIT STATUS**
1202
+
1203
+ 1 if the content index could not be built for any note. 1 if a dependency's
1204
+ cached content index is stale or unaddressable. 1 if any link problem is
1205
+ found. Otherwise 0.
1206
+
1207
+ **EXAMPLES**
1208
+
1209
+ ```
1210
+ $ content-build links
1211
+ […] 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.
1212
+ ```
1213
+
1214
+ **SEE ALSO**
1215
+
1216
+ `content-build lint [root]`, `content-build reachability <dir> [file]`,
1217
+ `content-build deps fetch`, [Diagnostics](diagnostics.md),
1218
+ [Configuration](configuration.md).
1219
+
1220
+ ### `content-build format [paths..]`
1221
+
1222
+ **NAME**
1223
+
1224
+ Check formatting with the shared Prettier configuration.
1225
+
1226
+ **SYNOPSIS**
1227
+
1228
+ ```
1229
+ content-build format [paths..] [--write]
1230
+ ```
1231
+
1232
+ **DESCRIPTION**
1233
+
1234
+ Checks formatting with the shared Prettier configuration, over the whole
1235
+ repository rather than only the content tree — a repository that has not
1236
+ configured this package at all must still be able to format itself. A
1237
+ consumer's own Prettier configuration wins wherever it declares one; only
1238
+ its `.prettierignore` excludes a path. Every run first reports, as
1239
+ warnings, any shared convention the repository's own configuration resolves
1240
+ differently (or declares none of at all) — nothing there fails the run,
1241
+ because a deliberate local choice is allowed to win; the point is only that
1242
+ it is visible, rather than silently guaranteeing an editor and a bare `npx
1243
+ prettier` disagree with this command. `--write` rewrites unformatted files
1244
+ in place instead of reporting them; `--check` (the default) only reports.
1245
+ Reads every matched file; `--write` rewrites them.
1246
+
1247
+ **OPTIONS**
1248
+
1249
+ | Positional | Type | Default | Description |
1250
+ | ---------- | --------- | -------------------- | ------------------------------ |
1251
+ | `paths` | string(s) | the whole repository | Files or directories to check. |
1252
+
1253
+ | Option | Type | Default | Description |
1254
+ | --------- | ------- | ------------------- | ------------------------------------------------------------------------------------------------ |
1255
+ | `--write` | boolean | `false` | Rewrite unformatted files in place instead of reporting them. |
1256
+ | `--check` | boolean | (default behaviour) | Report unformatted files without rewriting them. Naming both `--check` and `--write` is refused. |
1257
+
1258
+ **EXIT STATUS**
1259
+
1260
+ 1 if `--check` and `--write` are both named. 1 if any file is unformatted
1261
+ (`--check` mode) or could not be formatted at all (`--write` mode).
1262
+ Otherwise 0.
1263
+
1264
+ **EXAMPLES**
1265
+
1266
+ ```
1267
+ $ content-build format
1268
+ 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";
1269
+ build/dist/module.json: error: is not formatted; run `content-build format --write` to fix it
1270
+ […]
1271
+ […] ERROR: 10 of 15 file(s) are not formatted.
1272
+
1273
+ $ content-build format --write
1274
+ […] Formatted 10 of 15 file(s).
1275
+
1276
+ $ content-build format
1277
+ […] Formatting is clean (15 file(s)).
1278
+ ```
1279
+
1280
+ **SEE ALSO**
1281
+
1282
+ `content-build markdown [paths..]`, [Diagnostics](diagnostics.md).
1283
+
1284
+ ### `content-build markdown [paths..]`
1285
+
1286
+ **NAME**
1287
+
1288
+ Lint markdown with the shared markdownlint rule set.
1289
+
1290
+ **SYNOPSIS**
1291
+
1292
+ ```
1293
+ content-build markdown [paths..] [--fix]
1294
+ ```
1295
+
1296
+ **DESCRIPTION**
1297
+
1298
+ Lints markdown with the shared markdownlint rule set — the structural
1299
+ checks Prettier cannot make: a heading level that skips, two sibling
1300
+ headings sharing an anchor, a reversed link, an emphasis marker other than
1301
+ the one these repositories write. Runs over the repository rather than the
1302
+ content tree, taking its rules from this package unless the consumer
1303
+ declares its own. `--fix` applies the fixes markdownlint can make. Reads
1304
+ every matched file; `--fix` rewrites them.
1305
+
1306
+ **OPTIONS**
1307
+
1308
+ | Positional | Type | Default | Description |
1309
+ | ---------- | --------- | ------------------------------------- | -------------- |
1310
+ | `paths` | string(s) | every markdown file in the repository | Globs to lint. |
1311
+
1312
+ | Option | Type | Default | Description |
1313
+ | ------- | ------- | ------- | -------------------------------------- |
1314
+ | `--fix` | boolean | `false` | Apply the fixes markdownlint can make. |
1315
+
1316
+ **EXIT STATUS**
1317
+
1318
+ 1 if any finding remains after fixing (or without `--fix`, if any finding
1319
+ exists at all). Otherwise 0.
1320
+
1321
+ **EXAMPLES**
1322
+
1323
+ ```
1324
+ $ content-build markdown
1325
+ […] Markdown is clean.
1326
+ ```
1327
+
1328
+ **SEE ALSO**
1329
+
1330
+ `content-build format [paths..]`, [Diagnostics](diagnostics.md).
1331
+
1332
+ ### `content-build content-index [root]`
1333
+
1334
+ **NAME**
1335
+
1336
+ Emit this package's note index as JSON Lines.
1337
+
1338
+ **SYNOPSIS**
1339
+
1340
+ ```
1341
+ content-build content-index [root] [--out <dir>]
1342
+ ```
1343
+
1344
+ **DESCRIPTION**
1345
+
1346
+ Emits this package's note index as JSON Lines — one record per note,
1347
+ carrying its full frontmatter plus its place in the tree. Every build
1348
+ already walks the tree and parses every note's frontmatter and then throws
1349
+ the result away; this publishes that walk so anything outside a build can
1350
+ query the content without paying for a second walk. Its own command rather
1351
+ than only a build step, because the point is that anyone can regenerate it
1352
+ at will — the artifact costs a frontmatter parse, not a build, which is
1353
+ also what lets it stay uncommitted. Reads the content tree named by `root`,
1354
+ defaulting to `paths.content`; writes `<contentPackage>-metadata.jsonl`
1355
+ into `--out`, defaulting to the configured `paths.contentIndex`.
1356
+
1357
+ **OPTIONS**
1358
+
1359
+ | Positional | Type | Default | Description |
1360
+ | ---------- | ------ | ------------------------------ | --------------------- |
1361
+ | `root` | string | the configured `paths.content` | Content tree to read. |
1362
+
1363
+ | Option | Type | Default | Description |
1364
+ | ------- | ------ | ----------------------------------- | ------------------------ |
1365
+ | `--out` | string | the configured `paths.contentIndex` | Directory to write into. |
1366
+
1367
+ **EXIT STATUS**
1368
+
1369
+ 1 on any thrown error. Otherwise 0.
1370
+
1371
+ **EXAMPLES**
1372
+
1373
+ ```
1374
+ $ content-build content-index
1375
+ […] demo → build/content-index/demo-metadata.jsonl (4 notes, 2 KiB)
1376
+ ```
1377
+
1378
+ **SEE ALSO**
1379
+
1380
+ `content-build site`, `content-build pdf`, `content-build deps fetch`,
1381
+ [Configuration](configuration.md).
1382
+
1383
+ ### `content-build site`
1384
+
1385
+ **NAME**
1386
+
1387
+ Publish the content tree as a Hugo content mount.
1388
+
1389
+ **SYNOPSIS**
1390
+
1391
+ ```
1392
+ content-build site [--out <dir>]
1393
+ ```
1394
+
1395
+ **DESCRIPTION**
1396
+
1397
+ Publishes the content tree as a Hugo content mount — the sibling of
1398
+ `package compile`: the same tree, rendered as pages instead of compiled
1399
+ into packs. Everything a consumer would otherwise write for itself happens
1400
+ here: the walk, address derivation, the address index, table expansion,
1401
+ wikilink resolution, code-fence protection, the foreign-manifest merge and
1402
+ the section-landing backfill. Every gate is checked and reported, and the
1403
+ run stops at the first that fires, ordered so the report names the cause
1404
+ rather than its symptoms — an unusable dependency manifest, reported after
1405
+ the links that failed because of it, would otherwise read as a pile of
1406
+ broken notes. Reads the content tree named by `paths.content`; writes into
1407
+ `--out`, defaulting to the configured `site.out`, which is wiped on every
1408
+ run.
1409
+
1410
+ **OPTIONS**
1411
+
1412
+ | Option | Type | Default | Description |
1413
+ | ------- | ------ | ------------------------- | -------------------------------------------------------- |
1414
+ | `--out` | string | the configured `site.out` | Write the mount here instead of the configured location. |
1415
+
1416
+ **EXIT STATUS**
1417
+
1418
+ 1 if `site.out` is unset and `--out` is not given (an unset output would
1419
+ resolve to the repository root, which this command refuses to wipe). 1 if
1420
+ any gate fires — no homepage or two competing for it, a frontmatter
1421
+ wikilink, an address that cannot be derived, a stale or unaddressable
1422
+ dependency manifest, an address published twice, a table that failed to
1423
+ expand, or a dead wikilink. Otherwise 0.
1424
+
1425
+ **EXAMPLES**
1426
+
1427
+ ```
1428
+ $ content-build site
1429
+ […] wrote 1 homepage(s) + 1 content page(s) + 0 tree page(s) + 0 landing(s) to build/site
1430
+ ```
1431
+
1432
+ **SEE ALSO**
1433
+
1434
+ `content-build package <action> [pack] [entry]`, `content-build pdf`, `content-build content-index [root]`,
1435
+ [Diagnostics](diagnostics.md), [Configuration](configuration.md).
1436
+
1437
+ ### `content-build pdf`
1438
+
1439
+ **NAME**
1440
+
1441
+ Build the book the content tree publishes as.
1442
+
1443
+ **SYNOPSIS**
1444
+
1445
+ ```
1446
+ content-build pdf [--out <path>] [--book-version <version>] [--compile]
1447
+ ```
1448
+
1449
+ **DESCRIPTION**
1450
+
1451
+ Builds the book the content tree publishes as — the third surface beside
1452
+ `package compile` and `site`, reporting in the same `file:line:column:
1453
+ severity: message` shape both of those use. **Not building a book is a
1454
+ normal outcome and exits 0**: a package publishing only a homepage, one
1455
+ with no `pdf:` block, and one with no content tree have each said they
1456
+ publish no book, and failing the command over that would break the release
1457
+ of every package that is not a book. `--compile` (on by default) runs the
1458
+ Typst compiler; `--no-compile` emits the Typst source and stops there,
1459
+ which is what lets the source be inspected or compiled by hand. Reads the
1460
+ content tree named by `pdf.document` and `paths.content`; writes the
1461
+ `.typ` source and, unless `--no-compile`, the `.pdf`, to `--out` or the
1462
+ configured `pdf.out`.
1463
+
1464
+ **OPTIONS**
1465
+
1466
+ | Option | Type | Default | Description |
1467
+ | ---------------- | ------- | ------------------------ | ------------------------------------------------------------------ |
1468
+ | `--out` | string | the configured `pdf.out` | Write the book here instead. |
1469
+ | `--book-version` | string | — | Stamp this version on the title page and in the file name. |
1470
+ | `--compile` | boolean | `true` | Run the Typst compiler. `--no-compile` emits the source and stops. |
1471
+
1472
+ **EXIT STATUS**
1473
+
1474
+ 0 when there is a stated reason not to build (`publish.site` is
1475
+ `homepage`, no `pdf:` block, no content tree). 1 if the document tree
1476
+ `pdf.document` names cannot be read or parsed, or on any other thrown
1477
+ error. Otherwise 0 — findings inside a book that did build (a filter that
1478
+ matched nothing, for instance) are reported but never fail the command.
1479
+
1480
+ **EXAMPLES**
1481
+
1482
+ ```
1483
+ $ content-build pdf
1484
+ […] No book built: no `pdf:` block is configured, so this package publishes no book
1485
+
1486
+ $ content-build pdf --no-compile
1487
+ demo-book: error: the document tree named by `pdf.document` cannot be read
1488
+ ```
1489
+
1490
+ **SEE ALSO**
1491
+
1492
+ `content-build site`, `content-build package <action> [pack] [entry]`,
1493
+ [Diagnostics](diagnostics.md), [Configuration](configuration.md).
1494
+
1495
+ ### `content-build reachability <dir> [file]`
1496
+
1497
+ **NAME**
1498
+
1499
+ Check that every document in a corpus is reachable by reading.
1500
+
1501
+ **SYNOPSIS**
1502
+
1503
+ ```
1504
+ content-build reachability <dir> [file] [--index <shortcode> ...] [--root <path>]
1505
+ ```
1506
+
1507
+ **DESCRIPTION**
1508
+
1509
+ Checks that every document in a corpus is reachable by reading — a corpus
1510
+ is a book, not a pile of notes, so every page in it must be linked from the
1511
+ chapter or section that owns it. The corpus is named on the command line
1512
+ because it never changes for a given repository, so a consumer hardcodes
1513
+ the invocation in `package.json` and gets the check with no script of its
1514
+ own. `--index` names a page (by shortcode) that is walked _to_ but not
1515
+ _through_ — an index links to nearly everything it covers, so walking one
1516
+ would make the check vacuous. Reads the content tree named by `--root`,
1517
+ defaulting to `paths.content`; writes nothing.
1518
+
1519
+ **OPTIONS**
1520
+
1521
+ | Positional | Type | Default | Description |
1522
+ | ---------- | ------ | ----------- | -------------------------------------------------------- |
1523
+ | `dir` | string | — | The corpus directory, relative to the content tree root. |
1524
+ | `file` | string | `README.md` | The corpus's entry page within that directory. |
1525
+
1526
+ | Option | Type | Default | Description |
1527
+ | --------- | ------------------ | ------------------------------ | ---------------------------------------------- |
1528
+ | `--index` | string, repeatable | `[]` | Shortcode of a page walked to but not through. |
1529
+ | `--root` | string | the configured `paths.content` | Content tree to read. |
1530
+
1531
+ **EXIT STATUS**
1532
+
1533
+ 1 if the content index could not be built for any note. 1 if any document
1534
+ in the corpus is unreachable from the entry page. Otherwise 0.
1535
+
1536
+ **EXAMPLES**
1537
+
1538
+ ```
1539
+ $ content-build reachability Guide
1540
+ […] All 2 document(s) in Guide are reachable from README.md.
1541
+
1542
+ $ content-build reachability assets/content
1543
+ […] ERROR: no note at assets/content/README.md, so the corpus has no page to be read from
1544
+ ```
1545
+
1546
+ **SEE ALSO**
1547
+
1548
+ `content-build links [root]`, `content-build lint [root]`, [Diagnostics](diagnostics.md),
1549
+ [Configuration](configuration.md).
1550
+
1551
+ ### `content-build addresses diff`
1552
+
1553
+ **NAME**
1554
+
1555
+ Report every published address a build no longer publishes.
1556
+
1557
+ **SYNOPSIS**
1558
+
1559
+ ```
1560
+ content-build addresses diff --from <zip|dir> [--strict]
1561
+ ```
1562
+
1563
+ **DESCRIPTION**
1564
+
1565
+ Reports every published `(type, shortcode)` address this build no longer
1566
+ publishes, against a released artifact — the signal that a shortcode
1567
+ rename or a withdrawn note used to cost nothing and now does, emitted in
1568
+ the repository doing the renaming while the change is still in front of
1569
+ its author. Its own command rather than a step of `package compile`,
1570
+ because it reads a _second_ artifact the compile knows nothing about and
1571
+ asks a question about a release, not about a build — a repository between
1572
+ releases has nothing to compare against. `--from` names the baseline
1573
+ explicitly — a release's `.zip`, or the directory built from one — never
1574
+ derived and never downloaded, for the same reason `deps fetch --from` is
1575
+ explicit: a command that reaches the network on its own is not
1576
+ reproducible. A finding is placed against the tree, at the note that made
1577
+ the rename, not against the compiled output it was read from. Reads the
1578
+ baseline artifact and the current content tree; writes nothing.
1579
+
1580
+ **OPTIONS**
1581
+
1582
+ | Positional | Type | Default | Description |
1583
+ | ---------- | --------------------- | ------- | ---------------------- |
1584
+ | `action` | string, one of `diff` | — | The action to perform. |
1585
+
1586
+ | Option | Type | Default | Description |
1587
+ | ---------- | ------- | ----------------------- | --------------------------------------------------------------------------------------------- |
1588
+ | `--from` | string | — (required for `diff`) | The released artifact to compare against — a package zip, or the directory it was built from. |
1589
+ | `--strict` | boolean | `false` | Report findings as errors and exit non-zero, for a release workflow that gates on them. |
1590
+
1591
+ **EXIT STATUS**
1592
+
1593
+ 1 if `diff` is named with no `--from`. Without `--strict`, a renamed or
1594
+ withdrawn address is reported as a warning and does not fail the run. With
1595
+ `--strict`, 1 if any address is no longer published. 1 on any other thrown
1596
+ error (the baseline declaring an Item pack this build does not have, or
1597
+ this repository declaring no Item pack at all to diff). Otherwise 0.
1598
+
1599
+ **EXAMPLES**
1600
+
1601
+ ```
1602
+ $ content-build addresses diff --from build/dist/module.zip
1603
+ […] ERROR: demo-package@1.0.0: pack "items" is declared at packs/items, which the package does not contain
1604
+ ```
1605
+
1606
+ **SEE ALSO**
1607
+
1608
+ `content-build package <action> [pack] [entry]`, `content-build deps fetch`,
1609
+ [Diagnostics](diagnostics.md).