@heroiclands/package-build 8.0.0 → 9.0.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 +173 -0
- package/bin/content-build.mjs +44 -118
- package/bin/package-build.mjs +27 -69
- package/bin/report.mjs +1 -2
- package/bundle.mjs +2 -10
- package/config.mjs +31 -106
- package/container.mjs +13 -57
- package/content-config.mjs +45 -164
- package/coverage.mjs +14 -55
- package/deploy.mjs +4 -13
- package/e2e.mjs +16 -55
- package/engine/address-diff.mjs +1 -4
- package/engine/base-compiler.mjs +10 -28
- package/engine/code-fences.mjs +4 -13
- package/engine/compendiums.mjs +13 -37
- package/engine/content-address.mjs +2 -6
- package/engine/content-links.mjs +13 -44
- package/engine/content-lint.mjs +4 -15
- package/engine/content-slug.mjs +2 -6
- package/engine/content-tables.mjs +26 -79
- package/engine/diagnostics.mjs +4 -15
- package/engine/field-reference.mjs +6 -20
- package/engine/field-spec.mjs +1 -3
- package/engine/foreign-catalog.mjs +7 -22
- package/engine/foreign-manifests.mjs +1 -4
- package/engine/frontmatter-lint.mjs +6 -18
- package/engine/frontmatter.mjs +3 -8
- package/engine/generate.mjs +4 -16
- package/engine/helpers.mjs +13 -52
- package/engine/homepage.mjs +4 -15
- package/engine/ids.mjs +3 -12
- package/engine/item-registry.mjs +2 -6
- package/engine/journals.mjs +4 -14
- package/engine/kb-manifest.mjs +5 -17
- package/engine/macros.mjs +2 -10
- package/engine/manifest-emit.mjs +6 -17
- package/engine/map-notes.mjs +19 -69
- package/engine/note-package.mjs +1 -4
- package/engine/pack-config.mjs +17 -38
- package/engine/pack-router.mjs +1 -2
- package/engine/prose-config.mjs +20 -4
- package/engine/prose-lint.mjs +6 -14
- package/engine/region-events.mjs +1 -3
- package/engine/scene-levels.mjs +8 -22
- package/engine/scenes.mjs +13 -47
- package/engine/schema-check.mjs +1 -4
- package/engine/schema-extract.mjs +70 -45
- package/engine/site-build.mjs +12 -37
- package/engine/site-index.mjs +3 -15
- package/engine/web-wikilinks.mjs +4 -13
- package/engine/wikilinks.mjs +115 -167
- package/index.mjs +1 -5
- package/lang.mjs +1 -3
- package/manifest.mjs +10 -37
- package/markdownlint-config.mjs +1 -5
- package/package.json +1 -1
- package/sohl/actors.mjs +10 -40
- package/sohl/being-info.mjs +3 -6
- package/sohl/index.mjs +1 -6
- package/sohl/item-builders.mjs +1 -3
- package/sohl/item-fields.mjs +16 -34
- package/sohl/items.mjs +1 -3
- package/sohl/kb-passes.mjs +29 -39
- package/sohl/skill-base.mjs +7 -23
- package/stage.mjs +3 -13
- package/templates.mjs +4 -15
- package/types/bundle.d.mts +1 -1
- package/types/container.d.mts +2 -2
- package/types/coverage.d.mts +1 -1
- package/types/e2e.d.mts +4 -4
- package/types/engine/generate.d.mts +1 -1
- package/types/engine/helpers.d.mts +1 -1
- package/types/engine/schema-extract.d.mts +1 -1
- package/types/engine/site-index.d.mts +1 -1
- package/types/manifest.d.mts +1 -1
|
@@ -89,9 +89,7 @@ import { draftRetiredMessage } from "./retired-fields.mjs";
|
|
|
89
89
|
*
|
|
90
90
|
* @type {ReadonlySet<string>}
|
|
91
91
|
*/
|
|
92
|
-
export const UNIVERSAL_KEYS = Object.freeze(
|
|
93
|
-
new Set(["folder", "pack", "archetype", "kbcat"]),
|
|
94
|
-
);
|
|
92
|
+
export const UNIVERSAL_KEYS = Object.freeze(new Set(["folder", "pack", "archetype", "kbcat"]));
|
|
95
93
|
|
|
96
94
|
/**
|
|
97
95
|
* Edit distance, capped — enough to answer "did you mean".
|
|
@@ -166,11 +164,7 @@ export function matchesKind(value, kind) {
|
|
|
166
164
|
value.trim() !== "" &&
|
|
167
165
|
Number.isFinite(Number(value));
|
|
168
166
|
case "boolean":
|
|
169
|
-
return
|
|
170
|
-
typeof value === "boolean" ||
|
|
171
|
-
value === "true" ||
|
|
172
|
-
value === "false"
|
|
173
|
-
);
|
|
167
|
+
return typeof value === "boolean" || value === "true" || value === "false";
|
|
174
168
|
case "string":
|
|
175
169
|
return typeof value !== "object" || value === null;
|
|
176
170
|
case "list":
|
|
@@ -179,9 +173,7 @@ export function matchesKind(value, kind) {
|
|
|
179
173
|
// An emptied map arrives as `[]` from the property editor, and
|
|
180
174
|
// means "this note authors no entries" — the same thing `{}` means.
|
|
181
175
|
return (
|
|
182
|
-
(typeof value === "object" &&
|
|
183
|
-
value !== null &&
|
|
184
|
-
!Array.isArray(value)) ||
|
|
176
|
+
(typeof value === "object" && value !== null && !Array.isArray(value)) ||
|
|
185
177
|
(Array.isArray(value) && value.length === 0)
|
|
186
178
|
);
|
|
187
179
|
default:
|
|
@@ -197,9 +189,7 @@ export function matchesKind(value, kind) {
|
|
|
197
189
|
*/
|
|
198
190
|
function sohlBlock(fm) {
|
|
199
191
|
const block = fm?.sohl;
|
|
200
|
-
return block && typeof block === "object" && !Array.isArray(block) ?
|
|
201
|
-
block
|
|
202
|
-
: {};
|
|
192
|
+
return block && typeof block === "object" && !Array.isArray(block) ? block : {};
|
|
203
193
|
}
|
|
204
194
|
|
|
205
195
|
/**
|
|
@@ -217,8 +207,7 @@ export function lintNote(note, { schemas, index }) {
|
|
|
217
207
|
const fm = note.fm ?? {};
|
|
218
208
|
const type = String(fm.type ?? "");
|
|
219
209
|
const raw = () => note.raw ?? "";
|
|
220
|
-
const at = (key, literal) =>
|
|
221
|
-
positionInFrontmatter(raw(), key, literal ?? undefined);
|
|
210
|
+
const at = (key, literal) => positionInFrontmatter(raw(), key, literal ?? undefined);
|
|
222
211
|
|
|
223
212
|
// The retired top-level fields, checked before the type: a note may carry
|
|
224
213
|
// one whatever its type is, and each finding stands on its own. Reported
|
|
@@ -312,8 +301,7 @@ export function lintNote(note, { schemas, index }) {
|
|
|
312
301
|
const [head, ...rest] = field.name.split(".");
|
|
313
302
|
let value = block[head];
|
|
314
303
|
for (const segment of rest) {
|
|
315
|
-
value =
|
|
316
|
-
value && typeof value === "object" ? value[segment] : undefined;
|
|
304
|
+
value = value && typeof value === "object" ? value[segment] : undefined;
|
|
317
305
|
}
|
|
318
306
|
const absent = value === undefined || value === null;
|
|
319
307
|
|
package/engine/frontmatter.mjs
CHANGED
|
@@ -119,8 +119,7 @@ export function resolveCharges(fm) {
|
|
|
119
119
|
// A blank maximum means "does not use charges" — a stray current count
|
|
120
120
|
// cannot outlive it, since the logic layer disables both modifiers.
|
|
121
121
|
return {
|
|
122
|
-
value:
|
|
123
|
-
max === null ? null : toCount(sohlField(fm, "charges.value", null)),
|
|
122
|
+
value: max === null ? null : toCount(sohlField(fm, "charges.value", null)),
|
|
124
123
|
max,
|
|
125
124
|
};
|
|
126
125
|
}
|
|
@@ -149,9 +148,7 @@ export function resolveCharges(fm) {
|
|
|
149
148
|
export function resolveSkillAptitudes(fm, ctx = "item") {
|
|
150
149
|
const entries = readMapEntries(fm, "skillAptitudes");
|
|
151
150
|
if (entries === null) {
|
|
152
|
-
throw new Error(
|
|
153
|
-
`${ctx}: skillAptitudes must be a map of selector → number`,
|
|
154
|
-
);
|
|
151
|
+
throw new Error(`${ctx}: skillAptitudes must be a map of selector → number`);
|
|
155
152
|
}
|
|
156
153
|
const out = {};
|
|
157
154
|
for (const [selector, value] of entries) {
|
|
@@ -187,9 +184,7 @@ export function resolveSkillAptitudes(fm, ctx = "item") {
|
|
|
187
184
|
export function resolveRelation(fm, ctx = "item") {
|
|
188
185
|
const entries = readMapEntries(fm, "relation");
|
|
189
186
|
if (entries === null) {
|
|
190
|
-
throw new Error(
|
|
191
|
-
`${ctx}: relation must be a map of shortcode → standing`,
|
|
192
|
-
);
|
|
187
|
+
throw new Error(`${ctx}: relation must be a map of shortcode → standing`);
|
|
193
188
|
}
|
|
194
189
|
const out = {};
|
|
195
190
|
for (const [code, value] of entries) {
|
package/engine/generate.mjs
CHANGED
|
@@ -48,12 +48,7 @@ import { Journals } from "./journals.mjs";
|
|
|
48
48
|
import { Actors } from "../sohl/actors.mjs";
|
|
49
49
|
import { Macros } from "./macros.mjs";
|
|
50
50
|
import { Scenes } from "./scenes.mjs";
|
|
51
|
-
import {
|
|
52
|
-
statsForPack,
|
|
53
|
-
loadFolders,
|
|
54
|
-
buildFolderResolver,
|
|
55
|
-
writeFolderDocs,
|
|
56
|
-
} from "./helpers.mjs";
|
|
51
|
+
import { statsForPack, loadFolders, buildFolderResolver, writeFolderDocs } from "./helpers.mjs";
|
|
57
52
|
import { countContentNotes } from "./content-tree.mjs";
|
|
58
53
|
import { emitDiagnostic } from "./diagnostics.mjs";
|
|
59
54
|
import { loadPackConfig } from "./pack-config.mjs";
|
|
@@ -170,10 +165,7 @@ export function orderPassesByDependency(packs) {
|
|
|
170
165
|
readsOutputOf(pack.type).every(
|
|
171
166
|
(dependency) =>
|
|
172
167
|
!declared.has(dependency) ||
|
|
173
|
-
packs.every(
|
|
174
|
-
(other) =>
|
|
175
|
-
other.type !== dependency || emitted.has(other.name),
|
|
176
|
-
),
|
|
168
|
+
packs.every((other) => other.type !== dependency || emitted.has(other.name)),
|
|
177
169
|
);
|
|
178
170
|
|
|
179
171
|
while (remaining.length) {
|
|
@@ -269,8 +261,7 @@ async function generatePack(
|
|
|
269
261
|
let folderList;
|
|
270
262
|
let resolver;
|
|
271
263
|
try {
|
|
272
|
-
folderList =
|
|
273
|
-
folders ? loadFolders(path.join(contentBase, folders)) : [];
|
|
264
|
+
folderList = folders ? loadFolders(path.join(contentBase, folders)) : [];
|
|
274
265
|
({ resolver } = buildFolderResolver(folderList));
|
|
275
266
|
} catch (err) {
|
|
276
267
|
log.error(`${name} ${folders} validation failed: ${err.message}`);
|
|
@@ -365,10 +356,7 @@ export function emptyPassErrors(passes) {
|
|
|
365
356
|
* @throws {Error} If the configured Foundry package id has drifted from the
|
|
366
357
|
* shipped manifest's `id` (see `package-manifest.mjs`).
|
|
367
358
|
*/
|
|
368
|
-
export async function generatePacksJson({
|
|
369
|
-
only,
|
|
370
|
-
config = loadPackConfig(),
|
|
371
|
-
} = {}) {
|
|
359
|
+
export async function generatePacksJson({ only, config = loadPackConfig() } = {}) {
|
|
372
360
|
// Before anything is generated: every UUID written below is addressed to
|
|
373
361
|
// the configured `foundryPackage`, so a value that has drifted from the shipped
|
|
374
362
|
// manifest's `id` produces a whole pack of links that resolve nowhere.
|
package/engine/helpers.mjs
CHANGED
|
@@ -89,8 +89,7 @@ export function parseMarkdownFile(filePath) {
|
|
|
89
89
|
// be reported as a file position (#17). The frontmatter's lines and the
|
|
90
90
|
// blank lines `trim()` removes both sit in between, and the trim can take
|
|
91
91
|
// indentation off the first line as well — hence a column, not just a line.
|
|
92
|
-
const bodyStart =
|
|
93
|
-
content.length - raw.length + (raw.length - raw.trimStart().length);
|
|
92
|
+
const bodyStart = content.length - raw.length + (raw.length - raw.trimStart().length);
|
|
94
93
|
const before = content.slice(0, bodyStart);
|
|
95
94
|
const bodyLine = before.split("\n").length;
|
|
96
95
|
const bodyColumn = bodyStart - before.lastIndexOf("\n");
|
|
@@ -321,10 +320,7 @@ export function supportedCoreVersion(config = loadPackConfig()) {
|
|
|
321
320
|
* The resolved build configuration. Defaults to this repository's.
|
|
322
321
|
* @returns {object} The `_stats` block.
|
|
323
322
|
*/
|
|
324
|
-
export function buildStats(
|
|
325
|
-
systemVersion = undefined,
|
|
326
|
-
config = loadPackConfig(),
|
|
327
|
-
) {
|
|
323
|
+
export function buildStats(systemVersion = undefined, config = loadPackConfig()) {
|
|
328
324
|
return {
|
|
329
325
|
systemId: config.stats.systemId,
|
|
330
326
|
systemVersion: systemVersion ?? config.stats.systemVersion,
|
|
@@ -473,9 +469,7 @@ export function buildContentLinkIndex(contentBase, router = packRouter()) {
|
|
|
473
469
|
);
|
|
474
470
|
if (stale.length) {
|
|
475
471
|
for (const st of stale) {
|
|
476
|
-
log.error(
|
|
477
|
-
`Unusable link manifest for "${st.package}": ${st.reason}`,
|
|
478
|
-
);
|
|
472
|
+
log.error(`Unusable link manifest for "${st.package}": ${st.reason}`);
|
|
479
473
|
}
|
|
480
474
|
throw new Error(
|
|
481
475
|
"Cross-package links cannot be resolved from a stale manifest; " +
|
|
@@ -486,12 +480,7 @@ export function buildContentLinkIndex(contentBase, router = packRouter()) {
|
|
|
486
480
|
`Wikilink index: ${docs.length} local document(s), ` +
|
|
487
481
|
`${foreign.size} foreign address(es)`,
|
|
488
482
|
);
|
|
489
|
-
return buildWikilinkIndex(
|
|
490
|
-
docs,
|
|
491
|
-
foundryPackageId(),
|
|
492
|
-
foreign,
|
|
493
|
-
contentPackage(),
|
|
494
|
-
);
|
|
483
|
+
return buildWikilinkIndex(docs, foundryPackageId(), foreign, contentPackage());
|
|
495
484
|
}
|
|
496
485
|
|
|
497
486
|
/**
|
|
@@ -518,18 +507,7 @@ export function buildContentLinkIndex(contentBase, router = packRouter()) {
|
|
|
518
507
|
*/
|
|
519
508
|
export function convertNoteWikilinks(
|
|
520
509
|
body,
|
|
521
|
-
{
|
|
522
|
-
type,
|
|
523
|
-
id,
|
|
524
|
-
pack,
|
|
525
|
-
docPack,
|
|
526
|
-
index,
|
|
527
|
-
name,
|
|
528
|
-
file,
|
|
529
|
-
bodyLine,
|
|
530
|
-
bodyColumn,
|
|
531
|
-
lineMap,
|
|
532
|
-
},
|
|
510
|
+
{ type, id, pack, docPack, index, name, file, bodyLine, bodyColumn, lineMap },
|
|
533
511
|
) {
|
|
534
512
|
const result = convertWikilinks(body ?? "", {
|
|
535
513
|
type,
|
|
@@ -580,9 +558,7 @@ export function convertNoteWikilinks(
|
|
|
580
558
|
const claims = u.candidates ?? [];
|
|
581
559
|
const named =
|
|
582
560
|
claims.length ?
|
|
583
|
-
claims
|
|
584
|
-
.map((c) => `"${c.name}" (${c.type}-${c.shortcode})`)
|
|
585
|
-
.join(" and ")
|
|
561
|
+
claims.map((c) => `"${c.name}" (${c.type}-${c.shortcode})`).join(" and ")
|
|
586
562
|
: "two or more notes";
|
|
587
563
|
fail(
|
|
588
564
|
u,
|
|
@@ -613,9 +589,7 @@ export function convertNoteWikilinks(
|
|
|
613
589
|
`unresolved wikilink ${u.link} (${u.reason}) in "${name}"` +
|
|
614
590
|
// A link this build wrote is not at any authored position, so
|
|
615
591
|
// say where it came from instead of implying an edit site.
|
|
616
|
-
(at.generated ?
|
|
617
|
-
" — emitted by the content table on this line"
|
|
618
|
-
: ""),
|
|
592
|
+
(at.generated ? " — emitted by the content table on this line" : ""),
|
|
619
593
|
});
|
|
620
594
|
}
|
|
621
595
|
return result;
|
|
@@ -668,8 +642,7 @@ export function collectContentDocs(contentBase) {
|
|
|
668
642
|
* unlinkable; a note missing either renders as plain text rather than shipping a
|
|
669
643
|
* literal wikilink into a journal.
|
|
670
644
|
*/
|
|
671
|
-
const packLinkable = (doc) =>
|
|
672
|
-
Boolean(doc.fm?.shortcode) && Boolean(doc.fm?.type);
|
|
645
|
+
const packLinkable = (doc) => Boolean(doc.fm?.shortcode) && Boolean(doc.fm?.type);
|
|
673
646
|
|
|
674
647
|
/**
|
|
675
648
|
* Expand the fenced `dataview` tables in one note's markdown, before wikilinks
|
|
@@ -710,9 +683,7 @@ export function expandNoteTables(body, { docs, name, fm, bodyLine }) {
|
|
|
710
683
|
self,
|
|
711
684
|
});
|
|
712
685
|
if (errors.length) {
|
|
713
|
-
const err = new Error(
|
|
714
|
-
errors.map((e) => `content table — ${e.reason}`).join("; "),
|
|
715
|
-
);
|
|
686
|
+
const err = new Error(errors.map((e) => `content table — ${e.reason}`).join("; "));
|
|
716
687
|
// The first failing directive's line. Reporting one position for a
|
|
717
688
|
// message that may name several is honest here: a caller opens the
|
|
718
689
|
// file at the first thing to fix, and the message lists the rest.
|
|
@@ -735,18 +706,14 @@ export function expandNoteTables(body, { docs, name, fm, bodyLine }) {
|
|
|
735
706
|
*/
|
|
736
707
|
export function loadFolders(foldersFile) {
|
|
737
708
|
if (!fs.existsSync(foldersFile)) {
|
|
738
|
-
log.warn(
|
|
739
|
-
`No folders.yaml at ${foldersFile}; no folders will be emitted`,
|
|
740
|
-
);
|
|
709
|
+
log.warn(`No folders.yaml at ${foldersFile}; no folders will be emitted`);
|
|
741
710
|
return [];
|
|
742
711
|
}
|
|
743
712
|
const raw = fs.readFileSync(foldersFile, "utf8");
|
|
744
713
|
const parsed = yaml.parse(raw);
|
|
745
714
|
if (parsed == null) return [];
|
|
746
715
|
if (!Array.isArray(parsed)) {
|
|
747
|
-
throw new Error(
|
|
748
|
-
`folders.yaml must contain a YAML list; got ${typeof parsed}`,
|
|
749
|
-
);
|
|
716
|
+
throw new Error(`folders.yaml must contain a YAML list; got ${typeof parsed}`);
|
|
750
717
|
}
|
|
751
718
|
return parsed;
|
|
752
719
|
}
|
|
@@ -818,10 +785,7 @@ export function buildFolderResolver(folders) {
|
|
|
818
785
|
* underscores.
|
|
819
786
|
*/
|
|
820
787
|
export function folderFilename(name, id) {
|
|
821
|
-
return (
|
|
822
|
-
`folder_${unidecode(name)}_${id}`.replace(/[^0-9a-zA-Z]+/g, "_") +
|
|
823
|
-
".json"
|
|
824
|
-
);
|
|
788
|
+
return `folder_${unidecode(name)}_${id}`.replace(/[^0-9a-zA-Z]+/g, "_") + ".json";
|
|
825
789
|
}
|
|
826
790
|
|
|
827
791
|
/**
|
|
@@ -843,10 +807,7 @@ export function writeFolderDocs(folders, stats, destDir, documentType) {
|
|
|
843
807
|
_stats: stats,
|
|
844
808
|
_key: `!folders!${folder.id}`,
|
|
845
809
|
};
|
|
846
|
-
const outPath = path.join(
|
|
847
|
-
destDir,
|
|
848
|
-
folderFilename(folder.name, folder.id),
|
|
849
|
-
);
|
|
810
|
+
const outPath = path.join(destDir, folderFilename(folder.name, folder.id));
|
|
850
811
|
fs.writeFileSync(outPath, JSON.stringify(doc, null, 2), "utf8");
|
|
851
812
|
}
|
|
852
813
|
log.info(`Emitted ${folders.length} folder document(s) to ${destDir}`);
|
package/engine/homepage.mjs
CHANGED
|
@@ -262,9 +262,7 @@ export function checkHomepageCount(found, { contentBase, contentPackage }) {
|
|
|
262
262
|
if (pages.length === 1) return [];
|
|
263
263
|
|
|
264
264
|
return pages.map((page) => {
|
|
265
|
-
const others = pages
|
|
266
|
-
.filter((p) => p !== page)
|
|
267
|
-
.map((p) => formatLocator({ file: p.file }));
|
|
265
|
+
const others = pages.filter((p) => p !== page).map((p) => formatLocator({ file: p.file }));
|
|
268
266
|
return {
|
|
269
267
|
file: page.file,
|
|
270
268
|
...positionOfType(page.file),
|
|
@@ -292,11 +290,7 @@ export function checkHomepageCount(found, { contentBase, contentPackage }) {
|
|
|
292
290
|
*/
|
|
293
291
|
function positionOfType(file) {
|
|
294
292
|
try {
|
|
295
|
-
return positionInFrontmatter(
|
|
296
|
-
fs.readFileSync(file, "utf8"),
|
|
297
|
-
"type",
|
|
298
|
-
HOMEPAGE_TYPE,
|
|
299
|
-
);
|
|
293
|
+
return positionInFrontmatter(fs.readFileSync(file, "utf8"), "type", HOMEPAGE_TYPE);
|
|
300
294
|
} catch {
|
|
301
295
|
return {};
|
|
302
296
|
}
|
|
@@ -325,9 +319,7 @@ export function homepageTitle(fm, config) {
|
|
|
325
319
|
config?.packageBuild?.manifest
|
|
326
320
|
);
|
|
327
321
|
const title = manifest?.title;
|
|
328
|
-
return typeof title === "string" && title.trim() ?
|
|
329
|
-
title
|
|
330
|
-
: config.contentPackage;
|
|
322
|
+
return typeof title === "string" && title.trim() ? title : config.contentPackage;
|
|
331
323
|
}
|
|
332
324
|
|
|
333
325
|
/**
|
|
@@ -394,10 +386,7 @@ export const HOMEPAGE_ADDRESS_KEYS = Object.freeze(new Set(["url", "href"]));
|
|
|
394
386
|
*/
|
|
395
387
|
function collectProse(text, field, kind, out, skipCode = false) {
|
|
396
388
|
const pattern = new RegExp(MARKDOWN_LINK.source, "g");
|
|
397
|
-
const matches =
|
|
398
|
-
skipCode ?
|
|
399
|
-
matchAllOutsideCode(text, pattern)
|
|
400
|
-
: [...text.matchAll(pattern)];
|
|
389
|
+
const matches = skipCode ? matchAllOutsideCode(text, pattern) : [...text.matchAll(pattern)];
|
|
401
390
|
for (const m of matches) out.push({ field, url: m[1], kind });
|
|
402
391
|
}
|
|
403
392
|
|
package/engine/ids.mjs
CHANGED
|
@@ -36,11 +36,7 @@ import crypto from "crypto";
|
|
|
36
36
|
* @returns {string} A 16-character hexadecimal Foundry id.
|
|
37
37
|
*/
|
|
38
38
|
export function makeId(namespace, value) {
|
|
39
|
-
return crypto
|
|
40
|
-
.createHash("sha1")
|
|
41
|
-
.update(`${namespace}:${value}`)
|
|
42
|
-
.digest("hex")
|
|
43
|
-
.slice(0, 16);
|
|
39
|
+
return crypto.createHash("sha1").update(`${namespace}:${value}`).digest("hex").slice(0, 16);
|
|
44
40
|
}
|
|
45
41
|
|
|
46
42
|
/**
|
|
@@ -55,9 +51,7 @@ export function makeId(namespace, value) {
|
|
|
55
51
|
*
|
|
56
52
|
* @type {ReadonlySet<string>}
|
|
57
53
|
*/
|
|
58
|
-
export const MAP_TYPES = Object.freeze(
|
|
59
|
-
new Set(["battlemap", "localmap", "regionalmap"]),
|
|
60
|
-
);
|
|
54
|
+
export const MAP_TYPES = Object.freeze(new Set(["battlemap", "localmap", "regionalmap"]));
|
|
61
55
|
|
|
62
56
|
/**
|
|
63
57
|
* Content type → the pack its documents compile into, and the document type
|
|
@@ -77,10 +71,7 @@ export const PACK_BY_TYPE = Object.freeze({
|
|
|
77
71
|
macro: { pack: "macros", docType: "Macro" },
|
|
78
72
|
being: { pack: "actors", docType: "Actor" },
|
|
79
73
|
...Object.fromEntries(
|
|
80
|
-
[...MAP_TYPES].map((type) => [
|
|
81
|
-
type,
|
|
82
|
-
{ pack: "scenes", docType: "Scene" },
|
|
83
|
-
]),
|
|
74
|
+
[...MAP_TYPES].map((type) => [type, { pack: "scenes", docType: "Scene" }]),
|
|
84
75
|
),
|
|
85
76
|
});
|
|
86
77
|
|
package/engine/item-registry.mjs
CHANGED
|
@@ -72,9 +72,7 @@ export function itemTypes() {
|
|
|
72
72
|
* @throws {Error} When the configuration registers no builder for `type`.
|
|
73
73
|
*/
|
|
74
74
|
export function itemBuilder(type) {
|
|
75
|
-
const builder = /** @type {Record<string, Function>} */ (
|
|
76
|
-
loadPackConfig().itemBuilders
|
|
77
|
-
)[type];
|
|
75
|
+
const builder = /** @type {Record<string, Function>} */ (loadPackConfig().itemBuilders)[type];
|
|
78
76
|
if (typeof builder !== "function") {
|
|
79
77
|
throw new Error(
|
|
80
78
|
`No builder registered for item type "${type}" — add one to the ` +
|
|
@@ -112,9 +110,7 @@ export function itemBuilder(type) {
|
|
|
112
110
|
* @throws {Error} When the type's registry entry pairs no `img`.
|
|
113
111
|
*/
|
|
114
112
|
export function itemArt(type) {
|
|
115
|
-
const art = /** @type {Record<string, string|undefined>} */ (
|
|
116
|
-
loadPackConfig().itemArt
|
|
117
|
-
)[type];
|
|
113
|
+
const art = /** @type {Record<string, string|undefined>} */ (loadPackConfig().itemArt)[type];
|
|
118
114
|
if (!art) {
|
|
119
115
|
throw new Error(
|
|
120
116
|
`No default art for item type "${type}" — the note carries no ` +
|
package/engine/journals.mjs
CHANGED
|
@@ -45,13 +45,7 @@
|
|
|
45
45
|
|
|
46
46
|
import log from "loglevel";
|
|
47
47
|
|
|
48
|
-
import {
|
|
49
|
-
sohlField,
|
|
50
|
-
makeId,
|
|
51
|
-
resolveName,
|
|
52
|
-
defaultStats,
|
|
53
|
-
md,
|
|
54
|
-
} from "./helpers.mjs";
|
|
48
|
+
import { sohlField, makeId, resolveName, defaultStats, md } from "./helpers.mjs";
|
|
55
49
|
import { BasePackCompiler } from "./base-compiler.mjs";
|
|
56
50
|
import { anchorPageId } from "./wikilinks.mjs";
|
|
57
51
|
import { hasDocEntry, itemDocEntryId } from "./item-docs.mjs";
|
|
@@ -98,12 +92,10 @@ export function splitPages(body, leadName = "Introduction") {
|
|
|
98
92
|
// An H1 starts a page, as does any heading carrying an `{#slug}`
|
|
99
93
|
// anchor: a Foundry UUID can only address a page, so a linkable
|
|
100
94
|
// section has to be one.
|
|
101
|
-
const headingMatch =
|
|
102
|
-
!inCodeBlock ? line.match(/^\s*(#{1,6})\s+(.+?)\s*#*\s*$/) : null;
|
|
95
|
+
const headingMatch = !inCodeBlock ? line.match(/^\s*(#{1,6})\s+(.+?)\s*#*\s*$/) : null;
|
|
103
96
|
const rawHeading = headingMatch?.[2]?.trim();
|
|
104
97
|
const anchorMatch = rawHeading?.match(/^(.*?)\s*\{#([^}]+)\}\s*$/);
|
|
105
|
-
const startsPage =
|
|
106
|
-
headingMatch && (headingMatch[1].length === 1 || anchorMatch);
|
|
98
|
+
const startsPage = headingMatch && (headingMatch[1].length === 1 || anchorMatch);
|
|
107
99
|
if (startsPage) {
|
|
108
100
|
closeCurrent();
|
|
109
101
|
current = {
|
|
@@ -371,8 +363,6 @@ export class Journals extends BasePackCompiler {
|
|
|
371
363
|
|
|
372
364
|
/** @inheritdoc */
|
|
373
365
|
reportDetail(stats) {
|
|
374
|
-
log.debug(
|
|
375
|
-
`Skipped ${stats.skippedOther} non-doc file(s) (not type:doc)`,
|
|
376
|
-
);
|
|
366
|
+
log.debug(`Skipped ${stats.skippedOther} non-doc file(s) (not type:doc)`);
|
|
377
367
|
}
|
|
378
368
|
}
|
package/engine/kb-manifest.mjs
CHANGED
|
@@ -180,9 +180,7 @@ export const PACKAGE_BASE = Object.freeze({
|
|
|
180
180
|
*/
|
|
181
181
|
function checkBase(base, what) {
|
|
182
182
|
if (typeof base !== "string" || !base.endsWith("/")) {
|
|
183
|
-
throw new Error(
|
|
184
|
-
`${what}: package base ${JSON.stringify(base)} must end in a slash`,
|
|
185
|
-
);
|
|
183
|
+
throw new Error(`${what}: package base ${JSON.stringify(base)} must end in a slash`);
|
|
186
184
|
}
|
|
187
185
|
return base;
|
|
188
186
|
}
|
|
@@ -226,8 +224,7 @@ export function resolvePackageUrl(rel, base) {
|
|
|
226
224
|
checkBase(base, "resolvePackageUrl");
|
|
227
225
|
if (typeof rel !== "string" || !rel || rel.startsWith("/")) {
|
|
228
226
|
throw new Error(
|
|
229
|
-
`resolvePackageUrl: ${JSON.stringify(rel)} is not a package-` +
|
|
230
|
-
`relative address`,
|
|
227
|
+
`resolvePackageUrl: ${JSON.stringify(rel)} is not a package-` + `relative address`,
|
|
231
228
|
);
|
|
232
229
|
}
|
|
233
230
|
return `${base}${rel}`;
|
|
@@ -289,8 +286,7 @@ export function buildManifest(pkg, entries, base, foundryPackage) {
|
|
|
289
286
|
// address, so there is no fact being restated, and an anchor is not
|
|
290
287
|
// required to live inside its own entry. Publishing the complete link
|
|
291
288
|
// also keeps the page-id hash out of the published contract entirely.
|
|
292
|
-
if (e.anchors && Object.keys(e.anchors).length)
|
|
293
|
-
entry.anchors = e.anchors;
|
|
289
|
+
if (e.anchors && Object.keys(e.anchors).length) entry.anchors = e.anchors;
|
|
294
290
|
out[e.key ?? canonicalKey(pkg, type, shortcode)] = entry;
|
|
295
291
|
}
|
|
296
292
|
return {
|
|
@@ -327,12 +323,7 @@ export function writeManifests(entriesByPackage, dir, bases, foundryPackages) {
|
|
|
327
323
|
fs.mkdirSync(dir, { recursive: true });
|
|
328
324
|
const written = [];
|
|
329
325
|
for (const [pkg, entries] of entriesByPackage) {
|
|
330
|
-
const doc = buildManifest(
|
|
331
|
-
pkg,
|
|
332
|
-
entries,
|
|
333
|
-
bases?.[pkg],
|
|
334
|
-
foundryPackages?.[pkg],
|
|
335
|
-
);
|
|
326
|
+
const doc = buildManifest(pkg, entries, bases?.[pkg], foundryPackages?.[pkg]);
|
|
336
327
|
const file = path.join(dir, `${pkg}.json`);
|
|
337
328
|
fs.writeFileSync(file, `${JSON.stringify(doc, null, 2)}\n`);
|
|
338
329
|
written.push({
|
|
@@ -430,10 +421,7 @@ export function loadForeignManifests(dir, localPackages, bases = PACKAGE_BASE) {
|
|
|
430
421
|
// Absent for an entry with no page. A consumer must
|
|
431
422
|
// tolerate that rather than invent an href, exactly as
|
|
432
423
|
// it already tolerates an entry with no `uuid`.
|
|
433
|
-
url:
|
|
434
|
-
v.path == null ?
|
|
435
|
-
undefined
|
|
436
|
-
: resolvePackageUrl(v.path, base),
|
|
424
|
+
url: v.path == null ? undefined : resolvePackageUrl(v.path, base),
|
|
437
425
|
uuid: v.uuid,
|
|
438
426
|
doc: v.doc,
|
|
439
427
|
anchors: v.anchors,
|
package/engine/macros.mjs
CHANGED
|
@@ -51,12 +51,7 @@
|
|
|
51
51
|
|
|
52
52
|
import log from "loglevel";
|
|
53
53
|
|
|
54
|
-
import {
|
|
55
|
-
sohlField,
|
|
56
|
-
resolveName,
|
|
57
|
-
resolveImg,
|
|
58
|
-
defaultStats,
|
|
59
|
-
} from "./helpers.mjs";
|
|
54
|
+
import { sohlField, resolveName, resolveImg, defaultStats } from "./helpers.mjs";
|
|
60
55
|
import { BasePackCompiler } from "./base-compiler.mjs";
|
|
61
56
|
import { splitPages } from "./journals.mjs";
|
|
62
57
|
|
|
@@ -256,10 +251,7 @@ export function resolveMacroScope(fm, label) {
|
|
|
256
251
|
* @returns {MacroDocument} The Macro document.
|
|
257
252
|
* @throws {Error} When the frontmatter's macro type or scope is unusable.
|
|
258
253
|
*/
|
|
259
|
-
export function buildMacroEntry(
|
|
260
|
-
fm,
|
|
261
|
-
{ command, folder = null, stats = defaultStats() },
|
|
262
|
-
) {
|
|
254
|
+
export function buildMacroEntry(fm, { command, folder = null, stats = defaultStats() }) {
|
|
263
255
|
const name = resolveName(fm);
|
|
264
256
|
const id = fm.id;
|
|
265
257
|
return {
|
package/engine/manifest-emit.mjs
CHANGED
|
@@ -142,11 +142,7 @@ export function entriesForNote(fm, name, address, body, ctx) {
|
|
|
142
142
|
: undefined;
|
|
143
143
|
|
|
144
144
|
if (hasDocEntry(fm.type)) {
|
|
145
|
-
const docKey = canonicalKey(
|
|
146
|
-
contentPackage,
|
|
147
|
-
`doc${fm.type}`,
|
|
148
|
-
fm.shortcode,
|
|
149
|
-
);
|
|
145
|
+
const docKey = canonicalKey(contentPackage, `doc${fm.type}`, fm.shortcode);
|
|
150
146
|
const docEntryId = fm.id ? itemDocEntryId(fm.id) : undefined;
|
|
151
147
|
const docUuid = uuidFor("doc", docEntryId);
|
|
152
148
|
return [
|
|
@@ -166,10 +162,7 @@ export function entriesForNote(fm, name, address, body, ctx) {
|
|
|
166
162
|
// documentation, so both addresses resolve to the same URL.
|
|
167
163
|
url,
|
|
168
164
|
uuid: docUuid,
|
|
169
|
-
anchors:
|
|
170
|
-
docUuid ?
|
|
171
|
-
anchorsOf(docUuid, docEntryId, body ?? "", name)
|
|
172
|
-
: undefined,
|
|
165
|
+
anchors: docUuid ? anchorsOf(docUuid, docEntryId, body ?? "", name) : undefined,
|
|
173
166
|
},
|
|
174
167
|
];
|
|
175
168
|
}
|
|
@@ -184,10 +177,7 @@ export function entriesForNote(fm, name, address, body, ctx) {
|
|
|
184
177
|
name,
|
|
185
178
|
url,
|
|
186
179
|
uuid: own,
|
|
187
|
-
anchors:
|
|
188
|
-
own && fm.type === "doc" ?
|
|
189
|
-
anchorsOf(own, fm.id, body ?? "", name)
|
|
190
|
-
: undefined,
|
|
180
|
+
anchors: own && fm.type === "doc" ? anchorsOf(own, fm.id, body ?? "", name) : undefined,
|
|
191
181
|
},
|
|
192
182
|
];
|
|
193
183
|
}
|
|
@@ -221,10 +211,9 @@ export function collectManifestEntries(contentBase, ctx) {
|
|
|
221
211
|
// note yields two entries, so reporting one as the other overstates how
|
|
222
212
|
// much of the tree is published.
|
|
223
213
|
let notes = 0;
|
|
224
|
-
for (const { frontmatter: fm, body, absPath } of walkMarkdownTree(
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
)) {
|
|
214
|
+
for (const { frontmatter: fm, body, absPath } of walkMarkdownTree(contentBase, {
|
|
215
|
+
skipDirectories: ctx.skipDirectories,
|
|
216
|
+
})) {
|
|
228
217
|
if (!fm) continue;
|
|
229
218
|
const rel = path.relative(contentBase, absPath);
|
|
230
219
|
assertNoDeclaredPackage(fm, {
|