@heroiclands/package-build 20.3.1 → 20.4.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 +75 -0
- package/CONTENT.md +111 -92
- package/README.md +2 -9
- package/bin/content-build.mjs +70 -61
- package/bin/package-build.mjs +2 -2
- package/config.mjs +6 -8
- package/content-config.mjs +187 -64
- package/docs/content-format.md +57 -68
- package/e2e.mjs +4 -4
- package/engine/actor-compiler.mjs +36 -36
- package/engine/address-charset.mjs +6 -6
- package/engine/address-diff.mjs +12 -11
- package/engine/anchored-sections.mjs +1 -1
- package/engine/anchors.mjs +1 -1
- package/engine/base-compiler.mjs +36 -38
- package/engine/bundle-notes.mjs +4 -4
- package/engine/bundles.mjs +8 -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 +15 -15
- 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 +17 -17
- package/engine/content-links.mjs +30 -30
- package/engine/content-lint.mjs +20 -24
- package/engine/content-package.mjs +7 -9
- package/engine/content-slug.mjs +2 -2
- package/engine/content-tables.mjs +7 -8
- 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 +14 -14
- package/engine/foreign-catalog.mjs +8 -9
- package/engine/foundry-entries.mjs +19 -20
- package/engine/frontmatter-lint.mjs +64 -69
- package/engine/frontmatter.mjs +11 -11
- package/engine/generate.mjs +39 -39
- package/engine/helpers.mjs +63 -54
- package/engine/homepage.mjs +20 -22
- package/engine/ids.mjs +15 -15
- package/engine/index-records.mjs +4 -4
- package/engine/index.mjs +14 -8
- 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 +15 -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 +36 -34
- 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 -3
- package/engine/note-vocabulary.mjs +27 -26
- package/engine/pack-config.mjs +13 -13
- package/engine/pack-router.mjs +0 -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 +19 -20
- package/engine/schema-check.mjs +20 -20
- package/engine/schema-extract.mjs +1 -1
- package/engine/site-build.mjs +45 -47
- 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 +35 -35
- package/engine/wikilink-syntax.mjs +16 -16
- package/engine/wikilinks.mjs +39 -39
- package/hm3/actors.mjs +22 -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 +20 -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 +3 -3
- package/sohl/actors.mjs +28 -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 +20 -6
- package/sohl/kb-passes.mjs +5 -5
- package/sohl/note-schemas.mjs +7 -7
- package/sohl/skill-base.mjs +1 -1
- package/types/content-config.d.mts +25 -24
- package/types/e2e.d.mts +3 -3
- package/types/engine/actor-compiler.d.mts +14 -13
- package/types/engine/address-charset.d.mts +6 -6
- package/types/engine/address-diff.d.mts +3 -3
- package/types/engine/base-compiler.d.mts +17 -19
- package/types/engine/bundle-notes.d.mts +3 -3
- package/types/engine/bundles.d.mts +1 -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 +11 -11
- 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 +2 -2
- package/types/engine/content-links.d.mts +8 -8
- package/types/engine/content-lint.d.mts +2 -2
- package/types/engine/content-package.d.mts +6 -8
- 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 +7 -7
- package/types/engine/frontmatter.d.mts +7 -7
- package/types/engine/generate.d.mts +7 -7
- package/types/engine/helpers.d.mts +25 -27
- 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 +2 -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 +3 -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 +16 -15
- 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 +9 -8
- package/types/engine/pack-config.d.mts +3 -3
- package/types/engine/pack-router.d.mts +1 -1
- 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/schema-check.d.mts +11 -11
- package/types/engine/site-build.d.mts +18 -19
- 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 +13 -12
- package/types/hm3/actors.d.mts +1 -1
- package/types/hm3/document-subtypes.d.mts +1 -1
- package/types/hm3/items.d.mts +1 -2
- package/types/hm3/template-priority.d.mts +1 -1
- package/types/manifest.d.mts +8 -8
- package/types/sohl/actors.d.mts +2 -2
- 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 +1 -2
- package/types/sohl/note-schemas.d.mts +1 -1
- package/MIGRATING.md +0 -608
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
|
|
@@ -55,7 +55,7 @@ import { HOMEPAGE_FIELDS, HOMEPAGE_TYPE } from "./homepage.mjs";
|
|
|
55
55
|
* all three and nothing implemented them, so a note using one was reported as
|
|
56
56
|
* having no schema and then *skipped entirely*: `lintNote` returns after that
|
|
57
57
|
* finding, so the note's `data:`, `subType`, references and system block all
|
|
58
|
-
* went unexamined
|
|
58
|
+
* went unexamined.
|
|
59
59
|
*
|
|
60
60
|
* @type {readonly import("./field-spec.mjs").FieldSpec[]}
|
|
61
61
|
*/
|
|
@@ -90,7 +90,7 @@ const BUNDLE_FIELDS = Object.freeze([]);
|
|
|
90
90
|
* Empty for the same reason a bundle's is: a folder declares no system-block
|
|
91
91
|
* fields, because a `Folder` is a core Foundry document rather than any
|
|
92
92
|
* system's. Its two properties — `parent` and `color` — are `data:` keys, held
|
|
93
|
-
* closed by `engine/note-vocabulary.mjs
|
|
93
|
+
* closed by `engine/note-vocabulary.mjs`.
|
|
94
94
|
*
|
|
95
95
|
* @type {readonly import("./field-spec.mjs").FieldSpec[]}
|
|
96
96
|
*/
|
|
@@ -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,17 +42,17 @@
|
|
|
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
|
|
55
|
+
* **A type name and a subType value are held to the address charset**, so
|
|
56
56
|
* both are `^[A-Za-z0-9]+$` — the charset `engine/address-charset.mjs` states
|
|
57
57
|
* 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
|
|
@@ -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
|
*/
|
|
@@ -235,7 +235,7 @@ const CHARGES = Object.freeze([
|
|
|
235
235
|
* @type {Readonly<Record<string, TypeVocabulary>>}
|
|
236
236
|
*/
|
|
237
237
|
/**
|
|
238
|
-
* The tags that **classify** a note, grouped by what they classify
|
|
238
|
+
* The tags that **classify** a note, grouped by what they classify.
|
|
239
239
|
*
|
|
240
240
|
* `tags:` lives at the open top level and most tags belong there: a theme, a
|
|
241
241
|
* region, a working state is the author's own and this build has no opinion
|
|
@@ -261,7 +261,7 @@ const CHARGES = Object.freeze([
|
|
|
261
261
|
* this replaced had to spell it `Fishing Village` as a value of its own.
|
|
262
262
|
*/
|
|
263
263
|
/**
|
|
264
|
-
* The declared tag that marks a note as **unfinished
|
|
264
|
+
* The declared tag that marks a note as **unfinished**.
|
|
265
265
|
*
|
|
266
266
|
* Named once and referenced from the declaration below, because a second
|
|
267
267
|
* spelling is how the two come apart: rename the tag in `DECLARED_TAGS` and a
|
|
@@ -399,7 +399,7 @@ export function hasTag(fm, tag) {
|
|
|
399
399
|
}
|
|
400
400
|
|
|
401
401
|
/**
|
|
402
|
-
* Whether a note is tagged as an unfinished **draft
|
|
402
|
+
* Whether a note is tagged as an unfinished **draft**.
|
|
403
403
|
*
|
|
404
404
|
* The one reader of {@link DRAFT_TAG}, so both builds ask the same question of
|
|
405
405
|
* the same field. Presentation only: a draft note is in the packs, in the
|
|
@@ -418,7 +418,7 @@ export const NOTE_VOCABULARY = Object.freeze({
|
|
|
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([
|
|
@@ -794,10 +794,10 @@ export const NOTE_VOCABULARY = Object.freeze({
|
|
|
794
794
|
|
|
795
795
|
doc: Object.freeze({
|
|
796
796
|
// `userguide`, not `user-guide`: a subType is held to the address
|
|
797
|
-
// charset, and a segment carries no hyphen
|
|
797
|
+
// charset, and a segment carries no hyphen. The old spelling was
|
|
798
798
|
// accepted transitionally for one release so the consumer trees could
|
|
799
799
|
// sweep; they have, so it is refused by the charset check now, with no
|
|
800
|
-
// retirement-specific code left over
|
|
800
|
+
// retirement-specific code left over.
|
|
801
801
|
subTypes: Object.freeze(["rules", "userguide", "reference"]),
|
|
802
802
|
data: Object.freeze([]),
|
|
803
803
|
}),
|
|
@@ -805,12 +805,12 @@ export const NOTE_VOCABULARY = Object.freeze({
|
|
|
805
805
|
macro: Object.freeze({ data: Object.freeze([]) }),
|
|
806
806
|
|
|
807
807
|
// Foundry's `Folder`, and the last document this package compiled from
|
|
808
|
-
// bespoke configuration rather than from a note
|
|
808
|
+
// bespoke configuration rather than from a note. It declares no
|
|
809
809
|
// system-block fields, like a bundle: a `Folder` is a core Foundry
|
|
810
810
|
// document, so its address carries the `none` system segment and
|
|
811
811
|
// everything it says is a `data` property.
|
|
812
812
|
//
|
|
813
|
-
// It carries **no prose
|
|
813
|
+
// It carries **no prose**: a folder
|
|
814
814
|
// is structure, not content, so it wants no documentation journal and takes
|
|
815
815
|
// no part in `docEntryTypes`.
|
|
816
816
|
folder: Object.freeze({
|
|
@@ -836,7 +836,7 @@ export const NOTE_VOCABULARY = Object.freeze({
|
|
|
836
836
|
// calls it: a set of documents taken as a unit. The document is an
|
|
837
837
|
// installer — it carries copies, and importing one creates or updates each
|
|
838
838
|
// document in the world — which is what separates a bundle from a folder,
|
|
839
|
-
// a live grouping that persists in the pack
|
|
839
|
+
// a live grouping that persists in the pack.
|
|
840
840
|
//
|
|
841
841
|
// How many Adventures a bundle makes is decided by its system blocks, as
|
|
842
842
|
// for every other type, and not by a property: an `Adventure` has no
|
|
@@ -940,12 +940,12 @@ export const NOTE_VOCABULARY = Object.freeze({
|
|
|
940
940
|
|
|
941
941
|
map: Object.freeze({
|
|
942
942
|
// One type, three subTypes: they differ only in the canvas defaults
|
|
943
|
-
// derived for them, which is precisely what a subType decides
|
|
943
|
+
// derived for them, which is precisely what a subType decides.
|
|
944
944
|
subTypes: Object.freeze(["battlemap", "localmap", "regionalmap"]),
|
|
945
945
|
data: Object.freeze([
|
|
946
946
|
// `img`, as every other note type spells its artwork. A map alone
|
|
947
947
|
// read `image` out of its `sohl:` block; that spelling is retired
|
|
948
|
-
// and gone
|
|
948
|
+
// and gone, so the two names are one again.
|
|
949
949
|
{
|
|
950
950
|
name: "img",
|
|
951
951
|
...TEXT,
|
|
@@ -1001,10 +1001,11 @@ export const NOTE_VOCABULARY = Object.freeze({
|
|
|
1001
1001
|
/**
|
|
1002
1002
|
* What a note carrying a subType outside the address charset is told.
|
|
1003
1003
|
*
|
|
1004
|
-
* **Why the charset holds for a subType, which reaches no address.**
|
|
1004
|
+
* **Why the charset holds for a subType, which reaches no address.** The rule
|
|
1005
|
+
* said
|
|
1005
1006
|
* "the hyphen separates the segments of an address", and that was true of a
|
|
1006
1007
|
* subType when it shipped: `sectionOf` returned a `doc`'s subType, so the value
|
|
1007
|
-
* was a URL path segment.
|
|
1008
|
+
* was a URL path segment. Sections are retired and it is not one. The rule
|
|
1008
1009
|
* stays, on its own footing: a subType is a vocabulary term the whole toolchain
|
|
1009
1010
|
* keys on, and it is one closed set away from being an address segment again —
|
|
1010
1011
|
* so the reason to spell it in the address charset is that a charset holding
|
|
@@ -1054,8 +1055,8 @@ export function typeCharsetMessage(type) {
|
|
|
1054
1055
|
*
|
|
1055
1056
|
* The message states the reason **per key**, as {@link typeCharsetMessage} and
|
|
1056
1057
|
* {@link subTypeCharsetMessage} do: a type is an address segment, and a subType
|
|
1057
|
-
*
|
|
1058
|
-
* would be half wrong
|
|
1058
|
+
* is not one, since sections are retired, so a single claim covering both
|
|
1059
|
+
* would be half wrong.
|
|
1059
1060
|
*
|
|
1060
1061
|
* @param {Readonly<Record<string, TypeVocabulary>>} vocabulary - The registry.
|
|
1061
1062
|
* @param {string} [where] - What declares it, for the message.
|
|
@@ -1077,7 +1078,7 @@ export function assertVocabularyCharset(vocabulary, where = "the note vocabulary
|
|
|
1077
1078
|
`${where} declares ${bad.join(", ")}, which ${bad.length === 1 ? "is" : "are"} ` +
|
|
1078
1079
|
`not ${ADDRESS_SEGMENT_PATTERN.source}. A type is an address segment, ` +
|
|
1079
1080
|
`and the hyphen separates segments rather than occurring inside one. ` +
|
|
1080
|
-
`A subType reaches no address
|
|
1081
|
+
`A subType reaches no address, and is ` +
|
|
1081
1082
|
`held to the same charset anyway: it is a vocabulary term the whole ` +
|
|
1082
1083
|
`toolchain keys on, one closed set away from being a segment again, ` +
|
|
1083
1084
|
`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.
|
|
@@ -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
|
|
@@ -539,7 +539,7 @@ 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
544
|
if (input.foundryPackage !== undefined) {
|
|
545
545
|
throw new Error(
|
|
@@ -554,7 +554,7 @@ export function configFromData(data, configPath) {
|
|
|
554
554
|
const declared = input.itemBuilders;
|
|
555
555
|
const known = Object.keys(ITEM_BUILDER_REGISTRIES).join(", ");
|
|
556
556
|
// One name or several. A repository feeding two systems needs both
|
|
557
|
-
// vocabularies, and one registry can only carry one
|
|
557
|
+
// vocabularies, and one registry can only carry one; the scalar
|
|
558
558
|
// form every existing configuration uses is the one-element case and
|
|
559
559
|
// means exactly what it always did.
|
|
560
560
|
const names = Array.isArray(declared) ? declared : [declared];
|
|
@@ -691,7 +691,7 @@ export function loadPackConfig() {
|
|
|
691
691
|
}
|
|
692
692
|
|
|
693
693
|
// Two different files, one of which is about to be ignored. Said out loud
|
|
694
|
-
// because the alternative is
|
|
694
|
+
// because the alternative is a build that reads the parent
|
|
695
695
|
// checkout's configuration, compiles the parent's tree, and reports it only
|
|
696
696
|
// in absolute paths nobody rereads. A warning rather than an error — the
|
|
697
697
|
// shape is legitimate, and the working directory's answer is the right one
|
package/engine/pack-router.mjs
CHANGED
|
Binary file
|
package/engine/prose-config.mjs
CHANGED
|
@@ -14,14 +14,14 @@
|
|
|
14
14
|
/**
|
|
15
15
|
* The prose conventions every content repository writes to — one Prettier
|
|
16
16
|
* configuration and one markdownlint rule set, declared here so a note
|
|
17
|
-
* formatted in one repository is formatted the same way in the next
|
|
17
|
+
* formatted in one repository is formatted the same way in the next.
|
|
18
18
|
*
|
|
19
|
-
* These
|
|
19
|
+
* These belong in no single consumer. The SoHL repository carried
|
|
20
20
|
* both; `sohl-thalorna` had Prettier but never ran it from `lint`; and
|
|
21
21
|
* `sohl-kethira-basic` had neither, so the package least likely to have been
|
|
22
22
|
* proofread was checked for addresses and nothing else. A rule set copied into
|
|
23
23
|
* three repositories is one rule with three implementations, which is the drift
|
|
24
|
-
*
|
|
24
|
+
* this exists to remove — so it is declared once, here, and every consumer
|
|
25
25
|
* invokes it.
|
|
26
26
|
*
|
|
27
27
|
* **Neither of these is an override.** A consumer that declares its own
|
|
@@ -89,7 +89,7 @@ export const PRETTIER_BASE = Object.freeze({
|
|
|
89
89
|
* **Declared apart from the `overrides` block, not derived from it.** Prettier
|
|
90
90
|
* applies `overrides` only while resolving a config *file*; options handed to
|
|
91
91
|
* it directly keep the global values, so a consumer with no config of its own
|
|
92
|
-
* silently got markdown at 4
|
|
92
|
+
* silently got markdown at 4. The runner needs the adjustment as data it
|
|
93
93
|
* can apply itself, and {@link PRETTIER_CONFIG} composes the same values into
|
|
94
94
|
* the shape a config file wants — one source, two presentations.
|
|
95
95
|
*
|
|
@@ -128,7 +128,7 @@ export const PRETTIER_CONFIG = Object.freeze({
|
|
|
128
128
|
*
|
|
129
129
|
* @param {string} file - Path of the file about to be formatted.
|
|
130
130
|
* @returns {object} Options to hand Prettier directly. Never carries
|
|
131
|
-
* `overrides`: passing that inline is what silently did nothing
|
|
131
|
+
* `overrides`: passing that inline is what silently did nothing.
|
|
132
132
|
*/
|
|
133
133
|
export function sharedPrettierOptionsFor(file) {
|
|
134
134
|
return /\.md$/i.test(file) ? { ...PRETTIER_BASE, ...PRETTIER_MARKDOWN } : { ...PRETTIER_BASE };
|
|
@@ -140,11 +140,11 @@ export function sharedPrettierOptionsFor(file) {
|
|
|
140
140
|
* The runner resolves each file's options as *either* the consumer's own config
|
|
141
141
|
* or {@link sharedPrettierOptionsFor}, never a merge. That is what bare Prettier
|
|
142
142
|
* does and it is the contract — but it means the conventions this package exists
|
|
143
|
-
* to publish hold by convention alone, and they lapse in two opposite directions
|
|
144
|
-
*
|
|
143
|
+
* to publish hold by convention alone, and they lapse in two opposite directions.
|
|
144
|
+
* A consumer that declares any config of its own gets whatever that
|
|
145
145
|
* config says: spread {@link PRETTIER_BASE} without the markdown override and
|
|
146
|
-
* every note reindents at 4, the reindentation the override was added to prevent
|
|
147
|
-
*
|
|
146
|
+
* every note reindents at 4, the reindentation the override was added to prevent;
|
|
147
|
+
* write a partial `.prettierrc` such as `{"tabWidth": 2}` and
|
|
148
148
|
* `printWidth`, `trailingComma`, `experimentalTernaries` and the rest fall back
|
|
149
149
|
* to Prettier's own defaults. A consumer that declares *nothing* formats one way
|
|
150
150
|
* under this command and another under a bare `npx prettier`.
|
package/engine/prose-lint.mjs
CHANGED
|
@@ -13,13 +13,13 @@
|
|
|
13
13
|
|
|
14
14
|
/**
|
|
15
15
|
* Running the shared prose conventions over a repository — Prettier for
|
|
16
|
-
* formatting, markdownlint for the structure Prettier is indifferent to
|
|
16
|
+
* formatting, markdownlint for the structure Prettier is indifferent to.
|
|
17
17
|
*
|
|
18
18
|
* Both are thin: the rules live in `./prose-config.mjs` and the tools are
|
|
19
19
|
* Prettier and markdownlint themselves. What this module adds is the two things
|
|
20
20
|
* a consumer would otherwise have to get right on its own — supplying the
|
|
21
21
|
* shared configuration *as a default a local one overrides*, and reporting
|
|
22
|
-
* findings in the one parseable form every check in this package emits
|
|
22
|
+
* findings in the one parseable form every check in this package emits.
|
|
23
23
|
*
|
|
24
24
|
* **Neither tool's own file discovery is reimplemented.** Prettier decides what
|
|
25
25
|
* it formats and what an ignore file excludes, through `getFileInfo`;
|
|
@@ -68,7 +68,7 @@ const IGNORE_FILES = Object.freeze([".gitignore", ".prettierignore"]);
|
|
|
68
68
|
* `format` is *assumed* idempotent and is not guaranteed to be: a single pass
|
|
69
69
|
* can leave text the next pass would still change, and a `--write` run that
|
|
70
70
|
* takes one pass then reports success has called such a file formatted while
|
|
71
|
-
* `prettier --check` still rejects it
|
|
71
|
+
* `prettier --check` still rejects it. Formatting to a fixpoint removes
|
|
72
72
|
* the assumption — the file lands on the value repeated formatting converges
|
|
73
73
|
* to, whatever it took to get there.
|
|
74
74
|
*
|
|
@@ -86,7 +86,7 @@ const MAX_FORMAT_PASSES = 3;
|
|
|
86
86
|
* configured to do means asking about a file. These are the two answers that
|
|
87
87
|
* differ: markdown carries the shared `tabWidth` override and everything else
|
|
88
88
|
* does not, so a single probe would check half the conventions and miss the one
|
|
89
|
-
* most worth checking
|
|
89
|
+
* most worth checking.
|
|
90
90
|
*
|
|
91
91
|
* Ordinary names at the repository root, and neither has to exist —
|
|
92
92
|
* `resolveConfig` reads the path to walk up from it and to match `overrides`
|
|
@@ -147,8 +147,7 @@ function walkFiles(root) {
|
|
|
147
147
|
* @param {boolean} [opts.write=false] - Rewrite unformatted files in place
|
|
148
148
|
* rather than reporting them. Each file is formatted to a fixpoint (up to
|
|
149
149
|
* {@link MAX_FORMAT_PASSES} passes), so a written tree is one a second run
|
|
150
|
-
* leaves alone; a file that will not converge is reported and left unchanged
|
|
151
|
-
* (#125).
|
|
150
|
+
* leaves alone; a file that will not converge is reported and left unchanged.
|
|
152
151
|
* @param {object} [opts.prettier] - The Prettier module, for tests.
|
|
153
152
|
* @returns {Promise<{findings: Array<{file: string, severity: string,
|
|
154
153
|
* message: string}>, checked: number, written: string[]}>} The findings, how
|
|
@@ -185,7 +184,7 @@ export async function checkFormatting(root, opts = {}) {
|
|
|
185
184
|
});
|
|
186
185
|
// `resolveConfig` has already applied any `overrides` the consumer's
|
|
187
186
|
// own config declares. The shared fallback has to apply its own, since
|
|
188
|
-
// Prettier ignores an `overrides` block passed inline
|
|
187
|
+
// Prettier ignores an `overrides` block passed inline.
|
|
189
188
|
const options = {
|
|
190
189
|
...(local ?? sharedPrettierOptionsFor(file)),
|
|
191
190
|
filepath: file,
|
|
@@ -199,7 +198,7 @@ export async function checkFormatting(root, opts = {}) {
|
|
|
199
198
|
try {
|
|
200
199
|
if (write) {
|
|
201
200
|
// Format to a fixpoint rather than once, so what lands on disk
|
|
202
|
-
// is what a second run would have produced
|
|
201
|
+
// is what a second run would have produced.
|
|
203
202
|
let formatted = source;
|
|
204
203
|
let converged = false;
|
|
205
204
|
for (let pass = 0; pass < MAX_FORMAT_PASSES; pass += 1) {
|
|
@@ -221,7 +220,7 @@ export async function checkFormatting(root, opts = {}) {
|
|
|
221
220
|
file,
|
|
222
221
|
severity: "error",
|
|
223
222
|
// No line or column: the verdict is about the whole
|
|
224
|
-
// file, and
|
|
223
|
+
// file, and the rule is to drop a field rather than
|
|
225
224
|
// invent one.
|
|
226
225
|
message:
|
|
227
226
|
`did not converge after ${MAX_FORMAT_PASSES} formatting passes; ` +
|
|
@@ -242,7 +241,7 @@ export async function checkFormatting(root, opts = {}) {
|
|
|
242
241
|
file,
|
|
243
242
|
severity: "error",
|
|
244
243
|
// No line or column: Prettier's answer is about the whole
|
|
245
|
-
// file, and
|
|
244
|
+
// file, and the rule is to drop a field rather than
|
|
246
245
|
// invent one.
|
|
247
246
|
message: "is not formatted; run `content-build format --write` to fix it",
|
|
248
247
|
});
|
|
@@ -272,7 +271,7 @@ export async function checkFormatting(root, opts = {}) {
|
|
|
272
271
|
*
|
|
273
272
|
* The two cases read differently on purpose. A key set to something else is a
|
|
274
273
|
* choice someone made and can defend; a key that is simply absent is the
|
|
275
|
-
* silent half
|
|
274
|
+
* silent half — a consumer does not choose Prettier's default, it
|
|
276
275
|
* arrived because declaring one option discards every option not restated.
|
|
277
276
|
*
|
|
278
277
|
* @param {{key: string, shared: unknown, local: unknown}} divergence - From
|
|
@@ -291,7 +290,7 @@ function divergenceMessage({ key, shared, local }, scope = "") {
|
|
|
291
290
|
|
|
292
291
|
/**
|
|
293
292
|
* Report where a repository's own Prettier configuration parts from the shared
|
|
294
|
-
* one — or that it has none at all
|
|
293
|
+
* one — or that it has none at all.
|
|
295
294
|
*
|
|
296
295
|
* **Warnings, every one of them.** A consumer's config wins by design and this
|
|
297
296
|
* does not change that; it only refuses to let the divergence be silent, which
|
|
@@ -312,7 +311,7 @@ function divergenceMessage({ key, shared, local }, scope = "") {
|
|
|
312
311
|
* @returns {Promise<{findings: Array<{file?: string, severity: string,
|
|
313
312
|
* message: string}>, configFile: string|null}>} The findings and the config
|
|
314
313
|
* file they are about, which is `null` when the repository declares none. A
|
|
315
|
-
* finding about a missing file carries no `file`:
|
|
314
|
+
* finding about a missing file carries no `file`: the rule is to drop a
|
|
316
315
|
* field rather than invent one.
|
|
317
316
|
*/
|
|
318
317
|
export async function checkPrettierConventions(root, opts = {}) {
|
package/engine/region-events.mjs
CHANGED
|
@@ -22,10 +22,10 @@
|
|
|
22
22
|
* runtime bridge from drifting apart.
|
|
23
23
|
*
|
|
24
24
|
* It sits in this package rather than in the system's `src/` tree because the
|
|
25
|
-
* map-note compiler that reads it is installed as a dependency
|
|
25
|
+
* map-note compiler that reads it is installed as a dependency, and a
|
|
26
26
|
* relative path out of the package would resolve to garbage from
|
|
27
27
|
* `node_modules`. The runtime reaches it back through the package's
|
|
28
|
-
* `./engine/region-events` entry point
|
|
28
|
+
* `./engine/region-events` entry point. It is engine-side, not
|
|
29
29
|
* SoHL-side, because any content module that authors a scene region — an
|
|
30
30
|
* adventure module included — needs this vocabulary.
|
|
31
31
|
*
|