@heroiclands/package-build 22.12.3 → 22.13.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 +13 -0
- package/README.md +2 -2
- package/docs/api.md +19 -18
- package/docs/commands.md +0 -3
- package/docs/content-format.md +59 -66
- package/docs/getting-started.md +1 -3
- package/engine/address.mjs +39 -153
- package/engine/art-fields.mjs +1 -1
- package/engine/bundles.mjs +3 -2
- package/engine/content-address.mjs +6 -9
- package/engine/content-index.mjs +8 -10
- package/engine/content-links.mjs +9 -17
- package/engine/content-tables.mjs +1 -1
- package/engine/foundry-entries.mjs +4 -8
- package/engine/frontmatter-lint.mjs +2 -2
- package/engine/metadata-index.mjs +23 -6
- package/engine/note-addresses.mjs +27 -9
- package/engine/note-ids.mjs +6 -1
- package/engine/place-relations.mjs +4 -4
- package/engine/reference-targets.mjs +7 -7
- package/engine/shortcode-references.mjs +1 -1
- package/engine/site-index.mjs +8 -39
- package/engine/sql-tables.mjs +1 -1
- package/engine/systems.mjs +4 -1
- package/engine/web-wikilinks.mjs +3 -3
- package/engine/wikilinks.mjs +31 -106
- package/package.json +2 -3
- package/sohl/infobox.mjs +2 -2
- package/types/engine/address.d.mts +21 -88
- package/types/engine/content-address.d.mts +3 -6
- package/types/engine/content-links.d.mts +2 -6
- package/types/engine/note-addresses.d.mts +2 -0
- package/types/engine/systems.d.mts +2 -0
- package/bin/heroiclands-content-language-server.mjs +0 -8
- package/docs/content-language-server.md +0 -25
- package/engine/content-language-server.mjs +0 -439
- package/types/engine/content-language-server.d.mts +0 -64
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,18 @@
|
|
|
1
1
|
# @heroiclands/package-build
|
|
2
2
|
|
|
3
|
+
## 22.13.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
**Content links**
|
|
8
|
+
|
|
9
|
+
- Ordinary wikilinks open readable notes by default; image embeds and art fields resolve systemless assets.
|
|
10
|
+
- Readable notes use `note` addresses, while explicit game systems select Actors and Items and `none` selects systemless targets.
|
|
11
|
+
|
|
12
|
+
**Editor navigation**
|
|
13
|
+
|
|
14
|
+
- Install `@heroiclands/content-language-server` for content note search and navigation in an editor.
|
|
15
|
+
|
|
3
16
|
## 22.12.3
|
|
4
17
|
|
|
5
18
|
### Patch Changes
|
package/README.md
CHANGED
|
@@ -14,8 +14,8 @@ The content half is documented separately in **[CONTENT.md](CONTENT.md)** — th
|
|
|
14
14
|
note format, the pack pipeline, and the configuration contract a content tree
|
|
15
15
|
declares itself with.
|
|
16
16
|
|
|
17
|
-
Editor navigation
|
|
18
|
-
[content
|
|
17
|
+
Editor navigation for content projects is provided by
|
|
18
|
+
[`@heroiclands/content-language-server`](https://github.com/HeroicLands/content-language-server).
|
|
19
19
|
|
|
20
20
|
## Install
|
|
21
21
|
|
package/docs/api.md
CHANGED
|
@@ -596,16 +596,17 @@ The map from each place, drawn for the site: `content-build site` draws the map
|
|
|
596
596
|
|
|
597
597
|
The closed registry of system ids, and the `none` that stands for no system at all. An unknown system value is an error; adding a system is a data change to this registry rather than a hardcoded set scattered through the pipeline.
|
|
598
598
|
|
|
599
|
-
| Export | Signature | Returns | Use it when
|
|
600
|
-
| ---------------------- | -------------------------------------- | ------------------------------- |
|
|
601
|
-
| `NO_SYSTEM` | `const NO_SYSTEM` | — | naming
|
|
602
|
-
| `
|
|
603
|
-
| `
|
|
604
|
-
| `
|
|
605
|
-
| `
|
|
606
|
-
| `
|
|
607
|
-
| `
|
|
608
|
-
| `
|
|
599
|
+
| Export | Signature | Returns | Use it when |
|
|
600
|
+
| ---------------------- | -------------------------------------- | ------------------------------- | -------------------------------------------------------------------------------------- |
|
|
601
|
+
| `NO_SYSTEM` | `const NO_SYSTEM` | — | naming a systemless asset or Foundry document |
|
|
602
|
+
| `NOTE_SYSTEM` | `const NOTE_SYSTEM` | — | naming readable note content across web, book and Foundry |
|
|
603
|
+
| `SYSTEM_IDS` | `const SYSTEM_IDS` | — | enumerating every game system this toolchain compiles for |
|
|
604
|
+
| `SYSTEM_SEGMENTS` | `const SYSTEM_SEGMENTS` | — | enumerating the game systems, `note` and `none` |
|
|
605
|
+
| `isSystemId` | `isSystemId(value)` | `boolean` | checking whether a value names a game system this toolchain knows (`none` is rejected) |
|
|
606
|
+
| `isSystemSegment` | `isSystemSegment(value)` | `boolean` | checking whether a value is something the `<system>` address segment may hold |
|
|
607
|
+
| `unknownSystemMessage` | `unknownSystemMessage(value, where)` | `string` | building the message for a caller that wrote an unknown system |
|
|
608
|
+
| `assertSystemSegment` | `assertSystemSegment(value, where)` | `string` — the value, unchanged | refusing a value the `<system>` segment may not hold, validating inline |
|
|
609
|
+
| `assertSystemCharset` | `assertSystemCharset(segments, where)` | throws | refusing a registry declaration whose id could not be an address segment |
|
|
609
610
|
|
|
610
611
|
### `engine.calendars`
|
|
611
612
|
|
|
@@ -675,7 +676,7 @@ Where a content note publishes on the web. One rule, in one place, because two b
|
|
|
675
676
|
| `addressSlug` | `function addressSlug(fm)` | {string} The address segment, e.g. | The single path segment a note is addressed by: `type-shortcode`. |
|
|
676
677
|
| `packageAddress` | `function packageAddress(fm)` | {string} The package-relative address, with a trailing slash and no leading one. | A note's address: `<type>-<shortcode>/`, e.g. |
|
|
677
678
|
| `canonicalKey` | `function canonicalKey(pkg, system, type, shortcode)` | {string} `package-system-type-shortcode`, lowercased. | The **canonical** address of a note: fully qualified, one spelling per document, and globally unique. |
|
|
678
|
-
| `blockSystem` | `function blockSystem(keyPath)` | {string} The system id, or `
|
|
679
|
+
| `blockSystem` | `function blockSystem(keyPath)` | {string} The game system id, or `note`. | Which system a frontmatter key path is written under. |
|
|
679
680
|
| `expandAddress` | `function expandAddress(read, where)` | {string} The canonical `package-system-type-shortcode`. | Expand a written address to the one canonical address it names. |
|
|
680
681
|
| `CANONICAL_KEY_SEGMENTS` | `const CANONICAL_KEY_SEGMENTS` | — | How many segments a canonical key has, and therefore how many the reader below counts. |
|
|
681
682
|
| `readCanonicalKey` | `function readCanonicalKey(key)` | {{package: string, system: string, type: string, shortcode: string} \|null\|undefined} The parts; `null` when there is a string that is not in canonical form; `undefined` when there is no key at all. | Reads a canonical key back into its parts. |
|
|
@@ -1062,12 +1063,12 @@ Shared helpers for the pack compilers in `packages/content-build/`. The HeroicLa
|
|
|
1062
1063
|
|
|
1063
1064
|
**Item docs** — an item's prose compiled as a JournalEntry, with the item keeping only a pointer to it. An item note's body describes what the thing _is_. That is documentation, and documentation belongs in the journals pack, so each item note compiles into a JournalEntry and the item's `system.docHtml` becomes nothing but a `@UUID` link to that entry's first page — the description-as-pointer convention, which {@link sohl.utils.descriptionLinkTarget} recognises and Display Description follows.
|
|
1064
1065
|
|
|
1065
|
-
| Export | Signature | Returns | Use it when
|
|
1066
|
-
| ---------------- | --------------------------------------------------------------- | ------------------------------------------------------------------------------------------------ |
|
|
1067
|
-
| `docEntryTypes` | `function docEntryTypes()` | {ReadonlySet<string>} The configured doc-carrying types. | Every content type whose **prose compiles into a JournalEntry of its own**, addressed by the
|
|
1068
|
-
| `hasDocEntry` | `function hasDocEntry(type)` | {boolean} True for an item type, for `macro` and for a map type; false for `doc` and for actors. | Whether a content note's type is one whose prose becomes a JournalEntry of its own.
|
|
1069
|
-
| `itemDocEntryId` | `function itemDocEntryId(itemId)` | {string} A 16-character Foundry id. | The id of the JournalEntry a note's prose compiles into — an item's, or a macro's.
|
|
1070
|
-
| `itemDocPointer` | `function itemDocPointer(packageId, itemId, name, firstPageId)` | {string} The pointer to store in `system.docHtml`. | The description an item carries in place of its prose: a `@UUID` link to the first page of its item doc, and nothing else.
|
|
1066
|
+
| Export | Signature | Returns | Use it when |
|
|
1067
|
+
| ---------------- | --------------------------------------------------------------- | ------------------------------------------------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------- |
|
|
1068
|
+
| `docEntryTypes` | `function docEntryTypes()` | {ReadonlySet<string>} The configured doc-carrying types. | Every content type whose **prose compiles into a JournalEntry of its own**, addressed by `note` with the authored type. |
|
|
1069
|
+
| `hasDocEntry` | `function hasDocEntry(type)` | {boolean} True for an item type, for `macro` and for a map type; false for `doc` and for actors. | Whether a content note's type is one whose prose becomes a JournalEntry of its own. |
|
|
1070
|
+
| `itemDocEntryId` | `function itemDocEntryId(itemId)` | {string} A 16-character Foundry id. | The id of the JournalEntry a note's prose compiles into — an item's, or a macro's. |
|
|
1071
|
+
| `itemDocPointer` | `function itemDocPointer(packageId, itemId, name, firstPageId)` | {string} The pointer to store in `system.docHtml`. | The description an item carries in place of its prose: a `@UUID` link to the first page of its item doc, and nothing else. |
|
|
1071
1072
|
|
|
1072
1073
|
- `itemTypes` — re-exported: Every content type that compiles into an item — and therefore into an item doc.
|
|
1073
1074
|
|
|
@@ -1077,7 +1078,7 @@ Wikilink resolution for the pack compilers. Content notes link to one another wi
|
|
|
1077
1078
|
|
|
1078
1079
|
| Export | Signature | Returns | Use it when |
|
|
1079
1080
|
| -------------------- | ----------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
1080
|
-
| `resolveItemDocType` | `function resolveItemDocType(qualifier, types)` | {string\|null} The underlying document type, or `null` when the qualifier is not a virtual one. | Reads
|
|
1081
|
+
| `resolveItemDocType` | `function resolveItemDocType(qualifier, types)` | {string\|null} The underlying document type, or `null` when the qualifier is not a virtual one. | Reads an accepted `doc<type>` input alias as its authored type. |
|
|
1081
1082
|
| `readQualifier` | `function readQualifier(target, types, packages, noIndexPackages)` | {{type: string, shortcode: string, itemDoc: boolean, package?: string, system?: string, reason?: undefined} \| {reason: "unknown-type"\|"no-content-index", package?: string} \| null} The resolved qualifier; a `reason` when the target is definitely qualified but names no known type or no fetched index; or `null` when it is not an address at all. | Read a link target as a **qualified** `type-shortcode` reference, or report that it does not parse as one. |
|
|
1082
1083
|
| `anchorPageId` | `function anchorPageId(noteId, anchorSlug)` | {string} A 16-character alphanumeric id. | The deterministic JournalEntryPage id for one anchor: SHA-256 of `"<noteId>-<anchorSlug>"`, base64-encoded, reduced to the 16 alphanumeric characters a Foundry id allows. |
|
|
1083
1084
|
| `buildWikilinkIndex` | `function buildWikilinkIndex(docs, packageId, foreign, contentPackage)` | {{byShortcode: Map<string, object>, types: Set<string>}} `types` is every type the tree actually contains, so a qualifier naming no real type can be told apart from a missing target. | Builds the link-resolution tables for a content tree. |
|
package/docs/commands.md
CHANGED
|
@@ -6,9 +6,6 @@ container, drive the end-to-end suite. `content-build` is the content half —
|
|
|
6
6
|
compile a note tree into compendium packs, check it, publish it as a site or a
|
|
7
7
|
book, and manage the caches a build resolves other packages through.
|
|
8
8
|
|
|
9
|
-
The package also ships `heroiclands-content-language-server`, a stdio editor
|
|
10
|
-
service described in the [content language server guide](content-language-server.md).
|
|
11
|
-
|
|
12
9
|
Both read `package-build.config.yaml` from the repository root — see
|
|
13
10
|
[Configuration](configuration.md) for every key. Neither reads it for
|
|
14
11
|
`--version` or `--help`: those two answer in a directory with no
|
package/docs/content-format.md
CHANGED
|
@@ -96,7 +96,7 @@ column regardless.
|
|
|
96
96
|
|
|
97
97
|
**Embedded models link to documentation in an infobox.** The model Address
|
|
98
98
|
continues to identify the native Item used for compilation. Its displayed link
|
|
99
|
-
uses that same package, type and Shortcode under `
|
|
99
|
+
uses that same package, type and Shortcode under `note`, which names the
|
|
100
100
|
corresponding documentation journal. A model with no published documentation
|
|
101
101
|
keeps its known name as plain text; the infobox does not substitute an Actor or
|
|
102
102
|
Item link.
|
|
@@ -1268,85 +1268,78 @@ below.
|
|
|
1268
1268
|
<package>-<system>-<note_type>-<shortcode>
|
|
1269
1269
|
```
|
|
1270
1270
|
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
it means.
|
|
1271
|
+
The final segment is the shortcode, preceded by the note type, the system and
|
|
1272
|
+
the package. Each segment has one position. The system says which kind of target
|
|
1273
|
+
is named:
|
|
1275
1274
|
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1275
|
+
| system | target |
|
|
1276
|
+
| ------------- | --------------------------------------------------------------------------- |
|
|
1277
|
+
| `note` | Readable note content: a web page, a book entry, or a Foundry JournalEntry. |
|
|
1278
|
+
| `none` | A systemless target such as an asset, Folder, Macro or Scene. |
|
|
1279
|
+
| `sohl`, `hm3` | A game system's Actor or Item document. |
|
|
1280
|
+
|
|
1281
|
+
A note can publish more than one Address. A `being` note, for example, publishes
|
|
1282
|
+
`thalorna-note-being-bctrncml` for its readable content and
|
|
1283
|
+
`thalorna-sohl-being-bctrncml` for its SoHL Actor. An item note follows the same
|
|
1284
|
+
pattern for its page and Item. The type segment remains the authored note type
|
|
1285
|
+
in each Address.
|
|
1279
1286
|
|
|
1280
1287
|
#### Shorter forms
|
|
1281
1288
|
|
|
1282
|
-
|
|
1283
|
-
12,056 links in the current trees. The rest rely on shorter forms, each dropping
|
|
1284
|
-
segments from the left:
|
|
1289
|
+
Authors may omit segments from the left. The complete Address states all four:
|
|
1285
1290
|
|
|
1286
1291
|
| form | expands to |
|
|
1287
1292
|
| ------------------------------- | -------------------------------------------------------- |
|
|
1288
1293
|
| `package-system-type-shortcode` | itself |
|
|
1289
1294
|
| `system-type-shortcode` | `<this package>-system-type-shortcode` |
|
|
1290
|
-
| `type-shortcode` | `<this package>-<this
|
|
1291
|
-
| `shortcode` | as above, with the type from the field's
|
|
1295
|
+
| `type-shortcode` | `<this package>-<this position's system>-type-shortcode` |
|
|
1296
|
+
| `shortcode` | as above, with the type from the field's declaration |
|
|
1292
1297
|
|
|
1293
|
-
There is no `package-type-shortcode
|
|
1294
|
-
|
|
1295
|
-
`kethira-place-tashal` is three segments, which reads as system `kethira`, and
|
|
1296
|
-
fails.
|
|
1298
|
+
There is no `package-type-shortcode` form. A reference to another package states
|
|
1299
|
+
its system as well as its package.
|
|
1297
1300
|
|
|
1298
1301
|
#### Authored references and generated references
|
|
1299
1302
|
|
|
1300
|
-
Authored notes may use any permitted suffix. Builds leave
|
|
1301
|
-
|
|
1302
|
-
Wikilinks contain
|
|
1303
|
-
`data.parents: [north]
|
|
1304
|
-
`data.parents: [thalorna-none-place-north]`.
|
|
1303
|
+
Authored notes may use any permitted suffix. Builds leave source bytes unchanged.
|
|
1304
|
+
Generated content indexes, SQL records, site frontmatter and generated
|
|
1305
|
+
Wikilinks contain complete Addresses. For example, `data.parents: [north]` in
|
|
1306
|
+
`thalorna` emits `data.parents: [thalorna-note-place-north]`.
|
|
1305
1307
|
|
|
1306
|
-
SQL queries compare Address-valued fields to
|
|
1307
|
-
is a distinct identity component, so it is not equal to an Address
|
|
1308
|
-
`data.parents`, `data.domains` or a governance rank's `lore`. Use the
|
|
1309
|
-
complete identity for that comparison
|
|
1310
|
-
|
|
1311
|
-
components and its Shortcode.
|
|
1308
|
+
SQL queries compare Address-valued fields to complete Addresses. A note's
|
|
1309
|
+
`shortcode` is a distinct identity component, so it is not equal to an Address
|
|
1310
|
+
stored in `data.parents`, `data.domains` or a governance rank's `lore`. Use the
|
|
1311
|
+
target's complete identity for that comparison. A stub has no published page
|
|
1312
|
+
address; its note identity still contains the package, system, type and shortcode.
|
|
1312
1313
|
|
|
1313
|
-
The toolchain holds parsed
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1314
|
+
The toolchain holds parsed Addresses as complete tuples internally. Address-keyed
|
|
1315
|
+
relation maps hold typed targets and standing values. Only generated output
|
|
1316
|
+
serializes these properties to Address strings. Runtime Shortcodes, Foundry
|
|
1317
|
+
UUIDs, file paths and URLs keep their own representations.
|
|
1317
1318
|
|
|
1318
1319
|
An Address-valued `data:` field must name a target in this package or a declared
|
|
1319
|
-
dependency index. `content-build lint` reports a missing target
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1320
|
+
dependency index. `content-build lint` reports a missing target at the value or
|
|
1321
|
+
map key that names it. A stub counts as a target for data even though it
|
|
1322
|
+
publishes no page. Art slots use their own missing-art warning and document
|
|
1323
|
+
fallback.
|
|
1323
1324
|
|
|
1324
1325
|
#### An omitted segment defaults from where the link is written
|
|
1325
1326
|
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
- **
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
`
|
|
1342
|
-
so a prose `[[affiliation-sirvadar|Sirvadar]]` names the page, which is what
|
|
1343
|
-
prose almost always means. A prose link that means the **Item** states the
|
|
1344
|
-
system: `[[sohl-affiliation-sirvadar|…]]`.
|
|
1345
|
-
|
|
1346
|
-
Only a type whose own document carries a system is redirected this way. A
|
|
1347
|
-
`macro` and the map types have documentation journals too, but their own
|
|
1348
|
-
documents are core ones already at `none`, so `macro-autoattack` names the Macro
|
|
1349
|
-
and `docmacro-autoattack` its journal — two live addresses.
|
|
1327
|
+
An omitted segment is a default, not a wildcard. Every short form expands to one
|
|
1328
|
+
complete Address before lookup.
|
|
1329
|
+
|
|
1330
|
+
- **Package:** the current package. A short address never falls through to a
|
|
1331
|
+
dependency.
|
|
1332
|
+
- **System in `[[link|text]]`:** `note`, for every note type. Use an explicit
|
|
1333
|
+
game system to name its Actor or Item, or `none` to name a systemless target.
|
|
1334
|
+
- **System in `![[link|text]]`:** `none`. An embed names an image or icon asset;
|
|
1335
|
+
it also supplies `image` as the type of a bare shortcode.
|
|
1336
|
+
- **System in frontmatter:** a system block such as `sohl:` or `hm3:` supplies
|
|
1337
|
+
its game system. Other Address fields default to `note`, except fields
|
|
1338
|
+
declared for images, icons or folders, which default to `none`.
|
|
1339
|
+
|
|
1340
|
+
For example, `[[macro-autoattack|Automated Attack]]` names the readable macro
|
|
1341
|
+
note, while `[[none-macro-autoattack|Automated Attack]]` names the Foundry Macro.
|
|
1342
|
+
`![[icon-anvil|Anvil]]` names the systemless icon asset.
|
|
1350
1343
|
|
|
1351
1344
|
**A complete Address is lowercase throughout.** Every segment — package,
|
|
1352
1345
|
system, type and shortcode alike — contains only lowercase ASCII letters and
|
|
@@ -1572,7 +1565,7 @@ A **`Wikilink`** is how body prose names something:
|
|
|
1572
1565
|
The brackets, the `#<anchor>` and the `|<text>` label are the Wikilink's own.
|
|
1573
1566
|
What sits inside them is an `Address` like any other — read by the same grammar,
|
|
1574
1567
|
written at any of its lengths, and taking each omitted segment from where the
|
|
1575
|
-
link is written, which for body prose is this package and system `
|
|
1568
|
+
link is written, which for body prose is this package and system `note`. So
|
|
1576
1569
|
nothing about resolution belongs to the bracketed form: a wikilink and a
|
|
1577
1570
|
frontmatter field ask the same question of the same value, and the findings above
|
|
1578
1571
|
are the answers either of them gets.
|
|
@@ -2054,10 +2047,10 @@ system a note might write. A package shipping for a second system declares
|
|
|
2054
2047
|
`system:` on a pack of each class it compiles.
|
|
2055
2048
|
|
|
2056
2049
|
That includes actors. A being producing its Actor and nothing else would be
|
|
2057
|
-
|
|
2050
|
+
a system-bearing note with readable content, giving a prose link
|
|
2058
2051
|
naming it nowhere to land. It carries a documentation journal like every other
|
|
2059
2052
|
such note, addressed
|
|
2060
|
-
`<package>-
|
|
2053
|
+
`<package>-note-being-<shortcode>` beside the Actor's
|
|
2061
2054
|
`<package>-<system>-being-<shortcode>`.
|
|
2062
2055
|
|
|
2063
2056
|
**A being keeps its prose inline as well.** `system.appearance` and
|
|
@@ -4065,7 +4058,7 @@ await CONFIG.SOHL.class.Utility.currentCombatantAttack();
|
|
|
4065
4058
|
|
|
4066
4059
|
Three rules follow, and each is deliberate. The anchor names the **page**, not
|
|
4067
4060
|
the heading text, so the heading may be worded freely and
|
|
4068
|
-
`[[
|
|
4061
|
+
`[[macro-autoattack#script]]` still opens exactly this page. The fence must
|
|
4069
4062
|
be **tagged** — an untagged fence is a code sample whose language nobody stated,
|
|
4070
4063
|
and treating it as executable would make an illustrative snippet the macro. And
|
|
4071
4064
|
only the **first** tagged fence counts, so a note may document its macro
|
|
@@ -4141,8 +4134,8 @@ persists in the pack.
|
|
|
4141
4134
|
**Each address names the note's own document.** That is the same rule `pack:`
|
|
4142
4135
|
follows, so there is one answer and not two. A note that compiles into _two_
|
|
4143
4136
|
documents — an item and the JournalEntry its prose became — puts the second in a
|
|
4144
|
-
bundle
|
|
4145
|
-
`miscgear-bowlcer` is the
|
|
4137
|
+
bundle when the bundle names its `note` Address:
|
|
4138
|
+
`sohl-miscgear-bowlcer` is the Item, `miscgear-bowlcer` its description page.
|
|
4146
4139
|
|
|
4147
4140
|
**An address that resolves to nothing fails the build.** A `folder` address is
|
|
4148
4141
|
refused with a message of its own: a folder materialises in every pack holding
|
package/docs/getting-started.md
CHANGED
|
@@ -133,9 +133,7 @@ npm install --save-dev @heroiclands/package-build
|
|
|
133
133
|
```
|
|
134
134
|
|
|
135
135
|
That installs `package-build`, the packaging half, and `content-build`, the
|
|
136
|
-
content half, into `node_modules/.bin`.
|
|
137
|
-
`heroiclands-content-language-server` for editor navigation. Every build command
|
|
138
|
-
below uses one of the first two binaries. Run them through `npx`, or wire them into npm scripts as
|
|
136
|
+
content half, into `node_modules/.bin`. Run them through `npx`, or wire them into npm scripts as
|
|
139
137
|
[`project-setup.md`](project-setup.md) describes.
|
|
140
138
|
|
|
141
139
|
**How it worked:** `npx package-build --version` prints the installed version.
|
package/engine/address.mjs
CHANGED
|
@@ -79,41 +79,25 @@
|
|
|
79
79
|
* to guess which sense was meant. The cost is that `<package>-<type>-<shortcode>`
|
|
80
80
|
* is **not** a written form, so a link into another package states its system.
|
|
81
81
|
*
|
|
82
|
-
* ##
|
|
83
|
-
*
|
|
84
|
-
*
|
|
85
|
-
*
|
|
86
|
-
*
|
|
87
|
-
*
|
|
88
|
-
* Everything else takes the written system, then the position's default.
|
|
89
|
-
* - **`type` may not survive unchanged.** A system-bearing type reached at
|
|
90
|
-
* `none` is redirected to `doc<type>`, because a note's `none` address *is*
|
|
91
|
-
* its documentation journal — the Item is the one with a system.
|
|
92
|
-
* - **Acceptability accepts a type's documentation form.** A position that
|
|
93
|
-
* accepts `weapongear` accepts `docweapongear`, so a target addressed by its
|
|
94
|
-
* documentation journal is acceptable wherever the note itself would be.
|
|
82
|
+
* ## Defaults belong to the citing position
|
|
83
|
+
*
|
|
84
|
+
* Ordinary wikilinks default to `note`, embeds default to `none`, and typed
|
|
85
|
+
* frontmatter fields supply their declared system. The authored type stays
|
|
86
|
+
* the type in the complete tuple. A `doc<type>` qualifier is accepted as an
|
|
87
|
+
* input spelling for a readable note and resolves to `note-<type>`.
|
|
95
88
|
*
|
|
96
89
|
* ## The tuple keeps no memory of how it was written
|
|
97
90
|
*
|
|
98
91
|
* An abbreviation is a *rendering*, not a different value: `being-kaldor` in a
|
|
99
|
-
* `thalorna` note and `thalorna-
|
|
100
|
-
* they parse to the same four fields.
|
|
101
|
-
*
|
|
102
|
-
* journal. Nothing here records what the author typed — a tuple that did would
|
|
103
|
-
* make identity depend on a spelling, and two notes naming one document would
|
|
104
|
-
* compare unequal.
|
|
92
|
+
* `thalorna` note and `thalorna-note-being-kaldor` are the same Address, and
|
|
93
|
+
* they parse to the same four fields. Nothing here records what the author
|
|
94
|
+
* typed — a tuple that did would make identity depend on a spelling.
|
|
105
95
|
*
|
|
106
96
|
* The converse matters just as much: **one written string is two Addresses at
|
|
107
97
|
* two positions**, because the positions supply different defaults.
|
|
108
98
|
* `skill-wpnc` under `sohl.items` defaults its system from the block and names
|
|
109
|
-
* the
|
|
110
|
-
*
|
|
111
|
-
*
|
|
112
|
-
* That is why {@link acceptsType} widens the accepted set rather than reading a
|
|
113
|
-
* remembered pre-redirect type off the tuple. The redirect is not reversible
|
|
114
|
-
* from the four segments alone: `docmacro-autoattack` addresses a `macro`'s
|
|
115
|
-
* journal while `macro-autoattack` addresses the Macro, so `doc`-stripping is a
|
|
116
|
-
* question about the type vocabulary, not about the tuple.
|
|
99
|
+
* the Item; the same string in body prose defaults to `note` and names the
|
|
100
|
+
* readable page. Only the resolved tuple can tell them apart.
|
|
117
101
|
*
|
|
118
102
|
* ## What this module does not do
|
|
119
103
|
*
|
|
@@ -132,17 +116,15 @@
|
|
|
132
116
|
// `engine/systems.mjs` imports nothing but `engine/address-charset.mjs`, so the
|
|
133
117
|
// direction is toward the leaf and cannot close a cycle.
|
|
134
118
|
import { isAddressSegment } from "./address-charset.mjs";
|
|
135
|
-
import { NO_SYSTEM, assertSystemSegment, isSystemSegment } from "./systems.mjs";
|
|
136
|
-
//
|
|
137
|
-
// whether a type's `<system>` segment is fixed. `engine/asset-types.mjs` is a
|
|
138
|
-
// leaf, so the direction cannot close a cycle.
|
|
139
|
-
import { isAssetType } from "./asset-types.mjs";
|
|
119
|
+
import { NO_SYSTEM, NOTE_SYSTEM, assertSystemSegment, isSystemSegment } from "./systems.mjs";
|
|
120
|
+
// Document subtype maps identify the system of a note's own Actor or Item.
|
|
140
121
|
import { systemOf } from "./document-subtypes.mjs";
|
|
141
122
|
import { KNOWN_DOCUMENT_SUBTYPE_MAPS } from "./subtype-registry.mjs";
|
|
142
123
|
// `ids.mjs` is a leaf with no local imports, and `pack-config.mjs` reaches only
|
|
143
124
|
// the configuration loader and the diagnostics writer — neither reads an
|
|
144
125
|
// address, so the grammar sits below everything that consumes it.
|
|
145
|
-
import { currentType, ITEM_PACK, packForType } from "./ids.mjs";
|
|
126
|
+
import { currentType, ITEM_PACK, JOURNAL_TYPES, packForType } from "./ids.mjs";
|
|
127
|
+
import { HOMEPAGE_TYPE } from "./homepage.mjs";
|
|
146
128
|
import { loadPackConfig } from "./pack-config.mjs";
|
|
147
129
|
|
|
148
130
|
export { NO_SYSTEM };
|
|
@@ -166,32 +148,18 @@ export const ADDRESS_SEPARATOR = "-";
|
|
|
166
148
|
*/
|
|
167
149
|
export const CANONICAL_KEY_SEGMENTS = 4;
|
|
168
150
|
|
|
169
|
-
/**
|
|
170
|
-
* The qualifier prefix that addresses a note's **documentation** rather than
|
|
171
|
-
* the note's own document: `docskill-wpnc` is the JournalEntry that
|
|
172
|
-
* `skill-wpnc`'s prose compiled into.
|
|
173
|
-
*
|
|
174
|
-
* Every doc-carrying type has a `doc<type>` counterpart, formed by prefix and
|
|
175
|
-
* never enumerated, so the prefix itself is the whole of that vocabulary.
|
|
176
|
-
*
|
|
177
|
-
* @type {string}
|
|
178
|
-
*/
|
|
151
|
+
/** The accepted `doc<type>` input prefix for readable note links. */
|
|
179
152
|
export const ITEM_DOC_PREFIX = "doc";
|
|
180
153
|
|
|
181
154
|
/** Package, system and type are one spelling each, so they fold to lowercase. */
|
|
182
155
|
const norm = (s) => String(s).toLowerCase().trim();
|
|
183
156
|
|
|
184
157
|
/**
|
|
185
|
-
* A complete Address: four segments,
|
|
186
|
-
*
|
|
187
|
-
* `type` is the segment the canonical string carries, after the `doc<type>`
|
|
188
|
-
* redirect. There is no fifth field recording the written form, because the
|
|
189
|
-
* written form is a rendering rather than part of the value.
|
|
158
|
+
* A complete Address: four segments, with no memory of the authored form.
|
|
190
159
|
*
|
|
191
160
|
* @typedef {object} AddressTuple
|
|
192
161
|
* @property {string} package - The owning **content** package.
|
|
193
|
-
* @property {string} system -
|
|
194
|
-
* `none`.
|
|
162
|
+
* @property {string} system - `note`, `none`, or a game system id.
|
|
195
163
|
* @property {string} type - The type segment of the canonical Address.
|
|
196
164
|
* @property {string} shortcode - The note's `shortcode`.
|
|
197
165
|
*/
|
|
@@ -218,29 +186,8 @@ const norm = (s) => String(s).toLowerCase().trim();
|
|
|
218
186
|
*/
|
|
219
187
|
|
|
220
188
|
/**
|
|
221
|
-
*
|
|
222
|
-
*
|
|
223
|
-
*
|
|
224
|
-
* Every item type, every actor type, every map type, plus `macro` — a macro
|
|
225
|
-
* note's body documents the script the note also compiles into a Macro, which
|
|
226
|
-
* is the same shape as an item and its description: one note, two documents,
|
|
227
|
-
* the prose living in the journals pack.
|
|
228
|
-
*
|
|
229
|
-
* **One set, read by the compilers and by the grammar alike.** The journals
|
|
230
|
-
* pass decides what to compile from it, the link manifest decides what to
|
|
231
|
-
* publish a `doc<type>` entry for, and the parser decides whether `doc<type>`
|
|
232
|
-
* is a legal type segment. Held apart, the three drift into a manifest that
|
|
233
|
-
* asserts documentation nothing compiled, a compiled entry no consumer can
|
|
234
|
-
* address, or an authored `doc<type>` link that stops parsing. It is composed
|
|
235
|
-
* exactly once, in `defineConfig`, and read from there — never recomposed at a
|
|
236
|
-
* call site.
|
|
237
|
-
*
|
|
238
|
-
* `doc` notes are absent, for the reason that applies to them alone: a `doc`
|
|
239
|
-
* note's single document *is* its prose, so there is no second document for a
|
|
240
|
-
* `doc<type>` address to name.
|
|
241
|
-
*
|
|
242
|
-
* An accessor rather than a hoisted constant, so that importing this module
|
|
243
|
-
* needs no configuration.
|
|
189
|
+
* The configured types whose prose compiles into a separate JournalEntry.
|
|
190
|
+
* The journal has a `note` Address with the authored type segment.
|
|
244
191
|
*
|
|
245
192
|
* @returns {ReadonlySet<string>} The configured doc-carrying types.
|
|
246
193
|
*/
|
|
@@ -265,50 +212,20 @@ export function hasDocEntry(type) {
|
|
|
265
212
|
return docEntryTypes().has(String(currentType(type)));
|
|
266
213
|
}
|
|
267
214
|
|
|
268
|
-
/**
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
* the map types — whose documents Foundry itself defines and which therefore
|
|
274
|
-
* already live at `none`.
|
|
275
|
-
*
|
|
276
|
-
* It is what the `doc<type>` redirect tests rather than {@link hasDocEntry}: a
|
|
277
|
-
* `macro` has a documentation journal *and* a `none` address of its own, so
|
|
278
|
-
* redirecting on "has a doc entry" would collapse two live addresses into one
|
|
279
|
-
* and a `[[macro-autoattack|]]` would stop naming the Macro.
|
|
280
|
-
*
|
|
281
|
-
* @param {string} type - The note type.
|
|
282
|
-
* @returns {boolean} True when the type compiles into a system document.
|
|
283
|
-
*/
|
|
284
|
-
function isSystemBearing(type) {
|
|
285
|
-
return systemOf(type, KNOWN_DOCUMENT_SUBTYPE_MAPS) !== NO_SYSTEM;
|
|
215
|
+
/** The segment of a note's own document. */
|
|
216
|
+
export function ownDocumentSystem(type) {
|
|
217
|
+
return JOURNAL_TYPES.has(String(type)) || type === HOMEPAGE_TYPE ?
|
|
218
|
+
NOTE_SYSTEM
|
|
219
|
+
: systemOf(type, KNOWN_DOCUMENT_SUBTYPE_MAPS);
|
|
286
220
|
}
|
|
287
221
|
|
|
288
222
|
/**
|
|
289
|
-
*
|
|
290
|
-
*
|
|
291
|
-
*
|
|
292
|
-
*
|
|
293
|
-
*
|
|
294
|
-
*
|
|
295
|
-
* for a macro.
|
|
296
|
-
*
|
|
297
|
-
* The virtual form exists for a type that carries separate documentation
|
|
298
|
-
* ({@link docEntryTypes} — the set the journals compiler and the link manifest
|
|
299
|
-
* read too), **or** for one that routes to the items pack. The second clause
|
|
300
|
-
* covers the open, unenumerated set: types that compile into items are not
|
|
301
|
-
* enumerated anywhere, and a foreign package may publish an item type this
|
|
302
|
-
* build has never heard of. Dropping it would silently unlink every `doc<type>`
|
|
303
|
-
* address into such a package.
|
|
304
|
-
*
|
|
305
|
-
* A **real** type of the same name always wins: the virtual reading is only
|
|
306
|
-
* consulted for a qualifier no authored note claims.
|
|
307
|
-
*
|
|
308
|
-
* @param {string} qualifier - The already-normalised type segment.
|
|
309
|
-
* @param {Set<string>} types - Every type the content tree contains.
|
|
310
|
-
* @returns {string|null} The underlying document type, or `null` when the
|
|
311
|
-
* qualifier is not a virtual one.
|
|
223
|
+
* Read a `doc<type>` input qualifier as the underlying authored type.
|
|
224
|
+
* A real type with that spelling takes precedence.
|
|
225
|
+
*
|
|
226
|
+
* @param {string} qualifier - The type segment from the input.
|
|
227
|
+
* @param {Set<string>} types - Types in the content tree.
|
|
228
|
+
* @returns {string|null} The underlying type, or `null`.
|
|
312
229
|
*/
|
|
313
230
|
export function resolveItemDocType(qualifier, types) {
|
|
314
231
|
if (types.has(qualifier)) return null; // a real type owns its own name
|
|
@@ -445,21 +362,11 @@ export function renderAddress(tuple) {
|
|
|
445
362
|
}
|
|
446
363
|
|
|
447
364
|
/**
|
|
448
|
-
* Whether the type an Address names is
|
|
449
|
-
*
|
|
450
|
-
* The second of the two questions, and separate from the first because the
|
|
451
|
-
* accepted **set** is a property of the position while the grammar is not.
|
|
452
|
-
*
|
|
453
|
-
* **An accepted type carries its documentation form with it.** A position
|
|
454
|
-
* accepting `weapongear` accepts `docweapongear`, because the journal that
|
|
455
|
-
* documents a weapon is the same thing to a position that wanted a weapon — and
|
|
456
|
-
* a prose address of one *is* the journal, so refusing it would refuse the form
|
|
457
|
-
* an author most often writes. It is the widening `defineConfig` applies to the
|
|
458
|
-
* types a package may claim, applied here to the types a position may take.
|
|
365
|
+
* Whether the canonical type an Address names is accepted at this position.
|
|
459
366
|
*
|
|
460
|
-
* @param {AddressTuple} tuple - A
|
|
461
|
-
* @param {Iterable<string>} allowed -
|
|
462
|
-
* @returns {boolean}
|
|
367
|
+
* @param {AddressTuple} tuple - A complete Address.
|
|
368
|
+
* @param {Iterable<string>} allowed - Accepted types.
|
|
369
|
+
* @returns {boolean} Whether the type is accepted.
|
|
463
370
|
*/
|
|
464
371
|
export function acceptsType(tuple, allowed) {
|
|
465
372
|
const type = norm(tuple?.type ?? "");
|
|
@@ -467,7 +374,7 @@ export function acceptsType(tuple, allowed) {
|
|
|
467
374
|
for (const one of allowed ?? []) {
|
|
468
375
|
const want = norm(one);
|
|
469
376
|
if (!want) continue;
|
|
470
|
-
if (type === want
|
|
377
|
+
if (type === want) return true;
|
|
471
378
|
}
|
|
472
379
|
return false;
|
|
473
380
|
}
|
|
@@ -475,9 +382,7 @@ export function acceptsType(tuple, allowed) {
|
|
|
475
382
|
/**
|
|
476
383
|
* Apply the defaults to a partial reading, giving a complete Address.
|
|
477
384
|
*
|
|
478
|
-
*
|
|
479
|
-
* other: the package, then the system the type may fix, then the redirect the
|
|
480
|
-
* system may force.
|
|
385
|
+
* Complete a parsed Address using the citing position's package and system.
|
|
481
386
|
*
|
|
482
387
|
* @param {{type: string, shortcode: string, package?: string, system?: string,
|
|
483
388
|
* itemDoc?: boolean}} read - A partial reading, as {@link readQualifier}
|
|
@@ -490,27 +395,8 @@ export function completeAddress(read, where = {}) {
|
|
|
490
395
|
if (isAddressTuple(read)) return read;
|
|
491
396
|
const written = read.type;
|
|
492
397
|
const pkg = read.package ?? where.package;
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
// to no game system, so the segment is a property of the type rather than of
|
|
496
|
-
// where the reference was written. Without that, a `sohl:` block naming
|
|
497
|
-
// `icon-anvil` would expand to `sohl-sohl-icon-anvil` and resolve to
|
|
498
|
-
// nothing, which is exactly where an embedded item's art is written.
|
|
499
|
-
// Otherwise the block's system, which body prose reports as `none`.
|
|
500
|
-
const system =
|
|
501
|
-
read.itemDoc || isAssetType(written) ?
|
|
502
|
-
NO_SYSTEM
|
|
503
|
-
: (read.system ?? where.system ?? NO_SYSTEM);
|
|
504
|
-
// **Under `none`, a system-bearing type addresses its documentation
|
|
505
|
-
// journal.** A note's `none` address *is* its `doc<type>` entry — the Item
|
|
506
|
-
// is the one with a system — so a prose `[[affiliation-sirvadar|…]]` names
|
|
507
|
-
// the page, which is almost always what prose means. A value that means the
|
|
508
|
-
// Item states the system and gets it. A `doc<type>` written explicitly is
|
|
509
|
-
// `none` wherever it appears, even inside a system block: no game system
|
|
510
|
-
// defines a JournalEntry.
|
|
511
|
-
const redirected = system === NO_SYSTEM && isSystemBearing(written);
|
|
512
|
-
const type = read.itemDoc || redirected ? `${ITEM_DOC_PREFIX}${written}` : written;
|
|
513
|
-
return addressTuple({ package: pkg, system, type, shortcode: read.shortcode });
|
|
398
|
+
const system = read.itemDoc ? NOTE_SYSTEM : (read.system ?? where.system ?? NOTE_SYSTEM);
|
|
399
|
+
return addressTuple({ package: pkg, system, type: written, shortcode: read.shortcode });
|
|
514
400
|
}
|
|
515
401
|
|
|
516
402
|
/**
|