@heroiclands/package-build 22.12.3 → 22.13.1
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 +19 -0
- package/README.md +2 -2
- package/docs/api.md +42 -40
- package/docs/commands.md +0 -3
- package/docs/content-format.md +94 -84
- package/docs/getting-started.md +1 -3
- package/engine/address.mjs +39 -153
- package/engine/art-fields.mjs +1 -1
- package/engine/bundles.mjs +3 -2
- package/engine/content-address.mjs +6 -9
- package/engine/content-index.mjs +8 -10
- package/engine/content-links.mjs +9 -17
- package/engine/content-tables.mjs +1 -1
- package/engine/folder-notes.mjs +2 -1
- package/engine/foundry-entries.mjs +4 -8
- package/engine/frontmatter-lint.mjs +2 -2
- package/engine/frontmatter.mjs +2 -2
- package/engine/homepage.mjs +3 -3
- package/engine/item-compiler.mjs +7 -2
- package/engine/map-places.mjs +3 -1
- package/engine/metadata-index.mjs +23 -6
- package/engine/note-addresses.mjs +27 -9
- package/engine/note-ids.mjs +7 -2
- package/engine/note-vocabulary.mjs +24 -0
- package/engine/pack-router.mjs +7 -3
- package/engine/place-relations.mjs +4 -4
- package/engine/reference-targets.mjs +7 -7
- package/engine/shortcode-references.mjs +1 -1
- package/engine/site-index.mjs +8 -39
- package/engine/sql-tables.mjs +1 -1
- package/engine/system-block.mjs +19 -0
- package/engine/systems.mjs +4 -1
- package/engine/web-wikilinks.mjs +3 -3
- package/engine/wikilinks.mjs +31 -106
- package/hm3/actors.mjs +7 -2
- package/package.json +2 -3
- package/sohl/infobox.mjs +2 -2
- package/types/engine/address.d.mts +21 -88
- package/types/engine/content-address.d.mts +3 -6
- package/types/engine/content-links.d.mts +2 -6
- package/types/engine/frontmatter.d.mts +1 -1
- package/types/engine/homepage.d.mts +2 -2
- package/types/engine/note-addresses.d.mts +2 -0
- package/types/engine/system-block.d.mts +11 -0
- package/types/engine/systems.d.mts +2 -0
- package/bin/heroiclands-content-language-server.mjs +0 -8
- package/docs/content-language-server.md +0 -25
- package/engine/content-language-server.mjs +0 -439
- package/types/engine/content-language-server.d.mts +0 -64
|
@@ -54,7 +54,12 @@ import { metadataFileName, PACKAGEBUILD_PACKAGE } from "./packages.mjs";
|
|
|
54
54
|
import { packageBuildRecords } from "./packagebuild-index.mjs";
|
|
55
55
|
|
|
56
56
|
export { metadataFileName };
|
|
57
|
-
import {
|
|
57
|
+
import {
|
|
58
|
+
PACKAGE_BASE,
|
|
59
|
+
canonicalKey,
|
|
60
|
+
readCanonicalKey,
|
|
61
|
+
resolvePackageUrl,
|
|
62
|
+
} from "./content-address.mjs";
|
|
58
63
|
import { HOMEPAGE_TYPE } from "./homepage.mjs";
|
|
59
64
|
|
|
60
65
|
/**
|
|
@@ -369,11 +374,23 @@ export function loadForeignIndexes(config, localPackages, bases = PACKAGE_BASE)
|
|
|
369
374
|
error.position = line >= 0 ? { line: line + 1 } : {};
|
|
370
375
|
throw error;
|
|
371
376
|
}
|
|
372
|
-
|
|
373
|
-
const key = encodeAddresses(record?.address?.canonical);
|
|
377
|
+
let key = encodeAddresses(record?.address?.canonical);
|
|
374
378
|
if (!key) continue;
|
|
375
|
-
|
|
379
|
+
let parts = readCanonicalKey(key);
|
|
376
380
|
if (!parts) continue;
|
|
381
|
+
if (
|
|
382
|
+
parts.system === "none" &&
|
|
383
|
+
record.documents &&
|
|
384
|
+
parts.type.startsWith("doc") &&
|
|
385
|
+
record.type === parts.type
|
|
386
|
+
) {
|
|
387
|
+
record.type = parts.type.slice(3);
|
|
388
|
+
key = canonicalKey(parts.package, "note", record.type, parts.shortcode);
|
|
389
|
+
parts = readCanonicalKey(key);
|
|
390
|
+
record.foundry = { note: record.foundry?.none };
|
|
391
|
+
record.address.canonical = key;
|
|
392
|
+
}
|
|
393
|
+
referenceRecords.push(record);
|
|
377
394
|
// First writer wins, so two packages claiming one address cannot
|
|
378
395
|
// make the build depend on the order the cache was read in.
|
|
379
396
|
if (index.has(key)) continue;
|
|
@@ -436,11 +453,11 @@ export function loadForeignIndexes(config, localPackages, bases = PACKAGE_BASE)
|
|
|
436
453
|
if (entry.url) {
|
|
437
454
|
const identity = completeAddress({
|
|
438
455
|
package: record.package,
|
|
439
|
-
system: "
|
|
456
|
+
system: "note",
|
|
440
457
|
type: record.type,
|
|
441
458
|
shortcode: record.shortcode,
|
|
442
459
|
});
|
|
443
|
-
if (
|
|
460
|
+
if (key !== renderAddress(identity))
|
|
444
461
|
pageAliases.push([
|
|
445
462
|
renderAddress(identity),
|
|
446
463
|
{
|
|
@@ -24,7 +24,13 @@ export { AddressEntries, encodeAddresses, cloneAddressState } from "./address-va
|
|
|
24
24
|
|
|
25
25
|
/** Structured positions whose Address is nested inside a declared container. */
|
|
26
26
|
export const STRUCTURED_ADDRESSES = Object.freeze([
|
|
27
|
-
{
|
|
27
|
+
{
|
|
28
|
+
path: ["packFolder"],
|
|
29
|
+
shape: "scalar-or-map",
|
|
30
|
+
type: "folder",
|
|
31
|
+
accepts: ["folder"],
|
|
32
|
+
system: "none",
|
|
33
|
+
},
|
|
28
34
|
{
|
|
29
35
|
noteType: "affiliation",
|
|
30
36
|
path: ["data", "governance", "ranks", "*", "lore"],
|
|
@@ -67,6 +73,9 @@ export function addressPositions(fm, context = {}) {
|
|
|
67
73
|
shape,
|
|
68
74
|
type: field.ref,
|
|
69
75
|
accepts: field.accepts,
|
|
76
|
+
...(ASSET_TYPE_NAMES.has(field.ref) || field.ref === "folder" ?
|
|
77
|
+
{ system: "none" }
|
|
78
|
+
: {}),
|
|
70
79
|
});
|
|
71
80
|
}
|
|
72
81
|
out.push(...STRUCTURED_ADDRESSES.filter((p) => !p.noteType || p.noteType === fm.type));
|
|
@@ -79,7 +88,7 @@ export function addressPositions(fm, context = {}) {
|
|
|
79
88
|
shape: "scalar-or-map",
|
|
80
89
|
type: "folder",
|
|
81
90
|
accepts: ["folder"],
|
|
82
|
-
system,
|
|
91
|
+
system: "none",
|
|
83
92
|
});
|
|
84
93
|
out.push({
|
|
85
94
|
path: [system, "items", "*", "model"],
|
|
@@ -93,7 +102,7 @@ export function addressPositions(fm, context = {}) {
|
|
|
93
102
|
shape: "value",
|
|
94
103
|
type: slot.type,
|
|
95
104
|
accepts: slot.accepts,
|
|
96
|
-
system,
|
|
105
|
+
system: "none",
|
|
97
106
|
});
|
|
98
107
|
const spec = context.systemBlocks?.[system];
|
|
99
108
|
for (const [index, entry] of (Array.isArray(fm[system]?.items) ?
|
|
@@ -121,7 +130,13 @@ export function addressPositions(fm, context = {}) {
|
|
|
121
130
|
field.address.holds === "items" ? "list" : (field.address.holds ?? "value"),
|
|
122
131
|
type: field.address.type,
|
|
123
132
|
accepts: field.address.accepts,
|
|
124
|
-
system
|
|
133
|
+
system:
|
|
134
|
+
(
|
|
135
|
+
ASSET_TYPE_NAMES.has(field.address.type) ||
|
|
136
|
+
field.address.type === "folder"
|
|
137
|
+
) ?
|
|
138
|
+
"none"
|
|
139
|
+
: system,
|
|
125
140
|
});
|
|
126
141
|
}
|
|
127
142
|
}
|
|
@@ -135,7 +150,10 @@ export function addressPositions(fm, context = {}) {
|
|
|
135
150
|
shape: field.address.holds === "items" ? "list" : (field.address.holds ?? "value"),
|
|
136
151
|
type: field.address.type,
|
|
137
152
|
accepts: field.address.accepts,
|
|
138
|
-
system
|
|
153
|
+
system:
|
|
154
|
+
ASSET_TYPE_NAMES.has(field.address.type) || field.address.type === "folder" ?
|
|
155
|
+
"none"
|
|
156
|
+
: system,
|
|
139
157
|
};
|
|
140
158
|
out.push({ ...position, path: [system, "system", ...field.to.split(".")] });
|
|
141
159
|
out.push({
|
|
@@ -143,7 +161,7 @@ export function addressPositions(fm, context = {}) {
|
|
|
143
161
|
path: [system, ...(field.legacyKey ?? field.name).split(".")],
|
|
144
162
|
});
|
|
145
163
|
if (field.name?.startsWith("data.") && field.topLevelMeans === undefined)
|
|
146
|
-
out.push({ ...position,
|
|
164
|
+
out.push({ ...position, path: field.name.slice(5).split(".") });
|
|
147
165
|
}
|
|
148
166
|
}
|
|
149
167
|
for (const position of [...out]) {
|
|
@@ -171,7 +189,7 @@ export function decodeNoteAddresses(fm, context) {
|
|
|
171
189
|
const defaults = {
|
|
172
190
|
...context,
|
|
173
191
|
types,
|
|
174
|
-
system: position.system ?? context.system ?? "
|
|
192
|
+
system: position.system ?? context.system ?? "note",
|
|
175
193
|
type: position.type,
|
|
176
194
|
};
|
|
177
195
|
const fail = (value, path, description) => {
|
|
@@ -277,7 +295,7 @@ export function noteAddressContext(config) {
|
|
|
277
295
|
}
|
|
278
296
|
return {
|
|
279
297
|
package: config.contentPackage,
|
|
280
|
-
system: "
|
|
298
|
+
system: "note",
|
|
281
299
|
systemBlocks,
|
|
282
300
|
};
|
|
283
301
|
}
|
|
@@ -313,7 +331,7 @@ export function decodeIndexAddresses(record, context) {
|
|
|
313
331
|
written,
|
|
314
332
|
{
|
|
315
333
|
...context,
|
|
316
|
-
system: "
|
|
334
|
+
system: "note",
|
|
317
335
|
types: new Set([...Object.keys(NOTE_VOCABULARY), ...ASSET_TYPE_NAMES]),
|
|
318
336
|
},
|
|
319
337
|
{ declared: true },
|
package/engine/note-ids.mjs
CHANGED
|
@@ -44,6 +44,9 @@ import { documentId } from "./content-address.mjs";
|
|
|
44
44
|
import { systemOf } from "./document-subtypes.mjs";
|
|
45
45
|
import { contentPackage } from "./content-package.mjs";
|
|
46
46
|
import { KNOWN_DOCUMENT_SUBTYPE_MAPS } from "./subtype-registry.mjs";
|
|
47
|
+
import { JOURNAL_TYPES } from "./ids.mjs";
|
|
48
|
+
import { NOTE_SYSTEM } from "./systems.mjs";
|
|
49
|
+
import { HOMEPAGE_TYPE } from "./homepage.mjs";
|
|
47
50
|
// The folder id's derivation, taken from the pass that owns it rather than
|
|
48
51
|
// restated here — see the `folder` branch below. `folder-notes.mjs` reaches
|
|
49
52
|
// only `content-address`, `address-charset`, `ids` and `retired-fields`, none
|
|
@@ -102,7 +105,7 @@ function text(value) {
|
|
|
102
105
|
*/
|
|
103
106
|
export function noteDocId(fm, { pkg, maps = KNOWN_DOCUMENT_SUBTYPE_MAPS } = {}) {
|
|
104
107
|
if (!fm || typeof fm !== "object") return undefined;
|
|
105
|
-
const pinned = text(fm.id);
|
|
108
|
+
const pinned = text(fm.data?.id) ?? text(fm.id);
|
|
106
109
|
if (pinned) return pinned;
|
|
107
110
|
const type = text(fm.type);
|
|
108
111
|
const shortcode = text(fm.shortcode);
|
|
@@ -112,7 +115,9 @@ export function noteDocId(fm, { pkg, maps = KNOWN_DOCUMENT_SUBTYPE_MAPS } = {})
|
|
|
112
115
|
// the two must answer alike about the same note or the divergence this
|
|
113
116
|
// branch closes reopens under a capitalised `type: Folder`.
|
|
114
117
|
if (type.toLowerCase() === FOLDER_TYPE) return folderDocId(owner, shortcode);
|
|
115
|
-
|
|
118
|
+
const system =
|
|
119
|
+
JOURNAL_TYPES.has(type) || type === HOMEPAGE_TYPE ? NOTE_SYSTEM : systemOf(type, maps);
|
|
120
|
+
return documentId(owner, system, type, shortcode);
|
|
116
121
|
}
|
|
117
122
|
|
|
118
123
|
/**
|
|
@@ -277,6 +277,29 @@ const TOKEN_ICON = Object.freeze({
|
|
|
277
277
|
* @type {readonly DataFieldSpec[]}
|
|
278
278
|
*/
|
|
279
279
|
export const SHARED_DATA_FIELDS = Object.freeze([
|
|
280
|
+
Object.freeze({
|
|
281
|
+
name: "id",
|
|
282
|
+
...TEXT,
|
|
283
|
+
describe: "A fixed document identity when the derived identity is unsuitable.",
|
|
284
|
+
}),
|
|
285
|
+
Object.freeze({
|
|
286
|
+
name: "pack",
|
|
287
|
+
...TEXT,
|
|
288
|
+
describe: "The shared compendium route, with a system block overriding it.",
|
|
289
|
+
}),
|
|
290
|
+
Object.freeze({
|
|
291
|
+
name: "packFolder",
|
|
292
|
+
...LINK_BY_PACK,
|
|
293
|
+
ref: "folder",
|
|
294
|
+
accepts: ["folder"],
|
|
295
|
+
describe: "The shared compendium folder Address, with a system block overriding it.",
|
|
296
|
+
}),
|
|
297
|
+
Object.freeze({
|
|
298
|
+
name: "harnworld",
|
|
299
|
+
shape: "map",
|
|
300
|
+
kind: "map",
|
|
301
|
+
describe: "HârnWorld source details shared by every system.",
|
|
302
|
+
}),
|
|
280
303
|
Object.freeze({
|
|
281
304
|
name: "icon",
|
|
282
305
|
...LINK,
|
|
@@ -605,6 +628,7 @@ export const NOTE_VOCABULARY = Object.freeze({
|
|
|
605
628
|
// package's declared present compute — see `engine/being-age.mjs`.
|
|
606
629
|
check: checkBeingAge,
|
|
607
630
|
data: Object.freeze([
|
|
631
|
+
{ name: "social", shape: "map", kind: "map", describe: "The being's social profile." },
|
|
608
632
|
TOKEN_ICON,
|
|
609
633
|
TEMPLATE_PRIORITY,
|
|
610
634
|
{ name: "archetypes", ...LIST, describe: "Archetypal behaviours the being fits." },
|
package/engine/pack-router.mjs
CHANGED
|
@@ -82,7 +82,7 @@
|
|
|
82
82
|
|
|
83
83
|
import { NO_PACK, packForType } from "./ids.mjs";
|
|
84
84
|
import { loadPackConfig } from "./pack-config.mjs";
|
|
85
|
-
import {
|
|
85
|
+
import { blockDataProperty, systemBlock } from "./system-block.mjs";
|
|
86
86
|
|
|
87
87
|
/**
|
|
88
88
|
* A note that cannot be routed to a pack. Thrown rather than returned so no
|
|
@@ -131,7 +131,9 @@ export { NO_PACK };
|
|
|
131
131
|
*/
|
|
132
132
|
export function declaresNoPack(fm, system) {
|
|
133
133
|
const declared =
|
|
134
|
-
system === undefined ?
|
|
134
|
+
system === undefined ?
|
|
135
|
+
(fm?.data?.[PACK_FIELD] ?? fm?.[PACK_FIELD])
|
|
136
|
+
: blockDataProperty(fm, system, PACK_FIELD);
|
|
135
137
|
return declared === NO_PACK;
|
|
136
138
|
}
|
|
137
139
|
|
|
@@ -270,7 +272,9 @@ export function createPackRouter(packs) {
|
|
|
270
272
|
const inBlock = system === undefined ? undefined : systemBlock(fm, system)?.[PACK_FIELD];
|
|
271
273
|
const authoredInBlock = inBlock != null && inBlock !== "";
|
|
272
274
|
let declared =
|
|
273
|
-
system === undefined ?
|
|
275
|
+
system === undefined ?
|
|
276
|
+
(fm?.data?.[PACK_FIELD] ?? fm?.[PACK_FIELD])
|
|
277
|
+
: blockDataProperty(fm, system, PACK_FIELD);
|
|
274
278
|
// The declaration names where the note's *own* document goes. A pass
|
|
275
279
|
// writing a document derived from it — an item's prose becoming a
|
|
276
280
|
// JournalEntry — is not what the author was addressing.
|
|
@@ -67,7 +67,7 @@ import { encodeAddresses } from "./address-values.mjs";
|
|
|
67
67
|
|
|
68
68
|
import { positionOfFrontmatterPath } from "./diagnostics.mjs";
|
|
69
69
|
import { acceptsType, parseAddress, renderAddress } from "./address.mjs";
|
|
70
|
-
import {
|
|
70
|
+
import { NOTE_SYSTEM } from "./systems.mjs";
|
|
71
71
|
|
|
72
72
|
/* --------------------------------------------------------------------- */
|
|
73
73
|
/* The closed sets */
|
|
@@ -232,7 +232,7 @@ function addressDefaults(index) {
|
|
|
232
232
|
packages: index?.packages,
|
|
233
233
|
noIndexPackages: index?.noIndexPackages,
|
|
234
234
|
package: index?.contentPackage,
|
|
235
|
-
system:
|
|
235
|
+
system: NOTE_SYSTEM,
|
|
236
236
|
type: RELATION_TYPE,
|
|
237
237
|
};
|
|
238
238
|
}
|
|
@@ -356,7 +356,7 @@ function checkRelation(note, { field, index }) {
|
|
|
356
356
|
here.package && self ?
|
|
357
357
|
renderAddress({
|
|
358
358
|
package: here.package,
|
|
359
|
-
system:
|
|
359
|
+
system: NOTE_SYSTEM,
|
|
360
360
|
type: RELATION_TYPE,
|
|
361
361
|
shortcode: self,
|
|
362
362
|
})
|
|
@@ -425,7 +425,7 @@ function checkRelation(note, { field, index }) {
|
|
|
425
425
|
"to",
|
|
426
426
|
`${label(i, "to")} must name a place — the other place's \`shortcode\`, or ` +
|
|
427
427
|
`its address as \`${RELATION_TYPE}-<shortcode>\` or ` +
|
|
428
|
-
`\`<package>-${
|
|
428
|
+
`\`<package>-${NOTE_SYSTEM}-${RELATION_TYPE}-<shortcode>\` for a place in ` +
|
|
429
429
|
`another package — but reads ${JSON.stringify(encodeAddresses(to))}`,
|
|
430
430
|
);
|
|
431
431
|
} else if (!acceptsType(read, [RELATION_TYPE])) {
|
|
@@ -4,9 +4,10 @@
|
|
|
4
4
|
* SPDX-License-Identifier: GPL-3.0-or-later
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
import { completeAddress, renderAddress } from "./address.mjs";
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
7
|
+
import { completeAddress, ownDocumentSystem, renderAddress } from "./address.mjs";
|
|
8
|
+
import { NOTE_SYSTEM } from "./systems.mjs";
|
|
9
|
+
import { JOURNAL_TYPES } from "./ids.mjs";
|
|
10
|
+
import { hasDocEntry } from "./item-docs.mjs";
|
|
10
11
|
|
|
11
12
|
/**
|
|
12
13
|
* Names and published destinations for reference presentation.
|
|
@@ -21,10 +22,9 @@ export function buildReferenceTargets(records, published = new Map()) {
|
|
|
21
22
|
for (const record of records) {
|
|
22
23
|
if (!record.package || !record.type || !record.shortcode) continue;
|
|
23
24
|
const value = { name: record.name?.full ?? record.name, subType: record.subType };
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
])) {
|
|
25
|
+
const systems = new Set([ownDocumentSystem(record.type)]);
|
|
26
|
+
if (JOURNAL_TYPES.has(record.type) || hasDocEntry(record.type)) systems.add(NOTE_SYSTEM);
|
|
27
|
+
for (const system of systems) {
|
|
28
28
|
const address = completeAddress({
|
|
29
29
|
package: record.package,
|
|
30
30
|
system,
|
|
@@ -52,7 +52,7 @@ export function resolveShortcodeReference(indexes, code, hint) {
|
|
|
52
52
|
const { address: native, value } = matches.values().next().value;
|
|
53
53
|
const address = completeAddress({
|
|
54
54
|
package: native.package,
|
|
55
|
-
system: "
|
|
55
|
+
system: "note",
|
|
56
56
|
type: native.type,
|
|
57
57
|
shortcode: native.shortcode,
|
|
58
58
|
});
|
package/engine/site-index.mjs
CHANGED
|
@@ -61,12 +61,9 @@ import {
|
|
|
61
61
|
parseAddress,
|
|
62
62
|
renderAddress,
|
|
63
63
|
isAddressTuple,
|
|
64
|
-
|
|
64
|
+
ownDocumentSystem,
|
|
65
65
|
} from "./address.mjs";
|
|
66
|
-
import {
|
|
67
|
-
import { systemOf } from "./document-subtypes.mjs";
|
|
68
|
-
import { KNOWN_DOCUMENT_SUBTYPE_MAPS } from "./note-claims.mjs";
|
|
69
|
-
import { hasDocEntry } from "./item-docs.mjs";
|
|
66
|
+
import { NOTE_SYSTEM } from "./systems.mjs";
|
|
70
67
|
import { contentPackage } from "./content-package.mjs";
|
|
71
68
|
// The declared tag vocabulary, which is where `draft` is stated.
|
|
72
69
|
import { isDraftNote } from "./note-vocabulary.mjs";
|
|
@@ -241,42 +238,14 @@ export function buildSiteIndex(
|
|
|
241
238
|
// resolves it and records it as `pkg`. Never read out of
|
|
242
239
|
// frontmatter: `package:` is retired.
|
|
243
240
|
index.set(
|
|
244
|
-
canonicalKey(
|
|
245
|
-
e.pkg ?? ownPackage,
|
|
246
|
-
systemOf(type, KNOWN_DOCUMENT_SUBTYPE_MAPS),
|
|
247
|
-
type,
|
|
248
|
-
shortcode,
|
|
249
|
-
),
|
|
241
|
+
canonicalKey(e.pkg ?? ownPackage, ownDocumentSystem(type), type, shortcode),
|
|
250
242
|
value,
|
|
251
243
|
);
|
|
252
244
|
if (e.pkg) packages.add(e.pkg);
|
|
253
|
-
//
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
// ordinary in-page anchor. One authored link, correct in both
|
|
258
|
-
// builds — restricted to the types that actually have an item doc,
|
|
259
|
-
// so a qualifier the packs would reject is reported broken here too.
|
|
260
|
-
const proseAddress = completeAddress({
|
|
261
|
-
package: e.pkg ?? ownPackage,
|
|
262
|
-
system: NO_SYSTEM,
|
|
263
|
-
type,
|
|
264
|
-
shortcode,
|
|
265
|
-
});
|
|
266
|
-
if (proseAddress.type !== type || hasDocEntry(type)) {
|
|
267
|
-
contentTypes.add(`doc${type}`);
|
|
268
|
-
index.set(`doc${type}/${shortcode}`.toLowerCase(), value);
|
|
269
|
-
// The canonical documentation address too, so the page answers
|
|
270
|
-
// to the address a bare prose link expands to: body
|
|
271
|
-
// prose is under no system block, so it defaults to `none`, and
|
|
272
|
-
// a system-bearing type's `none` address is its `doc<type>`
|
|
273
|
-
// one. In Foundry that names a second document; here it names
|
|
274
|
-
// this same page, which is what makes one authored link correct
|
|
275
|
-
// in both builds.
|
|
276
|
-
index.set(
|
|
277
|
-
canonicalKey(e.pkg ?? ownPackage, NO_SYSTEM, `doc${type}`, shortcode),
|
|
278
|
-
value,
|
|
279
|
-
);
|
|
245
|
+
// A web page is the readable `note` target for every type.
|
|
246
|
+
if (ownDocumentSystem(type) !== NOTE_SYSTEM) {
|
|
247
|
+
// The same page answers to its readable Address.
|
|
248
|
+
index.set(canonicalKey(e.pkg ?? ownPackage, NOTE_SYSTEM, type, shortcode), value);
|
|
280
249
|
}
|
|
281
250
|
}
|
|
282
251
|
}
|
|
@@ -375,7 +344,7 @@ export function resolveInfoboxRef(siteIndex, ref, hint) {
|
|
|
375
344
|
}
|
|
376
345
|
const context = {
|
|
377
346
|
package: siteIndex.contentPackage ?? contentPackage(),
|
|
378
|
-
system: "
|
|
347
|
+
system: "note",
|
|
379
348
|
types: siteIndex.contentTypes,
|
|
380
349
|
packages: siteIndex.packages,
|
|
381
350
|
};
|
package/engine/sql-tables.mjs
CHANGED
|
@@ -253,7 +253,7 @@ export async function openNotesDatabase(records, { dir, dependencies = [], addre
|
|
|
253
253
|
addressContext: addressContext ?? {
|
|
254
254
|
package: records.find((record) => record.package)?.package,
|
|
255
255
|
types: new Set(Object.keys(NOTE_VOCABULARY)),
|
|
256
|
-
system: "
|
|
256
|
+
system: "note",
|
|
257
257
|
},
|
|
258
258
|
stubsExcluded,
|
|
259
259
|
async query(sql) {
|
package/engine/system-block.mjs
CHANGED
|
@@ -361,6 +361,25 @@ export function blockProperty(fm, block, key, defaultValue = undefined) {
|
|
|
361
361
|
return shared === undefined || shared === null ? defaultValue : shared;
|
|
362
362
|
}
|
|
363
363
|
|
|
364
|
+
/**
|
|
365
|
+
* A system override followed by a shared `data:` value. The top-level value
|
|
366
|
+
* remains readable while content repositories move their authored fields.
|
|
367
|
+
*
|
|
368
|
+
* @param {object} fm - The note's frontmatter.
|
|
369
|
+
* @param {string} block - The system block key.
|
|
370
|
+
* @param {string} key - The shared property.
|
|
371
|
+
* @param {any} [defaultValue] - Returned when no position declares it.
|
|
372
|
+
* @returns {any} The selected value.
|
|
373
|
+
*/
|
|
374
|
+
export function blockDataProperty(fm, block, key, defaultValue = undefined) {
|
|
375
|
+
const declared = systemBlock(fm, block)?.[key];
|
|
376
|
+
if (declared !== undefined && declared !== null) return declared;
|
|
377
|
+
const shared = isMapping(fm?.data) ? fm.data[key] : undefined;
|
|
378
|
+
if (shared !== undefined && shared !== null) return shared;
|
|
379
|
+
const legacy = isMapping(fm) ? fm[key] : undefined;
|
|
380
|
+
return legacy === undefined || legacy === null ? defaultValue : legacy;
|
|
381
|
+
}
|
|
382
|
+
|
|
364
383
|
/**
|
|
365
384
|
* The key a field is authored at **inside** a system block — step 2.
|
|
366
385
|
*
|
package/engine/systems.mjs
CHANGED
|
@@ -62,6 +62,9 @@ import { ADDRESS_SEGMENT_PATTERN, isAddressSegment } from "./address-charset.mjs
|
|
|
62
62
|
*/
|
|
63
63
|
export const NO_SYSTEM = "none";
|
|
64
64
|
|
|
65
|
+
/** The Address segment for a note's readable content. */
|
|
66
|
+
export const NOTE_SYSTEM = "note";
|
|
67
|
+
|
|
65
68
|
/**
|
|
66
69
|
* Every game system this toolchain compiles for.
|
|
67
70
|
*
|
|
@@ -86,7 +89,7 @@ export const SYSTEM_IDS = Object.freeze(new Set(["sohl", "hm3"]));
|
|
|
86
89
|
*
|
|
87
90
|
* @type {ReadonlySet<string>}
|
|
88
91
|
*/
|
|
89
|
-
export const SYSTEM_SEGMENTS = Object.freeze(new Set([...SYSTEM_IDS, NO_SYSTEM]));
|
|
92
|
+
export const SYSTEM_SEGMENTS = Object.freeze(new Set([...SYSTEM_IDS, NO_SYSTEM, NOTE_SYSTEM]));
|
|
90
93
|
|
|
91
94
|
/**
|
|
92
95
|
* Whether a value names a game system this toolchain knows.
|
package/engine/web-wikilinks.mjs
CHANGED
|
@@ -48,7 +48,7 @@ import { replaceOutsideCode } from "./code-fences.mjs";
|
|
|
48
48
|
// address is matched the way a vendored manifest publishes it — by the
|
|
49
49
|
// segments the target supplies, with the system wildcarded unless stated.
|
|
50
50
|
import { canonicalKey, expandAddress, readCanonicalKey } from "./content-address.mjs";
|
|
51
|
-
import {
|
|
51
|
+
import { NOTE_SYSTEM } from "./systems.mjs";
|
|
52
52
|
// The one rule about a link's shape both builds share: it carries a label, and
|
|
53
53
|
// {@link unlabelledLinkMessage} is the one place that says so.
|
|
54
54
|
import { unlabelledLinkMessage } from "./wikilink-syntax.mjs";
|
|
@@ -102,7 +102,7 @@ function lookupRead(index, read, contentPackage) {
|
|
|
102
102
|
// It replaced a system-blind short key for the unqualified form, which
|
|
103
103
|
// could be silently overwritten by a second note of the same
|
|
104
104
|
// `(type, shortcode)` under another system.
|
|
105
|
-
const key = expandAddress(read, { package: contentPackage, system:
|
|
105
|
+
const key = expandAddress(read, { package: contentPackage, system: NOTE_SYSTEM });
|
|
106
106
|
const found = index.get(key);
|
|
107
107
|
if (found || read.package) return found;
|
|
108
108
|
|
|
@@ -393,7 +393,7 @@ export function resolveWebWikilinks(body, ctx) {
|
|
|
393
393
|
return replaceOutsideCode(embedded.markdown, WIKILINK, (all, rawInner) => {
|
|
394
394
|
const parsed = readWikilink(rawInner, {
|
|
395
395
|
package: ctx.contentPackage,
|
|
396
|
-
system: "
|
|
396
|
+
system: "note",
|
|
397
397
|
types: ctx.contentTypes ?? new Set(),
|
|
398
398
|
packages: ctx.packages,
|
|
399
399
|
noIndexPackages: ctx.noIndexPackages,
|