@heroiclands/package-build 23.1.1 → 23.2.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 +63 -0
- package/bin/content-commands.mjs +68 -149
- package/bin/package-build.mjs +56 -10
- package/ci/check-banners.mjs +42 -6
- package/ci/confirm-npm-version.mjs +135 -0
- package/ci/content-format-reference.mjs +14 -5
- package/config.mjs +1 -0
- package/container.mjs +185 -4
- package/content-config.mjs +333 -210
- package/deploy.mjs +116 -10
- package/docs/api.md +238 -246
- package/docs/authoring/assets.md +11 -5
- package/docs/authoring/frontmatter.md +1 -1
- package/docs/authoring/links-and-markup.md +87 -0
- package/docs/commands.md +138 -99
- package/docs/configuration.md +277 -125
- package/docs/content-format.md +2 -0
- package/docs/diagnostics.md +48 -13
- package/docs/guides/book.md +341 -21
- package/docs/guides/e2e.md +13 -2
- package/docs/guides/icons.md +14 -4
- package/docs/guides/module-project.md +18 -8
- package/docs/guides/release-and-deploy.md +32 -0
- package/docs/guides/site.md +5 -2
- package/docs/project-setup.md +30 -10
- package/docs/reference/format-details.md +373 -170
- package/docs/reference/note-types.md +128 -109
- package/docs/reference/theme-pages.md +31 -6
- package/e2e.mjs +140 -28
- package/engine/actor-compiler.mjs +22 -31
- package/engine/address-charset.mjs +4 -17
- package/engine/address-fields.mjs +1 -1
- package/engine/address-render.mjs +12 -1
- package/engine/address-values.mjs +6 -0
- package/engine/address.mjs +46 -33
- package/engine/art-fields.mjs +30 -5
- package/engine/art-slots.mjs +10 -1
- package/engine/asset-bindings.mjs +12 -1
- package/engine/base-compiler.mjs +34 -65
- package/engine/being-age.mjs +2 -7
- package/engine/being-measurements.mjs +12 -1
- package/engine/calendar-artifacts.mjs +6 -0
- package/engine/calendar-choice.mjs +12 -1
- package/engine/calendar-format.mjs +12 -1
- package/engine/calendar-human.mjs +12 -1
- package/engine/compile-corpus.mjs +3 -1
- package/engine/content-address.mjs +9 -12
- package/engine/content-alerts.mjs +13 -1
- package/engine/content-blocks.mjs +14 -3
- package/engine/content-embeds.mjs +11 -3
- package/engine/content-figures.mjs +9 -0
- package/engine/content-footnotes.mjs +12 -1
- package/engine/content-format-check.mjs +11 -262
- package/engine/content-format.yaml +5 -2
- package/engine/content-images.mjs +3 -0
- package/engine/content-index.mjs +443 -20
- package/engine/content-links.mjs +29 -35
- package/engine/content-lint.mjs +2 -3
- package/engine/content-poetry.mjs +12 -1
- package/engine/content-slug.mjs +2 -14
- package/engine/content-spans.mjs +13 -1
- package/engine/content-tables.mjs +12 -1
- package/engine/culture-choice.mjs +210 -13
- package/engine/data-keys.mjs +19 -16
- package/engine/date-conversion.mjs +12 -1
- package/engine/declaration-check.mjs +9 -0
- package/engine/derived-fields.mjs +6 -7
- package/engine/diagnostics.mjs +34 -4
- package/engine/docs-checks.mjs +9 -0
- package/engine/document-subtypes.mjs +47 -34
- package/engine/event-views.mjs +139 -14
- package/engine/exported-scene.mjs +92 -28
- package/engine/extension-attributes.mjs +12 -1
- package/engine/field-spec.mjs +11 -102
- package/engine/folder-notes.mjs +0 -64
- package/engine/foreign-catalog.mjs +2 -1
- package/engine/format-generated.mjs +9 -0
- package/engine/foundry-entries.mjs +10 -28
- package/engine/frontmatter-lint.mjs +83 -392
- package/engine/frontmatter.mjs +20 -70
- package/engine/generate.mjs +10 -15
- package/engine/heading-attributes.mjs +12 -1
- package/engine/helpers.mjs +41 -102
- package/engine/holdings.mjs +1 -1
- package/engine/homepage.mjs +3 -10
- package/engine/ids.mjs +1 -167
- package/engine/index-records.mjs +6 -4
- package/engine/index.mjs +0 -3
- package/engine/infobox-render.mjs +15 -3
- package/engine/infobox.mjs +13 -14
- package/engine/item-compiler.mjs +4 -10
- package/engine/item-docs.mjs +3 -5
- package/engine/item-registry.mjs +2 -10
- package/engine/journals.mjs +1 -5
- package/engine/literature-notes.mjs +4 -12
- package/engine/macros.mjs +1 -2
- package/engine/map-graphviz.mjs +12 -1
- package/engine/markdown-expressions.mjs +12 -1
- package/engine/markdownlint-run.mjs +567 -0
- package/engine/metadata-index.mjs +42 -12
- package/engine/note-addresses.mjs +143 -35
- package/engine/note-claims.mjs +14 -28
- package/engine/note-dates.mjs +3 -3
- package/engine/note-frontmatter.mjs +6 -0
- package/engine/note-ids.mjs +8 -14
- package/engine/note-package.mjs +8 -25
- package/engine/note-state.mjs +1 -3
- package/engine/note-vocabulary.mjs +148 -84
- package/engine/number-words.mjs +12 -1
- package/engine/pack-config.mjs +15 -25
- package/engine/pack-router.mjs +10 -16
- package/engine/pathnames.mjs +8 -22
- package/engine/pdf-build.mjs +773 -121
- package/engine/pdf-coverage.mjs +188 -0
- package/engine/pdf-index.mjs +54 -7
- package/engine/pdf-pages.mjs +247 -0
- package/engine/pdf-render.mjs +114 -4
- package/engine/pdf-toc.mjs +29 -32
- package/engine/project-init.mjs +46 -6
- package/engine/prose-config.mjs +1 -1
- package/engine/prose-lint.mjs +52 -88
- package/engine/readability-lint.mjs +9 -0
- package/engine/reckoning-markers.mjs +12 -1
- package/engine/reference-targets.mjs +7 -0
- package/engine/runtime-only-fields.mjs +5 -8
- package/engine/scene-levels.mjs +2 -2
- package/engine/scenes.mjs +1 -1
- package/engine/shortcode-references.mjs +7 -0
- package/engine/site-build.mjs +30 -16
- package/engine/site-index.mjs +10 -16
- package/engine/site-preview.mjs +6 -0
- package/engine/site-root.mjs +8 -1
- package/engine/social-tie-terms.mjs +12 -1
- package/engine/social-ties.mjs +3 -0
- package/engine/sql-tables.mjs +56 -20
- package/engine/standing-terms.mjs +11 -1
- package/engine/standings.mjs +11 -27
- package/engine/system-block.mjs +39 -141
- package/engine/system-vocabulary.mjs +6 -0
- package/engine/walk-files.mjs +69 -0
- package/engine/web-wikilinks.mjs +23 -3
- package/engine/wikilink-syntax.mjs +32 -21
- package/engine/wikilinks.mjs +27 -16
- package/githooks/attribution.mjs +8 -0
- package/githooks/commit-message.mjs +9 -0
- package/hm3/actors.mjs +9 -48
- package/hm3/default-item-art.mjs +9 -9
- package/hm3/item-fields.mjs +2 -9
- package/hm3/template-priority.mjs +6 -7
- package/hugo-theme/data/banner-kinds.yaml +78 -0
- package/hugo-theme/data/banners.yaml +2 -3
- package/hugo-theme/layouts/404.html +3 -3
- package/hugo-theme/layouts/partials/banner-url.html +31 -7
- package/hugo-theme/layouts/partials/hero-banner.html +13 -14
- package/hugo-theme/layouts/partials/related.html +1 -1
- package/hugo-theme/layouts/partials/warn-missing-banner.html +1 -1
- package/hugo-theme/static/css/style.css +1 -1
- package/manifest.mjs +11 -4
- package/markdownlint-config.mjs +1 -1
- package/package.json +6 -3
- package/release.mjs +21 -14
- package/sohl/actors.mjs +16 -27
- package/sohl/default-item-art.mjs +3 -3
- package/sohl/document-subtypes.mjs +1 -16
- package/sohl/infobox.mjs +2 -3
- package/sohl/item-builders.mjs +5 -8
- package/sohl/item-fields.mjs +5 -35
- package/sohl/items.mjs +0 -4
- package/sohl/kb-passes.mjs +3 -3
- package/types/container.d.mts +116 -5
- package/types/content-config.d.mts +83 -35
- package/types/deploy.d.mts +42 -6
- package/types/e2e.d.mts +35 -3
- package/types/engine/address-charset.d.mts +4 -17
- package/types/engine/address.d.mts +17 -8
- package/types/engine/art-fields.d.mts +7 -4
- package/types/engine/art-slots.d.mts +6 -0
- package/types/engine/base-compiler.d.mts +15 -17
- package/types/engine/content-address.d.mts +9 -12
- package/types/engine/content-embeds.d.mts +4 -2
- package/types/engine/content-format-check.d.mts +0 -64
- package/types/engine/content-index.d.mts +82 -3
- package/types/engine/content-links.d.mts +2 -4
- package/types/engine/culture-choice.d.mts +51 -8
- package/types/engine/data-keys.d.mts +2 -2
- package/types/engine/derived-fields.d.mts +1 -2
- package/types/engine/diagnostics.d.mts +27 -4
- package/types/engine/document-subtypes.d.mts +12 -31
- package/types/engine/event-views.d.mts +14 -2
- package/types/engine/exported-scene.d.mts +3 -2
- package/types/engine/field-spec.d.mts +13 -122
- package/types/engine/folder-notes.d.mts +0 -39
- package/types/engine/foreign-catalog.d.mts +2 -1
- package/types/engine/foundry-entries.d.mts +4 -5
- package/types/engine/frontmatter-lint.d.mts +2 -2
- package/types/engine/frontmatter.d.mts +19 -45
- package/types/engine/generate.d.mts +1 -1
- package/types/engine/helpers.d.mts +25 -42
- package/types/engine/homepage.d.mts +3 -10
- package/types/engine/ids.d.mts +0 -114
- package/types/engine/index-records.d.mts +6 -4
- package/types/engine/index.d.mts +0 -1
- package/types/engine/infobox-render.d.mts +7 -1
- package/types/engine/infobox.d.mts +3 -2
- package/types/engine/item-docs.d.mts +1 -1
- package/types/engine/item-registry.d.mts +2 -4
- package/types/engine/literature-notes.d.mts +1 -1
- package/types/engine/markdownlint-run.d.mts +40 -0
- package/types/engine/metadata-index.d.mts +9 -1
- package/types/engine/note-addresses.d.mts +27 -2
- package/types/engine/note-claims.d.mts +3 -5
- package/types/engine/note-ids.d.mts +2 -4
- package/types/engine/note-package.d.mts +1 -1
- package/types/engine/note-state.d.mts +1 -3
- package/types/engine/note-vocabulary.d.mts +28 -21
- package/types/engine/pack-config.d.mts +2 -6
- package/types/engine/pack-router.d.mts +4 -4
- package/types/engine/pathnames.d.mts +3 -3
- package/types/engine/pdf-build.d.mts +98 -12
- package/types/engine/pdf-coverage.d.mts +60 -0
- package/types/engine/pdf-index.d.mts +34 -5
- package/types/engine/pdf-pages.d.mts +143 -0
- package/types/engine/pdf-render.d.mts +45 -1
- package/types/engine/pdf-toc.d.mts +12 -14
- package/types/engine/prose-config.d.mts +1 -1
- package/types/engine/prose-lint.d.mts +11 -10
- package/types/engine/runtime-only-fields.d.mts +2 -2
- package/types/engine/site-build.d.mts +9 -5
- package/types/engine/site-index.d.mts +4 -5
- package/types/engine/site-root.d.mts +1 -1
- package/types/engine/sql-tables.d.mts +31 -2
- package/types/engine/standing-terms.d.mts +2 -1
- package/types/engine/standings.d.mts +7 -9
- package/types/engine/system-block.d.mts +10 -44
- package/types/engine/walk-files.d.mts +15 -0
- package/types/engine/wikilink-syntax.d.mts +18 -19
- package/types/hm3/actors.d.mts +1 -2
- package/types/hm3/item-fields.d.mts +2 -4
- package/types/hm3/template-priority.d.mts +6 -7
- package/types/manifest.d.mts +3 -2
- package/types/release.d.mts +8 -7
- package/types/sohl/actors.d.mts +2 -1
- package/types/sohl/document-subtypes.d.mts +1 -2
- package/types/sohl/item-fields.d.mts +3 -7
- package/engine/retired-fields.mjs +0 -631
- package/types/engine/retired-fields.d.mts +0 -354
|
@@ -15,8 +15,8 @@
|
|
|
15
15
|
* The things the content format asserts that can be checked.
|
|
16
16
|
*
|
|
17
17
|
* `content-format.mjs` reads the specification as data; this compares it
|
|
18
|
-
* against the
|
|
19
|
-
* the
|
|
18
|
+
* against the two worlds it makes claims about — the systems it maps onto and
|
|
19
|
+
* the field declarations that compile them.
|
|
20
20
|
*
|
|
21
21
|
* ## The specification against a system's published schema
|
|
22
22
|
*
|
|
@@ -40,31 +40,6 @@
|
|
|
40
40
|
* this whole module avoids. So the question asked here is "does any schema
|
|
41
41
|
* declare this field?", and it narrows to the subtype once the map lands.
|
|
42
42
|
*
|
|
43
|
-
* ## The corpus against the declared vocabulary
|
|
44
|
-
*
|
|
45
|
-
* Every authored note is measured against the per-type `data` tables. Three
|
|
46
|
-
* classes of finding come out of them, one per slice of the migration:
|
|
47
|
-
*
|
|
48
|
-
* | class | what it means |
|
|
49
|
-
* | --- | --- |
|
|
50
|
-
* | `unknown-type` | the format declares no section for this note's `type` |
|
|
51
|
-
* | `unknown-data-key` | a key in `data:`, which is closed, that the type does not declare |
|
|
52
|
-
* | `top-level-data-key` | a declared `data` property written at top level instead |
|
|
53
|
-
* | `system-block-data-key` | a declared shared source written straight into a system block |
|
|
54
|
-
*
|
|
55
|
-
* **It reports; it does not fail.** All ~6,210 authored notes predate the
|
|
56
|
-
* format, so a failing check would be red on day one in every repository and
|
|
57
|
-
* would stay red for the length of the epic — which is a check nobody can act
|
|
58
|
-
* on and everybody learns to skip. The counts are the migration's progress bar
|
|
59
|
-
* instead, and `--strict` turns them fatal — turned on slice by slice, as each
|
|
60
|
-
* class reaches zero.
|
|
61
|
-
*
|
|
62
|
-
* **What it deliberately does not check.** A key inside a `sohl:` or `hm3:`
|
|
63
|
-
* block that the format says nothing about is left alone: those regions are
|
|
64
|
-
* closed against *the system's* schema, not against this document, and
|
|
65
|
-
* `frontmatter-lint.mjs` already checks them against the declared fields. This
|
|
66
|
-
* module only reports a key whose home the format actually states.
|
|
67
|
-
*
|
|
68
43
|
* ## The specification against the declarations that compile it
|
|
69
44
|
*
|
|
70
45
|
* The specification hand-writes a `data` table under most of its type sections,
|
|
@@ -88,9 +63,8 @@
|
|
|
88
63
|
* set and are not meant to be: the document names the *shared* source a field is
|
|
89
64
|
* written as, while a declaration names every key the system's own block accepts
|
|
90
65
|
* — including the system-specific ones (`heft`, `strikeModes`) that the document
|
|
91
|
-
* correctly never maps.
|
|
92
|
-
*
|
|
93
|
-
* fields only one side names come back as *coverage*, and the types only one side
|
|
66
|
+
* correctly never maps. Holding the sets equal would report a defect where
|
|
67
|
+
* there is none. So the fields only one side names come back as *coverage*, and the types only one side
|
|
94
68
|
* describes come back **named** rather than skipped in silence — a check that
|
|
95
69
|
* quietly compared nine of twenty-three types would read as one that covered
|
|
96
70
|
* them all.
|
|
@@ -98,15 +72,9 @@
|
|
|
98
72
|
* @module
|
|
99
73
|
*/
|
|
100
74
|
|
|
101
|
-
import { currentType } from "./ids.mjs";
|
|
102
75
|
import { SCHEMA_ARTIFACT_VERSION } from "./schema-check.mjs";
|
|
103
76
|
import { authoredFields } from "./field-spec.mjs";
|
|
104
|
-
import { positionInFrontmatter } from "./diagnostics.mjs";
|
|
105
|
-
import { RETIRED_FIELD_ALIASES } from "./retired-fields.mjs";
|
|
106
77
|
import { authoredKey } from "./system-block.mjs";
|
|
107
|
-
// The top-level vocabulary, read rather than restated — a second list of the
|
|
108
|
-
// keys a note's own identity uses is one more thing to drift.
|
|
109
|
-
import { NOTE_TOP_LEVEL_KEY_SET } from "./note-vocabulary.mjs";
|
|
110
78
|
|
|
111
79
|
/**
|
|
112
80
|
* Every field path any subtype of a published schema declares.
|
|
@@ -225,223 +193,6 @@ export function checkSchemaTargets({ format, schemas }) {
|
|
|
225
193
|
return { findings, checked, unchecked };
|
|
226
194
|
}
|
|
227
195
|
|
|
228
|
-
/**
|
|
229
|
-
* Edit distance, capped — enough to answer "did you mean".
|
|
230
|
-
*
|
|
231
|
-
* The same arithmetic `frontmatter-lint.mjs` carries. Duplicated rather than
|
|
232
|
-
* shared because that module keeps it private and this one is the second
|
|
233
|
-
* caller; the third is the moment to lift it out, not the second.
|
|
234
|
-
*
|
|
235
|
-
* @param {string} a - One string.
|
|
236
|
-
* @param {string} b - The other.
|
|
237
|
-
* @returns {number} The Levenshtein distance.
|
|
238
|
-
*/
|
|
239
|
-
function distance(a, b) {
|
|
240
|
-
const cols = b.length + 1;
|
|
241
|
-
let prev = Array.from({ length: cols }, (_, j) => j);
|
|
242
|
-
for (let i = 1; i <= a.length; i += 1) {
|
|
243
|
-
const row = [i];
|
|
244
|
-
for (let j = 1; j < cols; j += 1) {
|
|
245
|
-
row[j] = Math.min(
|
|
246
|
-
prev[j] + 1,
|
|
247
|
-
row[j - 1] + 1,
|
|
248
|
-
prev[j - 1] + (a[i - 1] === b[j - 1] ? 0 : 1),
|
|
249
|
-
);
|
|
250
|
-
}
|
|
251
|
-
prev = row;
|
|
252
|
-
}
|
|
253
|
-
return prev[cols - 1];
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
/**
|
|
257
|
-
* The declared key an unknown one was most likely meant to be.
|
|
258
|
-
*
|
|
259
|
-
* @param {string} key - The unknown key.
|
|
260
|
-
* @param {Iterable<string>} candidates - The declared keys.
|
|
261
|
-
* @returns {string|undefined} The nearest, when it is near enough to suggest.
|
|
262
|
-
*/
|
|
263
|
-
function nearest(key, candidates) {
|
|
264
|
-
let best;
|
|
265
|
-
let bestAt = Infinity;
|
|
266
|
-
for (const candidate of candidates) {
|
|
267
|
-
const d = distance(key.toLowerCase(), candidate.toLowerCase());
|
|
268
|
-
if (d < bestAt) {
|
|
269
|
-
bestAt = d;
|
|
270
|
-
best = candidate;
|
|
271
|
-
}
|
|
272
|
-
}
|
|
273
|
-
return bestAt <= Math.max(1, Math.floor(key.length / 3)) ? best : undefined;
|
|
274
|
-
}
|
|
275
|
-
|
|
276
|
-
/**
|
|
277
|
-
* Top-level keys the format names as the note's own, whatever its type.
|
|
278
|
-
*
|
|
279
|
-
* The top-level vocabulary itself, read rather than restated: this set exists
|
|
280
|
-
* to keep a note's own identity fields from being mistaken for a misplaced
|
|
281
|
-
* `data` property where the two share a name, and the keys that are a note's
|
|
282
|
-
* own are exactly the ones the region declares. `type` and `subType` are the
|
|
283
|
-
* pair that matters — several mapping tables name `subType` as a shared source,
|
|
284
|
-
* and it is authored at top level by design. A key the region does *not*
|
|
285
|
-
* declare, such as `id` beside `data.id`, is reported as the misplaced `data`
|
|
286
|
-
* property it is.
|
|
287
|
-
*
|
|
288
|
-
* @type {ReadonlySet<string>}
|
|
289
|
-
*/
|
|
290
|
-
export const NOTE_LEVEL_KEYS = NOTE_TOP_LEVEL_KEY_SET;
|
|
291
|
-
|
|
292
|
-
/** Whether a value is a plain object a block could be written as. */
|
|
293
|
-
function isBlock(value) {
|
|
294
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
295
|
-
}
|
|
296
|
-
|
|
297
|
-
/** @type {WeakMap<object, ReadonlySet<string>>} */
|
|
298
|
-
const systemCache = new WeakMap();
|
|
299
|
-
|
|
300
|
-
/**
|
|
301
|
-
* The systems the specification names.
|
|
302
|
-
*
|
|
303
|
-
* Taken from the columns of its mapping tables rather than from a list written
|
|
304
|
-
* here, so a system added to the document is measured with no code change.
|
|
305
|
-
* Cached per parsed document, since the answer is the same for every note.
|
|
306
|
-
*
|
|
307
|
-
* @param {import("./content-format.mjs").ContentFormat} format - The parsed
|
|
308
|
-
* specification.
|
|
309
|
-
* @returns {ReadonlySet<string>} The system ids.
|
|
310
|
-
*/
|
|
311
|
-
export function systemsNamed(format) {
|
|
312
|
-
let known = systemCache.get(format);
|
|
313
|
-
if (!known) {
|
|
314
|
-
known = new Set(format.claims.map((claim) => claim.system));
|
|
315
|
-
systemCache.set(format, known);
|
|
316
|
-
}
|
|
317
|
-
return known;
|
|
318
|
-
}
|
|
319
|
-
|
|
320
|
-
/**
|
|
321
|
-
* Measure one note against the vocabulary the format declares for its type.
|
|
322
|
-
*
|
|
323
|
-
* @param {object} note - `{file, raw, fm}` — as the link index hands one over.
|
|
324
|
-
* @param {import("./content-format.mjs").ContentFormat} format - The parsed
|
|
325
|
-
* specification.
|
|
326
|
-
* @param {object} [opts]
|
|
327
|
-
* @param {"warning"|"error"} [opts.severity="warning"] - What the findings are.
|
|
328
|
-
* A report by default; `--strict` raises it.
|
|
329
|
-
* @returns {object[]} Findings, each carrying the `class` it belongs to
|
|
330
|
-
* alongside the fields `emitDiagnostic` reads.
|
|
331
|
-
*/
|
|
332
|
-
export function measureNote(note, format, { severity = "warning" } = {}) {
|
|
333
|
-
const findings = [];
|
|
334
|
-
const fm = note?.fm ?? {};
|
|
335
|
-
const type = String(fm.type ?? "");
|
|
336
|
-
const raw = () => note?.raw ?? "";
|
|
337
|
-
const at = (key, value) => positionInFrontmatter(raw(), key, value);
|
|
338
|
-
const add = (cls, key, message, value) =>
|
|
339
|
-
findings.push({
|
|
340
|
-
file: note?.file,
|
|
341
|
-
...at(key, value),
|
|
342
|
-
severity,
|
|
343
|
-
class: cls,
|
|
344
|
-
message,
|
|
345
|
-
});
|
|
346
|
-
|
|
347
|
-
// The specification's sections are headed by the current spelling of a note
|
|
348
|
-
// type, so a note still on a renamed one is measured against the section it
|
|
349
|
-
// will move to rather than reported as a type the format does not declare.
|
|
350
|
-
// The rename itself is the frontmatter lint's finding.
|
|
351
|
-
const spec = format.types.get(currentType(type));
|
|
352
|
-
if (!spec) {
|
|
353
|
-
add(
|
|
354
|
-
"unknown-type",
|
|
355
|
-
"type",
|
|
356
|
-
`the content format declares no \`### type: ${type}\` section, so ` +
|
|
357
|
-
`nothing says what this note may write`,
|
|
358
|
-
type,
|
|
359
|
-
);
|
|
360
|
-
return findings;
|
|
361
|
-
}
|
|
362
|
-
|
|
363
|
-
// `data:` is closed: every key it carries must be one the type declares.
|
|
364
|
-
const data = isBlock(fm.data) ? fm.data : undefined;
|
|
365
|
-
// The retired spelling of a key the format states is measured as the key it
|
|
366
|
-
// will be written as, exactly as a note still on a renamed *type* is
|
|
367
|
-
// measured against the section it will move to. The rename itself is the
|
|
368
|
-
// frontmatter lint's finding, and reporting it here as well would count one
|
|
369
|
-
// note twice in a migration whose counts are the point.
|
|
370
|
-
const renamedDataKeys = new Set(
|
|
371
|
-
[...spec.dataKeys].map((key) => RETIRED_FIELD_ALIASES[key]).filter(Boolean),
|
|
372
|
-
);
|
|
373
|
-
for (const key of Object.keys(data ?? {})) {
|
|
374
|
-
if (spec.dataKeys.has(key) || renamedDataKeys.has(key)) continue;
|
|
375
|
-
const guess = nearest(key, spec.dataKeys);
|
|
376
|
-
add(
|
|
377
|
-
"unknown-data-key",
|
|
378
|
-
key,
|
|
379
|
-
`\`data.${key}\` is not a property of a ${type}` +
|
|
380
|
-
(guess ? `. Did you mean "${guess}"?` : ""),
|
|
381
|
-
);
|
|
382
|
-
}
|
|
383
|
-
|
|
384
|
-
// The two regions a shared source is written in today, and neither is where
|
|
385
|
-
// the format puts it. A key the type's own table declares is not merely an
|
|
386
|
-
// unrecognised one — it is one whose home the format states, so the finding
|
|
387
|
-
// names that home rather than the vocabulary.
|
|
388
|
-
for (const key of spec.dataKeys) {
|
|
389
|
-
if (NOTE_LEVEL_KEYS.has(key)) continue;
|
|
390
|
-
if (Object.hasOwn(fm, key)) {
|
|
391
|
-
add(
|
|
392
|
-
"top-level-data-key",
|
|
393
|
-
key,
|
|
394
|
-
`\`${key}\` is a declared property of a ${type} and belongs in ` +
|
|
395
|
-
`\`data.${key}\`; at top level it reaches the web page and no ` +
|
|
396
|
-
`Foundry document`,
|
|
397
|
-
);
|
|
398
|
-
}
|
|
399
|
-
for (const system of systemsNamed(format)) {
|
|
400
|
-
const block = fm[system];
|
|
401
|
-
if (!isBlock(block) || !Object.hasOwn(block, key)) continue;
|
|
402
|
-
add(
|
|
403
|
-
"system-block-data-key",
|
|
404
|
-
key,
|
|
405
|
-
`\`${system}.${key}\` is a declared shared source and belongs in ` +
|
|
406
|
-
`\`data.${key}\`; a system states an exception under ` +
|
|
407
|
-
`\`${system}.system\`, not by holding the shared value itself`,
|
|
408
|
-
);
|
|
409
|
-
}
|
|
410
|
-
}
|
|
411
|
-
|
|
412
|
-
return findings;
|
|
413
|
-
}
|
|
414
|
-
|
|
415
|
-
/**
|
|
416
|
-
* Measure a corpus, and count what it finds by class.
|
|
417
|
-
*
|
|
418
|
-
* The counts are the point as much as the findings: a class is promoted to
|
|
419
|
-
* fatal when its count reaches zero, so a run that prints them is the
|
|
420
|
-
* migration's progress bar.
|
|
421
|
-
*
|
|
422
|
-
* @param {Iterable<object>} notes - `{file, raw, fm}` for each authored note.
|
|
423
|
-
* @param {import("./content-format.mjs").ContentFormat} format - The parsed
|
|
424
|
-
* specification.
|
|
425
|
-
* @param {object} [opts]
|
|
426
|
-
* @param {boolean} [opts.strict=false] - Report the findings as errors rather
|
|
427
|
-
* than warnings. Turned on one slice at a time.
|
|
428
|
-
* @returns {{findings: object[], notes: number, byClass: Record<string, number>}}
|
|
429
|
-
*/
|
|
430
|
-
export function measureCorpus(notes, format, { strict = false } = {}) {
|
|
431
|
-
const findings = [];
|
|
432
|
-
let count = 0;
|
|
433
|
-
for (const note of notes) {
|
|
434
|
-
count += 1;
|
|
435
|
-
findings.push(...measureNote(note, format, { severity: strict ? "error" : "warning" }));
|
|
436
|
-
}
|
|
437
|
-
/** @type {Record<string, number>} */
|
|
438
|
-
const byClass = {};
|
|
439
|
-
for (const finding of findings) {
|
|
440
|
-
byClass[finding.class] = (byClass[finding.class] ?? 0) + 1;
|
|
441
|
-
}
|
|
442
|
-
return { findings, notes: count, byClass };
|
|
443
|
-
}
|
|
444
|
-
|
|
445
196
|
/**
|
|
446
197
|
* Where one declared field sends one authored value.
|
|
447
198
|
*
|
|
@@ -487,12 +238,11 @@ export function fieldDriftMessage({ noteType, source, target, name, to }) {
|
|
|
487
238
|
* A shared source, as the two sides spell it.
|
|
488
239
|
*
|
|
489
240
|
* The specification writes every type-specific row `data.<key>` — the container
|
|
490
|
-
* `data:`
|
|
491
|
-
*
|
|
492
|
-
*
|
|
493
|
-
*
|
|
494
|
-
*
|
|
495
|
-
* unmapped, which is the opposite of what this check is for.
|
|
241
|
+
* `data:` holds those facts in — while a declaration writes either the bare
|
|
242
|
+
* key or the same dotted path. Both are the same source, so both are
|
|
243
|
+
* normalized before they are compared; matching one spelling against the
|
|
244
|
+
* other would report every field as unmapped, which is the opposite of what
|
|
245
|
+
* this check is for.
|
|
496
246
|
*
|
|
497
247
|
* A note-level source — the `subType` several tables name — carries no prefix
|
|
498
248
|
* and is returned unchanged.
|
|
@@ -600,9 +350,8 @@ export function checkDeclaredFields({ format, itemFields, system, severity = "er
|
|
|
600
350
|
const target = claim.target.replace(/^system\./, "");
|
|
601
351
|
const found = declarationFor(claim.source, authored);
|
|
602
352
|
// No declaration names it. That is coverage, not a contradiction —
|
|
603
|
-
// the specification maps fields no builder emits
|
|
604
|
-
//
|
|
605
|
-
// already reports as unemitted.
|
|
353
|
+
// the specification maps fields no builder emits, which
|
|
354
|
+
// `schema-check.mjs` already reports as unemitted.
|
|
606
355
|
if (!found) continue;
|
|
607
356
|
const match = found.field;
|
|
608
357
|
fields += 1;
|
|
@@ -7,7 +7,6 @@ types:
|
|
|
7
7
|
- templatePriority
|
|
8
8
|
- archetypes
|
|
9
9
|
- occupation
|
|
10
|
-
- stations
|
|
11
10
|
- lore
|
|
12
11
|
- culture
|
|
13
12
|
- homes
|
|
@@ -56,6 +55,7 @@ types:
|
|
|
56
55
|
- economy
|
|
57
56
|
- lore
|
|
58
57
|
- parents
|
|
58
|
+
- culture
|
|
59
59
|
- relations
|
|
60
60
|
subTypes:
|
|
61
61
|
- guild
|
|
@@ -279,6 +279,7 @@ types:
|
|
|
279
279
|
data:
|
|
280
280
|
- scene
|
|
281
281
|
- fixup
|
|
282
|
+
- thumbnail
|
|
282
283
|
- place
|
|
283
284
|
subTypes:
|
|
284
285
|
- battlemap
|
|
@@ -293,6 +294,7 @@ types:
|
|
|
293
294
|
- purpose
|
|
294
295
|
- lore
|
|
295
296
|
- parents
|
|
297
|
+
- culture
|
|
296
298
|
- population
|
|
297
299
|
- government
|
|
298
300
|
- market
|
|
@@ -336,7 +338,8 @@ types:
|
|
|
336
338
|
- adventure
|
|
337
339
|
- encounter
|
|
338
340
|
doc:
|
|
339
|
-
data:
|
|
341
|
+
data:
|
|
342
|
+
- culture
|
|
340
343
|
subTypes:
|
|
341
344
|
- rules
|
|
342
345
|
- userguide
|
|
@@ -5,6 +5,9 @@
|
|
|
5
5
|
* This work is licensed under the GNU General Public License v3.0 (GPLv3).
|
|
6
6
|
* You may copy, modify, and distribute it under the terms of that license.
|
|
7
7
|
*
|
|
8
|
+
* For full terms, see the LICENSE.md file in the project root or visit:
|
|
9
|
+
* https://www.gnu.org/licenses/gpl-3.0.html
|
|
10
|
+
*
|
|
8
11
|
* SPDX-License-Identifier: GPL-3.0-or-later
|
|
9
12
|
*/
|
|
10
13
|
|