@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/engine/note-package.mjs
CHANGED
|
@@ -19,10 +19,10 @@
|
|
|
19
19
|
* single-sourced in the repository that ships it — so the package is a property
|
|
20
20
|
* of the *repository*, not of the note, and no note declares it.
|
|
21
21
|
*
|
|
22
|
-
* It
|
|
22
|
+
* It is not a property of the note, and not a **selector**: as one, the compile loop
|
|
23
23
|
* read `package:` out of frontmatter and skipped anything that did not match
|
|
24
|
-
* the configured value. That is the defect this module exists to remove
|
|
25
|
-
*
|
|
24
|
+
* the configured value. That is the defect this module exists to remove.
|
|
25
|
+
* The skip was silent and it was bucketed as "belongs to another pass",
|
|
26
26
|
* so a tree whose notes named a package no configuration answered to compiled
|
|
27
27
|
* **zero notes and exited 0** — which is exactly the state the un-migrated
|
|
28
28
|
* `hm-loc-*` / `hm-adv-*` repositories are in today.
|
|
@@ -64,7 +64,7 @@ import { locateFrontmatterKey } from "./retired-fields.mjs";
|
|
|
64
64
|
* field, so a collection note that scopes itself with `WHERE … and package =
|
|
65
65
|
* "sohl"` would match nothing now that the field is gone, and would render an
|
|
66
66
|
* **empty table** in silence. Supplying the derived value here is what kept the
|
|
67
|
-
* sweep mechanical rather than a trap
|
|
67
|
+
* sweep mechanical rather than a trap — and a query that never mentions
|
|
68
68
|
* `package` is unaffected either way.
|
|
69
69
|
*
|
|
70
70
|
* The frontmatter is copied rather than written into: it is the note's own
|
package/engine/note-renames.mjs
CHANGED
|
@@ -12,12 +12,12 @@
|
|
|
12
12
|
*/
|
|
13
13
|
|
|
14
14
|
/**
|
|
15
|
-
* The shortcodes a note declares it used to be published under
|
|
15
|
+
* The shortcodes a note declares it used to be published under.
|
|
16
16
|
*
|
|
17
17
|
* A package's `(type, shortcode)` addresses are a published interface, and
|
|
18
18
|
* `addresses diff` reports what a build stopped publishing — telling a
|
|
19
19
|
* **rename** from a **withdrawal** by matching document ids across two
|
|
20
|
-
* releases.
|
|
20
|
+
* releases. The property that rested on is gone: an id is derived from
|
|
21
21
|
* the canonical address, which carries the shortcode, so renaming a shortcode
|
|
22
22
|
* moves the id too: both sides of the join move together, the match finds
|
|
23
23
|
* nothing, and a rename is reported as a withdrawal with no successor named.
|
|
@@ -52,7 +52,7 @@
|
|
|
52
52
|
* **It is transient.** Once every baseline a build is compared against post-dates
|
|
53
53
|
* the rename, the declaration has nothing left to say and may be deleted. That
|
|
54
54
|
* is the difference from an `id:` pin, which is permanent, opaque, and a second
|
|
55
|
-
* identity for a thing that already has one
|
|
55
|
+
* identity for a thing that already has one.
|
|
56
56
|
*
|
|
57
57
|
* **It is one key per note, at the top level, however many systems the note
|
|
58
58
|
* compiles into.** A shortcode is the note's, not a system block's: a note
|
package/engine/note-schemas.mjs
CHANGED
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
|
|
14
14
|
/**
|
|
15
15
|
* The note types the **engine** declares — the ones whose vocabulary is a fact
|
|
16
|
-
* about the note format rather than about any game system
|
|
16
|
+
* about the note format rather than about any game system.
|
|
17
17
|
*
|
|
18
18
|
* `sohl/note-schemas.mjs` is the other half, and the line between them is the
|
|
19
19
|
* `engine/` ÷ `sohl/` line everywhere else in this package: note-format
|
|
@@ -34,11 +34,6 @@
|
|
|
34
34
|
|
|
35
35
|
import { HOMEPAGE_FIELDS, HOMEPAGE_TYPE } from "./homepage.mjs";
|
|
36
36
|
|
|
37
|
-
/**
|
|
38
|
-
* Every engine-level content type, and what a note of that type may write.
|
|
39
|
-
*
|
|
40
|
-
* @type {Readonly<Record<string, readonly import("./field-spec.mjs").FieldSpec[]>>}
|
|
41
|
-
*/
|
|
42
37
|
/**
|
|
43
38
|
* A note that compiles to a JournalEntry and nothing else.
|
|
44
39
|
*
|
|
@@ -55,7 +50,7 @@ import { HOMEPAGE_FIELDS, HOMEPAGE_TYPE } from "./homepage.mjs";
|
|
|
55
50
|
* all three and nothing implemented them, so a note using one was reported as
|
|
56
51
|
* having no schema and then *skipped entirely*: `lintNote` returns after that
|
|
57
52
|
* finding, so the note's `data:`, `subType`, references and system block all
|
|
58
|
-
* went unexamined
|
|
53
|
+
* went unexamined.
|
|
59
54
|
*
|
|
60
55
|
* @type {readonly import("./field-spec.mjs").FieldSpec[]}
|
|
61
56
|
*/
|
|
@@ -90,7 +85,7 @@ const BUNDLE_FIELDS = Object.freeze([]);
|
|
|
90
85
|
* Empty for the same reason a bundle's is: a folder declares no system-block
|
|
91
86
|
* fields, because a `Folder` is a core Foundry document rather than any
|
|
92
87
|
* system's. Its two properties — `parent` and `color` — are `data:` keys, held
|
|
93
|
-
* closed by `engine/note-vocabulary.mjs
|
|
88
|
+
* closed by `engine/note-vocabulary.mjs`.
|
|
94
89
|
*
|
|
95
90
|
* @type {readonly import("./field-spec.mjs").FieldSpec[]}
|
|
96
91
|
*/
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
|
|
14
14
|
/**
|
|
15
15
|
* The **closed** half of a note's frontmatter: the `data:` container, and the
|
|
16
|
-
* `subType` each note type declares
|
|
16
|
+
* `subType` each note type declares.
|
|
17
17
|
*
|
|
18
18
|
* A note's frontmatter has three regions, and only one of them is open. The
|
|
19
19
|
* **top level** describes the note as a published artefact, every key of it is
|
|
@@ -42,19 +42,19 @@
|
|
|
42
42
|
* transmission — and is true of it whichever system is reading. What each
|
|
43
43
|
* system makes of that value is declared elsewhere, in that system's own half.
|
|
44
44
|
*
|
|
45
|
-
* **The type names here are the specification's**,
|
|
45
|
+
* **The type names here are the specification's**, which renamed `armor`,
|
|
46
46
|
* `projectile` and `concoction` off the `…gear` spellings that named a SoHL
|
|
47
47
|
* document subtype rather than the thing the note is about. `weapon` is the one
|
|
48
48
|
* the specification and this registry still spell differently: both systems
|
|
49
49
|
* call that document a `weapongear`, so the name says nothing system-specific
|
|
50
|
-
* and
|
|
50
|
+
* and the table has no row for it. A note left on a renamed spelling still
|
|
51
51
|
* reaches its entry — every type-keyed lookup normalises through
|
|
52
52
|
* `RENAMED_TYPES` — and is reported rather than refused until the content trees
|
|
53
53
|
* have swept.
|
|
54
54
|
*
|
|
55
|
-
* **A type name and a subType value are held to the address charset
|
|
56
|
-
* both
|
|
57
|
-
* and the shortcode is already held to. For a type that is literal: it is a
|
|
55
|
+
* **A type name and a subType value are held to the address charset**, so
|
|
56
|
+
* both match `ADDRESS_SEGMENT_PATTERN` — the charset `engine/address-charset.mjs`
|
|
57
|
+
* states and the shortcode is already held to. For a type that is literal: it is a
|
|
58
58
|
* segment of every address — the first of the short form an author writes
|
|
59
59
|
* (`type-shortcode`), the third of the canonical
|
|
60
60
|
* `package-system-type-shortcode` — the hyphen is the separator between
|
|
@@ -63,7 +63,7 @@
|
|
|
63
63
|
* about why.
|
|
64
64
|
*
|
|
65
65
|
* A subType reaches no address of its own. It did when this rule was written —
|
|
66
|
-
* a `doc`'s was its section, a path segment — and
|
|
66
|
+
* a `doc`'s was its section, a path segment — and sections are retired from
|
|
67
67
|
* the note format one release later. It keeps the rule regardless, and the
|
|
68
68
|
* reason is not inertia: a subType is a vocabulary term the whole toolchain
|
|
69
69
|
* keys on, it is one closed set away from being an address again, and a charset
|
|
@@ -76,11 +76,11 @@
|
|
|
76
76
|
*/
|
|
77
77
|
|
|
78
78
|
// The one charset, read rather than restated. A second spelling of the pattern
|
|
79
|
-
// is how
|
|
79
|
+
// is how a disagreement between the three arises.
|
|
80
80
|
import { ADDRESS_SEGMENT_PATTERN, isAddressSegment } from "./address-charset.mjs";
|
|
81
81
|
// The retirement window for a renamed type, read rather than restated: a
|
|
82
82
|
// vocabulary that answered only to the current spelling would report every key
|
|
83
|
-
// of an unswept note as unknown
|
|
83
|
+
// of an unswept note as unknown.
|
|
84
84
|
import { currentType } from "./ids.mjs";
|
|
85
85
|
|
|
86
86
|
/**
|
|
@@ -88,7 +88,7 @@ import { currentType } from "./ids.mjs";
|
|
|
88
88
|
*
|
|
89
89
|
* A deliberate subset of {@link import("./field-spec.mjs").FieldSpec}: no `to`,
|
|
90
90
|
* because nothing here builds anything yet. Reading `data.*` through into a
|
|
91
|
-
* document's `system` block is the passthrough slice
|
|
91
|
+
* document's `system` block is the passthrough slice, and claiming an
|
|
92
92
|
* emitted path this does not produce would be a lie in the one place a reader
|
|
93
93
|
* would trust it.
|
|
94
94
|
*
|
|
@@ -155,9 +155,9 @@ const LINKS = Object.freeze({ shape: "list of wikilinks", kind: "list" });
|
|
|
155
155
|
* the scalar cannot, that the answer *differs by pack*. A folder's `parent` is
|
|
156
156
|
* the case it exists for — a folder's identity is one thing and its hierarchy
|
|
157
157
|
* another, and both large trees file the same folder under a different parent
|
|
158
|
-
* in the items pack and the journals pack
|
|
158
|
+
* in the items pack and the journals pack.
|
|
159
159
|
*
|
|
160
|
-
* Typing it as a bare {@link LINK}
|
|
160
|
+
* Typing it as a bare {@link LINK} makes the compiler read both
|
|
161
161
|
* forms and the lint rejected one of them, so every note using the form the
|
|
162
162
|
* specification prescribes was a finding and no note using it was not.
|
|
163
163
|
*/
|
|
@@ -223,19 +223,22 @@ const CHARGES = Object.freeze([
|
|
|
223
223
|
/* --------------------------------------------------------------------- */
|
|
224
224
|
|
|
225
225
|
/**
|
|
226
|
-
*
|
|
227
|
-
* declares.
|
|
226
|
+
* The declared tag that marks a note as **unfinished**.
|
|
228
227
|
*
|
|
229
|
-
*
|
|
230
|
-
*
|
|
231
|
-
*
|
|
232
|
-
* key will be called — and the disagreement is recorded on the field rather
|
|
233
|
-
* than resolved silently.
|
|
228
|
+
* Named once and referenced from the declaration below, because a second
|
|
229
|
+
* spelling is how the two come apart: rename the tag in `DECLARED_TAGS` and a
|
|
230
|
+
* private copy elsewhere keeps matching the old word, silently.
|
|
234
231
|
*
|
|
235
|
-
*
|
|
232
|
+
* It is a **presentation** fact and nothing more. A draft note compiles,
|
|
233
|
+
* validates, publishes and resolves like any other; only a link *into* it
|
|
234
|
+
* renders marked. What it emphatically is not is the retired `draft:` field,
|
|
235
|
+
* whose entire effect was to move a note from published to unresolvable — see
|
|
236
|
+
* {@link draftRetiredMessage}.
|
|
236
237
|
*/
|
|
238
|
+
export const DRAFT_TAG = "draft";
|
|
239
|
+
|
|
237
240
|
/**
|
|
238
|
-
* The tags that **classify** a note, grouped by what they classify
|
|
241
|
+
* The tags that **classify** a note, grouped by what they classify.
|
|
239
242
|
*
|
|
240
243
|
* `tags:` lives at the open top level and most tags belong there: a theme, a
|
|
241
244
|
* region, a working state is the author's own and this build has no opinion
|
|
@@ -260,21 +263,6 @@ const CHARGES = Object.freeze([
|
|
|
260
263
|
* fishing village is a `village` that is `fishing`, and the single-valued field
|
|
261
264
|
* this replaced had to spell it `Fishing Village` as a value of its own.
|
|
262
265
|
*/
|
|
263
|
-
/**
|
|
264
|
-
* The declared tag that marks a note as **unfinished** (#183).
|
|
265
|
-
*
|
|
266
|
-
* Named once and referenced from the declaration below, because a second
|
|
267
|
-
* spelling is how the two come apart: rename the tag in `DECLARED_TAGS` and a
|
|
268
|
-
* private copy elsewhere keeps matching the old word, silently.
|
|
269
|
-
*
|
|
270
|
-
* It is a **presentation** fact and nothing more. A draft note compiles,
|
|
271
|
-
* validates, publishes and resolves like any other; only a link *into* it
|
|
272
|
-
* renders marked. What it emphatically is not is the retired `draft:` field,
|
|
273
|
-
* whose entire effect was to move a note from published to unresolvable — see
|
|
274
|
-
* {@link draftRetiredMessage}.
|
|
275
|
-
*/
|
|
276
|
-
export const DRAFT_TAG = "draft";
|
|
277
|
-
|
|
278
266
|
export const DECLARED_TAGS = Object.freeze({
|
|
279
267
|
/** What a place *is*. */
|
|
280
268
|
placeKind: Object.freeze({
|
|
@@ -399,7 +387,7 @@ export function hasTag(fm, tag) {
|
|
|
399
387
|
}
|
|
400
388
|
|
|
401
389
|
/**
|
|
402
|
-
* Whether a note is tagged as an unfinished **draft
|
|
390
|
+
* Whether a note is tagged as an unfinished **draft**.
|
|
403
391
|
*
|
|
404
392
|
* The one reader of {@link DRAFT_TAG}, so both builds ask the same question of
|
|
405
393
|
* the same field. Presentation only: a draft note is in the packs, in the
|
|
@@ -413,12 +401,24 @@ export function isDraftNote(fm) {
|
|
|
413
401
|
return hasTag(fm, DRAFT_TAG);
|
|
414
402
|
}
|
|
415
403
|
|
|
404
|
+
/**
|
|
405
|
+
* Every note type this toolchain compiles, and the closed vocabulary it
|
|
406
|
+
* declares.
|
|
407
|
+
*
|
|
408
|
+
* Taken from the content-format specification, one `### type:` section per
|
|
409
|
+
* entry. Where the specification and the shape notes are authored in today
|
|
410
|
+
* disagree, the specification wins on the **name** — that is what a `data:`
|
|
411
|
+
* key will be called — and the disagreement is recorded on the field rather
|
|
412
|
+
* than resolved silently.
|
|
413
|
+
*
|
|
414
|
+
* @type {Readonly<Record<string, TypeVocabulary>>}
|
|
415
|
+
*/
|
|
416
416
|
export const NOTE_VOCABULARY = Object.freeze({
|
|
417
417
|
/* ----- actors --------------------------------------------------- */
|
|
418
418
|
|
|
419
419
|
being: Object.freeze({
|
|
420
420
|
// Derived from the note's `(type, subType)` by each system's map, which
|
|
421
|
-
// lands with
|
|
421
|
+
// lands with. Declared open until it does, because inventing the
|
|
422
422
|
// values here would put a second, weaker answer beside the real one.
|
|
423
423
|
subTypes: null,
|
|
424
424
|
data: Object.freeze([
|
|
@@ -793,24 +793,25 @@ export const NOTE_VOCABULARY = Object.freeze({
|
|
|
793
793
|
/* ----- core documents ------------------------------------------- */
|
|
794
794
|
|
|
795
795
|
doc: Object.freeze({
|
|
796
|
-
//
|
|
797
|
-
//
|
|
798
|
-
//
|
|
799
|
-
//
|
|
800
|
-
//
|
|
801
|
-
|
|
796
|
+
// Five genres, and a genre is all this field carries: what kind of page
|
|
797
|
+
// it is, never who reads it. An audience term alongside them would give
|
|
798
|
+
// a developer how-to two valid values and no rule for choosing.
|
|
799
|
+
//
|
|
800
|
+
// `userguide` and `howto`, not `user-guide` and `how-to`: a subType is
|
|
801
|
+
// held to the address charset, and a segment carries no hyphen.
|
|
802
|
+
subTypes: Object.freeze(["rules", "userguide", "reference", "howto", "concept"]),
|
|
802
803
|
data: Object.freeze([]),
|
|
803
804
|
}),
|
|
804
805
|
|
|
805
806
|
macro: Object.freeze({ data: Object.freeze([]) }),
|
|
806
807
|
|
|
807
808
|
// Foundry's `Folder`, and the last document this package compiled from
|
|
808
|
-
// bespoke configuration rather than from a note
|
|
809
|
+
// bespoke configuration rather than from a note. It declares no
|
|
809
810
|
// system-block fields, like a bundle: a `Folder` is a core Foundry
|
|
810
811
|
// document, so its address carries the `none` system segment and
|
|
811
812
|
// everything it says is a `data` property.
|
|
812
813
|
//
|
|
813
|
-
// It carries **no prose
|
|
814
|
+
// It carries **no prose**: a folder
|
|
814
815
|
// is structure, not content, so it wants no documentation journal and takes
|
|
815
816
|
// no part in `docEntryTypes`.
|
|
816
817
|
folder: Object.freeze({
|
|
@@ -836,7 +837,7 @@ export const NOTE_VOCABULARY = Object.freeze({
|
|
|
836
837
|
// calls it: a set of documents taken as a unit. The document is an
|
|
837
838
|
// installer — it carries copies, and importing one creates or updates each
|
|
838
839
|
// document in the world — which is what separates a bundle from a folder,
|
|
839
|
-
// a live grouping that persists in the pack
|
|
840
|
+
// a live grouping that persists in the pack.
|
|
840
841
|
//
|
|
841
842
|
// How many Adventures a bundle makes is decided by its system blocks, as
|
|
842
843
|
// for every other type, and not by a property: an `Adventure` has no
|
|
@@ -940,12 +941,12 @@ export const NOTE_VOCABULARY = Object.freeze({
|
|
|
940
941
|
|
|
941
942
|
map: Object.freeze({
|
|
942
943
|
// One type, three subTypes: they differ only in the canvas defaults
|
|
943
|
-
// derived for them, which is precisely what a subType decides
|
|
944
|
+
// derived for them, which is precisely what a subType decides.
|
|
944
945
|
subTypes: Object.freeze(["battlemap", "localmap", "regionalmap"]),
|
|
945
946
|
data: Object.freeze([
|
|
946
947
|
// `img`, as every other note type spells its artwork. A map alone
|
|
947
948
|
// read `image` out of its `sohl:` block; that spelling is retired
|
|
948
|
-
// and gone
|
|
949
|
+
// and gone, so the two names are one again.
|
|
949
950
|
{
|
|
950
951
|
name: "img",
|
|
951
952
|
...TEXT,
|
|
@@ -1001,10 +1002,11 @@ export const NOTE_VOCABULARY = Object.freeze({
|
|
|
1001
1002
|
/**
|
|
1002
1003
|
* What a note carrying a subType outside the address charset is told.
|
|
1003
1004
|
*
|
|
1004
|
-
* **Why the charset holds for a subType, which reaches no address.**
|
|
1005
|
+
* **Why the charset holds for a subType, which reaches no address.** The rule
|
|
1006
|
+
* said
|
|
1005
1007
|
* "the hyphen separates the segments of an address", and that was true of a
|
|
1006
1008
|
* subType when it shipped: `sectionOf` returned a `doc`'s subType, so the value
|
|
1007
|
-
* was a URL path segment.
|
|
1009
|
+
* was a URL path segment. Sections are retired and it is not one. The rule
|
|
1008
1010
|
* stays, on its own footing: a subType is a vocabulary term the whole toolchain
|
|
1009
1011
|
* keys on, and it is one closed set away from being an address segment again —
|
|
1010
1012
|
* so the reason to spell it in the address charset is that a charset holding
|
|
@@ -1054,8 +1056,8 @@ export function typeCharsetMessage(type) {
|
|
|
1054
1056
|
*
|
|
1055
1057
|
* The message states the reason **per key**, as {@link typeCharsetMessage} and
|
|
1056
1058
|
* {@link subTypeCharsetMessage} do: a type is an address segment, and a subType
|
|
1057
|
-
*
|
|
1058
|
-
* would be half wrong
|
|
1059
|
+
* is not one, since sections are retired, so a single claim covering both
|
|
1060
|
+
* would be half wrong.
|
|
1059
1061
|
*
|
|
1060
1062
|
* @param {Readonly<Record<string, TypeVocabulary>>} vocabulary - The registry.
|
|
1061
1063
|
* @param {string} [where] - What declares it, for the message.
|
|
@@ -1077,7 +1079,7 @@ export function assertVocabularyCharset(vocabulary, where = "the note vocabulary
|
|
|
1077
1079
|
`${where} declares ${bad.join(", ")}, which ${bad.length === 1 ? "is" : "are"} ` +
|
|
1078
1080
|
`not ${ADDRESS_SEGMENT_PATTERN.source}. A type is an address segment, ` +
|
|
1079
1081
|
`and the hyphen separates segments rather than occurring inside one. ` +
|
|
1080
|
-
`A subType reaches no address
|
|
1082
|
+
`A subType reaches no address, and is ` +
|
|
1081
1083
|
`held to the same charset anyway: it is a vocabulary term the whole ` +
|
|
1082
1084
|
`toolchain keys on, one closed set away from being a segment again, ` +
|
|
1083
1085
|
`and a charset holding for every term but that one would be a rule ` +
|
package/engine/pack-config.mjs
CHANGED
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
* One module, one import: everything the compilers used to hard-code — 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
|
-
* consuming repository's `package-build.config.yaml
|
|
20
|
+
* consuming repository's `package-build.config.yaml`.
|
|
21
21
|
*
|
|
22
22
|
* **The configuration is data, and a repository writes it as data.** Every
|
|
23
23
|
* value in it is a literal; the three consumers' configs held no logic between
|
|
@@ -58,7 +58,7 @@
|
|
|
58
58
|
* resolution walks parent directories. `import.meta.dirname` is then inside the
|
|
59
59
|
* parent, the walk lands on the parent's configuration, and the build compiles
|
|
60
60
|
* the parent's content tree into the parent's `build/` and exits 0 — saying so
|
|
61
|
-
* only in paths that are easy to read past
|
|
61
|
+
* only in paths that are easy to read past.
|
|
62
62
|
*
|
|
63
63
|
* That failure is undetectable on exactly the work most likely to provoke it.
|
|
64
64
|
* The usual tell is a zero diff where a change was expected; an
|
|
@@ -75,7 +75,7 @@
|
|
|
75
75
|
* `node_modules`. `PACKAGE_BUILD_CONFIG` names the file explicitly and skips
|
|
76
76
|
* both walks, which is why it was the workaround.
|
|
77
77
|
*
|
|
78
|
-
* What
|
|
78
|
+
* What was removed stays removed. The property it bought was not "resolve
|
|
79
79
|
* from the module"; it was that a build reads one tree however it was launched,
|
|
80
80
|
* and an upward walk from the working directory keeps that — every directory
|
|
81
81
|
* inside a repository resolves that repository's single configuration.
|
|
@@ -104,7 +104,7 @@ import path from "node:path";
|
|
|
104
104
|
import { createRequire } from "node:module";
|
|
105
105
|
import YAML from "yaml";
|
|
106
106
|
|
|
107
|
-
import { defineConfig, DERIVED_SYSTEM_VERSION } from "../content-config.mjs";
|
|
107
|
+
import { defineConfig, DERIVED_SYSTEM_VERSION, DOCUMENTATION_KIND } from "../content-config.mjs";
|
|
108
108
|
import {
|
|
109
109
|
emitDiagnostic,
|
|
110
110
|
formatDiagnostic,
|
|
@@ -179,7 +179,7 @@ export function findConfigFile(from) {
|
|
|
179
179
|
*
|
|
180
180
|
* Kept separate from {@link loadPackConfig} because the *choice* is worth being
|
|
181
181
|
* able to ask about without loading anything: the two walks disagreeing is the
|
|
182
|
-
* observable form of
|
|
182
|
+
* observable form of that, and a caller that wants to report it — or a test
|
|
183
183
|
* that wants to describe it — should not have to reproduce the resolution and
|
|
184
184
|
* risk disagreeing with the loader about it. It performs I/O, and is named for
|
|
185
185
|
* it, like the {@link findConfigFile} it calls twice.
|
|
@@ -244,7 +244,7 @@ const ITEM_BUILDER_REGISTRIES = Object.freeze({
|
|
|
244
244
|
*
|
|
245
245
|
* `stats.systemVersion` is stamped into every compiled document, and a
|
|
246
246
|
* transcribed copy of it froze at `0.6.0` for four releases before anyone
|
|
247
|
-
* noticed
|
|
247
|
+
* noticed. `package.json` is the file Changesets bumps, so reading it
|
|
248
248
|
* is what keeps the stamp equal to the version that did the compiling.
|
|
249
249
|
*
|
|
250
250
|
* The read happens *here*, in the loader, rather than in `defineConfig`:
|
|
@@ -309,7 +309,7 @@ function foundryPackageId(rootDir) {
|
|
|
309
309
|
* the whole point:
|
|
310
310
|
*
|
|
311
311
|
* - A **system** ships itself, so its own `package.json` version *is* the
|
|
312
|
-
* system version. That is the read
|
|
312
|
+
* system version. That is the read to make, because a transcribed copy
|
|
313
313
|
* froze at `0.6.0` for four releases.
|
|
314
314
|
* - A **module** ships content *for* someone else's system. Its own version is
|
|
315
315
|
* the module's — `sohl-thalorna` sits at `0.0.1` — so deriving from it would
|
|
@@ -343,7 +343,7 @@ function shippedSystemVersion(rootDir, input) {
|
|
|
343
343
|
const declaredSystems = /** @type {Record<string, unknown>} */ (input.relationships ?? {})
|
|
344
344
|
.systems;
|
|
345
345
|
|
|
346
|
-
// The `systems:` block declares without requiring
|
|
346
|
+
// The `systems:` block declares without requiring, so it is consulted
|
|
347
347
|
// first: a package that has adopted it needs no relationship, and one that
|
|
348
348
|
// ships for two systems could not express itself through a relationship at
|
|
349
349
|
// all. `requiresSystem` names the package-wide default when there is one;
|
|
@@ -373,7 +373,7 @@ function shippedSystemVersion(rootDir, input) {
|
|
|
373
373
|
// system data, and it installs under any system. There is no version to
|
|
374
374
|
// stamp, and inventing one would be the very thing the throw below guards
|
|
375
375
|
// against. The two signals together are what separate this from a module
|
|
376
|
-
// that simply forgot to declare its system
|
|
376
|
+
// that simply forgot to declare its system.
|
|
377
377
|
if (
|
|
378
378
|
(systemId === undefined || systemId === null) &&
|
|
379
379
|
!(Array.isArray(declaredSystems) && declaredSystems.length)
|
|
@@ -453,7 +453,7 @@ function positionInConfig(configPath, field) {
|
|
|
453
453
|
* worse than locating none — a reader would learn that some configuration
|
|
454
454
|
* errors carry a position and could not predict which — so the path rides on
|
|
455
455
|
* the error and every one of them is located here, at the boundary that knows
|
|
456
|
-
* which file was read
|
|
456
|
+
* which file was read.
|
|
457
457
|
*
|
|
458
458
|
* The message keeps its body and gains the `file:line:column: error: ` prefix
|
|
459
459
|
* every other finding in this build already uses, so nothing a reader has today
|
|
@@ -500,7 +500,7 @@ export function locateConfigError(err, configPath) {
|
|
|
500
500
|
* cannot write `import.meta.dirname`, and any absolute path it wrote instead
|
|
501
501
|
* would be one machine's — so authoring it is rejected rather than honoured.
|
|
502
502
|
* - **`itemBuilders`** is a *name* (`sohl`) — or a list of names, for a tree
|
|
503
|
-
* feeding more than one system
|
|
503
|
+
* feeding more than one system — resolved against the built-in
|
|
504
504
|
* registries. A registry's name is the system it belongs to. A registry of a
|
|
505
505
|
* consumer's own is code, and code goes in an `.mjs` configuration.
|
|
506
506
|
* - **`stats.systemVersion`** is derived from the adjacent `package.json` when
|
|
@@ -513,7 +513,7 @@ export function locateConfigError(err, configPath) {
|
|
|
513
513
|
*
|
|
514
514
|
* @param {unknown} data - The parsed configuration document.
|
|
515
515
|
* @param {string} configPath - Absolute path of the file it was parsed from.
|
|
516
|
-
* @returns {import("../config.mjs").ContentBuildConfig} The frozen configuration.
|
|
516
|
+
* @returns {import("../content-config.mjs").ContentBuildConfig} The frozen configuration.
|
|
517
517
|
* @throws {Error} When the document is not a mapping, declares `rootDir`, or
|
|
518
518
|
* names an item-builder registry this package does not ship.
|
|
519
519
|
*/
|
|
@@ -539,22 +539,27 @@ export function configFromData(data, configPath) {
|
|
|
539
539
|
|
|
540
540
|
// Transcribed from `package.json`, and therefore free to disagree with it.
|
|
541
541
|
// Every consumer's copy matched exactly, which is what a transcription
|
|
542
|
-
// looks like right up until it does not (
|
|
542
|
+
// looks like right up until it does not (a transcribed one freezes
|
|
543
543
|
// four releases while nothing said so).
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
544
|
+
// A documentation package has no Foundry package, so there is no id to
|
|
545
|
+
// derive: the validator refuses the key by name, with a locator, and
|
|
546
|
+
// deriving one here would hand it the very key it is about to refuse.
|
|
547
|
+
if (input.packageKind !== DOCUMENTATION_KIND) {
|
|
548
|
+
if (input.foundryPackage !== undefined) {
|
|
549
|
+
throw new Error(
|
|
550
|
+
`package-build: ${configPath} declares \`foundryPackage\`, which ` +
|
|
551
|
+
`a data configuration may not: it is the \`name\` of the ` +
|
|
552
|
+
`\`package.json\` beside it. Remove the key.`,
|
|
553
|
+
);
|
|
554
|
+
}
|
|
555
|
+
input.foundryPackage = foundryPackageId(rootDir);
|
|
550
556
|
}
|
|
551
|
-
input.foundryPackage = foundryPackageId(rootDir);
|
|
552
557
|
|
|
553
558
|
if (input.itemBuilders !== undefined) {
|
|
554
559
|
const declared = input.itemBuilders;
|
|
555
560
|
const known = Object.keys(ITEM_BUILDER_REGISTRIES).join(", ");
|
|
556
561
|
// One name or several. A repository feeding two systems needs both
|
|
557
|
-
// vocabularies, and one registry can only carry one
|
|
562
|
+
// vocabularies, and one registry can only carry one; the scalar
|
|
558
563
|
// form every existing configuration uses is the one-element case and
|
|
559
564
|
// means exactly what it always did.
|
|
560
565
|
const names = Array.isArray(declared) ? declared : [declared];
|
|
@@ -590,7 +595,16 @@ export function configFromData(data, configPath) {
|
|
|
590
595
|
}
|
|
591
596
|
|
|
592
597
|
const stats = input.stats;
|
|
593
|
-
|
|
598
|
+
// Skipped for a documentation package, which stamps no `_stats` and
|
|
599
|
+
// declares no system to take a version from: the derivation would throw a
|
|
600
|
+
// bare error about a missing system relationship, burying the located
|
|
601
|
+
// refusal of the `stats:` key that is the finding an author needs.
|
|
602
|
+
if (
|
|
603
|
+
input.packageKind !== DOCUMENTATION_KIND &&
|
|
604
|
+
stats !== null &&
|
|
605
|
+
typeof stats === "object" &&
|
|
606
|
+
!Array.isArray(stats)
|
|
607
|
+
) {
|
|
594
608
|
const declared = /** @type {Record<string, unknown>} */ (stats);
|
|
595
609
|
// `stats.systemId` and `stats.systemVersion` are both refused by
|
|
596
610
|
// `defineConfig`, which reports them with a locator — so nothing is
|
|
@@ -620,7 +634,7 @@ export function configFromData(data, configPath) {
|
|
|
620
634
|
* Load an `.mjs` configuration — one that called `defineConfig` itself.
|
|
621
635
|
*
|
|
622
636
|
* @param {string} configPath - Absolute path of the file.
|
|
623
|
-
* @returns {import("../config.mjs").ContentBuildConfig} What it exported.
|
|
637
|
+
* @returns {import("../content-config.mjs").ContentBuildConfig} What it exported.
|
|
624
638
|
* @throws {Error} When its module graph uses top-level `await`, which a
|
|
625
639
|
* synchronously-read configuration cannot.
|
|
626
640
|
*/
|
|
@@ -662,7 +676,7 @@ let loadedFrom;
|
|
|
662
676
|
* configuration (#2). The result is memoised, so calling it in a default
|
|
663
677
|
* parameter — the usual spelling here — costs one property read per call.
|
|
664
678
|
*
|
|
665
|
-
* @returns {import("../config.mjs").ContentBuildConfig} The frozen configuration.
|
|
679
|
+
* @returns {import("../content-config.mjs").ContentBuildConfig} The frozen configuration.
|
|
666
680
|
* @throws {Error} When no configuration file can be found, or the one named
|
|
667
681
|
* cannot be loaded. Absence is a defect, not a fallback: without it the
|
|
668
682
|
* compilers know neither what to compile nor where to put it.
|
|
@@ -691,7 +705,7 @@ export function loadPackConfig() {
|
|
|
691
705
|
}
|
|
692
706
|
|
|
693
707
|
// Two different files, one of which is about to be ignored. Said out loud
|
|
694
|
-
// because the alternative is
|
|
708
|
+
// because the alternative is a build that reads the parent
|
|
695
709
|
// checkout's configuration, compiles the parent's tree, and reports it only
|
|
696
710
|
// in absolute paths nobody rereads. A warning rather than an error — the
|
|
697
711
|
// shape is legitimate, and the working directory's answer is the right one
|
package/engine/pack-router.mjs
CHANGED
|
Binary file
|