@heroiclands/package-build 22.4.2 → 22.4.4
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 +32 -0
- package/CONTENT.md +53 -6
- package/README.md +6 -6
- package/bin/package-build.mjs +68 -18
- package/changelog.cjs +55 -0
- package/content-config.mjs +34 -0
- package/docs/api.md +16 -0
- package/docs/commands.md +70 -2
- package/docs/configuration.md +43 -3
- package/docs/project-setup.md +22 -13
- package/engine/changelog-group.mjs +277 -0
- package/engine/changelog-lint.mjs +188 -9
- package/engine/homepage.mjs +9 -1
- package/engine/related-pages.mjs +161 -0
- package/engine/site-build.mjs +107 -9
- package/engine/site-index.mjs +5 -1
- package/engine/web-wikilinks.mjs +13 -2
- package/package.json +4 -3
- package/types/engine/changelog-group.d.mts +28 -0
- package/types/engine/changelog-lint.d.mts +109 -2
- package/types/engine/homepage.d.mts +8 -1
- package/types/engine/related-pages.d.mts +70 -0
- package/types/engine/site-build.d.mts +36 -3
- package/types/engine/site-index.d.mts +5 -1
- package/types/engine/web-wikilinks.d.mts +11 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,37 @@
|
|
|
1
1
|
# @heroiclands/package-build
|
|
2
2
|
|
|
3
|
+
## 22.4.4
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
Every page a site build publishes carries a `related` block in its front matter — the pages that link to it and the pages it links to, each named once with its title, address and type — which the theme renders as the Related card at the foot of the page. A page nothing connects to carries no block, and the homepage is counted on both sides like any other page.
|
|
8
|
+
|
|
9
|
+
`changelog group` keeps a hard-wrapped paragraph as one block instead of fragmenting it one block per line, and no longer inserts a blank line under a bold sentence a paragraph continues directly beneath.
|
|
10
|
+
|
|
11
|
+
## 22.4.3
|
|
12
|
+
|
|
13
|
+
### Patch Changes
|
|
14
|
+
|
|
15
|
+
**A new `changelog group` command folds a release's changeset blocks together by their bold label.**
|
|
16
|
+
`@heroiclands/package-build/changelog` writes each changeset's summary into `CHANGELOG.md` as its
|
|
17
|
+
own block, so three pull requests each touching compendium content leave three separate
|
|
18
|
+
`**Compendiums**` blocks instead of one. `changelog group` rewrites the newest release section in
|
|
19
|
+
place: every block sharing one label is merged into a single block, bullets kept in their original
|
|
20
|
+
order with an exact duplicate kept once, and the merged blocks are ordered by a new
|
|
21
|
+
`changelog.labels` configuration key — the unlabelled lead paragraph first, then every declared
|
|
22
|
+
label in order, then any undeclared label last with a warning naming it. `changelog check` warns on
|
|
23
|
+
a label absent from a declared `changelog.labels` too, so a drifted spelling is caught before a
|
|
24
|
+
release ships it.
|
|
25
|
+
|
|
26
|
+
**A shipped changelog generator writes a release note without a commit hash.**
|
|
27
|
+
`@heroiclands/package-build/changelog` is a Changesets changelog generator
|
|
28
|
+
whose `getReleaseLine` renders a changeset's summary as a block, verbatim —
|
|
29
|
+
no bullet, no indentation, separated from its neighbours by one blank line —
|
|
30
|
+
and whose `getDependencyReleaseLine` writes nothing. Point
|
|
31
|
+
`.changeset/config.json` at it
|
|
32
|
+
(`"changelog": "@heroiclands/package-build/changelog"`) to stop every release
|
|
33
|
+
note from carrying a commit-hash prefix.
|
|
34
|
+
|
|
3
35
|
## 22.4.2
|
|
4
36
|
|
|
5
37
|
### Patch Changes
|
package/CONTENT.md
CHANGED
|
@@ -2333,6 +2333,53 @@ notes it lists, and link it from the homepage. Nothing is generated between
|
|
|
2333
2333
|
the homepage and the pages, so delete the key.
|
|
2334
2334
|
````
|
|
2335
2335
|
|
|
2336
|
+
### Every page carries what links to it, and what it links to
|
|
2337
|
+
|
|
2338
|
+
The build resolves every wikilink on every page through the address index, so
|
|
2339
|
+
at the moment it writes a page it holds the whole link graph. It writes the
|
|
2340
|
+
part that concerns each page into that page's front matter:
|
|
2341
|
+
|
|
2342
|
+
```yaml
|
|
2343
|
+
related:
|
|
2344
|
+
backlinks: # pages that link to this one
|
|
2345
|
+
- title: Afzandah Parnâzar
|
|
2346
|
+
url: /thalorna/being-afzndhprnzr/
|
|
2347
|
+
type: being
|
|
2348
|
+
mentions: # pages this one links to
|
|
2349
|
+
- title: Kethramír
|
|
2350
|
+
url: /thalorna/place-kthrmr/
|
|
2351
|
+
type: place
|
|
2352
|
+
```
|
|
2353
|
+
|
|
2354
|
+
The theme's `related.html` partial reads both lists and renders them as one
|
|
2355
|
+
"Related" card, grouped by `type`. What the build guarantees about them:
|
|
2356
|
+
|
|
2357
|
+
- **A page appears once per list**, however many times it is linked, and
|
|
2358
|
+
whichever address the link was written to: `docbeing-grod` and `being-grod`
|
|
2359
|
+
are one page, and a page that cites it under both lists it once. A link from
|
|
2360
|
+
a page to itself is not a connection and is dropped.
|
|
2361
|
+
- **Each list is sorted by `type`, then `title`**, so the card's grouping is
|
|
2362
|
+
the same on every build whatever order the notes were walked in.
|
|
2363
|
+
- **Both lists are present whenever either has an entry**, so a theme reads one
|
|
2364
|
+
shape; a page with no links either way carries **no `related` key at all**,
|
|
2365
|
+
which is the partial's silent-disappear convention kept at the source.
|
|
2366
|
+
- **`url` composes `<base><slug>/`**, like every other href this build renders
|
|
2367
|
+
— see [A page's URL is its address](#a-pages-url-is-its-address). The page
|
|
2368
|
+
states its own address as `/<slug>/`; everything pointing _at_ it, this
|
|
2369
|
+
included, carries the base.
|
|
2370
|
+
- **The homepage is a page like any other on both sides.** A content page
|
|
2371
|
+
reaches it through `[[homepage-root|Text]]` and is listed in its backlinks;
|
|
2372
|
+
its own body links are ordinary markdown, resolved against the package root
|
|
2373
|
+
the way a browser resolves them, so `being-grod/`, `/thalorna/being-grod/`
|
|
2374
|
+
and `./being-grod/#top` each count as a mention of that page. A link that
|
|
2375
|
+
names a host leaves the site and counts as nothing.
|
|
2376
|
+
- **Only a page of this site is an endpoint.** A link into another package
|
|
2377
|
+
resolves to an href the build renders but no page it writes, so it is listed
|
|
2378
|
+
on neither side; a backlink can be counted only on a page this build emits.
|
|
2379
|
+
|
|
2380
|
+
`related` is derived, never authored: what links to a page is a fact about
|
|
2381
|
+
every other page in the tree. A note that writes one has it replaced.
|
|
2382
|
+
|
|
2336
2383
|
### Why the output location is fixed
|
|
2337
2384
|
|
|
2338
2385
|
The content mount is a build artifact and is **deleted on every run**, so that a
|
|
@@ -2830,12 +2877,12 @@ zero differences, so only "which file was read" separates the outcomes.
|
|
|
2830
2877
|
Releasing is not a command anyone runs. It is a consequence of merging, in two
|
|
2831
2878
|
steps, and each step is visible while it is pending.
|
|
2832
2879
|
|
|
2833
|
-
**
|
|
2834
|
-
major/minor/patch; the summary you write becomes the
|
|
2835
|
-
|
|
2836
|
-
|
|
2837
|
-
|
|
2838
|
-
|
|
2880
|
+
**A pull request declares its bump when a consumer will notice.** Run
|
|
2881
|
+
`npx changeset` and pick major/minor/patch; the summary you write becomes the
|
|
2882
|
+
changelog entry and the release note. Nothing a consumer meets means no
|
|
2883
|
+
changeset at all — CI carries no gate demanding one. What CI does check is the
|
|
2884
|
+
quality of a changeset that exists: `node bin/package-build.mjs changelog check`
|
|
2885
|
+
lints every pending one against the rules a changeset is held to.
|
|
2839
2886
|
|
|
2840
2887
|
**Merging to `main` opens a Version Packages pull request** carrying the version
|
|
2841
2888
|
bump and the rewritten `CHANGELOG.md`. That pull request _is_ the pending
|
package/README.md
CHANGED
|
@@ -638,12 +638,12 @@ the first import.
|
|
|
638
638
|
Releasing is not a command anyone runs. It is a consequence of merging, in two
|
|
639
639
|
steps, and each step is visible while it is pending.
|
|
640
640
|
|
|
641
|
-
**
|
|
642
|
-
major/minor/patch; the summary you write becomes the
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
641
|
+
**A pull request declares its bump when a consumer will notice.** Run
|
|
642
|
+
`npx changeset` and pick major/minor/patch; the summary you write becomes the
|
|
643
|
+
changelog entry and the release note. Nothing a consumer meets means no
|
|
644
|
+
changeset at all — CI carries no gate demanding one. What CI does check is the
|
|
645
|
+
quality of a changeset that exists: `node bin/package-build.mjs changelog check`
|
|
646
|
+
lints every pending one against the rules a changeset is held to.
|
|
647
647
|
|
|
648
648
|
**Merging to `main` opens a Version Packages pull request** carrying the version
|
|
649
649
|
bump and the rewritten `CHANGELOG.md`. That pull request _is_ the pending
|
package/bin/package-build.mjs
CHANGED
|
@@ -54,6 +54,7 @@
|
|
|
54
54
|
* npx package-build lang coverage [--unused]
|
|
55
55
|
* npx package-build lang hardcoded
|
|
56
56
|
* npx package-build changelog check [--release] [paths..]
|
|
57
|
+
* npx package-build changelog group [paths..]
|
|
57
58
|
* npx package-build bundle check
|
|
58
59
|
* npx package-build release
|
|
59
60
|
* npx package-build deploy <stage>
|
|
@@ -82,7 +83,7 @@ import { loadPackageBuildConfig } from "../config.mjs";
|
|
|
82
83
|
import { writeSiteRoot } from "../engine/site-root.mjs";
|
|
83
84
|
import { DEPLOY_ROOT } from "../engine/site-config.mjs";
|
|
84
85
|
import { compilesFoundryDocuments } from "../content-config.mjs";
|
|
85
|
-
import { loadPackConfig, packConfigPath } from "../engine/pack-config.mjs";
|
|
86
|
+
import { loadPackConfig, packConfigPath, resolveConfigFile } from "../engine/pack-config.mjs";
|
|
86
87
|
import { cleanBuildArtifacts, stageAssets } from "../stage.mjs";
|
|
87
88
|
import { buildSchemaArtifact } from "../engine/schema-extract.mjs";
|
|
88
89
|
import { SCHEMA_ARTIFACT_FILE } from "../engine/foreign-catalog.mjs";
|
|
@@ -90,6 +91,7 @@ import { validateLangSource } from "../lang.mjs";
|
|
|
90
91
|
import { checkLabelRegistry } from "../labels.mjs";
|
|
91
92
|
import { lintYaml } from "../engine/yaml-lint.mjs";
|
|
92
93
|
import { lintChangesetText, lintReleaseText } from "../engine/changelog-lint.mjs";
|
|
94
|
+
import { groupChangelogText } from "../engine/changelog-group.mjs";
|
|
93
95
|
import { bumpDependencies } from "../engine/dependency-bump.mjs";
|
|
94
96
|
import {
|
|
95
97
|
analyzeCoverage,
|
|
@@ -779,41 +781,53 @@ function yamlCommand() {
|
|
|
779
781
|
}
|
|
780
782
|
|
|
781
783
|
/**
|
|
782
|
-
* `changelog check` —
|
|
783
|
-
*
|
|
784
|
-
*
|
|
785
|
-
*
|
|
786
|
-
*
|
|
787
|
-
* verbatim as a release note. Default reads every pending
|
|
788
|
-
* `--release` reads the first `## <version>` section of
|
|
789
|
-
* instead, for the **Version Packages** branch a merge to
|
|
784
|
+
* `changelog check` / `changelog group` — release-prose checks, and folding
|
|
785
|
+
* a release's changeset blocks together by their bold label.
|
|
786
|
+
*
|
|
787
|
+
* `check` answers one question a changeset otherwise goes unheld to — who
|
|
788
|
+
* notices, and what do they see — so a pull-request description pasted into
|
|
789
|
+
* one does not ship verbatim as a release note. Default reads every pending
|
|
790
|
+
* changeset; `--release` reads the first `## <version>` section of
|
|
791
|
+
* `CHANGELOG.md` instead, for the **Version Packages** branch a merge to
|
|
792
|
+
* `main` opens.
|
|
793
|
+
*
|
|
794
|
+
* `group` rewrites that same first `## <version>` section in place: every
|
|
795
|
+
* changeset in it writes its own `**Compendiums**` block, and nothing merges
|
|
796
|
+
* the three a release with three compendium fixes ends up with. It reads
|
|
797
|
+
* `changelog.labels` for the vocabulary and display order, when the
|
|
798
|
+
* repository declares one.
|
|
790
799
|
*
|
|
791
800
|
* @returns {object} The yargs command module.
|
|
792
801
|
*/
|
|
793
802
|
function changelogCommand() {
|
|
794
803
|
return {
|
|
795
804
|
command: "changelog <action> [paths..]",
|
|
796
|
-
describe: "Release-prose checks",
|
|
805
|
+
describe: "Release-prose checks, and folding a release's blocks by label",
|
|
797
806
|
builder: (y) =>
|
|
798
807
|
y
|
|
799
808
|
.positional("action", {
|
|
800
|
-
choices: ["check"],
|
|
801
|
-
describe:
|
|
809
|
+
choices: ["check", "group"],
|
|
810
|
+
describe:
|
|
811
|
+
"check: lint pending changesets, or a release section. " +
|
|
812
|
+
"group: fold a release section's blocks together by label",
|
|
802
813
|
})
|
|
803
814
|
.positional("paths", {
|
|
804
815
|
describe:
|
|
805
|
-
"Files to
|
|
806
|
-
"README.md excluded), or `CHANGELOG.md`
|
|
816
|
+
"Files to act on. Defaults to `.changeset/*.md` (config.json and " +
|
|
817
|
+
"README.md excluded) for check, or `CHANGELOG.md` for check --release " +
|
|
818
|
+
"and for group.",
|
|
807
819
|
type: "string",
|
|
808
820
|
})
|
|
809
821
|
.option("release", {
|
|
810
822
|
type: "boolean",
|
|
811
823
|
default: false,
|
|
812
824
|
describe:
|
|
813
|
-
"
|
|
814
|
-
"pending changesets",
|
|
825
|
+
"check only: check the first `## <version>` section of CHANGELOG.md " +
|
|
826
|
+
"instead of pending changesets",
|
|
815
827
|
}),
|
|
816
|
-
handler: handler(async (args) =>
|
|
828
|
+
handler: handler(async (args) =>
|
|
829
|
+
args.action === "group" ? changelogGroup(args) : changelogCheck(args),
|
|
830
|
+
),
|
|
817
831
|
};
|
|
818
832
|
}
|
|
819
833
|
|
|
@@ -835,19 +849,35 @@ function changelogFiles(args) {
|
|
|
835
849
|
);
|
|
836
850
|
}
|
|
837
851
|
|
|
852
|
+
/**
|
|
853
|
+
* `changelog.labels`, when this repository declares a configuration — `null`
|
|
854
|
+
* otherwise, so `check` and `group` both work in a tree with no
|
|
855
|
+
* `package-build.config.yaml` at all, exactly as `check` already does for
|
|
856
|
+
* pending changesets with no configuration to read.
|
|
857
|
+
*
|
|
858
|
+
* @returns {readonly string[]|null}
|
|
859
|
+
*/
|
|
860
|
+
function loadChangelogLabels() {
|
|
861
|
+
const found = resolveConfigFile();
|
|
862
|
+
if (!found.path) return null;
|
|
863
|
+
return loadPackConfig().changelog.labels;
|
|
864
|
+
}
|
|
865
|
+
|
|
838
866
|
/**
|
|
839
867
|
* Run `changelog check` over every resolved file and report the result.
|
|
840
868
|
*
|
|
841
869
|
* @param {object} args - Parsed CLI arguments.
|
|
842
870
|
*/
|
|
843
871
|
function changelogCheck(args) {
|
|
872
|
+
const labels = loadChangelogLabels();
|
|
844
873
|
const files = changelogFiles(args);
|
|
845
874
|
let errors = 0;
|
|
846
875
|
let total = 0;
|
|
847
876
|
for (const file of files) {
|
|
848
877
|
if (!fs.existsSync(file)) die(`changelog check: ${file} does not exist.`);
|
|
849
878
|
const text = fs.readFileSync(file, "utf8");
|
|
850
|
-
const { findings } =
|
|
879
|
+
const { findings } =
|
|
880
|
+
args.release ? lintReleaseText(text, { labels }) : lintChangesetText(text, { labels });
|
|
851
881
|
errors += reportFindings(findings, { file });
|
|
852
882
|
total += findings.length;
|
|
853
883
|
}
|
|
@@ -858,6 +888,26 @@ function changelogCheck(args) {
|
|
|
858
888
|
if (errors) process.exitCode = 1;
|
|
859
889
|
}
|
|
860
890
|
|
|
891
|
+
/**
|
|
892
|
+
* Run `changelog group` over every resolved file, writing each back in place.
|
|
893
|
+
*
|
|
894
|
+
* @param {object} args - Parsed CLI arguments.
|
|
895
|
+
*/
|
|
896
|
+
function changelogGroup(args) {
|
|
897
|
+
const labels = loadChangelogLabels();
|
|
898
|
+
const files = args.paths?.length ? args.paths : ["CHANGELOG.md"];
|
|
899
|
+
let warnings = 0;
|
|
900
|
+
for (const file of files) {
|
|
901
|
+
if (!fs.existsSync(file)) die(`changelog group: ${file} does not exist.`);
|
|
902
|
+
const original = fs.readFileSync(file, "utf8");
|
|
903
|
+
const { text, findings } = groupChangelogText(original, { labels });
|
|
904
|
+
reportFindings(findings, { file });
|
|
905
|
+
warnings += findings.length;
|
|
906
|
+
if (text !== original) fs.writeFileSync(file, text, "utf8");
|
|
907
|
+
}
|
|
908
|
+
console.log(`package-build: ${files.length} file(s) grouped · ${warnings} warning(s)`);
|
|
909
|
+
}
|
|
910
|
+
|
|
861
911
|
/**
|
|
862
912
|
* `labels check` — do the machine registry and the documented table agree?
|
|
863
913
|
*
|
package/changelog.cjs
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* This file is part of the Song of Heroic Lands (SoHL) system for Foundry VTT.
|
|
3
|
+
* Copyright (c) 2024-2026 Tom Rodriguez ("Toasty") — <toasty@heroiclands.org>
|
|
4
|
+
*
|
|
5
|
+
* This work is licensed under the GNU General Public License v3.0 (GPLv3).
|
|
6
|
+
* You may copy, modify, and distribute it under the terms of that license.
|
|
7
|
+
*
|
|
8
|
+
* For full terms, see the LICENSE.md file in the project root or visit:
|
|
9
|
+
* https://www.gnu.org/licenses/gpl-3.0.html
|
|
10
|
+
*
|
|
11
|
+
* SPDX-License-Identifier: GPL-3.0-or-later
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* The Changesets changelog generator every HeroicLands repository configures
|
|
16
|
+
* as `"changelog": "@heroiclands/package-build/changelog"`.
|
|
17
|
+
*
|
|
18
|
+
* Every built-in generator writes a commit hash into the release line
|
|
19
|
+
* whenever it knows the commit, which it always does in this org's
|
|
20
|
+
* squash-merge workflow — `@changesets/cli/changelog` writes `- <hash>: `,
|
|
21
|
+
* and `@changesets/changelog-git` adds a link on top of it. A commit hash is
|
|
22
|
+
* a commit-log artefact, not something the person installing the package
|
|
23
|
+
* needs, and the writing conventions keep tracker and commit references out
|
|
24
|
+
* of the tree. `getReleaseLine` writes the changeset's summary and nothing
|
|
25
|
+
* else; `getDependencyReleaseLine` writes nothing, because none of these
|
|
26
|
+
* packages narrate an internal dependency bump.
|
|
27
|
+
*
|
|
28
|
+
* Changesets loads a changelog generator with `require`, so this is
|
|
29
|
+
* CommonJS in a package that is otherwise `"type": "module"`.
|
|
30
|
+
*
|
|
31
|
+
* @module
|
|
32
|
+
*/
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* @param {{summary: string}} changeset - The pending changeset. Only
|
|
36
|
+
* `summary` is read; a known `commit` is deliberately ignored.
|
|
37
|
+
* @returns {string} The summary rendered as a block, verbatim — no leading
|
|
38
|
+
* `- `, no indentation of the following lines — wrapped in a single
|
|
39
|
+
* newline on each side so Changesets' own joiner (which counts, rather
|
|
40
|
+
* than reads, the newlines bracketing a release line) always separates
|
|
41
|
+
* this block from its neighbours by one blank line.
|
|
42
|
+
*/
|
|
43
|
+
function getReleaseLine(changeset) {
|
|
44
|
+
return `\n${changeset.summary}\n`;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* @returns {string} Always empty — these packages have no internal
|
|
49
|
+
* dependency bumps to narrate.
|
|
50
|
+
*/
|
|
51
|
+
function getDependencyReleaseLine() {
|
|
52
|
+
return "";
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
module.exports = { getReleaseLine, getDependencyReleaseLine };
|
package/content-config.mjs
CHANGED
|
@@ -756,10 +756,12 @@ const CONFIG_KEYS = [
|
|
|
756
756
|
"requiresSystem",
|
|
757
757
|
"packageBuild",
|
|
758
758
|
"publish",
|
|
759
|
+
"changelog",
|
|
759
760
|
];
|
|
760
761
|
const SYSTEM_KEYS = ["manifest", "compatibility"];
|
|
761
762
|
const COMPATIBILITY_KEYS = ["minimum", "verified"];
|
|
762
763
|
const DOCS_KEYS = ["itemFields"];
|
|
764
|
+
const CHANGELOG_KEYS = ["labels"];
|
|
763
765
|
const SITE_KEYS = [
|
|
764
766
|
"base",
|
|
765
767
|
"assets",
|
|
@@ -1458,6 +1460,37 @@ function normalizeDocs(value) {
|
|
|
1458
1460
|
});
|
|
1459
1461
|
}
|
|
1460
1462
|
|
|
1463
|
+
/**
|
|
1464
|
+
* `changelog.labels` — the vocabulary and display order `changelog check`
|
|
1465
|
+
* and `changelog group` read a release block's bold label against.
|
|
1466
|
+
*
|
|
1467
|
+
* Optional: a repository that groups nothing declares none, and `group`
|
|
1468
|
+
* falls back to ordering every label by first appearance. Declared, it is a
|
|
1469
|
+
* list of non-empty strings with no duplicate — a repeated label could never
|
|
1470
|
+
* be told apart from a genuine drift (`Compendiums` twice would silently
|
|
1471
|
+
* swallow the `Character data` beside `Characters` the list exists to catch).
|
|
1472
|
+
*
|
|
1473
|
+
* @param {unknown} value - The configured value, or `undefined`.
|
|
1474
|
+
* @returns {Readonly<{labels: readonly string[]|null}>}
|
|
1475
|
+
*/
|
|
1476
|
+
function normalizeChangelog(value) {
|
|
1477
|
+
if (value === undefined) return Object.freeze({ labels: null });
|
|
1478
|
+
if (!isPlainObject(value)) fail("changelog", "must be a mapping");
|
|
1479
|
+
const input = /** @type {Record<string, unknown>} */ (value);
|
|
1480
|
+
rejectUnknownKeys(input, CHANGELOG_KEYS, "changelog.");
|
|
1481
|
+
if (input.labels === undefined) return Object.freeze({ labels: null });
|
|
1482
|
+
if (!Array.isArray(input.labels)) fail("changelog.labels", "must be an array");
|
|
1483
|
+
const labels = input.labels.map((label, index) =>
|
|
1484
|
+
requireNonEmptyString(label, `changelog.labels[${index}]`),
|
|
1485
|
+
);
|
|
1486
|
+
const seen = new Set();
|
|
1487
|
+
for (const label of labels) {
|
|
1488
|
+
if (seen.has(label)) fail("changelog.labels", `declares \`${label}\` more than once`);
|
|
1489
|
+
seen.add(label);
|
|
1490
|
+
}
|
|
1491
|
+
return Object.freeze({ labels: Object.freeze(labels) });
|
|
1492
|
+
}
|
|
1493
|
+
|
|
1461
1494
|
/**
|
|
1462
1495
|
* The asset host the website resolves a pathname against.
|
|
1463
1496
|
*
|
|
@@ -2758,5 +2791,6 @@ export function defineConfig(config) {
|
|
|
2758
2791
|
requiresSystem,
|
|
2759
2792
|
packageBuild: normalizePackageBuild(input.packageBuild),
|
|
2760
2793
|
publish: normalizePublish(input.publish),
|
|
2794
|
+
changelog: normalizeChangelog(input.changelog),
|
|
2761
2795
|
});
|
|
2762
2796
|
}
|
package/docs/api.md
CHANGED
|
@@ -1555,6 +1555,22 @@ console.log(VISIBLE_ATTRIBUTES);
|
|
|
1555
1555
|
| `findHardcodedText` | `findHardcodedText(source, { allow = [] } = {})` | `TemplateFinding[]` | finding every user-visible literal a template leaves untranslated; `allow` is the explicit, reasoned escape hatch for a literal that is deliberately not a key |
|
|
1556
1556
|
| `findTemplateSyntaxErrors` | `findTemplateSyntaxErrors(source)` | `TemplateFinding[]` — one finding when it does not parse, else none | checking whether Handlebars can parse a template at all, without needing any helper it calls to exist |
|
|
1557
1557
|
|
|
1558
|
+
## `./changelog`
|
|
1559
|
+
|
|
1560
|
+
The Changesets changelog generator, loaded with `require` — a CommonJS module in a package that is otherwise `"type": "module"`. Every HeroicLands repository sets `"changelog": "@heroiclands/package-build/changelog"` in its `.changeset/config.json`, because every built-in generator writes a commit hash into the release line whenever it knows the commit, which it always does in this org's squash-merge workflow, and a commit hash is a commit-log artefact rather than something the person installing the package needs.
|
|
1561
|
+
|
|
1562
|
+
`getReleaseLine` renders a changeset's summary as a block, verbatim — no leading `- `, no indentation of the following lines — so a summary that opens with a paragraph, a bold label, or its own bullet list lands in `CHANGELOG.md` exactly as written. It wraps the summary in a single newline on each side, which costs nothing in the rendered file — Changesets' own joiner trims a release line's edges and only counts the newlines it finds there — but guarantees the block is separated from its neighbours, heading or sibling entry alike, by one blank line.
|
|
1563
|
+
|
|
1564
|
+
```json
|
|
1565
|
+
// .changeset/config.json
|
|
1566
|
+
{ "changelog": "@heroiclands/package-build/changelog" }
|
|
1567
|
+
```
|
|
1568
|
+
|
|
1569
|
+
| Export | Signature | Returns | Use it when |
|
|
1570
|
+
| -------------------------- | -------------------------------------------------------------------------- | ------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------- |
|
|
1571
|
+
| `getReleaseLine` | `getReleaseLine(changeset, type, changelogOpts)` | `string` — the summary, verbatim, separated from its neighbours by a blank line | writing one changeset's release line with no commit-hash prefix and no bullet |
|
|
1572
|
+
| `getDependencyReleaseLine` | `getDependencyReleaseLine(changesets, dependenciesUpdated, changelogOpts)` | `string` — always `""` | satisfying the `ChangelogFunctions` interface; these packages narrate no internal dependency bumps |
|
|
1573
|
+
|
|
1558
1574
|
## `./package.json`
|
|
1559
1575
|
|
|
1560
1576
|
Not a JavaScript module — this subpath entry exists so tooling (bundlers, `import.meta.resolve`, a script reading the installed version) can resolve the package's own `package.json` through Node's package-exports resolution instead of reaching outside the declared export map.
|
package/docs/commands.md
CHANGED
|
@@ -646,8 +646,11 @@ from the heading rule, since neither is authored.
|
|
|
646
646
|
A token that reads as code — `camelCase()`, a `path/with/slashes.ext`,
|
|
647
647
|
`SCREAMING_SNAKE` — outside any code span is a warning, not a failure: a
|
|
648
648
|
user-facing note sometimes needs one (`Compendium.hm3.items.Item.<id>`), but
|
|
649
|
-
rarely.
|
|
650
|
-
|
|
649
|
+
rarely. A block's bold label absent from a declared `changelog.labels` is
|
|
650
|
+
also a warning — the drift `**Character data**` beside `**Characters**`
|
|
651
|
+
produces — and checks nothing when the repository declares no
|
|
652
|
+
`changelog.labels`. Every other finding is an error. Reads the files given,
|
|
653
|
+
or resolves its own defaults; writes nothing.
|
|
651
654
|
|
|
652
655
|
**OPTIONS**
|
|
653
656
|
|
|
@@ -678,6 +681,71 @@ package-build: 1 file(s) checked · 1 error(s) · 0 warning(s)
|
|
|
678
681
|
|
|
679
682
|
[Diagnostics](diagnostics.md).
|
|
680
683
|
|
|
684
|
+
### `package-build changelog group`
|
|
685
|
+
|
|
686
|
+
**NAME**
|
|
687
|
+
|
|
688
|
+
Fold a release section's changeset blocks together by their bold label
|
|
689
|
+
(`group` is a sibling of `check`, not one of its actions).
|
|
690
|
+
|
|
691
|
+
**SYNOPSIS**
|
|
692
|
+
|
|
693
|
+
```
|
|
694
|
+
package-build changelog group [paths..]
|
|
695
|
+
```
|
|
696
|
+
|
|
697
|
+
**DESCRIPTION**
|
|
698
|
+
|
|
699
|
+
`@heroiclands/package-build/changelog` writes each changeset's summary into
|
|
700
|
+
`CHANGELOG.md` as its own block, verbatim, under `### <Bump> Changes` — one
|
|
701
|
+
per changeset, in whatever order Changesets read the files. Three pull
|
|
702
|
+
requests each touching compendium content each write their own
|
|
703
|
+
`**Compendiums**` block, and nothing merges them: the release reads as three
|
|
704
|
+
scattered entries instead of one.
|
|
705
|
+
|
|
706
|
+
`group` rewrites the first `## <version>` section of `CHANGELOG.md` in
|
|
707
|
+
place. Within each `### <Bump> Changes` body, every block sharing one bold
|
|
708
|
+
label is merged into a single block for that label — its bullets kept in
|
|
709
|
+
the order they were originally written, an exact-duplicate bullet kept
|
|
710
|
+
once — and the merged blocks are reordered: the unlabelled lead paragraph a
|
|
711
|
+
changeset writes with no category first, then every label
|
|
712
|
+
[`changelog.labels`](configuration.md#changelog) declares, in the order
|
|
713
|
+
given, then any label absent from that list last, in the order it first
|
|
714
|
+
appeared — each reported as a warning naming it. With no `changelog.labels`
|
|
715
|
+
declared, every group instead keeps the order its label first appeared in,
|
|
716
|
+
lead paragraph first, and nothing is reported as unknown. Every earlier
|
|
717
|
+
release section is untouched, byte for byte. Running `group` on its own
|
|
718
|
+
output is a no-op.
|
|
719
|
+
|
|
720
|
+
**OPTIONS**
|
|
721
|
+
|
|
722
|
+
| Positional | Type | Default | Description |
|
|
723
|
+
| ---------- | --------- | -------------- | ----------------------------------- |
|
|
724
|
+
| `paths` | string(s) | `CHANGELOG.md` | Files to group, rewritten in place. |
|
|
725
|
+
|
|
726
|
+
**EXIT STATUS**
|
|
727
|
+
|
|
728
|
+
1 if a named file does not exist. 0 otherwise — an undeclared label is a
|
|
729
|
+
warning, never a failure.
|
|
730
|
+
|
|
731
|
+
**EXAMPLES**
|
|
732
|
+
|
|
733
|
+
```
|
|
734
|
+
$ package-build changelog group
|
|
735
|
+
package-build: 1 file(s) grouped · 0 warning(s)
|
|
736
|
+
```
|
|
737
|
+
|
|
738
|
+
```
|
|
739
|
+
$ package-build changelog group
|
|
740
|
+
CHANGELOG.md:12: warning: changelog-group/unknown-label "Scenery" is not declared in `changelog.labels` — filed last, in order of first appearance
|
|
741
|
+
package-build: 1 file(s) grouped · 1 warning(s)
|
|
742
|
+
```
|
|
743
|
+
|
|
744
|
+
**SEE ALSO**
|
|
745
|
+
|
|
746
|
+
[`changelog check`](#package-build-changelog-check),
|
|
747
|
+
[`changelog.labels`](configuration.md#changelog).
|
|
748
|
+
|
|
681
749
|
### `package-build bundle check`
|
|
682
750
|
|
|
683
751
|
**NAME**
|
package/docs/configuration.md
CHANGED
|
@@ -48,7 +48,7 @@ around its own evaluation.
|
|
|
48
48
|
|
|
49
49
|
### Quick reference
|
|
50
50
|
|
|
51
|
-
|
|
51
|
+
21 top-level keys. `rootDir` is not one of them — a data configuration never
|
|
52
52
|
writes it — and is documented under [Derived values](#derived-values) instead,
|
|
53
53
|
alongside `foundryPackage`, `homepage`, `author` and `itemBuilders`, whose
|
|
54
54
|
data-configuration behaviour is also derivation rather than ordinary
|
|
@@ -76,10 +76,11 @@ authoring.
|
|
|
76
76
|
| [`requiresSystem`](#requiressystem) | string | no; refused in a `documentation` package | `null` |
|
|
77
77
|
| [`packageBuild`](#the-packagebuild-section) | object | no | `{}` |
|
|
78
78
|
| [`publish`](#publish) | object | no; **required**, with `site: content`, in a `documentation` package | `{site: "homepage", address: {prefix: ""}}` |
|
|
79
|
+
| [`changelog`](#changelog) | object | no | `{labels: null}` |
|
|
79
80
|
|
|
80
81
|
Any key outside this list is refused:
|
|
81
82
|
|
|
82
|
-
> `` `<key>` is not a recognized option (expected one of: rootDir, contentPackage, foundryPackage, homepage, author, packageKind, stats, itemBuilders, paths, skipDirectories, icons, packs, docs, site, pdf, compatibility, relationships, systems, requiresSystem, packageBuild, publish). ``
|
|
83
|
+
> `` `<key>` is not a recognized option (expected one of: rootDir, contentPackage, foundryPackage, homepage, author, packageKind, stats, itemBuilders, paths, skipDirectories, icons, packs, docs, site, pdf, compatibility, relationships, systems, requiresSystem, packageBuild, publish, changelog). ``
|
|
83
84
|
|
|
84
85
|
(`rootDir` appears in that list because it is a key `defineConfig` itself
|
|
85
86
|
accepts — an `.mjs` configuration authors it directly. A YAML configuration
|
|
@@ -289,7 +290,7 @@ Unlike the first three, authoring `itemBuilders` is not an error — it is
|
|
|
289
290
|
|
|
290
291
|
---
|
|
291
292
|
|
|
292
|
-
## The
|
|
293
|
+
## The 21 keys
|
|
293
294
|
|
|
294
295
|
### `contentPackage`
|
|
295
296
|
|
|
@@ -1334,6 +1335,45 @@ Any other key under `publish.address` is refused:
|
|
|
1334
1335
|
|
|
1335
1336
|
> ``package-build config: `publish.address.<key>` is not a recognized option (expected one of: prefix).``
|
|
1336
1337
|
|
|
1338
|
+
### `changelog`
|
|
1339
|
+
|
|
1340
|
+
**Type:** object · **Optional**, default `{labels: null}`.
|
|
1341
|
+
|
|
1342
|
+
`changelog check` and `changelog group` both read a release block's bold
|
|
1343
|
+
label — `**Compendiums**` — against `changelog.labels`: the vocabulary and
|
|
1344
|
+
display order a repository declares for the categories its changesets group
|
|
1345
|
+
under. Declaring none is ordinary — a repository that writes plain,
|
|
1346
|
+
unlabelled changesets needs nothing here.
|
|
1347
|
+
|
|
1348
|
+
| Key | Type | Required | Default |
|
|
1349
|
+
| ------------------ | -------- | -------- | ------- |
|
|
1350
|
+
| `changelog.labels` | string[] | no | `null` |
|
|
1351
|
+
|
|
1352
|
+
> ``package-build config: `changelog` must be a mapping.``
|
|
1353
|
+
|
|
1354
|
+
> ``package-build config: `changelog.<key>` is not a recognized option (expected one of: labels).``
|
|
1355
|
+
|
|
1356
|
+
> ``package-build config: `changelog.labels` must be an array.``
|
|
1357
|
+
|
|
1358
|
+
> ``package-build config: `changelog.labels[<n>]` must be a non-empty string.``
|
|
1359
|
+
|
|
1360
|
+
A repeated label is refused rather than silently deduplicated — it could
|
|
1361
|
+
never be told apart from the drift the list exists to catch (`Compendiums`
|
|
1362
|
+
written twice swallows a genuine `Character data` beside `Characters`):
|
|
1363
|
+
|
|
1364
|
+
> ``package-build config: `changelog.labels` declares `Compendiums` more than once.``
|
|
1365
|
+
|
|
1366
|
+
With `changelog.labels` declared, `changelog group` orders a release
|
|
1367
|
+
section's blocks by that list — the unlabelled lead paragraph first, then
|
|
1368
|
+
every declared label in the order given, then any undeclared label last, in
|
|
1369
|
+
the order it first appears, each reported as a warning naming it. With no
|
|
1370
|
+
`changelog.labels` declared, `group` orders every block by first
|
|
1371
|
+
appearance, lead paragraph first, and warns about nothing. `changelog check`
|
|
1372
|
+
warns on a block whose label is not in a declared `changelog.labels`; with
|
|
1373
|
+
none declared, it checks nothing. See
|
|
1374
|
+
[`package-build changelog check`](commands.md#package-build-changelog-check)
|
|
1375
|
+
and [`package-build changelog group`](commands.md#package-build-changelog-group).
|
|
1376
|
+
|
|
1337
1377
|
---
|
|
1338
1378
|
|
|
1339
1379
|
## The `packageBuild` section
|