@heroiclands/package-build 20.3.0 → 20.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +22 -0
- package/engine/content-icons.mjs +99 -3
- package/package.json +1 -1
- package/types/engine/content-icons.d.mts +41 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,27 @@
|
|
|
1
1
|
# @heroiclands/package-build
|
|
2
2
|
|
|
3
|
+
## 20.3.1
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 43188f2: A registry entry can now name an icon family, so a package that draws from two
|
|
8
|
+
of them can say so.
|
|
9
|
+
|
|
10
|
+
The SoHL icon legend states in its own prose that it uses Font Awesome plus
|
|
11
|
+
Game-Icons.net "for the arms, gear, and condition glyphs that Font Awesome does
|
|
12
|
+
not cover" — eighteen `ginf-*` classes the registry had no way to express. They
|
|
13
|
+
differ in more than a prefix: Game-Icons has no weights, so a `style` on such an
|
|
14
|
+
entry names something that does not exist, and it resolves to a different font,
|
|
15
|
+
which a PDF has to embed separately.
|
|
16
|
+
|
|
17
|
+
`family` defaults to `fontawesome`, so every existing entry is unchanged. A
|
|
18
|
+
style on an unstyled family is reported rather than silently ignored, because it
|
|
19
|
+
means the author expected a filled and hollow pair the family cannot spell.
|
|
20
|
+
|
|
21
|
+
Also adds `gem` and `gem-outline`, and repoints `diamond` at them. `fa-diamond`
|
|
22
|
+
is Font Awesome's playing-card suit and ships in solid only, so it could not
|
|
23
|
+
spell the hollow half of the Success Value scale it was being used for.
|
|
24
|
+
|
|
3
25
|
## 20.3.0
|
|
4
26
|
|
|
5
27
|
### Minor Changes
|
package/engine/content-icons.mjs
CHANGED
|
@@ -79,6 +79,57 @@ import path from "node:path";
|
|
|
79
79
|
*/
|
|
80
80
|
export const ICON_STYLES = Object.freeze(["solid", "regular", "brands"]);
|
|
81
81
|
|
|
82
|
+
/**
|
|
83
|
+
* The icon families a registry entry may draw from.
|
|
84
|
+
*
|
|
85
|
+
* **Two, because the interface uses two.** The SoHL icon legend says so in its
|
|
86
|
+
* own prose: Font Awesome for most things, and Game-Icons.net *"for the arms,
|
|
87
|
+
* gear, and condition glyphs that Font Awesome does not cover"* — eighteen of
|
|
88
|
+
* them, `ginf-broadsword` and its kin (#391).
|
|
89
|
+
*
|
|
90
|
+
* They differ in more than a class prefix, which is why this is a family rather
|
|
91
|
+
* than a naming convention:
|
|
92
|
+
*
|
|
93
|
+
* - Font Awesome has **weights**, so an entry names a `style` and a filled and
|
|
94
|
+
* hollow pair is one icon twice. Game-Icons has none, so a `style` on such an
|
|
95
|
+
* entry names something that does not exist.
|
|
96
|
+
* - They resolve to **different fonts**. A PDF has to embed both, and find each
|
|
97
|
+
* codepoint in its own table — Font Awesome's from the file it ships,
|
|
98
|
+
* Game-Icons' from the `game-icons-codepoints.json` the consumer's own
|
|
99
|
+
* `build-icon-font.mjs` writes.
|
|
100
|
+
*
|
|
101
|
+
* `class` is the prefix the web surfaces use. `styled` says whether a `style`
|
|
102
|
+
* belongs on the entry at all, which is what lets a mistake be reported rather
|
|
103
|
+
* than rendered as a class nobody defined.
|
|
104
|
+
*
|
|
105
|
+
* @type {Readonly<Record<string, {class: string, styled: boolean, describe: string}>>}
|
|
106
|
+
*/
|
|
107
|
+
export const ICON_FAMILIES = Object.freeze({
|
|
108
|
+
fontawesome: {
|
|
109
|
+
class: "fa",
|
|
110
|
+
styled: true,
|
|
111
|
+
describe: "Font Awesome Free",
|
|
112
|
+
},
|
|
113
|
+
"game-icons": {
|
|
114
|
+
class: "ginf",
|
|
115
|
+
styled: false,
|
|
116
|
+
describe: "the Game-Icons.net webfont a package builds for itself",
|
|
117
|
+
},
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
/** The family an entry that does not name one belongs to. */
|
|
121
|
+
export const DEFAULT_ICON_FAMILY = "fontawesome";
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* The family an entry draws from, named or defaulted.
|
|
125
|
+
*
|
|
126
|
+
* @param {{family?: string}} entry - A registry entry.
|
|
127
|
+
* @returns {string} The family name.
|
|
128
|
+
*/
|
|
129
|
+
export function familyOf(entry) {
|
|
130
|
+
return entry?.family ?? DEFAULT_ICON_FAMILY;
|
|
131
|
+
}
|
|
132
|
+
|
|
82
133
|
/**
|
|
83
134
|
* The sizes a note may ask for, and what each means on a page.
|
|
84
135
|
*
|
|
@@ -207,7 +258,14 @@ export const DEFAULT_ICONS = Object.freeze({
|
|
|
207
258
|
affiliation: { style: "solid", icon: "certificate", label: "affiliation" },
|
|
208
259
|
star: { style: "solid", icon: "star", label: "star" },
|
|
209
260
|
"star-outline": { style: "regular", icon: "star", label: "hollow star" },
|
|
210
|
-
|
|
261
|
+
// The Success Value scale. `fa-diamond` is Font Awesome's playing-card
|
|
262
|
+
// suit and ships in solid only, so it can spell no hollow half of a
|
|
263
|
+
// filled/hollow pair; `fa-gem` is a gemstone, has both weights, and is what
|
|
264
|
+
// a quality scale actually means. `diamond` stays as an alias of it so a
|
|
265
|
+
// note that already says `:icon-diamond:` keeps working.
|
|
266
|
+
gem: { style: "solid", icon: "gem", label: "value gem" },
|
|
267
|
+
"gem-outline": { style: "regular", icon: "gem", label: "unearned value gem" },
|
|
268
|
+
diamond: { style: "solid", icon: "gem", label: "value gem" },
|
|
211
269
|
edit: { style: "solid", icon: "pen-to-square", label: "edit" },
|
|
212
270
|
delete: { style: "solid", icon: "trash", label: "delete" },
|
|
213
271
|
add: { style: "solid", icon: "plus", label: "add" },
|
|
@@ -281,9 +339,22 @@ const attr = (value) =>
|
|
|
281
339
|
* @returns {string} An `<i>` element.
|
|
282
340
|
*/
|
|
283
341
|
export function iconHtml(entry, attrs = {}) {
|
|
284
|
-
const
|
|
342
|
+
const family = ICON_FAMILIES[familyOf(entry)] ?? ICON_FAMILIES[DEFAULT_ICON_FAMILY];
|
|
343
|
+
const prefix = family.class;
|
|
344
|
+
|
|
345
|
+
// A styled family spells the weight and the name as two classes; an
|
|
346
|
+
// unstyled one has a single class and no weight to spell.
|
|
347
|
+
const classes =
|
|
348
|
+
family.styled ?
|
|
349
|
+
[`${prefix}-${attr(entry.style)}`, `${prefix}-${attr(entry.icon)}`]
|
|
350
|
+
: [`${prefix}-${attr(entry.icon)}`];
|
|
351
|
+
|
|
352
|
+
// The size classes are Font Awesome's, and the Game-Icons stylesheet this
|
|
353
|
+
// toolchain's consumers generate mirrors its box metrics deliberately, so
|
|
354
|
+
// they apply to both families.
|
|
285
355
|
const sized = attrs.size ? ICON_SIZES[attrs.size] : undefined;
|
|
286
356
|
if (sized) classes.push(sized.class);
|
|
357
|
+
|
|
287
358
|
return `<i class="${classes.join(" ")}" role="img" aria-label="${attr(entry.label)}"></i>`;
|
|
288
359
|
}
|
|
289
360
|
|
|
@@ -410,7 +481,19 @@ export function checkIconRegistry(registry, where = "icons") {
|
|
|
410
481
|
});
|
|
411
482
|
continue;
|
|
412
483
|
}
|
|
413
|
-
|
|
484
|
+
const familyName = familyOf(entry);
|
|
485
|
+
const family = ICON_FAMILIES[familyName];
|
|
486
|
+
if (!family) {
|
|
487
|
+
findings.push({
|
|
488
|
+
severity: /** @type {const} */ ("warning"),
|
|
489
|
+
message:
|
|
490
|
+
`${at} names family \`${familyName}\`, and the families there ` +
|
|
491
|
+
`are: ${Object.keys(ICON_FAMILIES).join(", ")}`,
|
|
492
|
+
});
|
|
493
|
+
continue;
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
if (family.styled && !ICON_STYLES.includes(entry.style)) {
|
|
414
497
|
findings.push({
|
|
415
498
|
severity: /** @type {const} */ ("warning"),
|
|
416
499
|
message:
|
|
@@ -419,6 +502,19 @@ export function checkIconRegistry(registry, where = "icons") {
|
|
|
419
502
|
`absent from the font a book would embed`,
|
|
420
503
|
});
|
|
421
504
|
}
|
|
505
|
+
|
|
506
|
+
// A style on an unstyled family is not a harmless extra key: it says
|
|
507
|
+
// the author expected a weight, and the family has none, so what they
|
|
508
|
+
// get is not what they asked for.
|
|
509
|
+
if (!family.styled && entry.style !== undefined) {
|
|
510
|
+
findings.push({
|
|
511
|
+
severity: /** @type {const} */ ("warning"),
|
|
512
|
+
message:
|
|
513
|
+
`${at} names style \`${entry.style}\`, and ${family.describe} has ` +
|
|
514
|
+
`no weights — the style is ignored, so a filled and hollow pair ` +
|
|
515
|
+
`cannot be spelled this way`,
|
|
516
|
+
});
|
|
517
|
+
}
|
|
422
518
|
if (typeof entry.icon !== "string" || !entry.icon) {
|
|
423
519
|
findings.push({
|
|
424
520
|
severity: /** @type {const} */ ("warning"),
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@heroiclands/package-build",
|
|
3
|
-
"version": "20.3.
|
|
3
|
+
"version": "20.3.1",
|
|
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",
|
|
@@ -1,3 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The family an entry draws from, named or defaulted.
|
|
3
|
+
*
|
|
4
|
+
* @param {{family?: string}} entry - A registry entry.
|
|
5
|
+
* @returns {string} The family name.
|
|
6
|
+
*/
|
|
7
|
+
export function familyOf(entry: {
|
|
8
|
+
family?: string;
|
|
9
|
+
}): string;
|
|
1
10
|
/**
|
|
2
11
|
* Read the brace of an icon token.
|
|
3
12
|
*
|
|
@@ -132,6 +141,38 @@ export function iconPlugin(registry?: Record<string, object>): (md: object) => v
|
|
|
132
141
|
* @type {readonly string[]}
|
|
133
142
|
*/
|
|
134
143
|
export const ICON_STYLES: readonly string[];
|
|
144
|
+
/**
|
|
145
|
+
* The icon families a registry entry may draw from.
|
|
146
|
+
*
|
|
147
|
+
* **Two, because the interface uses two.** The SoHL icon legend says so in its
|
|
148
|
+
* own prose: Font Awesome for most things, and Game-Icons.net *"for the arms,
|
|
149
|
+
* gear, and condition glyphs that Font Awesome does not cover"* — eighteen of
|
|
150
|
+
* them, `ginf-broadsword` and its kin (#391).
|
|
151
|
+
*
|
|
152
|
+
* They differ in more than a class prefix, which is why this is a family rather
|
|
153
|
+
* than a naming convention:
|
|
154
|
+
*
|
|
155
|
+
* - Font Awesome has **weights**, so an entry names a `style` and a filled and
|
|
156
|
+
* hollow pair is one icon twice. Game-Icons has none, so a `style` on such an
|
|
157
|
+
* entry names something that does not exist.
|
|
158
|
+
* - They resolve to **different fonts**. A PDF has to embed both, and find each
|
|
159
|
+
* codepoint in its own table — Font Awesome's from the file it ships,
|
|
160
|
+
* Game-Icons' from the `game-icons-codepoints.json` the consumer's own
|
|
161
|
+
* `build-icon-font.mjs` writes.
|
|
162
|
+
*
|
|
163
|
+
* `class` is the prefix the web surfaces use. `styled` says whether a `style`
|
|
164
|
+
* belongs on the entry at all, which is what lets a mistake be reported rather
|
|
165
|
+
* than rendered as a class nobody defined.
|
|
166
|
+
*
|
|
167
|
+
* @type {Readonly<Record<string, {class: string, styled: boolean, describe: string}>>}
|
|
168
|
+
*/
|
|
169
|
+
export const ICON_FAMILIES: Readonly<Record<string, {
|
|
170
|
+
class: string;
|
|
171
|
+
styled: boolean;
|
|
172
|
+
describe: string;
|
|
173
|
+
}>>;
|
|
174
|
+
/** The family an entry that does not name one belongs to. */
|
|
175
|
+
export const DEFAULT_ICON_FAMILY: "fontawesome";
|
|
135
176
|
/**
|
|
136
177
|
* The sizes a note may ask for, and what each means on a page.
|
|
137
178
|
*
|