@heroiclands/package-build 22.15.0 → 22.16.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 +103 -0
- package/README.md +39 -678
- package/bin/{content-build.mjs → content-commands.mjs} +197 -118
- package/bin/package-build.mjs +303 -42
- package/bin/report.mjs +1 -1
- package/ci/ci-docker.mjs +6 -3
- package/ci/ci-steps.mjs +6 -4
- package/ci/content-format-reference.mjs +106 -0
- package/config.mjs +22 -2
- package/content-config.mjs +74 -134
- package/docs/api.md +294 -234
- package/docs/authoring/assets.md +31 -0
- package/docs/authoring/dates-and-calendars.md +33 -0
- package/docs/authoring/first-note.md +48 -0
- package/docs/authoring/frontmatter.md +40 -0
- package/docs/authoring/links-and-markup.md +137 -0
- package/docs/commands.md +631 -162
- package/docs/configuration.md +176 -140
- package/docs/content-format.md +50 -4229
- package/docs/design.md +32 -0
- package/docs/diagnostics.md +29 -22
- package/docs/getting-started.md +21 -877
- package/docs/guides/book.md +153 -0
- package/docs/guides/e2e.md +143 -0
- package/docs/guides/icons.md +126 -0
- package/docs/guides/localization.md +120 -0
- package/docs/guides/module-project.md +915 -0
- package/docs/guides/packs.md +167 -0
- package/docs/guides/reference-pages.md +148 -0
- package/docs/guides/release-and-deploy.md +114 -0
- package/docs/guides/site.md +152 -0
- package/docs/guides/system-project.md +45 -0
- package/docs/index.md +16 -0
- package/docs/project-setup.md +54 -35
- package/docs/reference/format-details.md +4420 -0
- package/docs/reference/note-types.md +531 -0
- package/engine/address-render.mjs +26 -0
- package/engine/address.mjs +3 -20
- package/engine/anchors.mjs +15 -6
- package/engine/asset-bindings.mjs +6 -2
- package/engine/base-compiler.mjs +47 -11
- package/engine/being-measurements.mjs +70 -0
- package/engine/calendar-artifacts.mjs +1 -1
- package/engine/calendar-choice.mjs +32 -0
- package/engine/calendar-human.mjs +71 -0
- package/engine/calendar-notes.mjs +41 -3
- package/engine/calendars.mjs +8 -1
- package/engine/changelog-lint.mjs +2 -2
- package/engine/code-fences.mjs +23 -97
- package/engine/compile-corpus.mjs +6 -7
- package/engine/content-admonitions.mjs +114 -0
- package/engine/content-captions.mjs +181 -0
- package/engine/content-embeds.mjs +2 -2
- package/engine/content-footnotes.mjs +34 -0
- package/engine/content-format-check.mjs +2 -2
- package/engine/content-format.mjs +127 -73
- package/engine/content-format.yaml +745 -0
- package/engine/content-html.mjs +1 -1
- package/engine/content-icons.mjs +45 -92
- package/engine/content-images.mjs +22 -74
- package/engine/content-index.mjs +12 -3
- package/engine/content-links.mjs +21 -26
- package/engine/content-lint.mjs +1 -5
- package/engine/content-secrets.mjs +3 -1
- package/engine/content-tables.mjs +47 -1257
- package/engine/culture-choice.mjs +32 -0
- package/engine/date-conversion.mjs +47 -0
- package/engine/declaration-check.mjs +87 -0
- package/engine/docs-checks.mjs +140 -0
- package/engine/documentation-links.mjs +31 -0
- package/engine/exported-scene.mjs +1 -1
- package/engine/extension-attributes.mjs +74 -0
- package/engine/field-reference.mjs +1 -1
- package/engine/field-spec.mjs +4 -0
- package/engine/foreign-catalog.mjs +5 -5
- package/engine/format-generated.mjs +15 -0
- package/engine/foundry-entries.mjs +3 -1
- package/engine/frontmatter-lint.mjs +5 -1
- package/engine/frontmatter.mjs +10 -0
- package/engine/generate.mjs +2 -2
- package/engine/helpers.mjs +71 -117
- package/engine/homepage.mjs +2 -3
- package/engine/index-records.mjs +1 -0
- package/engine/index.mjs +18 -3
- package/engine/infobox-registry.mjs +3 -1
- package/engine/infobox.mjs +38 -20
- package/engine/item-compiler.mjs +10 -8
- package/engine/item-docs.mjs +7 -6
- package/engine/itinerary-scenes.mjs +4 -2
- package/engine/journals.mjs +51 -10
- package/engine/macros.mjs +1 -1
- package/engine/map-build.mjs +1 -1
- package/engine/map-dot.mjs +1 -1
- package/engine/map-raster.mjs +4 -2
- package/engine/markdown-expressions.mjs +117 -0
- package/engine/metadata-index.mjs +7 -3
- package/engine/note-addresses.mjs +1 -0
- package/engine/note-dates.mjs +283 -61
- package/engine/note-package.mjs +5 -25
- package/engine/note-state.mjs +6 -2
- package/engine/note-vocabulary.mjs +74 -19
- package/engine/number-words.mjs +75 -0
- package/engine/office-holders.mjs +188 -0
- package/engine/pack-config.mjs +2 -15
- package/engine/pack-router.mjs +24 -5
- package/engine/packages.mjs +3 -2
- package/engine/pathnames.mjs +21 -24
- package/engine/pdf-build.mjs +217 -55
- package/engine/pdf-fonts.mjs +2 -2
- package/engine/pdf-images.mjs +82 -0
- package/engine/pdf-render.mjs +174 -22
- package/engine/populations.mjs +3 -2
- package/engine/project-init.mjs +499 -0
- package/engine/prose-config.mjs +1 -1
- package/engine/prose-lint.mjs +7 -6
- package/engine/readability-lint.mjs +86 -0
- package/engine/reckoning-markers.mjs +205 -93
- package/engine/scenes.mjs +5 -2
- package/engine/schema-check.mjs +3 -3
- package/engine/site-build.mjs +87 -37
- package/engine/site-config.mjs +8 -8
- package/engine/site-index.mjs +4 -0
- package/engine/site-maps.mjs +1 -1
- package/engine/site-preview.mjs +19 -8
- package/engine/social-ties.mjs +5 -1
- package/engine/sql-tables.mjs +92 -22
- package/engine/web-wikilinks.mjs +15 -31
- package/engine/wikilink-syntax.mjs +4 -1
- package/engine/wikilinks.mjs +12 -14
- package/hm3/item-fields.mjs +1 -1
- package/hm3/items.mjs +12 -45
- package/index.mjs +2 -14
- package/manifest.mjs +31 -4
- package/markdownlint-config.mjs +1 -1
- package/package.json +32 -11
- package/prettier-config.mjs +1 -1
- package/sohl/being-info.mjs +3 -145
- package/sohl/index.mjs +1 -1
- package/sohl/infobox.mjs +2 -1
- package/sohl/item-builders.mjs +3 -27
- package/sohl/item-fields.mjs +61 -22
- package/sohl/kb-passes.mjs +2 -2
- package/sohl/note-schemas.mjs +0 -8
- package/stage.mjs +19 -0
- package/types/config.d.mts +12 -3
- package/types/content-config.d.mts +15 -81
- package/types/engine/address-render.d.mts +13 -0
- package/types/engine/address.d.mts +2 -11
- package/types/engine/anchors.d.mts +3 -4
- package/types/engine/asset-bindings.d.mts +9 -2
- package/types/engine/base-compiler.d.mts +1 -2
- package/types/engine/being-measurements.d.mts +16 -0
- package/types/engine/calendar-choice.d.mts +14 -0
- package/types/engine/calendar-human.d.mts +4 -0
- package/types/engine/calendar-notes.d.mts +2 -0
- package/types/engine/calendars.d.mts +5 -3
- package/types/engine/code-fences.d.mts +6 -72
- package/types/engine/compile-corpus.d.mts +1 -3
- package/types/engine/content-admonitions.d.mts +24 -0
- package/types/engine/content-captions.d.mts +45 -0
- package/types/engine/content-footnotes.d.mts +10 -0
- package/types/engine/content-format-check.d.mts +17 -4
- package/types/engine/content-format.d.mts +24 -11
- package/types/engine/content-icons.d.mts +14 -23
- package/types/engine/content-images.d.mts +2 -2
- package/types/engine/content-index.d.mts +9 -2
- package/types/engine/content-links.d.mts +3 -5
- package/types/engine/content-tables.d.mts +11 -140
- package/types/engine/culture-choice.d.mts +14 -0
- package/types/engine/date-conversion.d.mts +4 -0
- package/types/engine/declaration-check.d.mts +24 -0
- package/types/engine/docs-checks.d.mts +28 -0
- package/types/engine/documentation-links.d.mts +7 -0
- package/types/engine/exported-scene.d.mts +6 -2
- package/types/engine/extension-attributes.d.mts +14 -0
- package/types/engine/field-spec.d.mts +9 -0
- package/types/engine/format-generated.d.mts +7 -0
- package/types/engine/frontmatter.d.mts +14 -4
- package/types/engine/generate.d.mts +1 -1
- package/types/engine/helpers.d.mts +37 -92
- package/types/engine/homepage.d.mts +2 -3
- package/types/engine/index.d.mts +5 -0
- package/types/engine/infobox-registry.d.mts +3 -1
- package/types/engine/infobox.d.mts +17 -6
- package/types/engine/item-compiler.d.mts +4 -4
- package/types/engine/item-docs.d.mts +4 -3
- package/types/engine/journals.d.mts +10 -7
- package/types/engine/map-graphviz.d.mts +1 -1
- package/types/engine/markdown-expressions.d.mts +17 -0
- package/types/engine/note-dates.d.mts +47 -1
- package/types/engine/note-package.d.mts +3 -19
- package/types/engine/note-state.d.mts +2 -1
- package/types/engine/note-vocabulary.d.mts +9 -0
- package/types/engine/number-words.d.mts +4 -0
- package/types/engine/office-holders.d.mts +14 -0
- package/types/engine/pack-config.d.mts +1 -1
- package/types/engine/packages.d.mts +3 -2
- package/types/engine/pdf-images.d.mts +38 -0
- package/types/engine/populations.d.mts +2 -2
- package/types/engine/project-init.d.mts +17 -0
- package/types/engine/prose-config.d.mts +1 -1
- package/types/engine/prose-lint.d.mts +3 -2
- package/types/engine/readability-lint.d.mts +22 -0
- package/types/engine/reckoning-markers.d.mts +4 -2
- package/types/engine/schema-check.d.mts +3 -3
- package/types/engine/site-build.d.mts +38 -28
- package/types/engine/site-config.d.mts +2 -2
- package/types/engine/site-index.d.mts +4 -0
- package/types/engine/site-preview.d.mts +1 -1
- package/types/engine/social-ties.d.mts +8 -2
- package/types/engine/sql-tables.d.mts +13 -2
- package/types/engine/web-wikilinks.d.mts +2 -1
- package/types/engine/wikilinks.d.mts +7 -1
- package/types/hm3/items.d.mts +5 -6
- package/types/labels.d.mts +40 -0
- package/types/manifest.d.mts +3 -3
- package/types/sohl/actors.d.mts +2 -2
- package/types/sohl/being-info.d.mts +3 -43
- package/types/sohl/index.d.mts +1 -1
- package/types/sohl/infobox.d.mts +5 -2
- package/types/sohl/item-fields.d.mts +4 -13
- package/types/stage.d.mts +9 -0
- package/CONTENT.md +0 -3239
package/engine/anchors.mjs
CHANGED
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
*/
|
|
13
13
|
|
|
14
14
|
/**
|
|
15
|
-
* The anchors a note declares on
|
|
15
|
+
* The anchors a note declares on headings and captioned blocks.
|
|
16
16
|
*
|
|
17
17
|
* **A leaf, deliberately.** This is asked by the link checker, by the content
|
|
18
18
|
* index, and by the builds that emit a link, and they cannot all import one
|
|
@@ -25,6 +25,8 @@
|
|
|
25
25
|
* @module
|
|
26
26
|
*/
|
|
27
27
|
|
|
28
|
+
import { scanCaptions } from "./content-captions.mjs";
|
|
29
|
+
|
|
28
30
|
/**
|
|
29
31
|
* A heading, and the `{#slug}` anchor it declares.
|
|
30
32
|
*
|
|
@@ -38,11 +40,10 @@
|
|
|
38
40
|
const HEADING = /^\s*(#{1,6})\s+(.+?)\s*#*\s*$/;
|
|
39
41
|
const ANCHOR = /^(.*?)\s*\{#([^}]+)\}\s*$/;
|
|
40
42
|
/**
|
|
41
|
-
* The `{#slug}` anchors a note
|
|
43
|
+
* The `{#slug}` and `:::caption {#slug}` anchors a note declares.
|
|
42
44
|
*
|
|
43
|
-
*
|
|
44
|
-
*
|
|
45
|
-
* it with `#…` — listing it would offer a link that cannot be written.
|
|
45
|
+
* A bare `#` heading starts a journal page without declaring a slug. Captioned
|
|
46
|
+
* blocks declare a slug and start an addressable journal page.
|
|
46
47
|
*
|
|
47
48
|
* @param {string} body - The note's markdown body, frontmatter already removed.
|
|
48
49
|
* @param {number} [bodyLine] - The 1-based file line the body starts on, from
|
|
@@ -79,5 +80,13 @@ export function collectAnchors(body, bodyLine = 1) {
|
|
|
79
80
|
line: bodyLine + i,
|
|
80
81
|
});
|
|
81
82
|
}
|
|
82
|
-
|
|
83
|
+
for (const caption of scanCaptions(body).captions) {
|
|
84
|
+
anchors.push({
|
|
85
|
+
slug: caption.id,
|
|
86
|
+
name: caption.label,
|
|
87
|
+
level: 1,
|
|
88
|
+
line: bodyLine + caption.line - 1,
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
return anchors.sort((a, b) => a.line - b.line);
|
|
83
92
|
}
|
|
@@ -22,7 +22,11 @@ function assetFiles(root) {
|
|
|
22
22
|
return files;
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
-
/**
|
|
25
|
+
/**
|
|
26
|
+
* Check configured foreign Address bindings against fetched indexes.
|
|
27
|
+
* @param {object} config
|
|
28
|
+
* @param {{foreignIndex?: Map<string, object>, assets?: object[]}} [options]
|
|
29
|
+
*/
|
|
26
30
|
export function checkForeignAssetBindings(config, { foreignIndex, assets } = {}) {
|
|
27
31
|
const copies = (assets ?? config.packageBuild?.assets ?? []).filter((asset) => asset.bindsTo);
|
|
28
32
|
if (!copies.length) return [];
|
|
@@ -39,7 +43,7 @@ export function checkForeignAssetBindings(config, { foreignIndex, assets } = {})
|
|
|
39
43
|
{
|
|
40
44
|
file: "package-build.config.yaml",
|
|
41
45
|
severity: "error",
|
|
42
|
-
message: `asset binding index for ${bindsTo.package} is unavailable; run
|
|
46
|
+
message: `asset binding index for ${bindsTo.package} is unavailable; run package-build deps fetch`,
|
|
43
47
|
},
|
|
44
48
|
];
|
|
45
49
|
}
|
package/engine/base-compiler.mjs
CHANGED
|
@@ -63,6 +63,10 @@
|
|
|
63
63
|
|
|
64
64
|
import { authoredFrontmatter } from "./index-records.mjs";
|
|
65
65
|
import { renderSecretBlocks } from "./content-secrets.mjs";
|
|
66
|
+
import { scanAdmonitions } from "./content-admonitions.mjs";
|
|
67
|
+
import { scanCaptions } from "./content-captions.mjs";
|
|
68
|
+
import { renderMarkdownExpressions } from "./markdown-expressions.mjs";
|
|
69
|
+
import { isGmNote } from "./note-vocabulary.mjs";
|
|
66
70
|
import { cloneAddressState } from "./address-values.mjs";
|
|
67
71
|
import fs from "fs";
|
|
68
72
|
import path from "path";
|
|
@@ -82,7 +86,7 @@ import {
|
|
|
82
86
|
import { isNoteRecord, noteFile } from "./index-records.mjs";
|
|
83
87
|
import { reduceAddressFields } from "./address-fields.mjs";
|
|
84
88
|
import { artPathname, artSlot, unacceptedArtMessage, unresolvedArtMessage } from "./art-fields.mjs";
|
|
85
|
-
import { emitDiagnostic } from "./diagnostics.mjs";
|
|
89
|
+
import { emitDiagnostic, positionOfLiteral } from "./diagnostics.mjs";
|
|
86
90
|
import { assertNoDeclaredPackage } from "./note-package.mjs";
|
|
87
91
|
import { assertNoDeclaredFolder } from "./folder-notes.mjs";
|
|
88
92
|
import {
|
|
@@ -434,6 +438,7 @@ export class BasePackCompiler {
|
|
|
434
438
|
* routes to no pack at all — a build failure, never a silent drop.
|
|
435
439
|
*/
|
|
436
440
|
routesHere(fm) {
|
|
441
|
+
if (isGmNote(fm) && !this.router?.privateOf?.(this.packName)) return false;
|
|
437
442
|
if (!this.router || !this.packName || !this.docType) return true;
|
|
438
443
|
return (
|
|
439
444
|
this.router.resolve(fm, this.docType, this.packSystem ?? undefined) === this.packName
|
|
@@ -600,7 +605,6 @@ export class BasePackCompiler {
|
|
|
600
605
|
// that converts no prose still compiles a document that carries art.
|
|
601
606
|
this.linkIndex = this.corpus.linkIndex;
|
|
602
607
|
if (this.constructor.convertsWikilinks) {
|
|
603
|
-
this.contentDocs = this.corpus.contentDocs;
|
|
604
608
|
this.sqlTables = this.corpus.sqlTables;
|
|
605
609
|
}
|
|
606
610
|
// The package's declared present, read once from the same corpus — so
|
|
@@ -622,7 +626,8 @@ export class BasePackCompiler {
|
|
|
622
626
|
* that does not convert.
|
|
623
627
|
*/
|
|
624
628
|
convertBody(fm, body) {
|
|
625
|
-
const secretError =
|
|
629
|
+
const secretError =
|
|
630
|
+
renderSecretBlocks(body, "book").errors[0] ?? scanAdmonitions(body).errors[0];
|
|
626
631
|
if (secretError) {
|
|
627
632
|
const error = new Error(secretError.message);
|
|
628
633
|
error.position = {
|
|
@@ -638,13 +643,35 @@ export class BasePackCompiler {
|
|
|
638
643
|
const name = resolveName(fm);
|
|
639
644
|
const { absPath, bodyLine, bodyColumn } = this.currentNote ?? {};
|
|
640
645
|
const { markdown: tabulated, lineMap } = expandNoteTables(body, {
|
|
641
|
-
docs: this.contentDocs,
|
|
642
646
|
name,
|
|
643
|
-
fm,
|
|
644
647
|
bodyLine,
|
|
645
648
|
sqlTables: absPath ? this.sqlTables?.get(absPath) : undefined,
|
|
646
649
|
});
|
|
647
|
-
const
|
|
650
|
+
const captionError = scanCaptions(tabulated).errors[0];
|
|
651
|
+
if (captionError) {
|
|
652
|
+
const error = new Error(captionError.message);
|
|
653
|
+
error.position = {
|
|
654
|
+
line:
|
|
655
|
+
(bodyLine ?? 1) +
|
|
656
|
+
(lineMap[captionError.line - 1]?.line ?? captionError.line - 1),
|
|
657
|
+
column: captionError.column,
|
|
658
|
+
};
|
|
659
|
+
throw error;
|
|
660
|
+
}
|
|
661
|
+
const expressions = renderMarkdownExpressions(tabulated, {
|
|
662
|
+
fm,
|
|
663
|
+
dates: reckoningContext(this.linkIndex),
|
|
664
|
+
sqlResults: absPath ? this.sqlTables?.inline?.get(absPath) : undefined,
|
|
665
|
+
file: absPath,
|
|
666
|
+
bodyLine,
|
|
667
|
+
});
|
|
668
|
+
if (expressions.findings.length) {
|
|
669
|
+
const finding = expressions.findings[0];
|
|
670
|
+
const error = new Error(finding.message);
|
|
671
|
+
error.position = { line: finding.line, column: finding.column };
|
|
672
|
+
throw error;
|
|
673
|
+
}
|
|
674
|
+
const { markdown, unresolved } = convertNoteWikilinks(expressions.markdown, {
|
|
648
675
|
type: fm.type,
|
|
649
676
|
id: fm.id,
|
|
650
677
|
// Where this note is, so a link that resolves nowhere is reported
|
|
@@ -808,7 +835,7 @@ export class BasePackCompiler {
|
|
|
808
835
|
* a line rather than at a note.
|
|
809
836
|
*
|
|
810
837
|
* Silent where nothing can answer — no published schema, or a subtype the
|
|
811
|
-
* artifact does not name. `
|
|
838
|
+
* artifact does not name. `package-build lint` says that out loud once for
|
|
812
839
|
* the whole build rather than once per note.
|
|
813
840
|
*
|
|
814
841
|
* @param {object} fm - The note's frontmatter.
|
|
@@ -1211,13 +1238,22 @@ export class BasePackCompiler {
|
|
|
1211
1238
|
this.onCompiled(fm, doc);
|
|
1212
1239
|
} catch (err) {
|
|
1213
1240
|
this.errorCount++;
|
|
1241
|
+
const repeatedAnchor = err.message.match(
|
|
1242
|
+
/declares the anchor \{#([^}]+)\} on more than one heading/,
|
|
1243
|
+
);
|
|
1244
|
+
const position =
|
|
1245
|
+
err.position ??
|
|
1246
|
+
(repeatedAnchor ?
|
|
1247
|
+
positionOfLiteral(
|
|
1248
|
+
fs.readFileSync(absPath, "utf8"),
|
|
1249
|
+
`{#${repeatedAnchor[1]}}`,
|
|
1250
|
+
2,
|
|
1251
|
+
)
|
|
1252
|
+
: undefined);
|
|
1214
1253
|
// `position` is set by whatever failed if it knew where — an
|
|
1215
1254
|
// unresolved address, a bad table directive — so the report
|
|
1216
1255
|
// points at the line rather than at the note.
|
|
1217
|
-
this.noteError(
|
|
1218
|
-
`${this.noteLabel(fm)} failed to compile: ${err.message}`,
|
|
1219
|
-
err.position,
|
|
1220
|
-
);
|
|
1256
|
+
this.noteError(`${this.noteLabel(fm)} failed to compile: ${err.message}`, position);
|
|
1221
1257
|
}
|
|
1222
1258
|
}
|
|
1223
1259
|
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/* SPDX-License-Identifier: GPL-3.0-or-later */
|
|
2
|
+
|
|
3
|
+
import { positionOfFrontmatterPath } from "./diagnostics.mjs";
|
|
4
|
+
|
|
5
|
+
const INCHES_PER_METRE = 39.3701;
|
|
6
|
+
const POUNDS_PER_KILOGRAM = 2.20462;
|
|
7
|
+
|
|
8
|
+
/** Measurements are displayed as whole imperial units. */
|
|
9
|
+
function safeRound(value) {
|
|
10
|
+
const rounded = Math.round(value);
|
|
11
|
+
return Number.isSafeInteger(rounded) && rounded >= 0 ? rounded : null;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/** Read a being's height as a whole number of inches. */
|
|
15
|
+
export function parseBeingHeight(value) {
|
|
16
|
+
if (typeof value === "number")
|
|
17
|
+
return Number.isFinite(value) && value >= 0 ? safeRound(value * INCHES_PER_METRE) : null;
|
|
18
|
+
if (typeof value !== "string") return null;
|
|
19
|
+
const metric = /^(\d+(?:\.\d+)?) ?m$/.exec(value);
|
|
20
|
+
if (metric) return safeRound(Number(metric[1]) * INCHES_PER_METRE);
|
|
21
|
+
const imperial = /^(\d+)'(?:\s*(\d{1,2})")?$/.exec(value);
|
|
22
|
+
if (!imperial) return null;
|
|
23
|
+
const inches = Number(imperial[2] ?? 0);
|
|
24
|
+
return inches <= 11 ? safeRound(Number(imperial[1]) * 12 + inches) : null;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/** Read a being's weight as a whole number of pounds. */
|
|
28
|
+
export function parseBeingWeight(value) {
|
|
29
|
+
if (typeof value === "number")
|
|
30
|
+
return Number.isFinite(value) && value >= 0 ? safeRound(value * POUNDS_PER_KILOGRAM) : null;
|
|
31
|
+
if (typeof value !== "string") return null;
|
|
32
|
+
const metric = /^(\d+(?:\.\d+)?) ?kg$/.exec(value);
|
|
33
|
+
if (metric) return safeRound(Number(metric[1]) * POUNDS_PER_KILOGRAM);
|
|
34
|
+
const imperial = /^(\d+) ?lbs$/.exec(value);
|
|
35
|
+
return imperial ? safeRound(Number(imperial[1])) : null;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/** Show a being's height in feet and inches. */
|
|
39
|
+
export function displayBeingHeight(value) {
|
|
40
|
+
const inches = parseBeingHeight(value);
|
|
41
|
+
if (inches === null) return String(value);
|
|
42
|
+
const feet = Math.floor(inches / 12);
|
|
43
|
+
const remainder = inches % 12;
|
|
44
|
+
return String(feet) + "′" + (remainder ? " " + String(remainder) + "″" : "");
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/** Show a being's weight in pounds. */
|
|
48
|
+
export function displayBeingWeight(value) {
|
|
49
|
+
const pounds = parseBeingWeight(value);
|
|
50
|
+
return pounds === null ? String(value) : String(pounds) + " lbs";
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/** Check one authored being measurement at its frontmatter position. */
|
|
54
|
+
export function checkBeingMeasurement(note, key, parse) {
|
|
55
|
+
const value = note.fm?.data?.[key];
|
|
56
|
+
if (value === undefined || value === null || parse(value) !== null) return [];
|
|
57
|
+
return [
|
|
58
|
+
{
|
|
59
|
+
...(note.file ? { file: note.file } : {}),
|
|
60
|
+
...positionOfFrontmatterPath(note.raw ?? "", ["data", key]),
|
|
61
|
+
severity: "error",
|
|
62
|
+
message:
|
|
63
|
+
"data." +
|
|
64
|
+
key +
|
|
65
|
+
(key === "height" ?
|
|
66
|
+
" needs metres or feet and inches, such as 1.91m or 6' 3\""
|
|
67
|
+
: " needs kilograms or pounds, such as 85kg or 187 lbs"),
|
|
68
|
+
},
|
|
69
|
+
];
|
|
70
|
+
}
|
|
@@ -32,7 +32,7 @@ function readIndex(file) {
|
|
|
32
32
|
const display = path.relative(process.cwd(), file) || file;
|
|
33
33
|
if (!fs.existsSync(file)) {
|
|
34
34
|
const error = new Error(
|
|
35
|
-
`${display}: error: content index is absent; run
|
|
35
|
+
`${display}: error: content index is absent; run package-build content-index`,
|
|
36
36
|
);
|
|
37
37
|
error.located = true;
|
|
38
38
|
throw error;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/* SPDX-License-Identifier: GPL-3.0-or-later */
|
|
2
|
+
|
|
3
|
+
import { isAddressTuple, parseAddress, renderAddress } from "./address.mjs";
|
|
4
|
+
import { positionOfFrontmatterPath } from "./diagnostics.mjs";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Require a being's or place's chosen calendar to address a calendar note.
|
|
8
|
+
* @param {object} note
|
|
9
|
+
* @param {{index?: object}} [options]
|
|
10
|
+
*/
|
|
11
|
+
export function checkCalendarChoice(note, { index } = {}) {
|
|
12
|
+
const value = note.fm?.data?.calendar;
|
|
13
|
+
if (value === undefined || value === null || !index?.addressHit) return [];
|
|
14
|
+
const tuple = parseAddress(value, {
|
|
15
|
+
package: index.contentPackage,
|
|
16
|
+
system: "note",
|
|
17
|
+
type: "lore",
|
|
18
|
+
types: index.types,
|
|
19
|
+
packages: index.packages,
|
|
20
|
+
});
|
|
21
|
+
if (tuple.reason || tuple.type !== "lore") return [];
|
|
22
|
+
const target = index.addressHit(renderAddress(tuple));
|
|
23
|
+
if (!target || (target.fm ?? target).subType === "calendar") return [];
|
|
24
|
+
return [
|
|
25
|
+
{
|
|
26
|
+
file: note.file,
|
|
27
|
+
...positionOfFrontmatterPath(note.raw ?? "", ["data", "calendar"]),
|
|
28
|
+
severity: "error",
|
|
29
|
+
message: `data.calendar ${isAddressTuple(value) ? renderAddress(value) : value} names lore whose subType is not calendar`,
|
|
30
|
+
},
|
|
31
|
+
];
|
|
32
|
+
}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/* SPDX-License-Identifier: GPL-3.0-or-later */
|
|
2
|
+
|
|
3
|
+
import { calendarStructure } from "./calendars.mjs";
|
|
4
|
+
|
|
5
|
+
/** The labels a human date may use to name one era. */
|
|
6
|
+
export function eraNames(era) {
|
|
7
|
+
return [era.abbreviation, era.name, era.marker, era.era.split(".").at(-1), era.era].filter(
|
|
8
|
+
(value) => typeof value === "string" && value.trim(),
|
|
9
|
+
);
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/** Translate a named date into the addressed form read by the date parser. */
|
|
13
|
+
export function addressedCalendarDate(text, eras) {
|
|
14
|
+
const first = /^(\d{1,2})\s+(.+)$/.exec(text);
|
|
15
|
+
let day = null;
|
|
16
|
+
let rest = text;
|
|
17
|
+
if (first) {
|
|
18
|
+
const firstWord = first[2].split(/[\s,]/)[0].toLowerCase();
|
|
19
|
+
const hasMonth = (calendarStructure(eras[0].calendar).months ?? []).some((month) =>
|
|
20
|
+
[month.name, month.abbreviation].some(
|
|
21
|
+
(name) => typeof name === "string" && name.toLowerCase().startsWith(firstWord),
|
|
22
|
+
),
|
|
23
|
+
);
|
|
24
|
+
if (hasMonth) {
|
|
25
|
+
day = first[1];
|
|
26
|
+
rest = first[2];
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
const months = calendarStructure(eras[0].calendar).months ?? [];
|
|
30
|
+
const matchingMonths = months.flatMap((month, index) =>
|
|
31
|
+
[month.name, month.abbreviation]
|
|
32
|
+
.filter((name) => typeof name === "string" && name)
|
|
33
|
+
.filter((name) => rest.toLowerCase().startsWith(name.toLowerCase()))
|
|
34
|
+
.filter((name) => /^[\s,]/.test(rest.slice(name.length)))
|
|
35
|
+
.map((name) => ({ name, number: index + 1 })),
|
|
36
|
+
);
|
|
37
|
+
if (day !== null && matchingMonths.length === 0)
|
|
38
|
+
throw new RangeError("date names no month in this calendar");
|
|
39
|
+
const longest = Math.max(0, ...matchingMonths.map((month) => month.name.length));
|
|
40
|
+
const choices = [
|
|
41
|
+
...new Set(
|
|
42
|
+
matchingMonths
|
|
43
|
+
.filter((month) => month.name.length === longest)
|
|
44
|
+
.map((month) => month.number),
|
|
45
|
+
),
|
|
46
|
+
];
|
|
47
|
+
if (choices.length > 1) throw new RangeError("date names an ambiguous month");
|
|
48
|
+
const monthName = matchingMonths.find((month) => month.name.length === longest)?.name;
|
|
49
|
+
const afterMonth = monthName ? rest.slice(monthName.length).replace(/^,?\s+/, "") : rest;
|
|
50
|
+
const match = /^(~?-?\d+)(?:\s+(.+?))?(?:\s+(\d{2}:\d{2}:\d{2}))?$/.exec(afterMonth);
|
|
51
|
+
if (!match) return null;
|
|
52
|
+
const [, year, eraName, clock] = match;
|
|
53
|
+
const selected =
|
|
54
|
+
eraName === undefined && eras.length === 1 ? eras[0]
|
|
55
|
+
: eraName === undefined ? null
|
|
56
|
+
: (() => {
|
|
57
|
+
const found = eras.filter((era) =>
|
|
58
|
+
eraNames(era).some((name) => name.toLowerCase() === eraName.toLowerCase()),
|
|
59
|
+
);
|
|
60
|
+
if (found.length > 1) throw new RangeError(`date names ambiguous era ${eraName}`);
|
|
61
|
+
return found[0] ?? null;
|
|
62
|
+
})();
|
|
63
|
+
if (!selected)
|
|
64
|
+
throw new RangeError(
|
|
65
|
+
eraName === undefined ?
|
|
66
|
+
"date needs an era in this calendar"
|
|
67
|
+
: `date names unknown era ${eraName}`,
|
|
68
|
+
);
|
|
69
|
+
const time = clock ? `:${clock.replaceAll(":", "")}` : "";
|
|
70
|
+
return `${year}${choices.length ? `/${choices[0]}` : ""}${day === null ? "" : `/${day}${time}`} ${selected.qualifier}`;
|
|
71
|
+
}
|
|
@@ -145,7 +145,7 @@ export const CALENDAR_FIELDS = Object.freeze([
|
|
|
145
145
|
},
|
|
146
146
|
{
|
|
147
147
|
name: "eras",
|
|
148
|
-
shape: "list of `{ shortcode, name, marker?, abbreviation?, proclaimedBy?, start, end? }`",
|
|
148
|
+
shape: "list of `{ shortcode, name, marker?, abbreviation?, proclaimedBy?, start, end?, label? }`",
|
|
149
149
|
kind: "list",
|
|
150
150
|
describe:
|
|
151
151
|
"The year-counts kept in this calendar. A marker names one era and uses these months.",
|
|
@@ -669,6 +669,35 @@ function checkEraShortcodes(note) {
|
|
|
669
669
|
);
|
|
670
670
|
}
|
|
671
671
|
seen.add(shortcode);
|
|
672
|
+
if (era.label === undefined) return;
|
|
673
|
+
const label = era.label;
|
|
674
|
+
const forms = typeof label === "string" ? { after: label } : label;
|
|
675
|
+
if (!forms || typeof forms !== "object" || Array.isArray(forms)) {
|
|
676
|
+
findings.push(
|
|
677
|
+
atData(
|
|
678
|
+
note,
|
|
679
|
+
["eras", position, "label"],
|
|
680
|
+
"error",
|
|
681
|
+
"an era label is a string or an {after, before} map",
|
|
682
|
+
),
|
|
683
|
+
);
|
|
684
|
+
return;
|
|
685
|
+
}
|
|
686
|
+
for (const [key, value] of Object.entries(forms)) {
|
|
687
|
+
if (
|
|
688
|
+
!["after", "before"].includes(key) ||
|
|
689
|
+
typeof value !== "string" ||
|
|
690
|
+
value.split("{date}").length !== 2
|
|
691
|
+
)
|
|
692
|
+
findings.push(
|
|
693
|
+
atData(
|
|
694
|
+
note,
|
|
695
|
+
["eras", position, "label", key],
|
|
696
|
+
"error",
|
|
697
|
+
"an era label uses after or before with exactly one {date} slot",
|
|
698
|
+
),
|
|
699
|
+
);
|
|
700
|
+
}
|
|
672
701
|
});
|
|
673
702
|
return findings;
|
|
674
703
|
}
|
|
@@ -752,8 +781,16 @@ function eraEntries(eras, dateContext = {}) {
|
|
|
752
781
|
for (const era of Array.isArray(eras) ? eras : []) {
|
|
753
782
|
const shortcode = String(era?.shortcode ?? "");
|
|
754
783
|
if (!shortcode) continue;
|
|
755
|
-
const start = parseNoteDate(era?.start, {
|
|
756
|
-
|
|
784
|
+
const start = parseNoteDate(era?.start, {
|
|
785
|
+
...dateContext,
|
|
786
|
+
allowUnknown: false,
|
|
787
|
+
ignoreEraBounds: true,
|
|
788
|
+
}).date;
|
|
789
|
+
const end = parseNoteDate(era?.end, {
|
|
790
|
+
...dateContext,
|
|
791
|
+
allowUnknown: false,
|
|
792
|
+
ignoreEraBounds: true,
|
|
793
|
+
}).date;
|
|
757
794
|
entries[shortcode] = written({
|
|
758
795
|
name: String(era?.name ?? ""),
|
|
759
796
|
abbreviation: era?.abbreviation === undefined ? undefined : String(era.abbreviation),
|
|
@@ -818,6 +855,7 @@ function moonEntries(moon, body, name, months, dateContext = {}) {
|
|
|
818
855
|
* @param {string} [opts.contentPackage] - The package shipping the note, which
|
|
819
856
|
* is a fact about the tree rather than about the note: a note never declares
|
|
820
857
|
* its own package.
|
|
858
|
+
* @param {object} [opts.dateContext] - Calendar conversion context.
|
|
821
859
|
* @returns {object} The definition.
|
|
822
860
|
*/
|
|
823
861
|
export function compileCalendar({ note, invariants, contentPackage, dateContext = {} }) {
|
package/engine/calendars.mjs
CHANGED
|
@@ -104,6 +104,13 @@ export function canonicalYear(year, epoch = CANONICAL_EPOCH) {
|
|
|
104
104
|
return epoch + (year > 0 ? year - 1 : year);
|
|
105
105
|
}
|
|
106
106
|
|
|
107
|
+
/** The signed year a reckoning counts at a canonical year, without year zero. */
|
|
108
|
+
export function eraYear(canonical, epoch = CANONICAL_EPOCH) {
|
|
109
|
+
if (!Number.isSafeInteger(canonical) || !Number.isSafeInteger(epoch))
|
|
110
|
+
throw new RangeError("era conversion needs whole canonical and epoch years");
|
|
111
|
+
return canonical >= epoch ? canonical - epoch + 1 : canonical - epoch;
|
|
112
|
+
}
|
|
113
|
+
|
|
107
114
|
/**
|
|
108
115
|
* The total-order key a date sorts on.
|
|
109
116
|
*
|
|
@@ -267,7 +274,7 @@ export function calendarStructure(calendar) {
|
|
|
267
274
|
/**
|
|
268
275
|
* How long one month of a calendar is, or `null` where the calendar is silent.
|
|
269
276
|
*
|
|
270
|
-
* @param {{months?: readonly CalendarMonth[]|null}}
|
|
277
|
+
* @param {{months?: readonly CalendarMonth[]|null}|undefined} calendar - The calendar.
|
|
271
278
|
* @param {number} month - The month, numbered from 1.
|
|
272
279
|
* @returns {number|null} Its length in days, or `null`.
|
|
273
280
|
*/
|
|
@@ -40,7 +40,7 @@
|
|
|
40
40
|
|
|
41
41
|
import { codeRegions } from "./code-fences.mjs";
|
|
42
42
|
|
|
43
|
-
/** The generated scaffold `
|
|
43
|
+
/** The generated scaffold `package-build`/Changesets writes, never authored prose. */
|
|
44
44
|
const RELEASE_HEADING_RE = /^## /;
|
|
45
45
|
const CHANGES_HEADING_RE = /^### (?:Major|Minor|Patch) Changes\s*$/;
|
|
46
46
|
|
|
@@ -79,7 +79,7 @@ const SCOREBOARD_PATTERNS = [
|
|
|
79
79
|
|
|
80
80
|
/** A token that reads as code but sits outside any code span — warning only. */
|
|
81
81
|
const CODE_TOKEN_RE =
|
|
82
|
-
|
|
82
|
+
/(?<![\p{L}\p{N}\p{M}_])[a-z][a-zA-Z0-9]*[A-Z][a-zA-Z0-9]*(?:\(\))?(?![\p{L}\p{N}\p{M}_])|\b[\w-]+(?:\/[\w-]+)+\.[A-Za-z]{1,8}\b|\b[A-Z][A-Z0-9]*(?:_[A-Z0-9]+)+\b/gu;
|
|
83
83
|
|
|
84
84
|
/** Bullets over this many words read as a paragraph, not a release note. */
|
|
85
85
|
const MAX_BULLET_WORDS = 40;
|
package/engine/code-fences.mjs
CHANGED
|
@@ -46,110 +46,36 @@
|
|
|
46
46
|
* prevent.
|
|
47
47
|
*/
|
|
48
48
|
|
|
49
|
+
import { parseExtensionAttributes } from "./extension-attributes.mjs";
|
|
50
|
+
|
|
49
51
|
/** A fence line, capturing its indent, its marker, and its info string. */
|
|
50
52
|
export const FENCE_LINE = /^([ \t]*)(`{3,}|~{3,})[ \t]*([^\r\n]*)$/;
|
|
51
53
|
|
|
52
54
|
/**
|
|
53
|
-
*
|
|
54
|
-
*
|
|
55
|
-
*
|
|
56
|
-
*
|
|
57
|
-
* section takes, later a caption. Those started as an ad-hoc bare word
|
|
58
|
-
* (`allow-empty`) and a `key=value` (`section-level=3`), each matched by its own
|
|
59
|
-
* regex: a grammar only in the sense that two regexes are one, and with no room
|
|
60
|
-
* to add a third property without adding a third spelling.
|
|
61
|
-
*
|
|
62
|
-
* Org-mode settled this long ago. A babel source block writes them after the
|
|
63
|
-
* language as `:key value`, which is a real grammar with a specification, a
|
|
64
|
-
* parser, and an editor that already completes it.
|
|
65
|
-
*
|
|
66
|
-
* ```text
|
|
67
|
-
* ```sql :section-level 3 :allow-empty
|
|
68
|
-
* ```
|
|
69
|
-
*
|
|
70
|
-
* **The language word stays first and stays plain.** `sql` is what GitHub,
|
|
71
|
-
* Prettier and every other markdown reader match on to syntax-highlight the
|
|
72
|
-
* block, so it leads and the header args follow; a reader that does not know
|
|
73
|
-
* them sees an ordinary SQL block.
|
|
74
|
-
*
|
|
75
|
-
* The grammar, matching org's:
|
|
76
|
-
*
|
|
77
|
-
* - a key is `:name` **starting a word** — a colon inside or ending a word is
|
|
78
|
-
* text, so `:caption Gear: the tables` is one argument;
|
|
79
|
-
* - a value runs to the next key or the end of the string, spaces included, and
|
|
80
|
-
* is trimmed;
|
|
81
|
-
* - a key with no value is `true`, which is what a statement like
|
|
82
|
-
* `:allow-empty` is;
|
|
83
|
-
* - a value may be `"quoted"` to hold a word that would otherwise read as a
|
|
84
|
-
* key — the one ambiguity org has too;
|
|
85
|
-
* - a repeated key takes its last value.
|
|
86
|
-
*
|
|
87
|
-
* @param {string} info - The text after the fence marker.
|
|
88
|
-
* @returns {{language: string, args: Record<string, string|true>}} The language,
|
|
89
|
-
* lowercased, and the header arguments in written order.
|
|
55
|
+
* Read a language name and optional braced attributes from a code fence.
|
|
56
|
+
* @param {string} info - Text after the fence marker.
|
|
57
|
+
* @returns {{language: string, args: Record<string, string>, problems: string[]}}
|
|
58
|
+
* Parsed values and syntax findings.
|
|
90
59
|
*/
|
|
91
60
|
export function parseHeaderArgs(info) {
|
|
92
61
|
const text = String(info ?? "").trim();
|
|
93
|
-
if (!text) return { language: "", args: {} };
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
const
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
// A key-like word inside a quoted value is part of the value — that is
|
|
111
|
-
// the whole reason quoting exists here.
|
|
112
|
-
if (quoted.some(([from, to]) => m.index >= from && m.index < to)) continue;
|
|
113
|
-
keys.push({ name: m[1], at: m.index, end: re.lastIndex });
|
|
114
|
-
}
|
|
115
|
-
for (let i = 0; i < keys.length; i += 1) {
|
|
116
|
-
const from = keys[i].end;
|
|
117
|
-
const to = i + 1 < keys.length ? keys[i + 1].at : rest.length;
|
|
118
|
-
const raw = rest.slice(from, to).trim();
|
|
119
|
-
args[keys[i].name] = raw === "" ? true : unquote(raw);
|
|
120
|
-
}
|
|
121
|
-
return { language, args };
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
/**
|
|
125
|
-
* The `"…"` and `'…'` spans in a header-argument string, as [start, end) offsets.
|
|
126
|
-
*
|
|
127
|
-
* Scanned once before the keys are, so a key-like word inside a value is never
|
|
128
|
-
* read as the next key.
|
|
129
|
-
*
|
|
130
|
-
* @param {string} text - The header-argument text.
|
|
131
|
-
* @returns {Array<[number, number]>} The quoted spans.
|
|
132
|
-
*/
|
|
133
|
-
function quotedSpans(text) {
|
|
134
|
-
const spans = [];
|
|
135
|
-
const re = /"[^"]*"|'[^']*'/g;
|
|
136
|
-
let m;
|
|
137
|
-
while ((m = re.exec(text)) !== null) spans.push([m.index, m.index + m[0].length]);
|
|
138
|
-
return spans;
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
/**
|
|
142
|
-
* A header-argument value with its surrounding quotes removed.
|
|
143
|
-
*
|
|
144
|
-
* Quoting is how a value holds a word that would otherwise read as the next key
|
|
145
|
-
* — the same escape hatch org offers, and the only one needed.
|
|
146
|
-
*
|
|
147
|
-
* @param {string} value - The raw value.
|
|
148
|
-
* @returns {string} The value, unquoted.
|
|
149
|
-
*/
|
|
150
|
-
function unquote(value) {
|
|
151
|
-
const m = /^"([^"]*)"$|^'([^']*)'$/.exec(value);
|
|
152
|
-
return m ? (m[1] ?? m[2]) : value;
|
|
62
|
+
if (!text) return { language: "", args: {}, problems: [] };
|
|
63
|
+
const match = /^([^\s{}]+)(?:\s+(.*))?$/.exec(text);
|
|
64
|
+
const language = match?.[1]?.toLowerCase() ?? "";
|
|
65
|
+
const rest = match?.[2]?.trim() ?? "";
|
|
66
|
+
if (!rest) return { language, args: {}, problems: [] };
|
|
67
|
+
if (!/^\{[\s\S]*\}$/.test(rest))
|
|
68
|
+
return { language, args: {}, problems: ["fence attributes need {key=value} syntax"] };
|
|
69
|
+
const parsed = parseExtensionAttributes(rest.slice(1, -1));
|
|
70
|
+
return {
|
|
71
|
+
language,
|
|
72
|
+
args: parsed.values,
|
|
73
|
+
problems: [
|
|
74
|
+
...parsed.problems,
|
|
75
|
+
...(parsed.id ? ["a code fence does not accept an id"] : []),
|
|
76
|
+
...(parsed.classes.length ? ["a code fence does not accept classes"] : []),
|
|
77
|
+
],
|
|
78
|
+
};
|
|
153
79
|
}
|
|
154
80
|
|
|
155
81
|
/** A list item's opening line, capturing the indent and the marker itself. */
|
|
@@ -39,7 +39,7 @@
|
|
|
39
39
|
*/
|
|
40
40
|
|
|
41
41
|
import { indexRecordsFor } from "./content-index.mjs";
|
|
42
|
-
import { buildContentLinkIndex
|
|
42
|
+
import { buildContentLinkIndex } from "./helpers.mjs";
|
|
43
43
|
import { loadPackConfig } from "./pack-config.mjs";
|
|
44
44
|
import { prepareTreeSqlTables } from "./sql-tables.mjs";
|
|
45
45
|
|
|
@@ -60,8 +60,7 @@ import { prepareTreeSqlTables } from "./sql-tables.mjs";
|
|
|
60
60
|
* created when none is passed, and returned on the corpus either way — a note
|
|
61
61
|
* the index refuses is a note the compile must still *report*, exactly as the
|
|
62
62
|
* compile loop reported it when the loop was the first to see it.
|
|
63
|
-
* @returns {Promise<{records: object[], linkIndex: object,
|
|
64
|
-
* sqlTables: Map<string, object[]>|undefined, problems: object[]}>} The corpus,
|
|
63
|
+
* @returns {Promise<{records: object[], linkIndex: object, sqlTables: Map<string, object[]>|undefined, problems: object[]}>} The corpus,
|
|
65
64
|
* its indexes, and the notes it could not record.
|
|
66
65
|
*/
|
|
67
66
|
export async function buildCompileCorpus({
|
|
@@ -81,12 +80,12 @@ export async function buildCompileCorpus({
|
|
|
81
80
|
skipDirectories,
|
|
82
81
|
problems: collected,
|
|
83
82
|
});
|
|
84
|
-
const
|
|
83
|
+
const corpusOptions = { config: resolved, records };
|
|
84
|
+
const sqlOptions = { ...corpusOptions, skipDirectories };
|
|
85
85
|
return {
|
|
86
86
|
records,
|
|
87
87
|
problems: collected,
|
|
88
|
-
linkIndex: buildContentLinkIndex(contentBase, router,
|
|
89
|
-
|
|
90
|
-
sqlTables: await prepareTreeSqlTables(contentBase, scope),
|
|
88
|
+
linkIndex: buildContentLinkIndex(contentBase, router, corpusOptions),
|
|
89
|
+
sqlTables: await prepareTreeSqlTables(contentBase, sqlOptions),
|
|
91
90
|
};
|
|
92
91
|
}
|