@heroiclands/package-build 22.12.3 → 22.13.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 +13 -0
- package/README.md +2 -2
- package/docs/api.md +19 -18
- package/docs/commands.md +0 -3
- package/docs/content-format.md +59 -66
- 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/foundry-entries.mjs +4 -8
- package/engine/frontmatter-lint.mjs +2 -2
- package/engine/metadata-index.mjs +23 -6
- package/engine/note-addresses.mjs +27 -9
- package/engine/note-ids.mjs +6 -1
- 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/systems.mjs +4 -1
- package/engine/web-wikilinks.mjs +3 -3
- package/engine/wikilinks.mjs +31 -106
- 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/note-addresses.d.mts +2 -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
package/engine/art-fields.mjs
CHANGED
|
@@ -88,7 +88,7 @@ export function artSlot(key) {
|
|
|
88
88
|
* not parse; or `not-accepted` when it parses to a type outside `accepts`.
|
|
89
89
|
*/
|
|
90
90
|
export function artTarget(value, defaultType, accepts, vocabulary = {}) {
|
|
91
|
-
const tuple = parseAddress(value, { ...vocabulary, type: defaultType });
|
|
91
|
+
const tuple = parseAddress(value, { ...vocabulary, system: ASSET_SYSTEM, type: defaultType });
|
|
92
92
|
if (tuple.reason) return tuple;
|
|
93
93
|
if (accepts && !acceptsType(tuple, accepts)) {
|
|
94
94
|
return { reason: "not-accepted", type: tuple.type };
|
package/engine/bundles.mjs
CHANGED
|
@@ -71,7 +71,7 @@ import {
|
|
|
71
71
|
import { contentPackage } from "./content-package.mjs";
|
|
72
72
|
import { FOLDER_TYPE } from "./folder-notes.mjs";
|
|
73
73
|
import { HOMEPAGE_TYPE } from "./homepage.mjs";
|
|
74
|
-
import { itemDocEntryId } from "./item-docs.mjs";
|
|
74
|
+
import { hasDocEntry, itemDocEntryId } from "./item-docs.mjs";
|
|
75
75
|
import { folderField, renderFoundryMarkdown, resolveName } from "./helpers.mjs";
|
|
76
76
|
import { packForType } from "./ids.mjs";
|
|
77
77
|
import { readQualifier } from "./wikilinks.mjs";
|
|
@@ -251,8 +251,9 @@ export class Bundles extends BasePackCompiler {
|
|
|
251
251
|
|
|
252
252
|
const baseType =
|
|
253
253
|
isAddressTuple(read) ? resolveItemDocType(read.type, index.types) : undefined;
|
|
254
|
-
const itemDoc = read.itemDoc || Boolean(baseType);
|
|
255
254
|
const type = baseType ?? read.type;
|
|
255
|
+
const itemDoc =
|
|
256
|
+
read.itemDoc || Boolean(baseType) || (read.system === "note" && hasDocEntry(type));
|
|
256
257
|
const note = index.byShortcode.get(`${type}/${read.shortcode}`);
|
|
257
258
|
if (!note) {
|
|
258
259
|
throw new Error(
|
|
@@ -45,7 +45,7 @@ import { DEFAULT_ADDRESS_SCHEME } from "../content-config.mjs";
|
|
|
45
45
|
// The system vocabulary is the `<system>` segment's own registry, and
|
|
46
46
|
// `engine/systems.mjs` imports nothing but `engine/address-charset.mjs`, so
|
|
47
47
|
// the direction is toward the leaf and cannot close a cycle.
|
|
48
|
-
import { NO_SYSTEM,
|
|
48
|
+
import { NO_SYSTEM, NOTE_SYSTEM, isSystemId } from "./systems.mjs";
|
|
49
49
|
|
|
50
50
|
// The address grammar, which sits below this module: a page URL is derived from
|
|
51
51
|
// an address, so the rule for reading and writing one is stated there and
|
|
@@ -171,12 +171,9 @@ export function packageAddress(fm) {
|
|
|
171
171
|
*
|
|
172
172
|
* The **enclosing system block** decides, at any depth within it, and nothing
|
|
173
173
|
* else does: `sohl.items[3].model` and `sohl.system.body.structure` are both
|
|
174
|
-
* `sohl` because both sit under `sohl:`. Everywhere else
|
|
175
|
-
*
|
|
176
|
-
*
|
|
177
|
-
*
|
|
178
|
-
* It is the block rather than the field, so an `Address` field needs no opinion
|
|
179
|
-
* about systems and no per-field table has to be kept in step with the schema.
|
|
174
|
+
* `sohl` because both sit under `sohl:`. Everywhere else takes
|
|
175
|
+
* {@link NOTE_SYSTEM}; fields declared for assets or folders supply their own
|
|
176
|
+
* `none` default at the field parser.
|
|
180
177
|
*
|
|
181
178
|
* The first segment must **be** a declared system, not merely look like one:
|
|
182
179
|
* `sohlish.items` is a key called `sohlish`, and `notes.sohl.thing` names no
|
|
@@ -187,9 +184,9 @@ export function packageAddress(fm) {
|
|
|
187
184
|
* @returns {string} The system id, or `none`.
|
|
188
185
|
*/
|
|
189
186
|
export function blockSystem(keyPath) {
|
|
190
|
-
if (typeof keyPath !== "string" || !keyPath) return
|
|
187
|
+
if (typeof keyPath !== "string" || !keyPath) return NOTE_SYSTEM;
|
|
191
188
|
const first = keyPath.split(".")[0].trim().toLowerCase();
|
|
192
|
-
return
|
|
189
|
+
return isSystemId(first) ? first : NOTE_SYSTEM;
|
|
193
190
|
}
|
|
194
191
|
|
|
195
192
|
/**
|
package/engine/content-index.mjs
CHANGED
|
@@ -102,12 +102,13 @@ import unidecode from "unidecode";
|
|
|
102
102
|
import { metadataFileName } from "./metadata-index.mjs";
|
|
103
103
|
import { collectAssetRecords } from "./asset-index.mjs";
|
|
104
104
|
import { addressSlug, canonicalKey } from "./content-address.mjs";
|
|
105
|
+
import { ownDocumentSystem } from "./address.mjs";
|
|
106
|
+
import { NOTE_SYSTEM } from "./systems.mjs";
|
|
105
107
|
// One reader for a note's anchors, shared with the link checker and with the
|
|
106
108
|
// builds that emit a link. Re-exported because this is where callers
|
|
107
109
|
// have always addressed it.
|
|
108
110
|
import { collectAnchors } from "./anchors.mjs";
|
|
109
|
-
import {
|
|
110
|
-
import { KNOWN_DOCUMENT_SUBTYPE_MAPS } from "./note-claims.mjs";
|
|
111
|
+
import { NO_SYSTEM } from "./document-subtypes.mjs";
|
|
111
112
|
|
|
112
113
|
/**
|
|
113
114
|
* The `<system>` a note belongs to when it belongs to none.
|
|
@@ -204,7 +205,7 @@ export function noteAddress(frontmatter, contentPackage) {
|
|
|
204
205
|
slug,
|
|
205
206
|
canonical: canonicalKey(
|
|
206
207
|
contentPackage,
|
|
207
|
-
|
|
208
|
+
ownDocumentSystem(frontmatter.type),
|
|
208
209
|
frontmatter.type,
|
|
209
210
|
frontmatter.shortcode,
|
|
210
211
|
),
|
|
@@ -504,10 +505,7 @@ export function buildIndexRecord({
|
|
|
504
505
|
link: address ? `${address.slug}#${a.slug}` : null,
|
|
505
506
|
}))
|
|
506
507
|
),
|
|
507
|
-
foundry: foundryBlock(
|
|
508
|
-
entries?.own,
|
|
509
|
-
systemOf(frontmatter?.type, KNOWN_DOCUMENT_SUBTYPE_MAPS),
|
|
510
|
-
),
|
|
508
|
+
foundry: foundryBlock(entries?.own, ownDocumentSystem(frontmatter?.type)),
|
|
511
509
|
// Forward link to the note's documentation journal, which is its
|
|
512
510
|
// own record. Named rather than nested, because the journal is a
|
|
513
511
|
// separate document with its own address — see `buildDocRecord`.
|
|
@@ -536,7 +534,7 @@ export function buildIndexRecord({
|
|
|
536
534
|
*
|
|
537
535
|
* An item note compiles into two documents — the item, and a JournalEntry
|
|
538
536
|
* holding its prose — and the second is a document in its own right: its own
|
|
539
|
-
* canonical address
|
|
537
|
+
* canonical `note` address with the authored type, its own UUID and pages.
|
|
540
538
|
* So it gets its own record, and resolving `docaffliction/blkdth` is the same
|
|
541
539
|
* lookup as resolving anything else. Nested inside the item's record it would
|
|
542
540
|
* be the one address in the index reachable only by knowing to look somewhere
|
|
@@ -572,7 +570,7 @@ function buildDocRecord({ frontmatter, address, entry, file, contentPackage, anc
|
|
|
572
570
|
return /** @type {Record<string, any>} */ (
|
|
573
571
|
sortKeysDeep({
|
|
574
572
|
package: contentPackage,
|
|
575
|
-
type:
|
|
573
|
+
type: frontmatter.type,
|
|
576
574
|
shortcode: frontmatter.shortcode,
|
|
577
575
|
name: frontmatter.name,
|
|
578
576
|
// The journal's own `_id`, taken from the entry rather than
|
|
@@ -586,7 +584,7 @@ function buildDocRecord({ frontmatter, address, entry, file, contentPackage, anc
|
|
|
586
584
|
// the forward link on that record, so either end reaches the other.
|
|
587
585
|
documents: address.canonical,
|
|
588
586
|
anchors: made ? anchors : null,
|
|
589
|
-
foundry: foundryBlock(entry),
|
|
587
|
+
foundry: foundryBlock(entry, NOTE_SYSTEM),
|
|
590
588
|
file,
|
|
591
589
|
})
|
|
592
590
|
);
|
package/engine/content-links.mjs
CHANGED
|
@@ -76,8 +76,8 @@ import { ASSET_TYPE_NAMES } from "./asset-types.mjs";
|
|
|
76
76
|
import { resolveEmbeds } from "./content-embeds.mjs";
|
|
77
77
|
import { foundryAddressProblem, servesFoundry } from "./pathnames.mjs";
|
|
78
78
|
import { hasDocEntry } from "./item-docs.mjs";
|
|
79
|
-
import {
|
|
80
|
-
import {
|
|
79
|
+
import { ownDocumentSystem } from "./address.mjs";
|
|
80
|
+
import { NOTE_SYSTEM } from "./systems.mjs";
|
|
81
81
|
import { loadPackConfig } from "./pack-config.mjs";
|
|
82
82
|
import { searchableFrontmatter } from "./note-package.mjs";
|
|
83
83
|
import {
|
|
@@ -138,12 +138,8 @@ export function anchorsOf(body) {
|
|
|
138
138
|
* before, since the walk read the file and it then read it again for the raw
|
|
139
139
|
* text.
|
|
140
140
|
*
|
|
141
|
-
* The index
|
|
142
|
-
*
|
|
143
|
-
* `doc<type>/shortcode` for the JournalEntry its prose compiles into. Once a
|
|
144
|
-
* manifest publishes `doc<type>` entries that prefix is a *known type*, and the
|
|
145
|
-
* virtual reading that used to answer for it no longer fires — a real type owns
|
|
146
|
-
* its own name — so the note is indexed under both.
|
|
141
|
+
* The index holds each compiled document under its complete Address. Readable
|
|
142
|
+
* content uses `note`, and system documents use their declared system.
|
|
147
143
|
*
|
|
148
144
|
* @param {string} contentBase - Root of the content tree.
|
|
149
145
|
* @param {object} [opts]
|
|
@@ -250,24 +246,20 @@ export function buildLinkIndex(
|
|
|
250
246
|
// once for the whole build.
|
|
251
247
|
const canonical =
|
|
252
248
|
encodeAddresses(record.address?.canonical) ??
|
|
253
|
-
canonicalKey(pkg,
|
|
249
|
+
canonicalKey(pkg, ownDocumentSystem(type), type, fm.shortcode);
|
|
254
250
|
// A note with no address publishes no page, so its identity is
|
|
255
251
|
// filed where a link cannot reach it and a reference still can.
|
|
256
252
|
const into = record.address ? byKey : byStub;
|
|
257
253
|
into.set(canonical, note);
|
|
258
254
|
if (hasDocEntry(type)) {
|
|
259
|
-
//
|
|
260
|
-
|
|
261
|
-
// system blocks it carries.
|
|
262
|
-
into.set(canonicalKey(pkg, NO_SYSTEM, `doc${type}`, fm.shortcode), note);
|
|
255
|
+
// The prose has one `note` Address across all system blocks.
|
|
256
|
+
into.set(canonicalKey(pkg, NOTE_SYSTEM, type, fm.shortcode), note);
|
|
263
257
|
}
|
|
264
258
|
}
|
|
265
259
|
}
|
|
266
260
|
|
|
267
261
|
// The types a link may name, which are the ones notes declare. A
|
|
268
|
-
//
|
|
269
|
-
// virtual, and `readQualifier` resolves it from the base type rather than
|
|
270
|
-
// from a type any tree declares.
|
|
262
|
+
// The `doc<type>` input spelling is an alias, not a published type.
|
|
271
263
|
//
|
|
272
264
|
// The **asset** types join unconditionally, whether or not this tree holds
|
|
273
265
|
// a file of each. They are a closed vocabulary rather than a census of what
|
|
@@ -423,7 +415,7 @@ export function buildLinkIndex(
|
|
|
423
415
|
for (const [all, rawInner] of matchAllOutsideCode(body, new RegExp(WIKILINK.source, "g"))) {
|
|
424
416
|
const parsed = readWikilink(rawInner, {
|
|
425
417
|
package: pkg,
|
|
426
|
-
system: "
|
|
418
|
+
system: "note",
|
|
427
419
|
types,
|
|
428
420
|
packages,
|
|
429
421
|
noIndexPackages,
|
|
@@ -1041,7 +1041,7 @@ export function renderContentTable(spec, rows, linkable, self) {
|
|
|
1041
1041
|
const label = text.replace(/\\?\|/g, "/");
|
|
1042
1042
|
const target = completeAddress(
|
|
1043
1043
|
{ type: doc.fm.type, shortcode: doc.fm.shortcode },
|
|
1044
|
-
{ package: doc.fm.package ?? contentPackage(), system: "
|
|
1044
|
+
{ package: doc.fm.package ?? contentPackage(), system: "note" },
|
|
1045
1045
|
);
|
|
1046
1046
|
return `[[${renderAddress(target)}\\|${label}]]`;
|
|
1047
1047
|
}),
|
|
@@ -60,7 +60,8 @@ import path from "node:path";
|
|
|
60
60
|
|
|
61
61
|
import { noteAddressContext } from "./note-addresses.mjs";
|
|
62
62
|
import { canonicalKey, packageAddress } from "./content-address.mjs";
|
|
63
|
-
import {
|
|
63
|
+
import { ownDocumentSystem } from "./address.mjs";
|
|
64
|
+
import { NOTE_SYSTEM } from "./systems.mjs";
|
|
64
65
|
import {
|
|
65
66
|
KNOWN_DOCUMENT_SUBTYPE_MAPS,
|
|
66
67
|
NEVER_PACKED_TYPES,
|
|
@@ -150,12 +151,7 @@ export function anchorsOf(entryUuid, entryId, body, name) {
|
|
|
150
151
|
*/
|
|
151
152
|
export function entriesForNote(fm, name, address, body, ctx) {
|
|
152
153
|
const { contentPackage, foundryPackageId, packRouter } = ctx;
|
|
153
|
-
const key = canonicalKey(
|
|
154
|
-
contentPackage,
|
|
155
|
-
systemOf(fm.type, KNOWN_DOCUMENT_SUBTYPE_MAPS),
|
|
156
|
-
fm.type,
|
|
157
|
-
fm.shortcode,
|
|
158
|
-
);
|
|
154
|
+
const key = canonicalKey(contentPackage, ownDocumentSystem(fm.type), fm.type, fm.shortcode);
|
|
159
155
|
// `buildManifest` records `packageRelative(url, base)`, so the pair it is
|
|
160
156
|
// given has to round-trip. The address is already package-relative, so the
|
|
161
157
|
// honest pair is the address under a base of `"/"` — which strips straight
|
|
@@ -244,7 +240,7 @@ export function entriesForNote(fm, name, address, body, ctx) {
|
|
|
244
240
|
// `NO_SYSTEM`, whatever the item is: a documentation journal is a
|
|
245
241
|
// JournalEntry, which no game system defines, and there is one of them
|
|
246
242
|
// however many system blocks the note carries.
|
|
247
|
-
const docKey = canonicalKey(contentPackage,
|
|
243
|
+
const docKey = canonicalKey(contentPackage, NOTE_SYSTEM, fm.type, fm.shortcode);
|
|
248
244
|
const docEntryId = fm.id ? itemDocEntryId(fm.id) : undefined;
|
|
249
245
|
const docUuid = uuidFor("doc", docEntryId);
|
|
250
246
|
return [
|
|
@@ -1109,7 +1109,7 @@ export function lintNote(
|
|
|
1109
1109
|
const fm = note.fm ?? {};
|
|
1110
1110
|
const referenceContext = {
|
|
1111
1111
|
package: index?.contentPackage,
|
|
1112
|
-
system: "
|
|
1112
|
+
system: "note",
|
|
1113
1113
|
...addressContext,
|
|
1114
1114
|
types: new Set([
|
|
1115
1115
|
...Object.keys(vocabulary ?? {}),
|
|
@@ -1773,7 +1773,7 @@ export function lintFrontmatter(
|
|
|
1773
1773
|
index: references ? index : undefined,
|
|
1774
1774
|
addressContext: {
|
|
1775
1775
|
package: index.contentPackage,
|
|
1776
|
-
system: "
|
|
1776
|
+
system: "note",
|
|
1777
1777
|
types: index.types,
|
|
1778
1778
|
},
|
|
1779
1779
|
...(systems ? { systems } : {}),
|
|
@@ -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
|
|
@@ -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
|
/**
|
|
@@ -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/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,
|