@heroiclands/package-build 10.0.1 → 11.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 +279 -0
- package/CONTENT.md +218 -71
- package/MIGRATING.md +64 -0
- package/bin/content-build.mjs +59 -75
- 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 +26 -13
- 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/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/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/engine/web-wikilinks.mjs
CHANGED
|
@@ -19,13 +19,13 @@
|
|
|
19
19
|
*
|
|
20
20
|
* `[[type-shortcode|Text]]` → `[Text](/section/slug/)`
|
|
21
21
|
* `[[type-shortcode|]]` → the same, showing the target's own name
|
|
22
|
-
* `[[Text]]` → the same, via a type-scoped alias
|
|
23
22
|
* `[[type-shortcode#slug|Text]]` → `[Text](/section/slug/#slug)`
|
|
24
23
|
* `[[#slug|Text]]` → `[Text](#slug)`
|
|
25
24
|
*
|
|
26
|
-
* **
|
|
27
|
-
*
|
|
28
|
-
* for this build and the
|
|
25
|
+
* **Every link is an address and carries a label** (#180). One written without
|
|
26
|
+
* a label addresses nothing and is reported — see
|
|
27
|
+
* {@link unlabelledLinkMessage}, which states the rule for this build and the
|
|
28
|
+
* pack build together.
|
|
29
29
|
*
|
|
30
30
|
* The KB *section* is not always the type: prose pages (`type: doc`) route by
|
|
31
31
|
* their `category`, so `doc/quickstart` lands on `/user-guide/sohl-quickstart/`.
|
|
@@ -46,12 +46,9 @@ import { replaceOutsideCode } from "./code-fences.mjs";
|
|
|
46
46
|
// The canonical `package-type-shortcode` key, so a package-qualified address
|
|
47
47
|
// is looked up the way a vendored manifest publishes it.
|
|
48
48
|
import { canonicalKey } from "./kb-manifest.mjs";
|
|
49
|
-
// The
|
|
50
|
-
//
|
|
51
|
-
import {
|
|
52
|
-
// Which namespace a target belongs to. The pipe decides, and this is the one
|
|
53
|
-
// place that says so.
|
|
54
|
-
import { resolvesAsAddress } from "./wikilink-syntax.mjs";
|
|
49
|
+
// The one rule about a link's shape both builds share: it carries a label, and
|
|
50
|
+
// {@link unlabelledLinkMessage} is the one place that says so (#180).
|
|
51
|
+
import { unlabelledLinkMessage } from "./wikilink-syntax.mjs";
|
|
55
52
|
// One slug rule for the whole build — see `./content-slug.mjs`. This module
|
|
56
53
|
// carried a copy that dropped non-ASCII letters rather than transliterating
|
|
57
54
|
// them, so a link to a heading named `Kûrbúl Helm` pointed at `#k-rb-l-helm`.
|
|
@@ -70,21 +67,24 @@ import { authoredLabel, WIKILINK, isSamePage, parseWikilink } from "./wikilink-s
|
|
|
70
67
|
*
|
|
71
68
|
* The KB index is keyed by the canonical `type/shortcode`, so a target written
|
|
72
69
|
* in the hyphen separator — which is what the content tree authors (#1398) —
|
|
73
|
-
* has to be rewritten to it before lookup.
|
|
74
|
-
* {@link readQualifier}, so recognising an address and resolving
|
|
75
|
-
* disagree: the two separators and the optional leading package
|
|
76
|
-
* stated once, there.
|
|
70
|
+
* has to be rewritten to it before lookup. The target is read by the pack
|
|
71
|
+
* build's own {@link readQualifier}, so recognising an address and resolving
|
|
72
|
+
* one can never disagree: the two separators and the optional leading package
|
|
73
|
+
* segment are stated once, there.
|
|
74
|
+
*
|
|
75
|
+
* It takes the **parsed** qualifier rather than the raw target because the
|
|
76
|
+
* caller needs the parse for a second purpose: `unknown-type` and
|
|
77
|
+
* `not-an-address` are different findings with different fixes, and only the
|
|
78
|
+
* `reason` tells them apart (#184). Reading the target twice would let the two
|
|
79
|
+
* readings drift.
|
|
77
80
|
*
|
|
78
81
|
* The build indexes an item note under both `skill/climb` and `docskill/climb`,
|
|
79
82
|
* and `contentTypes` carries both qualifiers, so either form finds the page.
|
|
80
83
|
*
|
|
81
|
-
* @param {
|
|
82
|
-
* @param {Set<string>} [contentTypes] - Every content type the KB build saw.
|
|
83
|
-
* @param {Set<string>} [packages] - Every package an address may name.
|
|
84
|
+
* @param {object|null} read - From {@link readQualifier}.
|
|
84
85
|
* @returns {string | null} The index key, or `null` when not an address.
|
|
85
86
|
*/
|
|
86
|
-
function
|
|
87
|
-
const read = readQualifier(target, contentTypes ?? new Set(), packages);
|
|
87
|
+
function keyOfRead(read) {
|
|
88
88
|
if (!read || read.reason) return null;
|
|
89
89
|
// A package-qualified address keeps its package: the canonical key is what
|
|
90
90
|
// a vendored manifest publishes, and dropping the segment would resolve
|
|
@@ -132,6 +132,39 @@ function unresolvedLink(text, target) {
|
|
|
132
132
|
);
|
|
133
133
|
}
|
|
134
134
|
|
|
135
|
+
/**
|
|
136
|
+
* How a link to a **draft** note renders (#183).
|
|
137
|
+
*
|
|
138
|
+
* A note tagged `draft` exists so a link into it is not dead, and nothing more.
|
|
139
|
+
* Unmarked, a reader follows a promising link into an empty page and an author
|
|
140
|
+
* cannot see which of their links still owe content.
|
|
141
|
+
*
|
|
142
|
+
* **The wrapper carries the cue and nothing else.** The link itself is
|
|
143
|
+
* untouched — Goldmark parses inline markdown inside an inline HTML span, so
|
|
144
|
+
* the markdown link still becomes an anchor, and the note is on the site, in
|
|
145
|
+
* the packs and in the manifest exactly as any other. Nothing here resembles
|
|
146
|
+
* the retired `draft:` field, which moved a note from published to unresolvable
|
|
147
|
+
* without saying so.
|
|
148
|
+
*
|
|
149
|
+
* The appearance lives in the Hugo theme for the website and in
|
|
150
|
+
* `scss/components/_draft-link.scss` for Foundry, not here.
|
|
151
|
+
*
|
|
152
|
+
* **Byte-identical with the pack build's copy** in `wikilinks.mjs`, down to the
|
|
153
|
+
* class name and the `title` wording — one authored link renders on two
|
|
154
|
+
* surfaces, and the two builds have drifted before over exactly this kind of
|
|
155
|
+
* detail (#1409). Duplicated rather than imported for the same reason
|
|
156
|
+
* {@link unresolvedLink} is; hoisting both is HeroicLands/content-build#13.
|
|
157
|
+
*
|
|
158
|
+
* The argument is already-built markup and is deliberately not escaped; the
|
|
159
|
+
* *authored* text inside it was escaped, or made into a link, by the caller.
|
|
160
|
+
*
|
|
161
|
+
* @param {string} inner - The resolved link, as this build emits it.
|
|
162
|
+
* @returns {string} An inline HTML span wrapping it.
|
|
163
|
+
*/
|
|
164
|
+
function draftLink(inner) {
|
|
165
|
+
return `<span class="sohl-draft-link" title="Draft — not yet written">${inner}</span>`;
|
|
166
|
+
}
|
|
167
|
+
|
|
135
168
|
/**
|
|
136
169
|
* A wikilink, as it is written, anywhere in a value that is not markdown.
|
|
137
170
|
*
|
|
@@ -207,33 +240,34 @@ function isPlainMap(value) {
|
|
|
207
240
|
/**
|
|
208
241
|
* Rewrites the wikilinks in a markdown body as KB-local markdown links.
|
|
209
242
|
*
|
|
210
|
-
*
|
|
211
|
-
* the
|
|
212
|
-
*
|
|
213
|
-
*
|
|
214
|
-
*
|
|
215
|
-
*
|
|
216
|
-
*
|
|
217
|
-
*
|
|
218
|
-
*
|
|
219
|
-
*
|
|
220
|
-
*
|
|
221
|
-
*
|
|
222
|
-
*
|
|
223
|
-
*
|
|
224
|
-
*
|
|
225
|
-
*
|
|
226
|
-
*
|
|
227
|
-
*
|
|
228
|
-
*
|
|
229
|
-
*
|
|
230
|
-
*
|
|
231
|
-
*
|
|
232
|
-
*
|
|
233
|
-
*
|
|
234
|
-
*
|
|
235
|
-
*
|
|
236
|
-
*
|
|
243
|
+
* **Every target is an address**, parsed by {@link readQualifier} and looked up
|
|
244
|
+
* case-insensitively in the KB-wide `ctx.index` (the canonical
|
|
245
|
+
* `package-type-shortcode`, `type/shortcode`, and the site's own
|
|
246
|
+
* `section/slug`), then in the vendored `ctx.foreign` manifests. A link written
|
|
247
|
+
* without a label addresses nothing at all and is reported as such (#180) —
|
|
248
|
+
* there is no second namespace left for it to name.
|
|
249
|
+
*
|
|
250
|
+
* Only a slash-qualified target reaches the raw key, which is what keeps
|
|
251
|
+
* `section/slug` addressable without a page's own slug answering for it.
|
|
252
|
+
*
|
|
253
|
+
* **Every target that resolves nowhere fails the build** (#184), and is
|
|
254
|
+
* classified into the vocabulary all three resolvers share — `unlabelled`,
|
|
255
|
+
* `not-an-address`, `unknown-type`, `ambiguous`, `unresolved`. Failures are
|
|
256
|
+
* collected in `ctx.errors`, each carrying the authored `link` and its
|
|
257
|
+
* `occurrence` so a caller can report the line and column it sits on.
|
|
258
|
+
*
|
|
259
|
+
* There used to be one exception: a hyphen-form address was let through while
|
|
260
|
+
* any linkable package had no vendored manifest, since a real cross-package
|
|
261
|
+
* reference and a typo look identical from here. The pack compilers and the
|
|
262
|
+
* link checker never made that allowance, so its only surviving effect was to
|
|
263
|
+
* give one authored link two verdicts. The advice moved into the message
|
|
264
|
+
* instead.
|
|
265
|
+
*
|
|
266
|
+
* A target that resolves nowhere still renders through {@link unresolvedLink}
|
|
267
|
+
* rather than as bare prose (#1665): the author's text is kept, marked so a
|
|
268
|
+
* reader can see a link was intended. The marking and the failure are separate
|
|
269
|
+
* jobs and always were — the mark is for whoever reads the page a *previous*
|
|
270
|
+
* build emitted, the failure is for the author of this one.
|
|
237
271
|
*
|
|
238
272
|
* A target that **resolved** to an entry with no page is not this case and is
|
|
239
273
|
* not marked: a pack-only package (#1516) publishes Foundry addresses and no
|
|
@@ -241,19 +275,48 @@ function isPlainMap(value) {
|
|
|
241
275
|
* link to.
|
|
242
276
|
*
|
|
243
277
|
* @param {string} body - The markdown body.
|
|
244
|
-
* @param {object} ctx - `{ index,
|
|
245
|
-
*
|
|
278
|
+
* @param {object} ctx - `{ index, collide, sections, contentTypes, packages,
|
|
279
|
+
* foreign, type, errors, src, file }`.
|
|
246
280
|
* `packages` is every package an address may name, without which the leading
|
|
247
281
|
* package segment of a canonical address reads as an unknown type; `foreign`
|
|
248
|
-
* is the cross-package manifest index (#1446)
|
|
249
|
-
*
|
|
250
|
-
*
|
|
282
|
+
* is the cross-package manifest index (#1446). `src` is the page's display
|
|
283
|
+
* path and `file` the source file a diagnostic should name — absent, `src`
|
|
284
|
+
* stands in.
|
|
251
285
|
* @returns {string} The body with wikilinks rewritten.
|
|
252
286
|
*/
|
|
253
287
|
export function resolveWebWikilinks(body, ctx) {
|
|
288
|
+
// How many times each authored link has been seen, so two identical links
|
|
289
|
+
// on one page are located at their own positions in the source file — the
|
|
290
|
+
// same counting the checker does, and what turns a finding into a
|
|
291
|
+
// `file:line:column:` diagnostic rather than a note-wide one (#17, #184).
|
|
292
|
+
const seen = new Map();
|
|
293
|
+
/**
|
|
294
|
+
* Records a finding, and returns the marked-up link it renders as.
|
|
295
|
+
*
|
|
296
|
+
* @param {string} all - The authored link, brackets and all.
|
|
297
|
+
* @param {object} finding - `{ target, reason }`, plus any extras the class
|
|
298
|
+
* carries.
|
|
299
|
+
* @param {string} text - What the link renders as.
|
|
300
|
+
* @returns {string} The marked span.
|
|
301
|
+
*/
|
|
302
|
+
const report = (all, finding, text) => {
|
|
303
|
+
const occurrence = (seen.get(all) ?? 0) + 1;
|
|
304
|
+
seen.set(all, occurrence);
|
|
305
|
+
ctx.errors.push({
|
|
306
|
+
// Absolute where the caller supplied one, so a diagnostic locates
|
|
307
|
+
// the file an editor opens; the display path otherwise.
|
|
308
|
+
file: ctx.file ?? ctx.src,
|
|
309
|
+
src: ctx.src,
|
|
310
|
+
link: all,
|
|
311
|
+
occurrence,
|
|
312
|
+
...finding,
|
|
313
|
+
});
|
|
314
|
+
return unresolvedLink(text, finding.target);
|
|
315
|
+
};
|
|
316
|
+
|
|
254
317
|
// Code is verbatim: a `[[…]]` inside a code fence, an indented block or an
|
|
255
318
|
// inline span is source text, not a link (#1505).
|
|
256
|
-
return replaceOutsideCode(body, WIKILINK, (
|
|
319
|
+
return replaceOutsideCode(body, WIKILINK, (all, rawInner) => {
|
|
257
320
|
const parsed = parseWikilink(rawInner);
|
|
258
321
|
const { target, anchor, display } = parsed;
|
|
259
322
|
// An empty label is not a label: `[[x|]]` addresses the target and
|
|
@@ -261,46 +324,43 @@ export function resolveWebWikilinks(body, ctx) {
|
|
|
261
324
|
// (#113). One reading, from {@link authoredLabel}.
|
|
262
325
|
const label = authoredLabel({ display });
|
|
263
326
|
|
|
327
|
+
// **Every link carries a label** (#180). Without one there is nothing
|
|
328
|
+
// to resolve against — the alias namespace a bare `[[Text]]` named is
|
|
329
|
+
// retired — and nothing to show either, a shortcode being an address
|
|
330
|
+
// rather than prose. Reported before the same-page form, because the
|
|
331
|
+
// rule is about how the link is *written*: `[[#slug]]` needs the pipe
|
|
332
|
+
// exactly as `[[skill-clmb]]` does.
|
|
333
|
+
if (!parsed.labelled) {
|
|
334
|
+
return report(all, { target: parsed.inner, reason: "unlabelled" }, parsed.inner);
|
|
335
|
+
}
|
|
336
|
+
|
|
264
337
|
// `[[#section-slug|Text]]` — a section of this same page.
|
|
265
338
|
if (isSamePage({ target, anchor })) {
|
|
266
339
|
return `[${label ?? anchor}](#${slugify(anchor)})`;
|
|
267
340
|
}
|
|
268
341
|
|
|
269
|
-
// **The pipe chooses the namespace, with no fallback either way**
|
|
270
|
-
// (#131). The two used to be tried in turn, so a note *name* that
|
|
271
|
-
// looked like an address resolved as one and a genuine address that
|
|
272
|
-
// resolved nowhere silently became a name lookup.
|
|
273
|
-
const addressed = resolvesAsAddress(parsed);
|
|
274
|
-
const typeKey = ctx.type ? aliasKey(ctx.type, target) : null;
|
|
275
342
|
// The canonical separator (#1398) has to be resolved, not merely
|
|
276
|
-
// recognised. `null` here means the
|
|
277
|
-
//
|
|
278
|
-
const
|
|
343
|
+
// recognised. `null` here means the target is not an address at all,
|
|
344
|
+
// which is a defect: there is no other namespace to try.
|
|
345
|
+
const read = readQualifier(target, ctx.contentTypes ?? new Set(), ctx.packages);
|
|
346
|
+
const hyphenKey = keyOfRead(read);
|
|
279
347
|
const rawKey = target.toLowerCase();
|
|
280
348
|
const hit =
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
// case below. Local wins: a live build is authoritative and a
|
|
292
|
-
// vendored manifest can only be staler.
|
|
293
|
-
(hyphenKey ? ctx.foreign?.get(hyphenKey) : undefined))
|
|
294
|
-
: typeKey ? ctx.typeAlias.get(typeKey)
|
|
295
|
-
: undefined;
|
|
349
|
+
(hyphenKey ? ctx.index.get(hyphenKey) : undefined) ??
|
|
350
|
+
// `section/slug` is the site's own address for a page, and it is in
|
|
351
|
+
// the same map. Admitted only when the target carries a slash, so
|
|
352
|
+
// a page's bare slug cannot answer for an address.
|
|
353
|
+
(rawKey.includes("/") ? ctx.index.get(rawKey) : undefined) ??
|
|
354
|
+
// A manifest entry carries the same `{ url, name }` shape as a
|
|
355
|
+
// local one (#1446), so a cross-package hit needs no special case
|
|
356
|
+
// below. Local wins: a live build is authoritative and a vendored
|
|
357
|
+
// manifest can only be staler.
|
|
358
|
+
(hyphenKey ? ctx.foreign?.get(hyphenKey) : undefined);
|
|
296
359
|
if (hit) {
|
|
297
|
-
// An address with
|
|
298
|
-
// not display text), so the document's **current**
|
|
299
|
-
// and a rename shows at every citation.
|
|
300
|
-
|
|
301
|
-
// name there would rewrite the sentence ("worsens the [[Shock
|
|
302
|
-
// State]]" must not render as "Shock").
|
|
303
|
-
const text = label ?? (addressed ? hit.name : target);
|
|
360
|
+
// An address with an *empty* label has no prose to show (a
|
|
361
|
+
// shortcode is not display text), so the document's **current**
|
|
362
|
+
// name stands in and a rename shows at every citation.
|
|
363
|
+
const text = label ?? hit.name;
|
|
304
364
|
// A pack-only package publishes Foundry addresses and no pages
|
|
305
365
|
// (#1516), so its entries carry no `path` and resolve to no URL.
|
|
306
366
|
// The address is real — this is not a typo and must not fail the
|
|
@@ -308,55 +368,51 @@ export function resolveWebWikilinks(body, ctx) {
|
|
|
308
368
|
// reader gets the text and no href. Emitting the href anyway is
|
|
309
369
|
// what the manifest exists to prevent: `[Name](undefined)` renders
|
|
310
370
|
// as a link and goes nowhere.
|
|
311
|
-
|
|
312
|
-
|
|
371
|
+
const link =
|
|
372
|
+
hit.url ? `[${text}](${anchor ? `${hit.url}#${slugify(anchor)}` : hit.url})` : text;
|
|
373
|
+
// A link into a note that exists but is not written renders marked
|
|
374
|
+
// (#183). Presentation only — the href above is unchanged, and a
|
|
375
|
+
// `[[#anchor]]` self-link is not marked because the reader is
|
|
376
|
+
// already on the page it would be telling them about.
|
|
377
|
+
return hit.draft ? draftLink(link) : link;
|
|
313
378
|
}
|
|
314
379
|
|
|
315
380
|
const slash = target.indexOf("/");
|
|
316
381
|
const prefix = slash === -1 ? null : target.slice(0, slash).toLowerCase();
|
|
317
|
-
// A slash-qualified target whose prefix is a real section
|
|
318
|
-
//
|
|
319
|
-
//
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
//
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
//
|
|
326
|
-
// separated that legitimate cross-package reference from a typo — so
|
|
327
|
-
// treating the form as definitely-local would have failed the build on
|
|
328
|
-
// correct content.
|
|
382
|
+
// A slash-qualified target whose prefix is a real KB **section** is an
|
|
383
|
+
// address in the site's own `section/slug` space, which the lookup
|
|
384
|
+
// above already consulted. It parses as no `type/shortcode`, but it did
|
|
385
|
+
// address something and nothing answered — so it is unresolved, not
|
|
386
|
+
// unaddressable. (A prefix that is a content *type* never reaches here:
|
|
387
|
+
// it yields a `hyphenKey`.)
|
|
388
|
+
const siteAddress = prefix !== null && ctx.sections.has(prefix);
|
|
389
|
+
|
|
390
|
+
// **An address resolving nowhere is a failure, unconditionally** (#184).
|
|
329
391
|
//
|
|
330
|
-
//
|
|
331
|
-
//
|
|
332
|
-
//
|
|
333
|
-
//
|
|
334
|
-
//
|
|
335
|
-
//
|
|
336
|
-
|
|
392
|
+
// It was gated on `manifestsComplete` — while any linkable package was
|
|
393
|
+
// invisible here, `Rules/Bestiary.md` addressing `being-grkrahk` in the
|
|
394
|
+
// `thalorna` package was indistinguishable from a typo, so the form
|
|
395
|
+
// stayed unguarded rather than fail correct content. The gate has
|
|
396
|
+
// outlived that: every linkable package publishes a manifest now, and
|
|
397
|
+
// more to the point the *other two* resolvers never had it. The pack
|
|
398
|
+
// compilers fail the note and the link checker reports an error, so the
|
|
399
|
+
// gate's only remaining effect was to give one authored link two
|
|
400
|
+
// verdicts depending on which build read it. The advice a missing
|
|
401
|
+
// manifest calls for is in the message instead — {@link
|
|
402
|
+
// unresolvedAddressMessage} names both corrections — which informs the
|
|
403
|
+
// author without excusing the link.
|
|
404
|
+
const reason =
|
|
405
|
+
ctx.collide?.has(hyphenKey ?? rawKey) ? "ambiguous"
|
|
406
|
+
: hyphenKey !== null || siteAddress ? "unresolved"
|
|
407
|
+
: read?.reason === "unknown-type" ? "unknown-type"
|
|
408
|
+
// Every link is an address, and this is not one. Distinct from
|
|
409
|
+
// a dead address, because the fix is different: a name has to
|
|
410
|
+
// become an address, not be corrected (#180).
|
|
411
|
+
: "not-an-address";
|
|
337
412
|
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
// (#131).
|
|
343
|
-
ctx.errors.push({ file: ctx.src, target, reason: "not-an-address" });
|
|
344
|
-
} else if (
|
|
345
|
-
addressed ?
|
|
346
|
-
rawKey.includes("/") && ctx.collide.has(rawKey)
|
|
347
|
-
: Boolean(typeKey && ctx.typeCollide.has(typeKey))
|
|
348
|
-
) {
|
|
349
|
-
ctx.errors.push({ file: ctx.src, target, reason: "ambiguous" });
|
|
350
|
-
} else if (badQualified || badAddress) {
|
|
351
|
-
ctx.errors.push({
|
|
352
|
-
file: ctx.src,
|
|
353
|
-
target,
|
|
354
|
-
reason: "broken type/shortcode",
|
|
355
|
-
});
|
|
356
|
-
}
|
|
357
|
-
// An unresolved *alias* stays soft: it may be ordinary prose, or a
|
|
358
|
-
// worldbuilding placeholder for a note not yet written. It still
|
|
359
|
-
// renders marked, so a reader can see a link was intended.
|
|
360
|
-
return unresolvedLink(label ?? target, target);
|
|
413
|
+
// Whether or not it failed the build, the link renders marked: the
|
|
414
|
+
// author's text is kept so the sentence still reads, and a reader can
|
|
415
|
+
// see that something was meant to be a link.
|
|
416
|
+
return report(all, { target, reason }, label ?? target);
|
|
361
417
|
});
|
|
362
418
|
}
|