@heroiclands/package-build 22.13.0 → 22.13.1
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 +6 -0
- package/docs/api.md +23 -22
- package/docs/content-format.md +35 -18
- package/engine/folder-notes.mjs +2 -1
- package/engine/frontmatter.mjs +2 -2
- package/engine/homepage.mjs +3 -3
- package/engine/item-compiler.mjs +7 -2
- package/engine/map-places.mjs +3 -1
- package/engine/note-ids.mjs +1 -1
- package/engine/note-vocabulary.mjs +24 -0
- package/engine/pack-router.mjs +7 -3
- package/engine/system-block.mjs +19 -0
- package/hm3/actors.mjs +7 -2
- package/package.json +1 -1
- package/types/engine/frontmatter.d.mts +1 -1
- package/types/engine/homepage.d.mts +2 -2
- package/types/engine/system-block.d.mts +11 -0
package/CHANGELOG.md
CHANGED
package/docs/api.md
CHANGED
|
@@ -119,28 +119,29 @@ Deterministic document ids, derived by hashing rather than stored, so compile pa
|
|
|
119
119
|
|
|
120
120
|
The per-system frontmatter block: how one note feeds more than one game system through properties named after that system (`<system>.system`, `<system>.type`, `<system>.img`, `<system>.items` on actors). Resolves a field's value through the block, the shared top level, and a retiring position in that order, and merges an authored `<system>.system` onto a compiler-built one without disturbing what the builder already wrote.
|
|
121
121
|
|
|
122
|
-
| Export | Signature
|
|
123
|
-
| --------------------------- |
|
|
124
|
-
| `SYSTEM_DATA_KEY` | `const SYSTEM_DATA_KEY`
|
|
125
|
-
| `BLOCK_DOCUMENT_PROPERTIES` | `const BLOCK_DOCUMENT_PROPERTIES`
|
|
126
|
-
| `BLOCK_DIRECTIVES` | `const BLOCK_DIRECTIVES`
|
|
127
|
-
| `SYSTEM_BLOCK_KEYS` | `const SYSTEM_BLOCK_KEYS`
|
|
128
|
-
| `setPath` | `setPath(target, dotted, value)`
|
|
129
|
-
| `systemBlock` | `systemBlock(fm, block)`
|
|
130
|
-
| `carriesSystemBlock` | `carriesSystemBlock(fm, block)`
|
|
131
|
-
| `systemData` | `systemData(fm, block)`
|
|
132
|
-
| `blockField` | `blockField(fm, block, key, defaultValue)`
|
|
133
|
-
| `sharedProperty` | `sharedProperty(fm, source, defaultValue)`
|
|
134
|
-
| `blockProperty` | `blockProperty(fm, block, key, defaultValue)`
|
|
135
|
-
| `
|
|
136
|
-
| `
|
|
137
|
-
| `
|
|
138
|
-
| `
|
|
139
|
-
| `
|
|
140
|
-
| `
|
|
141
|
-
| `
|
|
142
|
-
| `
|
|
143
|
-
| `
|
|
122
|
+
| Export | Signature | Returns | Use it when |
|
|
123
|
+
| --------------------------- | ------------------------------------------------- | -------------------------------------- | --------------------------------------------------------------------------------------------- |
|
|
124
|
+
| `SYSTEM_DATA_KEY` | `const SYSTEM_DATA_KEY` | — | naming the key inside a system block whose contents are the system's own vocabulary |
|
|
125
|
+
| `BLOCK_DOCUMENT_PROPERTIES` | `const BLOCK_DOCUMENT_PROPERTIES` | — | mapping a system-block key to the document property it becomes |
|
|
126
|
+
| `BLOCK_DIRECTIVES` | `const BLOCK_DIRECTIVES` | — | recognizing a build directive (e.g. `pack`) that names no document property |
|
|
127
|
+
| `SYSTEM_BLOCK_KEYS` | `const SYSTEM_BLOCK_KEYS` | — | enumerating every key any system block may carry |
|
|
128
|
+
| `setPath` | `setPath(target, dotted, value)` | `object` — `target`, for chaining | writing a value at a dotted path in a document's `system` block |
|
|
129
|
+
| `systemBlock` | `systemBlock(fm, block)` | `Record<string, unknown> \| undefined` | reading one system's whole block off a note, or nothing when it is absent or malformed |
|
|
130
|
+
| `carriesSystemBlock` | `carriesSystemBlock(fm, block)` | `boolean` | deciding whether a pack that declares a system should compile a note at all |
|
|
131
|
+
| `systemData` | `systemData(fm, block)` | `Record<string, unknown>` | reading a system block's `system` sub-block, `{}` when absent |
|
|
132
|
+
| `blockField` | `blockField(fm, block, key, defaultValue)` | the value | reading a key from one system's block, falling back to the top level |
|
|
133
|
+
| `sharedProperty` | `sharedProperty(fm, source, defaultValue)` | the value | reading a shared top-level property by a possibly-dotted path, blind to every system block |
|
|
134
|
+
| `blockProperty` | `blockProperty(fm, block, key, defaultValue)` | the value | reading a property a system block may override, else the shared top-level one |
|
|
135
|
+
| `blockDataProperty` | `blockDataProperty(fm, block, key, defaultValue)` | the value | reading a system override, then shared `data`, then a top-level value |
|
|
136
|
+
| `legacyKeyOf` | `legacyKeyOf(field)` | `string \| undefined` | finding the key a field is authored at inside a system block |
|
|
137
|
+
| `retiredTopLevelKey` | `retiredTopLevelKey(field)` | `string \| undefined` | finding the bare top-level key a `data:`-sourced field is being swept off |
|
|
138
|
+
| `authoredKey` | `authoredKey(name)` | `string` | reading a shared source as the key an author writes, without the `data:` container holding it |
|
|
139
|
+
| `resolveFieldValue` | `resolveFieldValue(field, fm, ...)` | `{value, from}` | resolving one declared field against a note, in the declared source order |
|
|
140
|
+
| `systemDataPaths` | `systemDataPaths(data, prefix)` | `string[]` | listing every path a note authors under `<system>.system`, containers included |
|
|
141
|
+
| `undeclaredPaths` | `undeclaredPaths(data, declared, prefix)` | `string[]` | finding authored paths a system's published schema does not declare |
|
|
142
|
+
| `unknownBlockKeys` | `unknownBlockKeys(fm, block, ...)` | `string[]` | finding keys directly under a system block that neither this format nor the system recognizes |
|
|
143
|
+
| `claimedPaths` | `claimedPaths(fields)` | `Set<string>` | listing the exact `system` paths a field declaration writes, for `mergeSystemData` |
|
|
144
|
+
| `mergeSystemData` | `mergeSystemData(built, fm, ...)` | `object` — `built`, for chaining | merging a note's `<system>.system` verbatim onto a built `system` block |
|
|
144
145
|
|
|
145
146
|
### `engine.codeFences`
|
|
146
147
|
|
package/docs/content-format.md
CHANGED
|
@@ -308,6 +308,12 @@ finding that names the key you meant; the same misspelling at top level would
|
|
|
308
308
|
silently become a theme parameter, which is exactly why these cannot live
|
|
309
309
|
together.
|
|
310
310
|
|
|
311
|
+
Every note type may put a pinned document id in `data.id`, a shared compendium
|
|
312
|
+
route in `data.pack`, a shared folder Address in `data.packFolder`, and source
|
|
313
|
+
details in `data.harnworld`. A system block's `pack` or `packFolder` overrides
|
|
314
|
+
the shared value for that system. A `being` may also describe its social profile
|
|
315
|
+
in `data.social`.
|
|
316
|
+
|
|
311
317
|
**Tags are open, except the ones that classify.** `tags:` shares the top level's
|
|
312
318
|
openness: a tag naming a theme or a region — `underworld`,
|
|
313
319
|
`byzaria`, `riverlands` — is the author's own and this build has no opinion about it.
|
|
@@ -569,8 +575,8 @@ type.
|
|
|
569
575
|
| ----------------------- | ------------------------- | ---------------------------- |
|
|
570
576
|
| `name.full` | `name` | `name` |
|
|
571
577
|
| `data.icon` | `img` | `img` |
|
|
572
|
-
| `id`
|
|
573
|
-
| `packFolder`
|
|
578
|
+
| `data.id` | `_id` | `_id` |
|
|
579
|
+
| `data.packFolder` | `folder` | `folder` |
|
|
574
580
|
| `shortcode` | `system.shortcode` | NA |
|
|
575
581
|
| `data.templatePriority` | `system.templatePriority` | `flags.hm3.templatePriority` |
|
|
576
582
|
| `actionDefs` | `system.actionDefs` | NA |
|
|
@@ -657,16 +663,20 @@ refused at all four slots: a sound is not art, and a value naming one — `icon:
|
|
|
657
663
|
audio-thunder`, say — is an error naming the set the slot accepts, never a
|
|
658
664
|
silent fall back to the note's default art.
|
|
659
665
|
|
|
660
|
-
**
|
|
666
|
+
**These properties are legal on _every_ note**, whatever its type. `data:` is a
|
|
661
667
|
closed container and the per-type vocabularies below are the only lists there
|
|
662
668
|
are, so a key legal everywhere is stated once here rather than repeated in
|
|
663
669
|
twenty-five tables where the one that was mistyped would be the one nobody
|
|
664
670
|
noticed:
|
|
665
671
|
|
|
666
|
-
| shared `data` property | Values
|
|
667
|
-
| ---------------------- |
|
|
668
|
-
| `
|
|
669
|
-
| `
|
|
672
|
+
| shared `data` property | Values | Description |
|
|
673
|
+
| ---------------------- | ---------------------- | -------------------------------------------------------------------- |
|
|
674
|
+
| `id` | `string` | A pinned document identity. |
|
|
675
|
+
| `pack` | `string` | The shared compendium route. |
|
|
676
|
+
| `packFolder` | `Address` or pack map | The shared compendium folder Address. |
|
|
677
|
+
| `harnworld` | `Map<string, unknown>` | HârnWorld source details. |
|
|
678
|
+
| `icon` | `Address` | The document's profile art — an `icon` address, resolved into `img`. |
|
|
679
|
+
| `banner` | `Address` | The page's hero image — an `image` address. |
|
|
670
680
|
|
|
671
681
|
`icon` is legal everywhere because most types compile into a document that
|
|
672
682
|
carries one; where a type's passes emit none, the lint says so and the value is
|
|
@@ -847,10 +857,11 @@ on any type, and the frontmatter lint never reports it.
|
|
|
847
857
|
|
|
848
858
|
#### The pack a note compiles into
|
|
849
859
|
|
|
850
|
-
`pack` names which configured compendium receives the note's document.
|
|
860
|
+
`data.pack` names which configured compendium receives the note's document.
|
|
851
861
|
|
|
852
862
|
```yaml
|
|
853
|
-
|
|
863
|
+
data:
|
|
864
|
+
pack: items-hm3
|
|
854
865
|
```
|
|
855
866
|
|
|
856
867
|
It is deliberately close to the retired `package:` and deliberately not the same
|
|
@@ -867,7 +878,8 @@ system: each block the note carries sends its own document where that block
|
|
|
867
878
|
says, and a note carrying two blocks may place them in two packs.
|
|
868
879
|
|
|
869
880
|
```yaml
|
|
870
|
-
|
|
881
|
+
data:
|
|
882
|
+
pack: items-sohl
|
|
871
883
|
hm3:
|
|
872
884
|
pack: items-hm3
|
|
873
885
|
```
|
|
@@ -898,10 +910,11 @@ and in no compendium — a page of developer documentation, a page about the
|
|
|
898
910
|
package itself:
|
|
899
911
|
|
|
900
912
|
```yaml
|
|
913
|
+
shortcode: architecture
|
|
901
914
|
type: doc
|
|
902
915
|
subType: concept
|
|
903
|
-
|
|
904
|
-
pack: none
|
|
916
|
+
data:
|
|
917
|
+
pack: none
|
|
905
918
|
```
|
|
906
919
|
|
|
907
920
|
A link to such a note resolves everywhere the note is addressable. On the web
|
|
@@ -1046,16 +1059,17 @@ none. It is the same principle that turned `folder: ONXsqZAIZr2qzxTb` into
|
|
|
1046
1059
|
`packFolder: <address>` above: an opaque derived identity does not belong in
|
|
1047
1060
|
authored content.
|
|
1048
1061
|
|
|
1049
|
-
**An authored `id`
|
|
1062
|
+
**An authored `data.id` wins**, and that is how a document's identity is
|
|
1050
1063
|
**pinned**:
|
|
1051
1064
|
|
|
1052
1065
|
```yaml
|
|
1053
|
-
type: miscgear
|
|
1054
1066
|
shortcode: bowlcer
|
|
1055
|
-
|
|
1067
|
+
type: miscgear
|
|
1068
|
+
data:
|
|
1069
|
+
id: plaiQQm2T5zVK5mO # pinned: this document keeps this id
|
|
1056
1070
|
```
|
|
1057
1071
|
|
|
1058
|
-
A blank `id:` is not a pin — it is a deleted value with the key left behind, and
|
|
1072
|
+
A blank `data.id:` is not a pin — it is a deleted value with the key left behind, and
|
|
1059
1073
|
is treated as absent.
|
|
1060
1074
|
|
|
1061
1075
|
**A rename moves the id, and that is the trade.** The address carries the
|
|
@@ -1144,7 +1158,8 @@ of the same entry is a warning; the declaration still works.
|
|
|
1144
1158
|
A note says which folder of its pack it lands in:
|
|
1145
1159
|
|
|
1146
1160
|
```yaml
|
|
1147
|
-
|
|
1161
|
+
data:
|
|
1162
|
+
packFolder: poisonsandtoxins # a folder note's address
|
|
1148
1163
|
```
|
|
1149
1164
|
|
|
1150
1165
|
**`packFolder` is a folder note's address** — an ordinary address, resolved the
|
|
@@ -2481,6 +2496,7 @@ Generates a living (or undead, or spirit) being.
|
|
|
2481
2496
|
| `templatePriority` | `number` | Template priority, _null_ = not a template |
|
|
2482
2497
|
| `archetypes` | `Archetype[]` | What sort of character this is. **Always an array** — `[]` where none apply; `null` is an error. |
|
|
2483
2498
|
| `occupation` | `string` | Name of the character's occupation |
|
|
2499
|
+
| `social` | `Map<string, unknown>` | The being's social profile |
|
|
2484
2500
|
| `stations` | `Address[]` | Name of the stations the character belongs to |
|
|
2485
2501
|
| `lore` | `Address[]` | Lore concerning this being — the people it is of, the standing it holds, the law it lives under |
|
|
2486
2502
|
| `homes` | `Address[]` | Place the being calls home |
|
|
@@ -2743,7 +2759,8 @@ no Foundry document: it is a page and nothing else.
|
|
|
2743
2759
|
It declares a `shortcode` — conventionally `root` — because that is what a
|
|
2744
2760
|
link is written with: `[[homepage-root|Text]]` is an ordinary wikilink, and it
|
|
2745
2761
|
resolves to `/<package>/`. The shortcode names the page in links; its address
|
|
2746
|
-
is the package root. `
|
|
2762
|
+
is the package root. The page heading reads `name.full` and defaults to
|
|
2763
|
+
`packageBuild.manifest.title`; a
|
|
2747
2764
|
`description` and a `banner` are read by the theme's hero; any other
|
|
2748
2765
|
top-level key is passed through to the page as a theme parameter.
|
|
2749
2766
|
|
package/engine/folder-notes.mjs
CHANGED
|
@@ -274,7 +274,8 @@ export function collectFolderNotes(notes, pkg) {
|
|
|
274
274
|
|
|
275
275
|
const { parent, color } = folderFields(fm);
|
|
276
276
|
const address = folderAddress(pkg, shortcode);
|
|
277
|
-
const
|
|
277
|
+
const pinnedId = fm.data?.id ?? fm.id;
|
|
278
|
+
const authoredId = pinnedId == null ? "" : String(pinnedId).trim();
|
|
278
279
|
|
|
279
280
|
folders.push({
|
|
280
281
|
shortcode,
|
package/engine/frontmatter.mjs
CHANGED
|
@@ -348,7 +348,7 @@ export function parseValueDesc(raw) {
|
|
|
348
348
|
/**
|
|
349
349
|
* The compendium folder a note names.
|
|
350
350
|
*
|
|
351
|
-
*
|
|
351
|
+
* `data.packFolder` is a folder note's **address**
|
|
352
352
|
* (`folder-poisonsandtoxins`), resolved through the address index the whole
|
|
353
353
|
* build shares. The `folder:` Foundry-id spelling this function once read
|
|
354
354
|
* beside it, and the per-pack `*-folders.yaml` that id was resolved against,
|
|
@@ -372,7 +372,7 @@ export function parseValueDesc(raw) {
|
|
|
372
372
|
* two spellings, and there is only one left to be.
|
|
373
373
|
*/
|
|
374
374
|
export function folderField(fm) {
|
|
375
|
-
const asAddress =
|
|
375
|
+
const asAddress = fm?.sohl?.packFolder ?? fm?.data?.packFolder ?? fm?.packFolder ?? null;
|
|
376
376
|
if (asAddress != null && asAddress !== "") return { value: asAddress, isAddress: true };
|
|
377
377
|
return { value: null, isAddress: true };
|
|
378
378
|
}
|
package/engine/homepage.mjs
CHANGED
|
@@ -340,8 +340,8 @@ function positionOfType(file) {
|
|
|
340
340
|
* The one defaulted value on the page, and it defaults to the package's own
|
|
341
341
|
* `packageBuild.manifest.title` — the name Foundry already shows for the
|
|
342
342
|
* package — so a homepage that adds nothing to it need not restate it. An
|
|
343
|
-
* authored `
|
|
344
|
-
* differently from a package browser.
|
|
343
|
+
* authored `name.full` wins, because a front page may greet a reader
|
|
344
|
+
* differently from a package browser. A top-level `title` is also readable.
|
|
345
345
|
*
|
|
346
346
|
* Falls back to `contentPackage` last, so a package that has no manifest of its
|
|
347
347
|
* own still yields a titled page rather than a blank heading.
|
|
@@ -351,7 +351,7 @@ function positionOfType(file) {
|
|
|
351
351
|
* @returns {string} The title.
|
|
352
352
|
*/
|
|
353
353
|
export function homepageTitle(fm, config) {
|
|
354
|
-
const authored = fm?.title;
|
|
354
|
+
const authored = fm?.name?.full ?? fm?.title;
|
|
355
355
|
if (typeof authored === "string" && authored.trim()) return authored;
|
|
356
356
|
const manifest = /** @type {Record<string, unknown>|undefined} */ (
|
|
357
357
|
config?.packageBuild?.manifest
|
package/engine/item-compiler.mjs
CHANGED
|
@@ -64,7 +64,12 @@ import { documentSubtype, subtypeRow } from "./document-subtypes.mjs";
|
|
|
64
64
|
// The note-level `<system>:` block: `<system>.system` onto the document's
|
|
65
65
|
// `system` verbatim, and `<system>.img` / `.effects` / `.flags` overriding
|
|
66
66
|
// their shared top-level forms for this system alone.
|
|
67
|
-
import {
|
|
67
|
+
import {
|
|
68
|
+
blockDataProperty,
|
|
69
|
+
blockProperty,
|
|
70
|
+
claimedPaths,
|
|
71
|
+
mergeSystemData,
|
|
72
|
+
} from "./system-block.mjs";
|
|
68
73
|
// The other direction of the same declaration: a field the *document* writes in
|
|
69
74
|
// play, which a note may not author and the builder does not emit.
|
|
70
75
|
import { assertNoDerivedFields } from "./derived-fields.mjs";
|
|
@@ -379,7 +384,7 @@ export class SystemItemCompiler extends BasePackCompiler {
|
|
|
379
384
|
// one spelling: `packFolder` names a folder note by its address, the
|
|
380
385
|
// `folder:` id spelling having been retired with the per-pack YAML it
|
|
381
386
|
// resolved against.
|
|
382
|
-
const folder = this.folderResolver(
|
|
387
|
+
const folder = this.folderResolver(blockDataProperty(fm, system, "packFolder", null), {
|
|
383
388
|
isAddress: true,
|
|
384
389
|
});
|
|
385
390
|
|
package/engine/map-places.mjs
CHANGED
|
@@ -207,7 +207,9 @@ export function placesFromRecords(records, { contentBase, base }) {
|
|
|
207
207
|
folder: record.file?.folder ?? "",
|
|
208
208
|
package: String(record.package ?? ""),
|
|
209
209
|
local: true,
|
|
210
|
-
hasPackFolder:
|
|
210
|
+
hasPackFolder:
|
|
211
|
+
Object.hasOwn(record.data ?? {}, "packFolder") ||
|
|
212
|
+
Object.hasOwn(record, "packFolder"),
|
|
211
213
|
});
|
|
212
214
|
} else if (record.type === "affiliation" && record.subType === "polity") {
|
|
213
215
|
polities.push({
|
package/engine/note-ids.mjs
CHANGED
|
@@ -105,7 +105,7 @@ function text(value) {
|
|
|
105
105
|
*/
|
|
106
106
|
export function noteDocId(fm, { pkg, maps = KNOWN_DOCUMENT_SUBTYPE_MAPS } = {}) {
|
|
107
107
|
if (!fm || typeof fm !== "object") return undefined;
|
|
108
|
-
const pinned = text(fm.id);
|
|
108
|
+
const pinned = text(fm.data?.id) ?? text(fm.id);
|
|
109
109
|
if (pinned) return pinned;
|
|
110
110
|
const type = text(fm.type);
|
|
111
111
|
const shortcode = text(fm.shortcode);
|
|
@@ -277,6 +277,29 @@ const TOKEN_ICON = Object.freeze({
|
|
|
277
277
|
* @type {readonly DataFieldSpec[]}
|
|
278
278
|
*/
|
|
279
279
|
export const SHARED_DATA_FIELDS = Object.freeze([
|
|
280
|
+
Object.freeze({
|
|
281
|
+
name: "id",
|
|
282
|
+
...TEXT,
|
|
283
|
+
describe: "A fixed document identity when the derived identity is unsuitable.",
|
|
284
|
+
}),
|
|
285
|
+
Object.freeze({
|
|
286
|
+
name: "pack",
|
|
287
|
+
...TEXT,
|
|
288
|
+
describe: "The shared compendium route, with a system block overriding it.",
|
|
289
|
+
}),
|
|
290
|
+
Object.freeze({
|
|
291
|
+
name: "packFolder",
|
|
292
|
+
...LINK_BY_PACK,
|
|
293
|
+
ref: "folder",
|
|
294
|
+
accepts: ["folder"],
|
|
295
|
+
describe: "The shared compendium folder Address, with a system block overriding it.",
|
|
296
|
+
}),
|
|
297
|
+
Object.freeze({
|
|
298
|
+
name: "harnworld",
|
|
299
|
+
shape: "map",
|
|
300
|
+
kind: "map",
|
|
301
|
+
describe: "HârnWorld source details shared by every system.",
|
|
302
|
+
}),
|
|
280
303
|
Object.freeze({
|
|
281
304
|
name: "icon",
|
|
282
305
|
...LINK,
|
|
@@ -605,6 +628,7 @@ export const NOTE_VOCABULARY = Object.freeze({
|
|
|
605
628
|
// package's declared present compute — see `engine/being-age.mjs`.
|
|
606
629
|
check: checkBeingAge,
|
|
607
630
|
data: Object.freeze([
|
|
631
|
+
{ name: "social", shape: "map", kind: "map", describe: "The being's social profile." },
|
|
608
632
|
TOKEN_ICON,
|
|
609
633
|
TEMPLATE_PRIORITY,
|
|
610
634
|
{ name: "archetypes", ...LIST, describe: "Archetypal behaviours the being fits." },
|
package/engine/pack-router.mjs
CHANGED
|
@@ -82,7 +82,7 @@
|
|
|
82
82
|
|
|
83
83
|
import { NO_PACK, packForType } from "./ids.mjs";
|
|
84
84
|
import { loadPackConfig } from "./pack-config.mjs";
|
|
85
|
-
import {
|
|
85
|
+
import { blockDataProperty, systemBlock } from "./system-block.mjs";
|
|
86
86
|
|
|
87
87
|
/**
|
|
88
88
|
* A note that cannot be routed to a pack. Thrown rather than returned so no
|
|
@@ -131,7 +131,9 @@ export { NO_PACK };
|
|
|
131
131
|
*/
|
|
132
132
|
export function declaresNoPack(fm, system) {
|
|
133
133
|
const declared =
|
|
134
|
-
system === undefined ?
|
|
134
|
+
system === undefined ?
|
|
135
|
+
(fm?.data?.[PACK_FIELD] ?? fm?.[PACK_FIELD])
|
|
136
|
+
: blockDataProperty(fm, system, PACK_FIELD);
|
|
135
137
|
return declared === NO_PACK;
|
|
136
138
|
}
|
|
137
139
|
|
|
@@ -270,7 +272,9 @@ export function createPackRouter(packs) {
|
|
|
270
272
|
const inBlock = system === undefined ? undefined : systemBlock(fm, system)?.[PACK_FIELD];
|
|
271
273
|
const authoredInBlock = inBlock != null && inBlock !== "";
|
|
272
274
|
let declared =
|
|
273
|
-
system === undefined ?
|
|
275
|
+
system === undefined ?
|
|
276
|
+
(fm?.data?.[PACK_FIELD] ?? fm?.[PACK_FIELD])
|
|
277
|
+
: blockDataProperty(fm, system, PACK_FIELD);
|
|
274
278
|
// The declaration names where the note's *own* document goes. A pass
|
|
275
279
|
// writing a document derived from it — an item's prose becoming a
|
|
276
280
|
// JournalEntry — is not what the author was addressing.
|
package/engine/system-block.mjs
CHANGED
|
@@ -361,6 +361,25 @@ export function blockProperty(fm, block, key, defaultValue = undefined) {
|
|
|
361
361
|
return shared === undefined || shared === null ? defaultValue : shared;
|
|
362
362
|
}
|
|
363
363
|
|
|
364
|
+
/**
|
|
365
|
+
* A system override followed by a shared `data:` value. The top-level value
|
|
366
|
+
* remains readable while content repositories move their authored fields.
|
|
367
|
+
*
|
|
368
|
+
* @param {object} fm - The note's frontmatter.
|
|
369
|
+
* @param {string} block - The system block key.
|
|
370
|
+
* @param {string} key - The shared property.
|
|
371
|
+
* @param {any} [defaultValue] - Returned when no position declares it.
|
|
372
|
+
* @returns {any} The selected value.
|
|
373
|
+
*/
|
|
374
|
+
export function blockDataProperty(fm, block, key, defaultValue = undefined) {
|
|
375
|
+
const declared = systemBlock(fm, block)?.[key];
|
|
376
|
+
if (declared !== undefined && declared !== null) return declared;
|
|
377
|
+
const shared = isMapping(fm?.data) ? fm.data[key] : undefined;
|
|
378
|
+
if (shared !== undefined && shared !== null) return shared;
|
|
379
|
+
const legacy = isMapping(fm) ? fm[key] : undefined;
|
|
380
|
+
return legacy === undefined || legacy === null ? defaultValue : legacy;
|
|
381
|
+
}
|
|
382
|
+
|
|
364
383
|
/**
|
|
365
384
|
* The key a field is authored at **inside** a system block — step 2.
|
|
366
385
|
*
|
package/hm3/actors.mjs
CHANGED
|
@@ -63,7 +63,12 @@ import {
|
|
|
63
63
|
// The note-level `hm3:` block: `hm3.system` onto the document's `system`
|
|
64
64
|
// verbatim, and `hm3.img` / `hm3.items` / `hm3.effects` / `hm3.flags`
|
|
65
65
|
// overriding their shared top-level forms for this system alone.
|
|
66
|
-
import {
|
|
66
|
+
import {
|
|
67
|
+
blockDataProperty,
|
|
68
|
+
blockField,
|
|
69
|
+
blockProperty,
|
|
70
|
+
mergeSystemData,
|
|
71
|
+
} from "../engine/system-block.mjs";
|
|
67
72
|
|
|
68
73
|
/**
|
|
69
74
|
* Native HM3 facts shared by its actor subtypes.
|
|
@@ -324,7 +329,7 @@ export class Hm3Actors extends SystemActorCompiler {
|
|
|
324
329
|
// by its address. This pass once read only the Foundry id, so an HM3
|
|
325
330
|
// tree could not file an actor by address at all — which its own sweep
|
|
326
331
|
// needs; the id spelling is retired outright.
|
|
327
|
-
const folder = this.folderResolver(
|
|
332
|
+
const folder = this.folderResolver(blockDataProperty(fm, block, "packFolder", null), {
|
|
328
333
|
isAddress: true,
|
|
329
334
|
});
|
|
330
335
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@heroiclands/package-build",
|
|
3
|
-
"version": "22.13.
|
|
3
|
+
"version": "22.13.1",
|
|
4
4
|
"description": "Shared toolchain for building and shipping a HeroicLands Foundry VTT package — content compilation, manifest, localization, staging, bundle, release and deployment.",
|
|
5
5
|
"license": "GPL-3.0-or-later",
|
|
6
6
|
"type": "module",
|
|
@@ -149,7 +149,7 @@ export function parseValueDesc(raw: any): {
|
|
|
149
149
|
/**
|
|
150
150
|
* The compendium folder a note names.
|
|
151
151
|
*
|
|
152
|
-
*
|
|
152
|
+
* `data.packFolder` is a folder note's **address**
|
|
153
153
|
* (`folder-poisonsandtoxins`), resolved through the address index the whole
|
|
154
154
|
* build shares. The `folder:` Foundry-id spelling this function once read
|
|
155
155
|
* beside it, and the per-pack `*-folders.yaml` that id was resolved against,
|
|
@@ -119,8 +119,8 @@ export function checkHomepageCount(found: ReadonlyArray<{
|
|
|
119
119
|
* The one defaulted value on the page, and it defaults to the package's own
|
|
120
120
|
* `packageBuild.manifest.title` — the name Foundry already shows for the
|
|
121
121
|
* package — so a homepage that adds nothing to it need not restate it. An
|
|
122
|
-
* authored `
|
|
123
|
-
* differently from a package browser.
|
|
122
|
+
* authored `name.full` wins, because a front page may greet a reader
|
|
123
|
+
* differently from a package browser. A top-level `title` is also readable.
|
|
124
124
|
*
|
|
125
125
|
* Falls back to `contentPackage` last, so a package that has no manifest of its
|
|
126
126
|
* own still yields a titled page rather than a blank heading.
|
|
@@ -93,6 +93,17 @@ export function sharedProperty(fm: object, source: string, defaultValue?: any):
|
|
|
93
93
|
* @returns {any} The value.
|
|
94
94
|
*/
|
|
95
95
|
export function blockProperty(fm: object, block: string, key: string, defaultValue?: any): any;
|
|
96
|
+
/**
|
|
97
|
+
* A system override followed by a shared `data:` value. The top-level value
|
|
98
|
+
* remains readable while content repositories move their authored fields.
|
|
99
|
+
*
|
|
100
|
+
* @param {object} fm - The note's frontmatter.
|
|
101
|
+
* @param {string} block - The system block key.
|
|
102
|
+
* @param {string} key - The shared property.
|
|
103
|
+
* @param {any} [defaultValue] - Returned when no position declares it.
|
|
104
|
+
* @returns {any} The selected value.
|
|
105
|
+
*/
|
|
106
|
+
export function blockDataProperty(fm: object, block: string, key: string, defaultValue?: any): any;
|
|
96
107
|
/**
|
|
97
108
|
* The key a field is authored at **inside** a system block — step 2.
|
|
98
109
|
*
|