@heroiclands/package-build 17.1.0 → 18.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 +1773 -0
- package/CONTENT.md +288 -72
- package/README.md +1 -1
- package/bin/content-build.mjs +218 -137
- package/bin/package-build.mjs +43 -0
- package/content-config.mjs +62 -67
- package/docs/content-format.md +768 -58
- package/engine/actor-compiler.mjs +586 -0
- package/engine/address-charset.mjs +11 -8
- package/engine/address-diff.mjs +266 -22
- package/engine/anchored-sections.mjs +83 -0
- package/engine/anchors.mjs +83 -0
- package/engine/base-compiler.mjs +148 -18
- package/engine/bundle-notes.mjs +276 -0
- package/engine/bundles.mjs +307 -0
- package/engine/code-fences.mjs +103 -0
- package/engine/compile-corpus.mjs +89 -0
- package/engine/content-address.mjs +267 -5
- package/engine/content-format-check.mjs +54 -11
- package/engine/content-format.mjs +37 -5
- package/engine/content-index.mjs +394 -111
- package/engine/content-links.mjs +185 -77
- package/engine/content-lint.mjs +232 -14
- package/engine/content-package.mjs +2 -1
- package/engine/content-tables.mjs +99 -3
- package/engine/document-subtypes.mjs +49 -6
- package/engine/field-reference.mjs +1 -1
- package/engine/field-spec.mjs +56 -6
- package/engine/folder-notes.mjs +470 -0
- package/engine/foreign-catalog.mjs +195 -4
- package/engine/{manifest-emit.mjs → foundry-entries.mjs} +78 -81
- package/engine/frontmatter-lint.mjs +245 -19
- package/engine/frontmatter.mjs +88 -8
- package/engine/generate.mjs +304 -13
- package/engine/helpers.mjs +293 -64
- package/engine/ids.mjs +137 -2
- package/engine/index-records.mjs +126 -0
- package/engine/index.mjs +23 -5
- package/engine/item-compiler.mjs +349 -0
- package/engine/item-docs.mjs +7 -1
- package/engine/item-registry.mjs +6 -0
- package/engine/journals.mjs +93 -27
- package/engine/macros.mjs +4 -2
- package/engine/metadata-index.mjs +495 -0
- package/engine/note-claims.mjs +146 -25
- package/engine/note-ids.mjs +112 -0
- package/engine/note-renames.mjs +134 -0
- package/engine/note-schemas.mjs +25 -0
- package/engine/note-vocabulary.mjs +110 -27
- package/engine/pack-config.mjs +4 -0
- package/engine/pack-router.mjs +23 -0
- package/engine/retired-fields.mjs +137 -3
- package/engine/scenes.mjs +66 -34
- package/engine/schema-check.mjs +58 -17
- package/engine/site-build.mjs +71 -12
- package/engine/site-index.mjs +20 -7
- package/engine/sql-tables.mjs +485 -0
- package/engine/subtype-registry.mjs +102 -0
- package/engine/system-block.mjs +96 -12
- package/engine/systems.mjs +201 -0
- package/engine/web-wikilinks.mjs +50 -20
- package/engine/wikilink-syntax.mjs +17 -8
- package/engine/wikilinks.mjs +124 -51
- package/engine/yaml-lint.mjs +282 -0
- package/hm3/actors.mjs +352 -0
- package/hm3/default-item-art.mjs +75 -0
- package/hm3/document-subtypes.mjs +134 -0
- package/hm3/index.mjs +56 -0
- package/hm3/item-builders.mjs +84 -0
- package/hm3/item-fields.mjs +180 -0
- package/hm3/items.mjs +96 -0
- package/hm3/template-priority.mjs +85 -0
- package/labels.mjs +191 -0
- package/manifest.mjs +43 -2
- package/package.json +19 -4
- package/release.mjs +62 -7
- package/sohl/actors.mjs +33 -487
- package/sohl/being-info.mjs +16 -7
- package/sohl/default-item-art.mjs +14 -3
- package/sohl/document-subtypes.mjs +16 -10
- package/sohl/item-builders.mjs +14 -5
- package/sohl/item-fields.mjs +68 -7
- package/sohl/items.mjs +44 -258
- package/sohl/note-schemas.mjs +2 -2
- package/types/content-config.d.mts +21 -42
- package/types/engine/actor-compiler.d.mts +204 -0
- package/types/engine/address-charset.d.mts +11 -8
- package/types/engine/address-diff.d.mts +53 -5
- package/types/engine/anchored-sections.d.mts +21 -0
- package/types/engine/anchors.d.mts +20 -0
- package/types/engine/base-compiler.d.mts +17 -17
- package/types/engine/bundle-notes.d.mts +173 -0
- package/types/engine/bundles.d.mts +60 -0
- package/types/engine/code-fences.d.mts +43 -0
- package/types/engine/compile-corpus.d.mts +32 -0
- package/types/engine/content-address.d.mts +205 -5
- package/types/engine/content-format-check.d.mts +6 -2
- package/types/engine/content-format.d.mts +57 -1
- package/types/engine/content-index.d.mts +64 -80
- package/types/engine/content-links.d.mts +52 -7
- package/types/engine/content-lint.d.mts +10 -1
- package/types/engine/content-package.d.mts +2 -1
- package/types/engine/content-tables.d.mts +21 -39
- package/types/engine/document-subtypes.d.mts +37 -3
- package/types/engine/field-spec.d.mts +76 -5
- package/types/engine/folder-notes.d.mts +159 -0
- package/types/engine/foreign-catalog.d.mts +53 -0
- package/types/engine/{manifest-emit.d.mts → foundry-entries.d.mts} +16 -38
- package/types/engine/frontmatter-lint.d.mts +10 -2
- package/types/engine/frontmatter.d.mts +64 -0
- package/types/engine/generate.d.mts +38 -0
- package/types/engine/helpers.d.mts +94 -30
- package/types/engine/ids.d.mts +96 -0
- package/types/engine/index-records.d.mts +68 -0
- package/types/engine/index.d.mts +9 -3
- package/types/engine/item-compiler.d.mts +131 -0
- package/types/engine/journals.d.mts +47 -9
- package/types/engine/metadata-index.d.mts +226 -0
- package/types/engine/note-claims.d.mts +57 -11
- package/types/engine/note-ids.d.mts +38 -0
- package/types/engine/note-renames.d.mts +102 -0
- package/types/engine/note-vocabulary.d.mts +44 -8
- package/types/engine/retired-fields.d.mts +75 -0
- package/types/engine/scenes.d.mts +3 -2
- package/types/engine/schema-check.d.mts +25 -4
- package/types/engine/site-build.d.mts +4 -4
- package/types/engine/site-index.d.mts +1 -1
- package/types/engine/sql-tables.d.mts +185 -0
- package/types/engine/subtype-registry.d.mts +49 -0
- package/types/engine/system-block.d.mts +40 -1
- package/types/engine/systems.d.mts +106 -0
- package/types/engine/web-wikilinks.d.mts +4 -2
- package/types/engine/wikilink-syntax.d.mts +10 -3
- package/types/engine/wikilinks.d.mts +41 -13
- package/types/engine/yaml-lint.d.mts +107 -0
- package/types/hm3/actors.d.mts +48 -0
- package/types/hm3/default-item-art.d.mts +42 -0
- package/types/hm3/document-subtypes.d.mts +24 -0
- package/types/hm3/index.d.mts +7 -0
- package/types/hm3/item-builders.d.mts +11 -0
- package/types/hm3/item-fields.d.mts +12 -0
- package/types/hm3/items.d.mts +23 -0
- package/types/hm3/template-priority.d.mts +21 -0
- package/types/manifest.d.mts +22 -1
- package/types/release.d.mts +9 -4
- package/types/sohl/actors.d.mts +7 -74
- package/types/sohl/being-info.d.mts +8 -27
- package/types/sohl/default-item-art.d.mts +5 -3
- package/types/sohl/items.d.mts +17 -32
- package/engine/foreign-manifests.mjs +0 -126
- package/engine/kb-manifest.mjs +0 -490
- package/types/engine/foreign-manifests.d.mts +0 -43
- package/types/engine/kb-manifest.d.mts +0 -241
|
@@ -1,36 +1,20 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* The `{#slug}` anchors a note's body declares, with where each one sits.
|
|
3
|
-
*
|
|
4
|
-
* Only headings carrying an explicit anchor are collected. A bare `#` heading
|
|
5
|
-
* also starts a journal page, but it declares no slug, so nothing can address
|
|
6
|
-
* it with `#…` — listing it would offer a link that cannot be written.
|
|
7
|
-
*
|
|
8
|
-
* @param {string} body - The note's markdown body, frontmatter already removed.
|
|
9
|
-
* @param {number} [bodyLine] - The 1-based file line the body starts on, from
|
|
10
|
-
* `parseMarkdownFile`. Anchors are reported at their position in the **file**,
|
|
11
|
-
* so an editor can jump straight to one; passing nothing numbers from the body.
|
|
12
|
-
* @returns {Array<{slug: string, name: string, level: number, line: number}>}
|
|
13
|
-
* In document order.
|
|
14
|
-
*/
|
|
15
|
-
export function collectAnchors(body: string, bodyLine?: number): Array<{
|
|
16
|
-
slug: string;
|
|
17
|
-
name: string;
|
|
18
|
-
level: number;
|
|
19
|
-
line: number;
|
|
20
|
-
}>;
|
|
21
1
|
/**
|
|
22
2
|
* The address a wikilink writes to reach a note, or `null` when it has none.
|
|
23
3
|
*
|
|
24
4
|
* A wikilink target is an address: `being-aurochs` locally, or
|
|
25
5
|
* `sohl-being-aurochs` from another package (`readQualifier` also accepts
|
|
26
|
-
* `being/aurochs`, the same two fields with a different separator). Both
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
*
|
|
33
|
-
*
|
|
6
|
+
* `being/aurochs`, the same two fields with a different separator). Both are
|
|
7
|
+
* *partial* addresses — the canonical one this field records is
|
|
8
|
+
* `sohl-sohl-being-aurochs`, and a target that omits the system is matched
|
|
9
|
+
* with that segment wildcarded. Every form is already derivable from `type` and
|
|
10
|
+
* `shortcode`, which every record carries, plus the system the type compiles
|
|
11
|
+
* into — so this field adds no information. What it adds is the *rule*: the
|
|
12
|
+
* lowercasing, the hyphen join and the system lookup live in one place, and a
|
|
13
|
+
* consumer that reimplements them slightly differently gets a lookup that
|
|
14
|
+
* matches nothing and says nothing about why. That is a real failure, not a
|
|
15
|
+
* hypothetical one — it is precisely how a resolver keyed on a bare
|
|
16
|
+
* `type/shortcode` silently misses every canonical
|
|
17
|
+
* `pkg-system-type-shortcode` entry.
|
|
34
18
|
*
|
|
35
19
|
* Derived by the same functions the link manifest and the site build use, so an
|
|
36
20
|
* index cannot disagree with either about where a note lives.
|
|
@@ -38,8 +22,8 @@ export function collectAnchors(body: string, bodyLine?: number): Array<{
|
|
|
38
22
|
* @param {Record<string, any>} frontmatter - The note's parsed frontmatter.
|
|
39
23
|
* @param {string} contentPackage - The package the tree compiles as.
|
|
40
24
|
* @returns {{slug: string, canonical: string}|null} `slug` is what goes inside
|
|
41
|
-
* `[[…]]` within this package; `canonical` is the
|
|
42
|
-
* manifest files the note under. `null` for a note with no type or no
|
|
25
|
+
* `[[…]]` within this package; `canonical` is the fully qualified key the
|
|
26
|
+
* manifest files the note under, carrying the package and the system as well. `null` for a note with no type or no
|
|
43
27
|
* shortcode, which has no address at all and is stated as such rather than
|
|
44
28
|
* left for every reader to rediscover.
|
|
45
29
|
*/
|
|
@@ -107,40 +91,21 @@ export function asciiName(name: unknown): string | null;
|
|
|
107
91
|
* a consumer iterating it should not have to check first.
|
|
108
92
|
*/
|
|
109
93
|
export function asciiAliases(aliases: unknown): Array<string>;
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
* @param {number} [options.bodyLine] - The 1-based file line the body starts on.
|
|
119
|
-
* @returns {Record<string, any>} The record, keys sorted at every depth.
|
|
120
|
-
* @throws {Error} When the note carries a key this module derives, which would
|
|
121
|
-
* otherwise be overwritten without a word.
|
|
122
|
-
*/
|
|
123
|
-
export function buildIndexRecord({ frontmatter, relPath, contentPackage, body, bodyLine }: {
|
|
124
|
-
frontmatter: Record<string, any>;
|
|
125
|
-
relPath: string;
|
|
126
|
-
contentPackage: string;
|
|
127
|
-
body?: string | undefined;
|
|
128
|
-
bodyLine?: number | undefined;
|
|
94
|
+
export function buildIndexRecord({ frontmatter, relPath, absPath, contentPackage, body, bodyLine, manifest, }: {
|
|
95
|
+
frontmatter: any;
|
|
96
|
+
relPath: any;
|
|
97
|
+
absPath: any;
|
|
98
|
+
contentPackage: any;
|
|
99
|
+
body: any;
|
|
100
|
+
bodyLine: any;
|
|
101
|
+
manifest: any;
|
|
129
102
|
}): Record<string, any>;
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
* @param {Array<string>} [options.skipDirectories] - Directory names to skip.
|
|
137
|
-
* @returns {Array<Record<string, any>>} The records, in a total order that does
|
|
138
|
-
* not depend on directory-read order.
|
|
139
|
-
*/
|
|
140
|
-
export function collectContentIndex(contentBase: string, { contentPackage, skipDirectories }: {
|
|
141
|
-
contentPackage: string;
|
|
142
|
-
skipDirectories?: string[] | undefined;
|
|
143
|
-
}): Array<Record<string, any>>;
|
|
103
|
+
export function collectContentIndex(contentBase: any, { contentPackage, skipDirectories, manifest, problems }: {
|
|
104
|
+
contentPackage: any;
|
|
105
|
+
skipDirectories: any;
|
|
106
|
+
manifest: any;
|
|
107
|
+
problems: any;
|
|
108
|
+
}): Record<string, any>[];
|
|
144
109
|
/**
|
|
145
110
|
* Serialize records as JSON Lines.
|
|
146
111
|
*
|
|
@@ -150,6 +115,36 @@ export function collectContentIndex(contentBase: string, { contentPackage, skipD
|
|
|
150
115
|
* the file is exactly the lines it holds.
|
|
151
116
|
*/
|
|
152
117
|
export function serializeContentIndex(records: Array<Record<string, any>>): string;
|
|
118
|
+
/**
|
|
119
|
+
* The index records for a content tree, without writing anything.
|
|
120
|
+
*
|
|
121
|
+
* The half of {@link emitContentIndex} that derives rather than emits, so a
|
|
122
|
+
* pass that needs the corpus in memory — a SQL content table, the link check,
|
|
123
|
+
* and in time every reader #243 converts — builds it the same way the artifact
|
|
124
|
+
* is built, rather than by walking and parsing again with its own idea of the
|
|
125
|
+
* scope.
|
|
126
|
+
*
|
|
127
|
+
* @param {object} [opts]
|
|
128
|
+
* @param {string} [opts.contentBase] - The tree, defaulting to the configured one.
|
|
129
|
+
* @param {object} [opts.config] - Resolved configuration, defaulting to ambient.
|
|
130
|
+
* @param {readonly string[]} [opts.skipDirectories] - The walk's scope, for a
|
|
131
|
+
* caller that resolved one of its own; defaults to the resolved
|
|
132
|
+
* configuration's. Stated separately from `config` because a caller that was
|
|
133
|
+
* *handed* a scope must be able to pass it on rather than have it silently
|
|
134
|
+
* replaced by the one its configuration happens to carry (#243).
|
|
135
|
+
* @param {object[]} [opts.problems] - Supplied by a **reader**: a note that
|
|
136
|
+
* cannot be recorded is pushed here as a diagnostic and skipped, instead of
|
|
137
|
+
* aborting the derivation. Omitted, the note throws — which is the contract
|
|
138
|
+
* the emitter needs, since an index missing a note asserts that it does not
|
|
139
|
+
* exist.
|
|
140
|
+
* @returns {object[]} One record per note, plus one per documentation entry.
|
|
141
|
+
*/
|
|
142
|
+
export function indexRecordsFor({ contentBase, config, skipDirectories, problems }?: {
|
|
143
|
+
contentBase?: string | undefined;
|
|
144
|
+
config?: object | undefined;
|
|
145
|
+
skipDirectories?: readonly string[] | undefined;
|
|
146
|
+
problems?: object[] | undefined;
|
|
147
|
+
}): object[];
|
|
153
148
|
/**
|
|
154
149
|
* Emit this package's content index.
|
|
155
150
|
*
|
|
@@ -175,20 +170,9 @@ export function emitContentIndex({ contentBase, outDir, config }?: {
|
|
|
175
170
|
notes: number;
|
|
176
171
|
bytes: number;
|
|
177
172
|
};
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
* matches what the content-table expander puts on the same field, so a query
|
|
185
|
-
* reads the same value from either. `file` namespaces the note's place in the
|
|
186
|
-
* tree, again matching the expander's `file.*`.
|
|
187
|
-
*
|
|
188
|
-
* Both are checked rather than assumed: `folder` is real frontmatter on most
|
|
189
|
-
* notes, so the neighbouring names are close enough to a real key that a silent
|
|
190
|
-
* overwrite is a plausible future rather than a hypothetical one.
|
|
191
|
-
*
|
|
192
|
-
* @type {ReadonlyArray<string>}
|
|
193
|
-
*/
|
|
194
|
-
export const DERIVED_KEYS: ReadonlyArray<string>;
|
|
173
|
+
import { collectAnchors } from "./anchors.mjs";
|
|
174
|
+
import { authoredFrontmatter } from "./index-records.mjs";
|
|
175
|
+
import { DERIVED_KEYS } from "./index-records.mjs";
|
|
176
|
+
import { isNoteRecord } from "./index-records.mjs";
|
|
177
|
+
import { noteFile } from "./index-records.mjs";
|
|
178
|
+
export { collectAnchors, authoredFrontmatter, DERIVED_KEYS, isNoteRecord, noteFile };
|
|
@@ -1,12 +1,43 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Every `{#anchor}` a note declares on a heading.
|
|
3
3
|
*
|
|
4
|
+
* **Read from the content index's reader, not a second one.** This module kept
|
|
5
|
+
* its own until #243, and the two disagreed: it matched `{#([a-z0-9-]+)}` while
|
|
6
|
+
* {@link module:engine/content-index.collectAnchors} matches `{#([^}]+)}`, so
|
|
7
|
+
* an anchor with a capital in it — `{#CalendarFormat}` — existed for the index
|
|
8
|
+
* and for the compiler and did not exist for the link checker. Nothing links to
|
|
9
|
+
* one today, so the disagreement was latent; the first link to one would have
|
|
10
|
+
* been reported dead against a heading plainly present in the file.
|
|
11
|
+
*
|
|
12
|
+
* The specification puts no charset on the id: "`#id` represents an id anchor
|
|
13
|
+
* named `id`". The narrower pattern was this module's invention, which is the
|
|
14
|
+
* argument for there being one reader rather than a well-chosen one.
|
|
15
|
+
*
|
|
4
16
|
* @param {string} body - The note's markdown body.
|
|
5
17
|
* @returns {Set<string>} The declared anchor slugs.
|
|
6
18
|
*/
|
|
7
19
|
export function anchorsOf(body: string): Set<string>;
|
|
8
20
|
/**
|
|
9
|
-
*
|
|
21
|
+
* Read a content tree into the index a link resolves against.
|
|
22
|
+
*
|
|
23
|
+
* **The corpus comes from the content index, not from a walk of this module's
|
|
24
|
+
* own** (#243). Every pass used to answer "which files are the content?" for
|
|
25
|
+
* itself and throw the answer away; this one now reads
|
|
26
|
+
* {@link module:engine/content-index.indexRecordsFor}, which is the same
|
|
27
|
+
* derivation the published artifact and the compilers are driven from. So a
|
|
28
|
+
* note the index records is a note the link check sees, and the addresses and
|
|
29
|
+
* anchors it resolves against are the ones every other pass will emit — rather
|
|
30
|
+
* than a second derivation that agrees with them only by inspection. That was
|
|
31
|
+
* not hypothetical: this module carried its own anchor reader until the anchor
|
|
32
|
+
* half of #243, and the two disagreed about which anchors existed.
|
|
33
|
+
*
|
|
34
|
+
* **The file is opened for its bytes and nothing else.** The index deliberately
|
|
35
|
+
* carries no note *body*, and a link lives in the body — so each note is read
|
|
36
|
+
* once, here, for the prose. Everything *about* the note — its frontmatter, its
|
|
37
|
+
* addresses, its anchors — is already in the record, and none of it is derived
|
|
38
|
+
* a second time. That is one read per note rather than the two this module did
|
|
39
|
+
* before, since the walk read the file and it then read it again for the raw
|
|
40
|
+
* text.
|
|
10
41
|
*
|
|
11
42
|
* The index mirrors what both builds construct, including the two addresses a
|
|
12
43
|
* doc-carrying note answers to: `type/shortcode` for the document, and
|
|
@@ -17,14 +48,28 @@ export function anchorsOf(body: string): Set<string>;
|
|
|
17
48
|
*
|
|
18
49
|
* @param {string} contentBase - Root of the content tree.
|
|
19
50
|
* @param {object} [opts]
|
|
20
|
-
* @param {
|
|
21
|
-
*
|
|
22
|
-
*
|
|
51
|
+
* @param {object} [opts.config] - The resolved build configuration, whose
|
|
52
|
+
* fetched dependency indexes foreign addresses resolve through (#239), and
|
|
53
|
+
* whose `contentPackage` every local address is built from. Omitted, the
|
|
54
|
+
* ambient configuration is resolved and no cross-package address resolves.
|
|
55
|
+
* @param {readonly string[]} [opts.skipDirectories] - The walk's scope, passed
|
|
56
|
+
* on to the index rather than defaulted away.
|
|
57
|
+
* @param {Map<string, object[]>} [opts.sqlTables] - Prepared `sql` results, by
|
|
58
|
+
* note path.
|
|
59
|
+
* @param {object[]} [opts.records] - Index records the caller already derived,
|
|
60
|
+
* so a command that also needs them — every one of them does, to answer its
|
|
61
|
+
* `sql` tables — enumerates the corpus once rather than twice.
|
|
62
|
+
* @param {object[]} [opts.problems] - Collects the notes the index cannot
|
|
63
|
+
* record, as diagnostics, instead of letting one of them abort the check
|
|
64
|
+
* before it has reported anything else.
|
|
23
65
|
* @returns {object} The notes, the index, and the resolvers built over it.
|
|
24
66
|
*/
|
|
25
|
-
export function buildLinkIndex(contentBase: string, {
|
|
26
|
-
|
|
67
|
+
export function buildLinkIndex(contentBase: string, { config, skipDirectories, sqlTables, records, problems }?: {
|
|
68
|
+
config?: object | undefined;
|
|
27
69
|
skipDirectories?: readonly string[] | undefined;
|
|
70
|
+
sqlTables?: Map<string, object[]> | undefined;
|
|
71
|
+
records?: object[] | undefined;
|
|
72
|
+
problems?: object[] | undefined;
|
|
28
73
|
}): object;
|
|
29
74
|
/**
|
|
30
75
|
* Every defect in the addresses a package homepage carries.
|
|
@@ -46,7 +91,7 @@ export function buildLinkIndex(contentBase: string, { manifestDir, skipDirectori
|
|
|
46
91
|
* and what replaced it, so this is a fact rather than a guess — and it is
|
|
47
92
|
* exactly the SoHL defect.
|
|
48
93
|
* - A **hardcoded absolute URL** into this package's own prefix, or into one a
|
|
49
|
-
*
|
|
94
|
+
* a fetched index names. Every one of them has a better form to write, which
|
|
50
95
|
* is why every one is reported — including a bare `/<package>/`, which names
|
|
51
96
|
* another package's landing (#87).
|
|
52
97
|
*
|
|
@@ -18,13 +18,22 @@ export function isValidShortcode(value: unknown): boolean;
|
|
|
18
18
|
* ignores. Defaults to the configured list.
|
|
19
19
|
* @param {string} [opts.contentPackage] - The package this tree builds, for the
|
|
20
20
|
* homepage rule. Dropped from that finding when unknown rather than guessed.
|
|
21
|
+
* @param {object} [opts.config] - The resolved build configuration, which the
|
|
22
|
+
* corpus is derived against.
|
|
23
|
+
* @param {readonly object[]} [opts.records] - Index records the caller already
|
|
24
|
+
* derived, so a command reads one corpus (#243).
|
|
25
|
+
* @param {object[]} [opts.problems] - Collects the notes the index cannot
|
|
26
|
+
* record, instead of letting one of them silence the lint.
|
|
21
27
|
* @returns {{findings: Array<{file: string, line?: number, column?: number,
|
|
22
28
|
* severity: "error"|"warning", message: string}>, notes: number,
|
|
23
29
|
* keys: number}} The findings, and what was inspected to produce them.
|
|
24
30
|
*/
|
|
25
|
-
export function lintContentTree(contentBase: string, { skipDirectories, contentPackage }?: {
|
|
31
|
+
export function lintContentTree(contentBase: string, { skipDirectories, contentPackage, config, records, problems }?: {
|
|
26
32
|
skipDirectories?: readonly string[] | undefined;
|
|
27
33
|
contentPackage?: string | undefined;
|
|
34
|
+
config?: object | undefined;
|
|
35
|
+
records?: readonly object[] | undefined;
|
|
36
|
+
problems?: object[] | undefined;
|
|
28
37
|
}): {
|
|
29
38
|
findings: Array<{
|
|
30
39
|
file: string;
|
|
@@ -2,7 +2,8 @@
|
|
|
2
2
|
* The **content** package: the distribution unit this repository's notes belong
|
|
3
3
|
* to, and the **address namespace** every one of them is published under.
|
|
4
4
|
*
|
|
5
|
-
* It is the first segment of every canonical key
|
|
5
|
+
* It is the first segment of every canonical key
|
|
6
|
+
* (`package-system-type-shortcode`, so `sohl-none-doc-gear`), the name
|
|
6
7
|
* of the link manifest this build emits (`sohl.json`), and the package a
|
|
7
8
|
* cross-package wikilink writes to reach one of these notes. So it is the
|
|
8
9
|
* repository's identity in the address space, not a switch — and never dead
|
|
@@ -73,52 +73,34 @@ export function selectRows(spec: object, docs: Array<ContentTableDoc>, self?: Co
|
|
|
73
73
|
* @returns {string} The markdown table (no trailing newline).
|
|
74
74
|
*/
|
|
75
75
|
export function renderContentTable(spec: object, rows: Array<ContentTableDoc>, linkable: (doc: ContentTableDoc) => boolean, self?: ContentTableDoc): string;
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
* A block that cannot be honoured — malformed or unsupported — is left in the
|
|
80
|
-
* body verbatim and reported in `errors`, so the failure is visible in the
|
|
81
|
-
* output as well as on the console. Every other code fence, and every code
|
|
82
|
-
* span, is left alone (that is how the syntax is documented).
|
|
83
|
-
*
|
|
84
|
-
* A query that matches **no** note is not an error: it renders as an empty
|
|
85
|
-
* table (headers only), which is what the author already sees in Obsidian, and
|
|
86
|
-
* a category with no content yet is a normal state of the corpus rather than a
|
|
87
|
-
* broken build.
|
|
88
|
-
*
|
|
89
|
-
* @param {string} markdown - The note body, frontmatter already stripped.
|
|
90
|
-
* @param {object} ctx
|
|
91
|
-
* @param {Array<ContentTableDoc>} ctx.docs - The searchable universe: every
|
|
92
|
-
* content note the caller considers in scope.
|
|
93
|
-
* @param {(doc: ContentTableDoc) => boolean} [ctx.linkable] - Whether a note can
|
|
94
|
-
* be linked to from a cell; defaults to never.
|
|
95
|
-
* @param {string} [ctx.source] - The note being expanded, for error reports.
|
|
96
|
-
* @param {ContentTableDoc} [ctx.self] - The note being expanded, as a searchable
|
|
97
|
-
* doc: what a query's `this` reads.
|
|
98
|
-
* @returns {{markdown: string, errors: Array<{source: string, directive: string,
|
|
99
|
-
* reason: string, line: number}>, lineMap: Array<{line: number,
|
|
100
|
-
* generated: boolean}>}} `lineMap` is parallel to the emitted lines and says
|
|
101
|
-
* which authored line each came from, so a diagnostic about the expanded
|
|
102
|
-
* body can name an authored position (#17). An `errors` entry carries the
|
|
103
|
-
* 0-based line of the directive that failed, for the same reason.
|
|
104
|
-
*/
|
|
105
|
-
export function expandContentTables(markdown: string, { docs, linkable, source, self }?: {
|
|
106
|
-
docs: Array<ContentTableDoc>;
|
|
107
|
-
linkable?: ((doc: ContentTableDoc) => boolean) | undefined;
|
|
76
|
+
export function expandContentTables(markdown: any, { docs, linkable, source, self, sqlTables, }?: {
|
|
77
|
+
docs?: never[] | undefined;
|
|
78
|
+
linkable?: (() => boolean) | undefined;
|
|
108
79
|
source?: string | undefined;
|
|
109
|
-
self?:
|
|
80
|
+
self?: undefined;
|
|
81
|
+
sqlTables?: undefined;
|
|
110
82
|
}): {
|
|
111
83
|
markdown: string;
|
|
112
|
-
errors:
|
|
84
|
+
errors: ({
|
|
113
85
|
source: string;
|
|
114
86
|
directive: string;
|
|
115
|
-
reason:
|
|
87
|
+
reason: any;
|
|
116
88
|
line: number;
|
|
117
|
-
|
|
118
|
-
|
|
89
|
+
column: number;
|
|
90
|
+
} | {
|
|
91
|
+
source: string;
|
|
92
|
+
directive: string;
|
|
93
|
+
reason: any;
|
|
119
94
|
line: number;
|
|
120
|
-
|
|
121
|
-
}
|
|
95
|
+
column?: undefined;
|
|
96
|
+
})[];
|
|
97
|
+
warnings: {
|
|
98
|
+
source: string;
|
|
99
|
+
line: number;
|
|
100
|
+
column: number;
|
|
101
|
+
reason: string;
|
|
102
|
+
}[];
|
|
103
|
+
lineMap: any[];
|
|
122
104
|
};
|
|
123
105
|
/**
|
|
124
106
|
* One content note as a content build hands it to the expander: its parsed
|
|
@@ -53,6 +53,13 @@ export function defineDocumentSubtypes({ system, block, types }?: {
|
|
|
53
53
|
/**
|
|
54
54
|
* The row a system declares for a note type, or nothing.
|
|
55
55
|
*
|
|
56
|
+
* The **one** place a map is indexed, so the retirement window for a renamed
|
|
57
|
+
* type is honoured everywhere a row is asked for — {@link documentSubtype},
|
|
58
|
+
* {@link mapsNoteType}, {@link noteTypesFor}'s consumers and
|
|
59
|
+
* {@link systemOf} all arrive here. A note still spelling `armorgear` finds the
|
|
60
|
+
* `armor` row, compiles into the `armorgear` document it always did, and keeps
|
|
61
|
+
* the address it publishes at; only the *report* tells it to move (#78).
|
|
62
|
+
*
|
|
56
63
|
* @param {DocumentSubtypeMap} map - The system's map.
|
|
57
64
|
* @param {string|undefined} noteType - The note's declared `type`.
|
|
58
65
|
* @returns {Readonly<DocumentSubtypeRow>|undefined} The row, or `undefined`
|
|
@@ -152,9 +159,17 @@ export function documentSubtype(map: DocumentSubtypeMap, noteType: string | unde
|
|
|
152
159
|
* whole point of the issue.
|
|
153
160
|
*
|
|
154
161
|
* A **retired** spelling is refused by name before any of that. Without it a
|
|
155
|
-
* reference left behind by a
|
|
156
|
-
*
|
|
157
|
-
*
|
|
162
|
+
* reference left behind by a merge would take the unmapped fallback and address
|
|
163
|
+
* a document of the old name — resolving silently, which is precisely what a
|
|
164
|
+
* retirement exists to stop.
|
|
165
|
+
*
|
|
166
|
+
* A **renamed** spelling is the opposite case and resolves normally, through
|
|
167
|
+
* the same {@link currentType} every other lookup goes through (#78). It has to
|
|
168
|
+
* be this side as much as the note's own `type:`: the overwhelming majority of
|
|
169
|
+
* the 31,000 occurrences of the old names are `(type, shortcode)` references
|
|
170
|
+
* inside a being's `items:` list, so a window that resolved notes but not
|
|
171
|
+
* references would silently drop 30,000 embedded items rather than compile them
|
|
172
|
+
* unchanged.
|
|
158
173
|
*
|
|
159
174
|
* @param {DocumentSubtypeMap} map - The system's map.
|
|
160
175
|
* @param {string|undefined} noteType - The type the reference names.
|
|
@@ -163,6 +178,24 @@ export function documentSubtype(map: DocumentSubtypeMap, noteType: string | unde
|
|
|
163
178
|
* @returns {ReferencedSubtype} The subtype, or why there is none.
|
|
164
179
|
*/
|
|
165
180
|
export function referencedSubtype(map: DocumentSubtypeMap, noteType: string | undefined, document: string): ReferencedSubtype;
|
|
181
|
+
/**
|
|
182
|
+
* Which system defines the document a note of this type compiles into.
|
|
183
|
+
*
|
|
184
|
+
* A journal, a macro, a scene and an item's documentation journal belong to no
|
|
185
|
+
* system and answer {@link NO_SYSTEM}; an item or an actor belongs to whichever
|
|
186
|
+
* system's map claims its note type.
|
|
187
|
+
*
|
|
188
|
+
* `maps` is required rather than defaulted, so this stays reachable from any
|
|
189
|
+
* pass: the registry of maps lives in `note-claims.mjs`, which imports half the
|
|
190
|
+
* engine, and defaulting to it here would drag that in wherever an address is
|
|
191
|
+
* built.
|
|
192
|
+
*
|
|
193
|
+
* @param {string} type - The note's `type`.
|
|
194
|
+
* @param {readonly object[]} maps - The document-subtype maps this build ships.
|
|
195
|
+
* @returns {string} The system id, or {@link NO_SYSTEM}.
|
|
196
|
+
*/
|
|
197
|
+
export function systemOf(type: string, maps: readonly object[]): string;
|
|
198
|
+
export { NO_SYSTEM };
|
|
166
199
|
/**
|
|
167
200
|
* One row of a system's map: what a note of this type becomes.
|
|
168
201
|
*/
|
|
@@ -231,3 +264,4 @@ export type ReferencedSubtype = {
|
|
|
231
264
|
*/
|
|
232
265
|
problem?: string | undefined;
|
|
233
266
|
};
|
|
267
|
+
import { NO_SYSTEM } from "./systems.mjs";
|
|
@@ -1,3 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Whether a resolution read a field from the position it is being swept off.
|
|
3
|
+
*
|
|
4
|
+
* The sweep's progress signal, in one predicate so the compile-time report and
|
|
5
|
+
* the frontmatter lint cannot disagree about what counts — the role
|
|
6
|
+
* {@link module:engine/retired-fields.declaresRetiredAlias} plays for a renamed
|
|
7
|
+
* field.
|
|
8
|
+
*
|
|
9
|
+
* **Only for a field that declares a `legacyKey`.** Every other field's
|
|
10
|
+
* in-block position is simply where it lives; reporting those would put a
|
|
11
|
+
* finding on every field of every note in every tree, which is #126's corpus
|
|
12
|
+
* migration rather than a signal anyone could act on.
|
|
13
|
+
*
|
|
14
|
+
* @param {FieldSpec} field - The declaration.
|
|
15
|
+
* @param {import("./system-block.mjs").FieldSource} from - Where
|
|
16
|
+
* {@link resolveFieldValue} said the value came from.
|
|
17
|
+
* @returns {boolean} True when the value came from the retiring position.
|
|
18
|
+
*/
|
|
19
|
+
export function readsLegacyKey(field: FieldSpec, from: import("./system-block.mjs").FieldSource): boolean;
|
|
1
20
|
/**
|
|
2
21
|
* Read one declared field out of a note's frontmatter.
|
|
3
22
|
*
|
|
@@ -14,10 +33,16 @@
|
|
|
14
33
|
* @param {string} [options.block="sohl"] - Which system's block to resolve
|
|
15
34
|
* against. The default is the one block every existing tree authors; a
|
|
16
35
|
* second system passes its own.
|
|
36
|
+
* @param {(field: FieldSpec) => void} [options.onLegacyKey] - Called with each
|
|
37
|
+
* field read from the position it is being swept off (#305). A callback
|
|
38
|
+
* rather than a returned list because the caller is a compiler, which already
|
|
39
|
+
* knows the note and how to locate a key in it; this module knows neither and
|
|
40
|
+
* would have to invent a finding shape to say so.
|
|
17
41
|
* @returns {any} The value to emit.
|
|
18
42
|
*/
|
|
19
|
-
export function readField(field: FieldSpec, fm: object, { block }?: {
|
|
43
|
+
export function readField(field: FieldSpec, fm: object, { block, onLegacyKey }?: {
|
|
20
44
|
block?: string | undefined;
|
|
45
|
+
onLegacyKey?: ((field: FieldSpec) => void) | undefined;
|
|
21
46
|
}): any;
|
|
22
47
|
/**
|
|
23
48
|
* Turn a field declaration into the builder it declares.
|
|
@@ -27,10 +52,14 @@ export function readField(field: FieldSpec, fm: object, { block }?: {
|
|
|
27
52
|
* @param {string} [options.block="sohl"] - Which system's block the builder
|
|
28
53
|
* reads. One declaration compiles against any block, which is what lets two
|
|
29
54
|
* systems declare the same shared source and different destinations.
|
|
55
|
+
* @param {(field: FieldSpec) => void} [options.onLegacyKey] - Passed through to
|
|
56
|
+
* {@link readField}: called with each field the note authored at the position
|
|
57
|
+
* it is being swept off (#305).
|
|
30
58
|
* @returns {(fm: object) => object} A `system`-block builder.
|
|
31
59
|
*/
|
|
32
|
-
export function buildFromFields(fields: readonly FieldSpec[], { block }?: {
|
|
60
|
+
export function buildFromFields(fields: readonly FieldSpec[], { block, onLegacyKey }?: {
|
|
33
61
|
block?: string | undefined;
|
|
62
|
+
onLegacyKey?: ((field: FieldSpec) => void) | undefined;
|
|
34
63
|
}): (fm: object) => object;
|
|
35
64
|
/**
|
|
36
65
|
* The fields of a declaration an author actually writes.
|
|
@@ -44,7 +73,6 @@ export function buildFromFields(fields: readonly FieldSpec[], { block }?: {
|
|
|
44
73
|
* @returns {FieldSpec[]} Only the fields with a frontmatter `name`.
|
|
45
74
|
*/
|
|
46
75
|
export function authoredFields(fields: readonly FieldSpec[]): FieldSpec[];
|
|
47
|
-
export { setPath };
|
|
48
76
|
/**
|
|
49
77
|
* @typedef {object} FieldSpec
|
|
50
78
|
* @property {string} to - Dotted path in the emitted `system` block — and,
|
|
@@ -60,9 +88,28 @@ export { setPath };
|
|
|
60
88
|
* to share a name. They constantly do not — one shared `data.portrait` feeds
|
|
61
89
|
* `sohl.system.portrait` *and* `hm3.system.bioImage` — so the source is
|
|
62
90
|
* declared rather than matched by spelling (#58). The in-block position is
|
|
63
|
-
* still read, second, until #126 moves the corpus off it
|
|
91
|
+
* still read, second, until #126 moves the corpus off it — keyed on
|
|
92
|
+
* `legacyKey` where the two spellings differ.
|
|
64
93
|
*
|
|
65
94
|
* Absent means the value is not authored at all — see `value`.
|
|
95
|
+
* @property {string} [legacyKey] - **The key this field is authored at inside
|
|
96
|
+
* the system block** — the second position of the resolution order — when
|
|
97
|
+
* that is not `name`. Absent, the position is keyed on `name`.
|
|
98
|
+
*
|
|
99
|
+
* The two were one property until #305, which held only while a field's
|
|
100
|
+
* shared source and its in-block key were the same word. `data:` (#128) ended
|
|
101
|
+
* that: a shared source is a path into a container, so `data.species` and
|
|
102
|
+
* `species` name two different places and no single value reached both.
|
|
103
|
+
* `name: "species"` could not see `data.species`; `name: "data.species"`
|
|
104
|
+
* could not see `hm3.species`; and each yielded the field's **default**
|
|
105
|
+
* wherever only the other position was authored, with the note compiling and
|
|
106
|
+
* the value simply gone.
|
|
107
|
+
*
|
|
108
|
+
* Declaring both restores the shape every other retirement in this package
|
|
109
|
+
* uses — read both spellings, let the current one win, and *report* the
|
|
110
|
+
* retiring one — so a field can move into `data:` while the corpus catches
|
|
111
|
+
* up, instead of on a flag day across four repositories. A declaration that
|
|
112
|
+
* names one is mid-sweep by construction; see {@link readsLegacyKey}.
|
|
66
113
|
* @property {string} [topLevelMeans] - **What the note's top-level key of this
|
|
67
114
|
* name means instead** — declared only where it means something else, and
|
|
68
115
|
* stating it removes the shared top-level position from this field's
|
|
@@ -190,11 +237,33 @@ export type FieldSpec = {
|
|
|
190
237
|
* to share a name. They constantly do not — one shared `data.portrait` feeds
|
|
191
238
|
* `sohl.system.portrait` *and* `hm3.system.bioImage` — so the source is
|
|
192
239
|
* declared rather than matched by spelling (#58). The in-block position is
|
|
193
|
-
* still read, second, until #126 moves the corpus off it
|
|
240
|
+
* still read, second, until #126 moves the corpus off it — keyed on
|
|
241
|
+
* `legacyKey` where the two spellings differ.
|
|
194
242
|
*
|
|
195
243
|
* Absent means the value is not authored at all — see `value`.
|
|
196
244
|
*/
|
|
197
245
|
name?: string | undefined;
|
|
246
|
+
/**
|
|
247
|
+
* - **The key this field is authored at inside
|
|
248
|
+
* the system block** — the second position of the resolution order — when
|
|
249
|
+
* that is not `name`. Absent, the position is keyed on `name`.
|
|
250
|
+
*
|
|
251
|
+
* The two were one property until #305, which held only while a field's
|
|
252
|
+
* shared source and its in-block key were the same word. `data:` (#128) ended
|
|
253
|
+
* that: a shared source is a path into a container, so `data.species` and
|
|
254
|
+
* `species` name two different places and no single value reached both.
|
|
255
|
+
* `name: "species"` could not see `data.species`; `name: "data.species"`
|
|
256
|
+
* could not see `hm3.species`; and each yielded the field's **default**
|
|
257
|
+
* wherever only the other position was authored, with the note compiling and
|
|
258
|
+
* the value simply gone.
|
|
259
|
+
*
|
|
260
|
+
* Declaring both restores the shape every other retirement in this package
|
|
261
|
+
* uses — read both spellings, let the current one win, and *report* the
|
|
262
|
+
* retiring one — so a field can move into `data:` while the corpus catches
|
|
263
|
+
* up, instead of on a flag day across four repositories. A declaration that
|
|
264
|
+
* names one is mid-sweep by construction; see {@link readsLegacyKey}.
|
|
265
|
+
*/
|
|
266
|
+
legacyKey?: string | undefined;
|
|
198
267
|
/**
|
|
199
268
|
* - **What the note's top-level key of this
|
|
200
269
|
* name means instead** — declared only where it means something else, and
|
|
@@ -284,4 +353,6 @@ export type FieldSpec = {
|
|
|
284
353
|
*/
|
|
285
354
|
describe: string;
|
|
286
355
|
};
|
|
356
|
+
import { legacyKeyOf } from "./system-block.mjs";
|
|
287
357
|
import { setPath } from "./system-block.mjs";
|
|
358
|
+
export { legacyKeyOf, setPath };
|