@heroiclands/package-build 17.1.0 → 18.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 +1773 -0
- package/CONTENT.md +288 -72
- package/README.md +1 -1
- package/bin/content-build.mjs +218 -137
- package/bin/package-build.mjs +43 -0
- package/content-config.mjs +62 -67
- package/docs/content-format.md +768 -58
- package/engine/actor-compiler.mjs +586 -0
- package/engine/address-charset.mjs +11 -8
- package/engine/address-diff.mjs +266 -22
- package/engine/anchored-sections.mjs +83 -0
- package/engine/anchors.mjs +83 -0
- package/engine/base-compiler.mjs +148 -18
- package/engine/bundle-notes.mjs +276 -0
- package/engine/bundles.mjs +307 -0
- package/engine/code-fences.mjs +103 -0
- package/engine/compile-corpus.mjs +89 -0
- package/engine/content-address.mjs +267 -5
- package/engine/content-format-check.mjs +54 -11
- package/engine/content-format.mjs +37 -5
- package/engine/content-index.mjs +394 -111
- package/engine/content-links.mjs +185 -77
- package/engine/content-lint.mjs +232 -14
- package/engine/content-package.mjs +2 -1
- package/engine/content-tables.mjs +99 -3
- package/engine/document-subtypes.mjs +49 -6
- package/engine/field-reference.mjs +1 -1
- package/engine/field-spec.mjs +56 -6
- package/engine/folder-notes.mjs +470 -0
- package/engine/foreign-catalog.mjs +195 -4
- package/engine/{manifest-emit.mjs → foundry-entries.mjs} +78 -81
- package/engine/frontmatter-lint.mjs +245 -19
- package/engine/frontmatter.mjs +88 -8
- package/engine/generate.mjs +304 -13
- package/engine/helpers.mjs +293 -64
- package/engine/ids.mjs +137 -2
- package/engine/index-records.mjs +126 -0
- package/engine/index.mjs +23 -5
- package/engine/item-compiler.mjs +349 -0
- package/engine/item-docs.mjs +7 -1
- package/engine/item-registry.mjs +6 -0
- package/engine/journals.mjs +93 -27
- package/engine/macros.mjs +4 -2
- package/engine/metadata-index.mjs +495 -0
- package/engine/note-claims.mjs +146 -25
- package/engine/note-ids.mjs +112 -0
- package/engine/note-renames.mjs +134 -0
- package/engine/note-schemas.mjs +25 -0
- package/engine/note-vocabulary.mjs +110 -27
- package/engine/pack-config.mjs +4 -0
- package/engine/pack-router.mjs +23 -0
- package/engine/retired-fields.mjs +137 -3
- package/engine/scenes.mjs +66 -34
- package/engine/schema-check.mjs +58 -17
- package/engine/site-build.mjs +71 -12
- package/engine/site-index.mjs +20 -7
- package/engine/sql-tables.mjs +485 -0
- package/engine/subtype-registry.mjs +102 -0
- package/engine/system-block.mjs +96 -12
- package/engine/systems.mjs +201 -0
- package/engine/web-wikilinks.mjs +50 -20
- package/engine/wikilink-syntax.mjs +17 -8
- package/engine/wikilinks.mjs +124 -51
- package/engine/yaml-lint.mjs +282 -0
- package/hm3/actors.mjs +352 -0
- package/hm3/default-item-art.mjs +75 -0
- package/hm3/document-subtypes.mjs +134 -0
- package/hm3/index.mjs +56 -0
- package/hm3/item-builders.mjs +84 -0
- package/hm3/item-fields.mjs +180 -0
- package/hm3/items.mjs +96 -0
- package/hm3/template-priority.mjs +85 -0
- package/labels.mjs +191 -0
- package/manifest.mjs +43 -2
- package/package.json +19 -4
- package/release.mjs +62 -7
- package/sohl/actors.mjs +33 -487
- package/sohl/being-info.mjs +16 -7
- package/sohl/default-item-art.mjs +14 -3
- package/sohl/document-subtypes.mjs +16 -10
- package/sohl/item-builders.mjs +14 -5
- package/sohl/item-fields.mjs +68 -7
- package/sohl/items.mjs +44 -258
- package/sohl/note-schemas.mjs +2 -2
- package/types/content-config.d.mts +21 -42
- package/types/engine/actor-compiler.d.mts +204 -0
- package/types/engine/address-charset.d.mts +11 -8
- package/types/engine/address-diff.d.mts +53 -5
- package/types/engine/anchored-sections.d.mts +21 -0
- package/types/engine/anchors.d.mts +20 -0
- package/types/engine/base-compiler.d.mts +17 -17
- package/types/engine/bundle-notes.d.mts +173 -0
- package/types/engine/bundles.d.mts +60 -0
- package/types/engine/code-fences.d.mts +43 -0
- package/types/engine/compile-corpus.d.mts +32 -0
- package/types/engine/content-address.d.mts +205 -5
- package/types/engine/content-format-check.d.mts +6 -2
- package/types/engine/content-format.d.mts +57 -1
- package/types/engine/content-index.d.mts +64 -80
- package/types/engine/content-links.d.mts +52 -7
- package/types/engine/content-lint.d.mts +10 -1
- package/types/engine/content-package.d.mts +2 -1
- package/types/engine/content-tables.d.mts +21 -39
- package/types/engine/document-subtypes.d.mts +37 -3
- package/types/engine/field-spec.d.mts +76 -5
- package/types/engine/folder-notes.d.mts +159 -0
- package/types/engine/foreign-catalog.d.mts +53 -0
- package/types/engine/{manifest-emit.d.mts → foundry-entries.d.mts} +16 -38
- package/types/engine/frontmatter-lint.d.mts +10 -2
- package/types/engine/frontmatter.d.mts +64 -0
- package/types/engine/generate.d.mts +38 -0
- package/types/engine/helpers.d.mts +94 -30
- package/types/engine/ids.d.mts +96 -0
- package/types/engine/index-records.d.mts +68 -0
- package/types/engine/index.d.mts +9 -3
- package/types/engine/item-compiler.d.mts +131 -0
- package/types/engine/journals.d.mts +47 -9
- package/types/engine/metadata-index.d.mts +226 -0
- package/types/engine/note-claims.d.mts +57 -11
- package/types/engine/note-ids.d.mts +38 -0
- package/types/engine/note-renames.d.mts +102 -0
- package/types/engine/note-vocabulary.d.mts +44 -8
- package/types/engine/retired-fields.d.mts +75 -0
- package/types/engine/scenes.d.mts +3 -2
- package/types/engine/schema-check.d.mts +25 -4
- package/types/engine/site-build.d.mts +4 -4
- package/types/engine/site-index.d.mts +1 -1
- package/types/engine/sql-tables.d.mts +185 -0
- package/types/engine/subtype-registry.d.mts +49 -0
- package/types/engine/system-block.d.mts +40 -1
- package/types/engine/systems.d.mts +106 -0
- package/types/engine/web-wikilinks.d.mts +4 -2
- package/types/engine/wikilink-syntax.d.mts +10 -3
- package/types/engine/wikilinks.d.mts +41 -13
- package/types/engine/yaml-lint.d.mts +107 -0
- package/types/hm3/actors.d.mts +48 -0
- package/types/hm3/default-item-art.d.mts +42 -0
- package/types/hm3/document-subtypes.d.mts +24 -0
- package/types/hm3/index.d.mts +7 -0
- package/types/hm3/item-builders.d.mts +11 -0
- package/types/hm3/item-fields.d.mts +12 -0
- package/types/hm3/items.d.mts +23 -0
- package/types/hm3/template-priority.d.mts +21 -0
- package/types/manifest.d.mts +22 -1
- package/types/release.d.mts +9 -4
- package/types/sohl/actors.d.mts +7 -74
- package/types/sohl/being-info.d.mts +8 -27
- package/types/sohl/default-item-art.d.mts +5 -3
- package/types/sohl/items.d.mts +17 -32
- package/engine/foreign-manifests.mjs +0 -126
- package/engine/kb-manifest.mjs +0 -490
- package/types/engine/foreign-manifests.d.mts +0 -43
- package/types/engine/kb-manifest.d.mts +0 -241
package/manifest.mjs
CHANGED
|
@@ -53,6 +53,7 @@ import fsSync from "node:fs";
|
|
|
53
53
|
import path from "node:path";
|
|
54
54
|
|
|
55
55
|
import { emitDiagnostic, positionOfYamlPath } from "./engine/diagnostics.mjs";
|
|
56
|
+
import { metadataFileName } from "./engine/metadata-index.mjs";
|
|
56
57
|
|
|
57
58
|
/**
|
|
58
59
|
* The two package kinds Foundry defines, as the artifact name each one's
|
|
@@ -97,7 +98,7 @@ export function normalizeRepoUrl(repository) {
|
|
|
97
98
|
}
|
|
98
99
|
|
|
99
100
|
/**
|
|
100
|
-
* The
|
|
101
|
+
* The addresses a Foundry manifest advertises.
|
|
101
102
|
*
|
|
102
103
|
* `manifest` deliberately points at **`releases/latest`** rather than at this
|
|
103
104
|
* version: it is the URL an *installed* package re-fetches to discover that a
|
|
@@ -120,6 +121,26 @@ export function releaseUrls({ repoUrl, version, artifact }) {
|
|
|
120
121
|
};
|
|
121
122
|
}
|
|
122
123
|
|
|
124
|
+
/**
|
|
125
|
+
* Where this release publishes its content index (#239).
|
|
126
|
+
*
|
|
127
|
+
* **Pinned to this version, like `download` and unlike `manifest`.** A
|
|
128
|
+
* consumer reaches this URL by reading the dependency's manifest, so the
|
|
129
|
+
* manifest it read and the index it then fetches describe the same release —
|
|
130
|
+
* which is the whole point of publishing them together. A `releases/latest`
|
|
131
|
+
* index would silently pair a pinned manifest with a moving index, and the
|
|
132
|
+
* mismatch would surface as a cross-package link that resolved yesterday.
|
|
133
|
+
*
|
|
134
|
+
* @param {object} opts
|
|
135
|
+
* @param {string} opts.repoUrl - Normalised repository URL.
|
|
136
|
+
* @param {string} opts.version - The version being built.
|
|
137
|
+
* @param {string} opts.contentPackage - The content package name.
|
|
138
|
+
* @returns {string} The version-pinned asset URL.
|
|
139
|
+
*/
|
|
140
|
+
export function metadataUrl({ repoUrl, version, contentPackage }) {
|
|
141
|
+
return `${repoUrl}/releases/download/v${version}/${metadataFileName(contentPackage)}`;
|
|
142
|
+
}
|
|
143
|
+
|
|
123
144
|
/**
|
|
124
145
|
* The order the manifest's keys are written in.
|
|
125
146
|
*
|
|
@@ -455,8 +476,28 @@ export function buildManifest({ config, packageJson, artifact, flags }) {
|
|
|
455
476
|
|
|
456
477
|
const merged = { ...declared, ...derived };
|
|
457
478
|
|
|
479
|
+
// The index every consumer resolves this package's addresses through
|
|
480
|
+
// (#239). Written unconditionally, because a package that publishes no
|
|
481
|
+
// index is one nothing can link into — and the failure of an absent one is
|
|
482
|
+
// a dead link in somebody else's build, which is exactly the kind of
|
|
483
|
+
// silence this replaced the vendored manifest to end.
|
|
484
|
+
//
|
|
485
|
+
// A flag rather than a top-level key because Foundry's manifest schema is
|
|
486
|
+
// closed and `flags` is its declared extension point; an unknown key at the
|
|
487
|
+
// top level is dropped by some readers and rejected by others.
|
|
488
|
+
if (config.contentPackage) {
|
|
489
|
+
merged.flags = {
|
|
490
|
+
...(declared.flags ?? {}),
|
|
491
|
+
metadataUrl: metadataUrl({
|
|
492
|
+
repoUrl,
|
|
493
|
+
version: packageJson.version,
|
|
494
|
+
contentPackage: config.contentPackage,
|
|
495
|
+
}),
|
|
496
|
+
};
|
|
497
|
+
}
|
|
498
|
+
|
|
458
499
|
if (flags && Object.keys(flags).length) {
|
|
459
|
-
merged.flags = { ...(declared.flags ?? {}) };
|
|
500
|
+
merged.flags = { ...(merged.flags ?? declared.flags ?? {}) };
|
|
460
501
|
for (const [namespace, values] of Object.entries(flags)) {
|
|
461
502
|
merged.flags[namespace] = {
|
|
462
503
|
...(declared.flags?.[namespace] ?? {}),
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@heroiclands/package-build",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "18.0.0",
|
|
4
4
|
"description": "Shared toolchain for building and shipping a HeroicLands Foundry VTT package — content compilation, manifest, localization, staging, bundle, release and deployment.",
|
|
5
5
|
"license": "GPL-3.0-or-later",
|
|
6
6
|
"type": "module",
|
|
@@ -27,6 +27,14 @@
|
|
|
27
27
|
"types": "./types/sohl/*.d.mts",
|
|
28
28
|
"import": "./sohl/*.mjs"
|
|
29
29
|
},
|
|
30
|
+
"./hm3": {
|
|
31
|
+
"types": "./types/hm3/index.d.mts",
|
|
32
|
+
"import": "./hm3/index.mjs"
|
|
33
|
+
},
|
|
34
|
+
"./hm3/*": {
|
|
35
|
+
"types": "./types/hm3/*.d.mts",
|
|
36
|
+
"import": "./hm3/*.mjs"
|
|
37
|
+
},
|
|
30
38
|
"./content-config": {
|
|
31
39
|
"types": "./types/content-config.d.mts",
|
|
32
40
|
"import": "./content-config.mjs"
|
|
@@ -105,8 +113,10 @@
|
|
|
105
113
|
"deploy.mjs",
|
|
106
114
|
"e2e.mjs",
|
|
107
115
|
"engine",
|
|
116
|
+
"hm3",
|
|
108
117
|
"index.mjs",
|
|
109
118
|
"lang.mjs",
|
|
119
|
+
"labels.mjs",
|
|
110
120
|
"manifest.mjs",
|
|
111
121
|
"markdownlint-config.mjs",
|
|
112
122
|
"prettier-config.mjs",
|
|
@@ -117,11 +127,14 @@
|
|
|
117
127
|
"types"
|
|
118
128
|
],
|
|
119
129
|
"dependencies": {
|
|
130
|
+
"@duckdb/node-api": "^1.5.5-r.4",
|
|
120
131
|
"@foundryvtt/foundryvtt-cli": "^3.0.4",
|
|
121
132
|
"acorn": "^8.18.0",
|
|
122
133
|
"archiver": "^8.0.0",
|
|
123
134
|
"classic-level": "^3.0.0",
|
|
124
135
|
"dotenv": "^17.2.3",
|
|
136
|
+
"eslint": "^10.10.0",
|
|
137
|
+
"eslint-plugin-yml": "^3.8.1",
|
|
125
138
|
"fflate": "^0.8.3",
|
|
126
139
|
"glob": "^13.0.6",
|
|
127
140
|
"gray-matter": "^4.0.3",
|
|
@@ -149,12 +162,14 @@
|
|
|
149
162
|
"build:types": "tsc -p tsconfig.dts.json",
|
|
150
163
|
"format": "prettier --write .",
|
|
151
164
|
"format:check": "prettier --check .",
|
|
152
|
-
"lint": "npm run format:check && npm run lint:markdown && npm run lint:content-format",
|
|
165
|
+
"lint": "npm run format:check && npm run lint:markdown && npm run lint:yaml && npm run lint:labels && npm run lint:content-format",
|
|
153
166
|
"lint:markdown": "node bin/content-build.mjs markdown",
|
|
167
|
+
"lint:yaml": "node bin/package-build.mjs yaml",
|
|
168
|
+
"lint:labels": "node bin/package-build.mjs labels check",
|
|
154
169
|
"lint:markdown:fix": "node bin/content-build.mjs markdown --fix",
|
|
155
170
|
"lint:content-format": "npm run lint:content-format:schema && npm run lint:content-format:fields",
|
|
156
|
-
"lint:content-format:schema": "node bin/content-build.mjs content-format schema --schema sohl=tests/fixtures/content-format/schema-sohl.json",
|
|
157
|
-
"lint:content-format:fields": "node bin/content-build.mjs content-format fields --fields sohl",
|
|
171
|
+
"lint:content-format:schema": "node bin/content-build.mjs content-format schema --schema sohl=tests/fixtures/content-format/schema-sohl.json --schema hm3=tests/fixtures/content-format/schema-hm3.json",
|
|
172
|
+
"lint:content-format:fields": "node bin/content-build.mjs content-format fields --fields sohl && node bin/content-build.mjs content-format fields --fields hm3",
|
|
158
173
|
"changeset": "changeset",
|
|
159
174
|
"changeset:check": "changeset status --since=origin/main",
|
|
160
175
|
"changeset:version": "changeset version && npm install --package-lock-only",
|
package/release.mjs
CHANGED
|
@@ -16,10 +16,12 @@
|
|
|
16
16
|
* carries.
|
|
17
17
|
*
|
|
18
18
|
* Foundry installs a package by fetching the `download` URL its manifest
|
|
19
|
-
* advertises, so a release publishes
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
19
|
+
* advertises, so a release publishes `<artifact>.zip`, the whole staged tree,
|
|
20
|
+
* and `<artifact>.json` beside it, which is what an already installed package
|
|
21
|
+
* re-fetches to notice a new version. A package that ships content publishes a
|
|
22
|
+
* third: the content index other packages resolve its addresses through (#239),
|
|
23
|
+
* named by the `flags.metadataUrl` the manifest advertises. Every name is fixed
|
|
24
|
+
* by what the manifest says, not chosen here — see `manifest.mjs`.
|
|
23
25
|
*
|
|
24
26
|
* Kept apart from `stage.mjs` because this is the only part of assembling a
|
|
25
27
|
* package that needs a dependency. A repository that never cuts a release from
|
|
@@ -53,9 +55,12 @@ import { ZipArchive } from "archiver";
|
|
|
53
55
|
* @param {string} [opts.outDir] - Where the release assets are written.
|
|
54
56
|
* @param {"system"|"module"} [opts.artifact] - Which artifact is shipped.
|
|
55
57
|
* Determines both asset names.
|
|
56
|
-
* @
|
|
57
|
-
*
|
|
58
|
-
*
|
|
58
|
+
* @param {string} [opts.metadataDir] - Where the build writes its content
|
|
59
|
+
* index, consulted when the advertised file was not staged.
|
|
60
|
+
* @returns {Promise<{zip: string, manifest: string, metadata?: string,
|
|
61
|
+
* bytes: number, version: string}>} The paths written, the archive's size,
|
|
62
|
+
* and the version the manifest declares. `metadata` is absent when the
|
|
63
|
+
* manifest advertises no content index.
|
|
59
64
|
* @throws {Error} When the stage has no manifest — there is nothing to release,
|
|
60
65
|
* and an archive without one installs as nothing.
|
|
61
66
|
*/
|
|
@@ -63,6 +68,7 @@ export async function packRelease({
|
|
|
63
68
|
stageDir = "build/stage",
|
|
64
69
|
outDir = "build/dist",
|
|
65
70
|
artifact = "system",
|
|
71
|
+
metadataDir = "build/content-index",
|
|
66
72
|
} = {}) {
|
|
67
73
|
const stage = path.resolve(stageDir);
|
|
68
74
|
const out = path.resolve(outDir);
|
|
@@ -104,10 +110,59 @@ export async function packRelease({
|
|
|
104
110
|
|
|
105
111
|
await fsp.copyFile(stagedManifest, path.join(out, manifestName));
|
|
106
112
|
|
|
113
|
+
const metadata = await publishMetadataIndex({ manifest, stage, out, metadataDir });
|
|
114
|
+
|
|
107
115
|
return {
|
|
108
116
|
zip: zipPath,
|
|
109
117
|
manifest: path.join(out, manifestName),
|
|
118
|
+
...(metadata ? { metadata } : {}),
|
|
110
119
|
bytes: archive.pointer(),
|
|
111
120
|
version: manifest.version,
|
|
112
121
|
};
|
|
113
122
|
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Place the content index the manifest advertises beside the archive (#239).
|
|
126
|
+
*
|
|
127
|
+
* **The asset's name comes from the manifest, not from here.** `flags.metadataUrl`
|
|
128
|
+
* is the URL every consumer fetches, so its basename is by definition the name
|
|
129
|
+
* the file has to be published under — deriving it a second time would let the
|
|
130
|
+
* two disagree, and a release whose asset is named differently from its
|
|
131
|
+
* advertised URL fails at the consumer, not here.
|
|
132
|
+
*
|
|
133
|
+
* A manifest that advertises no index publishes none: a package with no content
|
|
134
|
+
* tree has nothing to index, and is perfectly releasable. But a manifest that
|
|
135
|
+
* *does* advertise one and cannot produce it is a build error. The alternative
|
|
136
|
+
* is a release that promises an index it does not carry, whose symptom is a
|
|
137
|
+
* dead cross-package link in somebody else's build weeks later — the silent
|
|
138
|
+
* failure the vendored manifest was replaced to end.
|
|
139
|
+
*
|
|
140
|
+
* @param {object} opts
|
|
141
|
+
* @param {object} opts.manifest - The parsed staged manifest.
|
|
142
|
+
* @param {string} opts.stage - The staged tree.
|
|
143
|
+
* @param {string} opts.out - Where release assets are written.
|
|
144
|
+
* @param {string} opts.metadataDir - Where the build writes its index, tried
|
|
145
|
+
* when the file was not staged.
|
|
146
|
+
* @returns {Promise<string|undefined>} The published path, or nothing when the
|
|
147
|
+
* manifest advertises no index.
|
|
148
|
+
* @throws {Error} When one is advertised and no file backs it.
|
|
149
|
+
*/
|
|
150
|
+
async function publishMetadataIndex({ manifest, stage, out, metadataDir }) {
|
|
151
|
+
const url = manifest.flags?.metadataUrl;
|
|
152
|
+
if (!url) return undefined;
|
|
153
|
+
|
|
154
|
+
const name = path.basename(new URL(url, "https://example.invalid").pathname);
|
|
155
|
+
const candidates = [path.join(stage, name), path.resolve(metadataDir, name)];
|
|
156
|
+
const found = candidates.find((c) => fs.existsSync(c));
|
|
157
|
+
if (!found) {
|
|
158
|
+
throw new Error(
|
|
159
|
+
`the manifest advertises ${name} as \`flags.metadataUrl\` but no such ` +
|
|
160
|
+
`file exists — looked in ${candidates.join(" and ")}. Build the ` +
|
|
161
|
+
`content index before packing the release.`,
|
|
162
|
+
);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
const dest = path.join(out, name);
|
|
166
|
+
await fsp.copyFile(found, dest);
|
|
167
|
+
return dest;
|
|
168
|
+
}
|