@heroiclands/package-build 20.3.0 → 20.4.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 +97 -0
- package/CONTENT.md +111 -92
- package/README.md +2 -9
- package/bin/content-build.mjs +70 -61
- package/bin/package-build.mjs +2 -2
- package/config.mjs +6 -8
- package/content-config.mjs +187 -64
- package/docs/content-format.md +57 -68
- package/e2e.mjs +4 -4
- package/engine/actor-compiler.mjs +36 -36
- package/engine/address-charset.mjs +6 -6
- package/engine/address-diff.mjs +12 -11
- package/engine/anchored-sections.mjs +1 -1
- package/engine/anchors.mjs +1 -1
- package/engine/base-compiler.mjs +36 -38
- package/engine/bundle-notes.mjs +4 -4
- package/engine/bundles.mjs +8 -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 +15 -15
- 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 +225 -98
- package/engine/content-index.mjs +17 -17
- package/engine/content-links.mjs +30 -30
- package/engine/content-lint.mjs +20 -24
- package/engine/content-package.mjs +7 -9
- package/engine/content-slug.mjs +2 -2
- package/engine/content-tables.mjs +7 -8
- 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 +14 -14
- package/engine/foreign-catalog.mjs +8 -9
- package/engine/foundry-entries.mjs +19 -20
- package/engine/frontmatter-lint.mjs +64 -69
- package/engine/frontmatter.mjs +11 -11
- package/engine/generate.mjs +39 -39
- package/engine/helpers.mjs +63 -54
- package/engine/homepage.mjs +20 -22
- package/engine/ids.mjs +15 -15
- package/engine/index-records.mjs +4 -4
- package/engine/index.mjs +14 -8
- 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 +15 -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 +36 -34
- 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 -3
- package/engine/note-vocabulary.mjs +27 -26
- package/engine/pack-config.mjs +13 -13
- package/engine/pack-router.mjs +0 -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 +19 -20
- package/engine/schema-check.mjs +20 -20
- package/engine/schema-extract.mjs +1 -1
- package/engine/site-build.mjs +45 -47
- 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 +35 -35
- package/engine/wikilink-syntax.mjs +16 -16
- package/engine/wikilinks.mjs +39 -39
- package/hm3/actors.mjs +22 -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 +20 -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 +3 -3
- package/sohl/actors.mjs +28 -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 +20 -6
- package/sohl/kb-passes.mjs +5 -5
- package/sohl/note-schemas.mjs +7 -7
- package/sohl/skill-base.mjs +1 -1
- package/types/content-config.d.mts +25 -24
- package/types/e2e.d.mts +3 -3
- package/types/engine/actor-compiler.d.mts +14 -13
- package/types/engine/address-charset.d.mts +6 -6
- package/types/engine/address-diff.d.mts +3 -3
- package/types/engine/base-compiler.d.mts +17 -19
- package/types/engine/bundle-notes.d.mts +3 -3
- package/types/engine/bundles.d.mts +1 -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 +11 -11
- 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 +150 -62
- package/types/engine/content-index.d.mts +2 -2
- package/types/engine/content-links.d.mts +8 -8
- package/types/engine/content-lint.d.mts +2 -2
- package/types/engine/content-package.d.mts +6 -8
- 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 +7 -7
- package/types/engine/frontmatter.d.mts +7 -7
- package/types/engine/generate.d.mts +7 -7
- package/types/engine/helpers.d.mts +25 -27
- 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 +2 -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 +3 -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 +16 -15
- 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 +9 -8
- package/types/engine/pack-config.d.mts +3 -3
- package/types/engine/pack-router.d.mts +1 -1
- 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/schema-check.d.mts +11 -11
- package/types/engine/site-build.d.mts +18 -19
- 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 +13 -12
- package/types/hm3/actors.d.mts +1 -1
- package/types/hm3/document-subtypes.d.mts +1 -1
- package/types/hm3/items.d.mts +1 -2
- package/types/hm3/template-priority.d.mts +1 -1
- package/types/manifest.d.mts +8 -8
- package/types/sohl/actors.d.mts +2 -2
- 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 +1 -2
- package/types/sohl/note-schemas.d.mts +1 -1
- package/MIGRATING.md +0 -608
package/engine/wikilinks.mjs
CHANGED
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
* `[[#slug|Text]]` a section of the source note itself
|
|
23
23
|
* `[[doctype-shortcode|T]]` an item's *documentation* (see below)
|
|
24
24
|
*
|
|
25
|
-
* **Every link is an address, and every address carries a label
|
|
25
|
+
* **Every link is an address, and every address carries a label**. A
|
|
26
26
|
* link written without one addresses nothing and is reported — see
|
|
27
27
|
* {@link unlabelledLinkMessage}, which states the rule for both builds. The
|
|
28
28
|
* bare `[[Alias]]` form and the index it was looked up in are retired.
|
|
@@ -36,8 +36,7 @@
|
|
|
36
36
|
* Nothing narrower than `(type, shortcode)` is consulted — a note's directory
|
|
37
37
|
* and its `category` play no part in resolution — and nothing wider: a note's
|
|
38
38
|
* *name* is not an address, so two notes of a type may share a display name
|
|
39
|
-
* ("Gear" as a rules page and as a user guide page) with nothing to disambiguate
|
|
40
|
-
* (#179, #180).
|
|
39
|
+
* ("Gear" as a rules page and as a user guide page) with nothing to disambiguate.
|
|
41
40
|
*
|
|
42
41
|
* At compile time each becomes a Foundry UUID enricher, routed to the pack that
|
|
43
42
|
* the target's type compiles into (see {@link packForType}):
|
|
@@ -47,7 +46,7 @@
|
|
|
47
46
|
*
|
|
48
47
|
* **Every address is computed once, when the target is indexed** — see
|
|
49
48
|
* {@link buildWikilinkIndex} — and a link is resolved by looking that value up.
|
|
50
|
-
* Nothing here concatenates a prefix at the point of use
|
|
49
|
+
* Nothing here concatenates a prefix at the point of use.
|
|
51
50
|
*
|
|
52
51
|
* Section links address a **JournalEntryPage**, because Foundry UUIDs cannot
|
|
53
52
|
* target a position inside a page. A heading carrying `{#slug}` therefore starts
|
|
@@ -63,15 +62,15 @@
|
|
|
63
62
|
* **A document and its documentation are two documents.** An item note
|
|
64
63
|
* compiles into an item — and, separately, its prose compiles into a
|
|
65
64
|
* JournalEntry in the journals pack (see
|
|
66
|
-
* {@link sohl.utils.packs.itemDocEntryId}); a macro note works the same way
|
|
67
|
-
*
|
|
65
|
+
* {@link sohl.utils.packs.itemDocEntryId}); a macro note works the same way.
|
|
66
|
+
* `skill/wpnc` addresses the skill; the **virtual qualifier**
|
|
68
67
|
* `docskill/wpnc` addresses that skill's documentation, and
|
|
69
68
|
* `docskill/wpnc#crafting` a page within it. `docmacro/autoattack#script`
|
|
70
69
|
* reaches a macro's source. Every doc-carrying type has a `doc<type>`
|
|
71
70
|
* counterpart, formed by prefix and never enumerated; see
|
|
72
71
|
* {@link resolveItemDocType}. Without it a section link to an item note
|
|
73
72
|
* produced a UUID against the *items* pack, which cannot hold a
|
|
74
|
-
* JournalEntryPage, and dead-ended
|
|
73
|
+
* JournalEntryPage, and dead-ended.
|
|
75
74
|
*
|
|
76
75
|
* **The two builds read the qualifier differently, by design.** In Foundry the
|
|
77
76
|
* item and its documentation are separate documents in separate packs, so the
|
|
@@ -117,15 +116,15 @@ const norm = (s) => String(s).toLowerCase().trim();
|
|
|
117
116
|
* not one.
|
|
118
117
|
*
|
|
119
118
|
* A document and its documentation are two documents in two packs, so they
|
|
120
|
-
* need two addresses
|
|
119
|
+
* need two addresses. `skill/wpnc` is the item; `docskill/wpnc` is the
|
|
121
120
|
* JournalEntry its prose compiled into, and `docmacro/autoattack` is the same
|
|
122
|
-
* arrangement for a macro
|
|
121
|
+
* arrangement for a macro.
|
|
123
122
|
*
|
|
124
123
|
* The virtual form exists for a type that carries separate documentation
|
|
125
124
|
* ({@link sohl.utils.packs.docEntryTypes} — the set the journals compiler and
|
|
126
125
|
* the link manifest read too), **or** for one that routes to the items pack.
|
|
127
126
|
* The second clause is the older rule and stays: types that compile into items
|
|
128
|
-
* are the open, unenumerated set
|
|
127
|
+
* are the open, unenumerated set, and a foreign package may publish an
|
|
129
128
|
* item type this build has never heard of. Dropping it would silently unlink
|
|
130
129
|
* every `doc<type>` address into such a package.
|
|
131
130
|
*
|
|
@@ -153,8 +152,8 @@ export function resolveItemDocType(qualifier, types) {
|
|
|
153
152
|
* Two separators are accepted, and they are **not** interchangeable in how
|
|
154
153
|
* confidently they mark a target as qualified:
|
|
155
154
|
*
|
|
156
|
-
* - **`type-shortcode`** and its qualified forms — the canonical spelling
|
|
157
|
-
*
|
|
155
|
+
* - **`type-shortcode`** and its qualified forms — the canonical spelling.
|
|
156
|
+
* Obsidian reads `/` inside a wikilink as a *path* and resolves it
|
|
158
157
|
* against the vault's folders, so a slash-qualified link is a broken link in
|
|
159
158
|
* the editor where the content is now authored.
|
|
160
159
|
* - **`type/shortcode`** — the legacy form, still resolved so that a link
|
|
@@ -163,7 +162,7 @@ export function resolveItemDocType(qualifier, types) {
|
|
|
163
162
|
* before it is reported rather than guessed at. The split is at the **last**
|
|
164
163
|
* slash, as it always was.
|
|
165
164
|
*
|
|
166
|
-
* **The grammar is strict, and omission runs left to right
|
|
165
|
+
* **The grammar is strict, and omission runs left to right**:
|
|
167
166
|
*
|
|
168
167
|
* ```text
|
|
169
168
|
* [[[[<package>-]<system>-]<type>-]<shortcode>]
|
|
@@ -178,12 +177,13 @@ export function resolveItemDocType(qualifier, types) {
|
|
|
178
177
|
* **Parsing is plain positional counting**, the same rule
|
|
179
178
|
* {@link readCanonicalKey} follows, and it is sound for the same reason: every
|
|
180
179
|
* segment is `^[A-Za-z0-9]+$` (`ADDRESS_SEGMENT_PATTERN`, enforced on
|
|
181
|
-
* shortcodes by `content-lint.mjs`
|
|
180
|
+
* shortcodes by `content-lint.mjs`), so the hyphen is purely a
|
|
182
181
|
* separator and the count alone determines every field. Verified across the
|
|
183
182
|
* four content trees: 138,204 authored shortcodes, none carrying a separator.
|
|
184
183
|
*
|
|
185
184
|
* That replaced a first-hyphen split which let a shortcode contain a hyphen
|
|
186
|
-
* (`trauma-self-pro` → `trauma` + `self-pro`). The tolerance predates
|
|
185
|
+
* (`trauma-self-pro` → `trauma` + `self-pro`). The tolerance predates the
|
|
186
|
+
* charset rule and
|
|
187
187
|
* outlived it; no tree has used it, and keeping it would make a three-segment
|
|
188
188
|
* target ambiguous between a system and a hyphenated shortcode.
|
|
189
189
|
*
|
|
@@ -287,7 +287,7 @@ function readQualifierCased(target, types, packages) {
|
|
|
287
287
|
return read && { ...read, system, package: pkg };
|
|
288
288
|
}
|
|
289
289
|
|
|
290
|
-
// One segment is a bare name, which is not an address
|
|
290
|
+
// One segment is a bare name, which is not an address; five or
|
|
291
291
|
// more is not a hyphenated shortcode but a name that happens to carry
|
|
292
292
|
// separators, since no segment may contain one.
|
|
293
293
|
default:
|
|
@@ -347,7 +347,7 @@ export function anchorPageId(noteId, anchorSlug) {
|
|
|
347
347
|
* One entry per content note. `pack` / `docPack` name the packs the note's
|
|
348
348
|
* document and its documentation entry landed in; omitted, the conventional
|
|
349
349
|
* one-pack-per-type names stand in. `draft` says the note carries the `draft`
|
|
350
|
-
* tag, which marks links *into* it and changes nothing else
|
|
350
|
+
* tag, which marks links *into* it and changes nothing else.
|
|
351
351
|
* @param {string} packageId - The Foundry package shipping the packs; the first
|
|
352
352
|
* segment of every emitted UUID.
|
|
353
353
|
* @param {Map<string, object>} [foreign] - Canonically keyed entries from
|
|
@@ -363,7 +363,7 @@ export function buildWikilinkIndex(docs, packageId, foreign, contentPackage) {
|
|
|
363
363
|
throw new Error(
|
|
364
364
|
"buildWikilinkIndex: packageId is required — it is the first " +
|
|
365
365
|
"segment of every emitted UUID, and defaulting it is how links " +
|
|
366
|
-
"came to address the wrong package
|
|
366
|
+
"came to address the wrong package.",
|
|
367
367
|
);
|
|
368
368
|
}
|
|
369
369
|
|
|
@@ -382,7 +382,7 @@ export function buildWikilinkIndex(docs, packageId, foreign, contentPackage) {
|
|
|
382
382
|
uuidByDoc.set(d, {
|
|
383
383
|
// `d.pack` is where this note's document actually landed, resolved
|
|
384
384
|
// by the pack router when the index was collected. A repository may
|
|
385
|
-
// ship several packs of one type
|
|
385
|
+
// ship several packs of one type and a UUID carries the
|
|
386
386
|
// pack name, so the address cannot be derived from the type alone.
|
|
387
387
|
uuid: compendiumUuid(packageId, d.type, d.id, d.pack),
|
|
388
388
|
// An item's prose compiles into a separate JournalEntry, addressed
|
|
@@ -398,7 +398,7 @@ export function buildWikilinkIndex(docs, packageId, foreign, contentPackage) {
|
|
|
398
398
|
// foreign address resolves exactly like a local one and there is no
|
|
399
399
|
// precedence rule to get wrong. A foreign package's types are added to
|
|
400
400
|
// `types` too — without that, its addresses read as prose and silently lose
|
|
401
|
-
// their link
|
|
401
|
+
// their link.
|
|
402
402
|
const foreignByKey = new Map(foreign ?? []);
|
|
403
403
|
const foreignTypes = [];
|
|
404
404
|
for (const v of foreignByKey.values()) {
|
|
@@ -448,7 +448,7 @@ export function buildWikilinkIndex(docs, packageId, foreign, contentPackage) {
|
|
|
448
448
|
* The foreign manifest entry an address names, or `null`.
|
|
449
449
|
*
|
|
450
450
|
* A target is a **partial** address, matched on the segments it supplies with
|
|
451
|
-
* the rest wildcarded
|
|
451
|
+
* the rest wildcarded — so a package-qualified one is a scan rather than
|
|
452
452
|
* a single lookup, and may still match one entry per system. An unqualified one
|
|
453
453
|
* names no package either, so it resolves against whichever foreign package
|
|
454
454
|
* publishes it — and, in both readings, only when exactly one entry matches.
|
|
@@ -470,7 +470,7 @@ function findForeign(index, read) {
|
|
|
470
470
|
* Every foreign manifest entry an address names.
|
|
471
471
|
*
|
|
472
472
|
* The count is what separates *nothing publishes this* from *two packages do*,
|
|
473
|
-
* and those are different findings with different fixes
|
|
473
|
+
* and those are different findings with different fixes, so the caller
|
|
474
474
|
* gets the list rather than a single answer that has already collapsed the
|
|
475
475
|
* distinction.
|
|
476
476
|
*
|
|
@@ -484,7 +484,7 @@ function foreignHits(index, read) {
|
|
|
484
484
|
const wanted = norm(read.itemDoc ? `doc${read.type}` : read.type);
|
|
485
485
|
const shortcode = norm(read.shortcode);
|
|
486
486
|
|
|
487
|
-
// **An omitted package means this package
|
|
487
|
+
// **An omitted package means this package**, so a short form
|
|
488
488
|
// addresses nothing foreign and never reaches a dependency's index. A link
|
|
489
489
|
// that resolved into another package only because no local note claimed the
|
|
490
490
|
// address was resolving by accident, and would have retargeted silently the
|
|
@@ -534,7 +534,7 @@ function unresolvedLink(text, target) {
|
|
|
534
534
|
}
|
|
535
535
|
|
|
536
536
|
/**
|
|
537
|
-
* How a link to a **draft** note renders
|
|
537
|
+
* How a link to a **draft** note renders.
|
|
538
538
|
*
|
|
539
539
|
* A note tagged `draft` exists so a link into it is not dead, and nothing more.
|
|
540
540
|
* Unmarked, a reader follows a promising link into an empty page and an author
|
|
@@ -552,7 +552,7 @@ function unresolvedLink(text, target) {
|
|
|
552
552
|
* **Byte-identical with the site build's copy** in `web-wikilinks.mjs`, down to
|
|
553
553
|
* the class name and the `title` wording — one authored link renders on two
|
|
554
554
|
* surfaces, and the two builds have drifted before over exactly this kind of
|
|
555
|
-
* detail
|
|
555
|
+
* detail. The argument is already-built markup and is deliberately not
|
|
556
556
|
* escaped; the *authored* text inside it was escaped, or made into a link, by
|
|
557
557
|
* whichever resolver called this.
|
|
558
558
|
*
|
|
@@ -574,7 +574,7 @@ function draftLink(inner) {
|
|
|
574
574
|
*
|
|
575
575
|
* **Code is verbatim.** A `[[…]]` inside a fenced or indented code block, or
|
|
576
576
|
* inside an inline code span, is source text an author wrote to be read as
|
|
577
|
-
* written, so it is left alone and not reported
|
|
577
|
+
* written, so it is left alone and not reported. Without that, a
|
|
578
578
|
* script sample containing `grid[[0]]` became a link — and only for some
|
|
579
579
|
* array shapes, `[[1,2],[3,4]]` having an inner `]` the pattern cannot cross,
|
|
580
580
|
* so the corruption looked arbitrary. It reaches the reader through the
|
|
@@ -595,23 +595,23 @@ function draftLink(inner) {
|
|
|
595
595
|
* @returns {{markdown: string, unresolved: Array<{link: string, target: string,
|
|
596
596
|
* offset: number, reason: string, packages?: string[], anchor?: string}>}}
|
|
597
597
|
* Each `reason` is one of {@link LINK_FINDING_REASONS}, the vocabulary all
|
|
598
|
-
* three resolvers share
|
|
598
|
+
* three resolvers share — `ambiguous` carries the claiming `packages`
|
|
599
599
|
* and `unknown-anchor` the section it named. `offset` is the link's 0-based
|
|
600
600
|
* position in `markdown`, which is what lets a caller report the line and
|
|
601
|
-
* column it sits on
|
|
601
|
+
* column it sits on.
|
|
602
602
|
*/
|
|
603
603
|
export function convertWikilinks(markdown, { type, id, pack, docPack, index }) {
|
|
604
604
|
const unresolved = [];
|
|
605
605
|
|
|
606
606
|
// `offset` is the third replacer argument because the pattern has exactly
|
|
607
607
|
// one capture group. It is what makes two identical unresolved links on
|
|
608
|
-
// one note tellable apart, and a position reportable at all
|
|
608
|
+
// one note tellable apart, and a position reportable at all.
|
|
609
609
|
const out = replaceOutsideCode(markdown, WIKILINK, (all, rawInner, offset) => {
|
|
610
610
|
const parsed = parseWikilink(rawInner);
|
|
611
611
|
const target = parsed.target;
|
|
612
612
|
const slug = parsed.anchor || null;
|
|
613
613
|
|
|
614
|
-
// **Every link carries a label
|
|
614
|
+
// **Every link carries a label**. Without one there is nothing
|
|
615
615
|
// to resolve against: the alias namespace a bare `[[Text]]` was looked
|
|
616
616
|
// up in is retired, and a shortcode is an address rather than prose, so
|
|
617
617
|
// the link has neither a target this build can find nor text to show.
|
|
@@ -631,7 +631,7 @@ export function convertWikilinks(markdown, { type, id, pack, docPack, index }) {
|
|
|
631
631
|
|
|
632
632
|
// An *empty* label is not a label — `[[x|]]` means "show the target's
|
|
633
633
|
// name" — and that reading comes from {@link authoredLabel} so the web
|
|
634
|
-
// resolver cannot draw the line somewhere else
|
|
634
|
+
// resolver cannot draw the line somewhere else.
|
|
635
635
|
let text = authoredLabel(parsed) ?? "";
|
|
636
636
|
|
|
637
637
|
// Resolve the document: the source note itself for an empty target, or
|
|
@@ -648,7 +648,7 @@ export function convertWikilinks(markdown, { type, id, pack, docPack, index }) {
|
|
|
648
648
|
const qualified = readQualifier(target, index.types, index.packages);
|
|
649
649
|
qualifiedRead = qualified;
|
|
650
650
|
// A target that does not parse as an address is a defect: there is
|
|
651
|
-
// no second namespace left to fall through to
|
|
651
|
+
// no second namespace left to fall through to.
|
|
652
652
|
if (!qualified || qualified.reason) {
|
|
653
653
|
unresolved.push({
|
|
654
654
|
link: all,
|
|
@@ -659,7 +659,7 @@ export function convertWikilinks(markdown, { type, id, pack, docPack, index }) {
|
|
|
659
659
|
});
|
|
660
660
|
return unresolvedLink(text || target, target);
|
|
661
661
|
}
|
|
662
|
-
// An omitted system defaults from where the link is written
|
|
662
|
+
// An omitted system defaults from where the link is written,
|
|
663
663
|
// and a body is under no system block, so it is `none`. Under
|
|
664
664
|
// `none` a system-bearing type addresses its *documentation* — a
|
|
665
665
|
// note's `none` address IS its `doc<type>` entry — which is what a
|
|
@@ -686,7 +686,7 @@ export function convertWikilinks(markdown, { type, id, pack, docPack, index }) {
|
|
|
686
686
|
if (hits.length > 1) {
|
|
687
687
|
// Two packages publish the short address, so it names neither.
|
|
688
688
|
// Its own class: the fix is the package-qualified form, not a
|
|
689
|
-
// corrected shortcode
|
|
689
|
+
// corrected shortcode.
|
|
690
690
|
unresolved.push({
|
|
691
691
|
link: all,
|
|
692
692
|
target,
|
|
@@ -729,7 +729,7 @@ export function convertWikilinks(markdown, { type, id, pack, docPack, index }) {
|
|
|
729
729
|
// An address with an *empty* label — `[[skill-clmb|]]` — has no prose
|
|
730
730
|
// to show, a shortcode being an address rather than display text, so
|
|
731
731
|
// the document's **current** name stands in and a rename shows at every
|
|
732
|
-
// citation with no link edited
|
|
732
|
+
// citation with no link edited. The knowledgebase build reads
|
|
733
733
|
// the same authored link the same way.
|
|
734
734
|
if (!text) text = doc.name ?? target;
|
|
735
735
|
|
|
@@ -751,13 +751,13 @@ export function convertWikilinks(markdown, { type, id, pack, docPack, index }) {
|
|
|
751
751
|
// page an anchor names. An Item or Actor link opens that document's
|
|
752
752
|
// *sheet*, which has no sections, so the anchor has nothing to address
|
|
753
753
|
// and is dropped. Forging a JournalEntryPage id onto a document that
|
|
754
|
-
// can never hold one is what made such links dead-end
|
|
754
|
+
// can never hold one is what made such links dead-end; an
|
|
755
755
|
// item's pages are addressed through its `doc<type>` counterpart.
|
|
756
756
|
// A `#section` the target declares no heading for. Checked here, and
|
|
757
757
|
// not only by `content-build links`, because this is the build that
|
|
758
758
|
// *emits* the link: `anchorPageId` will hash any slug into a page id,
|
|
759
759
|
// so an undeclared one compiles to a `@UUID` that dead-ends for the
|
|
760
|
-
// reader
|
|
760
|
+
// reader. A foreign anchor has always been checked this way —
|
|
761
761
|
// the manifest carries the map — and a local one now is too, from the
|
|
762
762
|
// anchor set the index carries.
|
|
763
763
|
if (slug && isJournal && doc.anchors && !doc.anchors.has(slug)) {
|
|
@@ -774,8 +774,8 @@ export function convertWikilinks(markdown, { type, id, pack, docPack, index }) {
|
|
|
774
774
|
const uuid =
|
|
775
775
|
slug && isJournal ? pageUuid(entryUuid, anchorPageId(entryId, slug)) : entryUuid;
|
|
776
776
|
const link = `@UUID[${uuid}]{${text}}`;
|
|
777
|
-
// A link into a note that exists but is not written renders marked
|
|
778
|
-
//
|
|
777
|
+
// A link into a note that exists but is not written renders marked.
|
|
778
|
+
// Presentation only — the UUID above is unchanged, and a
|
|
779
779
|
// `[[#slug]]` self-link is not marked because the reader is already in
|
|
780
780
|
// the note it would be telling them about.
|
|
781
781
|
return doc.draft ? draftLink(link) : link;
|
package/hm3/actors.mjs
CHANGED
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
*
|
|
17
17
|
* The machinery is {@link module:engine/actor-compiler}'s and is shared with
|
|
18
18
|
* the SoHL pass: the predefined-item catalogue, the note-vocabulary →
|
|
19
|
-
* document-subtype translation each embedded reference goes through
|
|
19
|
+
* document-subtype translation each embedded reference goes through, the
|
|
20
20
|
* merge and the stable embedded ids. This module states HM3's data model and
|
|
21
21
|
* nothing else.
|
|
22
22
|
*
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
* splits what a note calls a `being` into a `character` and a `creature`, and
|
|
25
25
|
* nothing in the note's own vocabulary partitions cleanly onto that split — so
|
|
26
26
|
* the note says which, by writing `hm3.type`. An absent one is an error naming
|
|
27
|
-
* the note, never a default
|
|
27
|
+
* the note, never a default.
|
|
28
28
|
*
|
|
29
29
|
* **What is emitted, and what is deliberately not.** Four rows of the content
|
|
30
30
|
* format's `being` mapping table give HM3 a destination — `data.portrait` →
|
|
@@ -32,8 +32,8 @@
|
|
|
32
32
|
* `data.templatePriority` → `flags.hm3.templatePriority` — and every one of
|
|
33
33
|
* them is *declared as that source* rather than as the bare key the corpus
|
|
34
34
|
* writes, so the specification's mapping is executable rather than
|
|
35
|
-
* aspirational.
|
|
36
|
-
*
|
|
35
|
+
* aspirational. Plus the two anchored prose sections: `{#appearance}` is HM3's
|
|
36
|
+
* `description` and `{#dossier}` its
|
|
37
37
|
* `biography`. Everything else an HM3 actor carries — the thirteen abilities,
|
|
38
38
|
* the sunsign, `move`, `fatigue`, `shockIndex`, a creature's `loadRating` — has
|
|
39
39
|
* no shared source stated anywhere, so it is authored at its own path under
|
|
@@ -57,7 +57,7 @@ import { documentSubtype } from "../engine/document-subtypes.mjs";
|
|
|
57
57
|
import { HM3_DOCUMENT_SUBTYPES } from "./document-subtypes.mjs";
|
|
58
58
|
import { templateFlags } from "./template-priority.mjs";
|
|
59
59
|
// The retirement window's reports, shared with the frontmatter lint so the
|
|
60
|
-
// two cannot say different things about the same key
|
|
60
|
+
// two cannot say different things about the same key.
|
|
61
61
|
import {
|
|
62
62
|
legacyKeyMessage,
|
|
63
63
|
locateFrontmatterKey,
|
|
@@ -65,7 +65,7 @@ import {
|
|
|
65
65
|
} from "../engine/retired-fields.mjs";
|
|
66
66
|
// The note-level `hm3:` block: `hm3.system` onto the document's `system`
|
|
67
67
|
// verbatim, and `hm3.img` / `hm3.items` / `hm3.effects` / `hm3.flags`
|
|
68
|
-
// overriding their shared top-level forms for this system alone
|
|
68
|
+
// overriding their shared top-level forms for this system alone.
|
|
69
69
|
import { blockField, blockProperty, mergeSystemData } from "../engine/system-block.mjs";
|
|
70
70
|
|
|
71
71
|
/**
|
|
@@ -73,13 +73,13 @@ import { blockField, blockProperty, mergeSystemData } from "../engine/system-blo
|
|
|
73
73
|
*
|
|
74
74
|
* Declared rather than read by hand, so a value resolves by the same order
|
|
75
75
|
* every other declared field does — `hm3.system.<to>` first, then the in-block
|
|
76
|
-
* position, then the shared source, then the default
|
|
76
|
+
* position, then the shared source, then the default — and so the
|
|
77
77
|
* author-facing reference can be generated from the same statement the compiler
|
|
78
|
-
* obeys
|
|
78
|
+
* obeys.
|
|
79
79
|
*
|
|
80
|
-
* **Both positions, named separately
|
|
80
|
+
* **Both positions, named separately.** The specification maps
|
|
81
81
|
* `data.species` onto `system.species`, and every HM3 note in the corpus writes
|
|
82
|
-
* `hm3.species`. Until
|
|
82
|
+
* `hm3.species`. Until the sweep those are two live positions for one
|
|
83
83
|
* field, so the declaration names both: `name` is the shared source, `legacyKey`
|
|
84
84
|
* the key the block still carries, and the block wins while it is there. Naming
|
|
85
85
|
* only one of them is what made the row unreadable — a plain `species` could
|
|
@@ -107,12 +107,12 @@ const ACTOR_FIELDS = Object.freeze([
|
|
|
107
107
|
* `data.species` was: read with `blockProperty(fm, block, "portrait")` it saw
|
|
108
108
|
* the block and the note's top level and nothing else, so the `data.portrait`
|
|
109
109
|
* the specification names never reached the document and the `?? defaultImg`
|
|
110
|
-
* beside it dressed the miss up as "this note names no art"
|
|
110
|
+
* beside it dressed the miss up as "this note names no art".
|
|
111
111
|
*
|
|
112
112
|
* It is **not** in `ACTOR_FIELDS`, because `buildFromFields` has no seam for
|
|
113
113
|
* the subtype default that has to follow it — the `?? defaultImg` is the whole
|
|
114
|
-
* of what distinguishes an unnamed portrait from a deliberately blank one
|
|
115
|
-
*
|
|
114
|
+
* of what distinguishes an unnamed portrait from a deliberately blank one,
|
|
115
|
+
* and it needs a subtype the coercion is not handed.
|
|
116
116
|
*
|
|
117
117
|
* @type {import("../engine/field-spec.mjs").FieldSpec}
|
|
118
118
|
*/
|
|
@@ -270,7 +270,7 @@ export class Hm3Actors extends SystemActorCompiler {
|
|
|
270
270
|
const id = fm.id;
|
|
271
271
|
const ctx = `actor "${name}"`;
|
|
272
272
|
// The document's own subtype, and the art that goes with it. Both are
|
|
273
|
-
// looked up from the note's `type` rather than spelled here
|
|
273
|
+
// looked up from the note's `type` rather than spelled here; for
|
|
274
274
|
// HM3 the row is one-to-many, so the note's `hm3.type` decides.
|
|
275
275
|
const subType = /** @type {string} */ (
|
|
276
276
|
documentSubtype(this.documentSubtypes, fm.type, fm, {
|
|
@@ -283,7 +283,7 @@ export class Hm3Actors extends SystemActorCompiler {
|
|
|
283
283
|
|
|
284
284
|
// The sweep's progress signal, reported where an author meets it
|
|
285
285
|
// soonest — every consumer runs the compile, and not every one runs the
|
|
286
|
-
// lint
|
|
286
|
+
// lint. A warning: the note compiles to the correct document
|
|
287
287
|
// either way, and refusing the position comes once no tree writes it.
|
|
288
288
|
const onLegacyKey = (field) =>
|
|
289
289
|
this.noteWarn(
|
|
@@ -292,7 +292,7 @@ export class Hm3Actors extends SystemActorCompiler {
|
|
|
292
292
|
);
|
|
293
293
|
|
|
294
294
|
// The shared level's own retiring position — the top-level key `data:`
|
|
295
|
-
// gathered the field off
|
|
295
|
+
// gathered the field off. Same signal, same severity, separate
|
|
296
296
|
// callback: a note may have moved one position and not the other.
|
|
297
297
|
// Anchored at column 1, because the two spellings coincide — `portrait`
|
|
298
298
|
// names a block key and a top-level one — and a locator that took the
|
|
@@ -312,16 +312,16 @@ export class Hm3Actors extends SystemActorCompiler {
|
|
|
312
312
|
// One spelling, as everywhere else: `packFolder` names a folder note
|
|
313
313
|
// by its address. This pass once read only the Foundry id, so an HM3
|
|
314
314
|
// tree could not file an actor by address at all — which its own sweep
|
|
315
|
-
// needs; the id spelling is retired outright
|
|
315
|
+
// needs; the id spelling is retired outright.
|
|
316
316
|
const folder = this.folderResolver(blockField(fm, block, "packFolder", null), {
|
|
317
317
|
isAddress: true,
|
|
318
318
|
});
|
|
319
319
|
|
|
320
320
|
const system = {
|
|
321
|
-
// Nullish, not
|
|
321
|
+
// Nullish, not `||`: a note that names no portrait gets the
|
|
322
322
|
// subtype's default, one that writes `""` ships blank on purpose.
|
|
323
323
|
// Resolved through the declaration so `data.portrait` is reached at
|
|
324
|
-
// all — see {@link BIO_IMAGE_FIELD}
|
|
324
|
+
// all — see {@link BIO_IMAGE_FIELD}.
|
|
325
325
|
bioImage: readField(BIO_IMAGE_FIELD, fm, reports) ?? defaultImg,
|
|
326
326
|
description: renderSection(body || "", "appearance"),
|
|
327
327
|
biography: renderSection(body || "", "dossier"),
|
|
@@ -332,13 +332,13 @@ export class Hm3Actors extends SystemActorCompiler {
|
|
|
332
332
|
};
|
|
333
333
|
|
|
334
334
|
// Whatever the note authors under `hm3.system`, at the DataModel's own
|
|
335
|
-
// paths
|
|
335
|
+
// paths. This pass has no field declaration, so it claims
|
|
336
336
|
// nothing: every authored path is the author's, and the fields above
|
|
337
337
|
// are what a note that authors none still gets.
|
|
338
338
|
mergeSystemData(system, fm, { block });
|
|
339
339
|
this.reportUndeclaredSystemData(fm, block, "Actor", subType);
|
|
340
340
|
// And what this pass wrote itself — there is no field declaration for a
|
|
341
|
-
// being at all, so every key above is a compiler emission
|
|
341
|
+
// being at all, so every key above is a compiler emission.
|
|
342
342
|
this.reportEmittedSystemData(system, {
|
|
343
343
|
fm,
|
|
344
344
|
block,
|
|
@@ -385,7 +385,7 @@ export class Hm3Actors extends SystemActorCompiler {
|
|
|
385
385
|
* template priority.
|
|
386
386
|
*
|
|
387
387
|
* The rule itself is {@link module:hm3/template-priority.templateFlags},
|
|
388
|
-
* because the Item pass writes the same flag from the same statement
|
|
388
|
+
* because the Item pass writes the same flag from the same statement
|
|
389
389
|
* and two copies of it were one copy too many — this pass had the only one,
|
|
390
390
|
* and the Item pass had none.
|
|
391
391
|
*
|
|
@@ -14,18 +14,18 @@
|
|
|
14
14
|
/**
|
|
15
15
|
* **HM3's note-type → document-subtype map** — which Foundry document, and
|
|
16
16
|
* which subtype of it, a note of each content type compiles into for HârnMaster
|
|
17
|
-
* 3
|
|
17
|
+
* 3.
|
|
18
18
|
*
|
|
19
19
|
* The mechanism is `engine/document-subtypes.mjs`, the same one SoHL's
|
|
20
20
|
* declaration uses; the two halves share it and share nothing else. That is the
|
|
21
|
-
* `engine/` ÷ system line this package draws everywhere
|
|
21
|
+
* `engine/` ÷ system line this package draws everywhere, and this file is
|
|
22
22
|
* the first evidence that the line holds: a second system declared its map
|
|
23
23
|
* without a line of the mechanism changing.
|
|
24
24
|
*
|
|
25
25
|
* **Where this map differs from SoHL's, and why that matters.**
|
|
26
26
|
*
|
|
27
|
-
* SoHL's map
|
|
28
|
-
* which was a fact about SoHL rather than a rule
|
|
27
|
+
* SoHL's map would be the identity in every row but for three renames,
|
|
28
|
+
* which was a fact about SoHL rather than a rule — and while it stayed
|
|
29
29
|
* true nothing proved the map was doing any work. HM3's was never close. Four
|
|
30
30
|
* of its rows are one-to-many, one renames outright (`projectile` →
|
|
31
31
|
* `missilegear`), and five of its type names are SoHL's own with a *different
|
|
@@ -82,7 +82,7 @@ export const HM3_TYPE_KEY = "type";
|
|
|
82
82
|
* map does not name — `affiliation`, `affliction`, `attribute`,
|
|
83
83
|
* `concoction`, `mystery`, and every core type — compile into no HM3
|
|
84
84
|
* document at all, silently and correctly: HM3 has no form of them, and a
|
|
85
|
-
* finding on every such note would be the noise
|
|
85
|
+
* finding on every such note would be the noise the rule exists to prevent.
|
|
86
86
|
*
|
|
87
87
|
* @type {import("../engine/document-subtypes.mjs").DocumentSubtypeMap}
|
|
88
88
|
*/
|
package/hm3/index.mjs
CHANGED
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
|
|
14
14
|
/**
|
|
15
15
|
* The HM3-specific half of the toolchain: the knowledge of the HârnMaster 3
|
|
16
|
-
* data model that a generic content module must never receive
|
|
16
|
+
* data model that a generic content module must never receive.
|
|
17
17
|
*
|
|
18
18
|
* The second half, and the first evidence that `engine/` ÷ system is a real
|
|
19
19
|
* line rather than an aspiration: HM3's item-type registry and its builders,
|
|
@@ -39,7 +39,7 @@ export * as itemBuilders from "./item-builders.mjs";
|
|
|
39
39
|
/** The `hm3:` frontmatter vocabulary of every HM3 item type. */
|
|
40
40
|
export * as itemFields from "./item-fields.mjs";
|
|
41
41
|
|
|
42
|
-
/** This system's note-type → document-subtype map, one-to-many rows and all
|
|
42
|
+
/** This system's note-type → document-subtype map, one-to-many rows and all. */
|
|
43
43
|
export * as documentSubtypes from "./document-subtypes.mjs";
|
|
44
44
|
|
|
45
45
|
/** The Item compiler. */
|
package/hm3/item-builders.mjs
CHANGED
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
* both systems names both: `itemBuilders: [sohl, hm3]`. The engine keeps them
|
|
20
20
|
* apart from there on — `itemBuilder(type, system)` takes the system that is
|
|
21
21
|
* asking, and a type both registries declare **throws** rather than resolving
|
|
22
|
-
* when nobody says which
|
|
22
|
+
* when nobody says which. That guard existed before there was a second
|
|
23
23
|
* registry to trip it; this is the registry it was written for.
|
|
24
24
|
*
|
|
25
25
|
* **The builders are generated, not written**, from the field declarations in
|
package/hm3/item-fields.mjs
CHANGED
|
@@ -33,16 +33,16 @@
|
|
|
33
33
|
* **One shared source, two destinations.** `name` is the shared property a
|
|
34
34
|
* field draws from and `to` is where it lands, so a single authored `weight`
|
|
35
35
|
* feeds `sohl.system.weightBase` *and* `hm3.system.weight` — one authored fact,
|
|
36
|
-
* two documents
|
|
36
|
+
* two documents. That is the whole reason the two are declared
|
|
37
37
|
* separately.
|
|
38
38
|
*
|
|
39
39
|
* The specification writes those sources as `data.weight`, and the sources here
|
|
40
|
-
* are written bare, exactly as SoHL's are: the `data:` region is
|
|
40
|
+
* are written bare, exactly as SoHL's are: the `data:` region is the sweep's
|
|
41
41
|
* migration and neither half has moved to it. Spelling HM3's differently would
|
|
42
42
|
* make a note feeding both systems author the same fact twice for no gain, and
|
|
43
43
|
* would take these rows out of reach of `content-build content-format fields`,
|
|
44
44
|
* which pairs a `data.<key>` claim with a bare declared `<key>`. Both halves
|
|
45
|
-
* move together when
|
|
45
|
+
* move together when the sweep lands.
|
|
46
46
|
*
|
|
47
47
|
* **What a note authors under `hm3.system` is not declared here.** HM3 fields
|
|
48
48
|
* that no shared `data:` property feeds — a skill's `type`, an armour's
|
package/hm3/items.mjs
CHANGED
|
@@ -13,13 +13,13 @@
|
|
|
13
13
|
|
|
14
14
|
/**
|
|
15
15
|
* **HM3's Item pass** — the two things about compiling a note into an HM3 Item
|
|
16
|
-
* that are facts about HM3 rather than about the note format
|
|
16
|
+
* that are facts about HM3 rather than about the note format.
|
|
17
17
|
*
|
|
18
18
|
* Everything else is {@link module:engine/item-compiler}'s, and is the same
|
|
19
19
|
* code the SoHL pass runs: which notes are claimed, which subtype each becomes,
|
|
20
20
|
* which registry builds it, the authored `hm3.system` passthrough, the schema
|
|
21
21
|
* check, and the compendium envelope. A second system is a map and a handful of
|
|
22
|
-
* emitted keys, which is the arrangement
|
|
22
|
+
* emitted keys, which is the arrangement the mapping tables build towards.
|
|
23
23
|
*
|
|
24
24
|
* **What HM3's compiler writes on every item: one key, and only when there is
|
|
25
25
|
* something to write.** The content format gives an item's `{#appearance}`
|
|
@@ -35,9 +35,9 @@
|
|
|
35
35
|
* priority is a shared statement — a note declaring `data.templatePriority` says
|
|
36
36
|
* the same thing to both systems — but HM3 declares no `system` field for it, so
|
|
37
37
|
* it lands under this system's own flag scope as `flags.hm3.templatePriority`,
|
|
38
|
-
* exactly as the Actor pass writes it.
|
|
39
|
-
*
|
|
40
|
-
*
|
|
38
|
+
* exactly as the Actor pass writes it. Without it, an item note declaring the
|
|
39
|
+
* priority compiles into a SoHL item that knows it is a template and an HM3
|
|
40
|
+
* item that does not.
|
|
41
41
|
*
|
|
42
42
|
* **There is no HM3 equivalent of `docHtml`.** SoHL points an item at the
|
|
43
43
|
* JournalEntry its prose compiled into, and HM3's data model has nowhere to put
|
|
@@ -57,13 +57,25 @@ import { templateFlags } from "./template-priority.mjs";
|
|
|
57
57
|
export class Hm3Items extends SystemItemCompiler {
|
|
58
58
|
/**
|
|
59
59
|
* HM3's note-type → document-subtype map — the one declaration that says
|
|
60
|
-
* which block this pass reads, which notes it claims, and what each becomes
|
|
61
|
-
* (#58/#79).
|
|
60
|
+
* which block this pass reads, which notes it claims, and what each becomes.
|
|
62
61
|
*
|
|
63
62
|
* @type {import("../engine/document-subtypes.mjs").DocumentSubtypeMap}
|
|
64
63
|
*/
|
|
65
64
|
static documentSubtypes = HM3_DOCUMENT_SUBTYPES;
|
|
66
65
|
|
|
66
|
+
/**
|
|
67
|
+
* The `system` keys this pass derives from the note.
|
|
68
|
+
*
|
|
69
|
+
* `description` is the note's `{#appearance}` section, rendered. A note
|
|
70
|
+
* authoring it writes into a key the compiler overwrites from prose that
|
|
71
|
+
* may say something else.
|
|
72
|
+
*
|
|
73
|
+
* @type {readonly {key: string, from: string}[]}
|
|
74
|
+
*/
|
|
75
|
+
static derivedSystemKeys = Object.freeze([
|
|
76
|
+
{ key: "description", from: "the note's `{#appearance}` section" },
|
|
77
|
+
]);
|
|
78
|
+
|
|
67
79
|
/**
|
|
68
80
|
* The `system.*` field HM3 writes on an item from the note's prose.
|
|
69
81
|
*
|
|
@@ -85,7 +97,7 @@ export class Hm3Items extends SystemItemCompiler {
|
|
|
85
97
|
* The same statement the Actor pass records, through the same
|
|
86
98
|
* {@link module:hm3/template-priority.templateFlags} — see that module for
|
|
87
99
|
* why the priority lives in flags at all, and for what its absence here
|
|
88
|
-
* cost
|
|
100
|
+
* cost.
|
|
89
101
|
*
|
|
90
102
|
* @param {object} fm - The note's frontmatter.
|
|
91
103
|
* @returns {object} The flags to emit.
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
|
|
14
14
|
/**
|
|
15
15
|
* **Where HM3 records the template priority** — one statement, read by both of
|
|
16
|
-
* this system's passes
|
|
16
|
+
* this system's passes.
|
|
17
17
|
*
|
|
18
18
|
* `data.templatePriority` is the shared fact that a note is a *starting
|
|
19
19
|
* template*, and the specification states it as a row every type maps:
|
|
@@ -48,7 +48,7 @@ import { blockProperty } from "../engine/system-block.mjs";
|
|
|
48
48
|
* A note that is not a template — or states nothing — writes nothing, rather
|
|
49
49
|
* than a `null` nothing reads.
|
|
50
50
|
*
|
|
51
|
-
* **Read through the shared resolver, not a field declaration
|
|
51
|
+
* **Read through the shared resolver, not a field declaration**. A
|
|
52
52
|
* `FieldSpec`'s shared source is a single position, and this value has five:
|
|
53
53
|
* `data:`, this block, the top level, and the retiring `archetype` spelling in
|
|
54
54
|
* the latter two. The resolver is the single implementation of what a note said,
|
package/lang.mjs
CHANGED
|
@@ -29,13 +29,13 @@
|
|
|
29
29
|
* `expandObject` **throws**: it cannot create a `Note` property on the string
|
|
30
30
|
* `"The Pall"`. Foundry catches that throw and discards the **entire** file,
|
|
31
31
|
* so one colliding pair drops every translation in it and each string renders
|
|
32
|
-
* as its raw key
|
|
32
|
+
* as its raw key. A key must be a leaf **or** a branch, never both.
|
|
33
33
|
* - **A Handlebars placeholder.** Foundry interpolates with `format()` and
|
|
34
34
|
* SINGLE braces, so a `{{…}}` value renders literally unless some call site
|
|
35
|
-
* happens to hand it to a Handlebars pass
|
|
35
|
+
* happens to hand it to a Handlebars pass.
|
|
36
36
|
* - **Data baked into a key segment.** A segment carrying anything but
|
|
37
37
|
* `[A-Za-z0-9_-]` is a path or a UUID in a key, and a dotted payload is how
|
|
38
|
-
* the collision above gets in
|
|
38
|
+
* the collision above gets in.
|
|
39
39
|
*
|
|
40
40
|
* Every function here is pure — it takes source text and returns findings, and
|
|
41
41
|
* touches no filesystem and emits nothing. The caller owns discovery and
|