@heroiclands/package-build 25.6.3 → 25.7.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 +17 -0
- package/README.md +19 -1
- package/bin/content-commands.mjs +14 -70
- package/bin/package-build.mjs +25 -10
- package/ci/wip-workflows.test.mjs +252 -0
- package/content-config.mjs +40 -12
- package/docs/api.md +237 -48
- package/docs/commands.md +73 -36
- package/docs/configuration.md +92 -48
- package/docs/guides/release-and-deploy.md +26 -14
- package/docs/guides/site.md +29 -6
- package/engine/cdn-publish.mjs +164 -6
- package/engine/content-index.mjs +71 -19
- package/engine/content-spans.mjs +18 -1
- package/engine/content-styles.mjs +85 -0
- package/engine/derived-sections.mjs +4 -3
- package/engine/event-views.mjs +6 -4
- package/engine/helpers.mjs +33 -9
- package/engine/index.mjs +1 -0
- package/engine/note-cache.mjs +1107 -0
- package/engine/note-page.mjs +424 -0
- package/engine/pack-config.mjs +47 -4
- package/engine/pathnames.mjs +22 -7
- package/engine/prepared-only.mjs +32 -0
- package/engine/preview-index.mjs +55 -13
- package/engine/site-build.mjs +34 -448
- package/engine/site-config.mjs +33 -4
- package/engine/site-maps.mjs +16 -24
- package/engine/site-origins.mjs +23 -0
- package/engine/site-page.mjs +648 -0
- package/engine/site-render.mjs +142 -10
- package/engine/site-serve.mjs +1 -1
- package/engine/sql-tables.mjs +12 -3
- package/package.json +6 -1
- package/site-theme/static/css/style.css +14 -0
- package/types/content-config.d.mts +24 -4
- package/types/engine/cdn-publish.d.mts +22 -0
- package/types/engine/content-index.d.mts +36 -2
- package/types/engine/content-styles.d.mts +20 -0
- package/types/engine/derived-sections.d.mts +3 -3
- package/types/engine/event-views.d.mts +4 -3
- package/types/engine/helpers.d.mts +18 -1
- package/types/engine/index.d.mts +1 -0
- package/types/engine/note-cache.d.mts +149 -0
- package/types/engine/note-page.d.mts +92 -0
- package/types/engine/pack-config.d.mts +30 -0
- package/types/engine/prepared-only.d.mts +14 -0
- package/types/engine/preview-index.d.mts +35 -0
- package/types/engine/site-build.d.mts +5 -100
- package/types/engine/site-config.d.mts +3 -2
- package/types/engine/site-maps.d.mts +7 -11
- package/types/engine/site-origins.d.mts +9 -0
- package/types/engine/site-page.d.mts +261 -0
- package/types/engine/site-render.d.mts +20 -1
- package/types/engine/sql-tables.d.mts +8 -4
package/docs/api.md
CHANGED
|
@@ -193,16 +193,18 @@ Whether a content tree has anything to compile. The pack build's worst failure i
|
|
|
193
193
|
|
|
194
194
|
The consuming repository's resolved `package-build.config.yaml`: the content package, the Foundry package and its kind, every path, the `_stats` identity, item-type membership and the pack list — read on call rather than hoisted at import, so importing this package never requires a configuration to exist.
|
|
195
195
|
|
|
196
|
-
| Export | Signature | Returns | Use it when
|
|
197
|
-
| ------------------- | ------------------------------------ | ----------------------------- |
|
|
198
|
-
| `CONFIG_BASENAME` | `const CONFIG_BASENAME` | — | naming the stem every consuming repository declares its build under
|
|
199
|
-
| `CONFIG_FILENAMES` | `const CONFIG_FILENAMES` | — | listing the file names a configuration may be written as
|
|
200
|
-
| `findConfigFile` | `findConfigFile(from)` | `string \| undefined` | finding the nearest configuration file at or above a directory
|
|
201
|
-
| `resolveConfigFile` | `resolveConfigFile(...)` | `{path, fromCwd, fromModule}` | asking which configuration file a build launched here would read, without loading it
|
|
202
|
-
| `locateConfigError` | `locateConfigError(err, configPath)` | the decorated error | attaching the position of the key a configuration error names
|
|
203
|
-
| `configFromData` | `configFromData(data, configPath)` | `ContentBuildConfig` | turning a parsed YAML configuration into the frozen one the engine reads
|
|
204
|
-
| `loadPackConfig` | `loadPackConfig()` | `ContentBuildConfig` | reading the consuming repository's resolved, frozen configuration
|
|
205
|
-
| `packConfigPath` | `packConfigPath()` | `string` | naming the file `loadPackConfig` resolved the configuration from, for a diagnostic
|
|
196
|
+
| Export | Signature | Returns | Use it when |
|
|
197
|
+
| ------------------- | ------------------------------------ | ----------------------------- | ------------------------------------------------------------------------------------------------ |
|
|
198
|
+
| `CONFIG_BASENAME` | `const CONFIG_BASENAME` | — | naming the stem every consuming repository declares its build under |
|
|
199
|
+
| `CONFIG_FILENAMES` | `const CONFIG_FILENAMES` | — | listing the file names a configuration may be written as |
|
|
200
|
+
| `findConfigFile` | `findConfigFile(from)` | `string \| undefined` | finding the nearest configuration file at or above a directory |
|
|
201
|
+
| `resolveConfigFile` | `resolveConfigFile(...)` | `{path, fromCwd, fromModule}` | asking which configuration file a build launched here would read, without loading it |
|
|
202
|
+
| `locateConfigError` | `locateConfigError(err, configPath)` | the decorated error | attaching the position of the key a configuration error names |
|
|
203
|
+
| `configFromData` | `configFromData(data, configPath)` | `ContentBuildConfig` | turning a parsed YAML configuration into the frozen one the engine reads |
|
|
204
|
+
| `loadPackConfig` | `loadPackConfig()` | `ContentBuildConfig` | reading the consuming repository's resolved, frozen configuration |
|
|
205
|
+
| `packConfigPath` | `packConfigPath()` | `string` | naming the file `loadPackConfig` resolved the configuration from, for a diagnostic |
|
|
206
|
+
| `readPackConfig` | `readPackConfig(configPath)` | `ContentBuildConfig` | reading the configuration one named file declares, unmemoised |
|
|
207
|
+
| `withPackConfig` | `withPackConfig(config, fn, file?)` | what `fn` returns | running work, asynchronous included, with `config` as the configuration `loadPackConfig` answers |
|
|
206
208
|
|
|
207
209
|
### `engine.packRouter`
|
|
208
210
|
|
|
@@ -621,12 +623,11 @@ The npm-installed Graphviz runtime draws SVG maps. The `.dot` source stays besid
|
|
|
621
623
|
|
|
622
624
|
The map from each place, drawn for the site: `package-build site build` draws the map from every place of this package given a **From here** section — with the site's base, so every name links through it — and sets each drawing inline in that section, where its place names stay links. `site.maps: false` gives no place the section, and nothing is drawn.
|
|
623
625
|
|
|
624
|
-
| Export
|
|
625
|
-
|
|
|
626
|
-
| `SITE_MAP_DIR`
|
|
627
|
-
| `inlineSvg`
|
|
628
|
-
| `drawSiteMaps`
|
|
629
|
-
| `inlineDrawingFigure` | `inlineDrawingFigure(svg, alt)` | `string` | the one HTML block the website sets an inline drawing in, with no blank line to end it early |
|
|
626
|
+
| Export | Signature | Returns | Use it when |
|
|
627
|
+
| -------------- | ----------------------------------------------------------------------- | ------------------ | --------------------------------------------------------------------------------------------------------------- |
|
|
628
|
+
| `SITE_MAP_DIR` | `const SITE_MAP_DIR` | `string` | where the site build draws below a package's root, `build/map/site` |
|
|
629
|
+
| `inlineSvg` | `inlineSvg(svg)` | `string` | rewriting a GraphViz SVG for inlining — no prologue, no comment, no `xlink:`, the root sized by `viewBox` alone |
|
|
630
|
+
| `drawSiteMaps` | `drawSiteMaps({ records, foreignIndex, config, base, only?, outDir? })` | `{maps, findings}` | drawing the map from every place given a **From here** section, keyed by the file name the section names |
|
|
630
631
|
|
|
631
632
|
### `engine.systems`
|
|
632
633
|
|
|
@@ -1277,6 +1278,36 @@ The deployed layout of a package's pictures, and which of them ship. A Foundry p
|
|
|
1277
1278
|
| `stagePackageAssets` | `stagePackageAssets({ assetsBase, contentPackage, stageDir, title, transform, shippingLicenses })` | `{staged, excluded, findings, manifest}` | staging a package's shipped pictures flat and writing `asset-sets/default.json`; nothing is staged when `findings` is non-empty |
|
|
1278
1279
|
| `rowsStagingPictureRoots` | `rowsStagingPictureRoots(rows, { rootDir, assetsBase })` | `{index, from, root}[]` | finding `packageBuild.assets` rows that would copy a picture root around the filter |
|
|
1279
1280
|
|
|
1281
|
+
### CDN publication transports
|
|
1282
|
+
|
|
1283
|
+
Import `@heroiclands/package-build/engine/cdn-publish` directly. `planCdnPublish`
|
|
1284
|
+
collects the same asset records and shipping selection as staging, applies the
|
|
1285
|
+
provided transform, and builds destination-relative keys plus a named set whose
|
|
1286
|
+
sources use its `host` argument. It does not upload anything.
|
|
1287
|
+
|
|
1288
|
+
`publishToCdn(plan, { store, dryRun, log })` accepts a destination-independent
|
|
1289
|
+
store with async `size(key)`, `read(key)`, optional `write(key, body, contentType)`
|
|
1290
|
+
and a safe `describe` string. It validates every planned relative key before
|
|
1291
|
+
reading or writing, skips pictures by size, compares set bytes, and withholds the
|
|
1292
|
+
set after a picture failure. `dryRun` never calls `write`.
|
|
1293
|
+
|
|
1294
|
+
`internalStore({ destination, fetch, transfer })` combines public HTTPS reads at
|
|
1295
|
+
`INTERNAL_CDN_HOST` with rsync writes, defaulting to `INTERNAL_CDN_DESTINATION`
|
|
1296
|
+
(`atlas:/srv/preview/cdn`). An explicit SSH alias may target `alias:cdn` for the
|
|
1297
|
+
forced-rrsync receiver. Writes stage only the supplied bytes in temporary regular
|
|
1298
|
+
files, invoke rsync without deletion, and clean up after success or failure.
|
|
1299
|
+
`fetch` and `transfer(args)` can be supplied for isolated tests; the default
|
|
1300
|
+
transfer executes rsync with batch SSH and strict host-key checking.
|
|
1301
|
+
|
|
1302
|
+
`s3Store(credentials)` retains signed S3/R2 requests. `publicStore(host, fetch)`
|
|
1303
|
+
provides read-only HTTP comparisons. `CANONICAL_CDN_ORIGIN`, exported from
|
|
1304
|
+
`engine/site-config`, names the owned public CDN origin used by pathname
|
|
1305
|
+
projection. Its single definition lives in the leaf `engine/site-origins` module
|
|
1306
|
+
to avoid a cycle through site configuration and content rendering.
|
|
1307
|
+
The CLI owns environment loading and
|
|
1308
|
+
`--dest` selection; site builds never call a publisher. See
|
|
1309
|
+
[CDN publication](commands.md#package-build-cdn-publish) for operator access.
|
|
1310
|
+
|
|
1280
1311
|
### `engine.assetReferences`
|
|
1281
1312
|
|
|
1282
1313
|
Every asset reference in a compiled document states the asset's address: an `<img>` carries `data-asset`, and a path field's address sits in `flags.<system>.assets` on the document holding it, nested to mirror the field's path. A reference to a picture its package does not ship compiles to that package's stand-in and keeps the excluded address.
|
|
@@ -1357,6 +1388,16 @@ configuration and incremental render state stay in the calling thread. Every
|
|
|
1357
1388
|
update is prepared in a separate output tree, then installed between HTTP
|
|
1358
1389
|
requests so readers receive complete files.
|
|
1359
1390
|
|
|
1391
|
+
`buildPackageSite` invokes configured `site.inputs` before content work. The
|
|
1392
|
+
module's default export has the `SiteInputsProvider` contract:
|
|
1393
|
+
`({config}) => {navigation, files}` or a promise of that object. It receives the
|
|
1394
|
+
immutable effective configuration; `navigation` uses `NavigationEntry[]`, and
|
|
1395
|
+
`files` is `Record<string, string>` of relative output paths to UTF-8 text.
|
|
1396
|
+
Provider files are validated against unsafe paths, symlinks and builder-owned
|
|
1397
|
+
output before output replacement, written after the renderer's clearing step,
|
|
1398
|
+
and recorded in `written.files`. Without a provider, cached navigation remains
|
|
1399
|
+
required. See [local site inputs](configuration.md#local-site-inputs).
|
|
1400
|
+
|
|
1360
1401
|
### `engine.contentRender`
|
|
1361
1402
|
|
|
1362
1403
|
`createRenderer(target, options?)` builds the one markdown-it configuration every target renders through. `target` is `"foundry"`, `"web"`, `"preview"` or `"book"`. The renderer holds the configured parser (`md`), the names of the plugins installed (`plugins`), and three functions: `prepare` writes the constructs markdown-it has no syntax for — marked text, captioned items, named blocks, verse and alerts — as HTML between blank lines; `render` prepares and renders to HTML; `renderInline` renders one inline run. The `book` target parses only, and its Typst stage reads the tokens.
|
|
@@ -1407,9 +1448,17 @@ locate it in the complete source. LF and CRLF retain their authored lengths.
|
|
|
1407
1448
|
|
|
1408
1449
|
`previewIndex({ config, records, foreign? })` builds what [`renderPreview`](#enginesitepreview) takes as `index`: the link index, the asset address space, the languages' maps, the icon registry and an `expand` function that resolves a buffer's links, embeds, expressions and figure numbers as the website does. `records` are the content index's records and `foreign` the dependencies' indexes. Build it once per index and reuse it for every render.
|
|
1409
1450
|
|
|
1410
|
-
| Export
|
|
1411
|
-
|
|
|
1412
|
-
| `previewIndex`
|
|
1451
|
+
| Export | Signature | Returns | Use it when |
|
|
1452
|
+
| ------------------- | ------------------------------------------------- | -------------------------------------- | ------------------------------------------------------------------------- |
|
|
1453
|
+
| `previewIndex` | `previewIndex({ config, records, foreign })` | `{ runs, icons, lookupAsset, expand }` | An editor holds a content index and wants the preview to render with it. |
|
|
1454
|
+
| `recordSiteContext` | `recordSiteContext({ config, records, foreign })` | `{ site, assets, events, runs }` | Resolving a page against content-index records instead of a content tree. |
|
|
1455
|
+
|
|
1456
|
+
`recordSiteContext` places every note that publishes a page at the URL the
|
|
1457
|
+
site gives it: the homepage at the package root, a note `site.sections` names
|
|
1458
|
+
at its section, and every other note at its address. `anchorsOf(record)` may
|
|
1459
|
+
answer a note's anchors as its page emits them, generated sections included;
|
|
1460
|
+
answering nothing keeps the record's own. `pages` maps each page's URL to its
|
|
1461
|
+
record.
|
|
1413
1462
|
|
|
1414
1463
|
### `engine.sitePreview`
|
|
1415
1464
|
|
|
@@ -1589,16 +1638,16 @@ A list item opened with `[ ]` or `[x]`, reported. The format has no checkbox: a
|
|
|
1589
1638
|
|
|
1590
1639
|
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.
|
|
1591
1640
|
|
|
1592
|
-
| Export | Signature | Returns | Use it when
|
|
1593
|
-
| ----------------------- | --------------------------------------------- | -------------------------------------------------------------------------------------------------- |
|
|
1594
|
-
| `ASSETS_SEGMENT` | `const ASSETS_SEGMENT` | — | The directory a package ships its files in.
|
|
1595
|
-
| `PATHNAME_SURFACES` | `const PATHNAME_SURFACES` | — | The surfaces one authored pathname resolves for.
|
|
1596
|
-
| `pathnameProblem` | `function pathnameProblem(raw)` | {string} The problem, as a finding's sentence, or `""`. | What is wrong with an authored pathname, or `""` when nothing is.
|
|
1597
|
-
| `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.
|
|
1598
|
-
| `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.
|
|
1599
|
-
| `assetPathnameKey` | `function assetPathnameKey(src, config)` | {string\|null} The pathname, or `null` for an address naming no asset record to look up. | The pathname one authored image address keys an asset's role and pixel size under, whichever form it was written in.
|
|
1600
|
-
| `servesFoundry` | `function servesFoundry(config)` | {boolean} Whether the package being built has a Foundry root. | Whether this build installs anything into a Foundry data directory.
|
|
1601
|
-
| `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.
|
|
1641
|
+
| Export | Signature | Returns | Use it when |
|
|
1642
|
+
| ----------------------- | --------------------------------------------- | -------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
1643
|
+
| `ASSETS_SEGMENT` | `const ASSETS_SEGMENT` | — | The directory a package ships its files in. |
|
|
1644
|
+
| `PATHNAME_SURFACES` | `const PATHNAME_SURFACES` | — | The surfaces one authored pathname resolves for. |
|
|
1645
|
+
| `pathnameProblem` | `function pathnameProblem(raw)` | {string} The problem, as a finding's sentence, or `""`. | What is wrong with an authored pathname, or `""` when nothing is. |
|
|
1646
|
+
| `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. |
|
|
1647
|
+
| `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; canonical Heroic Lands CDN assets use effective site.assets in the web form only. |
|
|
1648
|
+
| `assetPathnameKey` | `function assetPathnameKey(src, config)` | {string\|null} The pathname, or `null` for an address naming no asset record to look up. | The pathname one authored image address keys an asset's role and pixel size under, whichever form it was written in. |
|
|
1649
|
+
| `servesFoundry` | `function servesFoundry(config)` | {boolean} Whether the package being built has a Foundry root. | Whether this build installs anything into a Foundry data directory. |
|
|
1650
|
+
| `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. |
|
|
1602
1651
|
|
|
1603
1652
|
### `engine.contentLinks`
|
|
1604
1653
|
|
|
@@ -1654,7 +1703,8 @@ Shared helpers for the pack compilers in `packages/package-build/`. The HeroicLa
|
|
|
1654
1703
|
| `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. |
|
|
1655
1704
|
| `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. |
|
|
1656
1705
|
| `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. |
|
|
1657
|
-
| `walkMarkdownTree` | `walkMarkdownTree(rootDir, { skipDirectories, sources, renderDescription })`
|
|
1706
|
+
| `walkMarkdownTree` | `walkMarkdownTree(rootDir, { skipDirectories, sources, renderDescription, parse })` | 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 |
|
|
1707
|
+
| `walkMarkdownFiles` | `walkMarkdownFiles(rootDir, { skipDirectories })` | generator yielding each `.md` file's absolute path | listing the files `walkMarkdownTree` reads, in its order, without reading them |
|
|
1658
1708
|
|
|
1659
1709
|
`buildContentLinkIndex` accepts `{ config?, records, sqlTables?, foreign? }`.
|
|
1660
1710
|
`records` is the required, supplied content corpus. The optional prepared
|
|
@@ -1665,9 +1715,12 @@ so glossary word links resolve to their dictionary entries.
|
|
|
1665
1715
|
`renderDescription` option defaults to true; false leaves `description` empty
|
|
1666
1716
|
while retaining authored body and positions. `walkMarkdownTree` accepts a
|
|
1667
1717
|
selected `sources` map in walk order and the same rendering option, also true
|
|
1668
|
-
by default
|
|
1669
|
-
|
|
1670
|
-
|
|
1718
|
+
by default, and a `parse` function called as `parseMarkdownFile` is, for a
|
|
1719
|
+
caller answering from earlier parses. Index collection explicitly omits unused
|
|
1720
|
+
rendered descriptions. `indexRecordsFor` accepts that selected map, a `syntax`
|
|
1721
|
+
projection function, a captured `foreignIndex` and a `cache` of earlier
|
|
1722
|
+
parses and per-note records, the one [`./note-page`](#note-page) keeps. These
|
|
1723
|
+
adapters do not retain state globally.
|
|
1671
1724
|
|
|
1672
1725
|
The physical wrappers `lintContentCharset`, `lintContentHtml`,
|
|
1673
1726
|
`lintContentIcons`, `lintContentImages` and `lintContentTaskLists` accept an
|
|
@@ -2152,6 +2205,138 @@ Where HM3 records the template priority — one statement (`data.templatePriorit
|
|
|
2152
2205
|
| `HM3_DEFAULT_ITEM_ART` | `const HM3_DEFAULT_ITEM_ART` | — | reading the full map of default item artwork keyed by **note** type (not document subtype) for HM3 items |
|
|
2153
2206
|
| `hm3DefaultItemArt` | `hm3DefaultItemArt(type)` | `string` — the default image path | getting the default art path for an HM3 item's note type, throwing when the type is unknown |
|
|
2154
2207
|
|
|
2208
|
+
### `engine.sitePage`
|
|
2209
|
+
|
|
2210
|
+
One content page, from its note to its body HTML. The site build renders every
|
|
2211
|
+
page through these functions, and [`./note-page`](#note-page) renders a single
|
|
2212
|
+
note through the same ones. Nothing here reads a file.
|
|
2213
|
+
|
|
2214
|
+
| Export | Signature | Returns | Use it when |
|
|
2215
|
+
| --------------------------- | ---------------------------------------- | -------------------------------------------------- | -------------------------------------------------------------------------------------------- |
|
|
2216
|
+
| `parseNote` | `parseNote(source, file, ctx)` | `{fm, body, bodyLine} \| null` | reading a note's frontmatter and body from its text, addresses decoded |
|
|
2217
|
+
| `contentPage` | `contentPage(note, ctx)` | `{page?, fmLinks, addressFinding?} \| null` | deciding whether a note publishes a page, and building it |
|
|
2218
|
+
| `inlineDrawingFigure` | `inlineDrawingFigure(svg, alt)` | `string` | the one HTML block the website sets an inline drawing in, with no blank line to end it early |
|
|
2219
|
+
| `pageFrontmatter` | `pageFrontmatter(page, options)` | `object` | the frontmatter a page publishes with |
|
|
2220
|
+
| `pageTitle` | `pageTitle(page)` | `string` | the title a page publishes under |
|
|
2221
|
+
| `resolveArtFields` | `resolveArtFields(data, webSrc, artSrc)` | `void` | rewriting a page's artwork addresses into the URLs the website serves |
|
|
2222
|
+
| `isPlainObject` | `isPlainObject(value)` | `boolean` | asking whether a value is a mapping a field may be read from |
|
|
2223
|
+
| `renderSitePage` | `renderSitePage(page, options)` | `{markdown, figures, data, resolved, …Errors}` | expanding one page with the passes the site writer uses |
|
|
2224
|
+
| `resolveSitePass` | `resolveSitePass(name, options)` | `{beforeLinks?: Function}` | resolving `site.pass` to its bundle |
|
|
2225
|
+
| `sitePageDecorator` | `sitePageDecorator(config, index)` | `Function` | adding the site's infobox and being metadata to a page's frontmatter |
|
|
2226
|
+
| `siteBodyRenderer` | `siteBodyRenderer(config)` | `(markdown, {runs, figures}) => string` | rendering an expanded page body to the HTML the site writer receives |
|
|
2227
|
+
| `pageDiagnostics` | `pageDiagnostics(result, rawOf)` | `Array<{file, line?, column?, severity, message}>` | turning a page render's findings into located diagnostics |
|
|
2228
|
+
| `frontmatterLinkDiagnostic` | `frontmatterLinkDiagnostic(finding)` | `{file, severity, message}` | reporting a wikilink authored in frontmatter |
|
|
2229
|
+
| `addressDiagnostic` | `addressDiagnostic(finding)` | `{file, severity, message}` | reporting a note whose page has no address |
|
|
2230
|
+
|
|
2231
|
+
`renderSitePage` takes `drawing`, a function from the file name a generated
|
|
2232
|
+
**From here** section names to that drawing's SVG text, and sets the drawing
|
|
2233
|
+
inline when it answers. The site build answers with the maps it drew.
|
|
2234
|
+
|
|
2235
|
+
## `./note-page`
|
|
2236
|
+
|
|
2237
|
+
One note, rendered to the body HTML its site page carries, for a preview that
|
|
2238
|
+
reads like the published page: links as their labels and unresolved ones
|
|
2239
|
+
marked, `.name` spans in bold, query fences evaluated into tables, inline verse
|
|
2240
|
+
broken onto its lines and the generated sections a page carries.
|
|
2241
|
+
|
|
2242
|
+
```js
|
|
2243
|
+
import { renderNoteFile } from "@heroiclands/package-build/note-page";
|
|
2244
|
+
|
|
2245
|
+
const { html, styles, findings, cache } = await renderNoteFile("assets/content/Lore/Tides.md", {
|
|
2246
|
+
root: "/path/to/repository",
|
|
2247
|
+
});
|
|
2248
|
+
```
|
|
2249
|
+
|
|
2250
|
+
| Export | Signature | Returns | Use it when |
|
|
2251
|
+
| ---------------- | --------------------------------------------------------- | -------------------------------------------- | ------------------------------------------------------------- |
|
|
2252
|
+
| `renderNoteFile` | `renderNoteFile(path, { root?, cache?, prepareTables? })` | `Promise<{ html, styles, findings, cache }>` | rendering a saved note of a repository as its site page shows |
|
|
2253
|
+
|
|
2254
|
+
`path` is the note, relative to `root` or absolute; `root` is the repository
|
|
2255
|
+
or any directory inside it, the working directory by default. The note is read
|
|
2256
|
+
from disk, so what is rendered is what is saved. `renderNoteFile` loads the
|
|
2257
|
+
repository's `package-build.config.yaml` and its dependencies' fetched indexes,
|
|
2258
|
+
derives the content tree's index records as every build does, answers the
|
|
2259
|
+
note's own queries and generated sections over them as `package-build site
|
|
2260
|
+
build` does, and renders the note through the site build's own page render.
|
|
2261
|
+
The repository's configuration answers every read of the configuration made
|
|
2262
|
+
during the call, wherever the caller stands. Nothing has to be built first: no
|
|
2263
|
+
content index and no site.
|
|
2264
|
+
|
|
2265
|
+
The page resolves against the notes it links as their own pages emit them.
|
|
2266
|
+
The note is rendered once to learn which of the package's pages it looks up,
|
|
2267
|
+
and those notes' query answers and generated sections are prepared beside its
|
|
2268
|
+
own, so a link to a generated section such as `#fromhere` resolves, and a
|
|
2269
|
+
cross-note `{{ref}}` finds the target's figures, numbered as its page numbers
|
|
2270
|
+
them. A place's **From here** drawing is drawn for that place alone, in a
|
|
2271
|
+
scratch directory under the system's temporary directory that is removed once
|
|
2272
|
+
the drawing is read, and set inline as the site sets it.
|
|
2273
|
+
|
|
2274
|
+
**The cache.** The records and the note's answers are kept in
|
|
2275
|
+
`build/cache/note-page/` under the repository root, and reused while every
|
|
2276
|
+
input they were derived from is unchanged. Nothing is reused otherwise: each
|
|
2277
|
+
stored answer is keyed by a hash of its inputs, which are
|
|
2278
|
+
|
|
2279
|
+
- package-build's version and the bytes of its own modules, the Node.js
|
|
2280
|
+
version and the DuckDB version;
|
|
2281
|
+
- the configuration file's bytes and the resolved configuration;
|
|
2282
|
+
- the bytes of every fetched dependency index, and the installed
|
|
2283
|
+
`@heroiclands/fonts` version and manifest;
|
|
2284
|
+
- every note's path and the hash of its text, and the package's asset records.
|
|
2285
|
+
|
|
2286
|
+
The dependencies' indexes and the configuration are read between two
|
|
2287
|
+
readings of those inputs, which are read again after the records are derived
|
|
2288
|
+
and around preparing any query answers the store lacks. When any reading
|
|
2289
|
+
differs, something changed while it was read: nothing is stored, and the whole
|
|
2290
|
+
render is done again from the start, the configuration resolved and the note's
|
|
2291
|
+
place in the tree checked afresh, so a retry answers as a fresh call would and
|
|
2292
|
+
a store always holds what was derived from the bytes its key names. After
|
|
2293
|
+
three such attempts the call throws. The cache holds two stores, each
|
|
2294
|
+
replaced whole by renaming a complete file over it. `notes.v8` keeps each note's parse, keyed by its text, and the
|
|
2295
|
+
records derived from that note alone, keyed also by what the tree gives every
|
|
2296
|
+
note: the setting date and calendars, the addresses the notes declare and the
|
|
2297
|
+
asset records. `tree.v8` keeps the whole tree's records, keyed by every input,
|
|
2298
|
+
and beside them the query answers of each note a render has needed. So a second render of an
|
|
2299
|
+
unchanged tree reads the records and the answers; a changed note re-derives
|
|
2300
|
+
its own records and re-runs the passes that read the whole tree; a changed
|
|
2301
|
+
address, calendar, asset, dependency index, configuration or toolchain
|
|
2302
|
+
re-derives every note. A store that cannot be read, that was written for
|
|
2303
|
+
other inputs, or that holds an entry of the wrong shape, down to each table
|
|
2304
|
+
result, inline answer, page-list result and parse inside it, is derived again
|
|
2305
|
+
and overwritten. Deleting the directory loses
|
|
2306
|
+
only time. `cache: false` derives everything and reads and writes nothing.
|
|
2307
|
+
`prepareTables` prepares the query answers, `prepareTreeSqlTables` from
|
|
2308
|
+
`engine/sql-tables.mjs` by default; a host may wrap it to time or observe the
|
|
2309
|
+
preparation.
|
|
2310
|
+
|
|
2311
|
+
`html` is the body before the site writer minifies it and before it unlinks
|
|
2312
|
+
links to pages its homepage cannot reach. Set it in
|
|
2313
|
+
`<div class="single-content"><div class="single-body">…</div></div>` with
|
|
2314
|
+
`styles`, the CSS the site theme applies to a page body, to show it as the page
|
|
2315
|
+
does. `styles` carries the theme's reset, body typography and ordinary link
|
|
2316
|
+
rules scoped to `.single-content`, so the body reads as the page does whatever
|
|
2317
|
+
the host's own styles, and sets the root font size on `:root`, since the
|
|
2318
|
+
theme measures its lengths in `rem`. Each finding is `{ file, line?, column?, severity, message }` and prints
|
|
2319
|
+
through `formatDiagnostic` as `file:line:column: severity: message`. `cache`
|
|
2320
|
+
says what the cache answered: `tree` is `"hit"` when the tree's records were
|
|
2321
|
+
reused whole, `"miss"` when they were derived, and `"off"` without the cache;
|
|
2322
|
+
`notes` counts the notes whose own records were derived again; `tables` is
|
|
2323
|
+
`"hit"` when the answers of the note and of every note it links were all
|
|
2324
|
+
reused, `"miss"` when any was prepared, and `"off"` without the cache.
|
|
2325
|
+
|
|
2326
|
+
It throws when there is no configuration at or above `root`; when the file is
|
|
2327
|
+
not a note of the content tree (outside `paths.content`, in a skipped
|
|
2328
|
+
directory, or not Markdown); when a dependency's index has not been fetched or
|
|
2329
|
+
cannot be read, naming `package-build deps fetch`; when a note of the tree
|
|
2330
|
+
cannot be indexed, as the content-index build reports it; and when the
|
|
2331
|
+
configuration, fonts or dependency indexes keep changing while they are read,
|
|
2332
|
+
three times over. A finding in the
|
|
2333
|
+
rendered note itself is returned in `findings`, never thrown.
|
|
2334
|
+
|
|
2335
|
+
`prepareTreeSqlTables(contentBase, { only })`, with one note's file or a
|
|
2336
|
+
collection of them, answers those notes' directives and generated sections and
|
|
2337
|
+
no other note's; its queries still read the whole corpus, so each note's
|
|
2338
|
+
results equal its results in a preparation of the whole tree.
|
|
2339
|
+
|
|
2155
2340
|
## `./preview`
|
|
2156
2341
|
|
|
2157
2342
|
Import the buffer-only renderer directly without loading the build pipeline:
|
|
@@ -2193,21 +2378,21 @@ const config = defineConfig({
|
|
|
2193
2378
|
});
|
|
2194
2379
|
```
|
|
2195
2380
|
|
|
2196
|
-
| Export | Signature
|
|
2197
|
-
| -------------------------- |
|
|
2198
|
-
| `defineConfig` | `defineConfig(config)`
|
|
2199
|
-
| `PACKAGE_KINDS` | `const PACKAGE_KINDS`
|
|
2200
|
-
| `DOCUMENTATION_KIND` | `const DOCUMENTATION_KIND`
|
|
2201
|
-
| `AUTHOR_ROLES` | `const AUTHOR_ROLES`
|
|
2202
|
-
| `RELATIONSHIP_KEYS` | `const RELATIONSHIP_KEYS`
|
|
2203
|
-
| `compilesFoundryDocuments` | `compilesFoundryDocuments(config)`
|
|
2204
|
-
| `PACK_DOCUMENT_TYPES` | `const PACK_DOCUMENT_TYPES`
|
|
2205
|
-
| `DEFAULT_PATHS` | `const DEFAULT_PATHS`
|
|
2206
|
-
| `DEFAULT_ADDRESS_SCHEME` | `const DEFAULT_ADDRESS_SCHEME`
|
|
2207
|
-
| `DERIVED_SYSTEM_VERSION` | `const DERIVED_SYSTEM_VERSION`
|
|
2208
|
-
| `publishesContentPages` | `publishesContentPages(config)`
|
|
2209
|
-
| `siteBase` | `siteBase(config)`
|
|
2210
|
-
| `siteBuildConfig` | `siteBuildConfig(config, { baseUrl, assets })` | `ContentBuildConfig` |
|
|
2381
|
+
| Export | Signature | Returns | Use it when |
|
|
2382
|
+
| -------------------------- | ---------------------------------------------------- | ---------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
2383
|
+
| `defineConfig` | `defineConfig(config)` | `ContentBuildConfig` — the frozen, defaulted configuration | validating and normalizing a content configuration; every configuration reaches this function whether authored as YAML or as `.mjs` |
|
|
2384
|
+
| `PACKAGE_KINDS` | `const PACKAGE_KINDS` | — | reading the three kinds of Foundry package a content module can be built into — `systems`, `modules`, and `documentation`, the kind that compiles no Foundry document at all (also the Foundry install directory name for the first two) |
|
|
2385
|
+
| `DOCUMENTATION_KIND` | `const DOCUMENTATION_KIND` | — | naming the `packageKind` value that compiles no Foundry documents, spelled once so the validator, the CLI and the compile passes cannot disagree about what it means |
|
|
2386
|
+
| `AUTHOR_ROLES` | `const AUTHOR_ROLES` | `readonly string[]` | reading the closed list an `authors` entry's `role` is drawn from — the configuration check refuses any other value |
|
|
2387
|
+
| `RELATIONSHIP_KEYS` | `const RELATIONSHIP_KEYS` | `readonly string[]` | reading every key a declared relationship may carry — the configuration check refuses anything else, and the manifest writer drops the ones Foundry's own schema does not name |
|
|
2388
|
+
| `compilesFoundryDocuments` | `compilesFoundryDocuments(config)` | `boolean` | checking whether a resolved configuration compiles Foundry documents at all — false only for a `documentation` package |
|
|
2389
|
+
| `PACK_DOCUMENT_TYPES` | `const PACK_DOCUMENT_TYPES` | — | reading the Foundry document types this toolchain is able to compile a compendium pack of |
|
|
2390
|
+
| `DEFAULT_PATHS` | `const DEFAULT_PATHS` | — | reading the conventional directory layout a build reads from and writes to, relative to `rootDir` |
|
|
2391
|
+
| `DEFAULT_ADDRESS_SCHEME` | `const DEFAULT_ADDRESS_SCHEME` | — | reading an unconfigured repository's address-scheme defaults (`prefix`, where the content tree mounts inside the package) |
|
|
2392
|
+
| `DERIVED_SYSTEM_VERSION` | `const DERIVED_SYSTEM_VERSION` | — | the loader-only symbol key `defineConfig` uses internally to receive a resolved system version; not something a configuration author writes |
|
|
2393
|
+
| `publishesContentPages` | `publishesContentPages(config)` | `boolean` | checking whether the authored content tree contains notes beyond its homepage, shared by site, PDF, index, and Foundry address emission |
|
|
2394
|
+
| `siteBase` | `siteBase(config)` | `string` | reading the path a package's site is served at — `site.base` where the configuration states one, `/<contentPackage>/` otherwise |
|
|
2395
|
+
| `siteBuildConfig` | `siteBuildConfig(config, { root, baseUrl, assets })` | `ContentBuildConfig` | selecting internal www by default, explicit root-derived CDN over configured assets, and low-level baseUrl/assets over derived values; exposes effective homepage/site.base/site.assets without changing authored configuration; the caller first validates the authored homepage with `checkHomepage`; the CLI uses it before index/body preflight |
|
|
2211
2396
|
|
|
2212
2397
|
## `./config`
|
|
2213
2398
|
|
|
@@ -2638,6 +2823,10 @@ preparation and kept separate.
|
|
|
2638
2823
|
`prepareTreeSqlTables` retains the complete record membership when opening a
|
|
2639
2824
|
public database, so derived citations can distinguish hidden notes from
|
|
2640
2825
|
unknown addresses. Only public records contribute rendered sources and links.
|
|
2826
|
+
`prepareTreeSqlTables(contentBase, { only })`, with one note's file, answers
|
|
2827
|
+
that note's directives and generated sections and no other note's; its queries
|
|
2828
|
+
still read the whole corpus, so its results equal that note's in a preparation
|
|
2829
|
+
of the whole tree.
|
|
2641
2830
|
|
|
2642
2831
|
| Export | Contract |
|
|
2643
2832
|
| ---------------------- | ---------------------------------------------------------------------------------------------------------------------------- |
|
package/docs/commands.md
CHANGED
|
@@ -1324,13 +1324,17 @@ package leaves out as a named asset set.
|
|
|
1324
1324
|
**SYNOPSIS**
|
|
1325
1325
|
|
|
1326
1326
|
```
|
|
1327
|
-
package-build cdn publish --set <file> --name <display name> [--dry-run]
|
|
1327
|
+
package-build cdn publish [--dest internal|cloudflare] --set <file> --name <display name> [--dry-run]
|
|
1328
1328
|
```
|
|
1329
1329
|
|
|
1330
1330
|
**DESCRIPTION**
|
|
1331
1331
|
|
|
1332
|
-
|
|
1333
|
-
|
|
1332
|
+
Publication is manual and separate from site building and deployment. No build
|
|
1333
|
+
or release runs it. `--dest internal` is the default: pictures go to
|
|
1334
|
+
`atlas:/srv/preview/cdn`, served at `https://cdn.internal.heroiclands.org`.
|
|
1335
|
+
`--dest cloudflare` uses the existing R2 bucket served at configured `site.assets`
|
|
1336
|
+
(normally `https://cdn.heroiclands.org`). Each destination uses the same planner,
|
|
1337
|
+
transforms, object keys and publication rules.
|
|
1334
1338
|
|
|
1335
1339
|
Every icon and image under the package's picture roots is uploaded flat, as
|
|
1336
1340
|
`icons/<basename>` or `images/<basename>` — shipped and left-out pictures
|
|
@@ -1339,11 +1343,10 @@ site build gives it. `packageBuild.assetTransform` runs on each file exactly
|
|
|
1339
1343
|
as `package-build assets` runs it, so a picture on the CDN holds the bytes the
|
|
1340
1344
|
staged package holds. SVG is served as `image/svg+xml`.
|
|
1341
1345
|
|
|
1342
|
-
|
|
1343
|
-
that address: `thalorna/images/thorn.webp` is
|
|
1346
|
+
An object's key is its path below the selected CDN origin. For Cloudflare, `thalorna/images/thorn.webp` is
|
|
1344
1347
|
`https://cdn.heroiclands.org/thalorna/images/thorn.webp`.
|
|
1345
1348
|
|
|
1346
|
-
`--set` names the set file, written at `<
|
|
1349
|
+
`--set` names the set file, written at `<selected CDN origin>/<contentPackage>/<file>`.
|
|
1347
1350
|
It lists only the pictures the package leaves out — an address a set does not
|
|
1348
1351
|
list resolves to the shipped copy — chosen by the same rule the stage applies,
|
|
1349
1352
|
`packageBuild.shippingLicenses` included. Its shape is the shipped set's, with
|
|
@@ -1380,9 +1383,32 @@ does not have, and a note naming left-out art that is not on the CDN when the
|
|
|
1380
1383
|
run ends, are warnings at the note's line and column: an art slot under
|
|
1381
1384
|
`data:`, an embed, or a body image.
|
|
1382
1385
|
|
|
1383
|
-
**
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
+
**Internal access.** Install `rsync` and OpenSSH on the publishing machine.
|
|
1387
|
+
The default uses your ordinary `atlas` SSH account, which must have write access
|
|
1388
|
+
to `/srv/preview/cdn`. Keys come from your SSH configuration or agent; the command
|
|
1389
|
+
uses batch mode and requires an already verified host key with
|
|
1390
|
+
`StrictHostKeyChecking=yes`. It uses only rsync's receiver protocol, keeps keys out
|
|
1391
|
+
of arguments and logs, and never deletes existing CDN objects. Transfer failures
|
|
1392
|
+
report the exit code and a bounded, sanitized receiver diagnostic; local staging
|
|
1393
|
+
failures name the operation and filesystem error code. Commands, key material and
|
|
1394
|
+
full child-process errors are omitted.
|
|
1395
|
+
|
|
1396
|
+
For an existing restricted publisher credential, configure an SSH alias with its
|
|
1397
|
+
`User preview-publish`, `IdentityFile` and verified `UserKnownHostsFile`. Then set
|
|
1398
|
+
`CDN_INTERNAL_DESTINATION=your-alias:cdn` in the environment or `.env.local`.
|
|
1399
|
+
The deployed forced command `rrsync -wo -munge /srv/preview` accepts that relative
|
|
1400
|
+
destination; do not repeat `/srv/preview` for this account. The override accepts
|
|
1401
|
+
only an SSH host alias followed by `:cdn` or `:/srv/preview/cdn`. It changes the
|
|
1402
|
+
connection, not the public origin. This command neither installs credentials nor
|
|
1403
|
+
changes server permissions.
|
|
1404
|
+
|
|
1405
|
+
Internal skip checks and set comparisons read the selected internal CDN over
|
|
1406
|
+
HTTPS. An internal dry run uses only those reads: no SSH connection, temporary
|
|
1407
|
+
staging or R2 credentials. The CDN must be reachable from the LAN or VPN.
|
|
1408
|
+
|
|
1409
|
+
**Cloudflare credentials.** With `--dest cloudflare`, the store is the existing
|
|
1410
|
+
S3-compatible bucket, signed with AWS Signature Version 4. Its settings are read
|
|
1411
|
+
from the environment, after `.env.local` and `.env` are loaded:
|
|
1386
1412
|
|
|
1387
1413
|
| Variable | What it is |
|
|
1388
1414
|
| ----------------------- | ------------------------------------------------------------------------- |
|
|
@@ -1391,46 +1417,35 @@ read from the environment, after `.env.local` and `.env` are loaded:
|
|
|
1391
1417
|
| `CDN_ACCESS_KEY_ID` | The access key id of a token that can write the bucket. |
|
|
1392
1418
|
| `CDN_SECRET_ACCESS_KEY` | That token's secret. Never printed. |
|
|
1393
1419
|
|
|
1394
|
-
Setting some but not all of them is an error naming the missing ones. With
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
A workflow calls the command the same way, passing the four values as named
|
|
1399
|
-
secrets in the step's environment:
|
|
1400
|
-
|
|
1401
|
-
```yaml
|
|
1402
|
-
- run: npx package-build cdn publish --set alt-art.json --name "Thalorna — illustrated"
|
|
1403
|
-
env:
|
|
1404
|
-
CDN_ENDPOINT: ${{ secrets.CDN_ENDPOINT }}
|
|
1405
|
-
CDN_BUCKET: ${{ secrets.CDN_BUCKET }}
|
|
1406
|
-
CDN_ACCESS_KEY_ID: ${{ secrets.CDN_ACCESS_KEY_ID }}
|
|
1407
|
-
CDN_SECRET_ACCESS_KEY: ${{ secrets.CDN_SECRET_ACCESS_KEY }}
|
|
1408
|
-
```
|
|
1420
|
+
Setting some but not all of them is an error naming the missing ones. With none
|
|
1421
|
+
set, `--dest cloudflare --dry-run` compares against `site.assets` read-only, and an
|
|
1422
|
+
upload is refused. Internal publication ignores all four R2 settings.
|
|
1409
1423
|
|
|
1410
1424
|
**OPTIONS**
|
|
1411
1425
|
|
|
1412
|
-
| Option | Type | Default
|
|
1413
|
-
| ----------- | ------- |
|
|
1414
|
-
| `--
|
|
1415
|
-
| `--
|
|
1416
|
-
| `--
|
|
1426
|
+
| Option | Type | Default | Description |
|
|
1427
|
+
| ----------- | ------- | ---------- | --------------------------------------------------------------------------- |
|
|
1428
|
+
| `--dest` | string | `internal` | `internal` for atlas; `cloudflare` for the configured R2 bucket. |
|
|
1429
|
+
| `--set` | string | — | Required. The set file's name under the package's CDN root, a `.json` file. |
|
|
1430
|
+
| `--name` | string | — | Required. The set's display name. |
|
|
1431
|
+
| `--dry-run` | boolean | `false` | List what would upload and what is skipped; write nothing. |
|
|
1417
1432
|
|
|
1418
1433
|
**EXIT STATUS**
|
|
1419
1434
|
|
|
1420
1435
|
1 when a picture's file has no address, when a picture fails to upload, when
|
|
1421
|
-
`--set` is `default.json` or a path, when `site.assets` is undeclared,
|
|
1422
|
-
when an upload
|
|
1436
|
+
`--set` is `default.json` or a path, when Cloudflare `site.assets` is undeclared, when an internal destination or
|
|
1437
|
+
planned relative key is unsafe, and when an upload lacks access to its store. Warnings about notes do not change
|
|
1423
1438
|
it. Otherwise 0.
|
|
1424
1439
|
|
|
1425
1440
|
**EXAMPLES**
|
|
1426
1441
|
|
|
1427
1442
|
```
|
|
1428
1443
|
$ package-build cdn publish --set alt-art.json --name "Thalorna — illustrated" --dry-run
|
|
1429
|
-
Dry run: comparing 582 pictures against https://cdn.heroiclands.org, read-only.
|
|
1444
|
+
Dry run: comparing 582 pictures against https://cdn.internal.heroiclands.org, read-only.
|
|
1430
1445
|
would upload thalorna/icons/anvilbdg.svg (969 bytes)
|
|
1431
1446
|
…
|
|
1432
1447
|
would upload thalorna/alt-art.json (582 assets)
|
|
1433
|
-
Would upload 582, skipped 0, failed 0; https://cdn.heroiclands.org/thalorna/alt-art.json lists 582 left-out pictures (would change).
|
|
1448
|
+
Would upload 582, skipped 0, failed 0; https://cdn.internal.heroiclands.org/thalorna/alt-art.json lists 582 left-out pictures (would change).
|
|
1434
1449
|
|
|
1435
1450
|
$ package-build cdn publish --set default.json --name Shipped
|
|
1436
1451
|
package-build: `--set default.json` is refused: `default.json` is the shipped set's name, the fallback every other set is read against. Name the set for its art
|
|
@@ -2685,7 +2700,7 @@ it for search.
|
|
|
2685
2700
|
**SYNOPSIS**
|
|
2686
2701
|
|
|
2687
2702
|
```
|
|
2688
|
-
package-build site build [--out <dir>] [--base-url <url>] [--assets <url>]
|
|
2703
|
+
package-build site build [--root <domain>] [--out <dir>] [--base-url <url>] [--assets <url>]
|
|
2689
2704
|
```
|
|
2690
2705
|
|
|
2691
2706
|
**DESCRIPTION**
|
|
@@ -2748,6 +2763,11 @@ out in the [site guide](guides/site.md#search).
|
|
|
2748
2763
|
|
|
2749
2764
|
The site's address, title and menu are read before the tree is, so a missing
|
|
2750
2765
|
`homepage` or a cold navigation cache fails with the last good site in place.
|
|
2766
|
+
With [`site.inputs`](configuration.md#local-site-inputs), a local module supplies
|
|
2767
|
+
the selected navigation and text artifacts instead of reading the navigation
|
|
2768
|
+
cache. It receives the effective configuration, runs before content rendering,
|
|
2769
|
+
and may reject canonical navigation drift. Its files are validated before
|
|
2770
|
+
output replacement and written after the renderer clears the site.
|
|
2751
2771
|
Every gate is then checked and reported, and the run stops at the first that
|
|
2752
2772
|
fires, ordered so the report names the cause rather than its symptoms — an
|
|
2753
2773
|
unusable dependency manifest, reported after the links that failed because of
|
|
@@ -2760,6 +2780,13 @@ run fails before the search index is built.
|
|
|
2760
2780
|
|
|
2761
2781
|
`--out <dir>` — the directory that is deployed. Defaults to `build/site`.
|
|
2762
2782
|
|
|
2783
|
+
`--root <domain>` — domain for the build's addresses, defaulting to
|
|
2784
|
+
`internal.heroiclands.org`. The site uses `https://www.<domain>` with the
|
|
2785
|
+
package's existing homepage path. An explicit root also selects
|
|
2786
|
+
`https://cdn.<domain>` over configured `site.assets`; omitting this flag keeps
|
|
2787
|
+
configured assets, or derives the CDN when none are configured. Supply a plain
|
|
2788
|
+
DNS domain without a scheme, path, port, credentials, query or fragment.
|
|
2789
|
+
|
|
2763
2790
|
`--base-url <url>` — the absolute HTTP(S) address of this build. Its pathname
|
|
2764
2791
|
becomes the effective `site.base`, and must end with `/`; `/` serves the site
|
|
2765
2792
|
at the origin's root. Pages, local links, theme files, script fonts and search
|
|
@@ -2768,8 +2795,18 @@ all use that prefix. Output lands under `<out>/<pathname>/`.
|
|
|
2768
2795
|
`--assets <url>` — the absolute HTTP(S) asset root for this build. It may
|
|
2769
2796
|
include a path; trailing slashes are trimmed.
|
|
2770
2797
|
|
|
2771
|
-
|
|
2772
|
-
|
|
2798
|
+
Explicit `--base-url` overrides the root-derived site URL, including its path;
|
|
2799
|
+
explicit `--assets` overrides both root-derived and configured assets. These
|
|
2800
|
+
low-level flags are independent and may be combined with `--root`. Cached
|
|
2801
|
+
navigation links on the canonical Heroic Lands www origin and the brand's
|
|
2802
|
+
license link use the effective site origin. External links and authored content
|
|
2803
|
+
links keep their addresses. Asset references on the exact canonical
|
|
2804
|
+
`https://cdn.heroiclands.org` origin use the effective asset root, preserving
|
|
2805
|
+
the path, query and fragment. Other image origins stay unchanged.
|
|
2806
|
+
The build only writes local output; it neither
|
|
2807
|
+
uploads nor fetches missing dependencies or assets.
|
|
2808
|
+
|
|
2809
|
+
Overrides apply in memory
|
|
2773
2810
|
before preflight and rendering; they never rewrite `package.json` or
|
|
2774
2811
|
`package-build.config.yaml`. The authored homepage must still pass its usual
|
|
2775
2812
|
validation. Both URLs require plain paths without percent escapes, dot
|