@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
|
@@ -34,7 +34,18 @@
|
|
|
34
34
|
* not.
|
|
35
35
|
*
|
|
36
36
|
* So the parse lives here, once. A resolver receives the parts and decides only
|
|
37
|
-
* what it is actually for: which
|
|
37
|
+
* what it is actually for: which document the address names.
|
|
38
|
+
*
|
|
39
|
+
* The one rule about a link's *shape* that both resolvers share also lives here
|
|
40
|
+
* — **a link carries a label** ({@link unlabelledLinkMessage}) — because the
|
|
41
|
+
* two used to state it in their own words and the author met whichever ran
|
|
42
|
+
* first.
|
|
43
|
+
*
|
|
44
|
+
* For the same reason, so does the **vocabulary of link findings**
|
|
45
|
+
* ({@link LINK_FINDING_REASONS}) and the message each one reports through
|
|
46
|
+
* ({@link linkFindingMessage}). Three builds read one authored link; an author
|
|
47
|
+
* meets whichever ran first, and a consumer switching on a `reason` should not
|
|
48
|
+
* be switching on which build produced it (#184).
|
|
38
49
|
*
|
|
39
50
|
* @module
|
|
40
51
|
*/
|
|
@@ -55,14 +66,14 @@ export const WIKILINK = /\[\[([^\]\n]+)\]\]/g;
|
|
|
55
66
|
* @typedef {object} ParsedWikilink
|
|
56
67
|
* @property {string} inner - The whole interior, unescaped and trimmed. What an
|
|
57
68
|
* unlabelled link displays, anchor and all.
|
|
58
|
-
* @property {string} target - What is linked to: an address,
|
|
59
|
-
*
|
|
69
|
+
* @property {string} target - What is linked to: an address, or `""` for a
|
|
70
|
+
* link to a section of the same page.
|
|
60
71
|
* @property {string} anchor - The `#section` slug, `""` when there is none.
|
|
61
72
|
* @property {string|null} display - The text after `|`, or `null` when the link
|
|
62
73
|
* is unlabelled. `null` and `""` differ: an author may write `[[x|]]`.
|
|
63
|
-
* @property {boolean} labelled - Whether a `|` was present at all.
|
|
64
|
-
*
|
|
65
|
-
*
|
|
74
|
+
* @property {boolean} labelled - Whether a `|` was present at all. A link
|
|
75
|
+
* without one addresses nothing and is a finding (#180) — see
|
|
76
|
+
* {@link unlabelledLinkMessage} — so every reader has to be able to ask.
|
|
66
77
|
*/
|
|
67
78
|
|
|
68
79
|
/**
|
|
@@ -98,8 +109,9 @@ export function parseWikilink(rawInner) {
|
|
|
98
109
|
* means "address this target, and show the target's own name" — so `display:
|
|
99
110
|
* ""` has to read as *absent* everywhere a fallback is chosen, exactly as
|
|
100
111
|
* `display: null` does. The two are still distinguishable through
|
|
101
|
-
* {@link ParsedWikilink.labelled}, which is the thing that genuinely differs
|
|
102
|
-
* and
|
|
112
|
+
* {@link ParsedWikilink.labelled}, which is the thing that genuinely differs:
|
|
113
|
+
* `[[x|]]` is labelled and `[[x]]` is not, and only the first addresses
|
|
114
|
+
* anything (#180).
|
|
103
115
|
*
|
|
104
116
|
* Stated here because the two resolvers had already drawn the line in two
|
|
105
117
|
* places and drawn it differently: the packs tested falsiness and were right,
|
|
@@ -117,33 +129,161 @@ export function authoredLabel({ display }) {
|
|
|
117
129
|
}
|
|
118
130
|
|
|
119
131
|
/**
|
|
120
|
-
*
|
|
121
|
-
*
|
|
122
|
-
*
|
|
123
|
-
*
|
|
124
|
-
*
|
|
125
|
-
*
|
|
126
|
-
*
|
|
127
|
-
*
|
|
128
|
-
*
|
|
129
|
-
*
|
|
130
|
-
*
|
|
131
|
-
*
|
|
132
|
-
*
|
|
133
|
-
*
|
|
134
|
-
*
|
|
135
|
-
*
|
|
136
|
-
*
|
|
137
|
-
*
|
|
138
|
-
*
|
|
139
|
-
*
|
|
140
|
-
*
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
132
|
+
* What an author writing an unlabelled link is told, in one place.
|
|
133
|
+
*
|
|
134
|
+
* Shared by the link checker, the pack compilers and the web resolver, because
|
|
135
|
+
* an author meets whichever runs first and they should read the same. It names
|
|
136
|
+
* the form to write rather than a value to correct: there is no value that
|
|
137
|
+
* makes an unlabelled link resolve.
|
|
138
|
+
*
|
|
139
|
+
* **Why there is nothing left for a bare link to mean** (#180). The pipe used
|
|
140
|
+
* to select between two namespaces — address and alias — and the alias one was
|
|
141
|
+
* empty in practice: across 8,305 wikilinks in three content trees, not one
|
|
142
|
+
* bare `[[Alias]]` resolved to a note. What the index it looked up in *did* do
|
|
143
|
+
* was fold every note's `name.full` into itself, which forbade two notes of a
|
|
144
|
+
* type from sharing a display name (#179). So the namespace is gone, every
|
|
145
|
+
* link is an address, and an address needs the pipe that says so.
|
|
146
|
+
*
|
|
147
|
+
* The **link part may still be an anchor**: `[[#slug|Text]]` addresses a
|
|
148
|
+
* section of the page it is written on. It is the label that is required, not
|
|
149
|
+
* a target.
|
|
150
|
+
*
|
|
151
|
+
* @param {string} target - The target as authored, named in the message.
|
|
152
|
+
* @returns {string} The message, unpunctuated at the end as a finding is.
|
|
153
|
+
*/
|
|
154
|
+
export function unlabelledLinkMessage(target) {
|
|
155
|
+
return (
|
|
156
|
+
`wikilink [[${target}]] carries no label, so it addresses nothing — ` +
|
|
157
|
+
`write [[type-shortcode|Text]]. Every link is an address; the bare ` +
|
|
158
|
+
`[[Name]] form named an alias, and the alias namespace is retired`
|
|
159
|
+
);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Every way a link can fail, named once for all three resolvers (#184).
|
|
164
|
+
*
|
|
165
|
+
* A link is read in three places — the checker (`content-links.mjs`), the pack
|
|
166
|
+
* compilers (`wikilinks.mjs`) and the web resolver (`web-wikilinks.mjs`) — and
|
|
167
|
+
* each used to name the failures in its own words. `unknown` in one was
|
|
168
|
+
* `unresolved` in another and `broken type/shortcode` in the third, so a
|
|
169
|
+
* consumer switching on a `reason` was switching on which build had produced
|
|
170
|
+
* it. The set is closed and lives here, beside the syntax the three share.
|
|
171
|
+
*
|
|
172
|
+
* - `unlabelled` — no `|`, so the link addresses nothing (#180).
|
|
173
|
+
* - `not-an-address` — labelled, but the target does not parse as an address.
|
|
174
|
+
* - `unknown-type` — definitely qualified, but names no type this build knows.
|
|
175
|
+
* - `unresolved` — parses as an address, and nothing publishes it.
|
|
176
|
+
* - `ambiguous` — more than one package publishes the short address.
|
|
177
|
+
* - `unknown-anchor` — the address resolved, the `#section` it names did not.
|
|
178
|
+
*
|
|
179
|
+
* @type {ReadonlySet<string>}
|
|
180
|
+
*/
|
|
181
|
+
export const LINK_FINDING_REASONS = Object.freeze(
|
|
182
|
+
new Set([
|
|
183
|
+
"unlabelled",
|
|
184
|
+
"not-an-address",
|
|
185
|
+
"unknown-type",
|
|
186
|
+
"unresolved",
|
|
187
|
+
"ambiguous",
|
|
188
|
+
"unknown-anchor",
|
|
189
|
+
]),
|
|
190
|
+
);
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* What an author writing an address that resolves to nothing is told.
|
|
194
|
+
*
|
|
195
|
+
* **Both corrections, because the author cannot tell which applies.** An
|
|
196
|
+
* address lands nowhere either because the shortcode is wrong or because the
|
|
197
|
+
* package publishing it has no manifest vendored here, and the link itself
|
|
198
|
+
* looks identical in the two cases.
|
|
199
|
+
*
|
|
200
|
+
* This used to be a **warning** in the checker and, in the site build, nothing
|
|
201
|
+
* at all until every linkable package had vendored a manifest — on the
|
|
202
|
+
* reasoning that a bare `[[Name]]` might be a placeholder for a note nobody had
|
|
203
|
+
* written yet. That reasoning was a property of the bare form, which is retired
|
|
204
|
+
* (#180); the intent behind it now has a real spelling, a `draft`-tagged note
|
|
205
|
+
* that exists and resolves and renders marked (#183). So an address naming no
|
|
206
|
+
* note is a typo or an omission, both want fixing, and all three builds say so.
|
|
207
|
+
*
|
|
208
|
+
* @param {string} target - The address as authored, named in the message.
|
|
209
|
+
* @returns {string} The message, unpunctuated at the end as a finding is.
|
|
210
|
+
*/
|
|
211
|
+
export function unresolvedAddressMessage(target) {
|
|
212
|
+
return (
|
|
213
|
+
`address [[${target}]] resolves to no note — no package publishes ` +
|
|
214
|
+
`it. Fix the shortcode, or vendor the link manifest of the package ` +
|
|
215
|
+
`that does`
|
|
216
|
+
);
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
/**
|
|
220
|
+
* What an author writing a short address more than one package publishes is
|
|
221
|
+
* told.
|
|
222
|
+
*
|
|
223
|
+
* There is no defensible way to pick one, and the correction is mechanical:
|
|
224
|
+
* write the package-qualified form. So it fails rather than warning, and the
|
|
225
|
+
* message names the claimants so the author can choose between them without
|
|
226
|
+
* going looking.
|
|
227
|
+
*
|
|
228
|
+
* @param {string} target - The address as authored.
|
|
229
|
+
* @param {Iterable<string>} [packages] - The packages that publish it.
|
|
230
|
+
* @returns {string} The message.
|
|
231
|
+
*/
|
|
232
|
+
export function ambiguousAddressMessage(target, packages = []) {
|
|
233
|
+
const named = [...packages].filter(Boolean).sort();
|
|
234
|
+
return (
|
|
235
|
+
`address [[${target}]] is published by ` +
|
|
236
|
+
(named.length ? `${named.join(" and ")}` : `more than one package`) +
|
|
237
|
+
`, so it names neither — write the package-qualified ` +
|
|
238
|
+
`[[package-type-shortcode|Text]]`
|
|
239
|
+
);
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
/**
|
|
243
|
+
* The message for one link finding, whichever resolver found it.
|
|
244
|
+
*
|
|
245
|
+
* The single table the checker, the pack compilers and the web resolver all
|
|
246
|
+
* report through, so one authored link cannot get three different explanations
|
|
247
|
+
* of the same mistake depending on which build the author ran first. Callers
|
|
248
|
+
* add their own context around it — the note's name, the file it sits in — and
|
|
249
|
+
* never their own wording for the defect.
|
|
250
|
+
*
|
|
251
|
+
* @param {object} finding
|
|
252
|
+
* @param {string} finding.reason - One of {@link LINK_FINDING_REASONS}.
|
|
253
|
+
* @param {string} finding.target - The link target as authored.
|
|
254
|
+
* @param {Iterable<string>} [finding.packages] - For `ambiguous`, the
|
|
255
|
+
* claimants.
|
|
256
|
+
* @param {string} [finding.anchor] - For `unknown-anchor`, the section named.
|
|
257
|
+
* @returns {string} The message.
|
|
258
|
+
* @throws {Error} On a reason outside the closed set — a resolver inventing one
|
|
259
|
+
* would otherwise report a link with no explanation at all.
|
|
144
260
|
*/
|
|
145
|
-
export function
|
|
146
|
-
|
|
261
|
+
export function linkFindingMessage({ reason, target, packages, anchor }) {
|
|
262
|
+
switch (reason) {
|
|
263
|
+
case "unlabelled":
|
|
264
|
+
return unlabelledLinkMessage(target);
|
|
265
|
+
case "not-an-address":
|
|
266
|
+
return (
|
|
267
|
+
`"${target}" is not an address — the "|" says one was meant, ` +
|
|
268
|
+
`so write [[type-shortcode|Text]]`
|
|
269
|
+
);
|
|
270
|
+
case "unknown-type":
|
|
271
|
+
return `address [[${target}]] names no known content type`;
|
|
272
|
+
case "ambiguous":
|
|
273
|
+
return ambiguousAddressMessage(target, packages ?? []);
|
|
274
|
+
case "unknown-anchor":
|
|
275
|
+
return (
|
|
276
|
+
`address [[${target}]] resolves, but no section "#${anchor ?? ""}" ` +
|
|
277
|
+
`is published for it`
|
|
278
|
+
);
|
|
279
|
+
case "unresolved":
|
|
280
|
+
return unresolvedAddressMessage(target);
|
|
281
|
+
default:
|
|
282
|
+
throw new Error(
|
|
283
|
+
`linkFindingMessage: "${reason}" is not one of ` +
|
|
284
|
+
`${[...LINK_FINDING_REASONS].join(", ")}`,
|
|
285
|
+
);
|
|
286
|
+
}
|
|
147
287
|
}
|
|
148
288
|
|
|
149
289
|
/**
|