@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
|
@@ -2,10 +2,12 @@
|
|
|
2
2
|
* Every `.md` file under `dir`, depth-first in directory order.
|
|
3
3
|
*
|
|
4
4
|
* Deliberately *not* {@link walkMarkdownTree}, whose stack-based walk yields a
|
|
5
|
-
* tree in reverse. Order
|
|
6
|
-
*
|
|
7
|
-
* the walk silently
|
|
8
|
-
*
|
|
5
|
+
* tree in reverse. Order was load-bearing here when the address index carried
|
|
6
|
+
* first-writer-wins fallbacks for a page's name, filename and slug — reversing
|
|
7
|
+
* the walk silently changed which page an ambiguous name resolved to. Those
|
|
8
|
+
* fallbacks are gone with the bare `[[Name]]` form (#180), so this is now
|
|
9
|
+
* ordinary reading order rather than a dependency; it is kept because a site's
|
|
10
|
+
* emitted pages should not reorder for no reason.
|
|
9
11
|
*
|
|
10
12
|
* @param {string} dir - Directory to walk.
|
|
11
13
|
* @param {readonly string[]} skip - Directory names to ignore at any depth.
|
|
@@ -16,14 +18,15 @@ export function walkSiteTree(dir: string, skip?: readonly string[]): string[];
|
|
|
16
18
|
* The content tree's pages, and what could not be addressed.
|
|
17
19
|
*
|
|
18
20
|
* @param {string} contentBase - Absolute path to the content tree.
|
|
19
|
-
* @param {object} ctx - `{ packages, contentPackage, skipDirectories,
|
|
20
|
-
* scheme }`. `contentPackage` is the package a note that declares none
|
|
21
|
-
* belongs to
|
|
22
|
-
*
|
|
21
|
+
* @param {object} ctx - `{ packages, contentPackage, skipDirectories, base,
|
|
22
|
+
* mount, scheme }`. `contentPackage` is the package a note that declares none
|
|
23
|
+
* belongs to; `base` is where the package is served and `mount` is where its
|
|
24
|
+
* content tree sits inside it.
|
|
25
|
+
* @returns {{pages: object[], addressFindings: object[], fmLinkFindings: object[]}}
|
|
23
26
|
*/
|
|
24
27
|
export function collectContentPages(contentBase: string, ctx: object): {
|
|
25
28
|
pages: object[];
|
|
26
|
-
|
|
29
|
+
addressFindings: object[];
|
|
27
30
|
fmLinkFindings: object[];
|
|
28
31
|
};
|
|
29
32
|
/**
|
|
@@ -54,15 +57,27 @@ export function collectTreePages(tree: object, ctx: object): {
|
|
|
54
57
|
* count is what {@link checkHomepageCount} judges (#52) — this walk reports
|
|
55
58
|
* what it found, and {@link buildSite} decides whether that is one.
|
|
56
59
|
*
|
|
60
|
+
* A homepage that declares no `shortcode` has no address (#182), and is
|
|
61
|
+
* reported rather than written: it is the same finding a content page's missing
|
|
62
|
+
* shortcode produces, and it has to be available in homepage-only mode, where
|
|
63
|
+
* no other gate runs.
|
|
64
|
+
*
|
|
65
|
+
* **It is still counted.** An unaddressable homepage is a homepage — dropping
|
|
66
|
+
* it from the list would make {@link checkHomepageCount} report a tree with one
|
|
67
|
+
* as having none, sending its author to write a second front page instead of a
|
|
68
|
+
* line of frontmatter.
|
|
69
|
+
*
|
|
57
70
|
* @param {string} contentBase - Absolute path to the content tree.
|
|
58
71
|
* @param {object} ctx - `{ skipDirectories }`.
|
|
59
|
-
* @returns {{pages: object[]}} The homepage notes,
|
|
72
|
+
* @returns {{pages: object[], addressFindings: object[]}} The homepage notes,
|
|
73
|
+
* in walk order, and the ones among them that could not be addressed.
|
|
60
74
|
*/
|
|
61
75
|
export function collectHomepages(contentBase: string, ctx: object): {
|
|
62
76
|
pages: object[];
|
|
77
|
+
addressFindings: object[];
|
|
63
78
|
};
|
|
64
79
|
/**
|
|
65
|
-
* Writes each homepage at the package's own root.
|
|
80
|
+
* Writes each homepage at its address, below the package's own root.
|
|
66
81
|
*
|
|
67
82
|
* Its own writer, deliberately small. A homepage is authored markdown published
|
|
68
83
|
* verbatim — no table expansion, no section landing and no link resolution — so
|
|
@@ -76,14 +91,25 @@ export function collectHomepages(contentBase: string, ctx: object): {
|
|
|
76
91
|
* {@link auditHomepageLinks} reads the `landing:` addresses and the body's
|
|
77
92
|
* markdown links, and reports a wikilink on the page rather than resolving one.
|
|
78
93
|
*
|
|
94
|
+
* **Its destination is no longer fixed** (#182). The file is written at the
|
|
95
|
+
* note's address, flat at the package's site root, and the page states that
|
|
96
|
+
* address as its `url` — the same separation of file from URL every other page
|
|
97
|
+
* has. Nothing is written at `/<package>/` itself: that becomes a redirect the
|
|
98
|
+
* package's own repository authors, which is a routing fact rather than a page.
|
|
99
|
+
*
|
|
79
100
|
* @param {string} outRoot - The package's site root — the configured `site.out`,
|
|
80
101
|
* one level above the content mount.
|
|
81
102
|
* @param {readonly object[]} pages - From {@link collectHomepages}.
|
|
82
103
|
* @param {object} config - The resolved configuration, for the package name and
|
|
83
104
|
* the default title.
|
|
105
|
+
* @param {object} [options] - Options.
|
|
106
|
+
* @param {string} [options.base] - Where the package is served; defaults to the
|
|
107
|
+
* configured `site.base`, and to `/<contentPackage>/` below that.
|
|
84
108
|
* @returns {number} How many pages were written.
|
|
85
109
|
*/
|
|
86
|
-
export function writeHomepages(outRoot: string, pages: readonly object[], config: object
|
|
110
|
+
export function writeHomepages(outRoot: string, pages: readonly object[], config: object, { base }?: {
|
|
111
|
+
base?: string | undefined;
|
|
112
|
+
}): number;
|
|
87
113
|
/**
|
|
88
114
|
* The integrity gates a site build runs before it writes anything.
|
|
89
115
|
*
|
|
@@ -93,15 +119,19 @@ export function writeHomepages(outRoot: string, pages: readonly object[], config
|
|
|
93
119
|
*
|
|
94
120
|
* - **Frontmatter wikilinks** first, because frontmatter is copied to the page
|
|
95
121
|
* verbatim and a link written in one reaches the reader as literal `[[…]]`.
|
|
96
|
-
* - **
|
|
97
|
-
*
|
|
122
|
+
* - **Addresses** next: a note that has no address — no shortcode to be
|
|
123
|
+
* addressed by, or no section to be filed under — would silently drop a page.
|
|
124
|
+
* There is no collision gate beside it: an address is `(type, shortcode)`,
|
|
125
|
+
* which is unique within a package by rule, so two pages cannot claim one URL
|
|
126
|
+
* (#181).
|
|
98
127
|
* - **Foreign manifests** last, in two steps. *Unusable* is a file this build
|
|
99
128
|
* cannot read; *unaddressable* is one it can read but cannot look anything up
|
|
100
129
|
* in — a distinction worth keeping, because the second surfaces as a pile of
|
|
101
130
|
* dead links blaming the notes that cite them rather than the file at fault.
|
|
102
131
|
*
|
|
103
132
|
* @param {object[]} pages - Every page, from both walks.
|
|
104
|
-
* @param {object} findings - `{
|
|
133
|
+
* @param {object} findings - `{ addressFindings, fmLinkFindings }` from
|
|
134
|
+
* collection.
|
|
105
135
|
* @param {object} options - `{ manifestDir }`.
|
|
106
136
|
* @returns {object} The gate results and, when they pass, the built index.
|
|
107
137
|
*/
|
|
@@ -157,12 +187,22 @@ export function sectionFrontmatter(meta: object): object;
|
|
|
157
187
|
/**
|
|
158
188
|
* The frontmatter a page publishes with.
|
|
159
189
|
*
|
|
160
|
-
* An authored `aliases` is
|
|
190
|
+
* An authored `aliases` is retired (#180) and refused before a build reaches
|
|
191
|
+
* here, which makes this a guard rather than a working path. It was Obsidian's
|
|
192
|
+
* — a list of *names* a reader might call
|
|
161
193
|
* the note, which is vault addressing and stays in the vault. Hugo reads
|
|
162
194
|
* `aliases` as **URL redirects**, so passing them through would publish a
|
|
163
195
|
* redirect stub at each name. They are dropped, and this build emits no
|
|
164
196
|
* redirects of its own.
|
|
165
197
|
*
|
|
198
|
+
* A content page states its own **`url`**, which is its address rather than its
|
|
199
|
+
* path (#181). Hugo would otherwise publish it where the file sits — under the
|
|
200
|
+
* mount, inside its section directory — and the file sits there for a reason:
|
|
201
|
+
* Hugo derives a page's section from its directory, which is what gives the
|
|
202
|
+
* section its landing page, `.CurrentSection` and its per-section layout
|
|
203
|
+
* lookup. So the directory stays and the address is stated, and the two are
|
|
204
|
+
* free to differ.
|
|
205
|
+
*
|
|
166
206
|
* A content page carries the package the build **derived** (#65). No note
|
|
167
207
|
* declares one — `package:` is retired (#56) — so the note's frontmatter alone
|
|
168
208
|
* would publish a page that does not say which package it belongs to. The
|
|
@@ -178,7 +218,20 @@ export function sectionFrontmatter(meta: object): object;
|
|
|
178
218
|
* @returns {object} The frontmatter to write.
|
|
179
219
|
*/
|
|
180
220
|
export function pageFrontmatter(page: object, { readmeSections, decorate }: object): object;
|
|
181
|
-
/**
|
|
221
|
+
/**
|
|
222
|
+
* Where a page is written, relative to the output root.
|
|
223
|
+
*
|
|
224
|
+
* **Into its section directory, which is not where it publishes** (#181). A
|
|
225
|
+
* content page's URL is its address — `/<package>/<type>-<shortcode>/` — and it
|
|
226
|
+
* is stated in the front matter; the file still goes to `<section>/`, because
|
|
227
|
+
* Hugo reads a page's section from its path and nothing else. Flattening the
|
|
228
|
+
* tree to match the URL would take the section landings, `.CurrentSection` and
|
|
229
|
+
* every per-section layout with it.
|
|
230
|
+
*
|
|
231
|
+
* The filename is the address rather than the section-relative half of it, so
|
|
232
|
+
* two sections cannot fight over one file: a `doc` note routes by its `subType`,
|
|
233
|
+
* which may be spelled the same as another note's `type`.
|
|
234
|
+
*/
|
|
182
235
|
export function pageDestination(page: any): string;
|
|
183
236
|
/**
|
|
184
237
|
* Renders and writes every page.
|
|
@@ -27,22 +27,27 @@ export function buildSiteIndex(entries: readonly SiteEntry[], { foreignIndex }?:
|
|
|
27
27
|
* @param {object} options - Per-page inputs.
|
|
28
28
|
* @param {string} options.src - Source path of the page being resolved, for
|
|
29
29
|
* diagnostics.
|
|
30
|
-
* @param {string|null} [options.type] - The citing note's type,
|
|
31
|
-
*
|
|
30
|
+
* @param {string|null} [options.type] - The citing note's type, carried for a
|
|
31
|
+
* consumer's own diagnostics.
|
|
32
32
|
* @param {object[]} options.errors - Collector the resolver appends to.
|
|
33
|
+
* @param {string} [options.file] - The page's source file, which a link
|
|
34
|
+
* diagnostic names. Absent, `src` stands in.
|
|
33
35
|
* @param {Map<string, object>} [options.foreignIndex] - The foreign index, for
|
|
34
36
|
* resolvers that distinguish a foreign hit from a local one.
|
|
35
|
-
* @param {boolean} [options.manifestsComplete] - Whether every package this
|
|
36
|
-
* build links into supplied a manifest. When false, a resolver may soften an
|
|
37
|
-
* unresolved cross-package link rather than fail.
|
|
38
37
|
* @returns {object} The resolver context.
|
|
38
|
+
*
|
|
39
|
+
* There is deliberately **no `manifestsComplete`**. It used to let a resolver
|
|
40
|
+
* soften an unresolved cross-package address while any package's manifest was
|
|
41
|
+
* missing; #184 retired the softening, since the pack compilers and the link
|
|
42
|
+
* checker never had it and one authored link must not get two verdicts. A
|
|
43
|
+
* caller still passing it is ignored rather than obeyed.
|
|
39
44
|
*/
|
|
40
|
-
export function wikiContext(built: SiteIndex, { src, type, errors, foreignIndex
|
|
45
|
+
export function wikiContext(built: SiteIndex, { src, file, type, errors, foreignIndex }: {
|
|
41
46
|
src: string;
|
|
42
47
|
type?: string | null | undefined;
|
|
43
48
|
errors: object[];
|
|
49
|
+
file?: string | undefined;
|
|
44
50
|
foreignIndex?: Map<string, object> | undefined;
|
|
45
|
-
manifestsComplete?: boolean | undefined;
|
|
46
51
|
}): object;
|
|
47
52
|
/**
|
|
48
53
|
* One page the site will publish, as the index needs to see it.
|
|
@@ -89,28 +94,21 @@ export type SiteEntry = {
|
|
|
89
94
|
*/
|
|
90
95
|
export type SiteIndex = {
|
|
91
96
|
/**
|
|
92
|
-
*
|
|
97
|
+
* Address → page. `draft` says the page
|
|
98
|
+
* carries the `draft` tag, which marks a
|
|
99
|
+
* link *into* it (#183).
|
|
93
100
|
*/
|
|
94
101
|
index: Map<string, {
|
|
95
102
|
url: string;
|
|
96
103
|
name?: string;
|
|
104
|
+
draft?: boolean;
|
|
97
105
|
}>;
|
|
98
106
|
/**
|
|
99
|
-
*
|
|
100
|
-
* deliberately absent
|
|
107
|
+
* Short addresses claimed by two
|
|
108
|
+
* packages, and so deliberately absent
|
|
109
|
+
* from `index`.
|
|
101
110
|
*/
|
|
102
111
|
ambiguous: Set<string>;
|
|
103
|
-
/**
|
|
104
|
-
* `type|alias`.
|
|
105
|
-
*/
|
|
106
|
-
typeAlias: Map<string, {
|
|
107
|
-
url: string;
|
|
108
|
-
name?: string;
|
|
109
|
-
}>;
|
|
110
|
-
/**
|
|
111
|
-
* Type-scoped aliases claimed twice.
|
|
112
|
-
*/
|
|
113
|
-
typeCollide: Set<string>;
|
|
114
112
|
/**
|
|
115
113
|
* Every type the resolver should read as
|
|
116
114
|
* an address qualifier, local and foreign.
|
|
@@ -30,33 +30,34 @@ export function frontmatterWikilinks(fm: unknown): Array<{
|
|
|
30
30
|
/**
|
|
31
31
|
* Rewrites the wikilinks in a markdown body as KB-local markdown links.
|
|
32
32
|
*
|
|
33
|
-
*
|
|
34
|
-
* the
|
|
33
|
+
* **Every target is an address**, parsed by {@link readQualifier} and looked up
|
|
34
|
+
* case-insensitively in the KB-wide `ctx.index` (the canonical
|
|
35
|
+
* `package-type-shortcode`, `type/shortcode`, and the site's own
|
|
36
|
+
* `section/slug`), then in the vendored `ctx.foreign` manifests. A link written
|
|
37
|
+
* without a label addresses nothing at all and is reported as such (#180) —
|
|
38
|
+
* there is no second namespace left for it to name.
|
|
35
39
|
*
|
|
36
|
-
* -
|
|
37
|
-
*
|
|
38
|
-
* play no part.
|
|
39
|
-
* - **Piped** — an address, parsed by {@link readQualifier} and looked up in
|
|
40
|
-
* the KB-wide `ctx.index` (the canonical `package-type-shortcode`,
|
|
41
|
-
* `type/shortcode`, and the site's own `section/slug`), then in the vendored
|
|
42
|
-
* `ctx.foreign` manifests.
|
|
40
|
+
* Only a slash-qualified target reaches the raw key, which is what keeps
|
|
41
|
+
* `section/slug` addressable without a page's own slug answering for it.
|
|
43
42
|
*
|
|
44
|
-
*
|
|
45
|
-
*
|
|
46
|
-
*
|
|
43
|
+
* **Every target that resolves nowhere fails the build** (#184), and is
|
|
44
|
+
* classified into the vocabulary all three resolvers share — `unlabelled`,
|
|
45
|
+
* `not-an-address`, `unknown-type`, `ambiguous`, `unresolved`. Failures are
|
|
46
|
+
* collected in `ctx.errors`, each carrying the authored `link` and its
|
|
47
|
+
* `occurrence` so a caller can report the line and column it sits on.
|
|
47
48
|
*
|
|
48
|
-
*
|
|
49
|
-
*
|
|
50
|
-
*
|
|
51
|
-
*
|
|
52
|
-
*
|
|
53
|
-
*
|
|
49
|
+
* There used to be one exception: a hyphen-form address was let through while
|
|
50
|
+
* any linkable package had no vendored manifest, since a real cross-package
|
|
51
|
+
* reference and a typo look identical from here. The pack compilers and the
|
|
52
|
+
* link checker never made that allowance, so its only surviving effect was to
|
|
53
|
+
* give one authored link two verdicts. The advice moved into the message
|
|
54
|
+
* instead.
|
|
54
55
|
*
|
|
55
|
-
*
|
|
56
|
-
*
|
|
57
|
-
*
|
|
58
|
-
*
|
|
59
|
-
*
|
|
56
|
+
* A target that resolves nowhere still renders through {@link unresolvedLink}
|
|
57
|
+
* rather than as bare prose (#1665): the author's text is kept, marked so a
|
|
58
|
+
* reader can see a link was intended. The marking and the failure are separate
|
|
59
|
+
* jobs and always were — the mark is for whoever reads the page a *previous*
|
|
60
|
+
* build emitted, the failure is for the author of this one.
|
|
60
61
|
*
|
|
61
62
|
* A target that **resolved** to an entry with no page is not this case and is
|
|
62
63
|
* not marked: a pack-only package (#1516) publishes Foundry addresses and no
|
|
@@ -64,13 +65,13 @@ export function frontmatterWikilinks(fm: unknown): Array<{
|
|
|
64
65
|
* link to.
|
|
65
66
|
*
|
|
66
67
|
* @param {string} body - The markdown body.
|
|
67
|
-
* @param {object} ctx - `{ index,
|
|
68
|
-
*
|
|
68
|
+
* @param {object} ctx - `{ index, collide, sections, contentTypes, packages,
|
|
69
|
+
* foreign, type, errors, src, file }`.
|
|
69
70
|
* `packages` is every package an address may name, without which the leading
|
|
70
71
|
* package segment of a canonical address reads as an unknown type; `foreign`
|
|
71
|
-
* is the cross-package manifest index (#1446)
|
|
72
|
-
*
|
|
73
|
-
*
|
|
72
|
+
* is the cross-package manifest index (#1446). `src` is the page's display
|
|
73
|
+
* path and `file` the source file a diagnostic should name — absent, `src`
|
|
74
|
+
* stands in.
|
|
74
75
|
* @returns {string} The body with wikilinks rewritten.
|
|
75
76
|
*/
|
|
76
77
|
export function resolveWebWikilinks(body: string, ctx: object): string;
|
|
@@ -4,14 +4,14 @@
|
|
|
4
4
|
* @typedef {object} ParsedWikilink
|
|
5
5
|
* @property {string} inner - The whole interior, unescaped and trimmed. What an
|
|
6
6
|
* unlabelled link displays, anchor and all.
|
|
7
|
-
* @property {string} target - What is linked to: an address,
|
|
8
|
-
*
|
|
7
|
+
* @property {string} target - What is linked to: an address, or `""` for a
|
|
8
|
+
* link to a section of the same page.
|
|
9
9
|
* @property {string} anchor - The `#section` slug, `""` when there is none.
|
|
10
10
|
* @property {string|null} display - The text after `|`, or `null` when the link
|
|
11
11
|
* is unlabelled. `null` and `""` differ: an author may write `[[x|]]`.
|
|
12
|
-
* @property {boolean} labelled - Whether a `|` was present at all.
|
|
13
|
-
*
|
|
14
|
-
*
|
|
12
|
+
* @property {boolean} labelled - Whether a `|` was present at all. A link
|
|
13
|
+
* without one addresses nothing and is a finding (#180) — see
|
|
14
|
+
* {@link unlabelledLinkMessage} — so every reader has to be able to ask.
|
|
15
15
|
*/
|
|
16
16
|
/**
|
|
17
17
|
* Split a wikilink's interior into its parts.
|
|
@@ -30,8 +30,9 @@ export function parseWikilink(rawInner: string): ParsedWikilink;
|
|
|
30
30
|
* means "address this target, and show the target's own name" — so `display:
|
|
31
31
|
* ""` has to read as *absent* everywhere a fallback is chosen, exactly as
|
|
32
32
|
* `display: null` does. The two are still distinguishable through
|
|
33
|
-
* {@link ParsedWikilink.labelled}, which is the thing that genuinely differs
|
|
34
|
-
* and
|
|
33
|
+
* {@link ParsedWikilink.labelled}, which is the thing that genuinely differs:
|
|
34
|
+
* `[[x|]]` is labelled and `[[x]]` is not, and only the first addresses
|
|
35
|
+
* anything (#180).
|
|
35
36
|
*
|
|
36
37
|
* Stated here because the two resolvers had already drawn the line in two
|
|
37
38
|
* places and drawn it differently: the packs tested falsiness and were right,
|
|
@@ -48,34 +49,88 @@ export function authoredLabel({ display }: {
|
|
|
48
49
|
display: string | null;
|
|
49
50
|
}): string | null;
|
|
50
51
|
/**
|
|
51
|
-
*
|
|
52
|
-
*
|
|
53
|
-
*
|
|
54
|
-
*
|
|
55
|
-
*
|
|
56
|
-
*
|
|
57
|
-
*
|
|
58
|
-
*
|
|
59
|
-
*
|
|
60
|
-
*
|
|
61
|
-
*
|
|
62
|
-
*
|
|
63
|
-
*
|
|
64
|
-
*
|
|
65
|
-
*
|
|
66
|
-
*
|
|
67
|
-
*
|
|
68
|
-
*
|
|
69
|
-
*
|
|
70
|
-
*
|
|
71
|
-
*
|
|
72
|
-
* @param {{labelled: boolean}} parsed - A parsed wikilink, or anything
|
|
73
|
-
* carrying its `labelled`.
|
|
74
|
-
* @returns {boolean} True when the target is an address.
|
|
52
|
+
* What an author writing an unlabelled link is told, in one place.
|
|
53
|
+
*
|
|
54
|
+
* Shared by the link checker, the pack compilers and the web resolver, because
|
|
55
|
+
* an author meets whichever runs first and they should read the same. It names
|
|
56
|
+
* the form to write rather than a value to correct: there is no value that
|
|
57
|
+
* makes an unlabelled link resolve.
|
|
58
|
+
*
|
|
59
|
+
* **Why there is nothing left for a bare link to mean** (#180). The pipe used
|
|
60
|
+
* to select between two namespaces — address and alias — and the alias one was
|
|
61
|
+
* empty in practice: across 8,305 wikilinks in three content trees, not one
|
|
62
|
+
* bare `[[Alias]]` resolved to a note. What the index it looked up in *did* do
|
|
63
|
+
* was fold every note's `name.full` into itself, which forbade two notes of a
|
|
64
|
+
* type from sharing a display name (#179). So the namespace is gone, every
|
|
65
|
+
* link is an address, and an address needs the pipe that says so.
|
|
66
|
+
*
|
|
67
|
+
* The **link part may still be an anchor**: `[[#slug|Text]]` addresses a
|
|
68
|
+
* section of the page it is written on. It is the label that is required, not
|
|
69
|
+
* a target.
|
|
70
|
+
*
|
|
71
|
+
* @param {string} target - The target as authored, named in the message.
|
|
72
|
+
* @returns {string} The message, unpunctuated at the end as a finding is.
|
|
75
73
|
*/
|
|
76
|
-
export function
|
|
77
|
-
|
|
78
|
-
|
|
74
|
+
export function unlabelledLinkMessage(target: string): string;
|
|
75
|
+
/**
|
|
76
|
+
* What an author writing an address that resolves to nothing is told.
|
|
77
|
+
*
|
|
78
|
+
* **Both corrections, because the author cannot tell which applies.** An
|
|
79
|
+
* address lands nowhere either because the shortcode is wrong or because the
|
|
80
|
+
* package publishing it has no manifest vendored here, and the link itself
|
|
81
|
+
* looks identical in the two cases.
|
|
82
|
+
*
|
|
83
|
+
* This used to be a **warning** in the checker and, in the site build, nothing
|
|
84
|
+
* at all until every linkable package had vendored a manifest — on the
|
|
85
|
+
* reasoning that a bare `[[Name]]` might be a placeholder for a note nobody had
|
|
86
|
+
* written yet. That reasoning was a property of the bare form, which is retired
|
|
87
|
+
* (#180); the intent behind it now has a real spelling, a `draft`-tagged note
|
|
88
|
+
* that exists and resolves and renders marked (#183). So an address naming no
|
|
89
|
+
* note is a typo or an omission, both want fixing, and all three builds say so.
|
|
90
|
+
*
|
|
91
|
+
* @param {string} target - The address as authored, named in the message.
|
|
92
|
+
* @returns {string} The message, unpunctuated at the end as a finding is.
|
|
93
|
+
*/
|
|
94
|
+
export function unresolvedAddressMessage(target: string): string;
|
|
95
|
+
/**
|
|
96
|
+
* What an author writing a short address more than one package publishes is
|
|
97
|
+
* told.
|
|
98
|
+
*
|
|
99
|
+
* There is no defensible way to pick one, and the correction is mechanical:
|
|
100
|
+
* write the package-qualified form. So it fails rather than warning, and the
|
|
101
|
+
* message names the claimants so the author can choose between them without
|
|
102
|
+
* going looking.
|
|
103
|
+
*
|
|
104
|
+
* @param {string} target - The address as authored.
|
|
105
|
+
* @param {Iterable<string>} [packages] - The packages that publish it.
|
|
106
|
+
* @returns {string} The message.
|
|
107
|
+
*/
|
|
108
|
+
export function ambiguousAddressMessage(target: string, packages?: Iterable<string>): string;
|
|
109
|
+
/**
|
|
110
|
+
* The message for one link finding, whichever resolver found it.
|
|
111
|
+
*
|
|
112
|
+
* The single table the checker, the pack compilers and the web resolver all
|
|
113
|
+
* report through, so one authored link cannot get three different explanations
|
|
114
|
+
* of the same mistake depending on which build the author ran first. Callers
|
|
115
|
+
* add their own context around it — the note's name, the file it sits in — and
|
|
116
|
+
* never their own wording for the defect.
|
|
117
|
+
*
|
|
118
|
+
* @param {object} finding
|
|
119
|
+
* @param {string} finding.reason - One of {@link LINK_FINDING_REASONS}.
|
|
120
|
+
* @param {string} finding.target - The link target as authored.
|
|
121
|
+
* @param {Iterable<string>} [finding.packages] - For `ambiguous`, the
|
|
122
|
+
* claimants.
|
|
123
|
+
* @param {string} [finding.anchor] - For `unknown-anchor`, the section named.
|
|
124
|
+
* @returns {string} The message.
|
|
125
|
+
* @throws {Error} On a reason outside the closed set — a resolver inventing one
|
|
126
|
+
* would otherwise report a link with no explanation at all.
|
|
127
|
+
*/
|
|
128
|
+
export function linkFindingMessage({ reason, target, packages, anchor }: {
|
|
129
|
+
reason: string;
|
|
130
|
+
target: string;
|
|
131
|
+
packages?: Iterable<string> | undefined;
|
|
132
|
+
anchor?: string | undefined;
|
|
133
|
+
}): string;
|
|
79
134
|
/**
|
|
80
135
|
* Whether a parsed link addresses a section of the page it is written on.
|
|
81
136
|
*
|
|
@@ -109,7 +164,18 @@ export function isSamePage({ target, anchor }: ParsedWikilink): boolean;
|
|
|
109
164
|
* not.
|
|
110
165
|
*
|
|
111
166
|
* So the parse lives here, once. A resolver receives the parts and decides only
|
|
112
|
-
* what it is actually for: which
|
|
167
|
+
* what it is actually for: which document the address names.
|
|
168
|
+
*
|
|
169
|
+
* The one rule about a link's *shape* that both resolvers share also lives here
|
|
170
|
+
* — **a link carries a label** ({@link unlabelledLinkMessage}) — because the
|
|
171
|
+
* two used to state it in their own words and the author met whichever ran
|
|
172
|
+
* first.
|
|
173
|
+
*
|
|
174
|
+
* For the same reason, so does the **vocabulary of link findings**
|
|
175
|
+
* ({@link LINK_FINDING_REASONS}) and the message each one reports through
|
|
176
|
+
* ({@link linkFindingMessage}). Three builds read one authored link; an author
|
|
177
|
+
* meets whichever ran first, and a consumer switching on a `reason` should not
|
|
178
|
+
* be switching on which build produced it (#184).
|
|
113
179
|
*
|
|
114
180
|
* @module
|
|
115
181
|
*/
|
|
@@ -122,6 +188,26 @@ export function isSamePage({ target, anchor }: ParsedWikilink): boolean;
|
|
|
122
188
|
* text as written, which is the safe direction for a rewriter.
|
|
123
189
|
*/
|
|
124
190
|
export const WIKILINK: RegExp;
|
|
191
|
+
/**
|
|
192
|
+
* Every way a link can fail, named once for all three resolvers (#184).
|
|
193
|
+
*
|
|
194
|
+
* A link is read in three places — the checker (`content-links.mjs`), the pack
|
|
195
|
+
* compilers (`wikilinks.mjs`) and the web resolver (`web-wikilinks.mjs`) — and
|
|
196
|
+
* each used to name the failures in its own words. `unknown` in one was
|
|
197
|
+
* `unresolved` in another and `broken type/shortcode` in the third, so a
|
|
198
|
+
* consumer switching on a `reason` was switching on which build had produced
|
|
199
|
+
* it. The set is closed and lives here, beside the syntax the three share.
|
|
200
|
+
*
|
|
201
|
+
* - `unlabelled` — no `|`, so the link addresses nothing (#180).
|
|
202
|
+
* - `not-an-address` — labelled, but the target does not parse as an address.
|
|
203
|
+
* - `unknown-type` — definitely qualified, but names no type this build knows.
|
|
204
|
+
* - `unresolved` — parses as an address, and nothing publishes it.
|
|
205
|
+
* - `ambiguous` — more than one package publishes the short address.
|
|
206
|
+
* - `unknown-anchor` — the address resolved, the `#section` it names did not.
|
|
207
|
+
*
|
|
208
|
+
* @type {ReadonlySet<string>}
|
|
209
|
+
*/
|
|
210
|
+
export const LINK_FINDING_REASONS: ReadonlySet<string>;
|
|
125
211
|
/**
|
|
126
212
|
* The parts of a wikilink's interior.
|
|
127
213
|
*/
|
|
@@ -132,8 +218,8 @@ export type ParsedWikilink = {
|
|
|
132
218
|
*/
|
|
133
219
|
inner: string;
|
|
134
220
|
/**
|
|
135
|
-
* - What is linked to: an address,
|
|
136
|
-
*
|
|
221
|
+
* - What is linked to: an address, or `""` for a
|
|
222
|
+
* link to a section of the same page.
|
|
137
223
|
*/
|
|
138
224
|
target: string;
|
|
139
225
|
/**
|
|
@@ -146,9 +232,9 @@ export type ParsedWikilink = {
|
|
|
146
232
|
*/
|
|
147
233
|
display: string | null;
|
|
148
234
|
/**
|
|
149
|
-
* - Whether a `|` was present at all.
|
|
150
|
-
*
|
|
151
|
-
*
|
|
235
|
+
* - Whether a `|` was present at all. A link
|
|
236
|
+
* without one addresses nothing and is a finding (#180) — see
|
|
237
|
+
* {@link unlabelledLinkMessage} — so every reader has to be able to ask.
|
|
152
238
|
*/
|
|
153
239
|
labelled: boolean;
|
|
154
240
|
};
|