@heroiclands/package-build 22.15.1 → 22.16.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 +95 -0
- package/README.md +39 -678
- package/bin/{content-build.mjs → content-commands.mjs} +197 -118
- package/bin/package-build.mjs +303 -42
- package/bin/report.mjs +1 -1
- package/ci/ci-docker.mjs +6 -3
- package/ci/ci-steps.mjs +6 -4
- package/ci/content-format-reference.mjs +106 -0
- package/config.mjs +22 -2
- package/content-config.mjs +74 -134
- package/docs/api.md +294 -234
- package/docs/authoring/assets.md +31 -0
- package/docs/authoring/dates-and-calendars.md +33 -0
- package/docs/authoring/first-note.md +48 -0
- package/docs/authoring/frontmatter.md +40 -0
- package/docs/authoring/links-and-markup.md +137 -0
- package/docs/commands.md +631 -162
- package/docs/configuration.md +176 -140
- package/docs/content-format.md +50 -4229
- package/docs/design.md +32 -0
- package/docs/diagnostics.md +29 -22
- package/docs/getting-started.md +21 -877
- package/docs/guides/book.md +153 -0
- package/docs/guides/e2e.md +143 -0
- package/docs/guides/icons.md +126 -0
- package/docs/guides/localization.md +120 -0
- package/docs/guides/module-project.md +915 -0
- package/docs/guides/packs.md +167 -0
- package/docs/guides/reference-pages.md +148 -0
- package/docs/guides/release-and-deploy.md +114 -0
- package/docs/guides/site.md +152 -0
- package/docs/guides/system-project.md +45 -0
- package/docs/index.md +16 -0
- package/docs/project-setup.md +54 -35
- package/docs/reference/format-details.md +4420 -0
- package/docs/reference/note-types.md +531 -0
- package/engine/address-render.mjs +26 -0
- package/engine/address.mjs +3 -20
- package/engine/anchors.mjs +15 -6
- package/engine/asset-bindings.mjs +6 -2
- package/engine/base-compiler.mjs +47 -11
- package/engine/being-measurements.mjs +70 -0
- package/engine/calendar-artifacts.mjs +1 -1
- package/engine/calendar-choice.mjs +32 -0
- package/engine/calendar-human.mjs +71 -0
- package/engine/calendar-notes.mjs +41 -3
- package/engine/calendars.mjs +8 -1
- package/engine/changelog-lint.mjs +2 -2
- package/engine/code-fences.mjs +23 -97
- package/engine/compile-corpus.mjs +6 -7
- package/engine/content-admonitions.mjs +114 -0
- package/engine/content-captions.mjs +181 -0
- package/engine/content-embeds.mjs +2 -2
- package/engine/content-footnotes.mjs +34 -0
- package/engine/content-format-check.mjs +2 -2
- package/engine/content-format.mjs +127 -73
- package/engine/content-format.yaml +745 -0
- package/engine/content-html.mjs +1 -1
- package/engine/content-icons.mjs +45 -92
- package/engine/content-images.mjs +22 -74
- package/engine/content-index.mjs +12 -3
- package/engine/content-links.mjs +21 -26
- package/engine/content-lint.mjs +1 -5
- package/engine/content-secrets.mjs +3 -1
- package/engine/content-tables.mjs +47 -1257
- package/engine/culture-choice.mjs +32 -0
- package/engine/date-conversion.mjs +47 -0
- package/engine/declaration-check.mjs +87 -0
- package/engine/docs-checks.mjs +140 -0
- package/engine/documentation-links.mjs +31 -0
- package/engine/exported-scene.mjs +1 -1
- package/engine/extension-attributes.mjs +74 -0
- package/engine/field-reference.mjs +1 -1
- package/engine/field-spec.mjs +4 -0
- package/engine/foreign-catalog.mjs +5 -5
- package/engine/format-generated.mjs +15 -0
- package/engine/foundry-entries.mjs +3 -1
- package/engine/frontmatter-lint.mjs +5 -1
- package/engine/frontmatter.mjs +10 -0
- package/engine/generate.mjs +2 -2
- package/engine/helpers.mjs +71 -117
- package/engine/homepage.mjs +2 -3
- package/engine/index-records.mjs +1 -0
- package/engine/index.mjs +18 -3
- package/engine/infobox-registry.mjs +3 -1
- package/engine/infobox.mjs +38 -20
- package/engine/item-compiler.mjs +10 -8
- package/engine/item-docs.mjs +7 -6
- package/engine/journals.mjs +51 -10
- package/engine/macros.mjs +1 -1
- package/engine/map-build.mjs +1 -1
- package/engine/map-dot.mjs +1 -1
- package/engine/markdown-expressions.mjs +117 -0
- package/engine/metadata-index.mjs +7 -3
- package/engine/note-addresses.mjs +1 -0
- package/engine/note-dates.mjs +283 -61
- package/engine/note-package.mjs +5 -25
- package/engine/note-state.mjs +6 -2
- package/engine/note-vocabulary.mjs +74 -19
- package/engine/number-words.mjs +75 -0
- package/engine/office-holders.mjs +188 -0
- package/engine/pack-config.mjs +2 -15
- package/engine/pack-router.mjs +24 -5
- package/engine/packages.mjs +3 -2
- package/engine/pathnames.mjs +21 -24
- package/engine/pdf-build.mjs +217 -55
- package/engine/pdf-fonts.mjs +2 -2
- package/engine/pdf-images.mjs +82 -0
- package/engine/pdf-render.mjs +174 -22
- package/engine/populations.mjs +3 -2
- package/engine/project-init.mjs +499 -0
- package/engine/prose-config.mjs +1 -1
- package/engine/prose-lint.mjs +7 -6
- package/engine/readability-lint.mjs +86 -0
- package/engine/reckoning-markers.mjs +205 -93
- package/engine/scenes.mjs +5 -2
- package/engine/schema-check.mjs +3 -3
- package/engine/site-build.mjs +87 -37
- package/engine/site-config.mjs +8 -8
- package/engine/site-index.mjs +4 -0
- package/engine/site-maps.mjs +1 -1
- package/engine/site-preview.mjs +19 -8
- package/engine/social-ties.mjs +5 -1
- package/engine/sql-tables.mjs +92 -22
- package/engine/web-wikilinks.mjs +15 -31
- package/engine/wikilink-syntax.mjs +4 -1
- package/engine/wikilinks.mjs +12 -14
- package/hm3/item-fields.mjs +1 -1
- package/hm3/items.mjs +12 -45
- package/index.mjs +2 -14
- package/manifest.mjs +31 -4
- package/markdownlint-config.mjs +1 -1
- package/package.json +32 -11
- package/prettier-config.mjs +1 -1
- package/sohl/being-info.mjs +3 -145
- package/sohl/index.mjs +1 -1
- package/sohl/infobox.mjs +2 -1
- package/sohl/item-builders.mjs +3 -27
- package/sohl/item-fields.mjs +61 -22
- package/sohl/kb-passes.mjs +2 -2
- package/sohl/note-schemas.mjs +0 -8
- package/stage.mjs +19 -0
- package/types/config.d.mts +12 -3
- package/types/content-config.d.mts +15 -81
- package/types/engine/address-render.d.mts +13 -0
- package/types/engine/address.d.mts +2 -11
- package/types/engine/anchors.d.mts +3 -4
- package/types/engine/asset-bindings.d.mts +9 -2
- package/types/engine/base-compiler.d.mts +1 -2
- package/types/engine/being-measurements.d.mts +16 -0
- package/types/engine/calendar-choice.d.mts +14 -0
- package/types/engine/calendar-human.d.mts +4 -0
- package/types/engine/calendar-notes.d.mts +2 -0
- package/types/engine/calendars.d.mts +5 -3
- package/types/engine/code-fences.d.mts +6 -72
- package/types/engine/compile-corpus.d.mts +1 -3
- package/types/engine/content-admonitions.d.mts +24 -0
- package/types/engine/content-captions.d.mts +45 -0
- package/types/engine/content-footnotes.d.mts +10 -0
- package/types/engine/content-format-check.d.mts +17 -4
- package/types/engine/content-format.d.mts +24 -11
- package/types/engine/content-icons.d.mts +14 -23
- package/types/engine/content-images.d.mts +2 -2
- package/types/engine/content-index.d.mts +9 -2
- package/types/engine/content-links.d.mts +3 -5
- package/types/engine/content-tables.d.mts +11 -140
- package/types/engine/culture-choice.d.mts +14 -0
- package/types/engine/date-conversion.d.mts +4 -0
- package/types/engine/declaration-check.d.mts +24 -0
- package/types/engine/docs-checks.d.mts +28 -0
- package/types/engine/documentation-links.d.mts +7 -0
- package/types/engine/exported-scene.d.mts +6 -2
- package/types/engine/extension-attributes.d.mts +14 -0
- package/types/engine/field-spec.d.mts +9 -0
- package/types/engine/format-generated.d.mts +7 -0
- package/types/engine/frontmatter.d.mts +14 -4
- package/types/engine/generate.d.mts +1 -1
- package/types/engine/helpers.d.mts +37 -92
- package/types/engine/homepage.d.mts +2 -3
- package/types/engine/index.d.mts +5 -0
- package/types/engine/infobox-registry.d.mts +3 -1
- package/types/engine/infobox.d.mts +17 -6
- package/types/engine/item-compiler.d.mts +4 -4
- package/types/engine/item-docs.d.mts +4 -3
- package/types/engine/journals.d.mts +10 -7
- package/types/engine/map-graphviz.d.mts +1 -1
- package/types/engine/markdown-expressions.d.mts +17 -0
- package/types/engine/note-dates.d.mts +47 -1
- package/types/engine/note-package.d.mts +3 -19
- package/types/engine/note-state.d.mts +2 -1
- package/types/engine/note-vocabulary.d.mts +9 -0
- package/types/engine/number-words.d.mts +4 -0
- package/types/engine/office-holders.d.mts +14 -0
- package/types/engine/pack-config.d.mts +1 -1
- package/types/engine/packages.d.mts +3 -2
- package/types/engine/pdf-images.d.mts +38 -0
- package/types/engine/populations.d.mts +2 -2
- package/types/engine/project-init.d.mts +17 -0
- package/types/engine/prose-config.d.mts +1 -1
- package/types/engine/prose-lint.d.mts +3 -2
- package/types/engine/readability-lint.d.mts +22 -0
- package/types/engine/reckoning-markers.d.mts +4 -2
- package/types/engine/schema-check.d.mts +3 -3
- package/types/engine/site-build.d.mts +38 -28
- package/types/engine/site-config.d.mts +2 -2
- package/types/engine/site-index.d.mts +4 -0
- package/types/engine/site-preview.d.mts +1 -1
- package/types/engine/social-ties.d.mts +8 -2
- package/types/engine/sql-tables.d.mts +13 -2
- package/types/engine/web-wikilinks.d.mts +2 -1
- package/types/engine/wikilinks.d.mts +7 -1
- package/types/hm3/items.d.mts +5 -6
- package/types/labels.d.mts +40 -0
- package/types/manifest.d.mts +3 -3
- package/types/sohl/actors.d.mts +2 -2
- package/types/sohl/being-info.d.mts +3 -43
- package/types/sohl/index.d.mts +1 -1
- package/types/sohl/infobox.d.mts +5 -2
- package/types/sohl/item-fields.d.mts +4 -13
- package/types/stage.d.mts +9 -0
- package/CONTENT.md +0 -3239
package/markdownlint-config.mjs
CHANGED
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
/**
|
|
15
15
|
* The shared markdownlint rules, as a markdownlint-cli2 options module.
|
|
16
16
|
*
|
|
17
|
-
* `
|
|
17
|
+
* `package-build markdown` applies these without a consumer declaring
|
|
18
18
|
* anything; this is for an editor's markdownlint extension, and for a consumer
|
|
19
19
|
* that wants to *extend* the set rather than replace it:
|
|
20
20
|
*
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@heroiclands/package-build",
|
|
3
|
-
"version": "22.
|
|
3
|
+
"version": "22.16.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",
|
|
@@ -95,13 +95,11 @@
|
|
|
95
95
|
"./package.json": "./package.json"
|
|
96
96
|
},
|
|
97
97
|
"bin": {
|
|
98
|
-
"package-build": "./bin/package-build.mjs"
|
|
99
|
-
"content-build": "./bin/content-build.mjs"
|
|
98
|
+
"package-build": "./bin/package-build.mjs"
|
|
100
99
|
},
|
|
101
100
|
"files": [
|
|
102
101
|
"CHANGELOG-content-build.md",
|
|
103
102
|
"CHANGELOG.md",
|
|
104
|
-
"CONTENT.md",
|
|
105
103
|
"README.md",
|
|
106
104
|
"assets",
|
|
107
105
|
"packagebuild-metadata.jsonl",
|
|
@@ -149,18 +147,30 @@
|
|
|
149
147
|
"loglevel": "^1.9.2",
|
|
150
148
|
"loglevel-plugin-prefix": "^0.8.4",
|
|
151
149
|
"markdown-it": "^15.0.0",
|
|
150
|
+
"markdown-it-deflist": "^4.0.0",
|
|
151
|
+
"markdown-it-footnote": "^4.0.0",
|
|
152
152
|
"markdownlint-cli2": "^0.23.3",
|
|
153
153
|
"pagefind": "^1.5.2",
|
|
154
154
|
"prettier": "^3.9.6",
|
|
155
|
+
"remark-frontmatter": "^5.0.0",
|
|
156
|
+
"remark-parse": "^11.0.0",
|
|
157
|
+
"remark-retext": "^6.0.1",
|
|
158
|
+
"retext-english": "^5.0.0",
|
|
159
|
+
"retext-readability": "^8.0.0",
|
|
160
|
+
"retext-simplify": "^8.0.0",
|
|
161
|
+
"retext-stringify": "^4.0.0",
|
|
162
|
+
"sharp": "^0.35.4",
|
|
155
163
|
"smol-toml": "^1.8.0",
|
|
156
164
|
"ssh2-sftp-client": "^12.1.1",
|
|
157
165
|
"typescript": "^6.0.3",
|
|
158
166
|
"unidecode": "^1.1.0",
|
|
167
|
+
"unified": "^11.0.5",
|
|
159
168
|
"yaml": "^2.8.1",
|
|
160
169
|
"yargs": "^18.1.0"
|
|
161
170
|
},
|
|
162
171
|
"devDependencies": {
|
|
163
172
|
"@changesets/cli": "^3.0.0",
|
|
173
|
+
"@heroiclands/hugo-theme": "^0.9.0",
|
|
164
174
|
"@types/node": "^26.2.0",
|
|
165
175
|
"npm-run-all": "^4.1.5",
|
|
166
176
|
"vitest": "^5.0.0"
|
|
@@ -174,16 +184,26 @@
|
|
|
174
184
|
"format": "prettier --write .",
|
|
175
185
|
"format:check": "prettier --check .",
|
|
176
186
|
"lint": "run-p --aggregate-output -c --max-parallel 4 format:check lint:markdown lint:yaml lint:labels lint:content-format",
|
|
177
|
-
"lint:markdown": "node bin/
|
|
187
|
+
"lint:markdown": "node bin/package-build.mjs markdown",
|
|
178
188
|
"lint:yaml": "node bin/package-build.mjs yaml",
|
|
179
189
|
"lint:labels": "node bin/package-build.mjs labels check",
|
|
180
|
-
"lint:markdown:fix": "node bin/
|
|
181
|
-
"lint:content-format": "npm run lint:content-format:schema && npm run lint:content-format:fields",
|
|
182
|
-
"lint:content-format:schema": "node bin/
|
|
183
|
-
"lint:content-format:fields": "node bin/
|
|
190
|
+
"lint:markdown:fix": "node bin/package-build.mjs markdown --fix",
|
|
191
|
+
"lint:content-format": "npm run lint:content-format:schema && npm run lint:content-format:fields && node ci/content-format-reference.mjs --check",
|
|
192
|
+
"lint:content-format:schema": "node bin/package-build.mjs content-format schema --schema sohl=tests/fixtures/content-format/schema-sohl.json --schema hm3=tests/fixtures/content-format/schema-hm3.json",
|
|
193
|
+
"lint:content-format:fields": "node bin/package-build.mjs content-format fields --fields sohl && node bin/package-build.mjs content-format fields --fields hm3",
|
|
184
194
|
"changeset": "changeset",
|
|
185
195
|
"changeset:version": "changeset version && node bin/package-build.mjs changelog group && npm install --package-lock-only",
|
|
186
|
-
"prepare": "git config core.hooksPath .githooks || true"
|
|
196
|
+
"prepare": "git config core.hooksPath .githooks || true",
|
|
197
|
+
"build:deps": "node bin/package-build.mjs deps fetch",
|
|
198
|
+
"build:content-index": "node bin/package-build.mjs content-index",
|
|
199
|
+
"build:site-content": "node bin/package-build.mjs site",
|
|
200
|
+
"build:site-html": "hugo --source build/hugo --minify --gc --cleanDestinationDir",
|
|
201
|
+
"build:site-root": "node bin/package-build.mjs site-root",
|
|
202
|
+
"build:site": "run-s build:deps build:content-index build:site-content build:site-html build:site-root",
|
|
203
|
+
"build:book": "run-s build:deps build:content-index build:book-content",
|
|
204
|
+
"build:book-content": "node bin/package-build.mjs pdf",
|
|
205
|
+
"serve:site": "run-s build:deps build:content-index build:site-content serve:site-html",
|
|
206
|
+
"serve:site-html": "hugo server --source build/hugo"
|
|
187
207
|
},
|
|
188
208
|
"engines": {
|
|
189
209
|
"node": ">=24.0.0"
|
|
@@ -204,5 +224,6 @@
|
|
|
204
224
|
"heroiclands",
|
|
205
225
|
"sohl",
|
|
206
226
|
"song-of-heroic-lands"
|
|
207
|
-
]
|
|
227
|
+
],
|
|
228
|
+
"homepage": "https://github.com/HeroicLands/package-build#documentation"
|
|
208
229
|
}
|
package/prettier-config.mjs
CHANGED
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
/**
|
|
15
15
|
* The shared Prettier configuration, as a Prettier config module.
|
|
16
16
|
*
|
|
17
|
-
* `
|
|
17
|
+
* `package-build format` already applies these rules without a consumer
|
|
18
18
|
* declaring anything, so this exists for the **editor**: a format-on-save that
|
|
19
19
|
* disagrees with the lint chain is a papercut every contributor hits, and
|
|
20
20
|
* Prettier's editor integrations read a config file rather than asking this
|
package/sohl/being-info.mjs
CHANGED
|
@@ -11,42 +11,10 @@
|
|
|
11
11
|
* SPDX-License-Identifier: GPL-3.0-or-later
|
|
12
12
|
*/
|
|
13
13
|
|
|
14
|
-
/**
|
|
15
|
-
* **A being's info-block fields**, derived from the items it embeds.
|
|
16
|
-
*
|
|
17
|
-
* A `being` note carries its embedded documents as `sohl.items` — a flat list
|
|
18
|
-
* of `{ shortcode, type, system? }` — but the shared theme's sidebar reads
|
|
19
|
-
* *resolved* shapes: a `skills` map, `gear` grouped by kind, and `spells` /
|
|
20
|
-
* `talents` split out of the mystical abilities. This is the translation
|
|
21
|
-
* between the two, and it is SoHL data-model knowledge: which item type is a
|
|
22
|
-
* skill, where a mastery level lives, what distinguishes a spell from a talent.
|
|
23
|
-
*
|
|
24
|
-
* **It lives here because it was living in two places.** Both
|
|
25
|
-
* `Song-of-Heroic-Lands-FoundryVTT` and `sohl-thalorna` carried a copy, and the
|
|
26
|
-
* copies drifted: SoHL's caller still gated the derivation on `character` and
|
|
27
|
-
* `creature`, the two legacy spellings of `being`, so it had matched
|
|
28
|
-
* nothing since the merge and all 95 of its being pages published with empty
|
|
29
|
-
* sidebar sections. thalorna's copy checked `being` and was right.
|
|
30
|
-
* Nothing failed in either repository; the pages built and shipped.
|
|
31
|
-
*
|
|
32
|
-
* {@link isBeing} exists for that reason. The bug was not in the derivation —
|
|
33
|
-
* it was in each caller's idea of what a being *is*, written out per repository
|
|
34
|
-
* where it could rot independently. One definition, imported.
|
|
35
|
-
*
|
|
36
|
-
* @module
|
|
37
|
-
*/
|
|
38
|
-
|
|
39
|
-
// The retirement window for a renamed note type: an embedded reference
|
|
40
|
-
// still spelling `armorgear` names the same gear group as `armor`.
|
|
41
|
-
import { currentType } from "../engine/ids.mjs";
|
|
14
|
+
/** Shared being type and gear groups used by the SoHL infobox. @module */
|
|
42
15
|
|
|
43
16
|
/**
|
|
44
|
-
* The note
|
|
45
|
-
*
|
|
46
|
-
* One name: `character` and `creature` are the `being` they
|
|
47
|
-
* had always compiled into. The retired names are deliberately **not** accepted
|
|
48
|
-
* as aliases: they throw elsewhere in the system, and tolerating them here
|
|
49
|
-
* would hide the next drift of this kind rather than surface it.
|
|
17
|
+
* The note type for a being.
|
|
50
18
|
*/
|
|
51
19
|
export const BEING_TYPE = "being";
|
|
52
20
|
|
|
@@ -61,16 +29,7 @@ export function isBeing(fm) {
|
|
|
61
29
|
}
|
|
62
30
|
|
|
63
31
|
/**
|
|
64
|
-
* The
|
|
65
|
-
*
|
|
66
|
-
* Presentation naming, not data-model naming: the note type says `weapongear`,
|
|
67
|
-
* the sidebar heading says "weapons". Kept as one table so a new gear type is
|
|
68
|
-
* added in a single place rather than in each consumer's site build.
|
|
69
|
-
*
|
|
70
|
-
* Keyed by **note** type, which is what a being's embedded `(type, shortcode)`
|
|
71
|
-
* references spell — and three of those are not the document
|
|
72
|
-
* subtype they compile into. A reference still on a renamed spelling is
|
|
73
|
-
* normalised at the lookup below rather than given a second row here.
|
|
32
|
+
* The infobox group displayed for each gear note type.
|
|
74
33
|
*
|
|
75
34
|
* @type {Readonly<Record<string, string>>}
|
|
76
35
|
*/
|
|
@@ -82,104 +41,3 @@ export const GEAR_TYPE_TO_KEY = Object.freeze({
|
|
|
82
41
|
containergear: "containers",
|
|
83
42
|
concoctiongear: "concoctions",
|
|
84
43
|
});
|
|
85
|
-
|
|
86
|
-
/** Whether a value is a plain mapping. */
|
|
87
|
-
const isMap = (v) => Boolean(v) && typeof v === "object" && !Array.isArray(v);
|
|
88
|
-
|
|
89
|
-
/** Whether a value is a non-empty array. */
|
|
90
|
-
const nonEmpty = (v) => Array.isArray(v) && v.length > 0;
|
|
91
|
-
|
|
92
|
-
/**
|
|
93
|
-
* Derive a being's info-block fields from its raw `sohl.items[]`.
|
|
94
|
-
*
|
|
95
|
-
* Each item's `shortcode` is resolved against `index` — keyed
|
|
96
|
-
* `"<type>:<shortcode>"` — for a display name and a link to the item's own
|
|
97
|
-
* page. `attributes` already match the sidebar shape and pass through
|
|
98
|
-
* untouched.
|
|
99
|
-
*
|
|
100
|
-
* **Authored values win.** Only fields the author did not supply are derived,
|
|
101
|
-
* so a note that hand-writes `sohl.skills` keeps exactly what it wrote. An
|
|
102
|
-
* item's inline `name` beats the index, and an unresolved shortcode falls back
|
|
103
|
-
* to *itself* rather than being dropped — a page that names an item the index
|
|
104
|
-
* has not heard of is better than a page silently missing a row.
|
|
105
|
-
*
|
|
106
|
-
* Returns a new object; the input is not mutated.
|
|
107
|
-
*
|
|
108
|
-
* @param {object|null|undefined} sohl - The note's `sohl` frontmatter block.
|
|
109
|
-
* @param {Map<string, {name?: string, url?: string}>} index - Content index,
|
|
110
|
-
* `"<type>:<shortcode>"` → the item's page.
|
|
111
|
-
* @returns {object|null} The block with its info-block fields filled in, or the
|
|
112
|
-
* input unchanged when there is nothing to derive from — with an absent block
|
|
113
|
-
* reported as `null`, the value an empty one already carries.
|
|
114
|
-
*/
|
|
115
|
-
export function deriveBeingInfo(sohl, index) {
|
|
116
|
-
// A note declaring no `sohl:` key at all arrives as `undefined`, and the
|
|
117
|
-
// site emitter assigns this result straight into a page's front matter.
|
|
118
|
-
// js-yaml refuses to dump a property whose value is `undefined`, and the
|
|
119
|
-
// throw aborts the whole build rather than the one page — so "no block"
|
|
120
|
-
// is answered with the same `null` an empty block gets.
|
|
121
|
-
if (!isMap(sohl)) return sohl ?? null;
|
|
122
|
-
const out = { ...sohl };
|
|
123
|
-
const items = Array.isArray(out.items) ? out.items : [];
|
|
124
|
-
if (items.length === 0) return out;
|
|
125
|
-
|
|
126
|
-
const lookup = (type, shortcode) => (shortcode ? index.get(`${type}:${shortcode}`) : undefined);
|
|
127
|
-
|
|
128
|
-
/** An item's display name: its own, then the index's, then its shortcode. */
|
|
129
|
-
const displayName = (it, ref, shortcode) =>
|
|
130
|
-
(typeof it.name === "string" && it.name) || ref?.name || shortcode;
|
|
131
|
-
|
|
132
|
-
// Skills: { shortcode: masteryLevelBase }.
|
|
133
|
-
if (!(isMap(out.skills) && Object.keys(out.skills).length > 0)) {
|
|
134
|
-
const skills = {};
|
|
135
|
-
for (const it of items) {
|
|
136
|
-
if (!isMap(it) || it.type !== "skill") continue;
|
|
137
|
-
const level = it.system?.masteryLevelBase;
|
|
138
|
-
if (typeof it.shortcode === "string" && typeof level === "number") {
|
|
139
|
-
skills[it.shortcode] = level;
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
if (Object.keys(skills).length > 0) out.skills = skills;
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
// Gear: { weapons: [{ name, shortcode?, url? }], armor: [...], … }.
|
|
146
|
-
if (!isMap(out.gear)) {
|
|
147
|
-
const gear = {};
|
|
148
|
-
for (const it of items) {
|
|
149
|
-
if (!isMap(it)) continue;
|
|
150
|
-
const key = GEAR_TYPE_TO_KEY[currentType(it.type)];
|
|
151
|
-
if (!key) continue;
|
|
152
|
-
const shortcode = typeof it.shortcode === "string" ? it.shortcode : undefined;
|
|
153
|
-
const ref = lookup(it.type, shortcode);
|
|
154
|
-
const name = displayName(it, ref, shortcode);
|
|
155
|
-
if (!name) continue;
|
|
156
|
-
const entry = { name };
|
|
157
|
-
if (shortcode) entry.shortcode = shortcode;
|
|
158
|
-
if (ref?.url) entry.url = ref.url;
|
|
159
|
-
(gear[key] ??= []).push(entry);
|
|
160
|
-
}
|
|
161
|
-
if (Object.keys(gear).length > 0) out.gear = gear;
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
// Mystical abilities, split by subType into spells / talents.
|
|
165
|
-
const spells = [];
|
|
166
|
-
const talents = [];
|
|
167
|
-
for (const it of items) {
|
|
168
|
-
if (!isMap(it) || it.type !== "mysticalability") continue;
|
|
169
|
-
const shortcode = typeof it.shortcode === "string" ? it.shortcode : undefined;
|
|
170
|
-
const ref = lookup("mysticalability", shortcode);
|
|
171
|
-
// No shortcode fallback here: an ability with neither an inline name
|
|
172
|
-
// nor an index entry has nothing to show, and a row reading like a
|
|
173
|
-
// shortcode is worse than no row.
|
|
174
|
-
const name = (typeof it.name === "string" && it.name) || ref?.name;
|
|
175
|
-
if (!name) continue;
|
|
176
|
-
const entry = { name };
|
|
177
|
-
if (ref?.url) entry.url = ref.url;
|
|
178
|
-
if (it.subType === "arcaneincantation") spells.push(entry);
|
|
179
|
-
else if (it.subType === "arcanetalent") talents.push(entry);
|
|
180
|
-
}
|
|
181
|
-
if (spells.length > 0 && !nonEmpty(out.spells)) out.spells = spells;
|
|
182
|
-
if (talents.length > 0 && !nonEmpty(out.talents)) out.talents = talents;
|
|
183
|
-
|
|
184
|
-
return out;
|
|
185
|
-
}
|
package/sohl/index.mjs
CHANGED
|
@@ -53,4 +53,4 @@ export * as infobox from "./infobox.mjs";
|
|
|
53
53
|
// compilers arrived.
|
|
54
54
|
export { DEFAULT_ITEM_ART, defaultItemArt } from "./default-item-art.mjs";
|
|
55
55
|
export { AFFILIATION_STANDINGS } from "./affiliation-standings.mjs";
|
|
56
|
-
export { BEING_TYPE, GEAR_TYPE_TO_KEY,
|
|
56
|
+
export { BEING_TYPE, GEAR_TYPE_TO_KEY, isBeing } from "./being-info.mjs";
|
package/sohl/infobox.mjs
CHANGED
|
@@ -148,6 +148,7 @@ export const SOHL_FIELD_PRESENTATION = Object.freeze({
|
|
|
148
148
|
improveFlag: Object.freeze({
|
|
149
149
|
withheld: "character-sheet machinery — whether the item is flagged for improvement",
|
|
150
150
|
}),
|
|
151
|
+
"trauma.treatmentModifierBase": Object.freeze({ label: "Treatment modifier" }),
|
|
151
152
|
facing: Object.freeze({
|
|
152
153
|
withheld: "a body-location layout, which has no summary shape",
|
|
153
154
|
}),
|
|
@@ -312,7 +313,7 @@ function itemValue(item, resolve, block) {
|
|
|
312
313
|
* sparse creature's box short rather than mostly empty.
|
|
313
314
|
*
|
|
314
315
|
* @param {object} fm - The note's frontmatter.
|
|
315
|
-
* @param {
|
|
316
|
+
* @param {{block: string, resolve?: Function}} ctx - Rendering context.
|
|
316
317
|
* @returns {object[]} The sections.
|
|
317
318
|
*/
|
|
318
319
|
export function beingSections(fm, { block, resolve }) {
|
package/sohl/item-builders.mjs
CHANGED
|
@@ -49,31 +49,8 @@
|
|
|
49
49
|
import { defaultItemArt } from "./default-item-art.mjs";
|
|
50
50
|
import { documentSubtype } from "../engine/document-subtypes.mjs";
|
|
51
51
|
import { SOHL_DOCUMENT_SUBTYPES } from "./document-subtypes.mjs";
|
|
52
|
-
import { buildFromFields
|
|
53
|
-
import {
|
|
54
|
-
|
|
55
|
-
/**
|
|
56
|
-
* The conditionals a flat field list cannot state, keyed by item type.
|
|
57
|
-
*
|
|
58
|
-
* A finalizer runs after the declaration has produced the `system` block, and
|
|
59
|
-
* exists only for a field whose *presence* depends on another field's value.
|
|
60
|
-
* There is exactly one: a combat technique is authored as a `skill` of subtype
|
|
61
|
-
* `combattechnique`, and carries a strike mode that no other skill has.
|
|
62
|
-
*
|
|
63
|
-
* Keeping this list short is the point. Anything expressible as a field
|
|
64
|
-
* declaration is said there, where a generator and a linter can read it; a
|
|
65
|
-
* finalizer is opaque again, so it earns its place one conditional at a time.
|
|
66
|
-
*
|
|
67
|
-
* @type {Readonly<Record<string, (fm: object, out: object) => object>>}
|
|
68
|
-
*/
|
|
69
|
-
const FINALIZERS = Object.freeze({
|
|
70
|
-
skill(fm, out) {
|
|
71
|
-
if (out.subType === "combattechnique") {
|
|
72
|
-
out.strikeMode = readField(COMBAT_TECHNIQUE_STRIKE_MODE, fm);
|
|
73
|
-
}
|
|
74
|
-
return out;
|
|
75
|
-
},
|
|
76
|
-
});
|
|
52
|
+
import { buildFromFields } from "../engine/field-spec.mjs";
|
|
53
|
+
import { ITEM_FIELDS } from "./item-fields.mjs";
|
|
77
54
|
|
|
78
55
|
/**
|
|
79
56
|
* Build one registry entry: the declared fields, the builder they generate, and
|
|
@@ -102,9 +79,8 @@ const FINALIZERS = Object.freeze({
|
|
|
102
79
|
function entryFor(type) {
|
|
103
80
|
const fields = ITEM_FIELDS[type];
|
|
104
81
|
const build = buildFromFields(fields);
|
|
105
|
-
const finalize = FINALIZERS[type];
|
|
106
82
|
return Object.freeze({
|
|
107
|
-
system:
|
|
83
|
+
system: build,
|
|
108
84
|
img: defaultItemArt(documentSubtype(SOHL_DOCUMENT_SUBTYPES, type, {}) ?? type),
|
|
109
85
|
fields,
|
|
110
86
|
});
|
package/sohl/item-fields.mjs
CHANGED
|
@@ -336,6 +336,18 @@ const GEAR_COMMON = Object.freeze([
|
|
|
336
336
|
/* Per-type declarations */
|
|
337
337
|
/* --------------------------------------------------------------------- */
|
|
338
338
|
|
|
339
|
+
/** The strike mode required for combat techniques and omitted for other skills.
|
|
340
|
+
* @type {import("../engine/field-spec.mjs").FieldSpec}
|
|
341
|
+
*/
|
|
342
|
+
export const COMBAT_TECHNIQUE_STRIKE_MODE = Object.freeze({
|
|
343
|
+
name: "strikeMode",
|
|
344
|
+
to: "strikeMode",
|
|
345
|
+
...STRIKE_MODE,
|
|
346
|
+
required: true,
|
|
347
|
+
when: (_fm, out) => out.subType === "combattechnique",
|
|
348
|
+
describe: "The strike mode the combat technique trains. Required for combat techniques only.",
|
|
349
|
+
});
|
|
350
|
+
|
|
339
351
|
/**
|
|
340
352
|
* Every item type's frontmatter vocabulary, in the order the `system` block
|
|
341
353
|
* emits it.
|
|
@@ -513,6 +525,21 @@ export const ITEM_FIELDS = Object.freeze({
|
|
|
513
525
|
describe:
|
|
514
526
|
"Days from contracting to onset, rolled by the receiving actor. Unset means no incubation.",
|
|
515
527
|
},
|
|
528
|
+
{
|
|
529
|
+
name: "onsetMacroUuid",
|
|
530
|
+
to: "onsetMacroUuid",
|
|
531
|
+
...BLANK_IS_NULL,
|
|
532
|
+
default: null,
|
|
533
|
+
describe: "UUID of the macro run when symptoms begin, if any.",
|
|
534
|
+
},
|
|
535
|
+
{
|
|
536
|
+
name: "data.outcomeTraumas",
|
|
537
|
+
legacyKey: "outcomeTraumas",
|
|
538
|
+
to: "outcomeTraumas",
|
|
539
|
+
...BLANK_IS_NULL,
|
|
540
|
+
default: null,
|
|
541
|
+
describe: "SafeExpression selecting traumas caused by the affliction's outcome.",
|
|
542
|
+
},
|
|
516
543
|
{
|
|
517
544
|
name: "data.outcome",
|
|
518
545
|
legacyKey: "outcome",
|
|
@@ -630,6 +657,11 @@ export const ITEM_FIELDS = Object.freeze({
|
|
|
630
657
|
|
|
631
658
|
armorgear: Object.freeze([
|
|
632
659
|
...GEAR_COMMON,
|
|
660
|
+
{
|
|
661
|
+
to: "isWorn",
|
|
662
|
+
runtimeOnly: "whether the armor is currently worn by an actor",
|
|
663
|
+
describe: "Worn state set during play, never authored.",
|
|
664
|
+
},
|
|
633
665
|
{
|
|
634
666
|
name: "material",
|
|
635
667
|
to: "material",
|
|
@@ -973,6 +1005,13 @@ export const ITEM_FIELDS = Object.freeze({
|
|
|
973
1005
|
default: "",
|
|
974
1006
|
describe: "Shortcode of the skill this one specialises, for a specialisation.",
|
|
975
1007
|
},
|
|
1008
|
+
{
|
|
1009
|
+
name: "adoptParentMasteryLevel",
|
|
1010
|
+
to: "adoptParentMasteryLevel",
|
|
1011
|
+
...BOOLEAN,
|
|
1012
|
+
default: false,
|
|
1013
|
+
describe: "Whether a specialization uses its parent's mastery level.",
|
|
1014
|
+
},
|
|
976
1015
|
{
|
|
977
1016
|
name: "initSkillMult",
|
|
978
1017
|
to: "initSkillMult",
|
|
@@ -987,6 +1026,7 @@ export const ITEM_FIELDS = Object.freeze({
|
|
|
987
1026
|
default: [],
|
|
988
1027
|
describe: "Body-part roles whose impairment penalises tests against this skill.",
|
|
989
1028
|
},
|
|
1029
|
+
COMBAT_TECHNIQUE_STRIKE_MODE,
|
|
990
1030
|
]),
|
|
991
1031
|
|
|
992
1032
|
trauma: Object.freeze([
|
|
@@ -1018,6 +1058,27 @@ export const ITEM_FIELDS = Object.freeze({
|
|
|
1018
1058
|
default: 0,
|
|
1019
1059
|
describe: "How readily it heals.",
|
|
1020
1060
|
},
|
|
1061
|
+
{
|
|
1062
|
+
name: "treatmentModifierBase",
|
|
1063
|
+
to: "treatmentModifierBase",
|
|
1064
|
+
...NULLABLE_NUMBER,
|
|
1065
|
+
default: null,
|
|
1066
|
+
describe: "Modifier to treatment tests; unset means no modifier.",
|
|
1067
|
+
},
|
|
1068
|
+
{
|
|
1069
|
+
name: "permanentImpairmentEligible",
|
|
1070
|
+
to: "permanentImpairmentEligible",
|
|
1071
|
+
...BOOLEAN,
|
|
1072
|
+
default: false,
|
|
1073
|
+
describe: "Whether healing may leave a permanent impairment.",
|
|
1074
|
+
},
|
|
1075
|
+
{
|
|
1076
|
+
name: "infectable",
|
|
1077
|
+
to: "infectable",
|
|
1078
|
+
...BOOLEAN,
|
|
1079
|
+
default: false,
|
|
1080
|
+
describe: "Whether a failed healing test can cause infection.",
|
|
1081
|
+
},
|
|
1021
1082
|
{
|
|
1022
1083
|
name: "aspect",
|
|
1023
1084
|
to: "aspect",
|
|
@@ -1136,25 +1197,3 @@ export const ITEM_FIELDS = Object.freeze({
|
|
|
1136
1197
|
},
|
|
1137
1198
|
]),
|
|
1138
1199
|
});
|
|
1139
|
-
|
|
1140
|
-
/**
|
|
1141
|
-
* The one conditional field in the vocabulary: a combat technique's strike
|
|
1142
|
-
* mode.
|
|
1143
|
-
*
|
|
1144
|
-
* A combat technique is authored as a `skill` of subtype `combattechnique` —
|
|
1145
|
-
* the standalone item type was merged into Skill — and carries an embedded,
|
|
1146
|
-
* discriminated strike mode. It is mandatory for that subtype and absent from
|
|
1147
|
-
* every other skill, which is a conditional a flat field list cannot state, so
|
|
1148
|
-
* it is applied after the declaration runs.
|
|
1149
|
-
*
|
|
1150
|
-
* @type {import("../engine/field-spec.mjs").FieldSpec}
|
|
1151
|
-
*/
|
|
1152
|
-
export const COMBAT_TECHNIQUE_STRIKE_MODE = Object.freeze({
|
|
1153
|
-
name: "strikeMode",
|
|
1154
|
-
to: "strikeMode",
|
|
1155
|
-
...STRIKE_MODE,
|
|
1156
|
-
required: true,
|
|
1157
|
-
default: null,
|
|
1158
|
-
describe:
|
|
1159
|
-
"The strike mode the technique trains. Required on a `combattechnique` skill, and set on no other.",
|
|
1160
|
-
});
|
package/sohl/kb-passes.mjs
CHANGED
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
/**
|
|
15
15
|
* The `sohl` knowledgebase's own body pass.
|
|
16
16
|
*
|
|
17
|
-
* `
|
|
17
|
+
* `package-build site` publishes a content tree as a website, and almost all of
|
|
18
18
|
* that job is the same for every package. This rewrite is not: it is driven by
|
|
19
19
|
* a TypeDoc symbol map only this package has, and it is ruled explicitly
|
|
20
20
|
* per-consumer.
|
|
@@ -60,7 +60,7 @@ import log from "loglevel";
|
|
|
60
60
|
*
|
|
61
61
|
* The path is resolved against the **repository root**, never the process cwd.
|
|
62
62
|
* `site.passOptions.symbolMap` is authored repo-relative, so a cwd-relative
|
|
63
|
-
* read misses the moment `
|
|
63
|
+
* read misses the moment `package-build site` is driven from anywhere but the
|
|
64
64
|
* repository root — which is exactly how the end-to-end verification, running
|
|
65
65
|
* through `PACKAGE_BUILD_CONFIG` from outside the tree, found this.
|
|
66
66
|
*
|
package/sohl/note-schemas.mjs
CHANGED
|
@@ -295,14 +295,6 @@ const PRESENTATION_FIELDS = Object.freeze({
|
|
|
295
295
|
describe: "Descriptive traits, read by the publishing sites.",
|
|
296
296
|
},
|
|
297
297
|
]),
|
|
298
|
-
skill: Object.freeze([
|
|
299
|
-
{
|
|
300
|
-
name: "strikeMode",
|
|
301
|
-
...AS_AUTHORED,
|
|
302
|
-
describe:
|
|
303
|
-
"The strike mode a combat technique declares. Compiled, but applied by the builder rather than listed in the type's fields.",
|
|
304
|
-
},
|
|
305
|
-
]),
|
|
306
298
|
being: Object.freeze([
|
|
307
299
|
{
|
|
308
300
|
name: "attrRollFormula",
|
package/stage.mjs
CHANGED
|
@@ -166,3 +166,22 @@ export function cleanBuildArtifacts(root, { extra = [], includeNodeModules = fal
|
|
|
166
166
|
}
|
|
167
167
|
return removed;
|
|
168
168
|
}
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* Remove the configured Foundry stage before a complete build assembles it.
|
|
172
|
+
* Other build outputs, including the content index, remain available.
|
|
173
|
+
*
|
|
174
|
+
* @param {string} root - Repository root.
|
|
175
|
+
* @param {string} stageDir - Configured stage directory, relative to root.
|
|
176
|
+
* @returns {boolean} Whether the stage existed.
|
|
177
|
+
*/
|
|
178
|
+
export function resetStage(root, stageDir) {
|
|
179
|
+
const projectRoot = path.resolve(root);
|
|
180
|
+
const target = path.resolve(projectRoot, stageDir);
|
|
181
|
+
if (!target.startsWith(`${projectRoot}${path.sep}`)) {
|
|
182
|
+
throw new Error("The stage directory must be inside the project root.");
|
|
183
|
+
}
|
|
184
|
+
if (!fs.existsSync(target)) return false;
|
|
185
|
+
fs.rmSync(target, { recursive: true, force: true });
|
|
186
|
+
return true;
|
|
187
|
+
}
|
package/types/config.d.mts
CHANGED
|
@@ -68,7 +68,7 @@ export function resolvePackageBuildConfig(shared: object): Readonly<PackageBuild
|
|
|
68
68
|
/**
|
|
69
69
|
* The repository's resolved package-build configuration.
|
|
70
70
|
*
|
|
71
|
-
* Read on call rather than at import, exactly as
|
|
71
|
+
* Read on call rather than at import, exactly as package-build resolves its
|
|
72
72
|
* own: importing a module of this package must not require a configuration to
|
|
73
73
|
* exist anywhere above it.
|
|
74
74
|
*
|
|
@@ -93,7 +93,8 @@ export const DERIVED_MANIFEST_KEYS: Readonly<Record<string, string>>;
|
|
|
93
93
|
* The resolved `packageBuild` section, every optional half filled in.
|
|
94
94
|
*
|
|
95
95
|
* @typedef {object} PackageBuildConfig
|
|
96
|
-
* @property {string} rootDir The repository root, from
|
|
96
|
+
* @property {string} rootDir The repository root, from package-build.
|
|
97
|
+
* @property {{age: number, threshold: number, minWords: number}} proseLint Optional prose analysis settings.
|
|
97
98
|
* @property {string} packageKind `systems` or `modules`.
|
|
98
99
|
* @property {string} packageId The Foundry package id.
|
|
99
100
|
* @property {string} artifact Derived: `system` or `module`.
|
|
@@ -193,9 +194,17 @@ export type AssetSpec = {
|
|
|
193
194
|
*/
|
|
194
195
|
export type PackageBuildConfig = {
|
|
195
196
|
/**
|
|
196
|
-
* The repository root, from
|
|
197
|
+
* The repository root, from package-build.
|
|
197
198
|
*/
|
|
198
199
|
rootDir: string;
|
|
200
|
+
/**
|
|
201
|
+
* Optional prose analysis settings.
|
|
202
|
+
*/
|
|
203
|
+
proseLint: {
|
|
204
|
+
age: number;
|
|
205
|
+
threshold: number;
|
|
206
|
+
minWords: number;
|
|
207
|
+
};
|
|
199
208
|
/**
|
|
200
209
|
* `systems` or `modules`.
|
|
201
210
|
*/
|