@heroiclands/package-build 22.22.0 → 22.23.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 +32 -0
- package/bin/content-commands.mjs +51 -14
- package/ci/content-format-reference.mjs +1 -1
- package/content-config.mjs +80 -2
- package/docs/api.md +85 -72
- package/docs/authoring/assets.md +14 -0
- package/docs/authoring/dates-and-calendars.md +37 -1
- package/docs/authoring/links-and-markup.md +112 -31
- package/docs/commands.md +12 -10
- package/docs/configuration.md +90 -13
- package/docs/content-format.md +1 -1
- package/docs/guides/book.md +5 -4
- package/docs/reference/format-details.md +323 -60
- package/docs/reference/note-types.md +9 -9
- package/engine/anchors.mjs +16 -10
- package/engine/art-fields.mjs +118 -0
- package/engine/art-slots.mjs +7 -1
- package/engine/asset-index.mjs +160 -10
- package/engine/asset-replacement.mjs +93 -0
- package/engine/asset-types.mjs +240 -0
- package/engine/base-compiler.mjs +54 -2
- package/engine/bundles.mjs +13 -1
- package/engine/content-blocks.mjs +4 -4
- package/engine/content-embeds.mjs +19 -0
- package/engine/content-figures.mjs +468 -0
- package/engine/content-footnotes.mjs +10 -2
- package/engine/content-format.yaml +1 -1
- package/engine/content-images.mjs +126 -25
- package/engine/content-index.mjs +36 -10
- package/engine/field-reference.mjs +31 -10
- package/engine/field-spec.mjs +10 -8
- package/engine/foreign-catalog.mjs +143 -18
- package/engine/frontmatter-lint.mjs +73 -1
- package/engine/heading-attributes.mjs +31 -18
- package/engine/helpers.mjs +97 -30
- package/engine/infobox-registry.mjs +4 -1
- package/engine/infobox.mjs +47 -8
- package/engine/itinerary-scenes.mjs +4 -10
- package/engine/journals.mjs +264 -39
- package/engine/lore-events.mjs +197 -0
- package/engine/markdown-expressions.mjs +257 -7
- package/engine/note-dates.mjs +305 -12
- package/engine/note-vocabulary.mjs +11 -5
- package/engine/pathnames.mjs +36 -0
- package/engine/pdf-build.mjs +357 -34
- package/engine/pdf-images.mjs +64 -0
- package/engine/pdf-render.mjs +160 -36
- package/engine/scenes.mjs +16 -4
- package/engine/site-build.mjs +127 -13
- package/engine/site-index.mjs +72 -1
- package/engine/site-preview.mjs +2 -0
- package/engine/system-block.mjs +44 -16
- package/hm3/actors.mjs +2 -2
- package/manifest.mjs +30 -24
- package/package.json +1 -1
- package/packagebuild-metadata.jsonl +13 -13
- package/sohl/item-fields.mjs +4 -3
- package/types/content-config.d.mts +48 -1
- package/types/engine/anchors.d.mts +9 -4
- package/types/engine/art-fields.d.mts +64 -0
- package/types/engine/asset-index.d.mts +43 -0
- package/types/engine/asset-replacement.d.mts +22 -0
- package/types/engine/asset-types.d.mts +18 -0
- package/types/engine/base-compiler.d.mts +0 -17
- package/types/engine/content-embeds.d.mts +16 -0
- package/types/engine/content-figures.d.mts +101 -0
- package/types/engine/content-footnotes.d.mts +7 -0
- package/types/engine/content-images.d.mts +78 -12
- package/types/engine/content-index.d.mts +8 -2
- package/types/engine/field-reference.d.mts +21 -0
- package/types/engine/field-spec.d.mts +20 -16
- package/types/engine/foreign-catalog.d.mts +39 -0
- package/types/engine/heading-attributes.d.mts +13 -8
- package/types/engine/helpers.d.mts +23 -7
- package/types/engine/infobox-registry.d.mts +4 -1
- package/types/engine/infobox.d.mts +6 -0
- package/types/engine/journals.d.mts +31 -9
- package/types/engine/lore-events.d.mts +14 -0
- package/types/engine/markdown-expressions.d.mts +23 -2
- package/types/engine/note-dates.d.mts +64 -1
- package/types/engine/pathnames.d.mts +26 -0
- package/types/engine/pdf-build.d.mts +28 -6
- package/types/engine/pdf-images.d.mts +52 -0
- package/types/engine/pdf-render.d.mts +12 -0
- package/types/engine/site-build.d.mts +14 -3
- package/types/engine/site-index.d.mts +54 -0
- package/types/engine/system-block.d.mts +26 -2
- package/types/hm3/actors.d.mts +18 -0
- package/types/manifest.d.mts +19 -23
- package/engine/content-captions.mjs +0 -230
- package/types/engine/content-captions.d.mts +0 -58
package/engine/site-index.mjs
CHANGED
|
@@ -66,10 +66,12 @@ import {
|
|
|
66
66
|
renderAddress,
|
|
67
67
|
isAddressTuple,
|
|
68
68
|
ownDocumentSystem,
|
|
69
|
+
expandAddress,
|
|
69
70
|
} from "./address.mjs";
|
|
70
71
|
import { NOTE_SYSTEM } from "./systems.mjs";
|
|
71
72
|
import { contentPackage } from "./content-package.mjs";
|
|
72
73
|
import { reckoningContext } from "./reckoning-markers.mjs";
|
|
74
|
+
import { presentAmongRecords } from "./being-age.mjs";
|
|
73
75
|
// The declared tag vocabulary, which is where `draft` is stated.
|
|
74
76
|
import { isDraftNote } from "./note-vocabulary.mjs";
|
|
75
77
|
|
|
@@ -277,7 +279,12 @@ export function buildSiteIndex(
|
|
|
277
279
|
|
|
278
280
|
return {
|
|
279
281
|
contentPackage: ownPackage,
|
|
280
|
-
|
|
282
|
+
// Carried on the same context `resolvedDateFields` reads: a recurring
|
|
283
|
+
// event's `next` is computed against this present.
|
|
284
|
+
dateContext: {
|
|
285
|
+
...reckoningContext({ notes: records }),
|
|
286
|
+
present: presentAmongRecords(records),
|
|
287
|
+
},
|
|
281
288
|
referenceTargets: buildReferenceTargets(records, new Map([...foreignReferences, ...index])),
|
|
282
289
|
index,
|
|
283
290
|
ambiguous,
|
|
@@ -288,6 +295,70 @@ export function buildSiteIndex(
|
|
|
288
295
|
};
|
|
289
296
|
}
|
|
290
297
|
|
|
298
|
+
/**
|
|
299
|
+
* The address keys one page's figures are reachable by — the same two forms
|
|
300
|
+
* {@link buildSiteIndex} assigns the page itself, so a `ref` crossing into it
|
|
301
|
+
* resolves by exactly the address a wikilink would use.
|
|
302
|
+
*
|
|
303
|
+
* @param {string} pkg - The page's own content package.
|
|
304
|
+
* @param {string} type - The note's `type`.
|
|
305
|
+
* @param {string} shortcode - The note's `shortcode`.
|
|
306
|
+
* @returns {Set<string>} The short `type/shortcode` key and the canonical
|
|
307
|
+
* `package-system-type-shortcode` one.
|
|
308
|
+
*/
|
|
309
|
+
export function figureIndexKeys(pkg, type, shortcode) {
|
|
310
|
+
const system = ownDocumentSystem(type);
|
|
311
|
+
const keys = new Set([
|
|
312
|
+
`${type}/${shortcode}`.toLowerCase(),
|
|
313
|
+
canonicalKey(pkg, system, type, shortcode),
|
|
314
|
+
]);
|
|
315
|
+
if (system !== NOTE_SYSTEM) keys.add(canonicalKey(pkg, NOTE_SYSTEM, type, shortcode));
|
|
316
|
+
return keys;
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
/**
|
|
320
|
+
* Resolve a `ref` call's cross-note address to the target's own figures and
|
|
321
|
+
* its own address — the `figures.note` a `ref` helper's render context reads,
|
|
322
|
+
* built once and threaded into every page.
|
|
323
|
+
*
|
|
324
|
+
* Reads the written address exactly as a wikilink does
|
|
325
|
+
* ({@link readQualifier}, {@link expandAddress}), so a `ref` crossing into
|
|
326
|
+
* another note and a `[[…]]` link to the same note agree on what resolves and
|
|
327
|
+
* what does not: a target naming no known type or no fetched package's index
|
|
328
|
+
* is unresolved for both, and one address names one note for both.
|
|
329
|
+
*
|
|
330
|
+
* @param {string} target - The address as written, anchor already removed —
|
|
331
|
+
* {@link module:engine/wikilink-syntax.ParsedWikilink.target}.
|
|
332
|
+
* @param {object} options
|
|
333
|
+
* @param {Set<string>} options.contentTypes - Every type this build reads as
|
|
334
|
+
* an address qualifier.
|
|
335
|
+
* @param {Set<string>} options.packages - Every package an address may name.
|
|
336
|
+
* @param {Set<string>} options.noIndexPackages - Packages declared
|
|
337
|
+
* `contentIndex: false`.
|
|
338
|
+
* @param {string} options.contentPackage - The package a target with no
|
|
339
|
+
* package segment defaults to.
|
|
340
|
+
* @param {Map<string, {url: string}>} options.siteIndexMap - {@link SiteIndex}'s
|
|
341
|
+
* own `index`, read for the target's existence and its URL.
|
|
342
|
+
* @param {Map<string, Map<string, object>>} options.figuresByAddress - Every
|
|
343
|
+
* note's figures, by id, keyed as {@link figureIndexKeys} keys the note.
|
|
344
|
+
* @returns {{url: string, figures: Map<string, object>}|undefined} The
|
|
345
|
+
* target's own address and figures, or `undefined` when the address names
|
|
346
|
+
* no note this build resolves.
|
|
347
|
+
*/
|
|
348
|
+
export function resolveCrossNoteFigures(
|
|
349
|
+
target,
|
|
350
|
+
{ contentTypes, packages, noIndexPackages, contentPackage, siteIndexMap, figuresByAddress },
|
|
351
|
+
) {
|
|
352
|
+
const read = readQualifier(target, contentTypes, packages, noIndexPackages);
|
|
353
|
+
if (!read || read.reason) return undefined;
|
|
354
|
+
const key = expandAddress(read, { package: contentPackage, system: NOTE_SYSTEM });
|
|
355
|
+
const shortKey = `${read.itemDoc ? "doc" : ""}${read.type}/${read.shortcode}`.toLowerCase();
|
|
356
|
+
const entry = siteIndexMap.get(key) ?? siteIndexMap.get(shortKey);
|
|
357
|
+
if (!entry) return undefined;
|
|
358
|
+
const figures = figuresByAddress.get(key) ?? figuresByAddress.get(shortKey) ?? new Map();
|
|
359
|
+
return { url: entry.url, figures };
|
|
360
|
+
}
|
|
361
|
+
|
|
291
362
|
/**
|
|
292
363
|
* The per-page context a wikilink resolver takes.
|
|
293
364
|
*
|
package/engine/site-preview.mjs
CHANGED
|
@@ -270,6 +270,8 @@ export async function prepareSitePreview({ config = loadPackConfig() } = {}) {
|
|
|
270
270
|
const pos = positionOfLiteral(text, error.src, error.occurrence);
|
|
271
271
|
findings.push({ ...error, ...pos, severity: "error" });
|
|
272
272
|
}
|
|
273
|
+
for (const error of result.embedErrors)
|
|
274
|
+
findings.push({ ...error, severity: "error" });
|
|
273
275
|
if (findings.length) return { ok: false, findings };
|
|
274
276
|
const edges = snapshot.rendered.edges.filter(([source]) => source !== original.url);
|
|
275
277
|
for (const hit of result.resolved) if (hit.url) edges.push([page.url, hit.url]);
|
package/engine/system-block.mjs
CHANGED
|
@@ -104,21 +104,24 @@
|
|
|
104
104
|
*
|
|
105
105
|
* A field's `name` is both its identity and the shared property it draws from,
|
|
106
106
|
* and those coincide only while the two vocabularies agree about what the
|
|
107
|
-
* spelling means
|
|
108
|
-
*
|
|
109
|
-
*
|
|
110
|
-
*
|
|
107
|
+
* spelling means — or while the top-level vocabulary admits the spelling at
|
|
108
|
+
* all. They do not always. An `affiliation` item's `system.title` is the style
|
|
109
|
+
* of address an office carries — "Ajaw", "Warden"; a note's top-level `title`
|
|
110
|
+
* is refused outright by the closed top-level vocabulary, which has no `title`
|
|
111
|
+
* entry. Nothing relates them, and step 3 fed the first from the second anyway.
|
|
111
112
|
*
|
|
112
113
|
* It was not a harmless coincidence either, because step 3 answers **without**
|
|
113
114
|
* applying `field.default` — only step 2 does — so an authored `title: null`
|
|
114
115
|
* reached the field's coercion unguarded and shipped as the literal string
|
|
115
116
|
* `"null"` in fifteen documents.
|
|
116
117
|
*
|
|
117
|
-
* So a field may declare `topLevelMeans`:
|
|
118
|
-
*
|
|
119
|
-
*
|
|
120
|
-
*
|
|
121
|
-
* the
|
|
118
|
+
* So a field may declare `topLevelMeans`: why the note's top-level key of this
|
|
119
|
+
* name is not this field's source — declared wherever that key's spelling
|
|
120
|
+
* means something else at the note level, or the closed top-level vocabulary
|
|
121
|
+
* refuses it outright. Declaring it removes the whole shared level — step 3
|
|
122
|
+
* and the retiring 3b alike, since both read the note's top level and the
|
|
123
|
+
* objection is to that level, not to a spelling — leaving the two positions
|
|
124
|
+
* that describe the document rather than the note. It is deliberately
|
|
122
125
|
* a per-field opt-out rather than a change to the order — step 3 is right
|
|
123
126
|
* wherever the two levels state the same quantity, which is nearly everywhere —
|
|
124
127
|
* and its value is the reason rather than a bare flag, so the collision is
|
|
@@ -362,22 +365,47 @@ export function blockProperty(fm, block, key, defaultValue = undefined) {
|
|
|
362
365
|
}
|
|
363
366
|
|
|
364
367
|
/**
|
|
365
|
-
* A system override followed by a shared `data:` value
|
|
366
|
-
*
|
|
368
|
+
* A system override followed by a shared `data:` value followed by the
|
|
369
|
+
* retiring top-level spelling `data:` gathered it off, reporting which one
|
|
370
|
+
* answered.
|
|
371
|
+
*
|
|
372
|
+
* {@link module:engine/field-spec.resolveFieldValue}'s counterpart for a
|
|
373
|
+
* universal `data:` key — `pack` and `packFolder`, declared for every note
|
|
374
|
+
* type rather than per type, so neither is a `FieldSpec` that resolver takes.
|
|
375
|
+
* The order is the same shape: a system's own override, then the shared
|
|
376
|
+
* `data:` position, then the bare top-level key a tree authored before
|
|
377
|
+
* `data:` existed. The top-level value stays readable while a tree still on
|
|
378
|
+
* it moves its authored fields.
|
|
367
379
|
*
|
|
368
380
|
* @param {object} fm - The note's frontmatter.
|
|
369
381
|
* @param {string} block - The system block key.
|
|
370
382
|
* @param {string} key - The shared property.
|
|
371
383
|
* @param {any} [defaultValue] - Returned when no position declares it.
|
|
372
|
-
* @returns {any} The
|
|
384
|
+
* @returns {{value: any, from: FieldSource}} The value, and where it came
|
|
385
|
+
* from.
|
|
373
386
|
*/
|
|
374
|
-
export function
|
|
387
|
+
export function resolveDataProperty(fm, block, key, defaultValue = undefined) {
|
|
375
388
|
const declared = systemBlock(fm, block)?.[key];
|
|
376
|
-
if (declared !== undefined && declared !== null) return declared;
|
|
389
|
+
if (declared !== undefined && declared !== null) return { value: declared, from: "block" };
|
|
377
390
|
const shared = isMapping(fm?.data) ? fm.data[key] : undefined;
|
|
378
|
-
if (shared !== undefined && shared !== null) return shared;
|
|
391
|
+
if (shared !== undefined && shared !== null) return { value: shared, from: "shared" };
|
|
379
392
|
const legacy = isMapping(fm) ? fm[key] : undefined;
|
|
380
|
-
|
|
393
|
+
if (legacy !== undefined && legacy !== null) return { value: legacy, from: "topLevel" };
|
|
394
|
+
return { value: defaultValue, from: "default" };
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
/**
|
|
398
|
+
* {@link resolveDataProperty}'s value alone, for a caller that only compiles
|
|
399
|
+
* a document and has no use for which position answered.
|
|
400
|
+
*
|
|
401
|
+
* @param {object} fm - The note's frontmatter.
|
|
402
|
+
* @param {string} block - The system block key.
|
|
403
|
+
* @param {string} key - The shared property.
|
|
404
|
+
* @param {any} [defaultValue] - Returned when no position declares it.
|
|
405
|
+
* @returns {any} The selected value.
|
|
406
|
+
*/
|
|
407
|
+
export function blockDataProperty(fm, block, key, defaultValue = undefined) {
|
|
408
|
+
return resolveDataProperty(fm, block, key, defaultValue).value;
|
|
381
409
|
}
|
|
382
410
|
|
|
383
411
|
/**
|
package/hm3/actors.mjs
CHANGED
|
@@ -78,7 +78,7 @@ import {
|
|
|
78
78
|
*
|
|
79
79
|
* @type {readonly import("../engine/field-spec.mjs").FieldSpec[]}
|
|
80
80
|
*/
|
|
81
|
-
const ACTOR_FIELDS = Object.freeze([
|
|
81
|
+
export const ACTOR_FIELDS = Object.freeze([
|
|
82
82
|
{
|
|
83
83
|
name: "species",
|
|
84
84
|
to: "species",
|
|
@@ -97,7 +97,7 @@ const ACTOR_FIELDS = Object.freeze([
|
|
|
97
97
|
*
|
|
98
98
|
* @type {readonly import("../engine/field-spec.mjs").FieldSpec[]}
|
|
99
99
|
*/
|
|
100
|
-
const CHARACTER_FIELDS = Object.freeze([
|
|
100
|
+
export const CHARACTER_FIELDS = Object.freeze([
|
|
101
101
|
{
|
|
102
102
|
name: "data.gender",
|
|
103
103
|
legacyKey: "gender",
|
package/manifest.mjs
CHANGED
|
@@ -48,6 +48,7 @@
|
|
|
48
48
|
* @module
|
|
49
49
|
*/
|
|
50
50
|
|
|
51
|
+
import { RELATIONSHIP_KEYS } from "./content-config.mjs";
|
|
51
52
|
import fs from "node:fs/promises";
|
|
52
53
|
import fsSync from "node:fs";
|
|
53
54
|
import path from "node:path";
|
|
@@ -390,33 +391,38 @@ export function packFolderFindings({ packFolders, packs = [] }) {
|
|
|
390
391
|
}
|
|
391
392
|
|
|
392
393
|
/**
|
|
393
|
-
*
|
|
394
|
-
*
|
|
395
|
-
*
|
|
396
|
-
*
|
|
397
|
-
*
|
|
398
|
-
*
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
*
|
|
410
|
-
*
|
|
411
|
-
*
|
|
412
|
-
*
|
|
413
|
-
* to
|
|
414
|
-
*
|
|
415
|
-
*
|
|
394
|
+
* The relationship keys Foundry's own schema names.
|
|
395
|
+
*
|
|
396
|
+
* `RelatedPackage` declares exactly these (`common/packages/base-package.mjs`),
|
|
397
|
+
* so a key outside them is one the build put there for itself.
|
|
398
|
+
*
|
|
399
|
+
* @type {readonly string[]}
|
|
400
|
+
*/
|
|
401
|
+
export const FOUNDRY_RELATIONSHIP_KEYS = Object.freeze([
|
|
402
|
+
"id",
|
|
403
|
+
"type",
|
|
404
|
+
"manifest",
|
|
405
|
+
"compatibility",
|
|
406
|
+
"reason",
|
|
407
|
+
]);
|
|
408
|
+
|
|
409
|
+
/**
|
|
410
|
+
* The relationship keys the published manifest drops.
|
|
411
|
+
*
|
|
412
|
+
* Derived rather than listed: every key this package accepts on a relationship
|
|
413
|
+
* that Foundry's schema does not name is one the build reads for itself, so a
|
|
414
|
+
* key added to the configuration is dropped from the manifest without a second
|
|
415
|
+
* list being kept in step.
|
|
416
|
+
*
|
|
417
|
+
* The filter stays a denylist for the reason the pass-through below gives — a
|
|
418
|
+
* key Foundry adds later reaches the manifest unread rather than being stripped
|
|
419
|
+
* by an allowlist that has not heard of it.
|
|
416
420
|
*
|
|
417
421
|
* @type {readonly string[]}
|
|
418
422
|
*/
|
|
419
|
-
export const BUILD_ONLY_RELATIONSHIP_KEYS = Object.freeze(
|
|
423
|
+
export const BUILD_ONLY_RELATIONSHIP_KEYS = Object.freeze(
|
|
424
|
+
RELATIONSHIP_KEYS.filter((key) => !FOUNDRY_RELATIONSHIP_KEYS.includes(key)),
|
|
425
|
+
);
|
|
420
426
|
|
|
421
427
|
/**
|
|
422
428
|
* The `relationships` block as published — every declared dependency, with the
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@heroiclands/package-build",
|
|
3
|
-
"version": "22.
|
|
3
|
+
"version": "22.23.0",
|
|
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,13 +1,13 @@
|
|
|
1
|
-
{"address":{"canonical":"packagebuild-none-image-afflictionbnr"},"asset":{"ai":
|
|
2
|
-
{"address":{"canonical":"packagebuild-none-image-armorclothingbnr"},"asset":{"ai":
|
|
3
|
-
{"address":{"canonical":"packagebuild-none-image-attributebnr"},"asset":{"ai":
|
|
4
|
-
{"address":{"canonical":"packagebuild-none-image-containerbnr"},"asset":{"ai":
|
|
5
|
-
{"address":{"canonical":"packagebuild-none-image-devdocsbnr"},"asset":{"ai":
|
|
6
|
-
{"address":{"canonical":"packagebuild-none-image-miscgearbnr"},"asset":{"ai":
|
|
7
|
-
{"address":{"canonical":"packagebuild-none-image-mysticalabilitybnr"},"asset":{"ai":
|
|
8
|
-
{"address":{"canonical":"packagebuild-none-image-projectilebnr"},"asset":{"ai":
|
|
9
|
-
{"address":{"canonical":"packagebuild-none-image-rulesbnr"},"asset":{"ai":
|
|
10
|
-
{"address":{"canonical":"packagebuild-none-image-skillbnr"},"asset":{"ai":
|
|
11
|
-
{"address":{"canonical":"packagebuild-none-image-traumabnr"},"asset":{"ai":
|
|
12
|
-
{"address":{"canonical":"packagebuild-none-image-userguidebnr"},"asset":{"ai":
|
|
13
|
-
{"address":{"canonical":"packagebuild-none-image-weapongearbnr"},"asset":{"ai":
|
|
1
|
+
{"address":{"canonical":"packagebuild-none-image-afflictionbnr"},"asset":{"ai":false,"attribution":"Tom Rodriguez","height":768,"license":"CC-BY-SA-4.0","notes":"","path":"images/banners/afflictionbnr.webp","role":"","source":"","width":1792},"package":"packagebuild","shortcode":"afflictionbnr","type":"image"}
|
|
2
|
+
{"address":{"canonical":"packagebuild-none-image-armorclothingbnr"},"asset":{"ai":false,"attribution":"Tom Rodriguez","height":768,"license":"CC-BY-SA-4.0","notes":"","path":"images/banners/armorclothingbnr.webp","role":"","source":"","width":1792},"package":"packagebuild","shortcode":"armorclothingbnr","type":"image"}
|
|
3
|
+
{"address":{"canonical":"packagebuild-none-image-attributebnr"},"asset":{"ai":false,"attribution":"Tom Rodriguez","height":768,"license":"CC-BY-SA-4.0","notes":"","path":"images/banners/attributebnr.webp","role":"","source":"","width":1792},"package":"packagebuild","shortcode":"attributebnr","type":"image"}
|
|
4
|
+
{"address":{"canonical":"packagebuild-none-image-containerbnr"},"asset":{"ai":false,"attribution":"Tom Rodriguez","height":768,"license":"CC-BY-SA-4.0","notes":"","path":"images/banners/containerbnr.webp","role":"","source":"","width":1792},"package":"packagebuild","shortcode":"containerbnr","type":"image"}
|
|
5
|
+
{"address":{"canonical":"packagebuild-none-image-devdocsbnr"},"asset":{"ai":false,"attribution":"Tom Rodriguez","height":768,"license":"CC-BY-SA-4.0","notes":"","path":"images/banners/devdocsbnr.webp","role":"","source":"","width":1792},"package":"packagebuild","shortcode":"devdocsbnr","type":"image"}
|
|
6
|
+
{"address":{"canonical":"packagebuild-none-image-miscgearbnr"},"asset":{"ai":false,"attribution":"Tom Rodriguez","height":768,"license":"CC-BY-SA-4.0","notes":"","path":"images/banners/miscgearbnr.webp","role":"","source":"","width":1792},"package":"packagebuild","shortcode":"miscgearbnr","type":"image"}
|
|
7
|
+
{"address":{"canonical":"packagebuild-none-image-mysticalabilitybnr"},"asset":{"ai":false,"attribution":"Tom Rodriguez","height":768,"license":"CC-BY-SA-4.0","notes":"","path":"images/banners/mysticalabilitybnr.webp","role":"","source":"","width":1792},"package":"packagebuild","shortcode":"mysticalabilitybnr","type":"image"}
|
|
8
|
+
{"address":{"canonical":"packagebuild-none-image-projectilebnr"},"asset":{"ai":false,"attribution":"Tom Rodriguez","height":768,"license":"CC-BY-SA-4.0","notes":"","path":"images/banners/projectilebnr.webp","role":"","source":"","width":1792},"package":"packagebuild","shortcode":"projectilebnr","type":"image"}
|
|
9
|
+
{"address":{"canonical":"packagebuild-none-image-rulesbnr"},"asset":{"ai":false,"attribution":"Tom Rodriguez","height":768,"license":"CC-BY-SA-4.0","notes":"","path":"images/banners/rulesbnr.webp","role":"","source":"","width":1792},"package":"packagebuild","shortcode":"rulesbnr","type":"image"}
|
|
10
|
+
{"address":{"canonical":"packagebuild-none-image-skillbnr"},"asset":{"ai":false,"attribution":"Tom Rodriguez","height":768,"license":"CC-BY-SA-4.0","notes":"","path":"images/banners/skillbnr.webp","role":"","source":"","width":1792},"package":"packagebuild","shortcode":"skillbnr","type":"image"}
|
|
11
|
+
{"address":{"canonical":"packagebuild-none-image-traumabnr"},"asset":{"ai":false,"attribution":"Tom Rodriguez","height":768,"license":"CC-BY-SA-4.0","notes":"","path":"images/banners/traumabnr.webp","role":"","source":"","width":1792},"package":"packagebuild","shortcode":"traumabnr","type":"image"}
|
|
12
|
+
{"address":{"canonical":"packagebuild-none-image-userguidebnr"},"asset":{"ai":false,"attribution":"Tom Rodriguez","height":768,"license":"CC-BY-SA-4.0","notes":"","path":"images/banners/userguidebnr.webp","role":"","source":"","width":1792},"package":"packagebuild","shortcode":"userguidebnr","type":"image"}
|
|
13
|
+
{"address":{"canonical":"packagebuild-none-image-weapongearbnr"},"asset":{"ai":false,"attribution":"Tom Rodriguez","height":768,"license":"CC-BY-SA-4.0","notes":"","path":"images/banners/weapongearbnr.webp","role":"","source":"","width":1792},"package":"packagebuild","shortcode":"weapongearbnr","type":"image"}
|
package/sohl/item-fields.mjs
CHANGED
|
@@ -391,9 +391,10 @@ export const ITEM_FIELDS = Object.freeze({
|
|
|
391
391
|
...STRING,
|
|
392
392
|
default: "",
|
|
393
393
|
topLevelMeans:
|
|
394
|
-
"
|
|
395
|
-
"
|
|
396
|
-
"
|
|
394
|
+
"A top-level `title` is refused outright, since an office's " +
|
|
395
|
+
"style of address belongs to the membership that holds the " +
|
|
396
|
+
"office and nothing outside the item supplies it. Author it " +
|
|
397
|
+
"at `sohl.system.title` on the item, or at the " +
|
|
397
398
|
"`system.title` of the entry in a being's `sohl.items`.",
|
|
398
399
|
describe: "The style of address the office carries.",
|
|
399
400
|
},
|
|
@@ -126,6 +126,16 @@ export const RETIRED_ADDRESS_KEYS: Readonly<Record<string, string>>;
|
|
|
126
126
|
export const DEFAULT_ADDRESS_SCHEME: Readonly<{
|
|
127
127
|
prefix: "";
|
|
128
128
|
}>;
|
|
129
|
+
/**
|
|
130
|
+
* Every key a declared relationship may carry.
|
|
131
|
+
*
|
|
132
|
+
* Read by the manifest writer as well as by the configuration check: the keys
|
|
133
|
+
* Foundry's own relationship schema does not name are the build's own, and the
|
|
134
|
+
* published manifest carries none of them.
|
|
135
|
+
*
|
|
136
|
+
* @type {readonly string[]}
|
|
137
|
+
*/
|
|
138
|
+
export const RELATIONSHIP_KEYS: readonly string[];
|
|
129
139
|
/**
|
|
130
140
|
* How the loader hands {@link defineConfig} the system version it resolved.
|
|
131
141
|
*
|
|
@@ -274,7 +284,8 @@ export type PathsInput = {
|
|
|
274
284
|
unpack?: string | undefined;
|
|
275
285
|
/**
|
|
276
286
|
* Where a dependency declaring
|
|
277
|
-
* `itemCatalog: true`
|
|
287
|
+
* `itemCatalog: true` or
|
|
288
|
+
* `assetArchive: true` is unpacked.
|
|
278
289
|
* Inbound, and fetched rather than
|
|
279
290
|
* committed.
|
|
280
291
|
*/
|
|
@@ -438,6 +449,21 @@ export type RelationshipSpec = {
|
|
|
438
449
|
* `_stats.systemVersion` is stamped from.
|
|
439
450
|
*/
|
|
440
451
|
compatibility?: CompatibilitySpec | undefined;
|
|
452
|
+
/**
|
|
453
|
+
* Whether `deps fetch` unpacks this
|
|
454
|
+
* dependency's release archive and extracts
|
|
455
|
+
* its Item packs, so the actors pass can
|
|
456
|
+
* resolve embedded items this repository
|
|
457
|
+
* does not hold. Needs a `manifest`.
|
|
458
|
+
*/
|
|
459
|
+
itemCatalog?: boolean | undefined;
|
|
460
|
+
/**
|
|
461
|
+
* Whether `deps fetch` unpacks this
|
|
462
|
+
* dependency's release archive for its
|
|
463
|
+
* asset bytes alone, building no item
|
|
464
|
+
* catalogue from it. Needs a `manifest`.
|
|
465
|
+
*/
|
|
466
|
+
assetArchive?: boolean | undefined;
|
|
441
467
|
/**
|
|
442
468
|
* Whether `deps fetch` fetches this
|
|
443
469
|
* dependency's content index. Default
|
|
@@ -449,6 +475,16 @@ export type RelationshipSpec = {
|
|
|
449
475
|
* index.
|
|
450
476
|
*/
|
|
451
477
|
contentIndex?: boolean | undefined;
|
|
478
|
+
/**
|
|
479
|
+
* Whether this dependency's asset
|
|
480
|
+
* tree answers this package's own asset
|
|
481
|
+
* addresses ahead of its local record.
|
|
482
|
+
* Default `false`. Refuses `contentIndex:
|
|
483
|
+
* false` on the same entry, since a
|
|
484
|
+
* replacement with no fetched index to
|
|
485
|
+
* check against resolves nothing.
|
|
486
|
+
*/
|
|
487
|
+
assetReplacement?: boolean | undefined;
|
|
452
488
|
};
|
|
453
489
|
/**
|
|
454
490
|
* How a generated documentation page is framed in the repository publishing it.
|
|
@@ -628,6 +664,12 @@ export type ContentBuildConfigInput = {
|
|
|
628
664
|
* `Templates`). Default `[]`.
|
|
629
665
|
*/
|
|
630
666
|
skipDirectories?: string[] | undefined;
|
|
667
|
+
/**
|
|
668
|
+
* Whether `lint` refuses an asset
|
|
669
|
+
* record of this package carrying
|
|
670
|
+
* `ai: true`. Default `false`.
|
|
671
|
+
*/
|
|
672
|
+
forbidGeneratedArt?: boolean | undefined;
|
|
631
673
|
/**
|
|
632
674
|
* Reserved for
|
|
633
675
|
* `@heroiclands/package-build`, which
|
|
@@ -764,6 +806,11 @@ export type ContentBuildConfig = {
|
|
|
764
806
|
*/
|
|
765
807
|
docEntryTypes: ReadonlySet<string>;
|
|
766
808
|
skipDirectories: readonly string[];
|
|
809
|
+
/**
|
|
810
|
+
* Whether `lint` refuses an asset
|
|
811
|
+
* record of this package carrying `ai: true`.
|
|
812
|
+
*/
|
|
813
|
+
forbidGeneratedArt: boolean;
|
|
767
814
|
/**
|
|
768
815
|
* The
|
|
769
816
|
* fonts this package ships and the names it
|
|
@@ -1,17 +1,22 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* The `{#slug}` and `:::
|
|
2
|
+
* The `{#slug}` and `:::figure {#slug}` anchors a note declares.
|
|
3
3
|
*
|
|
4
|
-
* A bare `#` heading starts a journal page without declaring a slug
|
|
5
|
-
*
|
|
4
|
+
* A bare `#` heading starts a journal page without declaring a slug, and so
|
|
5
|
+
* does a figure fence that declares no id. A fence that declares one declares a
|
|
6
|
+
* slug and starts an addressable journal page.
|
|
6
7
|
*
|
|
7
8
|
* @param {string} body - The note's markdown body, frontmatter already removed.
|
|
8
9
|
* @param {number} [bodyLine] - The 1-based file line the body starts on, from
|
|
9
10
|
* `parseMarkdownFile`. Anchors are reported at their position in the **file**,
|
|
10
11
|
* so an editor can jump straight to one; passing nothing numbers from the body.
|
|
12
|
+
* @param {(address: string) => string|undefined} [resolveRole] - From a
|
|
13
|
+
* picture's address to the role its asset declares, so a figure anchor's
|
|
14
|
+
* `name` reads `Map 1` rather than `Figure 1` where it is due — see
|
|
15
|
+
* {@link module:engine/content-figures.scanFigures}.
|
|
11
16
|
* @returns {Array<{slug: string, name: string, level: number, line: number}>}
|
|
12
17
|
* In document order.
|
|
13
18
|
*/
|
|
14
|
-
export function collectAnchors(body: string, bodyLine?: number): Array<{
|
|
19
|
+
export function collectAnchors(body: string, bodyLine?: number, resolveRole?: (address: string) => string | undefined): Array<{
|
|
15
20
|
slug: string;
|
|
16
21
|
name: string;
|
|
17
22
|
level: number;
|
|
@@ -61,6 +61,29 @@ export function assetAddressIndex(records?: readonly object[], { config, foreign
|
|
|
61
61
|
} | undefined;
|
|
62
62
|
types?: Iterable<string> | undefined;
|
|
63
63
|
}): object;
|
|
64
|
+
/**
|
|
65
|
+
* What a renderer needs to size one picture, keyed by the pathname its
|
|
66
|
+
* address resolves to.
|
|
67
|
+
*
|
|
68
|
+
* An image reaches a renderer as the resolved pathname an embed or an authored
|
|
69
|
+
* body image already carries — see {@link readAssetAddress} — never as the
|
|
70
|
+
* address that produced it, so the lookup a renderer wants is by pathname, not
|
|
71
|
+
* by {@link assetAddressIndex}'s own `assets` map.
|
|
72
|
+
*
|
|
73
|
+
* @param {readonly object[]} records - Asset records, local or foreign.
|
|
74
|
+
* @returns {Map<string, {type: string, role?: string, width: number|"", height: number|""}>}
|
|
75
|
+
* One entry per addressable file, carrying only what a renderer sizes a
|
|
76
|
+
* picture from. `role` is omitted when the record states none; `width` and
|
|
77
|
+
* `height` are always present, blank (`""`) for a vector — the record's own
|
|
78
|
+
* convention, carried through rather than collapsed into an absence a
|
|
79
|
+
* renderer could not tell apart from "no asset resolved at all".
|
|
80
|
+
*/
|
|
81
|
+
export function assetImageInfoByPathname(records?: readonly object[]): Map<string, {
|
|
82
|
+
type: string;
|
|
83
|
+
role?: string;
|
|
84
|
+
width: number | "";
|
|
85
|
+
height: number | "";
|
|
86
|
+
}>;
|
|
64
87
|
/**
|
|
65
88
|
* The asset one authored value names, or why it names none.
|
|
66
89
|
*
|
|
@@ -116,6 +139,47 @@ export function resolveArtRecord(index: object, value: unknown, defaultType: str
|
|
|
116
139
|
path: string;
|
|
117
140
|
};
|
|
118
141
|
} | null;
|
|
142
|
+
/**
|
|
143
|
+
* Every asset's role, keyed by the pathname it resolves to — the form a body
|
|
144
|
+
* carries once an embed's address has been rewritten into the ordinary image
|
|
145
|
+
* every surface renders ({@link module:engine/content-embeds.resolveEmbeds}).
|
|
146
|
+
*
|
|
147
|
+
* This is the lookup a `:::figure` fence's `map` counter reaches through —
|
|
148
|
+
* see {@link module:engine/content-figures.scanFigures}'s `resolveRole` —
|
|
149
|
+
* for a caller that reads a body after that rewrite: a Foundry journal, an
|
|
150
|
+
* item or actor's documentation, and the book.
|
|
151
|
+
*
|
|
152
|
+
* @param {object} index - From {@link module:engine/wikilinks.buildWikilinkIndex},
|
|
153
|
+
* or the equivalent the site and the book build.
|
|
154
|
+
* @returns {Map<string, string>} Pathname → role, one entry per asset that
|
|
155
|
+
* declares one.
|
|
156
|
+
*/
|
|
157
|
+
export function pathnameRoles(index: object): Map<string, string>;
|
|
158
|
+
/**
|
|
159
|
+
* Flag a note whose art slot names a picture that is not the size that slot
|
|
160
|
+
* is cut to.
|
|
161
|
+
*
|
|
162
|
+
* The size is stated by the slot itself, in
|
|
163
|
+
* {@link module:engine/art-slots.ART_SLOTS}, so it is written once and a slot
|
|
164
|
+
* that states none asks nothing of the picture it names. Today the hero image
|
|
165
|
+
* is the one slot with a fixed strip to fill; a picture written in prose is
|
|
166
|
+
* fitted to the room it has and is never measured here.
|
|
167
|
+
*
|
|
168
|
+
* A vector carries no pixel size to compare, and neither does a raster whose
|
|
169
|
+
* header cannot be read; neither is flagged.
|
|
170
|
+
*
|
|
171
|
+
* @param {readonly object[]} records - The corpus, notes and assets together.
|
|
172
|
+
* @param {object} [opts]
|
|
173
|
+
* @param {object} [opts.config] - The resolved build configuration.
|
|
174
|
+
* @param {string} [opts.assetsBase] - Where the asset roots sit, so a finding
|
|
175
|
+
* names the file from the working directory as every other finding does.
|
|
176
|
+
* @returns {object[]} A finding per picture off its slot's size,
|
|
177
|
+
* `severity: "error"`.
|
|
178
|
+
*/
|
|
179
|
+
export function checkArtSlotSizes(records?: readonly object[], { config, assetsBase }?: {
|
|
180
|
+
config?: object | undefined;
|
|
181
|
+
assetsBase?: string | undefined;
|
|
182
|
+
}): object[];
|
|
119
183
|
/**
|
|
120
184
|
* The pathname an art value names, in the form an authored one takes.
|
|
121
185
|
*
|
|
@@ -19,6 +19,16 @@ export function collectAssetRecords(assetsBase: string, { contentPackage, proble
|
|
|
19
19
|
contentPackage: string;
|
|
20
20
|
problems?: object[] | undefined;
|
|
21
21
|
}): Array<Record<string, any>>;
|
|
22
|
+
/**
|
|
23
|
+
* The closed set of roles a picture may declare.
|
|
24
|
+
*
|
|
25
|
+
* Shared with the field declaration below and with every medium that sizes a
|
|
26
|
+
* picture by its role, so the vocabulary cannot drift between the record that
|
|
27
|
+
* carries it and the renderer that reads it.
|
|
28
|
+
*
|
|
29
|
+
* @type {readonly string[]}
|
|
30
|
+
*/
|
|
31
|
+
export const ASSET_ROLES: readonly string[];
|
|
22
32
|
/**
|
|
23
33
|
* The file a directory records provenance for its subtree in.
|
|
24
34
|
*
|
|
@@ -47,6 +57,18 @@ export const PROVENANCE_SIDECAR_SUFFIX: string;
|
|
|
47
57
|
* state this key. Omitting one is a finding rather than a blank, because a
|
|
48
58
|
* record resolves wholesale: the nearest one is the whole answer, so a key it
|
|
49
59
|
* leaves out is not inherited from above but simply absent.
|
|
60
|
+
* @property {"boolean"|"integer"} [type] - The value's own type, for a field
|
|
61
|
+
* whose answer is not a string. Omitted for every string field; `ai` carries
|
|
62
|
+
* the YAML boolean itself rather than a stringified copy, and `width` and
|
|
63
|
+
* `height` carry the pixel count the walk measured. Each is refused rather
|
|
64
|
+
* than coerced when the value does not match.
|
|
65
|
+
* @property {readonly string[]} [values] - The closed set a provenance
|
|
66
|
+
* declaration must choose one of; a value outside it is a finding and the
|
|
67
|
+
* key is left blank, the same treatment an `ai` value of the wrong shape
|
|
68
|
+
* gets. `role` is the one field that carries it.
|
|
69
|
+
* @property {string} [onlyType] - The one asset type this field may be
|
|
70
|
+
* declared for. A provenance record stating it for another type is a
|
|
71
|
+
* finding, and the value is dropped rather than carried through.
|
|
50
72
|
* @property {string} describe - One line, for the author-facing reference.
|
|
51
73
|
*/
|
|
52
74
|
/**
|
|
@@ -105,6 +127,27 @@ export type AssetRecordField = {
|
|
|
105
127
|
* leaves out is not inherited from above but simply absent.
|
|
106
128
|
*/
|
|
107
129
|
required?: boolean | undefined;
|
|
130
|
+
/**
|
|
131
|
+
* - The value's own type, for a field
|
|
132
|
+
* whose answer is not a string. Omitted for every string field; `ai` carries
|
|
133
|
+
* the YAML boolean itself rather than a stringified copy, and `width` and
|
|
134
|
+
* `height` carry the pixel count the walk measured. Each is refused rather
|
|
135
|
+
* than coerced when the value does not match.
|
|
136
|
+
*/
|
|
137
|
+
type?: "boolean" | "integer" | undefined;
|
|
138
|
+
/**
|
|
139
|
+
* - The closed set a provenance
|
|
140
|
+
* declaration must choose one of; a value outside it is a finding and the
|
|
141
|
+
* key is left blank, the same treatment an `ai` value of the wrong shape
|
|
142
|
+
* gets. `role` is the one field that carries it.
|
|
143
|
+
*/
|
|
144
|
+
values?: readonly string[] | undefined;
|
|
145
|
+
/**
|
|
146
|
+
* - The one asset type this field may be
|
|
147
|
+
* declared for. A provenance record stating it for another type is a
|
|
148
|
+
* finding, and the value is dropped rather than carried through.
|
|
149
|
+
*/
|
|
150
|
+
onlyType?: string | undefined;
|
|
108
151
|
/**
|
|
109
152
|
* - One line, for the author-facing reference.
|
|
110
153
|
*/
|