@heroiclands/package-build 20.3.1 → 20.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +351 -0
- package/CONTENT.md +322 -110
- package/README.md +21 -10
- package/bin/content-build.mjs +204 -92
- package/bin/package-build.mjs +48 -15
- package/config.mjs +6 -8
- package/content-config.mjs +518 -151
- package/docs/api.md +1352 -0
- package/docs/commands.md +1609 -0
- package/docs/configuration.md +1432 -0
- package/docs/content-format.md +73 -74
- package/docs/diagnostics.md +356 -0
- package/docs/getting-started.md +813 -0
- package/docs/project-setup.md +469 -0
- package/e2e.mjs +4 -4
- package/engine/actor-compiler.mjs +64 -61
- package/engine/address-charset.mjs +6 -6
- package/engine/address-diff.mjs +57 -52
- package/engine/anchored-sections.mjs +1 -1
- package/engine/anchors.mjs +1 -1
- package/engine/base-compiler.mjs +42 -38
- package/engine/bundle-notes.mjs +4 -4
- package/engine/bundles.mjs +17 -8
- package/engine/code-fences.mjs +3 -3
- package/engine/compendiums.mjs +8 -8
- package/engine/compile-corpus.mjs +4 -5
- package/engine/content-address.mjs +24 -24
- package/engine/content-charset.mjs +1 -1
- package/engine/content-format-check.mjs +24 -25
- package/engine/content-format.mjs +8 -8
- package/engine/content-html.mjs +202 -0
- package/engine/content-icons.mjs +196 -165
- package/engine/content-index.mjs +61 -40
- package/engine/content-links.mjs +74 -41
- package/engine/content-lint.mjs +64 -34
- package/engine/content-package.mjs +7 -9
- package/engine/content-slug.mjs +2 -2
- package/engine/content-tables.mjs +38 -34
- package/engine/content-tree.mjs +1 -1
- package/engine/derived-fields.mjs +174 -0
- package/engine/diagnostics.mjs +3 -4
- package/engine/document-subtypes.mjs +6 -7
- package/engine/field-reference.mjs +8 -8
- package/engine/field-spec.mjs +29 -30
- package/engine/folder-notes.mjs +18 -16
- package/engine/foreign-catalog.mjs +8 -9
- package/engine/foundry-entries.mjs +19 -20
- package/engine/frontmatter-lint.mjs +95 -103
- package/engine/frontmatter.mjs +11 -11
- package/engine/generate.mjs +44 -39
- package/engine/helpers.mjs +147 -84
- package/engine/homepage.mjs +20 -22
- package/engine/ids.mjs +15 -15
- package/engine/index-records.mjs +4 -4
- package/engine/index.mjs +26 -10
- package/engine/item-compiler.mjs +54 -29
- package/engine/item-docs.mjs +11 -13
- package/engine/item-registry.mjs +13 -13
- package/engine/journals.mjs +24 -15
- package/engine/macros.mjs +3 -3
- package/engine/map-notes.mjs +6 -6
- package/engine/metadata-index.mjs +5 -5
- package/engine/note-claims.mjs +51 -41
- package/engine/note-ids.mjs +5 -5
- package/engine/note-package.mjs +4 -4
- package/engine/note-renames.mjs +3 -3
- package/engine/note-schemas.mjs +3 -8
- package/engine/note-vocabulary.mjs +56 -54
- package/engine/pack-config.mjs +39 -25
- package/engine/pack-router.mjs +0 -0
- package/engine/pdf-build.mjs +464 -0
- package/engine/pdf-fonts.mjs +420 -0
- package/engine/pdf-render.mjs +798 -0
- package/engine/pdf-toc.mjs +525 -0
- package/engine/prose-config.mjs +9 -9
- package/engine/prose-lint.mjs +12 -13
- package/engine/region-events.mjs +2 -2
- package/engine/retired-fields.mjs +24 -22
- package/engine/runtime-only-fields.mjs +1 -1
- package/engine/scene-levels.mjs +1 -1
- package/engine/scenes.mjs +33 -25
- package/engine/schema-check.mjs +21 -21
- package/engine/schema-extract.mjs +1 -1
- package/engine/site-build.mjs +66 -50
- package/engine/site-index.mjs +17 -18
- package/engine/sql-tables.mjs +21 -13
- package/engine/subtype-registry.mjs +5 -4
- package/engine/system-block.mjs +18 -19
- package/engine/systems.mjs +2 -2
- package/engine/web-wikilinks.mjs +41 -38
- package/engine/wikilink-syntax.mjs +16 -16
- package/engine/wikilinks.mjs +40 -42
- package/hm3/actors.mjs +30 -22
- package/hm3/document-subtypes.mjs +5 -5
- package/hm3/index.mjs +2 -2
- package/hm3/item-builders.mjs +1 -1
- package/hm3/item-fields.mjs +3 -3
- package/hm3/items.mjs +28 -8
- package/hm3/template-priority.mjs +2 -2
- package/lang.mjs +3 -3
- package/manifest.mjs +17 -20
- package/package.json +1 -2
- package/release.mjs +66 -6
- package/sohl/actors.mjs +36 -28
- package/sohl/affiliation-standings.mjs +2 -2
- package/sohl/being-info.mjs +5 -5
- package/sohl/default-item-art.mjs +5 -5
- package/sohl/document-subtypes.mjs +5 -5
- package/sohl/index.mjs +3 -3
- package/sohl/item-builders.mjs +7 -7
- package/sohl/item-fields.mjs +8 -9
- package/sohl/items.mjs +28 -6
- package/sohl/kb-passes.mjs +5 -5
- package/sohl/note-schemas.mjs +12 -12
- package/sohl/skill-base.mjs +1 -1
- package/types/content-config.d.mts +91 -39
- package/types/e2e.d.mts +3 -3
- package/types/engine/actor-compiler.d.mts +45 -40
- package/types/engine/address-charset.d.mts +6 -6
- package/types/engine/address-diff.d.mts +60 -6
- package/types/engine/base-compiler.d.mts +27 -21
- package/types/engine/bundle-notes.d.mts +3 -3
- package/types/engine/bundles.d.mts +10 -1
- package/types/engine/code-fences.d.mts +3 -3
- package/types/engine/compendiums.d.mts +3 -3
- package/types/engine/compile-corpus.d.mts +1 -1
- package/types/engine/content-address.d.mts +20 -20
- package/types/engine/content-format-check.d.mts +6 -6
- package/types/engine/content-format.d.mts +2 -2
- package/types/engine/content-html.d.mts +78 -0
- package/types/engine/content-icons.d.mts +139 -92
- package/types/engine/content-index.d.mts +59 -15
- package/types/engine/content-links.d.mts +8 -8
- package/types/engine/content-lint.d.mts +8 -6
- package/types/engine/content-package.d.mts +6 -8
- package/types/engine/content-tables.d.mts +49 -18
- package/types/engine/derived-fields.d.mts +101 -0
- package/types/engine/diagnostics.d.mts +2 -2
- package/types/engine/document-subtypes.d.mts +3 -3
- package/types/engine/field-spec.d.mts +37 -39
- package/types/engine/folder-notes.d.mts +4 -5
- package/types/engine/foreign-catalog.d.mts +3 -3
- package/types/engine/foundry-entries.d.mts +6 -7
- package/types/engine/frontmatter-lint.d.mts +10 -9
- package/types/engine/frontmatter.d.mts +7 -7
- package/types/engine/generate.d.mts +7 -7
- package/types/engine/helpers.d.mts +128 -56
- package/types/engine/homepage.d.mts +16 -18
- package/types/engine/ids.d.mts +13 -13
- package/types/engine/index-records.d.mts +3 -3
- package/types/engine/index.d.mts +6 -0
- package/types/engine/item-compiler.d.mts +21 -5
- package/types/engine/item-docs.d.mts +2 -2
- package/types/engine/item-registry.d.mts +6 -6
- package/types/engine/journals.d.mts +12 -3
- package/types/engine/map-notes.d.mts +2 -2
- package/types/engine/metadata-index.d.mts +4 -4
- package/types/engine/note-claims.d.mts +30 -22
- package/types/engine/note-ids.d.mts +4 -4
- package/types/engine/note-package.d.mts +1 -1
- package/types/engine/note-renames.d.mts +3 -3
- package/types/engine/note-vocabulary.d.mts +31 -203
- package/types/engine/pack-config.d.mts +7 -7
- package/types/engine/pack-router.d.mts +1 -1
- package/types/engine/pdf-build.d.mts +42 -0
- package/types/engine/pdf-fonts.d.mts +30 -0
- package/types/engine/pdf-render.d.mts +144 -0
- package/types/engine/pdf-toc.d.mts +114 -0
- package/types/engine/prose-config.d.mts +9 -9
- package/types/engine/prose-lint.d.mts +3 -4
- package/types/engine/region-events.d.mts +2 -2
- package/types/engine/retired-fields.d.mts +10 -9
- package/types/engine/scenes.d.mts +10 -1
- package/types/engine/schema-check.d.mts +13 -13
- package/types/engine/site-build.d.mts +52 -25
- package/types/engine/site-index.d.mts +3 -4
- package/types/engine/sql-tables.d.mts +11 -5
- package/types/engine/subtype-registry.d.mts +3 -3
- package/types/engine/system-block.d.mts +3 -3
- package/types/engine/web-wikilinks.d.mts +7 -7
- package/types/engine/wikilink-syntax.d.mts +17 -17
- package/types/engine/wikilinks.d.mts +14 -14
- package/types/hm3/actors.d.mts +9 -1
- package/types/hm3/document-subtypes.d.mts +1 -1
- package/types/hm3/items.d.mts +9 -2
- package/types/hm3/template-priority.d.mts +1 -1
- package/types/manifest.d.mts +8 -8
- package/types/release.d.mts +15 -4
- package/types/sohl/actors.d.mts +12 -4
- package/types/sohl/affiliation-standings.d.mts +2 -2
- package/types/sohl/being-info.d.mts +2 -2
- package/types/sohl/document-subtypes.d.mts +1 -1
- package/types/sohl/items.d.mts +9 -2
- package/types/sohl/note-schemas.d.mts +1 -1
- package/MIGRATING.md +0 -608
package/lang.mjs
CHANGED
|
@@ -29,13 +29,13 @@
|
|
|
29
29
|
* `expandObject` **throws**: it cannot create a `Note` property on the string
|
|
30
30
|
* `"The Pall"`. Foundry catches that throw and discards the **entire** file,
|
|
31
31
|
* so one colliding pair drops every translation in it and each string renders
|
|
32
|
-
* as its raw key
|
|
32
|
+
* as its raw key. A key must be a leaf **or** a branch, never both.
|
|
33
33
|
* - **A Handlebars placeholder.** Foundry interpolates with `format()` and
|
|
34
34
|
* SINGLE braces, so a `{{…}}` value renders literally unless some call site
|
|
35
|
-
* happens to hand it to a Handlebars pass
|
|
35
|
+
* happens to hand it to a Handlebars pass.
|
|
36
36
|
* - **Data baked into a key segment.** A segment carrying anything but
|
|
37
37
|
* `[A-Za-z0-9_-]` is a path or a UUID in a key, and a dotted payload is how
|
|
38
|
-
* the collision above gets in
|
|
38
|
+
* the collision above gets in.
|
|
39
39
|
*
|
|
40
40
|
* Every function here is pure — it takes source text and returns findings, and
|
|
41
41
|
* touches no filesystem and emits nothing. The caller owns discovery and
|
package/manifest.mjs
CHANGED
|
@@ -18,16 +18,13 @@
|
|
|
18
18
|
* so there is one job here with two spellings: assemble the manifest from the
|
|
19
19
|
* repository's configuration and write it into the build stage.
|
|
20
20
|
*
|
|
21
|
-
* **There is no template
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
* nothing checking that the pairs agreed. `sohl-kethira-basic` hand-maintained
|
|
27
|
-
* its whole `module.json`, and its `download` named an older version than the
|
|
28
|
-
* module claimed.
|
|
21
|
+
* **There is no template.** A `system.template.json` this module stamped a few
|
|
22
|
+
* fields into would be the one build input written as JSON, by hand, per
|
|
23
|
+
* repository, with no schema and nothing checking it — and it would declare
|
|
24
|
+
* facts the configuration also declares, the pack list twice in two formats,
|
|
25
|
+
* with nothing checking that the pairs agree.
|
|
29
26
|
*
|
|
30
|
-
* So the manifest is generated
|
|
27
|
+
* So the manifest is generated. Three kinds of key end up in it:
|
|
31
28
|
*
|
|
32
29
|
* - **Declared** — the `packageBuild.manifest` block, emitted unchanged, so a
|
|
33
30
|
* key Foundry adds in a later version needs no release of this package.
|
|
@@ -122,7 +119,7 @@ export function releaseUrls({ repoUrl, version, artifact }) {
|
|
|
122
119
|
}
|
|
123
120
|
|
|
124
121
|
/**
|
|
125
|
-
* Where this release publishes its content index
|
|
122
|
+
* Where this release publishes its content index.
|
|
126
123
|
*
|
|
127
124
|
* **Pinned to this version, like `download` and unlike `manifest`.** A
|
|
128
125
|
* consumer reaches this URL by reading the dependency's manifest, so the
|
|
@@ -183,7 +180,7 @@ const MANIFEST_KEY_ORDER = Object.freeze([
|
|
|
183
180
|
/**
|
|
184
181
|
* The manifest's `packs`, derived from the one pack list the build already has.
|
|
185
182
|
*
|
|
186
|
-
* The two
|
|
183
|
+
* The two are not written separately, with `package-build.config.yaml` declaring
|
|
187
184
|
* a pack's name and type, and the manifest template declared them again beside
|
|
188
185
|
* a label, a path and a system id, with nothing checking that the pairs agreed.
|
|
189
186
|
* They are one list now.
|
|
@@ -265,12 +262,12 @@ function namedPacks(folders, at) {
|
|
|
265
262
|
* `styles`, `languages`), and a staged file is a different relation, checked
|
|
266
263
|
* against the stage rather than against configuration. So this is the one place
|
|
267
264
|
* a declaration can go stale against a value the build already computed — and
|
|
268
|
-
* until now nothing compared them
|
|
265
|
+
* until now nothing compared them.
|
|
269
266
|
*
|
|
270
267
|
* `HarnMaster-3-FoundryVTT` shipped the consequence: its folder named four
|
|
271
268
|
* packs, three of which had not existed since the compendium was consolidated,
|
|
272
269
|
* and omitted `items` — 1,577 of 1,597 documents, loose in Foundry's compendium
|
|
273
|
-
* browser, with the build reporting nothing
|
|
270
|
+
* browser, with the build reporting nothing.
|
|
274
271
|
*
|
|
275
272
|
* **The two findings are not the same finding**, and giving them one severity
|
|
276
273
|
* gets one of them wrong:
|
|
@@ -343,14 +340,14 @@ export function packFolderFindings({ packFolders, packs = [] }) {
|
|
|
343
340
|
*
|
|
344
341
|
* `relationships` is the one manifest block with a second reader.
|
|
345
342
|
* `@heroiclands/content-build` consumes it too, and v1.8.0 added
|
|
346
|
-
* `itemCatalog: true` as an opt-in on a declared dependency
|
|
347
|
-
*
|
|
343
|
+
* `itemCatalog: true` as an opt-in on a declared dependency: it selects that
|
|
344
|
+
* package's Item packs as a resolution
|
|
348
345
|
* source for the actors pass. That is an instruction to the build, not a fact
|
|
349
346
|
* about the shipped package — Foundry's relationship schema does not define
|
|
350
347
|
* it, and someone reading a published manifest cannot tell a build directive
|
|
351
348
|
* from a declaration about what the package needs.
|
|
352
349
|
*
|
|
353
|
-
* So the block is filtered rather than copied whole
|
|
350
|
+
* So the block is filtered rather than copied whole. The rule is the
|
|
354
351
|
* distinction, not the name: a key listed here answers *how is this built?*,
|
|
355
352
|
* and every key that survives answers *what does this package depend on?*.
|
|
356
353
|
* `itemCatalog` is the first build-time key to land on a relationship and is
|
|
@@ -441,7 +438,7 @@ export function buildManifest({ config, packageJson, artifact, flags }) {
|
|
|
441
438
|
};
|
|
442
439
|
if (config.compatibility) derived.compatibility = config.compatibility;
|
|
443
440
|
|
|
444
|
-
// `requiresSystem` is the gate half of the declare/require split
|
|
441
|
+
// `requiresSystem` is the gate half of the declare/require split. It
|
|
445
442
|
// emits the `relationships.systems` entry Foundry's `supportsSystem` reads,
|
|
446
443
|
// reusing the `systems:` declaration rather than restating it — a second
|
|
447
444
|
// transcription is free to disagree with what it copied, which is how
|
|
@@ -476,8 +473,8 @@ export function buildManifest({ config, packageJson, artifact, flags }) {
|
|
|
476
473
|
|
|
477
474
|
const merged = { ...declared, ...derived };
|
|
478
475
|
|
|
479
|
-
// The index every consumer resolves this package's addresses through
|
|
480
|
-
//
|
|
476
|
+
// The index every consumer resolves this package's addresses through.
|
|
477
|
+
// Written unconditionally, because a package that publishes no
|
|
481
478
|
// index is one nothing can link into — and the failure of an absent one is
|
|
482
479
|
// a dead link in somebody else's build, which is exactly the kind of
|
|
483
480
|
// silence this replaced the vendored manifest to end.
|
|
@@ -562,7 +559,7 @@ function reportPackFolders(findings, configFile) {
|
|
|
562
559
|
* The declared `packFolders` is checked against the derived `packs[]` first,
|
|
563
560
|
* and an unresolvable name **stops the write**: a manifest already known to
|
|
564
561
|
* describe packs the package does not ship should not reach the stage, where
|
|
565
|
-
* the next command would deploy it
|
|
562
|
+
* the next command would deploy it. See {@link packFolderFindings} for
|
|
566
563
|
* the rule and why its two findings carry different severities.
|
|
567
564
|
*
|
|
568
565
|
* @param {object} options - As {@link buildManifest}, plus where to write.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@heroiclands/package-build",
|
|
3
|
-
"version": "20.
|
|
3
|
+
"version": "20.5.0",
|
|
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",
|
|
@@ -101,7 +101,6 @@
|
|
|
101
101
|
"CHANGELOG-content-build.md",
|
|
102
102
|
"CHANGELOG.md",
|
|
103
103
|
"CONTENT.md",
|
|
104
|
-
"MIGRATING.md",
|
|
105
104
|
"README.md",
|
|
106
105
|
"bin",
|
|
107
106
|
"bundle.mjs",
|
package/release.mjs
CHANGED
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
* advertises, so a release publishes `<artifact>.zip`, the whole staged tree,
|
|
20
20
|
* and `<artifact>.json` beside it, which is what an already installed package
|
|
21
21
|
* re-fetches to notice a new version. A package that ships content publishes a
|
|
22
|
-
* third: the content index other packages resolve its addresses through
|
|
22
|
+
* third: the content index other packages resolve its addresses through,
|
|
23
23
|
* named by the `flags.metadataUrl` the manifest advertises. Every name is fixed
|
|
24
24
|
* by what the manifest says, not chosen here — see `manifest.mjs`.
|
|
25
25
|
*
|
|
@@ -37,7 +37,7 @@ import path from "node:path";
|
|
|
37
37
|
// archiver 8 is pure ESM and exports **classes**, with no default export. The
|
|
38
38
|
// old `import archiver from "archiver"` factory call throws at import —
|
|
39
39
|
// `does not provide an export named 'default'` — which is how this repository's
|
|
40
|
-
// release job came to fail before a single byte was written
|
|
40
|
+
// release job came to fail before a single byte was written.
|
|
41
41
|
import { ZipArchive } from "archiver";
|
|
42
42
|
|
|
43
43
|
/**
|
|
@@ -57,10 +57,17 @@ import { ZipArchive } from "archiver";
|
|
|
57
57
|
* Determines both asset names.
|
|
58
58
|
* @param {string} [opts.metadataDir] - Where the build writes its content
|
|
59
59
|
* index, consulted when the advertised file was not staged.
|
|
60
|
+
* @param {boolean} [opts.pdf] - Whether to build the book that ships beside the
|
|
61
|
+
* archive. `true` by default; `false` skips the build and reports the skip.
|
|
60
62
|
* @returns {Promise<{zip: string, manifest: string, metadata?: string,
|
|
61
|
-
*
|
|
62
|
-
*
|
|
63
|
-
*
|
|
63
|
+
* pdf?: string, pdfFindings: object[], pdfSkipped: string|null,
|
|
64
|
+
* bytes: number, version: string}>} The paths written, what the book build
|
|
65
|
+
* found, the archive's size, and the version the manifest declares.
|
|
66
|
+
* `metadata` is absent when the manifest advertises no content index, and
|
|
67
|
+
* `pdf` is absent when no book was written. `pdfSkipped` is `null` when a
|
|
68
|
+
* book was built, and otherwise the reason none was — itself `null` when the
|
|
69
|
+
* book builder could not be loaded, which is reported through
|
|
70
|
+
* `pdfFindings`.
|
|
64
71
|
* @throws {Error} When the stage has no manifest — there is nothing to release,
|
|
65
72
|
* and an archive without one installs as nothing.
|
|
66
73
|
*/
|
|
@@ -69,6 +76,7 @@ export async function packRelease({
|
|
|
69
76
|
outDir = "build/dist",
|
|
70
77
|
artifact = "system",
|
|
71
78
|
metadataDir = "build/content-index",
|
|
79
|
+
pdf = true,
|
|
72
80
|
} = {}) {
|
|
73
81
|
const stage = path.resolve(stageDir);
|
|
74
82
|
const out = path.resolve(outDir);
|
|
@@ -112,17 +120,69 @@ export async function packRelease({
|
|
|
112
120
|
|
|
113
121
|
const metadata = await publishMetadataIndex({ manifest, stage, out, metadataDir });
|
|
114
122
|
|
|
123
|
+
// Last, and never fatal: the archive and the manifest are the release, and
|
|
124
|
+
// a book that failed to set is a reported problem rather than a reason to
|
|
125
|
+
// publish neither.
|
|
126
|
+
const book =
|
|
127
|
+
pdf ?
|
|
128
|
+
await packReleasePdf({ out, version: manifest.version })
|
|
129
|
+
: { pdf: null, findings: [], reason: "the release was asked not to build one" };
|
|
130
|
+
|
|
115
131
|
return {
|
|
116
132
|
zip: zipPath,
|
|
117
133
|
manifest: path.join(out, manifestName),
|
|
118
134
|
...(metadata ? { metadata } : {}),
|
|
135
|
+
...(book.pdf ? { pdf: book.pdf } : {}),
|
|
136
|
+
pdfFindings: book.findings,
|
|
137
|
+
pdfSkipped: book.pdf ? null : book.reason,
|
|
119
138
|
bytes: archive.pointer(),
|
|
120
139
|
version: manifest.version,
|
|
121
140
|
};
|
|
122
141
|
}
|
|
123
142
|
|
|
124
143
|
/**
|
|
125
|
-
*
|
|
144
|
+
* Build the book that ships beside the archive.
|
|
145
|
+
*
|
|
146
|
+
* **Imported when it is used, not when this module is.** The book build pulls
|
|
147
|
+
* in a markdown parser, DuckDB and the whole content engine; `release.mjs`
|
|
148
|
+
* otherwise exists to zip a directory, and every consumer that publishes no
|
|
149
|
+
* book would pay for that graph on `import`. A dynamic import inside the one
|
|
150
|
+
* function that needs it keeps the cost where the benefit is.
|
|
151
|
+
*
|
|
152
|
+
* **Not building is the normal case and never an error.** A package publishing
|
|
153
|
+
* only a homepage, one with no `pdf:` block and one with no content tree have
|
|
154
|
+
* each said they publish no book. Four of the six packages that install this
|
|
155
|
+
* toolchain are in exactly that position, so a release that failed for the
|
|
156
|
+
* absence of a PDF would break more releases than it helped.
|
|
157
|
+
*
|
|
158
|
+
* @param {object} opts - Options.
|
|
159
|
+
* @param {string} opts.out - The release directory.
|
|
160
|
+
* @param {string} opts.version - The version the manifest declares.
|
|
161
|
+
* @returns {Promise<{pdf: string|null, findings: object[], reason: string|null}>}
|
|
162
|
+
* What was built, and what was found on the way.
|
|
163
|
+
*/
|
|
164
|
+
async function packReleasePdf({ out, version }) {
|
|
165
|
+
let buildPdf;
|
|
166
|
+
try {
|
|
167
|
+
({ buildPdf } = await import("./engine/pdf-build.mjs"));
|
|
168
|
+
} catch (err) {
|
|
169
|
+
return {
|
|
170
|
+
pdf: null,
|
|
171
|
+
findings: [
|
|
172
|
+
{
|
|
173
|
+
severity: "warning",
|
|
174
|
+
message: `the book builder could not be loaded: ${err.message}`,
|
|
175
|
+
},
|
|
176
|
+
],
|
|
177
|
+
reason: null,
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
const result = await buildPdf({ out, version });
|
|
181
|
+
return { pdf: result.pdf, findings: result.findings, reason: result.reason };
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* Place the content index the manifest advertises beside the archive.
|
|
126
186
|
*
|
|
127
187
|
* **The asset's name comes from the manifest, not from here.** `flags.metadataUrl`
|
|
128
188
|
* is the URL every consumer fetches, so its basename is by definition the name
|
package/sohl/actors.mjs
CHANGED
|
@@ -17,21 +17,21 @@
|
|
|
17
17
|
* The machinery an actor of any system needs — the predefined-item catalogue,
|
|
18
18
|
* reference translation through the system's map, embedding and its stable ids,
|
|
19
19
|
* the anchored prose sections — lives in
|
|
20
|
-
* {@link module:engine/actor-compiler}, where a second system reaches it
|
|
21
|
-
*
|
|
20
|
+
* {@link module:engine/actor-compiler}, where a second system reaches it.
|
|
21
|
+
* What is left here is SoHL's data model: the body structure and its
|
|
22
22
|
* movement profiles, the attributes-and-items frontmatter that becomes embedded
|
|
23
23
|
* documents, the opening mastery level a skill is baked with, and the `system`
|
|
24
24
|
* block itself.
|
|
25
25
|
*
|
|
26
26
|
* One content type today, and the actor subtype it produces is **declared**
|
|
27
27
|
* rather than assumed to be the same word: `sohl/document-subtypes.mjs` maps
|
|
28
|
-
* `being` → `Actor` / `being`, and this pass looks it up
|
|
28
|
+
* `being` → `Actor` / `being`, and this pass looks it up. It read
|
|
29
29
|
* `ACTOR_VAULT_TYPE = "being"` here and emitted `type: "being"` several hundred
|
|
30
30
|
* lines below, which made the two vocabularies agree by coincidence.
|
|
31
31
|
*
|
|
32
32
|
* It was two content types — `character` and `creature` — which compiled to the
|
|
33
33
|
* same `being` with no branch anywhere between them; they were retired in
|
|
34
|
-
*
|
|
34
|
+
* retired, and are reported by `assertTypeNotRetired` in
|
|
35
35
|
* `engine/ids.mjs`.
|
|
36
36
|
*
|
|
37
37
|
* Attributes (`sohl.attributes` map) become embedded attribute items with
|
|
@@ -56,16 +56,16 @@ import {
|
|
|
56
56
|
import { openingMasteryLevel } from "./skill-base.mjs";
|
|
57
57
|
import { SystemActorCompiler, renderSection } from "../engine/actor-compiler.mjs";
|
|
58
58
|
// Which Foundry Actor subtype a note's `type` compiles into. Looked up in the
|
|
59
|
-
// system's declared map, never inferred from the type itself
|
|
59
|
+
// system's declared map, never inferred from the type itself.
|
|
60
60
|
import { documentSubtype } from "../engine/document-subtypes.mjs";
|
|
61
61
|
import { SOHL_DOCUMENT_SUBTYPES } from "./document-subtypes.mjs";
|
|
62
62
|
// The note-level `sohl:` block: `sohl.system` onto the document's `system`
|
|
63
63
|
// verbatim, and `sohl.img` / `sohl.effects` / `sohl.flags` overriding their
|
|
64
|
-
// shared top-level forms for this system alone
|
|
64
|
+
// shared top-level forms for this system alone.
|
|
65
65
|
import { blockProperty, mergeSystemData } from "../engine/system-block.mjs";
|
|
66
66
|
import { readField, retiredTopLevelKey } from "../engine/field-spec.mjs";
|
|
67
67
|
// The retirement window's reports, shared with the frontmatter lint so the two
|
|
68
|
-
// cannot say different things about the same key
|
|
68
|
+
// cannot say different things about the same key.
|
|
69
69
|
import {
|
|
70
70
|
legacyKeyMessage,
|
|
71
71
|
locateFrontmatterKey,
|
|
@@ -76,7 +76,7 @@ import {
|
|
|
76
76
|
* The system this pass compiles for — the block its notes write.
|
|
77
77
|
*
|
|
78
78
|
* Read from the map rather than spelled here, so the block name and the subtype
|
|
79
|
-
* map are one statement
|
|
79
|
+
* map are one statement.
|
|
80
80
|
*
|
|
81
81
|
* @type {string}
|
|
82
82
|
*/
|
|
@@ -98,7 +98,7 @@ const DEFAULT_IMG = {
|
|
|
98
98
|
* and the note's top level and nothing else — so `data.portrait`, the position
|
|
99
99
|
* the specification names and `sohl-thalorna` writes on 646 beings, was
|
|
100
100
|
* invisible, and `?? defaultImg` on the next line turned every miss into the
|
|
101
|
-
* generic person icon rather than into a complaint
|
|
101
|
+
* generic person icon rather than into a complaint. Going through
|
|
102
102
|
* {@link module:engine/field-spec.readField} is what makes the mapping table
|
|
103
103
|
* executable here as it already is for HM3's `data.species`.
|
|
104
104
|
*
|
|
@@ -115,7 +115,7 @@ const PORTRAIT_FIELD = Object.freeze({
|
|
|
115
115
|
shape: "path",
|
|
116
116
|
// The two empties survive, because the caller's `?? defaultImg` is what
|
|
117
117
|
// tells them apart: `null` and an absent key mean "no art named, default
|
|
118
|
-
// me", `""` means "ship blank on purpose"
|
|
118
|
+
// me", `""` means "ship blank on purpose".
|
|
119
119
|
read: (raw) => resolveImg(raw),
|
|
120
120
|
default: null,
|
|
121
121
|
describe: "Path to the portrait image.",
|
|
@@ -190,11 +190,19 @@ function extractBodyAndMovement(fm) {
|
|
|
190
190
|
};
|
|
191
191
|
}
|
|
192
192
|
|
|
193
|
+
/**
|
|
194
|
+
* SoHL's Actor compile pass.
|
|
195
|
+
*
|
|
196
|
+
* Declares SoHL's note-type → document-subtype map and builds a `being` note's
|
|
197
|
+
* document: the body structure and its movement profiles, the embedded
|
|
198
|
+
* attribute and item documents the frontmatter names, and the `system` block.
|
|
199
|
+
* Everything else is {@link module:engine/actor-compiler}'s.
|
|
200
|
+
*/
|
|
193
201
|
export class Actors extends SystemActorCompiler {
|
|
194
202
|
/**
|
|
195
203
|
* SoHL's note-type → document-subtype map — the one declaration that says
|
|
196
204
|
* which block this pass reads, which notes it claims, and what each
|
|
197
|
-
* becomes
|
|
205
|
+
* becomes. It is also what every embedded reference is translated
|
|
198
206
|
* through, which is why a subclass replaces one thing and not two.
|
|
199
207
|
*
|
|
200
208
|
* @type {import("../engine/document-subtypes.mjs").DocumentSubtypeMap}
|
|
@@ -298,7 +306,7 @@ export class Actors extends SystemActorCompiler {
|
|
|
298
306
|
}
|
|
299
307
|
|
|
300
308
|
/**
|
|
301
|
-
* Bake each unopened skill's opening mastery level into the document
|
|
309
|
+
* Bake each unopened skill's opening mastery level into the document.
|
|
302
310
|
*
|
|
303
311
|
* A skill whose `masteryLevelBase` is still null once the note's frontmatter
|
|
304
312
|
* has been merged onto the catalogue entry is *not yet opened*, and the
|
|
@@ -352,7 +360,7 @@ export class Actors extends SystemActorCompiler {
|
|
|
352
360
|
const id = fm.id;
|
|
353
361
|
const ctx = `actor "${name}"`;
|
|
354
362
|
// The document's own subtype, and the art that goes with it. Both are
|
|
355
|
-
// looked up from the note's `type` rather than spelled here
|
|
363
|
+
// looked up from the note's `type` rather than spelled here.
|
|
356
364
|
const subType = /** @type {string} */ (
|
|
357
365
|
documentSubtype(this.constructor.documentSubtypes, fm.type, fm, {
|
|
358
366
|
absPath: this.currentNote?.absPath,
|
|
@@ -365,13 +373,13 @@ export class Actors extends SystemActorCompiler {
|
|
|
365
373
|
const { value: authoredFolder, isAddress } = folderField(fm);
|
|
366
374
|
const folder = this.folderResolver(authoredFolder, { isAddress });
|
|
367
375
|
|
|
368
|
-
// The two retiring positions a declared field may be read from
|
|
369
|
-
//
|
|
376
|
+
// The two retiring positions a declared field may be read from.
|
|
377
|
+
// **Warnings**, on the pattern every retirement in this package
|
|
370
378
|
// follows: the note compiles to the correct document either way, so
|
|
371
379
|
// reddening a tree over one would refuse before the sweep rather than
|
|
372
|
-
// after it. What they buy is a count —
|
|
373
|
-
//
|
|
374
|
-
//
|
|
380
|
+
// after it. What they buy is a count — without one, nothing says which
|
|
381
|
+
// position a value came from, and a default is indistinguishable from
|
|
382
|
+
// a miss.
|
|
375
383
|
const portraitReports = {
|
|
376
384
|
block: SYSTEM,
|
|
377
385
|
onLegacyKey: (field) =>
|
|
@@ -398,25 +406,25 @@ export class Actors extends SystemActorCompiler {
|
|
|
398
406
|
const system = {
|
|
399
407
|
// The frontmatter shortcode is the actor's stable `(type, shortcode)`
|
|
400
408
|
// key — and, for a being that is an archetype, its archetype
|
|
401
|
-
// identity (the dedup/override key of the Create-dialog picker
|
|
409
|
+
// identity (the dedup/override key of the Create-dialog picker).
|
|
402
410
|
shortcode: fm.shortcode || "",
|
|
403
411
|
// Required nullable number: a priority, or `null` for a being that
|
|
404
|
-
// is not a template (
|
|
405
|
-
//
|
|
412
|
+
// is not a template (the archetype contract). `system.archetype`
|
|
413
|
+
// is the legacy spelling; the emitted key moves
|
|
406
414
|
// with the schema, since an undeclared `system` key is discarded at
|
|
407
415
|
// construction without a warning.
|
|
408
416
|
templatePriority: systemTemplatePriority(fm, ctx),
|
|
409
|
-
// Nullish, not
|
|
417
|
+
// Nullish, not `||`: a note that names no portrait gets the
|
|
410
418
|
// subtype's default, one that writes `""` ships blank on purpose.
|
|
411
419
|
// Resolved through the declaration so `data.portrait` is reached at
|
|
412
|
-
// all — see {@link PORTRAIT_FIELD}
|
|
420
|
+
// all — see {@link PORTRAIT_FIELD}.
|
|
413
421
|
portrait: readField(PORTRAIT_FIELD, fm, portraitReports) ?? defaultImg,
|
|
414
422
|
appearance: renderSection(body || "", "appearance"),
|
|
415
423
|
dossier: renderSection(body || "", "dossier"),
|
|
416
424
|
};
|
|
417
425
|
|
|
418
426
|
// Fill `system.body` (+ the base-actor movement fields) from the being's
|
|
419
|
-
// frontmatter, rather than embedding a corpus item
|
|
427
|
+
// frontmatter, rather than embedding a corpus item. The `sohl`
|
|
420
428
|
// block mirrors `system` field-for-field: `sohl.body` nests the body
|
|
421
429
|
// (`structure` / `weight` / …), with `currentMoveMedium` /
|
|
422
430
|
// `movementProfiles` flat alongside it. An **incorporeal** being omits
|
|
@@ -441,14 +449,14 @@ export class Actors extends SystemActorCompiler {
|
|
|
441
449
|
}
|
|
442
450
|
|
|
443
451
|
// Whatever the note authors under `sohl.system`, at the DataModel's own
|
|
444
|
-
// paths
|
|
452
|
+
// paths. This pass has no field declaration, so it claims
|
|
445
453
|
// nothing: every authored path is the author's, and the fields above
|
|
446
454
|
// are what a note that authors none still gets.
|
|
447
455
|
mergeSystemData(system, fm, { block: SYSTEM });
|
|
448
456
|
this.reportUndeclaredSystemData(fm, SYSTEM, "Actor", subType);
|
|
449
457
|
// And what this pass wrote itself. There is no field declaration for a
|
|
450
458
|
// being at all, so *every* key here is a compiler emission — including
|
|
451
|
-
// `templatePriority
|
|
459
|
+
// `templatePriority`, which nothing compared until.
|
|
452
460
|
this.reportEmittedSystemData(system, {
|
|
453
461
|
fm,
|
|
454
462
|
block: SYSTEM,
|
|
@@ -462,7 +470,7 @@ export class Actors extends SystemActorCompiler {
|
|
|
462
470
|
return {
|
|
463
471
|
name,
|
|
464
472
|
type: subType,
|
|
465
|
-
// Nullish, not `||` — see the portrait above
|
|
473
|
+
// Nullish, not `||` — see the portrait above.
|
|
466
474
|
img: resolveImg(blockProperty(fm, SYSTEM, "img")) ?? defaultImg,
|
|
467
475
|
_id: id,
|
|
468
476
|
system,
|
|
@@ -483,7 +491,7 @@ export class Actors extends SystemActorCompiler {
|
|
|
483
491
|
ownership: { default: 0 },
|
|
484
492
|
// Whatever the note authors, and nothing else. The priority used to
|
|
485
493
|
// be spliced in here as `flags.sohl.docArchetype`; it is a schema
|
|
486
|
-
// field now and sits in `system
|
|
494
|
+
// field now and sits in `system`.
|
|
487
495
|
flags: blockProperty(fm, SYSTEM, "flags", {}),
|
|
488
496
|
_stats: this.stats,
|
|
489
497
|
_key: `!actors!${id}`,
|
|
@@ -13,12 +13,12 @@
|
|
|
13
13
|
|
|
14
14
|
/**
|
|
15
15
|
* The affiliation standings an authored `relation` map may use — one
|
|
16
|
-
* affiliation's stance toward another
|
|
16
|
+
* affiliation's stance toward another.
|
|
17
17
|
*
|
|
18
18
|
* Plain ESM for the same reason `./default-item-art.mjs` is: the pack scripts
|
|
19
19
|
* run under bare `node`, so they cannot read the runtime's TypeScript
|
|
20
20
|
* `AFFILIATION_STANDING`. This module is where the pipeline reads the list
|
|
21
|
-
* from, rather than restating it inside a compiler
|
|
21
|
+
* from, rather than restating it inside a compiler.
|
|
22
22
|
*
|
|
23
23
|
* The runtime keeps its own `defineType("SOHL.Affiliation.Standing", { … })`
|
|
24
24
|
* declaration in `src/utils/constants.ts`, because that literal is *parsed out
|
package/sohl/being-info.mjs
CHANGED
|
@@ -24,9 +24,9 @@
|
|
|
24
24
|
* **It lives here because it was living in two places.** Both
|
|
25
25
|
* `Song-of-Heroic-Lands-FoundryVTT` and `sohl-thalorna` carried a copy, and the
|
|
26
26
|
* copies drifted: SoHL's caller still gated the derivation on `character` and
|
|
27
|
-
* `creature`, the two
|
|
27
|
+
* `creature`, the two legacy spellings of `being`, so it had matched
|
|
28
28
|
* nothing since the merge and all 95 of its being pages published with empty
|
|
29
|
-
* sidebar sections
|
|
29
|
+
* sidebar sections. thalorna's copy checked `being` and was right.
|
|
30
30
|
* Nothing failed in either repository; the pages built and shipped.
|
|
31
31
|
*
|
|
32
32
|
* {@link isBeing} exists for that reason. The bug was not in the derivation —
|
|
@@ -36,14 +36,14 @@
|
|
|
36
36
|
* @module
|
|
37
37
|
*/
|
|
38
38
|
|
|
39
|
-
// The retirement window for a renamed note type
|
|
39
|
+
// The retirement window for a renamed note type: an embedded reference
|
|
40
40
|
// still spelling `armorgear` names the same gear group as `armor`.
|
|
41
41
|
import { currentType } from "../engine/ids.mjs";
|
|
42
42
|
|
|
43
43
|
/**
|
|
44
44
|
* The note `type` whose pages carry a being info block.
|
|
45
45
|
*
|
|
46
|
-
* One name
|
|
46
|
+
* One name: `character` and `creature` are the `being` they
|
|
47
47
|
* had always compiled into. The retired names are deliberately **not** accepted
|
|
48
48
|
* as aliases: they throw elsewhere in the system, and tolerating them here
|
|
49
49
|
* would hide the next drift of this kind rather than surface it.
|
|
@@ -68,7 +68,7 @@ export function isBeing(fm) {
|
|
|
68
68
|
* added in a single place rather than in each consumer's site build.
|
|
69
69
|
*
|
|
70
70
|
* Keyed by **note** type, which is what a being's embedded `(type, shortcode)`
|
|
71
|
-
* references spell — and
|
|
71
|
+
* references spell — and three of those are not the document
|
|
72
72
|
* subtype they compile into. A reference still on a renamed spelling is
|
|
73
73
|
* normalised at the lookup below rather than given a second row here.
|
|
74
74
|
*
|
|
@@ -27,15 +27,15 @@
|
|
|
27
27
|
* runtime can both import it. A `.ts` module could not be required by the build
|
|
28
28
|
* scripts (they run under bare `node`, without the bundler that resolves the
|
|
29
29
|
* `@src` alias and strips types). Keeping one map here is what prevents the
|
|
30
|
-
* build-time and runtime defaults from drifting apart (
|
|
30
|
+
* build-time and runtime defaults from drifting apart (which is exactly
|
|
31
31
|
* that drift: the builder had a default, runtime did not).
|
|
32
32
|
*
|
|
33
33
|
* It lives in this package rather than in the system's `src/` tree because the
|
|
34
|
-
* pack pipeline is installed as a dependency
|
|
34
|
+
* pack pipeline is installed as a dependency: a relative path out of
|
|
35
35
|
* the package would resolve to garbage once the pipeline runs from
|
|
36
36
|
* `node_modules`. The runtime imports it back through the package's
|
|
37
|
-
* `./sohl/default-item-art` entry point, so there is still exactly one map
|
|
38
|
-
*
|
|
37
|
+
* `./sohl/default-item-art` entry point, so there is still exactly one map.
|
|
38
|
+
* Injecting the map through configuration instead would re-open that drift,
|
|
39
39
|
* because the two sides would once more be free to disagree.
|
|
40
40
|
*
|
|
41
41
|
* Paths are fully resolved (`systems/sohl/...`) — the served path both layers
|
|
@@ -44,7 +44,7 @@
|
|
|
44
44
|
* **The keys are SoHL *document* subtypes, not markdown note types.** The
|
|
45
45
|
* runtime reads this with `itemData.type`, which is a Foundry Item subtype, and
|
|
46
46
|
* that is the vocabulary a compendium document carries. The two vocabularies
|
|
47
|
-
*
|
|
47
|
+
* would be the same string but for three renamed note types (`armorgear` →
|
|
48
48
|
* `armor`, and its two siblings), and this map stayed on the document side
|
|
49
49
|
* because that is the side the runtime cannot translate: `sohl/item-builders.mjs`
|
|
50
50
|
* asks SoHL's own note-type → subtype map before it looks art up here, so the
|
|
@@ -13,11 +13,11 @@
|
|
|
13
13
|
|
|
14
14
|
/**
|
|
15
15
|
* **SoHL's note-type → document-subtype map** — which Foundry document, and
|
|
16
|
-
* which subtype of it, a note of each content type compiles into
|
|
16
|
+
* which subtype of it, a note of each content type compiles into.
|
|
17
17
|
*
|
|
18
18
|
* The mechanism is `engine/document-subtypes.mjs`; what a *game system* does
|
|
19
19
|
* with it is here, which is the `engine/` ÷ `sohl/` line this package draws
|
|
20
|
-
* everywhere else
|
|
20
|
+
* everywhere else. A second system declares its own map in its own half
|
|
21
21
|
* and shares nothing but the mechanism.
|
|
22
22
|
*
|
|
23
23
|
* **Every row is written out, identity rows included.** `skill → skill` looks
|
|
@@ -31,7 +31,7 @@
|
|
|
31
31
|
* **Three rows are no longer the identity, and that is what the map was for.**
|
|
32
32
|
* `armor`, `concoction` and `projectile` were renamed from the `…gear`
|
|
33
33
|
* spellings that named SoHL's *document* subtype rather than the thing the note
|
|
34
|
-
* is about
|
|
34
|
+
* is about; the subtypes themselves did not move, so those three rows now
|
|
35
35
|
* say two different words where they used to say one twice. Landing the rename
|
|
36
36
|
* cost exactly what having the map first promised it would: a data change on
|
|
37
37
|
* three lines here, and no mechanism change anywhere.
|
|
@@ -40,7 +40,7 @@
|
|
|
40
40
|
* before this map is consulted — so a tree that has not swept its 30,741
|
|
41
41
|
* embedded `(type, shortcode)` references compiles byte-identically. `weapon`
|
|
42
42
|
* is deliberately **not** among them: both systems call that document a
|
|
43
|
-
* `weapongear`, so the name says nothing SoHL-specific and
|
|
43
|
+
* `weapongear`, so the name says nothing SoHL-specific and is left alone.
|
|
44
44
|
*
|
|
45
45
|
* **What this map is not.** It says which document a note becomes, never what
|
|
46
46
|
* that document contains: the `system` block comes from the item registry's
|
|
@@ -59,7 +59,7 @@ import { defineDocumentSubtypes } from "../engine/document-subtypes.mjs";
|
|
|
59
59
|
*
|
|
60
60
|
* The Item rows are the thirteen types the item registry declares; the one
|
|
61
61
|
* Actor row is `being`, which was two types (`character` and `creature`)
|
|
62
|
-
* compiling to the same actor
|
|
62
|
+
* compiling to the same actor before they were retired. Types this
|
|
63
63
|
* map does not name — `doc`, `macro`, the three map types — compile into
|
|
64
64
|
* documents that carry no system subtype at all, so they have no row and never
|
|
65
65
|
* needed one.
|
package/sohl/index.mjs
CHANGED
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
* Lands data model that a generic content module must never receive.
|
|
17
17
|
*
|
|
18
18
|
* The item-type registry and its builders, the items and actors compilers, the
|
|
19
|
-
* default-art map, and the affiliation standings live here
|
|
19
|
+
* default-art map, and the affiliation standings live here. Nothing in
|
|
20
20
|
* `@heroiclands/package-build/engine` exports any of it, so an adventure module
|
|
21
21
|
* that builds journals, macros, and scenes never receives `buildWeaponGear`.
|
|
22
22
|
*
|
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
/** The item-type registry: every type that compiles into an Item, and its builder. */
|
|
34
34
|
export * as itemBuilders from "./item-builders.mjs";
|
|
35
35
|
|
|
36
|
-
/** This system's note-type → document-subtype map, identity rows included
|
|
36
|
+
/** This system's note-type → document-subtype map, identity rows included. */
|
|
37
37
|
export * as documentSubtypes from "./document-subtypes.mjs";
|
|
38
38
|
|
|
39
39
|
/** The Item compiler. */
|
|
@@ -47,7 +47,7 @@ export * as kbPasses from "./kb-passes.mjs";
|
|
|
47
47
|
|
|
48
48
|
// Flat as well as namespaced: the Foundry runtime imports these by name through
|
|
49
49
|
// their own entry points, and they were this barrel's surface before the
|
|
50
|
-
// compilers arrived
|
|
50
|
+
// compilers arrived.
|
|
51
51
|
export { DEFAULT_ITEM_ART, defaultItemArt } from "./default-item-art.mjs";
|
|
52
52
|
export { AFFILIATION_STANDINGS } from "./affiliation-standings.mjs";
|
|
53
53
|
export { BEING_TYPE, GEAR_TYPE_TO_KEY, deriveBeingInfo, isBeing } from "./being-info.mjs";
|
package/sohl/item-builders.mjs
CHANGED
|
@@ -18,20 +18,20 @@
|
|
|
18
18
|
* There is one list, not two. This repository hands {@link ITEM_BUILDERS} to
|
|
19
19
|
* the build as `itemBuilders` in `package-build.config.yaml`, and `itemTypes()`
|
|
20
20
|
* — the whitelist — is derived from that table's own keys, so a type cannot be
|
|
21
|
-
* whitelisted for compilation without a builder to compile it.
|
|
21
|
+
* whitelisted for compilation without a builder to compile it. Split, the
|
|
22
22
|
* whitelist and the builder table were maintained by hand and had already
|
|
23
|
-
* drifted: `trait` —
|
|
23
|
+
* drifted: `trait` — a **retired** item type, absent from
|
|
24
24
|
* `documentTypes.Item` and reported by world migration as unrecognized — was
|
|
25
25
|
* still advertised as compilable, so a `type: trait` note passed the gate and
|
|
26
26
|
* then died on `BUILDERS[type] is not a function`, swallowed as a per-file
|
|
27
|
-
* error
|
|
27
|
+
* error.
|
|
28
28
|
*
|
|
29
29
|
* **The builders are generated, not written.** Each type's `system` builder
|
|
30
30
|
* comes from its field declaration in `item-fields.mjs` by way of
|
|
31
31
|
* {@link buildFromFields}, so the vocabulary a note may write is readable data
|
|
32
32
|
* rather than statements buried in a function body — which is what lets the
|
|
33
|
-
* authoring reference be generated and a note be linted against its type
|
|
34
|
-
*
|
|
33
|
+
* authoring reference be generated and a note be linted against its type.
|
|
34
|
+
* One consequence worth naming: the registry's keys are now
|
|
35
35
|
* {@link ITEM_FIELDS}'s keys, so adding an item type is one edit *there* (plus
|
|
36
36
|
* its `documentTypes.Item` declaration and its default art), and removing one
|
|
37
37
|
* is likewise a single deletion.
|
|
@@ -43,7 +43,7 @@
|
|
|
43
43
|
* own evaluation. The table travels into configuration; the engine's
|
|
44
44
|
* `item-registry.mjs` reads it back out and the Item compiler dispatches
|
|
45
45
|
* through that, which is how a consumer's own table is the one its notes
|
|
46
|
-
* compile with
|
|
46
|
+
* compile with.
|
|
47
47
|
*/
|
|
48
48
|
|
|
49
49
|
import { defaultItemArt } from "./default-item-art.mjs";
|
|
@@ -86,7 +86,7 @@ const FINALIZERS = Object.freeze({
|
|
|
86
86
|
*
|
|
87
87
|
* **The art map is keyed by the document subtype**, which the runtime reads
|
|
88
88
|
* with a Foundry `Item`'s own `type`, so this asks SoHL's map what an `armor`
|
|
89
|
-
* note becomes before looking art up
|
|
89
|
+
* note becomes before looking art up. Translating on the build side is
|
|
90
90
|
* the only place it can happen: the runtime has no note in hand. Every SoHL
|
|
91
91
|
* Item row is one-to-one, so `documentSubtype` needs no frontmatter and cannot
|
|
92
92
|
* throw for a discriminator.
|