@heroiclands/package-build 11.1.0 → 14.0.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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,365 @@
1
1
  # @heroiclands/package-build
2
2
 
3
+ ## 14.0.0
4
+
5
+ ### Major Changes
6
+
7
+ - a0a113b: **`publish.address.landing` is deleted.** `prefix` is the whole address scheme:
8
+
9
+ ```yaml
10
+ publish:
11
+ address:
12
+ prefix: kb/ # default: "" — the package root
13
+ ```
14
+
15
+ The key named which note addressed a whole section rather than a page within
16
+ one. #202 retired the second of its two rules and #204 retired the concept both
17
+ rules chose between — a section is a Hugo content directory the note format does
18
+ not carry, a page's address names no directory, and so no note lands one. What
19
+ survived was the key itself: resolved, refused-by-name for the retired value,
20
+ checked against a one-element vocabulary, frozen into the configuration, and
21
+ read by nobody. `LANDING_RULES` said so in its own doc comment — _"Inert since
22
+ #204."_
23
+
24
+ **A configuration still declaring it is refused, at the line it is written on.**
25
+ Not reported as an unrecognized option, which names a spelling to correct and
26
+ leaves the author to work out that the mechanism is gone:
27
+
28
+ ```text
29
+ package-build.config.yaml:14:9: error: package-build config:
30
+ `publish.address.landing` is a retired option — delete it. It named which note
31
+ addressed a whole section rather than a page within one, and there are no
32
+ sections to address: a section is a Hugo content directory the note format does
33
+ not carry, so no note lands one and every page is addressed
34
+ `<type>-<shortcode>`. Nothing replaces it.
35
+ ```
36
+
37
+ Presence is the whole test, as it is for a retired frontmatter field: no value
38
+ makes declaring it right, so `readme` and the already-retired `collection` are
39
+ refused alike, by `RETIRED_ADDRESS_KEYS` — the configuration-side twin of
40
+ `engine/retired-fields.mjs`.
41
+
42
+ **Why the key outlived its mechanism by one release.** `content-config.mjs` has
43
+ no warning channel — every finding goes through `fail()`, which throws — so
44
+ while both publishing consumers still declared the then-true `landing: readme`
45
+ the only options were to break them over a correct statement or to accept the
46
+ key in silence, and silent acceptance is what this codebase refuses everywhere
47
+ else. So it took the three steps `package:` took (#56): retire the value, have
48
+ consumers drop the key, delete the key. No consumer declares it now.
49
+
50
+ **The plumbing goes with it.** `packageAddress` took an address scheme only to
51
+ validate the `landing` rule it then discarded — its own docstring already said
52
+ the `prefix` half never applied, because an address is `(type, shortcode)`, a
53
+ package-wide identity that takes no mount. It is now a function of the
54
+ frontmatter and nothing else, and `manifestContext` no longer carries a `scheme`
55
+ that nothing reads.
56
+
57
+ | Removed | Where |
58
+ | ------------------------------------------------------------------------------------------------------------------------------------ | ---------------------------- |
59
+ | `LANDING_RULES`, `RETIRED_LANDING_RULES` | `content-config.mjs` |
60
+ | `DEFAULT_ADDRESS_SCHEME.landing`, `ADDRESS_KEYS`' second entry, `normalizePublish`'s resolve-and-check, `AddressSchemeInput.landing` | `content-config.mjs` |
61
+ | The `LANDING_RULES` re-export, `packageAddress`'s `{ scheme }` parameter and its landing check | `engine/content-address.mjs` |
62
+ | `manifestContext`'s `scheme`, and the argument `collectManifestEntries` passed on with it | `engine/manifest-emit.mjs` |
63
+
64
+ **Nothing a consumer emits moves.** Verified against pristine
65
+ `git archive origin/main` extractions of all three consumers, before and after:
66
+ `lint`, `links`, `manifest`, `package compile` and `site` produce **identical
67
+ console output, line for line**, and every emitted file is byte-identical —
68
+ 31,197 files across the three trees, with only LevelDB's own timestamped `LOG`
69
+ differing. `sohl` stays green (2,988 manifest entries, 3,125 pack documents,
70
+ 1,671 emitted pages); `sohl-thalorna` stays exactly as red as it was for its own
71
+ content gap (1,983 lint findings, 122 link findings); `sohl-kethira-basic` stays
72
+ green.
73
+
74
+ Closes #215
75
+ - 98ac362: **`subType: user-guide` is refused.** #206 renamed the `doc` subType
76
+ `user-guide` to `userguide` and held every `type` and `subType` to the address
77
+ charset, but shipped a **transitional acceptance** for the old spelling — a
78
+ warning naming the replacement rather than a refusal — because 43 `sohl` notes
79
+ authored it and no consumer can sweep ahead of the release that renames a value.
80
+ Every consumer tree has now swept: `sohl` **0**, `sohl-thalorna` **0**,
81
+ `sohl-kethira-basic` **0**, counted on a pristine extraction of each
82
+ `origin/main`. So the acceptance guards nothing, and this is the follow-up #207
83
+ named.
84
+
85
+ `RETIRED_SUBTYPES`, `retiredSubType()` and `retiredSubTypeMessage()` are gone
86
+ from `engine/note-vocabulary.mjs`, along with the retired-spelling branch that
87
+ ran ahead of the charset check in `checkSubType`. Nothing replaces them:
88
+ `user-guide` now falls through to the **charset** check and is refused as an
89
+ error, for the reason that always applied — it contains a hyphen. That is why
90
+ the acceptance could be deleted rather than promoted to an error: the permanent
91
+ rule already covers the case, so no retirement-specific code outlived the sweep.
92
+
93
+ **Breaking**, though the diff only removes code. A spelling that built at exit 0
94
+ one release ago now fails the build, and three exported symbols no longer exist.
95
+ A tree that has swept sees no change at all — which all three consumers have,
96
+ and each was verified unaffected.
97
+
98
+ **The subType charset diagnostic is reworded.** It justified the rule by "the
99
+ hyphen separates the segments of an address", true of a `subType` when #206
100
+ shipped — `sectionOf` returned a `doc`'s subType, so the value was a URL path
101
+ segment — and not true since #204 retired sections. The rule stands on its own
102
+ footing instead: a subType is a vocabulary term the whole toolchain keys on, one
103
+ closed set away from being an address segment again, and a charset holding for a
104
+ type, a shortcode and a `contentPackage` but not for a subType would be a rule
105
+ nobody could state in a sentence. `typeCharsetMessage` is untouched — a type
106
+ genuinely is the first segment of every address.
107
+
108
+ The same correction is applied to `assertVocabularyCharset`'s throw, which
109
+ carried the identical claim in a second place — _"A type and a subType are both
110
+ address segments"_ — where it would go unread until it fires, which is exactly
111
+ when it would be taken at face value. It now states the reason **per key**: the
112
+ address half for a type, the vocabulary-term half for a subType. The guard
113
+ itself is unchanged and stays where it is, running over `NOTE_VOCABULARY` as the
114
+ module loads.
115
+
116
+ Closes #210
117
+
118
+ ### Minor Changes
119
+
120
+ - 45b5bd0: **A section can say what it lists.** `site.sections` / `site.readmeSections`
121
+ take two more keys, `listType` and `listSubType`, and both reach the generated
122
+ `_index.md`:
123
+
124
+ ```yaml
125
+ sections:
126
+ weapongear: { title: Weapons, listType: weapongear }
127
+ user-guide: { title: User Guide, listType: doc, listSubType: userguide }
128
+ ```
129
+
130
+ Since #204 a content page is written flat under the mount, so a declared
131
+ section's directory holds nothing but the landing this build writes for it and a
132
+ layout reading Hugo's `.Pages` finds no members. The membership survives in the
133
+ `site.sections` map and in nothing a theme can read — not on the page, not on
134
+ the landing, not in any URL — so every section landing served by a generic list
135
+ layout renders empty. `sohl` was unaffected only because its eleven catalog
136
+ layouts already query `site.RegularPages` by `Params.type`; a consumer rendering
137
+ through the shared theme has no layout of its own to edit. The landing now
138
+ states that query and the theme runs it
139
+ (HeroicLands/heroiclands-hugo-theme#50).
140
+
141
+ **Two keys of their own, not `type` / `subType`.** On an `_index.md`, `type` is
142
+ Hugo's own layout selector: verified against Hugo 0.165, a section landing
143
+ carrying `type: doc` renders through `layouts/doc/list.html` rather than the
144
+ default list template — behaviour this build already relies on deliberately, for
145
+ the mount's own `landing`. Spelling the content type there would silently change
146
+ which template serves the landing.
147
+
148
+ **Two keys added to the closed set, not an open passthrough.** `site.landing` is
149
+ passed through unvalidated because it is written once, for the mount, in one
150
+ landing template's own vocabulary; a section entry is written fourteen to twenty
151
+ times per build against a contract every package and every section shares.
152
+ Unbounded there, a mistyped `listTpye:` would publish into front matter, list
153
+ nothing, and report no error — which is the bug being fixed, moved one step
154
+ downstream where no build can see it. `normalizeSectionMeta` stays the one place
155
+ the vocabulary is bounded, and the writers still name no keys.
156
+
157
+ **Both values are checked, because both ways of writing an inert declaration are
158
+ silent.** They name a content type and subType, so each must be an address
159
+ segment (`^[A-Za-z0-9]+$`), and a `listSubType` with no `listType` is refused —
160
+ a subType tells pages apart only within a type, so alone it names no query. The
161
+ charset check is the trap this came from: a section is named for a URL the site
162
+ chose and need not match the address (`/sohl/kb/user-guide/` is the section,
163
+ `userguide` the subType, #207), and copying the section's name in would match no
164
+ page at exit 0. All three refusals are located at the offending key:
165
+
166
+ ```text
167
+ package-build.config.yaml:504:90: error: package-build config:
168
+ `site.sections.user-guide.listSubType` is `user-guide`, which is not
169
+ alphanumeric. …
170
+ ```
171
+
172
+ **Additive.** A section that declares neither key emits exactly the bytes it
173
+ did before. Verified on a pristine `origin/main` extraction of `sohl`, the only
174
+ consumer running `content-build site` with declared sections: 1670 emitted files
175
+ byte-identical, and with the keys declared on two of its nineteen sections
176
+ exactly those two `_index.md` files change.
177
+
178
+ Closes #212
179
+
180
+ ## 13.0.0
181
+
182
+ ### Major Changes
183
+
184
+ - 0c2def0: A section is a Hugo directory concept, and the note format no longer carries one
185
+ (#204). Content pages emit **flat** under the mount, named by their address; the
186
+ `README.md` landing convention, `sectionOf`, the `<section>/` output routing, and
187
+ the "no section, so nowhere to file the page" refusal are all gone.
188
+
189
+ **Why it goes.** Since #181 a page's URL _is_ its address,
190
+ `/<package>/<type>-<shortcode>/`, so a section appears in **no address at all**.
191
+ Its only remaining job was choosing the directory a page was written into, and
192
+ the only reason that mattered was Hugo's rule about what counts as a section —
193
+ `writeSectionLandings`'s own docstring said both of its jobs "exist because of
194
+ how Hugo decides what a section is". So the note format carried a filename
195
+ convention, a landing rule, a routing function and a refusal in order to satisfy
196
+ a rendering engine's directory semantics. #197 was the bill for that: `subType`
197
+ did double duty — a genre for an ordinary `doc`, a URL section for a `README` —
198
+ the two vocabularies collided, and it took #198, #200, #201 and a release to
199
+ settle by widening one of them rather than by removing the overload.
200
+
201
+ **A page that introduces the notes of a type is now an ordinary note**, named by
202
+ convention and with no build path of its own: `type: doc`, `subType: reference`,
203
+ `shortcode: <type>`, addressed `doc-<type>`. The package's own front page already
204
+ worked this way (`homepage-root`, #182).
205
+
206
+ | Surface | Before | After |
207
+ | -------------------------------------- | --------------------------------------------- | ------------------------------------------------- |
208
+ | A content page's file | `<mount>/<section>/<type>-<shortcode>.md` | `<mount>/<type>-<shortcode>.md` |
209
+ | A content page's `url:` | `/<package>/<type>-<shortcode>/` | **unchanged** |
210
+ | A `README.md` in the content tree | its section's landing, addressed `<section>/` | an ordinary page, addressed `<type>-<shortcode>/` |
211
+ | A `doc` with no `subType` | refused — "no section" | published, at `doc-<shortcode>/` |
212
+ | A `doc`'s `subType` | a genre, or a section address on a `README` | a genre, closed to what the type declares |
213
+ | `packageAddress(fm, { isReadme })` | branched on the filename | a pure function of the frontmatter |
214
+ | `sectionOf` | exported | removed |
215
+ | `declaredSections` | exported, fed the lint | removed — it had no other reader |
216
+ | `lintNote` / `lintFrontmatter` options | `landing`, `types`, `sections` | removed |
217
+
218
+ **The closed-set check becomes meaningful again.** #206, released alongside this,
219
+ put a retired-spelling warning and an address-charset error ahead of it in
220
+ `checkSubType`; removing the section branch from underneath leaves three checks
221
+ in one order — retired spelling, then charset, then the genres the type declares
222
+ — with nothing widening the last of them. A `doc` note whose `subType` names a
223
+ content type is refused again, `README` or not.
224
+
225
+ **`site.sections` is not retired — it is now the whole of what a section is.**
226
+ With no page filed into `<section>/`, nothing else makes
227
+ `/<package>/<prefix><section>/` exist at all, so `writeSectionLandings` stays and
228
+ its role changes from backfilling directories that pages created to _declaring_
229
+ the Hugo sections a site wants. Two consequences follow for a consuming site:
230
+
231
+ - **Declare every section the site links to.** A card, menu entry or breadcrumb
232
+ pointing at an undeclared section is a 404.
233
+ - **A section landing lists no child pages.** Its directory holds only its own
234
+ `_index.md`, so a layout reading `.Pages` finds nothing; one that queries
235
+ `site.RegularPages` by `Params.type` is unaffected, and that is the shape a
236
+ content catalog wants anyway — it groups by what a page _is_, not by where its
237
+ file happened to be written.
238
+
239
+ **`publish.address.landing` is inert, and still accepted.** Both publishing
240
+ consumers declare `landing: readme`, which stated something true when they wrote
241
+ it; refusing it now would break them over a correct statement, and silently
242
+ ignoring it would be worse. It selects nothing and is deleted once no
243
+ configuration writes it — the third step, and a separate change. The retired
244
+ `collection` value stays refused by name (#202).
245
+
246
+ **Migration.** No note edit is required, and **no published URL moves** — an
247
+ address never contained a section. What a consuming site must check is its
248
+ layouts and its `site.sections`, per the two consequences above. Verified against
249
+ `sohl`, `sohl-thalorna` and `sohl-kethira-basic` at `origin/main`: `lint`,
250
+ `links`, `manifest` and `package compile` are byte-identical for all three (both
251
+ link manifests and every compiled pack document), and `sohl`'s emitted site is
252
+ byte-identical page for page, keyed by URL — all 1,606 addresses unchanged, 1,670
253
+ of 1,671 files identical. The one difference is a backfilled `kb/macro/_index.md`
254
+ that no longer exists, `macro` being the one section `sohl` did not declare;
255
+ nothing in its site or the shared theme links it.
256
+
257
+ ## 12.0.0
258
+
259
+ ### Major Changes
260
+
261
+ - ab34b1c: Retire the `collection` landing rule, the `section:` frontmatter key it read,
262
+ and the `collection` doc subtype that selected it (#202). A section is landed by
263
+ the `README.md` in its directory, and that is now the only rule.
264
+
265
+ **Why it goes.** Every publishing tree had already migrated: no note in `sohl`,
266
+ `sohl-thalorna` or `sohl-kethira-basic` declares `section:` or writes
267
+ `subType: collection`, and both publishing consumers configure `landing: readme`.
268
+ What was left was not merely unused but **unimplemented on one of the two sides
269
+ that must agree**. `engine/content-address.mjs` branched on the configured rule;
270
+ `engine/site-build.mjs` never read it — it derives `isReadme` from the basename
271
+ and treats a `README.md` as a landing whatever the configuration says. So under
272
+ `landing: collection` the link manifest and the emitted site would have disagreed
273
+ about where a page is, which is the single failure the shared address function
274
+ exists to prevent. With one rule they agree by construction.
275
+
276
+ **`landing: collection` is refused, not merely unrecognized.** Reported as a bad
277
+ value it would read as a misspelling of the rule that survives, and an author
278
+ would correct the value rather than learn that the mechanism is gone. The
279
+ diagnostic names the key, says the rule is retired, says a section is landed by
280
+ its `README.md`, and says the `section:` key went with it — located to the line
281
+ and column in `package-build.config.yaml`, as every configuration finding is.
282
+
283
+ **`section:` is refused the way `draft:`, `package:` and `aliases:` are.** It had
284
+ exactly one reader — the retired branch — and no schema or vocabulary declared
285
+ it, so nothing checked it: left in place it would be _ignored_, which reads to
286
+ its author as though it still works. It is now reported by `content-build lint`
287
+ and refused at compile, with the file, line and column.
288
+
289
+ | Surface | Before | After |
290
+ | -------------------------------- | ------------------------------------- | -------------------------------------- |
291
+ | `publish.address.landing` | `readme` \| `collection` | `readme` — the value is still accepted |
292
+ | `section:` in a note | read under `collection`, else ignored | refused, at lint and at compile |
293
+ | `subType: collection` on a `doc` | selected the rule | not a subtype the format declares |
294
+ | `LANDING_RULES` | `["readme", "collection"]` | `["readme"]` |
295
+
296
+ **Migration.** Delete `landing: collection` from `publish.address` — or write
297
+ `landing: readme` — and make each section's landing the `README.md` in its
298
+ directory. Delete any `section:` a note still carries. A repository that already
299
+ configures `landing: readme` and writes no `section:` needs no change: `lint`,
300
+ `links` and `site` were verified byte-identical against `sohl`, `sohl-thalorna`
301
+ and `sohl-kethira-basic` at `origin/main`, including all 1,657 pages `sohl`
302
+ emits.
303
+
304
+ **`publish.address.landing` itself survives, for now.** It is the key both
305
+ publishing consumers declare, and refusing a correct `landing: readme` would
306
+ break them over a statement that is still true. With one rule it selects nothing,
307
+ so it is a candidate for deletion once no configuration writes it — the third
308
+ step `package:` took (#56), and a separate change.
309
+
310
+ ### Minor Changes
311
+
312
+ - ed06be0: Hold `type` and `subType` to the address charset, and rename a `doc`'s
313
+ `user-guide` subtype to `userguide` (#206).
314
+
315
+ **The rule.** An address is `package-type-shortcode`, read back by counting
316
+ hyphen-separated segments, and that is sound for exactly one reason: no segment
317
+ may contain a hyphen. `ADDRESS_SEGMENT_PATTERN` (`^[A-Za-z0-9]+$`) stated it and
318
+ `SHORTCODE_PATTERN` aliased it, but only a **shortcode** was checked against it.
319
+ The other two values that reach an address were not: a `type` is the first
320
+ segment of every address, and a `doc`'s `subType` is the section it routes to —
321
+ a path segment of its own, and, under #204, a shortcode. Both are now held to
322
+ the same constant, read rather than restated; a third spelling of one rule is how
323
+ the disagreements found in #202 and #203 happened.
324
+
325
+ **The diagnostic** is located where the value was written, in the standard form:
326
+
327
+ ```text
328
+ assets/content/Beings/Folk.md:3:1: error: `subType` "common-folk" is not an address segment — a subType is letters and digits only (^[A-Za-z0-9]+$), the same charset a shortcode is held to. …
329
+ ```
330
+
331
+ The charset is checked **ahead of** the closed-set check, which is what makes it
332
+ reach a type whose `subTypes` are declared but not yet enumerated (`being`) —
333
+ values nothing may otherwise claim to check.
334
+
335
+ **`user-guide` became `userguide`**, the one declared value that broke the rule
336
+ and the only hyphenated `type` or `subType` in the vocabulary. A hyphenated
337
+ declaration can no longer be imported at all: the registry is checked against the
338
+ charset as `engine/note-vocabulary.mjs` loads.
339
+
340
+ **The old spelling is accepted for one release, and says so.** A `doc` written
341
+ `subType: user-guide` is reported as a **warning** naming the note, the retired
342
+ value and its replacement, and the note still compiles:
343
+
344
+ ```text
345
+ User_Guide/Actions.md:3:1: warning: `subType` "user-guide" is a retired spelling of "userguide" on a doc; write "userguide". …
346
+ ```
347
+
348
+ _The ordering is the reverse of the usual, deliberately._ For a retired field the
349
+ sweep goes first; here it must go last. 43 `sohl` notes author `user-guide`
350
+ today, and declaring only the new spelling would invalidate all 43 with a release
351
+ they had no chance to sweep ahead of. So the acceptance ships first, consumers
352
+ rename, and a later change removes the acceptance — at which point the old
353
+ spelling falls through to the ordinary undeclared-value error with no code left
354
+ to remove. That later change is the breaking one; this one breaks nothing, which
355
+ is why it is a minor.
356
+
357
+ **`type` gets no transitional path**, deliberately: no note in `sohl`,
358
+ `sohl-thalorna` or `sohl-kethira-basic` authors a hyphenated type, so an
359
+ acceptance would be dead code guarding a case that does not exist. Measured
360
+ against a pristine `origin/main` extraction of each tree, the only hyphenated
361
+ value of either key anywhere is the 43 `user-guide` notes.
362
+
3
363
  ## 11.1.0
4
364
 
5
365
  ### Minor Changes