@heroiclands/package-build 20.3.1 → 20.4.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 +75 -0
- package/CONTENT.md +111 -92
- package/README.md +2 -9
- package/bin/content-build.mjs +70 -61
- package/bin/package-build.mjs +2 -2
- package/config.mjs +6 -8
- package/content-config.mjs +187 -64
- package/docs/content-format.md +57 -68
- package/e2e.mjs +4 -4
- package/engine/actor-compiler.mjs +36 -36
- package/engine/address-charset.mjs +6 -6
- package/engine/address-diff.mjs +12 -11
- package/engine/anchored-sections.mjs +1 -1
- package/engine/anchors.mjs +1 -1
- package/engine/base-compiler.mjs +36 -38
- package/engine/bundle-notes.mjs +4 -4
- package/engine/bundles.mjs +8 -8
- package/engine/code-fences.mjs +3 -3
- package/engine/compendiums.mjs +8 -8
- package/engine/compile-corpus.mjs +4 -5
- package/engine/content-address.mjs +15 -15
- package/engine/content-charset.mjs +1 -1
- package/engine/content-format-check.mjs +24 -25
- package/engine/content-format.mjs +8 -8
- package/engine/content-html.mjs +202 -0
- package/engine/content-icons.mjs +196 -165
- package/engine/content-index.mjs +17 -17
- package/engine/content-links.mjs +30 -30
- package/engine/content-lint.mjs +20 -24
- package/engine/content-package.mjs +7 -9
- package/engine/content-slug.mjs +2 -2
- package/engine/content-tables.mjs +7 -8
- package/engine/content-tree.mjs +1 -1
- package/engine/derived-fields.mjs +174 -0
- package/engine/diagnostics.mjs +3 -4
- package/engine/document-subtypes.mjs +6 -7
- package/engine/field-reference.mjs +8 -8
- package/engine/field-spec.mjs +29 -30
- package/engine/folder-notes.mjs +14 -14
- package/engine/foreign-catalog.mjs +8 -9
- package/engine/foundry-entries.mjs +19 -20
- package/engine/frontmatter-lint.mjs +64 -69
- package/engine/frontmatter.mjs +11 -11
- package/engine/generate.mjs +39 -39
- package/engine/helpers.mjs +63 -54
- package/engine/homepage.mjs +20 -22
- package/engine/ids.mjs +15 -15
- package/engine/index-records.mjs +4 -4
- package/engine/index.mjs +14 -8
- package/engine/item-compiler.mjs +54 -29
- package/engine/item-docs.mjs +11 -13
- package/engine/item-registry.mjs +13 -13
- package/engine/journals.mjs +15 -15
- package/engine/macros.mjs +3 -3
- package/engine/map-notes.mjs +6 -6
- package/engine/metadata-index.mjs +5 -5
- package/engine/note-claims.mjs +36 -34
- package/engine/note-ids.mjs +5 -5
- package/engine/note-package.mjs +4 -4
- package/engine/note-renames.mjs +3 -3
- package/engine/note-schemas.mjs +3 -3
- package/engine/note-vocabulary.mjs +27 -26
- package/engine/pack-config.mjs +13 -13
- package/engine/pack-router.mjs +0 -0
- package/engine/prose-config.mjs +9 -9
- package/engine/prose-lint.mjs +12 -13
- package/engine/region-events.mjs +2 -2
- package/engine/retired-fields.mjs +24 -22
- package/engine/runtime-only-fields.mjs +1 -1
- package/engine/scene-levels.mjs +1 -1
- package/engine/scenes.mjs +19 -20
- package/engine/schema-check.mjs +20 -20
- package/engine/schema-extract.mjs +1 -1
- package/engine/site-build.mjs +45 -47
- package/engine/site-index.mjs +17 -18
- package/engine/sql-tables.mjs +21 -13
- package/engine/subtype-registry.mjs +5 -4
- package/engine/system-block.mjs +18 -19
- package/engine/systems.mjs +2 -2
- package/engine/web-wikilinks.mjs +35 -35
- package/engine/wikilink-syntax.mjs +16 -16
- package/engine/wikilinks.mjs +39 -39
- package/hm3/actors.mjs +22 -22
- package/hm3/document-subtypes.mjs +5 -5
- package/hm3/index.mjs +2 -2
- package/hm3/item-builders.mjs +1 -1
- package/hm3/item-fields.mjs +3 -3
- package/hm3/items.mjs +20 -8
- package/hm3/template-priority.mjs +2 -2
- package/lang.mjs +3 -3
- package/manifest.mjs +17 -20
- package/package.json +1 -2
- package/release.mjs +3 -3
- package/sohl/actors.mjs +28 -28
- package/sohl/affiliation-standings.mjs +2 -2
- package/sohl/being-info.mjs +5 -5
- package/sohl/default-item-art.mjs +5 -5
- package/sohl/document-subtypes.mjs +5 -5
- package/sohl/index.mjs +3 -3
- package/sohl/item-builders.mjs +7 -7
- package/sohl/item-fields.mjs +8 -9
- package/sohl/items.mjs +20 -6
- package/sohl/kb-passes.mjs +5 -5
- package/sohl/note-schemas.mjs +7 -7
- package/sohl/skill-base.mjs +1 -1
- package/types/content-config.d.mts +25 -24
- package/types/e2e.d.mts +3 -3
- package/types/engine/actor-compiler.d.mts +14 -13
- package/types/engine/address-charset.d.mts +6 -6
- package/types/engine/address-diff.d.mts +3 -3
- package/types/engine/base-compiler.d.mts +17 -19
- package/types/engine/bundle-notes.d.mts +3 -3
- package/types/engine/bundles.d.mts +1 -1
- package/types/engine/code-fences.d.mts +3 -3
- package/types/engine/compendiums.d.mts +3 -3
- package/types/engine/compile-corpus.d.mts +1 -1
- package/types/engine/content-address.d.mts +11 -11
- package/types/engine/content-format-check.d.mts +6 -6
- package/types/engine/content-format.d.mts +2 -2
- package/types/engine/content-html.d.mts +78 -0
- package/types/engine/content-icons.d.mts +139 -92
- package/types/engine/content-index.d.mts +2 -2
- package/types/engine/content-links.d.mts +8 -8
- package/types/engine/content-lint.d.mts +2 -2
- package/types/engine/content-package.d.mts +6 -8
- package/types/engine/derived-fields.d.mts +101 -0
- package/types/engine/diagnostics.d.mts +2 -2
- package/types/engine/document-subtypes.d.mts +3 -3
- package/types/engine/field-spec.d.mts +37 -39
- package/types/engine/folder-notes.d.mts +4 -5
- package/types/engine/foreign-catalog.d.mts +3 -3
- package/types/engine/foundry-entries.d.mts +6 -7
- package/types/engine/frontmatter-lint.d.mts +7 -7
- package/types/engine/frontmatter.d.mts +7 -7
- package/types/engine/generate.d.mts +7 -7
- package/types/engine/helpers.d.mts +25 -27
- package/types/engine/homepage.d.mts +16 -18
- package/types/engine/ids.d.mts +13 -13
- package/types/engine/index-records.d.mts +3 -3
- package/types/engine/index.d.mts +2 -0
- package/types/engine/item-compiler.d.mts +21 -5
- package/types/engine/item-docs.d.mts +2 -2
- package/types/engine/item-registry.d.mts +6 -6
- package/types/engine/journals.d.mts +3 -3
- package/types/engine/map-notes.d.mts +2 -2
- package/types/engine/metadata-index.d.mts +4 -4
- package/types/engine/note-claims.d.mts +16 -15
- package/types/engine/note-ids.d.mts +4 -4
- package/types/engine/note-package.d.mts +1 -1
- package/types/engine/note-renames.d.mts +3 -3
- package/types/engine/note-vocabulary.d.mts +9 -8
- package/types/engine/pack-config.d.mts +3 -3
- package/types/engine/pack-router.d.mts +1 -1
- package/types/engine/prose-config.d.mts +9 -9
- package/types/engine/prose-lint.d.mts +3 -4
- package/types/engine/region-events.d.mts +2 -2
- package/types/engine/retired-fields.d.mts +10 -9
- package/types/engine/schema-check.d.mts +11 -11
- package/types/engine/site-build.d.mts +18 -19
- package/types/engine/site-index.d.mts +3 -4
- package/types/engine/sql-tables.d.mts +11 -5
- package/types/engine/subtype-registry.d.mts +3 -3
- package/types/engine/system-block.d.mts +3 -3
- package/types/engine/web-wikilinks.d.mts +7 -7
- package/types/engine/wikilink-syntax.d.mts +17 -17
- package/types/engine/wikilinks.d.mts +13 -12
- package/types/hm3/actors.d.mts +1 -1
- package/types/hm3/document-subtypes.d.mts +1 -1
- package/types/hm3/items.d.mts +1 -2
- package/types/hm3/template-priority.d.mts +1 -1
- package/types/manifest.d.mts +8 -8
- package/types/sohl/actors.d.mts +2 -2
- package/types/sohl/affiliation-standings.d.mts +2 -2
- package/types/sohl/being-info.d.mts +2 -2
- package/types/sohl/document-subtypes.d.mts +1 -1
- package/types/sohl/items.d.mts +1 -2
- package/types/sohl/note-schemas.d.mts +1 -1
- package/MIGRATING.md +0 -608
package/engine/generate.mjs
CHANGED
|
@@ -22,18 +22,18 @@
|
|
|
22
22
|
*
|
|
23
23
|
* Each `*` compiler walks the whole content tree and selects its own entries by
|
|
24
24
|
* the note's `type` — every note in the tree belongs to this repository's
|
|
25
|
-
* `contentPackage`
|
|
25
|
+
* `contentPackage` — so routing is directory-agnostic: a file lands in a
|
|
26
26
|
* pack because of its `type`, not its location. Which packs exist is declared
|
|
27
27
|
* in `package-build.config.yaml`; the **folder hierarchy is not declared
|
|
28
|
-
* anywhere**. A folder is a note like any other
|
|
28
|
+
* anywhere**. A folder is a note like any other, named by `packFolder`
|
|
29
29
|
* — its address — and it materialises in every pack that holds a document
|
|
30
|
-
* naming it, its ancestors with it
|
|
30
|
+
* naming it, its ancestors with it. So no pack loads a folder list, and
|
|
31
31
|
* two packs can no longer disagree about a folder they both hold.
|
|
32
32
|
*
|
|
33
33
|
* **The order the passes run in is derived, not declared** — see
|
|
34
34
|
* {@link orderPassesByDependency}. The declared list is the manifest's `packs`
|
|
35
35
|
* array as well, so it is ordered for a reader; a pass that reads another's
|
|
36
|
-
* output states that on its compiler and is scheduled after it
|
|
36
|
+
* output states that on its compiler and is scheduled after it.
|
|
37
37
|
*
|
|
38
38
|
* This replaces the retired `packs:export` (vault → committed `_source/`); the
|
|
39
39
|
* HeroicLands vault is no longer a build input for SoHL content.
|
|
@@ -62,15 +62,15 @@ import {
|
|
|
62
62
|
} from "./folder-notes.mjs";
|
|
63
63
|
import { countContentNotes } from "./content-tree.mjs";
|
|
64
64
|
import { emitDiagnostic } from "./diagnostics.mjs";
|
|
65
|
-
// The corpus every pass runs over, derived once
|
|
65
|
+
// The corpus every pass runs over, derived once.
|
|
66
66
|
import { buildCompileCorpus } from "./compile-corpus.mjs";
|
|
67
|
-
// The record accessors only — see `engine/index-records.mjs
|
|
67
|
+
// The record accessors only — see `engine/index-records.mjs`.
|
|
68
68
|
import { isNoteRecord, noteFile } from "./index-records.mjs";
|
|
69
69
|
import { loadPackConfig } from "./pack-config.mjs";
|
|
70
70
|
import { routerFor } from "./pack-router.mjs";
|
|
71
71
|
import { NEVER_PACKED_TYPES, unclaimedNoteFindings } from "./note-claims.mjs";
|
|
72
72
|
// Which document a content type compiles into, so the art declaration below is
|
|
73
|
-
// answered from the same routing the compile uses
|
|
73
|
+
// answered from the same routing the compile uses.
|
|
74
74
|
import { RETIRED_TYPES, currentType, packForType } from "./ids.mjs";
|
|
75
75
|
import { contentPackage } from "./content-package.mjs";
|
|
76
76
|
|
|
@@ -90,7 +90,7 @@ import { contentPackage } from "./content-package.mjs";
|
|
|
90
90
|
* system-neutral passes have one implementation because a JournalEntry, a
|
|
91
91
|
* Macro, a Scene and an Adventure are Foundry's documents rather than any
|
|
92
92
|
* system's — an `Adventure` does not even have a `system` field, which is why a
|
|
93
|
-
* bundle spanning two systems is two documents
|
|
93
|
+
* bundle spanning two systems is two documents.
|
|
94
94
|
*/
|
|
95
95
|
const COMPILERS = {
|
|
96
96
|
Item: Items,
|
|
@@ -102,7 +102,7 @@ const COMPILERS = {
|
|
|
102
102
|
};
|
|
103
103
|
|
|
104
104
|
/**
|
|
105
|
-
* The system-specific compilers, by the system a pack declares
|
|
105
|
+
* The system-specific compilers, by the system a pack declares.
|
|
106
106
|
*
|
|
107
107
|
* A repository feeding two systems declares one Item pack and one Actor pack
|
|
108
108
|
* per system — `harn-ensemble` has `actors-hm3` and `actors-sohl` — and each
|
|
@@ -138,7 +138,7 @@ export function compilerFor(docType, system = null) {
|
|
|
138
138
|
|
|
139
139
|
/**
|
|
140
140
|
* The art fields a note of one content type reaches its document through, and
|
|
141
|
-
* the document it reaches
|
|
141
|
+
* the document it reaches.
|
|
142
142
|
*
|
|
143
143
|
* **Derived, never listed.** A note's type routes to a document type
|
|
144
144
|
* ({@link packForType}), a document type routes to the pass that compiles it
|
|
@@ -201,7 +201,7 @@ export const packJsonDir = (name, config = loadPackConfig()) =>
|
|
|
201
201
|
* The generated JSON of **every** configured Item pack — what the actors pass
|
|
202
202
|
* reads its predefined items from.
|
|
203
203
|
*
|
|
204
|
-
* All of them, not the first: a repository may ship several Item packs
|
|
204
|
+
* All of them, not the first: a repository may ship several Item packs,
|
|
205
205
|
* and an actor's embedded items may be sourced from any of them. Finding one
|
|
206
206
|
* pack and stopping is how embedded-item resolution would silently miss every
|
|
207
207
|
* item that landed in another. Returned in configured order, which is also the
|
|
@@ -209,7 +209,7 @@ export const packJsonDir = (name, config = loadPackConfig()) =>
|
|
|
209
209
|
* order among packs of one type — and every one of them is written before the
|
|
210
210
|
* actors pass that reads them.
|
|
211
211
|
*
|
|
212
|
-
* **Scoped to one system when the caller has one
|
|
212
|
+
* **Scoped to one system when the caller has one.** A being addresses an
|
|
213
213
|
* item by `(type, shortcode)`, and that address is unique within one system and
|
|
214
214
|
* not across two: `skill:sword` is an HM3 skill *and* a SoHL skill, with
|
|
215
215
|
* different data models behind them. The reference itself is unambiguous — it
|
|
@@ -227,7 +227,7 @@ export const packJsonDir = (name, config = loadPackConfig()) =>
|
|
|
227
227
|
* @returns {string[]} Each Item pack's JSON directory. Empty when the
|
|
228
228
|
* repository ships no items at all, which is a legitimate package: the actors
|
|
229
229
|
* pass accepts an empty list and reports an item it cannot resolve per
|
|
230
|
-
* `(type, shortcode)` instead, naming the being
|
|
230
|
+
* `(type, shortcode)` instead, naming the being.
|
|
231
231
|
*/
|
|
232
232
|
export function itemPackJsonDirs(config = loadPackConfig(), system = null) {
|
|
233
233
|
return config.packs
|
|
@@ -242,7 +242,7 @@ export function itemPackJsonDirs(config = loadPackConfig(), system = null) {
|
|
|
242
242
|
* An `Adventure` carries **copies**, not references, so a bundle resolves its
|
|
243
243
|
* `contents` against compiled output rather than against the content tree — the
|
|
244
244
|
* same arrangement the actors pass has for `itemsSourceDirs`, generalised to
|
|
245
|
-
* every document class an Adventure can hold
|
|
245
|
+
* every document class an Adventure can hold.
|
|
246
246
|
*
|
|
247
247
|
* Two kinds of pack are left out, each because it holds nothing a note
|
|
248
248
|
* addresses. A **prebuilt** pack's JSON is checked in rather than compiled, so
|
|
@@ -294,7 +294,7 @@ function readsOutputOf(type) {
|
|
|
294
294
|
/**
|
|
295
295
|
* The passes to run, ordered so that each one follows the output it reads.
|
|
296
296
|
*
|
|
297
|
-
* **Declaration order is presentation, not compile order
|
|
297
|
+
* **Declaration order is presentation, not compile order.** The same
|
|
298
298
|
* `packs:` list is the manifest's `packs` array, which a consumer orders for a
|
|
299
299
|
* reader browsing compendiums; the actors pass, meanwhile, resolves each
|
|
300
300
|
* being's embedded items against the item passes' *output*. Making one list
|
|
@@ -431,7 +431,7 @@ async function generatePack(
|
|
|
431
431
|
// Which folder notes this pack turned out to hold something for. A folder
|
|
432
432
|
// materialises in every pack holding a document that references it, so the
|
|
433
433
|
// set is not knowable until the pass has compiled, which is why these
|
|
434
|
-
// documents are written after `compile()
|
|
434
|
+
// documents are written after `compile()`.
|
|
435
435
|
/** @type {Set<import("./folder-notes.mjs").FolderNote>} */
|
|
436
436
|
const referencedFolders = new Set();
|
|
437
437
|
|
|
@@ -441,7 +441,7 @@ async function generatePack(
|
|
|
441
441
|
* There is one spelling. `packFolder` names a folder **note**, resolved
|
|
442
442
|
* through the address index shared by the whole build; the `folder:`
|
|
443
443
|
* Foundry-id spelling and the per-pack `*-folders.yaml` it resolved
|
|
444
|
-
* against are retired together
|
|
444
|
+
* against are retired together, so there is no second source left
|
|
445
445
|
* for a value to come from.
|
|
446
446
|
*
|
|
447
447
|
* @param {string|null|undefined} value - As authored.
|
|
@@ -481,38 +481,38 @@ async function generatePack(
|
|
|
481
481
|
const pack = new packClass({
|
|
482
482
|
contentBase,
|
|
483
483
|
dest,
|
|
484
|
-
// The corpus this compile derived once, shared by every pass
|
|
484
|
+
// The corpus this compile derived once, shared by every pass.
|
|
485
485
|
corpus,
|
|
486
486
|
companionDests,
|
|
487
487
|
// The actors pass resolves each being's embedded items against the items
|
|
488
|
-
// passes' output.
|
|
488
|
+
// passes' output. An unwritten sibling-directory contract
|
|
489
489
|
// (`path.resolve(dest, "..", "items")`); the configured pack list names
|
|
490
490
|
// the Item packs, so the dependency is stated rather than assumed
|
|
491
|
-
//
|
|
492
|
-
// than one
|
|
491
|
+
// — and it is every Item pack, since a repository may ship more
|
|
492
|
+
// than one.
|
|
493
493
|
// Scoped to this pack's system, so a being resolves `(type, shortcode)`
|
|
494
|
-
// against its own system's catalogue and the neutral one
|
|
494
|
+
// against its own system's catalogue and the neutral one.
|
|
495
495
|
itemsSourceDirs: itemPackJsonDirs(config, system ?? null),
|
|
496
496
|
// The catalogue of a package this repository depends on but does
|
|
497
497
|
// not contain, for a repository that authors beings without
|
|
498
498
|
// holding the items they are assembled from. Cache-only: a cold
|
|
499
499
|
// cache throws naming `content-build deps fetch` rather than
|
|
500
500
|
// downloading inside a compile. Scoped to this pack's system for the
|
|
501
|
-
// reason the local half is
|
|
501
|
+
// reason the local half is: both halves answer one lookup, so a
|
|
502
502
|
// dependency shipping two systems' items would otherwise supply the
|
|
503
503
|
// wrong vocabulary's document for an address that exists in both.
|
|
504
504
|
foreignSourceDirs: foreignItemCatalogDirs(config, system ?? null),
|
|
505
505
|
// The bundles pass resolves each Adventure's members against the output
|
|
506
506
|
// of every pass that produces one. Stated from the configured pack list
|
|
507
|
-
// for the same reason `itemsSourceDirs` is
|
|
507
|
+
// for the same reason `itemsSourceDirs` is, and scoped to this
|
|
508
508
|
// pack's system so a bundle holds the catalogue it is compiled for.
|
|
509
509
|
bundleSourceDirs: bundleSourceJsonDirs(config, system ?? null),
|
|
510
510
|
folderResolver: resolver,
|
|
511
511
|
// One answer to "which files are the corpus?", from the configuration
|
|
512
|
-
// this build resolved rather than from the working directory
|
|
512
|
+
// this build resolved rather than from the working directory.
|
|
513
513
|
skipDirectories: config.skipDirectories,
|
|
514
514
|
packName: name,
|
|
515
|
-
// Which system this pack's documents are stamped for
|
|
515
|
+
// Which system this pack's documents are stamped for.
|
|
516
516
|
packSystem: system ?? null,
|
|
517
517
|
docType: type,
|
|
518
518
|
router,
|
|
@@ -525,7 +525,7 @@ async function generatePack(
|
|
|
525
525
|
// journals pack both hold it when both hold something filed in it — and
|
|
526
526
|
// every copy carries the same `_id`, which is what files a documentation
|
|
527
527
|
// journal beside the item it describes rather than in a folder that merely
|
|
528
|
-
// looks alike
|
|
528
|
+
// looks alike.
|
|
529
529
|
writeFolderNoteDocs(
|
|
530
530
|
referencedFolders,
|
|
531
531
|
folderNotes,
|
|
@@ -579,7 +579,7 @@ function writeFolderNoteDocs(referenced, folderNotes, stats, dest, documentType,
|
|
|
579
579
|
*
|
|
580
580
|
* A pack ships blank whenever every note in a full tree was rejected — by a
|
|
581
581
|
* `selects` that claims nothing, or a `pack:` that routes everything elsewhere
|
|
582
|
-
* — and the build then exits 0
|
|
582
|
+
* — and the build then exits 0. The empty-tree guard in
|
|
583
583
|
* {@link generatePacksJson} cannot see that: the tree is full, it is the
|
|
584
584
|
* *output* that is empty.
|
|
585
585
|
*
|
|
@@ -644,11 +644,11 @@ export async function generatePacksJson({ only, config = loadPackConfig() } = {}
|
|
|
644
644
|
// goes, and the first pack of each document type owns the error message for
|
|
645
645
|
// a note of that type that goes nowhere. Resolved here because the corpus
|
|
646
646
|
// below is derived against it, and the corpus is what every reader from
|
|
647
|
-
// this point on reads
|
|
647
|
+
// this point on reads.
|
|
648
648
|
const router = routerFor(config);
|
|
649
649
|
|
|
650
650
|
// The corpus every pass runs over, and the three whole-tree indexes built
|
|
651
|
-
// over it, derived **once** for the whole compile
|
|
651
|
+
// over it, derived **once** for the whole compile. Each is a pure
|
|
652
652
|
// function of (tree, scope, router), none of which varies between passes —
|
|
653
653
|
// `router` is one object, handed to all of them — so the passes were
|
|
654
654
|
// deriving the same answers over and over. Compiling `sohl` read every note
|
|
@@ -668,11 +668,11 @@ export async function generatePacksJson({ only, config = loadPackConfig() } = {}
|
|
|
668
668
|
for (const problem of corpusProblems) emitDiagnostic(problem);
|
|
669
669
|
|
|
670
670
|
// A note whose `type:` no configured pack claims compiles into nothing, and
|
|
671
|
-
// used to say nothing
|
|
671
|
+
// used to say nothing — no pass got far enough to reject it, so the
|
|
672
672
|
// silence had no owner. Asked once, of the whole configuration, because
|
|
673
673
|
// that is the only place it can be answered: a per-pass check would report
|
|
674
674
|
// every type a system deliberately does not map, which is exactly the
|
|
675
|
-
// silence
|
|
675
|
+
// silence the rule requires. Independent of `only`, since it is a fact about the
|
|
676
676
|
// configured pack list rather than about which passes this run executes.
|
|
677
677
|
const unclaimed = unclaimedNoteFindings(config, undefined, { records: corpus.records });
|
|
678
678
|
for (const finding of unclaimed) emitDiagnostic(finding);
|
|
@@ -684,7 +684,7 @@ export async function generatePacksJson({ only, config = loadPackConfig() } = {}
|
|
|
684
684
|
//
|
|
685
685
|
// A **prebuilt** pack has no pass either, and for a plainer reason: its
|
|
686
686
|
// per-document JSON is checked in. Passed over rather than compiled — which
|
|
687
|
-
// it could not be
|
|
687
|
+
// it could not be otherwise, since the only prebuilt pack in the wild
|
|
688
688
|
// holds Adventures and no compiler was registered for that document type,
|
|
689
689
|
// so the pack failed the build with "no compiler for document type". Now
|
|
690
690
|
// one is registered, and running it would wipe `build/packs-json/<name>/`
|
|
@@ -700,12 +700,12 @@ export async function generatePacksJson({ only, config = loadPackConfig() } = {}
|
|
|
700
700
|
// definition with one address, and every pass resolves against the same
|
|
701
701
|
// index. A dangling `parent` or a parent cycle is therefore reported once,
|
|
702
702
|
// as a fact about the tree, rather than once per pass that happened to walk
|
|
703
|
-
// it
|
|
703
|
+
// it.
|
|
704
704
|
let folderNotes;
|
|
705
705
|
try {
|
|
706
706
|
folderNotes = buildFolderNoteIndex(
|
|
707
707
|
collectFolderNotes(
|
|
708
|
-
// The corpus this compile derived picks the notes
|
|
708
|
+
// The corpus this compile derived picks the notes; the
|
|
709
709
|
// file supplies their frontmatter, and this is one of the few
|
|
710
710
|
// places where that distinction is load-bearing rather than
|
|
711
711
|
// incidental.
|
|
@@ -713,8 +713,8 @@ export async function generatePacksJson({ only, config = loadPackConfig() } = {}
|
|
|
713
713
|
// `collectFolderNotes` treats `fm.id` as an **authored pin**,
|
|
714
714
|
// which wins over the id it derives under the folder namespace.
|
|
715
715
|
// A record's `id` is not that: the index fills it in for every
|
|
716
|
-
// addressable note
|
|
717
|
-
// would make every folder look pinned.
|
|
716
|
+
// addressable note, so handing records straight over
|
|
717
|
+
// would make every folder look pinned. The *value*
|
|
718
718
|
// would be right either way — the index derives a folder's id
|
|
719
719
|
// under the folder namespace, so the two agree — but `derivedId`
|
|
720
720
|
// would not, and it is what tells an author whether a duplicate
|
|
@@ -734,7 +734,7 @@ export async function generatePacksJson({ only, config = loadPackConfig() } = {}
|
|
|
734
734
|
// The package this build resolved, not the ambient accessor:
|
|
735
735
|
// they are the same value in a real repository and different
|
|
736
736
|
// ones under `PACKAGE_BUILD_CONFIG`, in a worktree, or in a
|
|
737
|
-
// test
|
|
737
|
+
// test.
|
|
738
738
|
config.contentPackage,
|
|
739
739
|
),
|
|
740
740
|
);
|
|
@@ -754,7 +754,7 @@ export async function generatePacksJson({ only, config = loadPackConfig() } = {}
|
|
|
754
754
|
|
|
755
755
|
// Compile order is derived from what each pass reads, not from the order
|
|
756
756
|
// `packs:` declares — that list is also the manifest's, which a consumer
|
|
757
|
-
// orders for a reader
|
|
757
|
+
// orders for a reader.
|
|
758
758
|
const ordered = orderPassesByDependency(packs);
|
|
759
759
|
if (ordered.some((pack, index) => pack !== packs[index])) {
|
|
760
760
|
log.info(
|
package/engine/helpers.mjs
CHANGED
|
@@ -43,19 +43,19 @@ import { resolveNoteId } from "./note-ids.mjs";
|
|
|
43
43
|
import { loadForeignIndexes } from "./metadata-index.mjs";
|
|
44
44
|
// The record accessors only — deriving records reaches the pack router and the
|
|
45
45
|
// manifest emitter, which reach the compilers, which load this module. Reading
|
|
46
|
-
// a record needs none of that
|
|
46
|
+
// a record needs none of that.
|
|
47
47
|
import { authoredFrontmatter, isNoteRecord, noteFile } from "./index-records.mjs";
|
|
48
48
|
import { buildWikilinkIndex, convertWikilinks } from "./wikilinks.mjs";
|
|
49
49
|
// One vocabulary of link findings, and one message per class, so the three
|
|
50
|
-
// resolvers cannot word the same defect differently
|
|
50
|
+
// resolvers cannot word the same defect differently.
|
|
51
51
|
import { linkFindingMessage } from "./wikilink-syntax.mjs";
|
|
52
|
-
// The declared tag vocabulary
|
|
52
|
+
// The declared tag vocabulary, which is where `draft` is stated. Read
|
|
53
53
|
// from there rather than respelt, so the tag and its one reader cannot drift.
|
|
54
54
|
import { isDraftNote } from "./note-vocabulary.mjs";
|
|
55
55
|
import { expandContentTables } from "./content-tables.mjs";
|
|
56
56
|
import { positionInBody } from "./diagnostics.mjs";
|
|
57
57
|
// The pure `sohl:` frontmatter readers live in a leaf module so the item-type
|
|
58
|
-
// registry can import them without reaching back through this one
|
|
58
|
+
// registry can import them without reaching back through this one.
|
|
59
59
|
// Re-exported here so every existing importer keeps its single import path.
|
|
60
60
|
import { getFrontmatter } from "./frontmatter.mjs";
|
|
61
61
|
export {
|
|
@@ -75,9 +75,21 @@ export {
|
|
|
75
75
|
* `html: true` is long-standing and load-bearing — notes carry raw blocks — and
|
|
76
76
|
* it is also why {@link module:engine/content-icons} exists rather than an
|
|
77
77
|
* instruction to write `<i class="fa-solid …">` by hand: that would render on
|
|
78
|
-
* the two HTML surfaces and be silently dropped by the third
|
|
78
|
+
* the two HTML surfaces and be silently dropped by the third.
|
|
79
79
|
*/
|
|
80
|
-
export const md = markdownit({ html: true }).use(
|
|
80
|
+
export const md = markdownit({ html: true }).use(
|
|
81
|
+
// Resolved per render, not at import: this constant is built before any
|
|
82
|
+
// configuration is read, and a package's own icons live in the
|
|
83
|
+
// configuration. A tree with none — or a caller with no configuration to
|
|
84
|
+
// find — falls back to the shipped table.
|
|
85
|
+
iconPlugin(() => {
|
|
86
|
+
try {
|
|
87
|
+
return loadPackConfig().icons;
|
|
88
|
+
} catch {
|
|
89
|
+
return undefined;
|
|
90
|
+
}
|
|
91
|
+
}),
|
|
92
|
+
);
|
|
81
93
|
|
|
82
94
|
/**
|
|
83
95
|
* Parses a markdown file with YAML frontmatter.
|
|
@@ -86,7 +98,7 @@ export const md = markdownit({ html: true }).use(iconPlugin());
|
|
|
86
98
|
* `body` is the trimmed raw markdown after the frontmatter block, and
|
|
87
99
|
* `description` is `body` rendered to HTML. `bodyLine` / `bodyColumn` are the
|
|
88
100
|
* 1-based **file** position of the body's first character, which is what turns
|
|
89
|
-
* an offset within `body` into a position a diagnostic can name
|
|
101
|
+
* an offset within `body` into a position a diagnostic can name — see
|
|
90
102
|
* {@link positionInBody}. If the file has no frontmatter block, returns
|
|
91
103
|
* `{ frontmatter: null, body: "", description: "" }` with a warn log, and no
|
|
92
104
|
* position: there is no body to have one.
|
|
@@ -108,7 +120,7 @@ export function parseMarkdownFile(filePath) {
|
|
|
108
120
|
const body = raw.trim();
|
|
109
121
|
const description = body ? md.render(body) : "";
|
|
110
122
|
// Where the trimmed body starts in the *file*, so an offset within it can
|
|
111
|
-
// be reported as a file position
|
|
123
|
+
// be reported as a file position. The frontmatter's lines and the
|
|
112
124
|
// blank lines `trim()` removes both sit in between, and the trim can take
|
|
113
125
|
// indentation off the first line as well — hence a column, not just a line.
|
|
114
126
|
const bodyStart = content.length - raw.length + (raw.length - raw.trimStart().length);
|
|
@@ -123,13 +135,13 @@ export function parseMarkdownFile(filePath) {
|
|
|
123
135
|
* Yields `{ frontmatter, body, description, file, absPath, bodyLine,
|
|
124
136
|
* bodyColumn }` for each match — the last two from
|
|
125
137
|
* {@link parseMarkdownFile}, so a caller can report a position inside the
|
|
126
|
-
* body as a position in the file
|
|
138
|
+
* body as a position in the file.
|
|
127
139
|
* Silently skips directories that don't exist.
|
|
128
140
|
*
|
|
129
141
|
* Directory names in `skipDirectories` are ignored wherever they appear. The
|
|
130
142
|
* walk itself knows nothing about what they mean: `Templates/` is an Obsidian
|
|
131
143
|
* templater convention this repository's vault happens to use, not a property
|
|
132
|
-
* of a content tree, so it is configured rather than hard-coded
|
|
144
|
+
* of a content tree, so it is configured rather than hard-coded.
|
|
133
145
|
*
|
|
134
146
|
* @param {string} rootDir - Root of the tree to walk.
|
|
135
147
|
* @param {object} [opts]
|
|
@@ -139,7 +151,7 @@ export function parseMarkdownFile(filePath) {
|
|
|
139
151
|
/**
|
|
140
152
|
* Refuse a corpus read whose scope its caller did not state.
|
|
141
153
|
*
|
|
142
|
-
* The rule
|
|
154
|
+
* The rule in one place, so every reader of the tree refuses the same
|
|
143
155
|
* way and says so in the same words. It is shared rather than repeated because
|
|
144
156
|
* the corpus is no longer read only by {@link walkMarkdownTree}: a pass that
|
|
145
157
|
* reads the content index instead is making the identical claim about which
|
|
@@ -172,7 +184,7 @@ export function assertStatedScope(skipDirectories, who) {
|
|
|
172
184
|
* cycle. They take the records their caller already holds.
|
|
173
185
|
*
|
|
174
186
|
* That is not a workaround dressed up as a rule. A compile runs several passes
|
|
175
|
-
* over one tree, and the whole
|
|
187
|
+
* over one tree, and the whole point is that they must not each answer "which
|
|
176
188
|
* files are the corpus?" for themselves. Requiring the answer to be handed in
|
|
177
189
|
* makes the sharing structural rather than remembered.
|
|
178
190
|
*
|
|
@@ -192,7 +204,7 @@ export function assertSuppliedCorpus(records, who) {
|
|
|
192
204
|
}
|
|
193
205
|
|
|
194
206
|
export function* walkMarkdownTree(rootDir, { skipDirectories } = {}) {
|
|
195
|
-
// Stated by the caller, never resolved here.
|
|
207
|
+
// Stated by the caller, never resolved here. A default here
|
|
196
208
|
// — `loadPackConfig().skipDirectories` — read whichever configuration
|
|
197
209
|
// resolved from the working directory rather than the one the caller was
|
|
198
210
|
// working under. In an ordinary build those are the same object and nothing
|
|
@@ -200,9 +212,9 @@ export function* walkMarkdownTree(rootDir, { skipDirectories } = {}) {
|
|
|
200
212
|
// `PACKAGE_BUILD_CONFIG` names one, or when the command runs from a
|
|
201
213
|
// worktree. Six of this function's twelve callers were on that default, so
|
|
202
214
|
// "which files are the corpus?" had two answers depending on who asked
|
|
203
|
-
//
|
|
204
|
-
//
|
|
205
|
-
//
|
|
215
|
+
// — the same defect class as `entriesForNote` reading `docEntryTypes` from
|
|
216
|
+
// the ambient config rather than the passed one, which a fixture can pass
|
|
217
|
+
// on indefinitely.
|
|
206
218
|
assertStatedScope(skipDirectories, "walkMarkdownTree");
|
|
207
219
|
if (!fs.existsSync(rootDir)) return;
|
|
208
220
|
const stack = [rootDir];
|
|
@@ -234,7 +246,7 @@ export function* walkMarkdownTree(rootDir, { skipDirectories } = {}) {
|
|
|
234
246
|
/**
|
|
235
247
|
* Every position a note may state its template priority at, for one system
|
|
236
248
|
* block, in the order they answer — and whichever of them the note actually
|
|
237
|
-
* wrote
|
|
249
|
+
* wrote.
|
|
238
250
|
*
|
|
239
251
|
* Three places, in the order the migration runs. The specification calls this
|
|
240
252
|
* `data.templatePriority`; `sohl-thalorna` already writes it there on 941
|
|
@@ -290,7 +302,7 @@ function findTemplatePriority(fm, block) {
|
|
|
290
302
|
|
|
291
303
|
/**
|
|
292
304
|
* The template priority a note states, for a system that treats an unstated one
|
|
293
|
-
* as "not a template" rather than as an authoring error
|
|
305
|
+
* as "not a template" rather than as an authoring error.
|
|
294
306
|
*
|
|
295
307
|
* Reads exactly the positions {@link resolveTemplatePriority} reads, including
|
|
296
308
|
* the retiring `archetype` spelling, and refuses the same contradiction — so
|
|
@@ -324,7 +336,7 @@ export function statedTemplatePriority(fm, label, { block = "sohl" } = {}) {
|
|
|
324
336
|
|
|
325
337
|
/**
|
|
326
338
|
* Resolve the required `templatePriority` frontmatter for an Item/Actor entry
|
|
327
|
-
* (the archetype contract
|
|
339
|
+
* (the archetype contract). The property is a nullable number that
|
|
328
340
|
* authors must state explicitly:
|
|
329
341
|
* - a number → the document is a template of that priority.
|
|
330
342
|
* - `null` → the document is not a template.
|
|
@@ -333,7 +345,7 @@ export function statedTemplatePriority(fm, label, { block = "sohl" } = {}) {
|
|
|
333
345
|
*
|
|
334
346
|
* Reads the positions {@link findTemplatePriority} lists: `data.templatePriority`
|
|
335
347
|
* first — the specified home — then the system block and the top level, and
|
|
336
|
-
* finally the retiring `archetype` spelling in the same two places
|
|
348
|
+
* finally the retiring `archetype` spelling in the same two places.
|
|
337
349
|
*
|
|
338
350
|
* @param {object} fm Parsed frontmatter.
|
|
339
351
|
* @param {string} label Human-readable context for error messages.
|
|
@@ -371,8 +383,7 @@ export function resolveTemplatePriority(fm, label, { block = "sohl" } = {}) {
|
|
|
371
383
|
|
|
372
384
|
/**
|
|
373
385
|
* The value a document's `system.templatePriority` carries, from the required
|
|
374
|
-
* `templatePriority` frontmatter (
|
|
375
|
-
* #266 / sohl#1836).
|
|
386
|
+
* `templatePriority` frontmatter (`archetype` is the legacy spelling).
|
|
376
387
|
*
|
|
377
388
|
* A **schema field**, so the tri-state is written out in full rather than
|
|
378
389
|
* expressed by a key's presence: a number is a template at that priority, and
|
|
@@ -459,7 +470,7 @@ function addressesAnotherPackage(s) {
|
|
|
459
470
|
*
|
|
460
471
|
* Content frontmatter (`img` / `portrait`) authors a single path that has to
|
|
461
472
|
* work for Foundry, the knowledgebase, and the website. **Its first segment
|
|
462
|
-
* says which package owns the file
|
|
473
|
+
* says which package owns the file**, and there are exactly three
|
|
463
474
|
* answers:
|
|
464
475
|
*
|
|
465
476
|
* | Authored path starts with | Owner | Emitted |
|
|
@@ -471,7 +482,7 @@ function addressesAnotherPackage(s) {
|
|
|
471
482
|
* So `icons/relic.svg` compiles to `systems/sohl/assets/icons/relic.svg` here
|
|
472
483
|
* and to `modules/sohl-thalorna/assets/icons/relic.svg` in a module — the asset
|
|
473
484
|
* root is derived from the configuration, and is the one place `systems/sohl`
|
|
474
|
-
* is ever spelled
|
|
485
|
+
* is ever spelled. An authored
|
|
475
486
|
* `systems/sohl/assets/icons/noun/shield.svg` is left exactly as written,
|
|
476
487
|
* whichever package is compiling it.
|
|
477
488
|
*
|
|
@@ -482,13 +493,13 @@ function addressesAnotherPackage(s) {
|
|
|
482
493
|
* `assets/artwork/`, so an authored `artwork/deity.webp` would have shipped
|
|
483
494
|
* unprefixed: a 404 in Foundry, reported by nothing. That a package owns its
|
|
484
495
|
* own tree is the fact; the directory names inside it are that package's
|
|
485
|
-
* business
|
|
496
|
+
* business.
|
|
486
497
|
*
|
|
487
498
|
* **Off-install addresses pass through too**, which is the same rule rather
|
|
488
499
|
* than a fourth: a URL, a `data:` URI, or a `/`-rooted path names something no
|
|
489
500
|
* package owns. See {@link addressesAnotherPackage}.
|
|
490
501
|
*
|
|
491
|
-
* **`banner:` does not follow this rule, deliberately
|
|
502
|
+
* **`banner:` does not follow this rule, deliberately.** It is not an
|
|
492
503
|
* asset path inside a Foundry install at all: it reaches no compiled document,
|
|
493
504
|
* and its only consumer is the Hugo theme, which prefixes a relative value with
|
|
494
505
|
* `images/` and joins it onto `params.cdnBaseURL`. The two fields look alike
|
|
@@ -496,7 +507,7 @@ function addressesAnotherPackage(s) {
|
|
|
496
507
|
* the CDN serves — so they are documented apart rather than reconciled into one
|
|
497
508
|
* rule that would be true of neither.
|
|
498
509
|
*
|
|
499
|
-
* **Two empties, and they mean opposite things
|
|
510
|
+
* **Two empties, and they mean opposite things.** `null` — or an absent
|
|
500
511
|
* key, which reaches here as `undefined` — means _unset_: the note names no art
|
|
501
512
|
* and the caller's default applies. `""` means _blank on purpose_: the note
|
|
502
513
|
* names no art **and wants none**, so no default may replace it. Both come back
|
|
@@ -515,7 +526,7 @@ function addressesAnotherPackage(s) {
|
|
|
515
526
|
* is `""` (`sohl/item-fields.mjs`), resolved from the very same shared top-level
|
|
516
527
|
* key the site emitter reads as the page title — so `title: null` stringifies
|
|
517
528
|
* into the compiled document as the literal `"null"`. One key, two destinations
|
|
518
|
-
* that disagree about what empty means; see
|
|
529
|
+
* that disagree about what empty means; see.
|
|
519
530
|
*
|
|
520
531
|
* This is translation only: the default for an unset path is domain-specific
|
|
521
532
|
* (actors default differently from items, and gear differently again), so each
|
|
@@ -564,7 +575,7 @@ export function resolveName(fm, defaultValue = "Unnamed") {
|
|
|
564
575
|
* its migration shims on: a record stamped older than a shim is rewritten by it
|
|
565
576
|
* on load. Every pack once shipped `coreVersion: "14"`, which sorts *below*
|
|
566
577
|
* every v14 build and so left all shipped content permanently eligible for
|
|
567
|
-
* every v14 migration
|
|
578
|
+
* every v14 migration.
|
|
568
579
|
*
|
|
569
580
|
* **Read from the configuration, not from the shipped manifest.** It used to
|
|
570
581
|
* open `paths.packageManifest` and take `compatibility.minimum` out of it,
|
|
@@ -598,7 +609,7 @@ export function supportedCoreVersion(config = loadPackConfig()) {
|
|
|
598
609
|
/**
|
|
599
610
|
* Default `_stats` block for compiled compendium entries.
|
|
600
611
|
*
|
|
601
|
-
* Every stamped identity is configuration
|
|
612
|
+
* Every stamped identity is configuration: four compilers used to pass
|
|
602
613
|
* the same frozen `"0.6.0"` literal, and `systemId` / `lastModifiedBy` were
|
|
603
614
|
* written into this function. `coreVersion` alone is *not* configuration — it
|
|
604
615
|
* comes from {@link supportedCoreVersion}, the configured Foundry floor,
|
|
@@ -623,13 +634,12 @@ export function buildStats(systemVersion = undefined, config = loadPackConfig())
|
|
|
623
634
|
}
|
|
624
635
|
|
|
625
636
|
/**
|
|
626
|
-
* The `_stats` block for one pack, stamped with the system that pack is for
|
|
627
|
-
* (#48).
|
|
637
|
+
* The `_stats` block for one pack, stamped with the system that pack is for.
|
|
628
638
|
*
|
|
629
639
|
* **`systemId` travels with `systemVersion`.** They are one decision, so where
|
|
630
640
|
* one is omitted both are. Stamping a per-pack version against a package-wide
|
|
631
641
|
* id would emit `systemId: sohl, systemVersion: 1.6.3` on HM3 documents — a
|
|
632
|
-
* *plausible lie*, which is worse than
|
|
642
|
+
* *plausible lie*, which is worse than a missing value, because
|
|
633
643
|
* nothing about it looks wrong.
|
|
634
644
|
*
|
|
635
645
|
* Resolution, in order:
|
|
@@ -708,11 +718,11 @@ import { collectAnchors } from "./anchors.mjs";
|
|
|
708
718
|
*
|
|
709
719
|
* Each note's pack is resolved here, once, and stored on its index entry: a
|
|
710
720
|
* UUID carries a pack name, so a repository shipping several packs of one type
|
|
711
|
-
*
|
|
721
|
+
* would otherwise address every one of them as the first. A note whose
|
|
712
722
|
* declaration is unroutable is indexed against the conventional name and left
|
|
713
723
|
* for the compile pass to report — the index has no business failing a build,
|
|
714
724
|
* and the pass fails it with a far better message. The one exception is a
|
|
715
|
-
* **retired** content type
|
|
725
|
+
* **retired** content type: this walk is the first to see every
|
|
716
726
|
* note together with its path, and unlike an unroutable declaration there is
|
|
717
727
|
* no pass that would ever claim such a note and report it.
|
|
718
728
|
*
|
|
@@ -740,11 +750,11 @@ export function buildContentLinkIndex(
|
|
|
740
750
|
const fm = authoredFrontmatter(record);
|
|
741
751
|
const absPath = noteFile(contentBase, record);
|
|
742
752
|
// The id a note's document is filed under: its authored pin, or the
|
|
743
|
-
// one derived from its canonical address
|
|
744
|
-
// against the configuration this build resolved —
|
|
745
|
-
//
|
|
746
|
-
//
|
|
747
|
-
//
|
|
753
|
+
// one derived from its canonical address. Derived by the index
|
|
754
|
+
// against the configuration this build resolved — never here through
|
|
755
|
+
// `resolveNoteId(fm)` with no package, which falls back to the ambient
|
|
756
|
+
// `contentPackage()` and so to whichever configuration the working
|
|
757
|
+
// directory answers with.
|
|
748
758
|
// What is left after that is a file with **no address** — no type, or
|
|
749
759
|
// no shortcode — which is not an addressable note and has no document
|
|
750
760
|
// to link to.
|
|
@@ -752,30 +762,30 @@ export function buildContentLinkIndex(
|
|
|
752
762
|
// The first walk of every note in the tree, and the only one holding
|
|
753
763
|
// both the declared type and the file that declares it — so a note
|
|
754
764
|
// left on a retired type is reported here, by name, rather than
|
|
755
|
-
// several frames deeper with nothing to go on
|
|
765
|
+
// several frames deeper with nothing to go on.
|
|
756
766
|
assertTypeNotRetired(fm.type, absPath);
|
|
757
767
|
const base = String(record.file.name).replace(/_/g, " ");
|
|
758
768
|
docs.push({
|
|
759
769
|
type: fm.type,
|
|
760
770
|
id: fm.id,
|
|
761
771
|
// Where this note's own document lands, and where the JournalEntry
|
|
762
|
-
// its prose compiles into lands — two documents, two packs
|
|
772
|
+
// its prose compiles into lands — two documents, two packs.
|
|
763
773
|
pack: router.resolveOrNull(fm, packForType(fm.type).docType),
|
|
764
774
|
docPack: router.resolveOrNull(fm, "JournalEntry"),
|
|
765
775
|
shortcode: fm.shortcode ?? null,
|
|
766
776
|
name: fm.name?.full ?? base,
|
|
767
|
-
// Whether the note is tagged `draft
|
|
777
|
+
// Whether the note is tagged `draft`. Read from the tag
|
|
768
778
|
// vocabulary that declares it, and used for one thing: a link
|
|
769
779
|
// *into* this note renders marked. It takes no part in resolution,
|
|
770
780
|
// so the note is indexed, compiled and published as any other.
|
|
771
781
|
draft: isDraftNote(fm),
|
|
772
782
|
// The anchors this note declares, carried so the *builds* can check
|
|
773
|
-
// a `#section` link and not only the checker
|
|
783
|
+
// a `#section` link and not only the checker. A foreign
|
|
774
784
|
// anchor has always been checked, because a fetched index
|
|
775
785
|
// publishes the map; a local one was not, because the set was
|
|
776
786
|
// discarded here — the walk yields the body and nothing read it.
|
|
777
787
|
// Read from the record rather than from a second reading of the
|
|
778
|
-
// note's headings — the one-anchor-reader rule
|
|
788
|
+
// note's headings — the one-anchor-reader rule.
|
|
779
789
|
anchors: new Set((record.anchors ?? []).map((anchor) => anchor.slug)),
|
|
780
790
|
});
|
|
781
791
|
}
|
|
@@ -783,7 +793,7 @@ export function buildContentLinkIndex(
|
|
|
783
793
|
// its own content index and this build fetched the ones it depends on, so
|
|
784
794
|
// a contributor without every repository checked out resolves the same
|
|
785
795
|
// links CI does — from an artifact the producer shipped rather than a copy
|
|
786
|
-
// this repository committed
|
|
796
|
+
// this repository committed.
|
|
787
797
|
const { index: foreign, stale } = loadForeignIndexes(
|
|
788
798
|
resolved,
|
|
789
799
|
[resolved.contentPackage],
|
|
@@ -810,7 +820,7 @@ export function buildContentLinkIndex(
|
|
|
810
820
|
* target in the content tree. Every compiler funnels through this so the
|
|
811
821
|
* diagnostic text and the leave-it-alone fallback are identical everywhere.
|
|
812
822
|
*
|
|
813
|
-
* Each report names the **file, line and column** the link sits on
|
|
823
|
+
* Each report names the **file, line and column** the link sits on, so
|
|
814
824
|
* it can be opened and fixed — and so two identical links on one note are
|
|
815
825
|
* tellable apart. That needs `file` and the note's `bodyLine` / `bodyColumn`;
|
|
816
826
|
* without them the diagnostic still reports, one field shorter, rather than
|
|
@@ -875,8 +885,8 @@ export function convertNoteWikilinks(
|
|
|
875
885
|
};
|
|
876
886
|
|
|
877
887
|
for (const u of result.unresolved) {
|
|
878
|
-
// Every class fails, and every class is worded by the shared table
|
|
879
|
-
//
|
|
888
|
+
// Every class fails, and every class is worded by the shared table.
|
|
889
|
+
// The three resolvers read one authored link, so an author who
|
|
880
890
|
// ran the pack build first and the link checker second must not be told
|
|
881
891
|
// two different things about the same mistake — and a class the pack
|
|
882
892
|
// build alone knew how to describe is how they came apart before.
|
|
@@ -916,9 +926,8 @@ export function collectContentDocs(
|
|
|
916
926
|
docs.push({
|
|
917
927
|
// With its package supplied for a `WHERE … package = "…"` query —
|
|
918
928
|
// synthesised from the configuration this build resolved, since no
|
|
919
|
-
// note declares it
|
|
920
|
-
// configuration in a worktree or under `PACKAGE_BUILD_CONFIG
|
|
921
|
-
// (#243).
|
|
929
|
+
// note declares it and the ambient one is a different
|
|
930
|
+
// configuration in a worktree or under `PACKAGE_BUILD_CONFIG`.
|
|
922
931
|
fm: searchableFrontmatter(fm, resolved.contentPackage),
|
|
923
932
|
// POSIX-separated and relative to the content root — what a
|
|
924
933
|
// `path:` search term globs, on every platform.
|
|
@@ -953,7 +962,7 @@ const packLinkable = (doc) => Boolean(doc.fm?.shortcode) && Boolean(doc.fm?.type
|
|
|
953
962
|
* A table searches the whole tree, which is one package's notes and nothing
|
|
954
963
|
* else — so there is no longer a package to scope on. It used to filter, back
|
|
955
964
|
* when a tree could hold several packages' notes and `package:` said which was
|
|
956
|
-
* which; that field is retired and the filter with it
|
|
965
|
+
* which; that field is retired and the filter with it.
|
|
957
966
|
*
|
|
958
967
|
* @param {string} body - The note's markdown body.
|
|
959
968
|
* @param {object} ctx
|
|
@@ -966,7 +975,7 @@ const packLinkable = (doc) => Boolean(doc.fm?.shortcode) && Boolean(doc.fm?.type
|
|
|
966
975
|
* @returns {{markdown: string, lineMap: Array<{line: number,
|
|
967
976
|
* generated: boolean}>}} The body with every table expanded, and where each
|
|
968
977
|
* emitted line came from — which is what lets a diagnostic about the
|
|
969
|
-
* expanded body name an authored position
|
|
978
|
+
* expanded body name an authored position.
|
|
970
979
|
* @throws {Error} When a query is malformed or unsupported — the note fails to
|
|
971
980
|
* compile rather than shipping a table-shaped hole. The error carries
|
|
972
981
|
* `position`, the directive's own line.
|