@heroiclands/package-build 20.3.0 → 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 +97 -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 +225 -98
- 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 +150 -62
- 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/site-build.mjs
CHANGED
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
*/
|
|
13
13
|
|
|
14
14
|
/**
|
|
15
|
-
* Publishing a content tree as a website
|
|
15
|
+
* Publishing a content tree as a website.
|
|
16
16
|
*
|
|
17
17
|
* Compiling a content tree into compendium packs is `content-build package
|
|
18
18
|
* compile`. Publishing the *same tree* as a website was a script each consumer
|
|
@@ -60,7 +60,7 @@ import { formatUnaddressableFinding, unaddressableForeignPackages } from "./meta
|
|
|
60
60
|
import { deriveBeingInfo, isBeing } from "../sohl/being-info.mjs";
|
|
61
61
|
import { loadPackConfig } from "./pack-config.mjs";
|
|
62
62
|
import { searchableFrontmatter } from "./note-package.mjs";
|
|
63
|
-
// The corpus, from the one pass that derives it
|
|
63
|
+
// The corpus, from the one pass that derives it.
|
|
64
64
|
import { indexRecordsFor } from "./content-index.mjs";
|
|
65
65
|
import { isNoteRecord, noteFile } from "./index-records.mjs";
|
|
66
66
|
import {
|
|
@@ -81,7 +81,7 @@ const require = createRequire(import.meta.url);
|
|
|
81
81
|
* tree in reverse. Order was load-bearing here when the address index carried
|
|
82
82
|
* first-writer-wins fallbacks for a page's name, filename and slug — reversing
|
|
83
83
|
* the walk silently changed which page an ambiguous name resolved to. Those
|
|
84
|
-
* fallbacks are gone with the bare `[[Name]]` form
|
|
84
|
+
* fallbacks are gone with the bare `[[Name]]` form, so this is now
|
|
85
85
|
* ordinary reading order rather than a dependency; it is kept because a site's
|
|
86
86
|
* emitted pages should not reorder for no reason.
|
|
87
87
|
*
|
|
@@ -108,7 +108,7 @@ export function walkSiteTree(dir, skip = []) {
|
|
|
108
108
|
/**
|
|
109
109
|
* The content-tree files this build publishes from, in the order it emits them.
|
|
110
110
|
*
|
|
111
|
-
* **The corpus comes from the content index**
|
|
111
|
+
* **The corpus comes from the content index** — the same derivation the
|
|
112
112
|
* packs are compiled from — so the site and the packs cannot disagree about
|
|
113
113
|
* which files are the content. The note is still read for its `{fm, body}`: the
|
|
114
114
|
* index carries no note text, and a page *is* its text.
|
|
@@ -118,7 +118,7 @@ export function walkSiteTree(dir, skip = []) {
|
|
|
118
118
|
* "a site's emitted pages should not reorder for no reason". Records are in
|
|
119
119
|
* content-path order, which is the same set in a different sequence. Nothing
|
|
120
120
|
* downstream depends on it any more: the first-writer-wins fallbacks that made
|
|
121
|
-
* order load-bearing went with the bare `[[Name]]` form
|
|
121
|
+
* order load-bearing went with the bare `[[Name]]` form, each page is
|
|
122
122
|
* emitted to its own file at an address derived from its frontmatter, and the
|
|
123
123
|
* one place order could still show — a section's page list — is sorted by the
|
|
124
124
|
* theme. Verified rather than argued: over `sohl`'s tree the emitted mount is
|
|
@@ -182,7 +182,7 @@ export function collectContentPages(contentBase, ctx) {
|
|
|
182
182
|
// Where an addressed page publishes. A missing `base` would put *every*
|
|
183
183
|
// page at `undefined/` rather than one, and it is the caller's contract
|
|
184
184
|
// rather than a note's defect, so it throws instead of being collected as a
|
|
185
|
-
// finding
|
|
185
|
+
// finding.
|
|
186
186
|
if (typeof ctx.base !== "string" || !ctx.base) {
|
|
187
187
|
throw new TypeError(
|
|
188
188
|
"collectContentPages: `ctx.base` must be a non-empty string — it is the package address every page's URL is built on",
|
|
@@ -197,14 +197,13 @@ export function collectContentPages(contentBase, ctx) {
|
|
|
197
197
|
if (!note) continue;
|
|
198
198
|
const { fm, body } = note;
|
|
199
199
|
// The configuration's, never a note's: `package:` is retired, so every
|
|
200
|
-
// note in the tree belongs to the package this repository compiles
|
|
201
|
-
// (#56).
|
|
200
|
+
// note in the tree belongs to the package this repository compiles.
|
|
202
201
|
const pkg = ctx.contentPackage;
|
|
203
202
|
if (!ctx.packages.has(pkg) || !fm.type) continue;
|
|
204
|
-
// A homepage is addressed like any other note
|
|
203
|
+
// A homepage is addressed like any other note, but it is
|
|
205
204
|
// gathered by {@link collectHomepages} rather than here: it is the
|
|
206
205
|
// whole of a homepage-only build, which never walks the tree for
|
|
207
|
-
// content pages at all
|
|
206
|
+
// content pages at all.
|
|
208
207
|
if (isHomepage(fm)) continue;
|
|
209
208
|
|
|
210
209
|
for (const hit of frontmatterWikilinks(fm)) {
|
|
@@ -212,7 +211,7 @@ export function collectContentPages(contentBase, ctx) {
|
|
|
212
211
|
}
|
|
213
212
|
|
|
214
213
|
const name = fm.name?.full ?? path.basename(file, ".md");
|
|
215
|
-
// The page's address, and therefore its URL
|
|
214
|
+
// The page's address, and therefore its URL. `name` is the
|
|
216
215
|
// display string and nothing else: it titles the page and labels an
|
|
217
216
|
// inbound link, and moving it moves no address.
|
|
218
217
|
let slug;
|
|
@@ -230,11 +229,11 @@ export function collectContentPages(contentBase, ctx) {
|
|
|
230
229
|
fm,
|
|
231
230
|
// The note's own path on disk. Carried so a link finding can be
|
|
232
231
|
// reported as `file:line:column:` against the source an author
|
|
233
|
-
// edits, rather than against the page this build emits
|
|
232
|
+
// edits, rather than against the page this build emits.
|
|
234
233
|
file,
|
|
235
234
|
// The page's package, recorded once here so every consumer — the
|
|
236
235
|
// index's canonical keys, the table universe, the local-package set
|
|
237
|
-
// — reads one configured value and never frontmatter
|
|
236
|
+
// — reads one configured value and never frontmatter.
|
|
238
237
|
pkg,
|
|
239
238
|
body,
|
|
240
239
|
name,
|
|
@@ -251,7 +250,7 @@ export function collectContentPages(contentBase, ctx) {
|
|
|
251
250
|
// authoring folder, for grouped landings.
|
|
252
251
|
folder: path.basename(path.dirname(file)),
|
|
253
252
|
// Every page is addressed by `(type, shortcode)` at the package
|
|
254
|
-
// root, which takes no content mount
|
|
253
|
+
// root, which takes no content mount. The file is written
|
|
255
254
|
// flat under the mount — see {@link pageDestination} — and the
|
|
256
255
|
// front matter carries this `url` so Hugo publishes it at its
|
|
257
256
|
// address rather than at its path.
|
|
@@ -299,7 +298,7 @@ export function collectTreePages(tree, ctx) {
|
|
|
299
298
|
kind: "tree",
|
|
300
299
|
tree,
|
|
301
300
|
fm,
|
|
302
|
-
// As above: the source file, for a located link diagnostic
|
|
301
|
+
// As above: the source file, for a located link diagnostic.
|
|
303
302
|
file,
|
|
304
303
|
// The H1 is stripped: the page title renders it.
|
|
305
304
|
body: body.replace(/^\s*#\s+.*$\r?\n?/m, ""),
|
|
@@ -327,13 +326,13 @@ export function collectTreePages(tree, ctx) {
|
|
|
327
326
|
* it, because in homepage-only mode it is the **whole** of the site build: the
|
|
328
327
|
* content tree is never read for pages at all, so the licensing constraint two
|
|
329
328
|
* packages ship under is a property of the code path rather than of a
|
|
330
|
-
* configuration that happens to be empty
|
|
329
|
+
* configuration that happens to be empty.
|
|
331
330
|
*
|
|
332
331
|
* Returned as a list rather than as the one note there should be, because the
|
|
333
|
-
* count is what {@link checkHomepageCount} judges
|
|
332
|
+
* count is what {@link checkHomepageCount} judges — this walk reports
|
|
334
333
|
* what it found, and {@link buildSite} decides whether that is one.
|
|
335
334
|
*
|
|
336
|
-
* A homepage that declares no `shortcode` has no address
|
|
335
|
+
* A homepage that declares no `shortcode` has no address, and is
|
|
337
336
|
* reported rather than written: it is the same finding a content page's missing
|
|
338
337
|
* shortcode produces, and it has to be available in homepage-only mode, where
|
|
339
338
|
* no other gate runs.
|
|
@@ -373,13 +372,13 @@ export function collectHomepages(contentBase, ctx) {
|
|
|
373
372
|
* for, and would make homepage-only mode depend on the index, the foreign
|
|
374
373
|
* manifests and the table universe that mode exists to not build.
|
|
375
374
|
*
|
|
376
|
-
* **Verbatim is the answer
|
|
375
|
+
* **Verbatim is the answer, not a gap.** A landing's links
|
|
377
376
|
* could not be *resolved* here without giving `homepage` mode the index its
|
|
378
377
|
* licensing fence exists to not build, so they are **checked** instead:
|
|
379
378
|
* {@link auditHomepageLinks} reads the `landing:` addresses and the body's
|
|
380
379
|
* markdown links, and reports a wikilink on the page rather than resolving one.
|
|
381
380
|
*
|
|
382
|
-
* **Its destination is no longer fixed
|
|
381
|
+
* **Its destination is no longer fixed**. The file is written at the
|
|
383
382
|
* note's address, flat at the package's site root, and the page states that
|
|
384
383
|
* address as its `url` — the same separation of file from URL every other page
|
|
385
384
|
* has. Nothing is written at `/<package>/` itself: that becomes a redirect the
|
|
@@ -417,8 +416,7 @@ export function writeHomepages(outRoot, pages, config) {
|
|
|
417
416
|
* - **Addresses** next: a note that has no address — no shortcode to be
|
|
418
417
|
* addressed by, or no section to be filed under — would silently drop a page.
|
|
419
418
|
* There is no collision gate beside it: an address is `(type, shortcode)`,
|
|
420
|
-
* which is unique within a package by rule, so two pages cannot claim one URL
|
|
421
|
-
* (#181).
|
|
419
|
+
* which is unique within a package by rule, so two pages cannot claim one URL.
|
|
422
420
|
* - **Foreign manifests** last, in two steps. *Unusable* is a file this build
|
|
423
421
|
* cannot read; *unaddressable* is one it can read but cannot look anything up
|
|
424
422
|
* in — a distinction worth keeping, because the second surfaces as a pile of
|
|
@@ -434,7 +432,7 @@ export function siteGates(pages, findings, { config }) {
|
|
|
434
432
|
const out = {
|
|
435
433
|
// Always empty here: the homepage count is decided in `buildSite`
|
|
436
434
|
// before the content walk, and a failing count returns without ever
|
|
437
|
-
// reaching these gates
|
|
435
|
+
// reaching these gates. Present so every caller reads one shape.
|
|
438
436
|
homepages: [],
|
|
439
437
|
frontmatterLinks: findings.fmLinkFindings ?? [],
|
|
440
438
|
addressErrors: findings.addressFindings ?? [],
|
|
@@ -525,7 +523,7 @@ export function tableUniverse(pages) {
|
|
|
525
523
|
byPackage.get(pkg).push({
|
|
526
524
|
// Package present for a `WHERE … package = "…"` clause,
|
|
527
525
|
// synthesised rather than authored — see
|
|
528
|
-
// {@link searchableFrontmatter}
|
|
526
|
+
// {@link searchableFrontmatter}.
|
|
529
527
|
fm: searchableFrontmatter(p.fm, pkg),
|
|
530
528
|
path: p.relPath,
|
|
531
529
|
tld: p.tld,
|
|
@@ -548,7 +546,7 @@ export function tableUniverse(pages) {
|
|
|
548
546
|
* a value YAML can carry, and would abort the serializer.
|
|
549
547
|
*
|
|
550
548
|
* Everything else the section declared is passed through. That is the point of
|
|
551
|
-
* the function:
|
|
549
|
+
* the function: two writers transcribing `title` and `banner` by
|
|
552
550
|
* name, so the vocabulary lived in three places — the schema that admits a key
|
|
553
551
|
* and the two writers that copy it — and a key added to the schema alone
|
|
554
552
|
* validated cleanly and then reached no page. The *schema* is the bound worth
|
|
@@ -571,7 +569,7 @@ export function sectionFrontmatter(meta) {
|
|
|
571
569
|
/**
|
|
572
570
|
* The frontmatter a page publishes with.
|
|
573
571
|
*
|
|
574
|
-
* An authored `aliases` is retired
|
|
572
|
+
* An authored `aliases` is retired and refused before a build reaches
|
|
575
573
|
* here, which makes this a guard rather than a working path. It was Obsidian's
|
|
576
574
|
* — a list of *names* a reader might call
|
|
577
575
|
* the note, which is vault addressing and stays in the vault. Hugo reads
|
|
@@ -580,13 +578,13 @@ export function sectionFrontmatter(meta) {
|
|
|
580
578
|
* redirects of its own.
|
|
581
579
|
*
|
|
582
580
|
* A content page states its own **`url`**, which is its address rather than its
|
|
583
|
-
* path
|
|
581
|
+
* path. It is written flat under the content mount, so Hugo would
|
|
584
582
|
* otherwise publish it at `<mount><type>-<shortcode>/` rather than at the
|
|
585
583
|
* package-wide address the link manifest records — the same address, one
|
|
586
584
|
* segment too deep. So the address is stated and the mount does not reach it.
|
|
587
585
|
*
|
|
588
|
-
* **It is stated relative to the site root, and so carries no package base
|
|
589
|
-
*
|
|
586
|
+
* **It is stated relative to the site root, and so carries no package base**.
|
|
587
|
+
* Hugo resolves a `url` against `baseURL`, whose path is already where
|
|
590
588
|
* the package is served — a consumer's Hugo site *is* its package — so writing
|
|
591
589
|
* `page.url`, which carries the base for every href this build renders, wrote
|
|
592
590
|
* that base a second time and published every content page a segment too deep
|
|
@@ -595,8 +593,8 @@ export function sectionFrontmatter(meta) {
|
|
|
595
593
|
* index a wikilink resolves through, and the link manifest — composes
|
|
596
594
|
* `<base><slug>/`.
|
|
597
595
|
*
|
|
598
|
-
* A content page carries the package the build **derived
|
|
599
|
-
* declares one — `package:` is retired
|
|
596
|
+
* A content page carries the package the build **derived**. No note
|
|
597
|
+
* declares one — `package:` is retired — so the note's frontmatter alone
|
|
600
598
|
* would publish a page that does not say which package it belongs to. The
|
|
601
599
|
* emitted page is what a
|
|
602
600
|
* theme reads: `breadcrumbs.html` builds its middle crumb from
|
|
@@ -623,7 +621,7 @@ export function pageFrontmatter(page, { readmeSections = {}, decorate }) {
|
|
|
623
621
|
// `package: undefined` is not a value YAML can carry.
|
|
624
622
|
...(page.pkg ? { package: page.pkg } : {}),
|
|
625
623
|
// The address, stated — site-root relative, because Hugo prefixes
|
|
626
|
-
// the site's own base to it
|
|
624
|
+
// the site's own base to it. `slug` is written beside it
|
|
627
625
|
// because it is the last segment of that address and Hugo's own key
|
|
628
626
|
// for one; it decides nothing while `url` is present, but a page
|
|
629
627
|
// that carried only `url` would report a slug Hugo had inferred
|
|
@@ -651,9 +649,9 @@ export function pageFrontmatter(page, { readmeSections = {}, decorate }) {
|
|
|
651
649
|
/**
|
|
652
650
|
* Where a page is written, relative to the output root.
|
|
653
651
|
*
|
|
654
|
-
* **Flat, under the mount, named by its address
|
|
652
|
+
* **Flat, under the mount, named by its address**. A content page's URL
|
|
655
653
|
* is its address — `/<package>/<type>-<shortcode>/` — and the file is now named
|
|
656
|
-
* the same way, so the two agree.
|
|
654
|
+
* the same way, so the two agree. Filing it into `<section>/` so that
|
|
657
655
|
* Hugo would read a section off its path; a section appears in no address, and
|
|
658
656
|
* a directory chosen only to satisfy a rendering engine's idea of what a
|
|
659
657
|
* section is has no business in the note format.
|
|
@@ -661,7 +659,7 @@ export function pageFrontmatter(page, { readmeSections = {}, decorate }) {
|
|
|
661
659
|
* The name is the *whole* address rather than a section-relative half of it, so
|
|
662
660
|
* two types cannot fight over one file: a `doc` note's `subType` may be spelled
|
|
663
661
|
* the same as another note's `type`, and `doc-gear.md` and `weapongear-gear.md`
|
|
664
|
-
* are distinct whatever the sections
|
|
662
|
+
* are distinct whatever the sections.
|
|
665
663
|
*
|
|
666
664
|
* **A `trees` entry is the exception, and always was.** Those pages preserve
|
|
667
665
|
* their source layout below a named section — they are a book with chapters,
|
|
@@ -714,8 +712,8 @@ export function renderPages(pages, options) {
|
|
|
714
712
|
for (const page of pages) {
|
|
715
713
|
// The page's path in the tree an author edits: below the content root
|
|
716
714
|
// for a content note, below the tree's own root for a `trees` page. It
|
|
717
|
-
//
|
|
718
|
-
// which named a directory that was never the note's
|
|
715
|
+
// is not composed as `<section>/<basename>` for a content note,
|
|
716
|
+
// which named a directory that was never the note's.
|
|
719
717
|
const src = page.relPath ?? page.rel ?? page.base;
|
|
720
718
|
const ctx = wikiContext(index, {
|
|
721
719
|
src,
|
|
@@ -765,7 +763,7 @@ export function renderPages(pages, options) {
|
|
|
765
763
|
/**
|
|
766
764
|
* Writes the Hugo sections a published tree declares.
|
|
767
765
|
*
|
|
768
|
-
* **This is where a section lives now, and the only place
|
|
766
|
+
* **This is where a section lives now, and the only place**. A content
|
|
769
767
|
* note carries none: it is addressed by `(type, shortcode)` and emitted flat
|
|
770
768
|
* under the mount, so nothing a page does creates a directory. A site that wants
|
|
771
769
|
* `/<package>/<prefix><section>/` to answer — with a title, a hero, and whatever
|
|
@@ -953,7 +951,7 @@ export function buildSite({ config, outRoot, sqlTables } = {}) {
|
|
|
953
951
|
const resolved = config ?? loadPackConfig();
|
|
954
952
|
const site = resolved.site;
|
|
955
953
|
const scheme = resolved.publish.address;
|
|
956
|
-
// Homepage-only or homepage-plus-content
|
|
954
|
+
// Homepage-only or homepage-plus-content. The floor is the homepage,
|
|
957
955
|
// so this decides whether the *content* surfaces are published, never
|
|
958
956
|
// whether anything is.
|
|
959
957
|
const publishesContent = publishesContentPages(resolved);
|
|
@@ -970,7 +968,7 @@ export function buildSite({ config, outRoot, sqlTables } = {}) {
|
|
|
970
968
|
// `<out>/<prefix>/<section>/` publishes at `<base><prefix><section>/`.
|
|
971
969
|
// Resolved against the repository root for the same reason every configured
|
|
972
970
|
// path is — so the build reads and writes the same places whatever
|
|
973
|
-
// directory it was launched from
|
|
971
|
+
// directory it was launched from.
|
|
974
972
|
const outBase = resolveOutputRoot(resolved.rootDir, site.out);
|
|
975
973
|
const out =
|
|
976
974
|
outRoot ? path.resolve(outRoot)
|
|
@@ -988,13 +986,13 @@ export function buildSite({ config, outRoot, sqlTables } = {}) {
|
|
|
988
986
|
const ctx = {
|
|
989
987
|
packages,
|
|
990
988
|
// The package every note in the tree belongs to. `package:` is
|
|
991
|
-
// retired, so this is the only source of it
|
|
989
|
+
// retired, so this is the only source of it.
|
|
992
990
|
contentPackage: resolved.contentPackage,
|
|
993
991
|
skipDirectories: resolved.skipDirectories,
|
|
994
992
|
config: resolved,
|
|
995
993
|
// The corpus, derived once for this build and handed to both
|
|
996
994
|
// collectors — the homepage pass and the content pass read one answer
|
|
997
|
-
// about which files the content is, rather than walking twice
|
|
995
|
+
// about which files the content is, rather than walking twice.
|
|
998
996
|
records: indexRecordsFor({
|
|
999
997
|
contentBase: resolved.paths.content,
|
|
1000
998
|
config: resolved,
|
|
@@ -1002,7 +1000,7 @@ export function buildSite({ config, outRoot, sqlTables } = {}) {
|
|
|
1002
1000
|
}),
|
|
1003
1001
|
// Where the package is served, which is where an addressed page
|
|
1004
1002
|
// publishes: an address is `(type, shortcode)`, a package-wide
|
|
1005
|
-
// identity that takes no content mount
|
|
1003
|
+
// identity that takes no content mount.
|
|
1006
1004
|
base,
|
|
1007
1005
|
mount,
|
|
1008
1006
|
scheme,
|
|
@@ -1012,7 +1010,7 @@ export function buildSite({ config, outRoot, sqlTables } = {}) {
|
|
|
1012
1010
|
const homepages = collected.pages;
|
|
1013
1011
|
|
|
1014
1012
|
// Exactly one homepage, and checked here — before the output tree is
|
|
1015
|
-
// cleared and before either mode branches
|
|
1013
|
+
// cleared and before either mode branches. Before the clear, because
|
|
1016
1014
|
// a gate that fired after it would have destroyed a good site to report a
|
|
1017
1015
|
// bad tree. Before the branch, because the requirement does not vary by
|
|
1018
1016
|
// mode: `publish.site` chooses whether the *content* surfaces are
|
|
@@ -1026,7 +1024,7 @@ export function buildSite({ config, outRoot, sqlTables } = {}) {
|
|
|
1026
1024
|
contentPackage: resolved.contentPackage,
|
|
1027
1025
|
});
|
|
1028
1026
|
// A homepage that cannot be addressed is reported in the same place, and
|
|
1029
|
-
// reaches homepage-only mode — which runs no other gate at all
|
|
1027
|
+
// reaches homepage-only mode — which runs no other gate at all.
|
|
1030
1028
|
const homepageFindings =
|
|
1031
1029
|
counted.length ? counted : (
|
|
1032
1030
|
collected.addressFindings.map((f) => ({
|
|
@@ -1051,7 +1049,7 @@ export function buildSite({ config, outRoot, sqlTables } = {}) {
|
|
|
1051
1049
|
|
|
1052
1050
|
// Homepage-only stops here, and stopping is the point: nothing below reads
|
|
1053
1051
|
// the content tree for pages, so `sohl-kethira-basic` and `harn-adventures`
|
|
1054
|
-
// cannot publish one whatever else their `site:` block declares
|
|
1052
|
+
// cannot publish one whatever else their `site:` block declares.
|
|
1055
1053
|
if (!publishesContent) {
|
|
1056
1054
|
return {
|
|
1057
1055
|
gates: emptyGates(),
|
|
@@ -1070,7 +1068,7 @@ export function buildSite({ config, outRoot, sqlTables } = {}) {
|
|
|
1070
1068
|
const pages = [...content.pages];
|
|
1071
1069
|
const fmLinkFindings = [...content.fmLinkFindings];
|
|
1072
1070
|
|
|
1073
|
-
// The homepage is **indexed but not rendered
|
|
1071
|
+
// The homepage is **indexed but not rendered**. Now that it has an
|
|
1074
1072
|
// address, `[[homepage-root|Text]]` is an ordinary wikilink and has to
|
|
1075
1073
|
// resolve to the page the build publishes — which means the address index
|
|
1076
1074
|
// must hold it. It still takes no part in `renderPages`: a homepage is
|
package/engine/site-index.mjs
CHANGED
|
@@ -31,14 +31,14 @@
|
|
|
31
31
|
* `type/shortcode` are unique by construction, so they always resolve.
|
|
32
32
|
* `type/shortcode` is the authored form; the canonical
|
|
33
33
|
* `package-system-type-shortcode` is set alongside it, which is what a
|
|
34
|
-
* cross-package link and every merged foreign entry use
|
|
34
|
+
* cross-package link and every merged foreign entry use. A
|
|
35
35
|
* cross-package target states its package and usually not its system, so it is
|
|
36
36
|
* matched by the segments it supplies rather than fetched by an exact key —
|
|
37
37
|
* one hit resolves, and anything else is a finding.
|
|
38
38
|
*
|
|
39
|
-
* **A page's *name* is not a key
|
|
39
|
+
* **A page's *name* is not a key**. It was, as one of a set of
|
|
40
40
|
* collision-aware fallbacks a bare `[[Name]]` was looked up in — which is what
|
|
41
|
-
* made two pages of one type forbidden from sharing a display name
|
|
41
|
+
* made two pages of one type forbidden from sharing a display name. The
|
|
42
42
|
* bare form is retired, so the fallbacks answer nothing and the constraint they
|
|
43
43
|
* imposed is gone with them.
|
|
44
44
|
*
|
|
@@ -57,7 +57,7 @@ import { systemOf } from "./document-subtypes.mjs";
|
|
|
57
57
|
import { KNOWN_DOCUMENT_SUBTYPE_MAPS } from "./note-claims.mjs";
|
|
58
58
|
import { hasDocEntry } from "./item-docs.mjs";
|
|
59
59
|
import { contentPackage } from "./content-package.mjs";
|
|
60
|
-
// The declared tag vocabulary
|
|
60
|
+
// The declared tag vocabulary, which is where `draft` is stated.
|
|
61
61
|
import { isDraftNote } from "./note-vocabulary.mjs";
|
|
62
62
|
|
|
63
63
|
/**
|
|
@@ -74,8 +74,7 @@ import { isDraftNote } from "./note-vocabulary.mjs";
|
|
|
74
74
|
* @property {string} [sec] The Hugo section a **tree** page is filed under, and
|
|
75
75
|
* the first segment of the `<sec>/<slug>` address it
|
|
76
76
|
* is reachable by. A content page has none: it is
|
|
77
|
-
* addressed by `(type, shortcode)` and emitted flat
|
|
78
|
-
* (#204).
|
|
77
|
+
* addressed by `(type, shortcode)` and emitted flat.
|
|
79
78
|
* @property {string} base Source file's basename, e.g. `Climbing.md`.
|
|
80
79
|
* @property {string} url The page's published address.
|
|
81
80
|
* @property {boolean} [isReadme] Whether a tree page is its directory's
|
|
@@ -89,7 +88,7 @@ import { isDraftNote } from "./note-vocabulary.mjs";
|
|
|
89
88
|
* @property {Map<string, {url: string, name?: string, draft?: boolean}>} index
|
|
90
89
|
* Address → page. `draft` says the page
|
|
91
90
|
* carries the `draft` tag, which marks a
|
|
92
|
-
* link *into* it
|
|
91
|
+
* link *into* it.
|
|
93
92
|
* @property {Set<string>} ambiguous Short addresses claimed by two
|
|
94
93
|
* packages, and so deliberately absent
|
|
95
94
|
* from `index`.
|
|
@@ -176,27 +175,27 @@ export function buildSiteIndex(entries, { foreignIndex = new Map() } = {}) {
|
|
|
176
175
|
// Every package an address may name: this build's own, plus every one a
|
|
177
176
|
// vendored manifest speaks for. Without it `readQualifier` cannot see the
|
|
178
177
|
// leading package segment of a canonical address, and `kethira-place-x`
|
|
179
|
-
// reads as the unknown type `kethira
|
|
178
|
+
// reads as the unknown type `kethira`.
|
|
180
179
|
const ownPackage = contentPackage();
|
|
181
180
|
const packages = new Set(ownPackage ? [ownPackage] : []);
|
|
182
181
|
|
|
183
182
|
// `section/slug` is unique by construction, and is now a **tree** page's
|
|
184
183
|
// address: a `trees` entry keeps its source layout below a named section,
|
|
185
184
|
// so `dev-docs/testing` is how one is cited. A content page carries no
|
|
186
|
-
// section at all
|
|
185
|
+
// section at all and is addressed by `(type, shortcode)` below —
|
|
187
186
|
// indexing it here as well would have written `weapongear/weapongear-dagger`,
|
|
188
187
|
// a key no author could reasonably write.
|
|
189
188
|
//
|
|
190
189
|
// A page's name, filename and bare slug were indexed here too, as
|
|
191
190
|
// collision-aware fallbacks the bare `[[Name]]` form looked up; that form is
|
|
192
191
|
// retired and nothing consults them, so they are gone and with them the rule
|
|
193
|
-
// that two pages of a type may not share a name
|
|
192
|
+
// that two pages of a type may not share a name.
|
|
194
193
|
for (const e of entries) {
|
|
195
194
|
if (typeof e.sec !== "string" || !e.sec) continue;
|
|
196
195
|
sections.add(e.sec.toLowerCase());
|
|
197
196
|
// `draft` rides on every key a page is addressable by, because a link
|
|
198
|
-
// into a draft note renders marked whichever of them the author wrote
|
|
199
|
-
//
|
|
197
|
+
// into a draft note renders marked whichever of them the author wrote.
|
|
198
|
+
// It decides nothing about resolution: the page is indexed and
|
|
200
199
|
// published as any other.
|
|
201
200
|
index.set(`${e.sec}/${e.slug}`.toLowerCase(), {
|
|
202
201
|
url: e.url,
|
|
@@ -243,10 +242,10 @@ export function buildSiteIndex(entries, { foreignIndex = new Map() } = {}) {
|
|
|
243
242
|
// The canonical address alongside the short one. The short form
|
|
244
243
|
// stays because a bare `[[skill-lang]]` defaults to the citing
|
|
245
244
|
// note's own package and must keep resolving unchanged; the
|
|
246
|
-
// canonical form is what cross-package links use
|
|
245
|
+
// canonical form is what cross-package links use.
|
|
247
246
|
// The page's package is the configured one — the site collection
|
|
248
247
|
// resolves it and records it as `pkg`. Never read out of
|
|
249
|
-
// frontmatter: `package:` is retired
|
|
248
|
+
// frontmatter: `package:` is retired.
|
|
250
249
|
index.set(
|
|
251
250
|
canonicalKey(
|
|
252
251
|
e.pkg ?? ownPackage,
|
|
@@ -258,7 +257,7 @@ export function buildSiteIndex(entries, { foreignIndex = new Map() } = {}) {
|
|
|
258
257
|
);
|
|
259
258
|
if (e.pkg) packages.add(e.pkg);
|
|
260
259
|
// In Foundry an item and its documentation are two documents, so
|
|
261
|
-
// `skill/wpnc` and `docskill/wpnc` are two UUIDs
|
|
260
|
+
// `skill/wpnc` and `docskill/wpnc` are two UUIDs. Here the
|
|
262
261
|
// item note renders as one page which *is* its documentation, so
|
|
263
262
|
// the two qualifiers alias one URL and an anchor on either is an
|
|
264
263
|
// ordinary in-page anchor. One authored link, correct in both
|
|
@@ -268,7 +267,7 @@ export function buildSiteIndex(entries, { foreignIndex = new Map() } = {}) {
|
|
|
268
267
|
contentTypes.add(`doc${type}`);
|
|
269
268
|
index.set(`doc${type}/${shortcode}`.toLowerCase(), value);
|
|
270
269
|
// The canonical documentation address too, so the page answers
|
|
271
|
-
// to the address a bare prose link expands to
|
|
270
|
+
// to the address a bare prose link expands to: body
|
|
272
271
|
// prose is under no system block, so it defaults to `none`, and
|
|
273
272
|
// a system-bearing type's `none` address is its `doc<type>`
|
|
274
273
|
// one. In Foundry that names a second document; here it names
|
|
@@ -316,7 +315,7 @@ export function buildSiteIndex(entries, { foreignIndex = new Map() } = {}) {
|
|
|
316
315
|
*
|
|
317
316
|
* There is deliberately **no `manifestsComplete`**. It used to let a resolver
|
|
318
317
|
* soften an unresolved cross-package address while any package's manifest was
|
|
319
|
-
* missing;
|
|
318
|
+
* missing; the softening is retired, since the pack compilers and the link
|
|
320
319
|
* checker never had it and one authored link must not get two verdicts. A
|
|
321
320
|
* caller still passing it is ignored rather than obeyed.
|
|
322
321
|
*/
|
|
@@ -329,7 +328,7 @@ export function wikiContext(built, { src, file, type = null, errors, foreignInde
|
|
|
329
328
|
contentTypes: built.contentTypes,
|
|
330
329
|
packages: built.packages,
|
|
331
330
|
// The package a link written on this page defaults to when it names
|
|
332
|
-
// none
|
|
331
|
+
// none. Taken from the resolved configuration, the same source
|
|
333
332
|
// the index's own addresses are built from, so a bare link cannot
|
|
334
333
|
// resolve against a package the index never keyed.
|
|
335
334
|
contentPackage: contentPackage(),
|
package/engine/sql-tables.mjs
CHANGED
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
* The tables were written in Dataview's query language, chosen when the corpus
|
|
18
18
|
* lived in an Obsidian vault so a table rendered live while authoring. The vault
|
|
19
19
|
* is gone, and what remained was a hand-written parser and evaluator for someone
|
|
20
|
-
* else's language, kept faithful to semantics nothing checked it against
|
|
20
|
+
* else's language, kept faithful to semantics nothing checked it against.
|
|
21
21
|
*
|
|
22
22
|
* **The query is real SQL, run by DuckDB** — not a dialect maintained here. That
|
|
23
23
|
* is the whole point: a partial reimplementation would accept some valid SQL and
|
|
@@ -39,7 +39,7 @@ import path from "node:path";
|
|
|
39
39
|
|
|
40
40
|
import { FENCE_LINE, parseHeaderArgs } from "./code-fences.mjs";
|
|
41
41
|
import { parseMarkdownFile } from "./helpers.mjs";
|
|
42
|
-
// The record accessors only — see `engine/index-records.mjs
|
|
42
|
+
// The record accessors only — see `engine/index-records.mjs`.
|
|
43
43
|
import { isNoteRecord, noteFile } from "./index-records.mjs";
|
|
44
44
|
|
|
45
45
|
/** Rendered in a cell whose value is absent. */
|
|
@@ -64,7 +64,7 @@ const escapeCell = (text) =>
|
|
|
64
64
|
* Every `sql` fence in a markdown body, with the position each occupies.
|
|
65
65
|
*
|
|
66
66
|
* Positions are 0-based lines into the body as given, which is what a diagnostic
|
|
67
|
-
* about a directive needs
|
|
67
|
+
* about a directive needs and what the expander uses to splice results
|
|
68
68
|
* back in.
|
|
69
69
|
*
|
|
70
70
|
* @param {string} markdown - The note body, frontmatter already stripped.
|
|
@@ -98,12 +98,12 @@ export function findSqlBlocks(markdown) {
|
|
|
98
98
|
query: lines.slice(i + 1, close).join("\n"),
|
|
99
99
|
// `:allow-empty` says a table selecting nothing is intended.
|
|
100
100
|
// Spelled on the fence rather than in the query because it is a
|
|
101
|
-
// statement about this directive and not part of SQL
|
|
101
|
+
// statement about this directive and not part of SQL.
|
|
102
102
|
allowEmpty: args["allow-empty"] === true,
|
|
103
103
|
sectionLevel: Number.isInteger(level) && level >= 1 && level <= 6 ? level : 2,
|
|
104
104
|
// Every header argument, so a caller can read one this module makes
|
|
105
105
|
// no use of — the point of taking a real grammar rather than a
|
|
106
|
-
// regex per property
|
|
106
|
+
// regex per property.
|
|
107
107
|
args,
|
|
108
108
|
block: lines.slice(i, close + 1).join("\n"),
|
|
109
109
|
});
|
|
@@ -140,7 +140,7 @@ export function findSqlBlocks(markdown) {
|
|
|
140
140
|
* It is `FROM` rather than a fence property naming a file, for two reasons. A
|
|
141
141
|
* path in authored content is a build artifact's name written into the corpus,
|
|
142
142
|
* so renaming the artifact means sweeping every note that cites it — the
|
|
143
|
-
* coupling
|
|
143
|
+
* coupling the corpus move exists to undo. And *which dataset a query reads* is what
|
|
144
144
|
* `FROM` is for: the same rule that keeps `_ref` and `_section` ordinary SQL,
|
|
145
145
|
* visible where an author is already looking, rather than fence options.
|
|
146
146
|
*
|
|
@@ -184,7 +184,7 @@ export async function openNotesDatabase(records, { dir, dependencies = [] } = {}
|
|
|
184
184
|
return {
|
|
185
185
|
// From the result's schema, not from the rows: a query that
|
|
186
186
|
// selects nothing still has columns, and reporting *that* is
|
|
187
|
-
// what tells a stale query from an empty category
|
|
187
|
+
// what tells a stale query from an empty category.
|
|
188
188
|
columnNames: reader.columnNames(),
|
|
189
189
|
rows: reader
|
|
190
190
|
.getRowObjects()
|
|
@@ -303,6 +303,11 @@ function cellText(value, column) {
|
|
|
303
303
|
* authored `ORDER BY` decides the section order too, and one query replaces the
|
|
304
304
|
* forty near-identical blocks the language used to require.
|
|
305
305
|
*
|
|
306
|
+
* **A result selecting nothing still renders its header and rule.** The finding
|
|
307
|
+
* is the point, not withholding the output: an authored heading with an empty
|
|
308
|
+
* table under it says the query ran and matched nothing, where a heading with
|
|
309
|
+
* *nothing* under it reads as a page that failed to build.
|
|
310
|
+
*
|
|
306
311
|
* @param {{columns: string[], rows: object[]}} result - From
|
|
307
312
|
* {@link runSqlQuery}.
|
|
308
313
|
* @param {object} [opts]
|
|
@@ -315,7 +320,9 @@ export function renderSqlTable(result, { linkable = () => true, sectionLevel = 2
|
|
|
315
320
|
const { columns, rows } = result;
|
|
316
321
|
if (!columns.length) throw new Error("query selects no rendered column");
|
|
317
322
|
|
|
318
|
-
|
|
323
|
+
// One group with no rows, so the header and the alignment rule are emitted
|
|
324
|
+
// for a result that selects nothing.
|
|
325
|
+
const groups = rows.length ? [] : [{ section: null, rows: [] }];
|
|
319
326
|
for (const row of rows) {
|
|
320
327
|
const section =
|
|
321
328
|
Object.hasOwn(row, RENDER_ALIASES.section) ?
|
|
@@ -362,7 +369,7 @@ export function renderSqlTable(result, { linkable = () => true, sectionLevel = 2
|
|
|
362
369
|
* {@link module:engine/content-tables.expandContentTables} is not — nor should
|
|
363
370
|
* it become so: two of its three callers are synchronous, and one of those,
|
|
364
371
|
* `renderPages`, is exported. Preparing the results first keeps every one of
|
|
365
|
-
* those signatures intact, and it is the shape
|
|
372
|
+
* those signatures intact, and it is the shape the index is heading for anyway —
|
|
366
373
|
* the corpus enumerated once, each pass reading the answer rather than
|
|
367
374
|
* deriving it again.
|
|
368
375
|
*
|
|
@@ -417,7 +424,8 @@ export async function prepareSqlTables(db, sources, { linkable } = {}) {
|
|
|
417
424
|
* Answer every `sql` directive in a content tree.
|
|
418
425
|
*
|
|
419
426
|
* The one entry point each pass uses, so the compiler, the link checker and the
|
|
420
|
-
* site build cannot disagree about what a table selects — the failure mode
|
|
427
|
+
* site build cannot disagree about what a table selects — the failure mode the
|
|
428
|
+
* shared index
|
|
421
429
|
* describes, where N passes each derive the corpus their own way.
|
|
422
430
|
*
|
|
423
431
|
* **Nothing is opened for a tree with no `sql` directive.** The corpus is still
|
|
@@ -431,7 +439,7 @@ export async function prepareSqlTables(db, sources, { linkable } = {}) {
|
|
|
431
439
|
* @param {readonly string[]} [opts.skipDirectories] - The walk's scope.
|
|
432
440
|
* @param {object[]} [opts.records] - Index records the caller already derived.
|
|
433
441
|
* A command that also builds a link index holds them already, and deriving
|
|
434
|
-
* them twice is the duplicated-corpus failure
|
|
442
|
+
* them twice is the duplicated-corpus failure this closes.
|
|
435
443
|
* @returns {Promise<Map<string, object[]>|undefined>} Results by note path, or
|
|
436
444
|
* nothing when the tree has no such directive.
|
|
437
445
|
*/
|
|
@@ -444,7 +452,7 @@ export async function prepareTreeSqlTables(contentBase, { config, skipDirectorie
|
|
|
444
452
|
const indexRecords = records ?? indexRecordsFor({ contentBase, config, skipDirectories });
|
|
445
453
|
|
|
446
454
|
// Which notes carry a directive, discovered over the same corpus every
|
|
447
|
-
// other pass reads rather than over a walk of this one's own
|
|
455
|
+
// other pass reads rather than over a walk of this one's own. The
|
|
448
456
|
// body has to be read to find a fence — the index carries no note text —
|
|
449
457
|
// but *which files* to read is no longer a second answer.
|
|
450
458
|
//
|
|
@@ -464,7 +472,7 @@ export async function prepareTreeSqlTables(contentBase, { config, skipDirectorie
|
|
|
464
472
|
// never ships a link the wikilink pass will then report dead.
|
|
465
473
|
const addresses = new Set(indexRecords.map((record) => record.address?.slug).filter(Boolean));
|
|
466
474
|
// Each declared dependency's published index, attached as its own schema so
|
|
467
|
-
// a table can read `FROM <package>.notes
|
|
475
|
+
// a table can read `FROM <package>.notes`. Imported here for the
|
|
468
476
|
// same cycle reason the index is, and tolerated when absent: a tree with no
|
|
469
477
|
// `sql` directive never reaches this line, and one whose dependency has not
|
|
470
478
|
// been fetched already fails earlier with a message naming the fetch.
|
|
@@ -16,7 +16,8 @@
|
|
|
16
16
|
* questions asked of the *list* rather than of any one map.
|
|
17
17
|
*
|
|
18
18
|
* One frozen list, and the two lookups that need to choose among its members
|
|
19
|
-
* before a map can be consulted at all. It
|
|
19
|
+
* before a map can be consulted at all. It sits here rather than in
|
|
20
|
+
* `note-claims.mjs`,
|
|
20
21
|
* which is where the *questions* asked of it live — but that module imports
|
|
21
22
|
* half the engine, so anything needing the bare list had to take all of it, and
|
|
22
23
|
* `helpers.mjs` could not take it at all: `note-claims.mjs` imports
|
|
@@ -42,7 +43,7 @@ import { HM3_DOCUMENT_SUBTYPES } from "../hm3/document-subtypes.mjs";
|
|
|
42
43
|
/**
|
|
43
44
|
* The note-type → document-subtype maps this toolchain ships.
|
|
44
45
|
*
|
|
45
|
-
* Two, since `hm3/` landed
|
|
46
|
+
* Two, since `hm3/` landed — and it joined this list rather than the
|
|
46
47
|
* claim table growing a second copy of the same fact, which is what the list
|
|
47
48
|
* was for.
|
|
48
49
|
*
|
|
@@ -72,7 +73,7 @@ export const KNOWN_DOCUMENT_SUBTYPE_MAPS = Object.freeze([
|
|
|
72
73
|
* {@link KNOWN_DOCUMENT_SUBTYPE_MAPS} exists rather than a hand-kept table.
|
|
73
74
|
*
|
|
74
75
|
* It exists because an actor note publishes documentation like every other
|
|
75
|
-
* system-bearing note
|
|
76
|
+
* system-bearing note. `docEntryTypes` was `itemTypes` plus `macro` and
|
|
76
77
|
* the map types, which left a being as the one system-bearing note with no
|
|
77
78
|
* `none` address — nothing a prose link could land on, since its only address
|
|
78
79
|
* named the Actor. Composing that set needs to know which types are actors, and
|
|
@@ -111,7 +112,7 @@ export function subtypeMapFor(system) {
|
|
|
111
112
|
* translation the *schema* check needs, and the reason it needs one.
|
|
112
113
|
*
|
|
113
114
|
* A schema artifact is keyed by document subtype; a field declaration is keyed
|
|
114
|
-
* by note type. Those
|
|
115
|
+
* by note type. Those would be the same string but for three renames, and
|
|
115
116
|
* a check that went on joining them by name simply stopped reporting on
|
|
116
117
|
* `armorgear` — a warning that vanishes is worse than one that fires, because
|
|
117
118
|
* nothing says it went.
|