@heroiclands/package-build 20.3.1 → 20.5.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 +351 -0
- package/CONTENT.md +322 -110
- package/README.md +21 -10
- package/bin/content-build.mjs +204 -92
- package/bin/package-build.mjs +48 -15
- package/config.mjs +6 -8
- package/content-config.mjs +518 -151
- package/docs/api.md +1352 -0
- package/docs/commands.md +1609 -0
- package/docs/configuration.md +1432 -0
- package/docs/content-format.md +73 -74
- package/docs/diagnostics.md +356 -0
- package/docs/getting-started.md +813 -0
- package/docs/project-setup.md +469 -0
- package/e2e.mjs +4 -4
- package/engine/actor-compiler.mjs +64 -61
- package/engine/address-charset.mjs +6 -6
- package/engine/address-diff.mjs +57 -52
- package/engine/anchored-sections.mjs +1 -1
- package/engine/anchors.mjs +1 -1
- package/engine/base-compiler.mjs +42 -38
- package/engine/bundle-notes.mjs +4 -4
- package/engine/bundles.mjs +17 -8
- package/engine/code-fences.mjs +3 -3
- package/engine/compendiums.mjs +8 -8
- package/engine/compile-corpus.mjs +4 -5
- package/engine/content-address.mjs +24 -24
- package/engine/content-charset.mjs +1 -1
- package/engine/content-format-check.mjs +24 -25
- package/engine/content-format.mjs +8 -8
- package/engine/content-html.mjs +202 -0
- package/engine/content-icons.mjs +196 -165
- package/engine/content-index.mjs +61 -40
- package/engine/content-links.mjs +74 -41
- package/engine/content-lint.mjs +64 -34
- package/engine/content-package.mjs +7 -9
- package/engine/content-slug.mjs +2 -2
- package/engine/content-tables.mjs +38 -34
- package/engine/content-tree.mjs +1 -1
- package/engine/derived-fields.mjs +174 -0
- package/engine/diagnostics.mjs +3 -4
- package/engine/document-subtypes.mjs +6 -7
- package/engine/field-reference.mjs +8 -8
- package/engine/field-spec.mjs +29 -30
- package/engine/folder-notes.mjs +18 -16
- package/engine/foreign-catalog.mjs +8 -9
- package/engine/foundry-entries.mjs +19 -20
- package/engine/frontmatter-lint.mjs +95 -103
- package/engine/frontmatter.mjs +11 -11
- package/engine/generate.mjs +44 -39
- package/engine/helpers.mjs +147 -84
- package/engine/homepage.mjs +20 -22
- package/engine/ids.mjs +15 -15
- package/engine/index-records.mjs +4 -4
- package/engine/index.mjs +26 -10
- package/engine/item-compiler.mjs +54 -29
- package/engine/item-docs.mjs +11 -13
- package/engine/item-registry.mjs +13 -13
- package/engine/journals.mjs +24 -15
- package/engine/macros.mjs +3 -3
- package/engine/map-notes.mjs +6 -6
- package/engine/metadata-index.mjs +5 -5
- package/engine/note-claims.mjs +51 -41
- package/engine/note-ids.mjs +5 -5
- package/engine/note-package.mjs +4 -4
- package/engine/note-renames.mjs +3 -3
- package/engine/note-schemas.mjs +3 -8
- package/engine/note-vocabulary.mjs +56 -54
- package/engine/pack-config.mjs +39 -25
- package/engine/pack-router.mjs +0 -0
- package/engine/pdf-build.mjs +464 -0
- package/engine/pdf-fonts.mjs +420 -0
- package/engine/pdf-render.mjs +798 -0
- package/engine/pdf-toc.mjs +525 -0
- package/engine/prose-config.mjs +9 -9
- package/engine/prose-lint.mjs +12 -13
- package/engine/region-events.mjs +2 -2
- package/engine/retired-fields.mjs +24 -22
- package/engine/runtime-only-fields.mjs +1 -1
- package/engine/scene-levels.mjs +1 -1
- package/engine/scenes.mjs +33 -25
- package/engine/schema-check.mjs +21 -21
- package/engine/schema-extract.mjs +1 -1
- package/engine/site-build.mjs +66 -50
- package/engine/site-index.mjs +17 -18
- package/engine/sql-tables.mjs +21 -13
- package/engine/subtype-registry.mjs +5 -4
- package/engine/system-block.mjs +18 -19
- package/engine/systems.mjs +2 -2
- package/engine/web-wikilinks.mjs +41 -38
- package/engine/wikilink-syntax.mjs +16 -16
- package/engine/wikilinks.mjs +40 -42
- package/hm3/actors.mjs +30 -22
- package/hm3/document-subtypes.mjs +5 -5
- package/hm3/index.mjs +2 -2
- package/hm3/item-builders.mjs +1 -1
- package/hm3/item-fields.mjs +3 -3
- package/hm3/items.mjs +28 -8
- package/hm3/template-priority.mjs +2 -2
- package/lang.mjs +3 -3
- package/manifest.mjs +17 -20
- package/package.json +1 -2
- package/release.mjs +66 -6
- package/sohl/actors.mjs +36 -28
- package/sohl/affiliation-standings.mjs +2 -2
- package/sohl/being-info.mjs +5 -5
- package/sohl/default-item-art.mjs +5 -5
- package/sohl/document-subtypes.mjs +5 -5
- package/sohl/index.mjs +3 -3
- package/sohl/item-builders.mjs +7 -7
- package/sohl/item-fields.mjs +8 -9
- package/sohl/items.mjs +28 -6
- package/sohl/kb-passes.mjs +5 -5
- package/sohl/note-schemas.mjs +12 -12
- package/sohl/skill-base.mjs +1 -1
- package/types/content-config.d.mts +91 -39
- package/types/e2e.d.mts +3 -3
- package/types/engine/actor-compiler.d.mts +45 -40
- package/types/engine/address-charset.d.mts +6 -6
- package/types/engine/address-diff.d.mts +60 -6
- package/types/engine/base-compiler.d.mts +27 -21
- package/types/engine/bundle-notes.d.mts +3 -3
- package/types/engine/bundles.d.mts +10 -1
- package/types/engine/code-fences.d.mts +3 -3
- package/types/engine/compendiums.d.mts +3 -3
- package/types/engine/compile-corpus.d.mts +1 -1
- package/types/engine/content-address.d.mts +20 -20
- package/types/engine/content-format-check.d.mts +6 -6
- package/types/engine/content-format.d.mts +2 -2
- package/types/engine/content-html.d.mts +78 -0
- package/types/engine/content-icons.d.mts +139 -92
- package/types/engine/content-index.d.mts +59 -15
- package/types/engine/content-links.d.mts +8 -8
- package/types/engine/content-lint.d.mts +8 -6
- package/types/engine/content-package.d.mts +6 -8
- package/types/engine/content-tables.d.mts +49 -18
- package/types/engine/derived-fields.d.mts +101 -0
- package/types/engine/diagnostics.d.mts +2 -2
- package/types/engine/document-subtypes.d.mts +3 -3
- package/types/engine/field-spec.d.mts +37 -39
- package/types/engine/folder-notes.d.mts +4 -5
- package/types/engine/foreign-catalog.d.mts +3 -3
- package/types/engine/foundry-entries.d.mts +6 -7
- package/types/engine/frontmatter-lint.d.mts +10 -9
- package/types/engine/frontmatter.d.mts +7 -7
- package/types/engine/generate.d.mts +7 -7
- package/types/engine/helpers.d.mts +128 -56
- package/types/engine/homepage.d.mts +16 -18
- package/types/engine/ids.d.mts +13 -13
- package/types/engine/index-records.d.mts +3 -3
- package/types/engine/index.d.mts +6 -0
- package/types/engine/item-compiler.d.mts +21 -5
- package/types/engine/item-docs.d.mts +2 -2
- package/types/engine/item-registry.d.mts +6 -6
- package/types/engine/journals.d.mts +12 -3
- package/types/engine/map-notes.d.mts +2 -2
- package/types/engine/metadata-index.d.mts +4 -4
- package/types/engine/note-claims.d.mts +30 -22
- package/types/engine/note-ids.d.mts +4 -4
- package/types/engine/note-package.d.mts +1 -1
- package/types/engine/note-renames.d.mts +3 -3
- package/types/engine/note-vocabulary.d.mts +31 -203
- package/types/engine/pack-config.d.mts +7 -7
- package/types/engine/pack-router.d.mts +1 -1
- package/types/engine/pdf-build.d.mts +42 -0
- package/types/engine/pdf-fonts.d.mts +30 -0
- package/types/engine/pdf-render.d.mts +144 -0
- package/types/engine/pdf-toc.d.mts +114 -0
- package/types/engine/prose-config.d.mts +9 -9
- package/types/engine/prose-lint.d.mts +3 -4
- package/types/engine/region-events.d.mts +2 -2
- package/types/engine/retired-fields.d.mts +10 -9
- package/types/engine/scenes.d.mts +10 -1
- package/types/engine/schema-check.d.mts +13 -13
- package/types/engine/site-build.d.mts +52 -25
- package/types/engine/site-index.d.mts +3 -4
- package/types/engine/sql-tables.d.mts +11 -5
- package/types/engine/subtype-registry.d.mts +3 -3
- package/types/engine/system-block.d.mts +3 -3
- package/types/engine/web-wikilinks.d.mts +7 -7
- package/types/engine/wikilink-syntax.d.mts +17 -17
- package/types/engine/wikilinks.d.mts +14 -14
- package/types/hm3/actors.d.mts +9 -1
- package/types/hm3/document-subtypes.d.mts +1 -1
- package/types/hm3/items.d.mts +9 -2
- package/types/hm3/template-priority.d.mts +1 -1
- package/types/manifest.d.mts +8 -8
- package/types/release.d.mts +15 -4
- package/types/sohl/actors.d.mts +12 -4
- package/types/sohl/affiliation-standings.d.mts +2 -2
- package/types/sohl/being-info.d.mts +2 -2
- package/types/sohl/document-subtypes.d.mts +1 -1
- package/types/sohl/items.d.mts +9 -2
- package/types/sohl/note-schemas.d.mts +1 -1
- package/MIGRATING.md +0 -608
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Every `{#anchor}` a note declares on a heading.
|
|
3
3
|
*
|
|
4
4
|
* **Read from the content index's reader, not a second one.** This module kept
|
|
5
|
-
* its own
|
|
5
|
+
* its own, and the two disagreed: it matched `{#([a-z0-9-]+)}` while
|
|
6
6
|
* {@link module:engine/content-index.collectAnchors} matches `{#([^}]+)}`, so
|
|
7
7
|
* an anchor with a capital in it — `{#CalendarFormat}` — existed for the index
|
|
8
8
|
* and for the compiler and did not exist for the link checker. Nothing links to
|
|
@@ -21,7 +21,7 @@ export function anchorsOf(body: string): Set<string>;
|
|
|
21
21
|
* Read a content tree into the index a link resolves against.
|
|
22
22
|
*
|
|
23
23
|
* **The corpus comes from the content index, not from a walk of this module's
|
|
24
|
-
* own
|
|
24
|
+
* own**. Every pass used to answer "which files are the content?" for
|
|
25
25
|
* itself and throw the answer away; this one now reads
|
|
26
26
|
* {@link module:engine/content-index.indexRecordsFor}, which is the same
|
|
27
27
|
* derivation the published artifact and the compilers are driven from. So a
|
|
@@ -29,7 +29,7 @@ export function anchorsOf(body: string): Set<string>;
|
|
|
29
29
|
* anchors it resolves against are the ones every other pass will emit — rather
|
|
30
30
|
* than a second derivation that agrees with them only by inspection. That was
|
|
31
31
|
* not hypothetical: this module carried its own anchor reader until the anchor
|
|
32
|
-
* half
|
|
32
|
+
* anchor half, and the two disagreed about which anchors existed.
|
|
33
33
|
*
|
|
34
34
|
* **The file is opened for its bytes and nothing else.** The index deliberately
|
|
35
35
|
* carries no note *body*, and a link lives in the body — so each note is read
|
|
@@ -49,7 +49,7 @@ export function anchorsOf(body: string): Set<string>;
|
|
|
49
49
|
* @param {string} contentBase - Root of the content tree.
|
|
50
50
|
* @param {object} [opts]
|
|
51
51
|
* @param {object} [opts.config] - The resolved build configuration, whose
|
|
52
|
-
* fetched dependency indexes foreign addresses resolve through
|
|
52
|
+
* fetched dependency indexes foreign addresses resolve through, and
|
|
53
53
|
* whose `contentPackage` every local address is built from. Omitted, the
|
|
54
54
|
* ambient configuration is resolved and no cross-package address resolves.
|
|
55
55
|
* @param {readonly string[]} [opts.skipDirectories] - The walk's scope, passed
|
|
@@ -87,13 +87,13 @@ export function buildLinkIndex(contentBase: string, { config, skipDirectories, s
|
|
|
87
87
|
* **What is checkable, stated plainly.** Only an address into this site is, and
|
|
88
88
|
* only against facts this build already holds:
|
|
89
89
|
*
|
|
90
|
-
* - A **retired content type** in the path. The engine knows
|
|
90
|
+
* - A **retired content type** in the path. The engine knows the retired names
|
|
91
91
|
* and what replaced it, so this is a fact rather than a guess — and it is
|
|
92
92
|
* exactly the SoHL defect.
|
|
93
93
|
* - A **hardcoded absolute URL** into this package's own prefix, or into one a
|
|
94
94
|
* a fetched index names. Every one of them has a better form to write, which
|
|
95
95
|
* is why every one is reported — including a bare `/<package>/`, which names
|
|
96
|
-
* another package's landing
|
|
96
|
+
* another package's landing.
|
|
97
97
|
*
|
|
98
98
|
* That last case was exempt until the better form was identified, on the
|
|
99
99
|
* reasoning that a landing is in no link manifest so nothing could resolve it.
|
|
@@ -133,7 +133,7 @@ export function auditHomepageLinks(index: ReturnType<typeof buildLinkIndex>): Ar
|
|
|
133
133
|
*
|
|
134
134
|
* **How the link is *written* is a separate finding from where it points**, and
|
|
135
135
|
* the two are kept apart because the corrections differ. An unlabelled link
|
|
136
|
-
*
|
|
136
|
+
* has to become `[[type-shortcode|Text]]`; a labelled one whose target
|
|
137
137
|
* resolves nowhere has a shortcode to fix. Reporting a bare `[[Name]]` as a
|
|
138
138
|
* dead address would send an author hunting for a note that was never named.
|
|
139
139
|
*
|
|
@@ -145,7 +145,7 @@ export function auditHomepageLinks(index: ReturnType<typeof buildLinkIndex>): Ar
|
|
|
145
145
|
* carries a `reason` from {@link LINK_FINDING_REASONS} —
|
|
146
146
|
* `"not-an-address"`, `"unknown-type"`, `"ambiguous"` (with the claiming
|
|
147
147
|
* `packages`), or `"unresolved"` — and every one of them is an **error**:
|
|
148
|
-
* the three resolvers agree on severity for every class
|
|
148
|
+
* the three resolvers agree on severity for every class.
|
|
149
149
|
*/
|
|
150
150
|
export function auditLinks(index: ReturnType<typeof buildLinkIndex>): {
|
|
151
151
|
deadAnchors: object[];
|
|
@@ -21,7 +21,7 @@ export function isValidShortcode(value: unknown): boolean;
|
|
|
21
21
|
* @param {object} [opts.config] - The resolved build configuration, which the
|
|
22
22
|
* corpus is derived against.
|
|
23
23
|
* @param {readonly object[]} [opts.records] - Index records the caller already
|
|
24
|
-
* derived, so a command reads one corpus
|
|
24
|
+
* derived, so a command reads one corpus.
|
|
25
25
|
* @param {object[]} [opts.problems] - Collects the notes the index cannot
|
|
26
26
|
* record, instead of letting one of them silence the lint.
|
|
27
27
|
* @returns {{findings: Array<{file: string, line?: number, column?: number,
|
|
@@ -46,17 +46,19 @@ export function lintContentTree(contentBase: string, { skipDirectories, contentP
|
|
|
46
46
|
keys: number;
|
|
47
47
|
};
|
|
48
48
|
/**
|
|
49
|
-
* The shape every `shortcode` must match: ASCII letters and digits
|
|
49
|
+
* The shape every `shortcode` must match: lowercase ASCII letters and digits
|
|
50
|
+
* only.
|
|
50
51
|
*
|
|
51
52
|
* This is {@link ADDRESS_SEGMENT_PATTERN}, not a second copy of it. A shortcode
|
|
52
53
|
* is the last segment of a canonical address, and the rule it is held to is the
|
|
53
54
|
* rule *every* segment is held to — so the two are one constant rather than two
|
|
54
|
-
* free to drift apart
|
|
55
|
+
* free to drift apart. The name survives because this is where the rule
|
|
55
56
|
* is applied to a note.
|
|
56
57
|
*
|
|
57
|
-
* Case is
|
|
58
|
-
*
|
|
59
|
-
*
|
|
58
|
+
* Case is held to that rule with no exception: two shortcodes differing only
|
|
59
|
+
* in case are two names nobody can tell apart, and `canonicalKey` lowercases
|
|
60
|
+
* every address it builds regardless, so a mixed-case shortcode addresses the
|
|
61
|
+
* same document as its lowercase spelling.
|
|
60
62
|
*
|
|
61
63
|
* A consuming system's *runtime* keeps its own copy of this pattern — it cannot
|
|
62
64
|
* import a build-time dependency into shipped code — and is expected to pin the
|
|
@@ -9,13 +9,11 @@
|
|
|
9
9
|
* repository's identity in the address space, not a switch — and never dead
|
|
10
10
|
* configuration, whatever else changes.
|
|
11
11
|
*
|
|
12
|
-
* It
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
* retired and declaring it now fails the build; this value stays, here, where
|
|
18
|
-
* it is declared once.
|
|
12
|
+
* It is not a **selector**. Every content tree is single-package — each is
|
|
13
|
+
* single-sourced in the repository that ships it — so a `package:` field on a
|
|
14
|
+
* note would restate this constant once per note, and a value matching nothing
|
|
15
|
+
* would filter the whole tree out in silence. That field is refused, and this
|
|
16
|
+
* value stays here, declared once.
|
|
19
17
|
*
|
|
20
18
|
* Stable across compilation targets. If this content were ever compiled for a
|
|
21
19
|
* second game system, it would still be published as `sohl` — only the Foundry
|
|
@@ -36,7 +34,7 @@ export function contentPackage(): string;
|
|
|
36
34
|
* here: a note is published under `sohl` and its documents are addressed as
|
|
37
35
|
* `Compendium.sohl.<pack>.<Type>.<id>`. In `sohl-thalorna` the two differ
|
|
38
36
|
* (`thalorna` vs `sohl-thalorna`), which is why they are separate values rather
|
|
39
|
-
* than one — treating them as interchangeable is
|
|
37
|
+
* than one — treating them as interchangeable is the mistake.
|
|
40
38
|
*
|
|
41
39
|
* Configured rather than read from the manifest so the link resolver stays
|
|
42
40
|
* filesystem-free and unit-testable. `assertPackageIdMatchesManifestFile` in
|
|
@@ -73,34 +73,65 @@ 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
|
-
|
|
76
|
+
/**
|
|
77
|
+
* Expand every fenced `dataview` and `sql` block in a markdown body.
|
|
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 selects **no** note is an error unless the fence says
|
|
85
|
+
* `allow-empty`, which states that an empty table is the intended result.
|
|
86
|
+
*
|
|
87
|
+
* @param {string} markdown - The note body, frontmatter already stripped.
|
|
88
|
+
* @param {object} ctx
|
|
89
|
+
* @param {Array<ContentTableDoc>} ctx.docs - The searchable universe: every
|
|
90
|
+
* content note the caller considers in scope.
|
|
91
|
+
* @param {(doc: ContentTableDoc) => boolean} [ctx.linkable] - Whether a note can
|
|
92
|
+
* be linked to from a cell; defaults to never.
|
|
93
|
+
* @param {string} [ctx.source] - The note being expanded, for error reports.
|
|
94
|
+
* @param {ContentTableDoc} [ctx.self] - The note being expanded, as a searchable
|
|
95
|
+
* doc: what a query's `this` reads.
|
|
96
|
+
* @param {object[]} [ctx.sqlTables] - This note's prepared `sql` results, in
|
|
97
|
+
* document order, from
|
|
98
|
+
* {@link module:engine/sql-tables.prepareSqlTables}. An `sql` directive with
|
|
99
|
+
* no prepared result is an error: nothing here runs a query.
|
|
100
|
+
* @returns {{markdown: string, errors: Array<{source: string, directive: string,
|
|
101
|
+
* reason: string, line: number, column?: number}>,
|
|
102
|
+
* warnings: Array<{source: string, line: number, column: number,
|
|
103
|
+
* reason: string}>, lineMap: Array<{line: number, generated: boolean}>}}
|
|
104
|
+
* `lineMap` is parallel to the emitted lines and says which authored line
|
|
105
|
+
* each came from, so a diagnostic about the expanded body can name an
|
|
106
|
+
* authored position. An `errors` entry carries the 0-based line of the
|
|
107
|
+
* directive that failed, for the same reason. `warnings` holds one entry per
|
|
108
|
+
* `dataview` directive the body still authors.
|
|
109
|
+
*/
|
|
110
|
+
export function expandContentTables(markdown: string, { docs, linkable, source, self, sqlTables, }?: {
|
|
111
|
+
docs: Array<ContentTableDoc>;
|
|
112
|
+
linkable?: ((doc: ContentTableDoc) => boolean) | undefined;
|
|
79
113
|
source?: string | undefined;
|
|
80
|
-
self?: undefined;
|
|
81
|
-
sqlTables?: undefined;
|
|
114
|
+
self?: ContentTableDoc | undefined;
|
|
115
|
+
sqlTables?: object[] | undefined;
|
|
82
116
|
}): {
|
|
83
117
|
markdown: string;
|
|
84
|
-
errors:
|
|
85
|
-
source: string;
|
|
86
|
-
directive: string;
|
|
87
|
-
reason: any;
|
|
88
|
-
line: number;
|
|
89
|
-
column: number;
|
|
90
|
-
} | {
|
|
118
|
+
errors: Array<{
|
|
91
119
|
source: string;
|
|
92
120
|
directive: string;
|
|
93
|
-
reason:
|
|
121
|
+
reason: string;
|
|
94
122
|
line: number;
|
|
95
|
-
column?:
|
|
96
|
-
}
|
|
97
|
-
warnings: {
|
|
123
|
+
column?: number;
|
|
124
|
+
}>;
|
|
125
|
+
warnings: Array<{
|
|
98
126
|
source: string;
|
|
99
127
|
line: number;
|
|
100
128
|
column: number;
|
|
101
129
|
reason: string;
|
|
102
|
-
}
|
|
103
|
-
lineMap:
|
|
130
|
+
}>;
|
|
131
|
+
lineMap: Array<{
|
|
132
|
+
line: number;
|
|
133
|
+
generated: boolean;
|
|
134
|
+
}>;
|
|
104
135
|
};
|
|
105
136
|
/**
|
|
106
137
|
* One content note as a content build hands it to the expander: its parsed
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What a note authoring a derived key is told, in one place.
|
|
3
|
+
*
|
|
4
|
+
* Shared by every caller that can meet one, so an author meets the same
|
|
5
|
+
* sentence whichever runs first. It names the correction rather than only the
|
|
6
|
+
* fault, because "delete this" is not the fix here: the text is wanted, and
|
|
7
|
+
* there is somewhere it belongs.
|
|
8
|
+
*
|
|
9
|
+
* @param {string} key - The **whole key the note wrote**, from the region it
|
|
10
|
+
* sits in down to the field: `sohl.system.docHtml`. Composed by the caller,
|
|
11
|
+
* because only it knows where it found the value.
|
|
12
|
+
* @param {string} from - Where the compiler reads the value from, completing
|
|
13
|
+
* "the compiler writes it from …". The declaration carries it.
|
|
14
|
+
* @param {string} [file] - The note's path, named in the message. Omit it where
|
|
15
|
+
* the caller emits through a diagnostic, whose locator already starts the
|
|
16
|
+
* line — repeating it prints the path twice.
|
|
17
|
+
* @returns {string} The message, unpunctuated at the end as a finding is.
|
|
18
|
+
*/
|
|
19
|
+
export function derivedMessage(key: string, from: string, file?: string): string;
|
|
20
|
+
/**
|
|
21
|
+
* The derived keys a note actually writes, in declaration order.
|
|
22
|
+
*
|
|
23
|
+
* **Presence is the whole test**, as it is for a retired or a runtime-only
|
|
24
|
+
* field: an authored empty string is as much a claim on the key as prose is,
|
|
25
|
+
* and it is the same belief the message exists to correct. So the question is
|
|
26
|
+
* whether the path resolves to anything at all, never whether the value is a
|
|
27
|
+
* usable one.
|
|
28
|
+
*
|
|
29
|
+
* @param {object|null|undefined} fm - Parsed frontmatter.
|
|
30
|
+
* @param {readonly {key: string, from: string}[]} [keys] - What the compiler
|
|
31
|
+
* derives. A pass that derives nothing passes.
|
|
32
|
+
* @param {object} options - Options.
|
|
33
|
+
* @param {string} options.block - The system block to look in.
|
|
34
|
+
* @returns {{key: string, from: string}[]} The offending declarations.
|
|
35
|
+
*/
|
|
36
|
+
export function authoredDerivedKeys(fm: object | null | undefined, keys?: readonly {
|
|
37
|
+
key: string;
|
|
38
|
+
from: string;
|
|
39
|
+
}[], { block }?: {
|
|
40
|
+
block: string;
|
|
41
|
+
}): {
|
|
42
|
+
key: string;
|
|
43
|
+
from: string;
|
|
44
|
+
}[];
|
|
45
|
+
/**
|
|
46
|
+
* The same question asked of a `system` block directly.
|
|
47
|
+
*
|
|
48
|
+
* A note's own block is reached through {@link authoredDerivedKeys}, but it is
|
|
49
|
+
* not the only place an author writes one: an actor note's `items:` entries
|
|
50
|
+
* carry a `system:` overlay that is deep-merged onto the template verbatim,
|
|
51
|
+
* with no field declaration in the path at all. That overlay is a `system`
|
|
52
|
+
* block by every meaning except where it sits, and a `docHtml` written there
|
|
53
|
+
* ships exactly as one written on the item's own note.
|
|
54
|
+
*
|
|
55
|
+
* @param {Record<string, unknown>|null|undefined} data - The authored `system`
|
|
56
|
+
* data.
|
|
57
|
+
* @param {readonly {key: string, from: string}[]} [keys] - What the compiler
|
|
58
|
+
* derives.
|
|
59
|
+
* @returns {{key: string, from: string}[]} The offending declarations.
|
|
60
|
+
*/
|
|
61
|
+
export function derivedIn(data: Record<string, unknown> | null | undefined, keys?: readonly {
|
|
62
|
+
key: string;
|
|
63
|
+
from: string;
|
|
64
|
+
}[]): {
|
|
65
|
+
key: string;
|
|
66
|
+
from: string;
|
|
67
|
+
}[];
|
|
68
|
+
/**
|
|
69
|
+
* Refuse a note that authors any key its compiler derives.
|
|
70
|
+
*
|
|
71
|
+
* Refused rather than reported, for the reason its two siblings are: the note
|
|
72
|
+
* is not compiled, so nothing it would have emitted reaches a pack, and each
|
|
73
|
+
* caller counts the refused note and emits a located diagnostic — a refusal is
|
|
74
|
+
* never a silent skip.
|
|
75
|
+
*
|
|
76
|
+
* The **first** offending key is thrown on. A note authoring two of them is one
|
|
77
|
+
* mistake with one fix, and the build stops on this note either way.
|
|
78
|
+
*
|
|
79
|
+
* @param {object|null|undefined} fm - Parsed frontmatter, or nothing when it
|
|
80
|
+
* could not be parsed.
|
|
81
|
+
* @param {readonly {key: string, from: string}[]} [keys] - What the compiler
|
|
82
|
+
* derives.
|
|
83
|
+
* @param {object} options - Options.
|
|
84
|
+
* @param {string} options.block - The system block to look in.
|
|
85
|
+
* @param {string} [options.file] - The note's path, named in the message. Omit
|
|
86
|
+
* it where the caller emits through a diagnostic, which puts the locator at
|
|
87
|
+
* the start of the line already.
|
|
88
|
+
* @param {string} [options.absPath] - The note's file on disk, read only on the
|
|
89
|
+
* failing path to locate the offending line and column. The position rides on
|
|
90
|
+
* the thrown error as `position`, for a caller that emits a diagnostic.
|
|
91
|
+
* @returns {void}
|
|
92
|
+
* @throws {Error} When the note authors one.
|
|
93
|
+
*/
|
|
94
|
+
export function assertNoDerivedFields(fm: object | null | undefined, keys?: readonly {
|
|
95
|
+
key: string;
|
|
96
|
+
from: string;
|
|
97
|
+
}[], { block, file, absPath }?: {
|
|
98
|
+
block: string;
|
|
99
|
+
file?: string | undefined;
|
|
100
|
+
absPath?: string | undefined;
|
|
101
|
+
}): void;
|
|
@@ -115,7 +115,7 @@ export function positionInBody(body: string, offset: number, { bodyLine, bodyCol
|
|
|
115
115
|
* same name answers when no top-level one is present — which is usually what a
|
|
116
116
|
* reader wants, the key being unique in nearly every note. Pass `topLevel` where
|
|
117
117
|
* it is not: `aliases` is both a retired top-level field and a **permitted**
|
|
118
|
-
* `name.aliases
|
|
118
|
+
* `name.aliases`, and a finding about the first must never open on the
|
|
119
119
|
* second, which would tell an author to delete a field they are allowed to
|
|
120
120
|
* write.
|
|
121
121
|
*
|
|
@@ -232,7 +232,7 @@ export function yamlKeyPath(field: string): Array<string | number>;
|
|
|
232
232
|
* the same name is legal in two regions — `data.weight` and a top-level
|
|
233
233
|
* `weight` are different keys, and a search finds whichever comes first.
|
|
234
234
|
*
|
|
235
|
-
* So this is the located form for the closed regions
|
|
235
|
+
* So this is the located form for the closed regions. It reuses
|
|
236
236
|
* {@link positionOfYamlPath} rather than repeating its parse, which is what
|
|
237
237
|
* keeps a finding about `data.weight` pointing at `data.weight`.
|
|
238
238
|
*
|
|
@@ -58,7 +58,7 @@ export function defineDocumentSubtypes({ system, block, types }?: {
|
|
|
58
58
|
* {@link mapsNoteType}, {@link noteTypesFor}'s consumers and
|
|
59
59
|
* {@link systemOf} all arrive here. A note still spelling `armorgear` finds the
|
|
60
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
|
|
61
|
+
* the address it publishes at; only the *report* tells it to move.
|
|
62
62
|
*
|
|
63
63
|
* @param {DocumentSubtypeMap} map - The system's map.
|
|
64
64
|
* @param {string|undefined} noteType - The note's declared `type`.
|
|
@@ -127,7 +127,7 @@ export function documentSubtype(map: DocumentSubtypeMap, noteType: string | unde
|
|
|
127
127
|
* @property {string} [problem] - Why the reference names no document subtype.
|
|
128
128
|
*/
|
|
129
129
|
/**
|
|
130
|
-
* The document subtype a `(type, shortcode)` **reference** addresses
|
|
130
|
+
* The document subtype a `(type, shortcode)` **reference** addresses.
|
|
131
131
|
*
|
|
132
132
|
* A being's frontmatter names each embedded item by the *note's* type — the
|
|
133
133
|
* vocabulary an author writes — while the predefined items it resolves against
|
|
@@ -164,7 +164,7 @@ export function documentSubtype(map: DocumentSubtypeMap, noteType: string | unde
|
|
|
164
164
|
* retirement exists to stop.
|
|
165
165
|
*
|
|
166
166
|
* A **renamed** spelling is the opposite case and resolves normally, through
|
|
167
|
-
* the same {@link currentType} every other lookup goes through
|
|
167
|
+
* the same {@link currentType} every other lookup goes through. It has to
|
|
168
168
|
* be this side as much as the note's own `type:`: the overwhelming majority of
|
|
169
169
|
* the 31,000 occurrences of the old names are `(type, shortcode)` references
|
|
170
170
|
* inside a being's `items:` list, so a window that resolved notes but not
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
*
|
|
9
9
|
* **Only for a field that declares a `legacyKey`.** Every other field's
|
|
10
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
|
|
11
|
+
* finding on every field of every note in every tree, which is the corpus
|
|
12
12
|
* migration rather than a signal anyone could act on.
|
|
13
13
|
*
|
|
14
14
|
* @param {FieldSpec} field - The declaration.
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
export function readsLegacyKey(field: FieldSpec, from: import("./system-block.mjs").FieldSource): boolean;
|
|
20
20
|
/**
|
|
21
21
|
* Whether a resolution read a field from the top-level key `data:` gathered it
|
|
22
|
-
* off — the shared level's retiring position
|
|
22
|
+
* off — the shared level's retiring position.
|
|
23
23
|
*
|
|
24
24
|
* {@link readsLegacyKey}'s sibling, and the same signal: a finding here counts
|
|
25
25
|
* one note still on the pre-`data:` spelling, so the sweep has something to
|
|
@@ -42,7 +42,7 @@ export function readsRetiredTopLevel(field: FieldSpec, from: import("./system-bl
|
|
|
42
42
|
*
|
|
43
43
|
* The *position* is resolved by {@link resolveFieldValue} — `<system>.system`
|
|
44
44
|
* first, then the legacy in-block key, then the declared shared source, then
|
|
45
|
-
* the default
|
|
45
|
+
* the default. The **coercion** is applied here, once, wherever the value
|
|
46
46
|
* came from: a field's `read` is a statement about the field, not about where
|
|
47
47
|
* an author happened to write it, so `weight: "7"` reads as `7` at every one of
|
|
48
48
|
* those positions.
|
|
@@ -54,12 +54,12 @@ export function readsRetiredTopLevel(field: FieldSpec, from: import("./system-bl
|
|
|
54
54
|
* against. The default is the one block every existing tree authors; a
|
|
55
55
|
* second system passes its own.
|
|
56
56
|
* @param {(field: FieldSpec) => void} [options.onLegacyKey] - Called with each
|
|
57
|
-
* field read from the position it is being swept off
|
|
57
|
+
* field read from the position it is being swept off. A callback
|
|
58
58
|
* rather than a returned list because the caller is a compiler, which already
|
|
59
59
|
* knows the note and how to locate a key in it; this module knows neither and
|
|
60
60
|
* would have to invent a finding shape to say so.
|
|
61
61
|
* @param {(field: FieldSpec) => void} [options.onRetiredTopLevel] - Called with
|
|
62
|
-
* each field read from the top-level key `data:` gathered it off
|
|
62
|
+
* each field read from the top-level key `data:` gathered it off. The
|
|
63
63
|
* shared level's counterpart to `onLegacyKey`, and a separate callback
|
|
64
64
|
* because it is a separate position: a note may have moved one of the two and
|
|
65
65
|
* not the other, and a caller that conflated them would tell its author to
|
|
@@ -78,7 +78,7 @@ export function readField(field: FieldSpec, fm: object, options?: {
|
|
|
78
78
|
* every caller wants. A builder has one further question — *should the key be
|
|
79
79
|
* written at all* — and it cannot be answered from the value: `null` from a
|
|
80
80
|
* note and `null` from a declared default are the same value and opposite
|
|
81
|
-
* facts
|
|
81
|
+
* facts.
|
|
82
82
|
*
|
|
83
83
|
* So the position rides back beside the value, resolved **once**. The
|
|
84
84
|
* alternative is a builder that calls {@link resolveFieldValue} for the source
|
|
@@ -111,7 +111,7 @@ export function readFieldEntry(field: FieldSpec, fm: object, { block, onLegacyKe
|
|
|
111
111
|
* The question {@link FieldSpec.omitWhenAbsent} turns on, asked of the
|
|
112
112
|
* *position* rather than of the value — which cannot answer it, since a
|
|
113
113
|
* declared `default: null` and an authored `null` are indistinguishable once
|
|
114
|
-
* the value is in hand
|
|
114
|
+
* the value is in hand.
|
|
115
115
|
*
|
|
116
116
|
* `undefined` counts as absent whatever position reported it, because writing
|
|
117
117
|
* the key then emits a value `JSON.stringify` drops — the key present in the
|
|
@@ -139,10 +139,10 @@ export function isAuthored(from: import("./system-block.mjs").FieldSource, value
|
|
|
139
139
|
* systems declare the same shared source and different destinations.
|
|
140
140
|
* @param {(field: FieldSpec) => void} [options.onLegacyKey] - Passed through to
|
|
141
141
|
* {@link readField}: called with each field the note authored at the position
|
|
142
|
-
* it is being swept off
|
|
142
|
+
* it is being swept off.
|
|
143
143
|
* @param {(field: FieldSpec) => void} [options.onRetiredTopLevel] - Passed
|
|
144
144
|
* through to {@link readField}: called with each field the note authored at
|
|
145
|
-
* the top-level key `data:` gathered it off
|
|
145
|
+
* the top-level key `data:` gathered it off.
|
|
146
146
|
* @returns {(fm: object) => object} A `system`-block builder.
|
|
147
147
|
*/
|
|
148
148
|
export function buildFromFields(fields: readonly FieldSpec[], { block, onLegacyKey, onRetiredTopLevel }?: {
|
|
@@ -166,8 +166,8 @@ export function authoredFields(fields: readonly FieldSpec[]): FieldSpec[];
|
|
|
166
166
|
* The fields of a declaration a note may **never** write.
|
|
167
167
|
*
|
|
168
168
|
* The complement of {@link authoredFields} in the direction that matters: those
|
|
169
|
-
* are the fields an author may write, these are the ones authoring is an error
|
|
170
|
-
*
|
|
169
|
+
* are the fields an author may write, these are the ones authoring is an error.
|
|
170
|
+
* Everything else in a declaration — a constant, a derived value — is
|
|
171
171
|
* simply not authored, which is a statement about the *builder* rather than
|
|
172
172
|
* about the author, and says nothing about what happens if a note writes the
|
|
173
173
|
* path anyway.
|
|
@@ -179,19 +179,19 @@ export function runtimeOnlyFields(fields: readonly FieldSpec[]): FieldSpec[];
|
|
|
179
179
|
/**
|
|
180
180
|
* @typedef {object} FieldSpec
|
|
181
181
|
* @property {string} to - Dotted path in the emitted `system` block — and,
|
|
182
|
-
*
|
|
182
|
+
* the path a note authors the field at directly:
|
|
183
183
|
* `<system>.system.<to>`.
|
|
184
184
|
* @property {string} [name] - The **shared, top-level property this field draws
|
|
185
185
|
* from** when the note authors no value at `<system>.system.<to>`. Dotted for
|
|
186
186
|
* a path into a shared container (`data.portrait`), which is now the ordinary
|
|
187
|
-
* case: `data:`
|
|
187
|
+
* case: `data:` puts every type-specific fact under one.
|
|
188
188
|
*
|
|
189
|
-
*
|
|
189
|
+
* Read as "frontmatter key under `sohl:`", that meaning is the
|
|
190
190
|
* degenerate case where the shared source and the system destination happen
|
|
191
191
|
* to share a name. They constantly do not — one shared `data.portrait` feeds
|
|
192
192
|
* `sohl.system.portrait` *and* `hm3.system.bioImage` — so the source is
|
|
193
|
-
* declared rather than matched by spelling
|
|
194
|
-
* still read, second, until
|
|
193
|
+
* declared rather than matched by spelling. The in-block position is
|
|
194
|
+
* still read, second, until the corpus moves off it — keyed on
|
|
195
195
|
* `legacyKey` where the two spellings differ.
|
|
196
196
|
*
|
|
197
197
|
* Absent means the value is not authored at all — see `value`.
|
|
@@ -199,8 +199,8 @@ export function runtimeOnlyFields(fields: readonly FieldSpec[]): FieldSpec[];
|
|
|
199
199
|
* the system block** — the second position of the resolution order — when
|
|
200
200
|
* that is not `name`. Absent, the position is keyed on `name`.
|
|
201
201
|
*
|
|
202
|
-
*
|
|
203
|
-
* shared source and its in-block key were the same word. `data:`
|
|
202
|
+
* As one property the two would hold only while a field's
|
|
203
|
+
* shared source and its in-block key were the same word. `data:` ended
|
|
204
204
|
* that: a shared source is a path into a container, so `data.species` and
|
|
205
205
|
* `species` name two different places and no single value reached both.
|
|
206
206
|
* `name: "species"` could not see `data.species`; `name: "data.species"`
|
|
@@ -216,7 +216,7 @@ export function runtimeOnlyFields(fields: readonly FieldSpec[]): FieldSpec[];
|
|
|
216
216
|
* @property {string} [topLevelMeans] - **What the note's top-level key of this
|
|
217
217
|
* name means instead** — declared only where it means something else, and
|
|
218
218
|
* stating it removes the shared top-level position from this field's
|
|
219
|
-
* resolution order
|
|
219
|
+
* resolution order.
|
|
220
220
|
*
|
|
221
221
|
* A field's `name` doubles as its identity and as the shared property it
|
|
222
222
|
* draws from, which is right wherever the two levels state the same quantity
|
|
@@ -243,7 +243,7 @@ export function runtimeOnlyFields(fields: readonly FieldSpec[]): FieldSpec[];
|
|
|
243
243
|
* if the two positions hold unrelated quantities then the *in-block* position
|
|
244
244
|
* is not the note-level field either, so a check about the note-level field
|
|
245
245
|
* reads past it. `engine/frontmatter-lint.mjs` resolves that through
|
|
246
|
-
* `collidingBlockKeys`.
|
|
246
|
+
* `collidingBlockKeys`. Reading it for the emitted field alone is
|
|
247
247
|
* how an affiliation's office style came to answer for its page heading.
|
|
248
248
|
* @property {string} [shape] - Human-readable shape, for documentation. Comes
|
|
249
249
|
* paired with `read` from one of the coercion constants below.
|
|
@@ -259,10 +259,9 @@ export function runtimeOnlyFields(fields: readonly FieldSpec[]): FieldSpec[];
|
|
|
259
259
|
* third step of {@link module:engine/system-block.resolveFieldValue}'s order,
|
|
260
260
|
* so the value resolves whichever region carries it. What it tells the
|
|
261
261
|
* author-facing surfaces is which of the two is the field's *home*, so a
|
|
262
|
-
* message about it names `img` rather than sending an author to `sohl.img
|
|
263
|
-
* (#142).
|
|
262
|
+
* message about it names `img` rather than sending an author to `sohl.img`.
|
|
264
263
|
* @property {"string"|"number"|"boolean"|"list"|"map"} [kind] - The value's
|
|
265
|
-
* shape, for the frontmatter linter
|
|
264
|
+
* shape, for the frontmatter linter. Distinct from `shape`, which is
|
|
266
265
|
* prose for a reader, and from `read`, which is what the compiler does: a
|
|
267
266
|
* field may declare `kind` without changing a byte of what it emits, and
|
|
268
267
|
* several do — `weight` is coerced leniently but is still a number, and
|
|
@@ -275,7 +274,7 @@ export function runtimeOnlyFields(fields: readonly FieldSpec[]): FieldSpec[];
|
|
|
275
274
|
* @property {any|((fm: object) => any)} [value] - For a field with no `name`:
|
|
276
275
|
* the constant, or a function deriving it from the frontmatter.
|
|
277
276
|
* @property {boolean} [omitWhenAbsent] - **The key is left out entirely when
|
|
278
|
-
* the note does not carry the field
|
|
277
|
+
* the note does not carry the field**, rather than written from a
|
|
279
278
|
* declared default.
|
|
280
279
|
*
|
|
281
280
|
* Every other field answers absence with a value: an unauthored `weight` is
|
|
@@ -310,7 +309,7 @@ export function runtimeOnlyFields(fields: readonly FieldSpec[]): FieldSpec[];
|
|
|
310
309
|
* which the generated reference states in the field's own row.
|
|
311
310
|
* @property {string} [runtimeOnly] - **What the field holds once play has
|
|
312
311
|
* started** — declared on a field the *document* writes for itself, which no
|
|
313
|
-
* note may author
|
|
312
|
+
* note may author.
|
|
314
313
|
*
|
|
315
314
|
* A schema declares plenty of fields a compiled document has no business
|
|
316
315
|
* carrying: an affliction's `onsetDate` is the world time its onset fired
|
|
@@ -404,7 +403,7 @@ export const BLANK_IS_DEFAULT: Readonly<{
|
|
|
404
403
|
export type FieldSpec = {
|
|
405
404
|
/**
|
|
406
405
|
* - Dotted path in the emitted `system` block — and,
|
|
407
|
-
*
|
|
406
|
+
* the path a note authors the field at directly:
|
|
408
407
|
* `<system>.system.<to>`.
|
|
409
408
|
*/
|
|
410
409
|
to: string;
|
|
@@ -412,14 +411,14 @@ export type FieldSpec = {
|
|
|
412
411
|
* - The **shared, top-level property this field draws
|
|
413
412
|
* from** when the note authors no value at `<system>.system.<to>`. Dotted for
|
|
414
413
|
* a path into a shared container (`data.portrait`), which is now the ordinary
|
|
415
|
-
* case: `data:`
|
|
414
|
+
* case: `data:` puts every type-specific fact under one.
|
|
416
415
|
*
|
|
417
|
-
*
|
|
416
|
+
* Read as "frontmatter key under `sohl:`", that meaning is the
|
|
418
417
|
* degenerate case where the shared source and the system destination happen
|
|
419
418
|
* to share a name. They constantly do not — one shared `data.portrait` feeds
|
|
420
419
|
* `sohl.system.portrait` *and* `hm3.system.bioImage` — so the source is
|
|
421
|
-
* declared rather than matched by spelling
|
|
422
|
-
* still read, second, until
|
|
420
|
+
* declared rather than matched by spelling. The in-block position is
|
|
421
|
+
* still read, second, until the corpus moves off it — keyed on
|
|
423
422
|
* `legacyKey` where the two spellings differ.
|
|
424
423
|
*
|
|
425
424
|
* Absent means the value is not authored at all — see `value`.
|
|
@@ -430,8 +429,8 @@ export type FieldSpec = {
|
|
|
430
429
|
* the system block** — the second position of the resolution order — when
|
|
431
430
|
* that is not `name`. Absent, the position is keyed on `name`.
|
|
432
431
|
*
|
|
433
|
-
*
|
|
434
|
-
* shared source and its in-block key were the same word. `data:`
|
|
432
|
+
* As one property the two would hold only while a field's
|
|
433
|
+
* shared source and its in-block key were the same word. `data:` ended
|
|
435
434
|
* that: a shared source is a path into a container, so `data.species` and
|
|
436
435
|
* `species` name two different places and no single value reached both.
|
|
437
436
|
* `name: "species"` could not see `data.species`; `name: "data.species"`
|
|
@@ -450,7 +449,7 @@ export type FieldSpec = {
|
|
|
450
449
|
* - **What the note's top-level key of this
|
|
451
450
|
* name means instead** — declared only where it means something else, and
|
|
452
451
|
* stating it removes the shared top-level position from this field's
|
|
453
|
-
* resolution order
|
|
452
|
+
* resolution order.
|
|
454
453
|
*
|
|
455
454
|
* A field's `name` doubles as its identity and as the shared property it
|
|
456
455
|
* draws from, which is right wherever the two levels state the same quantity
|
|
@@ -477,7 +476,7 @@ export type FieldSpec = {
|
|
|
477
476
|
* if the two positions hold unrelated quantities then the *in-block* position
|
|
478
477
|
* is not the note-level field either, so a check about the note-level field
|
|
479
478
|
* reads past it. `engine/frontmatter-lint.mjs` resolves that through
|
|
480
|
-
* `collidingBlockKeys`.
|
|
479
|
+
* `collidingBlockKeys`. Reading it for the emitted field alone is
|
|
481
480
|
* how an affiliation's office style came to answer for its page heading.
|
|
482
481
|
*/
|
|
483
482
|
topLevelMeans?: string | undefined;
|
|
@@ -511,13 +510,12 @@ export type FieldSpec = {
|
|
|
511
510
|
* third step of {@link module :engine/system-block.resolveFieldValue}'s order,
|
|
512
511
|
* so the value resolves whichever region carries it. What it tells the
|
|
513
512
|
* author-facing surfaces is which of the two is the field's *home*, so a
|
|
514
|
-
* message about it names `img` rather than sending an author to `sohl.img
|
|
515
|
-
* (#142).
|
|
513
|
+
* message about it names `img` rather than sending an author to `sohl.img`.
|
|
516
514
|
*/
|
|
517
515
|
shared?: boolean | undefined;
|
|
518
516
|
/**
|
|
519
517
|
* - The value's
|
|
520
|
-
* shape, for the frontmatter linter
|
|
518
|
+
* shape, for the frontmatter linter. Distinct from `shape`, which is
|
|
521
519
|
* prose for a reader, and from `read`, which is what the compiler does: a
|
|
522
520
|
* field may declare `kind` without changing a byte of what it emits, and
|
|
523
521
|
* several do — `weight` is coerced leniently but is still a number, and
|
|
@@ -539,7 +537,7 @@ export type FieldSpec = {
|
|
|
539
537
|
value?: any | ((fm: object) => any);
|
|
540
538
|
/**
|
|
541
539
|
* - **The key is left out entirely when
|
|
542
|
-
* the note does not carry the field
|
|
540
|
+
* the note does not carry the field**, rather than written from a
|
|
543
541
|
* declared default.
|
|
544
542
|
*
|
|
545
543
|
* Every other field answers absence with a value: an unauthored `weight` is
|
|
@@ -577,7 +575,7 @@ export type FieldSpec = {
|
|
|
577
575
|
/**
|
|
578
576
|
* - **What the field holds once play has
|
|
579
577
|
* started** — declared on a field the *document* writes for itself, which no
|
|
580
|
-
* note may author
|
|
578
|
+
* note may author.
|
|
581
579
|
*
|
|
582
580
|
* A schema declares plenty of fields a compiled document has no business
|
|
583
581
|
* carrying: an affliction's `onsetDate` is the world time its onset fired
|