@heroiclands/package-build 22.0.2 → 22.1.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 +79 -0
- package/CONTENT.md +34 -18
- package/README.md +1 -1
- package/bin/content-build.mjs +40 -22
- package/bin/package-build.mjs +34 -0
- package/config.mjs +88 -3
- package/content-config.mjs +246 -12
- package/docs/api.md +10 -7
- package/docs/commands.md +122 -32
- package/docs/configuration.md +257 -62
- package/docs/getting-started.md +15 -4
- package/docs/project-setup.md +45 -20
- package/engine/content-index.mjs +25 -6
- package/engine/pack-config.mjs +21 -2
- package/engine/site-build.mjs +15 -52
- package/engine/site-config.mjs +462 -0
- package/engine/site-root.mjs +165 -0
- package/engine/svg-theme.mjs +140 -0
- package/manifest.mjs +12 -7
- package/package.json +2 -1
- package/stage.mjs +4 -3
- package/types/config.d.mts +80 -33
- package/types/content-config.d.mts +53 -0
- package/types/engine/site-build.d.mts +3 -25
- package/types/engine/site-config.d.mts +223 -0
- package/types/engine/site-root.d.mts +87 -0
- package/types/engine/svg-theme.d.mts +22 -0
- package/types/manifest.d.mts +4 -4
- package/types/stage.d.mts +4 -3
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,84 @@
|
|
|
1
1
|
# @heroiclands/package-build
|
|
2
2
|
|
|
3
|
+
## 22.1.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- b0ec919: **The site build generates the Hugo configuration.** `content-build site`
|
|
8
|
+
writes the whole Hugo source tree under `build/hugo/` — a generated
|
|
9
|
+
`hugo.toml`, the content mount at `build/hugo/content/`, Hugo's cache —
|
|
10
|
+
beside the deployment root `build/site/`, and the repository carries no Hugo
|
|
11
|
+
configuration of its own. Every value in the generated file has one source:
|
|
12
|
+
`package.json` (`homepage`, `description`, `author`),
|
|
13
|
+
`package-build.config.yaml` (`packageBuild.manifest.title`, `site.assets`,
|
|
14
|
+
`site.list`, `site.notfound`), the installed `@heroiclands/hugo-theme`, and
|
|
15
|
+
the organisation's brand links and navigation. `package.json`'s `homepage`
|
|
16
|
+
is checked on every site build: it must be
|
|
17
|
+
`https://www.heroiclands.org/<contentPackage>/`.
|
|
18
|
+
|
|
19
|
+
**`deps fetch` also fetches the navigation.** The header menu every site
|
|
20
|
+
renders comes from `https://www.heroiclands.org/nav.json`, cached under
|
|
21
|
+
`build/cache/navigation/` by `content-build deps fetch` and read from the
|
|
22
|
+
cache by the site build — a cold cache fails naming `deps fetch`, so a
|
|
23
|
+
package with no other dependency now runs `deps fetch` before `site` too.
|
|
24
|
+
|
|
25
|
+
**To migrate a repository:**
|
|
26
|
+
|
|
27
|
+
- Delete `site/` and its `.gitignore` entries (`site/content`, `site/public`,
|
|
28
|
+
`site/resources`), and any `packageBuild.clean.extra` entry naming them.
|
|
29
|
+
- Delete `site.out` from `package-build.config.yaml`; the location is fixed.
|
|
30
|
+
- Move the `[params.notfound]` block from the old `hugo.toml` to
|
|
31
|
+
`site.notfound` (`tagline`, `sitenoun`, optional `heroimage` and
|
|
32
|
+
`links[]` of `{title, url, text}`), `params.list.shortcodes` to
|
|
33
|
+
`site.list.shortcodes`, and `params.cdnBaseURL` to `site.assets` where it
|
|
34
|
+
was not already there. Anything else the file carried that the generator
|
|
35
|
+
does not write — `markup.goldmark.extensions.linkify`, say — goes under
|
|
36
|
+
`site.hugo`, which is deep-merged last; every key the generator writes is
|
|
37
|
+
refused there, naming its source.
|
|
38
|
+
- Point the npm scripts at the generated tree:
|
|
39
|
+
`hugo --source build/hugo --minify --gc --cleanDestinationDir` and
|
|
40
|
+
`hugo server --source build/hugo`.
|
|
41
|
+
- Make sure `package.json` declares `homepage`, `description` and `author`,
|
|
42
|
+
and that `@heroiclands/hugo-theme` is under `devDependencies`.
|
|
43
|
+
- a5e83fd: **A manifest's `description` comes from `package.json`.**
|
|
44
|
+
`packageBuild.manifest.description` is refused, the same way `id` and
|
|
45
|
+
`version` are — delete it; the manifest carries `package.json`'s own
|
|
46
|
+
`description`.
|
|
47
|
+
|
|
48
|
+
**`package.json`'s `homepage` and `author` are read and normalised.**
|
|
49
|
+
`homepage` names the address every package's site is served at — an
|
|
50
|
+
absolute URL ending `/<contentPackage>/` — and `author` is accepted in
|
|
51
|
+
either of npm's forms, a string or `{name, email, url}`, and normalised to
|
|
52
|
+
the object form.
|
|
53
|
+
|
|
54
|
+
## 22.0.3
|
|
55
|
+
|
|
56
|
+
### Patch Changes
|
|
57
|
+
|
|
58
|
+
- fd58dc0: **A package may ship assets and compile nothing.** `packs: []` is now a package
|
|
59
|
+
saying it has no documents, rather than one that forgot to say which, and a
|
|
60
|
+
package with no packs needs no content tree — its index is its asset records.
|
|
61
|
+
An alternative-art module is the case: the same addresses another package
|
|
62
|
+
publishes, resolving to different files when it is installed.
|
|
63
|
+
|
|
64
|
+
A package that _does_ declare packs is unchanged: a missing content tree is
|
|
65
|
+
still a misconfigured path and still fails, and an index that would state a
|
|
66
|
+
package has no content at all is still refused.
|
|
67
|
+
- cfdf061: **Two pieces of per-repository tooling move here.** Every site-publishing
|
|
68
|
+
package held its own copy of both, and a copy per consumer is a copy free to
|
|
69
|
+
drift — which is what happened, invisibly, because nobody reads all of them at
|
|
70
|
+
once.
|
|
71
|
+
|
|
72
|
+
- `packageBuild.assetTransform: svg-theme` names a shipped transform, so an
|
|
73
|
+
icon follows the reader's colour scheme without a module in the repository.
|
|
74
|
+
The value still takes a path, so a consumer with a transform of its own is
|
|
75
|
+
unaffected.
|
|
76
|
+
- `package-build site-root` writes a deployment's `_headers` and `_redirects`:
|
|
77
|
+
indexing suppressed on every host-assigned address, and both spellings of the
|
|
78
|
+
prefix root redirected to the landing with a lifetime on the 301. The package
|
|
79
|
+
name comes from `contentPackage`, which was the only thing the copies actually
|
|
80
|
+
varied.
|
|
81
|
+
|
|
3
82
|
## 22.0.2
|
|
4
83
|
|
|
5
84
|
### Patch Changes
|
package/CONTENT.md
CHANGED
|
@@ -234,8 +234,9 @@ publish:
|
|
|
234
234
|
site: content
|
|
235
235
|
address: { prefix: guide/ }
|
|
236
236
|
|
|
237
|
-
|
|
238
|
-
|
|
237
|
+
packageBuild:
|
|
238
|
+
manifest:
|
|
239
|
+
title: The Toolkit
|
|
239
240
|
|
|
240
241
|
pdf:
|
|
241
242
|
title: The Toolkit
|
|
@@ -2260,22 +2261,22 @@ address anyone links to. A package publishes its content index regardless — th
|
|
|
2260
2261
|
licensing constraint is against publishing _pages_, not against the artifact
|
|
2261
2262
|
existing — but nothing may declare it as a dependency.
|
|
2262
2263
|
|
|
2263
|
-
The homepage's file is written at the root of
|
|
2264
|
-
site root, one level above the
|
|
2265
|
-
`publish.address.prefix` puts everything else —
|
|
2266
|
-
it, `homepage-root.md`. As with every other page, the front matter's `url`
|
|
2264
|
+
The homepage's file is written at the root of the content mount,
|
|
2265
|
+
`build/hugo/content/` — the package's own site root, one level above the
|
|
2266
|
+
content mount, which is where `publish.address.prefix` puts everything else —
|
|
2267
|
+
under the name its address gives it, `homepage-root.md`. As with every other page, the front matter's `url`
|
|
2267
2268
|
decides where it publishes, and states it relative to the site root — `site.base`
|
|
2268
2269
|
does not reach it.
|
|
2269
2270
|
|
|
2270
2271
|
**What it does not do is decide addresses.** Those come from `publish.address`,
|
|
2271
2272
|
the same setting the content index reads, so a page and its index record cannot
|
|
2272
2273
|
disagree about where the page is. Everything under `site:` is _framing_ —
|
|
2273
|
-
|
|
2274
|
-
|
|
2274
|
+
what a section is called, which extra trees are published beside the content,
|
|
2275
|
+
and the residue of the generated Hugo configuration that is this repository's
|
|
2276
|
+
own:
|
|
2275
2277
|
|
|
2276
2278
|
```yaml
|
|
2277
2279
|
site:
|
|
2278
|
-
out: kb/content # required; wiped on every run
|
|
2279
2280
|
base: /sohl/ # default: /<contentPackage>/ — hrefs only, never a page's `url:`
|
|
2280
2281
|
packages: [sohl, thalorna] # default: just contentPackage
|
|
2281
2282
|
backfillSections: true
|
|
@@ -2294,11 +2295,16 @@ site:
|
|
|
2294
2295
|
description: Folk, animals and the things that walk the world.
|
|
2295
2296
|
readmeSections:
|
|
2296
2297
|
dev-docs: { title: Developer Documentation, banner: banners/dev-docs.webp }
|
|
2298
|
+
list: { shortcodes: true }
|
|
2299
|
+
notfound:
|
|
2300
|
+
tagline: Song of Heroic Lands has no page at
|
|
2301
|
+
sitenoun: site
|
|
2302
|
+
links:
|
|
2303
|
+
- { title: Knowledgebase, url: kb/, text: Every section, with the full content catalog. }
|
|
2297
2304
|
```
|
|
2298
2305
|
|
|
2299
2306
|
| Key | What it decides |
|
|
2300
2307
|
| ------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
2301
|
-
| `out` | The Hugo content root. **Required** in both modes, and wiped on every run — see below. |
|
|
2302
2308
|
| `base` | Where the package is served: the prefix on every rendered `href`, and what a manifest `path` is measured against. It reaches no page's own `url:` — see [A page's URL is its address](#a-pages-url-is-its-address). Defaults to `/<contentPackage>/`. |
|
|
2303
2309
|
| `packages` | Which content packages this site renders. Defaults to its own. |
|
|
2304
2310
|
| `sections` | The Hugo sections this site declares, and what each says about itself — see below. |
|
|
@@ -2308,6 +2314,17 @@ site:
|
|
|
2308
2314
|
| `trees` | Extra source trees published beside the content, preserving their source layout below a section. |
|
|
2309
2315
|
| `pass` | A named bundle of this repository's own body rewrites. |
|
|
2310
2316
|
| `passOptions` | That bundle's options. |
|
|
2317
|
+
| `assets` | The host every package's imagery is served from; the generated `params.cdnBaseURL`. |
|
|
2318
|
+
| `list` | How a listing page renders; the generated `params.list`. |
|
|
2319
|
+
| `notfound` | The wording of the "page not found" page; the generated `params.notfound`. |
|
|
2320
|
+
| `hugo` | A mapping deep-merged over the generated Hugo configuration, last. Every key the generator writes is refused here — see `docs/configuration.md`. |
|
|
2321
|
+
|
|
2322
|
+
Where the tree is written is not among them. `content-build site` writes the
|
|
2323
|
+
whole Hugo source tree under `build/hugo/` — the generated `hugo.toml`, the
|
|
2324
|
+
content mount at `build/hugo/content/`, Hugo's cache — and the consumer's
|
|
2325
|
+
script runs `hugo --source build/hugo` over it. The generated file's every
|
|
2326
|
+
value has a source the repository already states; `docs/configuration.md`
|
|
2327
|
+
lists them.
|
|
2311
2328
|
|
|
2312
2329
|
### What a section may declare
|
|
2313
2330
|
|
|
@@ -2409,15 +2426,14 @@ The **writers** name no keys: a section's `_index.md` is whatever the entry
|
|
|
2409
2426
|
resolved to, `title` first. So extending the vocabulary is a change to the
|
|
2410
2427
|
schema alone, and the two can no longer drift apart.
|
|
2411
2428
|
|
|
2412
|
-
### Why
|
|
2429
|
+
### Why the output location is fixed
|
|
2413
2430
|
|
|
2414
|
-
The
|
|
2415
|
-
page whose note was renamed cannot linger and keep publishing.
|
|
2416
|
-
|
|
2417
|
-
|
|
2418
|
-
|
|
2419
|
-
|
|
2420
|
-
the repository root.
|
|
2431
|
+
The content mount is a build artifact and is **deleted on every run**, so that a
|
|
2432
|
+
page whose note was renamed cannot linger and keep publishing. A configurable
|
|
2433
|
+
location is a location that can be unset — and an unset one resolves to the
|
|
2434
|
+
repository root, where the wipe deletes the working tree. A fixed one under
|
|
2435
|
+
`build/` can point nowhere else, so `site.out` is refused by name and the wipe
|
|
2436
|
+
needs no guard.
|
|
2421
2437
|
|
|
2422
2438
|
### Consumer passes are named, not imported
|
|
2423
2439
|
|
package/README.md
CHANGED
package/bin/content-build.mjs
CHANGED
|
@@ -79,6 +79,7 @@ import {
|
|
|
79
79
|
unaddressableForeignPackages,
|
|
80
80
|
formatUnaddressableFinding,
|
|
81
81
|
} from "../engine/metadata-index.mjs";
|
|
82
|
+
import { fetchNavigation, generateHugoConfig, writeHugoConfig } from "../engine/site-config.mjs";
|
|
82
83
|
import { renderItemFieldReference } from "../engine/field-reference.mjs";
|
|
83
84
|
import { lintContentTree } from "../engine/content-lint.mjs";
|
|
84
85
|
import { lintContentCharset } from "../engine/content-charset.mjs";
|
|
@@ -1484,7 +1485,14 @@ function pdfCommand() {
|
|
|
1484
1485
|
* compiled into packs. Everything a consumer would otherwise write for itself —
|
|
1485
1486
|
* the walk, the address derivation, the address index, table expansion,
|
|
1486
1487
|
* wikilink resolution, code-fence protection, the foreign-manifest merge and
|
|
1487
|
-
* the section-landing backfill — happens here, from configuration.
|
|
1488
|
+
* the section-landing backfill — happens here, from configuration. So does the
|
|
1489
|
+
* Hugo configuration: the whole source tree Hugo reads lands under
|
|
1490
|
+
* `build/hugo/`, and the consumer's script runs Hugo over it.
|
|
1491
|
+
*
|
|
1492
|
+
* **The Hugo configuration is generated before anything is written.** Its
|
|
1493
|
+
* sources — `package.json`'s `homepage`, the cached navigation, the installed
|
|
1494
|
+
* theme — are each a way the build can fail, and failing before the output
|
|
1495
|
+
* tree is cleared leaves the last good site in place to be looked at.
|
|
1488
1496
|
*
|
|
1489
1497
|
* **Each gate is reported and the run stops at the first that fires.** They are
|
|
1490
1498
|
* ordered so the report names the cause rather than its symptoms: an unusable
|
|
@@ -1497,20 +1505,17 @@ function pdfCommand() {
|
|
|
1497
1505
|
function siteCommand() {
|
|
1498
1506
|
return {
|
|
1499
1507
|
command: "site",
|
|
1500
|
-
describe: "Build
|
|
1501
|
-
builder: (
|
|
1502
|
-
|
|
1503
|
-
describe: "Write the mount here instead of the configured `site.out`.",
|
|
1504
|
-
type: "string",
|
|
1505
|
-
});
|
|
1506
|
-
},
|
|
1507
|
-
handler: async (argv) => {
|
|
1508
|
+
describe: "Build the Hugo source tree from the content tree",
|
|
1509
|
+
builder: () => {},
|
|
1510
|
+
handler: async () => {
|
|
1508
1511
|
try {
|
|
1512
|
+
const config = loadPackConfig();
|
|
1513
|
+
const hugo = generateHugoConfig(config);
|
|
1509
1514
|
const result = buildSite({
|
|
1510
|
-
|
|
1511
|
-
|
|
1515
|
+
config,
|
|
1516
|
+
sqlTables: await prepareTreeSqlTables(config.paths.content, {
|
|
1517
|
+
skipDirectories: config.skipDirectories,
|
|
1512
1518
|
}),
|
|
1513
|
-
...(argv.out ? { outRoot: argv.out } : {}),
|
|
1514
1519
|
});
|
|
1515
1520
|
const { gates } = result;
|
|
1516
1521
|
|
|
@@ -1623,6 +1628,8 @@ function siteCommand() {
|
|
|
1623
1628
|
`${s.tree ?? 0} tree page(s) + ${s.landings} ` +
|
|
1624
1629
|
`landing(s) to ${path.relative(process.cwd(), s.out)}`,
|
|
1625
1630
|
);
|
|
1631
|
+
const { file } = writeHugoConfig(config, hugo);
|
|
1632
|
+
log.info(`wrote ${path.relative(process.cwd(), file)}`);
|
|
1626
1633
|
} catch (err) {
|
|
1627
1634
|
reportFailure(err);
|
|
1628
1635
|
process.exitCode = 1;
|
|
@@ -1782,16 +1789,19 @@ async function fetchFromLocalArtifact(config, argv) {
|
|
|
1782
1789
|
|
|
1783
1790
|
/**
|
|
1784
1791
|
* `deps fetch` — fill the caches this build resolves other packages through:
|
|
1785
|
-
* the **content index** of every declared dependency,
|
|
1786
|
-
*
|
|
1787
|
-
*
|
|
1788
|
-
*
|
|
1789
|
-
*
|
|
1790
|
-
*
|
|
1791
|
-
*
|
|
1792
|
-
*
|
|
1793
|
-
*
|
|
1794
|
-
*
|
|
1792
|
+
* the **content index** of every declared dependency, the **item catalogue**
|
|
1793
|
+
* of those additionally declaring `itemCatalog: true`, and the **site
|
|
1794
|
+
* navigation** heroiclands.org publishes, which the site build writes its
|
|
1795
|
+
* menu from.
|
|
1796
|
+
*
|
|
1797
|
+
* The two dependency sets differ deliberately. Citing another package's
|
|
1798
|
+
* *addresses* and embedding its *items* are separate edges, and a package may
|
|
1799
|
+
* have either without the other — `harn-ensemble` cites no foreign address and
|
|
1800
|
+
* embeds 324,016 item references. The navigation is fetched for every package,
|
|
1801
|
+
* because every package publishes a site.
|
|
1802
|
+
*
|
|
1803
|
+
* Its own command rather than a step of `package compile` or `site`, so that
|
|
1804
|
+
* neither reaches the network. A build that downloads silently is not
|
|
1795
1805
|
* reproducible, breaks offline, and hides a dependency's version change behind
|
|
1796
1806
|
* a passing run.
|
|
1797
1807
|
*
|
|
@@ -1838,6 +1848,14 @@ function depsCommand() {
|
|
|
1838
1848
|
await fetchFromLocalArtifact(config, argv);
|
|
1839
1849
|
return;
|
|
1840
1850
|
}
|
|
1851
|
+
// First, because every package needs it and it depends on
|
|
1852
|
+
// nothing a repository declares: a dependency whose release
|
|
1853
|
+
// cannot be read stops the run after the navigation is cached,
|
|
1854
|
+
// not before.
|
|
1855
|
+
const navigation = await fetchNavigation(config);
|
|
1856
|
+
log.info(
|
|
1857
|
+
`Fetched the site navigation to ${path.relative(process.cwd(), navigation)}.`,
|
|
1858
|
+
);
|
|
1841
1859
|
const indexes = await fetchAllMetadata(config);
|
|
1842
1860
|
if (indexes) log.info(`Fetched ${indexes} dependency content index(es).`);
|
|
1843
1861
|
const count = await fetchAllCatalogs(config);
|
package/bin/package-build.mjs
CHANGED
|
@@ -78,6 +78,8 @@ import yargs from "yargs";
|
|
|
78
78
|
import { hideBin } from "yargs/helpers";
|
|
79
79
|
|
|
80
80
|
import { loadPackageBuildConfig } from "../config.mjs";
|
|
81
|
+
import { writeSiteRoot } from "../engine/site-root.mjs";
|
|
82
|
+
import { DEPLOY_ROOT } from "../engine/site-config.mjs";
|
|
81
83
|
import { compilesFoundryDocuments } from "../content-config.mjs";
|
|
82
84
|
import { loadPackConfig, packConfigPath } from "../engine/pack-config.mjs";
|
|
83
85
|
import { cleanBuildArtifacts, stageAssets } from "../stage.mjs";
|
|
@@ -466,6 +468,37 @@ function manifestCommand() {
|
|
|
466
468
|
};
|
|
467
469
|
}
|
|
468
470
|
|
|
471
|
+
/**
|
|
472
|
+
* `package-build site-root` — the deployment's root files.
|
|
473
|
+
*
|
|
474
|
+
* Hugo owns everything under the `/<package>/` prefix; this owns what sits
|
|
475
|
+
* beside it, which is the pair Cloudflare Pages reads from the uploaded
|
|
476
|
+
* directory and nowhere else.
|
|
477
|
+
*
|
|
478
|
+
* @returns {object} The yargs command.
|
|
479
|
+
*/
|
|
480
|
+
function siteRootCommand() {
|
|
481
|
+
return {
|
|
482
|
+
command: "site-root",
|
|
483
|
+
describe: "Write the deployment's _headers and _redirects",
|
|
484
|
+
builder: (y) =>
|
|
485
|
+
y.option("out", {
|
|
486
|
+
type: "string",
|
|
487
|
+
describe: `The directory that is deployed (default: ${DEPLOY_ROOT})`,
|
|
488
|
+
}),
|
|
489
|
+
handler: handler(async (argv) => {
|
|
490
|
+
const config = loadPackageBuildConfig();
|
|
491
|
+
const shared = loadPackConfig();
|
|
492
|
+
const out = path.resolve(config.rootDir, argv.out ?? DEPLOY_ROOT);
|
|
493
|
+
|
|
494
|
+
const { files } = writeSiteRoot({ pkg: shared.contentPackage, out });
|
|
495
|
+
for (const file of files) {
|
|
496
|
+
console.log(`✅ Wrote ${path.relative(config.rootDir, file)}.`);
|
|
497
|
+
}
|
|
498
|
+
}),
|
|
499
|
+
};
|
|
500
|
+
}
|
|
501
|
+
|
|
469
502
|
/**
|
|
470
503
|
* Every file matching a glob, as `{ path, text }` with the path relative to the
|
|
471
504
|
* repository root — the form both the rules and the findings want.
|
|
@@ -1214,6 +1247,7 @@ yargs(hideBin(process.argv))
|
|
|
1214
1247
|
.command(cleanCommand())
|
|
1215
1248
|
.command(assetsCommand())
|
|
1216
1249
|
.command(manifestCommand())
|
|
1250
|
+
.command(siteRootCommand())
|
|
1217
1251
|
.command(schemaCommand())
|
|
1218
1252
|
.command(langCommand())
|
|
1219
1253
|
.command(labelsCommand())
|
package/config.mjs
CHANGED
|
@@ -46,7 +46,7 @@
|
|
|
46
46
|
* assetTransform: ./utils/svg-theme.mjs
|
|
47
47
|
* stageDir: build/stage
|
|
48
48
|
* clean:
|
|
49
|
-
* extra: [
|
|
49
|
+
* extra: [coverage]
|
|
50
50
|
* lang:
|
|
51
51
|
* sources: lang/*.json
|
|
52
52
|
* deploy:
|
|
@@ -57,6 +57,7 @@
|
|
|
57
57
|
*/
|
|
58
58
|
|
|
59
59
|
import path from "node:path";
|
|
60
|
+
import { fileURLToPath } from "node:url";
|
|
60
61
|
import { loadPackConfig, locateConfigError, packConfigPath } from "./engine/pack-config.mjs";
|
|
61
62
|
|
|
62
63
|
/** Keys the reserved section may declare. */
|
|
@@ -90,6 +91,7 @@ const SECTION_KEYS = [
|
|
|
90
91
|
export const DERIVED_MANIFEST_KEYS = Object.freeze({
|
|
91
92
|
id: "`foundryPackage`, itself derived from package.json `name`",
|
|
92
93
|
version: "package.json `version`",
|
|
94
|
+
description: "package.json `description`",
|
|
93
95
|
url: "package.json `repository`",
|
|
94
96
|
bugs: "package.json `repository`",
|
|
95
97
|
manifest: "package.json `repository` and the release tag",
|
|
@@ -565,6 +567,89 @@ function normalizeExceptions(value, field, where) {
|
|
|
565
567
|
* collections, as collection → source directory.
|
|
566
568
|
*/
|
|
567
569
|
|
|
570
|
+
/**
|
|
571
|
+
* The asset transforms this package ships, by the name a consumer writes.
|
|
572
|
+
*
|
|
573
|
+
* `packageBuild.assetTransform` takes either one of these names or a path to a
|
|
574
|
+
* module of the consumer's own. A name is the answer where every package wants
|
|
575
|
+
* the same behaviour — theming an icon to the reader's colour scheme is not a
|
|
576
|
+
* per-package decision, and a copy in each consumer is a copy that drifts.
|
|
577
|
+
*
|
|
578
|
+
* @type {Readonly<Record<string, string>>}
|
|
579
|
+
*/
|
|
580
|
+
export const BUILT_IN_ASSET_TRANSFORMS = Object.freeze({
|
|
581
|
+
"svg-theme": "./engine/svg-theme.mjs",
|
|
582
|
+
});
|
|
583
|
+
|
|
584
|
+
/**
|
|
585
|
+
* Where a declared `assetTransform` is loaded from.
|
|
586
|
+
*
|
|
587
|
+
* A built-in name resolves to the module this package ships. Anything else is a
|
|
588
|
+
* path, resolved against the repository root the way it always was — so a
|
|
589
|
+
* consumer with a transform of its own is unaffected.
|
|
590
|
+
*
|
|
591
|
+
* @param {string} declared - The authored value.
|
|
592
|
+
* @param {string} rootDir - The repository root.
|
|
593
|
+
* @returns {string} An absolute path to import.
|
|
594
|
+
*/
|
|
595
|
+
export function resolveAssetTransform(declared, rootDir) {
|
|
596
|
+
const builtIn = BUILT_IN_ASSET_TRANSFORMS[declared];
|
|
597
|
+
if (builtIn) {
|
|
598
|
+
return path.resolve(path.dirname(fileURLToPath(import.meta.url)), builtIn);
|
|
599
|
+
}
|
|
600
|
+
return path.resolve(rootDir, declared);
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
/**
|
|
604
|
+
* `package.json`'s `homepage`, checked against the address it must be.
|
|
605
|
+
*
|
|
606
|
+
* A package's Foundry manifest already derives its own `url` from
|
|
607
|
+
* `contentPackage` (`packageHomepage` in `manifest.mjs`); `homepage` states
|
|
608
|
+
* the same address a second time, in `package.json`, for the generated Hugo
|
|
609
|
+
* configuration to read a `baseURL` from without knowing where each
|
|
610
|
+
* repository keeps its own site configuration.
|
|
611
|
+
*
|
|
612
|
+
* Required unconditionally: every package publishes a site, so there is no
|
|
613
|
+
* package this does not apply to.
|
|
614
|
+
*
|
|
615
|
+
* **Not called by {@link resolvePackageBuildConfig}.** Every packaging
|
|
616
|
+
* command — `clean`, `deploy`, `manifest` and the rest — resolves through it,
|
|
617
|
+
* and none of them reads `homepage`: the Foundry manifest's own `url` is
|
|
618
|
+
* `packageHomepage(contentPackage)`, independent of it. The right caller is
|
|
619
|
+
* whatever reads `homepage` to write a site's `baseURL`.
|
|
620
|
+
*
|
|
621
|
+
* @param {string|null} homepage - The resolved `package.json` `homepage`, or
|
|
622
|
+
* `null` when none is declared.
|
|
623
|
+
* @param {string} contentPackage - The resolved `contentPackage`.
|
|
624
|
+
* @returns {void}
|
|
625
|
+
*/
|
|
626
|
+
export function checkHomepage(homepage, contentPackage) {
|
|
627
|
+
if (homepage === null) {
|
|
628
|
+
fail(
|
|
629
|
+
"homepage",
|
|
630
|
+
"is not declared in `package.json`, and every package needs one " +
|
|
631
|
+
"to build its site's `baseURL` from. Add " +
|
|
632
|
+
`\`https://www.heroiclands.org/${contentPackage}/\``,
|
|
633
|
+
);
|
|
634
|
+
}
|
|
635
|
+
|
|
636
|
+
const suffix = `/${contentPackage}/`;
|
|
637
|
+
let pathname;
|
|
638
|
+
try {
|
|
639
|
+
pathname = new URL(homepage).pathname;
|
|
640
|
+
} catch {
|
|
641
|
+
fail("homepage", `is \`${homepage}\`, which is not an absolute URL`);
|
|
642
|
+
}
|
|
643
|
+
if (!pathname.endsWith(suffix)) {
|
|
644
|
+
fail(
|
|
645
|
+
"homepage",
|
|
646
|
+
`is \`${homepage}\`, but \`contentPackage\` is \`${contentPackage}\` — ` +
|
|
647
|
+
`a package's site is served at \`https://www.heroiclands.org${suffix}\`, ` +
|
|
648
|
+
`so \`package.json\`'s \`homepage\` must end \`${suffix}\``,
|
|
649
|
+
);
|
|
650
|
+
}
|
|
651
|
+
}
|
|
652
|
+
|
|
568
653
|
/**
|
|
569
654
|
* Resolve a package-build configuration from an already-loaded shared one.
|
|
570
655
|
*
|
|
@@ -703,9 +788,9 @@ export function resolvePackageBuildConfig(shared) {
|
|
|
703
788
|
assetTransform:
|
|
704
789
|
section.assetTransform === undefined ?
|
|
705
790
|
null
|
|
706
|
-
:
|
|
707
|
-
shared.rootDir,
|
|
791
|
+
: resolveAssetTransform(
|
|
708
792
|
requireNonEmptyString(section.assetTransform, "packageBuild.assetTransform"),
|
|
793
|
+
shared.rootDir,
|
|
709
794
|
),
|
|
710
795
|
manifest: normalizeManifest(section.manifest),
|
|
711
796
|
manifestFlags:
|