@heroiclands/package-build 22.20.0 → 22.22.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-content-build.md +0 -8
- package/CHANGELOG.md +204 -0
- package/assets/styles/content-base.css +162 -0
- package/bin/content-commands.mjs +26 -3
- package/bin/package-build.mjs +15 -2
- package/ci/content-format-reference.mjs +68 -1
- package/config.mjs +26 -0
- package/content-config.mjs +73 -4
- package/docs/api.md +46 -28
- package/docs/authoring/dates-and-calendars.md +4 -2
- package/docs/authoring/frontmatter.md +2 -2
- package/docs/authoring/links-and-markup.md +237 -28
- package/docs/configuration.md +27 -1
- package/docs/content-format.md +3 -1
- package/docs/guides/reference-pages.md +15 -0
- package/docs/reference/format-details.md +265 -42
- package/docs/reference/note-types.md +45 -28
- package/engine/actor-compiler.mjs +23 -0
- package/engine/anchored-sections.mjs +35 -4
- package/engine/anchors.mjs +10 -20
- package/engine/base-compiler.mjs +86 -21
- package/engine/base-styles.mjs +149 -0
- package/engine/bundles.mjs +0 -1
- package/engine/compendiums.mjs +8 -1
- package/engine/content-blocks.mjs +410 -0
- package/engine/content-captions.mjs +57 -8
- package/engine/content-footnotes.mjs +189 -0
- package/engine/content-format-check.mjs +15 -12
- package/engine/content-index.mjs +2 -2
- package/engine/content-links.mjs +14 -6
- package/engine/content-tables.mjs +45 -4
- package/engine/content-tasklists.mjs +189 -0
- package/engine/docs-checks.mjs +4 -4
- package/engine/extension-attributes.mjs +48 -2
- package/engine/frontmatter-lint.mjs +62 -66
- package/engine/frontmatter.mjs +3 -3
- package/engine/heading-attributes.mjs +308 -0
- package/engine/helpers.mjs +19 -13
- package/engine/homepage.mjs +8 -8
- package/engine/index.mjs +3 -0
- package/engine/infobox-render.mjs +21 -4
- package/engine/infobox.mjs +167 -7
- package/engine/journals.mjs +36 -17
- package/engine/macros.mjs +1 -1
- package/engine/markdown-expressions.mjs +178 -53
- package/engine/note-addresses.mjs +17 -8
- package/engine/note-dates.mjs +49 -0
- package/engine/note-frontmatter.mjs +7 -16
- package/engine/note-vocabulary.mjs +216 -42
- package/engine/page-lists.mjs +358 -0
- package/engine/pdf-build.mjs +76 -42
- package/engine/pdf-render.mjs +422 -49
- package/engine/rank-ladder.mjs +198 -0
- package/engine/reckoning-markers.mjs +1 -1
- package/engine/reference-targets.mjs +13 -1
- package/engine/retired-fields.mjs +8 -13
- package/engine/scenes.mjs +10 -3
- package/engine/site-build.mjs +41 -12
- package/engine/site-config.mjs +10 -1
- package/engine/site-index.mjs +39 -4
- package/engine/site-preview.mjs +18 -7
- package/engine/social-ties.mjs +52 -14
- package/engine/sql-tables.mjs +66 -9
- package/engine/standing-terms.mjs +19 -0
- package/engine/standings.mjs +538 -0
- package/engine/svg-theme.mjs +15 -4
- package/engine/system-block.mjs +12 -14
- package/engine/web-wikilinks.mjs +48 -1
- package/engine/wikilinks.mjs +3 -1
- package/hm3/actors.mjs +6 -3
- package/manifest.mjs +13 -0
- package/package.json +1 -1
- package/sohl/actors.mjs +82 -103
- package/sohl/item-fields.mjs +4 -5
- package/sohl/items.mjs +1 -1
- package/sohl/note-schemas.mjs +11 -21
- package/types/config.d.mts +9 -0
- package/types/engine/actor-compiler.d.mts +11 -0
- package/types/engine/anchored-sections.d.mts +24 -0
- package/types/engine/base-compiler.d.mts +19 -0
- package/types/engine/base-styles.d.mts +93 -0
- package/types/engine/content-blocks.d.mts +87 -0
- package/types/engine/content-captions.d.mts +14 -1
- package/types/engine/content-footnotes.d.mts +73 -0
- package/types/engine/content-format-check.d.mts +8 -6
- package/types/engine/content-tables.d.mts +12 -4
- package/types/engine/content-tasklists.d.mts +72 -0
- package/types/engine/extension-attributes.d.mts +22 -1
- package/types/engine/frontmatter.d.mts +1 -1
- package/types/engine/heading-attributes.d.mts +174 -0
- package/types/engine/helpers.d.mts +7 -4
- package/types/engine/homepage.d.mts +7 -7
- package/types/engine/index.d.mts +1 -0
- package/types/engine/infobox-render.d.mts +6 -1
- package/types/engine/journals.d.mts +14 -7
- package/types/engine/markdown-expressions.d.mts +23 -0
- package/types/engine/note-frontmatter.d.mts +1 -4
- package/types/engine/note-vocabulary.d.mts +98 -8
- package/types/engine/page-lists.d.mts +126 -0
- package/types/engine/pdf-render.d.mts +72 -2
- package/types/engine/rank-ladder.d.mts +32 -0
- package/types/engine/retired-fields.d.mts +4 -6
- package/types/engine/site-build.d.mts +13 -1
- package/types/engine/site-config.d.mts +9 -0
- package/types/engine/site-index.d.mts +13 -1
- package/types/engine/site-preview.d.mts +1 -1
- package/types/engine/sql-tables.d.mts +31 -7
- package/types/engine/standing-terms.d.mts +16 -0
- package/types/engine/standings.d.mts +147 -0
- package/types/engine/system-block.d.mts +11 -12
- package/types/engine/web-wikilinks.d.mts +3 -1
- package/types/engine/wikilinks.d.mts +2 -1
- package/types/hm3/actors.d.mts +3 -3
- package/types/sohl/actors.d.mts +25 -7
- package/engine/content-admonitions.mjs +0 -114
- package/engine/content-secrets.mjs +0 -112
- package/types/engine/content-admonitions.d.mts +0 -24
- package/types/engine/content-secrets.d.mts +0 -17
package/config.mjs
CHANGED
|
@@ -65,6 +65,7 @@ const SECTION_KEYS = [
|
|
|
65
65
|
"stageDir",
|
|
66
66
|
"assets",
|
|
67
67
|
"assetTransform",
|
|
68
|
+
"baseStyles",
|
|
68
69
|
"manifest",
|
|
69
70
|
"manifestFlags",
|
|
70
71
|
"schema",
|
|
@@ -205,6 +206,23 @@ function requireNonEmptyString(value, where) {
|
|
|
205
206
|
return /** @type {string} */ (value);
|
|
206
207
|
}
|
|
207
208
|
|
|
209
|
+
/**
|
|
210
|
+
* A declared switch, with the behaviour that applies when it is not declared.
|
|
211
|
+
*
|
|
212
|
+
* A truthy string is refused rather than coerced: `baseStyles: "false"` reads
|
|
213
|
+
* as off and would be on, which is the one wrong answer available here.
|
|
214
|
+
*
|
|
215
|
+
* @param {unknown} value - What was declared, or `undefined`.
|
|
216
|
+
* @param {boolean} fallback - The undeclared behaviour.
|
|
217
|
+
* @param {string} where - Dotted path, for the error.
|
|
218
|
+
* @returns {boolean} The setting.
|
|
219
|
+
*/
|
|
220
|
+
function requireBoolean(value, fallback, where) {
|
|
221
|
+
if (value === undefined) return fallback;
|
|
222
|
+
if (typeof value !== "boolean") fail(where, "must be true or false");
|
|
223
|
+
return value;
|
|
224
|
+
}
|
|
225
|
+
|
|
208
226
|
/**
|
|
209
227
|
* One staging copy: a source path in the repository, and where it lands under
|
|
210
228
|
* the staged package root.
|
|
@@ -537,6 +555,9 @@ function normalizeExceptions(value, field, where) {
|
|
|
537
555
|
* @property {string} stageDir The staged package root, relative to
|
|
538
556
|
* `rootDir`. Every asset `to:` lands under it.
|
|
539
557
|
* @property {readonly Readonly<AssetSpec>[]} assets
|
|
558
|
+
* @property {boolean} baseStyles Whether the package takes the shared base
|
|
559
|
+
* stylesheet, staged into `stageDir` and
|
|
560
|
+
* named first in the manifest's `styles`.
|
|
540
561
|
* @property {Readonly<Record<string, unknown>>} manifest The manifest
|
|
541
562
|
* specification, emitted as declared.
|
|
542
563
|
* @property {string|null} manifestFlags Module to load a `flags` function
|
|
@@ -877,6 +898,11 @@ export function resolvePackageBuildConfig(shared) {
|
|
|
877
898
|
ARTIFACT_OF_KIND[/** @type {"systems"|"modules"} */ (shared.packageKind)]
|
|
878
899
|
: requireNonEmptyString(releaseInput.artifact, "packageBuild.release.artifact"),
|
|
879
900
|
assets: Object.freeze(assets),
|
|
901
|
+
// On by default, because the rules it carries are what compiled content
|
|
902
|
+
// needs to be legible and a package that declined them by omission
|
|
903
|
+
// would ship the content unstyled without anything saying so. A package
|
|
904
|
+
// that wants the surface entirely to itself says so.
|
|
905
|
+
baseStyles: requireBoolean(section.baseStyles, true, "packageBuild.baseStyles"),
|
|
880
906
|
schema: normalizeSchema(section.schema),
|
|
881
907
|
assetTransform:
|
|
882
908
|
section.assetTransform === undefined ?
|
package/content-config.mjs
CHANGED
|
@@ -1580,6 +1580,7 @@ export const DERIVED_HUGO_KEYS = Object.freeze({
|
|
|
1580
1580
|
"params.brand": "the organisation's brand links, in `engine/site-config.mjs`",
|
|
1581
1581
|
"params.notfound": "`site.notfound`",
|
|
1582
1582
|
"markup.goldmark.renderer.unsafe": "the toolchain, whose pages carry raw HTML",
|
|
1583
|
+
"markup.goldmark.extensions.taskList": "the toolchain, whose format has no checkbox",
|
|
1583
1584
|
menu: "the navigation `package-build deps fetch` caches from heroiclands.org",
|
|
1584
1585
|
});
|
|
1585
1586
|
|
|
@@ -1903,6 +1904,44 @@ function normalizePdf(value, rootDir) {
|
|
|
1903
1904
|
});
|
|
1904
1905
|
}
|
|
1905
1906
|
|
|
1907
|
+
/**
|
|
1908
|
+
* A dotted version string as the three-element tuple Foundry compares.
|
|
1909
|
+
*
|
|
1910
|
+
* A missing component reads as `0`, so `"14"` compares as `14.0.0` — the
|
|
1911
|
+
* common way an author writes a major-only floor or pin. A non-numeric
|
|
1912
|
+
* component reads as `0` too, rather than throwing: this runs on values
|
|
1913
|
+
* {@link requireNonEmptyString} has already accepted as non-empty strings,
|
|
1914
|
+
* and a version with a qualifier (`"14.359.0-beta"`) is not this function's
|
|
1915
|
+
* place to refuse.
|
|
1916
|
+
*
|
|
1917
|
+
* @param {string} version - A dotted version string.
|
|
1918
|
+
* @returns {readonly [number, number, number]} Major, minor, patch.
|
|
1919
|
+
*/
|
|
1920
|
+
function versionTriple(version) {
|
|
1921
|
+
const parts = version.split(".");
|
|
1922
|
+
return [0, 1, 2].map((index) => {
|
|
1923
|
+
const n = Number.parseInt(parts[index] ?? "0", 10);
|
|
1924
|
+
return Number.isNaN(n) ? 0 : n;
|
|
1925
|
+
});
|
|
1926
|
+
}
|
|
1927
|
+
|
|
1928
|
+
/**
|
|
1929
|
+
* Whether `verified` names a build below `minimum`, compared as version
|
|
1930
|
+
* triples.
|
|
1931
|
+
*
|
|
1932
|
+
* @param {string} minimum - The declared floor.
|
|
1933
|
+
* @param {string} verified - The declared verified build.
|
|
1934
|
+
* @returns {boolean} `true` when `verified` sorts below `minimum`.
|
|
1935
|
+
*/
|
|
1936
|
+
function verifiedBelowMinimum(minimum, verified) {
|
|
1937
|
+
const a = versionTriple(verified);
|
|
1938
|
+
const b = versionTriple(minimum);
|
|
1939
|
+
for (let i = 0; i < 3; i++) {
|
|
1940
|
+
if (a[i] !== b[i]) return a[i] < b[i];
|
|
1941
|
+
}
|
|
1942
|
+
return false;
|
|
1943
|
+
}
|
|
1944
|
+
|
|
1906
1945
|
/**
|
|
1907
1946
|
* Validate a Foundry version range.
|
|
1908
1947
|
*
|
|
@@ -1912,6 +1951,12 @@ function normalizePdf(value, rootDir) {
|
|
|
1912
1951
|
* load-bearing there is `verified`, and a relationship may reasonably name a
|
|
1913
1952
|
* package without pinning a floor at all.
|
|
1914
1953
|
*
|
|
1954
|
+
* **The two keys are a range, not two independent facts.** Foundry reads
|
|
1955
|
+
* `verified` as the build inside `minimum`'s floor that was actually tested,
|
|
1956
|
+
* so a `verified` below `minimum` names a build the package already refuses
|
|
1957
|
+
* to install on. Caught only when both are present — a relationship naming
|
|
1958
|
+
* `verified` alone has nothing to compare it against.
|
|
1959
|
+
*
|
|
1915
1960
|
* @param {unknown} value - The declared range, or `undefined`.
|
|
1916
1961
|
* @param {string} where - Dotted path, for the error.
|
|
1917
1962
|
* @param {boolean} [requireMinimum] - Whether `minimum` must be present.
|
|
@@ -1929,6 +1974,18 @@ function normalizeCompatibility(value, where, requireMinimum = true) {
|
|
|
1929
1974
|
if (input.verified !== undefined) {
|
|
1930
1975
|
out.verified = requireNonEmptyString(input.verified, `${where}.verified`);
|
|
1931
1976
|
}
|
|
1977
|
+
if (
|
|
1978
|
+
out.minimum !== undefined &&
|
|
1979
|
+
out.verified !== undefined &&
|
|
1980
|
+
verifiedBelowMinimum(out.minimum, out.verified)
|
|
1981
|
+
) {
|
|
1982
|
+
fail(
|
|
1983
|
+
`${where}.verified`,
|
|
1984
|
+
`declares \`${out.verified}\`, below \`${where}.minimum\`'s \`${out.minimum}\` — ` +
|
|
1985
|
+
"Foundry reads the two as one range, so a build below the floor is " +
|
|
1986
|
+
"never the one verified against it",
|
|
1987
|
+
);
|
|
1988
|
+
}
|
|
1932
1989
|
return Object.freeze(out);
|
|
1933
1990
|
}
|
|
1934
1991
|
|
|
@@ -2022,6 +2079,21 @@ function normalizeSystems(value) {
|
|
|
2022
2079
|
// declaration that cannot answer "which version was this built
|
|
2023
2080
|
// against" is the gap this block exists to close.
|
|
2024
2081
|
const verified = requireNonEmptyString(compat.verified, `${at}.compatibility.verified`);
|
|
2082
|
+
const minimum =
|
|
2083
|
+
compat.minimum === undefined || compat.minimum === null ?
|
|
2084
|
+
null
|
|
2085
|
+
: requireNonEmptyString(compat.minimum, `${at}.compatibility.minimum`);
|
|
2086
|
+
// Caught here too: the shape is the one {@link normalizeCompatibility}
|
|
2087
|
+
// validates, and the same contradiction is possible wherever it
|
|
2088
|
+
// appears.
|
|
2089
|
+
if (minimum !== null && verifiedBelowMinimum(minimum, verified)) {
|
|
2090
|
+
fail(
|
|
2091
|
+
`${at}.compatibility.verified`,
|
|
2092
|
+
`declares \`${verified}\`, below \`${at}.compatibility.minimum\`'s ` +
|
|
2093
|
+
`\`${minimum}\` — Foundry reads the two as one range, so a build ` +
|
|
2094
|
+
"below the floor is never the one verified against it",
|
|
2095
|
+
);
|
|
2096
|
+
}
|
|
2025
2097
|
|
|
2026
2098
|
out[id] = Object.freeze({
|
|
2027
2099
|
manifest:
|
|
@@ -2029,10 +2101,7 @@ function normalizeSystems(value) {
|
|
|
2029
2101
|
null
|
|
2030
2102
|
: requireNonEmptyString(spec.manifest, `${at}.manifest`),
|
|
2031
2103
|
compatibility: Object.freeze({
|
|
2032
|
-
minimum
|
|
2033
|
-
compat.minimum === undefined || compat.minimum === null ?
|
|
2034
|
-
null
|
|
2035
|
-
: requireNonEmptyString(compat.minimum, `${at}.compatibility.minimum`),
|
|
2104
|
+
minimum,
|
|
2036
2105
|
verified,
|
|
2037
2106
|
}),
|
|
2038
2107
|
});
|
package/docs/api.md
CHANGED
|
@@ -347,7 +347,7 @@ The note types the engine itself declares — vocabulary that is a fact about th
|
|
|
347
347
|
|
|
348
348
|
### `engine.noteVocabulary`
|
|
349
349
|
|
|
350
|
-
An addressed content note accepts exactly these top-level keys, in this order: `shortcode`, `name`, `type`, `subType`, `description`, `tags`, `data`, `
|
|
350
|
+
An addressed content note accepts exactly these top-level keys, in this order: `shortcode`, `name`, `type`, `subType`, `description`, `tags`, `data`, `dnd5e`, `hm3`, `sohl`. A key outside this list is an error at its own line. The system blocks are one per system the address vocabulary recognizes, sorted by name after the fixed keys, so a further system is a change to `SYSTEM_IDS` alone. No dnd5e-specific document types are declared. Within that shape, the closed vocabularies are the `data:` container and each type's `subType`.
|
|
351
351
|
|
|
352
352
|
| Export | Signature | Returns | Use it when |
|
|
353
353
|
| ------------------------- | -------------------------------------------- | ---------------------------------------- | -------------------------------------------------------------------------------------------- |
|
|
@@ -355,6 +355,9 @@ An addressed content note accepts exactly these top-level keys, in this order: `
|
|
|
355
355
|
| `GM_TAG` | `const GM_TAG` | — | naming the declared tag that marks a GM-only note |
|
|
356
356
|
| `BEING_ARCHETYPES` | `const BEING_ARCHETYPES` | `Readonly<Record<string, string>>` | looking up the case-sensitive being archetypes and their meanings |
|
|
357
357
|
| `NOTE_VOCABULARY` | `const NOTE_VOCABULARY` | — | looking up, per content type, its `data:` field list and its closed `subType` values |
|
|
358
|
+
| `NOTE_TOP_LEVEL_FIELDS` | `const NOTE_TOP_LEVEL_FIELDS` | `readonly TopLevelFieldSpec[]` | reading the top-level keys a note accepts, in formatted order, with what each one means |
|
|
359
|
+
| `NOTE_TOP_LEVEL_KEYS` | `const NOTE_TOP_LEVEL_KEYS` | `readonly string[]` | reading just the key names, in the order the formatter writes them |
|
|
360
|
+
| `NOTE_TOP_LEVEL_KEY_SET` | `const NOTE_TOP_LEVEL_KEY_SET` | `ReadonlySet<string>` | testing whether a top-level key is one the note format declares |
|
|
358
361
|
| `SHARED_DATA_FIELDS` | `const SHARED_DATA_FIELDS` | `readonly DataFieldSpec[]` | reading the `data:` keys every type accepts, whatever it is — the art slots legal everywhere |
|
|
359
362
|
| `DECLARED_TAGS` | `const DECLARED_TAGS` | — | looking up the tags a note type may declare, grouped |
|
|
360
363
|
| `declaredTags` | `declaredTags(type, groups)` | `readonly string[]` | reading the declared tags a note of a type may carry, flattened |
|
|
@@ -420,15 +423,15 @@ The infobox declarations this toolchain ships, one per system, and the single ca
|
|
|
420
423
|
|
|
421
424
|
Drawing a declared infobox: HTML for separate Foundry pages appended after authored pages, Typst panels after each note’s authored body in the book. The website's boxes travel in front matter and the site theme draws them, so there is no renderer for it here.
|
|
422
425
|
|
|
423
|
-
| Export | Signature
|
|
424
|
-
| ---------------------- |
|
|
425
|
-
| `infoboxTypstPreamble` | `infoboxTypstPreamble()`
|
|
426
|
-
| `infoboxesToHtml` | `infoboxesToHtml(boxes, options)`
|
|
427
|
-
| `infoboxesToTypst` | `infoboxesToTypst(boxes, options)`
|
|
428
|
-
| `linkToHtml` | `linkToHtml(value)`
|
|
429
|
-
| `linkToTypst` | `linkToTypst(value, links, labelFor)` | `string` | drawing a link value as a cross-reference into the book |
|
|
430
|
-
| `linkToUuid` | `linkToUuid(value)`
|
|
431
|
-
| `sectionHasContent` | `sectionHasContent(section)`
|
|
426
|
+
| Export | Signature | Returns | Use it when |
|
|
427
|
+
| ---------------------- | ------------------------------------------- | --------- | ------------------------------------------------------------------------ |
|
|
428
|
+
| `infoboxTypstPreamble` | `infoboxTypstPreamble()` | `string` | emitting the panel definitions the book's bodies call, once per document |
|
|
429
|
+
| `infoboxesToHtml` | `infoboxesToHtml(boxes, options)` | `string` | drawing the boxes as `<details>` disclosures, open by default |
|
|
430
|
+
| `infoboxesToTypst` | `infoboxesToTypst(boxes, options)` | `string` | drawing the boxes as panels that break between their sections |
|
|
431
|
+
| `linkToHtml` | `linkToHtml(value)` | `string` | drawing a link value as an anchor on its URL |
|
|
432
|
+
| `linkToTypst` | `linkToTypst(value, links, labelFor, site)` | `string` | drawing a link value as a cross-reference into the book |
|
|
433
|
+
| `linkToUuid` | `linkToUuid(value)` | `string` | drawing a link value as a Foundry document reference |
|
|
434
|
+
| `sectionHasContent` | `sectionHasContent(section)` | `boolean` | deciding whether a section holds anything worth drawing |
|
|
432
435
|
|
|
433
436
|
### `engine.noteState`
|
|
434
437
|
|
|
@@ -692,12 +695,14 @@ invokes the check during frontmatter lint.
|
|
|
692
695
|
|
|
693
696
|
### `engine.markdownExpressions`
|
|
694
697
|
|
|
695
|
-
`renderMarkdownExpressions(body, { fm, dates, sqlResults, file, bodyLine })` expands scalar frontmatter references and registered Handlebars helpers outside Markdown code. The `dateformat` helper uses `formatDateInCalendar`; `gt`, `gte`, `lt`, `lte`, `eq`, `and`, `or`, and `not` evaluate explicit comparisons. The `sql` helper reads a prepared scalar result, which must contain one row and one column. `sqlQueriesInMarkdown` discovers SQL calls, including nested calls; `prepareInlineSqlExpressions` evaluates them through the same DuckDB connection as SQL tables. The renderer returns `{ markdown, findings }`, with source positions for invalid expressions. Site, Foundry and PDF passes invoke it after generated tables expand and before links resolve. Hugo shortcodes remain literal for the site renderer.
|
|
698
|
+
`renderMarkdownExpressions(body, { fm, dates, sqlResults, file, bodyLine })` expands scalar frontmatter references and registered Handlebars helpers outside Markdown code. The `dateformat` helper uses `formatDateInCalendar`; `gt`, `gte`, `lt`, `lte`, `eq`, `and`, `or`, and `not` evaluate explicit comparisons. The `sql` helper reads a prepared scalar result, which must contain one row and one column. `sqlQueriesInMarkdown` discovers SQL calls, including nested calls; `prepareInlineSqlExpressions` evaluates them through the same DuckDB connection as SQL tables. `EXPRESSION_HELPERS` is the one list of helpers: the engine registers from it and the authoring document is checked against it, so each name there carries the parameters it takes and a sentence on what it does. The renderer returns `{ markdown, findings }`, with source positions for invalid expressions. Site, Foundry and PDF passes invoke it after generated tables expand and before links resolve. Hugo shortcodes remain literal for the site renderer.
|
|
696
699
|
|
|
697
|
-
| Export | Signature | Returns | Use it when
|
|
698
|
-
| --------------------------- | ------------------------------------------ | ---------------------- |
|
|
699
|
-
| `renderMarkdownExpressions` | `renderMarkdownExpressions(body, options)` | `{markdown, findings}` | expanding authored expressions in prose
|
|
700
|
-
| `sqlQueriesInMarkdown` | `sqlQueriesInMarkdown(body)`
|
|
700
|
+
| Export | Signature | Returns | Use it when |
|
|
701
|
+
| --------------------------- | ------------------------------------------ | ---------------------- | ------------------------------------------------ |
|
|
702
|
+
| `renderMarkdownExpressions` | `renderMarkdownExpressions(body, options)` | `{markdown, findings}` | expanding authored expressions in prose |
|
|
703
|
+
| `sqlQueriesInMarkdown` | `sqlQueriesInMarkdown(body, fm?)` | query strings | preparing inline SQL alongside generated tables |
|
|
704
|
+
| `EXPRESSION_HELPERS` | `EXPRESSION_HELPERS` | helpers by name | listing what an expression may call |
|
|
705
|
+
| `EXPRESSION` | `const EXPRESSION` | `RegExp` | recognising an expression a pass has yet to read |
|
|
701
706
|
|
|
702
707
|
### `engine.reckoningMarkers`
|
|
703
708
|
|
|
@@ -1070,6 +1075,17 @@ An image saying how wide it is and where it sits. A directive in the curly-attri
|
|
|
1070
1075
|
| `renderImageFigures` | `function renderImageFigures(body, resolveSrc)` | {string} The same body, with each block image as a `<figure>`. | Rewrite every block image in a body into the figure the website publishes. |
|
|
1071
1076
|
| `imagePlugin` | `function imagePlugin()` | {(md: object) => void} A markdown-it plugin. | A markdown-it plugin that reads an image's directive and renders its figure. |
|
|
1072
1077
|
|
|
1078
|
+
### `engine.contentTaskLists`
|
|
1079
|
+
|
|
1080
|
+
A list item opened with `[ ]` or `[x]`, reported. The format has no checkbox: a list is written in plain markdown, and the two characters right after a list item's marker are read as the start of its own text on every surface.
|
|
1081
|
+
|
|
1082
|
+
| Export | Signature | Returns | Use it when |
|
|
1083
|
+
| ---------------------- | -------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------- |
|
|
1084
|
+
| `TASK_LIST_MARKER` | `const TASK_LIST_MARKER` | — | A list item's marker, immediately followed by `[ ]` or `[x]` and a space. |
|
|
1085
|
+
| `taskListMessage` | `function taskListMessage(marker)` | {string} The message, unpunctuated at the end as a finding is. | What a note opening a list item with `[ ]` or `[x]` is told. |
|
|
1086
|
+
| `checkTaskLists` | `function checkTaskLists(body, file,` | {Array<{file: string, line: number, column: number, severity: "warning", message: string}>} One finding per item, in source order. | Every list item opened with `[ ]` or `[x]` in one note's body. |
|
|
1087
|
+
| `lintContentTaskLists` | `function lintContentTaskLists(contentBase,` | `{{findings: Array<{file: string, line: number, column: number, severity: "warning", message: string}>, files: number}}` The findings, and how many files were read. | Walk a content tree and report every list item opened with `[ ]` or `[x]`. |
|
|
1088
|
+
|
|
1073
1089
|
### `engine.pathnames`
|
|
1074
1090
|
|
|
1075
1091
|
One authored pathname, and the four addresses it resolves to. A note names a file once — in `img:`, in `data.portrait:`, in the body of a markdown image — and the first segment says which package owns it when an `assets/` follows. Every surface derives its own address from that one statement: the path inside a Foundry install, the file in the owning repository's tree, the address the website serves, and where the book stages its copy.
|
|
@@ -1111,9 +1127,9 @@ Prepared SQL content tables are expanded into Markdown before link resolution.
|
|
|
1111
1127
|
A query selecting no rows reports an error unless its fence states
|
|
1112
1128
|
`{allow-empty=true}`.
|
|
1113
1129
|
|
|
1114
|
-
| Export | Signature
|
|
1115
|
-
| --------------------- |
|
|
1116
|
-
| `expandContentTables` | `expandContentTables(markdown, { source, sqlTables })` | `{markdown: string, errors: object[], lineMap: object[], warnings: object[]}` | Replace prepared SQL fences and preserve source locations. |
|
|
1130
|
+
| Export | Signature | Returns | Use it when |
|
|
1131
|
+
| --------------------- | ----------------------------------------------------------------- | ----------------------------------------------------------------------------- | -------------------------------------------------------------------------- |
|
|
1132
|
+
| `expandContentTables` | `expandContentTables(markdown, { source, sqlTables, pageLists })` | `{markdown: string, errors: object[], lineMap: object[], warnings: object[]}` | Replace prepared SQL fences and page lists, and preserve source locations. |
|
|
1117
1133
|
|
|
1118
1134
|
### `engine.helpers`
|
|
1119
1135
|
|
|
@@ -1250,6 +1266,8 @@ A note's markdown, and a document plan, rendered as Typst source. **This module
|
|
|
1250
1266
|
| `iconNamesIn` | `function iconNamesIn(markdown)` | {string[]} The names, in order of appearance, with repeats. | Every icon name a body uses, so a build can resolve them once. |
|
|
1251
1267
|
| `BOOK_IMAGE_WIDTHS` | `const BOOK_IMAGE_WIDTHS` | — | The book measure requested by each named image size. |
|
|
1252
1268
|
| `bookTypstPreamble` | `function bookTypstPreamble()` | {string} Typst markup. | The Typst definitions the book's page furniture is drawn with. |
|
|
1269
|
+
| `stripHtmlComments` | `function stripHtmlComments(markdown)` | {string} The same body, its comments replaced by newlines. | A markdown body with its HTML comments taken out. |
|
|
1270
|
+
| `absolutePageUrl` | `function absolutePageUrl(url, site)` | {string} An absolute URL a reader of the book can follow. | A page address as a reader of the book can follow it. |
|
|
1253
1271
|
|
|
1254
1272
|
### `engine.pdfFonts`
|
|
1255
1273
|
|
|
@@ -1288,15 +1306,15 @@ The content tree, built into a book. The I/O half of the PDF surface: it reads t
|
|
|
1288
1306
|
|
|
1289
1307
|
Journals pack compiler — produces JSON pack files for the "journals" Foundry compendium from markdown notes in the `assets/content/` tree. The content root (`contentBase`) is walked recursively; any `.md` file whose frontmatter declares either `type: doc` or a **doc-carrying type** ({@link sohl.utils.packs.docEntryTypes} — every item type, plus `macro`) is compiled into one JournalEntry document. Each note's body is split on top-level H1 headings; the optional content before the first H1 becomes a lead page, and each subsequent H1 starts a new page named after its heading text. All page bodies are rendered to HTML.
|
|
1290
1308
|
|
|
1291
|
-
| Export | Signature | Returns
|
|
1292
|
-
| --------------------- | --------------------------------------------------------------------------- |
|
|
1293
|
-
| `splitPages` | `function splitPages(body, leadName` | —
|
|
1294
|
-
| `assertUniquePages` | `function assertUniquePages(rawPages, noteName)` | —
|
|
1295
|
-
| `assertUniqueAnchors` | `function assertUniqueAnchors(rawPages, noteName)` | —
|
|
1296
|
-
| `journalPageId` | `function journalPageId(entryId, page)` | {string} A 16-character Foundry id.
|
|
1297
|
-
| `buildPages` | `function buildPages(rawPages, entryId, noteName)` | {Array<{_id: string, name: string, type: string, title: {show: boolean, level: number}, text: {format: number, content: string}, _key: string}>} The page documents, in order. | Compile split pages into JournalEntryPage documents. |
|
|
1298
|
-
| `buildJournalEntry` | `buildJournalEntry({ id, name, markdown, leadName, folder, flags, stats })` | {object} The JournalEntry document, keyed for the pack.
|
|
1299
|
-
| `Journals` | `class Journals extends BasePackCompiler` | —
|
|
1309
|
+
| Export | Signature | Returns | Use it when |
|
|
1310
|
+
| --------------------- | --------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------- |
|
|
1311
|
+
| `splitPages` | `function splitPages(body, leadName` | — | Splits a markdown body into pages by top-level H1 headings. |
|
|
1312
|
+
| `assertUniquePages` | `function assertUniquePages(rawPages, noteName)` | — | Two pages in one note that would derive the same id, which the LevelDB packer reports only as an opaque duplicate-key collision. |
|
|
1313
|
+
| `assertUniqueAnchors` | `function assertUniqueAnchors(rawPages, noteName)` | — | The anchor half of {@link assertUniquePages}, under its former name. |
|
|
1314
|
+
| `journalPageId` | `function journalPageId(entryId, page)` | {string} A 16-character Foundry id. | The id of one page within its entry. |
|
|
1315
|
+
| `buildPages` | `function buildPages(rawPages, entryId, noteName)` | {Array<{\_id: string, name: string, type: string, title: {show: boolean, level: number}, text: {format: number, content: string}, \_key: string}>} The page documents, in order. | Compile split pages into JournalEntryPage documents. |
|
|
1316
|
+
| `buildJournalEntry` | `buildJournalEntry({ id, name, markdown, leadName, folder, flags, stats })` | {object} The JournalEntry document, keyed for the pack. | Assemble one JournalEntry document from a note's converted markdown. |
|
|
1317
|
+
| `Journals` | `class Journals extends BasePackCompiler` | — | not called directly — imported and driven by `engine/generate.mjs` as the "journals" pack compiler |
|
|
1300
1318
|
|
|
1301
1319
|
### `engine.macros`
|
|
1302
1320
|
|
|
@@ -1361,7 +1379,7 @@ Bundles, as notes — the Foundry `Adventure` a `type: bundle` note compiles int
|
|
|
1361
1379
|
| `BUNDLE_TYPE` | `const BUNDLE_TYPE` | — | The note type a bundle is authored as. |
|
|
1362
1380
|
| `ADVENTURE_CONTENT_FIELD` | `const ADVENTURE_CONTENT_FIELD` | — | Foundry's `Adventure.contentFields`, keyed by the document class each holds. |
|
|
1363
1381
|
| `CONTENTS_FIELD` | `const CONTENTS_FIELD` | — | The `data:` key a bundle lists its members under. |
|
|
1364
|
-
| `stripAdventureKeys` | `function stripAdventureKeys(value)` | {
|
|
1382
|
+
| `stripAdventureKeys` | `function stripAdventureKeys(value)` | {\*} The same shape with every `_key` removed. | Strip the LevelDB keys from a document tree. |
|
|
1365
1383
|
| `bareAddress` | `function bareAddress(value)` | {string\|null} The bare address, or `null` for a blank entry. | An authored address with any wikilink brackets and label stripped. |
|
|
1366
1384
|
| `bundleContents` | `function bundleContents(fm)` | {string[]} The bare addresses. | The addresses a bundle note names, in the order it names them. |
|
|
1367
1385
|
| `missingMemberVerdict` | `function missingMemberVerdict(packSystem)` | {"omit"\|"fail"} What to do about a member the sources lack. | What a pass should do about a member its sources do not hold. |
|
|
@@ -9,9 +9,11 @@ subType: howto
|
|
|
9
9
|
|
|
10
10
|
A canonical date uses the world's year and day count without choosing a culture's calendar. Write `<year>[.<day>[:HHMMSS]]`: `720` covers year 720, `720.136` covers its day 136, and `720.136:143005` identifies 14:30:05 on that day. The dot separates the year from the **day of year**, not a month. Negative years are valid, and the day must fit the world's declared year length. Prefix a date with `~` when its uncertainty extends outside the stated year, day, or second. `unknown` records an occurrence whose date is not known, in fields that permit it.
|
|
11
11
|
|
|
12
|
+
**Put a date that states a day in quotes.** Write `born: "667.130"`, not `born: 667.130`. Unquoted, YAML reads the scalar as a number, and a number keeps no trailing zero — `667.130` becomes `667.13`, which is day 13 rather than day 130, and the authored digits are gone before any reader of the date can object. A date whose lost zero would still name a day inside the world's year is therefore refused, naming the line and both readings, so quoting it is the fix. A bare year such as `born: 667` loses nothing and needs no quotes.
|
|
13
|
+
|
|
12
14
|
## Define a calendar
|
|
13
15
|
|
|
14
|
-
A `lore` note with `subType: calendar` names months, weekdays, seasons, named days, eras, and date formats. Its `data.epoch` is the **canonical day that equals calendar year 1, day 1**. For a calendar beginning on canonical day `1.91`, write `epoch: 1.91
|
|
16
|
+
A `lore` note with `subType: calendar` names months, weekdays, seasons, named days, eras, and date formats. Its `data.epoch` is the **canonical day that equals calendar year 1, day 1**. For a calendar beginning on canonical day `1.91`, write `epoch: "1.91"` — quoted, like every canonical date that states a day: calendar year 1 still starts at its own day 1. A calendar does not state the length of the world year: its months must sum to `data.year.days` on the world's `place` note.
|
|
15
17
|
|
|
16
18
|
```yaml
|
|
17
19
|
shortcode: commoncal
|
|
@@ -19,7 +21,7 @@ name: { full: The Common Calendar }
|
|
|
19
21
|
type: lore
|
|
20
22
|
subType: calendar
|
|
21
23
|
data:
|
|
22
|
-
epoch: 1.1
|
|
24
|
+
epoch: "1.1"
|
|
23
25
|
months:
|
|
24
26
|
- { name: Floralis, abbreviation: Flor, days: 30 }
|
|
25
27
|
- { name: Lusenar, abbreviation: Luse, days: 31 }
|
|
@@ -7,7 +7,7 @@ subType: howto
|
|
|
7
7
|
|
|
8
8
|
# Frontmatter and system blocks
|
|
9
9
|
|
|
10
|
-
HeroicLands notes begin with YAML frontmatter. A file is recognized as a note when its opening frontmatter has nonempty `shortcode` and `type` values. Ordinary Markdown files need neither. Keep top-level keys in this order, omitting keys that do not apply: `shortcode`, `name`, `type`, `subType`, `description`, `tags`, `data`, `hm3`, `sohl`. Any other top-level key is an error.
|
|
10
|
+
HeroicLands notes begin with YAML frontmatter. A file is recognized as a note when its opening frontmatter has nonempty `shortcode` and `type` values. Ordinary Markdown files need neither. Keep top-level keys in this order, omitting keys that do not apply: `shortcode`, `name`, `type`, `subType`, `description`, `tags`, `data`, `dnd5e`, `hm3`, `sohl`. Any other top-level key is an error at its own line, naming the closed vocabulary and the declared key it was most likely meant to be. The [note-type reference](../reference/note-types.md#top-level-keys) says what each one means.
|
|
11
11
|
|
|
12
12
|
```yaml
|
|
13
13
|
---
|
|
@@ -35,7 +35,7 @@ The [note-type reference](../reference/note-types.md) lists every field accepted
|
|
|
35
35
|
|
|
36
36
|
A being's `data.archetypes` names the roles it can fill in an adventure. Choose every specific role that fits, such as `entertainer` for a performer or `guildsperson` for someone whose professional training or connections matter. Use `[commoner]` when no specific archetype fits. `commoner` cannot share the list with another value. The [archetype reference](../reference/format-details.md#actors) defines the available roles.
|
|
37
37
|
|
|
38
|
-
A `sohl:` or `
|
|
38
|
+
A `sohl:`, `hm3:` or `dnd5e:` block holds values for that Foundry system. Its `system:` child contains values sent to the system's DataModel. The system block can override shared routing and art where the declared field permits it. `data.pack` and `data.packFolder` are shared routes; a system-specific route belongs in that system block. The [detailed mapping reference](../reference/format-details.md#mappings-every-type-shares) and [system-block explanation](../reference/format-details.md#frontmatter-has-three-regions) cover precedence and emitted fields.
|
|
39
39
|
|
|
40
40
|
One body describes the subject for both systems. A being's `{#appearance}` and `{#dossier}` sections feed SoHL Actor `system.appearance` and `system.dossier`, and HM3 Actor `system.description` and `system.biography`. An Item's `{#description}` page becomes a shared JournalEntryPage. Its resolved `@UUID[...]` pointer is stored in SoHL Item `system.docHtml` and HM3 Item `system.description`. An Item with no `{#description}` anchor uses its first page. Other H1 pages remain linkable.
|
|
41
41
|
|