@heroiclands/package-build 10.0.1 → 11.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +375 -0
- package/CONTENT.md +234 -71
- package/MIGRATING.md +64 -0
- package/bin/content-build.mjs +79 -75
- package/content-config.mjs +28 -0
- package/docs/content-format.md +90 -67
- package/engine/base-compiler.mjs +7 -1
- package/engine/content-address.mjs +71 -18
- package/engine/content-format-check.mjs +1 -1
- package/engine/content-links.mjs +93 -112
- package/engine/content-lint.mjs +14 -10
- package/engine/content-slug.mjs +39 -105
- package/engine/diagnostics.mjs +16 -2
- package/engine/frontmatter-lint.mjs +161 -18
- package/engine/helpers.mjs +31 -68
- package/engine/homepage.mjs +131 -86
- package/engine/index.mjs +2 -5
- package/engine/manifest-emit.mjs +23 -4
- package/engine/note-vocabulary.mjs +58 -1
- package/engine/retired-fields.mjs +117 -6
- package/engine/site-build.mjs +182 -59
- package/engine/site-index.mjs +57 -102
- package/engine/web-wikilinks.mjs +183 -127
- package/engine/wikilink-syntax.mjs +174 -34
- package/engine/wikilinks.mjs +159 -117
- package/package.json +1 -1
- package/types/content-config.d.mts +24 -0
- package/types/engine/base-compiler.d.mts +1 -1
- package/types/engine/content-address.d.mts +46 -14
- package/types/engine/content-links.d.mts +13 -17
- package/types/engine/content-slug.d.mts +11 -48
- package/types/engine/diagnostics.d.mts +14 -1
- package/types/engine/frontmatter-lint.d.mts +27 -2
- package/types/engine/helpers.d.mts +4 -3
- package/types/engine/homepage.d.mts +96 -60
- package/types/engine/index.d.mts +0 -1
- package/types/engine/note-vocabulary.d.mts +43 -0
- package/types/engine/retired-fields.d.mts +78 -1
- package/types/engine/site-build.d.mts +70 -17
- package/types/engine/site-index.d.mts +19 -21
- package/types/engine/web-wikilinks.d.mts +29 -28
- package/types/engine/wikilink-syntax.d.mts +126 -40
- package/types/engine/wikilinks.d.mts +29 -24
- package/engine/abbreviations.mjs +0 -0
- package/engine/alias-index.mjs +0 -153
- package/types/engine/abbreviations.d.mts +0 -44
- package/types/engine/alias-index.d.mts +0 -122
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,380 @@
|
|
|
1
1
|
# @heroiclands/package-build
|
|
2
2
|
|
|
3
|
+
## 11.1.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 8906d7a: **A `README` landing's `subType` is checked against the sections the repository declares, not the genre list.**
|
|
8
|
+
|
|
9
|
+
Two vocabularies were spelled with one key. For every note, `subType` is a
|
|
10
|
+
sub-kind of its type, checked against a closed list. For a `README` under
|
|
11
|
+
`publish.address.landing: readme` it is additionally the **URL section** —
|
|
12
|
+
`sectionOf` reads a `doc`'s `subType` as the segment the landing addresses — and
|
|
13
|
+
that is an open set the consuming repository names in `site.sections` /
|
|
14
|
+
`site.readmeSections`. `engine/frontmatter-lint.mjs` applied the closed reading
|
|
15
|
+
while `engine/content-address.mjs` applied the open one, so the two halves of the
|
|
16
|
+
toolchain disagreed about the same note: `content-build site` addressed and
|
|
17
|
+
published `Weapons/README.md` at `weapongear/`, and `content-build lint` refused
|
|
18
|
+
it because `doc` declares only `rules`, `user-guide` and `reference`. An item
|
|
19
|
+
section's landing was not expressible.
|
|
20
|
+
|
|
21
|
+
The check now widens **for a landing only**, and only under `landing: readme`: a
|
|
22
|
+
`README` whose `subType` is its section may name any section the repository
|
|
23
|
+
declares, as well as any of its type's own genres — a genre is a section a `doc`
|
|
24
|
+
tree publishes under whether or not the repository describes it, so narrowing to
|
|
25
|
+
the configured set alone would refuse a correct `README`. An ordinary note's
|
|
26
|
+
`subType` stays closed to its type's genres, unchanged.
|
|
27
|
+
|
|
28
|
+
**The guard survives**, checked against the set that actually decides where the
|
|
29
|
+
page goes:
|
|
30
|
+
|
|
31
|
+
```text
|
|
32
|
+
Weapons/README.md:4:1: error: `subType` "weapongeer" is the section this README
|
|
33
|
+
lands at, and nothing declares it: it is neither a section this repository
|
|
34
|
+
configures under `site.sections` / `site.readmeSections` (rules, user-guide,
|
|
35
|
+
weapongear, …) nor one of the subtypes doc declares (rules, user-guide,
|
|
36
|
+
reference). Did you mean "weapongear"?
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
Whether the value is an address is asked of `sectionOf` rather than by naming a
|
|
40
|
+
type, so the linter still knows no type names of its own; the section list is
|
|
41
|
+
read from the resolved configuration the site build renders those landings from,
|
|
42
|
+
through a new `declaredSections(config)`, so neither can name a section the other
|
|
43
|
+
does not.
|
|
44
|
+
|
|
45
|
+
_Minor rather than patch_: `lintNote` and `lintFrontmatter` take two new
|
|
46
|
+
options (`landing`, `sections`) and `content-config.mjs` exports
|
|
47
|
+
`declaredSections`. Both options default to today's behaviour, so a caller that
|
|
48
|
+
passes neither — and a repository that declares no sections — is unaffected.
|
|
49
|
+
|
|
50
|
+
Closes #197
|
|
51
|
+
|
|
52
|
+
### Patch Changes
|
|
53
|
+
|
|
54
|
+
- 68910b6: **A `README` landing may name any section that exists, not only a configured one.**
|
|
55
|
+
|
|
56
|
+
#197 widened a landing's `subType` check by one term and it was the wrong one:
|
|
57
|
+
the sections the repository configures in `site.sections` /
|
|
58
|
+
`site.readmeSections`. That keys on configuration a consumer may legitimately not
|
|
59
|
+
have. `sohl-thalorna` has **no `site:` block at all** — it renders its site
|
|
60
|
+
through a local fork of the emitter — so `declaredSections` answered `[]`, the
|
|
61
|
+
accepted set collapsed back to the three `doc` genres, and five of its landings
|
|
62
|
+
were still refused for naming their own content type:
|
|
63
|
+
|
|
64
|
+
```text
|
|
65
|
+
assets/content/Characters/README.md:7:1: error: `subType` "being" is not one of the subtypes doc declares (rules, user-guide, reference)
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
A landing's `subType` is an **address**, so it is now checked against the
|
|
69
|
+
addresses that exist — the range of `sectionOf` over every note the format
|
|
70
|
+
permits, plus whatever the repository names:
|
|
71
|
+
|
|
72
|
+
1. **Every content type the specification declares.** `sectionOf` returns
|
|
73
|
+
`fm.type` for a non-`doc` note, so `being`, `lore`, `scenario` and
|
|
74
|
+
`weapongear` are sections _by construction_, configured or not.
|
|
75
|
+
2. **The type's own subtypes** — `rules`, `user-guide`, `reference`.
|
|
76
|
+
3. **The configured sections**, which may name one that is neither: `sohl` has
|
|
77
|
+
`credits` and `dev-docs`.
|
|
78
|
+
|
|
79
|
+
**The guard survives.** A misspelling is in none of the three, so it still fails,
|
|
80
|
+
and the near miss is drawn from the whole union. What is deliberately no longer
|
|
81
|
+
caught is a landing for a section that exists but is currently empty — which is
|
|
82
|
+
legitimate, and is why the set is the types the format _declares_ rather than
|
|
83
|
+
those _present in the tree_: `sohl` ships two such landings above tables that
|
|
84
|
+
stay empty until the first note of each type does.
|
|
85
|
+
|
|
86
|
+
**The type list is the specification's, not the schema map's.** `NOTE_SCHEMAS`
|
|
87
|
+
declares 18 types and `docs/content-format.md` declares 23; `lore`, `place`,
|
|
88
|
+
`scenario`, `vehicle` and `armorlocation` are in the second only. Checking an
|
|
89
|
+
address against the schema map would refuse `Lore/README.md` for a reason that
|
|
90
|
+
is not about addresses, and would report one gap twice in two vocabularies.
|
|
91
|
+
Whether a note's fields can be checked is a different question, answered
|
|
92
|
+
elsewhere and reported on the notes themselves.
|
|
93
|
+
|
|
94
|
+
`lintNote` / `lintFrontmatter` take a `types` option beside `sections`; both
|
|
95
|
+
default to empty, so a caller that passes neither is unaffected.
|
|
96
|
+
|
|
97
|
+
Closes #200
|
|
98
|
+
|
|
99
|
+
## 11.0.0
|
|
100
|
+
|
|
101
|
+
### Major Changes
|
|
102
|
+
|
|
103
|
+
- 51f9b4f: **The package homepage becomes an ordinary addressed note** (#182).
|
|
104
|
+
|
|
105
|
+
A homepage declares a `shortcode` — conventionally `root` — and publishes at
|
|
106
|
+
its address, `/<package>/homepage-root/`, written by the same rule as every
|
|
107
|
+
other page. `[[homepage-root|Read the introduction]]` is an ordinary wikilink
|
|
108
|
+
now, resolving to the page the build actually writes.
|
|
109
|
+
|
|
110
|
+
**Why it was the exception, and why it is not one any more.** A page's URL used
|
|
111
|
+
to derive from `name.full` while a homepage's destination was fixed at
|
|
112
|
+
`_index.md`, so a `shortcode` on one put the note in the address index and
|
|
113
|
+
`[[homepage-<shortcode>]]` resolved _green_ to a page nothing wrote. That is
|
|
114
|
+
what `HOMEPAGE_REFUSED_FIELDS` refused, and the reason was entirely an artifact
|
|
115
|
+
of name-derived URLs. #181 removed the premise: the address a `shortcode`
|
|
116
|
+
computes is the address the build publishes.
|
|
117
|
+
|
|
118
|
+
**Breaking, two ways.**
|
|
119
|
+
|
|
120
|
+
- **Every homepage note must now declare a `shortcode`.** Without one the note
|
|
121
|
+
has no address, and both `content-build lint` and `content-build site` refuse
|
|
122
|
+
it, located at the `type:` value that makes it necessary. All six trees need
|
|
123
|
+
the same one-line edit: `shortcode: root`.
|
|
124
|
+
- **The landing moves** from `/<package>/` to `/<package>/homepage-root/`.
|
|
125
|
+
`/<package>/` becomes a `301` the package authors in its own `_redirects`,
|
|
126
|
+
with a pinned `Cache-Control: max-age=3600` in `_headers` — Cloudflare Pages
|
|
127
|
+
sets no `Cache-Control` on a redirect it generates, and an unpinned 301 is
|
|
128
|
+
cacheable indefinitely under RFC 9111. See `CONTENT.md`.
|
|
129
|
+
|
|
130
|
+
| Field | Was | Now |
|
|
131
|
+
| ----------- | ------- | -------------------------------------------- |
|
|
132
|
+
| `shortcode` | refused | **required** |
|
|
133
|
+
| `name` | refused | permitted, like any other note's |
|
|
134
|
+
| `id` | refused | refused — a homepage compiles to no document |
|
|
135
|
+
|
|
136
|
+
`id` is unaffected because its reason is: a homepage appears in no pack, and it
|
|
137
|
+
stays out of the **link manifest** for the same reason, now that a shortcode
|
|
138
|
+
alone would put it in.
|
|
139
|
+
|
|
140
|
+
**What is deleted.** `HOMEPAGE_DESTINATION`, and the comment describing the
|
|
141
|
+
homepage as "the one page for which neither addressing rule holds" — it no
|
|
142
|
+
longer is. `homepageDestination(fm)` replaces the constant, and
|
|
143
|
+
`homepageFrontmatter` takes a `base` so the emitted page can state its `url`.
|
|
144
|
+
|
|
145
|
+
**Singularity is now stated as a cardinality rule.** It used to rest on the
|
|
146
|
+
fixed destination every homepage shared — the second silently overwrote the
|
|
147
|
+
first — so the address rule enforced it as a side effect. Two homepages publish
|
|
148
|
+
two pages now and collide over nothing, so `checkHomepageCount` says what it
|
|
149
|
+
means: a package has one front page, and nothing here can decide which of two
|
|
150
|
+
`/<package>/` should redirect to.
|
|
151
|
+
- 6291dec: **A published page's URL is its address, not its display name** (#181).
|
|
152
|
+
|
|
153
|
+
Every content page now serves at `/<package>/<type>-<shortcode>/`. `(type,
|
|
154
|
+
shortcode)` names one note within a package — the rule `content-build lint`
|
|
155
|
+
already enforces — so the URL is unique _by construction_: there is no
|
|
156
|
+
collision check behind it, and renaming a note moves no URL, because no part of
|
|
157
|
+
the address comes from a display string.
|
|
158
|
+
|
|
159
|
+
**Every published URL moves**, which is what makes this a major. `sohl`'s
|
|
160
|
+
`/sohl/kb/rules/shock/` becomes `/sohl/doc-shock/`; `thalorna`'s
|
|
161
|
+
`/thalorna/affiliation/the-aerarium-imperii/` becomes
|
|
162
|
+
`/thalorna/affiliation-aerarium/`. Measured across the three live trees: 1,595
|
|
163
|
+
`sohl` pages, 1,848 `thalorna`, 370 `kethira` — every one of them unique, and no
|
|
164
|
+
content edit required in any repository.
|
|
165
|
+
|
|
166
|
+
**What it replaces.** The URL derived from `name.full`, which made a display
|
|
167
|
+
name load-bearing three ways at once: a rename silently 404'd every inbound
|
|
168
|
+
link, two notes in one section could derive one URL so a uniqueness gate had to
|
|
169
|
+
run, and long names were shortened through a table of 200 abbreviations. The
|
|
170
|
+
module doing it justified the cost by promising redirects — _"every change
|
|
171
|
+
appends to the legacy-URL map"_ — and no such map was ever written, here or in
|
|
172
|
+
any consumer. An address needs none of it.
|
|
173
|
+
|
|
174
|
+
**Sections stay, as directories.** Hugo derives a page's section from where its
|
|
175
|
+
file is written, not from its URL, and that section is what supplies the section
|
|
176
|
+
landing pages, `.CurrentSection` and per-section layout lookup. So a page is
|
|
177
|
+
still written into `<section>/` and now carries a front-matter `url:` publishing
|
|
178
|
+
it at its address. A **landing page** is the one exception: it _is_ its section,
|
|
179
|
+
so it still addresses the section under the configured `publish.address.prefix`
|
|
180
|
+
(`kb/rules/`), which is why an entry's `path` is still written to the manifest
|
|
181
|
+
rather than left for a consumer to compute.
|
|
182
|
+
|
|
183
|
+
**The `type-` prefix is deliberate.** Flattening to `<shortcode>` would put
|
|
184
|
+
content in the same namespace as a package's fixed mounts — `/<package>/` for
|
|
185
|
+
the landing page, `/<package>/api/` for generated API docs — neither of which
|
|
186
|
+
contains a hyphen or names a type. With it the namespace is provably disjoint.
|
|
187
|
+
|
|
188
|
+
**Removed**
|
|
189
|
+
|
|
190
|
+
| Gone | Why |
|
|
191
|
+
| ----------------------------------------------- | -------------------------------------------- |
|
|
192
|
+
| `contentSlug` | Nothing derives a URL from a name. |
|
|
193
|
+
| `findSlugCollisions`, and the site build's gate | Two addresses cannot collide. |
|
|
194
|
+
| `ABBREVIATIONS` / `abbreviateTokens` | They only ever shortened a name-derived URL. |
|
|
195
|
+
|
|
196
|
+
`slugify` stays, unchanged and un-abbreviated, in the two places it was always
|
|
197
|
+
right for: heading anchors and pack filenames.
|
|
198
|
+
|
|
199
|
+
**Breaking, for a consumer calling the engine directly**
|
|
200
|
+
|
|
201
|
+
- `packageAddress(fm, name, options)` → `packageAddress(fm, options)`, and
|
|
202
|
+
`contentAddress(fm, name, isReadme)` → `contentAddress(fm, isReadme)`. The
|
|
203
|
+
name was the input the address no longer has.
|
|
204
|
+
- `addressSlug(fm)` is new: the `type-shortcode` segment, lowercased, so it is
|
|
205
|
+
exactly the tail of the canonical key.
|
|
206
|
+
- The site build's gate result renames `slugErrors` to `addressErrors` and drops
|
|
207
|
+
`collisions` entirely. A note with no `shortcode`, or no section to be filed
|
|
208
|
+
under, is reported there rather than published.
|
|
209
|
+
- `engine/abbreviations.mjs` is deleted.
|
|
210
|
+
- a9fb0fc: **Retire the bare `[[Alias]]` wikilink form and the alias index** (#180, resolving #179).
|
|
211
|
+
|
|
212
|
+
Every wikilink is now an **address**, and every wikilink carries a **label**. The
|
|
213
|
+
pipe no longer selects between two namespaces — there is only one — so a link
|
|
214
|
+
written without one addresses nothing and is a finding wherever it is met:
|
|
215
|
+
`content-build links` fails on it, the pack compilers fail the note, and the site
|
|
216
|
+
resolver reports it. The correction is always the same, and the message says so:
|
|
217
|
+
write `[[type-shortcode|Text]]`. `[[#slug|Text]]` still resolves; the link part
|
|
218
|
+
may be an anchor, it is the label that is required.
|
|
219
|
+
|
|
220
|
+
**Why.** The alias namespace was empty in practice. Across 8,305 wikilinks in the
|
|
221
|
+
three content trees, **not one** bare `[[Alias]]` resolved to a note. What the
|
|
222
|
+
index behind it did do was fold every note's `name.full` into itself, so two
|
|
223
|
+
notes of one type could not share a display name — five `doc` notes in the `sohl`
|
|
224
|
+
tree collided, and every available fix moved a published URL (#179). The rule had
|
|
225
|
+
never prevented a broken link; it had only ever forbidden a name.
|
|
226
|
+
|
|
227
|
+
**The top-level `aliases:` is a retired field.** It fed nothing else, so it is
|
|
228
|
+
now **refused** naming the file and the line, the same way `draft:` and
|
|
229
|
+
`package:` are, and reported by the frontmatter lint as well as at compile.
|
|
230
|
+
|
|
231
|
+
**`name.aliases` is kept, and is read by nothing.** It fed the same index and
|
|
232
|
+
lost the same reader, but it is **reserved** — held for a use that does not
|
|
233
|
+
exist yet — so it is deliberately neither retired nor consulted. No index folds
|
|
234
|
+
it in, no rule validates it, nothing derives a name, address or URL from it, and
|
|
235
|
+
the refusal above never mentions it. A note carrying one compiles, resolves and
|
|
236
|
+
addresses exactly as the same note without it, and `tests/name-aliases-reserved.test.ts`
|
|
237
|
+
pins that equivalence across the pack compile, both wikilink resolvers, the link
|
|
238
|
+
manifest and the site index, so a reader cannot be reintroduced unnoticed.
|
|
239
|
+
|
|
240
|
+
**Removed.** `engine/alias-index.mjs` in its entirety — `aliasesOf`, `aliasKey`,
|
|
241
|
+
`indexAliases`, and the `engine.aliasIndex` namespace export — along with
|
|
242
|
+
`resolvesAsAddress` from `engine/wikilink-syntax.mjs`, replaced by
|
|
243
|
+
`unlabelledLinkMessage`, which is the one place that states the rule for both
|
|
244
|
+
builds.
|
|
245
|
+
|
|
246
|
+
**API changes** for anything importing the engine directly:
|
|
247
|
+
|
|
248
|
+
| was | now |
|
|
249
|
+
| ------------------------------------------------------ | ------------------------------ |
|
|
250
|
+
| `index.resolve(note, target, labelled)` | `index.resolve(target)` |
|
|
251
|
+
| `index.resolveAlias`, `aliasClaims`, `aliasCollisions` | gone |
|
|
252
|
+
| `auditLinks().deadAliases` / `.aliasCollisions` | `auditLinks().unlabelledLinks` |
|
|
253
|
+
| `buildWikilinkIndex().byAlias` / `.aliasClaims` | gone |
|
|
254
|
+
| `buildSiteIndex().typeAlias` / `.typeCollide` | gone |
|
|
255
|
+
| `wikiContext()`'s `typeAlias` / `typeCollide` | gone |
|
|
256
|
+
|
|
257
|
+
`buildSiteIndex` no longer indexes a page by its **name**, filename or bare slug
|
|
258
|
+
— those were the collision-aware fallbacks the bare form was looked up in, and
|
|
259
|
+
nothing consults them now. `ambiguous` / `collide` becomes the set of short
|
|
260
|
+
`type/shortcode` addresses two **foreign packages** both publish, which the web
|
|
261
|
+
resolver now reports as ambiguous rather than as merely broken.
|
|
262
|
+
|
|
263
|
+
**Consumer impact, measured.** Every tree needs a mechanical frontmatter sweep
|
|
264
|
+
of the **top-level** field, which is authored almost everywhere: 1,609 notes in
|
|
265
|
+
`sohl`, 1,850 in `thalorna`, 370 in `kethira`. A `name.aliases:` is left exactly
|
|
266
|
+
where it is — the sweep must delete the top-level list only. Links are cheaper — `sohl` has **0** unlabelled
|
|
267
|
+
links and `kethira` has none at all; `thalorna` carries 70 (68 bare links and 2
|
|
268
|
+
pipe-less anchors), which is content work in its own repository. The five `sohl`
|
|
269
|
+
alias collisions and `thalorna`'s thirteen cease to exist with no note renamed
|
|
270
|
+
and no published URL moved.
|
|
271
|
+
- ffb1b04: **An address that resolves to no note fails every build** (#184).
|
|
272
|
+
|
|
273
|
+
A wikilink whose address names no document was a **warning** in `content-build
|
|
274
|
+
links`, a **failure** in the pack compilers, and — in the site build — _nothing
|
|
275
|
+
at all_ while any linkable package had no vendored manifest. One authored link,
|
|
276
|
+
three verdicts. This makes it an error everywhere, and makes the three resolvers
|
|
277
|
+
name and word every class of link failure identically.
|
|
278
|
+
|
|
279
|
+
**Why the tolerance is spent.** It existed because a bare `[[Sunless Vault]]`
|
|
280
|
+
might be a worldbuilding placeholder for a note nobody had written. That was a
|
|
281
|
+
property of the bare form, which #180 retired, and the intent behind it now has
|
|
282
|
+
a real spelling: a note tagged `draft` exists, resolves, compiles, publishes,
|
|
283
|
+
and renders its inbound links marked (#183). An address naming no note is a typo
|
|
284
|
+
or an omission, and both want fixing.
|
|
285
|
+
|
|
286
|
+
**One vocabulary, one message.** `engine/wikilink-syntax.mjs` — which already
|
|
287
|
+
held the syntax the three resolvers share — now also holds the closed set of
|
|
288
|
+
failure classes (`LINK_FINDING_REASONS`) and the message each reports through
|
|
289
|
+
(`linkFindingMessage`, `unresolvedAddressMessage`, `ambiguousAddressMessage`).
|
|
290
|
+
An author meets whichever build ran first, and a consumer switching on a
|
|
291
|
+
`reason` should not be switching on which build produced it.
|
|
292
|
+
|
|
293
|
+
| finding | checker | pack build | site build |
|
|
294
|
+
| ---------------- | ------- | ---------- | ---------- |
|
|
295
|
+
| `unlabelled` | error | error | error |
|
|
296
|
+
| `not-an-address` | error | error | error |
|
|
297
|
+
| `unknown-type` | error | error | error |
|
|
298
|
+
| `unresolved` | error | error | **error** |
|
|
299
|
+
| `ambiguous` | error | error | error |
|
|
300
|
+
|
|
301
|
+
**Breaking changes.**
|
|
302
|
+
|
|
303
|
+
- _The site build fails an unresolved address unconditionally._
|
|
304
|
+
`wikiContext()` no longer takes `manifestsComplete`, and `resolveWebWikilinks`
|
|
305
|
+
ignores one on the context. A missing manifest is now advice inside the
|
|
306
|
+
message rather than a reason to let the link through.
|
|
307
|
+
- _Two reason strings were renamed into the shared vocabulary._ The pack
|
|
308
|
+
build's `"unknown"` and the site build's `"broken type/shortcode"` are both
|
|
309
|
+
`"unresolved"`. A slash-qualified target naming no known type reports as
|
|
310
|
+
`"unknown-type"` on the site build too, matching the checker.
|
|
311
|
+
- _`ambiguous` is a class in all three._ An address more than one package
|
|
312
|
+
publishes was reported by the checker and the pack build as though nothing
|
|
313
|
+
published it. The finding carries the claiming `packages`, and the message
|
|
314
|
+
names them.
|
|
315
|
+
- _Site-build wikilink findings are compiler-parseable._ They were
|
|
316
|
+
`log.error("bad wikilink [[x]]: reason (file)")` — a `loglevel` timestamp
|
|
317
|
+
sitting exactly where a parser reads the path from. They are now
|
|
318
|
+
`file:line:column: error: message`, located by the authored link's own
|
|
319
|
+
position in the source note, like every other diagnostic.
|
|
320
|
+
- _Pack-build failure messages are reworded_ by the shared table, and name the
|
|
321
|
+
address rather than the whole authored link.
|
|
322
|
+
|
|
323
|
+
**Consumer impact: none measured.** Across the three content trees — `sohl`
|
|
324
|
+
(1,607 notes / 3,618 links), `thalorna` (1,849 / 7,913) and `kethira` (371 / 0)
|
|
325
|
+
— the promotion produces **zero** new findings. `thalorna`'s 66 dead addresses
|
|
326
|
+
are all `not-an-address`, which was already an error, and its 70 unlabelled
|
|
327
|
+
links are #180's. No tree carries an address that resolves nowhere.
|
|
328
|
+
|
|
329
|
+
### Minor Changes
|
|
330
|
+
|
|
331
|
+
- ba51273: Mark a link whose target is tagged `draft` (#183).
|
|
332
|
+
|
|
333
|
+
A note that exists only so a link is not dead is now visibly distinct from one
|
|
334
|
+
that is written. Both builds wrap such a link in
|
|
335
|
+
`<span class="sohl-draft-link" title="Draft — not yet written">…</span>` —
|
|
336
|
+
byte-identically, as `unresolvedLink` already does, so one authored link carries
|
|
337
|
+
the same cue in a compiled journal and on the website. The link inside is
|
|
338
|
+
untouched: Foundry enriches inside HTML and Goldmark parses markdown inside an
|
|
339
|
+
inline span, so it is still a live link either way.
|
|
340
|
+
|
|
341
|
+
**The note stays in the graph.** This marks at presentation and nothing else.
|
|
342
|
+
Resolution, validation, pack compilation and manifest membership are unchanged
|
|
343
|
+
for a draft note, which is what separates the tag from the retired `draft:`
|
|
344
|
+
field — that field moved a note from _published_ to unresolvable without saying
|
|
345
|
+
so, and suppressed the build failures the note carried. Nothing here reinstates
|
|
346
|
+
any of it, and declaring the field is still refused by name.
|
|
347
|
+
|
|
348
|
+
**Read from the tag vocabulary, not respelt.** `draft` is declared in
|
|
349
|
+
`DECLARED_TAGS.state` (#172) and exported as `DRAFT_TAG`, with `isDraftNote()`
|
|
350
|
+
as its one reader — so the declared tag and the thing that acts on it cannot
|
|
351
|
+
drift apart. This is the first thing in either build to read `tags`.
|
|
352
|
+
|
|
353
|
+
**For consumers.** The class carries no appearance of its own. A Foundry system
|
|
354
|
+
supplies it in an SCSS partial beside `_unresolved-link.scss`; a site supplies
|
|
355
|
+
it in its theme. Until one does, a draft link renders exactly as it did before.
|
|
356
|
+
|
|
357
|
+
### Patch Changes
|
|
358
|
+
|
|
359
|
+
- 743b303: **`collectContentPages` refuses a missing `base` instead of publishing to `undefined/`.**
|
|
360
|
+
|
|
361
|
+
A page's URL is built as `` `${ctx.base}${slug}/` `` since _a page URL is its
|
|
362
|
+
address_ (#181). When `base` was absent the template still ran, so every
|
|
363
|
+
non-landing page in that build published at `undefineddoc-<shortcode>/` with no
|
|
364
|
+
diagnostic. The function already guards the section immediately below, on the
|
|
365
|
+
stated grounds that a note with none "is reported rather than written to
|
|
366
|
+
`undefined/`" — the same reasoning applies to `base`, which affects _every_ page
|
|
367
|
+
rather than one.
|
|
368
|
+
|
|
369
|
+
It is a caller contract rather than a note defect, so it throws rather than being
|
|
370
|
+
collected as a finding.
|
|
371
|
+
|
|
372
|
+
Fixes the two in-repo callers that still supplied the pre-#181 options and had
|
|
373
|
+
gone unnoticed: the end-to-end draft-link case (#183) and a homepage case, whose
|
|
374
|
+
combination with #181 left `main` red — neither pull request failed alone.
|
|
375
|
+
|
|
376
|
+
Closes #195
|
|
377
|
+
|
|
3
378
|
## 10.0.1
|
|
4
379
|
|
|
5
380
|
### Patch Changes
|