@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 +360 -0
- package/CONTENT.md +144 -72
- package/MIGRATING.md +71 -0
- package/bin/content-build.mjs +0 -20
- package/content-config.mjs +108 -69
- package/docs/content-format.md +50 -27
- package/engine/base-compiler.mjs +6 -1
- package/engine/content-address.mjs +29 -86
- package/engine/frontmatter-lint.mjs +74 -131
- package/engine/manifest-emit.mjs +19 -24
- package/engine/note-vocabulary.mjs +117 -1
- package/engine/retired-fields.mjs +76 -3
- package/engine/site-build.mjs +70 -78
- package/engine/site-index.mjs +20 -8
- package/engine/web-wikilinks.mjs +1 -1
- package/engine/wikilinks.mjs +1 -1
- package/package.json +1 -1
- package/types/content-config.d.mts +28 -50
- package/types/engine/content-address.d.mts +24 -43
- package/types/engine/frontmatter-lint.d.mts +2 -27
- package/types/engine/manifest-emit.d.mts +3 -9
- package/types/engine/note-vocabulary.d.mts +46 -0
- package/types/engine/retired-fields.d.mts +47 -0
- package/types/engine/site-build.d.mts +56 -31
- package/types/engine/site-index.d.mts +9 -4
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
|