@heroiclands/package-build 20.3.1 → 20.5.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 +351 -0
- package/CONTENT.md +322 -110
- package/README.md +21 -10
- package/bin/content-build.mjs +204 -92
- package/bin/package-build.mjs +48 -15
- package/config.mjs +6 -8
- package/content-config.mjs +518 -151
- package/docs/api.md +1352 -0
- package/docs/commands.md +1609 -0
- package/docs/configuration.md +1432 -0
- package/docs/content-format.md +73 -74
- package/docs/diagnostics.md +356 -0
- package/docs/getting-started.md +813 -0
- package/docs/project-setup.md +469 -0
- package/e2e.mjs +4 -4
- package/engine/actor-compiler.mjs +64 -61
- package/engine/address-charset.mjs +6 -6
- package/engine/address-diff.mjs +57 -52
- package/engine/anchored-sections.mjs +1 -1
- package/engine/anchors.mjs +1 -1
- package/engine/base-compiler.mjs +42 -38
- package/engine/bundle-notes.mjs +4 -4
- package/engine/bundles.mjs +17 -8
- package/engine/code-fences.mjs +3 -3
- package/engine/compendiums.mjs +8 -8
- package/engine/compile-corpus.mjs +4 -5
- package/engine/content-address.mjs +24 -24
- package/engine/content-charset.mjs +1 -1
- package/engine/content-format-check.mjs +24 -25
- package/engine/content-format.mjs +8 -8
- package/engine/content-html.mjs +202 -0
- package/engine/content-icons.mjs +196 -165
- package/engine/content-index.mjs +61 -40
- package/engine/content-links.mjs +74 -41
- package/engine/content-lint.mjs +64 -34
- package/engine/content-package.mjs +7 -9
- package/engine/content-slug.mjs +2 -2
- package/engine/content-tables.mjs +38 -34
- package/engine/content-tree.mjs +1 -1
- package/engine/derived-fields.mjs +174 -0
- package/engine/diagnostics.mjs +3 -4
- package/engine/document-subtypes.mjs +6 -7
- package/engine/field-reference.mjs +8 -8
- package/engine/field-spec.mjs +29 -30
- package/engine/folder-notes.mjs +18 -16
- package/engine/foreign-catalog.mjs +8 -9
- package/engine/foundry-entries.mjs +19 -20
- package/engine/frontmatter-lint.mjs +95 -103
- package/engine/frontmatter.mjs +11 -11
- package/engine/generate.mjs +44 -39
- package/engine/helpers.mjs +147 -84
- package/engine/homepage.mjs +20 -22
- package/engine/ids.mjs +15 -15
- package/engine/index-records.mjs +4 -4
- package/engine/index.mjs +26 -10
- package/engine/item-compiler.mjs +54 -29
- package/engine/item-docs.mjs +11 -13
- package/engine/item-registry.mjs +13 -13
- package/engine/journals.mjs +24 -15
- package/engine/macros.mjs +3 -3
- package/engine/map-notes.mjs +6 -6
- package/engine/metadata-index.mjs +5 -5
- package/engine/note-claims.mjs +51 -41
- package/engine/note-ids.mjs +5 -5
- package/engine/note-package.mjs +4 -4
- package/engine/note-renames.mjs +3 -3
- package/engine/note-schemas.mjs +3 -8
- package/engine/note-vocabulary.mjs +56 -54
- package/engine/pack-config.mjs +39 -25
- package/engine/pack-router.mjs +0 -0
- package/engine/pdf-build.mjs +464 -0
- package/engine/pdf-fonts.mjs +420 -0
- package/engine/pdf-render.mjs +798 -0
- package/engine/pdf-toc.mjs +525 -0
- package/engine/prose-config.mjs +9 -9
- package/engine/prose-lint.mjs +12 -13
- package/engine/region-events.mjs +2 -2
- package/engine/retired-fields.mjs +24 -22
- package/engine/runtime-only-fields.mjs +1 -1
- package/engine/scene-levels.mjs +1 -1
- package/engine/scenes.mjs +33 -25
- package/engine/schema-check.mjs +21 -21
- package/engine/schema-extract.mjs +1 -1
- package/engine/site-build.mjs +66 -50
- package/engine/site-index.mjs +17 -18
- package/engine/sql-tables.mjs +21 -13
- package/engine/subtype-registry.mjs +5 -4
- package/engine/system-block.mjs +18 -19
- package/engine/systems.mjs +2 -2
- package/engine/web-wikilinks.mjs +41 -38
- package/engine/wikilink-syntax.mjs +16 -16
- package/engine/wikilinks.mjs +40 -42
- package/hm3/actors.mjs +30 -22
- package/hm3/document-subtypes.mjs +5 -5
- package/hm3/index.mjs +2 -2
- package/hm3/item-builders.mjs +1 -1
- package/hm3/item-fields.mjs +3 -3
- package/hm3/items.mjs +28 -8
- package/hm3/template-priority.mjs +2 -2
- package/lang.mjs +3 -3
- package/manifest.mjs +17 -20
- package/package.json +1 -2
- package/release.mjs +66 -6
- package/sohl/actors.mjs +36 -28
- package/sohl/affiliation-standings.mjs +2 -2
- package/sohl/being-info.mjs +5 -5
- package/sohl/default-item-art.mjs +5 -5
- package/sohl/document-subtypes.mjs +5 -5
- package/sohl/index.mjs +3 -3
- package/sohl/item-builders.mjs +7 -7
- package/sohl/item-fields.mjs +8 -9
- package/sohl/items.mjs +28 -6
- package/sohl/kb-passes.mjs +5 -5
- package/sohl/note-schemas.mjs +12 -12
- package/sohl/skill-base.mjs +1 -1
- package/types/content-config.d.mts +91 -39
- package/types/e2e.d.mts +3 -3
- package/types/engine/actor-compiler.d.mts +45 -40
- package/types/engine/address-charset.d.mts +6 -6
- package/types/engine/address-diff.d.mts +60 -6
- package/types/engine/base-compiler.d.mts +27 -21
- package/types/engine/bundle-notes.d.mts +3 -3
- package/types/engine/bundles.d.mts +10 -1
- package/types/engine/code-fences.d.mts +3 -3
- package/types/engine/compendiums.d.mts +3 -3
- package/types/engine/compile-corpus.d.mts +1 -1
- package/types/engine/content-address.d.mts +20 -20
- package/types/engine/content-format-check.d.mts +6 -6
- package/types/engine/content-format.d.mts +2 -2
- package/types/engine/content-html.d.mts +78 -0
- package/types/engine/content-icons.d.mts +139 -92
- package/types/engine/content-index.d.mts +59 -15
- package/types/engine/content-links.d.mts +8 -8
- package/types/engine/content-lint.d.mts +8 -6
- package/types/engine/content-package.d.mts +6 -8
- package/types/engine/content-tables.d.mts +49 -18
- package/types/engine/derived-fields.d.mts +101 -0
- package/types/engine/diagnostics.d.mts +2 -2
- package/types/engine/document-subtypes.d.mts +3 -3
- package/types/engine/field-spec.d.mts +37 -39
- package/types/engine/folder-notes.d.mts +4 -5
- package/types/engine/foreign-catalog.d.mts +3 -3
- package/types/engine/foundry-entries.d.mts +6 -7
- package/types/engine/frontmatter-lint.d.mts +10 -9
- package/types/engine/frontmatter.d.mts +7 -7
- package/types/engine/generate.d.mts +7 -7
- package/types/engine/helpers.d.mts +128 -56
- package/types/engine/homepage.d.mts +16 -18
- package/types/engine/ids.d.mts +13 -13
- package/types/engine/index-records.d.mts +3 -3
- package/types/engine/index.d.mts +6 -0
- package/types/engine/item-compiler.d.mts +21 -5
- package/types/engine/item-docs.d.mts +2 -2
- package/types/engine/item-registry.d.mts +6 -6
- package/types/engine/journals.d.mts +12 -3
- package/types/engine/map-notes.d.mts +2 -2
- package/types/engine/metadata-index.d.mts +4 -4
- package/types/engine/note-claims.d.mts +30 -22
- package/types/engine/note-ids.d.mts +4 -4
- package/types/engine/note-package.d.mts +1 -1
- package/types/engine/note-renames.d.mts +3 -3
- package/types/engine/note-vocabulary.d.mts +31 -203
- package/types/engine/pack-config.d.mts +7 -7
- package/types/engine/pack-router.d.mts +1 -1
- package/types/engine/pdf-build.d.mts +42 -0
- package/types/engine/pdf-fonts.d.mts +30 -0
- package/types/engine/pdf-render.d.mts +144 -0
- package/types/engine/pdf-toc.d.mts +114 -0
- package/types/engine/prose-config.d.mts +9 -9
- package/types/engine/prose-lint.d.mts +3 -4
- package/types/engine/region-events.d.mts +2 -2
- package/types/engine/retired-fields.d.mts +10 -9
- package/types/engine/scenes.d.mts +10 -1
- package/types/engine/schema-check.d.mts +13 -13
- package/types/engine/site-build.d.mts +52 -25
- package/types/engine/site-index.d.mts +3 -4
- package/types/engine/sql-tables.d.mts +11 -5
- package/types/engine/subtype-registry.d.mts +3 -3
- package/types/engine/system-block.d.mts +3 -3
- package/types/engine/web-wikilinks.d.mts +7 -7
- package/types/engine/wikilink-syntax.d.mts +17 -17
- package/types/engine/wikilinks.d.mts +14 -14
- package/types/hm3/actors.d.mts +9 -1
- package/types/hm3/document-subtypes.d.mts +1 -1
- package/types/hm3/items.d.mts +9 -2
- package/types/hm3/template-priority.d.mts +1 -1
- package/types/manifest.d.mts +8 -8
- package/types/release.d.mts +15 -4
- package/types/sohl/actors.d.mts +12 -4
- package/types/sohl/affiliation-standings.d.mts +2 -2
- package/types/sohl/being-info.d.mts +2 -2
- package/types/sohl/document-subtypes.d.mts +1 -1
- package/types/sohl/items.d.mts +9 -2
- package/types/sohl/note-schemas.d.mts +1 -1
- package/MIGRATING.md +0 -608
|
@@ -0,0 +1,469 @@
|
|
|
1
|
+
# Project setup
|
|
2
|
+
|
|
3
|
+
[`getting-started.md`](getting-started.md) builds a package. This document
|
|
4
|
+
describes the repository around it: the files every HeroicLands package carries
|
|
5
|
+
beyond `package-build.config.yaml`, what each npm script in the chain is for,
|
|
6
|
+
and the directory layout the build reads and writes.
|
|
7
|
+
|
|
8
|
+
None of it is required to compile a pack. All of it is required for a repository
|
|
9
|
+
someone else can contribute to, and the reason to write it down is that
|
|
10
|
+
otherwise each repository copies it from the last and the copies drift.
|
|
11
|
+
|
|
12
|
+
## The layout
|
|
13
|
+
|
|
14
|
+
```text
|
|
15
|
+
acme-bestiary/
|
|
16
|
+
├── package.json # identity, and every script below
|
|
17
|
+
├── package-lock.json # committed
|
|
18
|
+
├── package-build.config.yaml # the build's single source
|
|
19
|
+
├── prettier.config.js # re-exports the shared Prettier options
|
|
20
|
+
├── .prettierignore # committed files Prettier must not rewrite
|
|
21
|
+
├── .gitignore # what is generated
|
|
22
|
+
├── .changeset/
|
|
23
|
+
│ ├── config.json
|
|
24
|
+
│ └── *.md # one per unreleased change
|
|
25
|
+
├── .github/
|
|
26
|
+
│ ├── labels.yml # the closed label registry
|
|
27
|
+
│ ├── ISSUE_REPORTING.md # §3 lists the same labels
|
|
28
|
+
│ └── workflows/
|
|
29
|
+
├── assets/
|
|
30
|
+
│ └── content/ # authored notes — an Obsidian vault
|
|
31
|
+
├── lang/
|
|
32
|
+
│ └── en.json
|
|
33
|
+
├── src/ # module JavaScript, if the package has any
|
|
34
|
+
├── styles/
|
|
35
|
+
├── README.md
|
|
36
|
+
├── LICENSE.md
|
|
37
|
+
└── build/ # everything generated; gitignored
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
`build/` is the only directory the toolchain writes into, and every path under
|
|
41
|
+
it is configurable through [`paths`](configuration.md#paths). The defaults:
|
|
42
|
+
|
|
43
|
+
| Path | Written by | Holds |
|
|
44
|
+
| ----------------------- | ------------------------------ | ------------------------------------------------------------- |
|
|
45
|
+
| `build/packs-json/` | `content-build package` | One JSON file per compiled document, before the pack is made. |
|
|
46
|
+
| `build/stage/packs/` | `content-build package` | The LevelDB compendium packs. |
|
|
47
|
+
| `build/stage/` | `package-build assets` | The staged package — manifest, packs, and the shipped files. |
|
|
48
|
+
| `build/content-index/` | `content-build content-index` | This package's published note index. |
|
|
49
|
+
| `build/cache/foreign/` | `content-build deps` | A dependency's unpacked item catalogue. |
|
|
50
|
+
| `build/cache/metadata/` | `content-build deps` | A dependency's fetched content index. |
|
|
51
|
+
| `build/tmp/packs/` | `content-build package unpack` | A compiled pack extracted back to JSON. |
|
|
52
|
+
| `build/dist/` | `package-build release` | The release archive, and the book when one is built. |
|
|
53
|
+
|
|
54
|
+
The two that matter when something is wrong are `build/packs-json/`, which is
|
|
55
|
+
the compiled document before Foundry ever sees it, and `build/stage/`, which is
|
|
56
|
+
exactly what ships.
|
|
57
|
+
|
|
58
|
+
**`assets/content/` is opened as an Obsidian vault.** There is no export step
|
|
59
|
+
and no vault environment variable: the notes in the repository are the source,
|
|
60
|
+
and a plain checkout builds. Dataview renders the content tables live while
|
|
61
|
+
authoring, which is why the tree is arranged for humans even though `type:`
|
|
62
|
+
rather than location is what routes a note.
|
|
63
|
+
|
|
64
|
+
## `package.json`
|
|
65
|
+
|
|
66
|
+
Beyond the identity keys [`getting-started.md`](getting-started.md) covers,
|
|
67
|
+
`package.json` carries the whole command surface of the repository. Nobody types
|
|
68
|
+
`npx content-build package compile`; they type `npm run build`.
|
|
69
|
+
|
|
70
|
+
The scripts fall into six groups, and the grouping is the point — a contributor
|
|
71
|
+
who knows the groups can guess a name.
|
|
72
|
+
|
|
73
|
+
### `prepare` — the git hooks
|
|
74
|
+
|
|
75
|
+
```json
|
|
76
|
+
"prepare": "git config core.hooksPath node_modules/@heroiclands/package-build/githooks || true"
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
npm runs `prepare` after every install, so this is what installs the hooks. It
|
|
80
|
+
points git at the hook directory this package ships, which means a repository
|
|
81
|
+
carries no hook files of its own and cannot drift from the others. `|| true` so
|
|
82
|
+
that an install outside a git checkout — a Docker build, a CI cache step —
|
|
83
|
+
does not fail on it.
|
|
84
|
+
|
|
85
|
+
Three hooks arrive with it, each with its own switch, read with git's normal
|
|
86
|
+
precedence: a plain `git config` sets one clone, `--global` sets a machine.
|
|
87
|
+
|
|
88
|
+
| Hook | What it does | Key | Default |
|
|
89
|
+
| -------------------------------- | --------------------------------------------------------------- | ------------------------------------ | ------- |
|
|
90
|
+
| `pre-commit`, `pre-merge-commit` | Refuse a commit on a protected branch. | `hooks.allowCommitOnMain` (inverted) | on |
|
|
91
|
+
| `commit-msg` | Refuse AI attribution in a commit message. | `hooks.noAttribution` | on |
|
|
92
|
+
| `pre-push` | Run this repository's own Build & Test workflow in a container. | `hooks.prePushCi` | off |
|
|
93
|
+
|
|
94
|
+
The defaults differ deliberately: a guard that costs nothing is on unless
|
|
95
|
+
refused, and one that runs a container for minutes is off unless asked for.
|
|
96
|
+
|
|
97
|
+
The branch guard is the one you meet first:
|
|
98
|
+
|
|
99
|
+
```console
|
|
100
|
+
$ git commit -m "Add the bestiary content package"
|
|
101
|
+
pre-commit: refusing to commit on the protected branch 'master'.
|
|
102
|
+
|
|
103
|
+
'master' is protected on GitHub, so this commit could never be pushed from
|
|
104
|
+
here. Move it onto a branch first — this keeps everything you have staged:
|
|
105
|
+
|
|
106
|
+
git switch -c <type>/<issue_#>_<slug>
|
|
107
|
+
|
|
108
|
+
To commit here anyway just this once, use 'git commit --no-verify'. To opt this
|
|
109
|
+
repository out permanently, 'git config hooks.allowCommitOnMain true'.
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
`pre-push` reads the step list out of `.github/workflows/build.yml` rather than
|
|
113
|
+
holding its own copy, and runs it over a clean export of `HEAD` in a
|
|
114
|
+
`linux/amd64` container. That last detail is the one a Mac cannot reproduce any
|
|
115
|
+
other way: this filesystem is case-insensitive and the runner's is not, so a
|
|
116
|
+
wrong-case import passes locally and fails there.
|
|
117
|
+
|
|
118
|
+
### `clean` — removing what the build wrote
|
|
119
|
+
|
|
120
|
+
```json
|
|
121
|
+
"clean": "package-build clean",
|
|
122
|
+
"distclean": "package-build clean --distclean"
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
`clean` removes the conventional build directories plus anything named in
|
|
126
|
+
`packageBuild.clean.extra` — a site's `content/`, `public/` and `resources/`,
|
|
127
|
+
say. `distclean` additionally removes `node_modules`. Both exit 0 whether or not
|
|
128
|
+
there was anything to remove.
|
|
129
|
+
|
|
130
|
+
### `lint:*` — the checks, one per question
|
|
131
|
+
|
|
132
|
+
Six checks, each answering one question and blind to what the others see. They
|
|
133
|
+
are separate scripts rather than one because a failing chain stops at its first
|
|
134
|
+
failure, and knowing _which_ question failed is most of the diagnosis.
|
|
135
|
+
|
|
136
|
+
```json
|
|
137
|
+
"lint": "run-s lint:format lint:markdown lint:addresses lint:content-links lint:lang lint:labels",
|
|
138
|
+
"lint:format": "content-build format",
|
|
139
|
+
"lint:markdown": "content-build markdown",
|
|
140
|
+
"lint:markdown:fix": "content-build markdown --fix",
|
|
141
|
+
"lint:addresses": "content-build lint",
|
|
142
|
+
"lint:content-links": "content-build links",
|
|
143
|
+
"lint:lang": "package-build lang check",
|
|
144
|
+
"lint:labels": "package-build labels check"
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
- **`lint:format`** — is every file formatted to the shared Prettier options?
|
|
148
|
+
Reports without writing, which is what a CI gate wants; `npm run format`
|
|
149
|
+
is the writing form.
|
|
150
|
+
- **`lint:markdown`** — does the prose satisfy the shared markdownlint rule set?
|
|
151
|
+
`lint:markdown:fix` applies the fixes markdownlint can make.
|
|
152
|
+
- **`lint:addresses`** — is every note's frontmatter well-formed, is every
|
|
153
|
+
address unique, and does the tree hold exactly one homepage?
|
|
154
|
+
- **`lint:content-links`** — does every `[[…]]` resolve, does every `#anchor`
|
|
155
|
+
land, and is every cross-package reference reachable through a fetched
|
|
156
|
+
manifest?
|
|
157
|
+
- **`lint:lang`** — does every localization file survive
|
|
158
|
+
`foundry.utils.expandObject`? A dotted-prefix collision (`"a.b": 1` beside
|
|
159
|
+
`"a.b.c": 2`) makes Foundry drop the whole file silently, which is the quiet
|
|
160
|
+
failure this catches. A package with templates and scripts also wires
|
|
161
|
+
`package-build lang coverage` and `package-build lang hardcoded`; a
|
|
162
|
+
content-only package has nothing for those two to read, and each dies naming
|
|
163
|
+
the glob that matched nothing rather than passing vacuously.
|
|
164
|
+
- **`lint:labels`** — do `.github/labels.yml` and §3 of
|
|
165
|
+
`.github/ISSUE_REPORTING.md` still list the same labels? Neither derives from
|
|
166
|
+
the other, so nothing else notices when they drift.
|
|
167
|
+
|
|
168
|
+
`run-s` comes from `npm-run-all`, a devDependency. It runs scripts in sequence
|
|
169
|
+
and stops at the first failure, which is what makes a named chain readable.
|
|
170
|
+
|
|
171
|
+
### `build:*` — the chain
|
|
172
|
+
|
|
173
|
+
```json
|
|
174
|
+
"build": "npm ci && npm run build:noci",
|
|
175
|
+
"build:local": "npm i && npm run build:noci",
|
|
176
|
+
"build:noci": "run-s lint build:db build:module",
|
|
177
|
+
"build:db": "run-s build:content-index build:assets build:compiledb",
|
|
178
|
+
"build:content-index": "content-build content-index",
|
|
179
|
+
"build:assets": "package-build assets",
|
|
180
|
+
"build:compiledb": "content-build package compile",
|
|
181
|
+
"build:unpackdb": "content-build package unpack",
|
|
182
|
+
"build:module": "package-build manifest",
|
|
183
|
+
"build:pack-release": "package-build release"
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
The split between `build` and `build:noci` is not cosmetic. **`build` installs
|
|
187
|
+
from the lockfile first**, so what it compiles is what CI compiles;
|
|
188
|
+
`build:local` installs without the lockfile, for a working tree mid-change; and
|
|
189
|
+
`build:noci` skips the install entirely, which is the one to reach for inside a
|
|
190
|
+
git worktree where `node_modules` is already correct.
|
|
191
|
+
|
|
192
|
+
Within the chain, two orderings are real:
|
|
193
|
+
|
|
194
|
+
- **`build:content-index` before `build:pack-release`.** The manifest advertises
|
|
195
|
+
the content index at a pinned address, and `package-build release` refuses an
|
|
196
|
+
archive whose manifest advertises an index the tree never wrote.
|
|
197
|
+
- **`lint` before any of it.** The checks read the tree; the compilers read the
|
|
198
|
+
tree and write. Finding a malformed address after the packs are built means
|
|
199
|
+
rebuilding them.
|
|
200
|
+
|
|
201
|
+
The three stage-writing steps — `build:assets`, `build:compiledb`,
|
|
202
|
+
`build:module` — each write their own part of `build/stage/` and none clobbers
|
|
203
|
+
another's, so their order among themselves is legibility rather than necessity.
|
|
204
|
+
|
|
205
|
+
`build:unpackdb` is the inverse of `build:compiledb`: it extracts a compiled
|
|
206
|
+
LevelDB pack back to JSON, which is how you read what actually shipped.
|
|
207
|
+
|
|
208
|
+
A package with a dependency adds `"build:deps": "content-build deps fetch"` at
|
|
209
|
+
the head of `build:db`. Fetching is its own step and never happens during a
|
|
210
|
+
compile, so a build never reaches the network silently — a cold cache fails
|
|
211
|
+
naming `deps fetch`.
|
|
212
|
+
|
|
213
|
+
### `format` — writing rather than checking
|
|
214
|
+
|
|
215
|
+
```json
|
|
216
|
+
"format": "content-build format --write",
|
|
217
|
+
"format:check": "content-build format"
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
Two names for one command because two audiences want it: a contributor wants
|
|
221
|
+
the writing form, and CI wants the checking form. `format:check` and
|
|
222
|
+
`lint:format` are the same command; both names exist because the lint chain
|
|
223
|
+
reads better with one and a CI workflow reads better with the other.
|
|
224
|
+
|
|
225
|
+
### `changeset:*` — the release mechanism
|
|
226
|
+
|
|
227
|
+
```json
|
|
228
|
+
"changeset": "changeset",
|
|
229
|
+
"changeset:check": "changeset status --since=origin/main",
|
|
230
|
+
"changeset:version": "changeset version && npm install --package-lock-only"
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
`changeset` adds one. `changeset:check` reports what is pending against `main`,
|
|
234
|
+
which is what a CI job gates on so that a behaviour change cannot merge without
|
|
235
|
+
declaring its bump. `changeset:version` consumes the pending changesets, writes
|
|
236
|
+
`CHANGELOG.md` and bumps `package.json` — and the `npm install --package-lock-only`
|
|
237
|
+
after it is what keeps `package-lock.json`'s recorded version in step, since
|
|
238
|
+
changesets does not touch the lockfile.
|
|
239
|
+
|
|
240
|
+
### The site scripts
|
|
241
|
+
|
|
242
|
+
A package that publishes a website adds a further group. They are outside this
|
|
243
|
+
document's scope because they need Hugo and the shared theme submodule, but the
|
|
244
|
+
shape is worth knowing so the names are not surprising:
|
|
245
|
+
`build:site-content` writes the Hugo content tree with `content-build site`,
|
|
246
|
+
`build:site-html` runs Hugo over it, and `serve:site` does the first and then
|
|
247
|
+
`hugo server` for a live preview.
|
|
248
|
+
|
|
249
|
+
### Deployment scripts
|
|
250
|
+
|
|
251
|
+
`push:dev`, `push:qa` and `push:prod` wrap `package-build deploy <stage>`, and
|
|
252
|
+
`deploy:*` chains a build in front of each. They read credentials from
|
|
253
|
+
`.env.local`, which is never committed. A fresh worktree has no `.env.local`, so
|
|
254
|
+
these exit without copying anything and the target keeps serving the previous
|
|
255
|
+
build — copy the file in before trusting a deployment from one.
|
|
256
|
+
|
|
257
|
+
## `prettier.config.js`
|
|
258
|
+
|
|
259
|
+
```js
|
|
260
|
+
/**
|
|
261
|
+
* The shared HeroicLands Prettier configuration.
|
|
262
|
+
*
|
|
263
|
+
* @type {import("prettier").Config}
|
|
264
|
+
*/
|
|
265
|
+
export { default } from "@heroiclands/package-build/prettier";
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
**Never restate an option here.** The options live in
|
|
269
|
+
`@heroiclands/package-build/prettier` so that every repository formats
|
|
270
|
+
identically, and a local override is how two repositories stop agreeing. The
|
|
271
|
+
file exists solely so that an editor and a bare `npx prettier` resolve the same
|
|
272
|
+
options the toolchain applies — without it they fall back to Prettier's own
|
|
273
|
+
defaults, and a print width that differs from the project's turns every
|
|
274
|
+
save into a diff the lint chain reverts.
|
|
275
|
+
|
|
276
|
+
`content-build markdown` needs no equivalent file: it hands the shared rule set
|
|
277
|
+
to markdownlint directly, and a repository that declares nothing gets it. A
|
|
278
|
+
`.markdownlint-cli2.jsonc` is for the editor's markdownlint extension, and
|
|
279
|
+
[`@heroiclands/package-build/markdownlint`](api.md) is the export it re-uses —
|
|
280
|
+
with one trap worth knowing before writing one. A consumer file overrides the
|
|
281
|
+
shared configuration **key by key, and each key wholesale**: declaring only
|
|
282
|
+
`ignores` keeps every rule intact but _replaces_ the shared ignore list rather
|
|
283
|
+
than extending it, so such a file must restate every shared entry it still
|
|
284
|
+
wants.
|
|
285
|
+
|
|
286
|
+
## `.prettierignore`
|
|
287
|
+
|
|
288
|
+
`content-build format` already consults `.gitignore`, so generated trees named
|
|
289
|
+
there need no second entry. `.prettierignore` is for the opposite case: files
|
|
290
|
+
that are **committed** and must not be rewritten.
|
|
291
|
+
|
|
292
|
+
```text
|
|
293
|
+
# Written by `changeset version`, not by hand. The Version Packages pull request
|
|
294
|
+
# runs no CI — it is opened by GITHUB_TOKEN — so a changelog the formatter has
|
|
295
|
+
# rewritten turns `main` red the moment that pull request merges.
|
|
296
|
+
CHANGELOG.md
|
|
297
|
+
```
|
|
298
|
+
|
|
299
|
+
Two more entries are common, and both are the same principle:
|
|
300
|
+
|
|
301
|
+
- **A generated site tree.** Hugo layouts are Go templates in `.html` files,
|
|
302
|
+
which Prettier reflows as HTML — folding the template directives into prose.
|
|
303
|
+
- **Vendored files from another build.** A file copied verbatim from another
|
|
304
|
+
repository is reformatted into a permanent diff against its source, which
|
|
305
|
+
re-diffs on every re-vendor.
|
|
306
|
+
|
|
307
|
+
`package-lock.json` needs no entry when `package.json` is formatted, because npm
|
|
308
|
+
matches `package.json`'s indentation when it writes the lockfile — so a
|
|
309
|
+
formatted `package.json` produces a lockfile the formatter already agrees with.
|
|
310
|
+
A repository whose `package.json` is indented some other way ignores the
|
|
311
|
+
lockfile instead, or `npm install` and the formatter rewrite it in turn.
|
|
312
|
+
|
|
313
|
+
## `.gitignore`
|
|
314
|
+
|
|
315
|
+
The build's own output, plus the local-only paths every repository has:
|
|
316
|
+
|
|
317
|
+
```text
|
|
318
|
+
node_modules
|
|
319
|
+
/build/
|
|
320
|
+
/nogit/
|
|
321
|
+
/.env
|
|
322
|
+
/.env.local
|
|
323
|
+
/.claude/
|
|
324
|
+
CLAUDE.md
|
|
325
|
+
AI.md
|
|
326
|
+
*.tgz
|
|
327
|
+
.DS_Store
|
|
328
|
+
```
|
|
329
|
+
|
|
330
|
+
Three of those carry rules rather than conventions.
|
|
331
|
+
|
|
332
|
+
**`nogit/` is local scratch** — design write-ups, working notes, downloaded
|
|
333
|
+
references. Nothing in it is ever committed.
|
|
334
|
+
|
|
335
|
+
**`.env.local` holds real credentials** for the deployment targets. Never
|
|
336
|
+
committed, never echoed.
|
|
337
|
+
|
|
338
|
+
**`node_modules` with no leading slash and no trailing slash.** The trailing
|
|
339
|
+
slash matches a directory only, so a worktree carrying `node_modules` as a
|
|
340
|
+
symlink is not ignored and a `git add -A` commits a symlink holding one
|
|
341
|
+
developer's absolute path. Unanchored so the same cannot happen one directory
|
|
342
|
+
down, which is where a nested worktree puts it.
|
|
343
|
+
|
|
344
|
+
Note also that `.gitignore` is what keeps `build/` out of the prose checks:
|
|
345
|
+
`content-build format` reads `.gitignore` and `.prettierignore` both, and
|
|
346
|
+
`content-build markdown` reads `.gitignore`.
|
|
347
|
+
|
|
348
|
+
## `.changeset/`
|
|
349
|
+
|
|
350
|
+
```json
|
|
351
|
+
{
|
|
352
|
+
"$schema": "https://unpkg.com/@changesets/config@4.0.0/schema.json",
|
|
353
|
+
"changelog": "@changesets/changelog-git",
|
|
354
|
+
"commit": true,
|
|
355
|
+
"baseBranch": "main",
|
|
356
|
+
"privatePackages": {
|
|
357
|
+
"version": true,
|
|
358
|
+
"tag": false
|
|
359
|
+
},
|
|
360
|
+
"updateInternalDependencies": "patch",
|
|
361
|
+
"ignore": []
|
|
362
|
+
}
|
|
363
|
+
```
|
|
364
|
+
|
|
365
|
+
`npx changeset init` writes a starting point — it prompts once, for whether to
|
|
366
|
+
use the GitHub changelog integration, and the answer here is no. Four keys then
|
|
367
|
+
need setting, and each is a decision rather than a preference.
|
|
368
|
+
|
|
369
|
+
- **`changelog: "@changesets/changelog-git"`** attributes each entry to its
|
|
370
|
+
commit. The GitHub changelog generator wants a token and produces entries
|
|
371
|
+
naming pull requests, which is history the changelog does not need.
|
|
372
|
+
- **`privatePackages: {version: true, tag: false}`** is what makes changesets
|
|
373
|
+
work at all here. Every package in the constellation is `private`, because it
|
|
374
|
+
ships as a Foundry release rather than to npm; without `version: true`
|
|
375
|
+
changesets skips it entirely, and `tag: false` because the release tag is cut
|
|
376
|
+
by the release workflow rather than by changesets.
|
|
377
|
+
- **`commit: true`** so `changeset version` commits its own result.
|
|
378
|
+
- **`baseBranch: "main"`** is what `changeset status --since=origin/main`
|
|
379
|
+
compares against.
|
|
380
|
+
|
|
381
|
+
Then one `.changeset/*.md` per unreleased change:
|
|
382
|
+
|
|
383
|
+
```markdown
|
|
384
|
+
---
|
|
385
|
+
"acme-bestiary": minor
|
|
386
|
+
---
|
|
387
|
+
|
|
388
|
+
**Creatures** — The marsh drake joins the bestiary, with its habits and the
|
|
389
|
+
stretch of water it holds.
|
|
390
|
+
```
|
|
391
|
+
|
|
392
|
+
Three rules about the body, and they are enforced by review rather than by a
|
|
393
|
+
tool:
|
|
394
|
+
|
|
395
|
+
- **It describes the setting, not the work.** Write for someone who installs or
|
|
396
|
+
upgrades and wants to know what changed for them.
|
|
397
|
+
- **Bold labels, never `#` headings.** Changesets nests the whole summary inside
|
|
398
|
+
one bullet, so an `h2` written here becomes a real `<h2>` in the changelog,
|
|
399
|
+
outranking the `Minor Changes` heading above it and polluting the outline.
|
|
400
|
+
- **On a 0.x package a breaking change is `minor`, never `major`.** `major`
|
|
401
|
+
takes the package to 1.0.0, which is a product decision rather than a
|
|
402
|
+
description of a diff.
|
|
403
|
+
|
|
404
|
+
## `.github/`
|
|
405
|
+
|
|
406
|
+
Two files the toolchain reads, and they must agree.
|
|
407
|
+
|
|
408
|
+
**`.github/labels.yml`** is the closed label registry — the machine-readable
|
|
409
|
+
source of truth, synced to GitHub by the org-wide `labels` action. A label on
|
|
410
|
+
GitHub that is not listed there is deleted on the next sync, which is what makes
|
|
411
|
+
the set closed and why nothing invents one.
|
|
412
|
+
|
|
413
|
+
```yaml
|
|
414
|
+
- name: documentation
|
|
415
|
+
color: "0075ca"
|
|
416
|
+
description: Documentation about this repository — README, process, authoring guides.
|
|
417
|
+
|
|
418
|
+
- name: devops
|
|
419
|
+
color: "e07b31"
|
|
420
|
+
description: Build, tooling, CI, release, repo config.
|
|
421
|
+
```
|
|
422
|
+
|
|
423
|
+
**`.github/ISSUE_REPORTING.md`** §3 is the same list in prose, for a human
|
|
424
|
+
filing an issue. `package-build labels check` compares the two and fails when
|
|
425
|
+
they disagree:
|
|
426
|
+
|
|
427
|
+
```console
|
|
428
|
+
$ npx package-build labels check
|
|
429
|
+
package-build: registry and §3 agree (2 labels).
|
|
430
|
+
```
|
|
431
|
+
|
|
432
|
+
Neither file derives from the other, so this check is the only thing standing
|
|
433
|
+
between them and a silent divergence. Editing the registry means editing both.
|
|
434
|
+
|
|
435
|
+
Labels are **subject matter only**. Work shape — bug, feature, epic, task, spike
|
|
436
|
+
— is an issue _type_, shared across the organization, so there is no `bug` label
|
|
437
|
+
and filtering on one returns nothing.
|
|
438
|
+
|
|
439
|
+
Neither file exists in a fresh repository, and `labels check` says so rather
|
|
440
|
+
than passing vacuously:
|
|
441
|
+
|
|
442
|
+
```console
|
|
443
|
+
$ npx package-build labels check
|
|
444
|
+
package-build: labels check: .github/labels.yml does not exist.
|
|
445
|
+
```
|
|
446
|
+
|
|
447
|
+
`.github/workflows/` then carries the repository's own CI. The org-wide
|
|
448
|
+
`HeroicLands/.github` repository supplies the shared composite actions those
|
|
449
|
+
workflows `uses:` — `labels`, `no-attribution` and `todos` — plus the reusable
|
|
450
|
+
site-deploy workflow, so a fix to any of them reaches every repository at once.
|
|
451
|
+
|
|
452
|
+
## What the toolchain reads, and what it writes
|
|
453
|
+
|
|
454
|
+
A summary, because "where does this come from" is the question that recurs.
|
|
455
|
+
|
|
456
|
+
| Read | By |
|
|
457
|
+
| -------------------------------------------------- | ----------------------------------------------------------- |
|
|
458
|
+
| `package.json` | The Foundry package id, the version, the release addresses. |
|
|
459
|
+
| `package-build.config.yaml` | Everything else about the build. |
|
|
460
|
+
| `assets/content/**/*.md` | Every content command. |
|
|
461
|
+
| `.gitignore` | `content-build format`, `content-build markdown`. |
|
|
462
|
+
| `.prettierignore` | `content-build format`. |
|
|
463
|
+
| `.github/labels.yml`, `.github/ISSUE_REPORTING.md` | `package-build labels check`. |
|
|
464
|
+
| `lang/*.json` | `package-build lang`. |
|
|
465
|
+
| `.env.local` | `package-build deploy`. |
|
|
466
|
+
| `.github/workflows/build.yml` | The `pre-push` hook, for its step list. |
|
|
467
|
+
|
|
468
|
+
Everything written goes under `build/`. Nothing the toolchain generates is
|
|
469
|
+
committed.
|
package/e2e.mjs
CHANGED
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
* of labour here. Standing a licensed Foundry up, seeding a world whose
|
|
20
20
|
* Gamemaster password is known, waiting for that world to be *active* rather
|
|
21
21
|
* than merely reachable, tearing it all down again — none of that is one
|
|
22
|
-
* repository's problem, and
|
|
22
|
+
* repository's problem, and none of it lives in one. What runs against
|
|
23
23
|
* the served world is named in `packageBuild.e2e.suite`, the same way an asset
|
|
24
24
|
* transform or a manifest-flags module is named: the repository's code, the
|
|
25
25
|
* toolchain's plumbing.
|
|
@@ -685,7 +685,7 @@ export function findExecutable(name, { cwd, env = process.env } = {}) {
|
|
|
685
685
|
*
|
|
686
686
|
* Asked twice per run, and the second asking is the point: an install running
|
|
687
687
|
* alongside the suite can take the runner out from under it mid-flight, which
|
|
688
|
-
* is precisely the failure that reported itself as green
|
|
688
|
+
* is precisely the failure that reported itself as green.
|
|
689
689
|
*
|
|
690
690
|
* @param {object} opts
|
|
691
691
|
* @param {readonly string[]} opts.command - The program and its arguments.
|
|
@@ -766,7 +766,7 @@ export function freshResults({ paths, since, cwd }) {
|
|
|
766
766
|
* exit status on its own cannot call a run green, because every way of stopping
|
|
767
767
|
* a runner before it starts — a corrupt install, a missing browser, a killed
|
|
768
768
|
* process, the concurrent `npm ci` that surfaced this — produces a run that
|
|
769
|
-
* executed nothing, and nothing is not a pass
|
|
769
|
+
* executed nothing, and nothing is not a pass.
|
|
770
770
|
*
|
|
771
771
|
* This can only ever make a verdict worse. A suite that failed keeps its own
|
|
772
772
|
* status; a suite that passed on no evidence loses the claim. Never the other
|
|
@@ -814,7 +814,7 @@ export function suiteVerdict({ status, vanished = [], declared = [], fresh = []
|
|
|
814
814
|
* `MODULE_NOT_FOUND` naming nothing relevant.
|
|
815
815
|
*
|
|
816
816
|
* The suite is bracketed by checks rather than trusted on its exit status,
|
|
817
|
-
* because a run that never started used to report as green
|
|
817
|
+
* because a run that never started used to report as green:
|
|
818
818
|
*
|
|
819
819
|
* - **Before.** Every executable the command needs is resolved, and a missing
|
|
820
820
|
* one is an error naming it — rather than a container stood up, a world
|