@heroiclands/package-build 23.12.5 → 23.12.7
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 +22 -0
- package/assets/styles/content-base.css +25 -0
- package/bin/content-commands.mjs +264 -3
- package/bin/package-build.mjs +25 -0
- package/content-config.mjs +98 -0
- package/docs/api.md +18 -14
- package/docs/authoring/dates-and-calendars.md +29 -1
- package/docs/authoring/links-and-markup.md +442 -50
- package/docs/commands.md +127 -0
- package/docs/configuration.md +90 -3
- package/docs/content-format.md +3 -1
- package/docs/guides/book.md +11 -0
- package/docs/guides/module-project.md +17 -1
- package/docs/guides/site.md +19 -0
- package/docs/reference/format-details.md +434 -23
- package/docs/reference/note-types.md +45 -21
- package/engine/base-compiler.mjs +2 -0
- package/engine/bundles.mjs +7 -2
- package/engine/compile-corpus.mjs +3 -1
- package/engine/content-charset.mjs +21 -2
- package/engine/content-format.yaml +1 -2
- package/engine/content-index.mjs +33 -2
- package/engine/content-links.mjs +3 -0
- package/engine/content-poetry.mjs +22 -4
- package/engine/content-scripts.mjs +1219 -0
- package/engine/content-session.mjs +2 -0
- package/engine/data-keys.mjs +5 -1
- package/engine/dictionary-layout.mjs +18 -12
- package/engine/dictionary-rows.mjs +11 -2
- package/engine/event-rows.mjs +63 -18
- package/engine/event-views.mjs +28 -15
- package/engine/helpers.mjs +8 -2
- package/engine/index-records.mjs +2 -0
- package/engine/infobox.mjs +4 -8
- package/engine/journals.mjs +14 -3
- package/engine/language-data.mjs +137 -1
- package/engine/language-tag.mjs +103 -0
- package/engine/language-terms.mjs +16 -4
- package/engine/map-lexicon.mjs +762 -0
- package/engine/markdown-expressions.mjs +72 -17
- package/engine/markup-validation.mjs +17 -3
- package/engine/names.mjs +1 -1
- package/engine/note-calendars.mjs +314 -0
- package/engine/note-vocabulary.mjs +124 -16
- package/engine/pdf-build.mjs +28 -10
- package/engine/pdf-render.mjs +116 -11
- package/engine/reckoning-markers.mjs +63 -3
- package/engine/run-rows.mjs +119 -0
- package/engine/run-words.mjs +266 -0
- package/engine/scenes.mjs +1 -0
- package/engine/script-font-delivery.mjs +321 -0
- package/engine/script-fonts.mjs +172 -0
- package/engine/site-build.mjs +10 -3
- package/engine/site-config.mjs +5 -0
- package/engine/site-root.mjs +19 -5
- package/engine/sql-tables.mjs +120 -4
- package/engine/text-maps.mjs +607 -0
- package/engine/verb-pattern.mjs +99 -0
- package/engine/word-pattern.mjs +27 -0
- package/engine/word-rows.mjs +8 -2
- package/engine/word-terms.mjs +44 -0
- package/hugo-theme/layouts/_default/baseof.html +3 -0
- package/hugo-theme/static/css/style.css +10 -0
- package/manifest.mjs +9 -1
- package/package.json +1 -1
- package/types/content-config.d.mts +38 -0
- package/types/engine/compile-corpus.d.mts +2 -1
- package/types/engine/content-index.d.mts +17 -3
- package/types/engine/content-poetry.d.mts +7 -0
- package/types/engine/content-scripts.d.mts +226 -0
- package/types/engine/data-keys.d.mts +2 -2
- package/types/engine/dictionary-rows.d.mts +15 -0
- package/types/engine/event-rows.d.mts +16 -1
- package/types/engine/event-views.d.mts +2 -1
- package/types/engine/helpers.d.mts +5 -1
- package/types/engine/journals.d.mts +7 -2
- package/types/engine/language-data.d.mts +2 -0
- package/types/engine/language-tag.d.mts +8 -0
- package/types/engine/language-terms.d.mts +2 -1
- package/types/engine/map-lexicon.d.mts +274 -0
- package/types/engine/markup-validation.d.mts +7 -2
- package/types/engine/note-calendars.d.mts +60 -0
- package/types/engine/pdf-build.d.mts +26 -3
- package/types/engine/pdf-render.d.mts +17 -2
- package/types/engine/reckoning-markers.d.mts +32 -1
- package/types/engine/run-rows.d.mts +89 -0
- package/types/engine/run-words.d.mts +86 -0
- package/types/engine/script-font-delivery.d.mts +134 -0
- package/types/engine/script-fonts.d.mts +168 -0
- package/types/engine/site-build.d.mts +2 -1
- package/types/engine/site-root.d.mts +11 -3
- package/types/engine/text-maps.d.mts +175 -0
- package/types/engine/verb-pattern.d.mts +65 -0
- package/types/engine/word-pattern.d.mts +13 -0
- package/types/engine/word-terms.d.mts +6 -0
|
@@ -32,14 +32,15 @@ These are the only keys a note writes at its top level, in the order the formatt
|
|
|
32
32
|
|
|
33
33
|
These fields are accepted by every note type. A field's value can still be irrelevant to a particular output; the build reports that where it can. `data.icon` is not shared: it is listed under each type whose document carries art.
|
|
34
34
|
|
|
35
|
-
| Field | Shape | Meaning
|
|
36
|
-
| ----------------- | ----------------------------------------------- |
|
|
37
|
-
| `data.seeAlso` | list of `{ address, gloss? }` | Deliberate cross-references in reading order, with optional glosses; shown on the site, in journals and in books.
|
|
38
|
-
| `data.
|
|
39
|
-
| `data.
|
|
40
|
-
| `data.
|
|
41
|
-
| `data.
|
|
42
|
-
| `data.
|
|
35
|
+
| Field | Shape | Meaning |
|
|
36
|
+
| ----------------- | ----------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
37
|
+
| `data.seeAlso` | list of `{ address, gloss? }` | Deliberate cross-references in reading order, with optional glosses; shown on the site, in journals and in books. |
|
|
38
|
+
| `data.calendar` | an Address | The calendar this note's dates print in, as a calendar lore note. Absent, the note takes its culture's calendar, then — for a place — its parents', then the configured `defaultCalendar`. |
|
|
39
|
+
| `data.id` | string | A fixed document identity when the derived identity is unsuitable. |
|
|
40
|
+
| `data.pack` | string | The shared compendium route, with a system block overriding it. |
|
|
41
|
+
| `data.packFolder` | an Address, or a map of Addresses keyed by pack | The shared compendium folder Address, with a system block overriding it. |
|
|
42
|
+
| `data.harnworld` | `{ realm?, ritual? }` | HârnWorld source details shared by every system — `realm` and `ritual`. |
|
|
43
|
+
| `data.banner` | an Address | The page's hero image — an `image` address, cut to 1792×768, or `none` to decline a hero image. Reaches no compiled document. |
|
|
43
44
|
|
|
44
45
|
**Inner keys** — a key a field's value does not declare is an error at its own line and column.
|
|
45
46
|
|
|
@@ -71,7 +72,6 @@ These fields are accepted by every note type. A field's value can still be irrel
|
|
|
71
72
|
| `data.gender` | string | One of `female`, `male`, `nonbinary`, `none` or `other`. `none` says the being has no gender; an absent field says its gender is unrecorded. See [a being's look](format-details.md#a-beings-look) for what each word means. |
|
|
72
73
|
| `data.species` | an Address | The being's species, as a lore note. |
|
|
73
74
|
| `data.born` | string | When the being was born — a date, or `unknown` where the birth is unrecorded. Absent, the being was never born. |
|
|
74
|
-
| `data.calendar` | an Address | Calendar that displays this being's dates. |
|
|
75
75
|
| `data.died` | string | When the being died — a date, or `unknown` where the death is unrecorded. Absent, the being is alive. |
|
|
76
76
|
| `data.age` | string | Age in years, stated only to override what `born` says — `34`, or `~34` for an estimate. Unstated beside a dated `born` it is computed; unstated beside an unknown or absent `born` the age is unknown. |
|
|
77
77
|
| `data.ageYears` | number | Written by the compiler beside an `age` estimate — the `~` stripped, the magnitude alone. A note never authors this. |
|
|
@@ -507,6 +507,10 @@ These fields are accepted by every note type. A field's value can still be irrel
|
|
|
507
507
|
| `data.words[].tones` | a string | no | A tone pattern, such as `LHL`. |
|
|
508
508
|
| `data.words[].root` | a string | no | A consonantal skeleton, such as `d-l-k`. |
|
|
509
509
|
| `data.words[].frame` | a frame of the language's `sounds.frames` | no | The frame a root is set in, named in the language's `sounds.frames`. |
|
|
510
|
+
| `data.words[].native` | a list of `{ map, font?, text }` | no | The word's native form under a map, stated only where the map's rows spell it differently. Its derived forms are spelled by the rows. |
|
|
511
|
+
| `data.words[].native[].map` | a mapping note's `data.map.name` | yes | The map the native form is written for. |
|
|
512
|
+
| `data.words[].native[].font` | a face the map lists | no | The one face the native form is for; unstated, it is for every face the map lists, and a form stated for a face wins over it there. |
|
|
513
|
+
| `data.words[].native[].text` | the word in the script, as the map's characters | yes | The word as the script writes it, where the map's rows would write it otherwise. |
|
|
510
514
|
| `data.affixes[].form` | the affix as written, such as `-rith` | no | The affix as a word's `built` names it; absent on a zero affix. |
|
|
511
515
|
| `data.affixes[].zero` | `true` | no | The affix is written as nothing, such as a case with no ending; it states no `form`, prints as `∅`, and no `built` piece names it. |
|
|
512
516
|
| `data.affixes[].kind` | one of case, derivation, tense, aspect, mood, voice, name, lineage | no | What the affix makes. |
|
|
@@ -523,15 +527,16 @@ These fields are accepted by every note type. A field's value can still be irrel
|
|
|
523
527
|
| `data.grammar.plural` | list of `{ ending, becomes }` | no | Default noun plurals, replacing the first matching literal ending in authored order. A word's explicit plural wins; no word is checked against this rule. |
|
|
524
528
|
| `data.grammar.plural[].ending` | a literal ending string, which may be empty | yes | The bare suffix replaced by this rule; empty matches every noun. |
|
|
525
529
|
| `data.grammar.plural[].becomes` | a literal ending string, which may be empty | yes | The bare replacement; empty removes the matching suffix. |
|
|
526
|
-
| `data.grammar.verbs` | a map — `{ invariant: true }` or `{ persons: { I, you, heShe, we, youAll, they } }`
|
|
530
|
+
| `data.grammar.verbs` | a map — `{ invariant: true }` or `{ persons: { I, you, heShe, we, youAll, they }, replaces? }` | no | One optional regular present pattern, printed once at the dictionary head. |
|
|
527
531
|
| `data.grammar.verbs.invariant` | true, as the alternative to persons | no | True where verbs keep the citation form for every person; excludes `persons`. |
|
|
528
|
-
| `data.grammar.verbs.persons` | a map — `{ I, you, heShe, we, youAll, they }` of ending strings | no | All six present endings, each added to the citation form
|
|
529
|
-
| `data.grammar.verbs.persons.I` | an ending string, empty for unchanged | yes | The ending added to the citation form
|
|
530
|
-
| `data.grammar.verbs.persons.you` | an ending string, empty for unchanged | yes | The ending added to the citation form
|
|
531
|
-
| `data.grammar.verbs.persons.heShe` | an ending string, empty for unchanged | yes | The ending added to the citation form
|
|
532
|
-
| `data.grammar.verbs.persons.we` | an ending string, empty for unchanged | yes | The ending added to the citation form
|
|
533
|
-
| `data.grammar.verbs.persons.youAll` | an ending string, empty for unchanged | yes | The ending added to the citation form
|
|
534
|
-
| `data.grammar.verbs.persons.they` | an ending string, empty for unchanged | yes | The ending added to the citation form
|
|
532
|
+
| `data.grammar.verbs.persons` | a map — `{ I, you, heShe, we, youAll, they }` of ending strings | no | All six present endings, each added to the citation form or in place of its closing `replaces`; excludes `invariant`. |
|
|
533
|
+
| `data.grammar.verbs.persons.I` | an ending string, empty for unchanged | yes | The ending for I, added to the citation form or in place of its closing `replaces`. |
|
|
534
|
+
| `data.grammar.verbs.persons.you` | an ending string, empty for unchanged | yes | The ending for you, added to the citation form or in place of its closing `replaces`. |
|
|
535
|
+
| `data.grammar.verbs.persons.heShe` | an ending string, empty for unchanged | yes | The ending for he/she, added to the citation form or in place of its closing `replaces`. |
|
|
536
|
+
| `data.grammar.verbs.persons.we` | an ending string, empty for unchanged | yes | The ending for we, added to the citation form or in place of its closing `replaces`. |
|
|
537
|
+
| `data.grammar.verbs.persons.youAll` | an ending string, empty for unchanged | yes | The ending for you all, added to the citation form or in place of its closing `replaces`. |
|
|
538
|
+
| `data.grammar.verbs.persons.they` | an ending string, empty for unchanged | yes | The ending for they, added to the citation form or in place of its closing `replaces`. |
|
|
539
|
+
| `data.grammar.verbs.replaces` | the closing letters every ending takes the place of | no | Letters a citation form closes on, swapped for each `persons` ending: with `then`, `lokathen` and `zor` give `lokazor`. A citation form closing otherwise takes the ending whole. With `persons` only. |
|
|
535
540
|
| `data.tongues[].tag` | a tag such as `dunhari`, or `exonym:<tongue>` | yes | The tag a register row's `tongue` names: lowercase letters and digits, or a tag closing on `:<tongue>`, which declares one tag per tongue named after the colon. |
|
|
536
541
|
| `data.tongues[].meaning` | a string | yes | What a name of this tongue is. |
|
|
537
542
|
| `data.tongues[].language` | a language skill Address | no | The language note the tag names, where it is one. |
|
|
@@ -867,7 +872,6 @@ Lore records in-world knowledge. A `culture` describes a people; a `custom` desc
|
|
|
867
872
|
| Field | Shape | Meaning |
|
|
868
873
|
| -------------------------------------- | ----------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
869
874
|
| `data.events` | list of event entries | What happened to or at this subject — each entry one dated, attributed event: a founding, a war, a fall, or an occasion that recurs. |
|
|
870
|
-
| `data.calendar` | an Address | Calendar that displays this place's dates. |
|
|
871
875
|
| `data.demonym` | string | What a person from this place is called — a Vylarian. |
|
|
872
876
|
| `data.purpose` | string | The reason this settlement, site, or structure exists, selected from its placeCharacter tags. |
|
|
873
877
|
| `data.lore` | list of Addresses | Lore concerning this place — its peoples, its law, its calendar, its history. |
|
|
@@ -963,9 +967,29 @@ Lore records in-world knowledge. A `culture` describes a people; a `custom` desc
|
|
|
963
967
|
|
|
964
968
|
**Subtypes:** `rules`, `userguide`, `reference`, `howto`, `concept`, `settingguide`.
|
|
965
969
|
|
|
966
|
-
| Field | Shape
|
|
967
|
-
| -------------- |
|
|
968
|
-
| `data.culture` | an Address
|
|
970
|
+
| Field | Shape | Meaning |
|
|
971
|
+
| -------------- | ---------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
972
|
+
| `data.culture` | an Address | The culture a setting guide introduces, as a culture lore note. Required on `settingguide`; no other subType states it. |
|
|
973
|
+
| `data.map` | a map — `{ name, vowels?, fonts, rows }` | A mapping, which text marked `map=` with its `name` is rewritten by before it is set. See [mapping notes](format-details.md#mapping-notes). |
|
|
974
|
+
|
|
975
|
+
**Inner keys** — a key a field's value does not declare is an error at its own line and column.
|
|
976
|
+
|
|
977
|
+
| Key | Shape | Required | Meaning |
|
|
978
|
+
| -------------------------------- | ---------------------------------------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
979
|
+
| `data.map.name` | string | yes | What `map=` names the map by: lowercase letters, digits and `-`, unique in the package. |
|
|
980
|
+
| `data.map.vowels` | string | no | The characters a row's `before: vowel` reads as vowels; any other letter is a consonant. |
|
|
981
|
+
| `data.map.rows` | list of `[from, to]` or `{ from, to, before?, at? }` | yes | The rewrites, each `[from, to]` or `{ from, to, before?, at? }`: `before` is `vowel` or `consonant`, `at` is `start` or `end`. The longest `from` wins, and a character no row matches passes through. |
|
|
982
|
+
| `data.map.rows[].from` | a string of one or more characters | yes | The one or more characters the row matches. |
|
|
983
|
+
| `data.map.rows[].to` | a string, which may be empty | yes | What the row writes in their place; empty to drop them. |
|
|
984
|
+
| `data.map.rows[].before` | string | no | `vowel` or `consonant`: what must follow the match for the row to apply. |
|
|
985
|
+
| `data.map.rows[].at` | string | no | `start` or `end`: where the match must sit for the row to apply. |
|
|
986
|
+
| `data.map.fonts` | list of `{ font, rows? }` | yes | The faces the map is drawn in, at least one, each with any rows of its own. A run in another face is refused, and each face must draw every character its rows and the lexicon's native forms write. |
|
|
987
|
+
| `data.map.fonts[].font` | an installed face's slug | yes | A face the map is drawn in, by the slug `font=` names it by. |
|
|
988
|
+
| `data.map.fonts[].rows` | list of `[from, to]` or `{ from, to, before?, at? }` | no | Rows for this face alone, tried before the shared `rows`; the same shapes and conditions. |
|
|
989
|
+
| `data.map.fonts[].rows[].from` | a string of one or more characters | yes | The one or more characters the row matches. |
|
|
990
|
+
| `data.map.fonts[].rows[].to` | a string, which may be empty | yes | What the row writes in their place; empty to drop them. |
|
|
991
|
+
| `data.map.fonts[].rows[].before` | string | no | `vowel` or `consonant`: what must follow the match for the row to apply. |
|
|
992
|
+
| `data.map.fonts[].rows[].at` | string | no | `start` or `end`: where the match must sit for the row to apply. |
|
|
969
993
|
|
|
970
994
|
### macro
|
|
971
995
|
|
package/engine/base-compiler.mjs
CHANGED
|
@@ -66,6 +66,7 @@ import { authoredFrontmatter } from "./index-records.mjs";
|
|
|
66
66
|
import { markupAnchorFindings } from "./anchors.mjs";
|
|
67
67
|
import { scanSpans } from "./content-spans.mjs";
|
|
68
68
|
import { scanBlocks } from "./content-blocks.mjs";
|
|
69
|
+
import { scanRuns } from "./content-scripts.mjs";
|
|
69
70
|
import { scanHeadingAttributes, withheldSections } from "./heading-attributes.mjs";
|
|
70
71
|
import { scanFigures } from "./content-figures.mjs";
|
|
71
72
|
import { footnoteFindings } from "./content-footnotes.mjs";
|
|
@@ -691,6 +692,7 @@ export class BasePackCompiler {
|
|
|
691
692
|
...(scanned?.blocks ?? scanBlocks(body)).errors,
|
|
692
693
|
...(scanned?.alerts ?? scanAlerts(body)).errors,
|
|
693
694
|
...(scanned?.spans ?? scanSpans(body)).errors,
|
|
695
|
+
...scanRuns(body, this.corpus?.runs, syntax).errors,
|
|
694
696
|
...markupAnchorFindings(body, syntax),
|
|
695
697
|
]) {
|
|
696
698
|
findings.push({
|
package/engine/bundles.mjs
CHANGED
|
@@ -332,8 +332,13 @@ export class Bundles extends BasePackCompiler {
|
|
|
332
332
|
// it compiles into already has somewhere to put the prose.
|
|
333
333
|
description:
|
|
334
334
|
markdown.trim() ?
|
|
335
|
-
renderFoundryMarkdown(
|
|
336
|
-
|
|
335
|
+
renderFoundryMarkdown(
|
|
336
|
+
markdown,
|
|
337
|
+
undefined,
|
|
338
|
+
undefined,
|
|
339
|
+
undefined,
|
|
340
|
+
(pathname) => roles.get(pathname),
|
|
341
|
+
this.corpus?.runs,
|
|
337
342
|
)
|
|
338
343
|
: "",
|
|
339
344
|
folder: this.folderResolver(authoredFolder, { isAddress: folderIsAddress }),
|
|
@@ -41,6 +41,7 @@
|
|
|
41
41
|
import { indexRecordsFor } from "./content-index.mjs";
|
|
42
42
|
import { buildContentLinkIndex } from "./helpers.mjs";
|
|
43
43
|
import { loadPackConfig } from "./pack-config.mjs";
|
|
44
|
+
import { runContext } from "./content-scripts.mjs";
|
|
44
45
|
import { prepareTreeSqlTables } from "./sql-tables.mjs";
|
|
45
46
|
|
|
46
47
|
/**
|
|
@@ -61,7 +62,7 @@ import { prepareTreeSqlTables } from "./sql-tables.mjs";
|
|
|
61
62
|
* the index refuses is a note the compile must still *report*, exactly as the
|
|
62
63
|
* compile loop reported it when the loop was the first to see it.
|
|
63
64
|
* @param {import("./content-session.mjs").ContentSession} [opts.session] - Captured context shared with other operations.
|
|
64
|
-
* @returns {Promise<{currentDate: string, records: object[], parsed: Map<string, object>, linkIndex: object, sqlTables: Map<string, object[]>|undefined, problems: object[], syntax?: import("./content-session.mjs").ContentSyntax}>} The corpus,
|
|
65
|
+
* @returns {Promise<{currentDate: string, runs: import("./content-scripts.mjs").RunContext, records: object[], parsed: Map<string, object>, linkIndex: object, sqlTables: Map<string, object[]>|undefined, problems: object[], syntax?: import("./content-session.mjs").ContentSyntax}>} The corpus,
|
|
65
66
|
* its indexes, and the notes it could not record.
|
|
66
67
|
*/
|
|
67
68
|
export async function buildCompileCorpus({
|
|
@@ -100,6 +101,7 @@ export async function buildCompileCorpus({
|
|
|
100
101
|
const sqlTables = await prepareTreeSqlTables(contentBase, sqlOptions);
|
|
101
102
|
return {
|
|
102
103
|
currentDate: resolved.currentDate,
|
|
104
|
+
runs: runContext(resolved, records),
|
|
103
105
|
records,
|
|
104
106
|
parsed,
|
|
105
107
|
problems: collected,
|
|
@@ -162,6 +162,19 @@ export const NOTATION = Object.freeze(
|
|
|
162
162
|
]),
|
|
163
163
|
);
|
|
164
164
|
|
|
165
|
+
/**
|
|
166
|
+
* Tier 4 — the blocks the HeroicLands script fonts are registered in, so a
|
|
167
|
+
* mapping note or a `font=` run never draws a warning for a script character.
|
|
168
|
+
*
|
|
169
|
+
* @type {ReadonlyArray<readonly [number, number]>}
|
|
170
|
+
*/
|
|
171
|
+
const SCRIPT_BLOCKS = Object.freeze([
|
|
172
|
+
[0xf0000, 0xf2fff], // plane 15: Pirzath, Thuravark, Semaron, the reserved alphabets, Teng Chou
|
|
173
|
+
[0x1e960, 0x1ec6f], // Zethu overflow, Qalezu, Zethu
|
|
174
|
+
[0x1ecc0, 0x1ecff], // Qalezu catch-before forms
|
|
175
|
+
[0x13430, 0x1345f], // the Egyptian format controls Zethu uses
|
|
176
|
+
]);
|
|
177
|
+
|
|
165
178
|
/**
|
|
166
179
|
* Whether the charset admits a code point anywhere in a note.
|
|
167
180
|
*
|
|
@@ -169,7 +182,12 @@ export const NOTATION = Object.freeze(
|
|
|
169
182
|
* @returns {boolean} Whether it is allowed outside a code fence.
|
|
170
183
|
*/
|
|
171
184
|
export function isAllowedCodePoint(cp) {
|
|
172
|
-
return
|
|
185
|
+
return (
|
|
186
|
+
isLetterTier(cp) ||
|
|
187
|
+
TYPOGRAPHY.has(cp) ||
|
|
188
|
+
NOTATION.has(cp) ||
|
|
189
|
+
SCRIPT_BLOCKS.some(([from, to]) => cp >= from && cp <= to)
|
|
190
|
+
);
|
|
173
191
|
}
|
|
174
192
|
|
|
175
193
|
/**
|
|
@@ -362,7 +380,8 @@ export function checkText(text, file) {
|
|
|
362
380
|
line: i + 1,
|
|
363
381
|
column,
|
|
364
382
|
severity: /** @type {const} */ ("warning"),
|
|
365
|
-
|
|
383
|
+
// A Private Use character draws as nothing, so quoting it leaves an empty span.
|
|
384
|
+
message: `${/\p{Co}/u.test(ch) ? "" : `\`${ch}\` `}${hex(cp)} is ${refusalFor(cp)}`,
|
|
366
385
|
});
|
|
367
386
|
}
|
|
368
387
|
}
|
|
@@ -15,7 +15,6 @@ types:
|
|
|
15
15
|
- gender
|
|
16
16
|
- species
|
|
17
17
|
- born
|
|
18
|
-
- calendar
|
|
19
18
|
- died
|
|
20
19
|
- age
|
|
21
20
|
- ageYears
|
|
@@ -315,7 +314,6 @@ types:
|
|
|
315
314
|
place:
|
|
316
315
|
data:
|
|
317
316
|
- events
|
|
318
|
-
- calendar
|
|
319
317
|
- demonym
|
|
320
318
|
- purpose
|
|
321
319
|
- lore
|
|
@@ -365,6 +363,7 @@ types:
|
|
|
365
363
|
doc:
|
|
366
364
|
data:
|
|
367
365
|
- culture
|
|
366
|
+
- map
|
|
368
367
|
subTypes:
|
|
369
368
|
- rules
|
|
370
369
|
- userguide
|
package/engine/content-index.mjs
CHANGED
|
@@ -86,6 +86,7 @@
|
|
|
86
86
|
*/
|
|
87
87
|
|
|
88
88
|
import { isAddressSegment } from "./address-charset.mjs";
|
|
89
|
+
import { configFileIn, resolveNoteCalendars } from "./note-calendars.mjs";
|
|
89
90
|
import { cultureInheritance, reportCultureParents } from "./culture-parents.mjs";
|
|
90
91
|
import {
|
|
91
92
|
formatDiagnostic,
|
|
@@ -105,6 +106,7 @@ import { AddressLink } from "./address-values.mjs";
|
|
|
105
106
|
import { scanSpans } from "./content-spans.mjs";
|
|
106
107
|
import { rebaseNameSpans } from "./name-markup.mjs";
|
|
107
108
|
import { analyzeNameSpans } from "./names.mjs";
|
|
109
|
+
import { recordRuns } from "./run-rows.mjs";
|
|
108
110
|
import fs from "node:fs";
|
|
109
111
|
import path from "node:path";
|
|
110
112
|
|
|
@@ -162,7 +164,7 @@ import { resolveNoteId } from "./note-ids.mjs";
|
|
|
162
164
|
// The `package:` refusal, so a note declaring one is reported where it is
|
|
163
165
|
// rather than as a bare abort.
|
|
164
166
|
import { assertNoDeclaredPackage } from "./note-package.mjs";
|
|
165
|
-
import { loadPackConfig } from "./pack-config.mjs";
|
|
167
|
+
import { CONFIG_FILENAMES, loadPackConfig } from "./pack-config.mjs";
|
|
166
168
|
// The record accessors, which live apart so that a module the compilers load
|
|
167
169
|
// can read a record without importing this one and closing a cycle.
|
|
168
170
|
// Re-exported because this is where callers have always addressed them.
|
|
@@ -447,6 +449,8 @@ function assertNoDerivedKeys(frontmatter, relPath, absPath, contentPackage, raw)
|
|
|
447
449
|
* `name` reads `Map 1` rather than `Figure 1` where it is due — see
|
|
448
450
|
* {@link module:engine/content-figures.scanFigures}.
|
|
449
451
|
* @param {import("./content-session.mjs").ContentSyntax} [options.syntax] - Session scanner projections.
|
|
452
|
+
* @param {import("./content-scripts.mjs").RunContext} [options.runContext] -
|
|
453
|
+
* The configured languages, which give each run's `lang=` its tag.
|
|
450
454
|
* @returns {Record<string, any>} The record, keys sorted at every depth. A
|
|
451
455
|
* **stub** — a note with an empty body, on a type an empty body suppresses —
|
|
452
456
|
* carries `address` and `anchors` as `null`: it publishes no page, so it
|
|
@@ -473,6 +477,7 @@ export function buildIndexRecord({
|
|
|
473
477
|
dateContext,
|
|
474
478
|
resolveRole,
|
|
475
479
|
syntax,
|
|
480
|
+
runContext,
|
|
476
481
|
}) {
|
|
477
482
|
assertNoDerivedKeys(frontmatter, relPath, absPath, contentPackage, raw);
|
|
478
483
|
|
|
@@ -540,6 +545,7 @@ export function buildIndexRecord({
|
|
|
540
545
|
...(Object.keys(resolvedDates).length ? { resolvedDates } : {}),
|
|
541
546
|
package: contentPackage,
|
|
542
547
|
names: marked.names,
|
|
548
|
+
runs: recordRuns(source, { context: runContext, bodyLine, bodyColumn, syntax }),
|
|
543
549
|
address: stub ? null : address,
|
|
544
550
|
nameAscii: asciiName(frontmatter?.name?.full),
|
|
545
551
|
aliasesAscii: asciiAliases(frontmatter?.name?.aliases),
|
|
@@ -694,6 +700,7 @@ export function indexRecordsForNote({
|
|
|
694
700
|
dateContext,
|
|
695
701
|
resolveRole,
|
|
696
702
|
syntax,
|
|
703
|
+
runContext,
|
|
697
704
|
}) {
|
|
698
705
|
resolveNoteId(frontmatter, { pkg: contentPackage });
|
|
699
706
|
const record = buildIndexRecord({
|
|
@@ -711,6 +718,7 @@ export function indexRecordsForNote({
|
|
|
711
718
|
dateContext,
|
|
712
719
|
resolveRole,
|
|
713
720
|
syntax,
|
|
721
|
+
runContext,
|
|
714
722
|
});
|
|
715
723
|
const records = [decodeIndexAddresses(record, addressContext ?? { package: contentPackage })];
|
|
716
724
|
const address = noteAddress(frontmatter, contentPackage);
|
|
@@ -1062,6 +1070,12 @@ export function resolveCultures(
|
|
|
1062
1070
|
* reader receives each as a finding, and the emitter throws once, listing
|
|
1063
1071
|
* every one.
|
|
1064
1072
|
* @param {string} [options.currentDate="720.1"] - Setting-wide neutral current date.
|
|
1073
|
+
* @param {string} [options.defaultCalendar] - The calendar of last resort, a
|
|
1074
|
+
* calendar note's shortcode; see {@link module:engine/note-calendars}.
|
|
1075
|
+
* @param {string} [options.configFile] - The configuration file, where an
|
|
1076
|
+
* unknown `defaultCalendar` is reported.
|
|
1077
|
+
* @param {Readonly<Record<string, {tag: string}>>} [options.languages] - The
|
|
1078
|
+
* configured languages, which give each run's `lang=` its tag.
|
|
1065
1079
|
* @param {(key: string) => ({culture?: unknown}|undefined)} [options.foreignRecord] -
|
|
1066
1080
|
* Another package's entry for a canonical address, read for a parent that
|
|
1067
1081
|
* package publishes; see {@link resolveCultures}.
|
|
@@ -1079,6 +1093,9 @@ export function collectContentIndex(
|
|
|
1079
1093
|
{
|
|
1080
1094
|
contentPackage,
|
|
1081
1095
|
currentDate,
|
|
1096
|
+
defaultCalendar,
|
|
1097
|
+
configFile,
|
|
1098
|
+
languages,
|
|
1082
1099
|
skipDirectories,
|
|
1083
1100
|
assetsBase,
|
|
1084
1101
|
manifest,
|
|
@@ -1185,6 +1202,7 @@ export function collectContentIndex(
|
|
|
1185
1202
|
dateContext: dates,
|
|
1186
1203
|
resolveRole,
|
|
1187
1204
|
syntax,
|
|
1205
|
+
runContext: { languages },
|
|
1188
1206
|
});
|
|
1189
1207
|
records.push(...made);
|
|
1190
1208
|
bodies.push({ record: made[0], body });
|
|
@@ -1234,7 +1252,17 @@ export function collectContentIndex(
|
|
|
1234
1252
|
|
|
1235
1253
|
// Every note record exists and every Address in it is a tuple, so a parent
|
|
1236
1254
|
// is found by its canonical address. Before the sort, which reads nothing
|
|
1237
|
-
// the pass writes.
|
|
1255
|
+
// the pass writes. Calendars first: they read the culture a note states,
|
|
1256
|
+
// which the culture pass then replaces with the one it resolves.
|
|
1257
|
+
resolveNoteCalendars(records, {
|
|
1258
|
+
contentPackage,
|
|
1259
|
+
defaultCalendar,
|
|
1260
|
+
configFile,
|
|
1261
|
+
contentBase,
|
|
1262
|
+
foreignRecord,
|
|
1263
|
+
problems,
|
|
1264
|
+
parsed,
|
|
1265
|
+
});
|
|
1238
1266
|
resolveCultures(records, { contentPackage, contentBase, foreignRecord, problems, parsed });
|
|
1239
1267
|
const cultureNotes = records
|
|
1240
1268
|
.filter((record) => isNoteRecord(record) && record.type === "lore")
|
|
@@ -1459,6 +1487,9 @@ export function indexRecordsFor({
|
|
|
1459
1487
|
return collectContentIndex(tree, {
|
|
1460
1488
|
contentPackage: resolved.contentPackage,
|
|
1461
1489
|
currentDate: resolved.currentDate,
|
|
1490
|
+
defaultCalendar: resolved.defaultCalendar ?? undefined,
|
|
1491
|
+
configFile: configFileIn(resolved.rootDir, CONFIG_FILENAMES),
|
|
1492
|
+
languages: resolved.languages,
|
|
1462
1493
|
addressContext: {
|
|
1463
1494
|
...noteAddressContext(resolved),
|
|
1464
1495
|
declaresAddress: dependencyAddresses(resolved),
|
package/engine/content-links.mjs
CHANGED
|
@@ -239,6 +239,9 @@ export function buildLinkIndex(
|
|
|
239
239
|
const body = splitFrontmatter(raw)?.body ?? raw;
|
|
240
240
|
const type = record.type.toLowerCase();
|
|
241
241
|
const note = { file: absPath, rel, fm, body, raw, type };
|
|
242
|
+
// The calendar the index resolved, which the reckoning context built
|
|
243
|
+
// over these notes hands to every date printed for this note.
|
|
244
|
+
if (record.resolvedCalendar) note.resolvedCalendar = record.resolvedCalendar;
|
|
242
245
|
const hierarchyProblems = cultureRecordProblems(record);
|
|
243
246
|
note.inheritedCultureFields = cultureInheritedFields(record);
|
|
244
247
|
if (hierarchyProblems)
|
|
@@ -77,6 +77,24 @@ import { parseExtensionAttributes, refusedAttributes } from "./extension-attribu
|
|
|
77
77
|
|
|
78
78
|
const structureParser = new MarkdownIt({ html: true });
|
|
79
79
|
|
|
80
|
+
/**
|
|
81
|
+
* The keys a poetry fence's attribute block takes. `lang`, `font` and `map`
|
|
82
|
+
* are read by `scanRuns` in content-scripts.mjs; `dir`, and on the site
|
|
83
|
+
* `data-pagefind-ignore`, are written by its rewrite of a fence set in one of
|
|
84
|
+
* the faces, and reach the element as they are written.
|
|
85
|
+
*/
|
|
86
|
+
export const POETRY_KEYS = Object.freeze([
|
|
87
|
+
"form",
|
|
88
|
+
"meter",
|
|
89
|
+
"rhyme",
|
|
90
|
+
"syllables",
|
|
91
|
+
"lang",
|
|
92
|
+
"font",
|
|
93
|
+
"map",
|
|
94
|
+
"dir",
|
|
95
|
+
"data-pagefind-ignore",
|
|
96
|
+
]);
|
|
97
|
+
|
|
80
98
|
/** Read poetry code fences; start/end are inclusive zero-based source lines.
|
|
81
99
|
* @param {string} source - Authored Markdown.
|
|
82
100
|
*/
|
|
@@ -157,15 +175,13 @@ export function scanPoetry(source) {
|
|
|
157
175
|
for (const message of [...parsed.problems, ...refusedAttributes(parsed.values)])
|
|
158
176
|
errors.push({ ...at, message });
|
|
159
177
|
for (const [key, value] of Object.entries(parsed.values)) {
|
|
160
|
-
if (!
|
|
178
|
+
if (!POETRY_KEYS.includes(key))
|
|
161
179
|
errors.push({ ...at, message: `${key}= is not a poetry attribute` });
|
|
162
180
|
else if (key === "syllables" && !/^[1-9]\d*(?:,[1-9]\d*)*$/.test(value))
|
|
163
181
|
errors.push({
|
|
164
182
|
...at,
|
|
165
183
|
message: "poetry syllables= needs positive comma-separated counts",
|
|
166
184
|
});
|
|
167
|
-
else if (key === "lang" && !/^[A-Za-z]{2,8}(?:-[A-Za-z0-9]{1,8})*$/.test(value))
|
|
168
|
-
errors.push({ ...at, message: "poetry lang= needs a language tag" });
|
|
169
185
|
}
|
|
170
186
|
}
|
|
171
187
|
if (opening?.name === "poetry")
|
|
@@ -197,7 +213,9 @@ export function renderPoetry(source) {
|
|
|
197
213
|
if (block.id) attrs.push(`id="${escape(slugify(block.id))}"`);
|
|
198
214
|
for (const [key, value] of Object.entries(block.attributes))
|
|
199
215
|
if (!refusedAttributes({ [key]: value }).length)
|
|
200
|
-
attrs.push(
|
|
216
|
+
attrs.push(
|
|
217
|
+
`${["lang", "dir", "data-pagefind-ignore"].includes(key) ? key : `data-${key}`}="${escape(value)}"`,
|
|
218
|
+
);
|
|
201
219
|
let indent = block.indent;
|
|
202
220
|
if (
|
|
203
221
|
indent.length >= 4 &&
|