@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
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
|
}
|
|
@@ -95,6 +95,37 @@ function propName(name) {
|
|
|
95
95
|
return name.getText();
|
|
96
96
|
}
|
|
97
97
|
|
|
98
|
+
/**
|
|
99
|
+
* A *field* name, which unlike a property name elsewhere must be knowable.
|
|
100
|
+
*
|
|
101
|
+
* A computed key — `[`${name}Date`]: worldTimeDateField()` — depends on an
|
|
102
|
+
* argument this reader does not evaluate, so its real name is not in the file.
|
|
103
|
+
* {@link propName} would hand back the source text, and a field called
|
|
104
|
+
* ``[`${name}Date`]`` matches nothing a builder could ever emit: it is absent
|
|
105
|
+
* from the schema for checking purposes while *looking* present, and it shows
|
|
106
|
+
* up as permanently unemitted noise.
|
|
107
|
+
*
|
|
108
|
+
* So this refuses rather than guessing. The schema is a contract other
|
|
109
|
+
* repositories read, and a contract it cannot state is worth stopping for —
|
|
110
|
+
* the same reason `compareFields` refuses an artifact of the wrong version
|
|
111
|
+
* instead of resolving it anyway. Writing the keys out fixes it at the source,
|
|
112
|
+
* where the names are actually decided.
|
|
113
|
+
*
|
|
114
|
+
* @param {ts.PropertyName} name - The property name node.
|
|
115
|
+
* @param {string} file - For the message.
|
|
116
|
+
* @returns {string} The literal field name.
|
|
117
|
+
* @throws {Error} When the name is computed.
|
|
118
|
+
*/
|
|
119
|
+
function fieldName(name, file) {
|
|
120
|
+
if (ts.isIdentifier(name) || ts.isStringLiteral(name)) return name.text;
|
|
121
|
+
throw new Error(
|
|
122
|
+
`${path.basename(file)} declares a schema field with a computed name, ` +
|
|
123
|
+
`\`${name.getText()}\`, whose value depends on an argument this ` +
|
|
124
|
+
`reader does not evaluate. Write the keys out so the published ` +
|
|
125
|
+
`schema can name them.`,
|
|
126
|
+
);
|
|
127
|
+
}
|
|
128
|
+
|
|
98
129
|
/**
|
|
99
130
|
* The `subtype: ClassName` entries of a registry object literal.
|
|
100
131
|
*
|
|
@@ -119,10 +150,7 @@ export function registryOf(src, name) {
|
|
|
119
150
|
}
|
|
120
151
|
if (ts.isObjectLiteralExpression(init)) {
|
|
121
152
|
for (const p of init.properties) {
|
|
122
|
-
if (
|
|
123
|
-
ts.isPropertyAssignment(p) &&
|
|
124
|
-
ts.isIdentifier(p.initializer)
|
|
125
|
-
) {
|
|
153
|
+
if (ts.isPropertyAssignment(p) && ts.isIdentifier(p.initializer)) {
|
|
126
154
|
out.set(propName(p.name), p.initializer.text);
|
|
127
155
|
}
|
|
128
156
|
}
|
|
@@ -185,10 +213,7 @@ function resolveSpecifier(fromDir, spec, aliases) {
|
|
|
185
213
|
}
|
|
186
214
|
if (!base) return null;
|
|
187
215
|
for (const ext of EXTENSIONS) {
|
|
188
|
-
for (const candidate of [
|
|
189
|
-
`${base}${ext}`,
|
|
190
|
-
path.join(base, `index${ext}`),
|
|
191
|
-
]) {
|
|
216
|
+
for (const candidate of [`${base}${ext}`, path.join(base, `index${ext}`)]) {
|
|
192
217
|
if (fs.existsSync(candidate)) return candidate;
|
|
193
218
|
}
|
|
194
219
|
}
|
|
@@ -212,11 +237,7 @@ function importSourceOf(src, name, aliases) {
|
|
|
212
237
|
if (!bindings.elements.some((e) => e.name.text === name)) return;
|
|
213
238
|
const spec = node.moduleSpecifier;
|
|
214
239
|
if (!ts.isStringLiteral(spec)) return;
|
|
215
|
-
found = resolveSpecifier(
|
|
216
|
-
path.dirname(src.fileName),
|
|
217
|
-
spec.text,
|
|
218
|
-
aliases,
|
|
219
|
-
);
|
|
240
|
+
found = resolveSpecifier(path.dirname(src.fileName), spec.text, aliases);
|
|
220
241
|
});
|
|
221
242
|
return found;
|
|
222
243
|
}
|
|
@@ -255,9 +276,7 @@ function locateClass(fromFile, className, aliases, cache) {
|
|
|
255
276
|
if (!imported) return null;
|
|
256
277
|
const importedSrc = parse(imported, cache);
|
|
257
278
|
const importedDecl = classDeclIn(importedSrc, className);
|
|
258
|
-
return importedDecl ?
|
|
259
|
-
{ file: imported, src: importedSrc, decl: importedDecl }
|
|
260
|
-
: null;
|
|
279
|
+
return importedDecl ? { file: imported, src: importedSrc, decl: importedDecl } : null;
|
|
261
280
|
}
|
|
262
281
|
|
|
263
282
|
/**
|
|
@@ -272,8 +291,7 @@ function superClassOf(decl) {
|
|
|
272
291
|
for (const clause of decl.heritageClauses ?? []) {
|
|
273
292
|
if (clause.token !== ts.SyntaxKind.ExtendsKeyword) continue;
|
|
274
293
|
const [type] = clause.types;
|
|
275
|
-
if (type && ts.isIdentifier(type.expression))
|
|
276
|
-
return type.expression.text;
|
|
294
|
+
if (type && ts.isIdentifier(type.expression)) return type.expression.text;
|
|
277
295
|
}
|
|
278
296
|
return null;
|
|
279
297
|
}
|
|
@@ -319,9 +337,8 @@ function literalReturnedBy(src, fnName) {
|
|
|
319
337
|
if (isTarget) {
|
|
320
338
|
const body =
|
|
321
339
|
ts.isFunctionDeclaration(node) ? node.body
|
|
322
|
-
: node.initializer && ts.isArrowFunction(node.initializer) ?
|
|
323
|
-
|
|
324
|
-
: null;
|
|
340
|
+
: node.initializer && ts.isArrowFunction(node.initializer) ? node.initializer.body
|
|
341
|
+
: null;
|
|
325
342
|
if (body) literal = returnedLiteral(body);
|
|
326
343
|
}
|
|
327
344
|
ts.forEachChild(node, visit);
|
|
@@ -373,14 +390,10 @@ function isObjectAssign(expr) {
|
|
|
373
390
|
*/
|
|
374
391
|
function schemaContributions(src, decl) {
|
|
375
392
|
const method = decl.members.find(
|
|
376
|
-
(m) =>
|
|
377
|
-
ts.isMethodDeclaration(m) &&
|
|
378
|
-
propName(m.name) === "defineSchema" &&
|
|
379
|
-
m.body,
|
|
393
|
+
(m) => ts.isMethodDeclaration(m) && propName(m.name) === "defineSchema" && m.body,
|
|
380
394
|
);
|
|
381
395
|
// No `defineSchema()` — the whole schema is the parent's.
|
|
382
|
-
if (!method)
|
|
383
|
-
return { literals: [], edges: [{ kind: "super", name: null }] };
|
|
396
|
+
if (!method) return { literals: [], edges: [{ kind: "super", name: null }] };
|
|
384
397
|
|
|
385
398
|
const direct = returnedLiteral(method.body);
|
|
386
399
|
if (direct) return { literals: [direct], edges: [] };
|
|
@@ -424,16 +437,16 @@ function isSchemaField(expr) {
|
|
|
424
437
|
}
|
|
425
438
|
|
|
426
439
|
/** The keys of a `new SchemaField({ … })`, recursively dotted. */
|
|
427
|
-
function nestedKeysOf(expr) {
|
|
440
|
+
function nestedKeysOf(expr, file) {
|
|
428
441
|
if (!isSchemaField(expr)) return [];
|
|
429
442
|
const arg = expr.arguments?.[0];
|
|
430
443
|
if (!arg || !ts.isObjectLiteralExpression(arg)) return [];
|
|
431
444
|
const out = [];
|
|
432
445
|
for (const p of arg.properties) {
|
|
433
446
|
if (!ts.isPropertyAssignment(p)) continue;
|
|
434
|
-
const key =
|
|
447
|
+
const key = fieldName(p.name, file);
|
|
435
448
|
out.push(key);
|
|
436
|
-
for (const child of nestedKeysOf(p.initializer)) {
|
|
449
|
+
for (const child of nestedKeysOf(p.initializer, file)) {
|
|
437
450
|
out.push(`${key}.${child}`);
|
|
438
451
|
}
|
|
439
452
|
}
|
|
@@ -445,7 +458,7 @@ function nestedKeysOf(expr) {
|
|
|
445
458
|
*
|
|
446
459
|
* @returns {{own: string[], edges: object[]}}
|
|
447
460
|
*/
|
|
448
|
-
function readLiteral(literal) {
|
|
461
|
+
function readLiteral(literal, file) {
|
|
449
462
|
const own = [];
|
|
450
463
|
const edges = [];
|
|
451
464
|
for (const p of literal.properties) {
|
|
@@ -455,9 +468,9 @@ function readLiteral(literal) {
|
|
|
455
468
|
continue;
|
|
456
469
|
}
|
|
457
470
|
if (!ts.isPropertyAssignment(p)) continue;
|
|
458
|
-
const key =
|
|
471
|
+
const key = fieldName(p.name, file);
|
|
459
472
|
own.push(key);
|
|
460
|
-
for (const child of nestedKeysOf(p.initializer)) {
|
|
473
|
+
for (const child of nestedKeysOf(p.initializer, file)) {
|
|
461
474
|
own.push(`${key}.${child}`);
|
|
462
475
|
}
|
|
463
476
|
}
|
|
@@ -505,7 +518,7 @@ export function fieldsForClass({ file, className, aliases, cache, rootDir }) {
|
|
|
505
518
|
|
|
506
519
|
/** Walk a schema literal, following the spreads inside it. */
|
|
507
520
|
const walkLiteral = (located, literal, into, superName) => {
|
|
508
|
-
const { own: fields, edges } = readLiteral(literal);
|
|
521
|
+
const { own: fields, edges } = readLiteral(literal, located.file);
|
|
509
522
|
into.push(...fields);
|
|
510
523
|
for (const edge of edges) followEdge(located, edge, superName);
|
|
511
524
|
};
|
|
@@ -516,12 +529,29 @@ export function fieldsForClass({ file, className, aliases, cache, rootDir }) {
|
|
|
516
529
|
const key = `local:${located.file}:${edge.name}`;
|
|
517
530
|
if (seen.has(key)) return;
|
|
518
531
|
seen.add(key);
|
|
519
|
-
// A
|
|
520
|
-
//
|
|
521
|
-
//
|
|
522
|
-
//
|
|
532
|
+
// A spread of a schema-building function is the parent's
|
|
533
|
+
// contribution — a shared `defineXDataSchema()` is where the common
|
|
534
|
+
// fields come from — so it lands in `inherited` whichever file it
|
|
535
|
+
// is written in.
|
|
523
536
|
const lit = literalReturnedBy(located.src, edge.name);
|
|
524
|
-
if (lit)
|
|
537
|
+
if (lit) {
|
|
538
|
+
walkLiteral(located, lit, inherited, superName);
|
|
539
|
+
return;
|
|
540
|
+
}
|
|
541
|
+
// Not declared here, so it was imported. Following it matters more
|
|
542
|
+
// than it looks: the shared base schema is spread by name from the
|
|
543
|
+
// file that exports it, and resolving only same-file functions
|
|
544
|
+
// dropped it **entirely and in silence** — every SoHL subtype lost
|
|
545
|
+
// `shortcode` and `actionDefs`, so content correctly authoring
|
|
546
|
+
// `system.shortcode` was reported as undeclared. A spread that
|
|
547
|
+
// resolves to nothing must not read as a spread of nothing.
|
|
548
|
+
const from = importSourceOf(located.src, edge.name, aliases);
|
|
549
|
+
if (!from) return;
|
|
550
|
+
const importedSrc = parse(from, cache);
|
|
551
|
+
const importedLit = literalReturnedBy(importedSrc, edge.name);
|
|
552
|
+
if (importedLit) {
|
|
553
|
+
walkLiteral({ file: from, src: importedSrc }, importedLit, inherited, superName);
|
|
554
|
+
}
|
|
525
555
|
return;
|
|
526
556
|
}
|
|
527
557
|
const name = edge.kind === "super" ? superName : edge.name;
|
|
@@ -563,12 +593,7 @@ export function fieldsForClass({ file, className, aliases, cache, rootDir }) {
|
|
|
563
593
|
* @param {string} opts.version - The package version.
|
|
564
594
|
* @returns {object} The artifact `schema-check.mjs` reads.
|
|
565
595
|
*/
|
|
566
|
-
export function buildSchemaArtifact({
|
|
567
|
-
rootDir,
|
|
568
|
-
registries,
|
|
569
|
-
packageId,
|
|
570
|
-
version,
|
|
571
|
-
}) {
|
|
596
|
+
export function buildSchemaArtifact({ rootDir, registries, packageId, version }) {
|
|
572
597
|
const aliases = pathAliases(rootDir);
|
|
573
598
|
const cache = new Map();
|
|
574
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`,
|