@heroiclands/package-build 23.1.1 → 23.2.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 +69 -0
- package/bin/content-commands.mjs +68 -149
- package/bin/package-build.mjs +56 -10
- package/ci/check-banners.mjs +42 -6
- package/ci/confirm-npm-version.mjs +135 -0
- package/ci/content-format-reference.mjs +14 -5
- package/config.mjs +1 -0
- package/container.mjs +185 -4
- package/content-config.mjs +333 -210
- package/deploy.mjs +116 -10
- package/docs/api.md +238 -246
- package/docs/authoring/assets.md +11 -5
- package/docs/authoring/frontmatter.md +1 -1
- package/docs/authoring/links-and-markup.md +87 -0
- package/docs/commands.md +138 -99
- package/docs/configuration.md +277 -125
- package/docs/content-format.md +2 -0
- package/docs/diagnostics.md +48 -13
- package/docs/guides/book.md +341 -21
- package/docs/guides/e2e.md +13 -2
- package/docs/guides/icons.md +14 -4
- package/docs/guides/module-project.md +18 -8
- package/docs/guides/release-and-deploy.md +32 -0
- package/docs/guides/site.md +5 -2
- package/docs/project-setup.md +30 -10
- package/docs/reference/format-details.md +373 -170
- package/docs/reference/note-types.md +128 -109
- package/docs/reference/theme-pages.md +31 -6
- package/e2e.mjs +140 -28
- package/engine/actor-compiler.mjs +22 -31
- package/engine/address-charset.mjs +4 -17
- package/engine/address-fields.mjs +1 -1
- package/engine/address-render.mjs +12 -1
- package/engine/address-values.mjs +6 -0
- package/engine/address.mjs +46 -33
- package/engine/art-fields.mjs +30 -5
- package/engine/art-slots.mjs +10 -1
- package/engine/asset-bindings.mjs +12 -1
- package/engine/base-compiler.mjs +34 -65
- package/engine/being-age.mjs +2 -7
- package/engine/being-measurements.mjs +12 -1
- package/engine/calendar-artifacts.mjs +6 -0
- package/engine/calendar-choice.mjs +12 -1
- package/engine/calendar-format.mjs +12 -1
- package/engine/calendar-human.mjs +12 -1
- package/engine/compile-corpus.mjs +3 -1
- package/engine/content-address.mjs +9 -12
- package/engine/content-alerts.mjs +13 -1
- package/engine/content-blocks.mjs +14 -3
- package/engine/content-embeds.mjs +11 -3
- package/engine/content-figures.mjs +9 -0
- package/engine/content-footnotes.mjs +12 -1
- package/engine/content-format-check.mjs +11 -262
- package/engine/content-format.yaml +5 -2
- package/engine/content-images.mjs +3 -0
- package/engine/content-index.mjs +443 -20
- package/engine/content-links.mjs +29 -35
- package/engine/content-lint.mjs +2 -3
- package/engine/content-poetry.mjs +12 -1
- package/engine/content-slug.mjs +2 -14
- package/engine/content-spans.mjs +13 -1
- package/engine/content-tables.mjs +12 -1
- package/engine/culture-choice.mjs +210 -13
- package/engine/data-keys.mjs +19 -16
- package/engine/date-conversion.mjs +12 -1
- package/engine/declaration-check.mjs +9 -0
- package/engine/derived-fields.mjs +6 -7
- package/engine/diagnostics.mjs +34 -4
- package/engine/docs-checks.mjs +9 -0
- package/engine/document-subtypes.mjs +47 -34
- package/engine/event-views.mjs +139 -14
- package/engine/exported-scene.mjs +92 -28
- package/engine/extension-attributes.mjs +12 -1
- package/engine/field-spec.mjs +11 -102
- package/engine/folder-notes.mjs +0 -64
- package/engine/foreign-catalog.mjs +2 -1
- package/engine/format-generated.mjs +9 -0
- package/engine/foundry-entries.mjs +10 -28
- package/engine/frontmatter-lint.mjs +83 -392
- package/engine/frontmatter.mjs +20 -70
- package/engine/generate.mjs +10 -15
- package/engine/heading-attributes.mjs +12 -1
- package/engine/helpers.mjs +41 -102
- package/engine/holdings.mjs +1 -1
- package/engine/homepage.mjs +3 -10
- package/engine/ids.mjs +1 -167
- package/engine/index-records.mjs +6 -4
- package/engine/index.mjs +0 -3
- package/engine/infobox-render.mjs +15 -3
- package/engine/infobox.mjs +13 -14
- package/engine/item-compiler.mjs +4 -10
- package/engine/item-docs.mjs +3 -5
- package/engine/item-registry.mjs +2 -10
- package/engine/journals.mjs +1 -5
- package/engine/literature-notes.mjs +4 -12
- package/engine/macros.mjs +1 -2
- package/engine/map-graphviz.mjs +12 -1
- package/engine/markdown-expressions.mjs +12 -1
- package/engine/markdownlint-run.mjs +567 -0
- package/engine/metadata-index.mjs +42 -12
- package/engine/note-addresses.mjs +143 -35
- package/engine/note-claims.mjs +14 -28
- package/engine/note-dates.mjs +3 -3
- package/engine/note-frontmatter.mjs +6 -0
- package/engine/note-ids.mjs +8 -14
- package/engine/note-package.mjs +8 -25
- package/engine/note-state.mjs +1 -3
- package/engine/note-vocabulary.mjs +148 -84
- package/engine/number-words.mjs +12 -1
- package/engine/pack-config.mjs +15 -25
- package/engine/pack-router.mjs +10 -16
- package/engine/pathnames.mjs +8 -22
- package/engine/pdf-build.mjs +773 -121
- package/engine/pdf-coverage.mjs +188 -0
- package/engine/pdf-index.mjs +54 -7
- package/engine/pdf-pages.mjs +247 -0
- package/engine/pdf-render.mjs +114 -4
- package/engine/pdf-toc.mjs +29 -32
- package/engine/project-init.mjs +46 -6
- package/engine/prose-config.mjs +1 -1
- package/engine/prose-lint.mjs +52 -88
- package/engine/readability-lint.mjs +9 -0
- package/engine/reckoning-markers.mjs +12 -1
- package/engine/reference-targets.mjs +7 -0
- package/engine/runtime-only-fields.mjs +5 -8
- package/engine/scene-levels.mjs +2 -2
- package/engine/scenes.mjs +1 -1
- package/engine/shortcode-references.mjs +7 -0
- package/engine/site-build.mjs +30 -16
- package/engine/site-index.mjs +10 -16
- package/engine/site-preview.mjs +6 -0
- package/engine/site-root.mjs +8 -1
- package/engine/social-tie-terms.mjs +12 -1
- package/engine/social-ties.mjs +3 -0
- package/engine/sql-tables.mjs +56 -20
- package/engine/standing-terms.mjs +11 -1
- package/engine/standings.mjs +11 -27
- package/engine/system-block.mjs +39 -141
- package/engine/system-vocabulary.mjs +6 -0
- package/engine/walk-files.mjs +69 -0
- package/engine/web-wikilinks.mjs +23 -3
- package/engine/wikilink-syntax.mjs +32 -21
- package/engine/wikilinks.mjs +27 -16
- package/githooks/attribution.mjs +8 -0
- package/githooks/commit-message.mjs +9 -0
- package/hm3/actors.mjs +9 -48
- package/hm3/default-item-art.mjs +9 -9
- package/hm3/item-fields.mjs +2 -9
- package/hm3/template-priority.mjs +6 -7
- package/hugo-theme/data/banner-kinds.yaml +78 -0
- package/hugo-theme/data/banners.yaml +2 -3
- package/hugo-theme/layouts/404.html +3 -3
- package/hugo-theme/layouts/partials/banner-url.html +31 -7
- package/hugo-theme/layouts/partials/hero-banner.html +13 -14
- package/hugo-theme/layouts/partials/related.html +1 -1
- package/hugo-theme/layouts/partials/warn-missing-banner.html +1 -1
- package/hugo-theme/static/css/style.css +1 -1
- package/labels.mjs +2 -2
- package/manifest.mjs +11 -4
- package/markdownlint-config.mjs +1 -1
- package/package.json +6 -3
- package/release.mjs +21 -14
- package/sohl/actors.mjs +16 -27
- package/sohl/default-item-art.mjs +3 -3
- package/sohl/document-subtypes.mjs +1 -16
- package/sohl/infobox.mjs +2 -3
- package/sohl/item-builders.mjs +5 -8
- package/sohl/item-fields.mjs +5 -35
- package/sohl/items.mjs +0 -4
- package/sohl/kb-passes.mjs +3 -3
- package/types/container.d.mts +116 -5
- package/types/content-config.d.mts +83 -35
- package/types/deploy.d.mts +42 -6
- package/types/e2e.d.mts +35 -3
- package/types/engine/address-charset.d.mts +4 -17
- package/types/engine/address.d.mts +17 -8
- package/types/engine/art-fields.d.mts +7 -4
- package/types/engine/art-slots.d.mts +6 -0
- package/types/engine/base-compiler.d.mts +15 -17
- package/types/engine/content-address.d.mts +9 -12
- package/types/engine/content-embeds.d.mts +4 -2
- package/types/engine/content-format-check.d.mts +0 -64
- package/types/engine/content-index.d.mts +82 -3
- package/types/engine/content-links.d.mts +2 -4
- package/types/engine/culture-choice.d.mts +51 -8
- package/types/engine/data-keys.d.mts +2 -2
- package/types/engine/derived-fields.d.mts +1 -2
- package/types/engine/diagnostics.d.mts +27 -4
- package/types/engine/document-subtypes.d.mts +12 -31
- package/types/engine/event-views.d.mts +14 -2
- package/types/engine/exported-scene.d.mts +3 -2
- package/types/engine/field-spec.d.mts +13 -122
- package/types/engine/folder-notes.d.mts +0 -39
- package/types/engine/foreign-catalog.d.mts +2 -1
- package/types/engine/foundry-entries.d.mts +4 -5
- package/types/engine/frontmatter-lint.d.mts +2 -2
- package/types/engine/frontmatter.d.mts +19 -45
- package/types/engine/generate.d.mts +1 -1
- package/types/engine/helpers.d.mts +25 -42
- package/types/engine/homepage.d.mts +3 -10
- package/types/engine/ids.d.mts +0 -114
- package/types/engine/index-records.d.mts +6 -4
- package/types/engine/index.d.mts +0 -1
- package/types/engine/infobox-render.d.mts +7 -1
- package/types/engine/infobox.d.mts +3 -2
- package/types/engine/item-docs.d.mts +1 -1
- package/types/engine/item-registry.d.mts +2 -4
- package/types/engine/literature-notes.d.mts +1 -1
- package/types/engine/markdownlint-run.d.mts +40 -0
- package/types/engine/metadata-index.d.mts +9 -1
- package/types/engine/note-addresses.d.mts +27 -2
- package/types/engine/note-claims.d.mts +3 -5
- package/types/engine/note-ids.d.mts +2 -4
- package/types/engine/note-package.d.mts +1 -1
- package/types/engine/note-state.d.mts +1 -3
- package/types/engine/note-vocabulary.d.mts +28 -21
- package/types/engine/pack-config.d.mts +2 -6
- package/types/engine/pack-router.d.mts +4 -4
- package/types/engine/pathnames.d.mts +3 -3
- package/types/engine/pdf-build.d.mts +98 -12
- package/types/engine/pdf-coverage.d.mts +60 -0
- package/types/engine/pdf-index.d.mts +34 -5
- package/types/engine/pdf-pages.d.mts +143 -0
- package/types/engine/pdf-render.d.mts +45 -1
- package/types/engine/pdf-toc.d.mts +12 -14
- package/types/engine/prose-config.d.mts +1 -1
- package/types/engine/prose-lint.d.mts +11 -10
- package/types/engine/runtime-only-fields.d.mts +2 -2
- package/types/engine/site-build.d.mts +9 -5
- package/types/engine/site-index.d.mts +4 -5
- package/types/engine/site-root.d.mts +1 -1
- package/types/engine/sql-tables.d.mts +31 -2
- package/types/engine/standing-terms.d.mts +2 -1
- package/types/engine/standings.d.mts +7 -9
- package/types/engine/system-block.d.mts +10 -44
- package/types/engine/walk-files.d.mts +15 -0
- package/types/engine/wikilink-syntax.d.mts +18 -19
- package/types/hm3/actors.d.mts +1 -2
- package/types/hm3/item-fields.d.mts +2 -4
- package/types/hm3/template-priority.d.mts +6 -7
- package/types/manifest.d.mts +3 -2
- package/types/release.d.mts +8 -7
- package/types/sohl/actors.d.mts +2 -1
- package/types/sohl/document-subtypes.d.mts +1 -2
- package/types/sohl/item-fields.d.mts +3 -7
- package/engine/retired-fields.mjs +0 -631
- package/types/engine/retired-fields.d.mts +0 -354
|
@@ -44,15 +44,12 @@
|
|
|
44
44
|
* transmission — and is true of it whichever system is reading. What each
|
|
45
45
|
* system makes of that value is declared elsewhere, in that system's own half.
|
|
46
46
|
*
|
|
47
|
-
* **The type names here are the specification's
|
|
48
|
-
*
|
|
49
|
-
*
|
|
47
|
+
* **The type names here are the specification's**: `armor`, `projectile`
|
|
48
|
+
* and `concoction` name the thing the note is about, not a SoHL document
|
|
49
|
+
* subtype in a `…gear` spelling. `weapon` is the one
|
|
50
50
|
* the specification and this registry still spell differently: both systems
|
|
51
51
|
* call that document a `weapongear`, so the name says nothing system-specific
|
|
52
|
-
* and the table has no row for it.
|
|
53
|
-
* reaches its entry — every type-keyed lookup normalises through
|
|
54
|
-
* `RENAMED_TYPES` — and is reported rather than refused until the content trees
|
|
55
|
-
* have swept.
|
|
52
|
+
* and the table has no row for it.
|
|
56
53
|
*
|
|
57
54
|
* **A type name and a subType value are held to the address charset**, so
|
|
58
55
|
* both match `ADDRESS_SEGMENT_PATTERN` — the charset `engine/address-charset.mjs`
|
|
@@ -64,10 +61,8 @@
|
|
|
64
61
|
* would be read back as two segments and resolve to nothing, reporting nothing
|
|
65
62
|
* about why.
|
|
66
63
|
*
|
|
67
|
-
* A subType reaches no address of its own. It
|
|
68
|
-
*
|
|
69
|
-
* the note format one release later. It keeps the rule regardless, and the
|
|
70
|
-
* reason is not inertia: a subType is a vocabulary term the whole toolchain
|
|
64
|
+
* A subType reaches no address of its own. It keeps the charset rule regardless,
|
|
65
|
+
* and the reason is not inertia: a subType is a vocabulary term the whole toolchain
|
|
71
66
|
* keys on, it is one closed set away from being an address again, and a charset
|
|
72
67
|
* that held for a type, a shortcode and a package but not for a subType would
|
|
73
68
|
* be a rule nobody could state in one sentence. The registry below is checked
|
|
@@ -84,10 +79,7 @@ import { ADDRESS_SEGMENT_PATTERN, isAddressSegment } from "./address-charset.mjs
|
|
|
84
79
|
// The system registry, so the top-level block keys below are the systems this
|
|
85
80
|
// toolchain recognises rather than a second list of them.
|
|
86
81
|
import { SYSTEM_IDS } from "./systems.mjs";
|
|
87
|
-
|
|
88
|
-
// vocabulary that answered only to the current spelling would report every key
|
|
89
|
-
// of an unswept note as unknown.
|
|
90
|
-
import { currentType } from "./ids.mjs";
|
|
82
|
+
import { packForType } from "./ids.mjs";
|
|
91
83
|
// What a place is next to and reachable from — the two relation lists and
|
|
92
84
|
// the checks that hold them to their closed sets and to each other.
|
|
93
85
|
import { checkBorders, checkRoutes } from "./place-relations.mjs";
|
|
@@ -118,7 +110,7 @@ import { checkNoteEvents } from "./note-events.mjs";
|
|
|
118
110
|
// list the compiled Item is held to, so the lint and the compile cannot differ.
|
|
119
111
|
import { AFFILIATION_STANDINGS } from "../sohl/affiliation-standings.mjs";
|
|
120
112
|
import { EVENT_ENTRY } from "./note-event-terms.mjs";
|
|
121
|
-
import {
|
|
113
|
+
import { CULTURE_FIELD, checkCultureSubType } from "./culture-choice.mjs";
|
|
122
114
|
import { LITERATURE_FIELDS, checkLiteratureNote } from "./literature-notes.mjs";
|
|
123
115
|
import { SOCIAL_TIES, SOCIAL_TIE_TARGET_TYPES } from "./social-tie-terms.mjs";
|
|
124
116
|
import { parseNoteDate } from "./note-dates.mjs";
|
|
@@ -148,15 +140,12 @@ import { positionOfFrontmatterPath } from "./diagnostics.mjs";
|
|
|
148
140
|
* @typedef {object} DataFieldSpec
|
|
149
141
|
* @property {string} name - The key under `data:`, dotted for a nested one
|
|
150
142
|
* (`charges.value`).
|
|
151
|
-
* @property {"string"|"number"|"boolean"|"list"|"map"|"
|
|
143
|
+
* @property {"string"|"number"|"boolean"|"list"|"map"|"scalar-or-map"|"address"|"shortcode"} [kind] -
|
|
152
144
|
* The value's shape, for the lint. Absent means no claim is made about the
|
|
153
145
|
* value — which is the honest answer wherever the specification's stated
|
|
154
146
|
* shape and the shape notes are authored in today disagree.
|
|
155
147
|
* @property {"address"|"shortcode"} [entryKind] - Type of each list or pack-map value.
|
|
156
|
-
* @property {"address"|"shortcode"} [keyKind] - Type of each map key.
|
|
157
|
-
* `list-or-map` field it describes the map form alone, and `entryKind` the
|
|
158
|
-
* list form alone: reading both of one value would check a map's entries as
|
|
159
|
-
* if they were its keys.
|
|
148
|
+
* @property {"address"|"shortcode"} [keyKind] - Type of each map key.
|
|
160
149
|
* @property {boolean} [standings] - The map's entries are standings held in the
|
|
161
150
|
* body each is keyed by, so a row names the two together — see
|
|
162
151
|
* {@link module:engine/standings}.
|
|
@@ -164,6 +153,9 @@ import { positionOfFrontmatterPath } from "./diagnostics.mjs";
|
|
|
164
153
|
* so each takes a row labelled and anchored by the post's own name.
|
|
165
154
|
* @property {"subType"} [keySelector] - An alternative `subType:<skill-subtype>` key.
|
|
166
155
|
* @property {readonly string[]} [accepts] - Allowed Address types, separate from `ref`.
|
|
156
|
+
* @property {string} [declines] - The literal value that declines the art this
|
|
157
|
+
* Address field names. It is not an Address, is never resolved, and is not
|
|
158
|
+
* reported as unresolved.
|
|
167
159
|
* @property {readonly ("prose"|"event")[]} [anchors] - The anchor kinds an
|
|
168
160
|
* Address here may name with `#<anchor>`. Absent, an anchor is refused.
|
|
169
161
|
* @property {boolean} [single] - The Address names exactly one event: an
|
|
@@ -179,6 +171,10 @@ import { positionOfFrontmatterPath } from "./diagnostics.mjs";
|
|
|
179
171
|
* one — `icon` for `icon` and `tokenIcon`, `image` for `banner` — so `icon: anvil` names `icon-anvil` while a value carrying the
|
|
180
172
|
* separator states its own address. Distinct from a `FieldSpec`'s `code`
|
|
181
173
|
* ({@link module:engine/field-spec}), which names a **Shortcode** instead.
|
|
174
|
+
* @property {string} [sceneProperty] - On a map, the exported Scene property an
|
|
175
|
+
* Address field's resolved path is written into, as a `data.fixup` path. The
|
|
176
|
+
* Scene compile applies the field as one more fixup — see
|
|
177
|
+
* {@link module:engine/exported-scene}.
|
|
182
178
|
* @property {"pack"} [keys] - For a `scalar-or-map` field, what its keys name.
|
|
183
179
|
* `"pack"` means each is a pack this package declares, so a key naming none
|
|
184
180
|
* is a finding of its own: it addresses a hierarchy nothing will ever read.
|
|
@@ -196,6 +192,8 @@ import { positionOfFrontmatterPath } from "./diagnostics.mjs";
|
|
|
196
192
|
* entries are not Addresses (`entryKind` says that).
|
|
197
193
|
* @property {object} [values] - What the value under each key of a keyed map
|
|
198
194
|
* is, where the keys are data rather than declared names.
|
|
195
|
+
* @property {readonly string[]} [allowed] - The closed set a present string
|
|
196
|
+
* value is one of; any other word is a finding at the value.
|
|
199
197
|
* @property {string} describe - One line, for the author-facing reference.
|
|
200
198
|
*/
|
|
201
199
|
|
|
@@ -243,6 +241,18 @@ const NUM = Object.freeze({ shape: "number", kind: "number" });
|
|
|
243
241
|
/** A string, including the enumerated-value fields the format spells as one. */
|
|
244
242
|
const TEXT = Object.freeze({ shape: "string", kind: "string" });
|
|
245
243
|
|
|
244
|
+
/** The words a being's `gender` takes. */
|
|
245
|
+
const BEING_GENDERS = Object.freeze(["male", "female", "nonbinary", "none", "other"]);
|
|
246
|
+
|
|
247
|
+
/** The words a being's `frame` takes, lightest to heaviest. */
|
|
248
|
+
const BEING_FRAMES = Object.freeze(["scant", "light", "medium", "heavy", "massive"]);
|
|
249
|
+
|
|
250
|
+
/** Words as code spans, joined by commas and a final "or". */
|
|
251
|
+
function wordList(words) {
|
|
252
|
+
const spans = words.map((word) => `\`${word}\``);
|
|
253
|
+
return `${spans.slice(0, -1).join(", ")} or ${spans.at(-1)}`;
|
|
254
|
+
}
|
|
255
|
+
|
|
246
256
|
/** A list. */
|
|
247
257
|
const LIST = Object.freeze({ shape: "list", kind: "list" });
|
|
248
258
|
|
|
@@ -258,8 +268,6 @@ const TEXT_LIST = Object.freeze({
|
|
|
258
268
|
*
|
|
259
269
|
* The two are the same fact at different lengths rather than two different
|
|
260
270
|
* facts, so a single value means a list of one and a consumer reads one shape.
|
|
261
|
-
* That is what separates this from `list-or-map`, whose halves say different
|
|
262
|
-
* things.
|
|
263
271
|
*/
|
|
264
272
|
const TEXT_OR_LIST = Object.freeze({
|
|
265
273
|
shape: "string or list",
|
|
@@ -278,17 +286,11 @@ const LINKS = Object.freeze({ shape: "list of Addresses", kind: "list", entryKin
|
|
|
278
286
|
*
|
|
279
287
|
* A map keyed by the body's Address, whose entry is that membership's `rank`
|
|
280
288
|
* and `office` — the pair a flat list of bodies cannot state, because it has
|
|
281
|
-
* nowhere to say which body a standing is held in.
|
|
282
|
-
*
|
|
283
|
-
* **`list-or-map` is the sweep's shape**, and it says which half each
|
|
284
|
-
* declaration describes: `entryKind` the list's entries, `keyKind` the map's
|
|
285
|
-
* keys. A tree still writing the list form keeps the check it had, and the map
|
|
286
|
-
* form earns the two the body can answer — see {@link module:engine/standings}.
|
|
289
|
+
* nowhere to say which body a standing is held in. A list is an error.
|
|
287
290
|
*/
|
|
288
291
|
const STANDINGS = Object.freeze({
|
|
289
|
-
shape: "a standing map keyed by Address
|
|
290
|
-
kind: "
|
|
291
|
-
entryKind: "address",
|
|
292
|
+
shape: "a standing map keyed by Address",
|
|
293
|
+
kind: "map",
|
|
292
294
|
keyKind: "address",
|
|
293
295
|
values: Object.freeze({
|
|
294
296
|
kind: "map",
|
|
@@ -484,13 +486,10 @@ export const NOTE_TOP_LEVEL_KEY_SET = Object.freeze(new Set(NOTE_TOP_LEVEL_KEYS)
|
|
|
484
486
|
* row in twenty-five tables would be twenty-five chances for one of them to
|
|
485
487
|
* disagree with the rest.
|
|
486
488
|
*
|
|
487
|
-
*
|
|
488
|
-
*
|
|
489
|
-
*
|
|
490
|
-
*
|
|
491
|
-
* value may still be read by a page template. `banner` reaches no compiled
|
|
492
|
-
* document at all — it is the page's hero image — and a page is what every note
|
|
493
|
-
* publishes.
|
|
489
|
+
* `banner` is an art slot legal everywhere: it reaches no compiled document, it
|
|
490
|
+
* is the page's hero image, and a page is what every note publishes. `icon` is
|
|
491
|
+
* not shared; it is declared by {@link ICON_FIELD} for the types whose
|
|
492
|
+
* document carries art.
|
|
494
493
|
*
|
|
495
494
|
* @type {readonly DataFieldSpec[]}
|
|
496
495
|
*/
|
|
@@ -533,24 +532,54 @@ export const SHARED_DATA_FIELDS = Object.freeze([
|
|
|
533
532
|
]),
|
|
534
533
|
describe: "HârnWorld source details shared by every system — `realm` and `ritual`.",
|
|
535
534
|
}),
|
|
536
|
-
Object.freeze({
|
|
537
|
-
name: "icon",
|
|
538
|
-
...LINK,
|
|
539
|
-
ref: "icon",
|
|
540
|
-
accepts: ART_SLOTS.find((slot) => slot.key === "icon").accepts,
|
|
541
|
-
describe: "The document's profile art — an `icon` address, resolved into `img`.",
|
|
542
|
-
}),
|
|
543
535
|
Object.freeze({
|
|
544
536
|
name: "banner",
|
|
545
537
|
...LINK,
|
|
546
538
|
ref: "image",
|
|
547
539
|
accepts: ART_SLOTS.find((slot) => slot.key === "banner").accepts,
|
|
540
|
+
declines: ART_SLOTS.find((slot) => slot.key === "banner").declines,
|
|
548
541
|
describe:
|
|
549
|
-
"The page's hero image — an `image` address, cut to 1792×768
|
|
550
|
-
"Reaches no compiled document.",
|
|
542
|
+
"The page's hero image — an `image` address, cut to 1792×768, or `none` " +
|
|
543
|
+
"to decline a hero image. Reaches no compiled document.",
|
|
551
544
|
}),
|
|
552
545
|
]);
|
|
553
546
|
|
|
547
|
+
/**
|
|
548
|
+
* The profile-art field, declared for the types whose document carries art.
|
|
549
|
+
*
|
|
550
|
+
* @type {DataFieldSpec}
|
|
551
|
+
*/
|
|
552
|
+
const ICON_FIELD = Object.freeze({
|
|
553
|
+
name: "icon",
|
|
554
|
+
...LINK,
|
|
555
|
+
ref: "icon",
|
|
556
|
+
accepts: ART_SLOTS.find((slot) => slot.key === "icon").accepts,
|
|
557
|
+
describe: "The document's profile art — an `icon` address, resolved into `img`.",
|
|
558
|
+
});
|
|
559
|
+
|
|
560
|
+
/**
|
|
561
|
+
* The document types whose builder emits `icon` art: the Actor, Item and Macro
|
|
562
|
+
* passes and the Adventure (bundle) pass. A Scene and a JournalEntry carry
|
|
563
|
+
* none. Every pass declares this through its `emitsArt`, and a test holds the
|
|
564
|
+
* two statements together.
|
|
565
|
+
*
|
|
566
|
+
* @type {ReadonlySet<string>}
|
|
567
|
+
*/
|
|
568
|
+
const ICON_DOCUMENT_TYPES = new Set(["Actor", "Item", "Macro", "Adventure"]);
|
|
569
|
+
|
|
570
|
+
/**
|
|
571
|
+
* Whether a note type compiles into a document carrying `icon` art.
|
|
572
|
+
*
|
|
573
|
+
* A folder and a homepage route like any other type but compile into no art.
|
|
574
|
+
*
|
|
575
|
+
* @param {string} type - The current content type.
|
|
576
|
+
* @returns {boolean} Whether `data.icon` is declared for the type.
|
|
577
|
+
*/
|
|
578
|
+
function emitsIcon(type) {
|
|
579
|
+
if (type === "folder" || type === "homepage") return false;
|
|
580
|
+
return ICON_DOCUMENT_TYPES.has(packForType(type).docType);
|
|
581
|
+
}
|
|
582
|
+
|
|
554
583
|
/**
|
|
555
584
|
* An Address naming one event: `place-ironfells#sack`, or a note holding
|
|
556
585
|
* exactly one event, with a bare shortcode read as a `lore` note.
|
|
@@ -686,9 +715,7 @@ function checkPlacePurpose(note) {
|
|
|
686
715
|
*
|
|
687
716
|
* It is a **presentation** fact and nothing more. A draft note compiles,
|
|
688
717
|
* validates, publishes and resolves like any other; only a link *into* it
|
|
689
|
-
* renders marked.
|
|
690
|
-
* whose entire effect was to move a note from published to unresolvable — see
|
|
691
|
-
* {@link draftRetiredMessage}.
|
|
718
|
+
* renders marked.
|
|
692
719
|
*/
|
|
693
720
|
export const DRAFT_TAG = "draft";
|
|
694
721
|
|
|
@@ -960,13 +987,6 @@ export const NOTE_VOCABULARY = Object.freeze({
|
|
|
960
987
|
TEMPLATE_PRIORITY,
|
|
961
988
|
{ name: "archetypes", ...TEXT_LIST, describe: "Archetypal behaviours the being fits." },
|
|
962
989
|
{ name: "occupation", ...TEXT, describe: "What the being does for a living." },
|
|
963
|
-
{
|
|
964
|
-
name: "stations",
|
|
965
|
-
...LINKS,
|
|
966
|
-
ref: "lore",
|
|
967
|
-
accepts: ["lore"],
|
|
968
|
-
describe: "Stations the being holds.",
|
|
969
|
-
},
|
|
970
990
|
{
|
|
971
991
|
name: "lore",
|
|
972
992
|
...LINKS,
|
|
@@ -977,11 +997,7 @@ export const NOTE_VOCABULARY = Object.freeze({
|
|
|
977
997
|
"subject to, the traditions it was raised in.",
|
|
978
998
|
},
|
|
979
999
|
{
|
|
980
|
-
|
|
981
|
-
...LINK,
|
|
982
|
-
ref: "lore",
|
|
983
|
-
accepts: ["lore"],
|
|
984
|
-
check: checkCultureChoice,
|
|
1000
|
+
...CULTURE_FIELD,
|
|
985
1001
|
describe: "The being's primary culture, as a culture lore note.",
|
|
986
1002
|
},
|
|
987
1003
|
{
|
|
@@ -1016,8 +1032,9 @@ export const NOTE_VOCABULARY = Object.freeze({
|
|
|
1016
1032
|
{
|
|
1017
1033
|
name: "gender",
|
|
1018
1034
|
...TEXT,
|
|
1035
|
+
allowed: BEING_GENDERS,
|
|
1019
1036
|
describe:
|
|
1020
|
-
|
|
1037
|
+
`One of ${wordList(BEING_GENDERS)}. ` +
|
|
1021
1038
|
"`none` says the being has no gender; an absent field says its " +
|
|
1022
1039
|
"gender is unrecorded.",
|
|
1023
1040
|
},
|
|
@@ -1042,7 +1059,7 @@ export const NOTE_VOCABULARY = Object.freeze({
|
|
|
1042
1059
|
ref: "lore",
|
|
1043
1060
|
accepts: ["lore"],
|
|
1044
1061
|
check: checkCalendarChoice,
|
|
1045
|
-
describe: "Calendar
|
|
1062
|
+
describe: "Calendar that displays this being's dates.",
|
|
1046
1063
|
},
|
|
1047
1064
|
{
|
|
1048
1065
|
name: "died",
|
|
@@ -1088,8 +1105,8 @@ export const NOTE_VOCABULARY = Object.freeze({
|
|
|
1088
1105
|
{
|
|
1089
1106
|
name: "frame",
|
|
1090
1107
|
...TEXT,
|
|
1091
|
-
|
|
1092
|
-
|
|
1108
|
+
allowed: BEING_FRAMES,
|
|
1109
|
+
describe: `Relative frame — one of ${wordList(BEING_FRAMES)}.`,
|
|
1093
1110
|
},
|
|
1094
1111
|
{ name: "appearance.eye_color", ...TEXT, describe: "Eye colour." },
|
|
1095
1112
|
{ name: "appearance.hair_color", ...TEXT, describe: "Hair colour." },
|
|
@@ -1230,6 +1247,10 @@ export const NOTE_VOCABULARY = Object.freeze({
|
|
|
1230
1247
|
accepts: ["affiliation"],
|
|
1231
1248
|
describe: "Affiliations it is subordinate to.",
|
|
1232
1249
|
},
|
|
1250
|
+
{
|
|
1251
|
+
...CULTURE_FIELD,
|
|
1252
|
+
describe: "The people this affiliation belongs to, as a culture lore note.",
|
|
1253
|
+
},
|
|
1233
1254
|
{
|
|
1234
1255
|
name: "relations",
|
|
1235
1256
|
kind: "map",
|
|
@@ -1525,8 +1546,20 @@ export const NOTE_VOCABULARY = Object.freeze({
|
|
|
1525
1546
|
"concept",
|
|
1526
1547
|
"settingguide",
|
|
1527
1548
|
]),
|
|
1528
|
-
|
|
1529
|
-
|
|
1549
|
+
// A setting guide introduces one culture and names it; no other genre
|
|
1550
|
+
// states one.
|
|
1551
|
+
check: (note, context) => [
|
|
1552
|
+
...checkCitedPopulations(note, context),
|
|
1553
|
+
...checkCultureSubType(note, context),
|
|
1554
|
+
],
|
|
1555
|
+
data: Object.freeze([
|
|
1556
|
+
{
|
|
1557
|
+
...CULTURE_FIELD,
|
|
1558
|
+
describe:
|
|
1559
|
+
"The culture a setting guide introduces, as a culture lore note. " +
|
|
1560
|
+
"Required on `settingguide`; no other subType states it.",
|
|
1561
|
+
},
|
|
1562
|
+
]),
|
|
1530
1563
|
}),
|
|
1531
1564
|
|
|
1532
1565
|
macro: Object.freeze({ stubbable: true, data: Object.freeze([]) }),
|
|
@@ -1607,15 +1640,28 @@ export const NOTE_VOCABULARY = Object.freeze({
|
|
|
1607
1640
|
]),
|
|
1608
1641
|
// A lore note is prose, and what it *is* about is its subType — with
|
|
1609
1642
|
// two exceptions. A calendar is a division of the year, and a division
|
|
1610
|
-
// is data; a work of literature states
|
|
1611
|
-
//
|
|
1612
|
-
//
|
|
1613
|
-
//
|
|
1643
|
+
// is data; a work of literature states in what tongue it is and what it
|
|
1644
|
+
// concerns. The checks scope each family to the subType that means it,
|
|
1645
|
+
// because `DataFieldSpec` declares the keys a type accepts and not the
|
|
1646
|
+
// subType that may write them. Any lore note may name the people it
|
|
1647
|
+
// belongs to; a culture note names only itself.
|
|
1614
1648
|
check: (note, context) => [
|
|
1615
1649
|
...checkCalendarNote(note, context),
|
|
1616
1650
|
...checkLiteratureNote(note),
|
|
1651
|
+
...checkCultureSubType(note, context),
|
|
1617
1652
|
],
|
|
1618
|
-
data: Object.freeze([
|
|
1653
|
+
data: Object.freeze([
|
|
1654
|
+
...CALENDAR_FIELDS,
|
|
1655
|
+
{
|
|
1656
|
+
...CULTURE_FIELD,
|
|
1657
|
+
describe:
|
|
1658
|
+
"The people this lore belongs to — for a work of literature, the people " +
|
|
1659
|
+
"whose work it is — as a culture lore note. A culture note is its own " +
|
|
1660
|
+
"culture, and states the field only as itself.",
|
|
1661
|
+
},
|
|
1662
|
+
...LITERATURE_FIELDS,
|
|
1663
|
+
EVENTS_FIELD,
|
|
1664
|
+
]),
|
|
1619
1665
|
}),
|
|
1620
1666
|
|
|
1621
1667
|
place: Object.freeze({
|
|
@@ -1638,7 +1684,7 @@ export const NOTE_VOCABULARY = Object.freeze({
|
|
|
1638
1684
|
ref: "lore",
|
|
1639
1685
|
accepts: ["lore"],
|
|
1640
1686
|
check: checkCalendarChoice,
|
|
1641
|
-
describe: "Calendar
|
|
1687
|
+
describe: "Calendar that displays this place's dates.",
|
|
1642
1688
|
},
|
|
1643
1689
|
{
|
|
1644
1690
|
name: "demonym",
|
|
@@ -1668,6 +1714,10 @@ export const NOTE_VOCABULARY = Object.freeze({
|
|
|
1668
1714
|
accepts: ["place"],
|
|
1669
1715
|
describe: "Enclosing places this one sits within.",
|
|
1670
1716
|
},
|
|
1717
|
+
{
|
|
1718
|
+
...CULTURE_FIELD,
|
|
1719
|
+
describe: "The people this place belongs to, as a culture lore note.",
|
|
1720
|
+
},
|
|
1671
1721
|
{
|
|
1672
1722
|
name: "population",
|
|
1673
1723
|
...NUM,
|
|
@@ -1836,6 +1886,18 @@ export const NOTE_VOCABULARY = Object.freeze({
|
|
|
1836
1886
|
}),
|
|
1837
1887
|
describe: "Asset address replacements in an exported Scene.",
|
|
1838
1888
|
},
|
|
1889
|
+
{
|
|
1890
|
+
name: "thumbnail",
|
|
1891
|
+
...LINK,
|
|
1892
|
+
ref: "image",
|
|
1893
|
+
accepts: Object.freeze(["image"]),
|
|
1894
|
+
// The Scene property the resolved path is written into, as a
|
|
1895
|
+
// fixup path: the compile applies it as one more `data.fixup`.
|
|
1896
|
+
sceneProperty: ".thumb",
|
|
1897
|
+
describe:
|
|
1898
|
+
"The map's thumbnail — an `image` address, resolved into the Scene's " +
|
|
1899
|
+
"`thumb` the way a `data.fixup` entry is. Background art is untouched.",
|
|
1900
|
+
},
|
|
1839
1901
|
{
|
|
1840
1902
|
name: "place",
|
|
1841
1903
|
...LINK,
|
|
@@ -1853,11 +1915,8 @@ export const NOTE_VOCABULARY = Object.freeze({
|
|
|
1853
1915
|
* What a note carrying a subType outside the address charset is told.
|
|
1854
1916
|
*
|
|
1855
1917
|
* **Why the charset holds for a subType, which reaches no address.** The rule
|
|
1856
|
-
*
|
|
1857
|
-
*
|
|
1858
|
-
* subType when it shipped: `sectionOf` returned a `doc`'s subType, so the value
|
|
1859
|
-
* was a URL path segment. Sections are retired and it is not one. The rule
|
|
1860
|
-
* stays, on its own footing: a subType is a vocabulary term the whole toolchain
|
|
1918
|
+
* says "the hyphen separates the segments of an address", and a subType is not
|
|
1919
|
+
* one. The rule stands on its own footing: a subType is a vocabulary term the whole toolchain
|
|
1861
1920
|
* keys on, and it is one closed set away from being an address segment again —
|
|
1862
1921
|
* so the reason to spell it in the address charset is that a charset holding
|
|
1863
1922
|
* for a type, a shortcode and a `contentPackage` but not for a subType is a
|
|
@@ -1906,7 +1965,7 @@ export function typeCharsetMessage(type) {
|
|
|
1906
1965
|
*
|
|
1907
1966
|
* The message states the reason **per key**, as {@link typeCharsetMessage} and
|
|
1908
1967
|
* {@link subTypeCharsetMessage} do: a type is an address segment, and a subType
|
|
1909
|
-
* is not one,
|
|
1968
|
+
* is not one, so a single claim covering both
|
|
1910
1969
|
* would be half wrong.
|
|
1911
1970
|
*
|
|
1912
1971
|
* @param {Readonly<Record<string, TypeVocabulary>>} vocabulary - The registry.
|
|
@@ -1953,11 +2012,16 @@ assertVocabularyCharset(NOTE_VOCABULARY);
|
|
|
1953
2012
|
* one, and is why the lint makes no claim rather than refusing every key.
|
|
1954
2013
|
*/
|
|
1955
2014
|
export function dataFields(type, vocabulary = NOTE_VOCABULARY) {
|
|
1956
|
-
const entry = vocabulary?.[
|
|
2015
|
+
const entry = vocabulary?.[type];
|
|
1957
2016
|
if (!entry) return undefined;
|
|
1958
2017
|
// The shared keys first, so a type's own declarations keep their authored
|
|
1959
2018
|
// order behind them and a reader meets the art before the mechanics.
|
|
1960
|
-
|
|
2019
|
+
const key = type;
|
|
2020
|
+
return Object.freeze([
|
|
2021
|
+
...SHARED_DATA_FIELDS,
|
|
2022
|
+
...(emitsIcon(key) ? [ICON_FIELD] : []),
|
|
2023
|
+
...(entry.data ?? []),
|
|
2024
|
+
]);
|
|
1961
2025
|
}
|
|
1962
2026
|
|
|
1963
2027
|
/**
|
|
@@ -1971,7 +2035,7 @@ export function dataFields(type, vocabulary = NOTE_VOCABULARY) {
|
|
|
1971
2035
|
* it has no `subType` at all — see {@link TypeVocabulary}.
|
|
1972
2036
|
*/
|
|
1973
2037
|
export function subTypes(type, vocabulary = NOTE_VOCABULARY) {
|
|
1974
|
-
const entry = vocabulary?.[
|
|
2038
|
+
const entry = vocabulary?.[type];
|
|
1975
2039
|
if (!entry || !Object.hasOwn(entry, "subTypes")) return undefined;
|
|
1976
2040
|
return entry.subTypes;
|
|
1977
2041
|
}
|
package/engine/number-words.mjs
CHANGED
|
@@ -1,4 +1,15 @@
|
|
|
1
|
-
/*
|
|
1
|
+
/*
|
|
2
|
+
* This file is part of the Song of Heroic Lands (SoHL) system for Foundry VTT.
|
|
3
|
+
* Copyright (c) 2024-2026 Tom Rodriguez ("Toasty") — <toasty@heroiclands.org>
|
|
4
|
+
*
|
|
5
|
+
* This work is licensed under the GNU General Public License v3.0 (GPLv3).
|
|
6
|
+
* You may copy, modify, and distribute it under the terms of that license.
|
|
7
|
+
*
|
|
8
|
+
* For full terms, see the LICENSE.md file in the project root or visit:
|
|
9
|
+
* https://www.gnu.org/licenses/gpl-3.0.html
|
|
10
|
+
*
|
|
11
|
+
* SPDX-License-Identifier: GPL-3.0-or-later
|
|
12
|
+
*/
|
|
2
13
|
|
|
3
14
|
/** English cardinal words for finite whole numbers. */
|
|
4
15
|
const ONES = [
|
package/engine/pack-config.mjs
CHANGED
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
/**
|
|
15
15
|
* The resolved package-build configuration the pack pipeline reads.
|
|
16
16
|
*
|
|
17
|
-
* One module, one import: everything the compilers
|
|
17
|
+
* One module, one import: everything the compilers need — the
|
|
18
18
|
* content package, the Foundry package and its kind, every path, the `_stats`
|
|
19
19
|
* identity, the item-type membership, and the pack list — arrives from the
|
|
20
20
|
* consuming repository's `package-build.config.yaml`.
|
|
@@ -123,11 +123,7 @@ export const CONFIG_BASENAME = "package-build.config";
|
|
|
123
123
|
* the order the names are *reported* in, which is the order a reader should
|
|
124
124
|
* reach for them: YAML first, `.mjs` last.
|
|
125
125
|
*
|
|
126
|
-
*
|
|
127
|
-
* merged is **not** resolved. 3.0.0 renames the file rather than accepting both:
|
|
128
|
-
* a deprecation window here would mean a repository could sit indefinitely on a
|
|
129
|
-
* name for a package that no longer exists, and the upgrade already requires
|
|
130
|
-
* touching the consumer's manifest and imports.
|
|
126
|
+
* Only the `package-build.config.*` stem is resolved.
|
|
131
127
|
*
|
|
132
128
|
* @type {readonly string[]}
|
|
133
129
|
*/
|
|
@@ -142,7 +138,7 @@ export const CONFIG_FILENAMES = Object.freeze([
|
|
|
142
138
|
*
|
|
143
139
|
* **Two of them in one directory is an error.** Picking one by precedence would
|
|
144
140
|
* mean a repository mid-conversion silently builds from the file its author is
|
|
145
|
-
*
|
|
141
|
+
* not editing, and the build would look entirely healthy while doing it.
|
|
146
142
|
* The walk continues past a directory holding none, so a repository may still
|
|
147
143
|
* sit inside one that has its own.
|
|
148
144
|
*
|
|
@@ -163,7 +159,7 @@ export function findConfigFile(from) {
|
|
|
163
159
|
`package-build: ${dir} holds more than one configuration ` +
|
|
164
160
|
`(${found.map((f) => path.basename(f)).join(", ")}). A ` +
|
|
165
161
|
`repository declares its build in exactly one file — ` +
|
|
166
|
-
`delete the one you are
|
|
162
|
+
`delete the one you are not using.`,
|
|
167
163
|
);
|
|
168
164
|
}
|
|
169
165
|
if (found.length === 1) return found[0];
|
|
@@ -267,8 +263,8 @@ function readPackageJson(rootDir) {
|
|
|
267
263
|
} catch (err) {
|
|
268
264
|
throw new Error(
|
|
269
265
|
`package-build: ${manifestPath} could not be read, and the ` +
|
|
270
|
-
`configuration derives its Foundry package id, system version
|
|
271
|
-
`
|
|
266
|
+
`configuration derives its Foundry package id, system version ` +
|
|
267
|
+
`and homepage from it.`,
|
|
272
268
|
{ cause: err },
|
|
273
269
|
);
|
|
274
270
|
}
|
|
@@ -555,24 +551,18 @@ export function configFromData(data, configPath) {
|
|
|
555
551
|
input.foundryPackage = foundryPackageId(rootDir);
|
|
556
552
|
}
|
|
557
553
|
|
|
558
|
-
// `homepage`
|
|
559
|
-
//
|
|
560
|
-
//
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
`package-build: ${configPath} declares \`${field}\`, which a data ` +
|
|
568
|
-
`configuration may not: it is \`package.json\`'s own ${label}. ` +
|
|
569
|
-
`Remove the key.`,
|
|
570
|
-
);
|
|
571
|
-
}
|
|
554
|
+
// `homepage` is `package.json`'s own, transcribed, and read regardless of
|
|
555
|
+
// `packageKind`: a documentation package publishes a site too, and needs
|
|
556
|
+
// it for that.
|
|
557
|
+
if (input.homepage !== undefined) {
|
|
558
|
+
throw new Error(
|
|
559
|
+
`package-build: ${configPath} declares \`homepage\`, which a data ` +
|
|
560
|
+
`configuration may not: it is \`package.json\`'s own \`homepage\`. ` +
|
|
561
|
+
`Remove the key.`,
|
|
562
|
+
);
|
|
572
563
|
}
|
|
573
564
|
const { pkg } = readPackageJson(rootDir);
|
|
574
565
|
if (pkg.homepage !== undefined) input.homepage = pkg.homepage;
|
|
575
|
-
if (pkg.author !== undefined) input.author = pkg.author;
|
|
576
566
|
|
|
577
567
|
if (input.itemBuilders !== undefined) {
|
|
578
568
|
const declared = input.itemBuilders;
|
package/engine/pack-router.mjs
CHANGED
|
@@ -15,14 +15,10 @@
|
|
|
15
15
|
* Which pack a note's document lands in, when a document type has more than
|
|
16
16
|
* one.
|
|
17
17
|
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
* "Expanded Spells" are two Item packs — and `sohl-kethira-basic` shipped
|
|
23
|
-
* three. Collapsing them is a breaking change for every existing world,
|
|
24
|
-
* because a compendium UUID carries its pack name
|
|
25
|
-
* (`Compendium.<pkg>.characteristics.Item.<id>`).
|
|
18
|
+
* Editorial grouping of same-type documents into separate compendiums is
|
|
19
|
+
* ordinary Foundry practice — "Core Spells" and "Expanded Spells" are two Item
|
|
20
|
+
* packs. Collapsing them breaks every existing world, because a compendium
|
|
21
|
+
* UUID carries its pack name (`Compendium.<pkg>.characteristics.Item.<id>`).
|
|
26
22
|
*
|
|
27
23
|
* **A note declares its pack; the configuration declares the default.** The
|
|
28
24
|
* routing key is one frontmatter field, `pack:`, naming a pack from the
|
|
@@ -100,10 +96,10 @@ export class PackRoutingError extends Error {
|
|
|
100
96
|
/**
|
|
101
97
|
* The frontmatter field a note declares its pack in.
|
|
102
98
|
*
|
|
103
|
-
* Deliberately close to
|
|
104
|
-
*
|
|
105
|
-
* repository's `contentPackage`, and
|
|
106
|
-
*
|
|
99
|
+
* Deliberately close to `package:` and deliberately not the same word:
|
|
100
|
+
* `package:` would say which *distribution* owns a note — that is the
|
|
101
|
+
* repository's `contentPackage`, and not authorable — while `pack:` says which
|
|
102
|
+
* *compendium* receives its document.
|
|
107
103
|
*/
|
|
108
104
|
export const PACK_FIELD = "pack";
|
|
109
105
|
|
|
@@ -132,9 +128,7 @@ export { NO_PACK };
|
|
|
132
128
|
*/
|
|
133
129
|
export function declaresNoPack(fm, system) {
|
|
134
130
|
const declared =
|
|
135
|
-
system === undefined ?
|
|
136
|
-
(fm?.data?.[PACK_FIELD] ?? fm?.[PACK_FIELD])
|
|
137
|
-
: blockDataProperty(fm, system, PACK_FIELD);
|
|
131
|
+
system === undefined ? fm?.data?.[PACK_FIELD] : blockDataProperty(fm, system, PACK_FIELD);
|
|
138
132
|
return declared === NO_PACK;
|
|
139
133
|
}
|
|
140
134
|
|
|
@@ -274,7 +268,7 @@ export function createPackRouter(packs) {
|
|
|
274
268
|
const authoredInBlock = inBlock != null && inBlock !== "";
|
|
275
269
|
let declared =
|
|
276
270
|
system === undefined ?
|
|
277
|
-
|
|
271
|
+
fm?.data?.[PACK_FIELD]
|
|
278
272
|
: blockDataProperty(fm, system, PACK_FIELD);
|
|
279
273
|
// The declaration names where the note's *own* document goes. A pass
|
|
280
274
|
// writing a document derived from it — an item's prose becoming a
|