@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.
- package/CHANGELOG.md +54 -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 +11 -39
- 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
package/engine/schema-check.mjs
CHANGED
|
@@ -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
|
-
|
|
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 = {};
|
package/engine/site-build.mjs
CHANGED
|
@@ -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
|
-
|
|
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
|
-
:
|
|
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,
|
package/engine/site-index.mjs
CHANGED
|
@@ -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,
|
package/engine/web-wikilinks.mjs
CHANGED
|
@@ -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`,
|