@heroiclands/package-build 23.8.0 → 23.10.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 +53 -0
- package/README.md +2 -0
- package/bin/content-commands.mjs +207 -70
- package/docs/api.md +646 -78
- package/docs/authoring/first-note.md +1 -1
- package/docs/authoring/links-and-markup.md +253 -102
- package/docs/commands.md +111 -13
- package/docs/content-format.md +7 -2
- package/docs/diagnostics.md +1 -1
- package/docs/guides/packs.md +2 -1
- package/docs/guides/reference-pages.md +86 -63
- package/docs/reference/format-details.md +398 -137
- package/docs/reference/note-types.md +188 -174
- package/engine/anchors.mjs +15 -26
- package/engine/base-compiler.mjs +6 -4
- package/engine/changelog-group.mjs +58 -34
- package/engine/changelog-lint.mjs +6 -2
- package/engine/compendiums.mjs +15 -1
- package/engine/compile-corpus.mjs +17 -3
- package/engine/content-charset.mjs +13 -1
- package/engine/content-format.yaml +1 -0
- package/engine/content-html.mjs +18 -4
- package/engine/content-icons.mjs +15 -1
- package/engine/content-images.mjs +28 -9
- package/engine/content-index.mjs +496 -16
- package/engine/content-links.mjs +70 -27
- package/engine/content-session.mjs +247 -0
- package/engine/content-sources.mjs +110 -0
- package/engine/content-spans.mjs +87 -8
- package/engine/content-tables.mjs +9 -4
- package/engine/content-tasklists.mjs +20 -4
- package/engine/culture-reach.mjs +61 -34
- package/engine/data-keys.mjs +3 -1
- package/engine/diagnostics.mjs +17 -6
- package/engine/dictionary-layout.mjs +151 -19
- package/engine/dictionary-rows.mjs +129 -79
- package/engine/extension-attributes.mjs +28 -5
- package/engine/first-use.mjs +371 -0
- package/engine/foundry-entries.mjs +29 -8
- package/engine/generate.mjs +13 -4
- package/engine/glossary-entries.mjs +11 -11
- package/engine/heading-attributes.mjs +21 -28
- package/engine/helpers.mjs +54 -19
- package/engine/index-records.mjs +1 -0
- package/engine/index.mjs +3 -0
- package/engine/journals.mjs +13 -6
- package/engine/language-data.mjs +55 -2
- package/engine/language-rows.mjs +4 -4
- package/engine/language-terms.mjs +90 -4
- package/engine/language-words.mjs +2 -2
- package/engine/lexical-sections.mjs +247 -0
- package/engine/markup-validation.mjs +7 -2
- package/engine/metadata-index.mjs +1 -0
- package/engine/name-markup.mjs +114 -0
- package/engine/name-rows.mjs +70 -0
- package/engine/names.mjs +460 -0
- package/engine/note-addresses.mjs +25 -4
- package/engine/note-claims.mjs +2 -0
- package/engine/note-state.mjs +9 -3
- package/engine/note-vocabulary.mjs +7 -6
- package/engine/path-charset.mjs +24 -1
- package/engine/pdf-build.mjs +9 -9
- package/engine/pdf-render.mjs +15 -1
- package/engine/site-build.mjs +16 -2
- package/engine/site-index.mjs +5 -1
- package/engine/sql-tables.mjs +324 -44
- package/engine/stub-lint.mjs +10 -5
- package/engine/web-wikilinks.mjs +8 -6
- package/engine/word-rows.mjs +20 -12
- package/engine/word-terms.mjs +15 -6
- package/engine/yaml-lint.mjs +3 -5
- package/hugo-theme/static/css/style.css +4 -0
- package/package.json +1 -1
- package/types/engine/anchors.d.mts +4 -2
- package/types/engine/compendiums.d.mts +5 -1
- package/types/engine/compile-corpus.d.mts +5 -2
- package/types/engine/content-charset.d.mts +5 -1
- package/types/engine/content-html.d.mts +5 -1
- package/types/engine/content-icons.d.mts +5 -1
- package/types/engine/content-images.d.mts +5 -1
- package/types/engine/content-index.d.mts +102 -18
- package/types/engine/content-links.d.mts +22 -8
- package/types/engine/content-session.d.mts +108 -0
- package/types/engine/content-sources.d.mts +32 -0
- package/types/engine/content-spans.d.mts +3 -5
- package/types/engine/content-tables.d.mts +2 -1
- package/types/engine/content-tasklists.d.mts +5 -1
- package/types/engine/culture-reach.d.mts +22 -2
- package/types/engine/data-keys.d.mts +7 -0
- package/types/engine/diagnostics.d.mts +10 -0
- package/types/engine/dictionary-layout.d.mts +35 -4
- package/types/engine/dictionary-rows.d.mts +9 -0
- package/types/engine/extension-attributes.d.mts +16 -2
- package/types/engine/first-use.d.mts +31 -0
- package/types/engine/foundry-entries.d.mts +14 -5
- package/types/engine/generate.d.mts +5 -1
- package/types/engine/heading-attributes.d.mts +2 -2
- package/types/engine/helpers.d.mts +33 -7
- package/types/engine/index.d.mts +1 -0
- package/types/engine/journals.d.mts +5 -2
- package/types/engine/language-terms.d.mts +7 -0
- package/types/engine/lexical-sections.d.mts +96 -0
- package/types/engine/markup-validation.d.mts +2 -1
- package/types/engine/name-markup.d.mts +176 -0
- package/types/engine/name-rows.d.mts +35 -0
- package/types/engine/names.d.mts +356 -0
- package/types/engine/note-addresses.d.mts +3 -3
- package/types/engine/note-state.d.mts +2 -1
- package/types/engine/path-charset.d.mts +16 -1
- package/types/engine/site-build.d.mts +6 -3
- package/types/engine/site-index.d.mts +3 -1
- package/types/engine/sql-tables.d.mts +39 -4
- package/types/engine/stub-lint.d.mts +3 -1
- package/types/engine/word-rows.d.mts +9 -7
package/docs/api.md
CHANGED
|
@@ -426,7 +426,14 @@ Drawing a declared infobox: HTML for separate Foundry pages appended after autho
|
|
|
426
426
|
|
|
427
427
|
### `engine.noteState`
|
|
428
428
|
|
|
429
|
-
The three states of a note — **stub**, **draft**, **full** —
|
|
429
|
+
The three states of a note — **stub**, **draft**, **full** — describe its page
|
|
430
|
+
content. A stubbable note with an empty body and no generated Dictionary or
|
|
431
|
+
Glossary content is a stub: its facts remain queryable in the index, but it
|
|
432
|
+
has no page, address or link target. `isStubNote` recognizes nonempty lexical
|
|
433
|
+
frontmatter as generated content, so collectors retain the resulting page.
|
|
434
|
+
That lexical frontmatter supplies content without an authored body.
|
|
435
|
+
The ladder is derived in the SQL `entries` view; a note cannot assert its own
|
|
436
|
+
state.
|
|
430
437
|
|
|
431
438
|
| Export | Signature | Returns | Use it when |
|
|
432
439
|
| --------------------- | ------------------------------------------- | --------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
@@ -458,12 +465,12 @@ What a draft note says about itself, in one sentence, on every surface it reache
|
|
|
458
465
|
|
|
459
466
|
### `engine.stubLint`
|
|
460
467
|
|
|
461
|
-
Every typed note must have a nonempty body
|
|
468
|
+
Nonempty `data.words` on a `skill`/`language` note, or `data.glossary` on a culture or affiliation, supplies content without an authored body. Absent or empty arrays supply no such exemption. Every other typed note must have a nonempty body: empty and whitespace-only bodies are errors regardless of description or `draft` tag, including `folder` and `homepage`. Nonempty placeholder-only bodies still produce errors, including on lexical notes. Nonempty bodies with fewer than 25 prose words produce warnings unless tagged `draft`. A `folder` note is held only to the nonempty rule: any body, placeholder or short, passes with or without `draft`. Untyped vault scaffolding is excluded. The lint, content-index, package compile, site and PDF commands enforce body errors before publishing.
|
|
462
469
|
|
|
463
|
-
| Export | Signature
|
|
464
|
-
| ------------------ |
|
|
465
|
-
| `SHORT_BODY_WORDS` | `const SHORT_BODY_WORDS`
|
|
466
|
-
| `lintNoteStates` | `lintNoteStates(contentBase, { records, contentPackage })` | `{findings, counts, byType, oldestDrafts, summary}` | checking a tree's states and counting the ladder, from the corpus the command already derived |
|
|
470
|
+
| Export | Signature | Returns | Use it when |
|
|
471
|
+
| ------------------ | ------------------------------------------------------------------ | --------------------------------------------------- | --------------------------------------------------------------------------------------------- |
|
|
472
|
+
| `SHORT_BODY_WORDS` | `const SHORT_BODY_WORDS` | `number` | reading the word count below which an unmarked body is asked whether it is a draft |
|
|
473
|
+
| `lintNoteStates` | `lintNoteStates(contentBase, { records, contentPackage, parsed })` | `{findings, counts, byType, oldestDrafts, summary}` | checking a tree's states and counting the ladder, from the corpus the command already derived |
|
|
467
474
|
|
|
468
475
|
### `engine.placeRelations`
|
|
469
476
|
|
|
@@ -861,45 +868,308 @@ The published content index — the artifact packages exchange addresses through
|
|
|
861
868
|
|
|
862
869
|
Emitting this package's cross-package link manifest. `engine/content-address.mjs` owns the address _grammar_ — how a key is version is read, how a foreign file resolves. This module owns the _pass_: walking a content tree and deriving, for every note it publishes, the addresses that entry states. The two halves were split across the format module and a hand-written script in each consuming repository, which is how the two scripts came to differ in ways nobody chose — one routes its UUIDs through the pack router and one does not, and neither knew.
|
|
863
870
|
|
|
864
|
-
| Export | Signature | Returns | Use it when
|
|
865
|
-
| ----------------------- | ------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------- |
|
|
866
|
-
| `LEAD_ANCHOR` | `const LEAD_ANCHOR` | — | The reserved anchor name for a journal's **first** page.
|
|
867
|
-
| `anchorsOf` | `
|
|
868
|
-
| `entriesForNote` | `function entriesForNote(fm, name, address, body, ctx)` | {Array<object>} One or two entries, in {@link buildManifest}'s shape. | The manifest entries a single note produces.
|
|
869
|
-
| `collectFoundryEntries` | `function collectFoundryEntries(contentBase, ctx)` | `{{entries: Array<object>, notes: number, skipped: Array<{file: string, reason: string}>}}` | Every note this package publishes, as manifest entries.
|
|
870
|
-
| `foundryIdentities` | `function foundryIdentities(config` | `{{contentPackage: string, foundryPackageId: string, packRouter: object, web: boolean, skipDirectories: readonly string[]}}` | The identities an emission runs against, from configuration.
|
|
871
|
-
| `entryContext` | `function entryContext(config` | `{{contentPackage: string, foundryPackageId: string, packRouter: object, web: boolean, skipDirectories: readonly string[]}}` | The identities an emission runs against, from configuration.
|
|
871
|
+
| Export | Signature | Returns | Use it when |
|
|
872
|
+
| ----------------------- | ------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------- |
|
|
873
|
+
| `LEAD_ANCHOR` | `const LEAD_ANCHOR` | — | The reserved anchor name for a journal's **first** page. |
|
|
874
|
+
| `anchorsOf` | `anchorsOf(entryUuid, entryId, body, name, syntax?)` | {Record<string, string>} The anchors. | Every anchor of a note's journal, as `anchorName → Foundry target`. |
|
|
875
|
+
| `entriesForNote` | `function entriesForNote(fm, name, address, body, ctx)` | {Array<object>} One or two entries, in {@link buildManifest}'s shape. | The manifest entries a single note produces. |
|
|
876
|
+
| `collectFoundryEntries` | `function collectFoundryEntries(contentBase, ctx)` | `{{entries: Array<object>, notes: number, skipped: Array<{file: string, reason: string}>}}` | Every note this package publishes, as manifest entries. |
|
|
877
|
+
| `foundryIdentities` | `function foundryIdentities(config` | `{{contentPackage: string, foundryPackageId: string, packRouter: object, web: boolean, skipDirectories: readonly string[]}}` | The identities an emission runs against, from configuration. |
|
|
878
|
+
| `entryContext` | `function entryContext(config` | `{{contentPackage: string, foundryPackageId: string, packRouter: object, web: boolean, skipDirectories: readonly string[]}}` | The identities an emission runs against, from configuration. |
|
|
879
|
+
|
|
880
|
+
A generated dictionary headword maps to its containing page's UUID followed
|
|
881
|
+
by its word fragment. Other authored spans keep their containing-page
|
|
882
|
+
mapping.
|
|
883
|
+
|
|
884
|
+
### `engine.contentSession`
|
|
885
|
+
|
|
886
|
+
One invocation's content capture and lazy prepared views. Import the namespace
|
|
887
|
+
from `@heroiclands/package-build/engine`, or import the functions directly from
|
|
888
|
+
`@heroiclands/package-build/engine/content-session`.
|
|
889
|
+
|
|
890
|
+
| Export | Signature | Returns | Use it when |
|
|
891
|
+
| ---------------------- | ---------------------------------------- | -------------------------- | ---------------------------------------------- |
|
|
892
|
+
| `CONTENT_TARGETS` | `readonly [index, lint, links, compile]` | the closed execution order | Selecting operations in a shared invocation. |
|
|
893
|
+
| `createContentSession` | `createContentSession(options?)` | `Promise<ContentSession>` | Sharing source work across content operations. |
|
|
894
|
+
|
|
895
|
+
Options are the resolved `config`, `contentBase`, `assetsBase`,
|
|
896
|
+
`skipDirectories` and compile `router`. Paths and scope default from that
|
|
897
|
+
configuration. The session binds its exact configuration object, canonical
|
|
898
|
+
roots, directory scope and router. `assertContext(options?)` refuses reuse
|
|
899
|
+
with a different bound value. A new invocation, an input edit, a changed
|
|
900
|
+
dependency cache or a changed configuration needs a fresh session; no state
|
|
901
|
+
is persisted or shared across sessions.
|
|
902
|
+
|
|
903
|
+
The session exposes `config`, `contentBase`, `assetsBase`, `skipDirectories`,
|
|
904
|
+
`router`, physical `inventory`, raw `sources`, derived `records`, authored
|
|
905
|
+
`parsed` notes, preparation `problems` and captured dependency `foreign`
|
|
906
|
+
views. Treat these inputs as read-only. Parsed notes retain exact `raw`
|
|
907
|
+
strings, trimmed `body`, authored `frontmatter` and file coordinates; malformed
|
|
908
|
+
or untyped physical files remain available to raw checks. Derivation and
|
|
909
|
+
publication use their own record projections, including per-system clones.
|
|
910
|
+
|
|
911
|
+
| Method | Result and scope |
|
|
912
|
+
| ------------------------- | ------------------------------------------------------------------------------------------------------------------- |
|
|
913
|
+
| `syntax(text)` | Memoized block, alert and span scanner results for that exact string; a different projection has its own positions. |
|
|
914
|
+
| `sqlTables(audience?)` | Prepared SQL and generated sections for `all` (default) or `public`, once per audience in this context. |
|
|
915
|
+
| `linkTables()` | Source-audience results selected for prose-link checking. |
|
|
916
|
+
| `audit(kind?)` | The diagnostic link index for `links` (default) or `lint`; this stays separate from routed native resolution. |
|
|
917
|
+
| `publication()` | The complete `PreparedContentIndex`, including source-audience and native bodies. |
|
|
918
|
+
| `compileCorpus()` | Records, parsed notes, native SQL, scanner projections and the routed compiler link index. |
|
|
919
|
+
| `prepare(targets)` | Prepare selected closed targets in `CONTENT_TARGETS` order before a caller starts a writer; returns the session. |
|
|
920
|
+
| `assertContext(options?)` | Check configuration, roots, scope and router against the captured context. |
|
|
921
|
+
| `dispose()` | Invalidate derived views and methods at the end of the invocation. |
|
|
922
|
+
|
|
923
|
+
SQL query answers remain local to each audience preparation; their databases
|
|
924
|
+
close inside preparation. The session reuses results without sharing a public
|
|
925
|
+
answer with the complete native corpus. Scanner results are also read-only.
|
|
926
|
+
`prepare` refuses writers when shared source preparation or the existing note
|
|
927
|
+
state preflight fails. Ordinary lint findings add no gate to compilation.
|
|
928
|
+
|
|
929
|
+
```js
|
|
930
|
+
import { contentSession, contentIndex, compendiums } from "@heroiclands/package-build/engine";
|
|
931
|
+
|
|
932
|
+
const session = await contentSession.createContentSession({ config });
|
|
933
|
+
try {
|
|
934
|
+
await session.prepare(["index", "compile"]);
|
|
935
|
+
contentIndex.emitContentIndex({ config, publication: await session.publication() });
|
|
936
|
+
await compendiums.compilePacks({ config, session });
|
|
937
|
+
} finally {
|
|
938
|
+
session.dispose();
|
|
939
|
+
}
|
|
940
|
+
```
|
|
941
|
+
|
|
942
|
+
### `engine/content-sources.mjs`
|
|
943
|
+
|
|
944
|
+
Reachable through `@heroiclands/package-build/engine/content-sources`; the
|
|
945
|
+
session uses these physical input adapters without adding a namespace export.
|
|
946
|
+
|
|
947
|
+
| Export | Signature | Returns | Use it when |
|
|
948
|
+
| ----------------------- | -------------------------------------------------------------------- | -------------------------------- | ------------------------------------------------------------ |
|
|
949
|
+
| `captureContentSources` | `captureContentSources(contentBase, assetsBase, skipDirectories)` | `{sources, indexSources, paths}` | Capturing exact text and physical path walks before parsing. |
|
|
950
|
+
| `capturedSources` | `capturedSources(contentBase, sources, skipDirectories, extensions)` | generator of `[file, raw]` | Selecting a raw checker's physical scope from captured text. |
|
|
951
|
+
|
|
952
|
+
`sources` maps absolute text-file paths to `{raw}`. `indexSources` retains the
|
|
953
|
+
index walk's selected Markdown order. `paths` retains `{root, file, name,
|
|
954
|
+
directory}` for each physical walk, including its root identity so overlapping
|
|
955
|
+
content and asset scopes preserve their own filtering and order.
|
|
956
|
+
|
|
957
|
+
### `engine/first-use.mjs`
|
|
958
|
+
|
|
959
|
+
Import from `@heroiclands/package-build/engine/first-use`.
|
|
960
|
+
|
|
961
|
+
| Export | Signature | Returns | Use it when |
|
|
962
|
+
| ----------------------- | ----------------------------------------------------------------------- | ------------------------ | ------------------------------------------------------------------------------ |
|
|
963
|
+
| `createFirstUseChecker` | `createFirstUseChecker({ records, index, catalog?, parsed?, syntax? })` | `checker({ file, raw })` | Checking authored prose against the current corpus's name-marking conventions. |
|
|
964
|
+
|
|
965
|
+
Construct one checker with the session's read-only `records` and
|
|
966
|
+
`await session.audit("lint")`. Call it for each existing link-index note.
|
|
967
|
+
It performs no I/O and returns `{file, line, column, severity: "warning",
|
|
968
|
+
rules, message}` diagnostics. `rules` is a list of applicable rule names, merged
|
|
969
|
+
into one warning for each occurrence; the message names the full name or
|
|
970
|
+
explicit label that needs attention. Only `.name` spans and authored link
|
|
971
|
+
labels enter name tracking; plain text is not searched. Indexed full names and
|
|
972
|
+
declared aliases share an identity, while other labels use their whole text.
|
|
973
|
+
A whole bold indexed name receives an advisory without entering name tracking.
|
|
974
|
+
Local and captured dependency names and aliases share the same corpus
|
|
975
|
+
context. The command's lint target constructs
|
|
976
|
+
the checker once, uses exact authored `raw` strings and keeps these findings
|
|
977
|
+
advisory. See the [authoring conventions](authoring/links-and-markup.md#marking-names-in-prose).
|
|
978
|
+
|
|
979
|
+
Supply the invocation's `catalog` to share its full-name and alias lookup with
|
|
980
|
+
the package names report. Optional `parsed` maps absolute files to captured
|
|
981
|
+
`{raw, body, bodyLine?, bodyColumn?}`; `syntax` is the session's scanner
|
|
982
|
+
projection. When the captured raw text matches, the checker rebases the body's
|
|
983
|
+
existing name inventory into its authored raw source coordinates. Standalone
|
|
984
|
+
calls use their supplied raw text.
|
|
985
|
+
|
|
986
|
+
### `engine/name-markup.mjs`
|
|
987
|
+
|
|
988
|
+
Import from `@heroiclands/package-build/engine/name-markup`.
|
|
989
|
+
|
|
990
|
+
| Export | Signature | Returns | Use it when |
|
|
991
|
+
| ----------------- | --------------------------------------------------------- | ---------------------------------------------- | -------------------------------------------------------------------------------------------------- |
|
|
992
|
+
| `inlineText` | `inlineText(tokens)` | `string` | Reading visible name text from actual parsed inline children and their resolved reference context. |
|
|
993
|
+
| `rebaseNameSpans` | `rebaseNameSpans(spans, { raw, bodyLine?, bodyColumn? })` | Copied name spans at original file coordinates | Rebasing a captured body inventory and attribute ranges without parsing it again. |
|
|
994
|
+
|
|
995
|
+
`scanSpans(source)` returns `{spans, names, errors}`. `names` contains the same
|
|
996
|
+
span objects with the exact `name` class, including spans inside links. Each
|
|
997
|
+
span retains `text`, `attrs`, offsets, 1-based line/UTF-16 column and `visible`
|
|
998
|
+
text from actual parsed children. `contentEnd` is the exclusive raw inner-label
|
|
999
|
+
endpoint. `attributePositions` maps each named attribute to `key` and `value`
|
|
1000
|
+
ranges with `{start, end, line, column}`. Offsets are 0-based with exclusive
|
|
1001
|
+
ends. Rebasing copies these values and leaves the captured inventory unchanged.
|
|
1002
|
+
|
|
1003
|
+
The leaf module exports the declaration types `ContentSpan`, `SpanSourceSite`,
|
|
1004
|
+
`SpanAttributePosition`, `SpanFinding` and `SpanScan`. The generic rebasing
|
|
1005
|
+
helper retains additional fields of the captured span subtype.
|
|
1006
|
+
|
|
1007
|
+
`engine/extension-attributes.mjs` exposes the shared attribute grammar:
|
|
1008
|
+
|
|
1009
|
+
| Export | Signature | Returns | Use it when |
|
|
1010
|
+
| -------------------------- | ------------------------------------------------------------------- | --------------------------------------------- | ------------------------------------------------------------------------------------------------------------- |
|
|
1011
|
+
| `parseExtensionAttributes` | `parseExtensionAttributes(source, { idPattern?, positions? } = {})` | `{id, classes, values, problems, positions?}` | Parsing identifiers, ordered classes and named values; `positions: true` retains attribute key/value offsets. |
|
|
1012
|
+
|
|
1013
|
+
Attribute-parser positions are ranges within its supplied attribute source;
|
|
1014
|
+
the span scanner maps them to Markdown coordinates. The optional range map
|
|
1015
|
+
does not change the parsed values.
|
|
1016
|
+
|
|
1017
|
+
### `engine/name-rows.mjs`
|
|
1018
|
+
|
|
1019
|
+
Import from `@heroiclands/package-build/engine/name-rows`.
|
|
1020
|
+
|
|
1021
|
+
| Export | Signature | Returns | Use it when |
|
|
1022
|
+
| -------------- | -------------------- | --------------------------- | ---------------------------------------------------------------------- |
|
|
1023
|
+
| `NAME_COLUMNS` | `const NAME_COLUMNS` | Frozen ordered column names | Reading `owner`, `name`, `type`, `subType`, `line`, `column`. |
|
|
1024
|
+
| `namesRows` | `namesRows(records)` | `NameRow[]` | Projecting own-note occurrences into SQL rows without I/O or mutation. |
|
|
1025
|
+
|
|
1026
|
+
Rows preserve record and occurrence order. `owner` is the canonical NOTE page
|
|
1027
|
+
derived from the record's canonical Address, including for native Item
|
|
1028
|
+
sources; an unaddressable record keeps `null`. Assets and documentation mirrors
|
|
1029
|
+
contribute no rows. Each row carries the occurrence's visible `name`, nullable
|
|
1030
|
+
`type`/`subType`, and numeric 1-based `line`/UTF-16 `column`. SQL ingestion fixes
|
|
1031
|
+
text columns as `VARCHAR` and positions as `BIGINT`, including empty,
|
|
1032
|
+
entirely-null and late-supplied classifications.
|
|
1033
|
+
|
|
1034
|
+
### `engine/names.mjs`
|
|
1035
|
+
|
|
1036
|
+
Import from `@heroiclands/package-build/engine/names`.
|
|
1037
|
+
|
|
1038
|
+
| Export | Signature | Returns | Use it when |
|
|
1039
|
+
| ----------------------- | -------------------------------------------------------------- | --------------------------------- | -------------------------------------------------------------------------------------------- |
|
|
1040
|
+
| `normalizeName` | `normalizeName(text)` | `string` | Normalizing NFC, apostrophes and whitespace while preserving case. |
|
|
1041
|
+
| `nameForms` | `nameForms(text)` | `string[]` | Enumerating supported whole-label possessives and regular plurals. |
|
|
1042
|
+
| `createNameCatalog` | `createNameCatalog({ records, index })` | `NameCatalog` | Sharing indexed full-name, alias and resolved Address matching across checks. |
|
|
1043
|
+
| `collectUnwrittenNames` | `collectUnwrittenNames({ records, catalog })` | `{groups, firstUse}` | Grouping every unresolved captured occurrence by supplied classification. |
|
|
1044
|
+
| `analyzeNameSpans` | `analyzeNameSpans(spans, { file?, vocabulary?, types? } = {})` | `{names, findings}` | Validating supplied `data-type` against actual note declarations at attribute-key positions. |
|
|
1045
|
+
| `createNamesReport` | `createNamesReport({ package, records, catalog })` | `NamesReport` | Preparing deterministic current-package unwritten and conflicting groups without I/O. |
|
|
1046
|
+
| `namesReportFinding` | `namesReportFinding(report, { contentBase? } = {})` | A located warning, or `undefined` | Formatting one human package advisory with every reported use. |
|
|
1047
|
+
|
|
1048
|
+
`NameCatalog` provides `recognize(label, preferred?)` and `resolve(target)`;
|
|
1049
|
+
indexed full names and declared aliases share canonical identity, and ambiguous
|
|
1050
|
+
labels retain every candidate. Dependency names come from the captured link
|
|
1051
|
+
index; folder-note identities are excluded. These pure helpers read the
|
|
1052
|
+
invocation's existing corpus.
|
|
1053
|
+
|
|
1054
|
+
`analyzeNameSpans` accepts actual parsed spans at file coordinates, with
|
|
1055
|
+
`visible`, `attrs`, `line`, `column` and optional `attributePositions`. It
|
|
1056
|
+
returns valid five-field `MarkedName` entries and supplied-value error findings.
|
|
1057
|
+
`vocabulary` defaults to `NOTE_VOCABULARY`; `types` supplies additional bare
|
|
1058
|
+
types from configured declarations. Callers propagate findings through their
|
|
1059
|
+
existing preparation error channel.
|
|
1060
|
+
|
|
1061
|
+
`NamesReport` has `{package, unwritten, conflicts}`. Each unwritten group has
|
|
1062
|
+
`{type, subType, names: [{name, uses}]}`. Each conflict has
|
|
1063
|
+
`{name, kinds: [{type, subType, uses}]}`. A use has
|
|
1064
|
+
`{owner, file, line, column}`, with its file relative to the content root and
|
|
1065
|
+
the same NOTE/null owner convention as `namesRows`. Untyped uses are excluded
|
|
1066
|
+
from conflicts; matching bare-type/subtype refinements are compatible.
|
|
1067
|
+
`namesReportFinding` rebases file paths with `contentBase`, anchors the advisory
|
|
1068
|
+
at the earliest reported actual use, and returns no warning for an empty report.
|
|
872
1069
|
|
|
873
1070
|
### `engine.contentIndex`
|
|
874
1071
|
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
1072
|
+
Collecting and publishing this package's content index. Records describe note frontmatter, assets, prose and event anchors, and the document targets those notes produce. Dynamic publication prepares the same generated Markdown and scalar expressions as the rendering surfaces, using each source note's audience.
|
|
1073
|
+
|
|
1074
|
+
Own-note records carry derived `names` entries in source order as
|
|
1075
|
+
`{name, type, subType, line, column}`. Missing classifications are explicit
|
|
1076
|
+
`null`; no occurrences gives `[]`. Positions refer to original authored span
|
|
1077
|
+
openings, before generated content. Metadata JSONL transports these entries
|
|
1078
|
+
to dependency SQL without an additional artifact. This field is reserved for
|
|
1079
|
+
derived data, and is not an authored frontmatter key.
|
|
1080
|
+
|
|
1081
|
+
`buildIndexRecord` and `indexRecordsForNote` accept `bodyColumn` alongside
|
|
1082
|
+
`bodyLine` to locate the authored body's first UTF-16 character. Their optional
|
|
1083
|
+
`nameTypes` iterable, also accepted by `collectContentIndex` and
|
|
1084
|
+
`indexRecordsFor`, supplies additional bare kinds from configured type
|
|
1085
|
+
declarations. `indexRecordsFor` defaults that iterable to the resolved
|
|
1086
|
+
configuration's `itemTypes`.
|
|
1087
|
+
|
|
1088
|
+
| Export | Signature | Returns | Use it when |
|
|
1089
|
+
| ----------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
1090
|
+
| `noteAddress` | `function noteAddress(frontmatter, contentPackage)` | {{slug: string, canonical: string}\|null} `slug` is what goes inside `[[…]]` within this package; `canonical` is the fully qualified key the manifest files the note under, carrying the package and the system as well. | The address a wikilink writes to reach a note, or `null` when it has none. |
|
|
1091
|
+
| `sortKeysDeep` | `function sortKeysDeep(value)` | {unknown} The value with every plain object's keys in sorted order. | Recursively sort an object's keys, so serialization is order-independent. |
|
|
1092
|
+
| `asciiName` | `function asciiName(name)` | {string\|null} The ASCII form, or `null` when there is no name, or nothing printable survives. | A note's display name reduced to printable 7-bit ASCII. |
|
|
1093
|
+
| `asciiAliases` | `function asciiAliases(aliases)` | {Array<string>} Possibly empty, never null: a note with no aliases has an empty set of them, which is a fact rather than a missing value, and a consumer iterating it should not have to check first. | A note's `name.aliases` reduced to printable 7-bit ASCII, in order. |
|
|
1094
|
+
| `serializeContentIndex` | `function serializeContentIndex(records)` | {string} One compact JSON object per line, newline-terminated. | Serialize records as JSON Lines. |
|
|
1095
|
+
| `indexRecordsFor` | `indexRecordsFor({ contentBase, config, skipDirectories, problems, parsed, sources, syntax, foreignIndex, nameTypes? })` | {object[]} One record per note, plus one per documentation entry. | The index records for a content tree, without writing anything. |
|
|
1096
|
+
| `indexRecordsForNote` | `indexRecordsForNote({ frontmatter, body, bodyLine, bodyColumn?, nameTypes?, relPath, absPath, raw, contentPackage, manifest, addressContext })` | `object[]` | Derive the note's own record and its documentation record from parsed text. |
|
|
1097
|
+
| `prepareContentIndex` | `async prepareContentIndex({ contentBase, config, session } = {})` | `Promise<PreparedContentIndex>` — the captured source context and complete expanded bodies, described below | Prepare dynamic publication without writing files. |
|
|
1098
|
+
| `emitContentIndex` | `emitContentIndex({ contentBase, outDir, config, publication } = {})` | `{{file: string, notes: number, assets: number, records: number, bytes: number, full: number, draft: number, stub: number, dictionary: {file: string, rows: number}, terms: {file: string, rows: number}}}` | Synchronously publish metadata and the dictionary and terms datafiles; dynamic content requires a complete `publication`. |
|
|
1099
|
+
| `buildIndexRecord` | `buildIndexRecord({ frontmatter, relPath, absPath, raw, contentPackage, body, bodyLine, bodyColumn?, nameTypes?, manifest })` | `Record<string, any>` — the record, with derived fields sorted deep | building one note's content-index record; refuses a note that authors a key the index derives itself (`package` among them) before building anything |
|
|
1100
|
+
| `collectContentIndex` | `collectContentIndex(contentBase, { contentPackage, skipDirectories, assetsBase, manifest, problems, foreignRecord, parsed, sources, syntax, nameTypes? })` | `Array<Record<string, any>>` | reading a whole content tree into index records, in a total order independent of directory-read order; an item note yields two records (its own and its documentation journal's), and each asset root yields one per file; each note carries its resolved culture as `data.culture` |
|
|
1101
|
+
| `resolveCultures` | `resolveCultures(records, { contentPackage, contentBase, foreignRecord, problems })` | `Array<Record<string, any>>` — the same records | writing each note's resolved culture into its record as `data.culture`: its own value, else its parents', else for a polity its seat's and governed places', pooled; parents that disagree give a finding into `problems`, or one thrown Error listing every conflict |
|
|
1102
|
+
|
|
1103
|
+
`prepareContentIndex` resolves `contentBase` from the configured `paths.content`
|
|
1104
|
+
when omitted. Its `PreparedContentIndex` contains:
|
|
1105
|
+
|
|
1106
|
+
| Field | Value |
|
|
1107
|
+
| ---------------- | -------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
1108
|
+
| `config` | The resolved configuration object used for preparation. |
|
|
1109
|
+
| `contentBase` | The absolute source root. |
|
|
1110
|
+
| `records` | The complete note, documentation and asset records, including hidden providers needed for lineage. |
|
|
1111
|
+
| `parsed` | A `Map` keyed by absolute note file, holding authored `frontmatter`, `body`, `raw`, `bodyLine` and `bodyColumn`. |
|
|
1112
|
+
| `expandedBodies` | A `Map` keyed by absolute note file, holding `markdown`, its body-relative `lineMap` of `{line, generated}` entries, and optional `generatedFrom`. |
|
|
1113
|
+
| `foundryBodies` | A `Map` with the same body shape, prepared against the complete native compendium corpus. |
|
|
1114
|
+
| `syntax` | Optional session-local scanner projection function; read-only and valid for that session's lifetime. |
|
|
1115
|
+
|
|
1116
|
+
Preparation expands SQL tables, page lists, automatic sections and scalar
|
|
1117
|
+
expressions. Prose declarations in `expandedBodies` use each source's audience:
|
|
1118
|
+
public notes use public providers and usage; GM notes use the complete corpus.
|
|
1119
|
+
Automatic lexical sections keep that source audience on every surface.
|
|
1120
|
+
Explicit native SQL uses the complete corpus in `foundryBodies`.
|
|
1121
|
+
|
|
1122
|
+
Native own and documentation target maps derive their actual page identities
|
|
1123
|
+
from `foundryBodies`. Each map contains only prose slugs declared in the
|
|
1124
|
+
source-audience body and present in the native body, plus the journal's
|
|
1125
|
+
reserved `$lead` target. A prose target present only on the website remains
|
|
1126
|
+
valid for website links and prose-link checks; a native link to it is refused.
|
|
1127
|
+
Native-only slugs and private heading names stay out of a public source's
|
|
1128
|
+
metadata. Event anchors and authored source positions remain attached to their
|
|
1129
|
+
sources. A published empty prose-anchor list declares no prose targets,
|
|
1130
|
+
including on web-only pages.
|
|
1131
|
+
|
|
1132
|
+
Pass the envelope directly to the synchronous emitter:
|
|
1133
|
+
|
|
1134
|
+
```js
|
|
1135
|
+
const publication = await prepareContentIndex({ config, contentBase });
|
|
1136
|
+
const result = emitContentIndex({ publication, outDir });
|
|
1137
|
+
```
|
|
1138
|
+
|
|
1139
|
+
The emitter defaults `config` and `contentBase` from the envelope. If supplied,
|
|
1140
|
+
they must name that same configuration object and source root. It consumes
|
|
1141
|
+
`records`, `parsed`, `expandedBodies` and `foundryBodies` without mutating them;
|
|
1142
|
+
frozen inputs are valid. Every note requires a parsed source and both body
|
|
1143
|
+
entries.
|
|
1144
|
+
Missing or mismatched preparation is refused before creating or replacing any
|
|
1145
|
+
metadata, dictionary or terms file.
|
|
1146
|
+
|
|
1147
|
+
Without `publication`, the synchronous emitter accepts authored bodies and
|
|
1148
|
+
pure frontmatter dictionary or glossary sections. Active SQL, page lists,
|
|
1149
|
+
scalar expressions, events or other generated sections require
|
|
1150
|
+
`prepareContentIndex`. The CLI performs this preparation automatically.
|
|
1151
|
+
`indexRecordsFor` and `collectContentIndex` collect authored records and pure
|
|
1152
|
+
lexical sections; use preparation and emission to publish dynamic body targets.
|
|
1153
|
+
|
|
1154
|
+
On lore and affiliation notes, `data.language` and `data.glossary[].language`
|
|
1155
|
+
require a language NOTE Address. Decoding refuses other recognized system
|
|
1156
|
+
segments at the authored value, naming the complete
|
|
1157
|
+
`<pkg>-note-skill-<shortcode>` to write. A short local form such as
|
|
1158
|
+
`skill-provnzlng` is also accepted. See the [glossary language
|
|
1159
|
+
rules](reference/format-details.md#dictionaries-and-glossaries).
|
|
890
1160
|
|
|
891
1161
|
`engine.contentIndex` also re-exports eight names from neighbouring leaf modules, at this same import path:
|
|
892
1162
|
|
|
893
|
-
| Export | Signature
|
|
894
|
-
| --------------------- |
|
|
895
|
-
| `collectAnchors` | `collectAnchors(body, bodyLine)` | `Array<{slug: string, line: number, kind: string}>` | reading a note body's anchors, each with its kind, shared with the link checker and every build that emits a link |
|
|
896
|
-
| `DERIVED_KEYS` | `const DERIVED_KEYS`
|
|
897
|
-
| `noteFile` | `noteFile(contentBase, record)`
|
|
898
|
-
| `authoredFrontmatter` | `authoredFrontmatter(record)`
|
|
899
|
-
| `isNoteRecord` | `isNoteRecord(record)`
|
|
900
|
-
| `isStub` | `isStub(record)`
|
|
901
|
-
| `isAssetRecord` | `isAssetRecord(record)`
|
|
902
|
-
| `recordPath` | `recordPath(record)`
|
|
1163
|
+
| Export | Signature | Returns | Use it when |
|
|
1164
|
+
| --------------------- | ------------------------------------------------------- | -------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
1165
|
+
| `collectAnchors` | `collectAnchors(body, bodyLine, resolveRole?, syntax?)` | `Array<{slug: string, line: number, kind: string}>` | reading a note body's anchors, each with its kind, shared with the link checker and every build that emits a link |
|
|
1166
|
+
| `DERIVED_KEYS` | `const DERIVED_KEYS` | — | reading which keys the content index adds to a record, and which a note therefore may not author itself |
|
|
1167
|
+
| `noteFile` | `noteFile(contentBase, record)` | `string` — the note's absolute path | composing an index record's `file.path` (recorded relative, for a byte-stable artifact) back into an openable absolute path |
|
|
1168
|
+
| `authoredFrontmatter` | `authoredFrontmatter(record)` | `Record<string, any>` — the frontmatter without `DERIVED_KEYS` | reading a note's frontmatter back from its index record, its resolved `data.culture` included, so a pass can lint or compile from the index without reasoning about derived keys as if the author wrote them |
|
|
1169
|
+
| `isNoteRecord` | `isNoteRecord(record)` | `boolean` | telling a note's own record apart from its documentation journal's and from an asset's, when enumerating the corpus |
|
|
1170
|
+
| `isStub` | `isStub(record)` | `boolean` | telling a stub apart from a note that publishes a page; asked of the absent address, which is the whole signal |
|
|
1171
|
+
| `isAssetRecord` | `isAssetRecord(record)` | `boolean` | telling a record that addresses a file apart from one that addresses a note; the `asset` block is what marks it |
|
|
1172
|
+
| `recordPath` | `recordPath(record)` | `string` — the path the record names inside its package | ordering or reporting across both record shapes: a note names the `.md` it was parsed from, an asset the file it is |
|
|
903
1173
|
|
|
904
1174
|
### `engine.assetTypes`
|
|
905
1175
|
|
|
@@ -1002,7 +1272,7 @@ content tree can also compile into Foundry compendium packs.
|
|
|
1002
1272
|
| `collectContentPages` | `function collectContentPages(contentBase, ctx)` | `{{pages: object[], addressFindings: object[], fmLinkFindings: object[]}}` | The content tree's pages, and what could not be addressed. |
|
|
1003
1273
|
| `collectHomepages` | `function collectHomepages(contentBase, ctx)` | `{{pages: object[], addressFindings: object[]}}` The homepage notes, in walk order, and the ones among them that could not be addressed. | The package's homepage notes — the authored page at `/<contentPackage>/`. |
|
|
1004
1274
|
| `writeHomepages` | `function writeHomepages(outRoot, pages, config)` | {number} How many pages were written. | Writes each homepage as the package root's `_index.md`. |
|
|
1005
|
-
| `siteGates` | `
|
|
1275
|
+
| `siteGates` | `siteGates(pages, findings, options)` | {object} The gate results and, when they pass, the built index. | The integrity gates a site build runs before it writes anything. |
|
|
1006
1276
|
| `emptyGates` | `function emptyGates()` | {object} An all-clear gate result. | The gate result of a build that ran none of them. |
|
|
1007
1277
|
| `gatesFailed` | `function gatesFailed(gates)` | — | Whether any gate produced a finding. |
|
|
1008
1278
|
| `tableUniverse` | `function tableUniverse(pages)` | {Map<string, object[]>} Package → the notes it may tabulate. | The universe a generated table searches, grouped by package. |
|
|
@@ -1014,6 +1284,10 @@ content tree can also compile into Foundry compendium packs.
|
|
|
1014
1284
|
| `sitePageDecorator` | `sitePageDecorator(config, index)` | Function | Add the site's infobox and being metadata to a rendered page. |
|
|
1015
1285
|
| `buildSite` | `buildSite({ config, sqlTables })` | `{{gates: object, stats: object\|null, tableErrors: object[], wikiErrors: object[], mapFindings: object[], manifests: object\|null}}` | Builds a Hugo content tree from a content tree, and reports what it found. |
|
|
1016
1286
|
|
|
1287
|
+
`siteGates` takes `{ config, records, sqlTables? }`. Pass the prepared
|
|
1288
|
+
`sqlTables` map so anchors emitted by automatic dictionary sections are present in the
|
|
1289
|
+
index for checking page links.
|
|
1290
|
+
|
|
1017
1291
|
### `engine.sitePreview`
|
|
1018
1292
|
|
|
1019
1293
|
`prepareSitePreview({ config })` reads the saved content tree and declared dependency indexes, prepares its SQL corpus, and returns a private preview context. Pass the active note's absolute path and complete buffer text to `render(file, text)`. A successful result has `{ ok: true, markdown, frontmatter, findings: [] }`; a failed result has `{ ok: false, findings }` and no replacement content. Findings identify the source file and carry a line and column when the offending text can be located.
|
|
@@ -1176,13 +1450,20 @@ One authored pathname, and the four addresses it resolves to. A note names a fil
|
|
|
1176
1450
|
|
|
1177
1451
|
Resolving every link in a content tree, and reporting the ones that land nowhere. Three link defects survive both content builds silently, so neither the pack compilers nor a site build catches them:
|
|
1178
1452
|
|
|
1179
|
-
| Export | Signature
|
|
1180
|
-
| -------------------- |
|
|
1181
|
-
| `anchorsOf` | `function anchorsOf(body)`
|
|
1182
|
-
| `buildLinkIndex` | `buildLinkIndex(contentBase, { config, skipDirectories, sqlTables, records, problems })` | {object} The notes, the index, and the resolvers built over it. | Read a content tree into the index a link resolves against. |
|
|
1183
|
-
| `auditHomepageLinks` | `function auditHomepageLinks(index)`
|
|
1184
|
-
| `auditLinks` | `function auditLinks(index)`
|
|
1185
|
-
| `walkReachability` | `function walkReachability(index,`
|
|
1453
|
+
| Export | Signature | Returns | Use it when |
|
|
1454
|
+
| -------------------- | ------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------- |
|
|
1455
|
+
| `anchorsOf` | `function anchorsOf(body)` | {Set<string>} The declared anchor slugs. | Every `{#anchor}` a note declares on a heading. |
|
|
1456
|
+
| `buildLinkIndex` | `buildLinkIndex(contentBase, { config, skipDirectories, sqlTables, records, parsed, problems })` | {object} The notes, the index, and the resolvers built over it. | Read a content tree into the index a link resolves against. |
|
|
1457
|
+
| `auditHomepageLinks` | `function auditHomepageLinks(index)` | {Array<{note: object, field: string, url: string, text: string, occurrence: number, message: string}>} One finding per defect, `text` and `occurrence` locating it in the note's raw source. | Every defect in the addresses a package homepage carries. |
|
|
1458
|
+
| `auditLinks` | `function auditLinks(index)` | `{{deadAnchors: object[], deadAddresses: object[], unlabelledLinks: object[], frontmatterLinks: object[], homepageLinks: object[], usedManifest: Set<string>}}` The findings, and which addresses a foreign manifest answered. | Every link in a tree that lands nowhere. |
|
|
1459
|
+
| `walkReachability` | `function walkReachability(index,` | `{{root: object, reached: Set<object>, orphans: object[]}}` The root, everything reached from it, and the corpus members that were not. | Walk a corpus from its root and report what nothing links to. |
|
|
1460
|
+
|
|
1461
|
+
`buildLinkIndex` optionally accepts the `parsed` map captured by `indexRecordsFor`
|
|
1462
|
+
alongside the supplied `records` in the same invocation. It reads each entry's
|
|
1463
|
+
`raw` string by absolute note path and derives the untrimmed body as usual;
|
|
1464
|
+
without a raw string it reads that file. The caller owns this map, and the
|
|
1465
|
+
link index leaves it unchanged. This source capture is separate from published
|
|
1466
|
+
metadata and has no persistent or cross-invocation cache.
|
|
1186
1467
|
|
|
1187
1468
|
### `engine.webWikilinks`
|
|
1188
1469
|
|
|
@@ -1211,7 +1492,7 @@ Shared helpers for the pack compilers in `packages/package-build/`. The HeroicLa
|
|
|
1211
1492
|
| ------------------------- | ----------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
1212
1493
|
| `md` | `const md` | — | The markdown renderer every surface shares. |
|
|
1213
1494
|
| `renderFoundryMarkdown` | `function renderFoundryMarkdown(body)` | `string` — HTML with secret sections for GM passages. | Rendering a note body for a Foundry document. |
|
|
1214
|
-
| `parseMarkdownFile` | `
|
|
1495
|
+
| `parseMarkdownFile` | `parseMarkdownFile(filePath, { addressContext, raw, renderDescription } = {})` | — | Parses a markdown file with YAML frontmatter; returns frontmatter, body, rendered description, raw source, and body coordinates. |
|
|
1215
1496
|
| `assertStatedScope` | `function assertStatedScope(skipDirectories, who)` | {void} | Refuse a corpus read whose scope its caller did not state. |
|
|
1216
1497
|
| `assertSuppliedCorpus` | `function assertSuppliedCorpus(records, who)` | {void} | Refuse a corpus read whose records its caller did not supply. |
|
|
1217
1498
|
| `statedTemplatePriority` | `function statedTemplatePriority(fm, label,` | {number\|null} The priority, or `null` when the note is not a template or states nothing. | The template priority a note states, for a system that treats an unstated one as "not a template" rather than as an authoring error. |
|
|
@@ -1224,11 +1505,44 @@ Shared helpers for the pack compilers in `packages/package-build/`. The HeroicLa
|
|
|
1224
1505
|
| `buildStats` | `function buildStats(systemVersion` | {object} The `_stats` block. | Default `_stats` block for compiled compendium entries. |
|
|
1225
1506
|
| `statsForPack` | `function statsForPack(packSystem, config` | {object} The `_stats` block for that pack. | The `_stats` block for one pack, stamped with the system that pack is for. |
|
|
1226
1507
|
| `defaultStats` | `function defaultStats()` | {object} The default `_stats` block, shared by every compiler. | The `_stats` block every compiler stamps on an entry it emits, built once. |
|
|
1227
|
-
| `buildContentLinkIndex` | `buildContentLinkIndex(contentBase, router
|
|
1508
|
+
| `buildContentLinkIndex` | `buildContentLinkIndex(contentBase, router?, options?)` | `{{byShortcode: Map, types: Set}}` From `buildWikilinkIndex`. | Indexes **every** note in the content tree so any pack compiler can resolve a wikilink to any other document. |
|
|
1228
1509
|
| `convertNoteWikilinks` | `convertNoteWikilinks(body, { type, id, pack, docPack, index, name, file, bodyLine, bodyColumn, lineMap })` | `{{markdown: string, unresolved: Array<object>}}` | Converts the wikilinks in one note's markdown, reporting any that have no target in the content tree. |
|
|
1229
1510
|
| `expandNoteTables` | `function expandNoteTables(body,` | `{{markdown: string, lineMap: Array<{line: number, generated: boolean}>}}` The body with every table expanded, and where each emitted line came from — which is what lets a diagnostic about the expanded body name an authored position. | Expand prepared SQL tables in one note's markdown, before wikilinks are resolved — so a generated cell may itself be a wikilink. |
|
|
1230
1511
|
| `folderFilename` | `function folderFilename(name, id)` | — | Builds a compendium-source filename for a folder JSON document: `folder_Name_id.json` with non-alphanumeric runs replaced by underscores. |
|
|
1231
|
-
| `walkMarkdownTree` | `walkMarkdownTree(rootDir, { skipDirectories })`
|
|
1512
|
+
| `walkMarkdownTree` | `walkMarkdownTree(rootDir, { skipDirectories, sources, renderDescription })` | generator yielding `{frontmatter, body, description, raw, bodyLine?, bodyColumn?, file, absPath}` | walking a content tree and reading every markdown file's frontmatter and body; `skipDirectories` is required rather than defaulted, so two callers cannot silently disagree about the corpus's scope |
|
|
1513
|
+
|
|
1514
|
+
`buildContentLinkIndex` accepts `{ config?, records, sqlTables?, foreign? }`.
|
|
1515
|
+
`records` is the required, supplied content corpus. The optional prepared
|
|
1516
|
+
`sqlTables` map supplies generated anchors and their Foundry page mappings,
|
|
1517
|
+
so glossary word links resolve to their dictionary entries.
|
|
1518
|
+
|
|
1519
|
+
`parseMarkdownFile` reads its supplied exact `raw` string when present. Its
|
|
1520
|
+
`renderDescription` option defaults to true; false leaves `description` empty
|
|
1521
|
+
while retaining authored body and positions. `walkMarkdownTree` accepts a
|
|
1522
|
+
selected `sources` map in walk order and the same rendering option, also true
|
|
1523
|
+
by default. Index collection explicitly omits unused rendered descriptions.
|
|
1524
|
+
`indexRecordsFor` accepts that selected map, a `syntax` projection function
|
|
1525
|
+
and a captured `foreignIndex`. These adapters do not retain state globally.
|
|
1526
|
+
|
|
1527
|
+
The physical wrappers `lintContentCharset`, `lintContentHtml`,
|
|
1528
|
+
`lintContentIcons`, `lintContentImages` and `lintContentTaskLists` accept an
|
|
1529
|
+
optional `sources` map of absolute paths to `{raw}`. Their existing extension,
|
|
1530
|
+
hidden-path and skip filters still apply; without the map they walk and read
|
|
1531
|
+
the tree. `lintPathCharset` accepts optional physical `inventory`, retaining
|
|
1532
|
+
walk roots and order. `lintNoteStates` accepts the authored `parsed` map, with
|
|
1533
|
+
its existing file reader for missing entries.
|
|
1534
|
+
`parseMarkdownFile` accepts LF and CRLF frontmatter delimiters and retains the
|
|
1535
|
+
exact source as `raw`. Its trimmed `body` keeps the authored line endings, with
|
|
1536
|
+
`bodyLine` and `bodyColumn` locating its first character in that original source.
|
|
1537
|
+
|
|
1538
|
+
### `./engine/diagnostics`
|
|
1539
|
+
|
|
1540
|
+
The `@heroiclands/package-build/engine/diagnostics` wildcard module provides
|
|
1541
|
+
source locations and frontmatter source slicing.
|
|
1542
|
+
|
|
1543
|
+
| Export | Signature | Returns | Use it when |
|
|
1544
|
+
| ------------------ | ----------------------- | -------------------------------------- | ------------------------------------------------------------------------------------------------------------------------ |
|
|
1545
|
+
| `splitFrontmatter` | `splitFrontmatter(raw)` | `{yaml: string, body: string} \| null` | Split opening LF or CRLF frontmatter into exact YAML and untrimmed body slices, without parsing or rewriting the source. |
|
|
1232
1546
|
|
|
1233
1547
|
### `engine.itemRegistry`
|
|
1234
1548
|
|
|
@@ -1384,15 +1698,15 @@ The content tree, built into a book. The I/O half of the PDF surface: it reads t
|
|
|
1384
1698
|
|
|
1385
1699
|
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.
|
|
1386
1700
|
|
|
1387
|
-
| Export | Signature
|
|
1388
|
-
| --------------------- |
|
|
1389
|
-
| `splitPages` | `
|
|
1390
|
-
| `assertUniquePages` | `function assertUniquePages(rawPages, noteName)`
|
|
1391
|
-
| `assertUniqueAnchors` | `function assertUniqueAnchors(rawPages, noteName)`
|
|
1392
|
-
| `journalPageId` | `function journalPageId(entryId, page)`
|
|
1393
|
-
| `buildPages` | `function buildPages(rawPages, entryId, noteName)`
|
|
1394
|
-
| `buildJournalEntry` | `buildJournalEntry({ id, name, markdown, leadName, folder,
|
|
1395
|
-
| `Journals` | `class Journals extends BasePackCompiler`
|
|
1701
|
+
| Export | Signature | Returns | Use it when |
|
|
1702
|
+
| --------------------- | ---------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------- |
|
|
1703
|
+
| `splitPages` | `splitPages(body, leadName, resolveRole, syntax?)` | — | Splits a markdown body into pages by top-level H1 headings. |
|
|
1704
|
+
| `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. |
|
|
1705
|
+
| `assertUniqueAnchors` | `function assertUniqueAnchors(rawPages, noteName)` | — | The anchor half of {@link assertUniquePages}, under its former name. |
|
|
1706
|
+
| `journalPageId` | `function journalPageId(entryId, page)` | {string} A 16-character Foundry id. | The id of one page within its entry. |
|
|
1707
|
+
| `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. |
|
|
1708
|
+
| `buildJournalEntry` | `buildJournalEntry({ id, name, markdown, leadName, folder, stats, syntax })` | {object} The JournalEntry document, keyed for the pack. | Assemble one JournalEntry document from a note's converted markdown. |
|
|
1709
|
+
| `Journals` | `class Journals extends BasePackCompiler` | — | not called directly — imported and driven by `engine/generate.mjs` as the "journals" pack compiler |
|
|
1396
1710
|
|
|
1397
1711
|
### `engine.macros`
|
|
1398
1712
|
|
|
@@ -1465,28 +1779,28 @@ Adventure pack compiler — `type: bundle` notes → Foundry `Adventure` documen
|
|
|
1465
1779
|
|
|
1466
1780
|
Pack JSON generation — in-repo Markdown → per-entry JSON (build-only). Reads the authoritative content tree at the configured content root and compiles each pack's entries to per-entry JSON under its build directory (`build/packs-json/<pack>/` in this repository). The JSON is a disposable build intermediate consumed by `build:compiledb` (which turns it into the shipped LevelDB packs) — it is never committed.
|
|
1467
1781
|
|
|
1468
|
-
| Export | Signature
|
|
1469
|
-
| ----------------------------- |
|
|
1470
|
-
| `systemCompilers` | `function systemCompilers(passes)`
|
|
1471
|
-
| `compilerFor` | `function compilerFor(docType, system`
|
|
1472
|
-
| `emittedArtFor` | `function emittedArtFor(type)`
|
|
1473
|
-
| `packJsonDir` | `const packJsonDir`
|
|
1474
|
-
| `itemPackJsonDirs` | `function itemPackJsonDirs(config`
|
|
1475
|
-
| `bundleSourceJsonDirs` | `function bundleSourceJsonDirs(config`
|
|
1476
|
-
| `orderPassesByDependency` | `function orderPassesByDependency(packs)`
|
|
1477
|
-
| `unsatisfiedPassDependencies` | `function unsatisfiedPassDependencies(running, config)`
|
|
1478
|
-
| `emptyPassErrors` | `function emptyPassErrors(passes)`
|
|
1479
|
-
| `generatePacksJson` | `
|
|
1782
|
+
| Export | Signature | Returns | Use it when |
|
|
1783
|
+
| ----------------------------- | ----------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------- |
|
|
1784
|
+
| `systemCompilers` | `function systemCompilers(passes)` | {Readonly<Record<string, Readonly<Record<string, Function>>>>} System id → document class → pass. | The system-specific compilers, derived from the passes themselves. |
|
|
1785
|
+
| `compilerFor` | `function compilerFor(docType, system` | {Function\|undefined} The compiler class, or `undefined` for a document type nothing here compiles — which {@link generatePack} reports rather than defaulting past. | The compiler class a pack of one document type and one system gets. |
|
|
1786
|
+
| `emittedArtFor` | `function emittedArtFor(type)` | {{document: string\|null, art: readonly string[]}\|null} What the type compiles into and the art it carries there, or `null` where no claim can be made. | The art fields a note of one content type reaches its document through, and the document it reaches. |
|
|
1787
|
+
| `packJsonDir` | `const packJsonDir` | {string} The pack's JSON directory. | Root of the build-only JSON tree for one pack. |
|
|
1788
|
+
| `itemPackJsonDirs` | `function itemPackJsonDirs(config` | {string[]} Each Item pack's JSON directory. | The generated JSON of **every** configured Item pack — what the actors pass reads its predefined items from. |
|
|
1789
|
+
| `bundleSourceJsonDirs` | `function bundleSourceJsonDirs(config` | {Record<string, string[]>} Each readable pack's JSON directory, by the Foundry document type it holds. | The compiled JSON a bundle may hold copies of, by document type. |
|
|
1790
|
+
| `orderPassesByDependency` | `function orderPassesByDependency(packs)` | {object[]} A new list, in compile order. | The passes to run, ordered so that each one follows the output it reads. |
|
|
1791
|
+
| `unsatisfiedPassDependencies` | `function unsatisfiedPassDependencies(running, config)` | {string[]} One message per unsatisfiable dependency. | The dependencies this run cannot satisfy by ordering, because the pass that would produce them is not in it. |
|
|
1792
|
+
| `emptyPassErrors` | `function emptyPassErrors(passes)` | {string[]} One message per pass that must not have been empty. | The passes that compiled nothing when they were expected to compile something — a build failure, not a quiet no-op. |
|
|
1793
|
+
| `generatePacksJson` | `generatePacksJson({ only, config, session, reportPreparation })` | {Promise<number>} Total error count across the generated packs. | Generate the build-only JSON for every pack (or one, when `only` is given). |
|
|
1480
1794
|
|
|
1481
1795
|
### `engine.compendiums`
|
|
1482
1796
|
|
|
1483
1797
|
Compendium pack library — compile / unpack / clean LevelDB packs. Wraps `@foundryvtt/foundryvtt-cli` over the packs a consuming repository declares: - {@link compilePacks}: generates each pack's per-entry JSON from the `assets/content/` Markdown into `build/packs-json/<name>/` (via generate.mjs), then builds LevelDB from it; no committed JSON, no vault. - {@link unpackPacks}: extracts a compiled pack back to per-entry JSON, rebuilding folder paths. - {@link cleanPacks}: normalizes/strips extracted JSON.
|
|
1484
1798
|
|
|
1485
|
-
| Export | Signature
|
|
1486
|
-
| -------------- |
|
|
1487
|
-
| `compilePacks` | `
|
|
1488
|
-
| `cleanPacks` | `async cleanPacks({ config, packDest, packName, entryName })`
|
|
1489
|
-
| `unpackPacks` | `async unpackPacks({ packs, config, stageDest, packDest, packName })`
|
|
1799
|
+
| Export | Signature | Returns | Use it when |
|
|
1800
|
+
| -------------- | ---------------------------------------------------------------------------------------- | ------- | ------------------------------------------------------------------------------------------------------------------------------------ |
|
|
1801
|
+
| `compilePacks` | `compilePacks({ config, sourcePacks, stageDest, packName, session, reportPreparation })` | — | Generates each pack's per-entry JSON from `assets/content/` into `build/packs-json/<name>/`, then builds the LevelDB output from it. |
|
|
1802
|
+
| `cleanPacks` | `async cleanPacks({ config, packDest, packName, entryName })` | — | Cleans and formats source JSON files, removing unnecessary permissions and flags and adding the proper spacing. |
|
|
1803
|
+
| `unpackPacks` | `async unpackPacks({ packs, config, stageDest, packDest, packName })` | — | Extracts compiled LevelDB packs back to per-entry JSON, rebuilding the folder hierarchy as directories. |
|
|
1490
1804
|
|
|
1491
1805
|
### `engine.regionEvents`
|
|
1492
1806
|
|
|
@@ -2033,6 +2347,15 @@ The Changesets changelog generator, loaded with `require` — a CommonJS module
|
|
|
2033
2347
|
| `getReleaseLine` | `getReleaseLine(changeset, type, changelogOpts)` | `string` — the summary, verbatim, separated from its neighbours by a blank line | writing one changeset's release line with no commit-hash prefix and no bullet |
|
|
2034
2348
|
| `getDependencyReleaseLine` | `getDependencyReleaseLine(changesets, dependenciesUpdated, changelogOpts)` | `string` — always `""` | satisfying the `ChangelogFunctions` interface; these packages narrate no internal dependency bumps |
|
|
2035
2349
|
|
|
2350
|
+
`engine/changelog-group.mjs` exports `groupChangelogText(text, { labels })`,
|
|
2351
|
+
returning `{ text, findings }`. It groups the newest release's summaries
|
|
2352
|
+
within each bump tier, retaining prose, bullets, wrapped continuations,
|
|
2353
|
+
nested item content and literal code. Merged labels' opening paragraphs
|
|
2354
|
+
combine in source order before their items. Exact duplicate items print
|
|
2355
|
+
once; labels follow the configured order, with unlabelled lead items first.
|
|
2356
|
+
Earlier release sections stay byte-identical, and grouping the result
|
|
2357
|
+
again leaves its text unchanged.
|
|
2358
|
+
|
|
2036
2359
|
## `./package.json`
|
|
2037
2360
|
|
|
2038
2361
|
Not a JavaScript module — this subpath entry exists so tooling (bundlers, `import.meta.resolve`, a script reading the installed version) can resolve the package's own `package.json` through Node's package-exports resolution instead of reaching outside the declared export map.
|
|
@@ -2046,7 +2369,7 @@ The engine wildcard subpath also exposes the body parsers below. Their figure ex
|
|
|
2046
2369
|
| `engine/content-figures.mjs` | `FIGURE_NAMES`, `FIGURE_CLASSES`, `scanFigures`, `numberFigures`, `renderFigureBlocks` | Scan leading captions and their next supported blocks; `numbered` distinguishes `:` and `:@`. `type` may override inference; `example` is supported. `FIGURE_CLASSES` retains built-in styled classes, not a closed author vocabulary. |
|
|
2047
2370
|
| `engine/content-blocks.mjs` | `BLOCK_NAMES`, `BLOCK_CONTAINERS`, `scanBlocks`, `renderBlocks`, `renderWithheldSections` | Read div and secret fences; generic div records have `name: "div"` and no generated title. Scanning includes poetry errors. |
|
|
2048
2371
|
| `engine/content-poetry.mjs` | `verseLines`, `poetryStanzas`, `inlineVerse`, `poetryMarkdown`, `scanPoetry`, `renderPoetry` | Read `poetry` code fences, validate authored metadata and tabs, and preserve relative indent levels and stanzas. |
|
|
2049
|
-
| `engine/content-spans.mjs` | `spanMarkdownPlugin`, `scanSpans`, `renderSpans` | Parse bracket spans after recognized links. The scanner returns spans and located errors;
|
|
2372
|
+
| `engine/content-spans.mjs` | `spanMarkdownPlugin`, `scanSpans`, `renderSpans` | Parse bracket spans after recognized links. The scanner returns spans, the exact `.name` inventory and located errors, retaining visible text and attribute positions; rendering preserves inline Markdown and permitted attributes. |
|
|
2050
2373
|
|
|
2051
2374
|
`scanFigures` retains `line` (1-based caption line), `bodyStart`/`bodyEnd` (0-based, exclusive-end target range), and `close` (last consumed target line), plus caption attributes, kind, label and number. Unnumbered captions have an empty label and consume no number. `collectAnchors` discovers heading, caption, div, poem and span identifiers; `markupAnchorFindings(body)` reports collisions involving non-page identifiers; Foundry maps non-page anchors to the actual containing page UUID. An unnumbered `ref` requires `form="title"` or `form="full"`.
|
|
2052
2375
|
|
|
@@ -2056,7 +2379,7 @@ The engine wildcard subpath also exposes the body parsers below. Their figure ex
|
|
|
2056
2379
|
| ---------------- | ----------------------------------------------------------------------------------- |
|
|
2057
2380
|
| `markupFindings` | Validate complete Markdown source and return diagnostics for the supplied filename. |
|
|
2058
2381
|
|
|
2059
|
-
`markupFindings(source, file)` masks YAML frontmatter without moving source positions and returns block and alert scanner errors as `{file, line, column, severity, message}` diagnostics. Content lint, Markdown checks and formatting preflight share this validation; malformed files are not rewritten by formatting or Markdown fixes.
|
|
2382
|
+
`markupFindings(source, file, syntax?)` masks YAML frontmatter without moving source positions and returns block and alert scanner errors as `{file, line, column, severity, message}` diagnostics. Content lint, Markdown checks and formatting preflight share this validation; malformed files are not rewritten by formatting or Markdown fixes.
|
|
2060
2383
|
|
|
2061
2384
|
`engine/content-alerts.mjs` exposes `ALERT_TYPES`, `scanAlerts(source)` and `renderAlerts(source, target)` and `alertMarkdownPlugin(md)`. The scanner returns `{blocks, errors}`; alert records carry `type`, `id`, `classes`, `attributes`, `title`, `body` and inclusive zero-based `start`/`end` source lines. The renderer produces attributed `aside.alert` wrappers while leaving alert body Markdown in the document's scope. Alert validation and anchor discovery run in site, Foundry and book pipelines.
|
|
2062
2385
|
|
|
@@ -2069,8 +2392,253 @@ that map as `parsed` for its document passes. `unclaimedNoteFindings` accepts it
|
|
|
2069
2392
|
alongside `records`. `locateFrontmatterKey` and `assertNoDeclaredPackage` accept
|
|
2070
2393
|
`raw` in their options to locate findings without opening the note again.
|
|
2071
2394
|
|
|
2395
|
+
`buildCompileCorpus`, `generatePacksJson` and `compilePacks` accept an optional
|
|
2396
|
+
`session` bound to their resolved configuration, roots, scope and router.
|
|
2397
|
+
`prepareContentIndex` also accepts it. Without a session, the existing entry
|
|
2398
|
+
points derive their own inputs. Generation and pack compilation accept
|
|
2399
|
+
`reportPreparation` (default true); a coordinating caller may set it false
|
|
2400
|
+
only after reporting the shared source findings itself.
|
|
2401
|
+
|
|
2402
|
+
`buildLinkIndex` and `buildContentLinkIndex` accept an optional `foreign` view
|
|
2403
|
+
from the same session; their diagnostic and routed resolver contracts remain
|
|
2404
|
+
distinct. `collectAnchors`, `markupAnchorFindings`, `markupFindings`, journal
|
|
2405
|
+
`splitPages`, Foundry `anchorsOf` and `buildJournalEntry` accept the optional
|
|
2406
|
+
`syntax` function described above. `buildSiteIndex` and `prepareTreeSqlTables`
|
|
2407
|
+
accept it in options, and `insertLexicalSections` accepts it in context.
|
|
2408
|
+
Prepared per-note SQL results carry it to `expandContentTables`. No scan result
|
|
2409
|
+
is reused for a different source projection.
|
|
2410
|
+
|
|
2072
2411
|
`prepareSqlTables` accepts sources with an optional `authoredSqlBlocks` count,
|
|
2073
2412
|
set by `joinGeneratedSections`. Subsequent fences are generated event queries
|
|
2074
2413
|
whose stub-inclusive results are identical; only authored fences receive a
|
|
2075
2414
|
stub-inclusion comparison. Query answers and comparisons are cached for one
|
|
2076
2415
|
preparation and kept separate.
|
|
2416
|
+
|
|
2417
|
+
`prepareTreeSqlTables` retains the complete record membership when opening a
|
|
2418
|
+
public database, so derived citations can distinguish hidden notes from
|
|
2419
|
+
unknown addresses. Only public records contribute rendered sources and links.
|
|
2420
|
+
|
|
2421
|
+
| Export | Contract |
|
|
2422
|
+
| ---------------------- | ---------------------------------------------------------------------------------------------------------------------------- |
|
|
2423
|
+
| `prepareLinkSqlTables` | Prepare SQL, page-list and automatic lexical sections for link checking, using one preparation per required source audience. |
|
|
2424
|
+
|
|
2425
|
+
`engine/sql-tables.mjs` exports `prepareLinkSqlTables(contentBase, options)`.
|
|
2426
|
+
Its options are `config`, `skipDirectories`, complete `records`, the shared
|
|
2427
|
+
`parsed` map, `foreignIndex` and optional `prepareAudience(audience)` callback. The callback supplies the bound session's existing `public` or `all` preparation. It returns prepared tables with `pageLists`
|
|
2428
|
+
and `inline` maps, or `undefined` when no source needs a directive or automatic
|
|
2429
|
+
lexical section. Public
|
|
2430
|
+
sources use public rows; GM sources use all rows. Query results remain local
|
|
2431
|
+
to each audience preparation, and the caller’s records remain complete for
|
|
2432
|
+
checking authored references. Unresolved derived citations retain the
|
|
2433
|
+
renderer’s plain-label behavior.
|
|
2434
|
+
|
|
2435
|
+
`generatedSectionsFor(db, records, contentBase, options?)` accepts complete
|
|
2436
|
+
lineage records as `options.all` (default `records`). Its result includes
|
|
2437
|
+
`lexical`, a map from note file to `LexicalSection[]`, beside `sections`,
|
|
2438
|
+
`views` and `derived`. `attachGeneratedSections(prepared, generated)` places
|
|
2439
|
+
those lists on each note's prepared results as `lexical`; body expansion
|
|
2440
|
+
inserts them alongside the prepared directives.
|
|
2441
|
+
|
|
2442
|
+
## Dictionary and glossary rows
|
|
2443
|
+
|
|
2444
|
+
The wildcard engine subpath exposes
|
|
2445
|
+
`@heroiclands/package-build/engine/dictionary-rows` for deriving the
|
|
2446
|
+
`dictionary` and `terms` SQL relations from content-index records:
|
|
2447
|
+
|
|
2448
|
+
| Export | Signature | Returns | Use it when |
|
|
2449
|
+
| ----------------------- | ----------------------------------------------- | ------------------------------------------- | ----------------------------------------------------------------------- |
|
|
2450
|
+
| `DICTIONARY_COLUMNS` | `const DICTIONARY_COLUMNS` | A readonly list of column names | Declaring the schema of a language's dictionary. |
|
|
2451
|
+
| `TERM_COLUMNS` | `const TERM_COLUMNS` | A readonly list of column names | Declaring the schema of owner-specific glossary terms. |
|
|
2452
|
+
| `ETYMOLOGY_KEYS` | `const ETYMOLOGY_KEYS` | A readonly list of keys | Reading the structure of a dictionary word's etymology. |
|
|
2453
|
+
| `dictionaryLetter` | `dictionaryLetter(text)` | `string` | Deriving a heading letter from a sort key. |
|
|
2454
|
+
| `dictionaryEntryAnchor` | `dictionaryEntryAnchor(language, name)` | `string` | Deriving the stable entry anchor for a word in a language's dictionary. |
|
|
2455
|
+
| `dictionaryAndTermRows` | `dictionaryAndTermRows(records, all = records)` | `{ dictionary: object[], terms: object[] }` | Deriving dictionary words and glossary terms for the selected records. |
|
|
2456
|
+
| `serializeRows` | `serializeRows(rows)` | `string` | Writing deterministic JSON Lines with keys sorted at every depth. |
|
|
2457
|
+
|
|
2458
|
+
`dictionaryEntryAnchor` takes the language's canonical Address and the
|
|
2459
|
+
word's form. Its `word-` anchor is derived from the full SHA-256 digest of
|
|
2460
|
+
that language and the NFC-normalized form, with case and accents preserved.
|
|
2461
|
+
It identifies the word's entry on that language page, including a word
|
|
2462
|
+
inherited from an ancestor.
|
|
2463
|
+
|
|
2464
|
+
`records` is the selected audience; `all` supplies the complete record set for
|
|
2465
|
+
language and ownership resolution. A dictionary word takes its sort key and
|
|
2466
|
+
pronunciation from the language's word declaration. A glossary entry can
|
|
2467
|
+
supply its own sort key and pronunciation, using the dictionary word's
|
|
2468
|
+
values where unstated.
|
|
2469
|
+
|
|
2470
|
+
A term naming a dictionary word carries its `class` and a `word` Address
|
|
2471
|
+
linking to its dictionary entry. The target is in the resolved language's
|
|
2472
|
+
dictionary when that language has own words; otherwise it is in the visible
|
|
2473
|
+
language that defines the word. The term's `language` stays resolved to the
|
|
2474
|
+
owner or entry's language. Without an entry's `definition`, its `senses`
|
|
2475
|
+
contain every dictionary gloss sense with
|
|
2476
|
+
an empty `bodies` list. With a definition, that owner's meaning is the sense
|
|
2477
|
+
instead. A rank or office of the same owner contributes its distinct
|
|
2478
|
+
descriptions after the dictionary senses; the owner's definition replaces
|
|
2479
|
+
both. Ranks, offices and terms outside the dictionary carry their source
|
|
2480
|
+
bodies. `common` belongs to
|
|
2481
|
+
glossary entries and `terms`, with each owner's choice kept on that owner's
|
|
2482
|
+
row. The [format
|
|
2483
|
+
reference](reference/format-details.md#dictionaries-and-glossaries) defines
|
|
2484
|
+
all columns and the rules for culture reach and ranks. The `cultures` list
|
|
2485
|
+
records associations for custom queries. Automatic Glossary sections select
|
|
2486
|
+
only their owner's explicitly declared `data.glossary` entries. A custom
|
|
2487
|
+
view can also select reached rank and office rows, keeping common marks only
|
|
2488
|
+
on its owner's own rows.
|
|
2489
|
+
|
|
2490
|
+
Dictionary rows carry `plural`, the noun's nonempty explicit plural or its
|
|
2491
|
+
listed language's first matching literal suffix replacement, and `verbs`,
|
|
2492
|
+
that language's stated `{invariant: true}` or six-person pattern, otherwise
|
|
2493
|
+
null. These optional facts use no ancestor grammar and perform no word-rule
|
|
2494
|
+
validation. The raw `words` relation's `plural` remains the authored value.
|
|
2495
|
+
Dictionary SQL types are projected only for that relation, retaining its
|
|
2496
|
+
etymology struct, sense list, usage list and integer depth even when all
|
|
2497
|
+
optional grammar values are null.
|
|
2498
|
+
|
|
2499
|
+
`@heroiclands/package-build/engine/language-terms` exposes the person
|
|
2500
|
+
descriptors used by the schema, SQL pattern type and head table:
|
|
2501
|
+
|
|
2502
|
+
| Export | Signature | Returns | Use it when |
|
|
2503
|
+
| -------------------- | -------------------------- | ------------------------------ | -------------------------------------------------------------------------------------------- |
|
|
2504
|
+
| `VERB_PERSON_FIELDS` | `const VERB_PERSON_FIELDS` | Readonly inner-key descriptors | Reading the six present person names, labels and ending-string shapes in presentation order. |
|
|
2505
|
+
|
|
2506
|
+
`serializeRows` returns the empty string for no rows; otherwise each object
|
|
2507
|
+
occupies one line and the file ends with a newline.
|
|
2508
|
+
|
|
2509
|
+
## Dictionary and glossary layouts
|
|
2510
|
+
|
|
2511
|
+
`@heroiclands/package-build/engine/dictionary-layout` renders SQL results as
|
|
2512
|
+
Markdown for the dictionary or glossary on its owner's note:
|
|
2513
|
+
|
|
2514
|
+
| Export | Signature | Returns | Use it when |
|
|
2515
|
+
| ------------------------- | ------------------------------------ | -------------------------- | ----------------------------------------------------------------------------------- |
|
|
2516
|
+
| `LAYOUT_REQUIRED_COLUMNS` | `const LAYOUT_REQUIRED_COLUMNS` | Required columns by layout | Checking the columns a dictionary or glossary query must select. |
|
|
2517
|
+
| `COMMON_MARK` | `const COMMON_MARK` | `string` | Reading the mark a common glossary entry opens with. |
|
|
2518
|
+
| `DICTIONARY_ENTRY_CLASS` | `const DICTIONARY_ENTRY_CLASS` | `string` | Identifying generated headword spans that are native dictionary targets in Foundry. |
|
|
2519
|
+
| `dictionaryEntryPlugin` | `dictionaryEntryPlugin(md)` | `void` | Registering native dictionary headword rendering on the Foundry Markdown parser. |
|
|
2520
|
+
| `renderDictionary` | `renderDictionary(result, options?)` | `string` | Rendering a language's words with classes, etymology, senses and citations. |
|
|
2521
|
+
| `renderGlossary` | `renderGlossary(result, options?)` | `string` | Rendering an owner's glossary entries, ranks and offices. |
|
|
2522
|
+
|
|
2523
|
+
`result` is `{ columns: string[], rows: object[] }`. Both layouts require
|
|
2524
|
+
`name`, `senses` and `usage`; a missing column throws an error naming it.
|
|
2525
|
+
`LayoutOptions` accepts `linkable(ref)`, `sectionLevel`, `addressContext`,
|
|
2526
|
+
`labelOf(address)`, `showKey` (`true`, `false` or `"only"`), `pageLanguage`, `pageOwner`,
|
|
2527
|
+
`verbs` and `entryAnchors` (default `true`). These control link availability, letter-heading level,
|
|
2528
|
+
Address resolution, labels, the abbreviation key and the language whose tag
|
|
2529
|
+
is omitted. `entryAnchors` controls canonical dictionary headword targets;
|
|
2530
|
+
custom SQL dictionary views set it to `false`, leaving those targets to the
|
|
2531
|
+
language's automatic Dictionary section. A dictionary marks nothing common;
|
|
2532
|
+
a glossary prints the mark and its explanation when a selected term carries
|
|
2533
|
+
`common: true`.
|
|
2534
|
+
|
|
2535
|
+
A dictionary entry has a stable word anchor. A glossary headword with a
|
|
2536
|
+
`word` Address links to that entry, and its `class` prints beside the language
|
|
2537
|
+
tag. Glossary rows sharing a headword and language combine into one entry;
|
|
2538
|
+
identical senses print once with all their source bodies. Dictionary-derived
|
|
2539
|
+
senses carry no body label. Dictionary entries print their `field` in italic
|
|
2540
|
+
parentheses and their etymology in brackets.
|
|
2541
|
+
|
|
2542
|
+
Nouns print their optional `plural` beside the class. Each represented
|
|
2543
|
+
language's optional `verbs` column supplies one table after the key and
|
|
2544
|
+
before the first letter, with empty endings shown as "unchanged". For an
|
|
2545
|
+
automatic dictionary, `options.verbs` supplies the owning language's pattern
|
|
2546
|
+
even when its own words contain only excluded classes. The existing
|
|
2547
|
+
nonempty-own-words gate still applies. `showKey: false` suppresses the key
|
|
2548
|
+
and verb table while retaining entries; `showKey: "only"` prints the
|
|
2549
|
+
abbreviation key alone. With the default or `showKey: true`, a stated pattern
|
|
2550
|
+
still prints when the result needs no abbreviation key. Each head-enabled
|
|
2551
|
+
result prints its represented languages' tables independently.
|
|
2552
|
+
|
|
2553
|
+
`pageOwner` is the containing page's canonical note Address. A glossary omits
|
|
2554
|
+
a sense's body label only when every contributing row belongs to that page
|
|
2555
|
+
and attributes the sense solely to itself. Other bodies and shared senses
|
|
2556
|
+
keep their labels. Without `pageOwner` or a selected `owner` column, all body
|
|
2557
|
+
labels remain.
|
|
2558
|
+
|
|
2559
|
+
`DICTIONARY_ENTRY_CLASS` is `"dictionary-entry"`. The Foundry parser's
|
|
2560
|
+
`dictionaryEntryPlugin` promotes the marked headword at the start of a
|
|
2561
|
+
dictionary paragraph to an inline native H6 heading in a div container. It
|
|
2562
|
+
keeps the containing journal page. Site and book rendering retain the
|
|
2563
|
+
headword's span and paragraph. `anchorsOf` maps this dictionary target to
|
|
2564
|
+
its containing page's UUID with the word fragment; generic authored spans
|
|
2565
|
+
keep their containing-page behavior.
|
|
2566
|
+
|
|
2567
|
+
## Automatic dictionary and glossary sections
|
|
2568
|
+
|
|
2569
|
+
`@heroiclands/package-build/engine/lexical-sections` derives and inserts the
|
|
2570
|
+
same automatic lists for the site, Foundry journal, book and published index:
|
|
2571
|
+
|
|
2572
|
+
| Export | Signature | Returns | Use it when |
|
|
2573
|
+
| ----------------------- | ------------------------------------------------------ | ------------------------------------- | ----------------------------------------------------------------- |
|
|
2574
|
+
| `LEXICAL_SECTIONS` | `const LEXICAL_SECTIONS` | A frozen list of section declarations | Reading section headings, accepted anchors and frontmatter gates. |
|
|
2575
|
+
| `hasLexicalEntries` | `hasLexicalEntries(frontmatter)` | `boolean` | Checking whether a note supplies automatic lexical content. |
|
|
2576
|
+
| `lexicalSectionsFor` | `lexicalSectionsFor(records, all = records)` | `Map<string, LexicalSection[]>` | Deriving lists keyed by their owner's canonical Address. |
|
|
2577
|
+
| `insertLexicalSections` | `insertLexicalSections(markdown, sections?, context?)` | `{markdown, lineMap, generatedFrom?}` | Inserting lists while preserving authored text and source lines. |
|
|
2578
|
+
|
|
2579
|
+
Each registry declaration has `slug`, `heading`, `aliases`, `dataKey` and
|
|
2580
|
+
`types`, whose entries have `type` and an optional `subType`. The Dictionary
|
|
2581
|
+
declaration applies to `skill`/`language` notes with nonempty `data.words`,
|
|
2582
|
+
and accepts `dictionary` and `lexicon` heading anchors. The Glossary
|
|
2583
|
+
declaration applies to cultures (`lore`/`culture`) and affiliations with
|
|
2584
|
+
nonempty `data.glossary`, and accepts `glossary`. `hasLexicalEntries` reads
|
|
2585
|
+
these same type and nonempty-array gates; it does not validate the authored
|
|
2586
|
+
body.
|
|
2587
|
+
|
|
2588
|
+
`records` is the visible audience; `all` supplies complete lineage records.
|
|
2589
|
+
Each `LexicalSection` has `slug`, `heading`, `aliases`, a query-shaped
|
|
2590
|
+
`result: {columns, rows}` and dictionary-layout `options`. Dictionary rows
|
|
2591
|
+
cover the language's full dictionary; glossary rows select its owner's
|
|
2592
|
+
explicit entries only, including a rank or office only when named there.
|
|
2593
|
+
A qualifying words declaration containing only excluded word classes still
|
|
2594
|
+
produces its Dictionary heading, without entries or a key.
|
|
2595
|
+
|
|
2596
|
+
`insertLexicalSections` takes the body after authored directives expand.
|
|
2597
|
+
Pass the original body as `context.authored` together with the expansion's
|
|
2598
|
+
line map, so generated headings cannot select or end an authored section.
|
|
2599
|
+
Without `authored`, the helper treats `markdown` as its source. It inserts a
|
|
2600
|
+
list at the end of the first matching authored anchored section, after its
|
|
2601
|
+
text and before the next authored heading at the same or a higher level.
|
|
2602
|
+
Headings inside comments, raw HTML, literal code or named blocks do not select
|
|
2603
|
+
sections.
|
|
2604
|
+
Without that heading, it appends an H1 with the section's canonical anchor.
|
|
2605
|
+
Letter headings sit one level below the containing heading, up to H6. The
|
|
2606
|
+
optional context is `{authored?, lineMap?, generatedFrom?}`; the returned line map keeps
|
|
2607
|
+
authored positions, marks inserted lines as generated and adjusts the start
|
|
2608
|
+
of appended nonlexical sections. It leaves the supplied line-map array
|
|
2609
|
+
unchanged.
|
|
2610
|
+
|
|
2611
|
+
## Glossary term validation
|
|
2612
|
+
|
|
2613
|
+
The wildcard module `@heroiclands/package-build/engine/culture-reach`
|
|
2614
|
+
exposes the term lookup used for glossary validation;
|
|
2615
|
+
`@heroiclands/package-build/engine/glossary-entries` exposes its findings:
|
|
2616
|
+
|
|
2617
|
+
| Export | Signature | Returns | Use it when |
|
|
2618
|
+
| --------------- | ------------------------------------- | ----------------------- | ----------------------------------------------------------------------------------------------- |
|
|
2619
|
+
| `noteTerms` | `noteTerms(index, note, entry?)` | `Map<string, string[]>` | Reading eligible dictionary words and reached rank or office descriptions by NFC name. |
|
|
2620
|
+
| `checkGlossary` | `checkGlossary(note, { index } = {})` | `object[]` | Checking the meanings and language references of a culture's or affiliation's glossary entries. |
|
|
2621
|
+
|
|
2622
|
+
`noteTerms` selects words from the entry's resolved language and its
|
|
2623
|
+
ancestors; without `entry`, it uses the note's own resolved language. It also
|
|
2624
|
+
includes ranks and offices that reach the note. `checkGlossary` returns
|
|
2625
|
+
located findings for entries naming no language, duplicate names, invalid
|
|
2626
|
+
pronunciations and entries without a definition that match neither a word in
|
|
2627
|
+
their resolved language nor a reached rank or office. A definition repeating
|
|
2628
|
+
the only sense it replaces produces a warning. With no `index`, the check
|
|
2629
|
+
validates only what the note states locally.
|
|
2630
|
+
|
|
2631
|
+
## Built-piece resolution
|
|
2632
|
+
|
|
2633
|
+
The wildcard engine subpath exposes `pieceGlosses` from
|
|
2634
|
+
`@heroiclands/package-build/engine/word-rows`.
|
|
2635
|
+
|
|
2636
|
+
| Export | Signature | Returns | Use it when |
|
|
2637
|
+
| -------------- | ------------------------ | ----------------------------------- | ---------------------------------------------------------------------------- |
|
|
2638
|
+
| `pieceGlosses` | `pieceGlosses(...datas)` | A lookup returning a string or null | Resolving a word's built pieces for lint, word rows or dictionary etymology. |
|
|
2639
|
+
|
|
2640
|
+
Pass language `data` objects nearest first, in `lineageChain` order. Each
|
|
2641
|
+
language contributes words, affixes and elements, in that order. Words and
|
|
2642
|
+
affixes supply `gloss`, including an affix matched by its `front` form. Elements
|
|
2643
|
+
supply `sense` and match their `form` and `variants`. Exact matches precede matches without joining hyphens; case and
|
|
2644
|
+
accents are preserved. A missing gloss returns `null`.
|