@heroiclands/package-build 22.1.1 → 22.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 +10 -0
- package/bin/package-build.mjs +9 -26
- package/config.mjs +7 -2
- package/content-config.mjs +31 -1
- package/docs/api.md +1 -0
- package/docs/commands.md +22 -22
- package/docs/configuration.md +62 -20
- package/engine/pack-config.mjs +14 -1
- package/engine/schema-check.mjs +6 -6
- package/engine/site-config.mjs +22 -22
- package/manifest.mjs +16 -10
- package/package.json +1 -1
- package/release.mjs +41 -8
- package/types/config.d.mts +17 -0
- package/types/engine/schema-check.d.mts +5 -5
- package/types/engine/site-config.d.mts +6 -8
- package/types/manifest.d.mts +5 -3
- package/types/release.d.mts +7 -6
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
# @heroiclands/package-build
|
|
2
2
|
|
|
3
|
+
## 22.2.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 241451e: **`schema.json` is a release asset, not a committed file.** `package-build schema` writes `build/schema.json` instead of the repository root, and `--check` is gone — there is no committed copy left to compare against. `package-build release` publishes `schema.json` beside the archive and the manifest, the way it already publishes the content index, whenever the staged tree carries one.
|
|
8
|
+
|
|
9
|
+
To keep publishing a schema: drop the committed `schema.json` and any lint step that runs `package-build schema --check`; add `{ from: build/schema.json, to: schema.json }` to `packageBuild.assets`, and run `package-build schema` before `package-build assets` in the build chain so the release never ships one older than the source it was cut from.
|
|
10
|
+
- 483b630: **`content-build site` refuses to generate a configuration with no `site.assets`.** The theme resolves every relative asset — the brand logo, the 404 hero, every CDN-resolved image — against `site.assets`, and there is no defensible default: a package that built a site with the key absent published every one of those as a broken relative path. Declare `site.assets` — the `https://` host every package's imagery is served from — in `package-build.config.yaml` for any package that publishes a site.
|
|
11
|
+
- 985f599: **A package now describes itself once for Foundry and once for the site.** Foundry's package browser wants a pitch — HTML, any length — and a site's `<meta name="description">` wants one plain sentence; deriving both from `package.json`'s `description` forced one string onto both. Declare `packageBuild.manifest.descriptionHtml` in `package-build.config.yaml` for the Foundry pitch (HTML allowed, emitted as the manifest's `description`) and `site.description` for the site's meta description (plain text, required for `content-build site`). `package.json`'s own `description` is read by neither any more — a warning names both keys when one is still declared — and the field can be deleted.
|
|
12
|
+
|
|
3
13
|
## 22.1.1
|
|
4
14
|
|
|
5
15
|
### Patch Changes
|
package/bin/package-build.mjs
CHANGED
|
@@ -334,9 +334,11 @@ async function formatGenerated(text, filepath) {
|
|
|
334
334
|
* so the system publishes the field sets as data — the same shape the link
|
|
335
335
|
* manifest already uses for addresses.
|
|
336
336
|
*
|
|
337
|
-
*
|
|
338
|
-
*
|
|
339
|
-
*
|
|
337
|
+
* Writes under `build/`, alongside every other generated artifact: a release
|
|
338
|
+
* publishes it beside the archive and the manifest (see `package-build
|
|
339
|
+
* release`), and `packageBuild.assets` carries it into the staged tree from
|
|
340
|
+
* there. There is nothing committed to compare it against, so there is no
|
|
341
|
+
* `--check`.
|
|
340
342
|
*
|
|
341
343
|
* @returns {object} The yargs command module.
|
|
342
344
|
*/
|
|
@@ -344,15 +346,8 @@ function schemaCommand() {
|
|
|
344
346
|
return {
|
|
345
347
|
command: "schema",
|
|
346
348
|
describe: "Publish this package's DataModel field sets as schema.json",
|
|
347
|
-
builder: (y) =>
|
|
348
|
-
|
|
349
|
-
type: "boolean",
|
|
350
|
-
default: false,
|
|
351
|
-
describe:
|
|
352
|
-
"Fail when the committed schema.json is out of date " +
|
|
353
|
-
"rather than rewriting it",
|
|
354
|
-
}),
|
|
355
|
-
handler: handler(async (argv) => {
|
|
349
|
+
builder: (y) => y,
|
|
350
|
+
handler: handler(async () => {
|
|
356
351
|
const config = loadPackageBuildConfig();
|
|
357
352
|
if (!config.schema.length) {
|
|
358
353
|
console.log(
|
|
@@ -369,25 +364,13 @@ function schemaCommand() {
|
|
|
369
364
|
version: pkg.version,
|
|
370
365
|
});
|
|
371
366
|
|
|
372
|
-
const out = path.join(config.rootDir, SCHEMA_ARTIFACT_FILE);
|
|
367
|
+
const out = path.join(config.rootDir, "build", SCHEMA_ARTIFACT_FILE);
|
|
373
368
|
const text = await formatGenerated(JSON.stringify(artifact), out);
|
|
374
369
|
const counts = Object.entries(artifact.documents)
|
|
375
370
|
.map(([kind, subtypes]) => `${Object.keys(subtypes).length} ${kind}`)
|
|
376
371
|
.join(", ");
|
|
377
372
|
|
|
378
|
-
|
|
379
|
-
const current = fs.existsSync(out) ? fs.readFileSync(out, "utf8") : null;
|
|
380
|
-
if (current !== text) {
|
|
381
|
-
die(
|
|
382
|
-
`${SCHEMA_ARTIFACT_FILE} does not match what this ` +
|
|
383
|
-
`package's data models would produce — regenerate ` +
|
|
384
|
-
`it with \`package-build schema\`.`,
|
|
385
|
-
);
|
|
386
|
-
}
|
|
387
|
-
console.log(`✅ ${SCHEMA_ARTIFACT_FILE} is up to date ` + `(${counts} subtypes).`);
|
|
388
|
-
return;
|
|
389
|
-
}
|
|
390
|
-
|
|
373
|
+
fs.mkdirSync(path.dirname(out), { recursive: true });
|
|
391
374
|
fs.writeFileSync(out, text, "utf8");
|
|
392
375
|
console.log(
|
|
393
376
|
`✅ Wrote ${SCHEMA_ARTIFACT_FILE} for ${artifact.system} ` +
|
package/config.mjs
CHANGED
|
@@ -91,7 +91,7 @@ const SECTION_KEYS = [
|
|
|
91
91
|
export const DERIVED_MANIFEST_KEYS = Object.freeze({
|
|
92
92
|
id: "`foundryPackage`, itself derived from package.json `name`",
|
|
93
93
|
version: "package.json `version`",
|
|
94
|
-
description: "
|
|
94
|
+
description: "`packageBuild.manifest.descriptionHtml`",
|
|
95
95
|
url: "package.json `repository`",
|
|
96
96
|
bugs: "package.json `repository`",
|
|
97
97
|
manifest: "package.json `repository` and the release tag",
|
|
@@ -152,11 +152,16 @@ const ARTIFACT_OF_KIND = Object.freeze({
|
|
|
152
152
|
* message, so {@link loadPackageBuildConfig} — the half that knows which file
|
|
153
153
|
* was read — can resolve it to a line and column. This half stays pure.
|
|
154
154
|
*
|
|
155
|
+
* Exported so a sibling module composing a configuration value this module
|
|
156
|
+
* does not itself validate — {@link module:engine/site-config}'s
|
|
157
|
+
* `hugoConfig`, checking `site.assets` — reports through the one helper
|
|
158
|
+
* rather than a second copy.
|
|
159
|
+
*
|
|
155
160
|
* @param {string} where - Dotted path of the offending key.
|
|
156
161
|
* @param {string} problem - What is wrong with it.
|
|
157
162
|
* @returns {never}
|
|
158
163
|
*/
|
|
159
|
-
function fail(where, problem) {
|
|
164
|
+
export function fail(where, problem) {
|
|
160
165
|
throw Object.assign(new TypeError(`package-build config: \`${where}\` ${problem}.`), {
|
|
161
166
|
field: where,
|
|
162
167
|
});
|
package/content-config.mjs
CHANGED
|
@@ -759,6 +759,7 @@ const DOCS_KEYS = ["itemFields"];
|
|
|
759
759
|
const SITE_KEYS = [
|
|
760
760
|
"base",
|
|
761
761
|
"assets",
|
|
762
|
+
"description",
|
|
762
763
|
"packages",
|
|
763
764
|
"sections",
|
|
764
765
|
"readmeSections",
|
|
@@ -1575,6 +1576,33 @@ function normalizeSiteAssets(value) {
|
|
|
1575
1576
|
return assets.replace(/\/+$/, "");
|
|
1576
1577
|
}
|
|
1577
1578
|
|
|
1579
|
+
/**
|
|
1580
|
+
* The site's `<meta name="description">`, plain text.
|
|
1581
|
+
*
|
|
1582
|
+
* Foundry's package browser wants a pitch — HTML, any length,
|
|
1583
|
+
* `packageBuild.manifest.descriptionHtml` — and a `<meta>` tag wants one
|
|
1584
|
+
* plain sentence. The two audiences are different enough that one string
|
|
1585
|
+
* cannot serve both, so this is the site's own, checked for markup rather
|
|
1586
|
+
* than trusted to carry none: a value with a `<` in it is refused, naming
|
|
1587
|
+
* `descriptionHtml` as where markup belongs.
|
|
1588
|
+
*
|
|
1589
|
+
* @param {unknown} value - The configured value, or `undefined`.
|
|
1590
|
+
* @returns {string} The description; `""` when unset.
|
|
1591
|
+
*/
|
|
1592
|
+
function normalizeSiteDescription(value) {
|
|
1593
|
+
if (value === undefined) return "";
|
|
1594
|
+
const description = requireNonEmptyString(value, "site.description");
|
|
1595
|
+
if (description.includes("<")) {
|
|
1596
|
+
fail(
|
|
1597
|
+
"site.description",
|
|
1598
|
+
"contains `<` — this is plain text for the site's " +
|
|
1599
|
+
'`<meta name="description">`; markup belongs in ' +
|
|
1600
|
+
"`packageBuild.manifest.descriptionHtml`",
|
|
1601
|
+
);
|
|
1602
|
+
}
|
|
1603
|
+
return description;
|
|
1604
|
+
}
|
|
1605
|
+
|
|
1578
1606
|
/**
|
|
1579
1607
|
* A map of section name → landing metadata.
|
|
1580
1608
|
*
|
|
@@ -1616,7 +1644,7 @@ export const DERIVED_HUGO_KEYS = Object.freeze({
|
|
|
1616
1644
|
disableKinds: "whether any note in the tree carries `tags:`, which the site walk discovers",
|
|
1617
1645
|
taxonomies: "whether any note in the tree carries `tags:`, which the site walk discovers",
|
|
1618
1646
|
outputs: "whether any note in the tree carries `tags:`, which the site walk discovers",
|
|
1619
|
-
"params.description": "
|
|
1647
|
+
"params.description": "`site.description`",
|
|
1620
1648
|
"params.author": "package.json `author`",
|
|
1621
1649
|
"params.cdnBaseURL": "`site.assets`",
|
|
1622
1650
|
"params.brand": "the organisation's brand links, in `engine/site-config.mjs`",
|
|
@@ -1739,6 +1767,7 @@ function normalizeSite(value) {
|
|
|
1739
1767
|
const empty = Object.freeze({
|
|
1740
1768
|
base: "",
|
|
1741
1769
|
assets: "",
|
|
1770
|
+
description: "",
|
|
1742
1771
|
packages: Object.freeze([]),
|
|
1743
1772
|
sections: Object.freeze({}),
|
|
1744
1773
|
readmeSections: Object.freeze({}),
|
|
@@ -1807,6 +1836,7 @@ function normalizeSite(value) {
|
|
|
1807
1836
|
return Object.freeze({
|
|
1808
1837
|
base: input.base === undefined ? "" : requireNonEmptyString(input.base, "site.base"),
|
|
1809
1838
|
assets: normalizeSiteAssets(input.assets),
|
|
1839
|
+
description: normalizeSiteDescription(input.description),
|
|
1810
1840
|
packages: Object.freeze(packages),
|
|
1811
1841
|
sections: normalizeSectionMap(input.sections, "site.sections"),
|
|
1812
1842
|
readmeSections: normalizeSectionMap(input.readmeSections, "site.readmeSections"),
|
package/docs/api.md
CHANGED
|
@@ -1290,6 +1290,7 @@ console.log(config.stageDir);
|
|
|
1290
1290
|
| `resolvePackageBuildConfig` | `resolvePackageBuildConfig(shared)` | `Readonly<PackageBuildConfig>` | validating the `packageBuild:` section from an already-loaded shared configuration — the pure half, usable without touching disk |
|
|
1291
1291
|
| `loadPackageBuildConfig` | `loadPackageBuildConfig()` | `Readonly<PackageBuildConfig>` | reading and validating the repository's resolved package-build configuration from disk, read fresh on each call rather than cached at import |
|
|
1292
1292
|
| `checkHomepage` | `checkHomepage(homepage, contentPackage)` | `void` | validating a resolved `homepage` against `contentPackage` — called by `content-build site` before the generated `baseURL` is written, not by `resolvePackageBuildConfig` itself |
|
|
1293
|
+
| `fail` | `fail(where, problem)` | `never` | rejecting a configured value from a sibling module, naming the dotted key path it was written under, so the loader can resolve it to a line and column |
|
|
1293
1294
|
|
|
1294
1295
|
## `./prettier`
|
|
1295
1296
|
|
package/docs/commands.md
CHANGED
|
@@ -166,7 +166,7 @@ Publish this package's DataModel field sets as `schema.json`.
|
|
|
166
166
|
**SYNOPSIS**
|
|
167
167
|
|
|
168
168
|
```
|
|
169
|
-
package-build schema
|
|
169
|
+
package-build schema
|
|
170
170
|
```
|
|
171
171
|
|
|
172
172
|
**DESCRIPTION**
|
|
@@ -174,23 +174,20 @@ package-build schema [--check]
|
|
|
174
174
|
Publishes the registries named in `packageBuild.schema` as `schema.json`, read
|
|
175
175
|
by `content-build content-format schema` and, in a consuming package, by
|
|
176
176
|
`content-build lint`'s emitted-versus-declared check. A repository that
|
|
177
|
-
declares no registries has nothing to publish.
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
`
|
|
181
|
-
|
|
177
|
+
declares no registries has nothing to publish. Reads the registries
|
|
178
|
+
`packageBuild.schema` names; writes `build/schema.json`. A release publishes
|
|
179
|
+
it beside the archive and the manifest when `packageBuild.assets` names
|
|
180
|
+
`build/schema.json` (see `package-build release`); a module's
|
|
181
|
+
`content-build deps fetch` keeps the copy from the archive it downloads.
|
|
182
182
|
|
|
183
183
|
**OPTIONS**
|
|
184
184
|
|
|
185
|
-
|
|
186
|
-
| --------- | ------- | ------- | ------------------------------------------------------------------------------ |
|
|
187
|
-
| `--check` | boolean | `false` | Fail when the committed `schema.json` is out of date rather than rewriting it. |
|
|
185
|
+
None.
|
|
188
186
|
|
|
189
187
|
**EXIT STATUS**
|
|
190
188
|
|
|
191
|
-
1
|
|
192
|
-
|
|
193
|
-
`packageBuild.schema` is empty, which logs and publishes nothing.
|
|
189
|
+
1 on any thrown error. Otherwise 0 — including when `packageBuild.schema` is
|
|
190
|
+
empty, which logs and publishes nothing.
|
|
194
191
|
|
|
195
192
|
**EXAMPLES**
|
|
196
193
|
|
|
@@ -202,7 +199,7 @@ package-build: no `packageBuild.schema` declared; nothing to publish.
|
|
|
202
199
|
**SEE ALSO**
|
|
203
200
|
|
|
204
201
|
`content-build content-format schema`, `content-build lint [root]`,
|
|
205
|
-
[Configuration](configuration.md).
|
|
202
|
+
`package-build release`, [Configuration](configuration.md).
|
|
206
203
|
|
|
207
204
|
### `package-build manifest`
|
|
208
205
|
|
|
@@ -670,14 +667,17 @@ package-build release [--no-pdf]
|
|
|
670
667
|
|
|
671
668
|
The artifact name comes from `packageKind` — a system ships as
|
|
672
669
|
`system.json`'s sibling, a module as `module.json`'s — so no repository
|
|
673
|
-
states it a second time.
|
|
674
|
-
(`
|
|
675
|
-
`
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
the
|
|
680
|
-
|
|
670
|
+
states it a second time. It also publishes the content index the manifest
|
|
671
|
+
advertises (`flags.metadataUrl`) and, when the stage carries one,
|
|
672
|
+
`schema.json` — a repository that names `build/schema.json` in
|
|
673
|
+
`packageBuild.assets` gets it released beside the archive; one that does not
|
|
674
|
+
gets none. When the package publishes content (`publish.site: content`), it
|
|
675
|
+
also builds the content-tree book (see `content-build pdf`) and reports it
|
|
676
|
+
alongside the archive; `--no-pdf` skips that step for a release that has a
|
|
677
|
+
tree but does not want the book this time. A book that fails to build is
|
|
678
|
+
reported, never fatal — the archive above is the release regardless. Reads
|
|
679
|
+
the staged package and (for the book) the content tree; writes
|
|
680
|
+
`<artifact>.zip` and, unless skipped, the book, both under `build/dist`.
|
|
681
681
|
|
|
682
682
|
**OPTIONS**
|
|
683
683
|
|
|
@@ -701,7 +701,7 @@ $ package-build release
|
|
|
701
701
|
|
|
702
702
|
**SEE ALSO**
|
|
703
703
|
|
|
704
|
-
`content-build pdf`, [Configuration](configuration.md).
|
|
704
|
+
`content-build pdf`, `package-build schema`, [Configuration](configuration.md).
|
|
705
705
|
|
|
706
706
|
### `package-build deploy <stage>`
|
|
707
707
|
|
package/docs/configuration.md
CHANGED
|
@@ -752,21 +752,22 @@ Any other key under `docs.itemFields` is refused:
|
|
|
752
752
|
|
|
753
753
|
**Type:** object · **Optional** · every key defaults to nothing published:
|
|
754
754
|
|
|
755
|
-
| Key | Type | Default
|
|
756
|
-
| ----------------------- | -------- |
|
|
757
|
-
| `site.base` | string | `""`
|
|
758
|
-
| `site.assets` | string | `""`
|
|
759
|
-
| `site.
|
|
760
|
-
| `site.
|
|
761
|
-
| `site.
|
|
762
|
-
| `site.
|
|
763
|
-
| `site.
|
|
764
|
-
| `site.
|
|
765
|
-
| `site.
|
|
766
|
-
| `site.
|
|
767
|
-
| `site.
|
|
768
|
-
| `site.
|
|
769
|
-
| `site.
|
|
755
|
+
| Key | Type | Default |
|
|
756
|
+
| ----------------------- | -------- | ------------------------------------------- |
|
|
757
|
+
| `site.base` | string | `""` |
|
|
758
|
+
| `site.assets` | string | `""`, but required for `content-build site` |
|
|
759
|
+
| `site.description` | string | `""`, but required for `content-build site` |
|
|
760
|
+
| `site.packages` | string[] | `[]` |
|
|
761
|
+
| `site.sections` | object | `{}` |
|
|
762
|
+
| `site.readmeSections` | object | `{}` |
|
|
763
|
+
| `site.landing` | object | `null` |
|
|
764
|
+
| `site.trees` | array | `[]` |
|
|
765
|
+
| `site.pass` | string | `""` |
|
|
766
|
+
| `site.passOptions` | object | `{}` |
|
|
767
|
+
| `site.backfillSections` | boolean | `false` |
|
|
768
|
+
| `site.list` | object | `{shortcodes: false}` |
|
|
769
|
+
| `site.notfound` | object | `null` |
|
|
770
|
+
| `site.hugo` | object | `{}` |
|
|
770
771
|
|
|
771
772
|
How much of a package reaches the web at all is **not** here — it is
|
|
772
773
|
[`publish.site`](#publish). `site` is framing: what a section is called,
|
|
@@ -785,7 +786,7 @@ published. A `site.out` is refused by name:
|
|
|
785
786
|
|
|
786
787
|
> ``package-build config: `site` must be a mapping.``
|
|
787
788
|
|
|
788
|
-
> ``package-build config: `site.<key>` is not a recognized option (expected one of: base, assets, packages, sections, readmeSections, landing, trees, pass, passOptions, backfillSections, list, notfound, hugo).``
|
|
789
|
+
> ``package-build config: `site.<key>` is not a recognized option (expected one of: base, assets, description, packages, sections, readmeSections, landing, trees, pass, passOptions, backfillSections, list, notfound, hugo).``
|
|
789
790
|
|
|
790
791
|
`site.assets` is the host every package's imagery is served from, and it is
|
|
791
792
|
the one address in this file that is not this repository's own. A note names
|
|
@@ -798,11 +799,30 @@ Absolute, and the trailing slash is trimmed:
|
|
|
798
799
|
|
|
799
800
|
> ``package-build config: `site.assets` must be an absolute `http://` or `https://` address — it is the host every package's imagery is served from, and a relative value resolves against whichever page happens to carry the image.``
|
|
800
801
|
|
|
802
|
+
`content-build site` refuses to generate a configuration with no
|
|
803
|
+
`site.assets` at all — there is no defensible default, because the theme
|
|
804
|
+
resolves every relative asset against it:
|
|
805
|
+
|
|
806
|
+
> ``package-build config: `site.assets` is not declared, and a site build needs one — it is the host every package's imagery is served from, and the theme resolves every relative asset against it.``
|
|
807
|
+
|
|
801
808
|
The generated Hugo configuration carries the same host as
|
|
802
809
|
`params.cdnBaseURL`, which the theme resolves a relative asset path against.
|
|
803
810
|
The two are one value read by two readers: the toolchain emits it into a
|
|
804
811
|
page, and the theme joins it onto anything the toolchain left relative.
|
|
805
812
|
|
|
813
|
+
`site.description` is the site's `<meta name="description">` — one plain
|
|
814
|
+
sentence, distinct from the Foundry package browser's pitch
|
|
815
|
+
([`packageBuild.manifest.descriptionHtml`](#packagebuildmanifest), which
|
|
816
|
+
allows HTML). Required for `content-build site`, the way `packageBuild.manifest.title`
|
|
817
|
+
is:
|
|
818
|
+
|
|
819
|
+
> ``package-build config: `site.description` is not declared, and the site's `<meta name="description">` reads from it.``
|
|
820
|
+
|
|
821
|
+
Markup belongs in `descriptionHtml`, not here — a value containing `<` is
|
|
822
|
+
refused:
|
|
823
|
+
|
|
824
|
+
> ``package-build config: `site.description` contains `<` — this is plain text for the site's `<meta name="description">`; markup belongs in `packageBuild.manifest.descriptionHtml`.``
|
|
825
|
+
|
|
806
826
|
`site.packages` names which content packages' notes the site walks, beyond
|
|
807
827
|
this one's own; `site.pass` names a repository's own body-rewrite bundle
|
|
808
828
|
(the one part of the site contract that is code, exactly as `itemBuilders`
|
|
@@ -968,9 +988,9 @@ in it has one source, and that source is where it is edited:
|
|
|
968
988
|
| `disableKinds` | whether any note in the tree carries `tags:`, which the site walk discovers: `["taxonomy", "term", "RSS"]` when none does, `["RSS"]` when at least one does |
|
|
969
989
|
| `taxonomies` | the same fact — written as `{ tag = "tags" }` when at least one note carries `tags:`, absent otherwise |
|
|
970
990
|
| `outputs` | the same fact — written as `{ taxonomy = ["HTML"], term = ["HTML"] }` when at least one note carries `tags:`, absent otherwise |
|
|
971
|
-
| `params.description` | `
|
|
991
|
+
| `params.description` | `site.description`, which is required |
|
|
972
992
|
| `params.author` | `package.json` `author`, its `name`; absent when the package declares none |
|
|
973
|
-
| `params.cdnBaseURL` | `site.assets
|
|
993
|
+
| `params.cdnBaseURL` | `site.assets`, which is required |
|
|
974
994
|
| `params.brand` | the organisation's brand links — `logo`, `licenseURL`, `discordURL` — in `engine/site-config.mjs` |
|
|
975
995
|
| `params.list` | `site.list` |
|
|
976
996
|
| `params.notfound` | `site.notfound`; absent when undeclared |
|
|
@@ -991,6 +1011,17 @@ none fails the site build:
|
|
|
991
1011
|
|
|
992
1012
|
> ``package-build config: `packageBuild.manifest.title` is not declared, and the site's `title` reads from it.``
|
|
993
1013
|
|
|
1014
|
+
`params.description` reads from `site.description` the same way, and fails
|
|
1015
|
+
the same way when it is absent:
|
|
1016
|
+
|
|
1017
|
+
> ``package-build config: `site.description` is not declared, and the site's `<meta name="description">` reads from it.``
|
|
1018
|
+
|
|
1019
|
+
`params.cdnBaseURL` reads from `site.assets`, and the theme resolves every
|
|
1020
|
+
relative asset against it, so a configuration declaring none fails the site
|
|
1021
|
+
build the same way:
|
|
1022
|
+
|
|
1023
|
+
> ``package-build config: `site.assets` is not declared, and a site build needs one — it is the host every package's imagery is served from, and the theme resolves every relative asset against it.``
|
|
1024
|
+
|
|
994
1025
|
Nothing else is emitted. A site whose notes carry no `tags:` publishes no
|
|
995
1026
|
taxonomy pages — `[taxonomies]` and `[outputs]` go unwritten, and Hugo's
|
|
996
1027
|
defaults never apply because `taxonomy` and `term` are disabled kinds. A site
|
|
@@ -1436,11 +1467,22 @@ that has a wrong answer rather than an unknown one: a key the build
|
|
|
1436
1467
|
silently overwritten and the two would be free to disagree with nothing to
|
|
1437
1468
|
say so.
|
|
1438
1469
|
|
|
1470
|
+
`packageBuild.manifest.descriptionHtml` is the exception worth calling out on
|
|
1471
|
+
its own: it is not forbidden, it **is** how `description` is authored. It is
|
|
1472
|
+
the pitch Foundry's package browser shows — HTML allowed, any length — and it
|
|
1473
|
+
is emitted into the generated manifest as `description`; the key itself never
|
|
1474
|
+
survives into the manifest under its own name. `package.json`'s own
|
|
1475
|
+
`description` is read by neither this nor the site (see
|
|
1476
|
+
[`site.description`](#site)) — a declared one is reported as a warning naming
|
|
1477
|
+
both real keys, so it cannot drift back into use:
|
|
1478
|
+
|
|
1479
|
+
> `package.json: warning: \`description\` is read by nothing; the Foundry pitch is \`packageBuild.manifest.descriptionHtml\` and the site's is \`site.description\`` — a JSON manifest carries no line to point at, so only the file is named.
|
|
1480
|
+
|
|
1439
1481
|
| Forbidden key | Derived from |
|
|
1440
1482
|
| ------------------------------------- | ---------------------------------------------------------------- |
|
|
1441
1483
|
| `packageBuild.manifest.id` | `foundryPackage`, itself derived from `package.json` `name` |
|
|
1442
1484
|
| `packageBuild.manifest.version` | `package.json` `version` |
|
|
1443
|
-
| `packageBuild.manifest.description` | `
|
|
1485
|
+
| `packageBuild.manifest.description` | `packageBuild.manifest.descriptionHtml` |
|
|
1444
1486
|
| `packageBuild.manifest.url` | `package.json` `repository` |
|
|
1445
1487
|
| `packageBuild.manifest.bugs` | `package.json` `repository` |
|
|
1446
1488
|
| `packageBuild.manifest.manifest` | `package.json` `repository` and the release tag |
|
|
@@ -1451,7 +1493,7 @@ say so.
|
|
|
1451
1493
|
|
|
1452
1494
|
> ``package-build config: `packageBuild.manifest.version` is derived from package.json `version` and must not be declared — it would be overwritten, and the two would disagree with nothing to say so.``
|
|
1453
1495
|
|
|
1454
|
-
> ``package-build config: `packageBuild.manifest.description` is derived from
|
|
1496
|
+
> ``package-build config: `packageBuild.manifest.description` is derived from `packageBuild.manifest.descriptionHtml` and must not be declared — it would be overwritten, and the two would disagree with nothing to say so.``
|
|
1455
1497
|
|
|
1456
1498
|
> ``package-build config: `packageBuild.manifest` must be a mapping.``
|
|
1457
1499
|
|
package/engine/pack-config.mjs
CHANGED
|
@@ -570,9 +570,22 @@ export function configFromData(data, configPath) {
|
|
|
570
570
|
);
|
|
571
571
|
}
|
|
572
572
|
}
|
|
573
|
-
const { pkg } = readPackageJson(rootDir);
|
|
573
|
+
const { manifestPath, pkg } = readPackageJson(rootDir);
|
|
574
574
|
if (pkg.homepage !== undefined) input.homepage = pkg.homepage;
|
|
575
575
|
if (pkg.author !== undefined) input.author = pkg.author;
|
|
576
|
+
// `description` is npm metadata nothing displays for a private package —
|
|
577
|
+
// read by neither the manifest nor the site, so a declared one is a
|
|
578
|
+
// warning rather than a silent no-op that looks like it did something.
|
|
579
|
+
if (pkg.description !== undefined) {
|
|
580
|
+
emitDiagnostic({
|
|
581
|
+
file: manifestPath,
|
|
582
|
+
severity: "warning",
|
|
583
|
+
message:
|
|
584
|
+
"`description` is read by nothing; the Foundry pitch is " +
|
|
585
|
+
"`packageBuild.manifest.descriptionHtml` and the site's is " +
|
|
586
|
+
"`site.description`",
|
|
587
|
+
});
|
|
588
|
+
}
|
|
576
589
|
|
|
577
590
|
if (input.itemBuilders !== undefined) {
|
|
578
591
|
const declared = input.itemBuilders;
|
package/engine/schema-check.mjs
CHANGED
|
@@ -466,8 +466,8 @@ export function compareEmittedSystem({
|
|
|
466
466
|
* Two places to find it, because a system checks itself against source it owns
|
|
467
467
|
* while a module checks against a dependency it fetched:
|
|
468
468
|
*
|
|
469
|
-
* - **A system**: its own `schema.json`, generated from its `src/`
|
|
470
|
-
*
|
|
469
|
+
* - **A system**: its own `schema.json`, generated from its `src/` into
|
|
470
|
+
* `build/` by `package-build schema`.
|
|
471
471
|
* - **A module**: the copy cached by `content-build deps fetch`, from the
|
|
472
472
|
* archive of the version it pins — which is what makes the comparison happen
|
|
473
473
|
* at `verified` rather than against whatever the system's `main` holds today.
|
|
@@ -505,7 +505,7 @@ export function resolveSchemaArtifact(config, system = undefined) {
|
|
|
505
505
|
|
|
506
506
|
// The system checking itself, against the schema its own build published.
|
|
507
507
|
if (config.packageKind === "systems" && config.foundryPackage === systemId) {
|
|
508
|
-
const own = path.join(config.rootDir, SCHEMA_ARTIFACT_FILE);
|
|
508
|
+
const own = path.join(config.rootDir, "build", SCHEMA_ARTIFACT_FILE);
|
|
509
509
|
return fs.existsSync(own) ? read(own) : null;
|
|
510
510
|
}
|
|
511
511
|
|
|
@@ -683,9 +683,9 @@ export function checkAuthoredSystemData(
|
|
|
683
683
|
* subtype declares.
|
|
684
684
|
*
|
|
685
685
|
* The build-time face of {@link compareEmittedSystem}: it resolves the schema
|
|
686
|
-
* the way every other check here does — the system's own
|
|
687
|
-
* the cached one from the release a module pins — and attaches the message
|
|
688
|
-
* reader sees.
|
|
686
|
+
* the way every other check here does — the system's own published artifact,
|
|
687
|
+
* or the cached one from the release a module pins — and attaches the message
|
|
688
|
+
* a reader sees.
|
|
689
689
|
*
|
|
690
690
|
* **Silent where there is nothing to check against**, exactly as its two
|
|
691
691
|
* siblings are: a module pinning a system version released before the artifact
|
package/engine/site-config.mjs
CHANGED
|
@@ -44,7 +44,7 @@ import fs from "node:fs";
|
|
|
44
44
|
import path from "node:path";
|
|
45
45
|
import { stringify as stringifyToml } from "smol-toml";
|
|
46
46
|
|
|
47
|
-
import { checkHomepage } from "../config.mjs";
|
|
47
|
+
import { checkHomepage, fail } from "../config.mjs";
|
|
48
48
|
import { slugify } from "./content-slug.mjs";
|
|
49
49
|
|
|
50
50
|
/** The Hugo source directory, relative to the repository root. */
|
|
@@ -381,7 +381,6 @@ function deepMerge(base, overrides) {
|
|
|
381
381
|
*
|
|
382
382
|
* @param {object} options - The sources.
|
|
383
383
|
* @param {object} options.config - The resolved build configuration.
|
|
384
|
-
* @param {string} [options.description] - `package.json`'s `description`.
|
|
385
384
|
* @param {readonly NavigationEntry[]} options.navigation - The navigation.
|
|
386
385
|
* @param {string} options.themesDir - From {@link resolveThemesDir}.
|
|
387
386
|
* @param {boolean} [options.hasTags] - Whether any note the site build walked
|
|
@@ -389,9 +388,10 @@ function deepMerge(base, overrides) {
|
|
|
389
388
|
* `hasTags`. Defaults to `false` — no tagged note, no taxonomy pages.
|
|
390
389
|
* @returns {Record<string, any>} The configuration Hugo reads.
|
|
391
390
|
* @throws {TypeError} When `homepage` fails `checkHomepage`, or the
|
|
392
|
-
* configuration declares no `packageBuild.manifest.title
|
|
391
|
+
* configuration declares no `packageBuild.manifest.title`, no
|
|
392
|
+
* `site.description`, or no `site.assets`.
|
|
393
393
|
*/
|
|
394
|
-
export function hugoConfig({ config,
|
|
394
|
+
export function hugoConfig({ config, navigation, themesDir, hasTags = false }) {
|
|
395
395
|
checkHomepage(config.homepage, config.contentPackage);
|
|
396
396
|
|
|
397
397
|
const title = config.packageBuild?.manifest?.title;
|
|
@@ -401,10 +401,23 @@ export function hugoConfig({ config, description, navigation, themesDir, hasTags
|
|
|
401
401
|
"and the site's `title` reads from it.",
|
|
402
402
|
);
|
|
403
403
|
}
|
|
404
|
+
if (!config.site.description) {
|
|
405
|
+
fail(
|
|
406
|
+
"site.description",
|
|
407
|
+
'is not declared, and the site\'s `<meta name="description">` reads from it',
|
|
408
|
+
);
|
|
409
|
+
}
|
|
410
|
+
if (!config.site.assets) {
|
|
411
|
+
fail(
|
|
412
|
+
"site.assets",
|
|
413
|
+
"is not declared, and a site build needs one — it is the host every " +
|
|
414
|
+
"package's imagery is served from, and the theme resolves every " +
|
|
415
|
+
"relative asset against it",
|
|
416
|
+
);
|
|
417
|
+
}
|
|
404
418
|
|
|
405
419
|
/** @type {Record<string, unknown>} */
|
|
406
|
-
const params = {};
|
|
407
|
-
if (typeof description === "string" && description.trim()) params.description = description;
|
|
420
|
+
const params = { description: config.site.description };
|
|
408
421
|
if (config.author?.name) params.author = config.author.name;
|
|
409
422
|
if (config.site.assets) params.cdnBaseURL = config.site.assets;
|
|
410
423
|
params.brand = { ...BRAND };
|
|
@@ -449,24 +462,12 @@ export function hugoToml(generated) {
|
|
|
449
462
|
);
|
|
450
463
|
}
|
|
451
464
|
|
|
452
|
-
/**
|
|
453
|
-
* `package.json`'s `description`, or `undefined` when it declares none.
|
|
454
|
-
*
|
|
455
|
-
* @param {string} rootDir - The repository root.
|
|
456
|
-
* @returns {string|undefined} The description.
|
|
457
|
-
*/
|
|
458
|
-
function packageDescription(rootDir) {
|
|
459
|
-
const pkg = JSON.parse(fs.readFileSync(path.join(rootDir, "package.json"), "utf8"));
|
|
460
|
-
return typeof pkg.description === "string" ? pkg.description : undefined;
|
|
461
|
-
}
|
|
462
|
-
|
|
463
465
|
/**
|
|
464
466
|
* The Hugo configuration, every source read from the repository.
|
|
465
467
|
*
|
|
466
|
-
* Reads
|
|
467
|
-
*
|
|
468
|
-
*
|
|
469
|
-
* intact.
|
|
468
|
+
* Reads the cached navigation and the installed theme's location, and
|
|
469
|
+
* composes them with {@link hugoConfig}. Nothing is written, so a caller can
|
|
470
|
+
* run this before touching the output tree and fail with it intact.
|
|
470
471
|
*
|
|
471
472
|
* @param {object} config - The resolved build configuration.
|
|
472
473
|
* @param {object} [options] - Options.
|
|
@@ -480,7 +481,6 @@ function packageDescription(rootDir) {
|
|
|
480
481
|
export function generateHugoConfig(config, { hasTags = false } = {}) {
|
|
481
482
|
return hugoConfig({
|
|
482
483
|
config,
|
|
483
|
-
description: packageDescription(config.rootDir),
|
|
484
484
|
navigation: readCachedNavigation(config),
|
|
485
485
|
themesDir: resolveThemesDir(config.rootDir),
|
|
486
486
|
hasTags,
|
package/manifest.mjs
CHANGED
|
@@ -28,10 +28,12 @@
|
|
|
28
28
|
*
|
|
29
29
|
* - **Declared** — the `packageBuild.manifest` block, emitted unchanged, so a
|
|
30
30
|
* key Foundry adds in a later version needs no release of this package.
|
|
31
|
-
*
|
|
32
|
-
*
|
|
33
|
-
*
|
|
34
|
-
*
|
|
31
|
+
* `descriptionHtml` is the one exception, folded into the derived
|
|
32
|
+
* `description` below rather than surviving under its own name.
|
|
33
|
+
* - **Derived** — the identity, the description (from `descriptionHtml`), the
|
|
34
|
+
* version, the release addresses, the compatibility ranges and the pack
|
|
35
|
+
* list. Declaring `description` directly is an error rather than an
|
|
36
|
+
* override: the authored copy would be silently overwritten.
|
|
35
37
|
* - **Computed** — namespaced `flags` a repository works out for itself.
|
|
36
38
|
*
|
|
37
39
|
* **Nothing here invents an address.** The repository URL is read from
|
|
@@ -459,11 +461,13 @@ function withoutBuildKeys(entry) {
|
|
|
459
461
|
* Three kinds of key end up in the result:
|
|
460
462
|
*
|
|
461
463
|
* - **Declared** — everything in `packageBuild.manifest`, emitted unchanged, so
|
|
462
|
-
* a key Foundry adds later needs no release of this package.
|
|
464
|
+
* a key Foundry adds later needs no release of this package. The one
|
|
465
|
+
* exception is `descriptionHtml`, folded into the description below rather
|
|
466
|
+
* than surviving under its own name.
|
|
463
467
|
* - **Derived** — the identity, the description, the release addresses, the
|
|
464
468
|
* version, the Foundry and system compatibility ranges, and the pack list.
|
|
465
|
-
* These are refused if also declared
|
|
466
|
-
* and the two would disagree with nothing to say so.
|
|
469
|
+
* These are refused if also declared (`description` directly; `descriptionHtml`
|
|
470
|
+
* is how it is authored) and the two would disagree with nothing to say so.
|
|
467
471
|
* - **Computed** — namespaced `flags` a repository works out for itself, merged
|
|
468
472
|
* over any it declared.
|
|
469
473
|
*
|
|
@@ -479,7 +483,9 @@ function withoutBuildKeys(entry) {
|
|
|
479
483
|
* @returns {object} The manifest, ready to serialise.
|
|
480
484
|
*/
|
|
481
485
|
export function buildManifest({ config, packageJson, artifact, flags }) {
|
|
482
|
-
|
|
486
|
+
// `descriptionHtml` is the authored source of `description` — pulled out
|
|
487
|
+
// so it never survives the spread below under its own name.
|
|
488
|
+
const { descriptionHtml, ...declared } = config.packageBuild?.manifest ?? {};
|
|
483
489
|
const repoUrl = normalizeRepoUrl(packageJson.repository);
|
|
484
490
|
|
|
485
491
|
const derived = {
|
|
@@ -495,8 +501,8 @@ export function buildManifest({ config, packageJson, artifact, flags }) {
|
|
|
495
501
|
};
|
|
496
502
|
// Own-property presence, not just value, decides whether a key survives
|
|
497
503
|
// into `ordered` below — an explicit `undefined` would still occupy a slot
|
|
498
|
-
// in it. Set only when
|
|
499
|
-
if (
|
|
504
|
+
// in it. Set only when the repository actually declares one.
|
|
505
|
+
if (descriptionHtml !== undefined) derived.description = descriptionHtml;
|
|
500
506
|
if (config.compatibility) derived.compatibility = config.compatibility;
|
|
501
507
|
|
|
502
508
|
// `requiresSystem` is the gate half of the declare/require split. It
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@heroiclands/package-build",
|
|
3
|
-
"version": "22.
|
|
3
|
+
"version": "22.2.0",
|
|
4
4
|
"description": "Shared toolchain for building and shipping a HeroicLands Foundry VTT package — content compilation, manifest, localization, staging, bundle, release and deployment.",
|
|
5
5
|
"license": "GPL-3.0-or-later",
|
|
6
6
|
"type": "module",
|
package/release.mjs
CHANGED
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
|
|
14
14
|
/**
|
|
15
15
|
* The release archive — the two files a Foundry package's GitHub Release
|
|
16
|
-
* carries.
|
|
16
|
+
* carries, plus the optional siblings a repository opts into.
|
|
17
17
|
*
|
|
18
18
|
* Foundry installs a package by fetching the `download` URL its manifest
|
|
19
19
|
* advertises, so a release publishes `<artifact>.zip`, the whole staged tree,
|
|
@@ -21,7 +21,10 @@
|
|
|
21
21
|
* re-fetches to notice a new version. A package that ships content publishes a
|
|
22
22
|
* third: the content index other packages resolve its addresses through,
|
|
23
23
|
* named by the `flags.metadataUrl` the manifest advertises. Every name is fixed
|
|
24
|
-
* by what the manifest says, not chosen here — see `manifest.mjs`.
|
|
24
|
+
* by what the manifest says, not chosen here — see `manifest.mjs`. A package
|
|
25
|
+
* that publishes a DataModel schema (`packageBuild.schema`, staged through
|
|
26
|
+
* `packageBuild.assets`) gets a fourth: `schema.json`, published whenever the
|
|
27
|
+
* stage carries one and silently skipped otherwise.
|
|
25
28
|
*
|
|
26
29
|
* Kept apart from `stage.mjs` because this is the only part of assembling a
|
|
27
30
|
* package that needs a dependency. A repository that never cuts a release from
|
|
@@ -40,6 +43,8 @@ import path from "node:path";
|
|
|
40
43
|
// release job came to fail before a single byte was written.
|
|
41
44
|
import { ZipArchive } from "archiver";
|
|
42
45
|
|
|
46
|
+
import { SCHEMA_ARTIFACT_FILE } from "./engine/foreign-catalog.mjs";
|
|
47
|
+
|
|
43
48
|
/**
|
|
44
49
|
* Zip the staged tree and place the manifest beside the archive.
|
|
45
50
|
*
|
|
@@ -60,14 +65,14 @@ import { ZipArchive } from "archiver";
|
|
|
60
65
|
* @param {boolean} [opts.pdf] - Whether to build the book that ships beside the
|
|
61
66
|
* archive. `true` by default; `false` skips the build and reports the skip.
|
|
62
67
|
* @returns {Promise<{zip: string, manifest: string, metadata?: string,
|
|
63
|
-
* pdf?: string, pdfFindings: object[], pdfSkipped: string|null,
|
|
68
|
+
* schema?: string, pdf?: string, pdfFindings: object[], pdfSkipped: string|null,
|
|
64
69
|
* bytes: number, version: string}>} The paths written, what the book build
|
|
65
70
|
* found, the archive's size, and the version the manifest declares.
|
|
66
|
-
* `metadata` is absent when the manifest advertises no content index,
|
|
67
|
-
* `
|
|
68
|
-
*
|
|
69
|
-
*
|
|
70
|
-
* `pdfFindings`.
|
|
71
|
+
* `metadata` is absent when the manifest advertises no content index,
|
|
72
|
+
* `schema` is absent when the stage carries no `schema.json`, and `pdf` is
|
|
73
|
+
* absent when no book was written. `pdfSkipped` is `null` when a book was
|
|
74
|
+
* built, and otherwise the reason none was — itself `null` when the book
|
|
75
|
+
* builder could not be loaded, which is reported through `pdfFindings`.
|
|
71
76
|
* @throws {Error} When the stage has no manifest — there is nothing to release,
|
|
72
77
|
* and an archive without one installs as nothing.
|
|
73
78
|
*/
|
|
@@ -119,6 +124,7 @@ export async function packRelease({
|
|
|
119
124
|
await fsp.copyFile(stagedManifest, path.join(out, manifestName));
|
|
120
125
|
|
|
121
126
|
const metadata = await publishMetadataIndex({ manifest, stage, out, metadataDir });
|
|
127
|
+
const schema = await publishSchemaAsset({ stage, out });
|
|
122
128
|
|
|
123
129
|
// Last, and never fatal: the archive and the manifest are the release, and
|
|
124
130
|
// a book that failed to set is a reported problem rather than a reason to
|
|
@@ -132,6 +138,7 @@ export async function packRelease({
|
|
|
132
138
|
zip: zipPath,
|
|
133
139
|
manifest: path.join(out, manifestName),
|
|
134
140
|
...(metadata ? { metadata } : {}),
|
|
141
|
+
...(schema ? { schema } : {}),
|
|
135
142
|
...(book.pdf ? { pdf: book.pdf } : {}),
|
|
136
143
|
pdfFindings: book.findings,
|
|
137
144
|
pdfSkipped: book.pdf ? null : book.reason,
|
|
@@ -226,3 +233,29 @@ async function publishMetadataIndex({ manifest, stage, out, metadataDir }) {
|
|
|
226
233
|
await fsp.copyFile(found, dest);
|
|
227
234
|
return dest;
|
|
228
235
|
}
|
|
236
|
+
|
|
237
|
+
/**
|
|
238
|
+
* Place the published DataModel schema beside the archive, when the stage
|
|
239
|
+
* carries one.
|
|
240
|
+
*
|
|
241
|
+
* **The stage is the one place this looks.** `package-build schema` writes
|
|
242
|
+
* `build/schema.json`, and a repository that wants it released names it in
|
|
243
|
+
* `packageBuild.assets` (`from: build/schema.json`) the way every other
|
|
244
|
+
* staged file is declared — so a schema at the stage root is a repository
|
|
245
|
+
* that opted in, and its absence is a repository that has not, which is
|
|
246
|
+
* exactly as releasable as one that never adopted the artifact at all.
|
|
247
|
+
*
|
|
248
|
+
* @param {object} opts
|
|
249
|
+
* @param {string} opts.stage - The staged tree.
|
|
250
|
+
* @param {string} opts.out - Where release assets are written.
|
|
251
|
+
* @returns {Promise<string|undefined>} The published path, or nothing when
|
|
252
|
+
* the stage carries no `schema.json`.
|
|
253
|
+
*/
|
|
254
|
+
async function publishSchemaAsset({ stage, out }) {
|
|
255
|
+
const src = path.join(stage, SCHEMA_ARTIFACT_FILE);
|
|
256
|
+
if (!fs.existsSync(src)) return undefined;
|
|
257
|
+
|
|
258
|
+
const dest = path.join(out, SCHEMA_ARTIFACT_FILE);
|
|
259
|
+
await fsp.copyFile(src, dest);
|
|
260
|
+
return dest;
|
|
261
|
+
}
|
package/types/config.d.mts
CHANGED
|
@@ -1,3 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Reject a configured value, naming the key it was written under.
|
|
3
|
+
*
|
|
4
|
+
* The dotted path rides on the error as `field` as well as appearing in the
|
|
5
|
+
* message, so {@link loadPackageBuildConfig} — the half that knows which file
|
|
6
|
+
* was read — can resolve it to a line and column. This half stays pure.
|
|
7
|
+
*
|
|
8
|
+
* Exported so a sibling module composing a configuration value this module
|
|
9
|
+
* does not itself validate — {@link module:engine/site-config}'s
|
|
10
|
+
* `hugoConfig`, checking `site.assets` — reports through the one helper
|
|
11
|
+
* rather than a second copy.
|
|
12
|
+
*
|
|
13
|
+
* @param {string} where - Dotted path of the offending key.
|
|
14
|
+
* @param {string} problem - What is wrong with it.
|
|
15
|
+
* @returns {never}
|
|
16
|
+
*/
|
|
17
|
+
export function fail(where: string, problem: string): never;
|
|
1
18
|
/**
|
|
2
19
|
* Where a declared `assetTransform` is loaded from.
|
|
3
20
|
*
|
|
@@ -157,8 +157,8 @@ export function compareEmittedSystem({ system, artifact, documentType, subtype,
|
|
|
157
157
|
* Two places to find it, because a system checks itself against source it owns
|
|
158
158
|
* while a module checks against a dependency it fetched:
|
|
159
159
|
*
|
|
160
|
-
* - **A system**: its own `schema.json`, generated from its `src/`
|
|
161
|
-
*
|
|
160
|
+
* - **A system**: its own `schema.json`, generated from its `src/` into
|
|
161
|
+
* `build/` by `package-build schema`.
|
|
162
162
|
* - **A module**: the copy cached by `content-build deps fetch`, from the
|
|
163
163
|
* archive of the version it pins — which is what makes the comparison happen
|
|
164
164
|
* at `verified` rather than against whatever the system's `main` holds today.
|
|
@@ -282,9 +282,9 @@ export function checkAuthoredSystemData(fm: object, { block, documentType, subTy
|
|
|
282
282
|
* subtype declares.
|
|
283
283
|
*
|
|
284
284
|
* The build-time face of {@link compareEmittedSystem}: it resolves the schema
|
|
285
|
-
* the way every other check here does — the system's own
|
|
286
|
-
* the cached one from the release a module pins — and attaches the message
|
|
287
|
-
* reader sees.
|
|
285
|
+
* the way every other check here does — the system's own published artifact,
|
|
286
|
+
* or the cached one from the release a module pins — and attaches the message
|
|
287
|
+
* a reader sees.
|
|
288
288
|
*
|
|
289
289
|
* **Silent where there is nothing to check against**, exactly as its two
|
|
290
290
|
* siblings are: a module pinning a system version released before the artifact
|
|
@@ -89,7 +89,6 @@ export function resolveThemesDir(rootDir: string): string;
|
|
|
89
89
|
*
|
|
90
90
|
* @param {object} options - The sources.
|
|
91
91
|
* @param {object} options.config - The resolved build configuration.
|
|
92
|
-
* @param {string} [options.description] - `package.json`'s `description`.
|
|
93
92
|
* @param {readonly NavigationEntry[]} options.navigation - The navigation.
|
|
94
93
|
* @param {string} options.themesDir - From {@link resolveThemesDir}.
|
|
95
94
|
* @param {boolean} [options.hasTags] - Whether any note the site build walked
|
|
@@ -97,11 +96,11 @@ export function resolveThemesDir(rootDir: string): string;
|
|
|
97
96
|
* `hasTags`. Defaults to `false` — no tagged note, no taxonomy pages.
|
|
98
97
|
* @returns {Record<string, any>} The configuration Hugo reads.
|
|
99
98
|
* @throws {TypeError} When `homepage` fails `checkHomepage`, or the
|
|
100
|
-
* configuration declares no `packageBuild.manifest.title
|
|
99
|
+
* configuration declares no `packageBuild.manifest.title`, no
|
|
100
|
+
* `site.description`, or no `site.assets`.
|
|
101
101
|
*/
|
|
102
|
-
export function hugoConfig({ config,
|
|
102
|
+
export function hugoConfig({ config, navigation, themesDir, hasTags }: {
|
|
103
103
|
config: object;
|
|
104
|
-
description?: string | undefined;
|
|
105
104
|
navigation: readonly NavigationEntry[];
|
|
106
105
|
themesDir: string;
|
|
107
106
|
hasTags?: boolean | undefined;
|
|
@@ -116,10 +115,9 @@ export function hugoToml(generated: Record<string, unknown>): string;
|
|
|
116
115
|
/**
|
|
117
116
|
* The Hugo configuration, every source read from the repository.
|
|
118
117
|
*
|
|
119
|
-
* Reads
|
|
120
|
-
*
|
|
121
|
-
*
|
|
122
|
-
* intact.
|
|
118
|
+
* Reads the cached navigation and the installed theme's location, and
|
|
119
|
+
* composes them with {@link hugoConfig}. Nothing is written, so a caller can
|
|
120
|
+
* run this before touching the output tree and fail with it intact.
|
|
123
121
|
*
|
|
124
122
|
* @param {object} config - The resolved build configuration.
|
|
125
123
|
* @param {object} [options] - Options.
|
package/types/manifest.d.mts
CHANGED
|
@@ -187,11 +187,13 @@ export function publishedRelationships(relationships: Record<string, unknown>):
|
|
|
187
187
|
* Three kinds of key end up in the result:
|
|
188
188
|
*
|
|
189
189
|
* - **Declared** — everything in `packageBuild.manifest`, emitted unchanged, so
|
|
190
|
-
* a key Foundry adds later needs no release of this package.
|
|
190
|
+
* a key Foundry adds later needs no release of this package. The one
|
|
191
|
+
* exception is `descriptionHtml`, folded into the description below rather
|
|
192
|
+
* than surviving under its own name.
|
|
191
193
|
* - **Derived** — the identity, the description, the release addresses, the
|
|
192
194
|
* version, the Foundry and system compatibility ranges, and the pack list.
|
|
193
|
-
* These are refused if also declared
|
|
194
|
-
* and the two would disagree with nothing to say so.
|
|
195
|
+
* These are refused if also declared (`description` directly; `descriptionHtml`
|
|
196
|
+
* is how it is authored) and the two would disagree with nothing to say so.
|
|
195
197
|
* - **Computed** — namespaced `flags` a repository works out for itself, merged
|
|
196
198
|
* over any it declared.
|
|
197
199
|
*
|
package/types/release.d.mts
CHANGED
|
@@ -18,14 +18,14 @@
|
|
|
18
18
|
* @param {boolean} [opts.pdf] - Whether to build the book that ships beside the
|
|
19
19
|
* archive. `true` by default; `false` skips the build and reports the skip.
|
|
20
20
|
* @returns {Promise<{zip: string, manifest: string, metadata?: string,
|
|
21
|
-
* pdf?: string, pdfFindings: object[], pdfSkipped: string|null,
|
|
21
|
+
* schema?: string, pdf?: string, pdfFindings: object[], pdfSkipped: string|null,
|
|
22
22
|
* bytes: number, version: string}>} The paths written, what the book build
|
|
23
23
|
* found, the archive's size, and the version the manifest declares.
|
|
24
|
-
* `metadata` is absent when the manifest advertises no content index,
|
|
25
|
-
* `
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
* `pdfFindings`.
|
|
24
|
+
* `metadata` is absent when the manifest advertises no content index,
|
|
25
|
+
* `schema` is absent when the stage carries no `schema.json`, and `pdf` is
|
|
26
|
+
* absent when no book was written. `pdfSkipped` is `null` when a book was
|
|
27
|
+
* built, and otherwise the reason none was — itself `null` when the book
|
|
28
|
+
* builder could not be loaded, which is reported through `pdfFindings`.
|
|
29
29
|
* @throws {Error} When the stage has no manifest — there is nothing to release,
|
|
30
30
|
* and an archive without one installs as nothing.
|
|
31
31
|
*/
|
|
@@ -39,6 +39,7 @@ export function packRelease({ stageDir, outDir, artifact, metadataDir, pdf, }?:
|
|
|
39
39
|
zip: string;
|
|
40
40
|
manifest: string;
|
|
41
41
|
metadata?: string;
|
|
42
|
+
schema?: string;
|
|
42
43
|
pdf?: string;
|
|
43
44
|
pdfFindings: object[];
|
|
44
45
|
pdfSkipped: string | null;
|