@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
@@ -277,10 +277,7 @@ export function resolveSchemaArtifact(config) {
277
277
  });
278
278
 
279
279
  // The system checking itself, against the schema its own build published.
280
- if (
281
- config.packageKind === "systems" &&
282
- config.foundryPackage === systemId
283
- ) {
280
+ if (config.packageKind === "systems" && config.foundryPackage === systemId) {
284
281
  const own = path.join(config.rootDir, SCHEMA_ARTIFACT_FILE);
285
282
  return fs.existsSync(own) ? read(own) : null;
286
283
  }
@@ -150,10 +150,7 @@ export function registryOf(src, name) {
150
150
  }
151
151
  if (ts.isObjectLiteralExpression(init)) {
152
152
  for (const p of init.properties) {
153
- if (
154
- ts.isPropertyAssignment(p) &&
155
- ts.isIdentifier(p.initializer)
156
- ) {
153
+ if (ts.isPropertyAssignment(p) && ts.isIdentifier(p.initializer)) {
157
154
  out.set(propName(p.name), p.initializer.text);
158
155
  }
159
156
  }
@@ -216,10 +213,7 @@ function resolveSpecifier(fromDir, spec, aliases) {
216
213
  }
217
214
  if (!base) return null;
218
215
  for (const ext of EXTENSIONS) {
219
- for (const candidate of [
220
- `${base}${ext}`,
221
- path.join(base, `index${ext}`),
222
- ]) {
216
+ for (const candidate of [`${base}${ext}`, path.join(base, `index${ext}`)]) {
223
217
  if (fs.existsSync(candidate)) return candidate;
224
218
  }
225
219
  }
@@ -243,11 +237,7 @@ function importSourceOf(src, name, aliases) {
243
237
  if (!bindings.elements.some((e) => e.name.text === name)) return;
244
238
  const spec = node.moduleSpecifier;
245
239
  if (!ts.isStringLiteral(spec)) return;
246
- found = resolveSpecifier(
247
- path.dirname(src.fileName),
248
- spec.text,
249
- aliases,
250
- );
240
+ found = resolveSpecifier(path.dirname(src.fileName), spec.text, aliases);
251
241
  });
252
242
  return found;
253
243
  }
@@ -286,9 +276,7 @@ function locateClass(fromFile, className, aliases, cache) {
286
276
  if (!imported) return null;
287
277
  const importedSrc = parse(imported, cache);
288
278
  const importedDecl = classDeclIn(importedSrc, className);
289
- return importedDecl ?
290
- { file: imported, src: importedSrc, decl: importedDecl }
291
- : null;
279
+ return importedDecl ? { file: imported, src: importedSrc, decl: importedDecl } : null;
292
280
  }
293
281
 
294
282
  /**
@@ -303,8 +291,7 @@ function superClassOf(decl) {
303
291
  for (const clause of decl.heritageClauses ?? []) {
304
292
  if (clause.token !== ts.SyntaxKind.ExtendsKeyword) continue;
305
293
  const [type] = clause.types;
306
- if (type && ts.isIdentifier(type.expression))
307
- return type.expression.text;
294
+ if (type && ts.isIdentifier(type.expression)) return type.expression.text;
308
295
  }
309
296
  return null;
310
297
  }
@@ -350,9 +337,8 @@ function literalReturnedBy(src, fnName) {
350
337
  if (isTarget) {
351
338
  const body =
352
339
  ts.isFunctionDeclaration(node) ? node.body
353
- : node.initializer && ts.isArrowFunction(node.initializer) ?
354
- node.initializer.body
355
- : null;
340
+ : node.initializer && ts.isArrowFunction(node.initializer) ? node.initializer.body
341
+ : null;
356
342
  if (body) literal = returnedLiteral(body);
357
343
  }
358
344
  ts.forEachChild(node, visit);
@@ -404,14 +390,10 @@ function isObjectAssign(expr) {
404
390
  */
405
391
  function schemaContributions(src, decl) {
406
392
  const method = decl.members.find(
407
- (m) =>
408
- ts.isMethodDeclaration(m) &&
409
- propName(m.name) === "defineSchema" &&
410
- m.body,
393
+ (m) => ts.isMethodDeclaration(m) && propName(m.name) === "defineSchema" && m.body,
411
394
  );
412
395
  // No `defineSchema()` — the whole schema is the parent's.
413
- if (!method)
414
- return { literals: [], edges: [{ kind: "super", name: null }] };
396
+ if (!method) return { literals: [], edges: [{ kind: "super", name: null }] };
415
397
 
416
398
  const direct = returnedLiteral(method.body);
417
399
  if (direct) return { literals: [direct], edges: [] };
@@ -568,12 +550,7 @@ export function fieldsForClass({ file, className, aliases, cache, rootDir }) {
568
550
  const importedSrc = parse(from, cache);
569
551
  const importedLit = literalReturnedBy(importedSrc, edge.name);
570
552
  if (importedLit) {
571
- walkLiteral(
572
- { file: from, src: importedSrc },
573
- importedLit,
574
- inherited,
575
- superName,
576
- );
553
+ walkLiteral({ file: from, src: importedSrc }, importedLit, inherited, superName);
577
554
  }
578
555
  return;
579
556
  }
@@ -616,12 +593,7 @@ export function fieldsForClass({ file, className, aliases, cache, rootDir }) {
616
593
  * @param {string} opts.version - The package version.
617
594
  * @returns {object} The artifact `schema-check.mjs` reads.
618
595
  */
619
- export function buildSchemaArtifact({
620
- rootDir,
621
- registries,
622
- packageId,
623
- version,
624
- }) {
596
+ export function buildSchemaArtifact({ rootDir, registries, packageId, version }) {
625
597
  const aliases = pathAliases(rootDir);
626
598
  const cache = new Map();
627
599
  const documents = {};
@@ -56,10 +56,7 @@ import { expandContentTables } from "./content-tables.mjs";
56
56
  import { buildSiteIndex, wikiContext } from "./site-index.mjs";
57
57
  import { frontmatterWikilinks, resolveWebWikilinks } from "./web-wikilinks.mjs";
58
58
  import { loadForeignManifests, manifestsComplete } from "./kb-manifest.mjs";
59
- import {
60
- formatUnaddressableFinding,
61
- unaddressableForeignPackages,
62
- } from "./foreign-manifests.mjs";
59
+ import { formatUnaddressableFinding, unaddressableForeignPackages } from "./foreign-manifests.mjs";
63
60
  import { deriveBeingInfo, isBeing } from "../sohl/being-info.mjs";
64
61
  import { loadPackConfig } from "./pack-config.mjs";
65
62
  import { searchableFrontmatter } from "./note-package.mjs";
@@ -192,10 +189,7 @@ export function collectContentPages(contentBase, ctx) {
192
189
  // authoring folder, for grouped landings.
193
190
  folder: path.basename(path.dirname(file)),
194
191
  sec,
195
- url:
196
- isReadme ?
197
- `${ctx.mount}${sec}/`
198
- : `${ctx.mount}${sec}/${slug}/`,
192
+ url: isReadme ? `${ctx.mount}${sec}/` : `${ctx.mount}${sec}/${slug}/`,
199
193
  isReadme,
200
194
  });
201
195
  }
@@ -231,10 +225,8 @@ export function collectTreePages(tree, ctx) {
231
225
  const isReadme = base.toLowerCase() === "readme.md";
232
226
  const sec = fm.category ?? tree.section;
233
227
  const h1 = /^#\s+(.+?)\s*$/m.exec(body);
234
- const h1Title =
235
- h1 ? h1[1].replace(/\{@link\s+[^}]*\}/g, "").trim() : null;
236
- const name =
237
- fm.name?.full ?? fm.title ?? h1Title ?? path.basename(base, ".md");
228
+ const h1Title = h1 ? h1[1].replace(/\{@link\s+[^}]*\}/g, "").trim() : null;
229
+ const name = fm.name?.full ?? fm.title ?? h1Title ?? path.basename(base, ".md");
238
230
  const slug = fm.slug ?? slugify(path.basename(base, ".md"));
239
231
  const relNoExt = rel.slice(0, -3).toLowerCase();
240
232
  const dir = path.posix.dirname(relNoExt);
@@ -562,9 +554,7 @@ export function pageDestination(page) {
562
554
  : path.join(page.sec, `${page.slug}.md`);
563
555
  }
564
556
  const rel =
565
- page.isReadme ?
566
- path.posix.join(path.posix.dirname(page.rel), "_index.md")
567
- : page.rel;
557
+ page.isReadme ? path.posix.join(path.posix.dirname(page.rel), "_index.md") : page.rel;
568
558
  return path.join(page.sec, rel);
569
559
  }
570
560
 
@@ -640,10 +630,7 @@ export function renderPages(pages, options) {
640
630
  const data = pageFrontmatter(page, { readmeSections, decorate });
641
631
  const dest = path.join(outRoot, pageDestination(page));
642
632
  fs.mkdirSync(path.dirname(dest), { recursive: true });
643
- fs.writeFileSync(
644
- dest,
645
- matter.stringify(protectCode(body, resolve), data),
646
- );
633
+ fs.writeFileSync(dest, matter.stringify(protectCode(body, resolve), data));
647
634
  byKind[page.kind] = (byKind[page.kind] ?? 0) + 1;
648
635
  }
649
636
 
@@ -676,10 +663,7 @@ export function renderPages(pages, options) {
676
663
  * @param {object} options - `{ sections, landing, sectionTitle }`.
677
664
  * @returns {number} How many landings were written.
678
665
  */
679
- export function writeSectionLandings(
680
- outRoot,
681
- { sections = {}, landing, sectionTitle },
682
- ) {
666
+ export function writeSectionLandings(outRoot, { sections = {}, landing, sectionTitle }) {
683
667
  let written = 0;
684
668
 
685
669
  // The mount's own landing carries a `type` of its own. Hugo's template
@@ -689,10 +673,7 @@ export function writeSectionLandings(
689
673
  // template out of the path where it could be inherited.
690
674
  if (landing) {
691
675
  fs.mkdirSync(outRoot, { recursive: true });
692
- fs.writeFileSync(
693
- path.join(outRoot, "_index.md"),
694
- matter.stringify("", landing),
695
- );
676
+ fs.writeFileSync(path.join(outRoot, "_index.md"), matter.stringify("", landing));
696
677
  written += 1;
697
678
  }
698
679
 
@@ -713,10 +694,7 @@ export function writeSectionLandings(
713
694
  if (!entry.isDirectory()) continue;
714
695
  const index = path.join(outRoot, entry.name, "_index.md");
715
696
  if (fs.existsSync(index)) continue;
716
- fs.writeFileSync(
717
- index,
718
- matter.stringify("", { title: sectionTitle(entry.name) }),
719
- );
697
+ fs.writeFileSync(index, matter.stringify("", { title: sectionTitle(entry.name) }));
720
698
  written += 1;
721
699
  }
722
700
  return written;
@@ -859,19 +837,16 @@ export function buildSite({ config, outRoot } = {}) {
859
837
  const outBase = resolveOutputRoot(resolved.rootDir, site.out);
860
838
  const out =
861
839
  outRoot ? path.resolve(outRoot)
862
- : publishesContent ?
863
- path.join(outBase, scheme.prefix.replace(/\/$/, ""))
840
+ : publishesContent ? path.join(outBase, scheme.prefix.replace(/\/$/, ""))
864
841
  // Homepage-only has no content mount, so the package's root *is*
865
842
  // the output root and `--out` redirects the whole of it.
866
- : outBase;
843
+ : outBase;
867
844
  // The homepage publishes at `/<contentPackage>/`, which is the package's
868
845
  // own root — one level above the content mount, and the same directory in
869
846
  // homepage-only mode.
870
847
  const homeRoot = publishesContent ? outBase : out;
871
848
 
872
- const packages = new Set(
873
- site.packages.length ? site.packages : [resolved.contentPackage],
874
- );
849
+ const packages = new Set(site.packages.length ? site.packages : [resolved.contentPackage]);
875
850
 
876
851
  const ctx = {
877
852
  packages,
@@ -152,10 +152,7 @@ function mergeForeign(index, foreignIndex) {
152
152
  const parts = readCanonicalKey(key);
153
153
  if (!parts) continue;
154
154
  const shortKey = `${parts.type}/${parts.shortcode}`;
155
- if (
156
- short.has(shortKey) &&
157
- short.get(shortKey).package !== value.package
158
- ) {
155
+ if (short.has(shortKey) && short.get(shortKey).package !== value.package) {
159
156
  ambiguous.add(shortKey);
160
157
  } else {
161
158
  short.set(shortKey, value);
@@ -242,10 +239,7 @@ export function buildSiteIndex(entries, { foreignIndex = new Map() } = {}) {
242
239
  // The page's package is the configured one — the site collection
243
240
  // resolves it and records it as `pkg`. Never read out of
244
241
  // frontmatter: `package:` is retired (#56).
245
- index.set(
246
- canonicalKey(e.pkg ?? contentPackage(), type, shortcode),
247
- value,
248
- );
242
+ index.set(canonicalKey(e.pkg ?? contentPackage(), type, shortcode), value);
249
243
  // In Foundry an item and its documentation are two documents, so
250
244
  // `skill/wpnc` and `docskill/wpnc` are two UUIDs (#1362). Here the
251
245
  // item note renders as one page which *is* its documentation, so
@@ -315,13 +309,7 @@ export function buildSiteIndex(entries, { foreignIndex = new Map() } = {}) {
315
309
  */
316
310
  export function wikiContext(
317
311
  built,
318
- {
319
- src,
320
- type = null,
321
- errors,
322
- foreignIndex = new Map(),
323
- manifestsComplete = true,
324
- },
312
+ { src, type = null, errors, foreignIndex = new Map(), manifestsComplete = true },
325
313
  ) {
326
314
  return {
327
315
  index: built.index,
@@ -46,12 +46,7 @@ import { slugify } from "./content-slug.mjs";
46
46
  // Re-exported so a site build keeps one import path for the whole of link
47
47
  // resolution: the same rule that names a page also names an anchor within it.
48
48
  export { slugify };
49
- import {
50
- authoredLabel,
51
- WIKILINK,
52
- isSamePage,
53
- parseWikilink,
54
- } from "./wikilink-syntax.mjs";
49
+ import { authoredLabel, WIKILINK, isSamePage, parseWikilink } from "./wikilink-syntax.mjs";
55
50
 
56
51
  /** KB heading/anchor slug: lowercase, non-alphanumerics to single hyphens. */
57
52
 
@@ -280,9 +275,7 @@ export function resolveWebWikilinks(body, ctx) {
280
275
  // Both separators qualify: `type-shortcode` is the canonical form
281
276
  // (#1398), and a hyphen inside a note *name* ("Grukar-ahk") is not
282
277
  // one, which is why the rule is the packs' own (#1409).
283
- const text =
284
- label ??
285
- (isAddress(target, ctx.contentTypes) ? hit.name : target);
278
+ const text = label ?? (isAddress(target, ctx.contentTypes) ? hit.name : target);
286
279
  // A pack-only package publishes Foundry addresses and no pages
287
280
  // (#1516), so its entries carry no `path` and resolve to no URL.
288
281
  // The address is real — this is not a typo and must not fail the
@@ -295,8 +288,7 @@ export function resolveWebWikilinks(body, ctx) {
295
288
  }
296
289
 
297
290
  const slash = target.indexOf("/");
298
- const prefix =
299
- slash === -1 ? null : target.slice(0, slash).toLowerCase();
291
+ const prefix = slash === -1 ? null : target.slice(0, slash).toLowerCase();
300
292
  // Deliberately *not* extended to the hyphen form, which is also how a
301
293
  // note addresses content in a package this build does not publish
302
294
  // (`Rules/Bestiary.md` → `being-grkrahk`, a real note in the `thalorna`
@@ -307,8 +299,7 @@ export function resolveWebWikilinks(body, ctx) {
307
299
  // which holds the reviewed list of cross-package exceptions — and which,
308
300
  // unlike this build, runs as part of `npm run lint` on every change.
309
301
  const badQualified =
310
- prefix !== null &&
311
- (ctx.sections.has(prefix) || ctx.contentTypes.has(prefix));
302
+ prefix !== null && (ctx.sections.has(prefix) || ctx.contentTypes.has(prefix));
312
303
  // The hyphen form is the canonical address (#1398) and is what the
313
304
  // authored content writes. It could not be guarded while some packages
314
305
  // were invisible here: `Rules/Bestiary.md` addresses `being-grkrahk`,