@heroiclands/package-build 0.2.0 → 0.2.1
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 +140 -0
- package/README.md +37 -0
- package/package.json +6 -2
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
# @heroiclands/package-build
|
|
2
|
+
|
|
3
|
+
## 0.2.1
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 4144291: **Release from merged changesets instead of a remembered command**
|
|
8
|
+
|
|
9
|
+
Fixes [#4](https://github.com/HeroicLands/package-build/issues/4). Releasing was
|
|
10
|
+
hand-driven — bump `package.json` on a branch, merge, then remember
|
|
11
|
+
`gh release create`, because cutting the Release is what published. Nothing
|
|
12
|
+
enforced the last step, so a merged version could sit unpublished with no check
|
|
13
|
+
red; the sibling repository lost two versions that way.
|
|
14
|
+
|
|
15
|
+
- Every pull request now declares its bump as a `.changeset/*.md` file, and CI's
|
|
16
|
+
**Changeset declared** job fails one that does not. `npx changeset add --empty`
|
|
17
|
+
is how a change says it needs no release — explicitly, rather than by omission.
|
|
18
|
+
- Merging to `main` opens a **Version Packages** pull request carrying the bump
|
|
19
|
+
and the rewritten `CHANGELOG.md`. An unreleased state is now a pull request
|
|
20
|
+
waiting in the queue rather than nothing at all.
|
|
21
|
+
- Merging that runs `changeset publish`: npm publish, the `v<version>` tag, and
|
|
22
|
+
the GitHub Release with the changelog section as its body. The OIDC Trusted
|
|
23
|
+
Publishing step is unchanged and still last; there is still no `NPM_TOKEN`, and
|
|
24
|
+
re-running on a published version is a no-op.
|
|
25
|
+
- `CHANGELOG.md` is seeded from the two hand-cut Releases so far and now ships
|
|
26
|
+
with the package. A changeset is also where a raised dependency floor gets
|
|
27
|
+
recorded — 0.2.0 raised one to `@heroiclands/content-build >= 0.15.0` and said
|
|
28
|
+
so nowhere.
|
|
29
|
+
|
|
30
|
+
<!-- Sections at 0.2.1 and above are generated by `changeset version` from the
|
|
31
|
+
changesets merged into `main`. Sections at 0.2.0 and below predate that
|
|
32
|
+
pipeline and are the hand-written GitHub Release notes, kept verbatim
|
|
33
|
+
(headings demoted one level to sit under their version) so no history was
|
|
34
|
+
lost in adopting it. -->
|
|
35
|
+
|
|
36
|
+
## 0.2.0
|
|
37
|
+
|
|
38
|
+
_2026-08-22 — a command line, not a wrapper script per job_
|
|
39
|
+
|
|
40
|
+
**A command line, so a consumer writes configuration instead of scripts.**
|
|
41
|
+
|
|
42
|
+
This package was library-only, so every consuming repository wrote a wrapper script per job — six of them in the Song of Heroic Lands repository, 441 lines that between them contained no logic:
|
|
43
|
+
|
|
44
|
+
| Wrapper | Lines | What was in it |
|
|
45
|
+
| --- | --- | --- |
|
|
46
|
+
| `build-system-json.mjs` | 143 | |
|
|
47
|
+
| `push-stage.mjs` | 76 | argv, dotenv, then `packageKind: "systems"` and `packageId: "sohl"` hard-coded beside a configuration that already declared both |
|
|
48
|
+
| `copy-assets.mjs` | 71 | a nine-entry data table, plus one genuine repository-specific transform |
|
|
49
|
+
| `check-lang.mjs` | 69 | a glob, a call, and a help string |
|
|
50
|
+
| `clean.mjs` | 47 | a `repoRoot` from `import.meta.url`, one flag, one call — no consumer-specific value at all |
|
|
51
|
+
| `pack-release.mjs` | 35 | one call passing `{ artifact: "system" }`, which `packageKind` already decides |
|
|
52
|
+
|
|
53
|
+
Every copy had drifted from its sibling in the other repositories, because copies do: `clean.mjs` was 47 lines in SoHL and 48 in `sohl-thalorna`, `copy-assets.mjs` 71 and 76. `sohl-thalorna`'s copy still reimplements the recursive directory copy this package has exported since it was extracted, because it was written before the extraction and nobody went back.
|
|
54
|
+
|
|
55
|
+
It is the same shape the configuration had before it became data: not logic, but the boilerplate a code file needs in order to *state a literal*. So the literals moved into configuration, and the boilerplate lives here, once.
|
|
56
|
+
|
|
57
|
+
#### The commands
|
|
58
|
+
|
|
59
|
+
```
|
|
60
|
+
npx package-build clean [--distclean]
|
|
61
|
+
npx package-build assets
|
|
62
|
+
npx package-build lang check
|
|
63
|
+
npx package-build release
|
|
64
|
+
npx package-build deploy <stage>
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
Wrapped as npm scripts:
|
|
68
|
+
|
|
69
|
+
```json
|
|
70
|
+
{
|
|
71
|
+
"clean": "package-build clean",
|
|
72
|
+
"build:assets": "package-build assets",
|
|
73
|
+
"lint:lang": "package-build lang check",
|
|
74
|
+
"build:pack-release": "package-build release",
|
|
75
|
+
"push:qa": "package-build deploy qa"
|
|
76
|
+
}
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
#### One configuration file, not two
|
|
80
|
+
|
|
81
|
+
Settings come from the reserved `packageBuild:` section of `content-build.config.yaml` — the file a repository already has — which requires **`@heroiclands/content-build` 0.15.0 or later**.
|
|
82
|
+
|
|
83
|
+
```yaml
|
|
84
|
+
packageKind: systems # read from the top level, never restated below
|
|
85
|
+
foundryPackage: sohl
|
|
86
|
+
|
|
87
|
+
packageBuild:
|
|
88
|
+
assets:
|
|
89
|
+
- { from: lang, to: lang }
|
|
90
|
+
assetTransform: ./utils/svg-theme.mjs
|
|
91
|
+
clean:
|
|
92
|
+
extra: [site/content, site/public]
|
|
93
|
+
deploy:
|
|
94
|
+
envPrefix: SOHL
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
A second config file would have restated `packageKind` and `foundryPackage`, which is two places for one fact. content-build validates only that the section is a mapping and hands it back frozen; everything inside it is validated here, so neither package learns the other's schema.
|
|
98
|
+
|
|
99
|
+
**Derived, never stated:** the repository root (the config file's own location), `packageKind` and `packageId` (the shared configuration's top level), and the release artifact — a system ships `system.json`, a module `module.json`, so the kind already decides it.
|
|
100
|
+
|
|
101
|
+
**The one genuine piece of consumer code stays the consumer's.** SoHL rewrites each SVG's hard-coded fill so icons follow the Foundry theme; `packageBuild.assetTransform` names a module exporting `transform(sourcePath) -> string | null`.
|
|
102
|
+
|
|
103
|
+
#### Also in this release
|
|
104
|
+
|
|
105
|
+
- `exports["./config"]` now ships its type declaration. It was declared but never generated — `tsconfig.dts.json` lists its inputs explicitly and `config.mjs` was missing from the list — so the subpath would have pointed at a file that does not exist.
|
|
106
|
+
- The library modules are unchanged. Every signature the CLI needed was already there.
|
|
107
|
+
|
|
108
|
+
#### Notes
|
|
109
|
+
|
|
110
|
+
`--version` and `--help` answer in a directory with no configuration at all. Failures report one line rather than a stack, whether the handler was synchronous or asynchronous. Localization findings follow the diagnostics contract, `file:line:column: severity: message`.
|
|
111
|
+
|
|
112
|
+
**Full changelog:** https://github.com/HeroicLands/package-build/compare/v0.1.0...v0.2.0
|
|
113
|
+
|
|
114
|
+
## 0.1.0
|
|
115
|
+
|
|
116
|
+
_2026-08-22 — the Foundry package toolchain_
|
|
117
|
+
|
|
118
|
+
The counterpart to [`@heroiclands/content-build`](https://github.com/HeroicLands/content-build). The two split by **input**, not by repository: content-build reads `assets/content/**` and answers for what a package *says*; this one reads `lang/`, `styles/`, `src/`, `assets/` and the manifest template, and answers for what a package *is* — the parts Foundry loads whether or not the package ships any content.
|
|
119
|
+
|
|
120
|
+
A module uses either, or both. An adventure module that ships only notes needs no bundler; a variant module that ships only behavior needs no Markdown pipeline. The coupling runs one way: package-build asks content-build for the compiled `packs[]` block, never the reverse.
|
|
121
|
+
|
|
122
|
+
### Seven modules — the whole of assemble → validate → ship
|
|
123
|
+
|
|
124
|
+
- **`manifest`** — `system.json` / `module.json`. The artifact is inferred from the template's name, and every address is derived from `package.json`'s `repository` rather than transcribed. Handles the `git+https://….git` spelling npm writes, which yields a 404 on every Foundry update check if left in place.
|
|
125
|
+
- **`stage`** — assembling the build stage and clearing it away. A listed asset that does not exist **fails the build** instead of shipping a package that quietly lacks its localization or templates; the whole list is checked before anything is copied.
|
|
126
|
+
- **`lang`** — what a shippable localization file must satisfy: it parses, its top level is an object, no key is both a leaf and a dotted prefix of another (which makes Foundry discard the entire file), placeholders are single-braced, key segments carry no data.
|
|
127
|
+
- **`bundle`** — whether the manifest agrees with the file it points at. Declared under `"esmodules"` the bundle must parse as a module; declared under `"scripts"` it must declare **nothing** at top level, because each top-level declaration in a classic script is a global lexical binding and one colliding with a non-configurable `window` property throws at parse time. That rule bricked SoHL v0.8.0.
|
|
128
|
+
- **`release`** — the two assets a GitHub Release carries. Waits for the archive to be *written*, not merely finalized.
|
|
129
|
+
- **`deploy`** — installing into a Foundry data directory, local or SFTP, always as a staged atomic swap: a running Foundry holds its LevelDB packs open, and replacing them in place leaves a directory LevelDB "repairs" to zero.
|
|
130
|
+
- **`text`** — locating a literal, so a finding names its line and column.
|
|
131
|
+
|
|
132
|
+
### Design
|
|
133
|
+
|
|
134
|
+
The rules are pure functions over data; the functions that touch disk or a network are named for what they do. That is what makes them testable at all — the scripts they were extracted from each ran their work at import time and exported nothing.
|
|
135
|
+
|
|
136
|
+
### Provenance
|
|
137
|
+
|
|
138
|
+
Developed as a workspace inside the Song of Heroic Lands repository across six changes (SoHL#1680, #1682, #1684, #1685, #1686), exactly as content-build incubated before its own extraction, and extracted now that its shape has stopped changing.
|
|
139
|
+
|
|
140
|
+
128 tests; declaration files are emitted from the JSDoc at pack time.
|
package/README.md
CHANGED
|
@@ -198,6 +198,43 @@ would let something reach for one.
|
|
|
198
198
|
declared `dependency`. Inside a workspace a missing declaration is invisible;
|
|
199
199
|
installed from npm it fails on the first import.
|
|
200
200
|
|
|
201
|
+
## Releasing
|
|
202
|
+
|
|
203
|
+
Releasing is not a command anyone runs. It is a consequence of merging, in two
|
|
204
|
+
steps, and each step is visible while it is pending.
|
|
205
|
+
|
|
206
|
+
**Every pull request declares its bump.** Run `npx changeset` and pick
|
|
207
|
+
major/minor/patch; the summary you write becomes the changelog entry and the
|
|
208
|
+
release note. If the change ships nothing a consumer can see, say so explicitly
|
|
209
|
+
with `npx changeset add --empty`. CI's **Changeset declared** job fails a pull
|
|
210
|
+
request that declares neither — `npm run changeset:check` is the same check,
|
|
211
|
+
locally.
|
|
212
|
+
|
|
213
|
+
**Merging to `main` opens a Version Packages pull request** carrying the version
|
|
214
|
+
bump and the rewritten `CHANGELOG.md`. That pull request _is_ the pending
|
|
215
|
+
release: as long as something is merged but unpublished, there is an open pull
|
|
216
|
+
request saying so. The previous, hand-driven process failed by leaving _nothing_
|
|
217
|
+
behind when its final step was forgotten (#4).
|
|
218
|
+
|
|
219
|
+
**Merging that publishes.** `changeset publish` puts the version on npm through
|
|
220
|
+
Trusted Publishing (OIDC — there is no `NPM_TOKEN`), tags the commit `v<version>`
|
|
221
|
+
and cuts the GitHub Release with the changelog section as its body. It publishes
|
|
222
|
+
only versions that are not already on the registry, so re-running it is a no-op;
|
|
223
|
+
`workflow_dispatch` on **Publish to npm** is the recovery path if a run fails
|
|
224
|
+
after versioning.
|
|
225
|
+
|
|
226
|
+
A changeset is also where a **raised dependency floor** gets recorded. 0.2.0 began
|
|
227
|
+
requiring `@heroiclands/content-build >= 0.15.0` and announced it nowhere; a
|
|
228
|
+
changeset is the place that now happens.
|
|
229
|
+
|
|
230
|
+
Below 1.0.0, `^0.x` never crosses a minor — a consumer on `^0.2.0` will not see
|
|
231
|
+
`0.3.0` until it bumps the pin deliberately, and Dependabot raises that as its own
|
|
232
|
+
pull request.
|
|
233
|
+
|
|
234
|
+
> After a successful publish, `npm view @heroiclands/package-build version` can
|
|
235
|
+
> report the _previous_ version for a minute or so. `dist-tags` is correct
|
|
236
|
+
> immediately, and is what the workflow prints.
|
|
237
|
+
|
|
201
238
|
## Licence
|
|
202
239
|
|
|
203
240
|
GPL-3.0-or-later.
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@heroiclands/package-build",
|
|
3
|
-
"version": "0.2.
|
|
4
|
-
"description": "Shared toolchain for building and shipping a HeroicLands Foundry VTT package
|
|
3
|
+
"version": "0.2.1",
|
|
4
|
+
"description": "Shared toolchain for building and shipping a HeroicLands Foundry VTT package \u2014 manifest, localization, staging, bundle, release and deployment.",
|
|
5
5
|
"license": "GPL-3.0-or-later",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"main": "./index.mjs",
|
|
@@ -55,6 +55,7 @@
|
|
|
55
55
|
"stage.mjs",
|
|
56
56
|
"text.mjs",
|
|
57
57
|
"types",
|
|
58
|
+
"CHANGELOG.md",
|
|
58
59
|
"README.md",
|
|
59
60
|
"bin",
|
|
60
61
|
"config.mjs"
|
|
@@ -66,6 +67,8 @@
|
|
|
66
67
|
"build:types": "tsc -p tsconfig.dts.json",
|
|
67
68
|
"format": "prettier --write .",
|
|
68
69
|
"format:check": "prettier --check .",
|
|
70
|
+
"changeset": "changeset",
|
|
71
|
+
"changeset:check": "changeset status --since=origin/main",
|
|
69
72
|
"prepare": "git config core.hooksPath .githooks || true"
|
|
70
73
|
},
|
|
71
74
|
"dependencies": {
|
|
@@ -78,6 +81,7 @@
|
|
|
78
81
|
"yargs": "^18.1.0"
|
|
79
82
|
},
|
|
80
83
|
"devDependencies": {
|
|
84
|
+
"@changesets/cli": "^3.0.0",
|
|
81
85
|
"@types/node": "^26.2.0",
|
|
82
86
|
"prettier": "^3.9.6",
|
|
83
87
|
"typescript": "^6.0.3",
|