@heroiclands/package-build 8.1.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.
Files changed (75) hide show
  1. package/CHANGELOG.md +54 -0
  2. package/bin/content-build.mjs +44 -118
  3. package/bin/package-build.mjs +27 -69
  4. package/bin/report.mjs +1 -2
  5. package/bundle.mjs +2 -10
  6. package/config.mjs +31 -106
  7. package/container.mjs +13 -57
  8. package/content-config.mjs +45 -164
  9. package/coverage.mjs +14 -55
  10. package/deploy.mjs +4 -13
  11. package/e2e.mjs +16 -55
  12. package/engine/address-diff.mjs +1 -4
  13. package/engine/base-compiler.mjs +10 -28
  14. package/engine/code-fences.mjs +4 -13
  15. package/engine/compendiums.mjs +13 -37
  16. package/engine/content-address.mjs +2 -6
  17. package/engine/content-links.mjs +13 -44
  18. package/engine/content-lint.mjs +4 -15
  19. package/engine/content-slug.mjs +2 -6
  20. package/engine/content-tables.mjs +26 -79
  21. package/engine/diagnostics.mjs +4 -15
  22. package/engine/field-reference.mjs +6 -20
  23. package/engine/field-spec.mjs +1 -3
  24. package/engine/foreign-catalog.mjs +7 -22
  25. package/engine/foreign-manifests.mjs +1 -4
  26. package/engine/frontmatter-lint.mjs +6 -18
  27. package/engine/frontmatter.mjs +3 -8
  28. package/engine/generate.mjs +4 -16
  29. package/engine/helpers.mjs +13 -52
  30. package/engine/homepage.mjs +4 -15
  31. package/engine/ids.mjs +3 -12
  32. package/engine/item-registry.mjs +2 -6
  33. package/engine/journals.mjs +4 -14
  34. package/engine/kb-manifest.mjs +5 -17
  35. package/engine/macros.mjs +2 -10
  36. package/engine/manifest-emit.mjs +6 -17
  37. package/engine/map-notes.mjs +19 -69
  38. package/engine/note-package.mjs +1 -4
  39. package/engine/pack-config.mjs +17 -38
  40. package/engine/pack-router.mjs +1 -2
  41. package/engine/prose-config.mjs +20 -4
  42. package/engine/prose-lint.mjs +6 -14
  43. package/engine/region-events.mjs +1 -3
  44. package/engine/scene-levels.mjs +8 -22
  45. package/engine/scenes.mjs +13 -47
  46. package/engine/schema-check.mjs +1 -4
  47. package/engine/schema-extract.mjs +11 -39
  48. package/engine/site-build.mjs +12 -37
  49. package/engine/site-index.mjs +3 -15
  50. package/engine/web-wikilinks.mjs +4 -13
  51. package/engine/wikilinks.mjs +115 -167
  52. package/index.mjs +1 -5
  53. package/lang.mjs +1 -3
  54. package/manifest.mjs +10 -37
  55. package/markdownlint-config.mjs +1 -5
  56. package/package.json +1 -1
  57. package/sohl/actors.mjs +10 -40
  58. package/sohl/being-info.mjs +3 -6
  59. package/sohl/index.mjs +1 -6
  60. package/sohl/item-builders.mjs +1 -3
  61. package/sohl/item-fields.mjs +16 -34
  62. package/sohl/items.mjs +1 -3
  63. package/sohl/kb-passes.mjs +29 -39
  64. package/sohl/skill-base.mjs +7 -23
  65. package/stage.mjs +3 -13
  66. package/templates.mjs +4 -15
  67. package/types/bundle.d.mts +1 -1
  68. package/types/container.d.mts +2 -2
  69. package/types/coverage.d.mts +1 -1
  70. package/types/e2e.d.mts +4 -4
  71. package/types/engine/generate.d.mts +1 -1
  72. package/types/engine/helpers.d.mts +1 -1
  73. package/types/engine/schema-extract.d.mts +1 -1
  74. package/types/engine/site-index.d.mts +1 -1
  75. package/types/manifest.d.mts +1 -1
package/container.mjs CHANGED
@@ -155,10 +155,7 @@ export function containerName(packageId, stage, base = null) {
155
155
  * @returns {number} The host port.
156
156
  * @throws {Error} When the stage has no port from any source.
157
157
  */
158
- export function resolveStagePort(
159
- stage,
160
- { env = process.env, stages = {} } = {},
161
- ) {
158
+ export function resolveStagePort(stage, { env = process.env, stages = {} } = {}) {
162
159
  const fromEnv = env[`FOUNDRYVTT_${stage.toUpperCase()}_PORT`]?.trim();
163
160
  const port =
164
161
  fromEnv ? Number(fromEnv)
@@ -205,12 +202,7 @@ const EXACT_BUILD = /^\d+\.\d+$/;
205
202
  */
206
203
  export function resolveFoundryVersion(
207
204
  stage,
208
- {
209
- env = process.env,
210
- stages = {},
211
- compatibilityMinimum = null,
212
- e2eStage = "test",
213
- } = {},
205
+ { env = process.env, stages = {}, compatibilityMinimum = null, e2eStage = "test" } = {},
214
206
  ) {
215
207
  const fromEnv = env[`FOUNDRYVTT_${stage.toUpperCase()}_VERSION`]?.trim();
216
208
  if (fromEnv) return fromEnv;
@@ -267,9 +259,7 @@ export function resolveWorld(stage, { env = process.env, stages = {} } = {}) {
267
259
  const fromEnv = env[`FOUNDRYVTT_${stage.toUpperCase()}_WORLD`];
268
260
  if (fromEnv !== undefined) return fromEnv.trim();
269
261
  const declared = stages[stage]?.world;
270
- return declared === undefined || declared === null ?
271
- null
272
- : String(declared);
262
+ return declared === undefined || declared === null ? null : String(declared);
273
263
  }
274
264
 
275
265
  /**
@@ -387,12 +377,7 @@ export function dockerRunArgs({
387
377
  `${dataRoot}:/data`,
388
378
  ];
389
379
  if (cacheDir) {
390
- args.push(
391
- "--volume",
392
- `${cacheDir}:${CACHE_MOUNT}`,
393
- "-e",
394
- `CONTAINER_CACHE=${CACHE_MOUNT}`,
395
- );
380
+ args.push("--volume", `${cacheDir}:${CACHE_MOUNT}`, "-e", `CONTAINER_CACHE=${CACHE_MOUNT}`);
396
381
  }
397
382
  for (const [key, value] of passthroughEnv(env)) {
398
383
  args.push("-e", `${key}=${value}`);
@@ -414,10 +399,7 @@ export function dockerRunArgs({
414
399
  export function runDocker(args) {
415
400
  const result = spawnSync("docker", args, { stdio: "inherit" });
416
401
  if (result.error) {
417
- if (
418
- /** @type {NodeJS.ErrnoException} */ (result.error).code ===
419
- "ENOENT"
420
- ) {
402
+ if (/** @type {NodeJS.ErrnoException} */ (result.error).code === "ENOENT") {
421
403
  throw new Error(
422
404
  "docker was not found on PATH. Install Docker and make sure " +
423
405
  "the `docker` CLI is available.",
@@ -464,13 +446,7 @@ export function containerExists(name, runningOnly = false) {
464
446
  * @returns {string[]} The container names.
465
447
  */
466
448
  export function runningFoundryContainers(except = "") {
467
- return captureDocker([
468
- "ps",
469
- "--filter",
470
- "name=-foundry-",
471
- "--format",
472
- "{{.Names}}",
473
- ])
449
+ return captureDocker(["ps", "--filter", "name=-foundry-", "--format", "{{.Names}}"])
474
450
  .split("\n")
475
451
  .filter((name) => name && name !== except);
476
452
  }
@@ -559,26 +535,18 @@ export function resolveContainer({ stage, config, env = process.env }) {
559
535
  * @param {(message: string) => void} [opts.log] - Progress reporting.
560
536
  * @returns {number} The exit status.
561
537
  */
562
- export function startContainer(
563
- container,
564
- { dataRoot, env = process.env, log = () => {} },
565
- ) {
538
+ export function startContainer(container, { dataRoot, env = process.env, log = () => {} }) {
566
539
  if (containerExists(container.name)) {
567
540
  // Nothing is running against this data root — we are about to start it
568
541
  // — so any lock present was left by a previous crash.
569
- if (!containerExists(container.name, true))
570
- clearStaleLock(dataRoot, log);
571
- log(
572
- `Starting existing container '${container.name}' → ${container.url}`,
573
- );
542
+ if (!containerExists(container.name, true)) clearStaleLock(dataRoot, log);
543
+ log(`Starting existing container '${container.name}' → ${container.url}`);
574
544
  return runDocker(["start", container.name]);
575
545
  }
576
546
 
577
547
  clearStaleLock(dataRoot, log);
578
548
  if (container.cacheDir && !fs.existsSync(container.cacheDir)) {
579
- throw new Error(
580
- `FOUNDRYVTT_CACHE directory does not exist: ${container.cacheDir}.`,
581
- );
549
+ throw new Error(`FOUNDRYVTT_CACHE directory does not exist: ${container.cacheDir}.`);
582
550
  }
583
551
  log(
584
552
  `Creating container '${container.name}' from ${container.image}\n` +
@@ -634,13 +602,7 @@ export function removeContainer(name, log = () => {}) {
634
602
  * @returns {number} The exit status.
635
603
  * @throws {Error} On an unknown action, or a stage with no usable data root.
636
604
  */
637
- export function containerAction({
638
- action,
639
- stage,
640
- config,
641
- env = process.env,
642
- log = () => {},
643
- }) {
605
+ export function containerAction({ action, stage, config, env = process.env, log = () => {} }) {
644
606
  if (!CONTAINER_ACTIONS.includes(action)) {
645
607
  throw new Error(
646
608
  `Invalid container action '${action}'. Valid actions are: ` +
@@ -677,12 +639,7 @@ export function containerAction({
677
639
  removeContainer(container.name, log);
678
640
  return 0;
679
641
  case "status":
680
- return runDocker([
681
- "ps",
682
- "-a",
683
- "--filter",
684
- `name=^${container.name}$`,
685
- ]);
642
+ return runDocker(["ps", "-a", "--filter", `name=^${container.name}$`]);
686
643
  case "logs":
687
644
  return runDocker(["logs", "-f", container.name]);
688
645
  default:
@@ -702,8 +659,7 @@ function requireDataRoot(stage, env) {
702
659
  const dataRoot = resolveDataRoot(stage, { env });
703
660
  if (!fs.existsSync(dataRoot)) {
704
661
  throw new Error(
705
- `Data directory does not exist: ${dataRoot} (from ` +
706
- `${dataEnvVar(stage)}).`,
662
+ `Data directory does not exist: ${dataRoot} (from ` + `${dataEnvVar(stage)}).`,
707
663
  );
708
664
  }
709
665
  return dataRoot;
@@ -608,13 +608,7 @@ const SITE_TREE_KEYS = ["from", "section"];
608
608
  const SECTION_META_KEYS = ["title", "banner", "description"];
609
609
  const DOC_PAGE_KEYS = ["title", "out", "preamble"];
610
610
  const RELATIONSHIP_KINDS = ["systems", "requires", "recommends", "conflicts"];
611
- const RELATIONSHIP_KEYS = [
612
- "id",
613
- "type",
614
- "manifest",
615
- "compatibility",
616
- "itemCatalog",
617
- ];
611
+ const RELATIONSHIP_KEYS = ["id", "type", "manifest", "compatibility", "itemCatalog"];
618
612
  const ITEM_BUILDER_KEYS = ["system", "img", "fields"];
619
613
  const PACK_KEYS = [
620
614
  "name",
@@ -644,9 +638,7 @@ const STATS_KEYS = ["lastModifiedBy"];
644
638
  *
645
639
  * @type {symbol}
646
640
  */
647
- export const DERIVED_SYSTEM_VERSION = Symbol.for(
648
- "package-build.derivedSystemVersion",
649
- );
641
+ export const DERIVED_SYSTEM_VERSION = Symbol.for("package-build.derivedSystemVersion");
650
642
  const PUBLISH_KEYS = ["site", "manifests", "address"];
651
643
  const MANIFEST_KEYS = ["publish", "consume"];
652
644
  const ADDRESS_KEYS = ["prefix", "landing"];
@@ -672,10 +664,7 @@ function isPlainObject(value) {
672
664
  * @returns {never}
673
665
  */
674
666
  function fail(field, problem) {
675
- throw Object.assign(
676
- new TypeError(`package-build config: \`${field}\` ${problem}.`),
677
- { field },
678
- );
667
+ throw Object.assign(new TypeError(`package-build config: \`${field}\` ${problem}.`), { field });
679
668
  }
680
669
 
681
670
  /**
@@ -752,10 +741,7 @@ function normalizePack(value, where, nested = false) {
752
741
  typeof type !== "string" ||
753
742
  !(/** @type {readonly string[]} */ (PACK_DOCUMENT_TYPES).includes(type))
754
743
  ) {
755
- fail(
756
- `${where}.type`,
757
- `must be one of: ${PACK_DOCUMENT_TYPES.join(", ")}`,
758
- );
744
+ fail(`${where}.type`, `must be one of: ${PACK_DOCUMENT_TYPES.join(", ")}`);
759
745
  }
760
746
 
761
747
  if (pack.folders !== undefined && pack.folders !== null) {
@@ -837,20 +823,14 @@ function normalizePack(value, where, nested = false) {
837
823
  name,
838
824
  type: /** @type {PackDocumentType} */ (type),
839
825
  label:
840
- pack.label === undefined ?
841
- name
842
- : requireNonEmptyString(pack.label, `${where}.label`),
826
+ pack.label === undefined ? name : requireNonEmptyString(pack.label, `${where}.label`),
843
827
  private: optionalBoolean(pack.private, `${where}.private`, false),
844
828
  folders:
845
829
  pack.folders === undefined || pack.folders === null ?
846
830
  null
847
831
  : /** @type {string} */ (pack.folders),
848
832
  companions: Object.freeze(companions),
849
- mayBeEmpty: optionalBoolean(
850
- pack.mayBeEmpty,
851
- `${where}.mayBeEmpty`,
852
- false,
853
- ),
833
+ mayBeEmpty: optionalBoolean(pack.mayBeEmpty, `${where}.mayBeEmpty`, false),
854
834
  // Which pack of a type receives a note that declares none. Validated
855
835
  // across the whole list in `defineConfig` — at most one per type.
856
836
  default: optionalBoolean(pack.default, `${where}.default`, false),
@@ -938,10 +918,7 @@ function normalizeStats(value, derived) {
938
918
  // the package-wide answer for everything that has no pack in hand.
939
919
  systemId: derived.systemId,
940
920
  systemVersion: derived.systemVersion,
941
- lastModifiedBy: requireNonEmptyString(
942
- input.lastModifiedBy,
943
- "stats.lastModifiedBy",
944
- ),
921
+ lastModifiedBy: requireNonEmptyString(input.lastModifiedBy, "stats.lastModifiedBy"),
945
922
  });
946
923
  }
947
924
 
@@ -1056,10 +1033,7 @@ function normalizeSectionMeta(value, where) {
1056
1033
  out.banner = requireNonEmptyString(input.banner, `${where}.banner`);
1057
1034
  }
1058
1035
  if (input.description !== undefined) {
1059
- out.description = requireNonEmptyString(
1060
- input.description,
1061
- `${where}.description`,
1062
- );
1036
+ out.description = requireNonEmptyString(input.description, `${where}.description`);
1063
1037
  }
1064
1038
  return Object.freeze(out);
1065
1039
  }
@@ -1075,9 +1049,7 @@ function normalizeSectionMap(value, where) {
1075
1049
  if (value === undefined) return Object.freeze({});
1076
1050
  if (!isPlainObject(value)) fail(where, "must be a mapping");
1077
1051
  const out = {};
1078
- for (const [name, meta] of Object.entries(
1079
- /** @type {Record<string, unknown>} */ (value),
1080
- )) {
1052
+ for (const [name, meta] of Object.entries(/** @type {Record<string, unknown>} */ (value))) {
1081
1053
  out[name] = normalizeSectionMeta(meta, `${where}.${name}`);
1082
1054
  }
1083
1055
  return Object.freeze(out);
@@ -1124,10 +1096,7 @@ function normalizeSite(value) {
1124
1096
  trees.push(
1125
1097
  Object.freeze({
1126
1098
  from: requireNonEmptyString(tree.from, `${where}.from`),
1127
- section: requireNonEmptyString(
1128
- tree.section,
1129
- `${where}.section`,
1130
- ),
1099
+ section: requireNonEmptyString(tree.section, `${where}.section`),
1131
1100
  // The tree's own path, POSIX-separated — what a
1132
1101
  // repository-relative link inside it is resolved against.
1133
1102
  rel: String(tree.from).split(path.sep).join("/"),
@@ -1141,9 +1110,7 @@ function normalizeSite(value) {
1141
1110
  if (!Array.isArray(input.packages)) {
1142
1111
  fail("site.packages", "must be a list");
1143
1112
  }
1144
- packages = input.packages.map((p, i) =>
1145
- requireNonEmptyString(p, `site.packages[${i}]`),
1146
- );
1113
+ packages = input.packages.map((p, i) => requireNonEmptyString(p, `site.packages[${i}]`));
1147
1114
  }
1148
1115
 
1149
1116
  let landing = null;
@@ -1157,35 +1124,19 @@ function normalizeSite(value) {
1157
1124
  }
1158
1125
 
1159
1126
  return Object.freeze({
1160
- out:
1161
- input.out === undefined ?
1162
- ""
1163
- : requireNonEmptyString(input.out, "site.out"),
1164
- base:
1165
- input.base === undefined ?
1166
- ""
1167
- : requireNonEmptyString(input.base, "site.base"),
1127
+ out: input.out === undefined ? "" : requireNonEmptyString(input.out, "site.out"),
1128
+ base: input.base === undefined ? "" : requireNonEmptyString(input.base, "site.base"),
1168
1129
  packages: Object.freeze(packages),
1169
1130
  sections: normalizeSectionMap(input.sections, "site.sections"),
1170
- readmeSections: normalizeSectionMap(
1171
- input.readmeSections,
1172
- "site.readmeSections",
1173
- ),
1131
+ readmeSections: normalizeSectionMap(input.readmeSections, "site.readmeSections"),
1174
1132
  landing,
1175
1133
  trees: Object.freeze(trees),
1176
- pass:
1177
- input.pass === undefined ?
1178
- ""
1179
- : requireNonEmptyString(input.pass, "site.pass"),
1134
+ pass: input.pass === undefined ? "" : requireNonEmptyString(input.pass, "site.pass"),
1180
1135
  passOptions:
1181
1136
  input.passOptions === undefined ?
1182
1137
  Object.freeze({})
1183
1138
  : Object.freeze({ ...input.passOptions }),
1184
- backfillSections: optionalBoolean(
1185
- input.backfillSections,
1186
- "site.backfillSections",
1187
- false,
1188
- ),
1139
+ backfillSections: optionalBoolean(input.backfillSections, "site.backfillSections", false),
1189
1140
  });
1190
1141
  }
1191
1142
 
@@ -1213,10 +1164,7 @@ function normalizeCompatibility(value, where, requireMinimum = true) {
1213
1164
  out.minimum = requireNonEmptyString(input.minimum, `${where}.minimum`);
1214
1165
  }
1215
1166
  if (input.verified !== undefined) {
1216
- out.verified = requireNonEmptyString(
1217
- input.verified,
1218
- `${where}.verified`,
1219
- );
1167
+ out.verified = requireNonEmptyString(input.verified, `${where}.verified`);
1220
1168
  }
1221
1169
  return Object.freeze(out);
1222
1170
  }
@@ -1255,8 +1203,7 @@ function normalizeCompatibility(value, where, requireMinimum = true) {
1255
1203
  */
1256
1204
  function normalizeSystems(value) {
1257
1205
  if (value === undefined || value === null) return Object.freeze({});
1258
- if (!isPlainObject(value))
1259
- fail("systems", "must be a mapping of id to spec");
1206
+ if (!isPlainObject(value)) fail("systems", "must be a mapping of id to spec");
1260
1207
  const input = /** @type {Record<string, unknown>} */ (value);
1261
1208
 
1262
1209
  const out = {};
@@ -1276,10 +1223,7 @@ function normalizeSystems(value) {
1276
1223
  // `verified` is required because it is the value a pack stamps. A
1277
1224
  // declaration that cannot answer "which version was this built
1278
1225
  // against" is the gap this block exists to close.
1279
- const verified = requireNonEmptyString(
1280
- compat.verified,
1281
- `${at}.compatibility.verified`,
1282
- );
1226
+ const verified = requireNonEmptyString(compat.verified, `${at}.compatibility.verified`);
1283
1227
 
1284
1228
  out[id] = Object.freeze({
1285
1229
  manifest:
@@ -1290,10 +1234,7 @@ function normalizeSystems(value) {
1290
1234
  minimum:
1291
1235
  compat.minimum === undefined || compat.minimum === null ?
1292
1236
  null
1293
- : requireNonEmptyString(
1294
- compat.minimum,
1295
- `${at}.compatibility.minimum`,
1296
- ),
1237
+ : requireNonEmptyString(compat.minimum, `${at}.compatibility.minimum`),
1297
1238
  verified,
1298
1239
  }),
1299
1240
  });
@@ -1346,10 +1287,7 @@ function normalizeRelationships(value) {
1346
1287
  };
1347
1288
  for (const key of ["type", "manifest"]) {
1348
1289
  if (rel[key] !== undefined) {
1349
- spec[key] = requireNonEmptyString(
1350
- rel[key],
1351
- `${at}.${key}`,
1352
- );
1290
+ spec[key] = requireNonEmptyString(rel[key], `${at}.${key}`);
1353
1291
  }
1354
1292
  }
1355
1293
  const compat = normalizeCompatibility(
@@ -1367,10 +1305,7 @@ function normalizeRelationships(value) {
1367
1305
  fail(`${at}.itemCatalog`, "must be true or false");
1368
1306
  }
1369
1307
  if (rel.itemCatalog && spec.manifest === undefined) {
1370
- fail(
1371
- `${at}.itemCatalog`,
1372
- "needs a `manifest` naming the package to fetch",
1373
- );
1308
+ fail(`${at}.itemCatalog`, "needs a `manifest` naming the package to fetch");
1374
1309
  }
1375
1310
  spec.itemCatalog = rel.itemCatalog;
1376
1311
  }
@@ -1397,9 +1332,7 @@ function normalizePackageBuild(value) {
1397
1332
  "reads, and that package validates what is inside it",
1398
1333
  );
1399
1334
  }
1400
- return /** @type {Readonly<PackageBuildSection>} */ (
1401
- deepFreeze(structuredClone(value))
1402
- );
1335
+ return /** @type {Readonly<PackageBuildSection>} */ (deepFreeze(structuredClone(value)));
1403
1336
  }
1404
1337
 
1405
1338
  /**
@@ -1467,10 +1400,7 @@ function normalizeItemBuilders(value) {
1467
1400
  }
1468
1401
  itemBuilders[type] = /** @type {Function} */ (paired.system);
1469
1402
  if (paired.img !== undefined) {
1470
- itemArt[type] = requireNonEmptyString(
1471
- paired.img,
1472
- `itemBuilders.${type}.img`,
1473
- );
1403
+ itemArt[type] = requireNonEmptyString(paired.img, `itemBuilders.${type}.img`);
1474
1404
  }
1475
1405
  if (paired.fields !== undefined) {
1476
1406
  if (!Array.isArray(paired.fields)) {
@@ -1556,9 +1486,7 @@ function normalizePublish(value) {
1556
1486
  if (manifestsInput !== undefined && !isPlainObject(manifestsInput)) {
1557
1487
  fail("publish.manifests", "must be an object");
1558
1488
  }
1559
- const manifests = /** @type {Record<string, unknown>} */ (
1560
- manifestsInput ?? {}
1561
- );
1489
+ const manifests = /** @type {Record<string, unknown>} */ (manifestsInput ?? {});
1562
1490
  rejectUnknownKeys(manifests, MANIFEST_KEYS, "publish.manifests.");
1563
1491
 
1564
1492
  const addressInput = publish.address;
@@ -1589,26 +1517,15 @@ function normalizePublish(value) {
1589
1517
  DEFAULT_ADDRESS_SCHEME.landing
1590
1518
  : optionalString(address.landing, "publish.address.landing");
1591
1519
  if (!LANDING_RULES.includes(landing)) {
1592
- fail(
1593
- "publish.address.landing",
1594
- `must be one of ${LANDING_RULES.join(", ")}`,
1595
- );
1520
+ fail("publish.address.landing", `must be one of ${LANDING_RULES.join(", ")}`);
1596
1521
  }
1597
1522
 
1598
1523
  return Object.freeze({
1599
1524
  site: normalizeSiteMode(publish.site),
1600
1525
  address: Object.freeze({ prefix, landing }),
1601
1526
  manifests: Object.freeze({
1602
- publish: optionalBoolean(
1603
- manifests.publish,
1604
- "publish.manifests.publish",
1605
- false,
1606
- ),
1607
- consume: optionalBoolean(
1608
- manifests.consume,
1609
- "publish.manifests.consume",
1610
- false,
1611
- ),
1527
+ publish: optionalBoolean(manifests.publish, "publish.manifests.publish", false),
1528
+ consume: optionalBoolean(manifests.consume, "publish.manifests.consume", false),
1612
1529
  }),
1613
1530
  });
1614
1531
  }
@@ -1629,13 +1546,9 @@ function normalizePublish(value) {
1629
1546
  */
1630
1547
  export function defineConfig(config) {
1631
1548
  if (!isPlainObject(config)) {
1632
- throw new TypeError(
1633
- "package-build config: expected a configuration object.",
1634
- );
1549
+ throw new TypeError("package-build config: expected a configuration object.");
1635
1550
  }
1636
- const input = /** @type {Record<string, unknown>} */ (
1637
- /** @type {unknown} */ (config)
1638
- );
1551
+ const input = /** @type {Record<string, unknown>} */ (/** @type {unknown} */ (config));
1639
1552
  rejectUnknownKeys(input, CONFIG_KEYS, "");
1640
1553
 
1641
1554
  const rootDir = requireNonEmptyString(input.rootDir, "rootDir");
@@ -1650,21 +1563,14 @@ export function defineConfig(config) {
1650
1563
  const packageKind = input.packageKind;
1651
1564
  if (
1652
1565
  typeof packageKind !== "string" ||
1653
- !(
1654
- /** @type {readonly string[]} */ (PACKAGE_KINDS).includes(
1655
- packageKind,
1656
- )
1657
- )
1566
+ !(/** @type {readonly string[]} */ (PACKAGE_KINDS).includes(packageKind))
1658
1567
  ) {
1659
1568
  fail("packageKind", `must be one of: ${PACKAGE_KINDS.join(", ")}`);
1660
1569
  }
1661
1570
 
1662
1571
  if (!Array.isArray(input.packs)) fail("packs", "must be an array");
1663
- if (input.packs.length === 0)
1664
- fail("packs", "must declare at least one pack");
1665
- const packs = input.packs.map((pack, index) =>
1666
- normalizePack(pack, `packs[${index}]`),
1667
- );
1572
+ if (input.packs.length === 0) fail("packs", "must declare at least one pack");
1573
+ const packs = input.packs.map((pack, index) => normalizePack(pack, `packs[${index}]`));
1668
1574
 
1669
1575
  // One list, so the compile order and the directory list cannot disagree —
1670
1576
  // they used to be `PACK_CONFIGS` and `SOURCE_PACKS`, maintained apart (#1508).
@@ -1686,9 +1592,7 @@ export function defineConfig(config) {
1686
1592
  const declaredSystems = new Set(Object.keys(systems));
1687
1593
  /** `relationships.systems`, for the derivations that still consult it. */
1688
1594
  const relationshipSystems = /** @type {{id?: string}[]} */ (
1689
- (isPlainObject(input.relationships) ?
1690
- input.relationships.systems
1691
- : null) ?? []
1595
+ (isPlainObject(input.relationships) ? input.relationships.systems : null) ?? []
1692
1596
  );
1693
1597
 
1694
1598
  // A name that resolves to nothing is a build error rather than a
@@ -1747,32 +1651,21 @@ export function defineConfig(config) {
1747
1651
  defaultsByType.set(pack.type, pack.name);
1748
1652
  }
1749
1653
 
1750
- if (
1751
- input.skipDirectories !== undefined &&
1752
- !Array.isArray(input.skipDirectories)
1753
- ) {
1654
+ if (input.skipDirectories !== undefined && !Array.isArray(input.skipDirectories)) {
1754
1655
  fail("skipDirectories", "must be an array");
1755
1656
  }
1756
1657
  const skipDirectories = (input.skipDirectories ?? []).map((name, index) =>
1757
1658
  requireNonEmptyString(name, `skipDirectories[${index}]`),
1758
1659
  );
1759
1660
 
1760
- const foundryPackage = requireNonEmptyString(
1761
- input.foundryPackage,
1762
- "foundryPackage",
1763
- );
1661
+ const foundryPackage = requireNonEmptyString(input.foundryPackage, "foundryPackage");
1764
1662
 
1765
- const { itemBuilders, itemArt, itemFields } = normalizeItemBuilders(
1766
- input.itemBuilders,
1767
- );
1663
+ const { itemBuilders, itemArt, itemFields } = normalizeItemBuilders(input.itemBuilders);
1768
1664
  const itemTypes = Object.freeze(new Set(Object.keys(itemBuilders)));
1769
1665
 
1770
1666
  return Object.freeze({
1771
1667
  rootDir,
1772
- contentPackage: requireNonEmptyString(
1773
- input.contentPackage,
1774
- "contentPackage",
1775
- ),
1668
+ contentPackage: requireNonEmptyString(input.contentPackage, "contentPackage"),
1776
1669
  foundryPackage,
1777
1670
  packageKind: /** @type {PackageKind} */ (packageKind),
1778
1671
  // Foundry serves a package's files from `<kind>/<id>/`, so this is the
@@ -1798,9 +1691,8 @@ export function defineConfig(config) {
1798
1691
  // which is the duplication this whole change exists to
1799
1692
  // remove. Several entries have no single answer and get
1800
1693
  // none.
1801
- : relationshipSystems.length === 1 ?
1802
- (relationshipSystems[0]?.id ?? null)
1803
- : null,
1694
+ : relationshipSystems.length === 1 ? (relationshipSystems[0]?.id ?? null)
1695
+ : null,
1804
1696
  // Derived here where the answer is pure data — the `verified` of
1805
1697
  // whichever system the package-wide block takes — and supplied by
1806
1698
  // the loader otherwise. The loader is the half that may do I/O, and
@@ -1811,16 +1703,10 @@ export function defineConfig(config) {
1811
1703
  (() => {
1812
1704
  const id =
1813
1705
  requiresSystem ??
1814
- (Object.keys(systems).length === 1 ?
1815
- Object.keys(systems)[0]
1816
- : null);
1817
- return id ?
1818
- (systems[id]?.compatibility?.verified ?? null)
1819
- : null;
1706
+ (Object.keys(systems).length === 1 ? Object.keys(systems)[0] : null);
1707
+ return id ? (systems[id]?.compatibility?.verified ?? null) : null;
1820
1708
  })() ??
1821
- (isPlainObject(input.stats) ?
1822
- input.stats[DERIVED_SYSTEM_VERSION]
1823
- : null) ??
1709
+ (isPlainObject(input.stats) ? input.stats[DERIVED_SYSTEM_VERSION] : null) ??
1824
1710
  null,
1825
1711
  }),
1826
1712
  itemBuilders,
@@ -1833,18 +1719,13 @@ export function defineConfig(config) {
1833
1719
  // runtime. Two would drift, which is the whole reason the composition
1834
1720
  // was written down in one place to begin with.
1835
1721
  itemTypes,
1836
- docEntryTypes: Object.freeze(
1837
- new Set([...itemTypes, "macro", ...MAP_TYPES]),
1838
- ),
1722
+ docEntryTypes: Object.freeze(new Set([...itemTypes, "macro", ...MAP_TYPES])),
1839
1723
  skipDirectories: Object.freeze(skipDirectories),
1840
1724
  packs: Object.freeze(packs),
1841
1725
  packDirectories: Object.freeze(packDirectories),
1842
1726
  docs: normalizeDocs(input.docs),
1843
1727
  site: normalizeSite(input.site),
1844
- compatibility: normalizeCompatibility(
1845
- input.compatibility,
1846
- "compatibility",
1847
- ),
1728
+ compatibility: normalizeCompatibility(input.compatibility, "compatibility"),
1848
1729
  relationships: normalizeRelationships(input.relationships),
1849
1730
  systems,
1850
1731
  requiresSystem,