@heroiclands/package-build 17.1.0 → 18.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 +1773 -0
- package/CONTENT.md +288 -72
- package/README.md +1 -1
- package/bin/content-build.mjs +218 -137
- package/bin/package-build.mjs +43 -0
- package/content-config.mjs +62 -67
- package/docs/content-format.md +768 -58
- package/engine/actor-compiler.mjs +586 -0
- package/engine/address-charset.mjs +11 -8
- package/engine/address-diff.mjs +266 -22
- package/engine/anchored-sections.mjs +83 -0
- package/engine/anchors.mjs +83 -0
- package/engine/base-compiler.mjs +148 -18
- package/engine/bundle-notes.mjs +276 -0
- package/engine/bundles.mjs +307 -0
- package/engine/code-fences.mjs +103 -0
- package/engine/compile-corpus.mjs +89 -0
- package/engine/content-address.mjs +267 -5
- package/engine/content-format-check.mjs +54 -11
- package/engine/content-format.mjs +37 -5
- package/engine/content-index.mjs +394 -111
- package/engine/content-links.mjs +185 -77
- package/engine/content-lint.mjs +232 -14
- package/engine/content-package.mjs +2 -1
- package/engine/content-tables.mjs +99 -3
- package/engine/document-subtypes.mjs +49 -6
- package/engine/field-reference.mjs +1 -1
- package/engine/field-spec.mjs +56 -6
- package/engine/folder-notes.mjs +470 -0
- package/engine/foreign-catalog.mjs +195 -4
- package/engine/{manifest-emit.mjs → foundry-entries.mjs} +78 -81
- package/engine/frontmatter-lint.mjs +245 -19
- package/engine/frontmatter.mjs +88 -8
- package/engine/generate.mjs +304 -13
- package/engine/helpers.mjs +293 -64
- package/engine/ids.mjs +137 -2
- package/engine/index-records.mjs +126 -0
- package/engine/index.mjs +23 -5
- package/engine/item-compiler.mjs +349 -0
- package/engine/item-docs.mjs +7 -1
- package/engine/item-registry.mjs +6 -0
- package/engine/journals.mjs +93 -27
- package/engine/macros.mjs +4 -2
- package/engine/metadata-index.mjs +495 -0
- package/engine/note-claims.mjs +146 -25
- package/engine/note-ids.mjs +112 -0
- package/engine/note-renames.mjs +134 -0
- package/engine/note-schemas.mjs +25 -0
- package/engine/note-vocabulary.mjs +110 -27
- package/engine/pack-config.mjs +4 -0
- package/engine/pack-router.mjs +23 -0
- package/engine/retired-fields.mjs +137 -3
- package/engine/scenes.mjs +66 -34
- package/engine/schema-check.mjs +58 -17
- package/engine/site-build.mjs +71 -12
- package/engine/site-index.mjs +20 -7
- package/engine/sql-tables.mjs +485 -0
- package/engine/subtype-registry.mjs +102 -0
- package/engine/system-block.mjs +96 -12
- package/engine/systems.mjs +201 -0
- package/engine/web-wikilinks.mjs +50 -20
- package/engine/wikilink-syntax.mjs +17 -8
- package/engine/wikilinks.mjs +124 -51
- package/engine/yaml-lint.mjs +282 -0
- package/hm3/actors.mjs +352 -0
- package/hm3/default-item-art.mjs +75 -0
- package/hm3/document-subtypes.mjs +134 -0
- package/hm3/index.mjs +56 -0
- package/hm3/item-builders.mjs +84 -0
- package/hm3/item-fields.mjs +180 -0
- package/hm3/items.mjs +96 -0
- package/hm3/template-priority.mjs +85 -0
- package/labels.mjs +191 -0
- package/manifest.mjs +43 -2
- package/package.json +19 -4
- package/release.mjs +62 -7
- package/sohl/actors.mjs +33 -487
- package/sohl/being-info.mjs +16 -7
- package/sohl/default-item-art.mjs +14 -3
- package/sohl/document-subtypes.mjs +16 -10
- package/sohl/item-builders.mjs +14 -5
- package/sohl/item-fields.mjs +68 -7
- package/sohl/items.mjs +44 -258
- package/sohl/note-schemas.mjs +2 -2
- package/types/content-config.d.mts +21 -42
- package/types/engine/actor-compiler.d.mts +204 -0
- package/types/engine/address-charset.d.mts +11 -8
- package/types/engine/address-diff.d.mts +53 -5
- package/types/engine/anchored-sections.d.mts +21 -0
- package/types/engine/anchors.d.mts +20 -0
- package/types/engine/base-compiler.d.mts +17 -17
- package/types/engine/bundle-notes.d.mts +173 -0
- package/types/engine/bundles.d.mts +60 -0
- package/types/engine/code-fences.d.mts +43 -0
- package/types/engine/compile-corpus.d.mts +32 -0
- package/types/engine/content-address.d.mts +205 -5
- package/types/engine/content-format-check.d.mts +6 -2
- package/types/engine/content-format.d.mts +57 -1
- package/types/engine/content-index.d.mts +64 -80
- package/types/engine/content-links.d.mts +52 -7
- package/types/engine/content-lint.d.mts +10 -1
- package/types/engine/content-package.d.mts +2 -1
- package/types/engine/content-tables.d.mts +21 -39
- package/types/engine/document-subtypes.d.mts +37 -3
- package/types/engine/field-spec.d.mts +76 -5
- package/types/engine/folder-notes.d.mts +159 -0
- package/types/engine/foreign-catalog.d.mts +53 -0
- package/types/engine/{manifest-emit.d.mts → foundry-entries.d.mts} +16 -38
- package/types/engine/frontmatter-lint.d.mts +10 -2
- package/types/engine/frontmatter.d.mts +64 -0
- package/types/engine/generate.d.mts +38 -0
- package/types/engine/helpers.d.mts +94 -30
- package/types/engine/ids.d.mts +96 -0
- package/types/engine/index-records.d.mts +68 -0
- package/types/engine/index.d.mts +9 -3
- package/types/engine/item-compiler.d.mts +131 -0
- package/types/engine/journals.d.mts +47 -9
- package/types/engine/metadata-index.d.mts +226 -0
- package/types/engine/note-claims.d.mts +57 -11
- package/types/engine/note-ids.d.mts +38 -0
- package/types/engine/note-renames.d.mts +102 -0
- package/types/engine/note-vocabulary.d.mts +44 -8
- package/types/engine/retired-fields.d.mts +75 -0
- package/types/engine/scenes.d.mts +3 -2
- package/types/engine/schema-check.d.mts +25 -4
- package/types/engine/site-build.d.mts +4 -4
- package/types/engine/site-index.d.mts +1 -1
- package/types/engine/sql-tables.d.mts +185 -0
- package/types/engine/subtype-registry.d.mts +49 -0
- package/types/engine/system-block.d.mts +40 -1
- package/types/engine/systems.d.mts +106 -0
- package/types/engine/web-wikilinks.d.mts +4 -2
- package/types/engine/wikilink-syntax.d.mts +10 -3
- package/types/engine/wikilinks.d.mts +41 -13
- package/types/engine/yaml-lint.d.mts +107 -0
- package/types/hm3/actors.d.mts +48 -0
- package/types/hm3/default-item-art.d.mts +42 -0
- package/types/hm3/document-subtypes.d.mts +24 -0
- package/types/hm3/index.d.mts +7 -0
- package/types/hm3/item-builders.d.mts +11 -0
- package/types/hm3/item-fields.d.mts +12 -0
- package/types/hm3/items.d.mts +23 -0
- package/types/hm3/template-priority.d.mts +21 -0
- package/types/manifest.d.mts +22 -1
- package/types/release.d.mts +9 -4
- package/types/sohl/actors.d.mts +7 -74
- package/types/sohl/being-info.d.mts +8 -27
- package/types/sohl/default-item-art.d.mts +5 -3
- package/types/sohl/items.d.mts +17 -32
- package/engine/foreign-manifests.mjs +0 -126
- package/engine/kb-manifest.mjs +0 -490
- package/types/engine/foreign-manifests.d.mts +0 -43
- package/types/engine/kb-manifest.d.mts +0 -241
package/engine/schema-check.mjs
CHANGED
|
@@ -39,7 +39,7 @@
|
|
|
39
39
|
*
|
|
40
40
|
* **The rest of the emission is only observable.** A compiler writes keys of
|
|
41
41
|
* its own alongside the declared fields — `shortcode`, `actionDefs`, `notes`,
|
|
42
|
-
* `docHtml`, and since #126 `
|
|
42
|
+
* `docHtml`, and since #126 `templatePriority` — and those appear in neither set
|
|
43
43
|
* {@link compareFields} compares, so nothing compared them at all (#155). They
|
|
44
44
|
* cannot be listed here without the list going stale the next time a compiler
|
|
45
45
|
* grows a key, so they are read off the `system` object the compiler produced:
|
|
@@ -357,7 +357,7 @@ function undeclaredEmittedPaths(data, declared, enumerated, prefix = "") {
|
|
|
357
357
|
* *observed*. {@link compareFields} reads the `itemBuilders` declarations and
|
|
358
358
|
* {@link checkAuthoredSystemData} reads a note's `<system>.system`; between them
|
|
359
359
|
* they miss every key a compiler writes on its own initiative, which is not a
|
|
360
|
-
* residue — it is `shortcode`, `actionDefs`, `notes`, `docHtml` and `
|
|
360
|
+
* residue — it is `shortcode`, `actionDefs`, `notes`, `docHtml` and `templatePriority`.
|
|
361
361
|
*
|
|
362
362
|
* **The keys come from the object the compiler built, after a JSON round trip.**
|
|
363
363
|
* That is exactly what the pack file receives, so a key whose value is
|
|
@@ -455,12 +455,25 @@ export function compareEmittedSystem({
|
|
|
455
455
|
* stamps no system at all, and a system that has not adopted the artifact yet
|
|
456
456
|
* is simply unchecked. Neither is an error, and the caller says which it was.
|
|
457
457
|
*
|
|
458
|
+
* **A build may have more than one system, and then the caller names it (#139).**
|
|
459
|
+
* `stats.systemId` is the package-wide answer, and a repository shipping content
|
|
460
|
+
* for two systems has no package-wide answer — it is deliberately `null` there,
|
|
461
|
+
* because a module feeding both `sohl` and `hm3` targets neither. Left at that,
|
|
462
|
+
* every schema check in such a build would be skipped in silence, which is the
|
|
463
|
+
* state #60 exists to remove: the five type names the two systems *share* are
|
|
464
|
+
* exactly the ones a wrong-system emission hides in. So a pass supplies the
|
|
465
|
+
* system its pack declares, and the version comes from that system's own
|
|
466
|
+
* `systems:` entry rather than from a package-wide stamp.
|
|
467
|
+
*
|
|
458
468
|
* @param {object} config - The resolved build configuration.
|
|
469
|
+
* @param {string|null} [system] - The system whose schema is wanted. Defaults
|
|
470
|
+
* to the package-wide `stats.systemId`, which is every single-system build
|
|
471
|
+
* and the behaviour this always had.
|
|
459
472
|
* @returns {{artifact: SchemaArtifact, source: string}|null} The schema and
|
|
460
473
|
* where it was read from.
|
|
461
474
|
*/
|
|
462
|
-
export function resolveSchemaArtifact(config) {
|
|
463
|
-
const systemId = config?.stats?.systemId;
|
|
475
|
+
export function resolveSchemaArtifact(config, system = undefined) {
|
|
476
|
+
const systemId = system === undefined ? config?.stats?.systemId : system;
|
|
464
477
|
if (!systemId) return null;
|
|
465
478
|
|
|
466
479
|
const read = (file) => ({
|
|
@@ -474,7 +487,13 @@ export function resolveSchemaArtifact(config) {
|
|
|
474
487
|
return fs.existsSync(own) ? read(own) : null;
|
|
475
488
|
}
|
|
476
489
|
|
|
477
|
-
|
|
490
|
+
// The version this build compiles *against*, for this system. A `systems:`
|
|
491
|
+
// entry is the per-system statement and wins; `stats.systemVersion` is the
|
|
492
|
+
// package-wide one and answers only for the package-wide system, which is
|
|
493
|
+
// what keeps a module still deriving from `relationships.systems` working.
|
|
494
|
+
const version =
|
|
495
|
+
config?.systems?.[systemId]?.compatibility?.verified ??
|
|
496
|
+
(systemId === config?.stats?.systemId ? config?.stats?.systemVersion : null);
|
|
478
497
|
if (!version) return null;
|
|
479
498
|
const cached = cachedSchemaPath(config, systemId, version);
|
|
480
499
|
return fs.existsSync(cached) ? read(cached) : null;
|
|
@@ -556,22 +575,29 @@ export function emittedUndeclaredMessage(finding) {
|
|
|
556
575
|
);
|
|
557
576
|
}
|
|
558
577
|
|
|
559
|
-
/** One resolved schema artifact per
|
|
578
|
+
/** One resolved schema artifact per configuration **and system**. */
|
|
560
579
|
const artifacts = new WeakMap();
|
|
561
580
|
|
|
562
581
|
/**
|
|
563
|
-
* {@link resolveSchemaArtifact}, read once per configuration.
|
|
582
|
+
* {@link resolveSchemaArtifact}, read once per configuration and system.
|
|
564
583
|
*
|
|
565
|
-
* The per-note check below runs thousands of times in a build and
|
|
584
|
+
* The per-note check below runs thousands of times in a build and an artifact
|
|
566
585
|
* never changes inside one, so reading and parsing it per note would be a
|
|
567
586
|
* megabyte of JSON per hundred documents for an answer that is already known.
|
|
587
|
+
* Keyed by system as well as by configuration since #139: a build with two
|
|
588
|
+
* systems has two artifacts, and caching one of them under the configuration
|
|
589
|
+
* alone would hand every pass whichever system asked first.
|
|
568
590
|
*
|
|
569
591
|
* @param {object} config - The resolved build configuration.
|
|
592
|
+
* @param {string|null} [system] - The system whose schema is wanted.
|
|
570
593
|
* @returns {{artifact: SchemaArtifact, source: string}|null} The schema.
|
|
571
594
|
*/
|
|
572
|
-
function schemaFor(config) {
|
|
573
|
-
|
|
574
|
-
|
|
595
|
+
function schemaFor(config, system = undefined) {
|
|
596
|
+
let bySystem = artifacts.get(config);
|
|
597
|
+
if (!bySystem) artifacts.set(config, (bySystem = new Map()));
|
|
598
|
+
const key = system === undefined ? null : system;
|
|
599
|
+
if (!bySystem.has(key)) bySystem.set(key, resolveSchemaArtifact(config, system));
|
|
600
|
+
return bySystem.get(key);
|
|
575
601
|
}
|
|
576
602
|
|
|
577
603
|
/**
|
|
@@ -600,18 +626,21 @@ function schemaFor(config) {
|
|
|
600
626
|
* @param {string} opts.block - The system block to read, e.g. `"sohl"`.
|
|
601
627
|
* @param {string} opts.documentType - `Item`, `Actor`, …
|
|
602
628
|
* @param {string} opts.subType - The document subtype the note compiles into.
|
|
629
|
+
* @param {string} [opts.system] - The system whose published schema to read,
|
|
630
|
+
* where a build has more than one (#139). Defaults to the package-wide
|
|
631
|
+
* `stats.systemId`.
|
|
603
632
|
* @param {object} [opts.config] - The resolved build configuration.
|
|
604
633
|
* @returns {{path: string, message: string}[]} One finding per undeclared path,
|
|
605
634
|
* shallowest-first.
|
|
606
635
|
*/
|
|
607
636
|
export function checkAuthoredSystemData(
|
|
608
637
|
fm,
|
|
609
|
-
{ block, documentType, subType, config = loadPackConfig() },
|
|
638
|
+
{ block, documentType, subType, system = undefined, config = loadPackConfig() },
|
|
610
639
|
) {
|
|
611
640
|
const data = systemData(fm, block);
|
|
612
641
|
if (!Object.keys(data).length) return [];
|
|
613
642
|
|
|
614
|
-
const schema = schemaFor(config);
|
|
643
|
+
const schema = schemaFor(config, system);
|
|
615
644
|
if (!schema) return [];
|
|
616
645
|
const declared = declaredFields(schema.artifact, documentType, subType);
|
|
617
646
|
if (!declared) return [];
|
|
@@ -651,18 +680,30 @@ export function checkAuthoredSystemData(
|
|
|
651
680
|
* @param {string} opts.type - The note's content type, for the message.
|
|
652
681
|
* @param {readonly {to?: string}[]} [opts.fields] - The type's field
|
|
653
682
|
* declaration, which decides each finding's origin.
|
|
683
|
+
* @param {string} [opts.system] - The system whose published schema to read,
|
|
684
|
+
* where a build has more than one (#139). Defaults to the package-wide
|
|
685
|
+
* `stats.systemId`.
|
|
654
686
|
* @param {object} [opts.config] - The resolved build configuration.
|
|
655
687
|
* @returns {(EmissionFinding & {message: string})[]} One per undeclared path.
|
|
656
688
|
*/
|
|
657
689
|
export function checkEmittedSystemData(
|
|
658
|
-
|
|
659
|
-
{
|
|
690
|
+
emitted,
|
|
691
|
+
{
|
|
692
|
+
fm,
|
|
693
|
+
block,
|
|
694
|
+
documentType,
|
|
695
|
+
subType,
|
|
696
|
+
type,
|
|
697
|
+
fields,
|
|
698
|
+
system = undefined,
|
|
699
|
+
config = loadPackConfig(),
|
|
700
|
+
},
|
|
660
701
|
) {
|
|
661
|
-
const schema = schemaFor(config);
|
|
702
|
+
const schema = schemaFor(config, system);
|
|
662
703
|
if (!schema) return [];
|
|
663
704
|
|
|
664
705
|
return compareEmittedSystem({
|
|
665
|
-
system,
|
|
706
|
+
system: emitted,
|
|
666
707
|
artifact: schema.artifact,
|
|
667
708
|
documentType,
|
|
668
709
|
subtype: subType,
|
package/engine/site-build.mjs
CHANGED
|
@@ -55,11 +55,14 @@ import { protectCode } from "./code-fences.mjs";
|
|
|
55
55
|
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
|
-
import {
|
|
59
|
-
import { formatUnaddressableFinding, unaddressableForeignPackages } from "./
|
|
58
|
+
import { loadForeignIndexes } from "./metadata-index.mjs";
|
|
59
|
+
import { formatUnaddressableFinding, unaddressableForeignPackages } from "./metadata-index.mjs";
|
|
60
60
|
import { deriveBeingInfo, isBeing } from "../sohl/being-info.mjs";
|
|
61
61
|
import { loadPackConfig } from "./pack-config.mjs";
|
|
62
62
|
import { searchableFrontmatter } from "./note-package.mjs";
|
|
63
|
+
// The corpus, from the one pass that derives it (#243).
|
|
64
|
+
import { indexRecordsFor } from "./content-index.mjs";
|
|
65
|
+
import { isNoteRecord, noteFile } from "./index-records.mjs";
|
|
63
66
|
import {
|
|
64
67
|
checkHomepageCount,
|
|
65
68
|
homepageDestination,
|
|
@@ -102,6 +105,50 @@ export function walkSiteTree(dir, skip = []) {
|
|
|
102
105
|
return out;
|
|
103
106
|
}
|
|
104
107
|
|
|
108
|
+
/**
|
|
109
|
+
* The content-tree files this build publishes from, in the order it emits them.
|
|
110
|
+
*
|
|
111
|
+
* **The corpus comes from the content index** (#243) — the same derivation the
|
|
112
|
+
* packs are compiled from — so the site and the packs cannot disagree about
|
|
113
|
+
* which files are the content. The note is still read for its `{fm, body}`: the
|
|
114
|
+
* index carries no note text, and a page *is* its text.
|
|
115
|
+
*
|
|
116
|
+
* **The order changes, and that is the point of stating it here.** The walk this
|
|
117
|
+
* replaces yielded directory order, and this module kept it deliberately —
|
|
118
|
+
* "a site's emitted pages should not reorder for no reason". Records are in
|
|
119
|
+
* content-path order, which is the same set in a different sequence. Nothing
|
|
120
|
+
* downstream depends on it any more: the first-writer-wins fallbacks that made
|
|
121
|
+
* order load-bearing went with the bare `[[Name]]` form (#180), each page is
|
|
122
|
+
* emitted to its own file at an address derived from its frontmatter, and the
|
|
123
|
+
* one place order could still show — a section's page list — is sorted by the
|
|
124
|
+
* theme. Verified rather than argued: over `sohl`'s tree the emitted mount is
|
|
125
|
+
* byte-identical, all 1,749 files.
|
|
126
|
+
*
|
|
127
|
+
* A **content-path** order is also the better of the two. Directory-read order
|
|
128
|
+
* is a fact about the filesystem, not about the content, so it can differ
|
|
129
|
+
* between two checkouts of one tree; this order cannot.
|
|
130
|
+
*
|
|
131
|
+
* `collectTreePages` is deliberately **not** converted: it walks an auxiliary
|
|
132
|
+
* tree (`site.trees`, the developer docs), which is not the content tree and
|
|
133
|
+
* appears in no record.
|
|
134
|
+
*
|
|
135
|
+
* @param {string} contentBase - Root of the content tree.
|
|
136
|
+
* @param {object} ctx - The build context. `ctx.records` is the corpus when the
|
|
137
|
+
* caller already derived it — the site build derives one and hands it to both
|
|
138
|
+
* collectors, so the two cannot disagree.
|
|
139
|
+
* @returns {string[]} Absolute paths, in emission order.
|
|
140
|
+
*/
|
|
141
|
+
function siteCorpusFiles(contentBase, ctx) {
|
|
142
|
+
const records =
|
|
143
|
+
ctx.records ??
|
|
144
|
+
indexRecordsFor({
|
|
145
|
+
contentBase,
|
|
146
|
+
config: ctx.config,
|
|
147
|
+
skipDirectories: ctx.skipDirectories,
|
|
148
|
+
});
|
|
149
|
+
return records.filter(isNoteRecord).map((record) => noteFile(contentBase, record));
|
|
150
|
+
}
|
|
151
|
+
|
|
105
152
|
/**
|
|
106
153
|
* Reads a note, returning `null` for one that cannot be parsed.
|
|
107
154
|
*
|
|
@@ -145,7 +192,7 @@ export function collectContentPages(contentBase, ctx) {
|
|
|
145
192
|
const addressFindings = [];
|
|
146
193
|
const fmLinkFindings = [];
|
|
147
194
|
|
|
148
|
-
for (const file of
|
|
195
|
+
for (const file of siteCorpusFiles(contentBase, ctx)) {
|
|
149
196
|
const note = readNote(file);
|
|
150
197
|
if (!note) continue;
|
|
151
198
|
const { fm, body } = note;
|
|
@@ -304,7 +351,7 @@ export function collectTreePages(tree, ctx) {
|
|
|
304
351
|
export function collectHomepages(contentBase, ctx) {
|
|
305
352
|
const pages = [];
|
|
306
353
|
const addressFindings = [];
|
|
307
|
-
for (const file of
|
|
354
|
+
for (const file of siteCorpusFiles(contentBase, ctx)) {
|
|
308
355
|
const note = readNote(file);
|
|
309
356
|
if (!note || !isHomepage(note.fm)) continue;
|
|
310
357
|
try {
|
|
@@ -380,10 +427,10 @@ export function writeHomepages(outRoot, pages, config) {
|
|
|
380
427
|
* @param {object[]} pages - Every page, from both walks.
|
|
381
428
|
* @param {object} findings - `{ addressFindings, fmLinkFindings }` from
|
|
382
429
|
* collection.
|
|
383
|
-
* @param {object} options - `{
|
|
430
|
+
* @param {object} options - `{ config }`.
|
|
384
431
|
* @returns {object} The gate results and, when they pass, the built index.
|
|
385
432
|
*/
|
|
386
|
-
export function siteGates(pages, findings, {
|
|
433
|
+
export function siteGates(pages, findings, { config }) {
|
|
387
434
|
const out = {
|
|
388
435
|
// Always empty here: the homepage count is decided in `buildSite`
|
|
389
436
|
// before the content walk, and a failing count returns without ever
|
|
@@ -406,7 +453,7 @@ export function siteGates(pages, findings, { manifestDir }) {
|
|
|
406
453
|
// configured list instead silently discarded the manifest of any package
|
|
407
454
|
// the list named but the tree did not contain.
|
|
408
455
|
const localPackages = new Set(content.map((p) => p.pkg));
|
|
409
|
-
const foreign =
|
|
456
|
+
const foreign = loadForeignIndexes(config, localPackages);
|
|
410
457
|
out.foreign = foreign;
|
|
411
458
|
if (foreign.stale.length) {
|
|
412
459
|
out.staleManifests = foreign.stale;
|
|
@@ -416,7 +463,6 @@ export function siteGates(pages, findings, { manifestDir }) {
|
|
|
416
463
|
out.unaddressable = unaddressableForeignPackages(foreign.index);
|
|
417
464
|
if (out.unaddressable.length) return out;
|
|
418
465
|
|
|
419
|
-
out.manifests = manifestsComplete(localPackages, foreign.packages);
|
|
420
466
|
const index = buildSiteIndex(pages, { foreignIndex: foreign.index });
|
|
421
467
|
out.conflicts = index.conflicts;
|
|
422
468
|
if (out.conflicts.length) return out;
|
|
@@ -658,6 +704,7 @@ export function renderPages(pages, options) {
|
|
|
658
704
|
readmeSections,
|
|
659
705
|
decorate,
|
|
660
706
|
linkable = (d) => Boolean(d.fm.shortcode),
|
|
707
|
+
sqlTables,
|
|
661
708
|
} = options;
|
|
662
709
|
|
|
663
710
|
const tableErrors = [];
|
|
@@ -692,6 +739,10 @@ export function renderPages(pages, options) {
|
|
|
692
739
|
docs: universe.get(page.pkg) ?? [],
|
|
693
740
|
linkable,
|
|
694
741
|
source: src,
|
|
742
|
+
// Prepared before this render began — DuckDB is async and this
|
|
743
|
+
// is not. Keyed by the note's own file, absolute here as in
|
|
744
|
+
// every other pass, so the three cannot disagree about a note.
|
|
745
|
+
sqlTables: sqlTables?.get(page.file),
|
|
695
746
|
self: {
|
|
696
747
|
fm: searchableFrontmatter(page.fm, page.pkg),
|
|
697
748
|
path: page.relPath,
|
|
@@ -898,7 +949,7 @@ export function resolveOutputRoot(rootDir, out) {
|
|
|
898
949
|
* @returns {{gates: object, stats: object|null, tableErrors: object[],
|
|
899
950
|
* wikiErrors: object[], manifests: object|null}}
|
|
900
951
|
*/
|
|
901
|
-
export function buildSite({ config, outRoot } = {}) {
|
|
952
|
+
export function buildSite({ config, outRoot, sqlTables } = {}) {
|
|
902
953
|
const resolved = config ?? loadPackConfig();
|
|
903
954
|
const site = resolved.site;
|
|
904
955
|
const scheme = resolved.publish.address;
|
|
@@ -940,6 +991,15 @@ export function buildSite({ config, outRoot } = {}) {
|
|
|
940
991
|
// retired, so this is the only source of it (#56).
|
|
941
992
|
contentPackage: resolved.contentPackage,
|
|
942
993
|
skipDirectories: resolved.skipDirectories,
|
|
994
|
+
config: resolved,
|
|
995
|
+
// The corpus, derived once for this build and handed to both
|
|
996
|
+
// collectors — the homepage pass and the content pass read one answer
|
|
997
|
+
// about which files the content is, rather than walking twice (#243).
|
|
998
|
+
records: indexRecordsFor({
|
|
999
|
+
contentBase: resolved.paths.content,
|
|
1000
|
+
config: resolved,
|
|
1001
|
+
skipDirectories: resolved.skipDirectories,
|
|
1002
|
+
}),
|
|
943
1003
|
// Where the package is served, which is where an addressed page
|
|
944
1004
|
// publishes: an address is `(type, shortcode)`, a package-wide
|
|
945
1005
|
// identity that takes no content mount (#181).
|
|
@@ -1041,7 +1101,7 @@ export function buildSite({ config, outRoot } = {}) {
|
|
|
1041
1101
|
const gates = siteGates(
|
|
1042
1102
|
[...pages, ...homepageEntries],
|
|
1043
1103
|
{ ...content, fmLinkFindings },
|
|
1044
|
-
{
|
|
1104
|
+
{ config: resolved },
|
|
1045
1105
|
);
|
|
1046
1106
|
if (gatesFailed(gates)) {
|
|
1047
1107
|
return {
|
|
@@ -1049,7 +1109,6 @@ export function buildSite({ config, outRoot } = {}) {
|
|
|
1049
1109
|
stats: null,
|
|
1050
1110
|
tableErrors: [],
|
|
1051
1111
|
wikiErrors: [],
|
|
1052
|
-
manifests: gates.manifests,
|
|
1053
1112
|
};
|
|
1054
1113
|
}
|
|
1055
1114
|
|
|
@@ -1060,6 +1119,7 @@ export function buildSite({ config, outRoot } = {}) {
|
|
|
1060
1119
|
|
|
1061
1120
|
const rendered = renderPages(pages, {
|
|
1062
1121
|
outRoot: out,
|
|
1122
|
+
sqlTables,
|
|
1063
1123
|
index: gates.index,
|
|
1064
1124
|
foreign: gates.foreign,
|
|
1065
1125
|
universe: tableUniverse(pages),
|
|
@@ -1088,7 +1148,6 @@ export function buildSite({ config, outRoot } = {}) {
|
|
|
1088
1148
|
|
|
1089
1149
|
return {
|
|
1090
1150
|
gates,
|
|
1091
|
-
manifests: gates.manifests,
|
|
1092
1151
|
tableErrors: rendered.tableErrors,
|
|
1093
1152
|
wikiErrors: rendered.wikiErrors,
|
|
1094
1153
|
stats: {
|
package/engine/site-index.mjs
CHANGED
|
@@ -30,8 +30,11 @@
|
|
|
30
30
|
* **Two key spaces, one map**, and both are addresses. `section/slug` and
|
|
31
31
|
* `type/shortcode` are unique by construction, so they always resolve.
|
|
32
32
|
* `type/shortcode` is the authored form; the canonical
|
|
33
|
-
* `package-type-shortcode` is set alongside it, which is what a
|
|
34
|
-
* link and every merged foreign entry use (#1499).
|
|
33
|
+
* `package-system-type-shortcode` is set alongside it, which is what a
|
|
34
|
+
* cross-package link and every merged foreign entry use (#1499, #59). A
|
|
35
|
+
* cross-package target states its package and usually not its system, so it is
|
|
36
|
+
* matched by the segments it supplies rather than fetched by an exact key —
|
|
37
|
+
* one hit resolves, and anything else is a finding.
|
|
35
38
|
*
|
|
36
39
|
* **A page's *name* is not a key** (#180). It was, as one of a set of
|
|
37
40
|
* collision-aware fallbacks a bare `[[Name]]` was looked up in — which is what
|
|
@@ -48,7 +51,9 @@
|
|
|
48
51
|
|
|
49
52
|
import path from "node:path";
|
|
50
53
|
|
|
51
|
-
import { canonicalKey, readCanonicalKey } from "./
|
|
54
|
+
import { canonicalKey, readCanonicalKey } from "./content-address.mjs";
|
|
55
|
+
import { systemOf } from "./document-subtypes.mjs";
|
|
56
|
+
import { KNOWN_DOCUMENT_SUBTYPE_MAPS } from "./note-claims.mjs";
|
|
52
57
|
import { hasDocEntry } from "./item-docs.mjs";
|
|
53
58
|
import { contentPackage } from "./content-package.mjs";
|
|
54
59
|
// The declared tag vocabulary (#172), which is where `draft` is stated.
|
|
@@ -158,7 +163,7 @@ function mergeForeign(index, foreignIndex) {
|
|
|
158
163
|
* each already knowing its own `url`.
|
|
159
164
|
* @param {object} [options] - Cross-package inputs.
|
|
160
165
|
* @param {Map<string, {package: string, type?: string}>} [options.foreignIndex]
|
|
161
|
-
* The merged index from `
|
|
166
|
+
* The merged index from `loadForeignIndexes`. Omit when the build publishes
|
|
162
167
|
* no cross-package links.
|
|
163
168
|
* @returns {SiteIndex} The index, and what could not be addressed unambiguously.
|
|
164
169
|
*/
|
|
@@ -208,8 +213,8 @@ export function buildSiteIndex(entries, { foreignIndex = new Map() } = {}) {
|
|
|
208
213
|
}
|
|
209
214
|
|
|
210
215
|
// Merged *before* the local type-scoped pass below, so a local page always
|
|
211
|
-
// ends up owning its own canonical `package-type-shortcode` address: the
|
|
212
|
-
// local write lands last and wins. `
|
|
216
|
+
// ends up owning its own canonical `package-system-type-shortcode` address: the
|
|
217
|
+
// local write lands last and wins. `loadForeignIndexes` already excludes
|
|
213
218
|
// the local packages, so a manifest should never carry one — this is what
|
|
214
219
|
// makes that a belt-and-braces rather than the only thing standing between
|
|
215
220
|
// a stale vendored manifest and a shadowed local page.
|
|
@@ -241,7 +246,15 @@ export function buildSiteIndex(entries, { foreignIndex = new Map() } = {}) {
|
|
|
241
246
|
// The page's package is the configured one — the site collection
|
|
242
247
|
// resolves it and records it as `pkg`. Never read out of
|
|
243
248
|
// frontmatter: `package:` is retired (#56).
|
|
244
|
-
index.set(
|
|
249
|
+
index.set(
|
|
250
|
+
canonicalKey(
|
|
251
|
+
e.pkg ?? ownPackage,
|
|
252
|
+
systemOf(type, KNOWN_DOCUMENT_SUBTYPE_MAPS),
|
|
253
|
+
type,
|
|
254
|
+
shortcode,
|
|
255
|
+
),
|
|
256
|
+
value,
|
|
257
|
+
);
|
|
245
258
|
if (e.pkg) packages.add(e.pkg);
|
|
246
259
|
// In Foundry an item and its documentation are two documents, so
|
|
247
260
|
// `skill/wpnc` and `docskill/wpnc` are two UUIDs (#1362). Here the
|