@heroiclands/package-build 21.0.0 → 21.2.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 +228 -0
- package/CONTENT.md +109 -7
- package/assets/fonts/libertinus/LibertinusMono-Regular.otf +0 -0
- package/assets/fonts/libertinus/LibertinusSans-Bold.otf +0 -0
- package/assets/fonts/libertinus/LibertinusSans-Italic.otf +0 -0
- package/assets/fonts/libertinus/LibertinusSans-Regular.otf +0 -0
- package/assets/fonts/libertinus/OFL.txt +94 -0
- package/assets/fonts/libertinus/provenance.yaml +11 -0
- package/assets/images/banners/afflictionbnr.webp +0 -0
- package/assets/images/banners/armorclothingbnr.webp +0 -0
- package/assets/images/banners/attributebnr.webp +0 -0
- package/assets/images/banners/containerbnr.webp +0 -0
- package/assets/images/banners/devdocsbnr.webp +0 -0
- package/assets/images/banners/miscgearbnr.webp +0 -0
- package/assets/images/banners/mysticalabilitybnr.webp +0 -0
- package/assets/images/banners/projectilebnr.webp +0 -0
- package/assets/images/banners/rulesbnr.webp +0 -0
- package/assets/images/banners/skillbnr.webp +0 -0
- package/assets/images/banners/traumabnr.webp +0 -0
- package/assets/images/banners/userguidebnr.webp +0 -0
- package/assets/images/banners/weapongearbnr.webp +0 -0
- package/assets/images/provenance.yaml +4 -0
- package/bin/content-build.mjs +19 -5
- package/ci/emit-asset-index.mjs +32 -0
- package/content-config.mjs +30 -1
- package/docs/api.md +100 -26
- package/docs/configuration.md +16 -7
- package/docs/content-format.md +512 -109
- package/engine/actor-compiler.mjs +58 -9
- package/engine/art-fields.mjs +292 -0
- package/engine/asset-index.mjs +397 -0
- package/engine/asset-types.mjs +192 -0
- package/engine/base-compiler.mjs +69 -1
- package/engine/bundles.mjs +5 -5
- package/engine/compile-corpus.mjs +4 -0
- package/engine/content-address.mjs +14 -2
- package/engine/content-charset.mjs +5 -1
- package/engine/content-embeds.mjs +314 -0
- package/engine/content-html.mjs +5 -1
- package/engine/content-icons.mjs +5 -1
- package/engine/content-images.mjs +26 -5
- package/engine/content-index.mjs +68 -33
- package/engine/content-links.mjs +164 -24
- package/engine/frontmatter-lint.mjs +36 -23
- package/engine/helpers.mjs +83 -31
- package/engine/index-records.mjs +63 -4
- package/engine/index.mjs +15 -0
- package/engine/infobox-registry.mjs +50 -3
- package/engine/infobox-render.mjs +4 -3
- package/engine/infobox.mjs +12 -8
- package/engine/item-compiler.mjs +10 -10
- package/engine/journals.mjs +3 -0
- package/engine/macros.mjs +16 -13
- package/engine/map-notes.mjs +29 -22
- package/engine/metadata-index.mjs +44 -36
- package/engine/note-vocabulary.mjs +71 -29
- package/engine/pack-router.mjs +14 -0
- package/engine/packagebuild-index.mjs +124 -0
- package/engine/packages.mjs +83 -0
- package/engine/pathnames.mjs +74 -0
- package/engine/pdf-build.mjs +133 -10
- package/engine/pdf-render.mjs +41 -41
- package/engine/scenes.mjs +12 -9
- package/engine/site-build.mjs +43 -16
- package/engine/site-index.mjs +10 -1
- package/engine/subtype-registry.mjs +21 -0
- package/engine/web-wikilinks.mjs +46 -12
- package/engine/wikilink-syntax.mjs +20 -2
- package/engine/wikilinks.mjs +15 -2
- package/hm3/actors.mjs +11 -38
- package/hm3/infobox.mjs +1 -1
- package/package.json +5 -2
- package/packagebuild-metadata.jsonl +13 -0
- package/sohl/actors.mjs +12 -86
- package/sohl/infobox.mjs +1 -1
- package/sohl/note-schemas.mjs +4 -16
- package/types/content-config.d.mts +8 -0
- package/types/engine/actor-compiler.d.mts +29 -0
- package/types/engine/art-fields.d.mts +192 -0
- package/types/engine/asset-index.d.mts +92 -0
- package/types/engine/asset-types.d.mts +110 -0
- package/types/engine/base-compiler.d.mts +42 -1
- package/types/engine/compile-corpus.d.mts +4 -1
- package/types/engine/content-charset.d.mts +4 -0
- package/types/engine/content-embeds.d.mts +218 -0
- package/types/engine/content-html.d.mts +4 -0
- package/types/engine/content-icons.d.mts +4 -0
- package/types/engine/content-images.d.mts +15 -2
- package/types/engine/content-index.d.mts +27 -15
- package/types/engine/frontmatter-lint.d.mts +11 -14
- package/types/engine/helpers.d.mts +7 -2
- package/types/engine/index-records.d.mts +44 -3
- package/types/engine/index.d.mts +5 -0
- package/types/engine/infobox-registry.d.mts +36 -1
- package/types/engine/infobox.d.mts +6 -4
- package/types/engine/macros.d.mts +9 -5
- package/types/engine/map-notes.d.mts +10 -6
- package/types/engine/metadata-index.d.mts +2 -17
- package/types/engine/note-vocabulary.d.mts +30 -0
- package/types/engine/packagebuild-index.d.mts +56 -0
- package/types/engine/packages.d.mts +62 -0
- package/types/engine/pathnames.d.mts +41 -81
- package/types/engine/pdf-build.d.mts +58 -1
- package/types/engine/pdf-render.d.mts +3 -3
- package/types/engine/scenes.d.mts +2 -1
- package/types/engine/site-build.d.mts +5 -1
- package/types/engine/site-index.d.mts +4 -1
- package/types/engine/subtype-registry.d.mts +20 -0
- package/types/engine/web-wikilinks.d.mts +14 -4
- package/types/engine/wikilink-syntax.d.mts +13 -1
- package/types/engine/wikilinks.d.mts +7 -1
- package/types/sohl/actors.d.mts +0 -1
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* This file is part of the Song of Heroic Lands (SoHL) system for Foundry VTT.
|
|
3
|
+
* Copyright (c) 2024-2026 Tom Rodriguez ("Toasty") — <toasty@heroiclands.org>
|
|
4
|
+
*
|
|
5
|
+
* This work is licensed under the GNU General Public License v3.0 (GPLv3).
|
|
6
|
+
* You may copy, modify, and distribute it under the terms of that license.
|
|
7
|
+
*
|
|
8
|
+
* For full terms, see the LICENSE.md file in the project root or visit:
|
|
9
|
+
* https://www.gnu.org/licenses/gpl-3.0.html
|
|
10
|
+
*
|
|
11
|
+
* SPDX-License-Identifier: GPL-3.0-or-later
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Publish this package's own content index, for the readers that are not this
|
|
16
|
+
* process.
|
|
17
|
+
*
|
|
18
|
+
* Run by `prepack`, beside the declaration build and for the same reason: the
|
|
19
|
+
* artifact is derived from what the tree already holds, so it belongs in the
|
|
20
|
+
* tarball and not in the repository. A build inside this process never reads
|
|
21
|
+
* it — {@link module:engine/packagebuild-index.packageBuildRecords} walks the
|
|
22
|
+
* shipped tree — so an absent file makes nothing fail, and anything reading the
|
|
23
|
+
* published form gets the same records the walk produces.
|
|
24
|
+
*
|
|
25
|
+
* Not a `package-build` subcommand: no consuming repository has a reason to run
|
|
26
|
+
* it, and a CLI that offers one would be surface nobody asked for.
|
|
27
|
+
*/
|
|
28
|
+
|
|
29
|
+
import { emitPackageBuildIndex } from "../engine/packagebuild-index.mjs";
|
|
30
|
+
|
|
31
|
+
const { file, assets, bytes } = emitPackageBuildIndex();
|
|
32
|
+
console.log(`package-build: ${file} (${assets} asset(s), ${bytes} bytes)`);
|
package/content-config.mjs
CHANGED
|
@@ -70,6 +70,8 @@ import YAML from "yaml";
|
|
|
70
70
|
// Leaves with no local imports of their own, so naming them here cannot close
|
|
71
71
|
// a cycle around a consumer's config file (see `engine/pack-config.mjs`).
|
|
72
72
|
import { ADDRESS_SEGMENT_PATTERN, isAddressSegment } from "./engine/address-charset.mjs";
|
|
73
|
+
import { ASSET_TYPE_NAMES } from "./engine/asset-types.mjs";
|
|
74
|
+
import { isReservedPackage } from "./engine/packages.mjs";
|
|
73
75
|
import { EMPTY_ICON_REGISTRY, checkIconRegistry } from "./engine/content-icons.mjs";
|
|
74
76
|
import { MAP_TYPES, PACK_BY_TYPE } from "./engine/ids.mjs";
|
|
75
77
|
import { ACTOR_TYPES } from "./engine/subtype-registry.mjs";
|
|
@@ -149,6 +151,12 @@ const DOCUMENTATION_REFUSES = Object.freeze({
|
|
|
149
151
|
*/
|
|
150
152
|
export const DEFAULT_PATHS = /** @type {const} */ ({
|
|
151
153
|
content: "assets/content",
|
|
154
|
+
// The asset roots' parent — `icons/`, `images/` and `audio/` sit directly
|
|
155
|
+
// under it, and the content tree beside them. Named separately from
|
|
156
|
+
// `content` rather than derived from its parent, because the two are free to
|
|
157
|
+
// move independently and deriving one from the other would make relocating
|
|
158
|
+
// either a surprise for the other.
|
|
159
|
+
assets: "assets",
|
|
152
160
|
// Where `content-index` writes this package's note index. Under `build/`
|
|
153
161
|
// because it is derived and disposable — regenerating it costs a
|
|
154
162
|
// frontmatter parse — and emphatically not under `stage`, which is mirrored
|
|
@@ -361,6 +369,9 @@ export function publishesContentPages(config) {
|
|
|
361
369
|
*
|
|
362
370
|
* @typedef {object} PathsInput
|
|
363
371
|
* @property {string} [content] Content tree root.
|
|
372
|
+
* @property {string} [assets] The asset roots' parent — the directory
|
|
373
|
+
* holding `icons/`, `images/` and
|
|
374
|
+
* `audio/`.
|
|
364
375
|
* @property {string} [contentIndex] Where `content-index` writes this
|
|
365
376
|
* package's note index. Outbound, and a
|
|
366
377
|
* derived artifact — never a source, and
|
|
@@ -383,6 +394,7 @@ export function publishesContentPages(config) {
|
|
|
383
394
|
*
|
|
384
395
|
* @typedef {object} ResolvedPaths
|
|
385
396
|
* @property {string} content
|
|
397
|
+
* @property {string} assets
|
|
386
398
|
* @property {string} contentIndex
|
|
387
399
|
* @property {string} packJson
|
|
388
400
|
* @property {string} stage
|
|
@@ -827,7 +839,7 @@ function requireNonEmptyString(value, field) {
|
|
|
827
839
|
}
|
|
828
840
|
|
|
829
841
|
/**
|
|
830
|
-
* The `contentPackage`, checked against the
|
|
842
|
+
* The `contentPackage`, checked against the three rules an address puts on it.
|
|
831
843
|
*
|
|
832
844
|
* It is the first segment of every canonical address this repository publishes
|
|
833
845
|
* (`package-system-type-shortcode`, so `sohl-none-doc-gear`), and an address is
|
|
@@ -856,6 +868,13 @@ function requireNonEmptyString(value, field) {
|
|
|
856
868
|
* package's id to *be* its system id, and `sohl-sohl-skill-clmb` is the
|
|
857
869
|
* honest address that results — which is the reason to prevent the ones that
|
|
858
870
|
* are avoidable.
|
|
871
|
+
* 3. _Not reserved_. `packagebuild` addresses the files this toolchain ships
|
|
872
|
+
* itself, so a repository claiming the name would publish addresses that
|
|
873
|
+
* collide with them — see {@link module:engine/packages}.
|
|
874
|
+
*
|
|
875
|
+
* The type vocabulary rule reaches the **asset** types too: `icon`, `image` and
|
|
876
|
+
* `audio` are types an address names exactly as it names a being, so a package
|
|
877
|
+
* called `image` would make `image-thorn` readable two ways.
|
|
859
878
|
*
|
|
860
879
|
* @param {unknown} value - The configured `contentPackage`.
|
|
861
880
|
* @param {ReadonlySet<string>} docEntryTypes - Every type whose prose compiles
|
|
@@ -878,6 +897,15 @@ function requireContentPackage(value, docEntryTypes) {
|
|
|
878
897
|
"than merely ugly. `harn-adventures` became `harnadventures`",
|
|
879
898
|
);
|
|
880
899
|
}
|
|
900
|
+
if (isReservedPackage(pkg)) {
|
|
901
|
+
fail(
|
|
902
|
+
"contentPackage",
|
|
903
|
+
`is \`${pkg}\`, which is a reserved package name. ` +
|
|
904
|
+
`\`${pkg}-none-image-<shortcode>\` already addresses a file the ` +
|
|
905
|
+
"toolchain itself ships, so a package claiming the name would " +
|
|
906
|
+
"publish addresses that collide with it. Rename the package",
|
|
907
|
+
);
|
|
908
|
+
}
|
|
881
909
|
// The closed vocabulary is read alongside the configured registries, not
|
|
882
910
|
// instead of them, because neither is a superset of the other. The format's
|
|
883
911
|
// vocabulary holds every type a note may declare *however this repository
|
|
@@ -891,6 +919,7 @@ function requireContentPackage(value, docEntryTypes) {
|
|
|
891
919
|
const typeNames = new Set([
|
|
892
920
|
...Object.keys(PACK_BY_TYPE),
|
|
893
921
|
...Object.keys(NOTE_VOCABULARY),
|
|
922
|
+
...ASSET_TYPE_NAMES,
|
|
894
923
|
...docEntryTypes,
|
|
895
924
|
...[...docEntryTypes].map((type) => `doc${type}`),
|
|
896
925
|
]);
|
package/docs/api.md
CHANGED
|
@@ -336,6 +336,7 @@ The closed half of a note's frontmatter: the `data:` container and each type's `
|
|
|
336
336
|
| ------------------------- | -------------------------------------------- | ---------------------------------------- | -------------------------------------------------------------------------------------------- |
|
|
337
337
|
| `DRAFT_TAG` | `const DRAFT_TAG` | — | naming the declared tag that marks a note as unfinished |
|
|
338
338
|
| `NOTE_VOCABULARY` | `const NOTE_VOCABULARY` | — | looking up, per content type, its `data:` field list and its closed `subType` values |
|
|
339
|
+
| `SHARED_DATA_FIELDS` | `const SHARED_DATA_FIELDS` | `readonly DataFieldSpec[]` | reading the `data:` keys every type accepts, whatever it is — the art slots legal everywhere |
|
|
339
340
|
| `DECLARED_TAGS` | `const DECLARED_TAGS` | — | looking up the tags a note type may declare, grouped |
|
|
340
341
|
| `declaredTags` | `declaredTags(type, groups)` | `readonly string[]` | reading the declared tags a note of a type may carry, flattened |
|
|
341
342
|
| `applicableTagGroups` | `applicableTagGroups(type, groups)` | `object[]` | reading the declared tag groups that apply to a note type |
|
|
@@ -388,11 +389,12 @@ The declared infobox: what a note's summary panel holds, decided once and render
|
|
|
388
389
|
|
|
389
390
|
The infobox declarations this toolchain ships, one per system, and the single call each medium makes to build a note's boxes.
|
|
390
391
|
|
|
391
|
-
| Export
|
|
392
|
-
|
|
|
393
|
-
| `KNOWN_INFOBOXES`
|
|
394
|
-
| `infoboxFor`
|
|
395
|
-
| `noteInfoboxes`
|
|
392
|
+
| Export | Signature | Returns | Use it when |
|
|
393
|
+
| ------------------------ | ----------------------------------------- | --------------------- | ------------------------------------------------------------------------------------------------------------ |
|
|
394
|
+
| `KNOWN_INFOBOXES` | `const KNOWN_INFOBOXES` | — | enumerating every system's infobox declaration, in the order a page shows them |
|
|
395
|
+
| `infoboxFor` | `infoboxFor(system)` | `object \| undefined` | looking up one system's declaration by its id |
|
|
396
|
+
| `noteInfoboxes` | `noteInfoboxes(fm, options)` | `object[]`, throws | building every box one note carries, wired to the shipped registries |
|
|
397
|
+
| `compilesSystemDocument` | `compilesSystemDocument(fm, map, router)` | `boolean` | asking whether one system compiles a document for one note, which is what a system box's _available_ asserts |
|
|
396
398
|
|
|
397
399
|
### `engine.infoboxRender`
|
|
398
400
|
|
|
@@ -462,6 +464,7 @@ Which note-type → document-subtype maps this toolchain ships, and the two ques
|
|
|
462
464
|
| ----------------------------- | ---------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------- |
|
|
463
465
|
| `KNOWN_DOCUMENT_SUBTYPE_MAPS` | `const KNOWN_DOCUMENT_SUBTYPE_MAPS` | — | The note-type → document-subtype maps this toolchain ships. |
|
|
464
466
|
| `ACTOR_TYPES` | `const ACTOR_TYPES` | — | Every note type any shipped map compiles into an **Actor**. |
|
|
467
|
+
| `DEFAULT_DOCUMENT_SUBTYPES` | `const DEFAULT_DOCUMENT_SUBTYPES` | — | The map a pack declaring no `system:` compiles its documents against. |
|
|
465
468
|
| `subtypeMapFor` | `function subtypeMapFor(system)` | {import("./document-subtypes.mjs").DocumentSubtypeMap\|undefined} Its map, or `undefined` where this toolchain ships none for it. | The map one system ships, by its id. |
|
|
466
469
|
| `schemaSubtypeOf` | `function schemaSubtypeOf(system, type)` | {string} The document subtype to look up. | The document subtype a note type compiles into for one system — the translation the _schema_ check needs, and the reason it needs one. |
|
|
467
470
|
|
|
@@ -521,19 +524,19 @@ Emitting this package's cross-package link manifest. `engine/content-address.mjs
|
|
|
521
524
|
|
|
522
525
|
Emitting this package's content index. Every content build already walks the whole note tree and parses every note's frontmatter — the pack compilers, the site build, and the content-table expander each do it — and every one of them throws the result away when it finishes. So nothing outside a build can ask a question about the content: "which beings carry no `kbcat`?", "what does this table actually select?", "did that type rename leave anything behind?" have no answer short of writing a throwaway script that re-walks the tree. Eight dead Bestiary tables shipped for weeks behind exactly that gap.
|
|
523
526
|
|
|
524
|
-
| Export | Signature
|
|
525
|
-
| ----------------------- |
|
|
526
|
-
| `noteAddress` | `function noteAddress(frontmatter, contentPackage)`
|
|
527
|
-
| `sortKeysDeep` | `function sortKeysDeep(value)`
|
|
528
|
-
| `asciiName` | `function asciiName(name)`
|
|
529
|
-
| `asciiAliases` | `function asciiAliases(aliases)`
|
|
530
|
-
| `serializeContentIndex` | `function serializeContentIndex(records)`
|
|
531
|
-
| `indexRecordsFor` | `indexRecordsFor({ contentBase, config, skipDirectories, problems })`
|
|
532
|
-
| `emitContentIndex` | `emitContentIndex({ contentBase, outDir, config })`
|
|
533
|
-
| `buildIndexRecord` | `buildIndexRecord({ frontmatter, relPath, absPath, contentPackage, body, bodyLine, manifest })`
|
|
534
|
-
| `collectContentIndex` | `collectContentIndex(contentBase, { contentPackage, skipDirectories, manifest, problems })`
|
|
527
|
+
| Export | Signature | Returns | Use it when |
|
|
528
|
+
| ----------------------- | ------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
529
|
+
| `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. |
|
|
530
|
+
| `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. |
|
|
531
|
+
| `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. |
|
|
532
|
+
| `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. |
|
|
533
|
+
| `serializeContentIndex` | `function serializeContentIndex(records)` | {string} One compact JSON object per line, newline-terminated. | Serialize records as JSON Lines. |
|
|
534
|
+
| `indexRecordsFor` | `indexRecordsFor({ contentBase, config, skipDirectories, problems })` | {object[]} One record per note, plus one per documentation entry. | The index records for a content tree, without writing anything. |
|
|
535
|
+
| `emitContentIndex` | `emitContentIndex({ contentBase, outDir, config })` | {{file: string, notes: number, assets: number, records: number, bytes: number}} Where it was written, how many notes and how many assets it holds, how many records that is in all, and its size. | Emit this package's content index. |
|
|
536
|
+
| `buildIndexRecord` | `buildIndexRecord({ frontmatter, relPath, absPath, contentPackage, body, bodyLine, 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 |
|
|
537
|
+
| `collectContentIndex` | `collectContentIndex(contentBase, { contentPackage, skipDirectories, assetsBase, manifest, problems })` | `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 |
|
|
535
538
|
|
|
536
|
-
`engine.contentIndex` also re-exports
|
|
539
|
+
`engine.contentIndex` also re-exports seven names from neighbouring leaf modules, at this same import path:
|
|
537
540
|
|
|
538
541
|
| Export | Signature | Returns | Use it when |
|
|
539
542
|
| --------------------- | -------------------------------- | -------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
@@ -541,7 +544,59 @@ Emitting this package's content index. Every content build already walks the who
|
|
|
541
544
|
| `DERIVED_KEYS` | `const DERIVED_KEYS` | — | reading which keys the content index adds to a record, and which a note therefore may not author itself |
|
|
542
545
|
| `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 |
|
|
543
546
|
| `authoredFrontmatter` | `authoredFrontmatter(record)` | `Record<string, any>` — the frontmatter without `DERIVED_KEYS` | reading back exactly what a note authored from its index record, so a pass can lint or compile from the index without reasoning about derived fields as if the author wrote them |
|
|
544
|
-
| `isNoteRecord` | `isNoteRecord(record)` | `boolean` | telling a note's own record apart from its documentation journal's, when enumerating the corpus
|
|
547
|
+
| `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 |
|
|
548
|
+
| `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 |
|
|
549
|
+
| `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 |
|
|
550
|
+
|
|
551
|
+
### `engine.assetTypes`
|
|
552
|
+
|
|
553
|
+
The asset types — `icon`, `image` and `audio` — and the three roots they are walked from. An asset is addressed exactly as a note is; what differs is that a `.webp` carries no frontmatter and has nowhere to say what it is, so the root supplies the type and the list of roots is closed.
|
|
554
|
+
|
|
555
|
+
| Export | Signature | Returns | Use it when |
|
|
556
|
+
| ------------------ | ----------------------------- | ------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------- |
|
|
557
|
+
| `ASSET_TYPES` | `const ASSET_TYPES` | `readonly AssetType[]` — type, root, extensions, description | walking the roots, or presenting the vocabulary |
|
|
558
|
+
| `ASSET_TYPE_NAMES` | `const ASSET_TYPE_NAMES` | `ReadonlySet<string>` | admitting the asset types into a type vocabulary an address is parsed against |
|
|
559
|
+
| `ASSET_SYSTEM` | `const ASSET_SYSTEM` | `string` — always `none` | writing an asset address, whose `<system>` segment is a property of the type rather than of the reference |
|
|
560
|
+
| `IMAGE_EXTENSIONS` | `const IMAGE_EXTENSIONS` | `readonly string[]` — lowercase, dot-led | deciding whether a file under a picture root is an asset at all |
|
|
561
|
+
| `AUDIO_EXTENSIONS` | `const AUDIO_EXTENSIONS` | `readonly string[]` — lowercase, dot-led | the same question for `audio/` |
|
|
562
|
+
| `isAssetType` | `isAssetType(type)` | `boolean` | scoping a rewrite rule, which may substitute a file and nothing else |
|
|
563
|
+
| `assetTypeOfRoot` | `assetTypeOfRoot(root)` | `AssetType \| undefined` | reading a root directory name back to the type it declares |
|
|
564
|
+
| `isAssetShortcode` | `isAssetShortcode(shortcode)` | `boolean` | telling an addressable filename from one that carries a version string, a hyphen or a date stamp |
|
|
565
|
+
|
|
566
|
+
### `engine.assetIndex`
|
|
567
|
+
|
|
568
|
+
The asset record: one line of the content index per addressable file. The record is emitted by the package holding the bytes, so its `path` is that package's path and each consumer joins its own root onto it — Foundry its data directory, the website its CDN prefix, the book its asset base.
|
|
569
|
+
|
|
570
|
+
| Export | Signature | Returns | Use it when |
|
|
571
|
+
| --------------------------- | --------------------------------------------------------------- | ----------------------------- | --------------------------------------------------------------------------------- |
|
|
572
|
+
| `ASSET_RECORD_FIELDS` | `const ASSET_RECORD_FIELDS` | `readonly AssetRecordField[]` | reading what an `asset` block carries, and where each field comes from |
|
|
573
|
+
| `PROVENANCE_KEYS` | `const PROVENANCE_KEYS` | `ReadonlySet<string>` | validating a provenance file, whose keys are derived from the record's own fields |
|
|
574
|
+
| `PROVENANCE_FILE` | `const PROVENANCE_FILE` | `string` — `provenance.yaml` | naming the file a directory records provenance for its subtree in |
|
|
575
|
+
| `PROVENANCE_SIDECAR_SUFFIX` | `const PROVENANCE_SIDECAR_SUFFIX` | `string` — `.yaml` | naming a per-file record, which replaces an inherited one wholesale |
|
|
576
|
+
| `collectAssetRecords` | `collectAssetRecords(assetsBase, { contentPackage, problems })` | `Array<Record<string, any>>` | reading a package's asset roots into index records, without walking its notes |
|
|
577
|
+
|
|
578
|
+
### `engine.packages`
|
|
579
|
+
|
|
580
|
+
The `<package>` segment's own registry: the names no repository may claim. A content package names itself, and exactly one name is held back — `packagebuild`, which addresses the files the toolchain ships itself.
|
|
581
|
+
|
|
582
|
+
| Export | Signature | Returns | Use it when |
|
|
583
|
+
| ---------------------- | ---------------------------- | ------------------------------------- | ------------------------------------------------------------------------------ |
|
|
584
|
+
| `PACKAGEBUILD_PACKAGE` | `const PACKAGEBUILD_PACKAGE` | `string` — `packagebuild` | naming the address namespace package-build's own assets publish under |
|
|
585
|
+
| `metadataFileName` | `metadataFileName(pkg)` | `string` — `<package>-metadata.jsonl` | naming a package's content index, wherever it is written, published or fetched |
|
|
586
|
+
| `RESERVED_PACKAGES` | `const RESERVED_PACKAGES` | `ReadonlySet<string>` | listing the names a configuration is refused for claiming |
|
|
587
|
+
| `isReservedPackage` | `isReservedPackage(pkg)` | `boolean` | checking one candidate `contentPackage` against the reservation |
|
|
588
|
+
|
|
589
|
+
### `engine.packagebuildIndex`
|
|
590
|
+
|
|
591
|
+
The toolchain's own content index — the files it ships, addressed. Every other package's index is fetched; this one is read from disk, because package-build is an npm dependency of every consumer rather than a Foundry package. The records join `foreign.index` like any other package's, so every lookup stays one path, and the special case is entirely about acquisition.
|
|
592
|
+
|
|
593
|
+
| Export | Signature | Returns | Use it when |
|
|
594
|
+
| ------------------------- | --------------------------------------------- | ----------------------------------------------- | ------------------------------------------------------------------------- |
|
|
595
|
+
| `PACKAGEBUILD_ROOT` | `const PACKAGEBUILD_ROOT` | `string` | naming this package's own root, wherever it is installed |
|
|
596
|
+
| `PACKAGEBUILD_ASSETS` | `const PACKAGEBUILD_ASSETS` | `string` | naming the asset directory it ships |
|
|
597
|
+
| `PACKAGEBUILD_INDEX_FILE` | `const PACKAGEBUILD_INDEX_FILE` | `string` | naming where the published index sits, beside the files it describes |
|
|
598
|
+
| `packageBuildRecords` | `packageBuildRecords(assetsBase)` | `Array<Record<string, any>>` | reading the records for the files it ships, walked rather than read back |
|
|
599
|
+
| `emitPackageBuildIndex` | `emitPackageBuildIndex({ assetsBase, file })` | `{file: string, assets: number, bytes: number}` | publishing that walk as a file, for the readers that are not this process |
|
|
545
600
|
|
|
546
601
|
### `engine.siteBuild`
|
|
547
602
|
|
|
@@ -625,6 +680,20 @@ Raw HTML in a note's prose, reported. **A note is markdown.** What markdown cann
|
|
|
625
680
|
| `checkHtml` | `function checkHtml(body, file,` | {Array<{file: string, line: number, column: number, severity: "warning", message: string}>} One finding per tag, in source order. | Every raw HTML tag in one note's body. |
|
|
626
681
|
| `lintContentHtml` | `function lintContentHtml(contentBase,` | {{findings: Array<{file: string, line: number, column: number, severity: "warning", message: string}>, files: number}} The findings, and how many files were read. | Walk a content tree and report raw HTML in every note's prose. |
|
|
627
682
|
|
|
683
|
+
### `engine.contentEmbeds`
|
|
684
|
+
|
|
685
|
+
`![[address|label]]` — the wikilink that renders a picture where it stands. An embed is the wikilink the syntax module defines, with `!` meaning _render it here_, and the syntax supplying the default type `image`. It resolves to an ordinary markdown image before any surface renders it, so the figure, the staging list and the directive all come from the image machinery that already exists. An embed reaches asset types only.
|
|
686
|
+
|
|
687
|
+
| Export | Signature | Returns | Use it when |
|
|
688
|
+
| -------------------- | ---------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------- |
|
|
689
|
+
| `EMBED_DEFAULT_TYPE` | `const EMBED_DEFAULT_TYPE` | — | The type a bare shortcode takes, supplied by the syntax itself. |
|
|
690
|
+
| `EMBED_PATTERN` | `const EMBED_PATTERN` | — | An embed, as authored, with the directive it may carry. |
|
|
691
|
+
| `embedsIn` | `function embedsIn(body)` | {ParsedEmbed[]} One entry per embed. | Every embed in one body, in source order. |
|
|
692
|
+
| `resolveEmbed` | `function resolveEmbed(index, embed)` | {{pathname: string}\|{reason: string, target: string, type?: string}} The authored pathname, or the finding. | What one embed resolves to, or why it does not. |
|
|
693
|
+
| `embedProblems` | `function embedProblems(embed)` | {Array<{link: string, offset: number, message: string}>} One entry per defect. | What is wrong with one embed's directive or placement. |
|
|
694
|
+
| `resolveEmbeds` | `function resolveEmbeds(body, ctx)` | {{markdown: string, unresolved: Array<object>, problems: Array<object>}} The body, the embeds that named nothing, and the directives that could not be honoured. | Rewrite every embed into the image each surface renders. |
|
|
695
|
+
| `checkEmbeds` | `function checkEmbeds(body, file, opts)` | {Array<{file: string, line: number, column: number\|undefined, severity: "error", message: string}>} One finding per defect, in source order. | Every defect in one note's embeds, located. |
|
|
696
|
+
|
|
628
697
|
### `engine.contentImages`
|
|
629
698
|
|
|
630
699
|
An image saying how wide it is and where it sits. A markdown image carries no indication of either, so each of the three surfaces decides for itself and the author — who is the one who knows — has no way to say. A directive in the curly-attribute convention Pandoc and Kramdown use closes that, in two closed vocabularies: a width class, and a `float:` position.
|
|
@@ -652,13 +721,15 @@ An image saying how wide it is and where it sits. A markdown image carries no in
|
|
|
652
721
|
|
|
653
722
|
One authored pathname, and the four addresses it resolves to. A note names a file once — in `img:`, in `data.portrait:`, in the body of a markdown image — and the first segment says which package owns it when an `assets/` follows. Every surface derives its own address from that one statement: the path inside a Foundry install, the file in the owning repository's tree, the address the website serves, and where the book stages its copy.
|
|
654
723
|
|
|
655
|
-
| Export
|
|
656
|
-
|
|
|
657
|
-
| `ASSETS_SEGMENT`
|
|
658
|
-
| `PATHNAME_SURFACES`
|
|
659
|
-
| `pathnameProblem`
|
|
660
|
-
| `packageAddresses`
|
|
661
|
-
| `resolvePathname`
|
|
724
|
+
| Export | Signature | Returns | Use it when |
|
|
725
|
+
| ----------------------- | --------------------------------------------- | -------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------- |
|
|
726
|
+
| `ASSETS_SEGMENT` | `const ASSETS_SEGMENT` | — | The directory a package ships its files in. |
|
|
727
|
+
| `PATHNAME_SURFACES` | `const PATHNAME_SURFACES` | — | The surfaces one authored pathname resolves for. |
|
|
728
|
+
| `pathnameProblem` | `function pathnameProblem(raw)` | {string} The problem, as a finding's sentence, or `""`. | What is wrong with an authored pathname, or `""` when nothing is. |
|
|
729
|
+
| `packageAddresses` | `function packageAddresses(config)` | {Map<string, {root: string\|null, id: string\|null, own: boolean}>} The packages, by package name. | Every content package this build can resolve a pathname against. |
|
|
730
|
+
| `resolvePathname` | `function resolvePathname(raw, config)` | {PathnameForms\|null} The four forms, or `null` when the note names no file. | Resolve one authored pathname into the address each surface serves. |
|
|
731
|
+
| `servesFoundry` | `function servesFoundry(config)` | {boolean} Whether the package being built has a Foundry root. | Whether this build installs anything into a Foundry data directory. |
|
|
732
|
+
| `foundryAddressProblem` | `function foundryAddressProblem(raw, config)` | {string} The problem, as a finding's sentence, or `""`. | Why a pathname has no Foundry address, or `""` when it has one. |
|
|
662
733
|
|
|
663
734
|
### `engine.contentLinks`
|
|
664
735
|
|
|
@@ -845,7 +916,10 @@ The content tree, built into a book. The I/O half of the PDF surface: it reads t
|
|
|
845
916
|
| `buildPdf` | `async buildPdf({ config, out, version, compile })` | {Promise<object>} `{ built, reason, findings, typ, pdf, stats }`. | Build the book. |
|
|
846
917
|
| `stagedImagePath` | `function stagedImagePath(src, config)` | {{from: string, to: string}\|null} The file, and where under the output directory it is staged. | The file on disk an authored image address names, or `null`. |
|
|
847
918
|
| `stageBanners` | `function stageBanners(entries, config, outDir, findings)` | {Map<string, string>} Declared path → the staged file's path, relative to the `.typ`. | Copy every banner the document tree names into the output directory. |
|
|
848
|
-
| `compileTypst` | `function compileTypst(typPath, pdfPath, pdf` | {{ok: boolean, message: string}} What happened.
|
|
919
|
+
| `compileTypst` | `function compileTypst(typPath, pdfPath, pdf` | {{ok: boolean, message: string, findings: object[]}} What happened. | Run Typst over the emitted source. |
|
|
920
|
+
| `typstArgs` | `function typstArgs(typPath, pdfPath, pdf)` | {string[]} The arguments, in order. | The command line the compile runs, as data. |
|
|
921
|
+
| `typstWarnings` | `function typstWarnings(output)` | {object[]} One finding per warning the compiler wrote. | The compiler's own warnings, as findings. |
|
|
922
|
+
| `BOOK_FONTS_PATH` | `const BOOK_FONTS_PATH` | {string} The directory holding them. | The faces the book is set in, shipped with this package. |
|
|
849
923
|
|
|
850
924
|
### `engine.baseCompiler`
|
|
851
925
|
|
package/docs/configuration.md
CHANGED
|
@@ -439,6 +439,7 @@ HeroicLands layout, resolved against `rootDir`:
|
|
|
439
439
|
| Key | Default | What it is |
|
|
440
440
|
| --------------------- | ---------------------- | ------------------------------------------------------------------------------------------ |
|
|
441
441
|
| `paths.content` | `assets/content` | The content tree root. |
|
|
442
|
+
| `paths.assets` | `assets` | The asset roots' parent, holding `icons/`, `images/` and `audio/`. |
|
|
442
443
|
| `paths.contentIndex` | `build/content-index` | Where `content-index` writes this package's note index. Derived and disposable. |
|
|
443
444
|
| `paths.packJson` | `build/packs-json` | Build-only per-entry JSON intermediate. |
|
|
444
445
|
| `paths.stage` | `build/stage/packs` | Compiled LevelDB packs. |
|
|
@@ -457,7 +458,7 @@ An empty value is refused generically:
|
|
|
457
458
|
|
|
458
459
|
Any other key is refused:
|
|
459
460
|
|
|
460
|
-
> ``package-build config: `paths.<key>` is not a recognized option (expected one of: content, contentIndex, packJson, stage, unpack, foreignCache, metadataCache).``
|
|
461
|
+
> ``package-build config: `paths.<key>` is not a recognized option (expected one of: content, assets, contentIndex, packJson, stage, unpack, foreignCache, metadataCache).``
|
|
461
462
|
|
|
462
463
|
### `skipDirectories`
|
|
463
464
|
|
|
@@ -850,12 +851,20 @@ reader downloaded, and a title with no document has nothing to print:
|
|
|
850
851
|
`pdf.fonts` names font **families**, not files — the renderer asks the font
|
|
851
852
|
stack for a family by name:
|
|
852
853
|
|
|
853
|
-
| Key (under `pdf.fonts`) | Type | Required | Default
|
|
854
|
-
| ----------------------- | ------------- | -------- |
|
|
855
|
-
| `pdf.fonts.serif` | string | no | `""`
|
|
856
|
-
| `pdf.fonts.sans` | string | no | `""`
|
|
857
|
-
| `pdf.fonts.mono` | string | no | `""`
|
|
858
|
-
| `pdf.fonts.path` | string (path) | no | `""` —
|
|
854
|
+
| Key (under `pdf.fonts`) | Type | Required | Default |
|
|
855
|
+
| ----------------------- | ------------- | -------- | --------------------------------------------------------- |
|
|
856
|
+
| `pdf.fonts.serif` | string | no | `""` — the book is set in `Libertinus Serif` |
|
|
857
|
+
| `pdf.fonts.sans` | string | no | `""` — its headings in `Libertinus Sans` |
|
|
858
|
+
| `pdf.fonts.mono` | string | no | `""` — its raw and code spans in `DejaVu Sans Mono` |
|
|
859
|
+
| `pdf.fonts.path` | string (path) | no | `""` — faces of your own, searched as well as the shipped |
|
|
860
|
+
|
|
861
|
+
Each role falls back to a face that resolves on a machine carrying none of them:
|
|
862
|
+
the toolchain ships the sans and the compiler embeds the other two. The compile
|
|
863
|
+
searches the shipped directory, and `pdf.fonts.path` when one is given, and
|
|
864
|
+
nothing the machine has installed — so a face named here has to come from one of
|
|
865
|
+
those two places, and a name nothing resolves is reported rather than set in the
|
|
866
|
+
fallback. Shipped alongside the sans, and available to a package that names it,
|
|
867
|
+
is `Libertinus Mono`.
|
|
859
868
|
|
|
860
869
|
> ``package-build config: `pdf.fonts` must be a mapping.``
|
|
861
870
|
|