@heroiclands/package-build 22.4.0 → 22.4.2
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 +32 -0
- package/CONTENT.md +7 -0
- package/bin/package-build.mjs +83 -0
- package/docs/commands.md +67 -0
- package/engine/actor-compiler.mjs +79 -15
- package/engine/changelog-lint.mjs +629 -0
- package/engine/document-subtypes.mjs +24 -7
- package/githooks/pre-commit +53 -2
- package/hm3/actors.mjs +42 -0
- package/package.json +1 -1
- package/types/engine/actor-compiler.d.mts +62 -9
- package/types/engine/changelog-lint.d.mts +50 -0
- package/types/engine/document-subtypes.d.mts +14 -7
- package/types/hm3/actors.d.mts +9 -0
package/githooks/pre-commit
CHANGED
|
@@ -1,7 +1,12 @@
|
|
|
1
1
|
#!/usr/bin/env sh
|
|
2
2
|
#
|
|
3
|
-
# Refuse to commit on a protected branch
|
|
4
|
-
#
|
|
3
|
+
# Refuse to commit on a protected branch, and refuse a staged changeset that
|
|
4
|
+
# reads like a pull-request description.
|
|
5
|
+
#
|
|
6
|
+
# See protected-branch.sh for the branch rule and its opt-outs;
|
|
7
|
+
# pre-merge-commit is that guard's counterpart for merges. The changeset check
|
|
8
|
+
# below has no merge-commit counterpart — a merge commit stages no new
|
|
9
|
+
# changeset of its own.
|
|
5
10
|
#
|
|
6
11
|
# Installed for everyone via the package.json "prepare" script, which points git
|
|
7
12
|
# at this directory (`git config core.hooksPath .githooks`) on `npm install`.
|
|
@@ -9,3 +14,49 @@
|
|
|
9
14
|
. "$(dirname "$0")/protected-branch.sh"
|
|
10
15
|
|
|
11
16
|
guard_protected_branch
|
|
17
|
+
|
|
18
|
+
# On unless refused: `changelog check` reads only the files this commit is
|
|
19
|
+
# about to stage and runs in-process, so it costs a fraction of what the
|
|
20
|
+
# pre-push container check does and stays on by the same default as the
|
|
21
|
+
# no-attribution guard.
|
|
22
|
+
. "$(dirname "$0")/hook-enabled.sh"
|
|
23
|
+
|
|
24
|
+
hook_enabled changelogCheck true || exit 0
|
|
25
|
+
|
|
26
|
+
# Pending changesets this commit actually stages — added, copied or modified.
|
|
27
|
+
# A deleted changeset has nothing left to lint, and a changeset already on
|
|
28
|
+
# `main` was linted by the commit that staged it. `README.md` documents the
|
|
29
|
+
# changeset format; it is not one.
|
|
30
|
+
staged=""
|
|
31
|
+
while IFS= read -r file; do
|
|
32
|
+
[ -z "$file" ] && continue
|
|
33
|
+
case "$file" in
|
|
34
|
+
*/README.md | README.md) continue ;;
|
|
35
|
+
esac
|
|
36
|
+
staged="$staged $file"
|
|
37
|
+
done <<EOF
|
|
38
|
+
$(git diff --cached --name-only --diff-filter=ACM -- '.changeset/*.md')
|
|
39
|
+
EOF
|
|
40
|
+
|
|
41
|
+
if [ -z "$staged" ]; then
|
|
42
|
+
exit 0
|
|
43
|
+
fi
|
|
44
|
+
|
|
45
|
+
# The binary lives beside this hook inside the package. If it is not there,
|
|
46
|
+
# this hook is being used from somewhere else — a global `core.hooksPath`, a
|
|
47
|
+
# copy — in a repository that does not install the package, and there is
|
|
48
|
+
# nothing to run.
|
|
49
|
+
runner="$(dirname "$0")/../bin/package-build.mjs"
|
|
50
|
+
if [ ! -f "$runner" ]; then
|
|
51
|
+
exit 0
|
|
52
|
+
fi
|
|
53
|
+
|
|
54
|
+
# shellcheck disable=SC2086
|
|
55
|
+
node "$runner" changelog check $staged
|
|
56
|
+
status=$?
|
|
57
|
+
|
|
58
|
+
if [ "$status" -ne 0 ]; then
|
|
59
|
+
echo ""
|
|
60
|
+
echo "pre-commit: fix the changeset above, or skip this check with 'git commit --no-verify'."
|
|
61
|
+
exit 1
|
|
62
|
+
fi
|
package/hm3/actors.mjs
CHANGED
|
@@ -162,6 +162,35 @@ function defaultActorImg(subType) {
|
|
|
162
162
|
return img;
|
|
163
163
|
}
|
|
164
164
|
|
|
165
|
+
/**
|
|
166
|
+
* Default art for the three subtypes HM3's one-to-many `mysticalability` row
|
|
167
|
+
* permits — `spell`, `invocation` and `psionic` — keyed by the **document**
|
|
168
|
+
* subtype rather than the note type.
|
|
169
|
+
*
|
|
170
|
+
* `hm3/default-item-art.mjs` is keyed the other way on purpose: a registry
|
|
171
|
+
* entry is addressed by what a note calls itself, and no note ever authors
|
|
172
|
+
* `type: spell` — only `mysticalability`, discriminated by its own
|
|
173
|
+
* `hm3.type`. But a being's `(type, shortcode)` reference has no note to
|
|
174
|
+
* discriminate, so it names the subtype it wants directly, and an embedded
|
|
175
|
+
* entry that supplies neither a template's own `img` nor a `data.icon` of its
|
|
176
|
+
* own reaches {@link Hm3Actors#embeddedItemArt} in exactly that vocabulary.
|
|
177
|
+
*
|
|
178
|
+
* Each path is the icon HM3's own item sheet assigns a freshly created item
|
|
179
|
+
* of that subtype — `HM3.defaultMagicIconName`, `HM3.defaultRitualIconName`
|
|
180
|
+
* and `HM3.defaultPsionicsIconName` in the system's own `config.js` — so a
|
|
181
|
+
* compiled item looks like one created in the client. `weapongear` and
|
|
182
|
+
* `missilegear` need no row here: a reference spelled either resolves through
|
|
183
|
+
* `HM3_DEFAULT_ITEM_ART`'s own `weapongear` key (see #582), and every
|
|
184
|
+
* predefined weapon and missile in HM3's catalogue carries its own `img`.
|
|
185
|
+
*
|
|
186
|
+
* @type {Readonly<Record<string, string>>}
|
|
187
|
+
*/
|
|
188
|
+
const EMBEDDED_ITEM_ART = Object.freeze({
|
|
189
|
+
invocation: "systems/hm3/images/icons/svg/circle.svg",
|
|
190
|
+
psionic: "systems/hm3/images/icons/svg/psionics.svg",
|
|
191
|
+
spell: "systems/hm3/images/icons/svg/pentacle.svg",
|
|
192
|
+
});
|
|
193
|
+
|
|
165
194
|
/**
|
|
166
195
|
* HM3's Actor compile pass.
|
|
167
196
|
*
|
|
@@ -192,6 +221,19 @@ export class Hm3Actors extends SystemActorCompiler {
|
|
|
192
221
|
return this.buildActor(this.itemsMap, fm, markdown);
|
|
193
222
|
}
|
|
194
223
|
|
|
224
|
+
/**
|
|
225
|
+
* @inheritdoc
|
|
226
|
+
*
|
|
227
|
+
* Answers for `spell`, `invocation` and `psionic` too — see
|
|
228
|
+
* {@link EMBEDDED_ITEM_ART} — falling back to the engine's note-type
|
|
229
|
+
* table for every other reference, `weapongear` and `missilegear`
|
|
230
|
+
* included.
|
|
231
|
+
*/
|
|
232
|
+
embeddedItemArt(type, subType) {
|
|
233
|
+
const art = /** @type {Record<string, string|undefined>} */ (EMBEDDED_ITEM_ART)[subType];
|
|
234
|
+
return art ?? super.embeddedItemArt(type, subType);
|
|
235
|
+
}
|
|
236
|
+
|
|
195
237
|
/**
|
|
196
238
|
* Build every embedded item an HM3 actor carries, from `hm3.items`.
|
|
197
239
|
*
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@heroiclands/package-build",
|
|
3
|
-
"version": "22.4.
|
|
3
|
+
"version": "22.4.2",
|
|
4
4
|
"description": "Shared toolchain for building and shipping a HeroicLands Foundry VTT package — content compilation, manifest, localization, staging, bundle, release and deployment.",
|
|
5
5
|
"license": "GPL-3.0-or-later",
|
|
6
6
|
"type": "module",
|
|
@@ -90,13 +90,33 @@ export function packagedItemAddress(pkg: string, subType: string, shortcode: str
|
|
|
90
90
|
* @returns {string} The catalogue key.
|
|
91
91
|
*/
|
|
92
92
|
export function catalogueKey(subType: string, shortcode: string, pkg?: string): string;
|
|
93
|
+
/**
|
|
94
|
+
* An item's own shortcode, read off its compiled document.
|
|
95
|
+
*
|
|
96
|
+
* **`system.shortcode`**, where a system's data model declares such a field.
|
|
97
|
+
* Where it does not — HM3's has no such field — the handle instead lives in
|
|
98
|
+
* that system's own flag namespace, `flags.<systemId>.shortcode`: a system
|
|
99
|
+
* writes its per-document handle into its own flags and never another
|
|
100
|
+
* system's, so a document extracted from one system's catalogue is read
|
|
101
|
+
* through that system's namespace and no other. `system.shortcode` wins where
|
|
102
|
+
* both are present.
|
|
103
|
+
*
|
|
104
|
+
* @param {object} doc - A compiled Item document, or an embedded item merged
|
|
105
|
+
* from one.
|
|
106
|
+
* @param {string|null} [systemId] - The system whose catalogue `doc` was read
|
|
107
|
+
* from. Omitted or `null`, only `system.shortcode` is read.
|
|
108
|
+
* @returns {string|undefined} The shortcode, or `undefined` when the document
|
|
109
|
+
* states neither.
|
|
110
|
+
*/
|
|
111
|
+
export function shortcodeOf(doc: object, systemId?: string | null): string | undefined;
|
|
93
112
|
/**
|
|
94
113
|
* What identifies one embedded item on its actor.
|
|
95
114
|
*
|
|
96
|
-
* **Its own
|
|
97
|
-
* merely *selects* the catalogue template the
|
|
98
|
-
* never written to the document. Two daggers
|
|
99
|
-
* embodiments and each must declare its
|
|
115
|
+
* **Its own shortcode** — read by {@link shortcodeOf} — not the entry's
|
|
116
|
+
* top-level `shortcode`, which merely *selects* the catalogue template the
|
|
117
|
+
* entry is written from and is never written to the document. Two daggers
|
|
118
|
+
* may share a selector; they are two embodiments and each must declare its
|
|
119
|
+
* own.
|
|
100
120
|
*
|
|
101
121
|
* The name is a last resort, for a **stand-alone** entry that names no template
|
|
102
122
|
* and states no shortcode. It is a poor identity — presentation, and free to be
|
|
@@ -105,9 +125,12 @@ export function catalogueKey(subType: string, shortcode: string, pkg?: string):
|
|
|
105
125
|
* message says to state a `system.shortcode`.
|
|
106
126
|
*
|
|
107
127
|
* @param {object} item - The merged embedded item.
|
|
128
|
+
* @param {string|null} [systemId] - The system this item's document was
|
|
129
|
+
* compiled or extracted for, so a document whose own data model carries no
|
|
130
|
+
* `system.shortcode` field is still read by its own flag namespace.
|
|
108
131
|
* @returns {string} The identity, for {@link embeddedItemId}.
|
|
109
132
|
*/
|
|
110
|
-
export function embeddedIdentity(item: object): string;
|
|
133
|
+
export function embeddedIdentity(item: object, systemId?: string | null): string;
|
|
111
134
|
/**
|
|
112
135
|
* The `_id` of one item embedded on an actor.
|
|
113
136
|
*
|
|
@@ -135,9 +158,9 @@ export function embeddedItemId(actorId: string, subType: string, identity: strin
|
|
|
135
158
|
/**
|
|
136
159
|
* Load every JSON file under each of `itemsSourceDirs`, returning one Map keyed
|
|
137
160
|
* by {@link itemAddress} — the compiled document's **subtype** and its
|
|
138
|
-
*
|
|
139
|
-
* The `_key` field is stripped from each entry — it is
|
|
140
|
-
* data model.
|
|
161
|
+
* shortcode, read by {@link shortcodeOf}. Folder docs and entries without a
|
|
162
|
+
* shortcode are skipped. The `_key` field is stripped from each entry — it is
|
|
163
|
+
* not part of the item data model.
|
|
141
164
|
*
|
|
142
165
|
* The directories are read as one address space, because an actor names an item
|
|
143
166
|
* by `(type, shortcode)` and never by the pack it happens to ship in. Two local
|
|
@@ -151,12 +174,22 @@ export function embeddedItemId(actorId: string, subType: string, identity: strin
|
|
|
151
174
|
* colliding with it. Local directories are therefore read first, and anything
|
|
152
175
|
* already claimed is left alone.
|
|
153
176
|
*
|
|
177
|
+
* **Each foreign directory reads its own flag namespace.** A foreign entry's
|
|
178
|
+
* `package` is the system whose catalogue it was extracted from, and that is
|
|
179
|
+
* the only namespace {@link shortcodeOf} is asked to fall back to for it — a
|
|
180
|
+
* document carrying another system's flag, sitting in this system's catalogue,
|
|
181
|
+
* is exactly the defect a system writing outside its own namespace produces,
|
|
182
|
+
* and is silently skipped rather than resolved.
|
|
183
|
+
*
|
|
154
184
|
* @param {readonly string[]} itemsSourceDirs - Every local Item pack's JSON tree.
|
|
155
185
|
* @param {readonly string[]} [foreignSourceDirs] - Extracted dependency
|
|
156
186
|
* catalogues, consulted only for addresses no local pack defines.
|
|
187
|
+
* @param {string|null} [system] - The system `itemsSourceDirs` were compiled
|
|
188
|
+
* for, so a local document whose data model carries no `system.shortcode`
|
|
189
|
+
* field is still read by its own flag namespace.
|
|
157
190
|
* @returns {Map<string, object>} The predefined items, by address.
|
|
158
191
|
*/
|
|
159
|
-
export function loadItemsMap(itemsSourceDirs: readonly string[], foreignSourceDirs?: readonly string[]): Map<string, object>;
|
|
192
|
+
export function loadItemsMap(itemsSourceDirs: readonly string[], foreignSourceDirs?: readonly string[], system?: string | null): Map<string, object>;
|
|
160
193
|
/**
|
|
161
194
|
* The Actor compile pass of one game system.
|
|
162
195
|
*
|
|
@@ -247,6 +280,26 @@ export class SystemActorCompiler extends BasePackCompiler {
|
|
|
247
280
|
* subtype, or why the reference names none.
|
|
248
281
|
*/
|
|
249
282
|
embeddedSubtype(type: string): import("./document-subtypes.mjs").ReferencedSubtype;
|
|
283
|
+
/**
|
|
284
|
+
* The default art for an embedded item's type, when the entry names none
|
|
285
|
+
* of its own and copies no template that carries one.
|
|
286
|
+
*
|
|
287
|
+
* **The base case is {@link itemArt}**, keyed by the note vocabulary — the
|
|
288
|
+
* same table an item note's own compile defaults from, because most
|
|
289
|
+
* references are written in that vocabulary too (`weapongear`, `skill`,
|
|
290
|
+
* `armorgear`). It is not the whole answer: a reference into a one-to-many
|
|
291
|
+
* row may instead name one of the row's own **subtypes** directly — HM3's
|
|
292
|
+
* `spell`, `invocation` and `psionic` are never a note's own `type`, only
|
|
293
|
+
* `mysticalability`'s `hm3.type` discriminator ever writes them, so no
|
|
294
|
+
* note-type table has a row for them. A system whose one-to-many rows are
|
|
295
|
+
* addressed that way overrides this to answer for those subtypes too; see
|
|
296
|
+
* `hm3/actors.mjs`.
|
|
297
|
+
*
|
|
298
|
+
* @param {string} type - The **note** type the reference names.
|
|
299
|
+
* @param {string} subType - The document subtype it resolved to.
|
|
300
|
+
* @returns {string} The default image path.
|
|
301
|
+
*/
|
|
302
|
+
embeddedItemArt(type: string, subType: string): string;
|
|
250
303
|
/**
|
|
251
304
|
* Read an entry's `model:` — the address of the item it is a copy of.
|
|
252
305
|
*
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Lint one pending changeset (`.changeset/*.md`).
|
|
3
|
+
*
|
|
4
|
+
* @param {string} text - The file's full contents, frontmatter included.
|
|
5
|
+
* @returns {{findings: Array<{line: number, column?: number,
|
|
6
|
+
* severity: "error"|"warning", message: string}>}}
|
|
7
|
+
*/
|
|
8
|
+
export function lintChangesetText(text: string): {
|
|
9
|
+
findings: Array<{
|
|
10
|
+
line: number;
|
|
11
|
+
column?: number;
|
|
12
|
+
severity: "error" | "warning";
|
|
13
|
+
message: string;
|
|
14
|
+
}>;
|
|
15
|
+
};
|
|
16
|
+
/**
|
|
17
|
+
* Lint the first `## <version>` release section of a `CHANGELOG.md`.
|
|
18
|
+
*
|
|
19
|
+
* @param {string} text - The changelog's full contents.
|
|
20
|
+
* @returns {{findings: Array<{line?: number, column?: number,
|
|
21
|
+
* severity: "error"|"warning", message: string}>}}
|
|
22
|
+
*/
|
|
23
|
+
export function lintReleaseText(text: string): {
|
|
24
|
+
findings: Array<{
|
|
25
|
+
line?: number;
|
|
26
|
+
column?: number;
|
|
27
|
+
severity: "error" | "warning";
|
|
28
|
+
message: string;
|
|
29
|
+
}>;
|
|
30
|
+
};
|
|
31
|
+
/**
|
|
32
|
+
* The finding one rule reports, before its line is mapped into the caller's
|
|
33
|
+
* file.
|
|
34
|
+
*/
|
|
35
|
+
export type RelativeFinding = {
|
|
36
|
+
/**
|
|
37
|
+
* - 1-based line within the section text.
|
|
38
|
+
*/
|
|
39
|
+
line: number;
|
|
40
|
+
/**
|
|
41
|
+
* - 1-based column, dropped when not meaningful.
|
|
42
|
+
*/
|
|
43
|
+
column?: number | undefined;
|
|
44
|
+
severity: "error" | "warning";
|
|
45
|
+
/**
|
|
46
|
+
* - Prefixed `changelog-check/<class> `, so a
|
|
47
|
+
* finding names the rule it tripped as well as what to write instead.
|
|
48
|
+
*/
|
|
49
|
+
message: string;
|
|
50
|
+
};
|
|
@@ -140,7 +140,7 @@ export function documentSubtype(map: DocumentSubtypeMap, noteType: string | unde
|
|
|
140
140
|
* dependency catalogue actually carry, and a reference is translated forward
|
|
141
141
|
* here before it is looked up.
|
|
142
142
|
*
|
|
143
|
-
*
|
|
143
|
+
* Five answers, and only the last refuses:
|
|
144
144
|
*
|
|
145
145
|
* - _A one-to-one row_ → the subtype it declares. `armor` addresses an
|
|
146
146
|
* `armorgear`.
|
|
@@ -151,12 +151,19 @@ export function documentSubtype(map: DocumentSubtypeMap, noteType: string | unde
|
|
|
151
151
|
* existed.
|
|
152
152
|
* - _A row for another document class_ → a problem. A being is not an item,
|
|
153
153
|
* however the address is spelled.
|
|
154
|
-
* - _A one-to-many
|
|
155
|
-
*
|
|
156
|
-
*
|
|
157
|
-
*
|
|
158
|
-
*
|
|
159
|
-
*
|
|
154
|
+
* - _A one-to-many row, named by one of its own permitted subtypes_ → that
|
|
155
|
+
* subtype. HM3's `weapongear` row is keyed by the note type `weapongear` but
|
|
156
|
+
* permits `["weapongear", "missilegear"]`; a reference spelled `weapongear`
|
|
157
|
+
* is not ambiguous — it already names the subtype it wants, the same as a
|
|
158
|
+
* reference spelled `missilegear` does by matching no row at all and taking
|
|
159
|
+
* the unmapped fallback above. Only the row's own key can coincide with one
|
|
160
|
+
* of its subtypes, so this is never a second guess at the note's
|
|
161
|
+
* frontmatter — the row was looked up by this exact spelling.
|
|
162
|
+
* - _A one-to-many row, named by neither the row's other permitted subtypes
|
|
163
|
+
* nor resolved above_ → a problem naming the candidates. The note that owns
|
|
164
|
+
* such a row resolves it from its own frontmatter block; a reference naming
|
|
165
|
+
* only the row has no block to read a discriminator from, so nothing here
|
|
166
|
+
* can choose, and choosing anyway would be right about half the time.
|
|
160
167
|
*
|
|
161
168
|
* A **retired** spelling is refused by name before any of that. Without it a
|
|
162
169
|
* reference left behind by a merge would take the unmapped fallback and address
|
package/types/hm3/actors.d.mts
CHANGED
|
@@ -15,6 +15,15 @@ export class Hm3Actors extends SystemActorCompiler {
|
|
|
15
15
|
* @type {import("../engine/document-subtypes.mjs").DocumentSubtypeMap}
|
|
16
16
|
*/
|
|
17
17
|
static documentSubtypes: import("../engine/document-subtypes.mjs").DocumentSubtypeMap;
|
|
18
|
+
/**
|
|
19
|
+
* @inheritdoc
|
|
20
|
+
*
|
|
21
|
+
* Answers for `spell`, `invocation` and `psionic` too — see
|
|
22
|
+
* {@link EMBEDDED_ITEM_ART} — falling back to the engine's note-type
|
|
23
|
+
* table for every other reference, `weapongear` and `missilegear`
|
|
24
|
+
* included.
|
|
25
|
+
*/
|
|
26
|
+
embeddedItemArt(type: any, subType: any): string;
|
|
18
27
|
/**
|
|
19
28
|
* Build every embedded item an HM3 actor carries, from `hm3.items`.
|
|
20
29
|
*
|