@heroiclands/package-build 22.4.1 → 22.4.3
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 +54 -0
- package/CONTENT.md +6 -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/actor-compiler.mjs +26 -1
- package/engine/changelog-group.mjs +256 -0
- package/engine/changelog-lint.mjs +168 -9
- package/engine/document-subtypes.mjs +24 -7
- package/hm3/actors.mjs +42 -0
- package/package.json +4 -3
- package/types/engine/actor-compiler.d.mts +20 -0
- package/types/engine/changelog-group.d.mts +28 -0
- package/types/engine/changelog-lint.d.mts +107 -2
- package/types/engine/document-subtypes.d.mts +14 -7
- package/types/hm3/actors.d.mts +9 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,59 @@
|
|
|
1
1
|
# @heroiclands/package-build
|
|
2
2
|
|
|
3
|
+
## 22.4.3
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
`@heroiclands/package-build/changelog` writes each changeset's summary into `CHANGELOG.md` as its
|
|
8
|
+
|
|
9
|
+
own block, so three pull requests each touching compendium content leave three separate
|
|
10
|
+
|
|
11
|
+
`**Compendiums**` blocks instead of one. `changelog group` rewrites the newest release section in
|
|
12
|
+
|
|
13
|
+
place: every block sharing one label is merged into a single block, bullets kept in their original
|
|
14
|
+
|
|
15
|
+
order with an exact duplicate kept once, and the merged blocks are ordered by a new
|
|
16
|
+
|
|
17
|
+
`changelog.labels` configuration key — the unlabelled lead paragraph first, then every declared
|
|
18
|
+
|
|
19
|
+
label in order, then any undeclared label last with a warning naming it. `changelog check` warns on
|
|
20
|
+
|
|
21
|
+
a label absent from a declared `changelog.labels` too, so a drifted spelling is caught before a
|
|
22
|
+
|
|
23
|
+
release ships it.
|
|
24
|
+
|
|
25
|
+
`@heroiclands/package-build/changelog` is a Changesets changelog generator
|
|
26
|
+
|
|
27
|
+
whose `getReleaseLine` renders a changeset's summary as a block, verbatim —
|
|
28
|
+
|
|
29
|
+
no bullet, no indentation, separated from its neighbours by one blank line —
|
|
30
|
+
|
|
31
|
+
and whose `getDependencyReleaseLine` writes nothing. Point
|
|
32
|
+
|
|
33
|
+
`.changeset/config.json` at it
|
|
34
|
+
|
|
35
|
+
(`"changelog": "@heroiclands/package-build/changelog"`) to stop every release
|
|
36
|
+
|
|
37
|
+
note from carrying a commit-hash prefix.
|
|
38
|
+
|
|
39
|
+
**A new `changelog group` command folds a release's changeset blocks together by their bold label.**
|
|
40
|
+
|
|
41
|
+
**A shipped changelog generator writes a release note without a commit hash.**
|
|
42
|
+
|
|
43
|
+
## 22.4.2
|
|
44
|
+
|
|
45
|
+
### Patch Changes
|
|
46
|
+
|
|
47
|
+
- 8dedb23: **A module embedding HM3 spells, invocations or psionics compiles.** A being's
|
|
48
|
+
embedded `spell`, `invocation` or `psionic` reference that names no shortcode
|
|
49
|
+
template and no `data.icon` of its own now takes that subtype's own default
|
|
50
|
+
art — the icon HM3 itself assigns a freshly created item of that kind —
|
|
51
|
+
instead of the build refusing the actor outright.
|
|
52
|
+
- 54109cf: **A module embedding HârnMaster 3 weapons compiles.** A being's `(type,
|
|
53
|
+
shortcode)` reference into HM3's one-to-many `weapongear` row resolves when
|
|
54
|
+
its `type` names one of the row's own subtypes — `weapongear` or
|
|
55
|
+
`missilegear` — instead of being refused as ambiguous.
|
|
56
|
+
|
|
3
57
|
## 22.4.1
|
|
4
58
|
|
|
5
59
|
### Patch Changes
|
package/CONTENT.md
CHANGED
|
@@ -2830,12 +2830,12 @@ zero differences, so only "which file was read" separates the outcomes.
|
|
|
2830
2830
|
Releasing is not a command anyone runs. It is a consequence of merging, in two
|
|
2831
2831
|
steps, and each step is visible while it is pending.
|
|
2832
2832
|
|
|
2833
|
-
**
|
|
2834
|
-
major/minor/patch; the summary you write becomes the
|
|
2835
|
-
|
|
2836
|
-
|
|
2837
|
-
|
|
2838
|
-
|
|
2833
|
+
**A pull request declares its bump when a consumer will notice.** Run
|
|
2834
|
+
`npx changeset` and pick major/minor/patch; the summary you write becomes the
|
|
2835
|
+
changelog entry and the release note. Nothing a consumer meets means no
|
|
2836
|
+
changeset at all — CI carries no gate demanding one. What CI does check is the
|
|
2837
|
+
quality of a changeset that exists: `node bin/package-build.mjs changelog check`
|
|
2838
|
+
lints every pending one against the rules a changeset is held to.
|
|
2839
2839
|
|
|
2840
2840
|
**Merging to `main` opens a Version Packages pull request** carrying the version
|
|
2841
2841
|
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
|
package/docs/project-setup.md
CHANGED
|
@@ -82,12 +82,13 @@ carries no hook files of its own and cannot drift from the others. `|| true` so
|
|
|
82
82
|
that an install outside a git checkout — a Docker build, a CI cache step —
|
|
83
83
|
does not fail on it.
|
|
84
84
|
|
|
85
|
-
|
|
85
|
+
Four hooks arrive with it, each with its own switch, read with git's normal
|
|
86
86
|
precedence: a plain `git config` sets one clone, `--global` sets a machine.
|
|
87
87
|
|
|
88
88
|
| Hook | What it does | Key | Default |
|
|
89
89
|
| -------------------------------- | --------------------------------------------------------------- | ------------------------------------ | ------- |
|
|
90
90
|
| `pre-commit`, `pre-merge-commit` | Refuse a commit on a protected branch. | `hooks.allowCommitOnMain` (inverted) | on |
|
|
91
|
+
| `pre-commit` | Lint any `.changeset/*.md` the commit stages. | `hooks.changelogCheck` | on |
|
|
91
92
|
| `commit-msg` | Refuse AI attribution in a commit message. | `hooks.noAttribution` | on |
|
|
92
93
|
| `pre-push` | Run this repository's own Build & Test workflow in a container. | `hooks.prePushCi` | off |
|
|
93
94
|
|
|
@@ -130,19 +131,20 @@ anything to remove.
|
|
|
130
131
|
|
|
131
132
|
### `lint:*` — the checks, one per question
|
|
132
133
|
|
|
133
|
-
|
|
134
|
-
are separate scripts rather than one because a failing chain stops at its
|
|
135
|
-
failure, and knowing _which_ question failed is most of the diagnosis.
|
|
134
|
+
Seven checks, each answering one question and blind to what the others see.
|
|
135
|
+
They are separate scripts rather than one because a failing chain stops at its
|
|
136
|
+
first failure, and knowing _which_ question failed is most of the diagnosis.
|
|
136
137
|
|
|
137
138
|
```json
|
|
138
|
-
"lint": "run-s lint:format lint:markdown lint:addresses lint:content-links lint:lang lint:labels",
|
|
139
|
+
"lint": "run-s lint:format lint:markdown lint:addresses lint:content-links lint:lang lint:labels lint:changelog",
|
|
139
140
|
"lint:format": "content-build format",
|
|
140
141
|
"lint:markdown": "content-build markdown",
|
|
141
142
|
"lint:markdown:fix": "content-build markdown --fix",
|
|
142
143
|
"lint:addresses": "content-build lint",
|
|
143
144
|
"lint:content-links": "content-build links",
|
|
144
145
|
"lint:lang": "package-build lang check",
|
|
145
|
-
"lint:labels": "package-build labels check"
|
|
146
|
+
"lint:labels": "package-build labels check",
|
|
147
|
+
"lint:changelog": "package-build changelog check"
|
|
146
148
|
```
|
|
147
149
|
|
|
148
150
|
- **`lint:format`** — is every file formatted to the shared Prettier options?
|
|
@@ -165,6 +167,10 @@ failure, and knowing _which_ question failed is most of the diagnosis.
|
|
|
165
167
|
- **`lint:labels`** — do `.github/labels.yml` and §3 of
|
|
166
168
|
`.github/ISSUE_REPORTING.md` still list the same labels? Neither derives from
|
|
167
169
|
the other, so nothing else notices when they drift.
|
|
170
|
+
- **`lint:changelog`** — does every pending `.changeset/*.md` read like a
|
|
171
|
+
release note rather than a pasted pull-request description? Passes
|
|
172
|
+
trivially when nothing is pending: no pull request is required to carry a
|
|
173
|
+
changeset, only to write one that reads well when it does.
|
|
168
174
|
|
|
169
175
|
`run-s` comes from `npm-run-all`, a devDependency. It runs scripts in sequence
|
|
170
176
|
and stops at the first failure, which is what makes a named chain readable.
|
|
@@ -227,16 +233,19 @@ reads better with one and a CI workflow reads better with the other.
|
|
|
227
233
|
|
|
228
234
|
```json
|
|
229
235
|
"changeset": "changeset",
|
|
230
|
-
"changeset:check": "changeset status --since=origin/main",
|
|
231
236
|
"changeset:version": "changeset version && npm install --package-lock-only"
|
|
232
237
|
```
|
|
233
238
|
|
|
234
|
-
`changeset` adds one
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
`
|
|
238
|
-
|
|
239
|
-
|
|
239
|
+
`changeset` adds one, written only when a consumer will notice the change —
|
|
240
|
+
nothing gates a pull request on carrying one. `changeset:version` consumes the
|
|
241
|
+
pending changesets, writes `CHANGELOG.md` and bumps `package.json` — and the
|
|
242
|
+
`npm install --package-lock-only` after it is what keeps `package-lock.json`'s
|
|
243
|
+
recorded version in step, since changesets does not touch the lockfile.
|
|
244
|
+
|
|
245
|
+
What a pending changeset is held to is quality, not existence: `lint:changelog`
|
|
246
|
+
and the `pre-commit` hook `prepare` installs both run
|
|
247
|
+
`package-build changelog check` against it, in CI and at commit time
|
|
248
|
+
respectively, and both pass trivially when there is none.
|
|
240
249
|
|
|
241
250
|
### The site scripts
|
|
242
251
|
|