@heroiclands/package-build 22.5.0 → 22.6.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 +23 -0
- package/CONTENT.md +30 -12
- package/docs/api.md +13 -9
- package/docs/content-format.md +115 -23
- package/engine/actor-compiler.mjs +12 -0
- package/engine/base-compiler.mjs +51 -36
- package/engine/content-index.mjs +21 -12
- package/engine/foundry-entries.mjs +50 -16
- package/engine/generate.mjs +81 -10
- package/engine/infobox-registry.mjs +14 -13
- package/engine/item-compiler.mjs +12 -0
- package/engine/journals.mjs +13 -5
- package/engine/note-claims.mjs +98 -2
- package/engine/note-state.mjs +29 -0
- package/engine/subtype-registry.mjs +42 -5
- package/package.json +1 -1
- package/types/engine/actor-compiler.d.mts +11 -0
- package/types/engine/base-compiler.d.mts +46 -16
- package/types/engine/content-index.d.mts +4 -3
- package/types/engine/generate.d.mts +29 -0
- package/types/engine/infobox-registry.d.mts +11 -12
- package/types/engine/item-compiler.d.mts +11 -0
- package/types/engine/note-claims.d.mts +6 -1
- package/types/engine/note-state.d.mts +26 -0
- package/types/engine/subtype-registry.d.mts +32 -5
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,28 @@
|
|
|
1
1
|
# @heroiclands/package-build
|
|
2
2
|
|
|
3
|
+
## 22.6.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
**Compendium content**
|
|
8
|
+
|
|
9
|
+
- A person or thing with no game-system data no longer becomes a blank
|
|
10
|
+
compendium entry — the note still publishes its page and its journal, so
|
|
11
|
+
somebody can be written without being statted.
|
|
12
|
+
- A package carrying data for a game system it ships no compendium for is now
|
|
13
|
+
told so, rather than having it quietly dropped.
|
|
14
|
+
|
|
15
|
+
## 22.5.1
|
|
16
|
+
|
|
17
|
+
### Patch Changes
|
|
18
|
+
|
|
19
|
+
**Unwritten notes**
|
|
20
|
+
|
|
21
|
+
- A note left unwritten builds: an empty body no longer stops a package
|
|
22
|
+
compiling its packs.
|
|
23
|
+
- An unwritten note offers no compendium link, so nothing sends a reader to a
|
|
24
|
+
journal that was never made.
|
|
25
|
+
|
|
3
26
|
## 22.5.0
|
|
4
27
|
|
|
5
28
|
### Minor Changes
|
package/CONTENT.md
CHANGED
|
@@ -867,7 +867,12 @@ has no art.
|
|
|
867
867
|
A note is **system-agnostic**. The only system-specific things it carries are
|
|
868
868
|
the properties named after a system, and one note may carry more than one — a
|
|
869
869
|
`being` in `harn-ensemble` compiles into a SoHL `being` _and_ an HM3
|
|
870
|
-
`character`.
|
|
870
|
+
`character`.
|
|
871
|
+
|
|
872
|
+
**The block is what makes a game document.** A note carrying none produces no
|
|
873
|
+
Actor and no Item at all, in any package; its prose still compiles into a page,
|
|
874
|
+
a PDF page and a JournalEntry, so a person can be written without being statted.
|
|
875
|
+
Within a system's block:
|
|
871
876
|
|
|
872
877
|
| property | maps to |
|
|
873
878
|
| ------------------ | ------------------------------------------------------------- |
|
|
@@ -1006,12 +1011,19 @@ hold this package at a build that does not write it. A subtree the schema
|
|
|
1006
1011
|
declares but describes no further — a discriminated `TypedSchemaField`, stored
|
|
1007
1012
|
flat — is left alone rather than reported wholesale.
|
|
1008
1013
|
|
|
1009
|
-
**
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1014
|
+
**An Actor or Item pass takes only notes carrying the block it reads.** A note
|
|
1015
|
+
that says nothing about a system has no system data, and compiling it would emit
|
|
1016
|
+
a hollow document — a subtype, and none of the fields the subtype exists for. So
|
|
1017
|
+
a note carrying no block for any system produces no Actor and no Item anywhere,
|
|
1018
|
+
and is passed over in silence; its prose still compiles into a page, a PDF page
|
|
1019
|
+
and a JournalEntry. The question is asked of the pass's own system rather than
|
|
1020
|
+
of the pack's `system:`: a pack declaring none is compiled by the fallback pass,
|
|
1021
|
+
which reads one block like any other. A pass whose document is not system data
|
|
1022
|
+
at all — journals, macros, scenes — is not subject to the rule.
|
|
1023
|
+
|
|
1024
|
+
**A block for a system no pack of the class compiles is an error**, reported
|
|
1025
|
+
once per note per system with the position of the block key, because the
|
|
1026
|
+
authored data is discarded either way.
|
|
1015
1027
|
|
|
1016
1028
|
**`(type, shortcode)` resolves inside one system's catalogue.** A being names its
|
|
1017
1029
|
embedded items by address and never by pack, so the Item packs are read as one
|
|
@@ -1096,11 +1108,17 @@ was never the key count.)
|
|
|
1096
1108
|
### An empty body is a stub, on purpose
|
|
1097
1109
|
|
|
1098
1110
|
A note whose body is empty is a **stub**: it carries its frontmatter into the
|
|
1099
|
-
content index and is queryable there, it keeps its `id
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1111
|
+
content index and is queryable there, it keeps its `id`, and it publishes no
|
|
1112
|
+
page, no address and no link target. A note with a body and the `draft` tag is a
|
|
1113
|
+
**draft**, and one with a body and no tag is **full**.
|
|
1114
|
+
`docs/content-format.md` states the rule; this is what the lint does with it.
|
|
1115
|
+
|
|
1116
|
+
**A document that would be empty is not created**, and the index then names no
|
|
1117
|
+
artefact — `uuid`, `path` and `address` are `null`. An unwritten note still
|
|
1118
|
+
compiles every document derived from `data:`, so an arcane talent is still its
|
|
1119
|
+
Item; what it compiles no journal for is its prose, because there is none. An
|
|
1120
|
+
infobox does not count as content: the panel renders `data:` the index already
|
|
1121
|
+
carries.
|
|
1104
1122
|
|
|
1105
1123
|
Four findings and two reports:
|
|
1106
1124
|
|
package/docs/api.md
CHANGED
|
@@ -416,15 +416,16 @@ Drawing a declared infobox: HTML for a Foundry Journal Page, Typst for the book.
|
|
|
416
416
|
|
|
417
417
|
The three states of a note — **stub**, **draft**, **full** — and the one observable fact they rest on. A note whose body is empty is a stub: it carries its facts into the index and is queryable there, and it has no page, no address and no link target. Nothing is stored: the ladder is derived once in the SQL `entries` view and no note can assert its own state.
|
|
418
418
|
|
|
419
|
-
| Export | Signature | Returns | Use it when
|
|
420
|
-
| --------------------- | ------------------------------------------- | --------------------------------------- |
|
|
421
|
-
| `NOTE_STATES` | `const NOTE_STATES` | `ReadonlyArray<string>` | naming the three states in ladder order
|
|
422
|
-
| `isEmptyBody` | `isEmptyBody(body)` | `boolean` | asking the exact question: is everything after the closing frontmatter fence whitespace?
|
|
423
|
-
| `isStubbableType` | `isStubbableType(type, vocabulary)` | `boolean` | asking whether an empty body suppresses this type's page; `folder` and `homepage` are structural and exempt
|
|
424
|
-
| `isStubNote` | `isStubNote(frontmatter, body, vocabulary)` | `boolean` | deciding whether one note is a stub, which is what the index and the site build both ask
|
|
425
|
-
| `
|
|
426
|
-
| `
|
|
427
|
-
| `
|
|
419
|
+
| Export | Signature | Returns | Use it when |
|
|
420
|
+
| --------------------- | ------------------------------------------- | --------------------------------------- | -------------------------------------------------------------------------------------------------------------------------- |
|
|
421
|
+
| `NOTE_STATES` | `const NOTE_STATES` | `ReadonlyArray<string>` | naming the three states in ladder order |
|
|
422
|
+
| `isEmptyBody` | `isEmptyBody(body)` | `boolean` | asking the exact question: is everything after the closing frontmatter fence whitespace? |
|
|
423
|
+
| `isStubbableType` | `isStubbableType(type, vocabulary)` | `boolean` | asking whether an empty body suppresses this type's page; `folder` and `homepage` are structural and exempt |
|
|
424
|
+
| `isStubNote` | `isStubNote(frontmatter, body, vocabulary)` | `boolean` | deciding whether one note is a stub, which is what the index and the site build both ask |
|
|
425
|
+
| `journalHasContent` | `journalHasContent(body)` | `boolean` | asking whether the JournalEntry a body compiles into would hold anything, which is what decides whether one is made at all |
|
|
426
|
+
| `PLACEHOLDER_PHRASES` | `const PLACEHOLDER_PHRASES` | `ReadonlyArray<string>` | reading the phrases that stand in for prose nobody has written |
|
|
427
|
+
| `placeholderBody` | `placeholderBody(body)` | `Array<{phrase: string, line: number}>` | finding a body that reduces to nothing but placeholders, which is an abandoned draft rather than a stub |
|
|
428
|
+
| `bodyWordCount` | `bodyWordCount(body)` | `number` | counting the prose a reader meets, with a wikilink counting as the one word it renders |
|
|
428
429
|
|
|
429
430
|
### `engine.stubLint`
|
|
430
431
|
|
|
@@ -626,6 +627,8 @@ Which note-type → document-subtype maps this toolchain ships, and the two ques
|
|
|
626
627
|
| `KNOWN_DOCUMENT_SUBTYPE_MAPS` | `const KNOWN_DOCUMENT_SUBTYPE_MAPS` | — | The note-type → document-subtype maps this toolchain ships. |
|
|
627
628
|
| `ACTOR_TYPES` | `const ACTOR_TYPES` | — | Every note type any shipped map compiles into an **Actor**. |
|
|
628
629
|
| `DEFAULT_DOCUMENT_SUBTYPES` | `const DEFAULT_DOCUMENT_SUBTYPES` | — | The map a pack declaring no `system:` compiles its documents against. |
|
|
630
|
+
| `SHIPPED_SYSTEMS` | `const SHIPPED_SYSTEMS` | — | Every system this toolchain ships a map for, by id. |
|
|
631
|
+
| `SYSTEM_DOCUMENT_CLASSES` | `const SYSTEM_DOCUMENT_CLASSES` | — | The Foundry document classes a system compiles its own data into. |
|
|
629
632
|
| `subtypeMapFor` | `function subtypeMapFor(system)` | {import("./document-subtypes.mjs").DocumentSubtypeMap\|undefined} Its map, or `undefined` where this toolchain ships none for it. | The map one system ships, by its id. |
|
|
630
633
|
| `schemaSubtypeOf` | `function schemaSubtypeOf(system, type)` | {string} The document subtype to look up. | The document subtype a note type compiles into for one system — the translation the _schema_ check needs, and the reason it needs one. |
|
|
631
634
|
|
|
@@ -1196,6 +1199,7 @@ Pack JSON generation — in-repo Markdown → per-entry JSON (build-only). Reads
|
|
|
1196
1199
|
|
|
1197
1200
|
| Export | Signature | Returns | Use it when |
|
|
1198
1201
|
| ----------------------------- | ------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------- |
|
|
1202
|
+
| `systemCompilers` | `function systemCompilers(passes)` | {Readonly<Record<string, Readonly<Record<string, Function>>>>} System id → document class → pass. | The system-specific compilers, derived from the passes themselves. |
|
|
1199
1203
|
| `compilerFor` | `function compilerFor(docType, system` | {Function\|undefined} The compiler class, or `undefined` for a document type nothing here compiles — which {@link generatePack} reports rather than defaulting past. | The compiler class a pack of one document type and one system gets. |
|
|
1200
1204
|
| `emittedArtFor` | `function emittedArtFor(type)` | {{document: string\|null, art: readonly string[]}\|null} What the type compiles into and the art it carries there, or `null` where no claim can be made — a retired type, which is reported as retired instead. | The art fields a note of one content type reaches its document through, and the document it reaches. |
|
|
1201
1205
|
| `packJsonDir` | `const packJsonDir` | {string} The pack's JSON directory. | Root of the build-only JSON tree for one pack. |
|
package/docs/content-format.md
CHANGED
|
@@ -197,13 +197,14 @@ _Not available_ on every affiliation page would suggest a gap in the note when
|
|
|
197
197
|
the truth is about the system's scope.
|
|
198
198
|
|
|
199
199
|
Whether a document was produced is the compile's own answer rather than a
|
|
200
|
-
reading of the frontmatter.
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
200
|
+
reading of the frontmatter. Three things must hold: the note must carry that
|
|
201
|
+
system's block, its type must map to the system, and it must route to a pack of
|
|
202
|
+
the document class that map names. The block is the first of them because a
|
|
203
|
+
system block is what makes a game document — see
|
|
204
|
+
[A system block is what makes a game document](#a-system-block-is-what-makes-a-game-document).
|
|
205
|
+
A pack declaring **no** system is compiled by the fallback pass, which reads one
|
|
206
|
+
system's block and no other, so the question is the same one whether or not the
|
|
207
|
+
pack answered it.
|
|
207
208
|
|
|
208
209
|
#### Four section layouts
|
|
209
210
|
|
|
@@ -788,8 +789,9 @@ _compendium_ receives its document.
|
|
|
788
789
|
— an item's prose compiling into a `JournalEntry` of its own — is not what the
|
|
789
790
|
author was addressing, and is routed by the pass that produces it.
|
|
790
791
|
|
|
791
|
-
**`<system>.pack`
|
|
792
|
-
|
|
792
|
+
**`<system>.pack` names the pack for one system's document.** Routing is per
|
|
793
|
+
system: each block the note carries sends its own document where that block
|
|
794
|
+
says, and a note carrying two blocks may place them in two packs.
|
|
793
795
|
|
|
794
796
|
```yaml
|
|
795
797
|
pack: items-sohl
|
|
@@ -797,9 +799,22 @@ hm3:
|
|
|
797
799
|
pack: items-hm3
|
|
798
800
|
```
|
|
799
801
|
|
|
800
|
-
**Unstated,
|
|
801
|
-
|
|
802
|
-
|
|
802
|
+
**Unstated, a system's document goes to that system's default pack of its
|
|
803
|
+
class** — the pack of the class declaring that `system:`, or, where none does,
|
|
804
|
+
the pack of the class marked `default: true`. Where nothing answers, the build
|
|
805
|
+
refuses rather than guessing, and names the candidates.
|
|
806
|
+
|
|
807
|
+
**`pack` and `packFolder` are different questions.** `pack` names _which
|
|
808
|
+
compendium_ receives a document; `packFolder` names the folder note whose
|
|
809
|
+
address the document is filed under _within_ whichever compendium that is. They
|
|
810
|
+
compose, and both are read per system, so a note may place its two systems'
|
|
811
|
+
documents in different packs and different folders:
|
|
812
|
+
|
|
813
|
+
```yaml
|
|
814
|
+
sohl:
|
|
815
|
+
pack: characteristics
|
|
816
|
+
packFolder: miscgear
|
|
817
|
+
```
|
|
803
818
|
|
|
804
819
|
**`pack: none` compiles the note into no document.** The note is walked,
|
|
805
820
|
published as a page, present in the content index with an address and no
|
|
@@ -1662,11 +1677,15 @@ convention rather than declared in a field — see
|
|
|
1662
1677
|
A note has three states, and two of them are nothing but conventions the format
|
|
1663
1678
|
already carried.
|
|
1664
1679
|
|
|
1665
|
-
| State | Body | Marker | Page | Document
|
|
1666
|
-
| --------- | ------- | --------------- | ----------- |
|
|
1667
|
-
| **stub** | empty | none | no | yes, where
|
|
1668
|
-
| **draft** | written | `tags: [draft]` | yes, marked | yes
|
|
1669
|
-
| **full** | written | none | yes | yes
|
|
1680
|
+
| State | Body | Marker | Page | Document | Link target |
|
|
1681
|
+
| --------- | ------- | --------------- | ----------- | --------------------------------- | ----------- |
|
|
1682
|
+
| **stub** | empty | none | no | yes, where one would not be empty | no |
|
|
1683
|
+
| **draft** | written | `tags: [draft]` | yes, marked | yes | yes |
|
|
1684
|
+
| **full** | written | none | yes | yes | yes |
|
|
1685
|
+
|
|
1686
|
+
"Document" here is the document a note's frontmatter earns. A system's Actor or
|
|
1687
|
+
Item is earned by that system's block, in every state —
|
|
1688
|
+
[a system block is what makes a game document](#a-system-block-is-what-makes-a-game-document).
|
|
1670
1689
|
|
|
1671
1690
|
**Full is the absence of everything.** It is the default and carries no marker,
|
|
1672
1691
|
which is right: the overwhelming majority of a finished corpus should say
|
|
@@ -1706,10 +1725,35 @@ column a table renderer already reads.
|
|
|
1706
1725
|
It keeps everything else:
|
|
1707
1726
|
|
|
1708
1727
|
- **`file`**, which is what you edit and what every diagnostic names.
|
|
1709
|
-
- **`id
|
|
1710
|
-
|
|
1711
|
-
|
|
1712
|
-
|
|
1728
|
+
- **`id`**, which is the identity every diagnostic and every table reads.
|
|
1729
|
+
- **`foundry`**, naming each document that was made.
|
|
1730
|
+
|
|
1731
|
+
#### A document that would be empty is not created
|
|
1732
|
+
|
|
1733
|
+
> **If the resulting document would be empty, it is not created** — and the
|
|
1734
|
+
> index then names no artefact: `uuid`, `path` and `address` are `null`, which
|
|
1735
|
+
> is how a record says one was not made.
|
|
1736
|
+
|
|
1737
|
+
The test is about the **output**, which is what lets one rule cover every case:
|
|
1738
|
+
|
|
1739
|
+
| The note | The document | Made? |
|
|
1740
|
+
| ---------------------------------- | -------------------------------------- | ----- |
|
|
1741
|
+
| `place`, `lore`, `doc`, `scenario` | its JournalEntry, which _is_ the prose | no |
|
|
1742
|
+
| any item type, `macro` | the Item or Macro, from `data:` | yes |
|
|
1743
|
+
| the same note's documentation | a JournalEntry of the same prose | no |
|
|
1744
|
+
| `map` | the Scene, from `data:` | yes |
|
|
1745
|
+
|
|
1746
|
+
An unwritten arcane talent still compiles into its Item, because an Item is its
|
|
1747
|
+
`data:` and that is not empty. What it has no documentation journal for is the
|
|
1748
|
+
same thing an unwritten `place` has no journal for: there is nothing to put in
|
|
1749
|
+
one.
|
|
1750
|
+
|
|
1751
|
+
**An infobox does not count as content.** The panel is a rendering of `data:`
|
|
1752
|
+
that the index already publishes, so an entry holding nothing else would tell a
|
|
1753
|
+
reader exactly what the record told them.
|
|
1754
|
+
|
|
1755
|
+
Nothing anywhere names a document that was not made: a compendium UUID for one
|
|
1756
|
+
would send every consumer that resolves it to a document the pack does not hold.
|
|
1713
1757
|
|
|
1714
1758
|
What is suppressed with the page is the web address and the link target: a
|
|
1715
1759
|
wikilink into a stub is refused, and a table cell listing one prints its name as
|
|
@@ -1738,8 +1782,8 @@ cannot be is the target of a wikilink, because a wikilink points at a page.
|
|
|
1738
1782
|
|
|
1739
1783
|
Note types fall into two groups, and only the first has a mapping table.
|
|
1740
1784
|
|
|
1741
|
-
**Types
|
|
1742
|
-
system, described by the tables further down.
|
|
1785
|
+
**Types that _may_ produce a system document** — a SoHL or HM3 Actor or Item,
|
|
1786
|
+
one per system, described by the tables further down.
|
|
1743
1787
|
|
|
1744
1788
|
**Types producing only a core document** — `lore`, `place`, `scenario`, `doc`,
|
|
1745
1789
|
`map`, `macro` and `homepage`. A core document is one Foundry defines rather than
|
|
@@ -1755,6 +1799,54 @@ having nothing worth summarising.
|
|
|
1755
1799
|
Every note in **both** groups still produces its JournalEntry and its web page.
|
|
1756
1800
|
The difference is only whether a system Actor or Item is created as well.
|
|
1757
1801
|
|
|
1802
|
+
#### A system block is what makes a game document
|
|
1803
|
+
|
|
1804
|
+
> **A note produces a system's Actor or Item only where it carries that
|
|
1805
|
+
> system's block.** A note carrying no block for any system produces no Actor
|
|
1806
|
+
> and no Item anywhere, whatever its packs declare.
|
|
1807
|
+
|
|
1808
|
+
The block is not a place to put optional detail — it is the statement that this
|
|
1809
|
+
person or thing is meant to be played. Its absence is a statement too, and it is
|
|
1810
|
+
made silently: the note is passed over by the Actor and Item passes exactly as
|
|
1811
|
+
any note they do not own, with no finding.
|
|
1812
|
+
|
|
1813
|
+
**Prose is untouched by this.** A body with content still compiles into one
|
|
1814
|
+
JournalEntry, one web page and one PDF page, however many blocks the note
|
|
1815
|
+
carries and whether or not it carries any; an empty body still compiles none of
|
|
1816
|
+
the three. So a note has three states, and each reaches the next by one edit in
|
|
1817
|
+
one file, with its address, its id, its index record and every link into it
|
|
1818
|
+
unchanged:
|
|
1819
|
+
|
|
1820
|
+
| The note carries | Produces |
|
|
1821
|
+
| -------------------------- | ---------------------------------------------- |
|
|
1822
|
+
| no block, empty body | an index record — a name that can be queried |
|
|
1823
|
+
| no block, a written body | a page, a PDF page and a JournalEntry |
|
|
1824
|
+
| a block, a written body | all of those, plus that system's Actor or Item |
|
|
1825
|
+
| two blocks, a written body | one document per system, and one of each other |
|
|
1826
|
+
|
|
1827
|
+
Write the person; add a system block when someone will need a token. A setting's
|
|
1828
|
+
cast is mostly people nobody rolls dice against, and a note about one of them
|
|
1829
|
+
carries no block and occupies no slot in a compendium a referee browses.
|
|
1830
|
+
|
|
1831
|
+
**A note carrying no document publishes no UUID for one.** Its address, its
|
|
1832
|
+
page and its documentation journal are published as they are for any other note,
|
|
1833
|
+
and the `foundry` entry that would name an Actor or an Item is absent — the same
|
|
1834
|
+
statement [`pack: none`](#the-pack-a-note-compiles-into) makes about a pack,
|
|
1835
|
+
made about a system.
|
|
1836
|
+
|
|
1837
|
+
**A block for a system this package ships no pack for is an error.** The
|
|
1838
|
+
authored data would be discarded either way, so the build says so, once per note
|
|
1839
|
+
per system, naming the system and the document class:
|
|
1840
|
+
|
|
1841
|
+
```
|
|
1842
|
+
assets/content/People/Aelric.md:7:1: error: carries a `hm3:` block, and no Actor pack in this package compiles hm3 documents, so the block is discarded — declare `system: hm3` on one of the `packs:`, or remove the block.
|
|
1843
|
+
```
|
|
1844
|
+
|
|
1845
|
+
A pack declaring no `system:` is compiled by the fallback pass, which reads one
|
|
1846
|
+
system's block — so a systemless Actor pack serves that system and not every
|
|
1847
|
+
system a note might write. A package shipping for a second system declares
|
|
1848
|
+
`system:` on a pack of each class it compiles.
|
|
1849
|
+
|
|
1758
1850
|
That includes actors. A being producing its Actor and nothing else would be
|
|
1759
1851
|
the one system-bearing note with no address at `none`, leaving a prose link
|
|
1760
1852
|
naming it nowhere to land. It carries a documentation journal like every other
|
|
@@ -501,6 +501,18 @@ export class SystemActorCompiler extends BasePackCompiler {
|
|
|
501
501
|
*/
|
|
502
502
|
static requiresSystemBlock = true;
|
|
503
503
|
|
|
504
|
+
/**
|
|
505
|
+
* The Foundry document class this pass writes.
|
|
506
|
+
*
|
|
507
|
+
* Declared by the class that does the writing, so the table pairing a
|
|
508
|
+
* system with its passes is derived from the passes themselves rather than
|
|
509
|
+
* written out a second time — see
|
|
510
|
+
* {@link module:engine/generate.systemCompilers}.
|
|
511
|
+
*
|
|
512
|
+
* @type {string}
|
|
513
|
+
*/
|
|
514
|
+
static documentClass = "Actor";
|
|
515
|
+
|
|
504
516
|
/**
|
|
505
517
|
* The note-type → document-subtype map this pass compiles against.
|
|
506
518
|
*
|
package/engine/base-compiler.mjs
CHANGED
|
@@ -183,13 +183,16 @@ export class BasePackCompiler {
|
|
|
183
183
|
|
|
184
184
|
/**
|
|
185
185
|
* Whether this pass's document **is** a system's data, and therefore takes
|
|
186
|
-
* only notes that carry
|
|
186
|
+
* only notes that carry the block this pass reads.
|
|
187
187
|
*
|
|
188
|
-
*
|
|
189
|
-
*
|
|
190
|
-
*
|
|
191
|
-
* nothing to say about
|
|
192
|
-
* document**: a subtype, and none of the fields the subtype exists
|
|
188
|
+
* The question is asked of {@link BasePackCompiler#system} — the pass's own
|
|
189
|
+
* system, which an Actor or Item pass always has — rather than of the
|
|
190
|
+
* pack's `system:`, which a pack need not declare. A note carrying no such
|
|
191
|
+
* block has nothing to say about that system, so compiling it would emit a
|
|
192
|
+
* **hollow document**: a subtype, and none of the fields the subtype exists
|
|
193
|
+
* for. `harn-ensemble` ships an `actors-hm3` and an `actors-sohl` from one
|
|
194
|
+
* tree, and `sohl-thalorna` ships one `actors` declaring nothing; the rule
|
|
195
|
+
* is the same in both, because it is the pass that reads a block.
|
|
193
196
|
*
|
|
194
197
|
* False by default, because most passes write documents that are not
|
|
195
198
|
* system data at all. A JournalEntry of prose is the same document under
|
|
@@ -428,44 +431,56 @@ export class BasePackCompiler {
|
|
|
428
431
|
);
|
|
429
432
|
}
|
|
430
433
|
|
|
434
|
+
/**
|
|
435
|
+
* The system block this pass reads, where it reads one.
|
|
436
|
+
*
|
|
437
|
+
* `undefined` on the base class, because most passes read none: a
|
|
438
|
+
* JournalEntry, a Macro, a Scene and an Adventure are Foundry's documents
|
|
439
|
+
* rather than any system's. The Actor and Item passes answer with the
|
|
440
|
+
* block their {@link SystemActorCompiler.documentSubtypes} names, which is
|
|
441
|
+
* the same declaration that says which notes they claim and what each
|
|
442
|
+
* becomes.
|
|
443
|
+
*
|
|
444
|
+
* Stated here so {@link BasePackCompiler#eligibleFor} is total: a pass
|
|
445
|
+
* declaring `requiresSystemBlock` without a system reads no block, and a
|
|
446
|
+
* note can carry nothing for it.
|
|
447
|
+
*
|
|
448
|
+
* @returns {string|undefined} The block key, or `undefined`.
|
|
449
|
+
*/
|
|
450
|
+
get system() {
|
|
451
|
+
return undefined;
|
|
452
|
+
}
|
|
453
|
+
|
|
431
454
|
/**
|
|
432
455
|
* Whether a claimed, routed note may become this pack's document at all.
|
|
433
456
|
*
|
|
434
|
-
*
|
|
435
|
-
*
|
|
436
|
-
*
|
|
437
|
-
*
|
|
438
|
-
* documents
|
|
457
|
+
* **A system block is what makes a game document.** A pass whose document
|
|
458
|
+
* *is* a system's data compiles a note only where the note carries the
|
|
459
|
+
* block that pass reads, so a note saying nothing about any system becomes
|
|
460
|
+
* no Actor and no Item anywhere — its prose still compiles into a page, a
|
|
461
|
+
* PDF leaf and a JournalEntry, which are documents no system defines.
|
|
462
|
+
*
|
|
463
|
+
* Asked of this pass's own {@link BasePackCompiler#system}, never of the
|
|
464
|
+
* pack's `system:`. A pack declaring none is a single-system convenience
|
|
465
|
+
* compiled by the fallback pass for its document type, and that pass still
|
|
466
|
+
* reads exactly one block — so the note-side question is the same one
|
|
467
|
+
* whether or not the pack answered it.
|
|
468
|
+
*
|
|
469
|
+
* A note this pass has nothing to compile is **skipped**, counted like any
|
|
470
|
+
* other note the pack does not own. A deliberately unstatted person is the
|
|
471
|
+
* ordinary case, and a block deleted by accident shows in the diff that
|
|
472
|
+
* deleted it; what would otherwise be discarded in silence is the block
|
|
473
|
+
* written for a system no pack of the class serves, and that is reported by
|
|
474
|
+
* {@link module:engine/note-claims.unclaimedNoteFindings} rather than here,
|
|
475
|
+
* where a pass can see only its own half of the configuration.
|
|
439
476
|
*
|
|
440
477
|
* @param {object} fm - The note's frontmatter.
|
|
441
478
|
* @returns {boolean} True when the note may be compiled here; `false` when
|
|
442
|
-
* it
|
|
443
|
-
* skipped as quietly as any other note this pack does not own.
|
|
444
|
-
* @throws {Error} When this pack's system is absent from the note and no
|
|
445
|
-
* other configured system claims it. The error carries a `position` where
|
|
446
|
-
* the note's own file can be read.
|
|
479
|
+
* it carries nothing for this pass's system.
|
|
447
480
|
*/
|
|
448
481
|
eligibleFor(fm) {
|
|
449
|
-
if (!this.constructor.requiresSystemBlock
|
|
450
|
-
|
|
451
|
-
// Another system's pack of this document type will claim it. A
|
|
452
|
-
// note carrying only `hm3:` routes here because this pack is the
|
|
453
|
-
// *default* of its document type, and defaults are declared per type
|
|
454
|
-
// rather than per system — but it is not an incomplete note, it is
|
|
455
|
-
// another pass's. The single-system case is untouched: with no second
|
|
456
|
-
// system declared there is nothing for this to find, and the error
|
|
457
|
-
// below still fires.
|
|
458
|
-
const claimant = (this.router?.systemsOfType?.(this.docType) ?? []).find(
|
|
459
|
-
(system) => system !== this.packSystem && carriesSystemBlock(fm, system),
|
|
460
|
-
);
|
|
461
|
-
if (claimant) return false;
|
|
462
|
-
const label = fm?.name?.full ?? fm?.shortcode ?? fm?.id ?? "this note";
|
|
463
|
-
throw new Error(
|
|
464
|
-
`${label} carries no \`${this.packSystem}:\` block, so it has no ` +
|
|
465
|
-
`${this.packSystem} data to compile — but it routes to pack ` +
|
|
466
|
-
`"${this.packName}", which declares \`system: ${this.packSystem}\`. ` +
|
|
467
|
-
`Add the block, or route the note to a pack of another system.`,
|
|
468
|
-
);
|
|
482
|
+
if (!this.constructor.requiresSystemBlock) return true;
|
|
483
|
+
return carriesSystemBlock(fm, this.system);
|
|
469
484
|
}
|
|
470
485
|
|
|
471
486
|
/**
|
package/engine/content-index.mjs
CHANGED
|
@@ -422,9 +422,10 @@ function assertNoDerivedKeys(frontmatter, relPath, absPath, contentPackage) {
|
|
|
422
422
|
* @returns {Record<string, any>} The record, keys sorted at every depth. A
|
|
423
423
|
* **stub** — a note with an empty body, on a type an empty body suppresses —
|
|
424
424
|
* carries `address` and `anchors` as `null`: it publishes no page, so it
|
|
425
|
-
* holds no address and offers no anchor. It keeps
|
|
426
|
-
*
|
|
427
|
-
* derived from `data
|
|
425
|
+
* holds no address and offers no anchor. It keeps its `id`, and a `foundry`
|
|
426
|
+
* block naming every document that was made — which is every document
|
|
427
|
+
* derived from `data:`, and no journal, because a journal of an empty body
|
|
428
|
+
* would be empty and is not created.
|
|
428
429
|
* @throws {Error} When the note carries a key this module derives, which would
|
|
429
430
|
* otherwise be overwritten without a word. `file` and, where the file was
|
|
430
431
|
* read, `position` ride on the error.
|
|
@@ -443,16 +444,18 @@ export function buildIndexRecord({
|
|
|
443
444
|
const posix = relPath.split(path.sep).join("/");
|
|
444
445
|
const folder = posix.includes("/") ? posix.slice(0, posix.lastIndexOf("/")) : "";
|
|
445
446
|
const address = noteAddress(frontmatter, contentPackage);
|
|
446
|
-
// Derived from the address the note *would* hold, because a stub
|
|
447
|
-
//
|
|
448
|
-
//
|
|
447
|
+
// Derived from the address the note *would* hold, because a stub still
|
|
448
|
+
// compiles every document derived from `data:` — an unwritten arcane talent
|
|
449
|
+
// is its Item. The one document it has none of is the one that would be
|
|
450
|
+
// empty, and `entriesForNote` withholds that UUID, so this block names
|
|
451
|
+
// nothing that was not made.
|
|
449
452
|
const entries = foundryEntries({ frontmatter, address, body, manifest });
|
|
450
453
|
// A stub — an empty body on a type an empty body suppresses — publishes no
|
|
451
454
|
// page, so it holds no address and offers no anchor to link to. **The
|
|
452
455
|
// absent address is the signal**, and it is the column the table renderer
|
|
453
456
|
// already reads through `_ref`. Everything else the note declares is kept:
|
|
454
457
|
// its file is what the author edits and what every diagnostic names, and
|
|
455
|
-
// its `id`
|
|
458
|
+
// its `id` is the identity every diagnostic and every table reads.
|
|
456
459
|
const stub = isStubNote(frontmatter, body);
|
|
457
460
|
|
|
458
461
|
return /** @type {Record<string, any>} */ (
|
|
@@ -530,6 +533,12 @@ export function buildIndexRecord({
|
|
|
530
533
|
* @returns {Record<string, any>} The documentation journal's index record.
|
|
531
534
|
*/
|
|
532
535
|
function buildDocRecord({ frontmatter, address, entry, file, contentPackage, anchors }) {
|
|
536
|
+
// **The entry names a document only where one was made**, and where it does
|
|
537
|
+
// not, nothing on this record may name one either: no id, no address and no
|
|
538
|
+
// anchors. The record itself stays, because the index records what a note
|
|
539
|
+
// produces and `null` is how it says a document was not produced — the same
|
|
540
|
+
// signal a stub's absent address already is.
|
|
541
|
+
const made = Boolean(entry.uuid);
|
|
533
542
|
return /** @type {Record<string, any>} */ (
|
|
534
543
|
sortKeysDeep({
|
|
535
544
|
package: contentPackage,
|
|
@@ -540,13 +549,13 @@ function buildDocRecord({ frontmatter, address, entry, file, contentPackage, anc
|
|
|
540
549
|
// re-derived: every entry the index gives an identity to publishes
|
|
541
550
|
// both the id and the UUID, computed once by whatever owns that
|
|
542
551
|
// entry's derivation.
|
|
543
|
-
id: entry.id,
|
|
552
|
+
id: made ? entry.id : null,
|
|
544
553
|
nameAscii: asciiName(frontmatter?.name?.full),
|
|
545
|
-
address: { slug: address.slug, canonical: entry.key },
|
|
554
|
+
address: made ? { slug: address.slug, canonical: entry.key } : null,
|
|
546
555
|
// The record this is the documentation *for*. `documentation` is
|
|
547
556
|
// the forward link on that record, so either end reaches the other.
|
|
548
557
|
documents: address.canonical,
|
|
549
|
-
anchors,
|
|
558
|
+
anchors: made ? anchors : null,
|
|
550
559
|
foundry: foundryBlock(entry), // a journal: no system key
|
|
551
560
|
file,
|
|
552
561
|
})
|
|
@@ -637,8 +646,8 @@ export function collectContentIndex(
|
|
|
637
646
|
|
|
638
647
|
// An item note is two documents, so it is two records. Derived from
|
|
639
648
|
// the address the note *would* hold rather than from the one its
|
|
640
|
-
// record carries
|
|
641
|
-
//
|
|
649
|
+
// record carries, because a stub withholds its own address; the second
|
|
650
|
+
// record says whether the journal beside the item was made.
|
|
642
651
|
const address = noteAddress(fm, contentPackage);
|
|
643
652
|
const doc = foundryEntries({ frontmatter: fm, address, body, manifest })?.doc;
|
|
644
653
|
if (doc?.key && address) {
|