@heroiclands/package-build 6.1.0 → 7.0.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 +614 -0
- package/CONTENT.md +21 -1
- package/bin/content-build.mjs +29 -10
- package/bin/package-build.mjs +8 -1
- package/config.mjs +25 -3
- package/content-config.mjs +254 -22
- package/engine/base-compiler.mjs +25 -0
- package/engine/content-links.mjs +132 -27
- package/engine/diagnostics.mjs +61 -1
- package/engine/generate.mjs +10 -5
- package/engine/helpers.mjs +38 -0
- package/engine/journals.mjs +8 -1
- package/engine/macros.mjs +2 -0
- package/engine/pack-config.mjs +143 -13
- package/engine/prose-lint.mjs +10 -2
- package/engine/scenes.mjs +2 -2
- package/engine/web-wikilinks.mjs +13 -4
- package/engine/wikilink-syntax.mjs +25 -0
- package/engine/wikilinks.mjs +6 -3
- package/manifest.mjs +37 -2
- package/package.json +5 -3
- package/sohl/actors.mjs +23 -10
- package/sohl/items.mjs +1 -1
- package/types/content-config.d.mts +14 -0
- package/types/engine/base-compiler.d.mts +18 -1
- package/types/engine/content-links.d.mts +11 -3
- package/types/engine/diagnostics.d.mts +33 -1
- package/types/engine/generate.d.mts +3 -2
- package/types/engine/helpers.d.mts +29 -3
- package/types/engine/journals.d.mts +7 -1
- package/types/engine/pack-config.d.mts +22 -0
- package/types/engine/prose-lint.d.mts +10 -2
- package/types/engine/wikilink-syntax.d.mts +24 -0
- package/types/sohl/actors.d.mts +3 -3
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,619 @@
|
|
|
1
1
|
# @heroiclands/package-build
|
|
2
2
|
|
|
3
|
+
## 7.0.0
|
|
4
|
+
|
|
5
|
+
### Major Changes
|
|
6
|
+
|
|
7
|
+
- a136429: Separate declaring a system from requiring one, and stamp `_stats` per pack
|
|
8
|
+
(#48).
|
|
9
|
+
|
|
10
|
+
A module shipping content for two systems could not say so. The only place to
|
|
11
|
+
state a system version was `relationships.systems`, and that list is a
|
|
12
|
+
**restriction**: Foundry's `supportsSystem` drops a package from any world whose
|
|
13
|
+
system it does not name. So the two needs were in direct conflict — name your
|
|
14
|
+
systems and become unloadable elsewhere, or stay loadable and stamp nothing.
|
|
15
|
+
|
|
16
|
+
**`harn-ensemble` is the case, and it is live.** It declares an `actors-hm3`
|
|
17
|
+
pack and an `actors-sohl` pack, and resolves to:
|
|
18
|
+
|
|
19
|
+
```json
|
|
20
|
+
{ "statsSystemId": null, "statsSystemVersion": null }
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
Nothing stamped, on content that was certainly built against `hm3 1.6.3` and
|
|
24
|
+
`sohl 0.8.2`. It takes that path deliberately, because declaring the two systems
|
|
25
|
+
would hide the module from every other world — including the ones that want only
|
|
26
|
+
its system-neutral journals pack.
|
|
27
|
+
|
|
28
|
+
**The split.**
|
|
29
|
+
|
|
30
|
+
| | describes | gates |
|
|
31
|
+
| ----------------- | -------------------------------------------- | --------------------------- |
|
|
32
|
+
| `systems:` | which systems this package can stamp against | **nothing** |
|
|
33
|
+
| `requiresSystem:` | — | where the package will load |
|
|
34
|
+
|
|
35
|
+
Naming a system under `systems:` restricts nothing. `requiresSystem` is separate
|
|
36
|
+
and optional, and emits the `relationships.systems` entry Foundry reads —
|
|
37
|
+
_reusing_ the declaration rather than restating it, because
|
|
38
|
+
`stats.systemVersion` sat at `0.6.0` for four releases when a transcription was
|
|
39
|
+
free to disagree with what it copied.
|
|
40
|
+
|
|
41
|
+
```yaml
|
|
42
|
+
systems:
|
|
43
|
+
hm3: { compatibility: { minimum: "1.6.3", verified: "1.6.3" } }
|
|
44
|
+
sohl: { compatibility: { minimum: "0.8.2", verified: "0.8.2" } }
|
|
45
|
+
requiresSystem: null # optional; omitted, the package loads anywhere
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
**A pack's `system:` now selects what stamps its documents.** `_stats` was one
|
|
49
|
+
memoised block for the whole package, so every document in every pack was
|
|
50
|
+
stamped identically. It is per pack now: `statsForPack()` resolves the pack's
|
|
51
|
+
declared system through `systems:`, and `BasePackCompiler` exposes it as
|
|
52
|
+
`this.stats`, memoised per instance — one pass, one pack, one system.
|
|
53
|
+
|
|
54
|
+
**`systemId` travels with `systemVersion`.** They are one decision, so where one
|
|
55
|
+
is omitted both are. Stamping a per-pack version against a package-wide id would
|
|
56
|
+
emit `systemId: sohl, systemVersion: 1.6.3` on HM3 documents — a plausible lie,
|
|
57
|
+
which is worse than the missing value #43 fixed, because nothing about it looks
|
|
58
|
+
wrong.
|
|
59
|
+
|
|
60
|
+
**A name that resolves to nothing is an error.** A pack's `system:` must name a
|
|
61
|
+
declared system; `requiresSystem` must too; and with a gate set, a pack naming a
|
|
62
|
+
_different_ system is refused outright — Foundry would hide the whole package
|
|
63
|
+
from any world that pack could have appeared in, so it would ship and be
|
|
64
|
+
unreachable.
|
|
65
|
+
|
|
66
|
+
**Nothing that ships today moves.** All six consumers were resolved before and
|
|
67
|
+
after; none declares a `systems:` block yet, so every pack still falls through to
|
|
68
|
+
the package-wide block it used before:
|
|
69
|
+
|
|
70
|
+
| package | `systems:` | `requiresSystem` | stamps | packs unchanged |
|
|
71
|
+
| -------------------- | ---------: | ---------------- | -------------- | --------------- |
|
|
72
|
+
| `sohl` | 0 | — | `sohl / 0.8.2` | yes |
|
|
73
|
+
| `sohl-thalorna` | 0 | — | `sohl / 0.8.2` | yes |
|
|
74
|
+
| `sohl-kethira-basic` | 0 | — | `sohl / 0.8.2` | yes |
|
|
75
|
+
| `harn-ensemble` | 0 | — | `— / —` | yes |
|
|
76
|
+
| `harn-adventures` | 0 | — | `— / —` | yes |
|
|
77
|
+
| `hm3` | 0 | — | `hm3 / 1.6.3` | yes |
|
|
78
|
+
|
|
79
|
+
**`stats.systemId` and `stats.systemVersion` are refused outright.** Authoring a
|
|
80
|
+
derived value is an error rather than an override, which is the rule this
|
|
81
|
+
configuration already applies elsewhere — and the reason is the same one that let
|
|
82
|
+
`stats.systemVersion` sit at `0.6.0` for four releases: a transcribed copy is
|
|
83
|
+
free to drift from what it copied, and nothing reads a stamped `_stats` until
|
|
84
|
+
something migrates on it. The refusal names the key, its line, and what supplies
|
|
85
|
+
it now.
|
|
86
|
+
|
|
87
|
+
The value still has to reach the validator from the loader, which is the half
|
|
88
|
+
that may read the adjacent `package.json`. It travels under a **symbol**, so the
|
|
89
|
+
channel is not a second, forgeable spelling of the key just refused: a symbol
|
|
90
|
+
cannot be written in YAML and does not appear in `Object.keys`.
|
|
91
|
+
|
|
92
|
+
**`relationships.systems` keeps working, and still answers.** It carries
|
|
93
|
+
`itemCatalog` — a separate concern this split does not replace — so a repository
|
|
94
|
+
using it would otherwise have to restate its compatibility under `systems:`
|
|
95
|
+
purely to keep stamping, which is the duplication the change exists to remove. A
|
|
96
|
+
lone relationship is a declaration as much as a gate, so it derives `systemId`
|
|
97
|
+
too. Several have no single answer and get none.
|
|
98
|
+
|
|
99
|
+
**Every consumer was migrated in the same change.** One line each:
|
|
100
|
+
|
|
101
|
+
```diff
|
|
102
|
+
stats:
|
|
103
|
+
- systemId: sohl
|
|
104
|
+
lastModifiedBy: sohlbuilder00000
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
`sohl` and `hm3` are systems and are their own system by construction;
|
|
108
|
+
`sohl-thalorna` and `sohl-kethira-basic` derive it from the single system
|
|
109
|
+
relationship they already declare; `harn-ensemble` and `harn-adventures` declared
|
|
110
|
+
none and were already clean.
|
|
111
|
+
|
|
112
|
+
**Bump**
|
|
113
|
+
|
|
114
|
+
_Major._ `stats.systemId` and `stats.systemVersion` were accepted and are now
|
|
115
|
+
refused, so a configuration that resolved before can fail — which is the
|
|
116
|
+
definition this repository uses. Every HeroicLands consumer is migrated in
|
|
117
|
+
lockstep and verified to stamp exactly what it stamped before, but a consumer
|
|
118
|
+
outside that set must delete the two keys.
|
|
119
|
+
|
|
120
|
+
Part of #57, the third of its three keys that both describe and gate. #56 is
|
|
121
|
+
done, #49 shipped in 6.2.0, and this is #48.
|
|
122
|
+
|
|
123
|
+
### Minor Changes
|
|
124
|
+
|
|
125
|
+
- db40b24: Address another package's landing without naming a host (#87).
|
|
126
|
+
|
|
127
|
+
A link from one package's page to another package's landing had no form but a
|
|
128
|
+
hardcoded absolute URL, and the homepage link check (#54) exempted one — a
|
|
129
|
+
finding whose fix does not exist is noise. `sohl-kethira-basic`'s homepage writes
|
|
130
|
+
`https://www.heroiclands.org/sohl/` twice.
|
|
131
|
+
|
|
132
|
+
**The premise the exemption rested on is true, and does not lead where it looked
|
|
133
|
+
like it led.** A landing is in no link manifest: it compiles to no document and
|
|
134
|
+
is entered in no index. The reading that follows is that nothing can resolve it.
|
|
135
|
+
But a landing's address is not a _note's_ address — it is the **package's**, and
|
|
136
|
+
`PACKAGE_BASE` has recorded where each package is served all along. Consulting it
|
|
137
|
+
walks no tree, reads no manifest and builds no index, which is exactly why the
|
|
138
|
+
mechanism survives the fence: `kethira` and `harnadventures` publish a homepage
|
|
139
|
+
and nothing else, and that mode never walks a content tree.
|
|
140
|
+
|
|
141
|
+
**So the authored form is the absolute URL with the host struck off.** `/sohl/`
|
|
142
|
+
in a body, or `href: /sohl/` in a card. Both were already accepted here — nothing
|
|
143
|
+
had ever named one as the form to use, which is the whole of what was missing.
|
|
144
|
+
|
|
145
|
+
| Field | Cross-package landing | Why |
|
|
146
|
+
| ------- | --------------------- | ----------------------------------------- |
|
|
147
|
+
| body | `[SoHL](/sohl/)` | emitted verbatim, resolved by the browser |
|
|
148
|
+
| `href:` | `/sohl/` | "already resolved, used verbatim" |
|
|
149
|
+
| `url:` | **not expressible** | package-relative by construction |
|
|
150
|
+
|
|
151
|
+
`url:` cannot leave its own package, so a `url:` naming another package's landing
|
|
152
|
+
is now reported with the field as the fix rather than a path — previously it was
|
|
153
|
+
told to "write `/`", which is nonsense arrived at by taking the path after the
|
|
154
|
+
prefix when there is nothing after the prefix.
|
|
155
|
+
|
|
156
|
+
**The base comes from the roster, not from the prefix.** The finding names
|
|
157
|
+
`PACKAGE_BASE[pkg]`, so a package the roster relocates (`/setting/thalorna/`) is
|
|
158
|
+
addressed where it actually is. That is the property the manifest enforces
|
|
159
|
+
everywhere else — the address published is the address emitted — reaching these
|
|
160
|
+
links for the first time.
|
|
161
|
+
|
|
162
|
+
**Roster for landings only.** Widening the package set the other rules read would
|
|
163
|
+
have them offer manifest-based advice about packages no manifest is vendored for.
|
|
164
|
+
An in-site path naming no known package is still left alone: several surfaces a
|
|
165
|
+
landing routes to are built by other tools, and this build does not hold the set
|
|
166
|
+
of published pages.
|
|
167
|
+
|
|
168
|
+
**Measured across every homepage authored today.** All five were run before and
|
|
169
|
+
after, each under its own `package-build.config.yaml`:
|
|
170
|
+
|
|
171
|
+
| Package | Before | After | |
|
|
172
|
+
| ------------------------- | ------ | ----- | ----------------------- |
|
|
173
|
+
| `sohl-kethira-basic` | 0 | **2** | the two the issue names |
|
|
174
|
+
| `sohl-thalorna` | 0 | 0 | |
|
|
175
|
+
| `harn-ensemble` | 0 | 0 | |
|
|
176
|
+
| `harn-adventures` | 0 | 0 | |
|
|
177
|
+
| `HarnMaster-3-FoundryVTT` | 0 | 0 | |
|
|
178
|
+
|
|
179
|
+
Kethira vendors no manifest at all — it is homepage-only — so it is also the
|
|
180
|
+
proof that the roster reaches where an index does not. Applying the fix the
|
|
181
|
+
finding names takes it back to 0.
|
|
182
|
+
|
|
183
|
+
**Sequencing, stated because it decides the bump.** These are hard errors:
|
|
184
|
+
`severity: "error"`, counted into `failures`, `exitCode 1`. By this repository's
|
|
185
|
+
own rule — a new hard error is breaking only if it fails a previously-passing
|
|
186
|
+
consumer — this is minor **only once kethira's two lines are converted**, and
|
|
187
|
+
major if it ships before them. The conversion does not wait on this release:
|
|
188
|
+
`/sohl/` already passes under the current published version, verified, so it can
|
|
189
|
+
land in `sohl-kethira-basic` immediately and independently. It must land first.
|
|
190
|
+
|
|
191
|
+
**Bump**
|
|
192
|
+
|
|
193
|
+
_Minor, not patch, and not major._ Not patch: a check that previously passed a
|
|
194
|
+
page can now fail it. Not major, on the condition above — with kethira converted,
|
|
195
|
+
every homepage authored today passes before and after, and no export, option or
|
|
196
|
+
emitted document changes shape.
|
|
197
|
+
- 35bde43: Locate a configuration error in the file it was written in (#95).
|
|
198
|
+
|
|
199
|
+
Every check across `content-config.mjs` and `config.mjs` — 81 of them — reports
|
|
200
|
+
through one `fail()`, which named the offending key's **dotted path** and
|
|
201
|
+
nothing else. That is a good description and a bad locator: nothing in the line
|
|
202
|
+
is a path an editor can open or a CI annotator can resolve, in a file that runs
|
|
203
|
+
to 400 lines with fourteen sibling entries under `sections:` alone, several of
|
|
204
|
+
them flow-mapped onto one line.
|
|
205
|
+
|
|
206
|
+
The path now rides on the error as a `field`, and the loader that read the file
|
|
207
|
+
resolves it — through `positionOfYamlPath`, the same locator `manifest`'s
|
|
208
|
+
`packFolders` findings already use — so all 81 come out in the
|
|
209
|
+
`file:line:column: severity: message` form every other finding uses, path first:
|
|
210
|
+
|
|
211
|
+
```text
|
|
212
|
+
package-build.config.yaml:382:64: error: package-build config: `site.sections.being.descrption` is not a recognized option (expected one of: title, banner, description).
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
**Located at the boundary, not at the check.** `content-config.mjs` is the leaf
|
|
216
|
+
an `.mjs` configuration imports and performs no I/O, so it attaches the path and
|
|
217
|
+
`configFromData` — which knows the file — formats it. `config.mjs`'s pure
|
|
218
|
+
`resolvePackageBuildConfig` is unchanged for the same reason;
|
|
219
|
+
`loadPackageBuildConfig` is where its findings are located.
|
|
220
|
+
|
|
221
|
+
**Positions are dropped, never guessed.** A required key the file never declared
|
|
222
|
+
has no node of its own, so the position names the **mapping it belongs in**, one
|
|
223
|
+
level up and no further — that entry is a real node and the one the reader must
|
|
224
|
+
edit. A missing _top-level_ key has nothing above it but the document, and an
|
|
225
|
+
`.mjs` configuration has no YAML to resolve a path against at all (parsing
|
|
226
|
+
JavaScript as YAML would resolve some paths to lines that mean nothing). Both
|
|
227
|
+
report `package-build.config.yaml: error: …` — the file, without a line.
|
|
228
|
+
|
|
229
|
+
Both command lines print a located failure unprefixed, since `package-build: `
|
|
230
|
+
and `loglevel`'s `[timestamp] [ERROR]:` occupy exactly the position a parser
|
|
231
|
+
reads the path from.
|
|
232
|
+
|
|
233
|
+
**Additive.** A valid configuration resolves exactly as before; only the text of
|
|
234
|
+
a rejection changes, and it keeps the body it had. `positionOfYamlPath` gains an
|
|
235
|
+
optional `{ key: true }` — report where a key is _declared_ rather than where
|
|
236
|
+
its value sits, which is what a message naming a field wants — and
|
|
237
|
+
`engine/diagnostics` gains `yamlKeyPath`, the one translation between a dotted
|
|
238
|
+
path and a YAML key path.
|
|
239
|
+
- b7ad450: Compile actors without an Item pack of this package's own (#49).
|
|
240
|
+
|
|
241
|
+
The actors pass threw unless the package declared at least one pack of type
|
|
242
|
+
`Item`. An Item pack is system-bound by construction — Foundry requires `system`
|
|
243
|
+
on Item packs and on few others — so the guard asked a deliberately
|
|
244
|
+
system-agnostic module to declare the very thing it exists not to depend on.
|
|
245
|
+
|
|
246
|
+
**This is not hypothetical, and it is not a future case.** `harn-ensemble`
|
|
247
|
+
declares two Actor packs and no Item pack at all in its
|
|
248
|
+
`package-build.config.yaml` today. Run against that configuration, the compiler
|
|
249
|
+
does not start:
|
|
250
|
+
|
|
251
|
+
```text
|
|
252
|
+
packs declared : actors-hm3(Actor), actors-sohl(Actor)
|
|
253
|
+
itemPackJsonDirs : []
|
|
254
|
+
Actors : THREW — Actors compiler requires `itemsSourceDirs` …
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
Its 2,512 beings resolve their embedded items — `skill:awar`, `attribute:str`,
|
|
258
|
+
`weapongear:…` — against the `sohl` and `hm3` catalogues through
|
|
259
|
+
`foreignSourceDirs`. The optional mechanism is the one that matters there; the
|
|
260
|
+
mandatory one had nothing to contribute.
|
|
261
|
+
|
|
262
|
+
**The guard did not test what it claimed.** It counted _declared directories_,
|
|
263
|
+
not resolvable items. An Item pack containing no documents satisfied it, while a
|
|
264
|
+
being naming an item nothing defines still failed later — so it neither
|
|
265
|
+
prevented the failure it named nor reported it where it happened. And its
|
|
266
|
+
remedy, "declare at least one pack of type `Item`", is the opposite of the fix
|
|
267
|
+
for a system-agnostic package.
|
|
268
|
+
|
|
269
|
+
**The condition actually cared about was already checked, at the site of the
|
|
270
|
+
mistake.** `resolveEmbedded` reports each unresolved `(type, shortcode)` by
|
|
271
|
+
name, with the being as context, and counts it — and those counts aggregate into
|
|
272
|
+
`totalErrors`, so a package genuinely missing an item still fails the build:
|
|
273
|
+
|
|
274
|
+
```text
|
|
275
|
+
Bandit: no predefined item for "skill:awar"
|
|
276
|
+
```
|
|
277
|
+
|
|
278
|
+
That is the same line #43 and the frontmatter-lint work drew: a structural
|
|
279
|
+
precondition that is cheap to state is not the condition you care about, and
|
|
280
|
+
reporting where the mistake is beats refusing to start.
|
|
281
|
+
|
|
282
|
+
**Nothing tightens.** `itemsSourceDirs` defaults to `[]` and is otherwise
|
|
283
|
+
unchanged; `foreignSourceDirs` is untouched; `itemPackJsonDirs` already returned
|
|
284
|
+
an empty list for a repository with no Item packs, and only the constructor
|
|
285
|
+
rejected it. A package that declares Item packs behaves exactly as before — the
|
|
286
|
+
1,647 existing tests pass unchanged, with four added for the empty case and for
|
|
287
|
+
the point-of-use error that replaces the guard.
|
|
288
|
+
|
|
289
|
+
**Bump**
|
|
290
|
+
|
|
291
|
+
_Minor, not patch._ No consumer that compiled before fails now — the change only
|
|
292
|
+
removes a refusal — but a configuration that was rejected is now supported, which
|
|
293
|
+
is new surface rather than a repair to existing surface. Not major for the same
|
|
294
|
+
reason: nothing that was accepted stops being accepted.
|
|
295
|
+
|
|
296
|
+
Part of #57, which is the same defect in three places: a key that both describes
|
|
297
|
+
and gates, so the legitimate case cannot be expressed. Here the gate is removed
|
|
298
|
+
and the description — which Item packs this package ships — is left saying only
|
|
299
|
+
that.
|
|
300
|
+
|
|
301
|
+
### Patch Changes
|
|
302
|
+
|
|
303
|
+
- 8aab711: Bump `glob` from 11.1.0 to 13.0.6.
|
|
304
|
+
|
|
305
|
+
**Both majors are changes to glob's command-line program, not its library.**
|
|
306
|
+
|
|
307
|
+
- **12** removed the unsafe `--shell` option, keeping it only on shells where it
|
|
308
|
+
can be implemented safely (the remediation for GHSA-5j98-mcp5-4vw2, whose
|
|
309
|
+
mitigation 11.1 had introduced).
|
|
310
|
+
- **13** moved the CLI out to a separate `glob-bin` package.
|
|
311
|
+
|
|
312
|
+
This repository never invokes that program. The single use of the library is
|
|
313
|
+
`globSync(patterns, { cwd, absolute: true })` in `readMatching`
|
|
314
|
+
(`bin/package-build.mjs`), which is untouched across both majors — no workflow,
|
|
315
|
+
hook, or script calls `glob` from a shell, so the removed binary is not a
|
|
316
|
+
dependency this package had.
|
|
317
|
+
|
|
318
|
+
**Verified rather than assumed.** `globSync` was exercised under 13.0.6 with the
|
|
319
|
+
options `readMatching` actually passes, returning the same absolute paths; the
|
|
320
|
+
repository's 1,642 tests pass unchanged.
|
|
321
|
+
|
|
322
|
+
**Bump**
|
|
323
|
+
|
|
324
|
+
_Patch, not minor._ Nothing in this package's surface moves, and the majors are
|
|
325
|
+
the library's own. Worth noting for a consumer only in one case: a repository
|
|
326
|
+
that installed `glob` transitively through this package and relied on
|
|
327
|
+
`node_modules/.bin/glob` being present must now depend on `glob-bin` directly.
|
|
328
|
+
That was never a supported edge of this package, and no HeroicLands repository
|
|
329
|
+
does it.
|
|
330
|
+
- 8e0778e: Bump `markdown-it` from 14.3.0 to 15.0.0.
|
|
331
|
+
|
|
332
|
+
This package's entire use of the library is `markdownit({ html: true })` and
|
|
333
|
+
`md.render(body)`, at three call sites — `engine/helpers.mjs`,
|
|
334
|
+
`engine/journals.mjs` and `sohl/actors.mjs`. Nothing overrides a renderer rule,
|
|
335
|
+
installs a plugin, or reaches into the parser.
|
|
336
|
+
|
|
337
|
+
**Every breaking change in 15.0.0 lands outside that surface.**
|
|
338
|
+
|
|
339
|
+
| Breaking change | Why it does not reach here |
|
|
340
|
+
| ---------------------------------------------------------------------- | ------------------------------------------------------------------------------- |
|
|
341
|
+
| `linkify-it` → v6: no fuzzy links, no auth check, CJK link termination | `linkify` defaults to `false` and is never enabled, so the linkifier never runs |
|
|
342
|
+
| Package-internal subpath exports (`markdown-it/lib/*`) removed | Only the package root is imported |
|
|
343
|
+
| `validateLink`/`normalizeLink`/`normalizeLinkText` moved to prototype | None is read, assigned, or overridden |
|
|
344
|
+
| `StateBlock#ddIndent` removed | No plugin is installed, `markdown-it-deflist` included |
|
|
345
|
+
| Root now resolves to prebuilt `dist/` rather than raw sources | Import is by package name; the resolved path was never depended on |
|
|
346
|
+
|
|
347
|
+
**Verified rather than assumed.** The same corpus was rendered through 14.3.0
|
|
348
|
+
and 15.0.0 and diffed byte-for-byte, covering exactly the constructs the release
|
|
349
|
+
touched — bare URLs, autolinks, email-shaped text, reference links and their
|
|
350
|
+
definitions, CJK adjacent to a URL, hard line breaks, raw HTML, tables, nested
|
|
351
|
+
lists, and both fenced and indented code. Output is identical. The repository's
|
|
352
|
+
1,642 tests pass unchanged.
|
|
353
|
+
|
|
354
|
+
**Bump**
|
|
355
|
+
|
|
356
|
+
_Patch, not minor._ No export, option, or emitted document changes shape, and no
|
|
357
|
+
behaviour a consumer can observe moves. The version is a major on the library's
|
|
358
|
+
own surface, none of which this package presents onward.
|
|
359
|
+
- 6318722: Add `.github/dependabot.yml`. This repository had none, so nothing proposed a
|
|
360
|
+
dependency update — not npm, not GitHub Actions.
|
|
361
|
+
|
|
362
|
+
That matters more here than in a consumer: this is the toolchain every other
|
|
363
|
+
HeroicLands package builds with, and it is published to npm, so its dependency
|
|
364
|
+
tree reaches every consumer's build and everyone who installs it. The
|
|
365
|
+
consuming repositories are already covered; this producer was the one link in
|
|
366
|
+
the chain nothing watched.
|
|
367
|
+
|
|
368
|
+
`@foundryvtt/foundryvtt-cli` and `classic-level` are each an ungrouped
|
|
369
|
+
single-package entry, ordered ahead of the housekeeping catch-all, for every
|
|
370
|
+
update type including minor and patch. Both sit directly on the compendium
|
|
371
|
+
pack pipeline, and a version bump that silently changes how a pack compiles is
|
|
372
|
+
exactly the failure this repository's byte-level output comparisons exist to
|
|
373
|
+
catch — grouping either into a housekeeping pull request would hide which
|
|
374
|
+
dependency caused a regression.
|
|
375
|
+
|
|
376
|
+
No `ignore` entries: nothing here has a known-bad range to record.
|
|
377
|
+
`typescript` is a direct dependency, but `build.yml`'s "Declaration emit" step
|
|
378
|
+
already gates a breaking bump on every pull request.
|
|
379
|
+
|
|
380
|
+
Closes #100.
|
|
381
|
+
- cd32ea1: Render `[[target|]]` as the target's name on the web, as the packs already do
|
|
382
|
+
(#113).
|
|
383
|
+
|
|
384
|
+
A wikilink with an explicit but empty label produced an **empty anchor** —
|
|
385
|
+
`[](/rules/sohl-shock/)`, a link with no clickable text — silently, through
|
|
386
|
+
every site build:
|
|
387
|
+
|
|
388
|
+
```text
|
|
389
|
+
[[doc-shock]] => [Shock](/rules/sohl-shock/)
|
|
390
|
+
[[doc-shock|]] => [](/rules/sohl-shock/) ← before
|
|
391
|
+
[[doc-shock|]] => [Shock](/rules/sohl-shock/) ← after
|
|
392
|
+
[[#sec|]] => [](#sec) ← before
|
|
393
|
+
```
|
|
394
|
+
|
|
395
|
+
**The two resolvers had drawn the same line in two places, differently.**
|
|
396
|
+
`parseWikilink` distinguishes "no label" from "empty label" deliberately, and
|
|
397
|
+
its docstring says so — _"`null` and `""` differ: an author may write
|
|
398
|
+
`[[x|]]`"_. The packs resolver honoured that by testing falsiness. The web
|
|
399
|
+
resolver used `??`, which falls through on `null` only, so `""` survived to the
|
|
400
|
+
output at three sites: the same-page anchor, the resolved link, and the
|
|
401
|
+
unresolved link.
|
|
402
|
+
|
|
403
|
+
That is the third instance of exactly the drift `wikilink-syntax.mjs` was
|
|
404
|
+
created to stop — its module docstring opens on the two copies of the link
|
|
405
|
+
parser having already diverged. The parse was centralised; the _interpretation
|
|
406
|
+
of the parsed parts_ was not, and it drifted in the one case the docstring
|
|
407
|
+
names.
|
|
408
|
+
|
|
409
|
+
**So the reading is stated once.** `authoredLabel()` joins the syntax module and
|
|
410
|
+
both resolvers consult it, rather than each deciding what an empty label means.
|
|
411
|
+
`labelled` is untouched and still separates `[[x]]` from `[[x|]]`, which is what
|
|
412
|
+
#1409 actually depends on — an unlabelled `[[Shock State]]` still shows the
|
|
413
|
+
author's own prose rather than the canonical name.
|
|
414
|
+
|
|
415
|
+
**Why it is worth a release rather than waiting.** `[[x|]]` becomes load-bearing
|
|
416
|
+
under the four-segment address grammar (#59): the pipe is what distinguishes an
|
|
417
|
+
address lookup from an alias lookup, so `[[target|]]` is the canonical way to
|
|
418
|
+
write an address that displays its target's name — and the planned migration
|
|
419
|
+
rewrites every authored address link into that form. Converting the corpus into
|
|
420
|
+
a form that renders an empty anchor would be a corpus-wide regression, so this
|
|
421
|
+
has to land first.
|
|
422
|
+
|
|
423
|
+
**Bump**
|
|
424
|
+
|
|
425
|
+
_Patch._ A defect fix with no new surface. `authoredLabel` is exported because
|
|
426
|
+
both resolvers import it, not as a feature for consumers; no option, address, or
|
|
427
|
+
emitted document changes shape, and no link that renders correctly today renders
|
|
428
|
+
differently after.
|
|
429
|
+
- bc2c5c8: Bump `@types/node` in the lockfile for development tooling maintenance.
|
|
430
|
+
- 1a4d882: Hold `typescript` at major 6 in Dependabot, because TypeScript 7 removes the
|
|
431
|
+
compiler API `coverage.mjs` parses with.
|
|
432
|
+
|
|
433
|
+
Dependabot proposed 6.0.3 → 7.0.2 (#105). It cannot be taken.
|
|
434
|
+
|
|
435
|
+
**TypeScript 7 is the native port, and its npm package no longer ships the
|
|
436
|
+
JavaScript compiler API.** The `"."` export resolves to `lib/version.cjs`, whose
|
|
437
|
+
entire surface is `version` and `versionMajorMinor`.
|
|
438
|
+
|
|
439
|
+
`coverage.mjs` uses that API as a _parser_, not as a compiler: it reads
|
|
440
|
+
localization keys out of a consumer's `src/**/*.{ts,mjs}` by walking a real AST,
|
|
441
|
+
deliberately down one path so JavaScript and TypeScript cannot drift. Under 7.0.2
|
|
442
|
+
the AST **vocabulary** survives behind `typescript/unstable/ast` — `ScriptTarget`
|
|
443
|
+
and every `isX` guard the scan uses — but the three things that actually drive it
|
|
444
|
+
exist nowhere in the JS surface:
|
|
445
|
+
|
|
446
|
+
| Needed by `coverage.mjs` | In 7.0.2 |
|
|
447
|
+
| ------------------------------------------------------------- | ------------------------------------------ |
|
|
448
|
+
| `createSourceFile` | **missing** — only a factory of that name¹ |
|
|
449
|
+
| `forEachChild` | **missing** |
|
|
450
|
+
| `flattenDiagnosticMessageText` | **missing** |
|
|
451
|
+
| `ScriptTarget`, `isStringLiteral`, `isPropertyDeclaration`, … | present, behind `unstable/ast` |
|
|
452
|
+
|
|
453
|
+
¹ 7's `createSourceFile` assembles a SourceFile from statements already parsed.
|
|
454
|
+
It is not a parser.
|
|
455
|
+
|
|
456
|
+
**The gate this file predicted would catch it did not.** The previous comment in
|
|
457
|
+
`dependabot.yml` recorded no ignore entry on the reasoning that `build.yml`'s
|
|
458
|
+
"Declaration emit" step already guards a breaking `typescript` bump. That step
|
|
459
|
+
passes clean under 7.0.2 — `tsc` still emits every `.d.mts`. What failed was
|
|
460
|
+
`npm test`: 11 failures in `tests/coverage.test.ts`, all
|
|
461
|
+
`TypeError: Cannot read properties of undefined (reading 'Latest')`. A dependency
|
|
462
|
+
can be load-bearing in two unrelated ways at once, and the file named only one of
|
|
463
|
+
them. That correction is now written where the wrong prediction was.
|
|
464
|
+
|
|
465
|
+
**Why an ignore rather than a migration.** Parsing in 7 lives in the Go binary,
|
|
466
|
+
reachable only through `unstable/sync`'s Project/Program API. Adopting it would
|
|
467
|
+
put a subprocess and a virtual filesystem inside a module whose stated contract
|
|
468
|
+
is "everything here is pure — source text in, references or findings out", in
|
|
469
|
+
exchange for an API whose own export path says `unstable`. Acorn is not a
|
|
470
|
+
substitute either: the default scan glob is `src/**/*.{ts,mjs}`, and the largest
|
|
471
|
+
consumer's sources are TypeScript.
|
|
472
|
+
|
|
473
|
+
That migration is worth doing when the API stabilises. It is not a dependency
|
|
474
|
+
bump, and it should not arrive as one.
|
|
475
|
+
|
|
476
|
+
**Scope of the hold.** Majors only — minor and patch releases within 6 still
|
|
477
|
+
arrive on the weekly schedule. The entry names the two conditions that lift it:
|
|
478
|
+
a stable in-process parse entry point, or a `coverage.mjs` that no longer needs
|
|
479
|
+
one.
|
|
480
|
+
|
|
481
|
+
**Bump**
|
|
482
|
+
|
|
483
|
+
_Patch._ Nothing shipped changes. `.github/dependabot.yml` is this repository's
|
|
484
|
+
own automation and sits outside `files`; the `typescript` range in
|
|
485
|
+
`package.json` is untouched, because `^6.0.3` already excludes 7 — the entry
|
|
486
|
+
stops the pull request being reopened, it does not change what resolves.
|
|
487
|
+
- f3167c3: Read the changesets action's `pr-number` output, so the step that marks the
|
|
488
|
+
Version Packages pull request stops being dead code (#84).
|
|
489
|
+
|
|
490
|
+
`release.yml` pins `changesets/action@v2` but read v1's `pullRequestNumber`. An
|
|
491
|
+
unset output evaluates to the empty string rather than erroring, so the guard was
|
|
492
|
+
`'' != ''` — always false. The step has never run once.
|
|
493
|
+
|
|
494
|
+
**The failure was silent by construction, which is why it survived three
|
|
495
|
+
releases.** A misspelled output does not fail a workflow; it disappears. `v3.4.0`,
|
|
496
|
+
`v4.0.0` and `v5.0.0` were all cut with this step skipped, and every run reported
|
|
497
|
+
green.
|
|
498
|
+
|
|
499
|
+
**Only one of the three names actually moved.** Checked against v2's own
|
|
500
|
+
`action.yml` rather than against the assumption that v2 kebab-cased everything:
|
|
501
|
+
|
|
502
|
+
| v1 | v2 | Used here |
|
|
503
|
+
| ------------------- | -------------------- | ------------------------------------- |
|
|
504
|
+
| `pullRequestNumber` | `pr-number` | yes — the two lines this change fixes |
|
|
505
|
+
| `publishedPackages` | `published-packages` | no |
|
|
506
|
+
| `hasChangesets` | `has-changesets` | no |
|
|
507
|
+
| `published` | `published` | yes — **unchanged**, and left alone |
|
|
508
|
+
|
|
509
|
+
`published` is the one name v2 kept. A blanket kebab-case sweep of this file —
|
|
510
|
+
the obvious reading of "rename the v1 outputs" — would have broken the one step
|
|
511
|
+
that was working.
|
|
512
|
+
|
|
513
|
+
**Bracket notation, not `outputs.pr-number`.** A hyphen is the subtraction
|
|
514
|
+
operator in an Actions expression, so the dotted form parses as
|
|
515
|
+
`outputs.pr - number`: a second silent-ish defect sitting directly behind the
|
|
516
|
+
first. `steps.changesets.outputs['pr-number']` is the form that means what it
|
|
517
|
+
reads as.
|
|
518
|
+
|
|
519
|
+
**What this repairs.** The Version Packages pull request is opened by
|
|
520
|
+
`GITHUB_TOKEN`, and GitHub deliberately starts no workflow runs from that token,
|
|
521
|
+
so the required `Changeset declared` context never reports on it. This step
|
|
522
|
+
exists to post that status. With it inert, every Version Packages pull request
|
|
523
|
+
has needed the check waived or force-merged by hand.
|
|
524
|
+
|
|
525
|
+
**Not yet demonstrated running, and stated rather than glossed.** The acceptance
|
|
526
|
+
criterion asks for a run where the step is not `skipped`, and that can only
|
|
527
|
+
happen on `main`, on the next release that opens a Version Packages pull request
|
|
528
|
+
— this change cannot produce one from a branch. The expression is verified by
|
|
529
|
+
parsing the workflow and by v2's manifest; the live proof arrives with the next
|
|
530
|
+
bump.
|
|
531
|
+
|
|
532
|
+
**Sibling repositories are fixed individually, not swept from here.** The same
|
|
533
|
+
defect is open on `harn-ensemble` (#13) and `HarnMaster-3-FoundryVTT` (#427),
|
|
534
|
+
where it is more severe — there the misspelling gates the release itself, and no
|
|
535
|
+
release is ever cut. Each repository owns its own copy of `release.yml`; this one
|
|
536
|
+
is not a reusable workflow. Turning it into one is a real improvement and a
|
|
537
|
+
separate change, and folding it into a two-line fix would put a shared release
|
|
538
|
+
pipeline into production on the back of a typo correction.
|
|
539
|
+
|
|
540
|
+
**Bump**
|
|
541
|
+
|
|
542
|
+
_Patch, not minor._ Nothing this package exports, emits, or documents for a
|
|
543
|
+
consumer changes. The file is this repository's own release plumbing, and it is
|
|
544
|
+
not shipped: `.github` is outside `files`.
|
|
545
|
+
- 2bdc792: Make `lint:markdown` pass, and run it in CI (#92).
|
|
546
|
+
|
|
547
|
+
This repository provides `content-build markdown` and was the one repository that
|
|
548
|
+
never ran it. The script existed, failed, and was executed by nothing — no
|
|
549
|
+
aggregate `lint` script, and no workflow naming it.
|
|
550
|
+
|
|
551
|
+
**The finding count in the issue is wrong, and the correction changes the
|
|
552
|
+
decision.** 117 is what `npx markdownlint-cli2 CHANGELOG-content-build.md`
|
|
553
|
+
reports — markdownlint's _default_ rule set, which this toolchain deliberately
|
|
554
|
+
turns off (`default: false`, then each rule enabled by name). Under the rules the
|
|
555
|
+
repository actually uses, `npm run lint:markdown` reports **three**: two
|
|
556
|
+
`MD001` heading skips and one `MD034` bare URL. The issue's "fix it — 117
|
|
557
|
+
mechanical findings" and "exclude it — 117 is too many to fix" were both
|
|
558
|
+
arguments about a number that was never the repository's.
|
|
559
|
+
|
|
560
|
+
**Excluded anyway, and not because three is still too many.**
|
|
561
|
+
`CHANGELOG-content-build.md` is the published changelog of
|
|
562
|
+
`@heroiclands/content-build` — a **deprecated repository**, absorbed into this
|
|
563
|
+
one at 3.0.0 (#32). It is frozen, not merely generated: exactly one commit has
|
|
564
|
+
ever touched it, nothing regenerates it, and it ships only because `files` lists
|
|
565
|
+
it. Its three findings are facts about what content-build published. Rewriting
|
|
566
|
+
them would edit a historical record to satisfy a rule about prose nobody will
|
|
567
|
+
write again, and would put a style-only commit in the blame of a file whose whole
|
|
568
|
+
value is being what was published. It is the same class as `CHANGELOG.md`, which
|
|
569
|
+
the shared default already ignores for the weaker reason that the next release
|
|
570
|
+
rewrites it.
|
|
571
|
+
|
|
572
|
+
**Declared locally, so no consumer moves.** The exclusion is a new
|
|
573
|
+
`.markdownlint-cli2.jsonc` in this repository, not an entry added to the shared
|
|
574
|
+
`MARKDOWN_IGNORES`. One repository's retired filename does not belong in
|
|
575
|
+
configuration six repositories consume, and the shared rule set is unchanged for
|
|
576
|
+
all of them.
|
|
577
|
+
|
|
578
|
+
**What that file had to get right, and what it documents.** `content-build
|
|
579
|
+
markdown` passes the shared rules as markdownlint-cli2's `optionsDefault`, and a
|
|
580
|
+
consumer file merges over them **key by key, each key wholesale**:
|
|
581
|
+
|
|
582
|
+
| Declared locally | Effect on the shared default |
|
|
583
|
+
| ---------------- | --------------------------------------------------------------------- |
|
|
584
|
+
| only `ignores` | rule set survives intact — `default: false` and every per-rule option |
|
|
585
|
+
| `ignores` | **replaces** `MARKDOWN_IGNORES`; it does not extend it |
|
|
586
|
+
|
|
587
|
+
So the local file restates `CHANGELOG.md`. Verified rather than assumed — with
|
|
588
|
+
that entry dropped, `lint:markdown` reports ten findings in `CHANGELOG.md`; with
|
|
589
|
+
a probe file present, `MD049` still fires with its shared `underscore` option
|
|
590
|
+
while `MD013` stays silent, which is what proves the rule set was not replaced.
|
|
591
|
+
|
|
592
|
+
`engine/prose-lint.mjs` said a consumer config "replaces it", which is the
|
|
593
|
+
reading that would send the next person to add the ignore to the shared default
|
|
594
|
+
or to a full config copy. Its docstring now states the key-by-key rule and the
|
|
595
|
+
`ignores` trap by name.
|
|
596
|
+
|
|
597
|
+
**Wired as a separate CI step, not folded into the chain.** `npm run lint` is
|
|
598
|
+
added for running both checks locally in one command, but `build.yml` gets a
|
|
599
|
+
`Markdown` step of its own beside `Formatting`. The aggregate chains with `&&`,
|
|
600
|
+
so a formatting failure would short-circuit it and hide every markdown finding
|
|
601
|
+
behind it; as two steps the failing one is named in the checks UI.
|
|
602
|
+
`lint:markdown:fix` is added too — the issue referred to it, and it did not exist.
|
|
603
|
+
|
|
604
|
+
**The exclusion cannot rot silently.** If a future markdownlint-cli2 bump changes
|
|
605
|
+
those merge semantics, the restated `CHANGELOG.md` entry stops applying and its
|
|
606
|
+
ten findings reappear — in the CI step this change adds. The guard is checked by
|
|
607
|
+
the thing it guards.
|
|
608
|
+
|
|
609
|
+
**Bump**
|
|
610
|
+
|
|
611
|
+
_Patch._ Nothing a consumer imports, calls, or configures changes behaviour. The
|
|
612
|
+
one shipped file touched is a docstring in `engine/prose-lint.mjs`, which reaches
|
|
613
|
+
consumers through the emitted declarations; everything else — the workflow, the
|
|
614
|
+
local lint config, the scripts — is this repository's own plumbing and is outside
|
|
615
|
+
`files`.
|
|
616
|
+
|
|
3
617
|
## 6.1.0
|
|
4
618
|
|
|
5
619
|
### Minor Changes
|
package/CONTENT.md
CHANGED
|
@@ -155,7 +155,9 @@ the file sits in, fills the optional halves with their defaults
|
|
|
155
155
|
manifest switches off and `publish.site` at its `homepage` floor),
|
|
156
156
|
derives `assetRoot`, `packDirectories`, `itemTypes` and `docEntryTypes`, and
|
|
157
157
|
freezes the result. A malformed configuration throws a `TypeError` naming the
|
|
158
|
-
offending field
|
|
158
|
+
offending field — and the line and column it was written on, in the
|
|
159
|
+
[located form](#diagnostics) — so it fails at load rather than as an empty pack
|
|
160
|
+
much later.
|
|
159
161
|
|
|
160
162
|
**Four values are derived rather than authored**, because each is something a
|
|
161
163
|
file can be asked for rather than told:
|
|
@@ -1160,6 +1162,24 @@ Two rules keep it that way, both in `engine/diagnostics.mjs`:
|
|
|
1160
1162
|
meaningless, `file: …` when only the note is known. Nothing defaults to
|
|
1161
1163
|
`1:1`, which would send a reader to the frontmatter every time.
|
|
1162
1164
|
|
|
1165
|
+
**A configuration error is located the same way.** Every check in
|
|
1166
|
+
`content-config.mjs` and `config.mjs` reports through one `fail()`, naming the
|
|
1167
|
+
offending key's dotted path — a good description and a bad locator, in a file
|
|
1168
|
+
that runs to hundreds of lines with sibling entries flow-mapped onto one. The
|
|
1169
|
+
path now rides on the error, and the loader that read the file resolves it
|
|
1170
|
+
against the YAML, so all of them come out located:
|
|
1171
|
+
|
|
1172
|
+
```text
|
|
1173
|
+
package-build.config.yaml:382:64: error: package-build config: `site.sections.being.descrption` is not a recognized option (expected one of: title, banner).
|
|
1174
|
+
```
|
|
1175
|
+
|
|
1176
|
+
The same two rules apply. A key the file never declares — a required one that is
|
|
1177
|
+
simply missing — has no node of its own, so the position names the **mapping it
|
|
1178
|
+
belongs in** and no further out; a missing _top-level_ key has nothing above it
|
|
1179
|
+
but the document, and an `.mjs` configuration has no YAML to resolve a path
|
|
1180
|
+
against at all. Both report `package-build.config.yaml: error: …`, the file
|
|
1181
|
+
without a line, rather than a line that would be wrong.
|
|
1182
|
+
|
|
1163
1183
|
Establishing a position at all takes three corrections, applied only where they
|
|
1164
1184
|
hold — see `positionInBody`. A body offset is not a file line until the
|
|
1165
1185
|
frontmatter's lines are added (`bodyLine`); the trim that strips the body can
|